diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/CREDITS linux.18p17/CREDITS
--- linux.vanilla/CREDITS	Sat Sep  9 18:42:31 2000
+++ linux.18p17/CREDITS	Thu Oct 19 01:28:15 2000
@@ -46,6 +46,10 @@
 P: 1024/85AD9EED AD C0 49 08 91 67 DF D7  FA 04 1A EE 09 E8 44 B0
 D: Unix98 pty support.
 D: APM update to 1.2 spec.
+D: /devfs hacking.
+S: 322 N. Riverside Dr.
+S: Neptune, NJ 07753
+S: USA
 
 N: Erik Andersen
 E: andersee@debian.org
@@ -202,6 +206,14 @@
 S: 6369 BG Simpelveld
 S: The Netherlands
 
+N: Peter Berger
+E: pberger@brimson.com
+W: http://www.brimson.com
+D: Author/maintainer of Digi AccelePort USB driver
+S: 1549 Hiironen Rd.
+S: Brimson, MN  55602
+S: USA
+
 N: Hennus Bergman
 E: hennus@cybercomm.nl
 W: http://www.cybercomm.nl/~hennus/
@@ -222,6 +234,14 @@
 S: Marrickville NSW 2204
 S: Australia
 
+N: Hugh Blemings
+E: hugh@linuxcare.com
+W: http://www.linuxcare.com.au/hugh/
+D: Author and maintainer of the Keyspan USB to Serial drivers
+S: Po Box 234
+S: Belconnen ACT 2616
+S: Australia
+
 N: Philip Blundell
 E: philb@gnu.org
 D: Linux/ARM hacker
@@ -253,6 +273,13 @@
 D: Configuration help text support
 D: Linux CD and Support Giveaway List
 
+N: Al Borchers
+E: alborchers@steinerpoint.com
+D: Author/maintainer of Digi AccelePort USB driver
+S: 4912 Zenith Ave. S.
+S: Minneapolis, MN  55410
+S: USA
+
 N: Zoltán Böszörményi
 E: zboszor@mail.externet.hu
 D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support
@@ -393,6 +420,13 @@
 D: Assorted sched/mm titbits
 S: Oxfordshire, UK.
 
+N: Mark Corner
+E: mcorner@umich.edu
+W: http://www.eecs.umich.edu/~mcorner/
+D: USB Bluetooth Driver
+S: University of Michigan
+S: Ann Arbor, MI
+
 N: Alan Cox
 W: http://roadrunner.swansea.linux.org.uk/alan.shtml
 E: alan@lxorguk.ukuu.org.uk
@@ -500,6 +534,15 @@
 S: Blacksburg, Virginia 24061
 S: USA
 
+N: Randy Dunlap
+E: randy.dunlap@intel.com
+W: http://home.att.net/~randy.dunlap/
+W: http://www.linux-usb.org
+D: Linux-USB subsystem, USB core/UHCI/printer/storage drivers
+S: 5200 NE Elam Young Pkwy., M/S HF3-77
+S: Hillsboro, Oregon 97124
+S: USA
+
 N: Cyrus Durgin
 E: cider@speakeasy.org
 W: http://www.speakeasy.org/~cider/
@@ -554,6 +597,13 @@
 S: Huntington Beach, California 92649
 S: USA
 
+N: Johannes Erdfelt
+E: jerdfelt@valinux.com
+D: USB
+S: 6350 Stoneridge Mall Road
+S: Pleasanton, CA 94588
+S: USA
+
 N: Doug Evans
 E: dje@cygnus.com
 D: Wrote Xenix FS (part of standard kernel since 0.99.15)
@@ -681,6 +731,7 @@
 D: parent process death signal to children
 D: prctl() syscall
 D: /proc/mtrr support to manipulate MTRRs on Intel P6 family
+D: Device FileSystem (devfs)
 S: CSIRO Australia Telescope National Facility
 S: P.O. Box 76, Epping
 S: New South Wales, 2121
@@ -1128,6 +1179,18 @@
 S: L3R 8B2
 S: Canada
 
+N: Andrzej M. Krzysztofowicz
+E: ankry@mif.pg.gda.pl
+D: Some 8-bit XT disk driver and /devfs hacking 
+
+N: Greg Kroah-Hartman
+E: greg@kroah.com
+W: http://www.kroah.com/linux-usb/
+D: USB Serial Converter driver framework, USB Handspring Visor driver
+D: ConnectTech WHITEHeat USB driver, Generic USB Serial driver
+D: USB Bluetooth driver,
+D: bits and pieces of USB core code.
+
 N: Russell Kroll
 E: rkroll@exploits.org
 W: http://www.exploits.org/
@@ -1287,7 +1350,7 @@
 N: Pavel Machek
 E: pavel@atrey.karlin.mff.cuni.cz
 D: Softcursor for vga, hypertech cdrom support, vcsa bugfix
-D: Network block device, sun4/330 port 
+D: Network block device, sun4/330 port, USB
 S: Volkova 1131
 S: 198 00 Praha 9
 S: Czech Republic
@@ -1317,6 +1380,15 @@
 S: Halifax, Nova Scotia
 S: Canada B3J 3C8
 
+N: Petko Manolov
+E: petkan@dce.bg
+D: USB ethernet (pegasus) driver
+D: optimizing i[45]86 string routines
+D: i386 task swithing hacks
+S: 5, Vrah Mancho str.
+S: 1324 Sofia
+S: Bulgaria
+
 N: Martin Mares
 E: mj@atrey.karlin.mff.cuni.cz
 W: http://atrey.karlin.mff.cuni.cz/~mj/
@@ -1382,6 +1454,7 @@
 E: acme@conectiva.com.br
 D: wanrouter hacking
 D: cyclades 2X sync card driver (still in early devel stage)
+D: USB hacking
 S: R. Prof. Rubens Elke Braga, 558 - Parolin
 S: 80220-320 Curitiba - Parana
 S: Brazil
@@ -1473,6 +1546,13 @@
 S: Fremont, California 94536
 S: USA
 
+N: Sam Mosel
+E: sam.mosel@computer.org
+D: Wacom Intuos USB Support
+S: 22 Seaview St
+S: Fullarton 5063
+S: South Australia
+
 N: Ian A. Murdock
 E: imurdock@gnu.ai.mit.edu
 D: Creator of Debian distribution
@@ -1576,7 +1656,7 @@
 D: Joystick driver
 D: arcnet-hardware readme
 D: Minor ARCnet hacking
-D: USB development
+D: USB (HID, ACM, Printer ...)
 S: Ucitelska 1576
 S: Prague 8
 S: 182 00 Czech Republic
@@ -1711,10 +1791,10 @@
 S: Germany
 
 N: Joerg Reuter
-E: jreuter@poboxes.com
-W: http://poboxes.com/jreuter/
-W: http://qsl.net/dl1bke/
-D: Generic Z8530 driver, AX.25 DAMA slave implementation
+E: jreuter@yaina.de
+W: http://yaina.de/jreuter
+W: http://www.qsl.net/dl1bke/
+D: Generic Z8530 driver for AX.25, AX.25 DAMA slave implementation
 D: Several AX.25 hacks
 
 N: Francois-Rene Rideau
@@ -1799,6 +1879,14 @@
 W: http://netfilter.kernelnotes.org/ipchains
 D: Ruggedly handsome.
 D: Developed Generic IP Firewalling Chains with Michael Neuling.
+
+N: Bill Ryder
+E: bryder@sgi.com
+D: FTDI_SIO usb/serial converter driver
+W: http://reality.sgi.com/bryder_wellington/ftdi_sio
+S: I/3 Walter St
+S: Wellington
+S: New Zealand
 
 N: Thomas Sailer
 E: sailer@ife.ee.ethz.ch
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/ARM-README linux.18p17/Documentation/ARM-README
--- linux.vanilla/Documentation/ARM-README	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/ARM-README	Thu Jan  1 01:00:00 1970
@@ -1,181 +0,0 @@
-			   ARM Linux 2.1.99
-			   ================
-
-  Since this is a development kernel, it will not be as stable as the 2.0
-  series, and can cause very nasty problems (eg, trashing your hard disk).
-  When running one of these kernels, I advise you to back up the complete
-  contents of all your hard disks.
-
-
-Contributors
-------------
-
-  Here is a list of people actively working on the project (If you
-  wish to be added to the list, please email me):
-
-  Name: Russell King
-  Mail: linux@arm.uk.linux.org
-  Desc: Original developer of ARM Linux, project co-ordinator.
-
-  Name: Dave Gilbert
-  Mail: linux@treblig.org
-  Desc: A3/4/5xx floppy and hard disk code maintainer.
-
-  Name: Philip Blundell
-  Mail: Philip.Blundell@pobox.com
-  Desc: Architecture and processor selection during make config.
-
-
-Todo list
----------
-
-  This is the list of changes to be done (roughly prioritised):
-
-  * fully test new MEMC translation code
-  * fully test new AcornSCSI driver.
-  * reply to email ;)
-
-
-			     Notes
-			     =====
-
-Compilation of kernel
----------------------
-
-  In order to compile ARM Linux, you will need a compiler capable of
-  generating ARM ELF code with GNU extensions.  GCC-2.7.2.2 is good.
-
-  To build ARM Linux natively, you shouldn't have to alter the ARCH = line in
-  the top level Makefile.  However, if you don't have the ARM Linux ELF tools
-  installed as default, then you should change the CROSS_COMPILE line as
-  detailed below.
-
-  If you wish to cross-compile, then alter the following lines in the top
-  level make file:
-
-    ARCH = <whatever>
-	with
-    ARCH = arm
-
-	and
-
-    CROSS_COMPILE=
-	to
-    CROSS_COMPILE=<your-path-to-your-compiler-without-gcc>
-	eg.
-    CROSS_COMPILE=/usr/bin/arm-unknown-linuxelf-
-
-  Do a 'make config', followed by 'make dep', and finally 'make all' to
-  build the kernel (vmlinux).  A compressed image can be built by doing
-  a 'make zImage' instead of 'make all'.
-
-
-Bug reports etc.
-----------------
-
-  Please send patches, bug reports and code for the ARM Linux project
-  to linux@arm.uk.linux.org.  Patches will not be included into future
-  kernels unless they come to me (or the relevant person concerned).
-
-  When sending bug reports, please ensure that they contain all relevant
-  information, eg. the kernel messages that were printed before/during
-  the problem, what you were doing, etc.
-
-  For patches, please include some explanation as to what the patch does
-  and why (if relevant).
-
-
-Modules
--------
-
-  Although modularisation is supported (and required for the FP emulator),
-  each module on an arm2/arm250/arm3 machine when is loaded will take
-  memory up to the next 32k boundary due to the size of the pages.  Hence is
-  modularisation on these machines really worth it?
-
-  However, arm6 and up machines allow modules to take multiples of 4k, and
-  as such Acorn RiscPCs and other architectures using these processors can
-  make good use of modularisation.
-
-
-ADFS Image files
-----------------
-
-  You can access image files on your ADFS partitions by mounting the ADFS
-  partition, and then using the loopback device driver.  You must have
-  losetup installed.
-
-  Please note that the PCEmulator DOS partitions have a partition table at
-  the start, and as such, you will have to give '-o offset' to losetup.
-
-
-Kernel initialisation abort codes
----------------------------------
-
-  When the kernel is unable to boot, it will if possible display a colour
-  at the top of the screen.  The colours have the following significance
-  when run in a 16 colour mode with the default palette:
-
-    Stripes of white, red, yellow, and green:
-       Kernel does not support the processor architecture detected.
-
-
-Request to developers
----------------------
-
-  When writing device drivers which include a separate assembler file, please
-  include it in with the C file, and not the arch/arm/lib directory.  This
-  allows the driver to be compiled as a loadable module without requiring
-  half the code to be compiled into the kernel image.
-
-  In general, try to avoid using assembler unless it is really necessary.  It
-  makes drivers far less easy to port to other hardware.
-
-
-ST506 hard drives
------------------
-
-  The ST506 hard drive controllers seem to be working fine (if a little
-  slowly).  At the moment they will only work off the controllers on an
-  A4x0's motherboard, but for it to work off a Podule just requires
-  someone with a podule to add the addresses for the IRQ mask and the
-  HDC base to the source.
-
-  As of 31/3/96 it works with two drives (you should get the ADFS
-  *configure hard drive set to 2). I've got an internal 20 MB and a great
-  big external 5.25" FH 64 MB drive (who could ever want more :-) ).
-
-  I've just got 240 K/s off it (a dd with bs=128k); that's about half of what
-  RiscOS gets, but it's a heck of a lot better than the 50 K/s I was getting
-  last week :-)
-
-  Known bug: Drive data errors can cause a hang; including cases where
-  the controller has fixed the error using ECC. (Possibly ONLY
-  in that case...hmm).
-
-
-1772 Floppy
------------
-  This also seems to work OK, but hasn't been stressed much lately.  It
-  hasn't got any code for disc change detection in there at the moment which
-  could be a bit of a problem!  Suggestions on the correct way to do this
-  are welcome.
-
-
-Kernel entry (head-armv.S)
---------------------------
-  The initial entry into the kernel made via head-armv.S uses architecture
-  independent code.  The architecture is selected by the value of 'r1' on
-  entry, which must be kept unique.  You can register a new architecture
-  by mailing the following details to rmk@arm.uk.linux.org.  Please give
-  the mail a subject of 'Register new architecture':
-
-    Name: <name of your architecture>
-    ARCHDIR: <name of include/asm-arm/arch-* directory>
-    Description:
-    <description of your architecture>
-
-  Please follow this format - it is an automated system.  You should
-  receive a reply the next day.
----
-Russell King (03/05/1998)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Changes linux.18p17/Documentation/Changes
--- linux.vanilla/Documentation/Changes	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/Changes	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Configure.help linux.18p17/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/Configure.help	Sun Oct 15 21:57:14 2000
@@ -624,6 +624,11 @@
   say M here and read Documentation/modules.txt. The module will be
   called ps2esdi.o.
 
+ALI M15X3 chipset support (EXPERIMENTAL)
+CONFIG_BLK_DEV_ALI15X3
+  This driver ensures (U)DMA support for ALI 1543 and 1543C,
+  1535, 1535D onboard chipsets.
+
 Tekram TRM290 chipset support (EXPERIMENTAL)
 CONFIG_BLK_DEV_TRM290
   This driver adds support for bus master DMA transfers
@@ -658,6 +663,13 @@
 CONFIG_BLK_DEV_CMD646
   Say Y here if you have an IDE controller like this.
 
+Cyrix CS5530 MediaGX chipset support
+CONFIG_BLK_DEV_CS5530
+  Include support for UDMA on the Cyrix MediaGX 5530 chipset. This
+  will automatically be detected and configured if found.
+
+  It is safe to say Y to this question.
+
 QDI QD6580 support
 CONFIG_BLK_DEV_QD6580
   This driver is enabled at runtime using the "ide0=qd6580" kernel
@@ -681,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
@@ -1456,6 +1512,61 @@
 CONFIG_FB_SGIVW
   SGI Visual Workstation support for framebuffer graphics.
 
+I2O support
+CONFIG_I2O
+  The Intelligent Input/Output (I2O) architecture allows hardware
+  drivers to be split into two parts: an operating system specific
+  module called the OSM and an hardware specific module called the
+  HDM. The OSM can talk to a whole range of HDM's, and ideally the
+  HDM's are not OS dependent. This allows for the same HDM driver to
+  be used under different operating systems if the relevant OSM is in
+  place. In order for this to work, you need to have an I2O interface
+  adapter card in your computer. This card contains a special I/O
+  processor (IOP), thus allowing high speeds since the CPU does not
+  have to deal with I/O.
+
+  If you say Y here, you will get a choice of interface adapter
+  drivers and OSM's with the following questions.
+
+  This support is also available as a module ( = code which can be
+  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. You will get modules called i2o_core.o
+  and i20_config.o. 
+
+  If unsure, say N.
+
+I2O PCI support
+CONFIG_I2O_PCI
+  Say Y for support of PCI bus I2O interface adapters. Currently this
+  is the only variety supported, so you should say Y.
+
+  This support is also available as a module called i2o_pci.o ( = code
+  which can be 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.
+
+I2O Block OSM
+CONFIG_I2O_BLOCK
+  Include support for the I2O Block OSM. The Block OSM presents disk
+  and other structured block devices to the operating system.
+
+  This support is also available as a module called i2o_block.o ( =
+  code which can be 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.
+
+I2O SCSI OSM
+CONFIG_I2O_SCSI
+  Allows direct SCSI access to SCSI devices on a SCSI or FibreChannel
+  I2O controller. You can use both the SCSI and Block OSM together if
+  you wish.
+
+  This support is also available as a module called i2o_scsi.o ( =
+  code which can be 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.
+
 System V IPC
 CONFIG_SYSVIPC
   Inter Process Communication is a suite of library functions and
@@ -1818,6 +1929,21 @@
 CONFIG_FB_ATY
   This driver supports graphics boards with the ATI Mach64 chips.
 
+  This driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want).
+  The module will be called atyfb.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+ATI Rage128 display support (EXPERIMENTAL)
+CONFIG_FB_ATY128
+  This driver supports graphics boards with the ATI Rage128 chips.
+  Say Y if you have such a graphics board.
+
+  The driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want). The
+  module will be called aty128fb.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
 PowerMac "control" frame buffer device support
 CONFIG_FB_CONTROL
   This driver supports a frame buffer for the graphics adapter in the
@@ -1831,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
@@ -1840,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
@@ -3525,6 +3653,29 @@
   in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. The Bridging code is
   still in test. If unsure, say N.
 
+Frame Diverter (EXPERIMENTAL)
+CONFIG_NET_DIVERT
+  The Frame Diverter allows you to divert packets from the
+  network, that are not aimed at the interface receiving it (in 
+  promisc. mode). Typically, a Linux box setup as an ethernet bridge
+  with the Frames Diverter on, can do some *really* transparent www 
+  caching using a Squid proxy for example.
+
+  This is very useful when you don't want to change your router's
+  config (or if you simply don't have access to it).
+
+  The other possible usages of diverting Ethernet Frames are numberous:
+   - reroute smtp traffic to another interface
+   - traffic-shape certain network streams
+   - transparently proxy smtp connections
+   - etc...
+
+  For more informations, please refer to:
+    http://www.freshmeat.net/projects/etherdivert
+    http://perso.wanadoo.fr/magpie/EtherDivert.html
+
+  If unsure, say N
+
 Packet socket
 CONFIG_PACKET
   The Packet protocol is used by applications which communicate
@@ -4081,9 +4232,11 @@
   tagged command queuing and fast synchronous data transfers up to 80
   MB/s with wide FAST-40 LVD devices and controllers.
 
+  This driver does not support SYM53C1010 Ultra-160 PCI-SCSI chips.
+  Support for SYM53C1010 chips requires the "SYM53C8XX SCSI option" to
+  be configured. This option will configure a different driver.
   Recent versions of the 53C8XX chips are better supported by the
-  option "SYM53C8XX SCSI support", below. This option will configure 
-  a different driver.
+  option "SYM53C8XX SCSI support", below.
 
   If you want the kernel to select the recommended driver for each of 
   of your NCR/SYM53C8XX controllers you may just configure both the 
@@ -4102,7 +4255,8 @@
 CONFIG_SCSI_SYM53C8XX
   This driver supports all the features of recent 53C8XX chips (used
   in PCI SCSI controllers), notably the hardware phase mismatch
-  feature of the SYM53C896.
+  feature of the SYM53C896, SYM53C895A and SYM53C1010.
+  It also supports Ultra-160 SCSI data transfers for the SYM53C1010.
 
   Older versions of the 53C8XX chips are not supported by this
   driver. If your system uses either a 810 rev. < 16, a 815, or a 825
@@ -4124,8 +4278,8 @@
 
 synchronous data transfers frequency
 CONFIG_SCSI_NCR53C8XX_SYNC
-  The SCSI Parallel Interface-2 Standard defines 4 classes of transfer
-  rates: FAST-5, FAST-10, FAST-20 and FAST-40. The numbers are
+  The SCSI Parallel Interface-2 Standard defines 5 classes of transfer
+  rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80. The numbers are
   respectively the maximum data transfer rates in mega-transfers per
   second for each class. For example, a FAST-20 Wide 16 device is able
   to transfer data at 20 million 16 bit packets per second for a total
@@ -4133,9 +4287,9 @@
 
   You may specify 0 if you want to only use asynchronous data
   transfers. This is the safest and slowest option. Otherwise, specify
-  a value between 5 and 40, depending on the capability of your SCSI
+  a value between 5 and 80, depending on the capability of your SCSI
   controller. The higher the number, the faster the data transfer.
-  Note that 40 should normally be ok since the driver decreases the
+  Note that 80 should normally be ok since the driver decreases the
   value automatically according to the controller's capabilities.
 
   Your answer to this question is ignored for controllers with NVRAM,
@@ -4146,7 +4300,7 @@
   second).
 
   The normal answer therefore is not to go with the default but to
-  select the maximum value 40 allowing the driver to use the maximum
+  select the maximum value 80 allowing the driver to use the maximum
   value supported by each controller. If this causes problems with
   your SCSI devices, you should come back and decrease the value.
 
@@ -4197,7 +4351,9 @@
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS
   This option allows you to specify the maximum number of commands
   that can be queued to any device, when tagged command queuing is
-  possible. The default value is 32. Minimum is 2, maximum is 64.
+  possible. The default value is 32. Minimum is 2, maximum is 64 for
+  the generic NCR53C8XX driver and 255 for the SYM53C8XX driver.
+  
   Modern hard disks are able to support 64 tags and even more, but 
   do not seem to be faster when more than 32 tags are being used.
   
@@ -4449,6 +4605,23 @@
   The module will be called qlogicfc.o. If you want to compile it as
   a module, say M here and read Documentation/modules.txt.
 
+Compaq 64-bit / 66MHz PCI Fibre Channel Host Adapter
+CONFIG_SCSI_CPQFCTS
+  This driver supports Compaq HBA part #120186-B21, the non-
+  intelligent Tachyon TL/TS (HPFC-5166A/1.2) based adapter.
+  Kernel linked or kernel module, SMP support.  Information at
+  /proc/scsi/cpqfcTS/*
+  
+  Only supports FC-AL (non-loop mode not supported). Supports Fabric 
+  (FL_Port, public device); tested on Brocade switches.  Supports hot-
+  plug of new devices and movement of devices across ports (i.e.,
+  dynamic re-assignment of 24-bit FC port_id).
+
+  Tested on Compaq RA4x00 (f/w ver 2.40 or newer), RA8000, with Vixel
+  Rapport 1000 (7-port non-managed), Gadzoox 12-port, Gadzoox Capellix
+  3000, Brocade 2010, 2400, 2800.  Selective Storage 
+  Presentation (SSP) on RA4x00 f/w 2.54 (no redundancy).
+
 Seagate ST-02 and Future Domain TMC-8xx SCSI support
 CONFIG_SCSI_SEAGATE
   These are 8-bit SCSI controllers; the ST-01 is also supported by
@@ -5191,6 +5364,14 @@
   If you want to do that, say M here. The module will be called
   sealevel.o.
 
+SyncLink HDLC/SYNCPPP support
+CONFIG_SYNCLINK_SYNCPPP
+  Enables HDLC/SYNCPPP support for the SyncLink WAN driver.
+  Normally the SyncLink WAN driver works with the main PPP
+  driver (ppp.c) and pppd program. HDLC/SYNCPPP support allows use
+  of the Cisco HDLC/PPP driver (syncppp.c).
+  The SyncLink WAN driver (in character devices) must also be enabled.
+
 Frame Relay (DLCI) support
 CONFIG_DLCI
   This is support for the frame relay protocol; frame relay is a fast
@@ -5715,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
@@ -6929,6 +7124,35 @@
   This is support for the DIGITAL series of EISA (DEFEA) and PCI
   (DEFPA) controllers which can connect you to a local FDDI network.
 
+SysKonnect FDDI PCI support
+CONFIG_SKFP
+  Say Y here if you have a SysKonnect FDDI PCI adapter.
+  The following adapters are supported by this driver:
+  - SK-5521 (SK-NET FDDI-UP)
+  - SK-5522 (SK-NET FDDI-UP DAS)
+  - SK-5541 (SK-NET FDDI-FP)
+  - SK-5543 (SK-NET FDDI-LP)
+  - SK-5544 (SK-NET FDDI-LP DAS)
+  - SK-5821 (SK-NET FDDI-UP64)
+  - SK-5822 (SK-NET FDDI-UP64 DAS)
+  - SK-5841 (SK-NET FDDI-FP64)
+  - SK-5843 (SK-NET FDDI-LP64)
+  - SK-5844 (SK-NET FDDI-LP64 DAS)
+  - Netelligent 100 FDDI DAS Fibre SC
+  - Netelligent 100 FDDI SAS Fibre SC
+  - Netelligent 100 FDDI DAS UTP
+  - Netelligent 100 FDDI SAS UTP
+  - Netelligent 100 FDDI SAS Fibre MIC
+  Read Documentation/networking/skfp.txt for information about
+  the driver.
+  Questions concerning this driver can be addressed to:
+    linux@syskonnect.de
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. This is recommended.
+  The module will be called skfp.o.
+
 HIgh Performance Parallel Interface support (EXPERIMENTAL)
 CONFIG_HIPPI
   HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and
@@ -7260,6 +7484,647 @@
   only driver for hard disks.  These should be the first partition
   (eg. /dev/[sh]d?1) on each of your drives.  If unsure, say N.
 
+Support for USB
+CONFIG_USB
+  Universal Serial Bus (USB) is a specification for a serial bus
+  subsystem which offers higher speeds and more features than the
+  traditional PC serial port. The bus supplies power to peripherals
+  and allows for hot swapping. Up to 127 USB peripherals can be
+  connected to a single USB port in a tree structure. The USB port is
+  the root of the tree, the peripherals are the leaves and the inner
+  nodes are special USB devices called hubs. Many newer PC's have USB
+  ports and newer peripherals such as scanners, keyboards, mice,
+  modems, and printers support the USB protocol and can be connected
+  to the PC via those ports.
+
+  Say Y here if your computer has a USB port and you want to use USB
+  devices. You then need to say Y to at least one of "UHCI support" or
+  "OHCI support" below (the type of interface that the USB hardware in
+  your computer provides to the operating system) and then choose from
+  among the drivers for USB peripherals. You may want to check out the
+  information provided in Documentation/usb/ and especially the links
+  given in Documentation/usb/usb-help.txt.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usbcore.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB verbose debug messages
+CONFIG_USB_DEBUG
+  Say Y here if you want the USB core & hub drivers to produce a bunch
+  of debug messages to the system log. Select this if you are having a
+  problem with USB support and want to see more of what is going on.
+
+UHCI (intel PIIX4, VIA, ...) support?
+CONFIG_USB_UHCI
+  The Universal Host Controller Interface is a standard by Intel for
+  accessing the USB hardware in the PC (which is also called the USB
+  host controller). If your USB host controller conforms to this
+  standard, you may want to say Y, but see below. All recent boards
+  with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
+  i810, i820) conform to this standard. Also all VIA PCI chipsets
+  (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
+  133).
+
+  Currently there exist two drivers for UHCI host controllers: this
+  one and the so-called JE driver, which you can get from 
+  "UHCI alternate (JE) support", below. You need only one.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usb-uhci.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+UHCI (intel PIIX4, VIA, ...) alternate (JE) support?
+CONFIG_USB_UHCI_ALT
+  The Universal Host Controller Interface is a standard by Intel for
+  accessing the USB hardware in the PC (which is also called the USB
+  host controller). If your USB host controller conforms to this
+  standard, you may want to say Y, but see below. All recent boards
+  with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
+  i810, i820) conform to this standard. Also all VIA PCI chipsets
+  (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
+  133). If unsure, say Y.
+
+  Currently there exist two drivers for UHCI host controllers: this
+  so-called JE driver, and the one you get from "UHCI support", above.
+  You need only one.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called uhci.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+UHCI unlink optimizations (EXPERIMENTAL)
+CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
+  This option currently does nothing. You may say Y or N.
+
+OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support
+CONFIG_USB_OHCI
+  The Open Host Controller Interface is a standard by
+  Compaq/Microsoft/National for accessing the USB PC hardware (also
+  called USB host controller). If your USB host controller conforms to
+  this standard, say Y. The USB host controllers on most non-Intel
+  architectures and on several x86 compatibles with non-Intel chipsets
+  -- like SiS (aktual 610, 610 and so on) or ALi (ALi IV, ALi V,
+  Aladdin Pro..) -- conform to this standard.
+
+  You may want to read the file Documentation/usb/ohci.txt.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usb-ohci.o. If you want to compile it
+  as a module, say M here and read Documentation/modules.txt.
+
+USB Human Interface Device (HID) support
+CONFIG_USB_HID
+  Say Y here if you want to connect keyboards, mice, joysticks,
+  graphic tablets, or any other HID based devices to your
+  computer via USB. More information is available:
+  Documentation/usb/input.txt.
+
+  If unsure, say Y.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called hid.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB HIDBP Keyboard support
+CONFIG_USB_KBD
+  Say Y here if you don't want to use the generic HID driver for your
+  USB keyboard and prefer to use the keyboard in its limited Boot
+  Protocol mode. This driver is much smaller than the HID one.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usbkbd.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+  If unsure, say N.
+
+USB HIDBP Mouse support
+CONFIG_USB_MOUSE
+  Say Y here if you don't want to use the generic HID driver for your
+  USB mouse and prefer to use the mouse in its limited Boot Protocol
+  mode. This driver is much smaller than the HID one.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usbmouse.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+  If unsure, say N.
+
+Wacom Intuos/Graphire tablet support
+CONFIG_USB_WACOM
+  Say Y here if you want to use the USB version of the Wacom Intuos
+  or Graphire tablet. Make sure to say Y to "Mouse support"
+  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+  (CONFIG_INPUT_EVDEV) as well.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called wacom.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+Logitech WingMan Force joystick support
+CONFIG_USB_WMFORCE
+  Say Y here if you want to use the Logitech WingMan Force with Linux
+  on the USB port. No force-feedback support yet, but other than that
+  it should work like a normal joystick.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called wmforce.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+Keyboard support
+CONFIG_INPUT_KEYBDEV
+  Say Y here if you want your USB HID keyboard to be able to serve 
+  as a system keyboard.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called keybdev.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+Mouse support
+CONFIG_INPUT_MOUSEDEV
+  Say Y here if you want your USB HID mouse (or ADB mouse handled by
+  the input layer) to be accessible as char devices 13:32+ -
+  /dev/input/mouseX and 13:63 - /dev/input/mice as an emulated ImPS/2
+  mouse.  That way, all user space programs will be able to use your
+  mouse.
+  
+  If unsure, say Y.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called mousedev.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+Horizontal screen resolution
+CONFIG_INPUT_MOUSEDEV_SCREEN_X
+  If you're using a digitizer, or a graphic tablet, and want to use
+  it as a mouse then the mousedev driver needs to know the X window
+  screen resolution you are using to correctly scale the data. If
+  you're not using a digitizer, this value is ignored.
+
+Vertical screen resolution
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y
+  If you're using a digitizer, or a graphic tablet, and want to use
+  it as a mouse then the mousedev driver needs to know the X window
+  screen resolution you are using to correctly scale the data. If
+  you're not using a digitizer, this value is ignored.
+
+Joystick support
+CONFIG_INPUT_JOYDEV
+  Say Y here if you want your USB HID joystick or gamepad to be
+  accessible as char device 13:0+ - /dev/input/jsX device. 
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called joydev.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+Event interface support
+CONFIG_INPUT_EVDEV
+  Say Y here if you want your USB or ADB HID device events be accessible
+  under char device 13:64+ - /dev/input/eventX in a generic way.
+  This is the future ...
+
+USB Scanner support
+CONFIG_USB_SCANNER
+  Say Y here if you want to connect a USB scanner to your computer's
+  USB port. Please read Documentation/usb/scanner.txt and
+  Documentation/usb/scanner-hp-sane.txt for more information.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called scanner.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+USB Audio support
+CONFIG_USB_AUDIO
+  Say Y here if you want to connect UAB audio equipment such as
+  speakers to your computer's USB port.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called audio.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Modem (CDC ACM) support
+CONFIG_USB_ACM
+  This driver supports USB modems and ISDN adapters which support the
+  Communication Device Class Abstract Control Model interface.
+  Please read Documentation/usb/acm.txt for details.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called acm.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Serial converter support
+CONFIG_USB_SERIAL
+  Say Y here if you have a USB device that provides normal serial
+  ports, and you want to connect it to your USB bus. Supported devices
+  are the Tech WhiteHEAT multi-port USB to serial converter, and the
+  FTDI or Keyspan single port USB to serial converter Handspring
+  Visor. In addition to saying Y here, you need to say Y to the driver
+  for your specific hardware below. Some other devices may also be
+  used if you say Y to "USB Generic Serial Driver", below.
+
+  Please read Documentation/usb/usb-serial.txt for more information.
+  
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usbserial.o. If you want to compile it 
+  as a module, say M here and read Documentation/modules.txt.
+
+USB Generic Serial Driver
+CONFIG_USB_SERIAL_GENERIC
+  Say Y here if you want to use the generic USB serial driver. Please
+  read Documentation/usb/usb-serial.txt for more information on using
+  this driver. It is recommended that the "USB Serial converter
+  support" be compiled as a module for this driver to be used
+  properly.
+
+USB ConnectTech WhiteHEAT Serial Driver
+CONFIG_USB_SERIAL_WHITEHEAT
+  Say Y here if you want to use a ConnectTech WhiteHEAT 4 port
+  USB to serial converter device.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called whiteheat.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Handspring Visor Driver
+CONFIG_USB_SERIAL_VISOR
+  Say Y here if you want to connect to your HandSpring Visor through
+  its USB docking station. See http://usbvisor.sourceforge.net for
+  more information on using this driver.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called visor.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB FTDI Single Port Serial Driver
+CONFIG_USB_SERIAL_FTDI_SIO
+  Say Y here if you want to use a FTDI SIO single port USB to serial
+  converter device. The implementation I have is called the USC-1000.
+
+  See http://reality.sgi.com/bryder_wellington/ftdi_sio for more
+  information on this driver and the device.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ftdi_sio.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Keyspan PDA Single Port Serial Driver
+CONFIG_USB_SERIAL_KEYSPAN_PDA
+  Say Y here if you want to use a Keyspan PDA single port USB to
+  serial converter device.  This driver makes use of firmware
+  developed from scratch by Brian Warner.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called keyspan_pda.o. If you want to compile it 
+  as a module, say M here and read Documentation/modules.txt.
+
+USB Keyspan USA-xxx Serial Driver
+CONFIG_USB_SERIAL_KEYSPAN
+  Say Y here if you want to use Keyspan USB to serial converter
+  devices.  This driver makes use of Keyspan's official firmware
+  and was developed with their support.  You must also include
+  firmware to support your particular device(s). 
+
+  See http://www.linuxcare.com.au/hugh/keyspan.html for 
+  more information.
+ 
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called keyspan.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Keyspan USA-28 Firmware
+CONFIG_USB_SERIAL_KEYSPAN_USA28
+  Say Y here to include firmware for the USA-28 converter.
+
+USB Keyspan USA-28X Firmware
+CONFIG_USB_SERIAL_KEYSPAN_USA28X
+  Say Y here to include firmware for the USA-28X converter.
+
+USB Keyspan USA-19 Firmware
+CONFIG_USB_SERIAL_KEYSPAN_USA19
+  Say Y here to include firmware for the USA-19 converter.
+
+USB Keyspan USA-18X Firmware
+CONFIG_USB_SERIAL_KEYSPAN_USA18X
+  Say Y here to include firmware for the USA-18X converter.
+
+USB Keyspan USA-19W Firmware
+CONFIG_USB_SERIAL_KEYSPAN_USA19W
+  Say Y here to include firmware for the USA-19W converter.
+
+USB ZyXEL omni.net LCD Plus Driver
+CONFIG_USB_SERIAL_OMNINET
+  Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called omninet.o. If you want to compile it as a 
+  module, say M here and read Documentation/modules.txt.
+
+USB Digi International AccelePort USB Serial Driver
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT
+  Say Y here if you want to use Digi AccelePort USB 2 or 4 devices,
+  2 port (plus parallel port) and 4 port USB serial converters.  The
+  parallel port on the USB 2 appears as a third serial port on Linux.
+  The Digi Acceleport USB 8 is not yet supported by this driver.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called digi_acceleport.o. If you want to compile 
+  it as a module, say M here and read Documentation/modules.txt.
+
+USB Serial Converter verbose debug
+CONFIG_USB_SERIAL_DEBUG
+  Say Y here if you want verbose debug messages from the USB Serial
+  Converter. 
+
+USB Printer support
+CONFIG_USB_PRINTER
+  Say Y here if you want to connect a USB printer to your computer's
+  USB port.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called printer.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB IBM (Xirlink) C-It Camera support
+CONFIG_USB_IBMCAM
+  Say Y here if you want to connect a IBM "C-It" camera, also known as
+  "Xirlink PC Camera" to your computer's USB port. For more
+  information, read Documentation/usb/ibmcam.txt.
+
+  This driver uses the Video For Linux API.  You must enable
+  (Y or M in config) Video For Linux (under Character Devices)
+  to use this driver.  Information on this API and pointers to
+  "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ibmcam.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. This camera
+  has several configuration options which can be specified when you
+  load the module. Read Documentation/usb/ibmcam.txt to learn more.
+
+USB OV511 Camera support
+CONFIG_USB_OV511
+  Say Y here if you want to connect this type of camera to your
+  computer's USB port. See Documentation/usb/ov511.txt for more
+  information and for a list of supported cameras.
+  
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Character Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ov511.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB ADMtek Pegasus-based ethernet device support
+CONFIG_USB_PEGASUS
+  Say Y if you want to use your USB ethernet device. Supported
+  cards until now are:
+    ADMtek AN986 (eval. board)
+    Accton 10/100
+    Billington USB-100
+    Corega FEter USB-TX
+    MELCO/BUFFALO LUA-TX
+    D-Link DSB-650TX, DSB-650TX-PNA, DSB-650, DU-E10, DU-E100
+    Linksys USB100TX, USB10TX
+    LANEED Ethernet LD-USB/TX
+    SMC 202
+    SOHOware NUB Ethernet
+  If you have devices with vendor IDs other than noted above
+  you should add them in the driver code and send a message
+  to me (petkan@dce.bg) for update.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called pegasus.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Kodak DC-2xx Camera support
+CONFIG_USB_DC2XX
+  Say Y here if you want to connect this type of still camera to
+  your computer's USB port.  See Documentation/usb/dc2xx.txt for more
+  information; some non-Kodak cameras may also work with this
+  driver, given application support (such as www.gPhoto.org).
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called dc2xx.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Mustek MDC800 Digital Camera Support
+CONFIG_USB_MDC800
+  Say Y here if you want to connect this type of still camera to
+  your computer's USB port. This driver can be used with gphoto 0.4.3
+  and higher (look at http://www.gphoto.org ).
+  To use it create a device node with "mknod /dev/mustek c 180 32" and
+  configure it in your software.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called mdc800.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Mass Storage support
+CONFIG_USB_STORAGE
+  Say Y here if you want to connect USB mass storage devices to your
+  computer's USB port.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called usb-storage.o. If you want to compile it
+  as a module, say M here and read Documentation/modules.txt.
+
+USB Mass Storage verbose debug
+CONFIG_USB_STORAGE_DEBUG
+  Say Y here in order to have the USB Mass Storage code generate
+  verbose debugging messages.
+
+USS720 parport driver
+CONFIG_USB_USS720
+  This driver is for USB parallel port adapters that use the Lucent
+  Technologies USS-720 chip. These cables are plugged into your USB
+  port and provide USB compatibility to peripherals designed with
+  parallel port interfaces.
+  
+  The chip has two modes: automatic mode and manual mode. In automatic
+  mode, it looks to the computer like a standard USB printer. Only
+  printers may be connected to the USS-720 in this mode. The generic
+  USB printer driver ("USB Printer support", above) may be used in
+  that mode, and you can say N here if you want to use the chip only
+  in this mode.
+
+  Manual mode is not limited to printers, any parallel port
+  device should work. This driver utilizes manual mode.
+  Note however that some operations are three orders of magnitude
+  slower than on a PCI/ISA Parallel Port, so timing critical
+  applications might not work.
+
+  Say Y here if you own an USS-720 USB->Parport cable and intend to
+  connect anything other than a printer to it.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called uss720.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+  
+USB device file system
+CONFIG_USB_DEVICEFS
+  If you say Y here (and to "/proc file system support" below), you
+  will get a file /proc/usb/devices which lists the devices currently
+  connected to your USB busses, a file /proc/usb/drivers which lists
+  the USB kernel client drivers currently loaded, and for every
+  connected device a file named "/proc/usb/xxx/yyy", where xxx is the
+  bus number and yyy the device number; the latter files can be used
+  by user space programs to talk directly to the device. These files
+  are "virtual", meaning they are generated on the fly and not stored
+  on the hard drive.
+  
+  For the format of the /proc/usb/ files, please read
+  Documentation/usb/proc_usb_info.txt. 
+
+  Please note that this code is completely unrelated to devfs, the
+  "/dev file system support".
+
+  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
+  allocation and will prevent some device opens from succeeding
+  if they would cause USB bandwidth usage to go above 90% of
+  the bus bandwidth.
+
+  If you say N here, these conditions will cause warning messages
+  about USB bandwidth usage to be logged and some devices or
+  drivers may not work correctly.
+
+DABUSB driver
+CONFIG_USB_DABUSB
+  A Digital Audio Broadcasting (DAB) Receiver for USB and Linux
+  brought to you by the DAB-Team (http://dab.in.tum.de). This driver
+  can be taken as an example for URB-based bulk, control, and
+  isochronous transactions. URB's are explained in
+  Documentation/usb/URB.txt.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called dabusb.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+PLUSB driver
+CONFIG_USB_PLUSB
+  A driver for the Prolific PL-2302 USB-to-USB network device. This
+  'USB cable' connects two hosts via a point-to-point network with
+  bandwidth of 5 Mbit/s. Configure this driver after connecting the
+  USB cable via ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2 (and
+  vice versa on the other host).
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called plusb.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+USB Diamond Rio500 support
+CONFIG_USB_RIO500
+  Say Y here if you want to connect a USB Rio500 mp3 player to your
+  computer's USB port. Please read Documentation/usb/rio.txt
+  for more information.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called rio500.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+D-Link DSB-R100 FM radio support
+CONFIG_USB_DSBR
+  Say Y here if you want to connect this type of radio to your
+  computer's USB port. Note that the audio is not digital, and
+  you must connect the line out connector to a sound card or a
+  set of speakers.
+
+  This driver uses the Video For Linux API.  You must enable
+  (Y or M in config) Video For Linux (under Character Devices)
+  to use this driver.  Information on this API and pointers to
+  "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called dsbr100.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+
+Microtek USB scanner support
+CONFIG_USB_MICROTEK
+  Say Y here if you want support for the Microtek X6USB and possibly
+  some other scanners by that vendor. The scanner will appear as a
+  scsi generic device to the rest of the system.
+  A patched version of SANE is necessary to use the
+  scanner. It's available at 
+  http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html
+  This driver can be compiled as a module.
+
+USB Bluetooth support
+CONFIG_USB_BLUETOOTH
+  Say Y here if you want to connect a USB Bluetooth device to your
+  computer's USB port. You will need the Bluetooth stack (available
+  at http://developer.axis.com/software/index.shtml) to fully use
+  the device.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called bluetooth.o. If you want to compile it as 
+  a module, say M here and read Documentation/modules.txt.
+  
+Kawasaki USB ethernet controller support
+CONFIG_USB_KAWETH
+  Say Y here if you want support for devices based on the Kawasaki
+  LSI KL5KUSB100 USB to Ethernet 1-Chip Controller, such as the
+  NetGear EA101 USB to Ethernet Adapter.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called kaweth.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt.
+ 
 Minix fs support
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about OS's.
@@ -7535,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
@@ -7891,6 +8758,20 @@
   want), say M here and read Documentation/modules.txt. The module
   will be called smbfs.o. Most people say N, however.
 
+use nls by default
+CONFIG_SMB_NLS_DEFAULT
+  Enabling this will make smbfs use nls translations by default. You
+  need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls
+  settings and you need to give the default nls for the SMB server as
+  CONFIG_SMB_NLS_REMOTE.
+
+nls support setting
+CONFIG_SMB_NLS_REMOTE
+  This setting allows you to specify a default value for which
+  codepage the server uses. If this field is left blank no
+  translations will be done. The local codepage/charset default to
+  CONFIG_NLS_DEFAULT, you need to set that value in the NLS menu.
+
 Coda filesystem support
 CONFIG_CODA_FS
   Coda is an advanced network filesystem, similar to NFS in that it
@@ -9197,6 +10078,33 @@
   speed. If unsure, leave this disabled, i.e. leave it at 2000
   bits/sec.
 
+Direct Rendering Manager (XFree86 DRI support)
+CONFIG_DRM
+  Kernel-level support for the Direct Rendering Infrastructure (DRI)
+  introduced in XFree86 4.x. These modules provide support for
+  synchronization, security, and DMA transfers. Select the module that
+  provides support for your graphics card.
+
+3dfx Banshee/Voodoo3+
+CONFIG_DRM_TDFX
+  Choose M here if you have a 3dfx Banshee/Voodoo3 graphics card.
+
+3dlabs GMX 2000
+CONFIG_DRM_GAMMA
+  Choose M here if you have a 3dlabs GMX 2000 graphics card.
+
+ATI Rage 128
+CONFIG_DRM_R128
+  Choose M here if you have a ATI Rage 128 graphics card.
+
+Intel I810
+CONFIG_DRM_I810
+  Choose M here if you have an Intel I810 AGP graphics card.
+
+Matrox g200/g400
+CONFIG_DRM_MGA
+  Choose M here if you have a Matrox g200/g400 AGP graphics card.
+
 MTRR control and configuration
 CONFIG_MTRR
   On Intel P6 family processors (Pentium Pro, Pentium II and later)
@@ -9501,6 +10409,38 @@
  You can compile this driver directly into the kernel, or use
  it as a module.  The module will be called sbc60xxwdt.o.
 
+CONFIG_MICROCODE
+  /dev/cpu/microcode - Intel P6 CPU microcode support
+
+  If you say Y here you will be able to update the microcode on
+  Intel processors in the P6 family, e.g. Pentium Pro, Pentium II,
+  Pentium III, Xeon etc. You will obviously need the actual microcode
+  binary data itself which is not shipped with the Linux kernel.
+ 
+  For latest news and information on obtaining all the required
+  ingredients for this driver, check:
+  http://www.urbanmyth.org/microcode/
+ 
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called microcode.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+/dev/cpu/*/msr - Model-specific register support
+CONFIG_X86_MSR
+  This device gives privileged processes access to the x86
+  Model-Specific Registers (MSRs).  It is a character device with
+  major 202 and minors 0 to 31 for /dev/cpu/0/msr to /dev/cpu/31/msr.
+  MSR accesses are directed to a specific CPU on multi-processor
+  systems.
+
+/dev/cpu/*/cpuid - CPU information support
+CONFIG_X86_CPUID
+  This device gives processes access to the x86 CPUID instruction to
+  be executed on a specific processor.  It is a character device
+  with major 203 and minors 0 to 31 for /dev/cpu/0/cpuid to
+  /dev/cpu/31/cpuid.
+
 Enhanced Real Time Clock Support
 CONFIG_RTC
   If you say Y here and create a character special file /dev/rtc with
@@ -9519,6 +10459,74 @@
   sampling), then say Y here, and read Documentation/rtc.txt for
   details.
 
+AGP/GART support
+CONFIG_AGP
+  This provides a kernel interface (/dev/agpgart) for programming AGP
+  transfers on motherboards that support them. Primarily, this is used
+  for hardware-accelerated 3d graphics, though any other AGP device
+  could take advantage of it.
+
+  If you have a 3d-capable AGP video card say 'M' or 'Y' here.
+  Otherwise, say 'N'.
+
+  You will also have to indicate support for your specific chipset.
+  Consult the output of lspci, your motherboard manual, or the inside
+  of your computer if unsure what to choose. Multiple selections are ok.
+
+Intel 440LX/BX/GX support
+CONFIG_AGP_INTEL
+  This option give you AGP support for the GLX component of the
+  "soon to be released" XFree86-4 on Intel 440LX/BX/GX chipsets.
+
+  For the moment, most people should say no, unless you want to
+  test the GLX component which can be downloaded from
+       http://glx.on.openprojects.net/
+
+Intel I810/I810 DC100/I810e support
+CONFIG_AGP_I810
+  This option give you AGP support for the Xserver for the intel
+  810 chipset boards. This is required to do any useful video
+  modes.
+
+VIA VP3/MVP3/Apollo Pro support
+CONFIG_AGP_VIA
+  This option give you AGP support for the GLX component of the
+  "soon to be released" XFree86-4 on VIA MPV3/Apollo Pro chipsets.
+
+  For the moment, most people should say no, unless you want to
+  test the GLX component which can be downloaded from
+       http://glx.on.openprojects.net/
+
+AMD Irongate support
+CONFIG_AGP_AMD
+  This option give you AGP support for the GLX component of the
+  "soon to be released" XFree86-4 on Intel AMD Irongate chipset.
+
+  For the moment, most people should say no, unless you want to
+  test the GLX component which can be downloaded from
+       http://glx.on.openprojects.net/
+
+Generic SiS support
+CONFIG_AGP_SIS
+  This option give you AGP support for the GLX component of the
+  "soon to be released" XFree86-4 on Silicon Integrated Systems [SiS]
+  chipsets.
+
+  Note than 5591/5592 AGP chipsets are NOT supported.
+
+  For the moment, most people should say no, unless you want to
+  test the GLX component which can be downloaded from
+       http://glx.on.openprojects.net/
+
+ALI M1541 support
+CONFIG_AGP_ALI
+  This option give you AGP support for the GLX component of the
+  "soon to be released" XFree86-4 on ALI M1541 chipset.
+
+  For the moment, most people should say no, unless you want to
+  test the GLX component which can be downloaded from
+       http://glx.on.openprojects.net/
+
 Tadpole ANA H8 Support
 CONFIG_H8
   The Hitachi H8/337 is a microcontroller used to deal with the power
@@ -10275,7 +11283,7 @@
 Creative EMU10K1 based PCI sound cards
 CONFIG_SOUND_EMU10K1
   Say Y or M if you have a PCI sound card using the EMU10K1
-  chipset, such as the Creative SBLive! or SB PCI512.
+  chipset, such as the Creative SBLive!,  SB PCI512 or Emu-APS.
 
 Ensoniq ES1370 based PCI sound cards
 CONFIG_SOUND_ES1370
@@ -10960,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
@@ -11162,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.
@@ -11229,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
@@ -11344,6 +12417,11 @@
   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
   If you want to use the internal audio of your Atari or Amiga in
@@ -11588,6 +12666,23 @@
 CONFIG_RADIO_CADET_PORT
   Enter the I/O address of the card here (most commonly 330). 
 
+Maestro Radio
+CONFIG_RADIO_MAESTRO
+  Choose Y here if you have one of these Maestro sound cards
+  with FM radio included.
+
+  In order to control your radio card, you will need to use programs
+  that are compatible with the Video for Linux API. Information on
+  this API and pointers to "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
+  you need to have access to a machine on the Internet that has a
+  program like lynx or netscape.
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called radio-maestro.o
+
 SF16FMI Radio
 CONFIG_RADIO_SF16FMI
   Choose Y here if you have one of these FM radio cards, and then fill
@@ -12331,6 +13426,14 @@
    See "linux/Documentation/cpqarray.txt" for the current list of 
    boards supported by this driver, and for further information 
    on the use of this driver. 
+
+Compaq Smart Array support
+CONFIG_BLK_CPQ_CISS_DA
+   This is the driver for Compaq Smart Array controllers.
+   Everyone using these boards should say Y here.
+   See "linux/Documentation/cciss.txt" for the current list of
+   boards supported by this driver, and for further information
+   on the use of this driver.
 
 QuickNet Internet LineJack/PhoneJack support
 CONFIG_PHONE_IXJ
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/IO-mapping.txt linux.18p17/Documentation/IO-mapping.txt
--- linux.vanilla/Documentation/IO-mapping.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/IO-mapping.txt	Sun Oct 15 21:34:58 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/README.DAC960 linux.18p17/Documentation/README.DAC960
--- linux.vanilla/Documentation/README.DAC960	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/README.DAC960	Sun Sep 10 14:46:08 2000
@@ -1,11 +1,11 @@
    Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
 
-			Version 2.2.8 for Linux 2.2.16
-			Version 2.4.8 for Linux 2.4.0
+			Version 2.2.9 for Linux 2.2.17
+			Version 2.4.9 for Linux 2.4.0
 
 			      PRODUCTION RELEASE
 
-				19 August 2000
+			       7 September 2000
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -203,13 +203,13 @@
 
 			      DRIVER INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.2.16 or 2.4.0.
+This distribution was prepared for Linux kernel version 2.2.17 or 2.4.0.
 
 To install the DAC960 RAID driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf DAC960-2.2.8.tar.gz (or DAC960-2.4.8.tar.gz)
+  tar -xvzf DAC960-2.2.9.tar.gz (or DAC960-2.4.9.tar.gz)
   mv README.DAC960 linux/Documentation
   mv DAC960.[ch] linux/drivers/block
   patch -p0 < DAC960.patch (if DAC960.patch is included)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/SubmittingDrivers linux.18p17/Documentation/SubmittingDrivers
--- linux.vanilla/Documentation/SubmittingDrivers	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/SubmittingDrivers	Sun Oct 15 21:13:17 2000
@@ -0,0 +1,112 @@
+Submitting Drivers For The Linux Kernel
+---------------------------------------
+
+This document is intended to explain how to submit device drivers to the
+Linux 2.2 and 2.4test kernel trees. Note that if you are interested in video
+card drivers you should probably talk to XFree86 (http://wwww.xfree86.org) 
+instead.
+
+Allocating Device Numbers
+-------------------------
+
+Major and minor numbers for devices are allocated by the Linux assigned name
+and number authority (currently better known as H Peter Anvin). The
+site is http://www.lanana.org/. This also deals with allocating numbers for
+devices that are not going to be submitted to the mainstream kernel.
+
+If you don't use assigned numbers then when you device is submitted it will
+get given an assigned number even if that is different from values you may
+have shipped to customers before.
+
+Who To Submit Drivers To
+------------------------
+
+Linux 2.0:
+	No new drivers are accepted for this kernel tree
+
+Linux 2.2:
+	If the code area has a general maintainer then please submit it to
+	the maintainer listed in MAINTAINERS in the kernel file. If the
+	maintainer does not respond or you cannot find the appropriate
+	maintainer then please contact Alan Cox <alan@lxorguk.ukuu.org.uk>
+
+Linux 2.4test:
+	This kernel tree is under active development. The same rules apply
+	as 2.2 but you may wish to submit your driver via linux-kernel (see
+	resources) and follow that list to track changes in API's. These
+	should no longer be occuring as we are now in a code freeze.
+	The final contact point for Linux 2.4 submissions is 	
+	<torvalds@transmeta.com>.
+
+What Criteria Determine Acceptance
+----------------------------------
+
+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
+		to be accepted than if it invents gratuitous new ones. 
+		If you need to implement a common API over Linux and NT
+		drivers do it in userspace.
+
+Code:		Please use the Linux style of code formatting as documented
+		in Documentation/CodingStyle. If you have sections of code
+		that need to be in other formats, for example because they
+		are shared with a windows driver kit and you want to
+		maintain them just once seperate them out nicely and note
+		this fact.
+
+Portability:	Pointers are not always 32bits, people do not all have
+		floating point and you shouldn't use inline x86 assembler in 
+		your driver without careful thought. Pure x86 drivers
+		generally are not popular. If you only have x86 hardware it 
+		is hard to test portability but it is easy to make sure the
+		code can easily be made portable.
+
+Clarity:	It helps if anyone can see how to fix the driver. It helps
+		you because you get patches not bug reports. If you submit a
+		driver that intentionally obfuscates how the hardware works
+		it will go in the bitbucket.
+
+Control:	In general if there is active maintainance of a driver by
+		the author then patches will be redirected to them unless 
+		they are totally obvious and without need of checking.
+		If you want to be the contact and update point for the
+		driver it is a good idea to state this in the comments.
+
+What Criteria Do Not Determine Acceptance
+-----------------------------------------
+
+Vendor:		Being the hardware vendor and maintaining the driver is
+		often a good thing. If there is a stable working driver from
+		other people already in the tree don't expect 'we are the
+		vendor' to get your driver chosen. Ideally work with the 
+		existing driver author to build a single perfect driver.
+
+Author:		It doesn't matter if a large Linux company wrote the driver,
+		or you did. Nobody has any special access to the kernel
+		tree. Anyone who tells you otherwise isn't telling the
+		whole story.
+
+
+Resources
+---------
+
+Linux kernel master tree:
+	ftp.kernel.org:/pub/linux/kernel/...
+
+Linux kernel mailing list:		
+	linux-kernel@vger.kernel.org
+	[mail majordomo@vger.kernel.org to subscribe]
+
+Kernel traffic:
+	Weekly summary of kernel list activity (much easier to read)
+	[http://kt.linuxcare.com/kernel-traffic]
+
+Linux USB project:
+	http://sourceforge.net/projects/linux-usb/
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/arm/nwfpe/README linux.18p17/Documentation/arm/nwfpe/README
--- linux.vanilla/Documentation/arm/nwfpe/README	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/arm/nwfpe/README	Sun Oct 15 21:34:58 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/cciss.txt linux.18p17/Documentation/cciss.txt
--- linux.vanilla/Documentation/cciss.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/cciss.txt	Fri Sep  1 13:32:52 2000
@@ -0,0 +1,47 @@
+This driver is for Compaq's SMART Array Controllers.
+
+Supported Cards:
+----------------
+
+This driver is known to work with the following cards:
+
+	* SA 5300
+
+If notes are not already created in the /dev/cciss directory
+
+# mkdev.cciss [ctlrs]
+
+Where ctlrs is the number of controllers you have (defaults to 1 if not
+specified).
+
+Device Naming:
+--------------
+
+You need some entries in /dev for the cciss device.  The mkdev.cciss script
+can make device nodes for you automatically.  Currently the device setup
+is as follows:
+
+Major numbers:
+	104	cciss0	
+	105	cciss1	
+	106	cciss2 
+	etc...
+
+Minor numbers:
+        b7 b6 b5 b4 b3 b2 b1 b0
+        |----+----| |----+----|
+             |           |
+             |           +-------- Partition ID (0=wholedev, 1-15 partition)
+             |
+             +-------------------- Logical Volume number
+
+The suggested device naming scheme is:
+/dev/cciss/c0d0			Controller 0, disk 0, whole device
+/dev/cciss/c0d0p1		Controller 0, disk 0, partition 1
+/dev/cciss/c0d0p2		Controller 0, disk 0, partition 2
+/dev/cciss/c0d0p3		Controller 0, disk 0, partition 3
+
+/dev/cciss/c1d1			Controller 1, disk 1, whole device
+/dev/cciss/c1d1p1		Controller 1, disk 1, partition 1
+/dev/cciss/c1d1p2		Controller 1, disk 1, partition 2
+/dev/cciss/c1d1p3		Controller 1, disk 1, partition 3
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/cdrom/cdrom-standard.tex linux.18p17/Documentation/cdrom/cdrom-standard.tex
--- linux.vanilla/Documentation/cdrom/cdrom-standard.tex	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/cdrom/cdrom-standard.tex	Sun Oct 15 21:34:59 2000
@@ -24,7 +24,7 @@
 \title{A \linux\ \cdrom\ standard}
 \author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl}
 \\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}
-\\{\footnotesize updated by Jens Axboe {\tt(axboe@image.dk)}}}
+\\{\footnotesize updated by Jens Axboe {\tt(axboe@suse.de)}}}
 \date{12 March 1999}
 
 \maketitle
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/cdrom/ide-cd linux.18p17/Documentation/cdrom/ide-cd
--- linux.vanilla/Documentation/cdrom/ide-cd	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/cdrom/ide-cd	Sun Oct 15 21:34:59 2000
@@ -1,7 +1,7 @@
 IDE-CD driver documentation
 Originally by scott snyder  <snyder@fnald0.fnal.gov> (19 May 1996)
 Carrying on the torch is: Erik Andersen <andersee@debian.org>
-New maintainers (19 Oct 1998): Jens Axboe <axboe@image.dk>
+New maintainer (19 Oct 1998): Jens Axboe <axboe@suse.de>
 
 1. Introduction
 ---------------
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/computone.txt linux.18p17/Documentation/computone.txt
--- linux.vanilla/Documentation/computone.txt	Sun Jun 11 21:44:08 2000
+++ linux.18p17/Documentation/computone.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/cpqarray.txt linux.18p17/Documentation/cpqarray.txt
--- linux.vanilla/Documentation/cpqarray.txt	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/cpqarray.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/fb/vesafb.txt linux.18p17/Documentation/fb/vesafb.txt
--- linux.vanilla/Documentation/fb/vesafb.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/fb/vesafb.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/affs.txt linux.18p17/Documentation/filesystems/affs.txt
--- linux.vanilla/Documentation/filesystems/affs.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/filesystems/affs.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/coda.txt linux.18p17/Documentation/filesystems/coda.txt
--- linux.vanilla/Documentation/filesystems/coda.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/filesystems/coda.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/fat_cvf.txt linux.18p17/Documentation/filesystems/fat_cvf.txt
--- linux.vanilla/Documentation/filesystems/fat_cvf.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/filesystems/fat_cvf.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/ufs.txt linux.18p17/Documentation/filesystems/ufs.txt
--- linux.vanilla/Documentation/filesystems/ufs.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/filesystems/ufs.txt	Sun Oct 15 21:34:59 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/ioctl-number.txt linux.18p17/Documentation/ioctl-number.txt
--- linux.vanilla/Documentation/ioctl-number.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/ioctl-number.txt	Fri Sep 15 22:44:39 2000
@@ -85,6 +85,7 @@
 'W'	20-27	linux/octal-relay.h	in development
 'W'	28-2F	linux/iso16-relay.h	in development
 'Y'	all	linux/cyclades.h
+'Z'     all     linux/drivers/scsi/cpqfcTSioctl.h
 'a'	all	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
 'b'	00-FF	bit3 vme host bridge	in development:
 					<mailto:natalia@nikhefk.nikhef.nl>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/00-INDEX linux.18p17/Documentation/isdn/00-INDEX
--- linux.vanilla/Documentation/isdn/00-INDEX	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/00-INDEX	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/INTERFACE.fax linux.18p17/Documentation/isdn/INTERFACE.fax
--- linux.vanilla/Documentation/isdn/INTERFACE.fax	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/INTERFACE.fax	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README linux.18p17/Documentation/isdn/README
--- linux.vanilla/Documentation/isdn/README	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.HiSax linux.18p17/Documentation/isdn/README.HiSax
--- linux.vanilla/Documentation/isdn/README.HiSax	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.HiSax	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.act2000 linux.18p17/Documentation/isdn/README.act2000
--- linux.vanilla/Documentation/isdn/README.act2000	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.act2000	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,4 @@
-$Id: README.act2000,v 1.2 1998/04/29 19:49:06 he Exp $
+$Id: README.act2000,v 1.3 2000/08/06 09:22:51 armin Exp $
 
 This document describes the ACT2000 driver for the
 IBM Active 2000 ISDN card.
@@ -7,7 +7,7 @@
 Version. Currently, only the ISA-Bus version of the card is supported.
 However MCA and PCMCIA will follow soon.
 
-The ISA-Bus Version uses 8 IO-ports. The base port adress has to be set
+The ISA-Bus Version uses 8 IO-ports. The base port address has to be set
 manually using the DIP switches.
 
 Setting up the DIP switches for the IBM Active 2000 ISDN card:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.avmb1 linux.18p17/Documentation/isdn/README.avmb1
--- linux.vanilla/Documentation/isdn/README.avmb1	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.avmb1	Sat Sep 23 13:15:35 2000
@@ -175,7 +175,7 @@
 subscribe linux-avmb1
 in the body.
 
-German documentaion and several scripts can be found at
+German documentation and several scripts can be found at
 ftp://ftp.avm.de/cardware/b1/linux/
 
 Bugs 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.concap linux.18p17/Documentation/isdn/README.concap
--- linux.vanilla/Documentation/isdn/README.concap	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.concap	Sat Sep 23 13:15:35 2000
@@ -68,7 +68,7 @@
 Likewise, a similar encapsulation protocol will frequently be needed by
 several different interfaces of even different hardware type, e.g. the
 synchronous ppp implementation used by the isdn driver and the
-asyncronous ppp implementation used by the ppp driver have a lot of
+asynchronous ppp implementation used by the ppp driver have a lot of
 similar code in them. By cleanly separating the encapsulation protocol
 from the hardware specific interface stuff such code could be shared
 better in future.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.diversion linux.18p17/Documentation/isdn/README.diversion
--- linux.vanilla/Documentation/isdn/README.diversion	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.diversion	Sat Sep 23 13:15:35 2000
@@ -57,7 +57,7 @@
    compared to the mechanism of ipfwadm or ipchains. If a given rule matches
    the checking process is finished and the rule matching will be applied
    to the call.
-   The rules include primary and secondary service indentifiers, called 
+   The rules include primary and secondary service identifiers, called 
    number and subaddress, callers number and subaddress and whether the rule
    matches to all filtered calls or only those when all B-channel resources
    are exhausted.
@@ -82,7 +82,7 @@
    available in some countries (for example germany). Countries requiring the 
    keypad protocol for activating static diversions (like the netherlands) are
    not supported but may use the tty devices for this purpose.
-   The dynamic diversion servives may be used in all countries if the provider
+   The dynamic diversion services may be used in all countries if the provider
    enables the feature CF (call forwarding). This should work on both MSN- and
    point-to-point lines.
    To add and delete rules the additional divertctrl program is needed. This
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.icn linux.18p17/Documentation/isdn/README.icn
--- linux.vanilla/Documentation/isdn/README.icn	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.icn	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,4 @@
-$Id: README.icn,v 1.6 1998/04/29 19:49:08 he Exp $
+$Id: README.icn,v 1.7 2000/08/06 09:22:51 armin Exp $
 
 You can get the ICN-ISDN-card from:
 
@@ -139,7 +139,7 @@
   To load a 4B-card, the same command is used, except a second firmware
   file is appended to the commandline of icnctrl.
 
-  -> After dowloading firmware, the two LEDs at the back cover of the card
+  -> After downloading firmware, the two LEDs at the back cover of the card
      (ICN-4B: 4 LEDs) must be blinking intermittently now. If a connection
      is up, the corresponding led is lit continuously.
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/isdn/README.sc linux.18p17/Documentation/isdn/README.sc
--- linux.vanilla/Documentation/isdn/README.sc	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/isdn/README.sc	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/joystick-api.txt linux.18p17/Documentation/joystick-api.txt
--- linux.vanilla/Documentation/joystick-api.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/joystick-api.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/joystick-parport.txt linux.18p17/Documentation/joystick-parport.txt
--- linux.vanilla/Documentation/joystick-parport.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/joystick-parport.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/kbuild/commands.txt linux.18p17/Documentation/kbuild/commands.txt
--- linux.vanilla/Documentation/kbuild/commands.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/kbuild/commands.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/kbuild/config-language.txt linux.18p17/Documentation/kbuild/config-language.txt
--- linux.vanilla/Documentation/kbuild/config-language.txt	Sun Jun 11 21:44:08 2000
+++ linux.18p17/Documentation/kbuild/config-language.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/mkdev.cciss linux.18p17/Documentation/mkdev.cciss
--- linux.vanilla/Documentation/mkdev.cciss	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/mkdev.cciss	Fri Sep  1 13:32:52 2000
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Script to create device nodes for SMART array controllers
+# Usage:
+#	mkdev.cciss [num controllers] [num log volumes] [num partitions]
+#
+# With no arguments, the script assumes 1 controller, 16 logical volumes,
+# and 16 partitions/volume, which is adequate for most configurations.
+#
+# If you had 5 controllers and were planning on no more than 4 logical volumes
+# each, using a maximum of 8 partitions per volume, you could say:
+#
+# mkdev.cciss 5 4 8
+#
+# Of course, this has no real benefit over "mkdev.cciss 5" except that it
+# doesn't create so many device nodes in /dev/cciss.
+
+NR_CTLR=${1-1}
+NR_VOL=${2-16}
+NR_PART=${3-16}
+
+if [ ! -d /dev/cciss ]; then
+	mkdir -p /dev/cciss
+fi
+
+C=0; while [ $C -lt $NR_CTLR ]; do
+	MAJ=`expr $C + 104`
+	D=0; while [ $D -lt $NR_VOL ]; do
+		P=0; while [ $P -lt $NR_PART ]; do
+			MIN=`expr $D \* 16 + $P`
+			if [ $P -eq 0 ]; then
+				mknod /dev/cciss/c${C}d${D} b $MAJ $MIN
+			else
+				mknod /dev/cciss/c${C}d${D}p${P} b $MAJ $MIN
+			fi
+			P=`expr $P + 1`
+		done
+		D=`expr $D + 1`
+	done
+	C=`expr $C + 1`
+done
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/modules.txt linux.18p17/Documentation/modules.txt
--- linux.vanilla/Documentation/modules.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/modules.txt	Thu Aug 31 15:02:02 2000
@@ -124,7 +124,7 @@
 
 To use modprobe successfully, you generally place the following
 command in your /etc/rc.d/rc.S script.  (Read more about this in the
-"rc.hints" file in the module utilities package, "modules-x.y.z.tar.gz".)
+"rc.hints" file in the module utilities package, "modutils-x.y.z.tar.gz".)
 
 	/sbin/depmod -a
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/moxa-smartio linux.18p17/Documentation/moxa-smartio
--- linux.vanilla/Documentation/moxa-smartio	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/moxa-smartio	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/README.sb1000 linux.18p17/Documentation/networking/README.sb1000
--- linux.vanilla/Documentation/networking/README.sb1000	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/README.sb1000	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/README.xpeed linux.18p17/Documentation/networking/README.xpeed
--- linux.vanilla/Documentation/networking/README.xpeed	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/networking/README.xpeed	Sun Oct 15 21:57:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/comx.txt linux.18p17/Documentation/networking/comx.txt
--- linux.vanilla/Documentation/networking/comx.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/comx.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/dmfe.txt linux.18p17/Documentation/networking/dmfe.txt
--- linux.vanilla/Documentation/networking/dmfe.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/dmfe.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/olympic.txt linux.18p17/Documentation/networking/olympic.txt
--- linux.vanilla/Documentation/networking/olympic.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/olympic.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/shaper.txt linux.18p17/Documentation/networking/shaper.txt
--- linux.vanilla/Documentation/networking/shaper.txt	Sun Jun 11 21:44:08 2000
+++ linux.18p17/Documentation/networking/shaper.txt	Sun Oct 15 21:38:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/sis900.txt linux.18p17/Documentation/networking/sis900.txt
--- linux.vanilla/Documentation/networking/sis900.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/sis900.txt	Sun Oct 15 21:38:03 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/sk98lin.txt linux.18p17/Documentation/networking/sk98lin.txt
--- linux.vanilla/Documentation/networking/sk98lin.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/sk98lin.txt	Sun Oct 15 21:38:03 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/skfp.txt linux.18p17/Documentation/networking/skfp.txt
--- linux.vanilla/Documentation/networking/skfp.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/networking/skfp.txt	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,224 @@
+(C)Copyright 1998-2000 SysKonnect,
+===========================================================================
+
+skfp.txt created 17-Jul-2000
+
+Readme File for skfp.o v2.07
+
+
+This file contains
+(1) OVERVIEW
+(2) SUPPORTED ADAPTERS
+(3) GENERAL INFORMATION
+(4) INSTALLATION
+(5) INCLUSION OF THE ADAPTER IN SYSTEM START
+(6) TROUBLESHOOTING
+(7) FUNCTION OF THE ADAPTER LEDS
+(8) HISTORY
+
+===========================================================================
+
+
+
+(1) OVERVIEW
+============
+
+This README explains how to use the driver 'skfp' for Linux with your
+network adapter.
+
+Chapter 2: Contains a list of all network adapters that are supported by
+	   this driver.
+
+Chapter 3: Gives some general information.
+
+Chapter 4: Describes common problems and solutions.
+
+Chapter 5: Shows the changed functionality of the adapter LEDs.
+
+Chapter 6: History of development.
+
+***
+
+
+(2) SUPPORTED ADAPTERS
+======================
+
+The network driver 'skfp' supports the following network adapters:
+SysKonnect adapters:
+  - SK-5521 (SK-NET FDDI-UP)
+  - SK-5522 (SK-NET FDDI-UP DAS)
+  - SK-5541 (SK-NET FDDI-FP)
+  - SK-5543 (SK-NET FDDI-LP)
+  - SK-5544 (SK-NET FDDI-LP DAS)
+  - SK-5821 (SK-NET FDDI-UP64)
+  - SK-5822 (SK-NET FDDI-UP64 DAS)
+  - SK-5841 (SK-NET FDDI-FP64)
+  - SK-5843 (SK-NET FDDI-LP64)
+  - SK-5844 (SK-NET FDDI-LP64 DAS)
+Compaq adapters (not tested):
+  - Netelligent 100 FDDI DAS Fibre SC
+  - Netelligent 100 FDDI SAS Fibre SC
+  - Netelligent 100 FDDI DAS UTP
+  - Netelligent 100 FDDI SAS UTP
+  - Netelligent 100 FDDI SAS Fibre MIC
+***
+
+
+(3) GENERAL INFORMATION
+=======================
+
+From v2.01 on, the driver is integrated in the linux kernel sources.
+Therefor, the installation is the same as for any other adapter
+supported by the kernel.
+Refer to the manual of your distribution about the installation
+of network adapters.
+Makes my life much easier :-)
+***
+
+
+(4) TROUBLESHOOTING
+===================
+
+If you run into problems during installation, check those items:
+
+Problem:  The FDDI adapter can not be found by the driver.
+Reason:   Look in /proc/pci for the following entry:
+             'FDDI network controller: SysKonnect SK-FDDI-PCI ...'
+	  If this entry exists, then the FDDI adapter has been
+	  found by the system and should be able to be used.
+	  If this entry does not exist or if the file '/proc/pci'
+	  is not there, then you may have a hardware problem or PCI
+	  support may not be enabled in your kernel.
+	  The adapter can be checked using the diagnostic program
+	  which is available from the SysKonnect web site:
+	      www.syskonnect.de
+	  Some COMPAQ machines have a problem with PCI under
+	  Linux. This is described in the 'PCI howto' document
+	  (included in some distributions or available from the
+	  www, e.g. at 'www.linux.org') and no workaround is available.
+
+Problem:  You want to use your computer as a router between
+          multiple IP subnetworks (using multiple adapters), but
+	  you can not reach computers in other subnetworks.
+Reason:   Either the router's kernel is not configured for IP
+	  forwarding or there is a problem with the routing table
+	  and gateway configuration in at least one of the
+	  computers.
+
+If your problem is not listed here, please contact our
+technical support for help. 
+You can send email to:
+  linux@syskonnect.de
+When contacting our technical support,
+please ensure that the following information is available:
+- System Manufacturer and Model
+- Boards in your system
+- Distribution
+- Kernel version
+
+***
+
+
+(5) FUNCTION OF THE ADAPTER LEDS
+================================
+
+        The functionality of the LED's on the FDDI network adapters was
+        changed in SMT version v2.82. With this new SMT version, the yellow
+        LED works as a ring operational indicator. An active yellow LED
+        indicates that the ring is down. The green LED on the adapter now
+        works as a link indicator where an active GREEN LED indicates that
+        the respective port has a physical connection.
+
+        With versions of SMT prior to v2.82 a ring up was indicated if the
+        yellow LED was off while the green LED(s) showed the connection
+        status of the adapter. During a ring down the green LED was off and
+        the yellow LED was on.
+
+        All implementations indicate that a driver is not loaded if
+        all LEDs are off.
+
+***
+
+
+(6) HISTORY
+===========
+
+v2.07 (20000717) (In-Kernel version)
+    New features:
+	- in kernel 2.2.17 (hopefully !)
+
+v2.06 (20000511) (In-Kernel version)
+    New features:
+	- 64 bit support
+	- new pci dma interface
+	- in kernel 2.3.99
+
+v2.05 (20000217) (In-Kernel version)
+    New features:
+	- Changes for 2.3.45 kernel
+
+v2.04 (20000207) (Standalone version)
+    New features:
+	- Added rx/tx byte counter
+
+v2.03 (20000111) (Standalone version)
+    Problems fixed:
+	- Fixed printk statements from v2.02
+
+v2.02 (991215) (Standalone version)
+    Problems fixed:
+	- Removed unnecessary output
+	- Fixed path for "printver.sh" in makefile
+
+v2.01 (991122)
+    New features:
+	- Integration in Linux kernel sources
+	- Support for memory mapped I/O
+
+v2.00 (991112)
+    New features:
+	- Full source released under GPL
+
+v1.05 (991023)
+    Problems fixed:
+	- Compilation with kernel version 2.2.13 failed
+
+v1.04 (990427)
+    Changes:
+	- New SMT module included, changing LED functionality
+    Problems fixed:
+	- Synchronization on SMP machines was buggy
+
+v1.03 (990325)
+    Problems fixed:
+	- Interrupt routing on SMP machines could be incorrect
+
+v1.02 (990310)
+    New features:
+	- Support for kernel versions 2.2.x added
+	- Kernel patch instead of private duplicate of kernel functions
+
+v1.01 (980812)
+    Problems fixed:
+	Connection hangup with telnet
+	Slow telnet connection
+
+v1.00 beta 01 (980507)
+    New features:
+	None.
+    Problems fixed:
+	None.
+    Known limitations:
+        - tar archive instead of standard package format (rpm).
+	- FDDI statistic is empty.
+	- not tested with 2.1.xx kernels
+	- integration in kernel not tested
+	- not tested simultaneously with FDDI adapters from other vendors.
+	- only X86 processors supported.
+	- SBA (Synchronous Bandwidth Allocator) parameters can
+	  not be configured.
+	- does not work on some COMPAQ machines. See the PCI howto
+	  document for details about this problem.
+	- data corruption with kernel versions below 2.0.33.
+
+*** End of information file ***
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/tlan.txt linux.18p17/Documentation/networking/tlan.txt
--- linux.vanilla/Documentation/networking/tlan.txt	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/networking/tlan.txt	Sun Oct 15 21:38:03 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/wan-router.txt linux.18p17/Documentation/networking/wan-router.txt
--- linux.vanilla/Documentation/networking/wan-router.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/wan-router.txt	Sun Oct 15 21:38:03 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/wanpipe.txt linux.18p17/Documentation/networking/wanpipe.txt
--- linux.vanilla/Documentation/networking/wanpipe.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/wanpipe.txt	Sun Oct 15 21:38:03 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/networking/z8530drv.txt linux.18p17/Documentation/networking/z8530drv.txt
--- linux.vanilla/Documentation/networking/z8530drv.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/networking/z8530drv.txt	Thu Oct 19 01:28:39 2000
@@ -4,17 +4,14 @@
 Internet:
 =========
 
-1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils-3.0-1.tar.gz
+1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils_3.0-3.tar.gz
 
-2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils-3.0-1.tar.gz
-
-3. ftp://ftp.ucsd.edu/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz
-   If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz
+2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils_3.0-3.tar.gz
 
 Please note that the information in this document may be hopelessly outdated.
 A new version of the documentation, along with links to other important
 Linux Kernel AX.25 documentation and programs, is available on
-http://www.rat.de/jr
+http://yaina.de/jreuter
 
 -----------------------------------------------------------------------------
 
@@ -23,7 +20,7 @@
 
    ********************************************************************
 
-        (c) 1993,1998 by Joerg Reuter DL1BKE <jreuter@poboxes.com>
+        (c) 1993,2000 by Joerg Reuter DL1BKE <jreuter@yaina.de>
 
         portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -650,11 +647,10 @@
 Mysteriously this board seems not to work with the driver. Anyone
 got it up-and-running?
 
-
 Many thanks to Linus Torvalds and Alan Cox for including the driver
 in the Linux standard distribution and their support.
 
 Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@poboxes.com
-		WWW     : http://www.rat.de/jr/
+		AX-25   : DL1BKE @ DB0ABH.#BAY.DEU.EU
+		Internet: jreuter@yaina.de
+		WWW     : http://yaina.de/jreuter
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/nmi_watchdog.txt linux.18p17/Documentation/nmi_watchdog.txt
--- linux.vanilla/Documentation/nmi_watchdog.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/nmi_watchdog.txt	Tue Sep 12 11:51:04 2000
@@ -0,0 +1,34 @@
+
+Is your SMP system locking up unpredictably? No keyboard activity, just
+a frustrating complete hard lockup? Do you want to help us debugging
+such lockups? If all yes then this document is definitely for you.
+
+on Intel SMP hardware there is a feature that enables us to generate
+'watchdog NMI interrupts'. (NMI: Non Maskable Interrupt - these get
+executed even if the system is otherwise locked up hard) This can be
+used to debug hard kernel lockups. By executing periodic NMI interrupts,
+the kernel can monitor wether any CPU has locked up, and print out
+debugging messages if so.  You can enable/disable the NMI watchdog at boot
+time with the 'nmi_watchdog=1' boot parameter. Eg. the relevant
+lilo.conf entry:
+
+        append="nmi_watchdog=1"
+
+A 'lockup' is the following scenario: if any CPU in the system does not
+execute the period local timer interrupt for more than 5 seconds, then
+the NMI handler generates an oops and kills the process. This
+'controlled crash' (and the resulting kernel messages) can be used to
+debug the lockup. Thus whenever the lockup happens, wait 5 seconds and
+the oops will show up automatically. If the kernel produces no messages
+then the system has crashed so hard (eg. hardware-wise) that either it
+cannot even accept NMI interrupts, or the crash has made the kernel
+unable to print messages.
+
+You can find NMI watchdog patch against Linux 2.2.x at 
+http://people.redhat.com/mingo/NMI-watchdog-patches/
+
+
+[ feel free to send bug reports, suggestions and patches to
+  Ingo Molnar <mingo@redhat.com> or the Linux SMP mailing
+  list at <linux-smp@vger.kernel.org> ]
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/DASD linux.18p17/Documentation/s390/DASD
--- linux.vanilla/Documentation/s390/DASD	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/s390/DASD	Sun Oct 15 21:39:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/s390/cds.txt linux.18p17/Documentation/s390/cds.txt
--- linux.vanilla/Documentation/s390/cds.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/s390/cds.txt	Sun Oct 15 21:39:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/ESS linux.18p17/Documentation/sound/ESS
--- linux.vanilla/Documentation/sound/ESS	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/ESS	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/Introduction linux.18p17/Documentation/sound/Introduction
--- linux.vanilla/Documentation/sound/Introduction	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/Introduction	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/Maestro linux.18p17/Documentation/sound/Maestro
--- linux.vanilla/Documentation/sound/Maestro	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/Maestro	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/PSS linux.18p17/Documentation/sound/PSS
--- linux.vanilla/Documentation/sound/PSS	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/PSS	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/README.OSS linux.18p17/Documentation/sound/README.OSS
--- linux.vanilla/Documentation/sound/README.OSS	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/README.OSS	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/README.modules linux.18p17/Documentation/sound/README.modules
--- linux.vanilla/Documentation/sound/README.modules	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/sound/README.modules	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/stallion.txt linux.18p17/Documentation/stallion.txt
--- linux.vanilla/Documentation/stallion.txt	Fri Apr 21 12:45:49 2000
+++ linux.18p17/Documentation/stallion.txt	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sysctl/vm.txt linux.18p17/Documentation/sysctl/vm.txt
--- linux.vanilla/Documentation/sysctl/vm.txt	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Documentation/sysctl/vm.txt	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/CREDITS linux.18p17/Documentation/usb/CREDITS
--- linux.vanilla/Documentation/usb/CREDITS	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/CREDITS	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,169 @@
+Credits for the Simple Linux USB Driver:
+
+The following people have contributed to this code (in alphabetical
+order by last name).  I'm sure this list should be longer, its
+difficult to maintain, add yourself with a patch if desired.
+
+  Georg Acher <acher@informatik.tu-muenchen.de>
+  Alan Cox <alan@lxorguk.ukuu.org.uk>
+  Randy Dunlap <randy.dunlap@intel.com>
+  Johannes Erdfelt <jerdfelt@sventech.com>
+  Deti Fliegl <deti@fliegl.de>
+  ham <ham@unsuave.com>
+  Bradley M Keryan <keryan@andrew.cmu.edu>
+  Greg Kroah-Hartman <greg@kroah.com>
+  Pavel Machek <pavel@suse.cz>
+  Paul Mackerras <paulus@cs.anu.edu.au>
+  David E. Nelson <dnelson@jump.net>
+  Vojtech Pavlik <vojtech@suse.cz>
+  Bill Ryder <bryder@sgi.com>
+  Thomas Sailer <sailer@ife.ee.ethz.ch>
+  Gregory P. Smith <greg@electricrain.com>
+  Linus Torvalds <torvalds@transmeta.com>
+  Roman Weissgaerber <weissg@vienna.at>
+  <Kazuki.Yasumatsu@fujixerox.co.jp>
+
+Special thanks to:
+
+  Inaky Perez Gonzalez <inaky@peloncho.fis.ucm.es> for starting the
+  Linux USB driver effort and writing much of the larger uusbd driver.
+  Much has been learned from that effort.
+
+  The NetBSD & FreeBSD USB developers.  For being on the Linux USB list
+  and offering suggestions and sharing implementation experiences.
+
+Additional thanks to the following companies and people for donations
+of hardware, support, time and development (this is from the original
+THANKS file in Inaky's driver):
+
+        The following corporations have helped us in the development
+        of Linux USB / UUSBD:
+
+	- 3Com GmbH for donating a ISDN Pro TA and supporting me
+	  in technical questions and with test equipment. I'd never 
+	  expect such a great help.
+
+        - USAR Systems provided us with one of their excellent USB
+          Evaluation Kits. It allows us to test the Linux-USB driver
+          for compliance with the latest USB specification. USAR
+          Systems recognized the importance of an up-to-date open
+          Operating System and supports this project with
+          Hardware. Thanks!.
+
+        - Thanks to Intel Corporation for their precious help.
+
+        - We teamed up with Cherry to make Linux the first OS with
+          built-in USB support. Cherry is one of the biggest keyboard
+          makers in the world.
+
+        - CMD Technology, Inc. sponsored us kindly donating a CSA-6700
+          PCI-to-USB Controller Board to test the OHCI implementation.
+
+        - Due to their support to us, Keytronic can be sure that they
+          will sell keyboards to some of the 3 million (at least)
+          Linux users.
+
+        - Many thanks to ing büro h doran [http://www.ibhdoran.com]!
+          It was almost impossible to get a PC backplate USB connector
+          for the motherboard here at Europe (mine, home-made, was
+          quite lousy :). Now I know where to acquire nice USB stuff!
+
+        - Genius Germany donated a USB mouse to test the mouse boot
+          protocol. They've also donated a F-23 digital joystick and a
+          NetMouse Pro. Thanks! 
+
+        - AVM GmbH Berlin is supporting the development of the Linux
+          USB driver for the AVM ISDN Controller B1 USB. AVM is a
+          leading manufacturer for active and passive ISDN Controllers
+          and CAPI 2.0-based software. The active design of the AVM B1
+          is open for all OS platforms, including Linux.
+
+        - Thanks to Y-E Data, Inc. for donating their FlashBuster-U
+          USB Floppy Disk Drive, so we could test the bulk transfer
+          code.
+
+        - Many thanks to Logitech for contributing a three axis USB
+          mouse. 
+
+          Logitech designs, manufactures and markets
+          Human Interface Devices, having a long history and
+          experience in making devices such as keyboards, mice,
+          trackballs, cameras, loudspeakers and control devices for
+          gaming and professional use.
+
+          Being a recognized vendor and seller for all these devices,
+          they have donated USB mice, a joystick and a scanner, as a
+          way to acknowledge the importance of Linux and to allow
+          Logitech customers to enjoy support in their favorite
+          operating systems and all Linux users to use Logitech and
+          other USB hardware.
+
+          Logitech is official sponsor of the Linux Conference on
+          Feb. 11th 1999 in Vienna, where we'll will present the
+          current state of the Linux USB effort.
+
+        - CATC has provided means to uncover dark corners of the UHCI
+          inner workings with a USB Inspector.
+
+        - Thanks to Entrega for providing PCI to USB cards, hubs and
+          converter products for development. 
+
+	- Thanks to ConnectTech for providing a WhiteHEAT usb to
+	  serial converter, and the documentation for the device to
+	  allow a driver to be written.
+
+        And thanks go to (hey! in no particular order :)
+
+        - Oren Tirosh <orenti@hishome.net>, for standing so patiently
+          all my doubts'bout USB and giving lots of cool ideas.
+
+        - Jochen Karrer <karrer@wpfd25.physik.uni-wuerzburg.de>, for
+          pointing out mortal bugs and giving advice.
+
+        - Edmund Humemberger <ed@atnet.at>, for it's great work on
+          public relationships and general management stuff for the
+          Linux-USB effort.
+
+        - Alberto Menegazzi <flash@flash.iol.it> is starting the
+          documentation for the UUSBD. Go for it!
+
+        - Ric Klaren <ia_ric@cs.utwente.nl> for doing nice
+          introductory documents (competing with Alberto's :).
+
+        - Christian Groessler <cpg@aladdin.de>, for it's help on those
+          itchy bits ... :)
+
+        - Paul MacKerras for polishing OHCI and pushing me harder for
+          the iMac support, giving improvements and enhancements.
+
+        - Fernando Herrera <fherrera@eurielec.etsit.upm.es> has taken
+          charge of composing, maintaining and feeding the
+          long-awaited, unique and marvelous UUSBD FAQ! Tadaaaa!!!
+
+        - Rasca Gmelch <thron@gmx.de> has revived the raw driver and
+          pointed bugs, as well as started the uusbd-utils package.
+
+        - Peter Dettori <dettori@ozy.dec.com> is uncovering bugs like
+          crazy, as well as making cool suggestions, great :)
+
+        - All the Free Software and Linux community, the FSF & the GNU
+          project, the MIT X consortium, the TeX people ... everyone!
+          You know who you are!
+
+        - Big thanks to Richard Stallman for creating Emacs!
+
+        - The people at the linux-usb mailing list, for reading so
+          many messages :) Ok, no more kidding; for all your advises!
+
+        - All the people at the USB Implementors Forum for their
+          help and assistance.
+
+        - Nathan Myers <ncm@cantrip.org>, for his advice! (hope you
+          liked Cibeles' party).
+
+        - Linus Torvalds, for starting, developing and managing Linux.
+
+        - Mike Smith, Craig Keithley, Thierry Giron and Janet Schank
+          for convincing me USB Standard hubs are not that standard
+          and that's good to allow for vendor specific quirks on the
+          standard hub driver.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/URB.txt linux.18p17/Documentation/usb/URB.txt
--- linux.vanilla/Documentation/usb/URB.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/URB.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,196 @@
+1. Specification of the API
+
+1.1. Basic concept or 'What is an URB?'
+
+The basic idea of the new driver is message passing, the message itself is 
+called USB Request Block, or URB for short. 
+
+- An URB consists of all relevant information to execute any USB transaction 
+and deliver the data and status back. 
+
+- Execution of an URB is an inherently asynchronous operation, i.e. the 
+submit_urb(urb) call returns immediately after it has successfully queued 
+the requested action. 
+
+- Ongoing transfers for one URB (e.g. ISO) can simply be canceled with
+unlink_urb(urb) at any time. 
+
+- Each URB has a completion handler, which is called after the action
+has been successfully completed or canceled (INT transfers behave a bit
+different, see below). The URB also contains a context-pointer for free 
+usage and information passing to the completion handler.
+
+- URBs can be linked. After completing one URB, the next one can be
+automatically submitted. This is especially useful for ISO transfers:
+You only have read/write the data from/to the buffers in the completion 
+handler, the continuous streaming itself is transparently done by the 
+URB-machinery.
+
+1.2. The URB structure
+
+typedef struct urb
+{
+// ignore, for host controller/URB machine internal use
+	void *hcpriv;                   // private data for host controller
+	struct list_head urb_list;      // list pointer to all active urbs 
+
+// This is used for urb linking
+	struct urb* next;               // pointer to next URB  
+	struct usb_device *dev;         // pointer to associated USB device
+
+// pipe is assembled by the various well known pipe-macros in usb.h
+	unsigned int pipe;              // pipe information
+
+// status after each completion
+	int status;                     // returned status
+	unsigned int transfer_flags;    // ASAP, SP_OK, EARLY_COMPLETE
+
+// for data stage (CTRL), BULK, INT and ISO
+	void *transfer_buffer;          // associated data buffer
+
+// expected length
+	int transfer_buffer_length;     // data buffer length
+	int actual_length;              // actual data buffer length    
+
+// setup stage for CTRL (always 8 bytes!)
+	unsigned char* setup_packet;    // setup packet (control only)
+	
+// with ASAP, start_frame is set to the determined frame
+	int start_frame;                // start frame (iso/irq)
+	int number_of_packets;          // # of packets (iso/int)
+	int interval;                   // polling interval (irq only)
+	int error_count;                // number of errors (iso only)
+	//
+	void *context;                  // context for completion routine
+	usb_complete_t complete;        // pointer to completion routine
+	//
+// specification of the requested data offsets and length for ISO
+	iso_packet_descriptor_t  iso_frame_desc[0];
+} urb_t, *purb_t;
+
+1.3. How to get an URB?
+
+URBs are allocated with the following call
+
+	purb_t alloc_urb(int isoframes)
+
+Return value is a pointer to the allocated URB, 0 if allocation failed.
+The parameter isoframes specifies the number of isochronous transfer frames
+you want to schedule. For CTRL/BULK/INT, use 0.
+
+To free an URB, use
+
+	void free_urb(purb_t purb)
+
+This call also may free internal (host controller specific) memory in the
+future.
+
+1.4. What has to be filled in?
+
+Depending on the type of transaction, there are some macros 
+(FILL_CONTROL_URB, FILL_BULK_URB, and FILL_INT_URB, defined in uhci.h)
+that simplify the URB creation. In general, all macros need the usb
+device pointer, the pipe (usual format), the transfer buffer, the 
+desired transfer length, the completion  handler, and its context. 
+Take a look at the uhci_control_msg-function that convert the old API 
+into an URB.
+
+Flags:
+For ISO there are two startup behaviors: Specified start_frame or ASAP.
+For ASAP set USB_ISO_ASAP in transfer_flags.
+
+If short packets should NOT be tolerated, set USB_DISABLE_SPD in 
+transfer_flags.
+
+Usually, (to reduce restart time) the completion handler is called
+AFTER the URB re-submission. You can get the other way by setting
+USB_URB_EARLY_COMPLETE in transfer_flags. This is implicit for
+INT transfers.
+
+1.5. How to submit an URB?
+
+Just call
+
+	int submit_urb(purb_t purb)
+
+It immediately returns, either with status 0 (request queued) or some
+error code, usually caused by the following:
+
+- Out of memory (-ENOMEM)
+- Wrong pipe handle (-ENXIO)
+- Unplugged device (-ENODEV)
+- Stalled endpoint (-EPIPE)
+- Too many queued ISO transfers (-EAGAIN)
+- Too many requested ISO frames (-EFBIG)
+- Invalid INT interval (-EINVAL)
+- More than one packet for INT (-EINVAL)
+
+After submission, urb->status is USB_ST_URB_PENDING.
+
+For isochronous endpoints, subsequent submitting of URBs to the same endpoint
+with the ASAP flag result in a seamless ISO streaming. Exception: The 
+execution cannot be scheduled later than 900 frames from the 'now'-time. 
+The same applies to INT transfers, but here the seamless continuation is 
+independent of the transfer flags (implicitly ASAP).
+
+1.6. How to cancel an already running URB?
+
+Call
+	int unlink_urb(purb_t purb)
+
+It removes the urb from the internal list and frees all allocated
+HW descriptors. The status is changed to USB_ST_URB_KILLED. After 
+unlink_urb() returns, you can safely free the URB with free_urb(urb)
+and all other possibly associated data (urb->context etc.)
+
+1.7. What about the completion handler?
+
+The completion handler is optional, but useful for fast data processing
+or wakeup of a sleeping process (as shown in the compatibility wrapper's 
+completion handler).
+
+The handler is of the following type:
+
+	typedef void (*usb_complete_t)(struct urb *);
+
+i.e. it gets just the URB that caused the completion call.
+In the completion handler, you should have a look at urb->status to
+detect any USB errors. Since the context parameter is included in the URB,
+you can pass information to the completion handler. 
+
+
+1.8. How to do isochronous (ISO) transfers?
+
+For ISO transfers you have to append the iso_packet_descriptor_t structure 
+to the URB for each frame you want to schedule. When using alloc_urb(n)
+(recommended), the isoframe-parameter n can be used to allocate the
+structures for n frames.
+
+For each entry you have to specify the data offset for this frame (base is
+transfer_buffer), and the length you want to write/expect to read.
+After completion, actual_length contains the actual transfered length and 
+status contains the resulting USB-status for the ISO transfer for this frame.
+It is allowed to specify a varying length from frame to frame (e.g. for
+audio synchronisation/adaptive transfer rates). You can also use the length 
+0 to omit one or more frames (striping).
+
+As can be concluded from above, the UHCI-driver does not care for continuous
+data in case of short packet ISO reads! There's no fixup_isoc() like in the 
+old driver. There may be a common routine to do this in the future, but this 
+has nothing to do with the UHCI-driver!
+
+For scheduling you can choose your own start frame or ASAP. As written above,
+queuing more than one ISO frame with ASAP to the same device&endpoint result 
+in seamless ISO streaming. For continuous streaming you have to use URB
+linking. 
+
+1.9. How to start interrupt (INT) transfers?
+
+INT transfers are currently implemented with 8 different queues for intervals 
+for 1, 2, 4,... 128ms. Only one TD is allocated for each interrupt. After
+calling the completion handler, the TD is recycled.
+With the submission of one URB, the interrupt is scheduled until it is
+canceled by unlink_urb.
+
+The submit_urb()-call modifies urb->interval to the rounded value.
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/acm.txt linux.18p17/Documentation/usb/acm.txt
--- linux.vanilla/Documentation/usb/acm.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/acm.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,138 @@
+			  Linux ACM driver v0.16
+		 (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
+			     Sponsored by SuSE
+----------------------------------------------------------------------------
+
+0. Disclaimer
+~~~~~~~~~~~~~
+  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
+
+  Should you need to contact me, the author, you can do so either by e-mail
+- mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
+Ucitelska 1576, Prague 8, 182 00 Czech Republic
+
+  For your convenience, the GNU General Public License version 2 is included
+in the package: See the file COPYING.
+
+1. Usage
+~~~~~~~~
+  The drivers/usb/acm.c drivers works with USB modems and USB ISDN terminal
+adapters that conform to the Universal Serial Bus Communication Device Class
+Abstract Control Model (USB CDC ACM) specification.
+
+  Many modems do, here is a list of those I know of:
+
+	3Com OfficeConnect 56k
+	3Com Voice FaxModem Pro
+	3Com Sportster
+	MultiTech MultiModem 56k
+	Zoom 2986L FaxModem
+	Compaq 56k FaxModem
+	ELSA Microlink 56k
+
+  I know of one ISDN TA that does work with the acm driver:
+
+	3Com USR ISDN Pro TA
+
+  Unfortunately many modems and most ISDN TAs use proprietary interfaces and
+thus won't work with this drivers. Check for ACM compliance before buying.
+
+  The driver (with devfs) creates these devices in /dev/usb/acm:
+
+	crw-r--r--   1 root     root     166,   0 Apr  1 10:49 0
+	crw-r--r--   1 root     root     166,   1 Apr  1 10:49 1
+	crw-r--r--   1 root     root     166,   2 Apr  1 10:49 2
+
+  And so on, up to 31, with the limit being possible to change in acm.c to up
+to 256, so you can use up to 256 USB modems with one computer (you'll need
+three USB cards for that, though).
+
+  If you don't use devfs, then you can create device nodes with the same
+minor/major numbers anywhere you want, but either the above location or
+/dev/usb/ttyACM0 is preferred.
+
+  To use the modems you need these modules loaded:
+
+	usbcore.o
+	usb-[uo]hci.o or uhci.o
+	acm.o
+
+  After that, the modem[s] should be accessible. You should be able to use
+minicom, ppp and mgetty with them.
+
+2. Verifying that it works
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The first step would be to check /proc/bus/usb/devices, it should look
+like this:
+
+T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
+B:  Alloc=  0/900 us ( 0%), #Int=  0, #Iso=  0
+D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=0000 ProdID=0000 Rev= 0.00
+S:  Product=USB UHCI Root Hub
+S:  SerialNumber=6800
+C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
+E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms
+T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
+D:  Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs=  2
+P:  Vendor=04c1 ProdID=008f Rev= 2.07
+S:  Manufacturer=3Com Inc.
+S:  Product=3Com U.S. Robotics Pro ISDN TA
+S:  SerialNumber=UFT53A49BVT7
+C:  #Ifs= 1 Cfg#= 1 Atr=60 MxPwr=  0mA
+I:  If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
+E:  Ad=85(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+E:  Ad=04(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+E:  Ad=81(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr=  0mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+E:  Ad=81(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+I:  If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+E:  Ad=85(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+E:  Ad=04(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+
+The presence of these three lines (and the Cls= 'comm' and 'data' classes)
+is important, it means it's an ACM device. The Driver=acm means the acm
+driver is used for the device. If you see only Cls=ff(vend.) then you're out
+of luck, you have a device with vendor specific-interface.
+
+D:  Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs=  2
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+I:  If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+
+In the system log you should see:
+
+usb.c: USB new device connect, assigned device number 2
+usb.c: kmalloc IF c7691fa0, numif 1
+usb.c: kmalloc IF c7b5f3e0, numif 2
+usb.c: skipped 4 class/vendor specific interface descriptors
+usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
+usb.c: USB device number 2 default language ID 0x409
+Manufacturer: 3Com Inc.
+Product: 3Com U.S. Robotics Pro ISDN TA
+SerialNumber: UFT53A49BVT7
+acm.c: probing config 1
+acm.c: probing config 2
+ttyACM0: USB ACM device
+acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
+acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7
+usb.c: acm driver claimed interface c7b5f3e0
+usb.c: acm driver claimed interface c7b5f3f8
+usb.c: acm driver claimed interface c7691fa0
+
+If all this seems to be OK, fire up minicom and set it to talk to the ttyACM
+device and try typing 'at'. If it responds with 'OK', then everything is
+working.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/bluetooth.txt linux.18p17/Documentation/usb/bluetooth.txt
--- linux.vanilla/Documentation/usb/bluetooth.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/bluetooth.txt	Sat Oct 14 00:31:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/dc2xx.txt linux.18p17/Documentation/usb/dc2xx.txt
--- linux.vanilla/Documentation/usb/dc2xx.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/dc2xx.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,111 @@
+14 April 2000
+david-b@pacbell.net
+
+This is an overview of how to use the "dc2xx" USB driver with certain
+digital still cameras from Kodak and other vendors.
+
+
+CAMERAS
+
+This driver will mostly be used with Kodak DC-2xx series digital still
+cameras, but it should be trivial to tell it about several non-Kodak
+USB-enabled cameras.
+
+You'll most likely want to hook it up to recent versions of "gPhoto"
+(www.gphoto.org), since version 0.4 and later know how to use it to talk
+to Kodak DC-240 and DC-280 cameras over USB.
+
+In addition the DC-220, DC-260, DC-265, and DC-290 are also recognized.
+However, like other cameras using the "Digita OS" (from www.flashpoint.com)
+there is no gPhoto support for this camera.  There is a python script
+for accessing these cameras (see archives of the linux-usb mailing list)
+and a "Digita Services" library that can also use this driver.
+
+The HP PhotoSmart C500 should also work, since it's another Digita camera
+with USB support.
+
+
+USB HARDWARE
+
+Recent kernels have had no particular problems using this driver with
+either OHCI or UHCI chipsets, and have worked on the PowerMac platform.
+
+Note that in some cases changes in BIOS settings may be needed before
+your USB works.  At least one user has reported a need for SMP-related 
+settings as well, and some old hardware may not handle USB correctly.
+
+
+SETUP
+
+Configure in the DC2XX USB driver, and have it in your kernel.  It works
+as a module, or compiled in directly.
+
+Create at least one device, perhaps like this (both read and write):
+
+    # mknod -m 0660 /dev/usb/dc2xx0 c 180 80
+    # mknod -m 0660 /dev/usb/dc2xx1 c 180 81
+    ...
+
+NOTE:  you would normally configure PAM so that the user logged in at
+the console is granted ownership of these devices.  console.perms(5)
+explains how to do this.
+
+The driver supports multiple device nodes.  The USB framework supports
+a maximum of sixteen device nodes (up to minor device number 96).
+
+When you plug in one camera, it will use the first device node (dc2xx0
+in the example above).  A second camera will use the second device node,
+and so on.
+
+
+SANITY TESTING
+
+First:  if you've got /proc support, make sure that the driver has hooked
+itself up correctly.
+
+    - You should see an entry in /proc/bus/usb/drivers for "dc2xx",
+      if you enabled USB /proc support and correctly mounted the
+      usbdevfs on /proc/bus/usb.
+
+Second:  when you connect your camera to the computer, does it get recognized
+by the driver?  (Make sure the camera is powered on!)
+
+    - if you've got /proc/bus/usb/devices, you should see an entry
+      something like this.  The "ProdID" may be different if you didn't
+      plug in a DC-240, as may the strings presented, but "Driver=dc2xx"
+      had better be there.
+
+	T:  Lev=01 Prnt=00 Port=00 Cnt=01 Dev#=  1 Spd=12  MxCh= 0
+	D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+	P:  Vendor=040a ProdID=0120 Rev= 1.08
+	S:  Manufacturer=Eastman Kodak Company
+	S:  Product=KODAK DC240 Zoom Digital Camera
+	C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA
+	I:  If#= 0 Alt= 0 #EPs= 2 Cls=00(>ifc ) Sub=00 Prot=00 Driver=dc2xx
+	E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+	E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
+
+    - see if "dmesg" output tells you that you plugged in your camera.
+
+	Manufacturer: Eastman Kodak Company
+	Product: KODAK DC240 Zoom Digital Camera
+	dc2xx.c: USB Camera #0 connected
+
+Third:  (optional) can you use gPhoto to talk to the camera?
+
+    - When you configure your camera, tell it to use "/dev/usb/dc2xx0"
+      (or whatever name you used).  Right now, gPhoto emits a diagnostic
+      message (non-GUI) saying that it since it didn't act like a TTY,
+      it's assuming it's got a USB connection.
+
+    - With the camera turned on, get the "camera summary".  It'll
+      talk to the camera -- and tell you you're using USB.
+
+If you got that far, you should be able to use everything fine.
+
+
+ADDITIONAL INFORMATION
+
+You may find that you need more driver-specific information, which is
+currently accessible through a link from http://www.linux-usb.org/
+along with other Linux USB resources.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/error-codes.txt linux.18p17/Documentation/usb/error-codes.txt
--- linux.vanilla/Documentation/usb/error-codes.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/error-codes.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,108 @@
+$Id: README.error-codes,v 1.1 1999/12/14 14:03:02 fliegl Exp $
+
+This is the documentation of (hopefully) all possible error codes (and
+their interpretation) that can be returned from the hostcontroller driver 
+and from usbcore.
+
+NOTE:
+The USB_ST_* codes are deferred and are only listed for compatibility, new
+software should use only -E* instead!
+
+
+
+**************************************************************************
+*                   Error codes returned by usb_submit_urb               *
+**************************************************************************
+
+Non-USB-specific:
+
+USB_ST_NOERROR
+0		URB submission went fine
+
+-ENOMEM		no memory for allocation of internal structures	
+
+USB-specific:
+
+-ENODEV		specified USB-device or bus doesn't exist
+
+-ENXIO		specified endpoint doesn't exist on the device
+
+USB_ST_URB_INVALID_ERROR
+-EINVAL		a) Invalid transfer type specified (or not supported)
+		b) Invalid interrupt interval (0<=n<256)
+		c) more than one interrupt packet requested
+
+-EAGAIN		a) specified ISO start frame too early
+		b) (using ISO-ASAP) too much scheduled for the future
+		   wait some time and try again.
+
+-EFBIG		too much ISO frames requested (currently uhci>900)
+
+-EPIPE		specified pipe-handle is already stalled
+
+-EMSGSIZE	endpoint message size is zero, do interface/alternate setting
+
+
+**************************************************************************
+*                   Error codes returned by in urb->status               *
+*                   or in iso_frame_desc[n].status (for ISO)             *
+**************************************************************************
+
+USB_ST_NOERROR
+0			Transfer completed successfully
+
+USB_ST_URB_KILLED
+-ENOENT			URB was canceled by unlink_urb
+
+USB_ST_URB_PENDING
+-EINPROGRESS		URB still pending, no results yet
+			(actually no error until now;-)
+
+USB_ST_BITSTUFF
+USB_ST_INTERNALERROR
+-EPROTO			a) bitstuff error
+			b) unknown USB error 
+
+USB_ST_CRC
+-EILSEQ			CRC mismatch
+
+-EPIPE			a) babble detect
+			b) endpoint stalled
+
+USB_ST_BUFFERUNDERRUN
+-ENOST			buffer error
+
+USB_ST_NORESPONSE
+USB_ST_TIMEOUT
+-ETIMEDOUT		transfer timed out, NAK
+
+USB_ST_REMOVED	
+-ENODEV			device was removed
+
+USB_ST_SHORT_PACKET
+-EREMOTEIO		short packet detected
+
+USB_ST_PARTIAL_ERROR
+-EXDEV			ISO transfer only partially completed
+			look at individual frame status for details
+
+USB_ST_URB_INVALID_ERROR
+-EINVAL			ISO madness, if this happens: Log off and go home
+
+**************************************************************************
+*              Error codes returned by usbcore-functions                 *
+*           (expect also other submit and transfer status codes)         *
+**************************************************************************
+
+usb_register():
+USB_ST_NOTSUPPORTED
+-EINVAL			error during registering new driver
+
+usb_terminate_bulk():
+USB_ST_REMOVED
+-ENODEV			urb already removed
+
+usb_get_*/usb_set_*():
+			All USB errors (submit/status) can occur
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/ibmcam.txt linux.18p17/Documentation/usb/ibmcam.txt
--- linux.vanilla/Documentation/usb/ibmcam.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/ibmcam.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,288 @@
+README for Linux device driver for the IBM "C-It" USB video camera
+
+INTRODUCTION:
+
+This driver does not use all features known to exist in
+the IBM camera. However most of needed features work well.
+
+This driver was developed using logs of observed USB traffic
+which was produced by standard Windows driver (c-it98.sys).
+I did not have data sheets from Xirlink.
+
+Video formats:
+      128x96  [model 1]
+      176x144
+      320x240 [model 2]
+      352x240 [model 2]
+      352x288
+Frame rate: 3 - 30 frames per second (FPS)
+External interface: USB
+Internal interface: Video For Linux (V4L)
+Supported controls:
+- by V4L: Contrast,  Brightness, Color, Hue
+- by driver options: frame rate, lighting conditions, video format,
+                     default picture settings, sharpness.
+
+SUPPORTED CAMERAS:
+
+IBM "C-It" camera, also known as "Xirlink PC Camera"
+The device uses proprietary ASIC (and compression method);
+it is manufactured by Xirlink. See http://www.xirlink.com/
+http://www.ibmpccamera.com or http://www.c-itnow.com/ for
+details and pictures.
+
+The Linux driver was developed with camera with following
+model number (or FCC ID): KSX-XVP510. This camera has three
+interfaces, each with one endpoint (control, iso, iso). This
+type of cameras is referred to as "model 1". These cameras are
+no longer manufactured.
+
+Xirlink now manufactures new cameras which are somewhat different.
+In particular, following models [FCC ID] belong to that category:
+
+XVP300 [KSX-X9903]
+XVP600 [KSX-X9902]
+XVP610 [KSX-X9902]
+
+(see http://www.xirlink.com/ibmpccamera/ for updates, they refer
+to these new cameras by Windows driver dated 12-27-99, v3005 BETA)
+These cameras have two interfaces, one endpoint in each (iso, bulk).
+Such type of cameras is referred to as "model 2". They are supported
+(with exception of 352x288 native mode).
+
+Quirks of Model 2 cameras:
+-------------------------
+
+Model 2 does not have hardware contrast control. Corresponding V4L
+control is not used at the moment. It may be possible to implement
+contrast control in software, at cost of extra processor cycles.
+
+This driver provides 352x288 mode by switching the camera into
+quasi-352x288 RGB mode (800 Kbits per frame) essentially limiting
+this mode to 10 frames per second or less, in ideal conditions on
+the bus (USB is shared, after all). The frame rate
+has to be programmed very conservatively. Additional concern is that
+frame rate depends on brightness setting; therefore the picture can
+be good at one brightness and broken at another! I did not want to fix
+the frame rate at slowest setting, but I had to move it pretty much down
+the scale (so that framerate option barely matters). I also noticed that
+camera after first powering up produces frames slightly faster than during
+consecutive uses. All this means that if you use videosize=2 (which is
+default), be warned - you may encounter broken picture on first connect;
+try to adjust brightness - brighter image is slower, so USB will be able
+to send all data. However if you regularly use Model 2 cameras you may
+prefer videosize=1 which makes perfectly good I420, with no scaling and
+lesser demands on USB (300 Kbits per second, or 26 frames per second).
+
+The camera that I had also has a hardware quirk: if disconnected,
+it needs few minutes to "relax" before it can be plugged in again
+(poorly designed USB processor reset circuit?)
+
+Model 2 camera can be programmed for very high sensitivity (even starlight
+may be enough), this makes it convenient for tinkering with. The driver
+code has enough comments to help a programmer to tweak the camera
+as s/he feels necessary.
+
+WHAT YOU NEED:
+
+- A supported IBM PC (C-it) camera (model 1 or 2)
+
+- A Linux box with USB support (2.3/2.4; 2.2 w/backport may work)
+
+- A Video4Linux compatible frame grabber program such as xawtv.
+  
+HOW TO COMPILE THE DRIVER:
+
+You need to compile the driver only if you are a developer
+or if you want to make changes to the code. Most distributions
+precompile all modules, so you can go directly to the next
+section "HOW TO USE THE DRIVER".
+
+The driver consists of two files in usb/ directory:
+ibmcam.c and ibmcam.h These files are included into the
+Linux kernel build process if you configure the kernel
+for CONFIG_USB_IBMCAM. Run "make xconfig" and in USB section
+you will find the IBM camera driver. Select it, save the
+configuration and recompile.
+
+HOW TO USE THE DRIVER:
+
+I recommend to compile driver as a module. This gives you an
+easier access to its configuration. The camera has many more
+settings than V4L can operate, so some settings are done using
+module options.
+
+Typically module is installed with command 'modprobe', like this:
+
+# modprobe ibmcam framerate=1
+
+Alternatively you can use 'insmod' in similar fashion:
+
+# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1
+
+Module can be inserted with camera connected or disconnected.
+
+The driver can have options, though some defaults are provided.
+
+Driver options: (* indicates that option is model-dependent)
+
+Name            Type            Range [default] Example
+--------------  --------------  --------------  ------------------
+debug           Integer         0-9 [0]         debug=1
+flags           Integer         0-0xFF [0]      flags=0x0d
+framerate       Integer         0-6 [2]         framerate=1
+hue_correction  Integer         0-255 [128]     hue_correction=115
+init_brightness Integer         0-255 [128]     init_brightness=100
+init_contrast   Integer         0-255 [192]     init_contrast=200
+init_color      Integer         0-255 [128]     init_color=130
+init_hue        Integer         0-255 [128]     init_hue=115
+lighting        Integer         0-2* [1]        lighting=2
+sharpness       Integer         0-6* [4]        sharpness=3
+videosize       Integer         0-2* [2]        videosize=1
+
+Options for Model 2 only:
+
+Name            Type            Range [default] Example
+--------------  --------------  --------------  ------------------
+init_model2_rg  Integer         0..255 [0x70]   init_model2_rg=128
+init_model2_rg2 Integer         0..255 [0x2f]   init_model2_rg2=50
+init_model2_sat Integer         0..255 [0x34]   init_model2_sat=65
+init_model2_yb  Integer         0..255 [0xa0]   init_model2_yb=200
+
+debug           You don't need this option unless you are a developer.
+                If you are a developer then you will see in the code
+                what values do what. 0=off.
+
+flags           This is a bit mask, and you can combine any number of
+                bits to produce what you want. Usually you don't want
+                any of extra features this option provides:
+
+                FLAGS_RETRY_VIDIOCSYNC  1  This bit allows to retry failed
+                                           VIDIOCSYNC ioctls without failing.
+                                           Will work with xawtv, will not
+                                           with xrealproducer. Default is
+                                           not set.
+                FLAGS_MONOCHROME        2  Activates monochrome (b/w) mode.
+                FLAGS_DISPLAY_HINTS     4  Shows colored pixels which have
+                                           magic meaning to developers.
+                FLAGS_OVERLAY_STATS     8  Shows tiny numbers on screen,
+                                           useful only for debugging.
+                FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers.
+                FLAGS_SEPARATE_FRAMES   32 Shows each frame separately, as
+                                           it was received from the camera.
+                                           Default (not set) is to mix the
+                                           preceding frame in to compensate
+                                           for occasional loss of Isoc data
+                                           on high frame rates.
+                FLAGS_CLEAN_FRAMES      64 Forces "cleanup" of each frame
+                                           prior to use; relevant only if
+                                           FLAGS_SEPARATE_FRAMES is set.
+                                           Default is not to clean frames,
+                                           this is a little faster but may
+                                           produce flicker if frame rate is
+                                           too high and Isoc data gets lost.
+
+framerate       This setting controls frame rate of the camera. This is
+                an approximate setting (in terms of "worst" ... "best")
+                because camera changes frame rate depending on amount
+                of light available. Setting 0 is slowest, 6 is fastest.
+                Beware - fast settings are very demanding and may not
+                work well with all video sizes. Be conservative.
+
+hue_correction  This highly optional setting allows to adjust the
+                hue of the image in a way slightly different from
+                what usual "hue" control does. Both controls affect
+                YUV colorspace: regular "hue" control adjusts only
+                U component, and this "hue_correction" option similarly
+                adjusts only V component. However usually it is enough
+                to tweak only U or V to compensate for colored light or
+                color temperature; this option simply allows more
+                complicated correction when and if it is necessary.
+
+init_brightness These settings specify _initial_ values which will be
+init_contrast   used to set up the camera. If your V4L application has
+init_color      its own controls to adjust the picture then these
+init_hue        controls will be used too. These options allow you to
+                preconfigure the camera when it gets connected, before
+                any V4L application connects to it. Good for webcams.
+
+init_model2_rg  These initial settings alter color balance of the
+init_model2_rg2 camera on hardware level. All four settings may be used
+init_model2_sat to tune the camera to specific lighting conditions. These
+init_model2_yb  settings only apply to Model 2 cameras.
+
+lighting        This option selects one of three hardware-defined
+                photosensitivity settings of the camera. 0=bright light,
+                1=Medium (default), 2=Low light. This setting affects
+                frame rate: the dimmer the lighting the lower the frame
+                rate (because longer exposition time is needed). The
+                Model 2 cameras allow values more than 2 for this option,
+                thus enabling extremely high sensitivity at cost of frame
+                rate, color saturation and imaging sensor noise.
+
+sharpness       This option controls smoothing (noise reduction)
+                made by camera. Setting 0 is most smooth, setting 6
+                is most sharp. Be aware that CMOS sensor used in the
+                camera is pretty noisy, so if you choose 6 you will
+                be greeted with "snowy" image. Default is 4. Model 2
+                cameras do not support this feature.
+
+videosize       This setting chooses one if three image sizes that are
+                supported by this driver. Camera supports more, but
+                it's difficult to reverse-engineer all formats.
+                Following video sizes are supported:
+
+                videosize=0     128x96  (Model 1 only)
+                videosize=1     176x144
+                videosize=2     352x288
+                videosize=3     320x240 (Model 2 only)
+                videosize=4     352x240 (Model 2 only)
+
+                The last one (352x288) is the native size of the sensor
+                array, so it's the best resolution camera (Model 1) can
+                yield. The best resolution of Model 2 is 176x144, and
+                larger images are produced by stretching the bitmap.
+                Choose the image size you need. The smaller image can
+                support faster frame rate. Default is 352x288.
+
+WHAT NEEDS TO BE DONE:
+
+- The box freezes if camera is unplugged after being used (OHCI).
+  Workaround: remove usb-ohci module first.
+- On occasion camera (model 1) does not start properly (xawtv reports
+  errors), or camera produces negative image (funny colors.)
+  Workaround: reload the driver module. Reason: [1].
+- The button on the camera is not used. I don't know how to get to it.
+  I know now how to read button on Model 2, but what to do with it?
+
+[1]
+- Camera reports its status back to the driver; however I don't know
+  what returned data means. If camera fails at some initialization
+  stage then something should be done, and I don't do that because
+  I don't even know that some command failed. This is mostly Model 1
+  concern because Model 2 uses different commands which do not return
+  status (and seem to complete successfully every time).
+
+VIDEO SIZE AND IMAGE SIZE
+
+Camera produces picture X by Y pixels. This is camera-specific and can be
+altered by programming the camera accordingly. This image is placed onto
+larger (or equal) area W by H, this is V4L image. At this time the driver
+uses V4L image size (W by H) 352x288 pixels because many programs (such
+as xawtv) expect quite specific sizes and don't want to deal with arbitrary,
+camera-specific sizes. However this approach "hides" real image size, and
+application always sees the camera as producing only 352x288 image. It is
+possible to change the V4L image size to 128x96, and then if camera is
+switched to 128x96 mode then xawtv will correctly accept this image size. But
+many other popular sizes (such as 176x144) will not be welcomed. This is the
+reason why all camera images are at this time placed onto 352x288 "canvas",
+and size of that canvas (V4L) is reported to applications. It will be easy
+to add options to control the canvas size, but it will be application-
+specific because not all applications are ready to work with variety of
+camera-specific sizes.
+
+CREDITS:
+
+The code is based in no small part on the CPiA driver by Johannes Erdfelt,
+Randy Dunlap, and others. Big thanks to them for their pioneering work on that
+and the USB stack.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/input.txt linux.18p17/Documentation/usb/input.txt
--- linux.vanilla/Documentation/usb/input.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/input.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,306 @@
+			  Linux Input drivers v1.0
+	       (c) 1999-2000 Vojtech Pavlik <vojtech@suse.cz>
+			     Sponsored by SuSE
+	    $Id: input.txt,v 1.4 2000/05/28 17:57:22 vojtech Exp $
+----------------------------------------------------------------------------
+
+0. Disclaimer
+~~~~~~~~~~~~~
+  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
+
+  Should you need to contact me, the author, you can do so either by e-mail
+- mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
+Ucitelska 1576, Prague 8, 182 00 Czech Republic
+
+  For your convenience, the GNU General Public License version 2 is included
+in the package: See the file COPYING.
+
+1. Introduction
+~~~~~~~~~~~~~~~
+  This is a collection of drivers that is designed to support all input
+devices under Linux. However, in the current kernels, although it's
+possibilities are much bigger, it's limited to USB devices only. This is
+also why it resides in the drivers/usb subdirectory.
+
+  The center of the input drivers is the input.o module, which must be
+loaded before any other of the input modules - it serves as a way of
+communication between two groups of modules:
+
+1.1 Device drivers
+~~~~~~~~~~~~~~~~~~
+  These modules talk to the hardware (for example via USB), and provide
+events (keystrokes, mouse movements) to the input.o module.
+
+1.2 Event handlers
+~~~~~~~~~~~~~~~~~~
+  These modules get events from input.o and pass them where needed via
+various interfaces - keystrokes to the kernel, mouse movements via a
+simulated PS/2 interface to GPM and X and so on.
+
+2. Simple Usage
+~~~~~~~~~~~~~~~
+  For the most usual configuration, with one USB mouse and one USB keyboard,
+you'll have to load the following modules (or have them built in to the
+kernel):
+
+	input.o
+	mousedev.o
+	keybdev.o
+	usbcore.o
+	usb-[uo]hci.o
+	hid.o
+
+  After this, the USB keyboard will work straight away, and the USB mouse
+will be available as a character device on major 13, minor 63:
+
+	crw-r--r--   1 root     root      13,  63 Mar 28 22:45 mice
+
+  This device, has to be created, unless you use devfs, in which case it's
+created automatically. The commands to do that are:
+
+	cd /dev
+	mkdir input
+	mknod input/mice c 13 63
+
+  After that you have to point GPM (the textmode mouse cut&paste tool) and
+XFree to this device to use it - GPM should be called like:
+
+	gpm -t ps2 -m /dev/input/mice
+
+  And in X:
+
+	Section "Pointer"
+	    Protocol    "ImPS/2"
+	    Device      "/dev/input/mice"
+	    ZAxisMapping 4 5
+	EndSection
+
+  When you do all of the above, you can use your USB mouse and keyboard.
+
+3. Detailed Description
+~~~~~~~~~~~~~~~~~~~~~~~
+3.1 Device drivers
+~~~~~~~~~~~~~~~~~~
+  Device drivers are the modules that generate events. The events are
+however not useful without being handled, so you also will need to use some
+of the modules from section 3.2.
+
+3.1.1 hid.c
+~~~~~~~~~~~
+  Hid.c is the largest and most complex driver of the whole suite. It
+handles all HID devices, and because there is a very wide variety of them,
+and because the USB HID specification isn't simple, it needs to be this big.
+
+  Currently, it handles USB mice, joysticks, gamepads, steering wheels
+keyboards, trackballs and digitizers.
+
+ However, USB uses HID also for monitor controls, speaker controls, UPSs,
+LCDs and many other purposes.
+
+ The monitor and speaker controls should be easy to add to the hid/input
+interface, but for the UPSs and LCDs it doesn't make much sense. The driver
+doesn't support these yet, and a new, non-event interface should be designed
+for them. If you have any of these devices (I don't), feel free to design
+something and if it's good, it'll get into the driver.
+
+  The usage of the hid.o module is very simple, it takes no parameters,
+detects everything automatically and when a HID device is inserted, it
+detects it appropriately.
+
+  However, because the devices vary wildly, you might happen to have a
+device that doesn't work well. In that case #define DEBUG at the beginning
+of hid.c and send me the syslog traces.
+
+3.1.2 usbmouse.c
+~~~~~~~~~~~~~~~~
+  For embedded systems, for mice with broken HID descriptors and just any
+other use when the big hid.c wouldn't be a good choice, there is the
+usbmouse.c driver. It handles USB mice only. It uses a simpler HIDBP
+protocol. This also means the mice must support this simpler protocol. Not
+all do. If you don't have any strong reason to use this module, use hid.c
+instead.
+
+3.1.3 usbkbd.c
+~~~~~~~~~~~~~~
+  Much like usbmouse.c, this module talks to keyboards with a simplified
+HIDBP protocol. It's smaller, but doesn't support any extra special keys.
+Use hid.c instead if there isn't any special reason to use this.
+
+3.1.4 wacom.c
+~~~~~~~~~~~~~
+  This is a driver for Wacom Graphire and Intuos tablets. Not for Wacom
+PenPartner, that one is handled by the HID driver. Although the Intuos and
+Graphire tablets claim that they are HID tablets as well, they are not and
+thus need this specific driver.
+
+3.1.5 wmforce.c
+~~~~~~~~~~~~~~~
+  A driver for the Logitech WingMan Force joysticks, when connected via the
+USB port. It works quite well, but there is no force feedback support yet,
+because the interface to do that is a trade secret of Immersion Corp, and
+even Logitech can't disclose it.
+
+  Support for Logitech WingMan Force Wheel isn't present in this driver, but
+if someone has the device, and is willing to cooperate, it should be a
+matter of a couple days to add it.
+
+3.2 Event handlers
+~~~~~~~~~~~~~~~~~~
+  Event handlers distribute the events from the devices to userland and
+kernel, as needed.
+
+3.2.1 keybdev.c
+~~~~~~~~~~~~~~~
+  Keybdev is currently a rather ugly hack that translates the input events
+into architecture-specific keyboard raw mode (Xlated AT Set2 on x86), and
+passes them into the handle_scancode function of the keyboard.c module. This
+works well enough on all architectures that keybdev can generate rawmode on,
+other architectures can be added to it.
+
+  The right way would be to pass the events to keyboard.c directly, best if
+keyboard.c would itself be an event handler. This is done in the input
+patch, available on the webpage mentioned below.
+
+3.2.2 mousedev.c
+~~~~~~~~~~~~~~~~
+  Mousedev is also a hack to make programs that use mouse input work. It
+takes events from either mice or digitizers/tablets and makes a PS/2-style
+(a la /dev/psaux) mouse device available to the userland. Ideally, the
+programs could use a more reasonable interface, for example evdev.c
+
+  Mousedev devices in /dev/input (as shown above) are:
+
+	crw-r--r--   1 root     root      13,  32 Mar 28 22:45 mouse0
+	crw-r--r--   1 root     root      13,  33 Mar 29 00:41 mouse1
+	crw-r--r--   1 root     root      13,  34 Mar 29 00:41 mouse2
+	crw-r--r--   1 root     root      13,  35 Apr  1 10:50 mouse3
+	...
+	...
+	crw-r--r--   1 root     root      13,  62 Apr  1 10:50 mouse30
+	crw-r--r--   1 root     root      13,  63 Apr  1 10:50 mice
+
+Each 'mouse' device is assigned to a single mouse or digitizer, except the last
+one - 'mice'. This single character device is shared by all mice and
+digitizers, and even if none are connected, the device is present.  This is
+useful for hotplugging USB mice, so that programs can open the device even when
+no mice are present.
+
+  CONFIG_INPUT_MOUSEDEV_SCREEN_[XY] in the kernel configuration are the size
+of your screen (in pixels) in XFree86. This is needed if you want to use
+your digitizer in X, because it's movement is sent to X via a virtual PS/2
+mouse and thus needs to be scaled accordingly. These values won't be used if
+you use a mouse only.
+
+  Mousedev will generate either PS/2, ImPS/2 (Microsoft IntelliMouse) or
+GenPS/2 (Genius NetMouse/NetScroll) protocols, depending on what the program
+reading the data wishes. You can set GPM and X to any of these. You'll need
+ImPS/2 if you want to make use of a wheel on a USB mouse and GenPS/2 if you
+want to use extra (up to 5) buttons. I'm not sure how much is GenPS/2 supported
+in X, though.
+
+3.2.3 joydev.c
+~~~~~~~~~~~~~~
+  Joydev implements v0.x and v1.x Linux joystick api, much like
+drivers/char/joystick/joystick.c. See joystick-api.txt in the Documentation
+subdirectory for details. Joydev does it on top of the input subsystem,
+though. As soon as any USB joystick is connected, it can be accessed in
+/dev/input on:
+
+	crw-r--r--   1 root     root      13,   0 Apr  1 10:50 js0
+	crw-r--r--   1 root     root      13,   1 Apr  1 10:50 js1
+	crw-r--r--   1 root     root      13,   2 Apr  1 10:50 js2
+	crw-r--r--   1 root     root      13,   3 Apr  1 10:50 js3
+	...
+
+And so on up to js31.
+
+3.2.4 evdev.c
+~~~~~~~~~~~~~
+  Evdev is the generic input event interface. It passes the events generated
+in the kernel straight to the program, with timestamps. The API is still
+evolving, but should be usable now. It's described in section 5.
+
+  This should be the way for GPM and X to get keyboard and mouse mouse
+events. It allows for multihead in X without any specific multihead kernel
+support. The event codes are the same on all architectures and are hardware
+independent.
+
+  The devices are in /dev/input:
+
+	crw-r--r--   1 root     root      13,  64 Apr  1 10:49 event0
+	crw-r--r--   1 root     root      13,  65 Apr  1 10:50 event1
+	crw-r--r--   1 root     root      13,  66 Apr  1 10:50 event2
+	crw-r--r--   1 root     root      13,  67 Apr  1 10:50 event3
+	...
+
+3. Contacts
+~~~~~~~~~~~
+  This effort has it's home page at:
+
+	http://www.suse.cz/development/input/
+
+You'll find both the latest HID driver and the complete Input driver there
+as well as information how to access the CVS repository for latest revisions
+of the drivers.
+
+
+  There is also a mailing list for this:
+
+	majordomo@atrey.karlin.mff.cuni.cz
+
+Send "subscribe linux-input" to subscribe to it.
+
+4. Verifying if it works
+~~~~~~~~~~~~~~~~~~~~~~~~
+  Typing a couple keys on the keyboard should be enough to check that a USB
+keyboard works and is correctly connected to the kernel keyboard driver.
+
+  Doing a cat /dev/input/mouse0 (c, 13, 32) will verify that a mouse is also
+emulated, characters should appear if you move it.
+
+  You can test the joystick emulation with the 'jstest' utility, available
+in the joystick package (see Documentation/joystick.txt).
+
+  You can test the event devics with the 'evtest' utility available on the
+input driver homepage (see the URL above).
+
+5. Event interface
+~~~~~~~~~~~~~~~~~~
+  Should you want to add event device support into any application (X, gpm,
+svgalib ...) I <vojtech@suse.cz> will be happy to provide you any help I
+can. Here goes a description of the current state of things, which is going
+to be extended, but not changed incompatibly as time goes:
+
+  You can use blocking and nonblocking reads, also select() on the
+/dev/input/eventX devices, and you'll always get a whole number of input
+events on a read. Their layout is:
+
+struct input_event {
+	struct timeval time;
+	unsigned short type;
+	unsigned short code;
+	unsigned int value;
+};
+
+  'time' is the timestamp, it returns the time at which the event happened.
+Type is for example EV_REL for relative movement, REL_KEY for a keypress or
+release. More types are defined in include/linux/input.h.
+
+  'code' is event code, for example REL_X or KEY_BACKSPACE, again a complete
+list is in include/linux/input.h.
+
+  'value' is the value the event carries. Either a relative change for
+EV_REL, absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for
+release, 1 for keypress and 2 for autorepeat.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/ohci.txt linux.18p17/Documentation/usb/ohci.txt
--- linux.vanilla/Documentation/usb/ohci.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/ohci.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,98 @@
+
+The OHCI HCD layer is a simple but nearly complete implementation of what the
+USB people would call a HCD  for the OHCI. 
+ (ISO coming soon, Bulk, INT u. CTRL transfers enabled)
+It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree).
+The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. 
+
+- Roman Weissgaerber <weissg@vienna.at>
+
+ * v4.0 1999/08/18 removed all dummy eds, unlink unused eds, code cleanup, bulk transfers 
+ * v2.1 1999/05/09 ep_addr correction, code cleanup
+ * v0.2.0 1999/05/04 
+ * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers)
+ * virtual root hub is now an option, 
+ * memory allocation based on kmalloc and kfree now, simple Bus error handling, 
+ * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
+ * 
+ * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff
+ * from Greg Smith (ohci.c): better reset ohci-controller handling, hub
+ * 
+ * v0.1.0 1999/04/27 initial release
+ 
+to remove the module try:
+rmmod usb-ohci
+
+Features:
+- virtual root hub, all basic hub descriptors and commands (state: complete) 
+  this is an option now (v0.2.0)
+  #define  CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB)
+  default is with. 
+  (at the moment: the Virtual Root Hub is included automatically)
+  
+  files: ohci-root-hub.c, ohci-root-hub.h 
+ 
+
+- Endpoint Descriptor (ED) handling more static approach 
+ (EDs should be allocated in parallel to the SET CONFIGURATION command and they live
+ as long as the function (device) is alive or another configuration is chosen.
+ In the HCD layer the EDs has to be allocated manually either by calling a subroutine
+ or by sending a USB root hub vendor specific command to the virtual root hub.
+ At the alternate linux usb stack EDs will be added (allocated) at their first use.
+ ED will be unlinked from the HC chains if they are not busy.
+ 
+ files: ohci-hcd.c ohci-hcd.h
+ routines:   (do not use for drivers, use the top layer alternate usb commands instead)
+ 
+ int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1, 
+            int interval, int load, f_handler handler, int ep_size, int speed)
+       adds an endpoint, (if the endpoint already exists some parameters will be updated)
+       
+ int usb_ohci_rm_ep( ) 
+       removes an endpoint and all pending TDs of that EP
+       
+  usb_ohci_rm_function( )
+       removes all Endpoints of a function (device)
+
+- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers
+  The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has
+  to take care of buffer allocation.
+  files: ohci-hcd.c ohci-hcd.h 
+
+  There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL):
+  
+  int ohci_trans_req(struct ohci * ohci, hcd_ed, int ctrl_len, void  *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1)
+  
+  CTRL: ctrl, ctrl_len ... cmd buffer 
+        data, data_len ... data buffer (in or out)
+  INT, BULK:  ctrl = NULL, ctrl_len=0,
+  		data, data_len ... data buffer (in or out)
+  ISO: tbd
+
+  There is no buffer reinsertion done by the internal HCD function. 
+  (The interface layer does this for a INT-pipe on request.)
+  If you want a transfer then you have to 
+  provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED
+  you can send as many as you like. They should come back by the callback f_handler in
+  the same order (for each endpoint, not globally) If an error occurs all
+  queued transfers of an endpoint will return unsent. They will be marked with an error status.
+  
+  e.g double-buffering for int transfers:
+
+   	ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+  	ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+  	
+  and when a data0 packet returns by the callback f_handler requeue it:
+  	ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+  and when a data1 packet returns by the callback f_handler requeue it:
+  	ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+  	
+  lw0, lw1 are private fields for upper layers for ids or fine grained handlers.
+  The alternate usb uses them for dev_id and usb_device_irq handler.
+
+
+- Done list handling: returns the requests (callback f_handler in ED) and does 
+  some error handling, root-hub request dequeuing
+  (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0))
+
+  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/ov511.txt linux.18p17/Documentation/usb/ov511.txt
--- linux.vanilla/Documentation/usb/ov511.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/ov511.txt	Fri Sep 15 12:49:09 2000
@@ -0,0 +1,254 @@
+-------------------------------------------------------------------------------
+Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC
+-------------------------------------------------------------------------------
+
+Author: Mark McClelland
+Homepage: http://alpha.dyndns.org/ov511
+
+NEW IN THIS VERSION:
+ o Stability improvements
+ o Support for hue control
+ o 160x120 mostly working
+ o OV6620 color problems fixed
+ o MoreWebCam 3 detection improvements
+
+INTRODUCTION:
+
+This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
+Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It 
+supports streaming and capture of color or monochrome video via the Video4Linux
+API. Most V4L apps are compatible with it, but a few videoconferencing programs
+do not work yet. The following resolutions are supported: 640x480, 448x336,
+384x288, 352x288, and 320x240.
+
+WHAT YOU NEED:
+
+- If you want to help with the development, get the chip's specification docs at
+  http://www.ovt.com/omniusbp.html
+
+- A Video4Linux compatible frame grabber program (I recommend vidcat and xawtv)
+    vidcat is part of the w3cam package:  http://www.hdk-berlin.de/~rasca/w3cam/
+    xawtv is available at:  http://www.in-berlin.de/User/kraxel/xawtv.html
+
+HOW TO USE IT:
+
+You must have first compiled USB support, support for your specific USB host
+controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
+making them modules.)
+
+Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
+
+	insmod usb/usbcore.o
+	insmod usb/usb-uhci.o  <OR>  insmod usb/ohci-hcd.o
+	insmod misc/videodev.o
+	insmod usb/ov511.o
+
+If it is not already there (it usually is), create the video device:
+
+	mknod /dev/video c 81 0
+
+Sometimes /dev/video is a symlink to /dev/video0
+
+You will have to set permissions on this device to allow you to read/write
+from it:
+
+	chmod 666 /dev/video
+	chmod 666 /dev/video0 (if necessary)
+	
+Now you are ready to run a video app! Both vidcat and xawtv work well for me
+at 640x480.
+	
+[Using vidcat:]
+
+	vidcat -s 640x480 > test.jpg
+	xview test.jpg
+	
+[Using xawtv:]
+
+You must make some modifications to the source and compile it before you use it.
+(Note: this may not be applicable to versions other than 3.06)
+
+In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next,
+in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv
+directory:
+
+	make clean
+	./configure
+	make
+	make install
+
+Now you should be able to run xawtv. Right click for the options dialog. If
+you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
+Try setting the size to 320x240 if all else fails.
+
+FAQ:
+Q: "Why does the picture have noise and look grainy"
+A: This is a problem at low light levels, and may be also due to subtle bugs in
+   the code. The cause is most likely the OV7610 settings we are currently
+   using. I am looking into this problem.
+
+Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?"
+A: The I2C code that allows the OV511 to communicate with the camera chip is a
+   bit flaky right now. This message means that the I2C bus never got
+   initialized properly, and the camera will most likely not work even if you
+   disable this warning. Try unloading/reloading the driver or unplugging/re-
+   plugging the camera if this happens. Also try increasing the i2c_detect_tries
+   parameter (see below).
+
+Q: "Why do you bother with this phony camera detection crap? It doesn't do
+    anything useful!"
+A: The main purpose of only supporting known camera models is to force people
+   with new camera models to tell me about them, so I can assemble the list
+   above, and so the code can know what CCD chip you have. Right now, nearly all
+   of the cameras use the OV7610 and consequently I have not put support for
+   other ones in, so the value of the detection code is questionable. Eventually
+   though, new CCDs might appear and we will be fortunate to have the detection.
+
+MODULE PARAMETERS:
+
+  You can set these with:  insmod ov511 NAME=VALUE
+  There is currently no way to set these on a per-camera basis.
+
+  NAME: autoadjust
+  TYPE: integer (boolean)
+  DEFAULT: 1
+  DESC: The camera normally adjusts exposure, gain, and hue automatically. This
+        can be set to 0 to disable this automatic adjustment. Note that there is
+        currently no way to set these parameters manually once autoadjust is
+        disabled.
+
+  NAME: debug
+  TYPE: integer (0-6)
+  DEFAULT: 3
+  DESC: Sets the threshold for printing debug messages. The higher the value,
+        the more is printed. The levels are cumulative, and are as follows:
+          0=no debug messages
+          1=init/detection/unload and other significant messages
+          2=some warning messages
+          3=config/control function calls
+          4=most function calls and data parsing messages
+          5=highly repetitive mesgs
+
+  NAME: fix_rgb_offset
+  TYPE: integer (boolean)
+  DEFAULT: 0
+  DESC: Some people have reported that the blue component of the image is one
+        or so lines higher than the red component. This is only apparent in 
+        images with white objects on black backgrounds at 640x480. Setting this
+        to 1 will realign the color planes correctly. NOTE: This is still
+        experimental and very buggy. You will likely need a fast (500 Mhz) CPU.
+
+  NAME: snapshot
+  TYPE: integer (boolean)
+  DEFAULT: 0
+  DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
+        button is pressed. Note that this does not yet work with most apps,
+        including xawtv and vidcat. NOTE: See the section "TODO" for more info.
+
+  NAME: sensor
+  TYPE: integer ([0, 1, 3])
+  DEFAULT: [varies]
+  DESC: If you know that your camera sensor is not detected correctly, set this
+        parameter. This is a global option for all attached OV511 cameras. You
+        will probably never need to set this, but if you do, valid values are:
+        	0 for OV7620
+        	1 for OV7620AE
+        	3 for OV7610
+
+  NAME: i2c_detect_tries
+  TYPE: integer (don't set it insanely high!)
+  DEFAULT: 5
+  DESC: This is the number of times the driver will try to sync and detect the
+        internal i2c bus (which connects the OV511 and sensor). If you are
+        getting intermittent detection failures ("Failed to read sensor ID...")
+        you should increase this by a modest amount. If setting it to 20 or so
+        doesn't fix things, look elsewhere for the cause of the problem.
+
+  NAME: aperture
+  TYPE: integer (0 - 15)
+  DEFAULT: [varies by sensor]
+  DESC: For legal values, see the OV7610/7620 specs under register Common F.
+        This setting affects the upper nybble of that reg (bits 4-7). This is
+        for if you want to play with the camera's pixel saturation.
+
+  NAME: force_rgb
+  TYPE: integer (boolean)
+  DEFAULT: 0
+  DESC: Force image to be read in RGB instead of BGR. This option allow
+        programs that expect RGB data (e.g. gqcam) to work with this driver. If
+        your colors look VERY wrong, you may want to change this.
+
+  NAME: buf_timeout
+  TYPE: integer
+  DEFAULT: 5 (seconds)
+  DESC: Number of seconds before unused frame buffers are deallocated.
+        Previously, memory was allocated upon open() and deallocated upon
+        close(). Deallocation now occurs only if the driver is closed and this
+        timeout is reached. If you are capturing frames less frequently than
+        the default timeout, increase this. This will not make any difference
+        with programs that capture multiple frames during an open/close cycle.
+
+  NAME: cams
+  TYPE: integer (1-4 for OV511, 1-31 for OV511+)
+  DEFAULT: 1
+  DESC: Number of cameras allowed to stream simultaneously on a single bus.
+        Values higher than 1 reduce the data rate of each camera, allowing two
+        or more to be used at once. If you have a complicated setup involving
+        both OV511 and OV511+ cameras, trial-and-error may be necessary for
+        finding the optimum setting.
+
+  NAME: retry_sync
+  TYPE: boolean
+  DEFAULT: 0
+  DESC: Prevent apps from timing out if frame is not done in time. This is
+        useful if you are having problems with Xawtv getting "stuck" on a frame
+        when your system is under heavy load.
+ 
+WORKING FEATURES:
+ o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and
+   160x120
+ o RGB24, YUV420, YUV422, YUYV, and YUV422P color
+ o Monochrome
+ o Setting/getting of saturation, contrast, brightness, and hue (only some of
+   them work the OV7620 and OV7620AE)
+ o proc status reporting
+
+EXPERIMENTAL FEATURES:
+ o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
+   corrupted frames. If you have a very fast CPU, you can try it.
+ o Snapshot mode (only works with some read() based apps; see below for more)
+ o OV6620 sensor support
+ o GBR422 parsing
+
+TODO:
+ o Fix the noise / grainy image problem.
+ o Get compression working. It would be a nice addition as it improves
+   frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
+   so we can't really work on that yet. Please kindly inform OmniVision that you
+   would like them to release their specifications to the Linux community.
+ o YUV422
+ o Get snapshot mode working with mmap().
+ o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
+ o Get autoadjust disable working
+ o V4L2 support (Probably not until it goes into the kernel)
+ o Creative WebCam III has problems initializing its sensor. This should be
+   fixed now, but if you still have problems let me know.
+ o Get rid of the memory management functions (put them in videodev.c??)
+ o Setting of contrast and brightness not working with 7620/7620AE
+ o Driver/camera state save/restore for when USB supports suspend/resume
+ o Unstable on SMP systems
+ o OV7620/OV6620 experience frame corruption with moving objects
+ o OV6620 is too dark
+
+HOW TO CONTACT ME:
+
+You can email me at mwm@i.am . Please prefix the subject line
+with "OV511: " so that I am certain to notice your message.
+
+CREDITS:
+
+The code is based in no small part on the CPiA driver by Johannes Erdfelt,
+Randy Dunlap, and others. Big thanks to them for their pioneering work on that
+and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
+image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
+Matsuoka for their work as well.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/proc_usb_info.txt linux.18p17/Documentation/usb/proc_usb_info.txt
--- linux.vanilla/Documentation/usb/proc_usb_info.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/proc_usb_info.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,273 @@
+/proc/bus/usb filesystem output
+===============================
+(version 2000.08.15)
+
+
+The /proc filesystem for USB devices generates
+/proc/bus/usb/drivers and /proc/bus/usb/devices.
+
+/proc/bus/usb/drivers lists the registered drivers,
+one per line, with each driver's USB minor dev node
+number range if applicable.
+
+**NOTE**: If /proc/bus/usb appears empty, you need
+          to mount the filesystem, issue the command (as root):
+
+      mount -t usbdevfs none /proc/bus/usb
+
+	  An alternative and more permanent method would be to add
+
+      none  /proc/bus/usb  usbdevfs  defaults  0  0
+
+	  to /etc/fstab.  This will mount usbdevfs at each reboot.
+	  You can then issue `cat /proc/bus/usb/devices` to extract
+	  USB device information.
+
+For more information on mounting the usbdevfs file system, see the
+"USB Device Filesystem" section of the USB Guide. The latest copy 
+of the USB Guide can be found at http://www.linux-usb.org/
+
+In /proc/bus/usb/devices, each device's output has multiple
+lines of ASCII output.
+I made it ASCII instead of binary on purpose, so that someone
+can obtain some useful data from it without the use of an
+auxiliary program.  However, with an auxiliary program, the numbers
+in the first 4 columns of each "T:" line (topology info:
+Lev, Prnt, Port, Cnt) can be used to build a USB topology diagram.
+(I think.  I haven't proved this, but I have tested it with 3
+different topo/connections and it looked possible.)
+
+Each line is tagged with a one-character ID for that line:
+
+T = Topology (etc.)
+B = Bandwidth (applies only to USB host controllers, which are
+    virtualized as root hubs)
+D = Device descriptor info.
+P = Product ID info. (from Device descriptor, but they won't fit
+    together on one line)
+S = String descriptors.
+C = Configuration descriptor info. (* = active configuration)
+I = Interface descriptor info.
+E = Endpoint descriptor info.
+
+=======================================================================
+
+/proc/bus/usb/devices output format:
+
+Legend:
+  d = decimal number (may have leading spaces or 0's)
+  x = hexadecimal number (may have leading spaces or 0's)
+  s = string
+
+
+Topology info:
+
+T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd
+|   |      |      |       |       |      |        |       |__MaxChildren
+|   |      |      |       |       |      |        |__Device Speed in Mbps
+|   |      |      |       |       |      |__DeviceNumber
+|   |      |      |       |       |__Count of devices at this level
+|   |      |      |       |__Connector/Port on Parent for this device
+|   |      |      |__Parent DeviceNumber
+|   |      |__Level in topology for this bus
+|   |__Bus number
+|__Topology info tag
+
+
+Bandwidth info:
+B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
+|   |                       |         |__Number if isochronous requests
+|   |                       |__Number of interrupt requests
+|   |__Total Bandwidth allocated to this bus
+|__Bandwidth info tag
+
+
+Device descriptor info & Product ID info:
+
+D:  Ver=x.xx Cls=xx(s) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
+P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx
+
+where
+D:  Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
+|   |        |             |      |       |       |__NumberConfigurations
+|   |        |             |      |       |__MaxPacketSize of Default Endpoint
+|   |        |             |      |__DeviceProtocol
+|   |        |             |__DeviceSubClass
+|   |        |__DeviceClass
+|   |__Device USB version
+|__Device info tag #1
+
+where
+P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx
+|   |           |           |__Product revision number
+|   |           |__Product ID code
+|   |__Vendor ID code
+|__Device info tag #2
+
+
+String descriptor info:
+
+S:  Manufacturer=ssss
+|   |__Manufacturer of this device as read from the device.
+|__String info tag
+
+S:  Product=ssss
+|   |__Product description of this device as read from the device,
+|      except that it is a generated string for USB host controllers
+|      (virtual root hubs), in the form "USB *HCI Root Hub".
+|__String info tag
+
+S:  SerialNumber=ssss
+|   |__Serial Number of this device as read from the device,
+|      except that it is a generated string for USB host controllers
+|      (virtual root hubs), and represents the host controller's
+|      unique identification in the system (currently I/O or
+|      memory-mapped base address).
+|__String info tag
+
+
+Configuration descriptor info:
+
+C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
+|   |       |       |      |__MaxPower in mA
+|   |       |       |__Attributes
+|   |       |__ConfiguratioNumber
+|   |__NumberOfInterfaces
+|__Config info tag
+
+
+Interface descriptor info (can be multiple per Config):
+
+I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
+|   |      |      |       |             |      |       |__Driver name
+|   |      |      |       |             |      |__InterfaceProtocol
+|   |      |      |       |             |__InterfaceSubClass
+|   |      |      |       |__InterfaceClass
+|   |      |      |__NumberOfEndpoints
+|   |      |__AlternateSettingNumber
+|   |__InterfaceNumber
+|__Interface info tag
+
+
+Endpoint descriptor info (can be multiple per Interface):
+
+E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms
+E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms
+|   |        |            |         |__Interval
+|   |        |            |__EndpointMaxPacketSize
+|   |        |__Attributes(EndpointType)
+|   |__EndpointAddress(I=In,O=Out)
+|__Endpoint info tag
+
+=======================================================================
+
+
+If a user or script is interested only in Topology info, for
+example, use something like "grep ^T: /proc/bus/usb/devices"
+for only the Topology lines.  A command like
+"grep -i ^[tdp]: /proc/bus/usb/devices" can be used to list
+only the lines that begin with the characters in square brackets,
+where the valid characters are TDPCIE.  With a slightly more able
+script, it can display any selected lines (for example, only T, D,
+and P lines) and change their output format.  (The "procusb"
+Perl script is the beginning of this idea.  It will list only
+selected lines [selected from TBDPSCIE] or "All" lines from
+/proc/bus/usb/devices.)
+
+The Topology lines can be used to generate a graphic/pictorial
+of the USB devices on a system's root hub.  (See more below
+on how to do this.)
+
+The Interface lines can be used to determine what driver is
+being used for each device.
+
+The Configuration lines could be used to list maximum power
+(in milliamps) that a system's USB devices are using.
+For example, "grep ^C: /proc/bus/usb/devices".
+
+
+Here's an example, from a system which has a UHCI root hub,
+an external hub connected to the root hub, and a mouse and
+a serial converter connected to the external hub.
+
+T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
+B:  Alloc= 28/900 us ( 3%), #Int=  2, #Iso=  0
+D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=0000 ProdID=0000 Rev= 0.00
+S:  Product=USB UHCI Root Hub
+S:  SerialNumber=dce0
+C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
+E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms
+T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 4
+D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=0451 ProdID=1446 Rev= 1.00
+C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
+E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=255ms
+T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5 MxCh= 0
+D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=04b4 ProdID=0001 Rev= 0.00
+C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
+E:  Ad=81(I) Atr=03(Int.) MxPS=   3 Ivl= 10ms
+T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12  MxCh= 0
+D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=0565 ProdID=0001 Rev= 1.08
+S:  Manufacturer=Peracom Networks, Inc.
+S:  Product=Peracom USB to Serial Converter
+C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
+I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial
+E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl= 16ms
+E:  Ad=01(O) Atr=02(Bulk) MxPS=  16 Ivl= 16ms
+E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=  8ms
+
+
+Selecting only the "T:" and "I:" lines from this (for example, by using
+"procusb ti"), we have:
+
+T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
+T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 4
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
+T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5 MxCh= 0
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
+T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12  MxCh= 0
+I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial
+
+
+Physically this looks like (or could be converted to):
+
+                      +------------------+
+                      |  PC/root_hub (12)|   Dev# = 1
+                      +------------------+   (nn) is Mbps.
+    Level 0           |  CN.0   |  CN.1  |   [CN = connector/port #]
+                      +------------------+
+                          /
+                         /
+            +-----------------------+
+  Level 1   | Dev#2: 4-port hub (12)|
+            +-----------------------+
+            |CN.0 |CN.1 |CN.2 |CN.3 |
+            +-----------------------+
+                \           \____________________
+                 \_____                          \
+                       \                          \
+               +--------------------+      +--------------------+
+  Level 2      | Dev# 3: mouse (1.5)|      | Dev# 4: serial (12)|
+               +--------------------+      +--------------------+
+
+
+
+Or, in a more tree-like structure (ports [Connectors] without
+connections could be omitted):
+
+PC:  Dev# 1, root hub, 2 ports, 12 Mbps
+|_ CN.0:  Dev# 2, hub, 4 ports, 12 Mbps
+     |_ CN.0:  Dev #3, mouse, 1.5 Mbps
+     |_ CN.1:
+     |_ CN.2:  Dev #4, serial, 12 Mbps
+     |_ CN.3:
+|_ CN.1:
+
+
+                         ### END ###
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/rio.txt linux.18p17/Documentation/usb/rio.txt
--- linux.vanilla/Documentation/usb/rio.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/rio.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,138 @@
+Copyright (C) 1999, 2000 Bruce Tenison
+Portions Copyright (C) 1999, 2000 David Nelson
+Thanks to David Nelson for guidance and the usage of the scanner.txt
+and scanner.c files to model our driver and this informative file.
+
+Mar. 2, 2000
+
+CHANGES
+
+- Initial Revision
+
+
+OVERVIEW
+
+This README will address issues regarding how to configure the kernel
+to access a RIO 500 mp3 player.  
+Before I explain how to use this to access the Rio500 please be warned:
+
+W A R N I N G:
+--------------
+
+Please note that this software is still under development.  The authors
+are in no way responsible for any damage that may occur, no matter how
+inconsequential.
+
+It seems that the Rio has a problem when sending .mp3 with low batteries.
+I suggest when the batteries are low and want to transfer stuff that you
+replace it with a fresh one. In my case, what happened is I lost two 16kb
+blocks (they are no longer usable to store information to it). But I don't
+know if thats normal or not. It could simply be a problem with the flash 
+memory.
+
+In an extreme case, I left my Rio playing overnight and the batteries wore 
+down to nothing and appear to have corrupted the flash memory. My RIO 
+needed to be replaced as a result.  Diamond tech support is aware of the 
+problem.  Do NOT allow your batteries to wear down to nothing before 
+changing them.  It appears RIO 500 firmware does not handle low battery 
+power well at all. 
+
+On systems with OHCI controllers, the kernel OHCI code appears to have 
+power on problems with some chipsets.  If you are having problems 
+connecting to your RIO 500, try turning it on first and then plugging it 
+into the USB cable.  
+
+Contact information:
+--------------------
+
+   The main page for the project is hosted at sourceforge.net in the following
+   address: http://rio500.sourceforge.net You can also go to the sourceforge
+   project page at: http://sourceforge.net/project/?group_id=1944 There is 
+   also a mailing list: rio500-users@lists.sourceforge.net
+
+Authors:
+-------
+
+Most of the code was written by Cesar Miquel <miquel@df.uba.ar>. Keith 
+Clayton <kclayton@jps.net> is incharge of the PPC port and making sure
+things work there. Bruce Tenison <btenison@dibbs.net> is adding support
+for .fon files and also does testing. The program will mostly sure be
+re-written and Pete Ikusz along with the rest will re-design it. I would
+also like to thank Tri Nguyen <tmn_3022000@hotmail.com> who provided use 
+with some important information regarding the communication with the Rio.
+
+ADDITIONAL INFORMATION and Userspace tools
+
+http://rio500.sourceforge.net/
+
+
+REQUIREMENTS
+
+A host with a USB port.  Ideally, either a UHCI (Intel) or OHCI
+(Compaq and others) hardware port should work.
+
+A Linux development kernel (2.3.x) with USB support enabled or a
+backported version to linux-2.2.x.  See http://www.linux-usb.org for
+more information on accomplishing this.
+
+A Linux kernel with RIO 500 support enabled.
+
+'lspci' which is only needed to determine the type of USB hardware
+available in your machine.
+
+CONFIGURATION
+
+Using `lspci -v`, determine the type of USB hardware available.
+
+  If you see something like:
+
+    USB Controller: ......
+    Flags: .....
+    I/O ports at ....
+
+  Then you have a UHCI based controller.
+
+  If you see something like:
+
+     USB Controller: .....
+     Flags: ....
+     Memory at .....
+
+  Then you have a OHCI based controller.
+
+Using `make menuconfig` or your preferred method for configuring the
+kernel, select 'Support for USB', 'OHCI/UHCI' depending on your
+hardware (determined from the steps above), 'USB Diamond Rio500 support', and
+'Preliminary USB device filesystem'.  Compile and install the modules
+(you may need to execute `depmod -a` to update the module
+dependencies).
+
+Add a device for the USB rio500:
+  `mknod /dev/usb/rio500 c 180 64`
+
+Set appropriate permissions for /dev/usb/rio500 (don't forget about
+group and world permissions).  Both read and write permissions are
+required for proper operation.
+
+Load the appropriate modules (if compiled as modules):
+
+  OHCI:
+    modprobe usbcore
+    modprobe usb-ohci
+    modprobe rio500
+
+  UHCI:
+    modprobe usbcore
+    modprobe usb-uhci  (or uhci)
+    modprobe rio500
+
+That's it.  The Rio500 Utils at: http://rio500.sourceforge.net should
+be able to access the rio500.
+
+BUGS
+
+If you encounter any problems feel free to drop me an email.
+
+Bruce Tenison
+btenison@dibbs.net
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/scanner-hp-sane.txt linux.18p17/Documentation/usb/scanner-hp-sane.txt
--- linux.vanilla/Documentation/usb/scanner-hp-sane.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/scanner-hp-sane.txt	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,81 @@
+Copyright (C) 1999, 2000 David E. Nelson
+
+April 26, 2000
+
+CHANGES
+
+- Amended for Linux-2.3.99-pre6-3
+- Updated for multiple scanner support
+
+INTRODUCTION
+
+This document will hopefully provide enough info on how to get SANE
+working with a Hewlett Packard USB capable scanner using the USB
+interface.  The majority of HP Scanners support the Scanner Control
+Language (SCL) which is both published by HP and supported by SANE.
+The only HP Scanners that I'm aware of that do not support SCL are the
+4200C ,3300C, and the PhotoSmart S20.  All other HP scanners with USB
+interfaces should work (4100C, 5200C, 6200C, and 6300C) as do models
+that are derived from the models above. ie the 6350C which is a 6300C
+with a transparency adaptor included with the scanner at time of
+purchase.  Of course as HP releases new scanners this information may
+change.
+
+
+REQUIREMENTS
+
+In order to get this running you'll need USB support in your kernel in
+addition to USB Scanner support.  Please refer to scanner.txt for
+issues pertaining to Linux USB and USB Scanner support.
+
+An installed version of SANE which is available from
+http://www.mostang.com/sane/.  Testing has been performed using
+version SANE-1.0.1.  For instructions on building and installing SANE,
+refer to the various README files within the SANE distribution.
+
+The latest SANE HP backend is available from http://www.kirchgessner.net.
+At the time of this writing, version 0.83 was available.
+
+
+OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW?
+
+NOTE: $INSTALL_DIR is the location where SANE is installed.  It may be
+/usr/local, /usr, /opt or somewhere else.  If you don't know, ask your
+system administrator.
+
+1) Make sure that you have the libsane-hp.* libraries under the
+$INSTALL_DIR/lib/sane/ directory.  If you don't, then the HP backend
+was either not compiled or installed properly.
+
+2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following
+files: dll.conf, hp.conf.
+
+  dll.conf:  Make sure that the 'hp' entry is present and uncommented.
+
+  hp.conf: This should contain two lines:
+
+    /dev/usbscanner
+    option connect-device
+
+NOTE: If you are using multiple scanners, make sure to have the correct
+device, ie /dev/usbscanner0.  See scanner.txt for more info.
+
+3) You should now be able to use SANE (xscanimage or scanimage).
+
+Don't forget to read any relevant man pages regarding the usage of
+SANE.  If you have other entries uncommented in 'dll.conf', you may
+have to specify the device to (x)scanimage.  Again, `man` is your
+friend.  The xscanimage (1) man page has info on how to get 'The Gimp'
+to work with xscanimage.  Note that Gimp support must be compiled into
+SANE for it to work.  If you are dealing with a RedHat system, this
+means that you'll also need to install the gimp-devel rpm package
+prior to compiling SANE.
+
+NOTE: The issues regarding core dumping by (x)scanimage have (or seem
+to be thus far) been resolved with version 0.2+ of the USB scanner
+driver which should be available in linux-2.3.23.  If you notice
+otherwise, please contact me.
+
+David /\/elson
+dnelson@jump.net
+http://www.jump.net/~dnelson
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/scanner.txt linux.18p17/Documentation/usb/scanner.txt
--- linux.vanilla/Documentation/usb/scanner.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/scanner.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,362 @@
+Copyright (C) 1999, 2000 David E. Nelson
+
+April 26, 2000
+
+CHANGES
+
+- Amended for linux-2.3.99-pre6-3
+- Appended hp_scan.c to end of this README
+- Removed most references to HP
+- Updated uhci/ohci host controller info
+- Updated support for multiple scanner support
+- Updated supported scanners list
+- Updated usbdevfs info
+- Spellcheck
+
+OVERVIEW
+
+This README addresses issues regarding how to configure the kernel
+to access a USB scanner.  Although the driver was originally conceived
+for USB HP scanners, it's general enough so that it can be used with
+other scanners.  Also, one can now pass the USB Vendor and Product
+ID's using module parameters for unknown scanners.  Refer to the
+document scanner-hp-sane.txt for guidance on how to configure SANE to
+use a USB HP Scanner.
+
+
+ADDITIONAL INFORMATION
+
+http://www.linux-usb.org/
+
+
+REQUIREMENTS
+
+A host with a USB port.  Ideally, either a UHCI (Intel) or OHCI
+(Compaq and others) hardware port should work.  At the time of this
+writing, there are two UHCI drivers and one OHCI.
+
+A Linux development kernel (2.3.x) with USB support enabled or a
+backported version to linux-2.2.x.  See http://www.linux-usb.org for
+more information on accomplishing this.
+
+A Linux kernel with USB Scanner support enabled.
+
+'lspci' which is only needed to determine the type of USB hardware
+available/installed in your machine.
+
+CONFIGURATION
+
+Using `lspci -v`, determine the type of USB hardware available/installed.
+
+  If you see something like:
+
+    USB Controller: ......
+    Flags: .....
+    I/O ports at ....
+
+  Then you have a UHCI based controller.
+
+  If you see something like:
+
+     USB Controller: .....
+     Flags: ....
+     Memory at .....
+
+  Then you have a OHCI based controller.
+
+Using `make menuconfig` or your preferred method for configuring the
+kernel, select 'Support for USB', 'OHCI/UHCI' depending on your
+hardware (determined from the steps above), 'USB Scanner support', and
+'Preliminary USB device filesystem'.  Compile and install the modules
+(you may need to execute `depmod -a` to update the module
+dependencies). If any of the USB sections were compiled into the
+kernel, a reboot is necessary. NOTE: Updating the boot disk with
+'lilo' may also be required. Testing was performed only as modules,
+YMMV.
+
+Beginning with version 0.4 of the driver, up to 16 scanners can be
+connected/used simultaneously.  If you intend to use more than
+one scanner at a time:
+
+   Add a device for the USB scanner:
+	`mknod /dev/usbscanner0 c 180 48`
+	`mknod /dev/usbscanner1 c 180 49`
+                      . 
+                      .
+	`mknod /dev/usb/scanner15 180 63`
+
+
+If you foresee using only one scanner it is best to:
+	`mknod /dev/usbscanner0 c 180 48`
+	`ln -s /dev/usbscanner0 /dev/usbscanner`
+
+
+Set appropriate permissions for /dev/usbscanner[0-15] (don't forget
+about group and world permissions).  Both read and write permissions
+are required for proper operation. For example:
+	`chmod 666 /dev/usbscanner0`
+
+Load the appropriate modules (if compiled as modules):
+
+  OHCI:
+    modprobe usb-ohci
+    modprobe scanner
+
+  UHCI:
+    modprobe usb-uhci
+    modprobe scanner
+
+That's it.  SANE should now be able to access the device.  
+
+There is a small test program (hp_scan.c -- appended below) that can
+be used to test the scanner device if it's an HP scanner that supports
+SCL (Scanner Control Language).  Known HP scanner that support SCL are
+the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not*
+supported since it does not understand SCL; it's also strongly
+suspected that the 3300 and the PhotoSmart S20 are not SCL compliant.
+Hp_scan.c's purpose is to test the driver without having to
+retrieve/configure SANE.  Hp_scan.c will scan the entire bed and put
+the output into a file called 'out.dat' in the current directory.  The
+data in the file is raw data so it's not very useful for imaging.
+
+MESSAGES
+
+On occasions the message 'usb_control/bulk_msg: timeout' or something
+similar will appear in '/var/adm/messages' or on the console or both,
+depending on how your system is configured.  This is a side effect
+that scanners are sometimes very slow at warming up and/or
+initializing.  In most cases, however, only several of these messages
+should appear and is generally considered to be normal.  If you see
+a message of the type 'excessive NAK's received' then this should
+be considered abnormal and generally indicates that the USB system is
+unable to communicate with the scanner for some particular reason.
+
+SUPPORTED SCANNERS
+
+NOTE: Just because a product is listed here does not mean that
+applications exist that support the product.  It's in the hopes that
+this will allow developers a means to produce applications that will
+support the listed USB products.
+
+At the time of this writing, the following scanners were supported by
+scanner.c:
+
+ Acer
+	 Prisa Acerscan 620U & 640U (!)
+	 Prisa AcerScan 620U (!)
+ Agfa
+	 SnapScan 1212U
+	 Another SnapScan 1212U (?)
+	 SnapScan Touch
+ Colorado -- See Primax/Colorado below
+ Epson -- See Seiko/Epson below
+ Genius
+         ColorPage-Vivid Pro
+ Hewlett Packard
+	 3300C
+	 4100C
+	 4200C
+	 PhotoSmart S20
+	 5200C
+	 6200C
+	 6300C
+ Microtek
+	 ScanMaker X6 - X6U
+	 Phantom 336CX - C3
+	 Phantom 336CX - C3 #2
+	 Phantom C6
+	 ScanMaker V6USL
+	 ScanMaker V6USL #2
+	 ScanMaker V6UL - SpicyU
+ Mustek
+	 1200 CU
+ Primax/Colorado
+	 G2-300 #1
+	 G2-600 #1
+	 G2E-300 #1
+	 ReadyScan 636i
+	 G2-300 #2
+	 G2-600 #2
+	 G2E-300 #2
+	 G2E-600
+	 Colorado USB 9600
+	 Colorado USB 19200
+	 Colorado 600u
+	 Colorado 1200u
+ Seiko/Epson Corp.
+	 Perfection 636U and 636Photo
+	 Perfection 610
+	 Perfection 1200U and 1200Photo
+ Umax
+	 Astra 1220U
+	 Astra 1236U
+	 Astra 2000U
+	 Astra 2200U
+ Visioneer
+	 OneTouch 5300
+	 OneTouch 7600 duplicate ID (!)
+	 6100
+
+
+MODULE PARAMETERS
+
+If you have a device that you wish to experiment with or try using
+this driver with, but the Vendor and Product ID's are not coded in,
+don't despair.  If the driver was compiled as a module, you can pass
+options to the driver.  Simply add 
+
+  options scanner vendor=0x#### product=0x****
+
+to the /etc/modules.conf file replacing the #'s and the *'s with the
+correct ID's.  The ID's can be retrieved from the messages file or
+using `cat /proc/bus/usb/devices`. Note that USB /proc support must be
+enabled during kernel configuration.  If the 'scanner' module is
+already loaded into memory, it must be reloaded for the module
+parameters to take effect.  In essence, `rmmod scanner; modprobe
+scanner` must be performed.
+
+**NOTE**: In later kernels (2.3.38+), a new filesystem was introduced,
+usbdevfs.  To mount the filesystem, issue the command (as root):
+
+  mount -t usbdevfs /proc/bus/usb /proc/bus/usb
+
+An alternative and more permanent method would be to add
+
+  none  /proc/bus/usb  usbdevfs  defaults  0  0
+
+to /etc/fstab.  This will mount usbdevfs at each reboot.  You can then
+issue `cat /proc/bus/usb/devices` to extract USB device information.
+
+
+BUGS
+
+Just look at the list of fixes in the source files.  So, if you
+encounter any problems feel free to drop me an email.
+
+David /\/elson
+dnelson@jump.net
+http://www.jump.net/~dnelson
+
+--------------- snip -- hp_scan.c -- snip ---------------
+/*
+
+This is a really crude attempt at writing a short test program.  It's
+mostly only to be used to test connectivity with USB HP scanners that
+understand SCL.  Currently, the supported models are 4100C, 5200C,
+6200C, and the 6300C.  Note that the 4200C is *NOT* acceptable.
+
+Copyright (C) David E. Nelson <dnelson@jump.net>, 1999
+
+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.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+   Gray Output produces about a 8945400 byte file.
+   Color Output produces a 26836200 byte file. 
+   
+   To compile: gcc -o hp_scan hp_scan.c
+*/
+
+// #define COLOR /* Undef to scan GrayScale */
+
+int send_cmd(int, const char *, int);
+int read_cmd(int, char *, int);
+
+int
+main(void) {
+
+	ssize_t cnt = 0, total_cnt = 0;
+
+	FILE *fpout;
+
+	int fp;
+	int data_size = 32768;
+
+	char *data;
+
+	static char reset_cmd[] = {'\x1b','E'};
+
+#ifdef COLOR
+	static char data_type_cmd[] = {'\x1b','*','a','5','T'}; /* Color */
+	static char data_width_cmd[] = {'\x1b','*','a','2','4','G'}; /* 24 Bit Color */
+#else
+	static char data_type_cmd[] = {'\x1b','*','a','4','T'}; /* Gray */
+	static char data_width_cmd[] = {'\x1b','*','a','8','G'}; /* 8 Bit Gray */
+#endif
+
+	static char query_cmd[] = {'\x1b', '*', 's', '2', '5', '7', 'E'};
+	static char start_scan_cmd[] = {'\x1b','*','f','0','S'};
+	
+	if(!(data=malloc(data_size))) {
+		perror("malloc failed");
+		exit (1);
+	}
+	
+	if((fp=open("/dev/usbscanner", O_RDWR)) < 0) {
+		perror("Unable to open scanner device");
+		exit (1);
+	}
+
+	if((fpout=fopen("out.dat", "w+")) == NULL) {
+		perror("Unable to open ouput file");
+		exit(1);
+	}
+
+	send_cmd(fp, reset_cmd, sizeof(reset_cmd));
+	send_cmd(fp, data_type_cmd, sizeof(data_type_cmd));
+	send_cmd(fp, data_width_cmd, sizeof(data_width_cmd));
+	send_cmd(fp, start_scan_cmd, sizeof(start_scan_cmd));
+
+	while ((cnt = read(fp, data, data_size)) > 0) {
+		printf("Read: %u\n", cnt); 
+		if(fwrite(data, sizeof(char), cnt, fpout) < 0) {
+			perror("Write to output file failed");
+			exit (1);
+		}
+		total_cnt += cnt;
+	}
+	if (cnt < 0) {
+		perror("Read from scanner failed");
+		exit (1);
+	}
+
+	printf("\nRead %lu bytes.\n", total_cnt);
+
+	send_cmd(fp, reset_cmd, sizeof(reset_cmd));
+
+	close(fp);
+	fclose(fpout);
+	return (0);
+}
+
+int
+send_cmd(int fp, const char * cmd, int length) {
+
+	int result;
+	int x;
+
+	if((result = write(fp, cmd, length)) != length) {
+		printf ("Write warning: %d bytes requested, %d written\n");
+	} else if (result < 0) {
+		perror ("send_cmd failure");
+		exit (1);
+	}
+	return (result);
+}
+	
+int
+read_cmd(int fp, char * response, int length) {
+
+	return read(fp, response, length);
+
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/uhci.txt linux.18p17/Documentation/usb/uhci.txt
--- linux.vanilla/Documentation/usb/uhci.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/uhci.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,165 @@
+Specification and Internals for the New UHCI Driver (Whitepaper...)
+
+	brought to you by
+
+	Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+	Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
+	Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
+ 
+	$Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $
+
+This document and the new uhci sources can be found on
+		http://hotswap.in.tum.de/usb
+
+1. General issues
+
+1.1 Why a new UHCI driver, we already have one?!?
+
+Correct, but its internal structure got more and more mixed up by the (still
+ongoing) efforts to get isochronous transfers (ISO) to work.
+Since there is an increasing need for reliable ISO-transfers (especially 
+for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF), 
+this state was a bit unsatisfying in our opinion, so we've decided (based
+on knowledge and experiences with the old UHCI driver) to start 
+from scratch with a new approach, much simpler but at the same time more 
+powerful.
+It is inspired by the way Win98/Win2000 handles USB requests via URBs,
+but it's definitely 100% free of MS-code and doesn't crash while 
+unplugging an used ISO-device like Win98 ;-)
+Some code for HW setup and root hub management was taken from the 
+original UHCI driver, but heavily modified to fit into the new code.
+The invention of the basic concept, and major coding were completed in two 
+days (and nights) on the 16th and 17th of October 1999, now known as the
+great USB-October-Revolution started by GA, DF, and TS ;-)
+
+Since the concept is in no way UHCI dependent, we hope that it will also be 
+transfered to the OHCI-driver, so both drivers share a common API.
+
+1.2. Advantages and disadvantages
+
++ All USB transfer types work now!
++ Asynchronous operation
++ Simple, but powerful interface (only two calls for start and cancel)
++ Easy migration to the new API, simplified by a compatibility API
++ Simple usage of ISO transfers
++ Automatic linking of requests
++ ISO transfers allow variable length for each frame and striping
++ No CPU dependent and non-portable atomic memory access, no asm()-inlines
++ Tested on x86 and Alpha
+
+- Rewriting for ISO transfers needed
+
+1.3. Is there some compatibility to the old API?
+
+Yes, but only for control, bulk and interrupt transfers. We've implemented 
+some wrapper calls for these transfer types. The usbcore works fine with 
+these wrappers. For ISO there's no compatibility, because the old ISO-API 
+and its semantics were unnecessary complicated in our opinion.
+
+1.4. What's really working?
+
+As said above, CTRL and BULK already work fine even with the wrappers,
+so legacy code wouldn't notice the change.
+Regarding to Thomas, ISO transfers now run stable with USB audio.
+INT transfers (e.g. mouse driver) work fine, too.
+
+1.5. Are there any bugs?
+
+No ;-)
+Hm...
+Well, of course this implementation needs extensive testing on all available
+hardware, but we believe that any fixes shouldn't harm the overall concept.
+
+1.6. What should be done next?
+
+A large part of the request handling seems to be identical for UHCI and 
+OHCI, so it would be a good idea to extract the common parts and have only 
+the HW specific stuff in uhci.c. Furthermore, all other USB device drivers
+should need URBification, if they use isochronous or interrupt transfers.
+One thing missing in the current implementation (and the old UHCI driver) 
+is fair queueing for BULK transfers. Since this would need (in principle) 
+the alteration of already constructed TD chains (to switch from depth to 
+breadth execution), another way has to be found. Maybe some simple 
+heuristics work with the same effect.
+
+---------------------------------------------------------------------------
+
+2. Internal structure and mechanisms
+
+To get quickly familiar with the internal structures, here's a short
+description how the new UHCI driver works. However, the ultimate source of
+truth is only uhci.c!
+
+2.1. Descriptor structure (QHs and TDs)
+
+During initialization, the following skeleton is allocated in init_skel:
+
+	 framespecific           |           common chain     
+
+framelist[]
+[  0 ]-----> TD --> TD -------\
+[  1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL
+  ...        TD --> TD -------/
+[1023]-----> TD --> TD ------/
+	     
+	     ^^     ^^           ^^       ^^          ^^          ^^
+   1024 TDs for   7 TDs for    1 TD for   Start of    Start of    End Chain
+	    ISO  INT (2-128ms) 1ms-INT    CTRL Chain  BULK Chain
+
+For each CTRL or BULK transfer a new QH is allocated and the containing data
+transfers are appended as (vertical) TDs. After building the whole QH with its
+dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or
+before the End Chain QH (for BULK). Since only the QH->next pointers are
+affected, no atomic memory operation is required. The three QHs in the
+common chain are never equipped with TDs!
+
+For ISO or INT, the TD for each frame is simply inserted into the appropriate
+ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered
+among the 1024 frames similar to the old UHCI driver.
+
+For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT,
+every TD (there is only one...) has the IOC-bit set.
+
+Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors
+are double-linked through the .vertical and .horizontal elements in the 
+SW data of the descriptor (using the double-linked list structures and 
+operations), but SW-linking occurs only in closed domains, i.e. for each of
+the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This 
+simplifies all insertions and unlinking operations and avoids costly 
+bus_to_virt()-calls.
+
+2.2. URB structure and linking to QH/TDs
+
+During assembly of the QH and TDs of the requested action, these descriptors
+are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to
+this URB.
+If the assembly was successful and the descriptors were added to the HW chain,
+the corresponding URB is inserted into a global URB list for this controller.
+This list stores all pending URBs.
+
+2.3. Interrupt processing
+
+Since UHCI provides no means to directly detect completed transactions, the
+following is done in each UHCI interrupt (uhci_interrupt()):
+
+For each URB in the pending queue (process_urb()), the ACTIVE-flag of the 
+associated TDs are processed (depending on the transfer type 
+process_{transfer|interrupt|iso}()). If the TDs are not active anymore, 
+they indicate the completion of the transaction and the status is calculated. 
+Inactive QH/TDs are removed from the HW chain (since the host controller
+already removed the TDs from the QH, no atomic access is needed) and 
+eventually the URB is marked as completed (OK or errors) and removed from the 
+pending queue. Then the next linked URB is submitted. After (or immediately 
+before) that, the completion handler is called.
+
+2.4. Unlinking URBs
+
+First, all QH/TDs stored in the URB are unlinked from the HW chain. 
+To ensure that the host controller really left a vertical TD chain, we 
+wait for one frame. After that, the TDs are physically destroyed.
+
+2.5. URB linking and the consequences
+
+Since URBs can be linked and the corresponding submit_urb is called in
+the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be
+interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/usb-help.txt linux.18p17/Documentation/usb/usb-help.txt
--- linux.vanilla/Documentation/usb/usb-help.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/usb-help.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,18 @@
+usb-help.txt
+2000-July-12
+
+For USB help other than the readme files that are located in
+linux/Documentation/usb/*, see the following:
+
+Linux-USB project:  http://www.linux-usb.org
+  mirrors at        http://www.suse.cz/development/linux-usb/
+         and        http://usb.in.tum.de/linux-usb/
+Linux USB Guide:    http://linux-usb.sourceforge.net
+Linux-USB device overview (working devices and drivers):
+                    http://www.qbik.ch/usb/devices/
+    
+The Linux-USB mailing lists are:
+  linux-usb-users@lists.sourceforge.net   for general user help
+  linux-usb-devel@lists.sourceforge.net   for developer discussions
+
+###
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/usb-serial.txt linux.18p17/Documentation/usb/usb-serial.txt
--- linux.vanilla/Documentation/usb/usb-serial.txt	Thu Jan  1 01:00:00 1970
+++ linux.18p17/Documentation/usb/usb-serial.txt	Thu Sep 14 15:35:24 2000
@@ -0,0 +1,183 @@
+INTRODUCTION
+
+  The USB serial driver currently supports a number of different USB to
+  serial converter products, as well as some devices that use a serial
+  interface from userspace to talk to the device.
+
+  See the individual product section below for specific information about
+  the different devices.
+
+
+CONFIGURATION
+
+  Currently the driver can handle up to 256 different serial interfaces at
+  one time. 
+
+  If you are not using devfs:
+    The major number that the driver uses is 188 so to use the driver,
+    create the following nodes:
+	mknod /dev/ttyUSB0 c 188 0
+	mknod /dev/ttyUSB1 c 188 1
+	mknod /dev/ttyUSB2 c 188 2
+	mknod /dev/ttyUSB3 c 188 3
+		.
+		.
+		.
+	mknod /dev/ttyUSB254 c 188 254
+	mknod /dev/ttyUSB255 c 188 255
+
+  If you are using devfs:
+    The devices supported by this driver will show up as
+    /dev/usb/tts/{0,1,...}
+
+  When the device is connected and recognized by the driver, the driver
+  will print to the system log, which node(s) the device has been bound
+  to.
+  
+
+SPECIFIC DEVICES SUPPORTED
+
+
+ConnectTech WhiteHEAT 4 port converter
+
+  ConnectTech has been very forthcoming with information about their
+  device, including providing a unit to test with. This driver will end up
+  being fully supported.
+
+Current status:
+  The device's firmware is downloaded on connection, the new firmware 
+  runs properly and all four ports are successfully recognized and connected.
+  Data can be sent and received through the device on all ports.
+  Hardware flow control needs to be implemented.
+
+
+HandSpring Visor USB docking station
+
+Current status:
+  Only when the Visor tries to connect to the host, does the docking
+  station show up as a valid USB device. When this happens, the device is
+  properly enumerated, assigned a port, and then communication _should_ be
+  possible. The driver cleans up properly when the device is removed, or
+  the connection is canceled on the Visor.
+
+  NOTE:
+    This means that in order to talk to the Visor, the sync button must be
+    pressed BEFORE trying to get any program to communicate to the Visor.
+    This goes against the current documentation for pilot-xfer and other
+    packages, but is the only way that it will work due to the hardware
+    in the Visor.
+  
+  When the device is connected, try talking to it on the second port
+  (this is usually /dev/ttyUSB1 if you do not have any other usb-serial
+  devices in the system.) The system log should tell you which port is
+  the port to use for the HotSync transfer. The "Generic" port can be used
+  for other device communication, such as a PPP link.
+
+  There is a webpage and mailing lists for this portion of the driver at: 
+  http://usbvisor.sourceforge.net/
+
+
+Keyspan PDA Serial Adapter
+
+  Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
+  sold in Macintosh catalogs, comes in a translucent white/green dongle).
+  Fairly simple device. Firmware is homebrew.
+
+Current status:
+ Things that work:
+   basic input/output (tested with 'cu')
+   blocking write when serial line can't keep up
+   changing baud rates (up to 115200)
+   getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
+   sending break (although duration looks suspect)
+ Things that don't:
+   device strings (as logged by kernel) have trailing binary garbage
+   device ID isn't right, might collide with other Keyspan products
+   changing baud rates ought to flush tx/rx to avoid mangled half characters
+ Big Things on the todo list:
+   parity, 7 vs 8 bits per char, 1 or 2 stop bits
+   HW flow control
+   not all of the standard USB descriptors are handled: Get_Status, Set_Feature
+   O_NONBLOCK, select()
+
+
+Keyspan USA-series Serial Adapters
+
+  Single and Dual port adapters - driver uses Keyspan supplied 
+  firmware and is being developed with their support.
+  
+  Driver isn't as far advanced as Keyspan PDA driver mentioned above.
+  
+Current status:
+ Things that work:
+   Firmware upload for USA-18X, USA-28, USA-28X, USA-19 and USA-19W 
+   Simple character I/O fixed at 9600 baud on USA-19 only
+   
+ Things that don't:
+   Everything else. (for now...)
+   
+ Big Things on the todo list:
+   Driver is in infancy, much functionality remains to be added
+
+
+FTDI Single Port Serial Driver
+
+  This is a single port DB-25 serial adapter. More information about this
+  device and the Linux driver can be found at:
+	http://reality.sgi.com/bryder_wellington/ftdi_sio/
+
+
+ZyXEL omni.net lcd plus ISDN TA
+
+  This is an ISDN TA. Please report both successes and troubles to the
+  author at omninet@kroah.com
+
+
+Digi AccelePort Driver
+
+  This driver supports the Digi AccelePort USB 2 and 4 devices, 2 port
+  (plus a parallel port) and 4 port USB serial converters.  The driver
+  does NOT yet support the Digi AccelePort USB 8.
+
+  The driver is generally working, though we still have a few more ioctls
+  to implement and final testing and debugging to do.  The paralled port
+  on the USB 2 is supported as a serial to parallel converter; in other
+  words, it appears as another USB serial port on Linux, even though
+  physically it is really a parallel port.  The Digi Acceleport USB 8
+  is not yet supported.
+
+  Please contact Peter Berger (pberger@brimson.com) or Al Borchers
+  (alborchers@steinerpoint.com) for questions or problems with this
+  driver.
+
+
+Generic Serial driver
+
+  If your device is not one of the above listed devices, compatible with
+  the above models, you can try out the "generic" interface. This
+  interface does not provide any type of control messages sent to the
+  device, and does not support any kind of device flow control. All that
+  is required of your device is that it has at least one bulk in endpoint,
+  or one bulk out endpoint. 
+  
+  To enable the generic driver to recognize your device, build the driver
+  as a module and load it by the following invocation:
+	insmod usb-serial vendor=0x#### product=0x####
+  where the #### is replaced with the hex representation of your device's
+  vendor id and product id.
+
+  This driver has been successfully used to connect to the NetChip USB
+  development board, providing a way to develop USB firmware without
+  having to write a custom driver.
+
+
+CONTACT:
+
+  If anyone has any problems using this driver, with any of the above
+  specified products, 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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/CQcam.txt linux.18p17/Documentation/video4linux/CQcam.txt
--- linux.vanilla/Documentation/video4linux/CQcam.txt	Fri Apr 21 12:45:50 2000
+++ linux.18p17/Documentation/video4linux/CQcam.txt	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/README.cpia linux.18p17/Documentation/video4linux/README.cpia
--- linux.vanilla/Documentation/video4linux/README.cpia	Sun Jun 11 21:44:08 2000
+++ linux.18p17/Documentation/video4linux/README.cpia	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/radiotrack.txt linux.18p17/Documentation/video4linux/radiotrack.txt
--- linux.vanilla/Documentation/video4linux/radiotrack.txt	Fri Apr 21 12:45:50 2000
+++ linux.18p17/Documentation/video4linux/radiotrack.txt	Sun Oct 15 21:39:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/MAINTAINERS linux.18p17/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Sat Sep  9 18:42:32 2000
+++ linux.18p17/MAINTAINERS	Thu Oct 19 01:28:39 2000
@@ -93,6 +93,12 @@
 L:	linux-net@vger.kernel.org
 S:	Maintained
 
+AACRAID ADAPTEC RAID DRIVER
+P:	Brian M. Boerner
+M:	aacraid@ntc.adaptec.com
+L:	To Be Announced
+S:	Maintained
+
 AD1816 SOUND DRIVER
 P:	Thorsten Knabe
 M:	Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de>
@@ -177,6 +183,13 @@
 L:	linux-fbdev@vuser.vu.union.edu
 S:	Maintained
 
+COMPAQ FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA
+P:      Amy Vanzant-Hodge 
+M:      Amy Vanzant-Hodge (fibrechannel@compaq.com)
+L:	compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux
+S:      Supported
+
 COMPUTONE INTELLIPORT MULTIPORT CARD
 P:     Doug McNash
 P:     Michael H. Warfield
@@ -228,9 +241,9 @@
 
 DAMA SLAVE for AX.25
 P:	Joerg Reuter
-M:	jreuter@poboxes.com
-W:	http://poboxes.com/jreuter/
-W:	http://qsl.net/dl1bke/
+M:	jreuter@yaina.de
+W:	http://yaina.de/jreuter
+W:	http://www.qsl.net/dl1bke/
 L:	linux-hams@vger.kernel.org
 S:	Maintained
 
@@ -254,6 +267,12 @@
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
+DEVICE FILESYSTEM
+P:	Richard Gooch
+M:	rgooch@atnf.csiro.au
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 DIGI INTL. EPCA DRIVER
 P:      Daniel Taylor
 M:      support@digi.com
@@ -301,6 +320,13 @@
 W:	ftp.compaq.com/pub/products/drivers/linux
 S:	Supported
 
+COMPAQ SMART CISS RAID DRIVER 
+P:      Charles White
+M:      Charles White <arrays@compaq.com>
+L:      compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux	
+S:     	Supported 
+
 DAC960 RAID DRIVER
 P:	Leonard N. Zubkoff
 M:	Leonard N. Zubkoff <lnz@dandelion.com>
@@ -360,6 +386,12 @@
 W:	http://suburbia.net/~billm/floating-point/emulator/
 S:	Maintained
 
+FRAME DIVERTER
+P:	Benoit Locher
+M:	Benoit.Locher@skf.com
+W:	http://perso.wanadoo.fr/magpie/EtherDivert.html
+S:	Maintained
+
 FRAME RELAY DLCI/FRAD (Sangoma drivers too)
 P:	Mike McLagan
 M:	mike.mclagan@linux.org
@@ -442,7 +474,7 @@
 
 IDE/ATAPI CDROM DRIVER 
 P:	Jens Axboe
-M:	axboe@image.dk
+M:	axboe@suse.de
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
@@ -917,7 +949,7 @@
 
 UNIFORM CDROM DRIVER 
 P:	Jens Axboe
-M:	axboe@image.dk
+M:	axboe@suse.de
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
@@ -992,9 +1024,9 @@
 
 Z8530 DRIVER FOR AX.25
 P:	Joerg Reuter
-M:	jreuter@poboxes.com
-W:	http://poboxes.com/jreuter/
-W:	http://qsl.net/dl1bke/
+M:	jreuter@yaina.de
+W:	http://yaina.de/jreuter
+W:	http://www.qsl.net/dl1bke/
 L:	linux-hams@vger.kernel.org
 S:	Maintained
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Makefile linux.18p17/Makefile
--- linux.vanilla/Makefile	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Makefile	Thu Oct 19 01:10:25 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 17
-EXTRAVERSION =
+SUBLEVEL = 18
+EXTRAVERSION = pre17
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -22,7 +22,15 @@
 
 AS	=$(CROSS_COMPILE)as
 LD	=$(CROSS_COMPILE)ld
-CC	=$(CROSS_COMPILE)cc -D__KERNEL__ -I$(HPATH)
+#
+#	foo-bar-gcc for cross builds
+#	gcc272 for Debian's old compiler for kernels
+#	kgcc for Conectiva and Red Hat 7
+#	otherwise 'cc'
+#
+CC	=$(shell if [ -n "$(CROSS_COMPILE)" ]; then echo $(CROSS_COMPILE)gcc; else \
+	$(CONFIG_SHELL) scripts/kwhich gcc272 2>/dev/null || $(CONFIG_SHELL) scripts/kwhich kgcc 2>/dev/null || echo cc; fi) \
+	-D__KERNEL__ -I$(HPATH)
 CPP	=$(CC) -E
 AR	=$(CROSS_COMPILE)ar
 NM	=$(CROSS_COMPILE)nm
@@ -111,7 +119,7 @@
 FILESYSTEMS	=fs/filesystems.a
 NETWORKS	=net/network.a
 DRIVERS		=drivers/block/block.a \
-		 drivers/char/char.a \
+		 drivers/char/char.o \
 	         drivers/misc/misc.a
 LIBS		=$(TOPDIR)/lib/lib.a
 SUBDIRS		=kernel drivers mm fs net ipc lib
@@ -135,7 +143,7 @@
 endif
 
 ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) drivers/sound/sound.a
+DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o
 endif
 
 ifdef CONFIG_PCI
@@ -191,7 +199,7 @@
 endif
 
 ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/usb/usb.a
+DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o
 endif
 
 ifeq ($(CONFIG_I2O),y)
@@ -348,7 +356,10 @@
 	if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \
 	if [ -f FC4_MODULES   ]; then inst_mod FC4_MODULES   fc4;   fi; \
 	if [ -f IRDA_MODULES  ]; then inst_mod IRDA_MODULES  net;   fi; \
+	if [ -f USB_MODULES   ]; then inst_mod USB_MODULES   usb;   fi; \
+	if [ -f USB_SERIAL_MODULES ]; then inst_mod USB_SERIAL_MODULES usb; fi; \
 	if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES  net;   fi; \
+	if [ -f SKFP_MODULES ]; then inst_mod SKFP_MODULES   net;   fi; \
 	\
 	for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Rules.make linux.18p17/Rules.make
--- linux.vanilla/Rules.make	Sat Sep  9 18:42:32 2000
+++ linux.18p17/Rules.make	Tue Sep  5 22:20:28 2000
@@ -77,7 +77,7 @@
 $(O_TARGET): $(ALL_O)
 	rm -f $@
 ifneq "$(strip $(ALL_O))" ""
-	$(LD) $(EXTRA_LDFLAGS) -r -o $@ $(ALL_O)
+	$(LD) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(ALL_O), $^)
 else
 	$(AR) rcs $@
 endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/config.in linux.18p17/arch/alpha/config.in
--- linux.vanilla/arch/alpha/config.in	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/alpha/config.in	Fri Sep  1 13:51:28 2000
@@ -268,7 +268,7 @@
 endmenu
 
 source drivers/char/Config.in
-
+source drivers/usb/Config.in
 source fs/Config.in
 
 if [ "$CONFIG_VT" = "y" ]; then
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/alpha_ksyms.c linux.18p17/arch/alpha/kernel/alpha_ksyms.c
--- linux.vanilla/arch/alpha/kernel/alpha_ksyms.c	Sun Jun 11 21:44:08 2000
+++ linux.18p17/arch/alpha/kernel/alpha_ksyms.c	Tue Sep 12 13:14:57 2000
@@ -37,6 +37,7 @@
 extern struct hwrpb_struct *hwrpb;
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
+extern spinlock_t rtc_lock;
 
 /* these are C runtime functions with special calling conventions: */
 extern void __divl (void);
@@ -187,6 +188,8 @@
 EXPORT_SYMBOL(local_bh_count);
 EXPORT_SYMBOL(local_irq_count);
 #endif /* __SMP__ */
+
+EXPORT_SYMBOL(rtc_lock);
 
 /*
  * The following are special because they're not called
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/bios32.c linux.18p17/arch/alpha/kernel/bios32.c
--- linux.vanilla/arch/alpha/kernel/bios32.c	Sun Jun 11 21:44:08 2000
+++ linux.18p17/arch/alpha/kernel/bios32.c	Fri Sep 15 12:43:11 2000
@@ -828,6 +828,7 @@
 
 	for (dev = bus->devices; dev; dev = dev->sibling) {
 		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			disable_dev(dev);
 		}
@@ -840,6 +841,7 @@
 
 	for (dev = bus->devices; dev; dev = dev->sibling) {
 		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			layout_dev(dev);
 		}
@@ -1081,6 +1083,7 @@
 	 */
 	for (dev = pci_devices; dev; dev = dev->next) {
 		if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) &&
+		    (dev->class >> 8 != PCI_CLASS_BRIDGE_OTHER) &&
 		    (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA))
 			continue;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/core_mcpcia.c linux.18p17/arch/alpha/kernel/core_mcpcia.c
--- linux.vanilla/arch/alpha/kernel/core_mcpcia.c	Sun Jun 11 21:44:08 2000
+++ linux.18p17/arch/alpha/kernel/core_mcpcia.c	Sat Oct 14 00:46:52 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/core_tsunami.c linux.18p17/arch/alpha/kernel/core_tsunami.c
--- linux.vanilla/arch/alpha/kernel/core_tsunami.c	Fri Apr 21 12:45:44 2000
+++ linux.18p17/arch/alpha/kernel/core_tsunami.c	Sat Oct 14 00:46:52 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/setup.c linux.18p17/arch/alpha/kernel/setup.c
--- linux.vanilla/arch/alpha/kernel/setup.c	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/alpha/kernel/setup.c	Mon Oct  2 16:44:31 2000
@@ -841,7 +841,7 @@
 		       hwrpb->pagesize,
 		       hwrpb->pa_bits,
 		       hwrpb->max_asn,
-		       loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
+		       loops_per_jiffy * HZ / 500000, (loops_per_jiffy * HZ / 5000) % 100,
 		       unaligned[0].count, unaligned[0].pc, unaligned[0].va,
 		       unaligned[1].count, unaligned[1].pc, unaligned[1].va,
 		       platform_string(), nr_processors);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/smp.c linux.18p17/arch/alpha/kernel/smp.c
--- linux.vanilla/arch/alpha/kernel/smp.c	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/alpha/kernel/smp.c	Sat Oct 14 00:46:52 2000
@@ -94,7 +94,7 @@
 static inline void __init
 smp_store_cpu_info(int cpuid)
 {
-	cpu_data[cpuid].loops_per_sec = loops_per_sec;
+	cpu_data[cpuid].loops_per_jiffy = loops_per_jiffy;
 	cpu_data[cpuid].last_asn
 	  = (cpuid << WIDTH_HARDWARE_ASN) + ASN_FIRST_VERSION;
 
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/time.c linux.18p17/arch/alpha/kernel/time.c
--- linux.vanilla/arch/alpha/kernel/time.c	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/alpha/kernel/time.c	Tue Sep 12 13:14:57 2000
@@ -47,6 +47,7 @@
 
 static int set_rtc_mmss(unsigned long);
 
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  * Shift amount by which scaled_ticks_per_cycle is scaled.  Shifting
@@ -455,6 +456,8 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 
+	/* irq are locally disabled here */
+	spin_lock(&rtc_lock);
 	/* Tell the clock it's being set */
 	save_control = CMOS_READ(RTC_CONTROL);
 	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
@@ -504,6 +507,7 @@
 	 */
 	CMOS_WRITE(save_control, RTC_CONTROL);
 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+	spin_unlock(&rtc_lock);
 
 	return retval;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/lib/csum_partial_copy.c linux.18p17/arch/alpha/lib/csum_partial_copy.c
--- linux.vanilla/arch/alpha/lib/csum_partial_copy.c	Fri Apr 21 12:45:44 2000
+++ linux.18p17/arch/alpha/lib/csum_partial_copy.c	Sat Sep  2 23:17:03 2000
@@ -173,11 +173,11 @@
 {
 	unsigned long carry = 0;
 	unsigned long word;
+	unsigned long second_dest;
 	int err = 0;
 
 	mskql(partial_dest, doff, partial_dest);
 	while (len >= 0) {
-		unsigned long second_dest;
 		err |= __get_user(word, src);
 		len -= 8;
 		insql(word, doff, second_dest);
@@ -189,35 +189,30 @@
 		carry = checksum < word;
 		dst++;
 	}
-	len += doff;
-	checksum += carry;
-	if (len >= 0) {
-		unsigned long second_dest;
+	len += 8;
+	if (len) {
+		checksum += carry;
 		err |= __get_user(word, src);
-		mskql(word, len-doff, word);
+		mskql(word, len, word);
+		len -= 8;
 		checksum += word;
 		insql(word, doff, second_dest);
-		stq_u(partial_dest | second_dest, dst);
+		len += doff;
 		carry = checksum < word;
-		if (len) {
-			ldq_u(second_dest, dst+1);
+		partial_dest |= second_dest;
+		if (len >= 0) {
+			stq_u(partial_dest, dst);
+			if (!len) goto out;
+			dst++;
 			insqh(word, doff, partial_dest);
-			mskqh(second_dest, len, second_dest);
-			stq_u(partial_dest | second_dest, dst+1);
 		}
-		checksum += carry;
-	} else if (len & 7) {
-		unsigned long second_dest;
-		err |= __get_user(word, src);
-		ldq_u(second_dest, dst);
-		mskql(word, len-doff, word);
-		checksum += word;
-		mskqh(second_dest, len, second_dest);
-		carry = checksum < word;
-		insql(word, doff, word);
-		stq_u(partial_dest | word | second_dest, dst);
-		checksum += carry;
+		doff = len;
 	}
+	ldq_u(second_dest, dst);
+	mskqh(second_dest, doff, second_dest);
+	stq_u(partial_dest | second_dest, dst);
+out:
+	checksum += carry;
 	if (err) *errp = err;
 	return checksum;
 }
@@ -283,7 +278,7 @@
 			stq_u(partial_dest | second_dest, dst+1);
 		}
 		checksum += carry;
-	} else if (len & 7) {
+	} else {
 		unsigned long second, word;
 		unsigned long second_dest;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/math-emu/Makefile linux.18p17/arch/alpha/math-emu/Makefile
--- linux.vanilla/arch/alpha/math-emu/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/alpha/math-emu/Makefile	Sun Oct  1 19:12:21 2000
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := math-emu.o
-O_OBJS   := math.o
+O_OBJS   := math.o qrnnd.o
 CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w
 
 ifeq ($(CONFIG_MATHEMU),m)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/math-emu/math.c linux.18p17/arch/alpha/math-emu/math.c
--- linux.vanilla/arch/alpha/math-emu/math.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/alpha/math-emu/math.c	Sun Oct  1 19:12:21 2000
@@ -84,66 +84,6 @@
 
 #endif /* MODULE */
 
-/* For 128-bit division.  */
-
-void
-udiv128(unsigned long divisor_f0, unsigned long divisor_f1,
-	unsigned long dividend_f0, unsigned long dividend_f1,
-	unsigned long *quot, unsigned long *remd)
-{
-	_FP_FRAC_DECL_2(quo);
-	_FP_FRAC_DECL_2(rem);
-	_FP_FRAC_DECL_2(tmp);
-	unsigned long i, num_bits, bit;
-
-	_FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2);
-	_FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2);
-
-	if (_FP_FRAC_ZEROP_2(divisor))
-		goto out;
-
-	if (_FP_FRAC_GT_2(divisor, dividend)) {
-		_FP_FRAC_COPY_2(rem, dividend);
-		goto out;
-	}
-
-	if (_FP_FRAC_EQ_2(divisor, dividend)) {
-		__FP_FRAC_SET_2(quo, 0, 1);
-		goto out;
-	}
-
-	num_bits = 128;
-	while (1) {
-		bit = _FP_FRAC_NEGP_2(dividend);
-		_FP_FRAC_COPY_2(tmp, rem);
-		_FP_FRAC_SLL_2(tmp, 1);
-		_FP_FRAC_LOW_2(tmp) |= bit;
-		if (! _FP_FRAC_GE_2(tmp, divisor))
-			break;
-		_FP_FRAC_COPY_2(rem, tmp);
-		_FP_FRAC_SLL_2(dividend, 1);
-		num_bits--;
-	}
-
-	for (i = 0; i < num_bits; i++) {
-		bit = _FP_FRAC_NEGP_2(dividend);
-		_FP_FRAC_SLL_2(rem, 1);
-		_FP_FRAC_LOW_2(rem) |= bit;
-		_FP_FRAC_SUB_2(tmp, rem, divisor);
-		bit = _FP_FRAC_NEGP_2(tmp);
-		_FP_FRAC_SLL_2(dividend, 1);
-		_FP_FRAC_SLL_2(quo, 1);
-		if (!bit) {
-			_FP_FRAC_LOW_2(quo) |= 1;
-			_FP_FRAC_COPY_2(rem, tmp);
-		}
-	}
-
-out:
-	*quot = quo_f1;
-	*remd = rem_f1;
-	return;
-}
 
 /*
  * Emulate the floating point instruction at address PC.  Returns 0 if
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/math-emu/qrnnd.S linux.18p17/arch/alpha/math-emu/qrnnd.S
--- linux.vanilla/arch/alpha/math-emu/qrnnd.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/alpha/math-emu/qrnnd.S	Sun Oct  1 19:12:21 2000
@@ -0,0 +1,163 @@
+ # Alpha 21064 __udiv_qrnnd
+ # Copyright (C) 1992, 1994, 1995, 2000 Free Software Foundation, Inc.
+
+ # This file is part of GCC.
+
+ # The GNU MP Library 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.
+
+ # In addition to the permissions in the GNU General Public License, the
+ # Free Software Foundation gives you unlimited permission to link the
+ # compiled version of this file with other programs, and to distribute
+ # those programs without any restriction coming from the use of this
+ # file.  (The General Public License restrictions do apply in other
+ # respects; for example, they cover modification of the file, and
+ # distribution when not linked into another program.)
+
+ # This file is distributed in the hope that it will be useful, but
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+ # License for more details.
+
+ # You should have received a copy of the GNU General Public License
+ # along with GCC; see the file COPYING.  If not, write to the 
+ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ # MA 02111-1307, USA.
+
+        .set noreorder
+        .set noat
+
+	.text
+
+	.globl __udiv_qrnnd
+	.ent __udiv_qrnnd
+__udiv_qrnnd:
+	.frame $30,0,$26,0
+	.prologue 0
+
+#define cnt	$2
+#define tmp	$3
+#define rem_ptr	$16
+#define n1	$17
+#define n0	$18
+#define d	$19
+#define qb	$20
+#define AT	$at
+
+	ldiq	cnt,16
+	blt	d,$largedivisor
+
+$loop1:	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	d,n1,qb
+	subq	n1,d,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	d,n1,qb
+	subq	n1,d,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	d,n1,qb
+	subq	n1,d,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	d,n1,qb
+	subq	n1,d,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	subq	cnt,1,cnt
+	bgt	cnt,$loop1
+	stq	n1,0(rem_ptr)
+	bis	$31,n0,$0
+	ret	$31,($26),1
+
+$largedivisor:
+	and	n0,1,$4
+
+	srl	n0,1,n0
+	sll	n1,63,tmp
+	or	tmp,n0,n0
+	srl	n1,1,n1
+
+	and	d,1,$6
+	srl	d,1,$5
+	addq	$5,$6,$5
+
+$loop2:	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	$5,n1,qb
+	subq	n1,$5,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	$5,n1,qb
+	subq	n1,$5,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	$5,n1,qb
+	subq	n1,$5,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	cmplt	n0,0,tmp
+	addq	n1,n1,n1
+	bis	n1,tmp,n1
+	addq	n0,n0,n0
+	cmpule	$5,n1,qb
+	subq	n1,$5,tmp
+	cmovne	qb,tmp,n1
+	bis	n0,qb,n0
+	subq	cnt,1,cnt
+	bgt	cnt,$loop2
+
+	addq	n1,n1,n1
+	addq	$4,n1,n1
+	bne	$6,$Odd
+	stq	n1,0(rem_ptr)
+	bis	$31,n0,$0
+	ret	$31,($26),1
+
+$Odd:
+	/* q' in n0. r' in n1 */
+	addq	n1,n0,n1
+
+	cmpult	n1,n0,tmp	# tmp := carry from addq
+	subq	n1,d,AT
+	addq	n0,tmp,n0
+	cmovne	tmp,AT,n1
+
+	cmpult	n1,d,tmp
+	addq	n0,1,AT
+	cmoveq	tmp,AT,n0
+	subq	n1,d,AT
+	cmoveq	tmp,AT,n1
+
+	stq	n1,0(rem_ptr)
+	bis	$31,n0,$0
+	ret	$31,($26),1
+
+	.end	__udiv_qrnnd
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/math-emu/sfp-util.h linux.18p17/arch/alpha/math-emu/sfp-util.h
--- linux.vanilla/arch/alpha/math-emu/sfp-util.h	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/alpha/math-emu/sfp-util.h	Sun Oct  1 19:12:21 2000
@@ -17,18 +17,13 @@
 	   : "r" ((UDItype)(u)),		\
 	     "r" ((UDItype)(v)))
 
-extern void udiv128(unsigned long, unsigned long,
-		    unsigned long, unsigned long,
-		    unsigned long *,
-		    unsigned long *);
-
-#define udiv_qrnnd(q, r, n1, n0, d)		\
-  do {						\
-    unsigned long xr, xi;			\
-    udiv128((n0), (n1), 0, (d), &xr, &xi);	\
-    (q) = xr; 					\
-    (r) = xi;					\
+#define udiv_qrnnd(q, r, n1, n0, d)				\
+  do { unsigned long __r;					\
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));			\
+    (r) = __r;							\
   } while (0)
+extern unsigned long __udiv_qrnnd (unsigned long *, unsigned long,
+				   unsigned long , unsigned long);
 
 #define UDIV_NEEDS_NORMALIZATION 1  
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/vmlinux.lds linux.18p17/arch/alpha/vmlinux.lds
--- linux.vanilla/arch/alpha/vmlinux.lds	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/alpha/vmlinux.lds	Fri Sep 15 12:46:07 2000
@@ -27,11 +27,22 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+
+  . = ALIGN(8);
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
+
   . = ALIGN(2*8192);	/* Align double page for init_task_union */
   __init_end = .;
 
   /* The initial task and kernel stack */
   init_task : { *(init_task) }
+
 
   /* Global data */
   _data = .;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/Makefile linux.18p17/arch/arm/Makefile
--- linux.vanilla/arch/arm/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/Makefile	Fri Sep 15 23:28:37 2000
@@ -10,217 +10,172 @@
 # License.  See the file "COPYING" in the main directory of this archive
 # for more details.
 #
-# Copyright (C) 1995, 1996 by Russell King
+# Copyright (C) 1995-1999 by Russell King
 
-CFLAGS_PROC		:=
-ASFLAGS_PROC		:=
+LD		:= $(CROSS_COMPILE)ld
+OBJCOPY		:= $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
+OBJDUMP		 = $(CROSS_COMPILE)objdump
+CPP		:= $(CC) -E
+PERL		:= perl
+LINKFLAGS	:= -p -X -T arch/arm/vmlinux.lds
+ARCHCC		:= $(word 1,$(CC))
+
+AFLAGS		+= -mno-fpu
+CFLAGS_PIPE	:= -pipe
+CFLAGS		:= $(CFLAGS) $(CFLAGS_PIPE)
+
+ifdef CONFIG_FRAME_POINTER
+CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=)
+endif
+
+ifdef CONFIG_DEBUG_INFO
+CFLAGS		+= -g
+endif
 
-# All processors get `-mshort-load-bytes' for now, to work around alignment
-# problems.  This is more of a hack that just happens to work than a real fix
-# but it will do for now.
+# Ensure this is ld "2.9.4" or later
+NEW_LINKER	:= $(shell if $(LD) --gc-sections --version >/dev/null 2>&1; then echo y; else echo n; fi)
+
+ifneq ($(NEW_LINKER),y)
+dummy:;		@echo '*** 2.2 kernels no longer build correctly with old versions of binutils.'
+		@echo '*** Please upgrade your binutils to 2.9.5.'
+		@false
+endif
+
+
+# GCC 2.7 uses different options to later compilers; sort out which we have
+NEW_GCC		:= $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi)
+
+#
+# select flags depending on the compiler
+#
+ifeq ($(NEW_GCC),y)
+CFLAGS			+= -mshort-load-bytes -msoft-float
+CFLAGS_PROC_CPU_26	:= -mcpu=arm3 -Os
+CFLAGS_PROC_CPU_32v3	:= -march=armv3
+CFLAGS_PROC_CPU_32v4	:= -march=armv4
+CFLAGS_ARM6		:= -mtune=arm6
+CFLAGS_ARM7		:= -mtune=arm7
+CFLAGS_SA110		:= -mtune=strongarm110
+else
+CFLAGS_PROC_CPU_26	:= -m3
+CFLAGS_PROC_CPU_32v3	:=
+CFLAGS_PROC_CPU_32v4	:=
+CFLAGS_ARM6		:= -m6
+CFLAGS_ARM7		:= -m6
+CFLAGS_SA110		:= -m6
+endif
 
+#
+# Select CPU dependent flags
+#
 ifeq ($(CONFIG_CPU_26),y)
-  PROCESSOR		 = armo
-  TEXTADDR		 = 0x02080000
-  ZTEXTADDR		 = 0x01800000
-  ZRELADDR		 = 0x02080000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
-    CFLAGS_PROC		+= -mapcs-26 -mshort-load-bytes
-    ifeq ($(CONFIG_CPU_ARM2),y)
-      CFLAGS_PROC	+= -mcpu=arm2
-      ASFLAGS_PROC	+= -m2
-    endif
-    ifeq ($(CONFIG_CPU_ARM3),y)
-      CFLAGS_PROC	+= -mcpu=arm3
-      ASFLAGS_PROC	+= -m3
-    endif
-  else
-    ifeq ($(CONFIG_CPU_ARM2),y)
-      CFLAGS_PROC	+= -m2
-      ASFLAGS_PROC	+= -m2
-    endif
-    ifeq ($(CONFIG_CPU_ARM3),y)
-      CFLAGS_PROC	+= -m3
-      ASFLAGS_PROC	+= -m3
-    endif
-  endif
+ PROCESSOR	 = armo
+ TEXTADDR	 = 0x02080000
+ CFLAGS		+= $(CFLAGS_PROC_CPU_26)
+ AFLAGS		+= -mapcs-26
 endif
 
 ifeq ($(CONFIG_CPU_32),y)
-  PROCESSOR		 = armv
-  TEXTADDR		 = 0xC0008000
-  ifeq ($(CONFIG_BINUTILS_NEW),y)
-    CFLAGS_PROC		+= -mapcs-32 -mshort-load-bytes
-    ifeq ($(CONFIG_CPU_ARM6),y)
-      CFLAGS_PROC	+= -mcpu=arm6
-    endif
-    ifeq ($(CONFIG_CPU_ARM7),y)
-      CFLAGS_PROC	+= -mcpu=arm7
-    endif
-    ifeq ($(CONFIG_CPU_SA110),y)
-      CFLAGS_PROC	+= -mcpu=strongarm110
-    endif
-  else
-    CFLAGS_PROC		+= -m6
-  endif
-  ASFLAGS_PROC		+= -m6
-endif
-
-# Processor Architecture
-# CFLAGS_PROC - processor dependent CFLAGS
-# PROCESSOR - processor type
-# TEXTADDR - Uncompressed kernel link text address
-# ZTEXTADDR - Compressed kernel link text address
-# ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded).
-#
+ PROCESSOR	 = armv
+ TEXTADDR	 = 0xC0008000
+ ifeq ($(CONFIG_CPU_32v4),y)
+  CFLAGS	+= $(CFLAGS_PROC_CPU_32v4)
+  AFLAGS	+= -mapcs-32 -marmv4
+ else
+  CFLAGS	+= $(CFLAGS_PROC_CPU_32v3)
+  AFLAGS	+= -mapcs-32 -marmv3m
+ endif
+ #
+ # Exactly one of the following must be selected
+ #
+ ifeq ($(CONFIG_CPU_ARM6),y)
+  CFLAGS	+= $(CFLAGS_ARM6)
+ else
+ ifeq ($(CONFIG_CPU_ARM7),y)
+  CFLAGS	+= $(CFLAGS_ARM7)
+ else
+ ifeq ($(CONFIG_CPU_SA110),y)
+  CFLAGS	+= $(CFLAGS_SA110)
+ endif
+ endif
+ endif
+endif
 
-COMPRESSED_HEAD	 = head.o
+GCCLIB		:= $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
 
 ifeq ($(CONFIG_ARCH_A5K),y)
 MACHINE		 = a5k
 ARCHDIR		 = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
 endif
 
 ifeq ($(CONFIG_ARCH_ARC),y)
 MACHINE		 = arc
 ARCHDIR		 = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
 endif
 
 ifeq ($(CONFIG_ARCH_RPC),y)
 MACHINE		 = rpc
 ARCHDIR		 = rpc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
-ZTEXTADDR	 = 0x10008000
-ZRELADDR	 = 0x10008000
 endif
 
 ifeq ($(CONFIG_ARCH_EBSA110),y)
 MACHINE		 = ebsa110
 ARCHDIR		 = ebsa110
-ZTEXTADDR	 = 0x00008000
-ZRELADDR	 = 0x00008000
 endif
 
-ifeq ($(CONFIG_ARCH_EBSA285),y)
-MACHINE		 = ebsa285
+ifeq ($(CONFIG_FOOTBRIDGE),y)
+MACHINE		 = footbridge
 ARCHDIR		 = ebsa285
-ZTEXTADDR	 = 0x00008000
-ZRELADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_ARCH_CO285),y)
+TEXTADDR	 = 0x60008000
 endif
 
 ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
 MACHINE		 = nexuspci
 ARCHDIR		 = nexuspci
-ZTEXTADDR	 = 0x40200000
-ZRELADDR	 = 0x40008000
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
-COMPRESSED_HEAD	 = head-nexuspci.o
-endif
-
-ifeq ($(CONFIG_ARCH_VNC),y)
-TEXTADDR	 = 0xC000C000
-MACHINE		 = vnc
-ARCHDIR		 = vnc
-endif
-
-ifeq ($(CONFIG_ARCH_TBOX),y)
-MACHINE		 = tbox
-ARCHDIR		 = tbox
-ZTEXTADDR	 = 0x80008000
-ZRELDIR		 = 0x80008000
 endif
 
-PERL		 = perl
-ifeq ($(CONFIG_BINUTILS_NEW),y)
-LD		 = $(CROSS_COMPILE)ld -m elf32arm
-else
-LD		 = $(CROSS_COMPILE)ld -m elf_arm
-endif
-OBJCOPY		 = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
-OBJDUMP		 = $(CROSS_COMPILE)objdump
-CPP		 = $(CC) -E
-ARCHCC		:= $(word 1,$(CC))
-GCCLIB		:= `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name`
-#GCCARCH		:= -B/usr/bin/arm-linuxelf- 
-HOSTCFLAGS	:= $(CFLAGS:-fomit-frame-pointer=)
-ifeq ($(CONFIG_FRAME_POINTER),y)
-CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=)
-endif
-CFLAGS		:= $(CFLAGS_PROC) $(CFLAGS) -pipe
-ASFLAGS		:= $(ASFLAGS_PROC) $(ASFLAGS)
-LINKFLAGS	 = -T $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds -e stext -Ttext $(TEXTADDR)
-ZLINKFLAGS	 = -Ttext $(ZTEXTADDR)
-
-SUBDIRS		:= $(SUBDIRS:drivers=arch/arm/drivers) arch/arm/lib arch/arm/kernel arch/arm/mm
-HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
+HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o \
+		   arch/arm/kernel/init_task.o
+SUBDIRS		+= arch/arm/kernel arch/arm/mm arch/arm/lib \
+		   arch/arm/special arch/arm/nwfpe
 CORE_FILES	:= arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
-LIBS		:= arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
+LIBS		:= arch/arm/lib/lib.o arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
+DRIVERS		+= arch/arm/special/special.a
 
-BLOCK_DRIVERS	:= drivers/block/block.a
-CDROM_DRIVERS	:= drivers/cdrom/cdrom.a
-ifeq ($(CONFIG_FB),y)
-CHAR_DRIVERS	:= arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-CHAR_DRIVERS	:= arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-CHAR_DRIVERS	:= arch/arm/drivers/char/char.a
+ifeq ($(CONFIG_NWFPE),y)
+LIBS		:= arch/arm/nwfpe/math-emu.a $(LIBS)
 endif
-endif
-MISC_DRIVERS	:= drivers/misc/misc.a
-NET_DRIVERS	:= drivers/net/net.a
-PARIDE_DRIVERS	:= drivers/block/paride/paride.a
-PCI_DRIVERS	:= drivers/pci/pci.a
-SCSI_DRIVERS	:= drivers/scsi/scsi.a
-SOUND_DRIVERS	:= drivers/sound/sound.a
-VIDEO_DRIVERS	:= drivers/video/video.a
-PNP_DRIVERS	:= drivers/pnp/pnp.a
 
 ifeq ($(CONFIG_ARCH_ACORN),y)
-BLOCK_DRIVERS	+= drivers/acorn/block/acorn-block.a
-CHAR_DRIVERS	+= drivers/acorn/char/acorn-char.a
-NET_DRIVERS	+= drivers/acorn/net/acorn-net.a drivers/net/net.a
-SCSI_DRIVERS	+= drivers/acorn/scsi/acorn-scsi.a
+SUBDIRS		+= drivers/acorn
+DRIVERS		+= drivers/acorn/block/acorn-block.a
+DRIVERS		+= drivers/acorn/char/acorn-char.a
+DRIVERS		+= drivers/acorn/net/acorn-net.a
+DRIVERS		+= drivers/acorn/scsi/acorn-scsi.a
 endif
 
-DRIVERS		:= $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS)
+MAKEBOOT	 = $(MAKE) -C arch/$(ARCH)/boot
 
-ifeq ($(CONFIG_FB),y)
-DRIVERS		:= $(DRIVERS) $(VIDEO_DRIVERS)
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-DRIVERS		:= $(DRIVERS) $(VIDEO_DRIVERS)
-endif
-endif
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS		:= $(DRIVERS) $(SCSI_DRIVERS)
-endif
-ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
-DRIVERS		:= $(DRIVERS) $(CDROM_DRIVERS)
-endif
-ifdef CONFIG_PCI
-DRIVERS		:= $(DRIVERS) $(PCI_DRIVERS)
-endif
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS		:= $(DRIVERS) $(SOUND_DRIVERS)
-endif
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS		:= $(DRIVERS) $(PARIDE_DRIVERS)
-endif
-ifdef CONFIG_PNP
-DRIVERS		:= $(DRIVERS) $(PNP_DRIVERS)
-endif
+# The following is a hack to get 'constants.h' up
+# to date before starting compilation
+CONSTANTS	:= constants
+
+constants: $(TOPDIR)/include/asm-arm/proc-fns.h dummy
+	@$(MAKE) -C arch/arm/lib constants.h
+
+symlinks: archsymlinks
 
-symlinks::
+archsymlinks:
 	$(RM) include/asm-arm/arch include/asm-arm/proc
 	(cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc)
 
-# Once we've finished integrating the sources, the @$(MAKE) will disappear
-archmrproper:
-	rm -f include/asm-arm/arch include/asm-arm/proc
-	@$(MAKE) -C arch/$(ARCH)/drivers mrproper
+vmlinux: arch/arm/vmlinux.lds
+
+arch/arm/vmlinux.lds: arch/arm/vmlinux-$(PROCESSOR).lds.in dummy
+	@sed 's/TEXTADDR/$(TEXTADDR)/' <$< >$@
 
 arch/arm/kernel: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
@@ -231,19 +186,19 @@
 arch/arm/lib: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/lib
 
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+zImage zinstall Image install: vmlinux
+	@$(MAKEBOOT) $@
 
-zImage: vmlinux
-	@$(MAKEBOOT) zImage
-
-zinstall: vmlinux
-	@$(MAKEBOOT) zinstall
+archmrproper:
+	@$(MAKE) -C arch/$(ARCH)/special mrproper
+	$(RM) include/asm-arm/arch include/asm-arm/proc
 
-Image: vmlinux
-	@$(MAKEBOOT) Image
+archclean:
+	@$(MAKEBOOT) clean
+	$(RM) arch/arm/lib/constants.h arch/arm/vmlinux.lds
 
-install: vmlinux
-	@$(MAKEBOOT) install
+archdep:
+	@$(MAKEBOOT) dep
 
 # My testing targets (that short circuit a few dependencies)
 zImg:;	@$(MAKEBOOT) zImage
@@ -251,10 +206,23 @@
 i:;	@$(MAKEBOOT) install
 zi:;	@$(MAKEBOOT) zinstall
 
-archclean:
-	@$(MAKEBOOT) clean
-	$(RM) arch/arm/lib/constants.h
+#
+# Configuration targets.  Use these to select a
+# configuration for your architecture
+#
+a5k_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/a5k arch/arm/defconfig
+
+ebsa110_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/ebsa110 arch/arm/defconfig
+
+footbridge_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/footbridge arch/arm/defconfig
+
+rpc_config:
+	$(RM) arch/arm/defconfig
+	cp arch/arm/def-configs/rpc arch/arm/defconfig
 
-archdep:
-	@$(MAKEBOOT) dep
-sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/Makefile linux.18p17/arch/arm/boot/Makefile
--- linux.vanilla/arch/arm/boot/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/boot/Makefile	Mon Oct  2 10:12:51 2000
@@ -20,10 +20,10 @@
 	@$(MAKE) -C compressed vmlinux
 
 install: $(CONFIGURE) Image
-	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 zinstall: $(CONFIGURE) zImage
-	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 clean:
 	rm -f Image zImage
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/Makefile linux.18p17/arch/arm/boot/compressed/Makefile
--- linux.vanilla/arch/arm/boot/compressed/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/boot/compressed/Makefile	Fri Sep 15 23:28:37 2000
@@ -2,32 +2,97 @@
 # linux/arch/arm/boot/compressed/Makefile
 #
 # create a compressed vmlinuz image from the original vmlinux
+
+HEAD		 = head.o
+OBJS		 = misc.o
+SYSTEM		 = $(TOPDIR)/vmlinux
+CFLAGS		 = -O2 -DSTDC_HEADERS $(CFLAGS_PROC)
+FONTC		 = $(TOPDIR)/drivers/video/font_acorn_8x8.c
+ZLDFLAGS	 = -p -X -T vmlinux.lds
+
 #
-# With this config, max compressed image size = 640k
-# Uncompressed image size = 1.3M (text+data)
+# Architecture dependencies
+#
+ifeq ($(CONFIG_ARCH_ACORN),y)
+OBJS		+= ll_char_wr.o font.o
+endif
+
+ifeq ($(CONFIG_CPU_26),y)
+ZTEXTADDR	 = 0x02080000
+endif
+
+ifeq ($(CONFIG_ARCH_RPC),y)
+ZTEXTADDR	 = 0x10008000
+endif
+
+ifeq ($(CONFIG_ARCH_EBSA110),y)
+ZTEXTADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_FOOTBRIDGE),y)
+ZTEXTADDR	 = 0x00008000
+endif
+
+ifeq ($(CONFIG_ARCH_NETWINDER),y)
+OBJS		+= head-netwinder.o
+endif
+
+ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
+HEAD		 = head-nexuspci.o
+OBJS		+= $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
+ZTEXTADDR	 = 0x40200000
+ZRELADDR	 = 0x40008000
+endif
+
+ifeq ($(CONFIG_ARCH_SA1100),y)
+OBJS		+= head-sa1100.o
+ifeq ($(CONFIG_SA1100_VICTOR),y)
+ZTEXTADDR	 = 0x00002000
+ZBSSADDR	 = 0xc0100000
+else
+ZTEXTADDR	 = 0xc0008000
+endif
+ZRELADDR	 = 0xc0008000
+endif
 
-SYSTEM	=$(TOPDIR)/vmlinux
-HEAD	=$(COMPRESSED_HEAD)
-OBJS	=$(HEAD) misc.o $(COMPRESSED_EXTRA)
-CFLAGS	=-O2 -DSTDC_HEADERS $(CFLAGS_PROC)
-ARFLAGS =rc
+#
+# If you don't define ZRELADDR above,
+# then it defaults to ZTEXTADDR
+#
+ifeq ($(ZRELADDR),)
+ZRELADDR	= $(ZTEXTADDR)
+endif
+
+SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;
+
+ifneq ($(ZBSSADDR),)
+SEDFLAGS	+= s/BSS_START/$(ZBSSADDR)/
+else
+SEDFLAGS	+= s/BSS_START/ALIGN(4)/
+endif
 
 all:		vmlinux
 
-vmlinux:	piggy.o $(OBJS)
-		$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o
+vmlinux:	$(HEAD) $(OBJS) piggy.o vmlinux.lds
+		$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(GCCLIB) -o vmlinux
 
 $(HEAD): 	$(HEAD:.o=.S)
-		$(CC) -traditional -DLOADADDR=$(ZRELADDR) -c $(HEAD:.o=.S)
+		$(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S)
 
 piggy.o:	$(SYSTEM)
-		tmppiggy=_tmp_$$$$piggy; \
-		rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \
-		$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
-		gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
-		echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
-		$(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \
-		rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk;
+		$(OBJCOPY) $(SYSTEM) piggy
+		gzip -9 < piggy > piggy.gz
+		$(LD) -r -o $@ -b binary piggy.gz
+		rm -f piggy piggy.gz
+
+font.o:		$(FONTC)
+		$(CC) $(CFLAGS) -Dstatic= -c -o $@ $(FONTC)
+
+vmlinux.lds:	vmlinux.lds.in
+		@sed "$(SEDFLAGS)" < vmlinux.lds.in > $@
+
+clean:;		rm -f vmlinux core piggy* vmlinux.lds
 
-clean:;		rm -f vmlinux core
+.PHONY:	vmlinux.lds clean
 
+misc.o: misc.c $(TOPDIR)/include/asm/arch/uncompress.h $(TOPDIR)/lib/inflate.c
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/head-netwinder.S linux.18p17/arch/arm/boot/compressed/head-netwinder.S
--- linux.vanilla/arch/arm/boot/compressed/head-netwinder.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/boot/compressed/head-netwinder.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,31 @@
+		.section	".start", #alloc, #execinstr
+
+		adr	r2, 1f
+		ldmdb	r2, {r7, r8}
+		and	r3, r2, #0xc000
+		teq	r3, #0x8000
+		beq	2f
+		bic	r3, r2, #0xc000
+		orr	r3, r3, #0x8000
+		mov	r0, r3
+		mov	r4, #64
+		sub	r5, r8, r7
+		b	1f
+
+		.word	_start
+		.word	__bss_start
+
+1:
+		.rept	4
+		ldmia	r2!, {r6, r7, r8, r9}
+		stmia	r3!, {r6, r7, r8, r9}
+		.endr
+		subs	r4, r4, #64
+		bcs	1b
+		movs	r4, r5
+		mov	r5, #0
+		mov	r1, #5		@ only here to fix NeTTroms which dont set r1
+		movne	pc, r0
+
+		mov	r0, #0
+2:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/head-sa1100.S linux.18p17/arch/arm/boot/compressed/head-sa1100.S
--- linux.vanilla/arch/arm/boot/compressed/head-sa1100.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/boot/compressed/head-sa1100.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,65 @@
+/* 
+ * linux/arch/arm/boot/compressed/head-sa1100.S
+ * 
+ * Copyright (C) 1999 Nicolas Pitre <nico@cam.org>
+ * 
+ * SA1100 specific tweaks.  This is merged with head.S by the linker.
+ */
+
+#include <linux/config.h>
+
+
+		.section        ".start", #alloc, #execinstr
+
+#ifndef CONFIG_ARCH_SA1100
+#error What am I doing here...
+#endif
+
+#ifdef CONFIG_SA1100_BRUTUS
+@ need to enter SVC mode
+#define angel_SWIreason_EnterSVC 0x17   /* from arm.h, in angel source */
+#define angel_SWI_ARM (0xEF123456 & 0xffffff)
+		mov	r0, #angel_SWIreason_EnterSVC
+		swi	#angel_SWI_ARM
+
+		@ turn off interrupts to prevent the angel from running
+		mrs	r0, cpsr
+		orr	r0, r0, #0xc0
+		msr	cpsr, r0
+#endif
+
+#ifdef CONFIG_SA1100_VICTOR
+		@ Copy cmdline to 0xc0000000
+		mov	r1, #0xc0000000
+		cmp	r0, #0
+		moveq	r2, #0
+1:		ldrneb	r2, [r0], #1
+		cmpne	r2, #0
+		strb	r2, [r1], #1
+		bne	1b
+#endif
+
+		@ Data cache might be active.
+		@ Be sure to flush kernel binary out of the cache,
+		@ whatever state it is, before it is turned off.
+		@ This is done by fetching through currently executed
+		@ memory to be sure we hit the same cache.
+		bic	r2, pc, #0x1f
+		add	r3, r2, #0x4000		@ 16 kb is quite enough...
+1:		ldr	r0, [r2], #32
+		teq	r2, r3
+		bne	1b
+		mcr	p15, 0, r0, c7, c10, 4	@ drain WB
+		mcr	p15, 0, r0, c7, c7, 0	@ flush I & D caches
+
+		@ disabling MMU, enabling I cache
+		mrc	p15, 0, r0, c1, c0, 0	@ read control reg
+		bic	r0, r0, #0x0d		@ clear WB, DC, MMU
+		orr	r0, r0, #0x1000		@ set Icache
+		mcr	p15, 0, r0, c1, c0, 0
+
+		@ set registers for entry
+		mov	r0, #0
+		mov	r1, #16
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/head.S linux.18p17/arch/arm/boot/compressed/head.S
--- linux.vanilla/arch/arm/boot/compressed/head.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/boot/compressed/head.S	Fri Sep 15 23:28:37 2000
@@ -1,129 +1,253 @@
 /*
  * linux/arch/arm/boot/compressed/head.S
  *
- * Copyright (C) 1996,1997,1998 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
 #include <linux/linkage.h>
 
-		.text
+		.section ".start", #alloc, #execinstr
 /*
  * sort out different calling conventions
  */
 		.align
-		.globl	_start
-_start:
-start:		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
+start:
+		.type	start,#function
+		.rept	8
 		mov	r0, r0
+		.endr
+
 		b	1f
-		.word	0x016f2818			@ Magic numbers to help the loader
-		.word	_start	
+		.word	0x016f2818		@ Magic numbers to help the loader
+		.word	start	
+1:
+
+		/*
+		 * some architecture specific code can
+		 * be inserted by the linker here
+		 */
+
+		.text
 1:		teq	r0, #0
-		beq	2f
-		mov	r4, #0x02000000
-		add	r4, r4, #0x7C000
-		mov	r3, #0x4000
-		sub	r3, r3, #4
-1:		ldmia	r0!, {r5 - r12}
-		stmia	r4!, {r5 - r12}
-		subs	r3, r3, #32
-		bpl	1b
-2:		adr	r2, LC0
-		ldmia	r2, {r2, r3, r4, r5, r6, sp}
-		add	r2, r2, #3
-		add	r3, r3, #3
-		add	sp, sp, #3
-		bic	r2, r2, #3
-		bic	r3, r3, #3
-		bic	sp, sp, #3
-		adr	r7, start
-		sub	r6, r7, r6
-/*
- * Relocate pointers
- */
-		add	r2, r2, r6
-		add	r3, r3, r6
-		add	r5, r5, r6
-		add	sp, sp, r6
-/*
- * Clear zero-init
- */
-		mov	r6, #0
-1:		str	r6, [r2], #4
+		bne	1b
+		mov	r7, r1			@ save architecture ID
+		mrc	p15, 0, r6, c0, c0	@ get processor ID
+		adr	r2, LC0
+		ldmia	r2, {r2, r3, r4, r5, sp}
+
+		mov	r0, #0
+1:		str	r0, [r2], #4		@ clear bss
+		str	r0, [r2], #4
+		str	r0, [r2], #4
+		str	r0, [r2], #4
 		cmp	r2, r3
 		blt	1b
-		str	r1, [r5]		@ save architecture
-/*
- * Uncompress the kernel
- */
-		mov	r1, #0x8000
-		add	r3, r2, r1, lsl #1	@ Add 64k for malloc
-		sub	r1, r1, #1
-		add	r3, r3, r1
-		bic	r5, r3, r1		@ decompress kernel to after end of the compressed
+
+		mov	r1, sp			@ malloc space above stack
+		add	r2, sp, #0x10000	@ 64k max
+
+		teq	r4, r5			@ will we overwrite ourselves?
+		moveq	r5, r2
+		movne	r5, r4
+
 		mov	r0, r5
-		mov	r1, r2
-		mov	r2, r0
+		mov	r3, r7
 		bl	SYMBOL_NAME(decompress_kernel)
-		add	r0, r0, #7
-		bic	r2, r0, #7
+
+		teq	r4, r5			@ do we need to relocate
+		beq	call_kernel		@ the kernel?
+
+		add	r0, r0, #127
+		bic	r0, r0, #127		@ align the kernel length
 /*
- * Now move the kernel to the correct location (r5 -> r4, len r0)
- */
-		mov	r0, r4			@ r0 = start of real kernel
-		mov	r1, r5			@ r1 = start of kernel image
-		add	r3, r5, r2		@ r3 = end of kernel
-		adr	r4, movecode
-		adr	r5, movecodeend
-1:		ldmia	r4!, {r6 - r12, lr}
-		stmia	r3!, {r6 - r12, lr}
-		cmp	r4, r5
+ * r0     = decompressed kernel length
+ * r1-r3  = unused
+ * r4     = kernel execution address
+ * r5     = decompressed kernel start
+ * r6     = processor ID
+ * r7     = architecture ID
+ * r8-r14 = unused
+ */
+		add	r1, r5, r0		@ end of decompressed kernel
+		adr	r2, reloc_start
+		adr	r3, reloc_end
+1:		ldmia	r2!, {r8 - r13}		@ copy relocation code
+		stmia	r1!, {r8 - r13}
+		ldmia	r2!, {r8 - r13}
+		stmia	r1!, {r8 - r13}
+		cmp	r2, r3
 		blt	1b
-		mrc	p15, 0, r5, c0, c0
-		eor	r5, r5, #0x44 << 24
-		eor	r5, r5, #0x01 << 16
-		eor	r5, r5, #0xa1 << 8
-		movs	r5, r5, lsr #4
-		mov	r5, #0
-		mcreq	p15, 0, r5, c7, c5, 0	@ flush I cache
-		ldr	r5, LC0 + 12		@ get architecture
-		ldr	r5, [r5]
-		add	pc, r1, r2		@ Call move code
+
+		eor	r1, r6, #0x44 << 24	@ SA-110?
+		eor	r1, r1, #0x01 << 16
+		eor	r1, r1, #0xa1 << 8
+		movs	r1, r1, lsr #4
+		mcreq	p15, 0, r1, c7, c7, 0	@ flush I & D-cache
+		mcreq	p15, 0, r1, c7, c10, 4	@ drain WB
+		add	pc, r5, r0		@ call relocation code
 
 /*
- * r0 = length, r1 = to, r2 = from
- */
-movecode:	add	r3, r1, r2
-		mov	r4, r0
-1:		ldmia	r1!, {r6 - r12, lr}
-		stmia	r0!, {r6 - r12, lr}
-		cmp	r1, r3
+ * r0     = decompressed kernel length
+ * r1-r3  = unused
+ * r4     = kernel execution address
+ * r5     = decompressed kernel start
+ * r6     = processor ID
+ * r7     = architecture ID
+ * r8-r14 = unused
+ */
+reloc_start:	add	r8, r5, r0
+#if 0
+	mov r0, #'\n'
+	bl putc
+	mov r0, r6
+	mov r1, #8
+	bl phex
+	mov r0, #':'
+	bl putc
+	mov r0, r5
+	mov r1, #8
+	bl phex
+	mov r0, #'-'
+	bl putc
+	mov r0, r8
+	mov r1, #8
+	bl phex
+	mov r0, #'>'
+	bl putc
+	mov r0, r4
+	mov r1, #8
+	bl phex
+	mov r0, #'\n'
+	bl putc
+#endif
+		mov	r0, r8
+		mov	r1, r4
+1:
+		.rept	4
+		ldmia	r5!, {r2, r3, r8 - r13}	@ relocate kernel
+		stmia	r1!, {r2, r3, r8 - r13}
+		.endr
+
+		cmp	r5, r0
 		blt	1b
-		mrc	p15, 0, r0, c0, c0
-		eor	r0, r0, #0x44 << 24
+#if 0
+	mov r8, r0
+	mov r0, r5
+	mov r1, #8
+	bl phex
+	mov r0, #'-'
+	bl putc
+	mov r0, r8
+	mov r1, #8
+	bl phex
+	mov r0, #'\n'
+	bl putc
+	mov r0, r4
+	bl  memdump
+#endif
+		eor	r0, r6, #0x44 << 24	@ SA-110?
 		eor	r0, r0, #0x01 << 16
 		eor	r0, r0, #0xa1 << 8
 		movs	r0, r0, lsr #4
+		mcreq	p15, 0, r0, c7, c7, 0	@ flush I cache
+		mcreq	p15, 0, r1, c7, c10, 4	@ drain WB
+
+call_kernel:	mov	r0, #0
+		mov	r1, r7			@ restore architecture number
+		mov	pc, r4			@ call kernel
+
+phexbuf:	.space	12
+
+#if 0
+		.macro	loadsp,	rb
+		mov	\rb, #0x7c000000
+		.endm
+
+		.macro	writeb,	rb
+		strb	\rb, [r3, #0x3f8]
+		.endm
+#else
+		.macro	loadsp,	rb
+		mov	\rb, #0x03000000
+		orr	\rb, \rb, #0x00010000
+		.endm
+
+		.macro	writeb,	rb
+		strb	\rb, [r3, #0x3f8 << 2]
+		.endm
+#endif
+
+phex:		adr	r3, phexbuf
+		mov	r2, #0
+		strb	r2, [r3, r1]
+1:		subs	r1, r1, #1
+		movmi	r0, r3
+		bmi	puts
+		and	r2, r0, #15
+		mov	r0, r0, lsr #4
+		cmp	r2, #10
+		addge	r2, r2, #7
+		add	r2, r2, #'0'
+		strb	r2, [r3, r1]
+		b	1b
+
+puts:		loadsp	r3
+1:		ldrb	r2, [r0], #1
+		teq	r2, #0
+		moveq	pc, lr
+2:		writeb	r2
+		mov	r1, #0x00020000
+3:		subs	r1, r1, #1
+		bne	3b
+		teq	r2, #'\n'
+		moveq	r2, #'\r'
+		beq	2b
+		teq	r0, #0
+		bne	1b
+		mov	pc, lr
+putc:
+		mov	r2, r0
 		mov	r0, #0
-		mcreq	p15, 0, r0, c7, c5, 0	@ flush I cache
-		mov	r1, r5			@ call kernel correctly
-		mov	pc, r4			@ call via EXEC entry
-movecodeend:
-
-LC0:		.word	SYMBOL_NAME(_edata)
-		.word	SYMBOL_NAME(_end)
-		.word	LOADADDR
-		.word	SYMBOL_NAME(architecture)
-		.word	start
-		.word	SYMBOL_NAME(user_stack)+4096
-		.align
+		loadsp	r3
+		b	2b
 
-		.bss
-SYMBOL_NAME(architecture):
-		.space	4
+memdump:	mov	r12, r0
+		mov	r10, lr
+		mov	r1, #8
+		bl	phex
+		mov	r0, #'\n'
+		bl	putc
+		mov	r11, #0
+2:		mov	r0, r11, lsl #2
+		mov	r1, #4
+		bl	phex
+		mov	r0, #':'
+		bl	putc
+1:		mov	r0, #' '
+		bl	putc
+		ldr	r0, [r12, r11, lsl #2]
+		mov	r1, #8
+		bl	phex
+		and	r0, r11, #7
+		teq	r0, #3
+		moveq	r0, #' '
+		bleq	putc
+		and	r0, r11, #7
+		add	r11, r11, #1
+		teq	r0, #7
+		bne	1b
+		mov	r0, #'\n'
+		bl	putc
+		cmp	r11, #64
+		blt	2b
+		mov	pc, r10
+reloc_end:
+
+LC0:		.word	__bss_start
+		.word	_end
+		.word	_load_addr
+		.word	_start
+		.word	user_stack+4096
 		.align
+
+		.section	".stack"
+user_stack:	.space	4096
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/ll_char_wr.S linux.18p17/arch/arm/boot/compressed/ll_char_wr.S
--- linux.vanilla/arch/arm/boot/compressed/ll_char_wr.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/boot/compressed/ll_char_wr.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,158 @@
+/*
+ * linux/arch/arm/lib/ll_char_wr.S
+ *
+ * Copyright (C) 1995, 1996 Russell King.
+ *
+ * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
+ *
+ * 10-04-96	RMK	Various cleanups & reduced register usage.
+ * 08-04-98	RMK	Shifts re-ordered
+ */
+
+@ Regs: [] = corruptible
+@       {} = used
+@       () = do not use
+#define __ASSEMBLY__
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+		.text
+
+#define BOLD            0x01
+#define ITALIC          0x02
+#define UNDERLINE       0x04
+#define FLASH           0x08
+#define INVERSE         0x10
+
+LC0:		.word	SYMBOL_NAME(bytes_per_char_h)
+		.word	SYMBOL_NAME(video_size_row)
+		.word	SYMBOL_NAME(acorndata_8x8)
+		.word	SYMBOL_NAME(con_charconvtable)
+
+ENTRY(ll_write_char)
+		stmfd	sp!, {r4 - r7, lr}
+@
+@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+		eor	ip, r1, #UNDERLINE << 9
+/*
+ * calculate colours
+ */
+		tst	r1, #INVERSE << 9
+		moveq	r2, r1, lsr #16
+		moveq	r3, r1, lsr #24
+		movne	r2, r1, lsr #24
+		movne	r3, r1, lsr #16
+		and	r3, r3, #255
+		and	r2, r2, #255
+/*
+ * calculate offset into character table
+ */
+		mov	r1, r1, lsl #23
+		mov	r1, r1, lsr #20
+/*
+ * calculate offset required for each row [maybe I should make this an argument to this fn.
+ * Have to see what the register usage is like in the calling routines.
+ */
+		adr	r4, LC0
+		ldmia	r4, {r4, r5, r6, lr}
+		ldr	r4, [r4]
+		ldr	r5, [r5]
+/*
+ * Go to resolution-dependent routine...
+ */
+		cmp	r4, #4
+		blt	Lrow1bpp
+		eor	r2, r3, r2			@ Create eor mask to change colour from bg
+		orr	r3, r3, r3, lsl #8		@ to fg.
+		orr	r3, r3, r3, lsl #16
+		add	r0, r0, r5, lsl #3		@ Move to bottom of character
+		add	r1, r1, #7
+		ldrb	r7, [r6, r1]
+		tst	ip, #UNDERLINE << 9
+		eoreq	r7, r7, #255
+		teq	r4, #8
+		beq	Lrow8bpplp
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+		orr	r3, r3, r3, lsl #4
+Lrow4bpplp:	ldr	r7, [lr, r7, lsl #2]
+		mul	r7, r2, r7
+		tst	r1, #7				@ avoid using r7 directly after
+		eor	ip, r3, r7
+		str	ip, [r0, -r5]!
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r1, r1, #1
+		ldrb	r7, [r6, r1]
+		ldr	r7, [lr, r7, lsl #2]
+		mul	r7, r2, r7
+		tst	r1, #7				@ avoid using r7 directly after
+		eor	ip, r3, r7
+		str	ip, [r0, -r5]!
+		subne	r1, r1, #1
+		ldrneb	r7, [r6, r1]
+		bne	Lrow4bpplp
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+Lrow8bpplp:	mov	ip, r7, lsr #4
+		ldr	ip, [lr, ip, lsl #2]
+		mul	r4, r2, ip
+		and	ip, r7, #15			@ avoid r4
+		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
+		mul	ip, r2, ip			@ avoid r4
+		eor	r4, r3, r4			@ avoid ip
+		tst	r1, #7				@ avoid ip
+		sub	r0, r0, r5			@ avoid ip
+		eor	ip, r3, ip
+		stmia	r0, {r4, ip}
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r1, r1, #1
+		ldrb	r7, [r6, r1]
+		mov	ip, r7, lsr #4
+		ldr	ip, [lr, ip, lsl #2]
+		mul	r4, r2, ip
+		and	ip, r7, #15			@ avoid r4
+		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
+		mul	ip, r2, ip			@ avoid r4
+		eor	r4, r3, r4			@ avoid ip
+		tst	r1, #7				@ avoid ip
+		sub	r0, r0, r5			@ avoid ip
+		eor	ip, r3, ip
+		stmia	r0, {r4, ip}
+		subne	r1, r1, #1
+		ldrneb	r7, [r6, r1]
+		bne	Lrow8bpplp
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+Lrow1bpp:	add	r6, r6, r1
+		ldmia	r6, {r4, r7}
+		tst	ip, #INVERSE << 9
+		mvnne	r4, r4
+		mvnne	r7, r7
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		mov	r4, r4, lsr #8
+		strb	r4, [r0], r5
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		strb	r7, [r0], r5
+		mov	r7, r7, lsr #8
+		tst	ip, #UNDERLINE << 9
+		mvneq	r7, r7
+		strb	r7, [r0], r5
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+		.bss
+ENTRY(con_charconvtable)
+		.space	1024
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/misc.c linux.18p17/arch/arm/boot/compressed/misc.c
--- linux.vanilla/arch/arm/boot/compressed/misc.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/boot/compressed/misc.c	Fri Sep 15 23:28:37 2000
@@ -7,8 +7,17 @@
  * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
  *
  * Modified for ARM Linux by Russell King
+ *
+ * Nicolas Pitre <nico@visuaide.com>  1999/04/14 :
+ *  For this code to run directly from Flash, all constant variables must
+ *  be marked with 'const' and all other variables initialized at run-time 
+ *  only.  This way all non constant variables will end up in the bss segment,
+ *  which should point to addresses in RAM and cleared to 0 on start.
+ *  This allows for a much quicker boot time.
  */
 
+unsigned int __machine_arch_type;
+
 #include <asm/uaccess.h>
 #include <asm/arch/uncompress.h>
 #include <asm/proc/uncompress.h>
@@ -22,7 +31,7 @@
 /*
  * Optimised C version of memzero for the ARM.
  */
-extern __inline__ __ptr_t __memzero (__ptr_t s, size_t n)
+void __memzero (__ptr_t s, __kernel_size_t n)
 {
 	union { void *vp; unsigned long *ulp; unsigned char *ucp; } u;
 	int i;
@@ -62,11 +71,8 @@
 
 	if (n & 1)
 		*u.ucp++ = 0;
-	return s;
 }
 
-#define memzero(s,n) __memzero(s,n)
-
 extern __inline__ __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
 			    size_t __n)
 {
@@ -157,11 +163,11 @@
 static void gzip_release(void **);
 
 extern char input_data[];
-extern int  input_len;
+extern char input_data_end[];
 
 static uch *output_data;
 static ulg output_ptr;
-static ulg bytes_out = 0;
+static ulg bytes_out;
 
 static void *malloc(int size);
 static void free(void *where);
@@ -226,13 +232,14 @@
  * Fill the input buffer. This is called only when the buffer is empty
  * and at least one byte is really needed.
  */
-int fill_inbuf()
+int fill_inbuf(void)
 {
 	if (insize != 0)
 		error("ran out of input data\n");
 
 	inbuf = input_data;
-	insize = input_len;
+	insize = &input_data_end[0] - &input_data[0];
+
 	inptr = 1;
 	return inbuf[0];
 }
@@ -241,7 +248,7 @@
  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
  * (Used for the decompressed data only.)
  */
-void flush_window()
+void flush_window(void)
 {
 	ulg c = crc;
 	unsigned n;
@@ -257,6 +264,7 @@
 	bytes_out += (ulg)outcnt;
 	output_ptr += (ulg)outcnt;
 	outcnt = 0;
+	puts(".");
 }
 
 static void error(char *x)
@@ -270,26 +278,24 @@
 	while(1);	/* Halt */
 }
 
-#define STACK_SIZE (4096)
-
-ulg user_stack [STACK_SIZE];
-
 #ifndef STANDALONE_DEBUG
 
-ulg decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p)
-{
-	free_mem_ptr = free_mem_ptr_p;
-	free_mem_ptr_end = free_mem_ptr_end_p;
-
-	proc_decomp_setup ();
-	arch_decomp_setup ();
+ulg
+decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
+		  int arch_id)
+{
+	output_data		= (uch *)output_start;	/* Points to kernel start */
+	free_mem_ptr		= free_mem_ptr_p;
+	free_mem_ptr_end	= free_mem_ptr_end_p;
+	__machine_arch_type	= arch_id;
 
-	output_data = (uch *)output_start;	/* Points to kernel start */
+	proc_decomp_setup();
+	arch_decomp_setup();
 
 	makecrc();
 	puts("Uncompressing Linux...");
 	gunzip();
-	puts("done.\nNow booting the kernel\n");
+	puts(" done, booting the kernel.\n");
 	return output_ptr;
 }
 #else
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/boot/compressed/vmlinux.lds.in linux.18p17/arch/arm/boot/compressed/vmlinux.lds.in
--- linux.vanilla/arch/arm/boot/compressed/vmlinux.lds.in	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/boot/compressed/vmlinux.lds.in	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,52 @@
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+  . = LOAD_ADDR;
+  _load_addr = .;
+
+  . = TEXT_START;
+  _text = .;
+
+  .text : {
+    _start = .;
+    head.o(.start)
+    *(.start)
+    head.o(.text)
+    *(.text)
+    *(.fixup)
+    *(.gnu.warning)
+    input_data = .;
+    piggy.o
+    input_data_end = .;
+    . = ALIGN(4);
+  }
+
+  _etext = .;
+
+  .data : {
+    *(.data)
+  }
+
+  _edata = .;
+
+  . = BSS_START;
+  __bss_start = .;
+  .bss : {
+    *(.bss)
+  }
+  _end = .;
+
+  .stack : {
+    *(.stack)
+  }
+
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/config.in linux.18p17/arch/arm/config.in
--- linux.vanilla/arch/arm/config.in	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/arm/config.in	Fri Sep 15 23:28:37 2000
@@ -7,6 +7,11 @@
 define_bool CONFIG_ARM y
 
 mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
 comment 'System and processor type'
 
 choice 'ARM system type'	\
@@ -14,96 +19,111 @@
 	 A5000			CONFIG_ARCH_A5K \
 	 RiscPC			CONFIG_ARCH_RPC \
 	 EBSA-110		CONFIG_ARCH_EBSA110 \
-	 EBSA-285		CONFIG_ARCH_EBSA285 \
-	 NexusPCI		CONFIG_ARCH_NEXUSPCI \
-	 Corel-VNC		CONFIG_ARCH_VNC \
-	 Tbox			CONFIG_ARCH_TBOX" RiscPC
+	 FootBridge-based	CONFIG_FOOTBRIDGE" RiscPC
 
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
-  bool '  Include support for CATS boards' CONFIG_CATS
+if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+   bool 'FootBridge in HOST mode' CONFIG_HOST_FOOTBRIDGE
+   if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+      define_bool CONFIG_ADDIN_FOOTBRIDGE n
+   else
+      define_bool CONFIG_ADDIN_FOOTBRIDGE y
+   fi
+fi
+
+if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+   bool '  Include support for Intel EBSA285' CONFIG_ARCH_EBSA285
+   bool '  Include support for Chalice CATS boards' CONFIG_CATS
+   bool '  Include support for Corel NetWinder' CONFIG_ARCH_NETWINDER
+fi
+
+if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
+   # If we get any other footbridge-based plug-in boards, then
+   # add your architecture options here
+   define_bool CONFIG_ARCH_CO285 y
 fi
 
+#
 # Select various configuration options depending on the machine type
 #  Easy check for Acorn-style architectures
-
+#
 if [ "$CONFIG_ARCH_ARC" = "y" -o \
      "$CONFIG_ARCH_A5K" = "y" -o \
      "$CONFIG_ARCH_RPC" = "y" ]; then
-  define_bool CONFIG_ARCH_ACORN y
-else
-  define_bool CONFIG_ARCH_ACORN n
-fi
-
-if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
-  define_bool CONFIG_BUS_I2C y
-fi
-
-#  These machines always have PCI
-
-if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  define_bool CONFIG_PCI y
-fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
-  bool "PCI support" CONFIG_PCI
-fi
-
-# These machines have ISA-DMA
-if [ "$CONFIG_CATS" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  define_bool CONFIG_ISA_DMA y
+   define_bool CONFIG_ARCH_ACORN y
 else
-  define_bool CONFIG_ISA_DMA n
+   define_bool CONFIG_ARCH_ACORN n
 fi
 
+#
 # Figure out whether this system uses 26-bit or 32-bit CPUs.  Nobody has
 # ever built a machine that can take both, and now that ARM3 is obsolete
 # nobody is likely to either.
-
+#
 if [ "$CONFIG_ARCH_ARC" = "y" -o \
      "$CONFIG_ARCH_A5K" = "y" ]; then
-  define_bool CONFIG_CPU_32 n
-  define_bool CONFIG_CPU_26 y
-else
-  define_bool CONFIG_CPU_32 y
-  define_bool CONFIG_CPU_26 n
-fi
-
-# Now allow the user to choose a more precise CPU.  This is only used to set
-# the flags we pass to GCC, not in any code.
+   define_bool CONFIG_CPU_32 n
+   define_bool CONFIG_CPU_26 y
 
-choice 'Optimise for CPU'				\
-	"ARM2		CONFIG_CPU_ARM2 \
-	 ARM3		CONFIG_CPU_ARM3 \
-	 ARM6		CONFIG_CPU_ARM6 \
-	 ARM7		CONFIG_CPU_ARM7 \
-	 SA110		CONFIG_CPU_SA110" ARM6
+   #
+   # Select memory size
+   #
+   bool '2MB physical memory' CONFIG_PAGESIZE_16
+else
+   define_bool CONFIG_CPU_32 y
+   define_bool CONFIG_CPU_26 n
 
-if [ "$CONFIG_CPU_26" = "y" ]; then
+   #
+   # Select CPU and optimisation dependent on architecture
+   #
+   if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
+        "$CONFIG_FOOTBRIDGE" = "y" -o \
+        "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then
+      define_bool CONFIG_CPU_32v4 y
+      define_bool CONFIG_CPU_SA110 y
+   else
+      if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+	 define_bool CONFIG_CPU_32v3 y
+	 bool 'Support ARM610' CONFIG_CPU_ARM6
+	 bool 'Support ARM710' CONFIG_CPU_ARM7
+	 bool 'Support StrongARM110' CONFIG_CPU_SA110
+      fi
+   fi
+fi
+
+#if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
+#  define_bool CONFIG_BUS_I2C y
+#fi
 
-# For 26-bit CPUs, the page size changes with the amount of physical RAM!
-# The default is 4MB but if the user has less they have to own up to it here.
+#
+#  These machines always have PCI
+#
+if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
+     "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+   define_bool CONFIG_PCI y
+fi
 
-  choice 'Physical memory size'		\
-	"4MB+		CONFIG_PAGESIZE_32	\
-	 2MB		CONFIG_PAGESIZE_16	\
-	 1MB/512K	CONFIG_PAGESIZE_8" 4MB+
+#
+# These machines have ISA-DMA
+#
+if [ "$CONFIG_CATS" = "y" -o \
+     "$CONFIG_ARCH_NETWINDER" = "y" ]; then
+   define_bool CONFIG_ISA_DMA y
+else
+   define_bool CONFIG_ISA_DMA n
 fi
-endmenu
 
-mainmenu_option next_comment
-comment 'Code maturity level options'
-bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
-bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW
-bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+if [ "$CONFIG_CPU_32" = "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
+   bool 'Kernel-mode alignment trap handler' CONFIG_ALIGNMENT_TRAP
+fi
+#bool 'Split text into discardable sections' CONFIG_TEXT_SECTIONS
 endmenu
 
 mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
-  bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel module loader' CONFIG_KMOD
+   bool '  Set version information on all symbols for modules' CONFIG_MODVERSIONS
+   bool '  Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
@@ -113,13 +133,19 @@
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
+tristate 'Math emulation' CONFIG_NWFPE
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+if [ "$CONFIG_CPU_32" = "y" ]; then
+   tristate 'RISC OS personality' CONFIG_ARTHUR
+fi
 
 tristate 'Parallel port support' CONFIG_PARPORT
 if [ "$CONFIG_PARPORT" != "n" ]; then
-  dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+  if [ "$CONFIG_ARCH_ARC" = "y" ]; then
+    dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+  fi
   dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
 # If exactly one hardware type is selected then parport will optimise away
 # support for loading any others.  Defeat this if the user is keen.
@@ -129,10 +155,24 @@
     fi
   fi
 fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" -o \
-     "$CONFIG_ARCH_EBSA110" = "y" -o \
-     "$CONFIG_ARCH_VNC" = "y" ]; then
-  string 'Initial kernel command string' CONFIG_CMDLINE ""
+if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
+     "$CONFIG_ARCH_NETWINDER" = "y" -o \
+     "$CONFIG_CATS" = "y" ]; then
+   string 'Initial kernel command string' CONFIG_CMDLINE ""
+fi
+if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+     "$CONFIG_ARCH_EBSA110" = "y" -o   \
+     "$CONFIG_ARCH_EBSA285" = "y" -o   \
+     "$CONFIG_ARCH_CO285" = "y" ]; then
+   bool 'Timer and CPU usage LEDs' CONFIG_LEDS
+   if [ "$CONFIG_LEDS" = "y" ]; then
+      if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+	   "$CONFIG_ARCH_EBSA285" = "y" -o   \
+	   "$CONFIG_ARCH_CO285" = "y" ]; then
+         bool '  Timer LED' CONFIG_LEDS_TIMER
+         bool '  CPU usage LED' CONFIG_LEDS_CPU
+      fi
+   fi
 fi
 endmenu
 
@@ -141,80 +181,100 @@
 source drivers/block/Config.in
 
 if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-  source drivers/acorn/block/Config.in
+   source drivers/acorn/block/Config.in
 fi
 
-if [ "$CONFIG_VGA_CONSOLE" = "n" -a "$CONFIG_FB" = "n" ]; then
-  source arch/arm/drivers/char/Config.in
-else
-  source drivers/char/Config.in
-fi
+source drivers/char/Config.in
 if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-  source drivers/acorn/char/Config.in
+   if [ "$CONFIG_BUSMOUSE" = "y" ]; then
+      if [ "$CONFIG_ARCH_RPC" != "y" ]; then
+         define_bool CONFIG_KBDMOUSE y
+      else
+         define_bool CONFIG_RPCMOUSE y
+      fi
+   fi
 fi
 
 if [ "$CONFIG_VT" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Console drivers'
-  if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
-    bool 'VGA text console' CONFIG_VGA_CONSOLE
-  fi
-  bool 'Support Frame buffer devices' CONFIG_FB
-  source drivers/video/Config.in
-  endmenu
+   mainmenu_option next_comment
+   comment 'Console drivers'
+   if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
+      bool 'VGA text console' CONFIG_VGA_CONSOLE
+   fi
+   bool 'Support Frame buffer devices' CONFIG_FB
+   source drivers/video/Config.in
+   endmenu
 fi
 
 if [ "$CONFIG_NET" = "y" ]; then
-  source net/Config.in
-fi
+   source net/Config.in
 
-if [ "$CONFIG_NET" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Network device support'
+   source net/ax25/Config.in
 
-  bool 'Network device support?' CONFIG_NETDEVICES
-  if [ "$CONFIG_NETDEVICES" = "y" ]; then
-    source drivers/net/Config.in
-  fi
-  endmenu
+   source net/irda/Config.in
+
+   mainmenu_option next_comment
+   comment 'Network device support'
+
+   bool 'Network device support?' CONFIG_NETDEVICES
+   if [ "$CONFIG_NETDEVICES" = "y" ]; then
+      source drivers/net/Config.in
+   fi
+   endmenu
 fi
 
+# mainmenu_option next_comment
+# comment 'ISDN subsystem'
+#
+# tristate 'ISDN support' CONFIG_ISDN
+# if [ "$CONFIG_ISDN" != "n" ]; then
+#    source drivers/isdn/Config.in
+# fi
+# endmenu
+
 mainmenu_option next_comment
 comment 'SCSI support'
 
 tristate 'SCSI support?' CONFIG_SCSI
 
 if [ "$CONFIG_SCSI" != "n" ]; then
-  source drivers/scsi/Config.in
+   source drivers/scsi/Config.in
 fi
 endmenu
 
 if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_PCI" = "y" ]; then
-  mainmenu_option next_comment
-  comment 'Sound'
+   mainmenu_option next_comment
+   comment 'Sound'
 
-  tristate 'Sound support' CONFIG_SOUND
-  if [ "$CONFIG_SOUND" != "n" ]; then
-    source drivers/sound/Config.in
-  fi
-  endmenu
+   tristate 'Sound support' CONFIG_SOUND
+   if [ "$CONFIG_SOUND" != "n" ]; then
+      source drivers/sound/Config.in
+   fi
+   endmenu
 fi
 
-# mainmenu_option next_comment
-# comment 'ISDN subsystem'
-#
-# tristate 'ISDN support' CONFIG_ISDN
-# if [ "$CONFIG_ISDN" != "n" ]; then
-#   source drivers/isdn/Config.in
-# fi
-# endmenu
-
 source fs/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS
+bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+bool 'Verbose kernel error messages' CONFIG_DEBUG_ERRORS
+bool 'Verbose user fault messages' CONFIG_DEBUG_USER
+bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+if [ "$CONFIG_CPU_26" = "y" ]; then
+   bool 'Disable pgtable cache' CONFIG_NO_PGT_CACHE
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   # These options are only for real kernel hackers
+   # who want to get their hands dirty. 
+   bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL
+   if [ "$CONFIG_DEBUG_LL" = "y" ]; then
+      if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+         bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT
+      fi
+   fi
+fi
 endmenu
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/def-configs/a5k linux.18p17/arch/arm/def-configs/a5k
--- linux.vanilla/arch/arm/def-configs/a5k	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/def-configs/a5k	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,425 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+CONFIG_ARCH_A5K=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_FOOTBRIDGE is not set
+CONFIG_ARCH_ACORN=y
+# CONFIG_ISA_DMA is not set
+# CONFIG_CPU_32 is not set
+CONFIG_CPU_26=y
+# CONFIG_CPU_ARM2 is not set
+CONFIG_CPU_ARM3=y
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+# CONFIG_CPU_SA110 is not set
+CONFIG_PAGESIZE_32=y
+# CONFIG_PAGESIZE_16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=m
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Acorn-specific block devices
+#
+CONFIG_BLK_DEV_IDE_CARDS=y
+CONFIG_BLK_DEV_IDE_ICSIDE=y
+# CONFIG_BLK_DEV_IDE_RAPIDE is not set
+# CONFIG_BLK_DEV_FD1772 is not set
+CONFIG_BLK_DEV_MFM=m
+CONFIG_BLK_DEV_MFM_AUTODETECT=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_ATOMWIDE_SERIAL=y
+CONFIG_DUALSP_SERIAL=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=y
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_KBDMOUSE=y
+
+#
+# Console drivers
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FB_ACORN=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB2=y
+CONFIG_FBCON_CFB4=y
+CONFIG_FBCON_CFB8=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+# CONFIG_FBCON_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_ETHER1=y
+CONFIG_ARM_ETHER3=y
+# CONFIG_ARM_ETHERH is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+CONFIG_SCSI_PPA=m
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_ACORNSCSI_3=y
+CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
+CONFIG_SCSI_ACORNSCSI_SYNC=y
+CONFIG_SCSI_ARXESCSI=m
+# CONFIG_SCSI_CUMANA_2 is not set
+CONFIG_SCSI_EESOXSCSI=y
+# CONFIG_SCSI_POWERTECSCSI is not set
+
+#
+# The following drivers are not fully supported
+#
+# CONFIG_SCSI_CUMANA_1 is not set
+# CONFIG_SCSI_ECOSCSI is not set
+# CONFIG_SCSI_OAK1 is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+CONFIG_ACORN_PARTITION_ICS=y
+CONFIG_ACORN_PARTITION_POWERTEC=y
+CONFIG_ACORN_PARTITION_RISCIX=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_NO_PGT_CACHE=y
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/def-configs/ebsa110 linux.18p17/arch/arm/def-configs/ebsa110
--- linux.vanilla/arch/arm/def-configs/ebsa110	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/def-configs/ebsa110	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,340 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+CONFIG_ARCH_EBSA110=y
+# CONFIG_FOOTBRIDGE is not set
+# CONFIG_ARCH_ACORN is not set
+# CONFIG_ISA_DMA is not set
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/nfs rw mem=16M console=ttyS1,38400n8"
+CONFIG_LEDS=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_ONLY is not set
+
+#
+# Additional Block Devices
+#
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+CONFIG_SERIAL_EXTENDED=y
+# CONFIG_SERIAL_MANY_PORTS is not set
+# CONFIG_SERIAL_SHARE_IRQ is not set
+# CONFIG_SERIAL_DETECT_IRQ is not set
+# CONFIG_SERIAL_MULTIPORT is not set
+# CONFIG_HUB6 is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_NETLINK=y
+CONFIG_RTNETLINK=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_FIREWALL=y
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_FIREWALL=y
+# CONFIG_IP_FIREWALL_NETLINK is not set
+CONFIG_IP_ALWAYS_DEFRAG=y
+# CONFIG_IP_TRANSPARENT_PROXY is not set
+CONFIG_IP_MASQUERADE=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+CONFIG_IP_MASQUERADE_ICMP=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_MOD is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_AM79C961A=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=y
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SGI_DISKLABEL is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_FRAME_POINTER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/def-configs/footbridge linux.18p17/arch/arm/def-configs/footbridge
--- linux.vanilla/arch/arm/def-configs/footbridge	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/def-configs/footbridge	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,549 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+CONFIG_ARCH_EBSA285=y
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_PARIDE_PARPORT=y
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+CONFIG_IRLPT=m
+CONFIG_IRLPT_CLIENT=m
+CONFIG_IRLPT_SERVER=m
+# CONFIG_IRDA_OPTIONS is not set
+CONFIG_IRDA_COMPRESSION=y
+
+#
+#    IrDA compressors
+#
+CONFIG_IRDA_DEFLATE=m
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+CONFIG_IRPORT_SIR=m
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+CONFIG_WINBOND_FIR=m
+# CONFIG_SHARP_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=y
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_ACENIC is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SGI_DISKLABEL is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/def-configs/netwinder linux.18p17/arch/arm/def-configs/netwinder
--- linux.vanilla/arch/arm/def-configs/netwinder	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/def-configs/netwinder	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,677 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+# CONFIG_ARCH_EBSA285 is not set
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+CONFIG_CPU_ARMV4=y
+CONFIG_TUNE_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_CMDLINE="mem=32M root=/dev/hda1 ide0=autotune ide1=autotune"
+CONFIG_LEDS=y
+# CONFIG_LEDS_TIMER is not set
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=m
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_BLK_DEV_DAC960=m
+CONFIG_PARIDE_PARPORT=m
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+CONFIG_BLK_CPQ_DA=m
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# USB drivers - not for the faint of heart
+#
+# CONFIG_USB is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=m
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_PRINTER_READBACK is not set
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=m
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_21285_WATCHDOG=m
+CONFIG_977_WATCHDOG=m
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_21285_FLASH is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_BUZ is not set
+CONFIG_NVRAM=m
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_RADIO_RTRACK=m
+CONFIG_RADIO_RTRACK2=m
+CONFIG_RADIO_AZTECH=m
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_MIROPCM20 is not set
+CONFIG_RADIO_GEMTEK=m
+CONFIG_VIDEO_BT848=m
+CONFIG_VIDEO_BWQCAM=m
+CONFIG_VIDEO_CQCAM=m
+CONFIG_VIDEO_PMS=m
+CONFIG_VIDEO_SAA5249=m
+CONFIG_RADIO_SF16FMI=m
+CONFIG_RADIO_TYPHOON=m
+# CONFIG_RADIO_TYPHOON_PROC_FS is not set
+CONFIG_RADIO_ZOLTRIX=m
+CONFIG_TUNER_3036=m
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+CONFIG_FBCON_VGA=y
+CONFIG_FBCON_FONTWIDTH8_ONLY=y
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_NETLINK=y
+CONFIG_RTNETLINK=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_FIREWALL=y
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_FIREWALL=y
+# CONFIG_IP_FIREWALL_NETLINK is not set
+CONFIG_IP_ALWAYS_DEFRAG=y
+CONFIG_IP_TRANSPARENT_PROXY=y
+CONFIG_IP_MASQUERADE=y
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_ICMP is not set
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+CONFIG_IP_MASQUERADE_MOD=y
+CONFIG_IP_MASQUERADE_IPAUTOFW=m
+CONFIG_IP_MASQUERADE_IPPORTFW=m
+CONFIG_IP_MASQUERADE_MFW=m
+# CONFIG_IP_ROUTER is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_NET_IPGRE_BROADCAST is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+CONFIG_INET_RARP=m
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_SPX=m
+CONFIG_ATALK=m
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+CONFIG_IRLPT=m
+CONFIG_IRLPT_CLIENT=m
+CONFIG_IRLPT_SERVER=m
+CONFIG_IRDA_OPTIONS=y
+
+#
+#    IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
+# CONFIG_IRDA_COMPRESSION is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+CONFIG_IRPORT_SIR=m
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+CONFIG_WINBOND_FIR=m
+# CONFIG_SHARP_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_DUMMY=m
+CONFIG_EQUALIZER=m
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_SIS900 is not set
+CONFIG_YELLOWFIN=m
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_ACENIC is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+# CONFIG_SLIP_SMART is not set
+# CONFIG_SLIP_MODE_SLIP6 is not set
+# CONFIG_NET_RADIO is not set
+
+#
+# Token ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+CONFIG_SHAPER=m
+
+#
+# Wan interfaces
+#
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_SEALEVEL_4021 is not set
+# CONFIG_DLCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=m
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+CONFIG_SCSI_SYM53C8XX=m
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
+CONFIG_SCSI_NCR53C8XX_SYNC=20
+# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+CONFIG_SOUND_OPL3SA1=m
+# CONFIG_SOUND_SOFTOSS is not set
+CONFIG_SOUND_YM3812=m
+CONFIG_SOUND_VMIDI=m
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+CONFIG_QUOTA=y
+CONFIG_AUTOFS_FS=m
+CONFIG_ADFS_FS=m
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_UMSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=m
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_EFS_FS=m
+CONFIG_SGI_PARTITION=y
+
+#
+# Network File Systems
+#
+CONFIG_CODA_FS=m
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_SMB_FS=m
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_SGI_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SGI_DISKLABEL is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_ISO8859_9=m
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+
+#
+# Kernel hacking
+#
+# CONFIG_FRAME_POINTER is not set
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/def-configs/rpc linux.18p17/arch/arm/def-configs/rpc
--- linux.vanilla/arch/arm/def-configs/rpc	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/def-configs/rpc	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,471 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+CONFIG_ARCH_RPC=y
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_FOOTBRIDGE is not set
+CONFIG_ARCH_ACORN=y
+# CONFIG_ISA_DMA is not set
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_NWFPE is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=y
+CONFIG_BLK_DEV_IDEFLOPPY=y
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Acorn-specific block devices
+#
+CONFIG_BLK_DEV_IDE_CARDS=y
+CONFIG_BLK_DEV_IDE_ICSIDE=y
+CONFIG_BLK_DEV_IDE_RAPIDE=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_ATOMWIDE_SERIAL=y
+CONFIG_DUALSP_SERIAL=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_RPCMOUSE=y
+
+#
+# Console drivers
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FB_ACORN=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB2=y
+CONFIG_FBCON_CFB4=y
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA is not set
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_RARP is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_ARM_ETHER1=m
+CONFIG_ARM_ETHER3=m
+CONFIG_ARM_ETHERH=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_ACORNSCSI_3=m
+CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
+CONFIG_SCSI_ACORNSCSI_SYNC=y
+CONFIG_SCSI_ARXESCSI=m
+CONFIG_SCSI_CUMANA_2=m
+CONFIG_SCSI_EESOXSCSI=m
+CONFIG_SCSI_POWERTECSCSI=m
+
+#
+# The following drivers are not fully supported
+#
+CONFIG_SCSI_CUMANA_1=m
+CONFIG_SCSI_OAK1=m
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+# CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+CONFIG_SOUND_VIDC=m
+# CONFIG_SOUND_WAVEARTIST is not set
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+CONFIG_ACORN_PARTITION_ICS=y
+CONFIG_ACORN_PARTITION_POWERTEC=y
+CONFIG_ACORN_PARTITION_RISCIX=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_ISO8859_9=m
+# CONFIG_NLS_ISO8859_15 is not set
+CONFIG_NLS_KOI8_R=m
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_ARTHUR is not set
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/defconfig linux.18p17/arch/arm/defconfig
--- linux.vanilla/arch/arm/defconfig	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/defconfig	Fri Sep 15 23:28:37 2000
@@ -4,47 +4,71 @@
 CONFIG_ARM=y
 
 #
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+CONFIG_ARCH_EBSA285=y
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
+#
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
 
 #
 # Loadable module support
 #
 CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
 CONFIG_KMOD=y
 
 #
 # General setup
 #
-# CONFIG_ARCH_ARC is not set
-# CONFIG_ARCH_A5K is not set
-CONFIG_ARCH_RPC=y
-# CONFIG_ARCH_EBSA110 is not set
-# CONFIG_ARCH_NEXUSPCI is not set
-CONFIG_ARCH_ACORN=y
-# CONFIG_PCI is not set
-# CONFIG_CPU_ARM2 is not set
-# CONFIG_CPU_ARM3 is not set
-# CONFIG_CPU_ARM6 is not set
-CONFIG_CPU_SA110=y
-CONFIG_FRAME_POINTER=y
-# CONFIG_BINUTILS_NEW is not set
-CONFIG_DEBUG_ERRORS=y
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
 CONFIG_BINFMT_AOUT=y
-CONFIG_BINFMT_ELF=m
-# CONFIG_BINFMT_JAVA is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
 
 #
-# Floppy, IDE, and other block devices
+# Block devices
 #
-CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_FD is not set
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -52,32 +76,165 @@
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_IDE_PCMCIA is not set
-CONFIG_BLK_DEV_IDE_CARDS=y
-CONFIG_BLK_DEV_IDE_ICSIDE=y
-# CONFIG_BLK_DEV_IDE_RAPIDE is not set
-# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
 
 #
 # Additional Block Devices
 #
 CONFIG_BLK_DEV_LOOP=m
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
 CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
 CONFIG_PARIDE_PARPORT=y
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_PART=y
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
 # CONFIG_BLK_DEV_HD is not set
 
 #
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
+#
 # Networking options
 #
-# CONFIG_PACKET is not set
+CONFIG_PACKET=y
 # CONFIG_NETLINK is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_FILTER is not set
@@ -85,21 +242,20 @@
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
 # CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
+CONFIG_IP_ALIAS=y
 # CONFIG_SYN_COOKIES is not set
 
 #
 # (it is safe to leave these untouched)
 #
 # CONFIG_INET_RARP is not set
-CONFIG_IP_NOSR=y
-# CONFIG_SKB_LARGE is not set
+CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 
 #
@@ -111,107 +267,198 @@
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
+# CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
-# CONFIG_NET_PROFILE is not set
 
 #
-# SCSI support
+# Amateur Radio support
 #
-CONFIG_SCSI=y
+# CONFIG_HAMRADIO is not set
 
 #
-# SCSI support type (disk, tape, CD-ROM)
+# IrDA subsystem support
 #
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-CONFIG_BLK_DEV_SR=y
-# CONFIG_BLK_DEV_SR_VENDOR is not set
-# CONFIG_CHR_DEV_SG is not set
+# CONFIG_IRDA is not set
 
 #
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+# Network device support
 #
-# CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_LOGGING=y
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=y
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
 
 #
-# SCSI low-level drivers
+# CCP compressors for PPP are only built as modules.
 #
-CONFIG_SCSI_ACORNSCSI_3=m
-CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
-CONFIG_SCSI_ACORNSCSI_SYNC=y
-CONFIG_SCSI_CUMANA_2=m
-CONFIG_SCSI_POWERTECSCSI=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
-# The following drives are not fully supported
+# SCSI support
 #
-CONFIG_SCSI_CUMANA_1=m
-CONFIG_SCSI_OAK1=m
-CONFIG_SCSI_PPA=m
-CONFIG_SCSI_PPA_HAVE_PEDANTIC=2
+# CONFIG_SCSI is not set
 
 #
-# Network device support
+# Sound
 #
-CONFIG_NETDEVICES=y
-# CONFIG_DUMMY is not set
-# CONFIG_EQUALIZER is not set
-CONFIG_PPP=m
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
 
 #
-# CCP compressors for PPP are only built as modules.
+# Additional low level sound drivers
 #
-# CONFIG_SLIP is not set
-CONFIG_ETHER1=m
-CONFIG_ETHER3=m
-CONFIG_ETHERH=m
+# CONFIG_LOWLEVEL_SOUND is not set
 
 #
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-CONFIG_NFSD=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_UFS_FS is not set
-CONFIG_ADFS_FS=y
-CONFIG_ADFS_FS=y
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
 # CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+# CONFIG_ACORN_PARTITION_ICS is not set
+# CONFIG_ACORN_PARTITION_POWERTEC is not set
+# CONFIG_ACORN_PARTITION_RISCIX is not set
 CONFIG_NLS=y
 
 #
 # Native Language Support
 #
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
 # CONFIG_NLS_CODEPAGE_855 is not set
 # CONFIG_NLS_CODEPAGE_857 is not set
 # CONFIG_NLS_CODEPAGE_860 is not set
@@ -223,8 +470,8 @@
 # CONFIG_NLS_CODEPAGE_866 is not set
 # CONFIG_NLS_CODEPAGE_869 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
 # CONFIG_NLS_ISO8859_5 is not set
@@ -232,34 +479,15 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+CONFIG_NLS_ISO8859_15=m
 # CONFIG_NLS_KOI8_R is not set
 
 #
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=y
-# CONFIG_SERIAL_CONSOLE is not set
-# CONFIG_SERIAL_EXTENDED is not set
-CONFIG_ATOMWIDE_SERIAL=y
-CONFIG_DUALSP_SERIAL=y
-CONFIG_MOUSE=y
-CONFIG_PRINTER=m
-CONFIG_PRINTER_READBACK=y
-# CONFIG_UMISC is not set
-# CONFIG_WATCHDOG is not set
-CONFIG_RPCMOUSE=y
-
-#
-# Sound
-#
-CONFIG_SOUND=m
-CONFIG_VIDC=y
-CONFIG_AUDIO=y
-DSP_BUFFSIZE=65536
-
-#
 # Kernel hacking
 #
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
 CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/Makefile linux.18p17/arch/arm/kernel/Makefile
--- linux.vanilla/arch/arm/kernel/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/Makefile	Fri Sep 15 23:28:37 2000
@@ -9,66 +9,73 @@
 ENTRY_OBJ = entry-$(PROCESSOR).o
 
 O_TARGET := kernel.o
-O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \
-	    signal.o sys_arm.o time.o traps.o
+O_OBJS   := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
+	    setup.o signal.o sys_arm.o time.o traps.o
 
-DMA_OBJS_arc      = dma-arc.o
-DMA_OBJS_a5k      = dma-a5k.o
-DMA_OBJS_rpc      = dma-rpc.o
-DMA_OBJS_ebsa110  = dma-dummy.o
-DMA_OBJS_ebsa285  = dma-ebsa285.o
-DMA_OBJS_nexuspci =
-DMA_OBJS_vnc      = dma-vnc.o
-
-O_OBJS_arc        = ecard.o iic.o fiq.o oldlatches.o
-O_OBJS_a5k        = ecard.o iic.o fiq.o
-O_OBJS_rpc        = ecard.o iic.o fiq.o
-O_OBJS_ebsa110    = leds-ebsa110.o
-O_OBJS_ebsa285    = leds-ebsa285.o hw-ebsa285.o
-O_OBJS_nexuspci   =
-O_OBJS_vnc        = leds-ebsa285.o hw-vnc.o
+ifeq ($(CONFIG_ISA_DMA),y)
+  ISA_DMA_OBJS += dma-isa.o
+endif
+
+O_OBJS_arc        = dma-arc.o iic.o fiq.o oldlatches.o
+O_OBJS_a5k        = dma-a5k.o iic.o fiq.o
+O_OBJS_rpc        = dma-rpc.o iic.o fiq.o
+O_OBJS_ebsa110    = dma-dummy.o
+O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o
+O_OBJS_nexuspci   = dma-dummy.o
+
+OX_OBJS_arc	  = dma.o
+OX_OBJS_a5k	  = dma.o
+OX_OBJS_rpc	  = dma.o
+OX_OBJS_ebsa110	  = 
+OX_OBJS_footbridge= dma.o hw-footbridge.o
+OX_OBJS_nexuspci  =
 
-all: lib kernel.o $(HEAD_OBJ) init_task.o
+all: kernel.o $(HEAD_OBJ) init_task.o
+
+O_OBJS += $(O_OBJS_$(MACHINE))
 
 ifeq ($(CONFIG_MODULES),y)
   OX_OBJS = armksyms.o
-else
-  O_OBJS += armksyms.o
 endif
 
-ifeq ($(MACHINE),nexuspci)
-  ifdef CONFIG_PCI
+ifeq ($(CONFIG_ARCH_ACORN),y)
+  OX_OBJS += ecard.o
+endif
+
+ifeq ($(CONFIG_PCI),y)
+  ifeq ($(MACHINE),nexuspci)
     O_OBJS += plx9080.o
-  endif
-else
-  ifdef CONFIG_PCI
+  else
     O_OBJS += dec21285.o
   endif
 endif
 
-ifneq ($(DMA_OBJS_$(MACHINE)),)
-  OX_OBJS += dma.o
-  O_OBJS  += $(DMA_OBJS_$(MACHINE))
-  ifeq ($(CONFIG_ISA_DMA),y)
-    O_OBJS += dma-isa.o
-  endif
+ifeq ($(CONFIG_LEDS),y)
+  OX_OBJS += leds-$(MACHINE).o
+endif
+
+ifeq ($(CONFIG_MODULES),y)
+  OX_OBJS += $(OX_OBJS_$(MACHINE))
 else
-  O_OBJS += dma-dummy.o
+  O_OBJS += $(OX_OBJS_$(MACHINE))
 endif
 
-O_OBJS += $(O_OBJS_$(MACHINE))
+ifeq ($(CONFIG_ARTHUR),y)
+  O_OBJS += arthur.o
+else
+  ifeq ($(CONFIG_ARTHUR),m)
+    M_OBJS += arthur.o
+  endif
+endif
 
 $(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
-	$(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
-
-$(ENTRY_OBJ): $(ENTRY_OBJ:.o=.S)
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $(ENTRY_OBJ:.o=.S) -o $@
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
 
 include $(TOPDIR)/Rules.make
 
-$(ENTRY_OBJ): ../lib/constants.h
-
-.PHONY: lib
+.S.o:
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -c -o $*.o $<
 
-lib:
-	$(MAKE) -C ../lib constants.h
+# Spell out some dependencies that `make dep' doesn't spot
+entry-armv.o: calls.S ../lib/constants.h
+entry-armo.o: calls.S ../lib/constants.h
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/armksyms.c linux.18p17/arch/arm/kernel/armksyms.c
--- linux.vanilla/arch/arm/kernel/armksyms.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/armksyms.c	Fri Sep 15 23:28:37 2000
@@ -2,24 +2,49 @@
 #include <linux/module.h>
 #include <linux/user.h>
 #include <linux/string.h>
+#include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/in6.h>
+#include <linux/vt_kern.h>
 
-#include <asm/ecard.h>
+#include <asm/byteorder.h>
 #include <asm/elf.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/dma.h>
 #include <asm/pgtable.h>
+#include <asm/proc-fns.h>
+#include <asm/processor.h>
+#include <asm/semaphore.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 
+extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(struct pt_regs *, struct user_fp_struct *);
 extern void inswb(unsigned int port, void *to, int len);
 extern void outswb(unsigned int port, const void *to, int len);
 
+extern unsigned int local_bh_count[NR_CPUS];
+extern unsigned int local_irq_count[NR_CPUS];
+
+extern unsigned long system_serial_low;
+extern unsigned long system_serial_high;
+extern unsigned int system_rev;
+
+/*
+ * syscalls
+ */
+extern int sys_write(int, const char *, int);
+extern int sys_read(int, char *, int);
+extern int sys_lseek(int, off_t, int);
+extern int sys_exit(int);
+extern int sys_wait4(int, int *, int, struct rusage *);
+
 /*
  * libgcc functions - functions that are used internally by the
  * compiler...  (prototypes are not correct though, but that
@@ -43,6 +68,8 @@
 extern void __umoddi3(void);
 extern void __umodsi3(void);
 
+extern void ret_from_exception(void);
+extern void fpundefinstr(void);
 extern void fp_enter(void);
 #define EXPORT_SYMBOL_ALIAS(sym,orig) \
  const char __kstrtab_##sym##[] __attribute__((section(".kstrtab"))) = \
@@ -57,32 +84,47 @@
 EXPORT_SYMBOL_ALIAS(fp_printk,printk);
 EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
 
+#ifdef CONFIG_CPU_26
+EXPORT_SYMBOL(fpundefinstr);
+EXPORT_SYMBOL(ret_from_exception);
+#endif
+
+EXPORT_SYMBOL(kd_mksound);
+
 	/* platform dependent support */
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(udelay);
 EXPORT_SYMBOL(xchg_str);
-
-	/* expansion card support */
-#ifdef CONFIG_ARCH_ACORN
-EXPORT_SYMBOL(ecard_startfind);
-EXPORT_SYMBOL(ecard_find);
-EXPORT_SYMBOL(ecard_readchunk);
-EXPORT_SYMBOL(ecard_address);
-#endif
-
+EXPORT_SYMBOL(local_bh_count);
+EXPORT_SYMBOL(local_irq_count);
+#if defined(CONFIG_CPU_32) && !defined(CONFIG_ARCH_EBSA110)
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(__iounmap);
+#endif
+EXPORT_SYMBOL(init_mm);
+EXPORT_SYMBOL(kernel_thread);
+EXPORT_SYMBOL(system_rev);
+EXPORT_SYMBOL(system_serial_low);
+EXPORT_SYMBOL(system_serial_high);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 
 	/* processor dependencies */
 EXPORT_SYMBOL(processor);
-EXPORT_SYMBOL(machine_type);
+EXPORT_SYMBOL(__machine_arch_type);
+
+	/* networking */
+EXPORT_SYMBOL(csum_partial_copy);
+EXPORT_SYMBOL(__csum_ipv6_magic);
 
 	/* io */
-EXPORT_SYMBOL(outswb);
+EXPORT_SYMBOL(outsb);
 EXPORT_SYMBOL(outsw);
-EXPORT_SYMBOL(inswb);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
 EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
 
 	/* address translation */
 #ifndef __virt_to_phys__is_a_macro
@@ -98,7 +140,9 @@
 EXPORT_SYMBOL(__bus_to_virt);
 #endif
 
+#ifndef CONFIG_NO_PGT_CACHE
 EXPORT_SYMBOL(quicklists);
+#endif
 EXPORT_SYMBOL(__bad_pmd);
 EXPORT_SYMBOL(__bad_pmd_kernel);
 
@@ -116,19 +160,20 @@
 EXPORT_SYMBOL_NOVERS(strpbrk);
 EXPORT_SYMBOL_NOVERS(strtok);
 EXPORT_SYMBOL_NOVERS(strrchr);
+EXPORT_SYMBOL_NOVERS(strstr);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memmove);
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memscan);
-EXPORT_SYMBOL_NOVERS(memzero);
+EXPORT_SYMBOL_NOVERS(__memzero);
 
 	/* user mem (segment) */
 #if defined(CONFIG_CPU_32)
 EXPORT_SYMBOL(__arch_copy_from_user);
 EXPORT_SYMBOL(__arch_copy_to_user);
 EXPORT_SYMBOL(__arch_clear_user);
-EXPORT_SYMBOL(__arch_strlen_user);
+EXPORT_SYMBOL(__arch_strnlen_user);
 #elif defined(CONFIG_CPU_26)
 EXPORT_SYMBOL(uaccess_kernel);
 EXPORT_SYMBOL(uaccess_user);
@@ -164,6 +209,19 @@
 EXPORT_SYMBOL(find_next_zero_bit);
 
 	/* elf */
-EXPORT_SYMBOL(armidlist);
-EXPORT_SYMBOL(armidindex);
 EXPORT_SYMBOL(elf_platform);
+EXPORT_SYMBOL(elf_hwcap);
+
+	/* syscalls */
+EXPORT_SYMBOL(sys_write);
+EXPORT_SYMBOL(sys_read);
+EXPORT_SYMBOL(sys_lseek);
+EXPORT_SYMBOL(sys_open);
+EXPORT_SYMBOL(sys_exit);
+EXPORT_SYMBOL(sys_wait4);
+
+	/* semaphores */
+EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_interruptible_failed);
+EXPORT_SYMBOL_NOVERS(__up_wakeup);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/arthur.c linux.18p17/arch/arm/kernel/arthur.c
--- linux.vanilla/arch/arm/kernel/arthur.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/kernel/arthur.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,88 @@
+/*
+ * Arthur personality
+ * Copyright (C) 1998 Philip Blundell
+ */
+
+#include <linux/personality.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+#include <asm/ptrace.h>
+
+/* RISC OS doesn't have many signals, and a lot of those that it does
+   have don't map easily to any Linux equivalent.  Never mind.  */
+
+#define RISCOS_SIGABRT		1
+#define RISCOS_SIGFPE		2
+#define RISCOS_SIGILL		3
+#define RISCOS_SIGINT		4
+#define RISCOS_SIGSEGV		5
+#define RISCOS_SIGTERM		6
+#define RISCOS_SIGSTAK		7
+#define RISCOS_SIGUSR1		8
+#define RISCOS_SIGUSR2		9
+#define RISCOS_SIGOSERROR	10
+
+static unsigned long riscos_to_linux_signals[32] = {
+	0,	1,	2,	3,	4,	5,	6,	7,
+	8,	9,	10,	11,	12,	13,	14,	15,
+	16,	17,	18,	19,	20,	21,	22,	23,
+	24,	25,	26,	27,	28,	29,	30,	31
+};
+
+static unsigned long linux_to_riscos_signals[32] = {
+	0,		-1,		RISCOS_SIGINT,	-1,
+       	RISCOS_SIGILL,	5,		RISCOS_SIGABRT,	7,
+	RISCOS_SIGFPE,	9,		RISCOS_SIGUSR1,	RISCOS_SIGSEGV,	
+	RISCOS_SIGUSR2,	13,		14,		RISCOS_SIGTERM,
+	16,		17,		18,		19,
+	20,		21,		22,		23,
+	24,		25,		26,		27,
+	28,		29,		30,		31
+};
+
+static void arthur_lcall7(int nr, struct pt_regs *regs)
+{
+	struct siginfo info;
+	info.si_signo = SIGSWI;
+	info.si_code = nr;
+	/* Bounce it to the emulator */
+	send_sig_info(SIGSWI, &info, current);
+}
+
+static struct exec_domain riscos_exec_domain = {
+	"Arthur",	/* name */
+	(lcall7_func)arthur_lcall7,
+	PER_RISCOS, PER_RISCOS,
+	riscos_to_linux_signals,
+	linux_to_riscos_signals,
+#ifdef MODULE
+	&__this_module,	/* No usage counter. */
+#else
+	NULL,
+#endif
+	NULL		/* Nothing after this in the list. */
+};
+
+/*
+ * We could do with some locking to stop Arthur being removed while
+ * processes are using it.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+int initialise_arthur(void)
+#endif
+{
+	return register_exec_domain(&riscos_exec_domain);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	unregister_exec_domain(&riscos_exec_domain);
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/calls.S linux.18p17/arch/arm/kernel/calls.S
--- linux.vanilla/arch/arm/kernel/calls.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/calls.S	Fri Sep 15 23:28:37 2000
@@ -31,7 +31,7 @@
 		.long	SYMBOL_NAME(sys_lseek)
 /* 20 */	.long	SYMBOL_NAME(sys_getpid)
 		.long	SYMBOL_NAME(sys_mount_wrapper)
-		.long	SYMBOL_NAME(sys_umount)
+		.long	SYMBOL_NAME(sys_oldumount)
 		.long	SYMBOL_NAME(sys_setuid)
 		.long	SYMBOL_NAME(sys_getuid)
 /* 25 */	.long	SYMBOL_NAME(sys_stime)
@@ -61,7 +61,7 @@
 		.long	SYMBOL_NAME(sys_geteuid)
 /* 50 */	.long	SYMBOL_NAME(sys_getegid)
 		.long	SYMBOL_NAME(sys_acct)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_phys */
+		.long	SYMBOL_NAME(sys_umount)
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_lock */
 		.long	SYMBOL_NAME(sys_ioctl)
 /* 55 */	.long	SYMBOL_NAME(sys_fcntl)
@@ -110,7 +110,7 @@
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_profil */
 		.long	SYMBOL_NAME(sys_statfs)
 /* 100 */	.long	SYMBOL_NAME(sys_fstatfs)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* .long	_sys_ioperm */
+		.long	SYMBOL_NAME(sys_ni_syscall)
 		.long	SYMBOL_NAME(sys_socketcall)
 		.long	SYMBOL_NAME(sys_syslog)
 		.long	SYMBOL_NAME(sys_setitimer)
@@ -129,7 +129,7 @@
 		.long	SYMBOL_NAME(sys_ipc)
 		.long	SYMBOL_NAME(sys_fsync)
 		.long	SYMBOL_NAME(sys_sigreturn_wrapper)
-		.long	SYMBOL_NAME(sys_clone_wapper)
+/* 120 */	.long	SYMBOL_NAME(sys_clone_wapper)
 		.long	SYMBOL_NAME(sys_setdomainname)
 		.long	SYMBOL_NAME(sys_newuname)
 		.long	SYMBOL_NAME(sys_ni_syscall)		/* .long	SYMBOL_NAME(sys_modify_ldt) */
@@ -157,8 +157,8 @@
 /* 145 */	.long	SYMBOL_NAME(sys_readv)
 		.long	SYMBOL_NAME(sys_writev)
 		.long	SYMBOL_NAME(sys_getsid)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_ni_syscall)
+		.long	SYMBOL_NAME(sys_fdatasync)
+		.long	SYMBOL_NAME(sys_sysctl)
 /* 150 */	.long	SYMBOL_NAME(sys_mlock)
 		.long	SYMBOL_NAME(sys_munlock)
 		.long	SYMBOL_NAME(sys_mlockall)
@@ -196,6 +196,10 @@
 		.long	SYMBOL_NAME(sys_capget)
 /* 185 */	.long	SYMBOL_NAME(sys_capset)
 		.long	SYMBOL_NAME(sys_sigaltstack_wrapper)
+		.long	SYMBOL_NAME(sys_sendfile)
+		.long	SYMBOL_NAME(sys_ni_syscall)
+		.long	SYMBOL_NAME(sys_ni_syscall)
+/* 190 */	.long	SYMBOL_NAME(sys_vfork_wrapper)
 
 		.rept	NR_syscalls-186
 			.long	SYMBOL_NAME(sys_ni_syscall)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dec21285.c linux.18p17/arch/arm/kernel/dec21285.c
--- linux.vanilla/arch/arm/kernel/dec21285.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dec21285.c	Fri Sep 15 23:28:37 2000
@@ -8,17 +8,17 @@
 #include <linux/pci.h>
 #include <linux/ptrace.h>
 #include <linux/interrupt.h>
+#include <linux/mm.h>
 #include <linux/init.h>
 
 #include <asm/irq.h>
 #include <asm/system.h>
+#include <asm/hardware.h>
 
-#define MAX_SLOTS		20
+#define MAX_SLOTS		21
 
 extern void pcibios_fixup_ebsa285(struct pci_dev *dev);
 extern void pcibios_init_ebsa285(void);
-extern void pcibios_fixup_vnc(struct pci_dev *dev);
-extern void pcibios_init_vnc(void);
 
 int
 pcibios_present(void)
@@ -33,11 +33,12 @@
 		int slot = PCI_SLOT(dev_fn);
 		
 		if (slot < MAX_SLOTS)
-			return 0xf8c00000 + (slot << 11) + (PCI_FUNC(dev_fn) << 8);
+			return PCICFG0_BASE + 0xc00000 +
+				(slot << 11) + (PCI_FUNC(dev_fn) << 8);
 		else
 			return 0;
 	} else
-		return 0xf9000000 | (bus << 16) | (dev_fn << 8);
+		return PCICFG1_BASE | (bus << 16) | (dev_fn << 8);
 }
 
 int
@@ -151,10 +152,7 @@
 	struct pci_dev *dev;
 
 	for (dev = pci_devices; dev; dev = dev->next) {
-		if (machine_is_ebsa285() || machine_is_cats())
-			pcibios_fixup_ebsa285(dev);
-		if (machine_is_netwinder())
-			pcibios_fixup_vnc(dev);
+		pcibios_fixup_ebsa285(dev);
 
 		pcibios_write_config_byte(dev->bus->number, dev->devfn,
 					  PCI_INTERRUPT_LINE, dev->irq);
@@ -164,18 +162,83 @@
 			dev->bus->number, dev->devfn,
 			dev->vendor, dev->device, dev->irq);
 	}
-	if (machine_is_netwinder())
-		hw_init();
+
+	hw_init();
 }
 
 __initfunc(void pcibios_init(void))
 {
-	if (machine_is_ebsa285() || machine_is_cats())
-		pcibios_init_ebsa285();
-	if (machine_is_netwinder())
-		pcibios_init_vnc();
+	unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET;
+	unsigned long cntl;
+
+	*CSR_SDRAMBASEMASK    = (mem_size - 1) & 0x0ffc0000;
+	*CSR_SDRAMBASEOFFSET  = 0;
+	*CSR_ROMBASEMASK      = 0x80000000;
+	*CSR_CSRBASEMASK      = 0;
+	*CSR_CSRBASEOFFSET    = 0;
+	*CSR_PCIADDR_EXTN     = 0;
+
+#ifdef CONFIG_HOST_FOOTBRIDGE
+	/*
+	 * Against my better judgement, Philip Blundell still seems
+	 * to be saying that we should initialise the PCI stuff here
+	 * when the PCI_CFN bit is not set, dispite my comment below,
+	 * which he decided to remove.  If it is not set, then
+	 * the card is in add-in mode, and we're in a machine where
+	 * the bus is set up by 'others'.
+	 *
+	 * We should therefore not mess about with the mapping in
+	 * anyway, and we should not be using the virt_to_bus functions
+	 * that exist in the HOST architecture mode (since they assume
+	 * a fixed mapping).
+	 *
+	 * Instead, you should be using ADDIN mode, which allows for
+	 * this situation.  This does assume that you have correctly
+	 * initialised the PCI bus, which you must have done to get
+	 * your PC booted.
+	 *
+	 * Unfortunately, he seems to be blind to this.  I guess he'll
+	 * also remove all this.
+	 *
+	 * And THIS COMMENT STAYS, even if this gets patched, thank
+	 * you.
+	 */
+
+	/*
+	 * Map our SDRAM at a known address in PCI space, just in case
+	 * the firmware had other ideas.  Using a nonzero base is
+	 * necessary, since some VGA cards forcefully use PCI addresses
+	 * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards).
+	 *
+	 * NOTE! If you need to chec the PCI_CFN bit in the SA110
+	 * control register then you've configured the kernel wrong.
+	 * If you're not using host mode, then DO NOT set
+	 * CONFIG_HOST_FOOTBRIDGE, but use CONFIG_ADDIN_FOOTBRIDGE
+	 * instead.  In this case, you MUST supply some firmware
+	 * to allow your PC to boot, plus we should not modify the
+	 * mappings that the PC BIOS has set up for us.
+	 */
+	*CSR_PCICACHELINESIZE = 0x00002008;
+	*CSR_PCICSRBASE       = 0;
+	*CSR_PCICSRIOBASE     = 0;
+	*CSR_PCISDRAMBASE     = virt_to_bus((void *)PAGE_OFFSET);
+	*CSR_PCIROMBASE       = 0;
+	*CSR_PCICMD           = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+				PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK |
+				PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+				(1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
+#endif
+
+	/*
+	 * Clear any existing errors - we aren't
+	 * interested in historical data...
+	 */
+	cntl = *CSR_SA110_CNTL & 0xffffde07;
+	*CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
+
+	pcibios_init_ebsa285();
 
-	printk("DEC21285 PCI revision %02X\n", *(unsigned char *)0xfe000008);
+	printk(KERN_DEBUG"PCI: DEC21285 revision %02lX\n", *CSR_CLASSREV & 0xff);
 }
 
 __initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-a5k.c linux.18p17/arch/arm/kernel/dma-a5k.c
--- linux.vanilla/arch/arm/kernel/dma-a5k.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-a5k.c	Fri Sep 15 23:28:37 2000
@@ -12,7 +12,6 @@
 #include <asm/fiq.h>
 #include <asm/io.h>
 #include <asm/hardware.h>
-#include <asm/pgtable.h>
 
 #include "dma.h"
 
@@ -37,8 +36,9 @@
 	if (channel != DMA_VIRTUAL_FLOPPY)
 		printk("arch_dma_count: invalid channel %d\n", channel);
 	else {
-		extern int floppy_fiqresidual(void);
-		return floppy_fiqresidual();
+		struct pt_regs regs;
+		get_fiq_regs(&regs);
+		return regs.ARM_r9;
 	}
 	return 0;
 }
@@ -48,6 +48,7 @@
 	if (channel != DMA_VIRTUAL_FLOPPY)
 		printk("arch_enable_dma: invalid channel %d\n", channel);
 	else {
+		struct pt_regs regs;
 		void *fiqhandler_start;
 		unsigned int fiqhandler_length;
 		extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
@@ -67,8 +68,10 @@
 			return;
 		}
 		memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length);
-		flush_page_to_ram(0);
-		floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE);
+		regs.ARM_r9 = dma->buf.length;
+		regs.ARM_r10 = __bus_to_virt(dma->buf.address);
+		regs.ARM_fp = (int)PCIO_FLOPPYDMABASE;
+		set_fiq_regs(&regs);
 		enable_irq(dma->dma_irq);
 	}
 }
@@ -83,7 +86,12 @@
 	}
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	dma[DMA_VIRTUAL_FLOPPY].dma_irq = 64;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-arc.c linux.18p17/arch/arm/kernel/dma-arc.c
--- linux.vanilla/arch/arm/kernel/dma-arc.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-arc.c	Fri Sep 15 23:28:37 2000
@@ -1,10 +1,11 @@
 /*
  * arch/arm/kernel/dma-arc.c
  *
- * Copyright (C) 1998 Dave Gilbert / Russell King
+ * Copyright (C) 1998-1999 Dave Gilbert / Russell King
  *
  * DMA functions specific to Archimedes architecture
  */
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/init.h>
 
@@ -14,8 +15,11 @@
 
 #include "dma.h"
 
-int arch_request_dma(dmach_t channel, dma_t *dma)
+#define DEBUG
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char * dev_id)
 {
+  printk("arch_request_dma channel=%d F0=%d F1=%d\n",channel,DMA_VIRTUAL_FLOPPY0,DMA_VIRTUAL_FLOPPY1);
 	if (channel == DMA_VIRTUAL_FLOPPY0 ||
 	    channel == DMA_VIRTUAL_FLOPPY1)
 		return 0;
@@ -25,16 +29,13 @@
 
 void arch_free_dma(dmach_t channel, dma_t *dma)
 {
-	if (channel != DMA_VIRTUAL_FLOPPY0 &&
-	    channel != DMA_VIRTUAL_FLOPPY1)
-		return 0;
-	else
-		return -EINVAL;
 }
 
 void arch_enable_dma(dmach_t channel, dma_t *dma)
 {
+  printk("arch_enable_dma channel=%d F0=%d F1=%d\n",channel,DMA_VIRTUAL_FLOPPY0,DMA_VIRTUAL_FLOPPY1);
 	switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD1772
 	case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
 		switch (dma->dma_mode) {
 		case DMA_MODE_READ: /* read */
@@ -96,9 +97,38 @@
 		restore_flags(flags);
 	}
 	break;
+#endif
 	}
 }
 
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+  switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD1772
+    case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
+        extern unsigned int fdc1772_bytestogo;
+
+        /* 10/1/1999 DAG - I presume its the number of bytes left? */
+        return fdc1772_bytestogo;
+      };
+      break;
+
+    case DMA_VIRTUAL_FLOPPY1: { /* Command completed */
+        /* 10/1/1999 DAG - Presume whether there is an outstanding command? */
+        extern unsigned int fdc1772_fdc_int_done;
+
+        return (fdc1772_fdc_int_done==0)?1:0; /* Explicit! If the int done is 0 then 1 int to go */
+      };
+      break;
+
+#endif
+
+    default:
+      printk("dma-arc.c:arch_get_dma_residue called with unknown/unconfigured DMA channel\n");
+      return 0;
+  };
+}
+
 void arch_disable_dma(dmach_t channel, dma_t *dma)
 {
 	if (channel != DMA_VIRTUAL_FLOPPY0 &&
@@ -108,7 +138,12 @@
 		disable_irq(dma->dma_irq);
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64;
 	dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 65;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-dummy.c linux.18p17/arch/arm/kernel/dma-dummy.c
--- linux.vanilla/arch/arm/kernel/dma-dummy.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-dummy.c	Fri Sep 15 23:28:37 2000
@@ -9,6 +9,10 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 
+#include <asm/spinlock.h>
+
+spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED;
+
 int request_dma(int channel, const char *device_id)
 {
 	return -EINVAL;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-ebsa285.c linux.18p17/arch/arm/kernel/dma-ebsa285.c
--- linux.vanilla/arch/arm/kernel/dma-ebsa285.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-ebsa285.c	Thu Jan  1 01:00:00 1970
@@ -1,101 +0,0 @@
-/*
- * arch/arm/kernel/dma-ebsa285.c
- *
- * Copyright (C) 1998 Phil Blundell
- *
- * DMA functions specific to EBSA-285/CATS architectures
- *
- * Changelog:
- *  09/11/1998	RMK	Split out ISA DMA functions to dma-isa.c
- */
-
-#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
-	switch (channel) {
-	case 0:
-	case 1:	/* 21285 internal channels */
-		return 0;
-
-	case 2 ... 9:
-		if (machine_is_cats())
-			return isa_request_dma(channel - 2, dma, dev_name);
-	}
-
-	return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
-	/* nothing to do */
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
-	int residue = 0;
-
-	switch (channel) {
-	case 0:
-	case 1:
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			residue = isa_get_dma_residue(channel - 2);
-#endif
-	}
-	return residue;
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
-	switch (channel) {
-	case 0:
-	case 1:
-		/*
-		 * Not yet implemented
-		 */
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			isa_enable_dma(channel - 2, dma);
-#endif
-	}
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
-	switch (channel) {
-	case 0:
-	case 1:
-		/*
-		 * Not yet implemented
-		 */
-		break;
-#ifdef CONFIG_CATS
-	case 2 ... 9:
-		if (machine_is_cats())
-			isa_disable_dma(channel - 2, dma);
-#endif
-	}
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
-	/* Nothing to do */
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-footbridge.c linux.18p17/arch/arm/kernel/dma-footbridge.c
--- linux.vanilla/arch/arm/kernel/dma-footbridge.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/kernel/dma-footbridge.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/kernel/dma-ebsa285.c
+ *
+ * Copyright (C) 1998 Phil Blundell
+ *
+ * DMA functions specific to EBSA-285/CATS architectures
+ *
+ * Changelog:
+ *  09-Nov-1998	RMK	Split out ISA DMA functions to dma-isa.c
+ *  17-Mar-1999	RMK	Allow any EBSA285-like architecture to have
+ *			ISA DMA controllers.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include "dma.h"
+#include "dma-isa.h"
+
+#ifdef CONFIG_ISA_DMA
+static int has_isa_dma;
+#else
+#define has_isa_dma 0
+#endif
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):	/* 21285 internal channels */
+		return 0;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			return isa_request_dma(channel - _ISA_DMA(0), dma, dev_name);
+	}
+
+	return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+	/* nothing to do */
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+	int residue = 0;
+
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			residue = isa_get_dma_residue(channel - _ISA_DMA(0), dma);
+	}
+	return residue;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		/*
+		 * Not yet implemented
+		 */
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			isa_enable_dma(channel - _ISA_DMA(0), dma);
+	}
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+	switch (channel) {
+	case _DC21285_DMA(0):
+	case _DC21285_DMA(1):
+		/*
+		 * Not yet implemented
+		 */
+		break;
+
+	case _ISA_DMA(0) ... _ISA_DMA(7):
+		if (has_isa_dma)
+			isa_disable_dma(channel - _ISA_DMA(0), dma);
+	}
+}
+
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+	return 0;
+}
+
+void __init arch_dma_init(dma_t *dma)
+{
+#ifdef CONFIG_ISA_DMA
+	has_isa_dma = isa_init_dma();
+#endif
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-isa.c linux.18p17/arch/arm/kernel/dma-isa.c
--- linux.vanilla/arch/arm/kernel/dma-isa.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-isa.c	Fri Sep 15 23:28:37 2000
@@ -11,6 +11,7 @@
  *  Copyright (C) 1998 Phil Blundell
  */
 #include <linux/sched.h>
+#include <linux/init.h>
 
 #include <asm/dma.h>
 #include <asm/io.h>
@@ -18,6 +19,11 @@
 #include "dma.h"
 #include "dma-isa.h"
 
+#define ISA_DMA_MODE_READ	0x44
+#define ISA_DMA_MODE_WRITE	0x48
+#define ISA_DMA_MODE_CASCADE	0xc0
+#define ISA_DMA_AUTOINIT	0x10
+
 #define ISA_DMA_MASK		0
 #define ISA_DMA_MODE		1
 #define ISA_DMA_CLRFF		2
@@ -40,10 +46,7 @@
 
 int isa_request_dma(int channel, dma_t *dma, const char *dev_name)
 {
-	if (channel != 4)
-		return 0;
-
-	return -EINVAL;
+	return 0;
 }
 
 void isa_free_dma(int channel, dma_t *dma)
@@ -56,25 +59,27 @@
 	unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
 	int count;
 
-	count = 1 + inb(io_port) + (inb(io_port) << 8);
+	count = 1 + inb(io_port);
+	count |= inb(io_port) << 8;
 
 	return channel < 4 ? count : (count << 1);
 }
 
 void isa_enable_dma(int channel, dma_t *dma)
 {
-	unsigned long address, length;
-
 	if (dma->invalid) {
+		unsigned long address, length;
+		unsigned int mode;
+
 		address = dma->buf.address;
 		length  = dma->buf.length - 1;
 
-		outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
 		outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
+		outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
 
 		if (channel >= 4) {
 			address >>= 1;
-			length = (length >> 1) & 0xfe; /* why &0xfe? */
+			length >>= 1;
 		}
 
 		outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
@@ -85,17 +90,31 @@
 		outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
 		outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
 
-		outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]);
+		mode = channel & 3;
 
-		switch (dma->dma_mode) {
+		switch (dma->dma_mode & DMA_MODE_MASK) {
 		case DMA_MODE_READ:
+			mode |= ISA_DMA_MODE_READ;
 			dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length);
 			break;
 
 		case DMA_MODE_WRITE:
+			mode |= ISA_DMA_MODE_WRITE;
 			dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length);
 			break;
+
+		case DMA_MODE_CASCADE:
+			mode |= ISA_DMA_MODE_CASCADE;
+			break;
+
+		default:
+			break;
 		}
+
+		if (dma->dma_mode & DMA_AUTOINIT)
+			mode |= ISA_DMA_AUTOINIT;
+
+		outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
 		dma->invalid = 0;
 	}
 	outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
@@ -104,4 +123,57 @@
 void isa_disable_dma(int channel, dma_t *dma)
 {
 	outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
+}
+
+__initfunc(int isa_init_dma(void))
+{
+	int dmac_found;
+
+	outb(0xff, 0x0d);
+	outb(0xff, 0xda);
+
+	outb(0x55, 0x00);
+	outb(0xaa, 0x00);
+
+	dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa;
+
+	if (dmac_found) {
+		int channel;
+
+		for (channel = 0; channel < 8; channel++)
+			isa_disable_dma(channel, NULL);
+
+		outb(0x40, 0x0b);
+		outb(0x41, 0x0b);
+		outb(0x42, 0x0b);
+		outb(0x43, 0x0b);
+
+		outb(0xc0, 0xd6);
+		outb(0x41, 0xd6);
+		outb(0x42, 0xd6);
+		outb(0x43, 0xd6);
+
+		outb(0, 0xd4);
+
+		outb(0x10, 0x08);
+		outb(0x10, 0xd0);
+
+		/*
+		 * Is this correct?  According to
+		 * my documentation, it doesn't
+		 * appear to be.  It should be
+		 * outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
+		 */
+		outb(0x30, 0x40b);
+		outb(0x31, 0x40b);
+		outb(0x32, 0x40b);
+		outb(0x33, 0x40b);
+		outb(0x31, 0x4d6);
+		outb(0x32, 0x4d6);
+		outb(0x33, 0x4d6);
+
+		request_dma(DMA_ISA_CASCADE, "cascade");
+	}
+
+	return dmac_found;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-isa.h linux.18p17/arch/arm/kernel/dma-isa.h
--- linux.vanilla/arch/arm/kernel/dma-isa.h	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-isa.h	Fri Sep 15 23:28:37 2000
@@ -23,3 +23,7 @@
  */
 void isa_disable_dma(int channel, dma_t *dma);
 
+/*
+ * Initialise DMA
+ */
+int isa_init_dma(void);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-rpc.c linux.18p17/arch/arm/kernel/dma-rpc.c
--- linux.vanilla/arch/arm/kernel/dma-rpc.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-rpc.c	Fri Sep 15 23:28:37 2000
@@ -11,10 +11,10 @@
 #include <linux/init.h>
 
 #include <asm/page.h>
-#include <asm/pgtable.h>
 #include <asm/dma.h>
 #include <asm/fiq.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 #include <asm/hardware.h>
 #include <asm/uaccess.h>
 
@@ -223,8 +223,9 @@
 		break;
 
 	case DMA_VIRTUAL_FLOPPY: {
-		extern int floppy_fiqresidual(void);
-		residue = floppy_fiqresidual();
+		struct pt_regs regs;
+		get_fiq_regs(&regs);
+		return regs.ARM_r9;
 		}
 		break;
 	}
@@ -286,7 +287,6 @@
 		set_fiq_handler(fiqhandler_start, fiqhandler_length);
 		set_fiq_regs(&regs);
 		enable_irq(dma->dma_irq);
-
 		}
 		break;
 
@@ -319,14 +319,56 @@
 	}
 }
 
-__initfunc(void arch_dma_init(dma_t *dma))
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle)
+{
+	int tcr, speed;
+
+	if (cycle < 188)
+		speed = 3;
+	else if (cycle <= 250)
+		speed = 2;
+	else if (cycle < 438)
+		speed = 1;
+	else
+		speed = 0;
+
+	tcr = inb(IOMD_DMATCR);
+	speed &= 3;
+
+	switch (channel) {
+	case DMA_0:
+		tcr = (tcr & ~0x03) | speed;
+		break;
+
+	case DMA_1:
+		tcr = (tcr & ~0x0c) | (speed << 2);
+		break;
+
+	case DMA_2:
+		tcr = (tcr & ~0x30) | (speed << 4);
+		break;
+
+	case DMA_3:
+		tcr = (tcr & ~0xc0) | (speed << 6);
+		break;
+
+	default:
+		break;
+	}
+
+	outb(tcr, IOMD_DMATCR);
+
+	return speed;
+}
+
+void __init arch_dma_init(dma_t *dma)
 {
 	outb(0, IOMD_IO0CR);
 	outb(0, IOMD_IO1CR);
 	outb(0, IOMD_IO2CR);
 	outb(0, IOMD_IO3CR);
 
-//	outb(0xf0, IOMD_DMATCR);
+	outb(0xa0, IOMD_DMATCR);
 
 	dma[0].dma_base = ioaddr(IOMD_IO0CURA);
 	dma[0].dma_irq  = IRQ_DMA0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma-vnc.c linux.18p17/arch/arm/kernel/dma-vnc.c
--- linux.vanilla/arch/arm/kernel/dma-vnc.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma-vnc.c	Thu Jan  1 01:00:00 1970
@@ -1,51 +0,0 @@
-/*
- * arch/arm/kernel/dma-vnc.c
- *
- * Copyright (C) 1998 Russell King
- */
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
-	if (channel < 8)
-		return isa_request_dma(channel, dma, dev_name);
-	return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
-	isa_free_dma(channel, dma);
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
-	return isa_get_dma_residue(channel, dma);
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
-	isa_enable_dma(channel, dma);
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
-	isa_disable_dma(channel, dma);
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
-	/* Nothing to do */
-}
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma.c linux.18p17/arch/arm/kernel/dma.c
--- linux.vanilla/arch/arm/kernel/dma.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma.c	Fri Sep 15 23:28:37 2000
@@ -21,7 +21,6 @@
 #include <linux/init.h>
 
 #include <asm/page.h>
-#include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
@@ -201,6 +200,12 @@
 		printk (KERN_ERR "Trying to disable free DMA%d\n", channel);
 }
 
+void set_dma_speed(dmach_t channel, int cycle_ns)
+{
+	dma_chan[channel].speed =
+	   arch_set_dma_speed(channel, &dma_chan[channel], cycle_ns);
+}
+
 int get_dma_residue(dmach_t channel)
 {
 	return arch_get_dma_residue(channel, &dma_chan[channel]);
@@ -214,8 +219,9 @@
 EXPORT_SYMBOL(set_dma_mode);
 EXPORT_SYMBOL(get_dma_residue);
 EXPORT_SYMBOL(set_dma_sg);
+EXPORT_SYMBOL(set_dma_speed);
 
-__initfunc(void init_dma(void))
+void __init init_dma(void)
 {
 	arch_dma_init(dma_chan);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/dma.h linux.18p17/arch/arm/kernel/dma.h
--- linux.vanilla/arch/arm/kernel/dma.h	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/dma.h	Fri Sep 15 23:28:37 2000
@@ -15,6 +15,7 @@
 	unsigned int	active:1;	/* Transfer active		*/
 	unsigned int	invalid:1;	/* Address/Count changed	*/
 	dmamode_t	dma_mode;	/* DMA mode			*/
+	int		speed;		/* DMA speed			*/
 
 	unsigned int	lock;		/* Device is allocated		*/
 	const char	*device_id;	/* Device name			*/
@@ -62,6 +63,15 @@
  * Returns  : Number of bytes left to DMA
  */
 int arch_get_dma_residue(dmach_t channel, dma_t *dma);
+
+/* Prototype: int arch_set_dma_speed(channel, dma, cycle)
+ * Purpose  : Convert a cycle time to a register setting
+ * Params   : channel - DMA channel number
+ *          : dma     - DMA structure for channel
+ *          : cycle   - cycle time in NS
+ * Returns  : setting for 'dma->speed'
+ */
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle);
 
 /* Prototype: void arch_dma_init(dma)
  * Purpose  : Initialise architecture specific DMA
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/ecard.c linux.18p17/arch/arm/kernel/ecard.c
--- linux.vanilla/arch/arm/kernel/ecard.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/ecard.c	Mon Oct  2 10:12:17 2000
@@ -7,32 +7,42 @@
  *
  * Created from information from Acorns RiscOS3 PRMs
  *
- * 08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether podule slot.
+ * 08-Dec-1996	RMK	Added code for the 9'th expansion card - the ether
+ *			podule slot.
  * 06-May-1997  RMK	Added blacklist for cards whose loader doesn't work.
- * 12-Sep-1997	RMK	Created new handling of interrupt enables/disables - cards can
- *			now register their own routine to control interrupts (recommended).
- * 29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled on reset from
- *			Linux. (Caused cards not to respond under RiscOS without hard reset).
+ * 12-Sep-1997	RMK	Created new handling of interrupt enables/disables
+ *			- cards can now register their own routine to control
+ *			interrupts (recommended).
+ * 29-Sep-1997	RMK	Expansion card interrupt hardware not being re-enabled
+ *			on reset from Linux. (Caused cards not to respond
+ *			under RiscOS without hard reset).
  * 15-Feb-1998	RMK	Added DMA support
  * 12-Sep-1998	RMK	Added EASI support
+ * 10-Jan-1999	RMK	Run loaders in a simulated RISC OS environment.
+ * 17-Apr-1999	RMK	Support for EASI Type C cycles.
  */
 
 #define ECARD_C
+#define __KERNEL_SYSCALLS__
 
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
 #include <linux/init.h>
 
-#include <asm/io.h>
-#include <asm/hardware.h>
+#include <asm/dma.h>
 #include <asm/ecard.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/dma.h>
+#include <asm/pgtable.h>
 
 #ifdef CONFIG_ARCH_ARC
 #include <asm/arch/oldlatches.h>
@@ -40,45 +50,416 @@
 #define oldlatch_init()
 #endif
 
-#define BLACKLIST_NAME(m,p,s)	{ m, p, NULL, s }
-#define BLACKLIST_LOADER(m,p,l)	{ m, p, l, NULL }
-#define BLACKLIST_NOLOADER(m,p)	{ m, p, noloader, blacklisted_str }
-#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+enum req {
+	req_readbytes,
+	req_reset
+};
 
-extern unsigned long atomwide_serial_loader[], oak_scsi_loader[], noloader[];
-static const char blacklisted_str[] = "*loader s/w is not 32-bit compliant*";
+struct ecard_request {
+	enum req	req;
+	ecard_t		*ec;
+	unsigned int	address;
+	unsigned int	length;
+	unsigned int	use_loader;
+	void		*buffer;
+};
 
-static const struct expcard_blacklist {
+struct expcard_blacklist {
 	unsigned short	 manufacturer;
 	unsigned short	 product;
-	const loader_t 	 loader;
 	const char	*type;
-} blacklist[] = {
-/* Cards without names */
-    BLACKLIST_NAME(MANU_ACORN,		PROD_ACORN_ETHER1,	"Acorn Ether1"),
-
-/* Cards with corrected loader */
-  BLACKLIST_LOADER(MANU_ATOMWIDE,	PROD_ATOMWIDE_3PSERIAL,	atomwide_serial_loader),
-  BLACKLIST_LOADER(MANU_OAK,		PROD_OAK_SCSI,		oak_scsi_loader),
+};
 
-/* Supported cards with broken loader */
-  { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI" },
+static ecard_t *cards;
+static ecard_t *slot_to_expcard[MAX_ECARDS];
+static unsigned int ectcr;
+#ifdef HAS_EXPMASK
+static unsigned int have_expmask;
+#endif
 
-/* Unsupported cards with no loader */
-  BLACKLIST_NOLOADER(MANU_MCS,		PROD_MCS_CONNECT32)
+/* List of descriptions of cards which don't have an extended
+ * identification, or chunk directories containing a description.
+ */
+static const struct expcard_blacklist __init blacklist[] = {
+	{ MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
 };
 
+asmlinkage extern int
+ecard_loader_reset(volatile unsigned char *pa, loader_t loader);
+asmlinkage extern int
+ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader);
 extern int setup_arm_irq(int, struct irqaction *);
+extern void do_ecard_IRQ(int, struct pt_regs *);
+
+
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs);
+
+static struct irqaction irqexpansioncard = {
+	ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL
+};
+
+static inline unsigned short
+ecard_getu16(unsigned char *v)
+{
+	return v[0] | v[1] << 8;
+}
+
+static inline signed long
+ecard_gets24(unsigned char *v)
+{
+	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+}
+
+static inline ecard_t *
+slot_to_ecard(unsigned int slot)
+{
+	return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
+}
 
+/* ===================== Expansion card daemon ======================== */
 /*
- * from linux/arch/arm/kernel/irq.c
+ * Since the loader programs on the expansion cards need to be run
+ * in a specific environment, create a separate task with this
+ * environment up, and pass requests to this task as and when we
+ * need to.
+ *
+ * This should allow 99% of loaders to be called from Linux.
+ *
+ * From a security standpoint, we trust the card vendors.  This
+ * may be a misplaced trust.
  */
-extern void do_ecard_IRQ(int irq, struct pt_regs *);
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+#define POD_INT_ADDR(x)	((volatile unsigned char *)\
+			 ((BUS_ADDR((x)) - IO_BASE) + IO_START))
 
-static ecard_t expcard[MAX_ECARDS];
-static signed char irqno_to_expcard[16];
-static unsigned int ecard_numcards, ecard_numirqcards;
-static unsigned int have_expmask;
+static void
+ecard_task_reset(struct ecard_request *req)
+{
+	if (req->ec == NULL) {
+		ecard_t *ec;
+
+		for (ec = cards; ec; ec = ec->next) {
+			if (ec->loader)
+				ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
+						   ec->loader);
+		}
+	} else if (req->ec->loader)
+		ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
+				   req->ec->loader);
+}
+
+static void
+ecard_task_readbytes(struct ecard_request *req)
+{
+	unsigned char *buf = (unsigned char *)req->buffer;
+	volatile unsigned char *base_addr =
+		(volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
+	unsigned int len = req->length;
+
+	if (req->ec->slot_no == 8) {
+		/*
+		 * The card maintains an index which
+		 * increments the address into a 4096-byte
+		 * page on each access.  We need to keep
+		 * track of the counter.
+		 */
+		static unsigned int index;
+		unsigned int offset, page;
+		unsigned char byte = 0; /* keep gcc quiet */
+
+		offset = req->address & 4095;
+		page   = req->address >> 12;
+
+		if (page > 256)
+			return;
+
+		page *= 4;
+
+		if (offset == 0 || index > offset) {
+			/*
+			 * We need to reset the index counter.
+			 */
+			*base_addr = 0;
+			index = 0;
+		}
+
+		while (index <= offset) {
+			byte = base_addr[page];
+			index += 1;
+		}
+
+		while (len--) {
+			*buf++ = byte;
+			if (len) {
+				byte = base_addr[page];
+				index += 1;
+			}
+		}
+	} else {
+		unsigned int off = req->address;
+
+		if (!req->use_loader || !req->ec->loader) {
+			off *= 4;
+			while (len--) {
+				*buf++ = base_addr[off];
+				off += 4;
+			}
+		} else {
+			while(len--) {
+				/*
+				 * The following is required by some
+				 * expansion card loader programs.
+				 */
+				*(unsigned long *)0x108 = 0;
+				*buf++ = ecard_loader_read(off++, base_addr,
+							   req->ec->loader);
+			}
+		}
+	}
+
+}
+
+#ifdef CONFIG_CPU_32
+static pid_t ecard_pid;
+static struct wait_queue *ecard_wait;
+static struct wait_queue *ecard_done;
+static struct ecard_request *ecard_req;
+
+/*
+ * Set up the expansion card daemon's environment.
+ */
+static void
+ecard_init_task(void)
+{
+	/* We want to set up the page tables for the following mapping:
+	 *  Virtual	Physical
+	 *  0x03000000	0x03000000
+	 *  0x03010000	unmapped
+	 *  0x03210000	0x03210000
+	 *  0x03400000	unmapped
+	 *  0x08000000	0x08000000
+	 *  0x10000000	unmapped
+	 *
+	 * FIXME: we don't follow this 100% yet.
+	 */
+	pgd_t *src_pgd, *dst_pgd;
+	unsigned int dst_addr = IO_START;
+
+	src_pgd = pgd_offset(current->mm, IO_BASE);
+	dst_pgd = pgd_offset(current->mm, dst_addr);
+
+	while (dst_addr < IO_START + IO_SIZE) {
+		*dst_pgd++ = *src_pgd++;
+		dst_addr += PGDIR_SIZE;
+	}
+
+	flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
+
+	dst_addr = EASI_START;
+	src_pgd = pgd_offset(current->mm, EASI_BASE);
+	dst_pgd = pgd_offset(current->mm, dst_addr);
+
+	while (dst_addr < EASI_START + EASI_SIZE) {
+		*dst_pgd++ = *src_pgd++;
+		dst_addr += PGDIR_SIZE;
+	}
+
+	flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
+}
+
+static int
+ecard_task(void * unused)
+{
+	current->session = 1;
+	current->pgrp = 1;
+
+	/*
+	 * We don't want /any/ signals, not even SIGKILL
+	 */
+	sigfillset(&current->blocked);
+	sigemptyset(&current->signal);
+
+	strcpy(current->comm, "kecardd");
+
+	/*
+	 * Set up the environment
+	 */
+	ecard_init_task();
+
+	while (1) {
+		struct ecard_request *req;
+
+		do {
+			req = xchg(&ecard_req, NULL);
+
+			if (req == NULL) {
+				sigemptyset(&current->signal);
+				interruptible_sleep_on(&ecard_wait);
+			}
+		} while (req == NULL);
+
+		switch (req->req) {
+		case req_readbytes:
+			ecard_task_readbytes(req);
+			break;
+
+		case req_reset:
+			ecard_task_reset(req);
+			break;
+		}
+		wake_up(&ecard_done);
+	}
+}
+
+/*
+ * Wake the expansion card daemon to action our request.
+ *
+ * FIXME: The test here is not sufficient to detect if the
+ * kcardd is running.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+	/*
+	 * If we're called from task 0, or from an
+	 * interrupt (will be keyboard interrupt),
+	 * we forcefully set up the memory map, and
+	 * call the loader.  We can't schedule, or
+	 * sleep for this call.
+	 */
+	if ((current == task[0] || in_interrupt()) &&
+	    req->req == req_reset && req->ec == NULL) {
+		ecard_init_task();
+		ecard_task_reset(req);
+	} else {
+		if (ecard_pid <= 0)
+			ecard_pid = kernel_thread(ecard_task, NULL, 0);
+
+		ecard_req = req;
+
+		wake_up(&ecard_wait);
+
+		sleep_on(&ecard_done);
+	}
+}
+#else
+/*
+ * On 26-bit processors, we don't need the kcardd thread to access the
+ * expansion card loaders.  We do it directly.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+	if (req->req == req_reset)
+		ecard_task_reset(req);
+	else
+		ecard_task_readbytes(req);
+}
+#endif
+
+/* ======================= Mid-level card control ===================== */
+/*
+ * This is called to reset the loaders for each expansion card on reboot.
+ *
+ * This is required to make sure that the card is in the correct state
+ * that RiscOS expects it to be.
+ */
+void
+ecard_reset(int slot)
+{
+	struct ecard_request req;
+
+	req.req = req_reset;
+
+	if (slot < 0)
+		req.ec = NULL;
+	else
+		req.ec = slot_to_ecard(slot);
+
+	ecard_call(&req);
+
+#ifdef HAS_EXPMASK
+	if (have_expmask && slot < 0) {
+		have_expmask |= ~0;
+		EXPMASK_ENABLE = have_expmask;
+	}
+#endif
+}
+
+static void
+ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+{
+	struct ecard_request req;
+
+	req.req		= req_readbytes;
+	req.ec		= ec;
+	req.address	= off;
+	req.length	= len;
+	req.use_loader	= useld;
+	req.buffer	= addr;
+
+	ecard_call(&req);
+}
+
+int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+{
+	struct ex_chunk_dir excd;
+	int index = 16;
+	int useld = 0;
+
+	if (!ec->cid.cd)
+		return 0;
+
+	while(1) {
+		ecard_readbytes(&excd, ec, index, 8, useld);
+		index += 8;
+		if (c_id(&excd) == 0) {
+			if (!useld && ec->loader) {
+				useld = 1;
+				index = 0;
+				continue;
+			}
+			return 0;
+		}
+		if (c_id(&excd) == 0xf0) { /* link */
+			index = c_start(&excd);
+			continue;
+		}
+		if (c_id(&excd) == 0x80) { /* loader */
+			if (!ec->loader) {
+				ec->loader = (loader_t)kmalloc(c_len(&excd),
+							       GFP_KERNEL);
+				if (ec->loader)
+					ecard_readbytes(ec->loader, ec,
+							(int)c_start(&excd),
+							c_len(&excd), useld);
+				else
+					return 0;
+			}
+			continue;
+		}
+		if (c_id(&excd) == id && num-- == 0)
+			break;
+	}
+
+	if (c_id(&excd) & 0x80) {
+		switch (c_id(&excd) & 0x70) {
+		case 0x70:
+			ecard_readbytes((unsigned char *)excd.d.string, ec,
+					(int)c_start(&excd), c_len(&excd),
+					useld);
+			break;
+		case 0x00:
+			break;
+		}
+	}
+	cd->start_offset = c_start(&excd);
+	memcpy(cd->d.string, excd.d.string, 256);
+	return 1;
+}
+
+/* ======================= Interrupt control ============================ */
 
 static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
 {
@@ -100,6 +481,11 @@
 #endif
 }
 
+static int ecard_def_irq_pending(ecard_t *ec)
+{
+	return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
+}
+
 static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
 {
 	panic("ecard_def_fiq_enable called - impossible");
@@ -110,11 +496,18 @@
 	panic("ecard_def_fiq_disable called - impossible");
 }
 
+static int ecard_def_fiq_pending(ecard_t *ec)
+{
+	return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
+}
+
 static expansioncard_ops_t ecard_default_ops = {
 	ecard_def_irq_enable,
 	ecard_def_irq_disable,
+	ecard_def_irq_pending,
 	ecard_def_fiq_enable,
-	ecard_def_fiq_disable
+	ecard_def_fiq_disable,
+	ecard_def_fiq_pending
 };
 
 /*
@@ -125,10 +518,9 @@
  */
 void ecard_enableirq(unsigned int irqnr)
 {
-	irqnr &= 7;
-	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -142,10 +534,9 @@
 
 void ecard_disableirq(unsigned int irqnr)
 {
-	irqnr &= 7;
-	if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -156,10 +547,9 @@
 
 void ecard_enablefiq(unsigned int fiqnr)
 {
-	fiqnr &= 7;
-	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+	ecard_t *ec = slot_to_ecard(fiqnr);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -173,10 +563,9 @@
 
 void ecard_disablefiq(unsigned int fiqnr)
 {
-	fiqnr &= 7;
-	if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
-		ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+	ecard_t *ec = slot_to_ecard(fiqnr);
 
+	if (ec) {
 		if (!ec->ops)
 			ec->ops = &ecard_default_ops;
 
@@ -185,41 +574,89 @@
 	}
 }
 
-static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_dump_irq_state(ecard_t *ec)
+{
+	printk("  %d: %sclaimed, ",
+	       ec->slot_no,
+	       ec->claimed ? "" : "not ");
+
+	if (ec->ops && ec->ops->irqpending &&
+	    ec->ops != &ecard_default_ops)
+		printk("irq %spending\n",
+		       ec->ops->irqpending(ec) ? "" : "not ");
+	else
+		printk("irqaddr %p, mask = %02X, status = %02X\n",
+		       ec->irqaddr, ec->irqmask, *ec->irqaddr);
+}
+
+static void
+ecard_check_lockup(void)
 {
-	const int num_cards = ecard_numirqcards;
-	int i, called = 0;
+	static int last, lockup;
+	ecard_t *ec;
 
-	for (i = 0; i < num_cards; i++) {
-		if (expcard[i].claimed && expcard[i].irq &&
-		    (!expcard[i].irqmask ||
-		     expcard[i].irqaddr[0] & expcard[i].irqmask)) {
-			do_ecard_IRQ(expcard[i].irq, regs);
-			called ++;
+	/*
+	 * If the timer interrupt has not run since the last million
+	 * unrecognised expansion card interrupts, then there is
+	 * something seriously wrong.  Disable the expansion card
+	 * interrupts so at least we can continue.
+	 *
+	 * Maybe we ought to start a timer to re-enable them some time
+	 * later?
+	 */
+	if (last == jiffies) {
+		lockup += 1;
+		if (lockup > 1000000) {
+			printk(KERN_ERR "\nInterrupt lockup detected - "
+			       "disabling all expansion card interrupts\n");
+
+			disable_irq(IRQ_EXPANSIONCARD);
+
+			printk("Expansion card IRQ state:\n");
+
+			for (ec = cards; ec; ec = ec->next)
+				ecard_dump_irq_state(ec);
 		}
+	} else
+		lockup = 0;
+
+	/*
+	 * If we did not recognise the source of this interrupt,
+	 * warn the user, but don't flood the user with these messages.
+	 */
+	if (!last || time_after(jiffies, last + 5*HZ)) {
+		last = jiffies;
+		printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
 	}
-	cli();
-	if (called == 0) {
-		static int last, lockup;
+}
 
-		if (last == jiffies) {
-			lockup += 1;
-			if (lockup > 1000000) {
-				printk(KERN_ERR "\nInterrupt lockup detected - disabling expansion card IRQs\n");
-				disable_irq(intr_no);
-				printk("Expansion card IRQ state:\n");
-				for (i = 0; i < num_cards; i++)
-					printk("  %d: %sclaimed, irqaddr = %p, irqmask = %X, status=%X\n", expcard[i].irq - 32,
-						expcard[i].claimed ? "" : "not", expcard[i].irqaddr, expcard[i].irqmask, *expcard[i].irqaddr);
-			}
-		} else
-			lockup = 0;
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+{
+	ecard_t *ec;
+	int called = 0;
+
+	for (ec = cards; ec; ec = ec->next) {
+		int pending;
+
+		if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
+			continue;
+
+		if (ec->ops && ec->ops->irqpending)
+			pending = ec->ops->irqpending(ec);
+		else
+			pending = ecard_default_ops.irqpending(ec);
 
-		if (!last || time_after(jiffies, last + 5*HZ)) {
-			last = jiffies;
-			printk(KERN_ERR "\nUnrecognised interrupt from backplane\n");
+		if (pending) {
+			do_ecard_IRQ(ec->irq, regs);
+			called ++;
 		}
 	}
+	cli();
+
+	if (called == 0)
+		ecard_check_lockup();
 }
 
 #ifdef HAS_EXPMASK
@@ -234,31 +671,35 @@
 	0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
 };
 
-static void ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
 {
 	const unsigned int statusmask = 15;
 	unsigned int status;
 
 	status = EXPMASK_STATUS & statusmask;
 	if (status) {
-		unsigned int irqno;
+		unsigned int slot;
 		ecard_t *ec;
 again:
-		irqno = first_set[status];
-		ec = expcard + irqno_to_expcard[irqno];
+		slot = first_set[status];
+		ec = slot_to_ecard(slot);
 		if (ec->claimed) {
 			unsigned int oldexpmask;
 			/*
-			 * this ugly code is so that we can operate a prioritorising system.
+			 * this ugly code is so that we can operate a
+			 * prioritorising system:
+			 *
 			 * Card 0 	highest priority
 			 * Card 1
 			 * Card 2
 			 * Card 3	lowest priority
+			 *
 			 * Serial cards should go in 0/1, ethernet/scsi in 2/3
 			 * otherwise you will lose serial data at high speeds!
 			 */
 			oldexpmask = have_expmask;
-			EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]);
+			EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
 			sti();
 			do_ecard_IRQ(ec->irq, regs);
 			cli();
@@ -267,15 +708,18 @@
 			if (status)
 				goto again;
 		} else {
-			printk(KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno);
-			EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno));
+			printk(KERN_WARNING "card%d: interrupt from unclaimed "
+			       "card???\n", slot);
+			EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
 		}
 	} else
 		printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
 }
 
-static int ecard_checkirqhw(void)
+__initfunc(static void
+ecard_probeirqhw(void))
 {
+	ecard_t *ec;
 	int found;
 
 	EXPMASK_ENABLE = 0x00;
@@ -283,62 +727,78 @@
 	found = ((EXPMASK_STATUS & 15) == 0);
 	EXPMASK_ENABLE = 0xff;
 
-	return found;
+	if (!found)
+		return;
+
+	printk(KERN_DEBUG "Expansion card interrupt "
+	       "management hardware found\n");
+
+	irqexpansioncard.handler = ecard_irq_expmask;
+
+	/* for each card present, set a bit to '1' */
+	have_expmask = 0x80000000;
+
+	for (ec = cards; ec; ec = ec->next)
+		have_expmask |= 1 << ec->slot_no;
+
+	EXPMASK_ENABLE = have_expmask;
 }
+#else
+#define ecard_probeirqhw()
+#endif
+
+#ifndef IO_EC_MEMC8_BASE
+#define IO_EC_MEMC8_BASE 0
 #endif
 
-static void ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
 {
-	extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader);
-	unsigned char *a = (unsigned char *)addr;
+	unsigned long address = 0;
+	int slot = ec->slot_no;
 
-	if (ec->slot_no == 8) {
-		static unsigned int lowaddress;
-		unsigned int laddr, haddr;
-		unsigned char byte = 0; /* keep gcc quiet */
+	if (ec->slot_no == 8)
+		return IO_EC_MEMC8_BASE;
 
-		laddr = off & 4095;	/* number of bytes to read from offset + base addr */
-		haddr = off >> 12;	/* offset into card from base addr */
+	ectcr &= ~(1 << slot);
 
-		if (haddr > 256)
-			return;
+	switch (type) {
+	case ECARD_MEMC:
+		if (slot < 4)
+			address = IO_EC_MEMC_BASE + (slot << 12);
+		break;
 
-		/*
-		 * If we require a low address or address 0, then reset, and start again...
-		 */
-		if (!off || lowaddress > laddr) {
-			outb(0, ec->podaddr);
-			lowaddress = 0;
-		}
-		while (lowaddress <= laddr) {
-			byte = inb(ec->podaddr + haddr);
-			lowaddress += 1;
-		}
-		while (len--) {
-			*a++ = byte;
-			if (len) {
-				byte = inb(ec->podaddr + haddr);
-				lowaddress += 1;
-			}
-		}
-	} else {
-		if (!useld || !ec->loader) {
-			while(len--)
-				*a++ = inb(ec->podaddr + (off++));
-		} else {
-			while(len--) {
-				*(unsigned long *)0x108 = 0; /* hack for some loaders!!! */
-				*a++ = ecard_loader_read(off++, BUS_ADDR(ec->podaddr), ec->loader);
-			}
-		}
+	case ECARD_IOC:
+		if (slot < 4)
+			address = IO_EC_IOC_BASE + (slot << 12);
+#ifdef IO_EC_IOC4_BASE
+		else
+			address = IO_EC_IOC4_BASE + ((slot - 4) << 12);
+#endif
+		if (address)
+			address +=  speed << 17;
+		break;
+
+#ifdef IO_EC_EASI_BASE
+	case ECARD_EASI:
+		address = IO_EC_EASI_BASE + (slot << 22);
+		if (speed == ECARD_FAST)
+			ectcr |= 1 << slot;
+		break;
+#endif
 	}
+
+#ifdef IOMD_ECTCR
+	outb(ectcr, IOMD_ECTCR);
+#endif
+	return address;
 }
 
 static int ecard_prints(char *buffer, ecard_t *ec)
 {
 	char *start = buffer;
 
-	buffer += sprintf(buffer, "\n  %d: ", ec->slot_no);
+	buffer += sprintf(buffer, "  %d: %s ", ec->slot_no,
+			  ec->type == ECARD_EASI ? "EASI" : "    ");
 
 	if (ec->cid.id == 0) {
 		struct in_chunk_dir incd;
@@ -346,63 +806,59 @@
 		buffer += sprintf(buffer, "[%04X:%04X] ",
 			ec->cid.manufacturer, ec->cid.product);
 
-		if (!ec->card_desc && ec->cid.is && ec->cid.cd &&
+		if (!ec->card_desc && ec->cid.cd &&
 		    ecard_readchunk(&incd, ec, 0xf5, 0))
 			ec->card_desc = incd.d.string;
 
 		if (!ec->card_desc)
 			ec->card_desc = "*unknown*";
 
-		buffer += sprintf(buffer, "%s", ec->card_desc);
+		buffer += sprintf(buffer, "%s\n", ec->card_desc);
 	} else
-		buffer += sprintf(buffer, "Simple card %d", ec->cid.id);
+		buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
 
 	return buffer - start;
 }
 
-static inline unsigned short ecard_getu16(unsigned char *v)
-{
-	return v[0] | v[1] << 8;
-}
-
-static inline signed long ecard_gets24(unsigned char *v)
-{
-	return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
-}
-
 /*
  * Probe for an expansion card.
  *
  * If bit 1 of the first byte of the card is set, then the
  * card does not exist.
  */
-__initfunc(static int ecard_probe(int card, int freeslot, card_type_t type))
+__initfunc(static int
+ecard_probe(int slot, card_type_t type))
 {
-	ecard_t *ec = expcard + freeslot;
+	ecard_t **ecp;
+	ecard_t *ec;
 	struct ex_ecid cid;
 	char buffer[200];
-	int i;
+	int i, rc = -ENOMEM;
 
-	irqno_to_expcard[card] = -1;
+	ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
 
-	ec->slot_no	= card;
+	if (!ec)
+		goto nodev;
+
+	memset(ec, 0, sizeof(ecard_t));
+
+	ec->slot_no	= slot;
+	ec->type	= type;
 	ec->irq		= NO_IRQ;
 	ec->fiq		= NO_IRQ;
 	ec->dma		= NO_DMA;
 	ec->card_desc	= NULL;
 	ec->ops		= &ecard_default_ops;
 
+	rc = -ENODEV;
 	if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
-		return 0;
+		goto nodev;
 
 	cid.r_zero = 1;
 	ecard_readbytes(&cid, ec, 0, 16, 0);
 	if (cid.r_zero)
-		return 0;
-
-	irqno_to_expcard[card] = freeslot;
+		goto nodev;
 
-	ec->type	= type;
 	ec->cid.id	= cid.r_id;
 	ec->cid.cd	= cid.r_cd;
 	ec->cid.is	= cid.r_is;
@@ -415,9 +871,9 @@
 	ec->cid.fiqmask = cid.r_fiqmask;
 	ec->cid.fiqoff  = ecard_gets24(cid.r_fiqoff);
 	ec->fiqaddr	=
-	ec->irqaddr	= (unsigned char *)BUS_ADDR(ec->podaddr);
+	ec->irqaddr	= (unsigned char *)ioaddr(ec->podaddr);
 
-	if (ec->cid.cd && ec->cid.is) {
+	if (ec->cid.is) {
 		ec->irqmask = ec->cid.irqmask;
 		ec->irqaddr += ec->cid.irqoff;
 		ec->fiqmask = ec->cid.fiqmask;
@@ -430,88 +886,69 @@
 	for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
 		if (blacklist[i].manufacturer == ec->cid.manufacturer &&
 		    blacklist[i].product == ec->cid.product) {
-			ec->loader = blacklist[i].loader;
 			ec->card_desc = blacklist[i].type;
 			break;
 		}
 
-	ecard_prints(buffer, ec);
-	printk("%s", buffer);
-
-	ec->irq = 32 + card;
+	ec->irq = 32 + slot;
 #ifdef IO_EC_MEMC8_BASE
-	if (card == 8)
+	if (slot == 8)
 		ec->irq = 11;
 #endif
 #ifdef CONFIG_ARCH_RPC
 	/* On RiscPC, only first two slots have DMA capability */
-	if (card < 2)
-		ec->dma = 2 + card;
+	if (slot < 2)
+		ec->dma = 2 + slot;
 #endif
 #if 0	/* We don't support FIQs on expansion cards at the moment */
-	ec->fiq = 96 + card;
+	ec->fiq = 96 + slot;
 #endif
 
-	return 1;
-}
+	rc = 0;
 
-/*
- * This is called to reset the loaders for each expansion card on reboot.
- *
- * This is required to make sure that the card is in the correct state
- * that RiscOS expects it to be.
- */
-void ecard_reset(int card)
-{
-	extern int ecard_loader_reset(volatile unsigned int pa, loader_t loader);
+	for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
 
-	if (card >= ecard_numcards)
-		return;
-    
-	if (card < 0) {
-		for (card = 0; card < ecard_numcards; card++)
-			if (expcard[card].loader)
-				ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
-							expcard[card].loader);
-	} else
-		if (expcard[card].loader)
-			ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
-						expcard[card].loader);
+	*ecp = ec;
 
-#ifdef HAS_EXPMASK
-	if (have_expmask) {
-		have_expmask |= ~0;
-		EXPMASK_ENABLE = have_expmask;
+nodev:
+	if (rc && ec)
+		kfree(ec);
+	else {
+		slot_to_expcard[slot] = ec;
+
+		ecard_prints(buffer, ec);
+		printk("%s", buffer);
 	}
-#endif
+	return rc;
 }
 
-static unsigned int ecard_startcard;
+static ecard_t *finding_pos;
 
 void ecard_startfind(void)
 {
-	ecard_startcard = 0;
+	finding_pos = NULL;
 }
 
 ecard_t *ecard_find(int cid, const card_ids *cids)
 {
-	int card;
+	if (!finding_pos)
+		finding_pos = cards;
+	else
+		finding_pos = finding_pos->next;
 
-	if (!cids) {
-		for (card = ecard_startcard; card < ecard_numcards; card++)
-			if (!expcard[card].claimed &&
-			    (expcard[card].cid.id ^ cid) == 0)
+	for (; finding_pos; finding_pos = finding_pos->next) {
+		if (finding_pos->claimed)
+			continue;
+
+		if (!cids) {
+			if ((finding_pos->cid.id ^ cid) == 0)
 				break;
-	} else {
-		for (card = ecard_startcard; card < ecard_numcards; card++) {
+		} else {
 			unsigned int manufacturer, product;
 			int i;
 
-			if (expcard[card].claimed)
-				continue;
-
-			manufacturer = expcard[card].cid.manufacturer;
-			product = expcard[card].cid.product;
+			manufacturer = finding_pos->cid.manufacturer;
+			product = finding_pos->cid.product;
 
 			for (i = 0; cids[i].manufacturer != 65535; i++)
 				if (manufacturer == cids[i].manufacturer &&
@@ -523,111 +960,24 @@
 		}
 	}
 
-	ecard_startcard = card + 1;
-
-	return card < ecard_numcards ? &expcard[card] : NULL;
+	return finding_pos;
 }
 
-int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+__initfunc(static void ecard_free_all(void))
 {
-	struct ex_chunk_dir excd;
-	int index = 16;
-	int useld = 0;
+	ecard_t *ec, *ecn;
 
-	if (!ec->cid.is || !ec->cid.cd)
-		return 0;
+	for (ec = cards; ec; ec = ecn) {
+		ecn = ec->next;
 
-	while(1) {
-		ecard_readbytes(&excd, ec, index, 8, useld);
-		index += 8;
-		if (c_id(&excd) == 0) {
-			if (!useld && ec->loader) {
-				useld = 1;
-				index = 0;
-				continue;
-			}
-			return 0;
-		}
-		if (c_id(&excd) == 0xf0) { /* link */
-			index = c_start(&excd);
-			continue;
-		}
-		if (c_id(&excd) == 0x80) { /* loader */
-			if (!ec->loader) {
-				ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL);
-				ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld);
-			}
-			continue;
-		}
-		if (c_id(&excd) == id && num-- == 0)
-			break;
+		kfree(ec);
 	}
 
-	if (c_id(&excd) & 0x80) {
-		switch (c_id(&excd) & 0x70) {
-		case 0x70:
-			ecard_readbytes((unsigned char *)excd.d.string, ec,
-					(int)c_start(&excd), c_len(&excd), useld);
-			break;
-		case 0x00:
-			break;
-		}
-	}
-	cd->start_offset = c_start(&excd);
-	memcpy(cd->d.string, excd.d.string, 256);
-	return 1;
-}
-
-unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
-{
-	switch (ec->slot_no) {
-	case 0 ... 3:
-		switch (type) {
-		case ECARD_MEMC:
-			return IO_EC_MEMC_BASE + (ec->slot_no << 12);
-
-		case ECARD_IOC:
-			return IO_EC_IOC_BASE + (speed << 17) + (ec->slot_no << 12);
-
-#ifdef IO_EC_EASI_BASE
-		case ECARD_EASI:
-			return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
-		}
-		break;
-
-	case 4 ... 7:
-		switch (type) {
-#ifdef IO_EC_IOC4_BASE
-		case ECARD_IOC:
-			return IO_EC_IOC4_BASE + (speed << 17) + ((ec->slot_no - 4) << 12);
-#endif
-#ifdef IO_EC_EASI_BASE
-		case ECARD_EASI:
-			return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
-		default:
-			break;
-		}
-		break;
+	cards = NULL;
 
-#ifdef IO_EC_MEMC8_BASE
-	case 8:
-		return IO_EC_MEMC8_BASE;
-#endif
-	}
-	return 0;
+	memset(slot_to_expcard, 0, sizeof(slot_to_expcard));
 }
 
-static struct irqaction irqexpansioncard = {
-	ecard_irq_noexpmask,
-	SA_INTERRUPT,
-	0,
-	"expansion cards",
-	NULL,
-	NULL
-};
-
 /*
  * Initialise the expansion card system.
  * Locate all hardware - interrupt management and
@@ -635,51 +985,31 @@
  */
 __initfunc(void ecard_init(void))
 {
-	int i, nc = 0;
-
-	memset(expcard, 0, sizeof(expcard));
+	int slot;
 
-#ifdef HAS_EXPMASK
-	if (ecard_checkirqhw()) {
-		printk(KERN_DEBUG "Expansion card interrupt management hardware found\n");
-		irqexpansioncard.handler = ecard_irq_expmask;
-		irqexpansioncard.flags |= SA_IRQNOMASK;
-		have_expmask = -1;
-	}
-#endif
+	oldlatch_init();
 
-	printk("Installed expansion cards:");
+	printk("Probing expansion cards: (does not imply support)\n");
 
-	/*
-	 * First of all, probe all cards on the expansion card interrupt line
-	 */
-	for (i = 0; i < 8; i++)
-		if (ecard_probe(i, nc, ECARD_IOC) || ecard_probe(i, nc, ECARD_EASI))
-			nc += 1;
-		else
-			have_expmask &= ~(1<<i);
-
-	ecard_numirqcards = nc;
+	for (slot = 0; slot < 8; slot ++) {
+		if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
+			ecard_probe(slot, ECARD_IOC);
+	}
 
-	/* Now probe other cards with different interrupt lines
-	 */
 #ifdef IO_EC_MEMC8_BASE
-	if (ecard_probe(8, nc, ECARD_IOC))
-		nc += 1;
+	ecard_probe(8, ECARD_IOC);
 #endif
 
-	printk("\n");
-	ecard_numcards = nc;
+	ecard_probeirqhw();
 
-	if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
-		printk("Could not allocate interrupt for expansion cards\n");
-		return;
+	if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
+		printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n",
+		       IRQ_EXPANSIONCARD);
+		ecard_free_all();
 	}
-	
-#ifdef HAS_EXPMASK
-	if (nc && have_expmask)
-		EXPMASK_ENABLE = have_expmask;
-#endif
-
-	oldlatch_init();
 }
+
+EXPORT_SYMBOL(ecard_startfind);
+EXPORT_SYMBOL(ecard_find);
+EXPORT_SYMBOL(ecard_readchunk);
+EXPORT_SYMBOL(ecard_address);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-armo.S linux.18p17/arch/arm/kernel/entry-armo.S
--- linux.vanilla/arch/arm/kernel/entry-armo.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/entry-armo.S	Fri Sep 15 23:28:37 2000
@@ -159,8 +159,8 @@
 		.macro	restore_user_regs
 		ldmia	sp, {r0 - lr}^
 		mov	r0, r0
-		add	sp, sp, #15*4
-		ldr	lr, [sp], #8
+		ldr	lr, [sp, #15*4]
+		add	sp, sp, #15*4+8
 		movs	pc, lr
 		.endm
 
@@ -226,13 +226,6 @@
 		str	r0, [sp, #S_OLD_R0]	;\
 		mov	fp, #0
 
-#define USER_RESTORE_ALL			\
-		ldmia	sp, {r0 - lr}^		;\
-		mov	r0, r0			;\
-		add	sp, sp, #15*4		;\
-		ldr	lr, [sp], #8		;\
-		movs	pc, lr
-
 #define SVC_RESTORE_ALL				\
 		ldmfd	sp, {r0 - pc}^
 		
@@ -253,7 +246,7 @@
 		mov	r0, r0
 		movs	pc, lr
 
-Lfiqmsg:	.ascii	"*** Unexpeced FIQ\n\0"
+Lfiqmsg:	.ascii	"*** Unexpected FIQ\n\0"
 		.align
 
 .LCfiq:		.word	__temp_fiq
@@ -315,14 +308,14 @@
 		and	r4, r10, #255			@ get offset
 		and	r6, r10, #0x000f0000
 		tst	r10, #0x00800000		@ +/-
-		rsbeq	r4, r4, #0
 		ldr	r5, [sp, r6, lsr #14]		@ Load reg
+		rsbeq	r4, r4, #0
 		add	r5, r5, r4, lsl #2
 		str	r5, [sp, r6, lsr #14]		@ Save reg
 		b	ret_from_exception
 
-wfs_mask_data:	.word	0x0e200110			@ WFS
-		.word	0x0fff0fff
+wfs_mask_data:	.word	0x0e200110			@ WFS/RFS
+		.word	0x0fef0fff
 		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
 		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
 		.word	0x0f0f0f00
@@ -341,8 +334,7 @@
 		save_user_regs
 		teqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
 		mask_pc	r0, lr			@ Address of abort
-		mov	r1, #FAULT_CODE_PREFETCH|FAULT_CODE_USER @ Error code
-		mov	r2, sp			@ Tasks registers
+		mov	r1, sp			@ Tasks registers
 		bl	SYMBOL_NAME(do_PrefetchAbort)
 		teq	r0, #0			@ If non-zero, we believe this abort..
 		bne	ret_from_sys_call
@@ -451,6 +443,7 @@
 		adr	lr, 1b
 		orr	lr, lr, #0x08000003		@ Force SVC
 		bne	do_IRQ
+		mov	r4, #0
 		b	ret_with_reschedule
 
 		irq_prio_table
@@ -562,8 +555,8 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		mov	r1, r4, lsl #20
+		biceq	r0, r0, #PCMASK
 		tst	r4, #1 << 23
 		addne	r0, r0, r1, lsr #20
 		subeq	r0, r0, r1, lsr #20
@@ -578,12 +571,12 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		and	r7, r4, #15
+		biceq	r0, r0, #PCMASK
 		teq	r7, #15			@ Check for PC
 		ldr	r7, [r3, r7, lsl #2]	@ Get Rm
-		biceq	r7, r7, #PCMASK
 		and	r8, r4, #0x60		@ Get shift types
+		biceq	r7, r7, #PCMASK
 		mov	r9, r4, lsr #7		@ Get shift amount
 		and	r9, r9, #31
 		teq	r8, #0
@@ -645,8 +638,8 @@
 		and	r0, r0, #15 << 2	@ Mask out reg.
 		teq	r0, #15 << 2
 		ldr	r0, [r3, r0]		@ Get register
-		biceq	r0, r0, #PCMASK
 		mov	r1, r4, lsl #24		@ Get offset
+		biceq	r0, r0, #PCMASK
 		tst	r4, #1 << 23
 		addne	r0, r0, r1, lsr #24
 		subeq	r0, r0, r1, lsr #24
@@ -656,9 +649,54 @@
 #endif
 		b	SYMBOL_NAME(do_DataAbort)
 
-#include "entry-common.S"
+/*
+ *=============================================================================
+ *		Low-level interface code
+ *-----------------------------------------------------------------------------
+ *		Trap initialisation
+ *-----------------------------------------------------------------------------
+ *
+ * Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20
+ * that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes
+ * some excess cycles).
+ *
+ * What we need to put into 0-0x1c are branches to branch to the kernel.
+ */
 
-		.data
+		.section ".text.init",#alloc,#execinstr
+
+.Ljump_addresses:
+		swi	SYS_ERROR0
+		.word	vector_undefinstr	- 12
+		.word	vector_swi		- 16
+		.word	vector_prefetch		- 20
+		.word	vector_data		- 24
+		.word	vector_addrexcptn	- 28
+		.word	vector_IRQ		- 32
+		.word	_unexp_fiq		- 36
+		b	. + 8
+/*
+ * initialise the trap system
+ */
+ENTRY(trap_init)
+		stmfd	sp!, {r4 - r7, lr}
+		adr	r1, .Ljump_addresses
+		ldmia	r1, {r1 - r7, ip, lr}
+		orr	r2, lr, r2, lsr #2
+		orr	r3, lr, r3, lsr #2
+		orr	r4, lr, r4, lsr #2
+		orr	r5, lr, r5, lsr #2
+		orr	r6, lr, r6, lsr #2
+		orr	r7, lr, r7, lsr #2
+		orr	ip, lr, ip, lsr #2
+		mov	r0, #0
+		stmia	r0, {r1 - r7, ip}
+		ldmfd	sp!, {r4 - r7, pc}^
+
+		.text
+
+#include "entry-common.S"
 
-__temp_irq:	.word	0				@ saved lr_irq
+		.bss
+__temp_irq:	.space	4				@ saved lr_irq
 __temp_fiq:	.space	128
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-armv.S linux.18p17/arch/arm/kernel/entry-armv.S
--- linux.vanilla/arch/arm/kernel/entry-armv.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/entry-armv.S	Fri Sep 15 23:28:37 2000
@@ -19,6 +19,10 @@
 
 #include "../lib/constants.h"
 
+#ifndef MODE_SVC
+#define MODE_SVC 0x13
+#endif
+
 		.text
 
 #define PF_TRACESYS	0x20
@@ -61,8 +65,12 @@
 #define S_R1		4
 #define S_R0		0
 
+#define OFF_CR_ALIGNMENT(x)	cr_alignment - x
+
 #ifdef IOC_BASE
 /* IOC / IOMD based hardware */
+#include <asm/iomd.h>
+
 		.equ	ioc_base_high, IOC_BASE & 0xff000000
 		.equ	ioc_base_low, IOC_BASE & 0x00ff0000
 		.macro	disable_fiq
@@ -186,113 +194,113 @@
 		.byte	 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2
 		.endm
 
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#include <asm/dec21285.h>
 
 		.macro	disable_fiq
 		.endm
 
+		.equ	irq_mask_pci_err_high, IRQ_MASK_PCI_ERR & 0xff000000
+		.equ	irq_mask_pci_err_low,  IRQ_MASK_PCI_ERR & 0x00ffffff
+		.equ	dc21285_high, ARMCSR_BASE & 0xff000000
+		.equ	dc21285_low, ARMCSR_BASE & 0x00ffffff
+
 		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		mov	r4, #0xfe000000
+		mov	r4, #dc21285_high
+		.if	dc21285_low
+		orr	r4, r4, #dc21285_low
+		.endif
 		ldr	\irqstat, [r4, #0x180]		@ get interrupts
-		mov	\irqnr, #0
-1001:		tst	\irqstat, #1
-		addeq	\irqnr, \irqnr, #1
-		moveq	\irqstat, \irqstat, lsr #1
-		tsteq	\irqnr, #32
-		beq	1001b
-		teq	\irqnr, #32
-		.endm
 
-		.macro	irq_prio_table
-		.endm
+		mov	\irqnr, #IRQ_SDRAMPARITY
+		tst	\irqstat, #IRQ_MASK_SDRAMPARITY
+		bne	1001f
 
-#elif defined(CONFIG_ARCH_NEXUSPCI)
+		tst	\irqstat, #IRQ_MASK_UART_RX
+		movne	\irqnr, #IRQ_CONRX
+		bne	1001f
 
-		.macro	disable_fiq
-		.endm
+		tst	\irqstat, #IRQ_MASK_DMA1
+		movne	\irqnr, #IRQ_DMA1
+		bne	1001f
 
-		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		ldr	r4, =0xffe00000
-		ldr	\irqstat, [r4, #0x180]		@ get interrupts
-		mov	\irqnr, #0
-1001:		tst	\irqstat, #1
-		addeq	\irqnr, \irqnr, #1
-		moveq	\irqstat, \irqstat, lsr #1
-		tsteq	\irqnr, #32
-		beq	1001b
-		teq	\irqnr, #32
-		.endm
+		tst	\irqstat, #IRQ_MASK_DMA2
+		movne	\irqnr, #IRQ_DMA2
+		bne	1001f
 
-		.macro	irq_prio_table
-		.endm
+		tst	\irqstat, #IRQ_MASK_IN0
+		movne	\irqnr, #IRQ_IN0
+		bne	1001f
 
-#elif defined(CONFIG_ARCH_VNC)
+		tst	\irqstat, #IRQ_MASK_IN1
+		movne	\irqnr, #IRQ_IN1
+		bne	1001f
 
-		.macro	disable_fiq
-		.endm
+		tst	\irqstat, #IRQ_MASK_IN2
+		movne	\irqnr, #IRQ_IN2
+		bne	1001f
 
-		.equ	pci_iack_high, PCI_IACK & 0xff000000
-		.equ	pci_iack_low,  PCI_IACK & 0x00ff0000
+		tst	\irqstat, #IRQ_MASK_IN3
+		movne	\irqnr, #IRQ_IN3
+		bne	1001f
 
-		.macro	get_irqnr_and_base, irqnr, irqstat, base
-		mov	r4, #IO_BASE_ARM_CSR
-		ldr	\irqstat, [r4, #CSR_IRQ_STATUS]	@ just show us the unmasked ones
+		tst	\irqstat, #IRQ_MASK_PCI
+		movne	\irqnr, #IRQ_PCI
+		bne	1001f
 
-		@ run through hard priorities
-		@ timer
-		tst	\irqstat, #IRQ_MASK_TIMER0
-		movne	\irqnr, #IRQ_TIMER0
+		tst	\irqstat, #IRQ_MASK_DOORBELLHOST
+		movne	\irqnr, #IRQ_DOORBELLHOST
+		bne     1001f
+	
+		tst	\irqstat, #IRQ_MASK_I2OINPOST
+		movne	\irqnr, #IRQ_I2OINPOST
 		bne	1001f
 
-		@ ether10
-		tst	\irqstat, #IRQ_MASK_ETHER10
-		movne	\irqnr, #IRQ_ETHER10
+		tst	\irqstat, #IRQ_MASK_TIMER1
+		movne	\irqnr, #IRQ_TIMER1
 		bne	1001f
 
-		@ ether100
-		tst	\irqstat, #IRQ_MASK_ETHER100
-		movne	\irqnr, #IRQ_ETHER100
+		tst	\irqstat, #IRQ_MASK_TIMER2
+		movne	\irqnr, #IRQ_TIMER2
 		bne	1001f
 
-		@ video compressor
-		tst	\irqstat, #IRQ_MASK_VIDCOMP
-		movne	\irqnr, #IRQ_VIDCOMP
+		tst	\irqstat, #IRQ_MASK_TIMER3
+		movne	\irqnr, #IRQ_TIMER3
 		bne	1001f
 
-		@ now try all the PIC sources
-		@ determine whether we have an irq
-		tst	\irqstat, #IRQ_MASK_EXTERN_IRQ
-		beq	1002f
-		mov	r4, #pci_iack_high
-		orr	r4, r4, #pci_iack_low
-		ldrb	\irqnr, [r4]			@ get the IACK byte
-		b	1001f
+		tst	\irqstat, #IRQ_MASK_UART_TX
+		movne	\irqnr, #IRQ_CONTX
+		bne	1001f
 
-1002:		@ PCI errors
-		tst	\irqstat, #IRQ_MASK_PCI_ERR
+		tst	\irqstat, #irq_mask_pci_err_high
+		tsteq	\irqstat, #irq_mask_pci_err_low
 		movne	\irqnr, #IRQ_PCI_ERR
 		bne	1001f
+1001:
+		.endm
 
-		@ softint
-		tst	\irqstat, #IRQ_MASK_SOFTIRQ
-		movne	\irqnr, #IRQ_SOFTIRQ
-		bne	1001f
+		.macro	irq_prio_table
+		.endm
 
-		@ debug uart
-		tst	\irqstat, #IRQ_MASK_UART_DEBUG
-		movne	\irqnr, #IRQ_CONRX
-		bne	1001f
+#elif defined(CONFIG_ARCH_NEXUSPCI)
 
-		@ watchdog
-		tst	\irqstat, #IRQ_MASK_WATCHDOG
-		movne	\irqnr, #IRQ_WATCHDOG
+		.macro	disable_fiq
+		.endm
 
-1001:		@ If Z is set, then we will not enter an interrupt
+		.macro	get_irqnr_and_base, irqnr, irqstat, base
+		ldr	r4, =0xffe00000
+		ldr	\irqstat, [r4, #0x180]		@ get interrupts
+		mov	\irqnr, #0
+1001:		tst	\irqstat, #1
+		addeq	\irqnr, \irqnr, #1
+		moveq	\irqstat, \irqstat, lsr #1
+		tsteq	\irqnr, #32
+		beq	1001b
+		teq	\irqnr, #32
 		.endm
 
 		.macro	irq_prio_table
 		.endm
-
 #else
 #error Unknown architecture
 #endif
@@ -306,22 +314,20 @@
 		stmia	sp, {r0 - r12}			@ Calling r0 - r12
 		add	r8, sp, #S_PC
 		stmdb	r8, {sp, lr}^			@ Calling sp, lr
-		mov	r7, r0
+		str	lr, [r8, #0]			@ Save calling PC
 		mrs	r6, spsr
-		mov	r5, lr
-		stmia	r8, {r5, r6, r7}		@ Save calling PC, CPSR, OLD_R0
+		str	r6, [r8, #4]			@ Save CPSR
+		str	r0, [r8, #8]			@ Save OLD_R0
 		.endm
 
 		.macro	restore_user_regs
-		mrs	r0, cpsr			@ disable IRQs
-		orr	r0, r0, #I_BIT
-		msr	cpsr, r0
 		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
+		msr	cpsr_c, #I_BIT | MODE_SVC	@ disable IRQs
 		msr	spsr, r0			@ save in spsr_svc
 		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
 		mov	r0, r0
-		add	sp, sp, #S_PC
-		ldr	lr, [sp], #S_FRAME_SIZE - S_PC	@ Get PC and jump over PC, PSR, OLD_R0
+		ldr	lr, [sp, #S_PC]			@ Get PC
+		add	sp, sp, #S_FRAME_SIZE
 		movs	pc, lr				@ return & move spsr_svc into cpsr
 		.endm
 
@@ -331,16 +337,15 @@
 		/* If we're optimising for StrongARM the resulting code won't 
 		   run on an ARM7 and we can save a couple of instructions.  
 									--pb */
-#ifdef __ARM_ARCH_4__
 		.macro	arm700_bug_check, instr, temp
 		.endm
-#else
 		.macro	arm700_bug_check, instr, temp
+#ifndef __ARM_ARCH_4__
 		and	\temp, \instr, #0x0f000000	@ check for SWI
 		teq	\temp, #0x0f000000
 		bne	.Larm700bug
-		.endm
 #endif
+		.endm
 
 		.macro	enable_irqs, temp
 		mrs	\temp, cpsr
@@ -348,25 +353,6 @@
 		msr	cpsr, \temp
 		.endm
 
-		.macro	initialise_traps_extra
-		mrs	r0, cpsr
-		bic	r0, r0, #31
-		orr	r0, r0, #0xd3
-		msr	cpsr, r0
-		.endm
-
-
-#ifndef __ARM_ARCH_4__
-.Larm700bug:	str	lr, [r8]
-		ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
-		msr	spsr, r0
-		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
-		mov	r0, r0
-		add	sp, sp, #S_PC
-		ldr	lr, [sp], #S_FRAME_SIZE - S_PC	@ Get PC and jump over PC, PSR, OLD_R0
-		movs	pc, lr
-#endif
-
 		.macro	get_current_task, rd
 		mov	\rd, sp, lsr #13
 		mov	\rd, \rd, lsl #13
@@ -379,269 +365,124 @@
 		adr\cond	\reg, \label
 		.endm
 
-/*=============================================================================
- * Address exception handler
- *-----------------------------------------------------------------------------
- * These aren't too critical.
- * (they're not supposed to happen, and won't happen in 32-bit mode).
- */
-
-vector_addrexcptn:
-		b	vector_addrexcptn
-
-/*=============================================================================
- * Undefined FIQs
- *-----------------------------------------------------------------------------
- * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
- * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
- * Basically to switch modes, we *HAVE* to clobber one register...  brain
- * damage alert!  I don't think that we can execute any code in here in any
- * other mode than FIQ...  Ok you can switch to another mode, but you can't
- * get out of that mode without clobbering one register.
- */
-_unexp_fiq:	disable_fiq
-		subs	pc, lr, #4
-
-/*=============================================================================
- * Interrupt entry dispatcher
- *-----------------------------------------------------------------------------
- * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-vector_IRQ:	@
-		@ save mode specific registers
-		@
-		ldr	r13, .LCirq
-		sub	lr, lr, #4
-		str	lr, [r13]			@ save lr_IRQ
-		mrs	lr, spsr
-		str	lr, [r13, #4]			@ save spsr_IRQ
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr			@ switch to SVC mode
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__irq_invalid			@  4 - 15
-		b	__irq_usr			@  0  (USR_26 / USR_32)
-		b	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__irq_svc			@  3  (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-.LCirq:		.word	__temp_irq
-.LCund:		.word	__temp_und
-.LCabt:		.word	__temp_abt
+		.macro	alignment_trap, rbase, rtemp, sym
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	\rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
+		mcr	p15, 0, \rtemp, c1, c0
+#endif
+		.endm
 
-vector_undefinstr:
-		@
-		@ save mode specific registers
-		@
-		ldr	r13, [pc, #.LCund - . - 8]
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__und_invalid			@  4 - 15
-		b	__und_usr			@  0 (USR_26 / USR_32)
-		b	__und_invalid			@  1 (FIQ_26 / FIQ_32)
-		b	__und_invalid			@  2 (IRQ_26 / IRQ_32)
-		b	__und_svc			@  3 (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
- */
-vector_prefetch:
-		@
-		@ save mode specific registers
-		@
-		sub	lr, lr, #4
-		ldr	r13, .LCabt
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		adds	pc, pc, lr, lsl #2		@ Changes mode and branches
-		b	__pabt_invalid			@  4 - 15
-		b	__pabt_usr			@  0  (USR_26 / USR_32)
-		b	__pabt_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__pabt_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__pabt_invalid			@  3  (SVC_26 / SVC_32)
 /*
- *------------------------------------------------------------------------------------------------
- * Data abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ * Invalid mode handlers
  */
-vector_data:	@
-		@ save mode specific registers
-		@
-		sub	lr, lr, #8
-		ldr	r13, .LCabt
-		str	lr, [r13]
-		mrs	lr, spsr
-		str	lr, [r13, #4]
-		@
-		@ now branch to the relevent MODE handling routine
-		@
-		mrs	sp, cpsr
-		bic	sp, sp, #31
-		orr	sp, sp, #0x13
-		msr	spsr, sp
-		and	lr, lr, #15
-		cmp	lr, #4
-		addlts	pc, pc, lr, lsl #2		@ Changes mode & branches
-		b	__dabt_invalid			@  4 - 15
-		b	__dabt_usr			@  0  (USR_26 / USR_32)
-		b	__dabt_invalid			@  1  (FIQ_26 / FIQ_32)
-		b	__dabt_invalid			@  2  (IRQ_26 / IRQ_32)
-		b	__dabt_svc			@  3  (SVC_26 / SVC_32)
+__pabt_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - lr}			@ Save XXX r0 - lr
+		ldr	r4, .LCabt
+		mov	r1, #BAD_PREFETCH
+		b	1f
 
-/*=============================================================================
- * Prefetch abort handler
- *-----------------------------------------------------------------------------
- */
-pabtmsg:	.ascii	"Pabt: %08lX\n\0"
-		.align
-__pabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - r12}			@ Save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^			@ Save sp_usr lr_usr
+__dabt_invalid:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - lr}			@ Save SVC r0 - lr [lr *should* be intact]
 		ldr	r4, .LCabt
-		ldmia	r4, {r5 - r7}			@ Get USR pc, cpsr
-		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		mov	r1, #BAD_DATA
+		b	1f
 
-		mrs	r7, cpsr			@ Enable interrupts if they were
-		bic	r7, r7, #I_BIT			@ previously
-		msr	cpsr, r7
-		mov	r0, r5				@ address (pc)
-		mov	r1, sp				@ regs
-		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
-		teq	r0, #0				@ Does this still apply???
-		bne	ret_from_exception		@ Return from exception
-#ifdef DEBUG_UNDEF
-		adr	r0, t
-		bl	SYMBOL_NAME(printk)
-#endif
-		mov	r0, r5
-		mov	r1, sp
-		and	r2, r6, #31
-		bl	SYMBOL_NAME(do_undefinstr)
-		ldr	lr, [sp, #S_PSR]		@ Get USR cpsr
-		msr	spsr, lr
-		ldmia	sp, {r0 - pc}^			@ Restore USR registers
+__irq_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate space on stack for frame
+		stmfd	sp, {r0 - lr}			@ Save r0 - lr
+		ldr	r4, .LCirq
+		mov	r1, #BAD_IRQ
+		b	1f
 
-__pabt_invalid:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - lr}			@ Save XXX r0 - lr
-		mov	r7, r0				@ OLD R0
-		ldr	r4, .LCabt
-		ldmia	r4, {r5 - r7}			@ Get XXX pc, cpsr
+__und_invalid:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - lr}
+		ldr	r4, .LCund
+		mov	r1, #BAD_UNDEFINSTR		@ int reason
+
+1:		mov	fp, #0
+		ldmia	r4, {r5 - r7}			@ Get XXX pc, cpsr, old_r0
 		add	r4, sp, #S_PC
 		stmia	r4, {r5 - r7}			@ Save XXX pc, cpsr, old_r0
-		mov	r0, sp				@ Prefetch aborts are definitely *not*
-		mov	r1, #BAD_PREFETCH		@ allowed in non-user modes.  We cant
-		and	r2, r6, #31			@ recover from this problem.
+		mov	r0, sp
+		and	r2, r6, #31			@ int mode
 		b	SYMBOL_NAME(bad_mode)
 
-#ifdef DEBUG_UNDEF
-t:		.ascii "*** undef ***\r\n\0"
-		.align
-#endif
 
-/*=============================================================================
- * Data abort handler code
- *-----------------------------------------------------------------------------
+wfs_mask_data:	.word	0x0e200110			@ WFS/RFS
+		.word	0x0fef0fff
+		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
+		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
+		.word	0x0f0f0f00
+
+/* We get here if an undefined instruction happens and the floating
+ * point emulator is not present.  If the offending instruction was
+ * a WFS, we just perform a normal return as if we had emulated the
+ * operation.  This is a hack to allow some basic userland binaries
+ * to run so that the emulator module proper can be loaded. --philb
  */
-.LCprocfns:	.word	SYMBOL_NAME(processor)
+fpe_not_present:
+		adr	r10, wfs_mask_data
+		ldmia	r10, {r4, r5, r6, r7, r8}
+		ldr	r10, [sp, #S_PC]		@ Load PC
+		sub	r10, r10, #-4
+		mask_pc	r10, r10
+		ldrt	r10, [r10]			@ get instruction
+		and	r5, r10, r5
+		teq	r5, r4				@ Is it WFS?
+		moveq	pc, r9
+		and	r5, r10, r8
+		teq	r5, r6				@ Is it LDF/STF on sp or fp?
+		teqne	r5, r7
+		movne	pc, lr
+		tst	r10, #0x00200000		@ Does it have WB
+		moveq	pc, r9
+		and	r4, r10, #255			@ get offset
+		and	r6, r10, #0x000f0000
+		tst	r10, #0x00800000		@ +/-
+		ldr	r5, [sp, r6, lsr #14]		@ Load reg
+		rsbeq	r4, r4, #0
+		add	r5, r5, r4, lsl #2
+		str	r5, [sp, r6, lsr #14]		@ Save reg
+		mov	pc, r9
 
-__dabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
-		stmia	sp, {r0 - r12}			@ save r0 - r12
-		add	r3, sp, #S_PC
-		stmdb	r3, {sp, lr}^
-		ldr	r0, .LCabt
-		ldmia	r0, {r0 - r2}			@ Get USR pc, cpsr
-		stmia	r3, {r0 - r2}			@ Save USR pc, cpsr, old_r0
-		mov	fp, #0
-		mrs	r2, cpsr			@ Enable interrupts if they were
-		bic	r2, r2, #I_BIT			@ previously
-		msr	cpsr, r2
-		ldr	r2, .LCprocfns
-		mov	lr, pc
-		ldr	pc, [r2, #8]			@ call processor specific code
-		mov	r3, sp
-		bl	SYMBOL_NAME(do_DataAbort)
-		b	ret_from_sys_call
-
-__dabt_svc:	sub	sp, sp, #S_FRAME_SIZE
+/*
+ * SVC mode handlers
+ */
+		.align	5
+__dabt_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
 		ldr	r2, .LCabt
 		add	r0, sp, #S_FRAME_SIZE
+		ldmia	r2, {r2 - r4}			@ get pc, cpsr
 		add	r5, sp, #S_SP
 		mov	r1, lr
-		ldmia	r2, {r2 - r4}			@ get pc, cpsr
 		stmia	r5, {r0 - r4}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+		mrs	r9, cpsr			@ Enable interrupts if they were
 		tst	r3, #I_BIT
-		mrseq	r0, cpsr			@ Enable interrupts if they were
-		biceq	r0, r0, #I_BIT			@ previously
-		msreq	cpsr, r0
+		biceq	r9, r9, #I_BIT			@ previously
 		mov	r0, r2
+/*
+ * This routine must not corrupt r9
+ */
 		ldr	r2, .LCprocfns
 		mov	lr, pc
 		ldr	pc, [r2, #8]			@ call processor specific code
+		msr	cpsr_c, r9
 		mov	r3, sp
 		bl	SYMBOL_NAME(do_DataAbort)
+		msr	cpsr_c, #I_BIT | MODE_SVC
 		ldr	r0, [sp, #S_PSR]
 		msr	spsr, r0
 		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
 
-__dabt_invalid:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - lr}			@ Save SVC r0 - lr [lr *should* be intact]
-		mov	r7, r0
-		ldr	r4, .LCabt
-		ldmia	r4, {r5, r6}			@ Get SVC pc, cpsr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}		@ Save SVC pc, cpsr, old_r0
-		mov	r0, sp
-		mov	r1, #BAD_DATA
-		and	r2, r6, #31
-		b	SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Interrupt (IRQ) handler
- *-----------------------------------------------------------------------------
- */
-__irq_usr:	sub	sp, sp, #S_FRAME_SIZE
+		.align	5
+__irq_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^
-		ldr	r4, .LCirq
-		ldmia	r4, {r5 - r7}			@ get saved PC, SPSR
-		stmia	r8, {r5 - r7}			@ save pc, psr, old_r0
+		ldr	r7, .LCirq
+		add	r5, sp, #S_FRAME_SIZE
+		ldmia	r7, {r7 - r9}
+		add	r4, sp, #S_SP
+		mov	r6, lr
+		stmia	r4, {r5, r6, r7, r8, r9}	@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
 1:		get_irqnr_and_base r0, r6, r5
 		movne	r1, sp
 		@
@@ -649,148 +490,388 @@
 		@
 		adrsvc	ne, lr, 1b
 		bne	do_IRQ
-		b	ret_with_reschedule
-
-		irq_prio_table
+		ldr	r0, [sp, #S_PSR]		@ irqs are already disabled
+		msr	spsr, r0
+		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
 
-__irq_svc:	sub	sp, sp, #S_FRAME_SIZE
+		.align	5
+__und_svc:	sub	sp, sp, #S_FRAME_SIZE
 		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r7, .LCund
 		mov	r6, lr
-		ldr	r7, .LCirq
 		ldmia	r7, {r7 - r9}
 		add	r5, sp, #S_FRAME_SIZE
 		add	r4, sp, #S_SP
-		stmia	r4, {r5, r6, r7, r8, r9}	@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+		stmia	r4, {r5 - r9}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+
+		adrsvc	al, r9, 1f			@ r9  = normal FP return
+		bl	call_fpe			@ lr  = undefined instr return
+
+		mov	r0, r5				@ unsigned long pc
+		mov	r1, sp				@ struct pt_regs *regs
+		bl	SYMBOL_NAME(do_undefinstr)
+
+1:		msr	cpsr_c, #I_BIT | MODE_SVC
+		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
+		msr	spsr, lr
+		ldmia	sp, {r0 - pc}^			@ Restore SVC registers
+
+		.align	5
+.LCirq:		.word	__temp_irq
+.LCund:		.word	__temp_und
+.LCabt:		.word	__temp_abt
+.LCprocfns:	.word	SYMBOL_NAME(processor)
+.LCfp:		.word	SYMBOL_NAME(fp_enter)
+#ifdef CONFIG_ALIGNMENT_TRAP
+.LCswi:		.word	SYMBOL_NAME(cr_alignment)
+#endif
+
+		irq_prio_table
+
+/*
+ * User mode handlers
+ */
+		.align	5
+__dabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r4, .LCabt
+		add	r3, sp, #S_PC
+		ldmia	r4, {r0 - r2}			@ Get USR pc, cpsr
+		stmia	r3, {r0 - r2}			@ Save USR pc, cpsr, old_r0
+		stmdb	r3, {sp, lr}^
+		alignment_trap r4, r7, __temp_abt
+		mov	fp, #0
+		ldr	r2, .LCprocfns
+		mov	lr, pc
+		ldr	pc, [r2, #8]			@ call processor specific code
+		msr	cpsr_c, #MODE_SVC		@ Enable interrupts
+		mov	r3, sp
+		adrsvc	al, lr, ret_from_sys_call
+		b	SYMBOL_NAME(do_DataAbort)
+
+		.align	5
+__irq_usr:	sub	sp, sp, #S_FRAME_SIZE
+		stmia	sp, {r0 - r12}			@ save r0 - r12
+		ldr	r4, .LCirq
+		add	r8, sp, #S_PC
+		ldmia	r4, {r5 - r7}			@ get saved PC, SPSR
+		stmia	r8, {r5 - r7}			@ save pc, psr, old_r0
+		stmdb	r8, {sp, lr}^
+		alignment_trap r4, r7, __temp_irq
+		mov	fp, #0
 1:		get_irqnr_and_base r0, r6, r5
 		movne	r1, sp
+		adrsvc	ne, lr, 1b
 		@
 		@ routine called with r0 = irq number, r1 = struct pt_regs *
 		@
-		adrsvc	ne, lr, 1b
 		bne	do_IRQ
-		ldr	r0, [sp, #S_PSR]
-		msr	spsr, r0
-		ldmia	sp, {r0 - pc}^			@ load r0 - pc, cpsr
-
-__irq_invalid:	sub	sp, sp, #S_FRAME_SIZE	@ Allocate space on stack for frame
-		stmfd	sp, {r0 - lr}		@ Save r0 - lr
-		mov	r7, #-1
-		ldr	r4, .LCirq
-		ldmia	r4, {r5, r6}		@ get saved pc, psr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}
-		mov	fp, #0
-		mov	r0, sp
-		mov	r1, #BAD_IRQ
-		b	SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Undefined instruction handler
- *-----------------------------------------------------------------------------
- * Handles floating point instructions
- */
-.LC2:		.word	SYMBOL_NAME(fp_enter)
+		mov	r4, #0
+		b	ret_with_reschedule
 
+		.align	5
 __und_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
 		stmia	sp, {r0 - r12}			@ Save r0 - r12
-		add	r8, sp, #S_PC
-		stmdb	r8, {sp, lr}^			@ Save user r0 - r12
 		ldr	r4, .LCund
+		add	r8, sp, #S_PC
 		ldmia	r4, {r5 - r7}
 		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		stmdb	r8, {sp, lr}^			@ Save user r0 - r12
+		alignment_trap r4, r7, __temp_und
 		mov	fp, #0
-
-		adrsvc	al, r9, ret_from_exception	@ r9  = normal FP return
+		adrsvc	al, r9, ret_from_sys_call	@ r9  = normal FP return
 		adrsvc	al, lr, fpundefinstr		@ lr  = undefined instr return
 
-1:		get_current_task r10
+call_fpe:	get_current_task r10
 		mov	r8, #1
 		strb	r8, [r10, #TSK_USED_MATH]	@ set current->used_math
+		ldr	r4, .LCfp
 		add	r10, r10, #TSS_FPESAVE		@ r10 = workspace
-		ldr	r4, .LC2
 		ldr	pc, [r4]			@ Call FP module USR entry point
 
-__und_svc:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - r12}			@ save r0 - r12
-		mov	r6, lr
-		ldr	r7, .LCund
-		ldmia	r7, {r7 - r9}
-		add	r5, sp, #S_FRAME_SIZE
-		add	r4, sp, #S_SP
-		stmia	r4, {r5 - r9}			@ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-
-		adrsvc	al, r9, 3f			@ r9  = normal FP return
-		bl	1b				@ lr  = undefined instr return
+fpundefinstr:	mov	r0, lr
+		mov	r1, sp
+		msr	cpsr_c, #MODE_SVC
+		adrsvc	al, lr, ret_from_sys_call
+		b	SYMBOL_NAME(do_undefinstr)
 
-		mov	r0, r5				@ unsigned long pc
-		mov	r1, sp				@ struct pt_regs *regs
+		.align	5
+__pabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
+		stmia	sp, {r0 - r12}			@ Save r0 - r12
+		ldr	r4, .LCabt
+		add	r8, sp, #S_PC
+		ldmia	r4, {r5 - r7}			@ Get USR pc, cpsr
+		stmia	r8, {r5 - r7}			@ Save USR pc, cpsr, old_r0
+		stmdb	r8, {sp, lr}^			@ Save sp_usr lr_usr
+		alignment_trap r4, r7, __temp_abt
+		mov	fp, #0
+		msr	cpsr_c, #MODE_SVC		@ Enable interrupts
+		mov	r0, r5				@ address (pc)
+		mov	r1, sp				@ regs
+		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
+		teq	r0, #0				@ Does this still apply???
+		bne	ret_from_sys_call		@ Return from exception
+#ifdef DEBUG_UNDEF
+		adr	r0, t
+		bl	SYMBOL_NAME(printk)
+#endif
+		mov	r0, r5
+		mov	r1, sp
+		and	r2, r6, #31
 		bl	SYMBOL_NAME(do_undefinstr)
-
-3:		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
+		ldr	lr, [sp, #S_PSR]		@ Get USR cpsr
 		msr	spsr, lr
-		ldmia	sp, {r0 - pc}^			@ Restore SVC registers
+		ldmia	sp, {r0 - pc}^			@ Restore USR registers
 
-fpundefinstr:	mov	r0, lr
-		mov	r1, sp
-		mrs	r4, cpsr			@ Enable interrupts
-		bic	r4, r4, #I_BIT
-		msr	cpsr, r4
-		adrsvc	al, lr, ret_from_exception
-		b	SYMBOL_NAME(do_undefinstr)
+#ifdef DEBUG_UNDEF
+t:		.ascii "Prefetch -> undefined instruction\n\0"
+		.align
+#endif
 
-__und_invalid:	sub	sp, sp, #S_FRAME_SIZE
-		stmia	sp, {r0 - lr}
-		mov	r7, r0
-		ldr	r4, .LCund
-		ldmia	r4, {r5, r6}			@ Get UND/IRQ/FIQ/ABT pc, cpsr
-		add	r4, sp, #S_PC
-		stmia	r4, {r5, r6, r7}		@ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0
-		mov	r0, sp				@ struct pt_regs *regs
-		mov	r1, #BAD_UNDEFINSTR		@ int reason
-		and	r2, r6, #31			@ int mode
-		b	SYMBOL_NAME(bad_mode)		@ Does not ever return...
+#include "entry-common.S"
 
-/* We get here if an undefined instruction happens and the floating
- * point emulator is not present.  If the offending instruction was
- * a WFS, we just perform a normal return as if we had emulated the
- * operation.  This is a hack to allow some basic userland binaries
- * to run so that the emulator module proper can be loaded. --philb
+		.text
+
+#ifndef __ARM_ARCH_4__
+.Larm700bug:	ldr	r0, [sp, #S_PSR]		@ Get calling cpsr
+		str	lr, [r8]
+		msr	spsr, r0
+		ldmia	sp, {r0 - lr}^			@ Get calling r0 - lr
+		mov	r0, r0
+		ldr	lr, [sp, #S_PC]			@ Get PC
+		add	sp, sp, #S_FRAME_SIZE
+		movs	pc, lr
+#endif
+
+		.section ".text.init",#alloc,#execinstr
+/*
+ * Vector stubs.  NOTE that we only align 'vector_IRQ' to a cache line boundary,
+ * and we rely on each stub being exactly 48 (1.5 cache lines) in size.  This
+ * means that we only ever load two cache lines for this code, or one if we're
+ * lucky.  We also copy this code to 0x200 so that we can use branches in the
+ * vectors, rather than ldr's.
  */
-fpe_not_present:
-		adr	r10, wfs_mask_data
-		ldmia	r10, {r4, r5, r6, r7, r8}
-		ldr	r10, [sp, #S_PC]		@ Load PC
-		sub	r10, r10, #4
-		mask_pc	r10, r10
-		ldrt	r10, [r10]			@ get instruction
-		and	r5, r10, r5
-		teq	r5, r4				@ Is it WFS?
-		moveq	pc, r9
-		and	r5, r10, r8
-		teq	r5, r6				@ Is it LDF/STF on sp or fp?
-		teqne	r5, r7
-		movne	pc, lr
-		tst	r10, #0x00200000		@ Does it have WB
-		moveq	pc, r9
-		and	r4, r10, #255			@ get offset
-		and	r6, r10, #0x000f0000
-		tst	r10, #0x00800000		@ +/-
-		rsbeq	r4, r4, #0
-		ldr	r5, [sp, r6, lsr #14]		@ Load reg
-		add	r5, r5, r4, lsl #2
-		str	r5, [sp, r6, lsr #14]		@ Save reg
-		mov	pc, r9
+		.align	5
+__stubs_start:
+/*
+ * Interrupt dispatcher
+ * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_IRQ:	@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsirq
+		sub	lr, lr, #4
+		str	lr, [r13]			@ save lr_IRQ
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_IRQ
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
 
-wfs_mask_data:	.word	0x0e200110			@ WFS
-		.word	0x0fff0fff
-		.word	0x0d0d0100			@ LDF [sp]/STF [sp]
-		.word	0x0d0b0100			@ LDF [fp]/STF [fp]
-		.word	0x0f0f0f00
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
 
-#include "entry-common.S"
+.LCtab_irq:	.word	__irq_usr			@  0  (USR_26 / USR_32)
+		.word	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
+		.word	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
+		.word	__irq_svc			@  3  (SVC_26 / SVC_32)
+		.word	__irq_invalid			@  4
+		.word	__irq_invalid			@  5
+		.word	__irq_invalid			@  6
+		.word	__irq_invalid			@  7
+		.word	__irq_invalid			@  8
+		.word	__irq_invalid			@  9
+		.word	__irq_invalid			@  a
+		.word	__irq_invalid			@  b
+		.word	__irq_invalid			@  c
+		.word	__irq_invalid			@  d
+		.word	__irq_invalid			@  e
+		.word	__irq_invalid			@  f
+
+		.align	5
+
+/*
+ * Data abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_data:	@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsabt
+		sub	lr, lr, #8
+		str	lr, [r13]
+		mrs	lr, spsr
+		str	lr, [r13, #4]
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+.LCtab_dabt:	.word	__dabt_usr			@  0  (USR_26 / USR_32)
+		.word	__dabt_invalid			@  1  (FIQ_26 / FIQ_32)
+		.word	__dabt_invalid			@  2  (IRQ_26 / IRQ_32)
+		.word	__dabt_svc			@  3  (SVC_26 / SVC_32)
+		.word	__dabt_invalid			@  4
+		.word	__dabt_invalid			@  5
+		.word	__dabt_invalid			@  6
+		.word	__dabt_invalid			@  7
+		.word	__dabt_invalid			@  8
+		.word	__dabt_invalid			@  9
+		.word	__dabt_invalid			@  a
+		.word	__dabt_invalid			@  b
+		.word	__dabt_invalid			@  c
+		.word	__dabt_invalid			@  d
+		.word	__dabt_invalid			@  e
+		.word	__dabt_invalid			@  f
+
+		.align	5
+
+/*
+ * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_prefetch:
+		@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsabt
+		sub	lr, lr, #4
+		str	lr, [r13]			@ save lr_ABT
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_ABT
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		ands	lr, lr, #15
+		ldreq	lr, .LCtab_pabt
+		ldrne	lr, .LCtab_pabt + 4
+		movs	pc, lr
+
+.LCtab_pabt:	.word	__pabt_usr
+		.word	__pabt_invalid
+
+		.align	5
+
+/*
+ * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_undefinstr:
+		@
+		@ save mode specific registers
+		@
+		ldr	r13, .LCsund
+		str	lr, [r13]			@ save lr_UND
+		mrs	lr, spsr
+		str	lr, [r13, #4]			@ save spsr_UND
+		@
+		@ now branch to the relevent MODE handling routine
+		@
+		msr	spsr_c, #I_BIT | MODE_SVC	@ switch to SVC_32 mode
+
+		and	lr, lr, #15
+		ldr	lr, [pc, lr, lsl #2]
+		movs	pc, lr				@ Changes mode and branches
+
+.LCtab_und:	.word	__und_usr			@  0 (USR_26 / USR_32)
+		.word	__und_invalid			@  1 (FIQ_26 / FIQ_32)
+		.word	__und_invalid			@  2 (IRQ_26 / IRQ_32)
+		.word	__und_svc			@  3 (SVC_26 / SVC_32)
+		.word	__und_invalid			@  4
+		.word	__und_invalid			@  5
+		.word	__und_invalid			@  6
+		.word	__und_invalid			@  7
+		.word	__und_invalid			@  8
+		.word	__und_invalid			@  9
+		.word	__und_invalid			@  a
+		.word	__und_invalid			@  b
+		.word	__und_invalid			@  c
+		.word	__und_invalid			@  d
+		.word	__und_invalid			@  e
+		.word	__und_invalid			@  f
+
+		.align	5
+
+/*=============================================================================
+ * Undefined FIQs
+ *-----------------------------------------------------------------------------
+ * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
+ * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
+ * Basically to switch modes, we *HAVE* to clobber one register...  brain
+ * damage alert!  I don't think that we can execute any code in here in any
+ * other mode than FIQ...  Ok you can switch to another mode, but you can't
+ * get out of that mode without clobbering one register.
+ */
+vector_FIQ:	disable_fiq
+		subs	pc, lr, #4
+
+/*=============================================================================
+ * Address exception handler
+ *-----------------------------------------------------------------------------
+ * These aren't too critical.
+ * (they're not supposed to happen, and won't happen in 32-bit data mode).
+ */
+
+vector_addrexcptn:
+		b	vector_addrexcptn
+
+/*
+ * We group all the following data together to optimise
+ * for CPUs with separate I & D caches.
+ */
+		.align	5
+
+.LCvswi:	.word	vector_swi
+
+.LCsirq:	.word	__temp_irq
+.LCsund:	.word	__temp_und
+.LCsabt:	.word	__temp_abt
+
+__stubs_end:
+
+		.equ	__real_stubs_start, .LCvectors + 0x200
+
+.LCvectors:	swi	SYS_ERROR0
+		b	__real_stubs_start + (vector_undefinstr - __stubs_start)
+		ldr	pc, __real_stubs_start + (.LCvswi - __stubs_start)
+		b	__real_stubs_start + (vector_prefetch - __stubs_start)
+		b	__real_stubs_start + (vector_data - __stubs_start)
+		b	__real_stubs_start + (vector_addrexcptn - __stubs_start)
+		b	__real_stubs_start + (vector_IRQ - __stubs_start)
+		b	__real_stubs_start + (vector_FIQ - __stubs_start)
+
+ENTRY(trap_init)
+		stmfd	sp!, {r4 - r6, lr}
+
+		adr	r1, .LCvectors			@ set up the vectors
+		mov	r0, #0
+		ldmia	r1, {r1, r2, r3, r4, r5, r6, ip, lr}
+		stmia	r0, {r1, r2, r3, r4, r5, r6, ip, lr}
+
+		add	r2, r0, #0x200
+		adr	r0, __stubs_start		@ copy stubs to 0x200
+		adr	r1, __stubs_end
+1:		ldr	r3, [r0], #4
+		str	r3, [r2], #4
+		cmp	r0, r1
+		blt	1b
+		LOADREGS(fd, sp!, {r4 - r6, pc})
 
 		.data
 
+/*
+ * Do not reorder these, and do not insert extra data between...
+ */
+
 __temp_irq:	.word	0				@ saved lr_irq
 		.word	0				@ saved spsr_irq
 		.word	-1				@ old_r0
@@ -800,3 +881,10 @@
 __temp_abt:	.word	0				@ Saved lr_abt
 		.word	0				@ Saved spsr_abt
 		.word	-1				@ old_r0
+
+		.globl	SYMBOL_NAME(cr_alignment)
+		.globl	SYMBOL_NAME(cr_no_alignment)
+SYMBOL_NAME(cr_alignment):
+		.space	4
+SYMBOL_NAME(cr_no_alignment):
+		.space	4
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/entry-common.S linux.18p17/arch/arm/kernel/entry-common.S
--- linux.vanilla/arch/arm/kernel/entry-common.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/entry-common.S	Fri Sep 15 23:28:37 2000
@@ -1,51 +1,54 @@
 /*============================================================================
  * All exits to user mode from the kernel go through this code.
  */
-
-#include <linux/config.h>
-
 		.globl	ret_from_sys_call
 
-ret_from_exception:
-		adr	r0, 1f
-		ldmia	r0, {r0, r1}
+		.align	5
+fast_syscall_return:
+		str	r0, [sp, #S_R0 + 4]		@ returned r0
+slow_syscall_return:
+		add	sp, sp, #4
+ret_from_sys_call:
+		adr	r0, bh_data
+		ldmia	r0, {r0, r4}
 		ldr	r0, [r0]
-		ldr	r1, [r1]
+		ldr	r1, [r4]
 		tst	r0, r1
 		blne	SYMBOL_NAME(do_bottom_half)
-ret_from_intr:	ldr	r0, [sp, #S_PSR]
-		tst	r0, #3
-		beq	ret_with_reschedule
-		b	ret_from_all
+ret_with_reschedule:
+		get_current_task r1		@ check for scheduling
+		ldr	r0, [r1, #TSK_NEED_RESCHED]
+		teq	r0, #0
+		bne	ret_reschedule
+		ldr	r1, [r1, #TSK_SIGPENDING]
+		teq	r1, #0			@ check for signals
+		bne	ret_signal
+
+ret_from_all:	restore_user_regs
 
 ret_signal:	mov	r1, sp
 		adrsvc	al, lr, ret_from_all
+		mov	r2, r4
 		b	SYMBOL_NAME(do_signal)
 
-2:		bl	SYMBOL_NAME(schedule)
+ret_reschedule:	adrsvc	al, lr, ret_with_reschedule
+		b	SYMBOL_NAME(schedule)
 
-ret_from_sys_call:
-		adr	r0, 1f
+		.globl	ret_from_exception
+ret_from_exception:
+		adr	r0, bh_data
 		ldmia	r0, {r0, r1}
 		ldr	r0, [r0]
 		ldr	r1, [r1]
+		mov	r4, #0
 		tst	r0, r1
-		adrsvc	ne, lr, ret_from_intr
-		bne	SYMBOL_NAME(do_bottom_half)
-
-ret_with_reschedule:
-		get_current_task r1
-		ldr	r0, [r1, #TSK_NEED_RESCHED]
-		teq	r0, #0
-		bne	2b
-		ldr	r1, [r1, #TSK_SIGPENDING]
-		teq	r1, #0
-		bne	ret_signal
-
-ret_from_all:	restore_user_regs
+		blne	SYMBOL_NAME(do_bottom_half)
+		ldr	r0, [sp, #S_PSR]
+		tst	r0, #3			@ returning to user mode?
+		beq	ret_with_reschedule
+		b	ret_from_all
 
-1:		.word	SYMBOL_NAME(bh_mask)
-		.word	SYMBOL_NAME(bh_active)
+#include "calls.S"
 
 /*=============================================================================
  * SWI handler
@@ -57,84 +60,65 @@
  * too worried.
  */
 
-#include "calls.S"
-
+		.align	5
 vector_swi:	save_user_regs
-		mov	fp, #0
 		mask_pc	lr, lr
-		ldr	r6, [lr, #-4]!		@ get SWI instruction
+		mov	fp, #0
+		ldr	r6, [lr, #-4]		@ get SWI instruction
 		arm700_bug_check r6, r7
+#ifdef CONFIG_ALIGNMENT_TRAP
+		ldr	r7, .LCswi
+		ldr	r7, [r7]
+		mcr	p15, 0, r7, c1, c0
+#endif
 		enable_irqs r7
-		
+
+		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r4 = arg5)
+		adrsvc	al, lr, fast_syscall_return
+
 		bic	r6, r6, #0xff000000	@ mask off SWI op-code
 		eor	r6, r6, #OS_NUMBER<<20	@ check OS number
 		cmp	r6, #NR_syscalls	@ check upper syscall limit
 		bcs	2f
 
-		get_current_task r5
-		ldr	ip, [r5, #TSK_FLAGS]	@ check for syscall tracing
+		get_current_task r7
+		ldr	ip, [r7, #TSK_FLAGS]	@ check for syscall tracing
+		adr	r5, SYMBOL_NAME(sys_call_table)
 		tst	ip, #PF_TRACESYS
-		bne	1f
+		ldreq	pc, [r5, r6, lsl #2]	@ call sys routine
 
-		adr	ip, SYMBOL_NAME(sys_call_table)
-		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r5 = arg5)
-		mov	lr, pc
-		ldr	pc, [ip, r6, lsl #2]	@ call sys routine
-		add	sp, sp, #4
-		str	r0, [sp, #S_R0]		@ returned r0
-		b	ret_from_sys_call
-
-1:		ldr	r7, [sp, #S_IP]		@ save old IP
+		ldr	r7, [sp, #S_IP + 4]	@ save old IP
 		mov	r0, #0
-		str	r0, [sp, #S_IP]		@ trace entry [IP = 0]
+		str	r0, [sp, #S_IP + 4]	@ trace entry [IP = 0]
 		bl	SYMBOL_NAME(syscall_trace)
-		str	r7, [sp, #S_IP]
-		ldmia	sp, {r0 - r3}		@ have to reload r0 - r3
-		adr	ip, SYMBOL_NAME(sys_call_table)
-		str	r4, [sp, #-4]!		@ new style: (r0 = arg1, r5 = arg5)
+		str	r7, [sp, #S_IP + 4]
+
+		ldmib	sp, {r0 - r3}		@ have to reload r0 - r3
 		mov	lr, pc
-		ldr	pc, [ip, r6, lsl #2]	@ call sys routine
-		add	sp, sp, #4
-		str	r0, [sp, #S_R0]		@ returned r0
+		ldr	pc, [r5, r6, lsl #2]	@ call sys routine
+		str	r0, [sp, #S_R0 + 4]	@ returned r0
+
 		mov	r0, #1
-		str	r0, [sp, #S_IP]		@ trace exit [IP = 1]
+		str	r0, [sp, #S_IP + 4]	@ trace exit [IP = 1]
 		bl	SYMBOL_NAME(syscall_trace)
-		str	r7, [sp, #S_IP]
-		b	ret_from_sys_call
+		str	r7, [sp, #S_IP + 4]
+		b	slow_syscall_return
 
-2:		tst	r6, #0x00f00000		@ is it a Unix SWI?
+2:		add	r1, sp, #4
+		tst	r6, #0x00f00000		@ is it a Unix SWI?
 		bne	3f
-		cmp	r6, #(KSWI_SYS_BASE - KSWI_BASE)
-		bcc	4f			@ not private func
-		bic	r0, r6, #0x000f0000
-		mov	r1, sp
-		bl	SYMBOL_NAME(arm_syscall)
-		b	ret_from_sys_call
-
-3:		eor	r0, r6, #OS_NUMBER<<20	@ Put OS number back
-		mov	r1, sp
-		bl	SYMBOL_NAME(deferred)
-		ldmfd	sp, {r0 - r3}
-		b	ret_from_sys_call
-
-4:		bl	SYMBOL_NAME(sys_ni_syscall)
-		str	r0, [sp, #0]		@ returned r0
-		b	ret_from_sys_call
+		subs	r0, r6, #(KSWI_SYS_BASE - KSWI_BASE)
+		bcs	SYMBOL_NAME(arm_syscall)
+		b	SYMBOL_NAME(sys_ni_syscall) @ not private func
 
-@ r0 = syscall number
-@ r1 = syscall r0
-@ r5 = syscall r4
-@ ip = syscall table
-SYMBOL_NAME(sys_syscall):
-		mov	r6, r0
-		eor	r6, r6, #OS_NUMBER << 20
-		cmp	r6, #NR_syscalls		@ check range
-		movgt	r0, #-ENOSYS
-		movgt	pc, lr
-		add	sp, sp, #4			@ take of the save of our r4
-		ldmib	sp, {r0 - r4}			@ get our args
-		str	r4, [sp, #-4]!			@ Put our arg on the stack
-		ldr	pc, [ip, r6, lsl #2]
+3:		eor	r0, r6, #OS_NUMBER <<20	@ Put OS number back
+		adrsvc	al, lr, slow_syscall_return
+		b	SYMBOL_NAME(deferred)
+
+		.align	5
+
+bh_data:	.word	SYMBOL_NAME(bh_mask)
+		.word	SYMBOL_NAME(bh_active)
 
 ENTRY(sys_call_table)
 #include "calls.S"
@@ -142,10 +126,26 @@
 /*============================================================================
  * Special system call wrappers
  */
+@ r0 = syscall number
+@ r5 = syscall table
+SYMBOL_NAME(sys_syscall):
+		eor	r6, r0, #OS_NUMBER << 20
+		cmp	r6, #NR_syscalls	@ check range
+		addle	ip, sp, #4
+		ldmleib	ip, {r0 - r4}		@ get our args
+		strle	r4, [sp]		@ Put our arg on the stack
+		ldrle	pc, [r5, r6, lsl #2]
+		mov	r0, #-ENOSYS
+		mov	pc, lr
+
 sys_fork_wrapper:
 		add	r0, sp, #4
 		b	SYMBOL_NAME(sys_fork)
 
+sys_vfork_wrapper:
+		add	r0, sp, #4
+		b	SYMBOL_NAME(sys_vfork)
+
 sys_execve_wrapper:
 		add	r3, sp, #4
 		b	SYMBOL_NAME(sys_execve)
@@ -191,99 +191,6 @@
 sys_sigaltstack_wrapper:
 		ldr	r2, [sp, #4 + S_SP]
 		b	do_sigaltstack
-
-/*
- *=============================================================================
- *		Low-level interface code
- *-----------------------------------------------------------------------------
- *		Trap initialisation
- *-----------------------------------------------------------------------------
- *
- * Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20
- * that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes
- * some excess cycles).
- *
- * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000
- * (the kernel).
- * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for
- * the actual address to jump to.
- */
-
-		.section ".text.init",#alloc,#execinstr
-
-#if defined(CONFIG_CPU_32)
-/*
- * these go into 0x00
- */
-.Lbranches:	swi	SYS_ERROR0
-		ldr	pc, .Lbranches + 0xe4
-		ldr	pc, .Lbranches + 0xe8
-		ldr	pc, .Lbranches + 0xec
-		ldr	pc, .Lbranches + 0xf0
-		ldr	pc, .Lbranches + 0xf4
-		ldr	pc, .Lbranches + 0xf8
-		ldr	pc, .Lbranches + 0xfc
-/*
- * this is put into 0xe4 and above
- */
-.Ljump_addresses:
-		.word	vector_undefinstr	@ 0xe4
-		.word	vector_swi		@ 0xe8
-		.word	vector_prefetch		@ 0xec
-		.word	vector_data		@ 0xf0
-		.word	vector_addrexcptn	@ 0xf4
-		.word	vector_IRQ		@ 0xf8
-		.word	_unexp_fiq		@ 0xfc
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
-		stmfd	sp!, {r4 - r7, lr}
-		initialise_traps_extra
-		mov	r0, #0xe4
-		adr	r1, .Ljump_addresses
-		ldmia	r1, {r1 - r7}
-		stmia	r0, {r1 - r7}
-		mov	r0, #0
-		adr	r1, .Lbranches
-		ldmia	r1, {r1 - r7}
-		stmia	r0, {r1 - r7}
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-#elif defined(CONFIG_CPU_26)
-.Ljump_addresses:
-		swi	SYS_ERROR0
-		.word	vector_undefinstr	- 12
-		.word	vector_swi		- 16
-		.word	vector_prefetch		- 20
-		.word	vector_data		- 24
-		.word	vector_addrexcptn	- 28
-		.word	vector_IRQ		- 32
-		.word	_unexp_fiq		- 36
-		b	. + 8
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
-		stmfd	sp!, {r4 - r7, lr}
-		adr	r1, .Ljump_addresses
-		ldmia	r1, {r1 - r7, ip, lr}
-		orr	r2, lr, r2, lsr #2
-		orr	r3, lr, r3, lsr #2
-		orr	r4, lr, r4, lsr #2
-		orr	r5, lr, r5, lsr #2
-		orr	r6, lr, r6, lsr #2
-		orr	r7, lr, r7, lsr #2
-		orr	ip, lr, ip, lsr #2
-		mov	r0, #0
-		stmia	r0, {r1 - r7, ip}
-		ldmfd	sp!, {r4 - r7, pc}^
-#endif
-
-		.previous
-
-/*============================================================================
- * FP support
- */
 
 		.data
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/fiq.c linux.18p17/arch/arm/kernel/fiq.c
--- linux.vanilla/arch/arm/kernel/fiq.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/fiq.c	Fri Sep 15 23:28:37 2000
@@ -2,6 +2,8 @@
  *  linux/arch/arm/kernel/fiq.c
  *
  *  Copyright (C) 1998 Russell King
+ *  Copyright (C) 1998, 1999 Phil Blundell
+ *
  *  FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
  *
  *  FIQ support re-written by Russell King to be more generic
@@ -51,15 +53,12 @@
 #ifdef CONFIG_CPU_32
 static inline void unprotect_page_0(void)
 {
-	__asm__ __volatile__("mcr	p15, 0, %0, c3, c0" :
-			: "r" (DOMAIN_USER_MANAGER |
-			       DOMAIN_KERNEL_CLIENT |
-			       DOMAIN_IO_CLIENT));
+	modify_domain(DOMAIN_USER, DOMAIN_MANAGER);
 }
 
 static inline void protect_page_0(void)
 {
-	set_fs(get_fs());
+	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
 }
 #else
 
@@ -78,7 +77,7 @@
 		unprotect_page_0();
 		*(unsigned long *)FIQ_VECTOR = no_fiq_insn;
 		protect_page_0();
-		__flush_entry_to_ram(FIQ_VECTOR);
+		flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4);
 	}
 
 	return 0;
@@ -106,28 +105,77 @@
 	memcpy((void *)FIQ_VECTOR, start, length);
 
 	protect_page_0();
-#ifdef CONFIG_CPU_32
-	processor.u.armv3v4._flush_cache_area(FIQ_VECTOR, FIQ_VECTOR + length, 1);
-#endif
+	flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length);
 }
 
+/*
+ * Taking an interrupt in FIQ mode is death, so both these functions
+ * disable irqs for the duration. 
+ */
 void set_fiq_regs(struct pt_regs *regs)
 {
-	/* not yet -
-	 * this is temporary to get the floppy working
-	 * again on RiscPC.  It *will* become more
-	 * generic.
-	 */
-#ifdef CONFIG_ARCH_ACORN
-	extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
-					     unsigned long port);
-	floppy_fiqsetup(regs->ARM_r9, regs->ARM_r10, regs->ARM_fp);
+	register unsigned long tmp, tmp2;
+	__asm__ volatile (
+#ifdef CONFIG_CPU_26
+	"mov	%0, pc
+	bic	%1, %0, #0x3
+	orr	%1, %1, #0x0c000001
+	teqp	%1, #0		@ select FIQ mode
+	mov	r0, r0
+	ldmia	%2, {r8 - r14}
+	teqp	%0, #0		@ return to SVC mode
+	mov	r0, r0"
 #endif
+#ifdef CONFIG_CPU_32
+	"mrs	%0, cpsr
+	bic	%1, %0, #0xf
+	orr	%1, %1, #0xc1
+	msr	cpsr, %1	@ select FIQ mode
+	mov	r0, r0
+	ldmia	%2, {r8 - r14}
+	msr	cpsr, %0	@ return to SVC mode
+	mov	r0, r0"
+#endif
+	: "=&r" (tmp), "=&r" (tmp2)
+	: "r" (&regs->ARM_r8)
+	/* These registers aren't modified by the above code in a way
+	   visible to the compiler, but we mark them as clobbers anyway
+	   so that GCC won't put any of the input or output operands in
+	   them.  */
+	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
 }
 
 void get_fiq_regs(struct pt_regs *regs)
 {
-	/* not yet */
+	register unsigned long tmp, tmp2;
+	__asm__ volatile (
+#ifdef CONFIG_CPU_26
+	"mov	%0, pc
+	bic	%1, %0, #0x3
+	orr	%1, %1, #0x0c000001
+	teqp	%1, #0		@ select FIQ mode
+	mov	r0, r0
+	stmia	%2, {r8 - r14}
+	teqp	%0, #0		@ return to SVC mode
+	mov	r0, r0"
+#endif
+#ifdef CONFIG_CPU_32
+	"mrs	%0, cpsr
+	bic	%1, %0, #0xf
+	orr	%1, %1, #0xc1
+	msr	cpsr, %1	@ select FIQ mode
+	mov	r0, r0
+	stmia	%2, {r8 - r14}
+	msr	cpsr, %0	@ return to SVC mode
+	mov	r0, r0"
+#endif
+	: "=&r" (tmp), "=&r" (tmp2)
+	: "r" (&regs->ARM_r8)
+	/* These registers aren't modified by the above code in a way
+	   visible to the compiler, but we mark them as clobbers anyway
+	   so that GCC won't put any of the input or output operands in
+	   them.  */
+	: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
 }
 
 int claim_fiq(struct fiq_handler *f)
@@ -165,7 +213,7 @@
 	while (current_fiq->fiq_op(current_fiq->dev_id, 0));
 }
 
-__initfunc(void init_FIQ(void))
+void __init init_FIQ(void)
 {
 	no_fiq_insn = *(unsigned long *)FIQ_VECTOR;
 	set_fs(get_fs());
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/head-armv.S linux.18p17/arch/arm/kernel/head-armv.S
--- linux.vanilla/arch/arm/kernel/head-armv.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/head-armv.S	Fri Sep 15 23:28:37 2000
@@ -1,157 +1,238 @@
 /*
- * linux/arch/arm/kernel/head32.S
+ * linux/arch/arm/kernel/head-armv.S
  *
- * Copyright (C) 1994-1998 Russell King
+ * Copyright (C) 1994-1999 Russell King
  *
- * Kernel 32 bit startup code for ARM6 / ARM7 / StrongARM
+ * 32-bit kernel startup code for all architectures
  */
 #include <linux/config.h>
 #include <linux/linkage.h>
 
-#ifndef CONFIG_ARCH_VNC
+#include <asm/assembler.h>
+
 #if (TEXTADDR & 0xffff) != 0x8000
 #error TEXTADDR must start at 0xXXXX8000
 #endif
-#else
-		.text
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
-		mov	r0, r0
+
+#define SWAPPER_PGDIR_OFFSET	0x4000
+
+		.globl	SYMBOL_NAME(swapper_pg_dir)
+		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x8000 + SWAPPER_PGDIR_OFFSET
+
+		.section ".text.init",#alloc,#execinstr
+ENTRY(stext)
+ENTRY(_stext)
+
+#ifdef CONFIG_ARCH_NETWINDER
+/*
+ * Compatability cruft for old NetWinder NeTTroms.  This
+ * code is currently scheduled for destruction in 2.5.xx
+ */
+		.rept	8
 		mov	r0, r0
+		.endr
+
+		adr	r2, 1f
+		ldmdb	r2, {r7, r8}
+		and	r3, r2, #0xc000
+		teq	r3, #0x8000
+		beq	__entry
+		bic	r3, r2, #0xc000
+		orr	r3, r3, #0x8000
+		mov	r0, r3
+		mov	r4, #64
+		sub	r5, r8, r7
+		b	1f
+
+		.word	_stext
+		.word	__bss_start
+
+1:
+		.rept	4
+		ldmia	r2!, {r6, r7, r8, r9}
+		stmia	r3!, {r6, r7, r8, r9}
+		.endr
+		subs	r4, r4, #64
+		bcs	1b
+		movs	r4, r5
+		mov	r5, #0
+		movne	pc, r0
+
 		mov	r0, #0
 		mov	r1, #5
 #endif
 
-#define DEBUG
+/*
+ * Entry point.  Entry *must* be called with r0 == 0, with the MMU off.
+ *  r1 contains the unique architecture number.  See
+ * linux/arch/arm/kernel/setup.c machine_desc[] array for the complete
+ * list.  If you require a new number, please follow the instructions
+ * given in Documentation/arm/README.
+ */
+__entry:	teq	r0, #0
+		movne	r0, #'i'
+		bne	__error
+		bl	__lookup_processor_type
+		teq	r10, #0				@ invalid processor?
+		moveq	r0, #'p'
+		beq	__error
+		bl	__lookup_architecture_type
+		teq	r7, #0				@ invalid architecture?
+		moveq	r0, #'a'
+		beq	__error
+		bl	__create_page_tables
+		adr	lr, __ret
+		add	pc, r10, #12			@ flush caches (returns ctrl reg)
 
-		.globl	SYMBOL_NAME(swapper_pg_dir)
-		.equ	SYMBOL_NAME(swapper_pg_dir),	TEXTADDR - 0x4000
+__switch_data:	.long	__mmap_switched
+		.long	SYMBOL_NAME(__bss_start)
+		.long	SYMBOL_NAME(_end)
+		.long	SYMBOL_NAME(processor_id)
+		.long	SYMBOL_NAME(__machine_arch_type)
+		.long	SYMBOL_NAME(cr_alignment)
+		.long	SYMBOL_NAME(init_task_union)+8192
+
+__ret:		ldr	lr, __switch_data
+		mcr	p15, 0, r0, c1, c0
+		mov	r0, r0
+		mov	r0, r0
+		mov	r0, r0
+		mov	pc, lr
+
+		/*
+		 * This code follows on after the page
+		 * table switch and jump above.
+		 *
+		 * r0  = processor control register
+		 * r1  = machine ID
+		 * r9  = processor ID
+		 */
+		.align	5
+__mmap_switched:
+		adr	r3, __switch_data + 4
+		ldmia	r3, {r4, r5, r6, r7, r8, sp}	@ Setup stack
+
+		mov	fp, #0				@ Clear BSS
+1:		cmp	r4, r5
+		strcc	fp, [r4],#4
+		bcc	1b
+
+		str	r9, [r6]			@ Save processor ID
+		str	r1, [r7]			@ Save machine type
+#ifdef CONFIG_ALIGNMENT_TRAP
+		orr	r0, r0, #2			@ ...........A.
+#endif
+		bic	r2, r0, #2			@ Clear 'A' bit
+		stmia	r8, {r0, r2}			@ Save control register values
+		b	SYMBOL_NAME(start_kernel)
 
-		.text
-/*
- * Entry point and restart point.  Entry *must* be called with r0 == 0,
- * MMU off.  Note! These should be unique!!! Please read Documentation/ARM-README
- * for more information.
- *
- *  r1 = 0 -> DEC EBSA-110
- *  r1 = 1 -> Acorn RiscPC
- *  r1 = 2 -> ebsit
- *  r1 = 3 -> nexuspci
- *  r1 = 4 -> DEC EBSA-285
- *  r1 = 5 -> Corel Netwinder
- *  r1 = 6 -> CATS
- *  r1 = 7 -> tbox
- */
 
-ENTRY(stext)
-ENTRY(_stext)
-__entry:	teq	r0, #0					@ check for illegal entry...
-		bne	.Lerror					@ loop indefinitely
-		cmp	r1, #8					@ Unknown machine architecture
-		bge	.Lerror
-/* First thing to do is to get the page tables set up so that we can call the kernel
- * in the correct place.  This is relocatable code...
- * - Read processor ID register (CP#15, CR0).
- */
-		mrc	p15, 0, r9, c0, c0			@ get Processor ID
-/* Values are:
- *		XX01XXXX = ARMv4 architecture (StrongARM)
- *		XX00XXXX = ARMv3 architecture
- *		4156061X = ARM 610
- */
-		adr	r10, .LCProcTypes
-1:		ldmia	r10!, {r5, r6, r8}			@ Get Set, Mask, MMU Flags
-		teq	r5, #0					@ End of list?
-		beq	.Lerror
-		eor	r5, r5, r9
-		tst	r5, r6
-		addne	r10, r10, #8
-		bne	1b
 
-		adr	r4, .LCMachTypes
-		add	r4, r4, r1, lsl #4
-		ldmia	r4, {r4, r5, r6}
 /*
- * r4 = page dir in physical ram
+ * Setup the initial page tables.  We only setup the barest
+ * amount which are required to get the kernel running, which
+ * generally means mapping in the kernel code.
+ *
+ * We only map in 4MB of RAM, which should be sufficient in
+ * all cases.
+ *
  * r5 = physical address of start of RAM
- * r6 = I/O address
+ * r6 = physical IO address
+ * r7 = byte offset into page tables for IO
+ * r8 = page table flags
  */
+__create_page_tables:
+		add	r4, r5, #SWAPPER_PGDIR_OFFSET
 		mov	r0, r4
 		mov	r3, #0
-		add	r2, r0, #0x4000
-1:		str	r3, [r0], #4				@ Clear page table
+		add	r2, r0, #0x4000			@ Clear page table
+1:		str	r3, [r0], #4
+		str	r3, [r0], #4
+		str	r3, [r0], #4
+		str	r3, [r0], #4
 		teq	r0, r2
 		bne	1b
-/*
- * Add enough entries to allow the kernel to be called.
- * It will sort out the real mapping in paging_init.
- * We map in 2MB of memory into (TEXTADDR-0x8000) + 2MB
- */
-		add	r0, r4, #(TEXTADDR - 0x8000) >> 18
-		mov	r3, #0x0000000c				@ SECT_CACHEABLE | SECT_BUFFERABLE
+		/*
+		 * Create identity mapping for first MB of kernel.
+		 * map in four sections (4MB) for kernel.
+		 * these are marked cacheable and bufferable.
+		 *
+		 * The identity mapping will be removed by  paging_init()
+		 */
+		mov	r3, #0x0c
 		orr	r3, r3, r8
 		add	r3, r3, r5
+		add	r0, r4, r5, lsr #18
+		str	r3, [r0]
+		add	r0, r4, #(TEXTADDR - 0x8000) >> 18
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
-#ifdef DEBUG
-/* Map in IO space
- * This allows debug messages to be output via a serial
- * before/while paging_init.
- */
-		add	r0, r4, #0x3800
+		str	r3, [r0], #4
+		add	r3, r3, #1 << 20
+		str	r3, [r0], #4
+#ifdef CONFIG_DEBUG_LL
+		/*
+		 * Map in IO space for serial debugging.
+		 * This allows debug messages to be output
+		 * via a serial before paging_init.
+		 */
+		add	r0, r4, r7
+		rsb	r3, r7, #0x4000 @ PTRS_PER_PGD*sizeof(long)
+		cmp	r3, #0x0800
+		addge	r2, r0, #0x0800
+		addlt	r2, r0, r3
 		orr	r3, r6, r8
-		add	r2, r0, #0x0800
 1:		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		teq	r0, r2
 		bne	1b
-#ifdef CONFIG_ARCH_VNC
-		add	r0, r4, #0x3f00
-		add	r0, r0, #0x00f8
+#ifdef CONFIG_ARCH_NETWINDER
+		teq	r1, #5
+		bne	1f
+		add	r0, r4, #0x3fc0
 		mov	r3, #0x7c000000
 		orr	r3, r3, r8
 		str	r3, [r0], #4
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4
+1:
 #endif
 #endif
 #ifdef CONFIG_ARCH_RPC
-/* Map in screen at 0x02000000 & SCREEN2_BASE
- * Similar reasons here - for debug, and when things go
- * wrong to a certain extent.  This is of limited use to
- * non-Acorn RiscPC architectures though.
- */
+		/*
+		 * Map in screen at 0x02000000 & SCREEN2_BASE
+		 * Similar reasons here - for debug.  This is
+		 * only for Acorn RiscPC architectures.
+		 */
 		teq	r5, #0
-		addne	r0, r4, #0x80				@ 02000000
+		addne	r0, r4, #0x80			@ 02000000
 		movne	r3, #0x02000000
 		orrne	r3, r3, r8
 		strne	r3, [r0]
-		addne	r0, r4, #0x3600				@ d8000000
+		addne	r0, r4, #0x3600			@ d8000000
 		strne	r3, [r0]
 #endif
-@
-@ The following should work on both v3 and v4 implementations
-@
-		mov	lr, pc
-		mov	pc, r10					@ Call processor flush (returns ctrl reg)
-		adr	r5, __entry
-		sub	r10, r10, r5				@ Make r10 PIC
-		ldr	lr, .Lbranch
-		mcr	p15, 0, r0, c1, c0			@ Enable MMU & caches.  In 3 instructions
-								@ we lose this page!
 		mov	pc, lr
 
-.Lerror:
+
+
+/*
+ * Exception handling.  Something went wrong and we can't
+ * proceed.  We ought to tell the user, but since we
+ * don't have any guarantee that we're even running on
+ * the right architecture, we do virtually nothing.
+ * r0 = ascii error character
+ *
+ * Generally, only serious errors cause this.
+ */
+__error:
 #ifdef CONFIG_ARCH_RPC
-/* Turn the screen red on a error - RiscPC only.
+/*
+ * Turn the screen red on a error - RiscPC only.
  */
-1:		mov	r0, #0x02000000
+		mov	r0, #0x02000000
 		mov	r3, #0x11
 		orr	r3, r3, r3, lsl #8
 		orr	r3, r3, r3, lsl #16
@@ -160,160 +241,157 @@
 		str	r3, [r0], #4
 		str	r3, [r0], #4
 #endif
+1:		mov	r0, r0
 		b	1b
 
-.Lbranch:	.long	.Lalready_done_mmap			@ Real address of routine
 
-		@ DEC EBSA110 (pg dir phys, phys ram start, phys i/o)
-.LCMachTypes:	.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000	@ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0xe0000000				@ I/O address
+
+/*
+ * Read processor ID register (CP#15, CR0), and determine
+ * processor type.
+ *
+ * Returns:
+ *	r5, r6, r7 corrupted
+ *	r8  = page table flags
+ *	r9  = processor ID
+ *	r10 = pointer to processor structure
+ */
+__lookup_processor_type:
+		adr	r5, 2f
+		ldmia	r5, {r7, r9, r10}
+		sub	r5, r5, r9
+		add	r7, r7, r5
+		add	r10, r10, r5
+		mrc	p15, 0, r9, c0, c0		@ get processor id
+1:		ldmia	r10, {r5, r6, r8}		@ value, mask, mmuflags
+		eor	r5, r5, r9
+		tst	r5, r6
+		moveq	pc, lr
+		add	r10, r10, #36			@ sizeof(proc_info_list)
+		cmp	r10, r7
+		blt	1b
+		mov	r10, #0
+		mov	pc, lr
+
+2:		.long	__proc_info_end
+		.long	2b
+		.long	__proc_info_begin
+
+/*
+ * Lookup machine architecture
+ * r1 = machine architecture number
+ * Returns:
+ *  r4 = unused word
+ *  r5 = physical start address of RAM
+ *  r6 = physical address of IO
+ *  r7 = byte offset into page tables for IO
+ */
+__lookup_architecture_type:
+		cmp	r1, #(__arch_types_end - __arch_types_start) / 16
+		bge	1f
+		adr	r4, __arch_types_start
+		add	r4, r4, r1, lsl #4
+		ldmia	r4, {r4, r5, r6, r7}
+		mov	r7, r7, lsr #18
+		mov	pc, lr
+1:		mov	r7, #0
+		mov	pc, lr
+
+/*
+ * Machine parameters.  Each machine requires 4 words, which are:
+ *
+ * word0: unused
+ * word1: physical start address of RAM
+ * word2: physical start address of IO
+ * word3: virtual start address of IO
+ *
+ * The IO mappings entered here are used to set up mappings
+ * required for debugging information to be shown to the user.
+ * paging_init() does the real page table initialisation.
+ */
+		@ 0x00 - DEC EBSA110
+__arch_types_start:
+		.long	0
 		.long	0
+		.long	0xe0000000
+		.long	0xe0000000
 
-		@ Acorn RiscPC
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000
+		@ 0x01 - Acorn RiscPC
+		.long	0
 		.long	0x10000000
 		.long	0x03000000
-		.long	0
+		.long	0xe0000000
 
-		@ EBSIT ???
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000
+		@ 0x02 - Unused
 		.long	0
-		.long	0xe0000000
 		.long	0
+		.long	0xe0000000
+		.long	0xe0000000
 
-		@ NexusPCI
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000
+		@ 0x03 - NexusPCI
+		.long	0
 		.long	0x40000000
 		.long	0x10000000
-		.long	0
+		.long	0xe0000000
 
-		@ DEC EBSA285
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xFE000000)
+		@ 0x04 - DEC EBSA285
 		.long	0
-
-		@ Corel VNC
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xfe000000)
 		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-		@ CATS
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
-		.long	0					@ Address of RAM
-		.long	0x24000000				@ I/O base address (0x42000000 -> 0xfe000000)
+		@ 0x05 - Rebel.com NetWinder
 		.long	0
-
-		@ tbox
-		.long	SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000
-		.long	0x80000000				@ Address of RAM
-		.long	0x00400000				@ Uart
 		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-.LCProcTypes:	@ ARM6 / 610
-		.long	0x41560600
-		.long	0xffffff00
-		.long	0x00000c12
-		b	.Larmv3_flush_early			@ arm v3 flush & ctrl early setup
-		mov	pc, lr
-
-		@ ARM7
-		.long	0x41007000
-		.long	0xfffff000
-		.long	0x00000c12
-		b	.Larmv3_flush_late			@ arm v3 flush & ctrl late setup
-		mov	pc, lr
-
-		@ ARM710
-		.long	0x41007000
-		.long	0xfff8f000				@ arm710 processors are weird
-		.long	0x00000c12
-		b	.Larmv3_flush_late			@ arm v3 flush & ctrl late setup
-		mov	pc, lr
+		@ 0x06 - CATS
+		.long	0
+		.long	0
+		.long	0x42000000
+		.long	0xfe000000
 
-		@ StrongARM
-		.long	0x4401a100
-		.long	0xfffffff0
-		.long	0x00000c02
-		b	.Larmv4_flush_early
-		b	.Lsa_fastclock
+		@ 0x07 - tbox
+		.long	0
+		.long	0x80000000
+		.long	0x00400000			@ Uart
+		.long	0xe0000000
 
+		@ 0x08 - DEC EBSA285 as co-processor
 		.long	0
-		.align
+		.long	0
+		.long	0x42000000			@ Physical I/O base address
+		.long	0x7cf00000			@ Virtual I/O base address
 
-.Larmv3_flush_early:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0			@ flush caches on v3
-		mcr	p15, 0, r0, c5, c0			@ flush TLBs on v3
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mov	r0, #0x3d				@ ....S..DPWC.M
-		orr	r0, r0, #0x100
-		mov	pc, lr
+		@ 0x09 - CL-PS7110
 
-.Larmv3_flush_late:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0			@ flush caches on v3
-		mcr	p15, 0, r0, c5, c0			@ flush TLBs on v3
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mov	r0, #0x7d				@ ....S.LDPWC.M
-		orr	r0, r0, #0x100
-		mov	pc, lr
+		@ 0x0a - Acorn Archimedes
 
-.Larmv4_flush_early:
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c7			@ flush I,D caches on v4
-		mcr	p15, 0, r0, c7, c10, 4			@ drain write buffer on v4
-		mcr	p15, 0, r0, c8, c7			@ flush I,D TLBs on v4
-		mcr	p15, 0, r4, c2, c0			@ load page table pointer
-		mov	r0, #0x1f				@ Domains 0, 1 = client
-		mcr	p15, 0, r0, c3, c0			@ load domain access register
-		mrc	p15, 0, r0, c1, c0			@ get control register v4
-		bic	r0, r0, #0x0e00
-		bic	r0, r0, #0x0002
-		orr	r0, r0, #0x003d				@ I...S..DPWC.M
-		orr	r0, r0, #0x1100				@ v4 supports separate I cache
-		mov	pc, lr
+		@ 0x0b - Acorn A5000
 
-		.section ".text.init",#alloc,#execinstr
+		@ 0x0c - Etoile
 
-.Lsa_fastclock:	mcr	p15, 0, r4, c15, c1, 2			@ Enable clock switching
-		mov	pc, lr
+		@ 0x0d - LaCie_NAS
 
-.LC0:		.long	SYMBOL_NAME(__entry)
-		.long	SYMBOL_NAME(machine_type)
-		.long	SYMBOL_NAME(__bss_start)
-		.long	SYMBOL_NAME(processor_id)
-		.long	SYMBOL_NAME(_end)
-		.long	SYMBOL_NAME(init_task_union)+8192
-		.align
+		@ 0x0e - CL-PS7500
 
-.Lalready_done_mmap:
-		adr	r4, .LC0
-		ldmia	r4, {r3, r4, r5, r6, r8, sp}		@ Setup stack
-		add	r10, r10, r3				@ Add base back in
-		mov	fp, #0
-1:		cmp	r5, r8					@ Clear BSS
-		strcc	fp, [r5],#4
-		bcc	1b
+		@ 0x0f - Shark
 
-		str	r1, [r4]				@ Save machine type
-		str	r9, [r6]				@ Save processor ID
-		mov	lr, pc
-		add	pc, r10, #4				@ Call post-processor init
-		mov	fp, #0
-		b	SYMBOL_NAME(start_kernel)
+		@ 0x10 - SA1100
+
+		/*
+		 * Don't add anything here unless you have an
+		 * architecture number allocated - see
+		 * Documentation/arm/README
+		 */
+__arch_types_end:
 
-		.text
-#ifdef DEBUG
+#ifdef CONFIG_DEBUG_LL
 /*
  * Some debugging routines (useful if you've got MM problems and
- * printk isn't working).  For DEBUGGING ONLY!!!
+ * printk isn't working).  For DEBUGGING ONLY!!!  Do not leave
+ * references to these in a production kernel!
  */
 #if defined(CONFIG_ARCH_RPC)
 		.macro	addruart,rx
@@ -362,64 +440,71 @@
 		beq	1001b
 		.endm
 
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#ifndef CONFIG_DEBUG_DC21285_PORT
+	/* For NetWinder debugging */
 		.macro	addruart,rx
-		mov	\rx, #0xfe000000
+		mov	\rx, #0xff000000
+		orr	\rx, \rx, #0x000003f8
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, #0x160]	@ UARTDR
+		strb	\rd, [\rx]
 		.endm
 
 		.macro	busyuart,rd,rx
-1001:		ldr	\rd, [\rx, #0x178]	@ UARTFLG
-		tst	\rd, #1 << 3
-		bne	1001b
+1002:		ldrb	\rd, [\rx, #0x5]
+		and	\rd, \rd, #0x60
+		teq	\rd, #0x60
+		bne	1002b
 		.endm
 
 		.macro	waituart,rd,rx
+1001:		ldrb	\rd, [\rx, #0x6]
+		tst	\rd, #0x10
+		beq	1001b
 		.endm
+#else
+	/* For EBSA285 debugging */
+		.equ	dc21285_high, ARMCSR_BASE & 0xff000000
+		.equ	dc21285_low,  ARMCSR_BASE & 0x00ffffff
 
-#elif defined(CONFIG_ARCH_NEXUSPCI)
 		.macro	addruart,rx
-		ldr	\rx, =0xfff00000
+		mov	\rx, #dc21285_high
+		.if	dc21285_low
+		orr	\rx, \rx, #dc21285_low
+		.endif
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, #0xc]
+		str	\rd, [\rx, #0x160]	@ UARTDR
 		.endm
 
 		.macro	busyuart,rd,rx
-1001:		ldr	\rd, [\rx, #0x4]
-		tst	\rd, #1 << 0
+1001:		ldr	\rd, [\rx, #0x178]	@ UARTFLG
+		tst	\rd, #1 << 3
 		bne	1001b
 		.endm
 
 		.macro	waituart,rd,rx
 		.endm
-
-#elif defined(CONFIG_ARCH_VNC)
+#endif
+#elif defined(CONFIG_ARCH_NEXUSPCI)
 		.macro	addruart,rx
-		mov	\rx, #0xff000000
-		orr	\rx, \rx, #0x00e00000
-		orr	\rx, \rx, #0x000003f8
+		ldr	\rx, =0xfff00000
 		.endm
 
 		.macro	senduart,rd,rx
-		strb	\rd, [\rx]
+		str	\rd, [\rx, #0xc]
 		.endm
 
 		.macro	busyuart,rd,rx
-1002:		ldrb	\rd, [\rx, #0x5]
-		and	\rd, \rd, #0x60
-		teq	\rd, #0x60
-		bne	1002b
+1001:		ldr	\rd, [\rx, #0x4]
+		tst	\rd, #1 << 0
+		bne	1001b
 		.endm
 
 		.macro	waituart,rd,rx
-1001:		ldrb	\rd, [\rx, #0x6]
-		tst	\rd, #0x10
-		beq	1001b
 		.endm
 #else
 #error Unknown architecture
@@ -475,8 +560,6 @@
 		mov	r1, r0
 		mov	r0, #0
 		b	1b
-
-		.ltorg
 
 		.bss
 hexbuf:		.space 16
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/hw-ebsa285.c linux.18p17/arch/arm/kernel/hw-ebsa285.c
--- linux.vanilla/arch/arm/kernel/hw-ebsa285.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/hw-ebsa285.c	Thu Jan  1 01:00:00 1970
@@ -1,161 +0,0 @@
-/*
- * arch/arm/kernel/hw-ebsa286.c
- *
- * EBSA285 hardware specific functions
- *
- * Copyright (C) 1998 Russell King, Phil Blundel
- */
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ptrace.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-
-#include <asm/irq.h>
-#include <asm/system.h>
-
-extern int setup_arm_irq(int, struct irqaction *);
-
-extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
-extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
-extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
-
-static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 };
-static int irqmap_cats[] __initdata = { 18, 8, 9, 11 };
-
-__initfunc(static int ebsa_irqval(struct pci_dev *dev))
-{
-	unsigned char pin;
-	
-	pcibios_read_config_byte(dev->bus->number,
-				 dev->devfn,
-				 PCI_INTERRUPT_PIN,
-				 &pin);
-	
-	return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
-}
-
-__initfunc(static int cats_irqval(struct pci_dev *dev))
-{
-	if (dev->irq >= 128)
-		return 32 + (dev->irq & 0x1f);
-
-	switch (dev->irq) {
-	case 1:
-	case 2:
-	case 3:
-	case 4:
-		return irqmap_cats[dev->irq - 1];
-	case 0:
-		return 0;
-	}
-
-	printk("PCI: device %02x:%02x has unknown irq line %x\n",
-	       dev->bus->number, dev->devfn, dev->irq);
-	return 0;
-}
-
-__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
-{
-	char cmd;
-
-	/* sort out the irq mapping for this device */
-	switch (machine_type) {
-	case MACH_TYPE_EBSA285:
-		dev->irq = ebsa_irqval(dev);
-		break;
-	case MACH_TYPE_CATS:
-		dev->irq = cats_irqval(dev);
-		break;
-	}
-
-	/* Turn on bus mastering - boot loader doesn't
-	 * - perhaps it should! - dag
-	 */
-	pci_read_config_byte(dev, PCI_COMMAND, &cmd);
-	pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-}
-
-static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
-{
-	const char *err = "unknown";
-	unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff;
-	unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07;
-	static unsigned long next_warn[7];
-	int idx = 6;
-
-	switch(irq) {
-	case IRQ_PCIPARITY:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 31;
-		idx = 0;
-		err = "parity";
-		break;
-
-	case IRQ_PCITARGETABORT:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 28;
-		idx = 1;
-		err = "target abort";
-		break;
-
-	case IRQ_PCIMASTERABORT:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 29;
-		idx = 2;
-		err = "master abort";
-		break;
-
-	case IRQ_PCIDATAPARITY:
-		*(unsigned long *)0xfe000004 = cmd | 1 << 24;
-		idx = 3;
-		err = "data parity";
-		break;
-
-	case IRQ_DISCARDTIMER:
-		*(unsigned long *)0xfe00013c = ctrl | 1 << 8;
-		idx = 4;
-		err = "discard timer";
-		break;
-
-	case IRQ_SERR:
-		*(unsigned long *)0xfe00013c = ctrl | 1 << 3;
-		idx = 5;
-		err = "system";
-		break;
-	}
-	if (time_after_eq(jiffies, next_warn[idx])) {
-		next_warn[idx] = jiffies + 3 * HZ / 100;
-		printk(KERN_ERR "PCI %s error detected\n", err);
-	}
-}
-
-static struct irqaction irq_pci_error = {
-	irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
-};
-
-__initfunc(void pcibios_init_ebsa285(void))
-{
-	setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error);
-	setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error);
-	setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error);
-	setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error);
-	setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error);
-	setup_arm_irq(IRQ_SERR, &irq_pci_error);
-
-	/*
-	 * Map our SDRAM at a known address in PCI space, just in case
-	 * the firmware had other ideas.  Using a nonzero base is slightly
-	 * bizarre but apparently necessary to avoid problems with some
-	 * video cards.
-	 *
-	 * We should really only do this if the central function is enabled.
-	 */
-	*(unsigned long *)0xfe000010 = 0;
-	*(unsigned long *)0xfe000018 = 0xe0000000;
-	*(unsigned long *)0xfe0000f8 = 0;
-	*(unsigned long *)0xfe0000fc = 0;
-	*(unsigned long *)0xfe000100 = 0x01fc0000;
-	*(unsigned long *)0xfe000104 = 0;
-	*(unsigned long *)0xfe000108 = 0x80000000;
-	*(unsigned long *)0xfe000004 = 0x17;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/hw-footbridge.c linux.18p17/arch/arm/kernel/hw-footbridge.c
--- linux.vanilla/arch/arm/kernel/hw-footbridge.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/kernel/hw-footbridge.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,951 @@
+/*
+ * arch/arm/kernel/hw-footbridge.c
+ *
+ * Footbridge-dependent machine fixup
+ *
+ * Copyright (C) 1998, 1999 Russell King, Phil Blundell
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/dec21285.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#define IRDA_IO_BASE		0x180
+#define ETHER10_IO_BASE		0x301
+#define GP1_IO_BASE		0x338
+#define GP2_IO_BASE		0x33a
+#define DEC21143_IO_BASE	0x401
+#define DEC21143_MEM_BASE	0x00800000
+#define CYBER2000_MEM_BASE	0x01000000
+
+int	have_isa_bridge;
+
+extern int setup_arm_irq(int, struct irqaction *);
+extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
+extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
+extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+
+#ifdef CONFIG_PCI
+
+static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 };
+
+__initfunc(static int ebsa_irqval(struct pci_dev *dev))
+{
+	unsigned char pin;
+	
+	pcibios_read_config_byte(dev->bus->number,
+				 dev->devfn,
+				 PCI_INTERRUPT_PIN,
+				 &pin);
+	
+	return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
+}
+
+#ifdef CONFIG_CATS
+static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
+
+__initfunc(static int cats_irqval(struct pci_dev *dev))
+{
+	if (dev->irq >= 128)
+		return 16 + (dev->irq & 0x1f);
+
+	switch (dev->irq) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		return irqmap_cats[dev->irq - 1];
+	case 0:
+		return 0;
+	}
+
+	printk("PCI: device %02x:%02x has unknown irq line %x\n",
+	       dev->bus->number, dev->devfn, dev->irq);
+	return 0;
+}
+#endif
+
+__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
+{
+	/* Latency timer of 32 */
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32);
+
+	/* 32-byte cache line size */
+	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
+
+	/* Set SysErr enable, Parity enable */
+	pci_set_cmd(dev, 0, PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+
+	/* If this device is an ISA bridge, set the
+	 * have_isa_bridge flag.  We will then go looking
+	 * for things like keyboard, etc
+	 */
+	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA ||
+	    (dev->class >> 8) == PCI_CLASS_BRIDGE_EISA)
+		have_isa_bridge = !0;
+
+	/* sort out the irq mapping for this device */
+	switch (machine_arch_type) {
+	case MACH_TYPE_EBSA285:
+		dev->irq = ebsa_irqval(dev);
+		/* Turn on bus mastering - boot loader doesn't
+		 * - perhaps it should! - dag
+		 */
+		pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+		break;
+
+#ifdef CONFIG_CATS
+	case MACH_TYPE_CATS:
+		dev->irq = cats_irqval(dev);
+		/* Turn on bus mastering - boot loader doesn't
+		 * - perhaps it should! - dag
+		 */
+		pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+		break;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+	case MACH_TYPE_NETWINDER:
+		/* disable ROM */
+		pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0);
+
+#define DEV(v,d) ((v)<<16|(d))
+		switch (DEV(dev->vendor, dev->device)) {
+		/* Ether 100 */
+		case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142):
+			pci_set_base_addr(dev, 0, DEC21143_IO_BASE);
+			pci_set_base_addr(dev, 1, DEC21143_MEM_BASE);
+			pci_set_cmd(dev, 0, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+			/* Put the chip to sleep in case the driver isn't loaded */
+			pci_write_config_dword(dev, 0x40, 0x80000000);
+			dev->irq = IRQ_NETWINDER_ETHER100;
+			break;
+
+		/* Ether 10 */
+		case DEV(PCI_VENDOR_ID_WINBOND2,0x5a5a):
+			pci_set_base_addr(dev, 0, ETHER10_IO_BASE);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+			dev->irq = IRQ_NETWINDER_ETHER10;
+			break;
+
+		/* ISA bridge */
+		case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_83C553):
+			pci_set_base_addr(dev, 0, 0);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+			/*
+			 * Enable all memory requests from ISA to be channeled to PCI
+			 */
+			pci_write_config_byte(dev, 0x48, 255);
+			/*
+			 * Disable ping-pong (as per errata)
+			 */
+			pci_write_config_byte(dev, 0x42, 0);
+			/*
+			 * Enable PCI packet retry
+			 */
+			pci_write_config_byte(dev, 0x40, 0x22);
+			/*
+			 * Do not use PCI CPU park enable, park on
+			 * last master, disable GAT bit
+			 */
+			pci_write_config_byte(dev, 0x83, 0x02);
+			/*
+			 * Default rotating priorities
+			 */
+			pci_write_config_byte(dev, 0x80, 0xe0);
+			/*
+			 * Rotate bank 4
+			 */
+			pci_write_config_byte(dev, 0x81, 0x01);
+			break;
+
+		/* IDE */
+		case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_82C105):
+			pci_set_base_addr(dev, 0, 0x1f1);
+			pci_set_base_addr(dev, 1, 0x3f5);
+			pci_set_base_addr(dev, 2, 0x171);
+			pci_set_base_addr(dev, 3, 0x375);
+			pci_set_base_addr(dev, 4, 0xe801);
+			pci_set_cmd(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+			dev->irq = IRQ_ISA_HARDDISK1;
+			break;
+
+		/* VGA */
+		case DEV(PCI_VENDOR_ID_INTERG,0x2000):
+			pci_set_base_addr(dev, 0, CYBER2000_MEM_BASE);
+			pci_set_cmd(dev, PCI_COMMAND_MASTER, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+			dev->irq = IRQ_NETWINDER_VGA;
+			break;
+		}
+#endif
+	}
+}
+
+static inline void
+report_pci_dev_error(void)
+{
+	struct pci_dev *dev;
+
+	for (dev = pci_devices; dev; dev = dev->next) {
+		unsigned short status;
+
+		pci_read_config_word(dev, PCI_STATUS, &status);
+		if (status & 0xf900) {
+			printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n",
+				dev->vendor, dev->device, status);
+
+			pci_write_config_word(dev, PCI_STATUS, status & 0xf900);
+		}
+	}
+}
+#else
+#define report_pci_dev_error()
+#endif
+
+/*
+ * Warn on PCI errors.  Please report any occurances!
+ */
+static void
+irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+	static unsigned long next_warn;
+	unsigned long cmd       = *CSR_PCICMD & 0x0000ffff;
+	unsigned long ctrl      = (*CSR_SA110_CNTL) & 0xffffde07;
+	unsigned long irqstatus = *CSR_IRQ_RAWSTATUS;
+	int warn = time_after_eq(jiffies, next_warn);
+
+	ctrl |= SA110_CNTL_DISCARDTIMER;
+
+	if (warn) {
+		next_warn = jiffies + 3 * HZ / 100;
+		printk(KERN_DEBUG "PCI: ");
+	}
+
+	if (irqstatus & (1 << 31)) {
+		if (warn)
+			printk("parity error ");
+		cmd |= 1 << 31;
+	}
+
+	if (irqstatus & (1 << 30)) {
+		if (warn)
+			printk("target abort ");
+		cmd |= 1 << 28;
+	}
+
+	if (irqstatus & (1 << 29)) {
+		if (warn)
+			printk("master abort ");
+		cmd |= 1 << 29;
+	}
+
+	if (irqstatus & (1 << 28)) {
+		if (warn)
+			printk("data parity error ");
+		cmd |= 1 << 24;
+	}
+
+	if (irqstatus & (1 << 27)) {
+		if (warn)
+			printk("discard timer expired ");
+		ctrl &= ~SA110_CNTL_DISCARDTIMER;
+	}
+
+	if (irqstatus & (1 << 23)) {
+		if (warn)
+			printk("system error ");
+		ctrl |= SA110_CNTL_RXSERR;
+	}
+
+	if (warn)
+		printk("pc=[<%08lX>]\n", instruction_pointer(regs));
+
+	report_pci_dev_error();
+
+	*CSR_PCICMD = cmd;
+	*CSR_SA110_CNTL = ctrl;
+}
+
+static struct irqaction irq_pci_error = {
+	irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
+};
+
+__initfunc(void pcibios_init_ebsa285(void))
+{
+	setup_arm_irq(IRQ_PCI_ERR, &irq_pci_error);
+}
+
+/*
+ * Netwinder stuff
+ */
+#ifdef CONFIG_ARCH_NETWINDER
+
+/*
+ * Winbond WB83977F accessibility stuff
+ */
+static inline void wb977_open(void)
+{
+	outb(0x87, 0x370);
+	outb(0x87, 0x370);
+}
+
+static inline void wb977_close(void)
+{
+	outb(0xaa, 0x370);
+}
+
+static inline void wb977_wb(int reg, int val)
+{
+	outb(reg, 0x370);
+	outb(val, 0x371);
+}
+
+static inline void wb977_ww(int reg, int val)
+{
+	outb(reg, 0x370);
+	outb(val >> 8, 0x371);
+	outb(reg + 1, 0x370);
+	outb(val, 0x371);
+}
+
+#define wb977_device_select(dev)	wb977_wb(0x07, dev)
+#define wb977_device_disable()		wb977_wb(0x30, 0x00)
+#define wb977_device_enable()		wb977_wb(0x30, 0x01)
+
+/*
+ * This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE
+ */
+spinlock_t __netwinder_data gpio_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned int __netwinder_data current_gpio_op = 0;
+static unsigned int __netwinder_data current_gpio_io = 0;
+static unsigned int __netwinder_data current_cpld = 0;
+
+void __netwinder_text gpio_modify_op(int mask, int set)
+{
+	unsigned int new_gpio, changed;
+
+	new_gpio = (current_gpio_op & ~mask) | set;
+	changed = new_gpio ^ current_gpio_op;
+	current_gpio_op = new_gpio;
+
+	if (changed & 0xff)
+		outb(new_gpio, GP1_IO_BASE);
+	if (changed & 0xff00)
+		outb(new_gpio >> 8, GP2_IO_BASE);
+}
+
+static inline void __gpio_modify_io(int mask, int in)
+{
+	unsigned int new_gpio, changed;
+	int port;
+
+	new_gpio = (current_gpio_io & ~mask) | in;
+	changed = new_gpio ^ current_gpio_io;
+	current_gpio_io = new_gpio;
+
+	changed >>= 1;
+	new_gpio >>= 1;
+
+	wb977_device_select(7);
+
+	for (port = 0xe1; changed && port < 0xe8; changed >>= 1) {
+		wb977_wb(port, new_gpio & 1);
+
+		port += 1;
+		new_gpio >>= 1;
+	}
+
+	wb977_device_select(8);
+
+	for (port = 0xe8; changed && port < 0xec; changed >>= 1) {
+		wb977_wb(port, new_gpio & 1);
+
+		port += 1;
+		new_gpio >>= 1;
+	}
+}
+
+void __netwinder_text gpio_modify_io(int mask, int in)
+{
+	/* Open up the SuperIO chip */
+	wb977_open();
+
+	__gpio_modify_io(mask, in);
+
+	/* Close up the EFER gate */
+	wb977_close();
+}
+
+int __netwinder_text gpio_read(void)
+{
+	return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;
+}
+
+/*
+ * Initialise the Winbond W83977F global registers
+ */
+static inline void wb977_init_global(void)
+{
+	/*
+	 * Enable R/W config registers
+	 */
+	wb977_wb(0x26, 0x40);
+
+	/*
+	 * Power down FDC (not used)
+	 */
+	wb977_wb(0x22, 0xfe);
+
+	/*
+	 * GP12, GP11, CIRRX, IRRXH, GP10
+	 */
+	wb977_wb(0x2a, 0xc1);
+
+	/*
+	 * GP23, GP22, GP21, GP20, GP13
+	 */
+	wb977_wb(0x2b, 0x6b);
+
+	/*
+	 * GP17, GP16, GP15, GP14
+	 */
+	wb977_wb(0x2c, 0x55);
+}
+
+/*
+ * Initialise the Winbond W83977F printer port
+ */
+static inline void wb977_init_printer(void)
+{
+	wb977_device_select(1);
+
+	/*
+	 * mode 1 == EPP
+	 */
+	wb977_wb(0xf0, 0x01);
+}
+
+/*
+ * Initialise the Winbond W83977F keyboard controller
+ */
+static inline void wb977_init_keyboard(void)
+{
+	wb977_device_select(5);
+
+	/*
+	 * Keyboard controller address
+	 */
+	wb977_ww(0x60, 0x0060);
+	wb977_ww(0x62, 0x0064);
+
+	/*
+	 * Keyboard IRQ 1, active high, edge trigger
+	 */
+	wb977_wb(0x70, 1);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * Mouse IRQ 5, active high, edge trigger
+	 */
+	wb977_wb(0x72, 5);
+	wb977_wb(0x73, 0x02);
+
+	/*
+	 * KBC 8MHz
+	 */
+	wb977_wb(0xf0, 0x40);
+
+	/*
+	 * Enable device
+	 */
+	wb977_device_enable();
+}
+
+/*
+ * Initialise the Winbond W83977F Infra-Red device
+ */
+static inline void wb977_init_irda(void)
+{
+	wb977_device_select(6);
+
+	/*
+	 * IR base address
+	 */
+	wb977_ww(0x60, IRDA_IO_BASE);
+
+	/*
+	 * IRDA IRQ 6, active high, edge trigger
+	 */
+	wb977_wb(0x70, 6);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * RX DMA - ISA DMA 0
+	 */
+	wb977_wb(0x74, 0x00);
+
+	/*
+	 * TX DMA - Disable Tx DMA
+	 */
+	wb977_wb(0x75, 0x04);
+
+	/*
+	 * Append CRC, Enable bank selection
+	 */
+	wb977_wb(0xf0, 0x03);
+
+	/*
+	 * Enable device
+	 */
+	wb977_device_enable();
+}
+
+/*
+ * Initialise Winbond W83977F general purpose IO
+ */
+static inline void wb977_init_gpio(void)
+{
+	unsigned long flags;
+
+	/*
+	 * Set up initial I/O definitions
+	 */
+	current_gpio_io = -1;
+	__gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER);
+
+	wb977_device_select(7);
+
+	/*
+	 * Group1 base address
+	 */
+	wb977_ww(0x60, GP1_IO_BASE);
+	wb977_ww(0x62, 0);
+	wb977_ww(0x64, 0);
+
+	/*
+	 * GP10 (Orage button) IRQ 10, active high, edge trigger
+	 */
+	wb977_wb(0x70, 10);
+	wb977_wb(0x71, 0x02);
+
+	/*
+	 * GP10: Debounce filter enabled, IRQ, input
+	 */
+	wb977_wb(0xe0, 0x19);
+
+	/*
+	 * Enable Group1
+	 */
+	wb977_device_enable();
+
+	wb977_device_select(8);
+
+	/*
+	 * Group2 base address
+	 */
+	wb977_ww(0x60, GP2_IO_BASE);
+
+	/*
+	 * Clear watchdog timer regs
+	 *  - timer disable
+	 */
+	wb977_wb(0xf2, 0x00);
+
+	/*
+	 *  - disable LED, no mouse nor keyboard IRQ
+	 */
+	wb977_wb(0xf3, 0x00);
+
+	/*
+	 *  - timer counting, disable power LED, disable timeouot
+	 */
+	wb977_wb(0xf4, 0x00);
+
+	/*
+	 * Enable group2
+	 */
+	wb977_device_enable();
+
+	/*
+	 * Set Group1/Group2 outputs
+	 */
+	spin_lock_irqsave(&gpio_lock, flags);
+	gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
+	spin_unlock_irqrestore(&gpio_loc, flags);
+}
+
+/*
+ * Initialise the Winbond W83977F chip.
+ */
+__initfunc(static void wb977_init(void))
+{
+	request_region(0x370, 2, "W83977AF configuration");
+
+	/*
+	 * Open up the SuperIO chip
+	 */
+	wb977_open();
+
+	/*
+	 * Initialise the global registers
+	 */
+	wb977_init_global();
+
+	/*
+	 * Initialise the various devices in
+	 * the multi-IO chip.
+	 */
+	wb977_init_printer();
+	wb977_init_keyboard();
+	wb977_init_irda();
+	wb977_init_gpio();
+
+	/*
+	 * Close up the EFER gate
+	 */
+	wb977_close();
+}
+
+void __netwinder_text cpld_modify(int mask, int set)
+{
+	int msk;
+
+	current_cpld = (current_cpld & ~mask) | set;
+
+	gpio_modify_io(GPIO_DATA, 0);
+	gpio_modify_op(GPIO_IOLOAD, 0);
+
+	for (msk = 8; msk; msk >>= 1) {
+		int bit = current_cpld & msk;
+
+		gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
+		gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
+	}
+
+	gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
+	gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
+	gpio_modify_op(GPIO_IOLOAD, 0);
+}
+
+__initfunc(static void cpld_init(void))
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(-1, CPLD_UNMUTE | 4);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static unsigned char rwa_unlock[] __initdata =
+{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b,
+  0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74,
+  0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };
+
+#ifndef DEBUG
+#define dprintk if (0) printk
+#else
+#define dprintk printk
+#endif
+
+#define WRITE_RWA(r,v) do { outb((r), 0x279); outb((v), 0xa79); } while (0)
+
+static inline void rwa010_unlock(void)
+{
+	int i;
+
+	WRITE_RWA(2, 2);
+	mdelay(10);
+
+	for (i = 0; i < sizeof(rwa_unlock); i++)
+		outb(rwa_unlock[i], 0x279);
+}
+
+static inline void rwa010_read_ident(void)
+{
+	unsigned char si[9];
+	int i, j;
+
+	WRITE_RWA(3, 0);
+	WRITE_RWA(0, 128);
+
+	outb(1, 0x279);
+
+	mdelay(10);
+
+	dprintk("Identifier: ");
+	for (i = 0; i < 9; i++) {
+		si[i] = 0;
+		for (j = 0; j < 8; j++) {
+			int bit;
+			mdelay(1);
+			inb(0x203);
+			mdelay(1);
+			bit = inb(0x203);
+			dprintk("%02X ", bit);
+			si[i] |= bit << j;
+		}
+		mdelay(10);
+		dprintk("%02X ", si[i]);
+	}
+	dprintk("\n");
+}
+
+static inline void rwa010_global_init(void)
+{
+	WRITE_RWA(6, 2);	// Assign a card no = 2
+
+	dprintk("Card no = %d\n", inb(0x203));
+
+	WRITE_RWA(7, 3);
+	WRITE_RWA(0x30, 0);
+
+	WRITE_RWA(7, 4);
+	WRITE_RWA(0x30, 0);
+
+	WRITE_RWA(7, 2);
+	WRITE_RWA(0x30, 0);
+}
+
+static inline void rwa010_game_port_init(void)
+{
+	int i;
+
+	WRITE_RWA(7, 5);
+
+	dprintk("Slider base: ");
+	WRITE_RWA(0x61, 1);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, 2);
+	dprintk("%02X%02X (201)\n", inb(0x203), i);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_waveartist_init(int base, int irq, int dma)
+{
+	int i;
+
+	WRITE_RWA(7, 0);
+
+	dprintk("WaveArtist base: ");
+	WRITE_RWA(0x61, base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, base >> 8);
+	dprintk("%02X%02X (%X),", inb(0x203), i, base);
+
+	WRITE_RWA(0x70, irq);
+	dprintk(" irq: %d (%d),", inb(0x203), irq);
+
+	WRITE_RWA(0x74, dma);
+	dprintk(" dma: %d (%d)\n", inb(0x203), dma);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma)
+{
+	int i;
+
+	WRITE_RWA(7, 1);
+
+	dprintk("SoundBlaster base: ");
+	WRITE_RWA(0x61, sb_base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x60, sb_base >> 8);
+	dprintk("%02X%02X (%X),", inb(0x203), i, sb_base);
+
+	dprintk(" irq: ");
+	WRITE_RWA(0x70, irq);
+	dprintk("%d (%d),", inb(0x203), irq);
+
+	dprintk(" 8-bit DMA: ");
+	WRITE_RWA(0x74, dma);
+	dprintk("%d (%d)\n", inb(0x203), dma);
+
+	dprintk("AdLib base: ");
+	WRITE_RWA(0x63, al_base);
+	i = inb(0x203);
+
+	WRITE_RWA(0x62, al_base >> 8);
+	dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base);
+
+	WRITE_RWA(0x30, 1);
+}
+
+static void rwa010_soundblaster_reset(void)
+{
+	int i;
+
+	outb(1, 0x226);
+	udelay(3);
+	outb(0, 0x226);
+
+	for (i = 0; i < 5; i++) {
+		if (inb(0x22e) & 0x80)
+			break;
+		mdelay(1);
+	}
+	if (i == 5)
+		printk("SoundBlaster: DSP reset failed\n");
+
+	dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a));
+
+	for (i = 0; i < 5; i++) {
+		if ((inb(0x22c) & 0x80) == 0)
+			break;
+		mdelay(1);
+	}
+
+	if (i == 5)
+		printk("SoundBlaster: DSP not ready\n");
+	else {
+		outb(0xe1, 0x22c);
+
+		dprintk("SoundBlaster DSP id: ");
+		i = inb(0x22a);
+		udelay(1);
+		i |= inb(0x22a) << 8;
+		dprintk("%04X\n", i);
+
+		for (i = 0; i < 5; i++) {
+			if ((inb(0x22c) & 0x80) == 0)
+				break;
+			mdelay(1);
+		}
+
+		if (i == 5)
+			printk("SoundBlaster: could not turn speaker off\n");
+
+		outb(0xd3, 0x22c);
+	}
+
+	/* turn on OPL3 */
+	outb(5, 0x38a);
+	outb(1, 0x38b);
+}
+
+__initfunc(static void rwa010_init(void))
+{
+	rwa010_unlock();
+	rwa010_read_ident();
+	rwa010_global_init();
+	rwa010_game_port_init();
+	rwa010_waveartist_init(0x250, 3, 7);
+	rwa010_soundblaster_init(0x220, 0x388, 3, 1);
+	rwa010_soundblaster_reset();
+}
+
+EXPORT_SYMBOL(gpio_lock);
+EXPORT_SYMBOL(gpio_modify_op);
+EXPORT_SYMBOL(gpio_modify_io);
+EXPORT_SYMBOL(cpld_modify);
+
+#endif
+
+#ifdef CONFIG_LEDS
+#define DEFAULT_LEDS	0
+#else
+#define DEFAULT_LEDS	GPIO_GREEN_LED
+#endif
+
+/*
+ * CATS stuff
+ */
+#ifdef CONFIG_CATS
+
+#define CONFIG_PORT	0x370
+#define INDEX_PORT	(CONFIG_PORT)
+#define DATA_PORT	(CONFIG_PORT + 1)
+
+static void __init cats_hw_init(void)
+{
+	/* Set Aladdin to CONFIGURE mode */
+	outb(0x51, CONFIG_PORT);
+	outb(0x23, CONFIG_PORT);
+
+	/* Select logical device 3 */
+	outb(0x07, INDEX_PORT);
+	outb(0x03, DATA_PORT);
+
+	/* Set parallel port to DMA channel 3, ECP+EPP1.9, 
+	   enable EPP timeout */
+	outb(0x74, INDEX_PORT);
+	outb(0x03, DATA_PORT);
+	
+	outb(0xf0, INDEX_PORT);
+	outb(0x0f, DATA_PORT);
+
+	outb(0xf1, INDEX_PORT);
+	outb(0x07, DATA_PORT);
+
+	/* Select logical device 4 */
+	outb(0x07, INDEX_PORT);
+	outb(0x04, DATA_PORT);
+
+	/* UART1 high speed mode */
+	outb(0xf0, INDEX_PORT);
+	outb(0x02, DATA_PORT);
+
+	/* Select logical device 5 */
+	outb(0x07, INDEX_PORT);
+	outb(0x05, DATA_PORT);
+
+	/* UART2 high speed mode */
+	outb(0xf0, INDEX_PORT);
+	outb(0x02, DATA_PORT);
+
+	/* Set Aladdin to RUN mode */
+	outb(0xbb, CONFIG_PORT);
+}
+
+#endif
+
+__initfunc(void hw_init(void))
+{
+	extern void register_isa_ports(unsigned int, unsigned int, 
+				       unsigned int);
+	register_isa_ports(DC21285_PCI_MEM, DC21285_PCI_IO, 0);
+#ifdef CONFIG_ARCH_NETWINDER
+	/*
+	 * this ought to have a better home...
+	 * Since this calls the above routines, which are
+	 * compiled only if CONFIG_ARCH_NETWINDER is set,
+	 * these should only be parsed by the compiler
+	 * in the same circumstance.
+	 */
+	if (machine_is_netwinder()) {
+		unsigned long flags;
+
+		wb977_init();
+		cpld_init();
+		rwa010_init();
+
+		spin_lock_irqsave(&gpio_lock, flags);
+		gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+#endif
+
+	if (machine_is_cats())
+		cats_hw_init();
+
+	leds_event(led_start);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/iic.c linux.18p17/arch/arm/kernel/iic.c
--- linux.vanilla/arch/arm/kernel/iic.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/iic.c	Mon Oct  2 10:11:32 2000
@@ -7,20 +7,24 @@
  */
 
 #include <linux/delay.h>
+#include <linux/errno.h>
 
 #include <asm/system.h>
-#include <asm/io.h>
 #include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/ioc.h>
+
+#define FORCE_ONES	0xdc
 
 /*
  * if delay loop has been calibrated then us that,
  * else use IOC timer 1.
  */
-static void iic_delay (void)
+static void iic_delay(void)
 {
-	extern unsigned long loops_per_sec;
-	if (loops_per_sec != (1 << 12)) {
-		udelay(10);
+	extern unsigned long loops_per_jiffy;
+	if (loops_per_jiffy != (1 << 12)) {
+		udelay(100); /* was 10 */
 		return;
 	} else {
 		unsigned long flags;
@@ -30,7 +34,7 @@
 		outb(255,  IOC_T1LTCHH);
 		outb(0,    IOC_T1GO);
 		outb(1<<6, IOC_IRQCLRA);			/* clear T1 irq */
-		outb(4,    IOC_T1LTCHL);
+		outb(10,   IOC_T1LTCHL); /* was 4 */
 		outb(0,    IOC_T1LTCHH);
 		outb(0,    IOC_T1GO);
 		while ((inb(IOC_IRQSTATA) & (1<<6)) == 0);
@@ -38,124 +42,207 @@
 	}
 }
 
-static inline void iic_start (void)
+#define IIC_INIT()		dat = (inb(IOC_CONTROL) | FORCE_ONES) & ~3
+#define IIC_SET_DAT		outb(dat|=1, IOC_CONTROL);
+#define IIC_CLR_DAT		outb(dat&=~1, IOC_CONTROL);
+#define IIC_SET_CLK		outb(dat|=2, IOC_CONTROL);
+#define IIC_CLR_CLK		outb(dat&=~2, IOC_CONTROL);
+#define IIC_DELAY		iic_delay();
+#define IIC_READ_DATA()		(inb(IOC_CONTROL) & 1)
+
+static inline void iic_set_lines(int clk, int dat)
 {
-	unsigned char out;
+	int old;
 
-	out = inb(IOC_CONTROL) | 0xc2;
+	old = inb(IOC_CONTROL) | FORCE_ONES;
 
-	outb(out, IOC_CONTROL);
-	iic_delay();
+	old &= ~3;
+
+	if (clk)
+		old |= 2;
+	if (dat)
+		old |= 1;
+
+	outb(old, IOC_CONTROL);
 
-	outb(out ^ 1, IOC_CONTROL);
 	iic_delay();
 }
 
-static inline void iic_stop (void)
+static inline unsigned int iic_read_data(void)
 {
-	unsigned char out;
+	return inb(IOC_CONTROL) & 1;
+}
 
-	out = inb(IOC_CONTROL) | 0xc3;
+/*
+ * C: ==~~_
+ * D: =~~__
+ */
+static inline void iic_start(void)
+{
+	unsigned int dat;
 
-	iic_delay();
-	outb(out ^ 1, IOC_CONTROL);
+	IIC_INIT();
 
-	iic_delay();
-	outb(out, IOC_CONTROL);
+	IIC_SET_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_CLR_CLK
+	IIC_DELAY
 }
 
-static int iic_sendbyte (unsigned char b)
+/*
+ * C: __~~
+ * D: =__~
+ */
+static inline void iic_stop(void)
 {
-	unsigned char out, in;
-	int i;
+	unsigned int dat;
 
-	out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+	IIC_INIT();
 
-	outb(out, IOC_CONTROL);
-	for (i = 7; i >= 0; i--) {
-		unsigned char c;
-		c = out | ((b & (1 << i)) ? 1 : 0);
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+	IIC_SET_DAT
+	IIC_DELAY
+}
 
-		outb(c, IOC_CONTROL);
-		iic_delay();
+/*
+ * C: __~_
+ * D: =___
+ */
+static inline void iic_acknowledge(void)
+{
+	unsigned int dat;
 
-		outb(c | 2, IOC_CONTROL);
-		iic_delay();
+	IIC_INIT();
 
-		outb(c, IOC_CONTROL);
-	}
-	outb(out | 1, IOC_CONTROL);
-	iic_delay();
+	IIC_CLR_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
+	IIC_CLR_CLK
+	IIC_DELAY
+}
 
-	outb(out | 3, IOC_CONTROL);
-	iic_delay();
+/*
+ * C: __~_
+ * D: =~H~
+ */
+static inline int iic_is_acknowledged(void)
+{
+	unsigned int dat, ack_bit;
 
-	in = inb(IOC_CONTROL) & 1;
+	IIC_INIT();
 
-	outb(out | 1, IOC_CONTROL);
-	iic_delay();
+	IIC_SET_DAT
+	IIC_DELAY
+	IIC_SET_CLK
+	IIC_DELAY
 
-	outb(out, IOC_CONTROL);
-	iic_delay();
+	ack_bit = IIC_READ_DATA();
 
-	if(in) {
-		printk("No acknowledge from RTC\n");
-		return 1;
-	} else
-		return 0;
+	IIC_CLR_CLK
+	IIC_DELAY
+
+	return ack_bit == 0;
+}
+
+/*
+ * C: _~__~__~__~__~__~__~__~_
+ * D: =DDXDDXDDXDDXDDXDDXDDXDD
+ */
+static void iic_sendbyte(unsigned int b)
+{
+	unsigned int dat, i;
+
+	IIC_INIT();
+
+	for (i = 0; i < 8; i++) {
+		if (b & 128)
+			IIC_SET_DAT
+		else
+			IIC_CLR_DAT
+		IIC_DELAY
+
+		IIC_SET_CLK
+		IIC_DELAY
+		IIC_CLR_CLK
+		IIC_DELAY
+
+		b <<= 1;
+	}
 }
 
-static unsigned char iic_recvbyte (void)
+/*
+ * C: __~_~_~_~_~_~_~_~_
+ * D: =~HHHHHHHHHHHHHHHH
+ */
+static unsigned char iic_recvbyte(void)
 {
-	unsigned char out, in;
-	int i;
+	unsigned int dat, i, in;
+
+	IIC_INIT();
 
-	out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+	IIC_SET_DAT
+	IIC_DELAY
 
-	outb(out, IOC_CONTROL);
 	in = 0;
-	for (i = 7; i >= 0; i--) {
-		outb(out | 1, IOC_CONTROL);
-		iic_delay();
-		outb(out | 3, IOC_CONTROL);
-		iic_delay();
-		in = (in << 1) | (inb(IOC_CONTROL) & 1);
-		outb(out | 1, IOC_CONTROL);
-		iic_delay();
+	for (i = 0; i < 8; i++) {
+		IIC_SET_CLK
+		IIC_DELAY
+
+		in = (in << 1) | IIC_READ_DATA();
+
+		IIC_CLR_CLK
+		IIC_DELAY
 	}
-	outb(out, IOC_CONTROL);
-	iic_delay();
-	outb(out | 2, IOC_CONTROL);
-	iic_delay();
 
 	return in;
 }
 
-void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
+int iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
 {
-	iic_start();
+	int i, err = -EIO;
 
-	if (iic_sendbyte(addr & 0xfe))
+	iic_start();
+	iic_sendbyte(addr & 0xfe);
+	if (!iic_is_acknowledged())
 		goto error;
 
-	if (iic_sendbyte(loc))
+	iic_sendbyte(loc);
+	if (!iic_is_acknowledged())
 		goto error;
 
 	if (addr & 1) {
-		int i;
-
-		for (i = 0; i < len; i++)
-			if (iic_sendbyte (buf[i]))
-				break;
-	} else {
-		int i;
-
 		iic_stop();
 		iic_start();
 		iic_sendbyte(addr|1);
-		for (i = 0; i < len; i++)
-			buf[i] = iic_recvbyte ();
+		if (!iic_is_acknowledged())
+			goto error;
+
+		for (i = 0; i < len - 1; i++) {
+			buf[i] = iic_recvbyte();
+			iic_acknowledge();
+		}
+		buf[i] = iic_recvbyte();
+	} else {
+		for (i = 0; i < len; i++) {
+			iic_sendbyte(buf[i]);
+
+			if (!iic_is_acknowledged())
+				goto error;
+		}
 	}
+
+	err = 0;
 error:
 	iic_stop();
+
+	return err;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/ioport.c linux.18p17/arch/arm/kernel/ioport.c
--- linux.vanilla/arch/arm/kernel/ioport.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/ioport.c	Fri Sep 15 23:28:37 2000
@@ -1,29 +1,36 @@
 /*
  * linux/arch/arm/kernel/ioport.c
  *
- * Io-port support is not used for ARM
+ * IO permission support for ARM.
  */
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/ioport.h>
+#include <linux/mm.h>
 
-/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
-/*asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
-{
-}*/
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
 
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
+#ifdef CONFIG_CPU_32
+asmlinkage int sys_iopl(unsigned long turn_on)
 {
-	return -ENOSYS;
-}
+	if (turn_on && !capable(CAP_SYS_RAWIO))
+		return -EPERM;
 
-asmlinkage int sys_iopl(long ebx,long ecx,long edx,
-	     long esi, long edi, long ebp, long eax, long ds,
-	     long es, long fs, long gs, long orig_eax,
-	     long eip,long cs,long eflags,long esp,long ss)
+	/*
+	 * We only support an on_off approach
+	 */
+	modify_domain(DOMAIN_IO, turn_on ? DOMAIN_MANAGER : DOMAIN_CLIENT);
+
+	return 0;
+}
+#else
+asmlinkage int sys_iopl(unsigned long turn_on)
 {
 	return -ENOSYS;
 }
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/irq.c linux.18p17/arch/arm/kernel/irq.c
--- linux.vanilla/arch/arm/kernel/irq.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/irq.c	Fri Sep 15 23:28:37 2000
@@ -23,7 +23,6 @@
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
-#include <linux/timex.h>
 #include <linux/malloc.h>
 #include <linux/random.h>
 #include <linux/smp.h>
@@ -32,7 +31,6 @@
 
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 
 #ifndef SMP
@@ -46,10 +44,22 @@
 #define cliIF()
 #endif
 
+/*
+ * Maximum IRQ count.  Currently, this is arbitary.
+ * However, it should not be set too low to prevent
+ * false triggering.  Conversely, if it is set too
+ * high, then you could miss a stuck IRQ.
+ *
+ * Maybe we ought to set a timer and re-enable the
+ * IRQ at a later time?
+ */
+#define MAX_IRQ_CNT	100000
+
 unsigned int local_bh_count[NR_CPUS];
 unsigned int local_irq_count[NR_CPUS];
 spinlock_t irq_controller_lock;
 
+int setup_arm_irq(int, struct irqaction *);
 extern int get_fiq_list(char *);
 extern void init_FIQ(void);
 
@@ -60,17 +70,29 @@
 	unsigned int	 probing  : 1;		/* IRQ in use for a probe     */
 	unsigned int	 probe_ok : 1;		/* IRQ can be used for probe  */
 	unsigned int	 valid    : 1;		/* IRQ claimable	      */
-	unsigned int	 unused   :26;
+	unsigned int	 noautoenable : 1;	/* don't automatically enable IRQ */
+	unsigned int	 unused   :25;
 	void (*mask_ack)(unsigned int irq);	/* Mask and acknowledge IRQ   */
 	void (*mask)(unsigned int irq);		/* Mask IRQ		      */
 	void (*unmask)(unsigned int irq);	/* Unmask IRQ		      */
 	struct irqaction *action;
-	unsigned int	 unused2[3];
+	/*
+	 * IRQ lock detection
+	 */
+	unsigned int	 lck_cnt;
+	unsigned int	 lck_pc;
+	unsigned int	 lck_jif;
 };
 
 static struct irqdesc irq_desc[NR_IRQS];
 
 /*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
+/*
  * Dummy mask/unmask handler
  */
 static void dummy_mask_unmask_irq(unsigned int irq)
@@ -94,10 +116,12 @@
 
 	spin_lock_irqsave(&irq_controller_lock, flags);
 	cliIF();
-	irq_desc[irq].enabled = 1;
 	irq_desc[irq].probing = 0;
 	irq_desc[irq].triggered = 0;
-	irq_desc[irq].unmask(irq);
+	if (!irq_desc[irq].noautoenable) {
+		irq_desc[irq].enabled = 1;
+		irq_desc[irq].unmask(irq);
+	}
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
@@ -119,21 +143,52 @@
 		*p++ = '\n';
 	}
 
-#ifdef CONFIG_ACORN
+#ifdef CONFIG_ARCH_ACORN
 	p += get_fiq_list(p);
 #endif
 	return p - buf;
 }
 
 /*
+ * IRQ lock detection.
+ *
+ * Hopefully, this should get us out of a few locked situations.
+ * However, it may take a while for this to happen, since we need
+ * a large number if IRQs to appear in the same jiffie with the
+ * same instruction pointer (or within 2 instructions).
+ */
+static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs)
+{
+	unsigned long instr_ptr = instruction_pointer(regs);
+
+	if (desc->lck_jif == jiffies &&
+	    desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) {
+		desc->lck_cnt += 1;
+
+		if (desc->lck_cnt > MAX_IRQ_CNT) {
+			printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq);
+			disable_irq(irq);
+		}
+	} else {
+		desc->lck_cnt = 0;
+		desc->lck_pc  = instruction_pointer(regs);
+		desc->lck_jif = jiffies;
+	}
+}
+
+/*
  * do_IRQ handles all normal device IRQ's
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqdesc * desc = irq_desc + irq;
+	struct irqdesc * desc;
 	struct irqaction * action;
 	int status, cpu;
 
+	irq = fixup_irq(irq);
+
+	desc = irq_desc + irq;
+
 	spin_lock(&irq_controller_lock);
 	desc->mask_ack(irq);
 	spin_unlock(&irq_controller_lock);
@@ -174,6 +229,12 @@
 		}
 	}
 
+	/*
+	 * Debug measure - hopefully we can continue if an
+	 * IRQ lockup problem occurs...
+	 */
+	check_irq_lock(desc, irq, regs);
+
 	irq_exit(cpu, irq);
 
 	/*
@@ -181,15 +242,10 @@
 	 * a return code from the irq handler to tell us
 	 * whether the handler wants us to do software bottom
 	 * half handling or not..
-	 *
-	 * ** IMPORTANT NOTE: do_bottom_half() ENABLES IRQS!!! **
-	 * **  WE MUST DISABLE THEM AGAIN, ELSE IDE DISKS GO   **
-	 * **                       AWOL                       **
 	 */
 	if (1) {
 		if (bh_active & bh_mask)
 			do_bottom_half();
-		__cli();
 	}
 }
 
@@ -227,11 +283,27 @@
 	struct irqaction *old, **p;
 	unsigned long flags;
 
-	if (new->flags & SA_SAMPLE_RANDOM)
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
 	        rand_initialize_irq(irq);
+	}
 
+	/*
+	 * The following block of code has to be executed atomically
+	 */
 	spin_lock_irqsave(&irq_controller_lock, flags);
-
 	p = &irq_desc[irq].action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
@@ -252,28 +324,24 @@
 
 	if (!shared) {
 		irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
-		irq_desc[irq].enabled = 1;
 		irq_desc[irq].probing = 0;
-		irq_desc[irq].unmask(irq);
+		if (!irq_desc[irq].noautoenable) {
+			irq_desc[irq].enabled = 1;
+			irq_desc[irq].unmask(irq);
+		}
 	}
 
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 	return 0;
 }
 
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 		 unsigned long irq_flags, const char * devname, void *dev_id)
 {
 	unsigned long retval;
 	struct irqaction *action;
 
-	if (!irq_desc[irq].valid)
-		return -EINVAL;
-	if (!handler)
+	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler)
 		return -EINVAL;
 
 	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
@@ -299,28 +367,30 @@
 	struct irqaction * action, **p;
 	unsigned long flags;
 
-	if (!irq_desc[irq].valid) {
+	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
 		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 		__backtrace();
 #endif
 		return;
 	}
+
+	spin_lock_irqsave(&irq_controller_lock, flags);
 	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
 	    	/* Found it - now free it */
-		save_flags_cli (flags);
 		*p = action->next;
-		restore_flags (flags);
 		kfree(action);
-		return;
+		goto out;
 	}
 	printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 	__backtrace();
 #endif
+out:
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
 /* Start the interrupt probing.  Unlike other architectures,
@@ -346,7 +416,6 @@
 			continue;
 
 		irq_desc[i].probing = 1;
-		irq_desc[i].enabled = 1;
 		irq_desc[i].triggered = 0;
 		irq_desc[i].unmask(i);
 		irqs += 1;
@@ -364,7 +433,8 @@
 	 */
 	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
-		if (irq_desc[i].probing && irq_desc[i].triggered) {
+		if (irq_desc[i].probing &&
+		    irq_desc[i].triggered) {
 			irq_desc[i].probing = 0;
 			irqs -= 1;
 		}
@@ -383,7 +453,7 @@
 int probe_irq_off(unsigned long irqs)
 {
 	unsigned int i;
-	int irq_found = -1;
+	int irq_found = NO_IRQ;
 
 	/*
 	 * look at the interrupts, and find exactly one
@@ -393,7 +463,7 @@
 	for (i = 0; i < NR_IRQS; i++) {
 		if (irq_desc[i].probing &&
 		    irq_desc[i].triggered) {
-			if (irq_found != -1) {
+			if (irq_found != NO_IRQ) {
 				irq_found = NO_IRQ;
 				goto out;
 			}
@@ -405,21 +475,19 @@
 		irq_found = NO_IRQ;
 out:
 	spin_unlock_irq(&irq_controller_lock);
+
 	return irq_found;
 }
 
-/*
- * Get architecture specific interrupt handlers
- * and interrupt initialisation.
- */
-#include <asm/arch/irq.h>
-
-unsigned long __init init_IRQ(unsigned long memory)
+unsigned long  __init init_IRQ(unsigned long memory)
 {
 	extern void init_dma(void);
 	int irq;
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
+		irq_desc[irq].probe_ok = 0;
+		irq_desc[irq].valid    = 0;
+		irq_desc[irq].noautoenable = 0;
 		irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
 		irq_desc[irq].mask     = dummy_mask_unmask_irq;
 		irq_desc[irq].unmask   = dummy_mask_unmask_irq;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/isa.c linux.18p17/arch/arm/kernel/isa.c
--- linux.vanilla/arch/arm/kernel/isa.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/kernel/isa.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,47 @@
+/*
+ * arch/arm/kernel/isa.c
+ *
+ * ISA shared memory and I/O port support
+ *
+ * Copyright (C) 1999 Phil Blundell
+ */
+
+/* 
+ * Nothing about this is actually ARM specific.  One day we could move
+ * it into kernel/resource.c or some place like that.  
+ */
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+
+static unsigned int isa_membase, isa_portbase, isa_portshift;
+
+static ctl_table ctl_isa_vars[4] = {
+	{BUS_ISA_MEM_BASE, "membase", &isa_membase, 
+	 sizeof(isa_membase), 0444, NULL, &proc_dointvec},
+	{BUS_ISA_PORT_BASE, "portbase", &isa_portbase, 
+	 sizeof(isa_portbase), 0444, NULL, &proc_dointvec},
+	{BUS_ISA_PORT_SHIFT, "portshift", &isa_portshift, 
+	 sizeof(isa_portshift), 0444, NULL, &proc_dointvec},
+	{0}
+};
+
+static struct ctl_table_header *isa_sysctl_header;
+
+static ctl_table ctl_isa[2] = {{BUS_ISA, "isa", NULL, 0, 0555, ctl_isa_vars},
+			       {0}};
+static ctl_table ctl_bus[2] = {{CTL_BUS, "bus", NULL, 0, 0555, ctl_isa},
+			       {0}};
+
+void __init
+register_isa_ports(unsigned int membase, unsigned int portbase, unsigned int portshift)
+{
+	isa_membase = membase;
+	isa_portbase = portbase;
+	isa_portshift = portshift;
+	isa_sysctl_header = register_sysctl_table(ctl_bus, 0);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/leds-ebsa110.c linux.18p17/arch/arm/kernel/leds-ebsa110.c
--- linux.vanilla/arch/arm/kernel/leds-ebsa110.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/leds-ebsa110.c	Fri Sep 15 23:28:37 2000
@@ -7,11 +7,13 @@
  *
  *  - Red - toggles state every 50 timer interrupts
  */
+#include <linux/module.h>
+
 #include <asm/hardware.h>
 #include <asm/leds.h>
 #include <asm/system.h>
 
-void leds_event(led_event_t ledevt)
+void ebsa110_leds_event(led_event_t ledevt)
 {
 	unsigned long flags;
 
@@ -28,3 +30,7 @@
 
 	restore_flags(flags);
 }
+
+void (*leds_event)(led_event_t) = ebsa110_leds_event;
+
+EXPORT_SYMBOL(leds_event);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/leds-ebsa285.c linux.18p17/arch/arm/kernel/leds-ebsa285.c
--- linux.vanilla/arch/arm/kernel/leds-ebsa285.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/leds-ebsa285.c	Thu Jan  1 01:00:00 1970
@@ -1,44 +0,0 @@
-/*
- * arch/arm/kernel/leds-ebsa285.c
- *
- * Copyright (C) 1998 Russell King
- *
- * EBSA-285 LED control routines.  We use the leds as follows:
- *
- *  - Green - toggles state every 50 timer interrupts
- *  - Amber - On if system is not idle
- *  - Red   - currently unused
- */
-#include <asm/hardware.h>
-#include <asm/leds.h>
-#include <asm/system.h>
-
-static char led_state = XBUS_LED_RED | XBUS_LED_GREEN;
-
-void leds_event(led_event_t ledevt)
-{
-	unsigned long flags;
-
-	save_flags_cli(flags);
-
-	switch(ledevt) {
-	case led_idle_start:
-		led_state |= XBUS_LED_AMBER;
-		break;
-
-	case led_idle_end:
-		led_state &= ~XBUS_LED_AMBER;
-		break;
-
-	case led_timer:
-		led_state ^= XBUS_LED_GREEN;
-		break;
-
-	default:
-		break;
-	}
-
-	restore_flags(flags);
-
-	*XBUS_LEDS = led_state;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/leds-footbridge.c linux.18p17/arch/arm/kernel/leds-footbridge.c
--- linux.vanilla/arch/arm/kernel/leds-footbridge.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/kernel/leds-footbridge.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,245 @@
+/*
+ * arch/arm/kernel/leds-footbridge.c
+ *
+ * Copyright (C) 1998-1999 Russell King
+ *
+ * EBSA-285 and NetWinder LED control routines.
+ *
+ * The EBSA-285 uses the leds as follows:
+ *  - Green - toggles state every 50 timer interrupts
+ *  - Amber - On if system is not idle
+ *  - Red   - currently unused
+ *
+ * The Netwinder uses the leds as follows:
+ *  - Green - toggles state every 50 timer interrupts
+ *  - Red   - On if the system is not idle
+ *
+ * Changelog:
+ *   02-05-1999	RMK	Various cleanups
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/spinlock.h>
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+static char led_state;
+static char hw_led_state;
+
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+extern spinlock_t gpio_lock;
+
+#ifdef CONFIG_FOOTBRIDGE
+
+static void __ebsa285_text ebsa285_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	switch (evt) {
+	case led_start:
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN;
+#ifndef CONFIG_LEDS_IDLE
+		hw_led_state |= XBUS_LED_AMBER;
+#endif
+		led_state |= LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= XBUS_LED_GREEN;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= XBUS_LED_RED;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~XBUS_LED_RED;
+		break;
+#endif
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_GREEN;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_GREEN;
+		break;
+
+	case led_amber_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_AMBER;
+		break;
+
+	case led_amber_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_AMBER;
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~XBUS_LED_RED;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= XBUS_LED_RED;
+		break;
+
+	default:
+		break;
+	}
+
+	if  (led_state & LED_STATE_ENABLED)
+		*XBUS_LEDS = hw_led_state;
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+}
+
+#endif
+
+#ifdef CONFIG_ARCH_NETWINDER
+
+static void __netwinder_text netwinder_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	switch (evt) {
+	case led_start:
+		led_state |= LED_STATE_ENABLED;
+		hw_led_state = GPIO_GREEN_LED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = 0;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = 0;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= GPIO_GREEN_LED;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~GPIO_RED_LED;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= GPIO_RED_LED;
+		break;
+#endif
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_GREEN_LED;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~GPIO_GREEN_LED;
+		break;
+
+	case led_amber_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_GREEN_LED | GPIO_RED_LED;
+		break;
+
+	case led_amber_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~(GPIO_GREEN_LED | GPIO_RED_LED);
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= GPIO_RED_LED;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~GPIO_RED_LED;
+		break;
+
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+
+	if  (led_state & LED_STATE_ENABLED) {
+		spin_lock_irqsave(&gpio_lock, flags);
+		gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+}
+
+#endif
+
+static void dummy_leds_event(led_event_t evt)
+{
+}
+
+static void __init
+init_leds_event(led_event_t evt)
+{
+	leds_event = dummy_leds_event;
+
+#ifdef CONFIG_FOOTBRIDGE
+	if (machine_is_ebsa285() || machine_is_co285())
+		leds_event = ebsa285_leds_event;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder())
+		leds_event = netwinder_leds_event;
+#endif
+
+	leds_event(evt);
+}
+
+void (*leds_event)(led_event_t) = init_leds_event;
+
+EXPORT_SYMBOL(leds_event);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/oldlatches.c linux.18p17/arch/arm/kernel/oldlatches.c
--- linux.vanilla/arch/arm/kernel/oldlatches.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/oldlatches.c	Fri Sep 15 23:28:37 2000
@@ -4,6 +4,7 @@
  * (c) David Alan Gilbert 1995/1996
  */
 #include <linux/kernel.h>
+#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/hardware.h>
@@ -40,7 +41,7 @@
 }
 #endif
 
-void oldlatch_init(void)
+void __init oldlatch_init(void)
 {
     printk("oldlatch: init\n");
 #ifdef LATCHAADDR
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/process.c linux.18p17/arch/arm/kernel/process.c
--- linux.vanilla/arch/arm/kernel/process.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/process.c	Fri Sep 15 23:28:37 2000
@@ -32,11 +32,11 @@
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
 extern char *processor_modes[];
+extern void setup_mm_for_reboot(char mode);
 
 asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
@@ -53,46 +53,57 @@
 }
 
 /*
- * The idle loop on an arm..
+ * The idle loop on an ARM...
  */
 asmlinkage int sys_idle(void)
 {
-	int ret = -EPERM;
+	current->priority = 0;
+	current->counter = -100;
 
-	lock_kernel();
-	if (current->pid != 0)
-		goto out;
 	/* endless idle loop with no priority at all */
-	current->priority = -100;
-	for (;;)
-	{
+	while (1) {
+		if (!current->need_resched && !hlt_counter)
+			arch_do_idle();
+		schedule();
+#ifndef CONFIG_NO_PGT_CACHE
 		check_pgt_cache();
-#if 0 //def ARCH_IDLE_OK
-		if (!hlt_counter && !current->need_resched)
-			proc_idle ();
 #endif
-		run_task_queue(&tq_scheduler);
-		schedule();
 	}
-	ret = 0;
-out:
-	unlock_kernel();
-	return ret;
 }
 
+static char reboot_mode = 'h';
+
 __initfunc(void reboot_setup(char *str, int *ints))
 {
+	reboot_mode = str[0];
 }
 
-/*
- * This routine reboots the machine by resetting the expansion cards via
- * their loaders, turning off the processor cache (if ARM3), copying the
- * first instruction of the ROM to 0, and executing it there.
- */
 void machine_restart(char * __unused)
 {
-	proc_hard_reset ();
-	arch_hard_reset ();
+	/*
+	 * Clean and disable cache, turn off interrupts
+	 */
+	processor._proc_fin();
+
+	/*
+	 * Tell the mm system that we are going to reboot -
+	 * we may need to insert some 1:1 mappings so that
+	 * soft boot works.
+	 */
+	setup_mm_for_reboot(reboot_mode);
+
+	/*
+	 * Now call the architecture specific reboot code.
+	 */
+	arch_reset(reboot_mode);
+
+	/*
+	 * Whoops - the architecture was unable to reboot.
+	 * Tell the user!
+	 */
+	mdelay(1000);
+	panic("Reboot failed -- System halted\n");
+	while (1);
 }
 
 void machine_halt(void)
@@ -148,6 +159,67 @@
 }
 
 /*
+ * Task structure and kernel stack allocation.
+ *
+ * Taken from the i386 version.
+ */
+#ifdef CONFIG_CPU_32
+#define EXTRA_TASK_STRUCT	8
+static struct task_struct *task_struct_stack[EXTRA_TASK_STRUCT];
+static int task_struct_stack_ptr = -1;
+#endif
+
+struct task_struct *alloc_task_struct(void)
+{
+	struct task_struct *tsk;
+
+#ifndef EXTRA_TASK_STRUCT
+	tsk = ll_alloc_task_struct();
+#else
+	int index;
+
+	index = task_struct_stack_ptr;
+	if (index >= EXTRA_TASK_STRUCT/2)
+		goto use_cache;
+
+	tsk = ll_alloc_task_struct();
+
+	if (!tsk) {
+		index = task_struct_stack_ptr;
+
+		if (index >= 0) {
+use_cache:		tsk = task_struct_stack[index];
+			task_struct_stack_ptr = index - 1;
+		}
+	}
+#endif
+#ifdef CONFIG_SYSRQ
+	/* You need this if you want SYSRQ-T to give sensible stack
+	 * usage information
+	 */
+	if (tsk) {
+		char *p = (char *)tsk;
+		memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
+	}
+#endif
+
+	return tsk;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+#ifdef EXTRA_TASK_STRUCT
+	int index = task_struct_stack_ptr + 1;
+
+	if (index < EXTRA_TASK_STRUCT) {
+		task_struct_stack[index] = p;
+		task_struct_stack_ptr = index;
+	} else
+#endif
+		ll_free_task_struct(p);
+}
+
+/*
  * Free current thread data structures etc..
  */
 void exit_thread(void)
@@ -177,9 +249,10 @@
 	childregs = ((struct pt_regs *)((unsigned long)p + 8192)) - 1;
 	*childregs = *regs;
 	childregs->ARM_r0 = 0;
+	childregs->ARM_sp = esp;
 
 	save = ((struct context_save_struct *)(childregs)) - 1;
-	copy_thread_css(save);
+	init_thread_css(save);
 	p->tss.save = save;
 
 	return 0;
@@ -222,3 +295,29 @@
 	dump->regs = *regs;
 	dump->u_fpvalid = dump_fpu (regs, &dump->u_fp);
 }
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+	extern int sys_exit(int) __attribute__((noreturn));
+	pid_t __ret;
+
+	__asm__ __volatile__(
+	"mov	r0, %1		@ kernel_thread sys_clone\n"
+"	mov	r1, #0\n"
+	__syscall(clone)"\n"
+"	mov	%0, r0"
+        : "=r" (__ret)
+        : "Ir" (flags | CLONE_VM) : "r0", "r1");
+	if (__ret == 0)
+		sys_exit((fn)(arg));
+	return __ret;
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/ptrace.c linux.18p17/arch/arm/kernel/ptrace.c
--- linux.vanilla/arch/arm/kernel/ptrace.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/ptrace.c	Fri Sep 15 23:28:37 2000
@@ -25,6 +25,17 @@
  * Breakpoint SWI instruction: SWI &9F0001
  */
 #define BREAKINST	0xef9f0001
+#define PTRACE_GETREGS		12
+#define PTRACE_SETREGS		13
+#define PTRACE_GETFPREGS	14
+#define PTRACE_SETFPREGS	15
+
+static inline struct pt_regs *
+get_user_regs(struct task_struct *task)
+{
+	return (struct pt_regs *)
+		((unsigned long)task + 8192 - sizeof(struct pt_regs));
+}
 
 /*
  * this routine will get a word off of the processes privileged stack.
@@ -34,11 +45,7 @@
  */
 static inline long get_stack_long(struct task_struct *task, int offset)
 {
-	unsigned char *stack;
-
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	return *(unsigned long *)stack;
+	return get_user_regs(task)->uregs[offset];
 }
 
 /*
@@ -47,15 +54,21 @@
  * this routine assumes that all the privileged stacks are in our
  * data space.
  */
-static inline long put_stack_long(struct task_struct *task, int offset,
-	unsigned long data)
+static inline int
+put_stack_long(struct task_struct *task, int offset, long data)
 {
-	unsigned char *stack;
+	struct pt_regs newregs, *regs = get_user_regs(task);
+	int ret = -EINVAL;
 
-	stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
-	stack += offset << 2;
-	*(unsigned long *) stack = data;
-	return 0;
+	newregs = *regs;
+	newregs.uregs[offset] = data;
+
+	if (valid_user_regs(&newregs)) {
+		regs->uregs[offset] = data;
+		ret = 0;
+	}
+
+	return ret;
 }
 
 /*
@@ -71,39 +84,42 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
-	if (pgd_none(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (pgd_none(*pgdir))
+		goto none;
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 		pgd_clear(pgdir);
 		return 0;
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
-	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (pmd_none(*pgmiddle))
+		goto none;
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
 		pmd_clear(pgmiddle);
 		return 0;
 	}
 	pgtable = pte_offset(pgmiddle, addr);
-	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 0);
-		goto repeat;
-	}
+	if (!pte_present(*pgtable))
+		goto none;
 	page = pte_page(*pgtable);
  
 	if(MAP_NR(page) >= max_mapnr)
 		return 0;
 	page += addr & ~PAGE_MASK;
 	return *(unsigned long *)page;
+
+none:
+	fault = handle_mm_fault(tsk, vma, addr, 0);
+	if (fault > 0)
+		goto repeat;
+	if (fault < 0)
+		force_sig(SIGKILL, tsk);
+	return 0;
 }
 
 /*
@@ -122,46 +138,52 @@
 	pmd_t *pgmiddle;
 	pte_t *pgtable;
 	unsigned long page;
+	int fault;
 
 repeat:
 	pgdir = pgd_offset(vma->vm_mm, addr);
-	if (!pgd_present(*pgdir)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pgd_present(*pgdir))
+		goto none;
 	if (pgd_bad(*pgdir)) {
 		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
 		pgd_clear(pgdir);
 		return;
 	}
 	pgmiddle = pmd_offset(pgdir, addr);
-	if (pmd_none(*pgmiddle)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (pmd_none(*pgmiddle))
+		goto none;
 	if (pmd_bad(*pgmiddle)) {
 		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
 		pmd_clear(pgmiddle);
 		return;
 	}
 	pgtable = pte_offset(pgmiddle, addr);
-	if (!pte_present(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pte_present(*pgtable))
+		goto none;
 	page = pte_page(*pgtable);
-	if (!pte_write(*pgtable)) {
-		handle_mm_fault(tsk, vma, addr, 1);
-		goto repeat;
-	}
+	if (!pte_write(*pgtable))
+		goto none;
 	
 	if (MAP_NR(page) < max_mapnr) {
 		page += addr & ~PAGE_MASK;
+
+		flush_cache_range(vma->vm_mm, addr, addr + sizeof(unsigned long));
+
 		*(unsigned long *)page = data;
-		__flush_entry_to_ram(page);
+
+		clean_cache_area(page, sizeof(unsigned long));
+
+		set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+		flush_tlb_page(vma, addr & PAGE_MASK);
 	}
-	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
-	flush_tlb();
+	return;
+
+none:
+	fault = handle_mm_fault(tsk, vma, addr, 1);
+	if (fault > 0)
+		goto repeat;
+	if (fault < 0)
+		force_sig(SIGKILL, tsk);
 }
 
 static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
@@ -669,7 +691,6 @@
 				return 0;
 			wake_up_process (child);
 			child->exit_code = SIGKILL;
-			ptrace_cancel_bpt (child);
 			/* make sure single-step breakpoint is gone. */
 			ptrace_cancel_bpt (child);
 			ret = 0;
@@ -685,6 +706,54 @@
 			child->exit_code = data;
 			/* give it a chance to run. */
 			ret = 0;
+			goto out;
+			
+		case PTRACE_GETREGS:
+		{	/* Get all gp regs from the child. */
+			unsigned char *stack;
+
+			ret = 0;
+			stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+			if (copy_to_user((void *)data, stack,
+					 sizeof(struct pt_regs)))
+				ret = -EFAULT;
+
+			goto out;
+		};
+
+		/* Set all gp regs in the child. */
+		case PTRACE_SETREGS:
+		{
+			struct pt_regs newregs;
+
+			ret = -EFAULT;
+			if (copy_from_user(&newregs, (void *)data,
+					   sizeof(struct pt_regs)) == 0) {
+				struct pt_regs *regs = get_user_regs(child);
+
+				ret = -EINVAL;
+				if (valid_user_regs(&newregs)) {
+					*regs = newregs;
+					ret = 0;
+				}
+			}
+			goto out;
+		}
+
+		case PTRACE_GETFPREGS: 
+			/* Get the child FPU state. */
+			ret = 0;
+			if (copy_to_user((void *)data, &child->tss.fpstate,
+					 sizeof(struct user_fp)))
+				ret = -EFAULT;
+			goto out;
+		
+		case PTRACE_SETFPREGS:
+			/* Set the child FPU state. */
+			ret = 0;
+			if (copy_from_user(&child->tss.fpstate, (void *)data,
+					   sizeof(struct user_fp)))
+				ret = -EFAULT;
 			goto out;
 
 		case PTRACE_DETACH:				/* detach a process that was attached. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/setup.c linux.18p17/arch/arm/kernel/setup.c
--- linux.vanilla/arch/arm/kernel/setup.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/setup.c	Mon Oct  2 10:11:32 2000
@@ -1,68 +1,73 @@
 /*
  *  linux/arch/arm/kernel/setup.c
  *
- *  Copyright (C) 1995-1998 Russell King
+ *  Copyright (C) 1995-1999 Russell King
  */
-
-/*
- * This file obtains various parameters about the system that the kernel
- * is running on.
- */
-
 #include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/malloc.h>
-#include <linux/user.h>
-#include <linux/a.out.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
-#include <linux/major.h>
 #include <linux/utsname.h>
 #include <linux/blk.h>
-#include <linux/init.h>
 #include <linux/console.h>
+#include <linux/init.h>
 
 #include <asm/elf.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/pgtable.h>
 #include <asm/procinfo.h>
-#include <asm/segment.h>
 #include <asm/setup.h>
 #include <asm/system.h>
 
-/* Work out which CPUs to support */
-#ifdef CONFIG_ARCH_ACORN
-#define SUPPORT_CPU_ARM6
-#define SUPPORT_CPU_ARM7
-#define SUPPORT_CPU_SA110
-#else
-#define SUPPORT_CPU_SA110
-#endif
-#ifdef CONFIG_CPU_ARM6
-#define SUPPORT_CPU_ARM6
-#endif
-#ifdef CONFIG_CPU_ARM7
-#define SUPPORT_CPU_ARM7
-#endif
-#ifdef CONFIG_CPU_SA110
-#define SUPPORT_CPU_SA110
+#ifndef MEM_SIZE
+#define MEM_SIZE	(16*1024*1024)
 #endif
 
 #ifndef CONFIG_CMDLINE
-#define CONFIG_CMDLINE	"root=/dev/nfs rw"
+#define CONFIG_CMDLINE ""
 #endif
-#define MEM_SIZE	(16*1024*1024)
-#define COMMAND_LINE_SIZE 256
+
+extern void reboot_setup(char *str, int *ints);
+extern void disable_hlt(void);
+extern int root_mountflags;
+extern int _stext, _text, _etext, _edata, _end;
+
+unsigned int processor_id;
+unsigned int __machine_arch_type;
+unsigned int vram_size;
+unsigned int system_rev;
+unsigned int system_serial_low;
+unsigned int system_serial_high;
+unsigned int elf_hwcap;
+
+#ifdef CONFIG_ARCH_ACORN
+unsigned int memc_ctrl_reg;
+unsigned int number_mfm_drives;
+#endif
+
+static unsigned long mem_end;
+
+struct machine_desc {
+	const char	*name;		/* architecture name	*/
+	unsigned int	param_offset;	/* parameter page	*/
+	unsigned int	video_start;	/* start of video RAM	*/
+	unsigned int	video_end;	/* end of video RAM	*/
+	unsigned int	reserve_lp0 :1;	/* never has lp0	*/
+	unsigned int	reserve_lp1 :1;	/* never has lp1	*/
+	unsigned int	reserve_lp2 :1;	/* never has lp2	*/
+	unsigned int	broken_hlt  :1;	/* hlt is broken	*/
+	unsigned int	soft_reboot :1;	/* soft reboot		*/
+	void		(*fixup)(struct machine_desc *,
+				 struct param_struct *, char **);
+};
+
+struct processor processor;
 
 struct drive_info_struct { char dummy[32]; } drive_info;
+
 struct screen_info screen_info = {
  orig_video_lines:	30,
  orig_video_cols:	80,
@@ -71,312 +76,429 @@
  orig_video_isVGA:	1,
  orig_video_points:	8
 };
-struct processor processor;
+
 unsigned char aux_device_present;
+char elf_platform[ELF_PLATFORM_SIZE];
+char saved_command_line[COMMAND_LINE_SIZE];
 
-extern const struct processor arm2_processor_functions;
-extern const struct processor arm250_processor_functions;
-extern const struct processor arm3_processor_functions;
-extern const struct processor arm6_processor_functions;
-extern const struct processor arm7_processor_functions;
-extern const struct processor sa110_processor_functions;
+static struct proc_info_item proc_info;
+static const char *machine_name;
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
 
-char elf_platform[ELF_PLATFORM_SIZE];
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
+static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
+#define ENDIANNESS ((char)endian_test.l)
 
-const struct armversions armidlist[] = {
-  /*-- Match -- --- Mask -- -- Manu --  Processor  uname -m   --- ELF STUFF ---
-	--- processor asm funcs --- */
-#if defined(CONFIG_CPU_26)
-  { 0x41560200, 0xfffffff0, "ARM/VLSI",	"arm2"	 , "armv1"  , "v1", 0,
-	&arm2_processor_functions   },
-  { 0x41560250, 0xfffffff0, "ARM/VLSI",	"arm250" , "armv2"  , "v2", HWCAP_SWP,
-	&arm250_processor_functions },
-  { 0x41560300, 0xfffffff0, "ARM/VLSI",	"arm3"	 , "armv2"  , "v2", HWCAP_SWP,
-	&arm3_processor_functions   },
-#elif defined(CONFIG_CPU_32)
-#ifdef SUPPORT_CPU_ARM6
-  { 0x41560600, 0xfffffff0, "ARM/VLSI",	"arm6"	 , "armv3"  , "v3", HWCAP_SWP,
-	&arm6_processor_functions   },
-  { 0x41560610, 0xfffffff0, "ARM/VLSI",	"arm610" , "armv3"  , "v3", HWCAP_SWP,
-	&arm6_processor_functions   },
-#endif
-#ifdef SUPPORT_CPU_ARM7
-  { 0x41007000, 0xffffff00, "ARM/VLSI",	"arm7"	 , "armv3"  , "v3", HWCAP_SWP,
-	&arm7_processor_functions   },
-  /* ARM710 IDs are non-standard */
-  { 0x41007100, 0xfff8ff00, "ARM/VLSI",	"arm710" , "armv3"  , "v3", HWCAP_SWP,
-	&arm7_processor_functions   },
-#endif
-#ifdef SUPPORT_CPU_SA110
-  { 0x4401a100, 0xfffffff0, "DEC",	"sa110"	 , "armv4"  , "v3", HWCAP_SWP|HWCAP_HALF,
-	&sa110_processor_functions  },
-#endif
-#endif
-  { 0x00000000, 0x00000000, "***", "unknown", "unknown", "**", 0, NULL }
-};
+static void __init setup_processor(void)
+{
+	extern struct proc_info_list __proc_info_begin, __proc_info_end;
+	struct proc_info_list *list;
 
-/*
- * From head-armv.S
- */
-unsigned int processor_id;
-unsigned int machine_type;
-int armidindex;
+	/*
+	 * locate processor in the list of supported processor
+	 * types.  The linker builds this table for us from the
+	 * entries in arch/arm/mm/proc-*.S
+	 */
+	for (list = &__proc_info_begin; list < &__proc_info_end ; list++)
+		if ((processor_id & list->cpu_mask) == list->cpu_val)
+			break;
 
-extern int root_mountflags;
-extern int _etext, _edata, _end;
+	/*
+	 * If processor type is unrecognised, then we
+	 * can do nothing...
+	 */
+	if (list >= &__proc_info_end) {
+		printk("CPU configuration botched (ID %08x), unable "
+		       "to continue.\n", processor_id);
+		while (1);
+	}
 
-/*-------------------------------------------------------------------------
- * Early initialisation routines for various configurable items in the
- * kernel.  Each one either supplies a setup_ function, or defines this
- * symbol to be empty if not configured.
- */
+	proc_info = *list->info;
 
-/*
- * Risc-PC specific initialisation
- */
-#ifdef CONFIG_ARCH_RPC
+	processor = *list->proc;
 
-#include <asm/arch/mmu.h>
+	printk("Processor: %s %s revision %d\n",
+	       proc_info.manufacturer, proc_info.cpu_name,
+	       (int)processor_id & 15);
 
-unsigned int vram_half_sam;
+	sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS);
+	sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
+	elf_hwcap = list->elf_hwcap;
 
-static void
-setup_rpc(struct param_struct *params)
-{
-	extern void init_dram_banks(const struct param_struct *params);
+	processor._proc_init();
+}
 
-	init_dram_banks(params);
+static unsigned long __init memparse(char *ptr, char **retptr)
+{
+	unsigned long ret = simple_strtoul(ptr, retptr, 0);
 
-	switch (params->u1.s.pages_in_vram) {
-	case 256:
-		vram_half_sam = 1024;
-		break;
-	case 512:
+	switch (**retptr) {
+	case 'M':
+	case 'm':
+		ret <<= 10;
+	case 'K':
+	case 'k':
+		ret <<= 10;
+		(*retptr)++;
 	default:
-		vram_half_sam = 2048;
+		break;
 	}
+	return ret;
 }
-#else
-#define setup_rpc(x)
-#endif
-
-#ifdef PARAMS_BASE
-
-#ifdef CONFIG_ARCH_ACORN
-int memc_ctrl_reg;
-int number_ide_drives;
-int number_mfm_drives;
-#endif
 
-static struct param_struct *params = (struct param_struct *)PARAMS_BASE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
+/*
+ * Initial parsing of the command line.  We need to pick out the
+ * memory size.  We look for mem=size, where size is "size[KkMm]"
+ */
+static void __init
+parse_cmdline(char **cmdline_p, char *from)
 {
-	ROOT_DEV	  = to_kdev_t(params->u1.s.rootdev);
-	ORIG_X		  = params->u1.s.video_x;
-	ORIG_Y		  = params->u1.s.video_y;
-	ORIG_VIDEO_COLS	  = params->u1.s.video_num_cols;
-	ORIG_VIDEO_LINES  = params->u1.s.video_num_rows;
+	char c = ' ', *to = command_line;
+	int usermem = 0, len = 0;
 
-#ifdef CONFIG_ARCH_ACORN
-#ifndef CONFIG_FB
-	{
-		extern int bytes_per_char_h;
-		extern int bytes_per_char_v;
-
-		bytes_per_char_h  = params->u1.s.bytes_per_char_h;
-		bytes_per_char_v  = params->u1.s.bytes_per_char_v;
-	}
-#endif
-	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
-	number_ide_drives = (params->u1.s.adfsdrives >> 6) & 3;
-	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+	for (;;) {
+		if (c == ' ' && !memcmp(from, "mem=", 4)) {
+			unsigned long size, start;
 
-	setup_rpc(params);
+			if (to != command_line)
+				to -= 1;
 
-	if (!(params->u1.s.flags & FLAG_READONLY))
-		root_mountflags &= ~MS_RDONLY;
-#endif
-#ifdef CONFIG_BLK_DEV_RAM
-	{
-		extern int rd_doload;
-		extern int rd_prompt;
-		extern int rd_image_start;
-
-		rd_image_start = params->u1.s.rd_start;
-		rd_prompt = (params->u1.s.flags & FLAG_RDPROMPT) == 0;
-		rd_doload = (params->u1.s.flags & FLAG_RDLOAD) == 0;
+			start = PAGE_OFFSET;
+			size  = memparse(from + 4, &from);
+			if (*from == '@')
+				memparse(from + 1, &from);
+
+			if (usermem == 0) {
+				usermem = 1;
+				mem_end = start + size;
+			}
+		}
+		c = *from++;
+		if (!c)
+			break;
+		if (COMMAND_LINE_SIZE <= ++len)
+			break;
+		*to++ = c;
 	}
-#endif
-
-#ifdef CONFIG_ARCH_ACORN
-	*mem_end_p = GET_MEMORY_END(params);
-#elif defined(CONFIG_ARCH_EBSA285)
-	*mem_end_p = PAGE_OFFSET + params->u1.s.page_size * params->u1.s.nr_pages;
-#else
-	*mem_end_p = PAGE_OFFSET + MEM_SIZE;
-#endif
+	*to = '\0';
+	*cmdline_p = command_line;
 
-	return params->commandline;
+	/* remove trailing spaces */
+	while (*--to == ' ' && to != command_line)
+		*to = '\0';
 }
 
-#else
-
-static char default_command_line[] __initdata = CONFIG_CMDLINE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
+static void __init
+setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
 {
-	ROOT_DEV	  = 0x00ff;
-
 #ifdef CONFIG_BLK_DEV_RAM
-	{
-		extern int rd_doload;
-		extern int rd_prompt;
-		extern int rd_image_start;
-
-		rd_image_start = 0;
-		rd_prompt = 1;
-		rd_doload = 1;
-	}
-#endif
+	extern int rd_doload, rd_prompt, rd_image_start, rd_size;
 
-	*mem_end_p = PAGE_OFFSET + MEM_SIZE;
+	rd_image_start = image_start;
+	rd_prompt = prompt;
+	rd_doload = doload;
 
-	return default_command_line;
-}
+	if (rd_sz)
+		rd_size = rd_sz;
 #endif
+}
 
 /*
  * initial ram disk
  */
-#ifdef CONFIG_BLK_DEV_INITRD
-__initfunc(static void
-setup_initrd(const struct param_struct *params))
+static void __init setup_initrd(unsigned int start, unsigned int size)
 {
-	if (params->u1.s.initrd_start) {
-		initrd_start = params->u1.s.initrd_start;
-		initrd_end   = initrd_start + params->u1.s.initrd_size;
-	} else {
-		initrd_start = 0;
-		initrd_end   = 0;
-	}
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (start == 0)
+		size = 0;
+	initrd_start = start;
+	initrd_end   = start + size;
+#endif
 }
 
-__initfunc(static void
-check_initrd(unsigned long mem_start, unsigned long mem_end))
+/*
+ * Initialise memory.
+ */
+static void __init setup_bootmem(void)
 {
+	unsigned long mem_start;
+
+	mem_start = (unsigned long)&_end;
+
+#ifdef CONFIG_BLK_DEV_INITRD
 	if (initrd_end > mem_end) {
 		printk ("initrd extends beyond end of memory "
 			"(0x%08lx > 0x%08lx) - disabling initrd\n",
 			initrd_end, mem_end);
 		initrd_start = 0;
 	}
+#endif
 }
 
-#else
-#define setup_initrd(p)
-#define check_initrd(ms,me)
+/*
+ * Architecture specific fixups.  This is where any
+ * parameters in the params struct are fixed up, or
+ * any additional architecture specific information
+ * is pulled from the params struct.
+ */
+static void __init
+fixup_acorn(struct machine_desc *desc, struct param_struct *params,
+	    char **cmdline)
+{
+#ifdef CONFIG_ARCH_ACORN
+	if (machine_is_riscpc()) {
+		/*
+		 * RiscPC can't handle half-word loads and stores
+		 */
+		elf_hwcap &= ~HWCAP_HALF;
+
+		switch (params->u1.s.pages_in_vram) {
+		case 512:
+			vram_size += PAGE_SIZE * 256;
+		case 256:
+			vram_size += PAGE_SIZE * 256;
+		default:
+			break;
+		}
+	}
+	memc_ctrl_reg	  = params->u1.s.memc_control_reg;
+	number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
 #endif
+}
 
-__initfunc(void
-setup_processor(void))
+static void __init
+fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
+	      char **cmdline)
 {
-	armidindex = 0;
-
-	while ((armidlist[armidindex].id ^ processor_id) &
-	       armidlist[armidindex].mask)
-		armidindex += 1;
-
-	if (armidlist[armidindex].id == 0) {
-#ifdef CONFIG_ARCH_ACORN
-		int i;
+	ORIG_X		 = params->u1.s.video_x;
+	ORIG_Y		 = params->u1.s.video_y;
+	ORIG_VIDEO_COLS  = params->u1.s.video_num_cols;
+	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+}
 
-		for (i = 0; i < 3200; i++)
-			((unsigned long *)SCREEN2_BASE)[i] = 0x77113322;
-#endif
-		while (1);
+/*
+ * Older NeTTroms either do not provide a parameters
+ * page, or they don't supply correct information in
+ * the parameter page.
+ */
+static void __init
+fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
+		char **cmdline)
+{
+	if (params->u1.s.nr_pages != 0x02000 &&
+	    params->u1.s.nr_pages != 0x04000 &&
+	    params->u1.s.nr_pages != 0x08000 &&
+	    params->u1.s.nr_pages != 0x10000) {
+		printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+		       "detected, using defaults\n");
+
+		params->u1.s.nr_pages = 0x2000;	/* 32MB */
+		params->u1.s.ramdisk_size = 0;
+		params->u1.s.flags = FLAG_READONLY;
+		params->u1.s.initrd_start = 0;
+		params->u1.s.initrd_size = 0;
+		params->u1.s.rd_start = 0;
 	}
+}
 
-	processor = *armidlist[armidindex].proc;
-	processor._proc_init();
+/*
+ * CATS uses soft-reboot by default, since
+ * hard reboots fail on early boards.
+ */
+static void __init
+fixup_cats(struct machine_desc *desc, struct param_struct *params,
+	   char **cmdline)
+{
+	ORIG_VIDEO_LINES  = 25;
+	ORIG_VIDEO_POINTS = 16;
+	ORIG_Y = 24;
 }
 
-static char command_line[COMMAND_LINE_SIZE] = { 0, };
-       char saved_command_line[COMMAND_LINE_SIZE];
+#define NO_PARAMS	0
+#define NO_VIDEO	0, 0
 
-__initfunc(static void
-setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end))
+/*
+ * This is the list of all architectures supported by
+ * this kernel.  This should be integrated with the list
+ * in head-armv.S.
+ */
+static struct machine_desc machine_desc[] __initdata = {
+	{ "EBSA110",		/* RMK			*/
+		0x00000400,
+		NO_VIDEO,
+		1, 0, 1, 1, 1,
+		NULL
+	}, { "Acorn-RiscPC",	/* RMK			*/
+		0x10000100,
+		NO_VIDEO,
+		1, 1, 0, 0, 0,
+		fixup_acorn
+	}, { "unknown",
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "FTV/PCI",		/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "EBSA285",		/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 0,
+		fixup_ebsa285
+	}, { "Rebel-NetWinder",	/* RMK			*/
+		0x00000100,
+		0x000a0000, 0x000bffff,
+		1, 0, 1, 0, 0,
+		fixup_netwinder
+	}, { "Chalice-CATS",	/* Philip Blundell	*/
+		NO_PARAMS,
+		0x000a0000, 0x000bffff,
+		0, 0, 0, 0, 1,
+		fixup_cats
+	}, { "unknown-TBOX",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "co-EBSA285",	/* Mark van Doesburg	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "CL-PS7110",	/* Werner Almesberger	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "Acorn-Archimedes",/* RMK/DAG		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, { "Acorn-A5000",	/* RMK/PB		*/
+		0x0207c000,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		fixup_acorn
+	}, { "Etoile",		/* Alex de Vries	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "LaCie_NAS",	/* Benjamin Herrenschmidt */
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "CL-PS7500",	/* Philip Blundell	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "Shark",		/* Alexander Schulz	*/
+		NO_PARAMS,
+		/* do you really mean 0x200000? */
+		0x06000000, 0x06000000+0x00200000,
+		0, 0, 0, 0, 0,
+		NULL
+	}, { "SA1100-based",	/* Nicolas Pitre	*/
+		NO_PARAMS,
+		NO_VIDEO,
+		0, 0, 0, 0, 0,
+		NULL
+	}
+};
+
+void __init
+setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)
 {
-	char c, *to = command_line;
-	int len = 0;
+	struct param_struct *params = NULL;
+	struct machine_desc *mdesc;
+	char *from = default_command_line;
 
-	*mem_start = (unsigned long)&_end;
+#if defined(CONFIG_ARCH_ARC)
+	__machine_arch_type = MACH_TYPE_ARCHIMEDES;
+#elif defined(CONFIG_ARCH_A5K)
+	__machine_arch_type = MACH_TYPE_A5K;
+#endif
 
-	for (;;) {
-		if (cmd_line[0] == ' ' &&
-		    cmd_line[1] == 'm' &&
-		    cmd_line[2] == 'e' &&
-		    cmd_line[3] == 'm' &&
-		    cmd_line[4] == '=') {
-			*mem_end = simple_strtoul(cmd_line+5, &cmd_line, 0);
-			switch(*cmd_line) {
-			case 'M':
-			case 'm':
-				*mem_end <<= 10;
-			case 'K':
-			case 'k':
-				*mem_end <<= 10;
-				cmd_line++;
-			}
-			*mem_end = *mem_end + PAGE_OFFSET;
-		}
-		c = *cmd_line++;
-		if (!c)
-			break;
-		if (COMMAND_LINE_SIZE <= ++len)
-			break;
-		*to++ = c;
+	setup_processor();
+
+	ROOT_DEV = MKDEV(0, 255);
+
+	mdesc = machine_desc + machine_arch_type;
+	machine_name = mdesc->name;
+
+	if (mdesc->broken_hlt)
+		disable_hlt();
+
+	if (mdesc->soft_reboot)
+		reboot_setup("s", NULL);
+
+	if (mdesc->param_offset)
+		params = phys_to_virt(mdesc->param_offset);
+
+	if (mdesc->fixup)
+		mdesc->fixup(mdesc, params, &from);
+
+	if (params && params->u1.s.page_size != PAGE_SIZE) {
+		printk(KERN_WARNING "Warning: bad configuration page, "
+		       "trying to continue\n");
+		params = NULL;
 	}
 
-	*to = '\0';
-}
+	if (params) {
+		mem_end	  = params->u1.s.page_size *
+			    params->u1.s.nr_pages;
+
+		ROOT_DEV	   = to_kdev_t(params->u1.s.rootdev);
+		system_rev	   = params->u1.s.system_rev;
+		system_serial_low  = params->u1.s.system_serial_low;
+		system_serial_high = params->u1.s.system_serial_high;
+
+		setup_ramdisk((params->u1.s.flags & FLAG_RDLOAD) == 0,
+			      (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+			      params->u1.s.rd_start,
+			      params->u1.s.ramdisk_size);
 
-__initfunc(void
-setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p))
-{
-	static unsigned char smptrap;
-	unsigned long memory_end;
-	char endian = 'l';
-	char *from;
-
-	if (smptrap == 1)
-		return;
-	smptrap = 1;
+		setup_initrd(params->u1.s.initrd_start,
+			     params->u1.s.initrd_size);
 
-	setup_processor();
+		if (!(params->u1.s.flags & FLAG_READONLY))
+			root_mountflags &= ~MS_RDONLY;
+
+#ifdef CONFIG_ARCH_RPC
+		{
+			extern void init_dram_banks(struct param_struct *);
+			init_dram_banks(params);
+		}
+#endif
+		mem_end -= vram_size;
 
-	from = setup_params(&memory_end);
-	setup_initrd(params);
+		from = params->commandline;
+	}
 
-	/* Save unparsed command line copy for /proc/cmdline */
-	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
-	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+	if (!mem_end)
+		mem_end = MEM_SIZE;
 
-	setup_mem(from, memory_start_p, &memory_end);
-	check_initrd(*memory_start_p, memory_end);
+	mem_end += PAGE_OFFSET;
 
-	init_task.mm->start_code = TASK_SIZE;
-	init_task.mm->end_code	 = TASK_SIZE + (unsigned long) &_etext;
-	init_task.mm->end_data	 = TASK_SIZE + (unsigned long) &_edata;
-	init_task.mm->brk	 = TASK_SIZE + (unsigned long) &_end;
+	init_task.mm->start_code = (unsigned long) &_text;
+	init_task.mm->end_code	 = (unsigned long) &_etext;
+	init_task.mm->end_data	 = (unsigned long) &_edata;
+	init_task.mm->brk	 = (unsigned long) &_end;
 
-	*cmdline_p = command_line;
-	*memory_end_p = memory_end;
+	memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
+	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+	parse_cmdline(cmdline_p, from);
+	setup_bootmem();
 
-	sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, endian);
-	sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, endian);
+#ifdef CONFIG_NWFPE
+	fpe_init();
+#endif
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
@@ -385,69 +507,32 @@
 	conswitchp = &dummy_con;
 #endif
 #endif
+
+	*cmdline_p = command_line;
+	*memory_start_p = (unsigned long)&_end;
+	*memory_end_p = mem_end;
 }
 
-static const struct {
-	char *machine_name;
-	char *bus_name;
-} machine_desc[] = {
-	{ "DEC-EBSA110",	"DEC"		},
-	{ "Acorn-RiscPC",	"Acorn"		},
-	{ "Nexus-NexusPCI",	"PCI"		},
-	{ "DEC-EBSA285",	"PCI"		},
-	{ "Corel-Netwinder",	"PCI/ISA"	},
-	{ "Chalice-CATS",	"PCI"		},
-	{ "unknown-TBOX",	"PCI"		}
-};
+int get_cpuinfo(char * buffer)
+{
+	char *p = buffer;
 
-#if defined(CONFIG_ARCH_ARC)
-#define HARDWARE "Acorn-Archimedes"
-#define IO_BUS	 "Acorn"
-#elif defined(CONFIG_ARCH_A5K)
-#define HARDWARE "Acorn-A5000"
-#define IO_BUS	 "Acorn"
-#endif
+	p += sprintf(p, "Processor\t: %s %s rev %d (%s)\n",
+		     proc_info.manufacturer, proc_info.cpu_name,
+		     (int)processor_id & 15, elf_platform);
 
-#if defined(CONFIG_CPU_ARM2)
-#define OPTIMISATION "ARM2"
-#elif defined(CONFIG_CPU_ARM3)
-#define OPTIMISATION "ARM3"
-#elif defined(CONFIG_CPU_ARM6)
-#define OPTIMISATION "ARM6"
-#elif defined(CONFIG_CPU_ARM7)
-#define OPTIMISATION "ARM7"
-#elif defined(CONFIG_CPU_SA110)
-#define OPTIMISATION "StrongARM"
-#else
-#define OPTIMISATION "unknown"
-#endif
+	p += sprintf(p, "BogoMIPS\t: %lu.%02lu\n",
+		     (loops_per_jiffy+2500) / (500000/HZ),
+		     ((loops_per_jiffy+2500) / (5000/HZ)) % 100);
 
-int get_cpuinfo(char * buffer)
-{
-	int len;
+	p += sprintf(p, "Hardware\t: %s\n", machine_name);
 
-	len = sprintf(buffer,
-		"Processor\t: %s %s rev %d\n"
-		"BogoMips\t: %lu.%02lu\n"
-		"Hardware\t: %s\n"
-		"Optimisation\t: %s\n"
-		"IO Bus\t\t: %s\n",
-		armidlist[armidindex].manu,
-		armidlist[armidindex].name,
-		(int)processor_id & 15,
-		(loops_per_sec+2500) / 500000,
-		((loops_per_sec+2500) / 5000) % 100,
-#ifdef HARDWARE
-		HARDWARE,
-#else
-		machine_desc[machine_type].machine_name,
-#endif
-		OPTIMISATION,
-#ifdef IO_BUS
-		IO_BUS
-#else
-		machine_desc[machine_type].bus_name
-#endif
-		);
-	return len;
+	p += sprintf(p, "Revision\t: %04x\n",
+		     system_rev);
+
+	p += sprintf(p, "Serial\t\t: %08x%08x\n",
+		     system_serial_high,
+		     system_serial_low);
+
+	return p - buffer;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/signal.c linux.18p17/arch/arm/kernel/signal.c
--- linux.vanilla/arch/arm/kernel/signal.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/signal.c	Fri Sep 15 23:28:37 2000
@@ -28,7 +28,7 @@
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr,
 			 int options, unsigned long *ru);
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
 extern int ptrace_cancel_bpt (struct task_struct *);
 extern int ptrace_set_bpt (struct task_struct *);
 
@@ -50,7 +50,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -78,7 +78,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs, 0))
 			return regs->ARM_r0;
 	}
 }
@@ -158,12 +158,8 @@
 #ifdef CONFIG_CPU_32
 	err |= __get_user(regs->ARM_cpsr, &sc->arm_cpsr);
 #endif
-	if (!valid_user_regs(regs))
-		return 1;
 
-	/* send SIGTRAP if we're single-stepping */
-	if (ptrace_cancel_bpt (current))
-		send_sig (SIGTRAP, current, 1);
+	err |= !valid_user_regs(regs);
 
 	return err;
 }
@@ -173,6 +169,14 @@
 	struct sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -192,6 +196,10 @@
 	if (restore_sigcontext(regs, &frame->sc))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -204,6 +212,14 @@
 	struct rt_sigframe *frame;
 	sigset_t set;
 
+	/*
+	 * Since we stacked the signal on a word boundary,
+	 * then 'sp' should be word aligned here.  If it's
+	 * not, then the user is trying to mess with us.
+	 */
+	if (regs->ARM_sp & 3)
+		goto badframe;
+
 	frame = (struct rt_sigframe *)regs->ARM_sp;
 
 	if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
@@ -220,6 +236,10 @@
 	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
 		goto badframe;
 
+	/* Send SIGTRAP if we're single-stepping */
+	if (ptrace_cancel_bpt (current))
+		send_sig(SIGTRAP, current, 1);
+
 	return regs->ARM_r0;
 
 badframe:
@@ -260,6 +280,26 @@
 	return err;
 }
 
+static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+				 unsigned long framesize)
+{
+	unsigned long sp = regs->ARM_sp;
+
+	/*
+	 * This is the X/Open sanctioned signal stack switching.
+	 */
+	if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp))
+		sp = current->sas_ss_sp + current->sas_ss_size;
+
+	/*
+	 * No matter what happens, 'sp' must be word
+	 * aligned otherwise nasty things could happen
+	 */
+	sp &= ~3;
+
+	return (void *)(sp - framesize);
+}
+
 static void setup_frame(int sig, struct k_sigaction *ka,
 			sigset_t *set, struct pt_regs *regs)
 {
@@ -267,9 +307,9 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(*frame));
 
-	if (!access_ok(VERIFT_WRITE, frame, sizeof (*frame)))
+	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
 	err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
@@ -286,7 +326,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -299,6 +339,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -315,7 +360,8 @@
 	unsigned long retcode;
 	int err = 0;
 
-	frame = (struct rt_sigframe *)regs->ARM_sp - 1;
+	frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe));
+
 	if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
 		goto segv_and_exit;
 
@@ -337,7 +383,7 @@
 	} else {
 		retcode = (unsigned long)&frame->retcode;
 		err |= __put_user(SWI_SYS_RT_SIGRETURN, &frame->retcode);
-		__flush_entry_to_ram (&frame->retcode);
+		flush_icache_range(retcode, retcode + 4);
 	}
 
 	if (err)
@@ -350,6 +396,11 @@
 	regs->ARM_sp = (unsigned long)frame;
 	regs->ARM_lr = retcode;
 	regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+	/* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+	if (ka->sa.sa_flags & SA_THIRTYTWO)
+		regs->ARM_cpsr = USR_MODE;
+#endif
 	if (valid_user_regs(regs))
 		return;
 
@@ -393,18 +444,25 @@
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
 {
-	unsigned long instr, *pc = (unsigned long *)(instruction_pointer(regs)-4);
 	struct k_sigaction *ka;
 	siginfo_t info;
-	int single_stepping, swi_instr;
+	int single_stepping;
+
+	/*
+	 * We want the common case to go fast, which
+	 * is why we may in certain cases get here from
+	 * kernel mode. Just return without doing anything
+	 * if so.
+	 */
+	if (!user_mode(regs))
+		return 0;
 
 	if (!oldset)
 		oldset = &current->blocked;
 
 	single_stepping = ptrace_cancel_bpt (current);
-	swi_instr = (!get_user (instr, pc) && (instr & 0x0f000000) == 0x0f000000);
 
 	for (;;) {
 		unsigned long signr;
@@ -503,7 +561,7 @@
 		}
 
 		/* Are we from a system call? */
-		if (swi_instr) {
+		if (syscall) {
 			switch (regs->ARM_r0) {
 			case -ERESTARTNOHAND:
 				regs->ARM_r0 = -EINTR;
@@ -527,7 +585,7 @@
 		return 1;
 	}
 
-	if (swi_instr &&
+	if (syscall &&
 	    (regs->ARM_r0 == -ERESTARTNOHAND ||
 	     regs->ARM_r0 == -ERESTARTSYS ||
 	     regs->ARM_r0 == -ERESTARTNOINTR)) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/sys_arm.c linux.18p17/arch/arm/kernel/sys_arm.c
--- linux.vanilla/arch/arm/kernel/sys_arm.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/sys_arm.c	Fri Sep 15 23:28:37 2000
@@ -223,13 +223,7 @@
  */
 asmlinkage int sys_fork(struct pt_regs *regs)
 {
-	int ret;
-
-	lock_kernel();
-	ret = do_fork(SIGCHLD, regs->ARM_sp, regs);
-	unlock_kernel();
-
-	return ret;
+	return do_fork(SIGCHLD, regs->ARM_sp, regs);
 }
 
 /* Clone a task - this clones the calling program thread.
@@ -237,14 +231,14 @@
  */
 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
 {
-	int ret;
-
-	lock_kernel();
 	if (!newsp)
 		newsp = regs->ARM_sp;
-	ret = do_fork(clone_flags, newsp, regs);
-	unlock_kernel();
-	return ret;
+	return do_fork(clone_flags, newsp, regs);
+}
+
+asmlinkage int sys_vfork(struct pt_regs *regs)
+{
+	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs);
 }
 
 /* sys_execve() executes a new program.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/time.c linux.18p17/arch/arm/kernel/time.c
--- linux.vanilla/arch/arm/kernel/time.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/time.c	Fri Sep 15 23:28:37 2000
@@ -76,6 +76,25 @@
 		  )*60 + sec; /* finally seconds */
 }
 
+/*
+ * Handle profile stuff...
+ */
+static void do_profile(unsigned long pc)
+{
+	if (prof_buffer && current->pid) {
+		extern int _stext;
+
+		pc -= (unsigned long)&_stext;
+
+		pc >>= prof_shift;
+
+		if (pc >= prof_len)
+			pc = prof_len - 1;
+
+		prof_buffer[pc] += 1;
+	}
+}
+
 #include <asm/arch/time.h>
 
 static unsigned long do_gettimeoffset(void)
@@ -93,7 +112,7 @@
 
 	/*
 	 * xtime is atomically updated in timer_bh. lost_ticks is
-	 * nonzero if the tiemr bottom half hasnt executed yet.
+	 * nonzero if the timer bottom half hasnt executed yet.
 	 */
 	if (lost_ticks)
 		tv->tv_usec += USECS_PER_JIFFY;
@@ -127,27 +146,12 @@
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti ();
-}
-
-/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick.
- */
-static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	if (reset_timer ())
-		do_timer(regs);
-
-	update_rtc ();
+	sti();
 }
 
-static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL};
-
-__initfunc(void time_init(void))
+void __init time_init(void)
 {
-	xtime.tv_sec = setup_timer();
 	xtime.tv_usec = 0;
 
-	setup_arm_irq(IRQ_TIMER, &irqtimer);
+	setup_timer();
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/kernel/traps.c linux.18p17/arch/arm/kernel/traps.c
--- linux.vanilla/arch/arm/kernel/traps.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/kernel/traps.c	Fri Sep 15 23:28:37 2000
@@ -24,7 +24,6 @@
 #include <asm/atomic.h>
 #include <asm/pgtable.h>
 
-extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
 extern void c_backtrace (unsigned long fp, int pmode);
 extern int ptrace_cancel_bpt (struct task_struct *);
 
@@ -45,16 +44,17 @@
 
 int kstack_depth_to_print = 200;
 
-static int verify_stack_pointer (unsigned long stackptr, int size)
+/*
+ * Stack pointers should always be within the kernels view of
+ * physical memory.  If it is not there, then we can't dump
+ * out any information relating to the stack.
+ */
+static int verify_stack(unsigned long sp)
 {
-#ifdef CONFIG_CPU_26
-	if (stackptr < 0x02048000 || stackptr + size > 0x03000000)
-        	return -EFAULT;
-#else
-	if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory)
+	if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory)
 		return -EFAULT;
-#endif
-		return 0;
+
+	return 0;
 }
 
 /*
@@ -90,22 +90,26 @@
 
 static void dump_instr(unsigned long pc, int user)
 {
-	unsigned long module_start, module_end;
 	int pmin = -2, pmax = 3, ok = 0;
 	extern char start_kernel, _etext;
 
 	if (!user) {
+		unsigned long module_start, module_end;
+		unsigned long kernel_start, kernel_end;
+
 		module_start = VMALLOC_START;
 		module_end   = module_start + MODULE_RANGE;
 
-		if ((pc >= (unsigned long) &start_kernel) &&
-		    (pc <= (unsigned long) &_etext)) {
-			if (pc + pmin < (unsigned long) &start_kernel)
-				pmin = ((unsigned long) &start_kernel) - pc;
-			if (pc + pmax > (unsigned long) &_etext)
-				pmax = ((unsigned long) &_etext) - pc;
+		kernel_start = (unsigned long)&start_kernel;
+		kernel_end   = (unsigned long)&_etext;
+
+		if (pc >= kernel_start && pc < kernel_end) {
+			if (pc + pmin < kernel_start)
+				pmin = kernel_start - pc;
+			if (pc + pmax > kernel_end)
+				pmax = kernel_end - pc;
 			ok = 1;
-		} else if (pc >= module_start && pc <= module_end) {
+		} else if (pc >= module_start && pc < module_end) {
 			if (pc + pmin < module_start)
 				pmin = module_start - pc;
 			if (pc + pmax > module_end)
@@ -125,119 +129,139 @@
 		printk ("pc not in code space\n");
 }
 
-static void dump_state(char *str, struct pt_regs *regs, int err)
+spinlock_t die_lock;
+
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
 {
+	struct task_struct *tsk = current;
+
+	spin_lock_irq(&die_lock);
+
 	console_verbose();
 	printk("Internal error: %s: %x\n", str, err);
 	printk("CPU: %d\n", smp_processor_id());
 	show_regs(regs);
 	printk("Process %s (pid: %d, stackpage=%08lx)\n",
-		current->comm, current->pid, 4096+(unsigned long)current);
-}
+		current->comm, current->pid, 4096+(unsigned long)tsk);
 
-/*
- * This function is protected against kernel-mode re-entrancy.  If it
- * is re-entered it will hang the system since we can't guarantee in
- * this case that any of the functions that it calls are safe any more.
- * Even the panic function could be a problem, but we'll give it a go.
- */
-void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
-{
-	static int died = 0;
-	unsigned long cstack, sstack, frameptr;
-	
-	if (user_mode(regs))
-    		return;
+	if (!user_mode(regs)) {
+		unsigned long sp = (unsigned long)(regs + 1);
+		unsigned long fp;
+		int dump_info = 1;
+
+		printk("Stack: ");
+		if (verify_stack(sp)) {
+			printk("invalid kernel stack pointer %08lx", sp);
+			dump_info = 0;
+		} else if (sp < 4096+(unsigned long)tsk)
+			printk("kernel stack pointer underflow");
+		printk("\n");
+
+		if (dump_info)
+			dump_mem(sp - 16, 8192+(unsigned long)tsk);
+
+		dump_info = 1;
+
+		printk("Backtrace: ");
+		fp = regs->ARM_fp;
+		if (!fp) {
+			printk("no frame pointer");
+			dump_info = 0;
+		} else if (verify_stack(fp)) {
+			printk("invalid frame pointer %08lx", fp);
+			dump_info = 0;
+		} else if (fp < 4096+(unsigned long)tsk)
+			printk("frame pointer underflow");
+		printk("\n");
 
-	switch (died) {
-	case 2:
-		while (1);
-	case 1:
-		died ++;
-		panic ("die_if_kernel re-entered.  Major kernel corruption.  Please reboot me!");
-		break;
-	case 0:
-		died ++;
-		break;
-	}
+		if (dump_info)
+			c_backtrace(fp, processor_mode(regs));
 
-	dump_state(str, regs, err);
+		dump_instr(instruction_pointer(regs), 0);
+	}
 
-	cstack = (unsigned long)(regs + 1);
-	sstack = 4096+(unsigned long)current;
+	spin_unlock_irq(&die_lock);
+	do_exit(SIGSEGV);
+}
 
-	printk("Stack: ");
-	if (verify_stack_pointer(cstack, 4))
-		printk("invalid kernel stack pointer %08lx", cstack);
-	else if(cstack > sstack + 4096)
-		printk("(sp overflow)");
-	else if(cstack < sstack)
-		printk("(sp underflow)");
-	printk("\n");
-
-	dump_mem(cstack - 16, sstack + 4096);
-
-	frameptr = regs->ARM_fp;
-	if (frameptr) {
-		if (verify_stack_pointer (frameptr, 4))
-			printk ("Backtrace: invalid frame pointer\n");
-		else {
-			printk("Backtrace: \n");
-			c_backtrace (frameptr, processor_mode(regs));
-		}
-	}
+static void die_if_kernel(const char *str, struct pt_regs *regs, int err)
+{
+	if (user_mode(regs))
+    		return;
 
-	dump_instr(instruction_pointer(regs), 0);
-	died = 0;
-	if (ret != -1)
-		do_exit (ret);
-	else {
-		cli ();
-		while (1);
-	}
+    	die(str, regs, err);
 }
 
-void bad_user_access_alignment (const void *ptr)
+void bad_user_access_alignment(const void *ptr)
 {
-	void *pc;
-	__asm__("mov %0, lr\n": "=r" (pc));
-	printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc);
+	printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr, 
+		__builtin_return_address(0));
 	current->tss.error_code = 0;
 	current->tss.trap_no = 11;
-	force_sig (SIGBUS, current);
-/*	die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/
+	force_sig(SIGBUS, current);
+/*	die_if_kernel("Oops - bad user access alignment", regs, mode);*/
 }
 
-asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
 {
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n",
+		current->comm, current->pid, instruction_pointer(regs));
+#endif
 	current->tss.error_code = 0;
 	current->tss.trap_no = 6;
-	force_sig (SIGILL, current);
-	die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
+	force_sig(SIGILL, current);
+	die_if_kernel("Oops - undefined instruction", regs, mode);
 }
 
-asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode)
 {
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
+		current->comm, current->pid, instruction_pointer(regs));
+#endif
 	current->tss.error_code = 0;
 	current->tss.trap_no = 11;
-	force_sig (SIGBUS, current);
-	die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
+	force_sig(SIGBUS, current);
+	die_if_kernel("Oops - address exception", regs, mode);
 }
 
 asmlinkage void do_unexp_fiq (struct pt_regs *regs)
 {
 #ifndef CONFIG_IGNORE_FIQ
-	printk ("Hmm.  Unexpected FIQ received, but trying to continue\n");
-	printk ("You may have a hardware problem...\n");
+	printk("Hmm.  Unexpected FIQ received, but trying to continue\n");
+	printk("You may have a hardware problem...\n");
 #endif
 }
 
+/*
+ * bad_mode handles the impossible case in the vectors.
+ * If you see one of these, then it's extremely serious,
+ * and could mean you have buggy hardware.  It never
+ * returns, and never tries to sync.  We hope that we
+ * can dump out some state information...
+ */
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
 {
-	printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
-		handler[reason],
-		processor_modes[proc_mode]);
-	die_if_kernel ("Oops", regs, 0, -1);
+	console_verbose();
+
+	printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
+		handler[reason], processor_modes[proc_mode]);
+
+	/*
+	 * Dump out the vectors and stub routines
+	 */
+	printk(KERN_CRIT "Vectors:\n");
+	dump_mem(0, 0x40);
+	printk(KERN_CRIT "Stubs:\n");
+	dump_mem(0x200, 0x4b8);
+
+	die("Oops", regs, 0);
+	cli();
+	while(1);
 }
 
 /*
@@ -249,54 +273,85 @@
  */
 asmlinkage void math_state_restore (void)
 {
-    	current->used_math = 1;
+	current->used_math = 1;
 }
 
-asmlinkage void arm_syscall (int no, struct pt_regs *regs)
+asmlinkage int arm_syscall (int no, struct pt_regs *regs)
 {
 	switch (no) {
 	case 0: /* branch through 0 */
 		force_sig(SIGSEGV, current);
-//		if (user_mode(regs)) {
-//			dump_state("branch through zero", regs, 0);
-//			if (regs->ARM_fp)
-//				c_backtrace (regs->ARM_fp, processor_mode(regs));
-//		}
-		die_if_kernel ("branch through zero", regs, 0, SIGSEGV);
+		die_if_kernel("branch through zero", regs, 0);
 		break;
 
 	case 1: /* SWI_BREAK_POINT */
 		regs->ARM_pc -= 4; /* Decrement PC by one instruction */
-		ptrace_cancel_bpt (current);
-		force_sig (SIGTRAP, current);
+		ptrace_cancel_bpt(current);
+		force_sig(SIGTRAP, current);
+		return regs->ARM_r0;
+
+	case 2:	/* sys_cacheflush */
+#ifdef CONFIG_CPU_32
+		/* r0 = start, r1 = length, r2 = flags */
+		processor.u.armv3v4._flush_cache_area(regs->ARM_r0,
+						      regs->ARM_r1,
+						      1);
+#endif
 		break;
 
 	default:
-		printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
-		force_sig (SIGILL, current);
+		/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
+		   if not implemented, rather than raising SIGILL.  This
+		   way the calling program can gracefully determine whether
+		   a feature is supported.  */
+		if (no <= 0x7ff)
+			return -ENOSYS;
+#ifdef CONFIG_DEBUG_USER
+		/* experiance shows that these seem to indicate that
+		 * something catastrophic has happened
+		 */
+		printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
 		if (user_mode(regs)) {
-			show_regs (regs);
-			c_backtrace (regs->ARM_fp, processor_mode(regs));
+			show_regs(regs);
+			c_backtrace(regs->ARM_fp, processor_mode(regs));
 		}
-		die_if_kernel ("Oops", regs, no, SIGILL);
+#endif
+		force_sig(SIGILL, current);
+		die_if_kernel("Oops", regs, no);
 		break;
 	}
+	return 0;
 }
 
 asmlinkage void deferred(int n, struct pt_regs *regs)
 {
-	dump_state("old system call", regs, n);
-	force_sig (SIGILL, current);
+	/* You might think just testing `handler' would be enough, but PER_LINUX
+	 * points it to no_lcall7 to catch undercover SVr4 binaries.  Gutted.
+	 */
+	if (current->personality != PER_LINUX && current->exec_domain->handler) {
+		/* Hand it off to iBCS.  The extra parameter and consequent type 
+		 * forcing is necessary because of the weird ARM calling convention.
+		 */
+		void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler;
+		(*handler)(n, regs);
+		return;
+	}
+
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_ERR "[%d] %s: old system call.\n", current->pid, 
+	       current->comm);
+#endif
+	force_sig(SIGILL, current);
 }
 
 asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
 {
-	printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
+	printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
 }
 
-asmlinkage void arm_invalidptr (const char *function, int size)
+asmlinkage void arm_invalidptr(const char *function, int size)
 {
-	printk ("Invalid pointer size in %s (PC=%p) size %d\n",
+	printk("Invalid pointer size in %s (pc=%p) size %d\n",
 		function, __builtin_return_address(0), size);
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/Makefile linux.18p17/arch/arm/lib/Makefile
--- linux.vanilla/arch/arm/lib/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/Makefile	Fri Sep 15 23:28:37 2000
@@ -1,19 +1,25 @@
 #
 # linux/arch/arm/lib/Makefile
 #
-# Copyright (C) 1995-1998 Russell King
+# Copyright (C) 1995-1999 Russell King
 #
 
 L_TARGET := lib.a
-L_OBJS   := backtrace.o bitops.o checksum.o delay.o io.o memcpy.o \
-	    system.o string.o uaccess.o
+L_OBJS   := changebit.o checksum.o \
+	    clearbit.o copy_page.o findbit.o \
+	    memchr.o memcpy.o memset.o memzero.o setbit.o strchr.o \
+	    strrchr.o testchangebit.o testclearbit.o testsetbit.o  \
+	    semaphore.o system.o uaccess.o
+
+O_TARGET := lib.o
+O_OBJS   := backtrace.o delay.o
 
 ifeq ($(PROCESSOR),armo)
   L_OBJS += uaccess-armo.o
 endif
 
 ifdef CONFIG_ARCH_ACORN
-  L_OBJS += loaders.o ll_char_wr.o io-acorn.o
+  L_OBJS += io-acorn.o
   ifdef CONFIG_ARCH_A5K
     L_OBJS += floppydma.o
   endif
@@ -24,31 +30,23 @@
 
 ifeq ($(MACHINE),ebsa110)
   L_OBJS += io-ebsa110.o
+else
+  OX_OBJS += io.o
 endif
 
-ifeq ($(MACHINE),vnc)
-  L_OBJS += io-ebsa285.o
-endif
-
-ifeq ($(MACHINE),ebsa285)
-  L_OBJS += io-ebsa285.o
+ifeq ($(MACHINE),footbridge)
+  L_OBJS += io-footbridge.o
 endif
 
 include $(TOPDIR)/Rules.make
 
+%.o: %.S
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
+
 constants.h: getconsdata.o extractconstants.pl
 	$(PERL) extractconstants.pl $(OBJDUMP) > $@
 
 getconsdata.o: getconsdata.c
 	$(CC) $(CFLAGS) -c getconsdata.c
 
-checksum.o: constants.h
-
-%.o: %.S
-ifneq ($(CONFIG_BINUTILS_NEW),y)
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s
-	$(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s
-	$(RM) ..tmp.$<.s
-else
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
-endif
+checksum.o string.o: constants.h
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/backtrace.S linux.18p17/arch/arm/lib/backtrace.S
--- linux.vanilla/arch/arm/lib/backtrace.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/backtrace.S	Fri Sep 15 23:28:37 2000
@@ -21,6 +21,11 @@
 		mov	r0, fp
 
 ENTRY(c_backtrace)
+
+#ifndef CONFIG_FRAME_POINTER
+		mov	pc, lr
+#else
+
 		stmfd	sp!, {r4 - r8, lr}	@ Save an extra register so we have a location...
 #ifdef CONFIG_CPU_32
 		tst	r1, #0x10		@ 26 or 32-bit?
@@ -55,14 +60,14 @@
 
 		sub	r0, frame, #16
 		ldr	r1, [save, #4]
-		mov	r3, r1, lsr #10
+		mov	r3, r1, lsr #11
 		ldr	r2, .Ldsi+4
 		teq	r3, r2			@ Check for stmia sp!, {args}
 		addeq	save, save, #4		@ next instruction
 		bleq	.Ldumpstm
 
 		ldr	r1, [save, #4]		@ Get 'stmia sp!, {rlist, fp, ip, lr, pc}' instruction
-		mov	r3, r1, lsr #10
+		mov	r3, r1, lsr #11
 		ldr	r2, .Ldsi
 		teq	r3, r2
 		bleq	.Ldumpstm
@@ -78,28 +83,36 @@
 #define reg   r5
 #define stack r6
 
-.Ldumpstm:	stmfd	sp!, {instr, reg, stack, lr}
+.Ldumpstm:	stmfd	sp!, {instr, reg, stack, r7, lr}
 		mov	stack, r0
 		mov	instr, r1
-		mov	reg, #9
-
+		mov	reg, #10
+		mov	r7, #0
 1:		mov	r3, #1
 		tst	instr, r3, lsl reg
 		beq	2f
+		add	r7, r7, #1
+		teq	r7, #4
+		moveq	r7, #0
+		moveq	r3, #'\n'
+		movne	r3, #' '
 		ldr	r2, [stack], #-4
 		mov	r1, reg
 		adr	r0, .Lfp
 		bl	SYMBOL_NAME(printk)
 2:		subs	reg, reg, #1
 		bpl	1b
-
+		teq	r7, #0
+		adrne	r0, .Lcr
+		blne	SYMBOL_NAME(printk)
 		mov	r0, stack
-		LOADREGS(fd, sp!, {instr, reg, stack, pc})
+		LOADREGS(fd, sp!, {instr, reg, stack, r7, pc})
 
-.Lfe:		.ascii	"Function entered at [<%p>] from [<%p>]\n"
-		.byte 0
-.Lfp:		.ascii	"  r%d = %p\n"
-		.byte 0
+.Lfe:		.asciz	"Function entered at [<%p>] from [<%p>]\n"
+.Lfp:		.asciz	" r%d = %08X%c"
+.Lcr:		.asciz	"\n"
 		.align
-.Ldsi:		.word	0x00e92dd8 >> 2
-		.word	0x00e92d00 >> 2
+.Ldsi:		.word	0x00e92dd8 >> 3
+		.word	0x00e92d00 >> 3
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/bitops.S linux.18p17/arch/arm/lib/bitops.S
--- linux.vanilla/arch/arm/lib/bitops.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/bitops.S	Thu Jan  1 01:00:00 1970
@@ -1,152 +0,0 @@
-/*
- * linux/arch/arm/lib/bitops.S
- *
- * Copyright (C) 1995, 1996 Russell King
- */
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-                .text
-
-@ Purpose  : Function to set a bit
-@ Prototype: int set_bit(int bit,int *addr)
-
-ENTRY(set_bit)
-                and     r2, r0, #7
-                mov     r3, #1
-                mov     r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		orr	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_set_bit)
-		add	r1, r1, r0, lsr #3	@ Get byte offset
-		and	r3, r0, #7		@ Get bit offset
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		orr	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Function to clear a bit
-@ Prototype: int clear_bit(int bit,int *addr)
-
-ENTRY(clear_bit)
-		and	r2, r0, #7
-		mov	r3, #1
-		mov	r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		bic	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_clear_bit)
-		add	r1, r1, r0, lsr #3	@ Get byte offset
-		and	r3, r0, #7	@ Get bit offset
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		bic	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-/* Purpose  : Function to change a bit
- * Prototype: int change_bit(int bit,int *addr)
- */
-ENTRY(change_bit)
-                and     r2, r0, #7
-                mov     r3, #1
-                mov     r3, r3, lsl r2
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1, r0, lsr #3]
-		eor	r2, r2, r3
-		strb	r2, [r1, r0, lsr #3]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-ENTRY(test_and_change_bit)
-		add	r1, r1, r0, lsr #3
-		and	r3, r0, #7
-		mov	r0, #1
-		SAVEIRQS(ip)
-		DISABLEIRQS(ip)
-		ldrb	r2, [r1]
-		tst	r2, r0, lsl r3
-		eor	r2, r2, r0, lsl r3
-		moveq	r0, #0
-		strb	r2, [r1]
-		RESTOREIRQS(ip)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Find a 'zero' bit
-@ Prototype: int find_first_zero_bit(char *addr,int maxbit);
-
-ENTRY(find_first_zero_bit)
-		mov	r2, #0			@ Initialise bit position
-Lfindzbit1lp:	ldrb	r3, [r0, r2, lsr #3]	@ Check byte, if 0xFF, then all bits set
-		teq	r3, #0xFF
-		bne	Lfoundzbit
-		add	r2, r2, #8
-		cmp	r2, r1			@ Check to see if we have come to the end
-		bcc	Lfindzbit1lp
-		add	r0, r1, #1		@ Make sure that we flag an error
-		RETINSTR(mov,pc,lr)
-Lfoundzbit:	tst	r3, #1			@ Check individual bits
-		moveq	r0, r2
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #2
-		addeq	r0, r2, #1
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #4
-		addeq	r0, r2, #2
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #8
-		addeq	r0, r2, #3
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #16
-		addeq	r0, r2, #4
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #32
-		addeq	r0, r2, #5
-		RETINSTR(moveq,pc,lr)
-		tst	r3, #64
-		addeq	r0, r2, #6
-		RETINSTR(moveq,pc,lr)
-		add	r0, r2, #7
-		RETINSTR(mov,pc,lr)
-
-@ Purpose  : Find next 'zero' bit
-@ Prototype: int find_next_zero_bit(char *addr,int maxbit,int offset)
-
-ENTRY(find_next_zero_bit)
-		tst	r2, #7
-		beq	Lfindzbit1lp		@ If new byte, goto old routine
-		ldrb	r3, [r0, r2, lsr#3]
-		orr	r3, r3, #0xFF00		@ Set top bits so we wont get confused
-		stmfd	sp!, {r4}
-		and	r4, r2, #7
-		mov	r3, r3, lsr r4		@ Shift right by no. of bits
-		ldmfd	sp!, {r4}
-		and	r3, r3, #0xFF
-		teq	r3, #0xFF
-		orreq	r2, r2, #7
-		addeq	r2, r2, #1
-		beq	Lfindzbit1lp		@ If all bits are set, goto old routine
-		b	Lfoundzbit
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/changebit.S linux.18p17/arch/arm/lib/changebit.S
--- linux.vanilla/arch/arm/lib/changebit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/changebit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/changebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+/* Purpose  : Function to change a bit
+ * Prototype: int change_bit(int bit,int *addr)
+ */
+ENTRY(change_bit)
+                and     r2, r0, #7
+                mov     r3, #1
+                mov     r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		eor	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/checksum.S linux.18p17/arch/arm/lib/checksum.S
--- linux.vanilla/arch/arm/lib/checksum.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/checksum.S	Fri Sep 15 23:28:37 2000
@@ -74,8 +74,9 @@
 		stmfd	sp!, {r1 - r2, r4 - r8, fp, ip, lr, pc}
 		.endm
 
-#define LOAD_REGS(cond)					\
-		LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r8, fp, sp, pc})
+		.macro	load_regs,flags
+		ldm\flags	fp, {r1, r2, r4-r8, fp, sp, pc}
+		.endm
 
 		.macro	load1b,	reg1
 9999:		ldrbt	\reg1, [r0], $1
@@ -130,12 +131,13 @@
 		stmfd	sp!, {r1 - r2, r4 - r9, fp, ip, lr, pc}
 		mov	r9, sp, lsr #13
 		mov	r9, r9, lsl #13
-		ldr	r9, [r9, #TSK_ADDR_LIMIT]
+		ldr	r9, [r9, #ADDR_LIMIT]
 		mov	r9, r9, lsr #24
 		.endm
 
-#define LOAD_REGS(cond)					\
-		LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r9, fp, sp, pc})
+		.macro	load_regs,flags
+		ldm\flags	fp, {r1, r2, r4-r9, fp, sp, pc}^
+		.endm
 
 		.macro	load1b,	reg1
 		tst	r9, #0x01
@@ -245,7 +247,7 @@
 		adcs	r3, r3, r4
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		load1l	r4
 		tst	r2, #2
 		beq	.exit
@@ -259,11 +261,11 @@
 		andne	r4, r4, #255
 		adcnes	r3, r3, r4
 		adcs	r0, r3, #0
-		LOAD_REGS(al)
+		load_regs	ea
 
 .too_small_user:
 		teq	r2, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		cmp	r2, #2
 		blt	.too_small_user1
 		load2b	ip, r8
@@ -278,7 +280,7 @@
 		strb	ip, [r1], #1
 		adcs	r3, r3, ip
 .csum_exit:	adc	r0, r3, #0
-		LOAD_REGS(al)
+		load_regs	ea
 
 .src_not_aligned_user:
 		cmp	r2, #4
@@ -331,7 +333,7 @@
 		mov	r4, r5, lsr #8
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
@@ -384,13 +386,16 @@
 		mov	r4, r5, lsr #16
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
+		tst	r2, #1
+		adceq	r0, r3, #0
+		load_regs	eqea
 		load1b	r4
 		b	.exit
 
@@ -437,7 +442,7 @@
 		mov	r4, r5, lsr #24
 4:		ands	r2, r2, #3
 		adceq	r0, r3, #0
-		LOAD_REGS(eq)
+		load_regs	eqea
 		tst	r2, #2
 		beq	.exit
 		adcs	r3, r3, r4, lsl #16
@@ -461,7 +466,7 @@
 6002:		teq	r2, r1
 		strneb	r3, [r1], #1
 		bne	6002b
-		LOAD_REGS(al)
+		load_regs	ea
 #if defined(CONFIG_CPU_32)
 		.previous
 #endif
@@ -520,13 +525,13 @@
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		ldr	r4, [r0], #4
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 Ltoo_small:	teq	r2, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
@@ -538,10 +543,12 @@
 		adds	r3, r3, ip
 		strb	ip, [r1], #1
 		strb	r8, [r1], #1
-Lexit:		tst	r2, #1
-Ltoo_small1:	ldrneb	ip, [r0], #1
-		strneb	ip, [r1], #1
-		adcnes	r3, r3, ip
+		tst	r2, #1
+Ltoo_small1:	ldrneb	r4, [r0], #1
+Lexit_r4:	tst	r2, #1
+		strneb	r4, [r1], #1
+		andne	r4, r4, #255
+		adcnes	r3, r3, r4
 		adcs	r0, r3, #0
 		LOADREGS(ea,fp,{r4 - r8, fp, sp, pc})
 
@@ -598,13 +605,13 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 Lsrc2_aligned:	mov	r4, r4, lsr #16
 		adds	r3, r3, #0
@@ -650,13 +657,13 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		mov	r4, r4, lsr #8
 		strb	r4, [r1], #1
 		ldrb	r4, [r0], #1
-		b	Lexit
+		b	Lexit_r4
 
 Lsrc3_aligned:	mov	r4, r4, lsr #24
 		adds	r3, r3, #0
@@ -702,14 +709,14 @@
 		adceq	r0, r3, #0
 		LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
 		tst	r2, #2
-		beq	Lexit
+		beq	Lexit_r4
 		adcs	r3, r3, r4, lsl #16
 		strb	r4, [r1], #1
 		ldr	r4, [r0], #4
 		strb	r4, [r1], #1
 		adcs	r3, r3, r4, lsl #24
 		mov	r4, r4, lsr #8
-		b	Lexit
+		b	Lexit_r4
 
 ENTRY(__csum_ipv6_magic)
 		stmfd	sp!, {lr}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/clearbit.S linux.18p17/arch/arm/lib/clearbit.S
--- linux.vanilla/arch/arm/lib/clearbit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/clearbit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/clearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Function to clear a bit
+@ Prototype: int clear_bit(int bit,int *addr)
+
+ENTRY(clear_bit)
+		and	r2, r0, #7
+		mov	r3, #1
+		mov	r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		bic	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/copy_page.S linux.18p17/arch/arm/lib/copy_page.S
--- linux.vanilla/arch/arm/lib/copy_page.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/copy_page.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,35 @@
+/*
+ * linux/arch/arm/lib/copypage.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text 
+/*
+ * StrongARM optimised copy_page routine
+ * now 1.72bytes/cycle, was 1.60 bytes/cycle
+ * (50MHz bus -> 86MB/s)
+ */
+
+ENTRY(copy_page)
+		stmfd	sp!, {r4, lr}			@	2
+		mov	r2, #PAGE_SZ/64			@	1
+1:		ldmia	r1!, {r3, r4, ip, lr}		@	4
+		subs	r2, r2, #1			@	1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		ldmia	r1!, {r3, r4, ip, lr}		@	4+1
+		stmia	r0!, {r3, r4, ip, lr}		@	4
+		bne	1b				@	1
+		LOADREGS(fd, sp!, {r4, pc})		@	3
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/delay.S linux.18p17/arch/arm/lib/delay.S
--- linux.vanilla/arch/arm/lib/delay.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/delay.S	Mon Oct  2 10:11:32 2000
@@ -7,7 +7,7 @@
 #include <asm/assembler.h>
 		.text
 
-LC0:		.word	SYMBOL_NAME(loops_per_sec)
+LC0:		.word	SYMBOL_NAME(loops_per_jiffy)
 
 ENTRY(udelay)
 		mov	r2, #0x1000
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/findbit.S linux.18p17/arch/arm/lib/findbit.S
--- linux.vanilla/arch/arm/lib/findbit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/findbit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,65 @@
+/*
+ * linux/arch/arm/lib/bitops.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Find a 'zero' bit
+@ Prototype: int find_first_zero_bit(char *addr,int maxbit);
+
+ENTRY(find_first_zero_bit)
+		mov	r2, #0			@ Initialise bit position
+Lfindzbit1lp:	ldrb	r3, [r0, r2, lsr #3]	@ Check byte, if 0xFF, then all bits set
+		teq	r3, #0xFF
+		bne	Lfoundzbit
+		add	r2, r2, #8
+		cmp	r2, r1			@ Check to see if we have come to the end
+		bcc	Lfindzbit1lp
+		add	r0, r1, #1		@ Make sure that we flag an error
+		RETINSTR(mov,pc,lr)
+Lfoundzbit:	tst	r3, #1			@ Check individual bits
+		moveq	r0, r2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #2
+		addeq	r0, r2, #1
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #4
+		addeq	r0, r2, #2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #8
+		addeq	r0, r2, #3
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #16
+		addeq	r0, r2, #4
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #32
+		addeq	r0, r2, #5
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #64
+		addeq	r0, r2, #6
+		RETINSTR(moveq,pc,lr)
+		add	r0, r2, #7
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Find next 'zero' bit
+@ Prototype: int find_next_zero_bit(char *addr,int maxbit,int offset)
+
+ENTRY(find_next_zero_bit)
+		tst	r2, #7
+		beq	Lfindzbit1lp		@ If new byte, goto old routine
+		ldrb	r3, [r0, r2, lsr#3]
+		orr	r3, r3, #0xFF00		@ Set top bits so we wont get confused
+		stmfd	sp!, {r4}
+		and	r4, r2, #7
+		mov	r3, r3, lsr r4		@ Shift right by no. of bits
+		ldmfd	sp!, {r4}
+		and	r3, r3, #0xFF
+		teq	r3, #0xFF
+		orreq	r2, r2, #7
+		addeq	r2, r2, #1
+		beq	Lfindzbit1lp		@ If all bits are set, goto old routine
+		b	Lfoundzbit
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/floppydma.S linux.18p17/arch/arm/lib/floppydma.S
--- linux.vanilla/arch/arm/lib/floppydma.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/floppydma.S	Fri Sep 15 23:28:37 2000
@@ -26,32 +26,3 @@
 		strb	r12, [r11, #-4]
 		subs	pc, lr, #4
 SYMBOL_NAME(floppy_fiqout_end):
-
-@ Params:
-@ r0 = length
-@ r1 = address
-@ r2 = floppy port
-@ Puts these into R9_fiq, R10_fiq, R11_fiq
-ENTRY(floppy_fiqsetup)
-		mov	ip, sp
-		stmfd	sp!, {fp, ip, lr, pc}
-		sub	fp, ip, #4
-		MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ)	@ disable FIQs, IRQs, FIQ mode
-		mov	r0, r0
-		mov	r9, r0
-		mov	r10, r1
-		mov	r11, r2
-		RESTOREMODE(r3)				@ back to normal
-		mov	r0, r0
-		LOADREGS(ea,fp,{fp, sp, pc})
-
-ENTRY(floppy_fiqresidual)
-		mov	ip, sp
-		stmfd	sp!, {fp, ip, lr, pc}
-		sub	fp, ip, #4
-		MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ)	@ disable FIQs, IRQs, FIQ mode
-		mov	r0, r0
-		mov	r0, r9
-		RESTOREMODE(r3)
-		mov	r0, r0
-		LOADREGS(ea,fp,{fp, sp, pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/getconsdata.c linux.18p17/arch/arm/lib/getconsdata.c
--- linux.vanilla/arch/arm/lib/getconsdata.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/getconsdata.c	Fri Sep 15 23:28:37 2000
@@ -10,23 +10,25 @@
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 
+/*
+ * Make sure that the compiler and target are compatible
+ */
+#if (defined(__APCS_32__) && defined(CONFIG_CPU_26))
+#error Your compiler targets APCS-32 but this kernel requires APCS-26.
+#endif
+#if (defined(__APCS_26__) && defined(CONFIG_CPU_32))
+#error Your compiler targets APCS-26 but this kernel requires APCS-32.
+#endif
+
 #undef PAGE_READONLY
 
 #define OFF_TSK(n) (unsigned long)&(((struct task_struct *)0)->n)
 #define OFF_MM(n) (unsigned long)&(((struct mm_struct *)0)->n)
 
-#ifdef KERNEL_DOMAIN
-unsigned long DOM_KERNELDOMAIN = KERNEL_DOMAIN;
-#endif
-#ifdef USER_DOMAIN
-unsigned long DOM_USERDOMAIN = USER_DOMAIN;
-#endif
-
 unsigned long TSK_STATE = OFF_TSK(state);
 unsigned long TSK_FLAGS = OFF_TSK(flags);
 unsigned long TSK_NEED_RESCHED = OFF_TSK(need_resched);
 unsigned long TSK_SIGPENDING = OFF_TSK(sigpending);
-unsigned long TSK_ADDR_LIMIT = OFF_TSK(addr_limit);
 unsigned long TSK_USED_MATH = OFF_TSK(used_math);
 
 unsigned long MM = OFF_TSK(mm);
@@ -36,8 +38,12 @@
 unsigned long TSS_SAVE = OFF_TSK(tss.save);
 unsigned long TSS_FPESAVE = OFF_TSK(tss.fpstate.soft.save);
 #ifdef CONFIG_CPU_26
+unsigned long ADDR_LIMIT  = OFF_TSK(addr_limit);
 unsigned long TSS_MEMCMAP = OFF_TSK(tss.memcmap);
 #endif
+#ifdef CONFIG_CPU_32
+unsigned long TSS_DOMAIN = OFF_TSK(tss.domain);
+#endif
 
 #ifdef _PAGE_PRESENT
 unsigned long PAGE_PRESENT = _PAGE_PRESENT;
@@ -66,6 +72,25 @@
 #ifdef _PAGE_CLEAN
 unsigned long PAGE_CLEAN = _PAGE_CLEAN;
 #endif
+
+#ifdef PTE_TYPE_SMALL
+unsigned long HPTE_TYPE_SMALL = PTE_TYPE_SMALL;
+unsigned long HPTE_AP_READ    = PTE_AP_READ;
+unsigned long HPTE_AP_WRITE   = PTE_AP_WRITE;
+#endif
+
+#ifdef L_PTE_PRESENT
+unsigned long LPTE_PRESENT    = L_PTE_PRESENT;
+unsigned long LPTE_YOUNG      = L_PTE_YOUNG;
+unsigned long LPTE_BUFFERABLE = L_PTE_BUFFERABLE;
+unsigned long LPTE_CACHEABLE  = L_PTE_CACHEABLE;
+unsigned long LPTE_USER       = L_PTE_USER;
+unsigned long LPTE_WRITE      = L_PTE_WRITE;
+unsigned long LPTE_EXEC       = L_PTE_EXEC;
+unsigned long LPTE_DIRTY      = L_PTE_DIRTY;
+#endif
+
+unsigned long PAGE_SZ = PAGE_SIZE;
 
 unsigned long KSWI_BASE = 0x900000;
 unsigned long KSWI_SYS_BASE = 0x9f0000;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/io-acorn.S linux.18p17/arch/arm/lib/io-acorn.S
--- linux.vanilla/arch/arm/lib/io-acorn.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/io-acorn.S	Fri Sep 15 23:28:37 2000
@@ -11,50 +11,514 @@
 		.text
 		.align
 
-#define OUT(reg)						\
-		mov	r8, reg, lsl $16			;\
-		orr	r8, r8, r8, lsr $16			;\
-		str	r8, [r3, r0, lsl $2]			;\
-		mov	r8, reg, lsr $16			;\
-		orr	r8, r8, r8, lsl $16			;\
-		str	r8, [r3, r0, lsl $2]
-
-#define IN(reg)							\
-		ldr	reg, [r0]				;\
-		and	reg, reg, ip				;\
-		ldr	lr, [r0]				;\
-		orr	reg, reg, lr, lsl $16
-
-		.equ	pcio_base_high, PCIO_BASE & 0xff000000
-		.equ	pcio_base_low,	PCIO_BASE & 0x00ff0000
-		.equ	io_base_high, IO_BASE & 0xff000000
-		.equ	io_base_low, IO_BASE & 0x00ff0000
-
-		.equ	addr_io_diff_hi, pcio_base_high - io_base_high
-		.equ	addr_io_diff_lo, pcio_base_low - io_base_low
-
-		.macro	addr	reg, off
-		tst	\off, #0x80000000
-		.if	addr_io_diff_hi
-		movne	\reg, #IO_BASE
-		moveq	\reg, #pcio_base_high
-		.if	pcio_base_low
-		addeq	\reg, \reg, #pcio_base_low
-		.endif
-		.else
-		mov	\reg, #IO_BASE
-		addeq	\reg, \reg, #addr_io_diff_lo
-		.endif
+		.equ	diff_pcio_base, PCIO_BASE - IO_BASE
+
+		.macro	outw2	rd
+		mov	r8, \rd, lsl #16
+		orr	r8, r8, r8, lsr #16
+		str	r8, [r3, r0, lsl #2]
+		mov	r8, \rd, lsr #16
+		orr	r8, r8, r8, lsl #16
+		str	r8, [r3, r0, lsl #2]
+		.endm
+
+		.macro	inw2	rd, mask, temp
+		ldr	\rd, [r0]
+		and	\rd, \rd, \mask
+		ldr	\temp, [r0]
+		orr	\rd, \rd, \temp, lsl #16
+		.endm
+
+		.macro	addr	rd
+		tst	\rd, #0x80000000
+		mov	\rd, \rd, lsl #2
+		add	\rd, \rd, #IO_BASE
+		addeq	\rd, \rd, #diff_pcio_base
 		.endm
 
-@ Purpose: read a block of data from a hardware register to memory.
-@ Proto  : insw(int from_port, void *to, int len_in_words);
-@ Proto  : inswb(int from_port, void *to, int len_in_bytes);
-@ Notes  : increment to
+.iosw_bad_align_msg:
+		.ascii	"insw: bad buffer alignment (%p), called from %08lX\n\0"
+.iosl_warning:
+		.ascii	"<4>insl/outsl not implemented, called from %08lX\0"
+		.align
+
+/*
+ * These make no sense on Acorn machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+		adr	r0, .iosl_warning
+		mov	r1, lr
+		b	SYMBOL_NAME(printk)
+
+.iosw_bad_alignment:
+		adr	r0, .iosw_bad_align_msg
+		mov	r2, lr
+		b	SYMBOL_NAME(panic)
+
+
+/* Purpose: read a block of data from a hardware register to memory.
+ * Proto  : void insw(int from_port, void *to, int len_in_words);
+ * Notes  : increment to, 'to' must be 16-bit aligned
+ */
+
+.insw_align:	tst	r1, #1
+		bne	.iosw_bad_alignment
+
+		ldr	r3, [r0]
+		strb	r3, [r1], #1
+		mov	r3, r3, lsr #8
+		strb	r3, [r1], #1
+
+		subs	r2, r2, #1
+		bne	.insw_aligned
 
 ENTRY(insw)
+		teq	r2, #0
+		RETINSTR(moveq,pc,lr)
+		addr	r0
+		tst	r1, #3
+		bne	.insw_align
+
+.insw_aligned:	mov	ip, #0xff
+		orr	ip, ip, ip, lsl #8
+		stmfd	sp!, {r4, r5, r6, lr}
+
+		subs	r2, r2, #8
+		bmi	.no_insw_8
+
+.insw_8_lp:	ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		ldr	r4, [r0]
+		and	r4, r4, ip
+		ldr	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+
+		ldr	r5, [r0]
+		and	r5, r5, ip
+		ldr	r6, [r0]
+		orr	r5, r5, r6, lsl #16
+
+		ldr	r6, [r0]
+		and	r6, r6, ip
+		ldr	lr, [r0]
+		orr	r6, r6, lr, lsl #16
+
+		stmia	r1!, {r3 - r6}
+		subs	r2, r2, #8
+		bpl	.insw_8_lp
+		tst	r2, #7
+		LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_insw_8:	tst	r2, #4
+		beq	.no_insw_4
+
+		ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		ldr	r4, [r0]
+		and	r4, r4, ip
+		ldr	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+
+		stmia	r1!, {r3, r4}
+
+.no_insw_4:	tst	r2, #2
+		beq	.no_insw_2
+
+		ldr	r3, [r0]
+		and	r3, r3, ip
+		ldr	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+
+		str	r3, [r1], #4
+
+.no_insw_2:	tst	r2, #1
+		ldrne	r3, [r0]
+		strneb	r3, [r1], #1
+		movne	r3, r3, lsr #8
+		strneb	r3, [r1]
+		LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+@ Purpose: write a block of data from memory to a hardware register.
+@ Proto  : outsw(int to_reg, void *from, int len_in_words);
+@ Notes  : increments from
+
+.outsw_align:	tst	r1, #1
+		bne	.iosw_bad_alignment
+
+		add	r1, r1, #2
+
+		ldr	r3, [r1, #-4]
+		mov	r3, r3, lsr #16
+		orr	r3, r3, r3, lsl #16
+		str	r3, [r0]
+		subs	r2, r2, #1
+		bne	.outsw_aligned
+
+ENTRY(outsw)
+		teq	r2, #0
+		RETINSTR(moveq,pc,lr)
+		addr	r0
+		tst	r1, #3
+		bne	.outsw_align
+
+.outsw_aligned:	stmfd	sp!, {r4, r5, r6, lr}
+
+		subs	r2, r2, #8
+		bmi	.no_outsw_8
+.outsw_8_lp:	ldmia	r1!, {r3, r4, r5, r6}
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r5, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r5, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r6, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r6, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		subs	r2, r2, #8
+		bpl	.outsw_8_lp
+		tst	r2, #7
+		LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_outsw_8:	tst	r2, #4
+		beq	.no_outsw_4
+
+		ldmia	r1!, {r3, r4}
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r4, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+.no_outsw_4:	tst	r2, #2
+		beq	.no_outsw_2
+
+		ldr	r3, [r1], #4
+
+		mov	ip, r3, lsl #16
+		orr	ip, ip, ip, lsr #16
+		str	ip, [r0]
+
+		mov	ip, r3, lsr #16
+		orr	ip, ip, ip, lsl #16
+		str	ip, [r0]
+
+.no_outsw_2:	tst	r2, #1
+
+		ldrne	r3, [r1]
+
+		movne	ip, r3, lsl #16
+		orrne	ip, ip, ip, lsr #16
+		strne	ip, [r0]
+
+		LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+.insb_align:	rsb	ip, ip, #4
+		cmp	ip, r2
+		movgt	ip, r2
+		cmp	ip, #2
+		ldrb	r3, [r0]
+		strb	r3, [r1], #1
+		ldrgeb	r3, [r0]
+		strgeb	r3, [r1], #1
+		ldrgtb	r3, [r0]
+		strgtb	r3, [r1], #1
+		subs	r2, r2, ip
+		bne	.insb_aligned
+
+ENTRY(insb)
+		teq	r2, #0
+		moveq	pc, lr
+		addr	r0
+		ands	ip, r1, #3
+		bne	.insb_align
+
+.insb_aligned:	stmfd	sp!, {r4 - r6, lr}
+
+		subs	r2, r2, #16
+		bmi	.insb_no_16
+
+.insb_16_lp:	ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		ldrb	r4, [r0]
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #8
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #24
+		ldrb	r5, [r0]
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #8
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #16
+		ldrb	r6, [r0]
+		orr	r5, r5, r6, lsl #24
+		ldrb	r6, [r0]
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #8
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #16
+		ldrb	ip, [r0]
+		orr	r6, r6, ip, lsl #24
+		stmia	r1!, {r3 - r6}
+		subs	r2, r2, #16
+		bpl	.insb_16_lp
+
+		tst	r2, #15
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.insb_no_16:	tst	r2, #8
+		beq	.insb_no_8
+
+		ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		ldrb	r4, [r0]
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #8
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrb	r5, [r0]
+		orr	r4, r4, r5, lsl #24
+		stmia	r1!, {r3, r4}
+
+.insb_no_8:	tst	r2, #4
+		bne	.insb_no_4
+
+		ldrb	r3, [r0]
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #8
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrb	r4, [r0]
+		orr	r3, r3, r4, lsl #24
+		str	r3, [r1], #4
+
+.insb_no_4:	ands	r2, r2, #3
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+		cmp	r2, #2
+		ldrb	r3, [r0]
+		strb	r3, [r1], #1
+		ldrgeb	r3, [r0]
+		strgeb	r3, [r1], #1
+		ldrgtb	r3, [r0]
+		strgtb	r3, [r1]
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+.outsb_align:	rsb	ip, ip, #4
+		cmp	ip, r2
+		mov	ip, r2
+		cmp	ip, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0]
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0]
+		ldrgtb	r3, [r1], #1
+		strgtb	r3, [r0]
+		subs	r2, r2, ip
+		bne	.outsb_aligned
+
+ENTRY(outsb)
+		teq	r2, #0
+		moveq	pc, lr
+		addr	r0
+		ands	ip, r1, #3
+		bne	.outsb_align
+
+.outsb_aligned:	stmfd	sp!, {r4 - r6, lr}
+
+		subs	r2, r2, #16
+		bmi	.outsb_no_16
+
+.outsb_16_lp:	ldmia	r1!, {r3 - r6}
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+		mov	r5, r5, lsr #8
+		strb	r5, [r0]
+
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		mov	r6, r6, lsr #8
+		strb	r6, [r0]
+		subs	r2, r2, #16
+		bpl	.outsb_16_lp
+
+		tst	r2, #15
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.outsb_no_16:	tst	r2, #8
+		beq	.outsb_no_8
+
+		ldmia	r1, {r3, r4}
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+		mov	r4, r4, lsr #8
+		strb	r4, [r0]
+
+.outsb_no_8:	tst	r2, #4
+		bne	.outsb_no_4
+
+		ldr	r3, [r1], #4
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+		mov	r3, r3, lsr #8
+		strb	r3, [r0]
+
+.outsb_no_4:	ands	r2, r2, #3
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+		cmp	r2, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0]
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0]
+		ldrgtb	r3, [r1]
+		strgtb	r3, [r0]
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+
+@ Purpose: write a memc register
+@ Proto  : void memc_write(int register, int value);
+@ Returns: nothing
+
+#if defined(CONFIG_CPU_26)
+ENTRY(memc_write)
+		cmp	r0, #7
+		RETINSTR(movgt,pc,lr)
+		mov	r0, r0, lsl #17
+		mov	r1, r1, lsl #15
+		mov	r1, r1, lsr #17
+		orr	r0, r0, r1, lsl #2
+		add	r0, r0, #0x03600000
+		strb	r0, [r0]
+		RETINSTR(mov,pc,lr)
+#define CPSR2SPSR(rt)
+#else
+#define CPSR2SPSR(rt) \
+		mrs	rt, cpsr; \
+		msr	spsr, rt
+#endif
+
+@ Purpose: call an expansion card loader to read bytes.
+@ Proto  : char read_loader(int offset, char *card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_read)
+		stmfd	sp!, {r4 - r12, lr}
+		mov	r11, r1
+		mov	r1, r0
+		CPSR2SPSR(r0)
+		mov	lr, pc
+		mov	pc, r2
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+@ Purpose: call an expansion card loader to reset the card
+@ Proto  : void read_loader(int card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_reset)
+		stmfd	sp!, {r4 - r12, lr}
+		mov	r11, r0
+		CPSR2SPSR(r0)
+		mov	lr, pc
+		add	pc, r1, #8
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+
+#if 0
 		mov	r2, r2, lsl#1
-ENTRY(inswb)
 		mov	ip, sp
 		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
 		sub	fp, ip, #4
@@ -122,14 +586,9 @@
 		bgt	Linsw_notaligned
 		LOADREGS(ea, fp, {r4 - r10, fp, sp, pc})
 
-@ Purpose: write a block of data from memory to a hardware register.
-@ Proto  : outsw(int to_reg, void *from, int len_in_words);
-@ Proto  : outswb(int to_reg, void *from, int len_in_bytes);
-@ Notes  : increments from
 
 ENTRY(outsw)
-		mov	r2, r2, LSL#1
-ENTRY(outswb)
+		mov	r2, r2, lsl#1
 		mov	ip, sp
 		stmfd	sp!, {r4 - r8, fp, ip, lr, pc}
 		sub	fp, ip, #4
@@ -166,56 +625,5 @@
 		bgt	3b
 		LOADREGS(ea, fp, {r4 - r8, fp, sp, pc})
 
-/*
- * These make no sense on Acorn machines atm.
- */
-ENTRY(insl)
-ENTRY(outsl)
-		RETINSTR(mov,pc,lr)
-
-@ Purpose: write a memc register
-@ Proto  : void memc_write(int register, int value);
-@ Returns: nothing
-
-#if defined(CONFIG_CPU_26)
-ENTRY(memc_write)
-		cmp	r0, #7
-		RETINSTR(movgt,pc,lr)
-		mov	r0, r0, lsl #17
-		mov	r1, r1, lsl #15
-		mov	r1, r1, lsr #17
-		orr	r0, r0, r1, lsl #2
-		add	r0, r0, #0x03600000
-		strb	r0, [r0]
-		RETINSTR(mov,pc,lr)
-#define CPSR2SPSR(rt)
-#else
-#define CPSR2SPSR(rt) \
-		mrs	rt, cpsr; \
-		msr	spsr, rt
 #endif
 
-@ Purpose: call an expansion card loader to read bytes.
-@ Proto  : char read_loader(int offset, char *card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_read)
-		stmfd	sp!, {r4 - r12, lr}
-		mov	r11, r1
-		mov	r1, r0
-		CPSR2SPSR(r0)
-		mov	lr, pc
-		mov	pc, r2
-		LOADREGS(fd, sp!, {r4 - r12, pc})
-
-@ Purpose: call an expansion card loader to reset the card
-@ Proto  : void read_loader(int card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_reset)
-		stmfd	sp!, {r4 - r12, lr}
-		mov	r11, r0
-		CPSR2SPSR(r0)
-		mov	lr, pc
-		add	pc, r1, #8
-		LOADREGS(fd, sp!, {r4 - r12, pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/io-ebsa110.S linux.18p17/arch/arm/lib/io-ebsa110.S
--- linux.vanilla/arch/arm/lib/io-ebsa110.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/io-ebsa110.S	Fri Sep 15 23:28:37 2000
@@ -22,6 +22,22 @@
 		ldr	lr, [r0]			;\
 		orr	reg, reg, lr, lsl $16
 
+/*
+ * These make no sense on these machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+ENTRY(insb)
+ENTRY(outsb)
+		adr	r0, io_long_warning
+		mov	r1, lr
+		b	SYMBOL_NAME(printk)
+
+io_long_warning:
+		.ascii	"<4>ins?/outs? not implemented on this architecture\0"
+		.align
+
 @ Purpose: read a block of data from a hardware register to memory.
 @ Proto  : insw(int from_port, void *to, int len_in_words);
 @ Proto  : inswb(int from_port, void *to, int len_in_bytes);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/io-ebsa285.S linux.18p17/arch/arm/lib/io-ebsa285.S
--- linux.vanilla/arch/arm/lib/io-ebsa285.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/io-ebsa285.S	Thu Jan  1 01:00:00 1970
@@ -1,197 +0,0 @@
-#include <linux/linkage.h>
-
-ENTRY(insl)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-		ands	ip, r1, #3
-		bne	2f
-
-1:		ldr	r3, [r0]
-		str	r3, [r1], #4
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-2:		cmp	ip, #2
-		ldr	ip, [r0]
-		blt	3f
-		bgt	4f
-
-		strh	ip, [r1], #2
-		mov	ip, ip, lsr #16
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #16
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #16
-		bne	1b
-		strh	ip, [r1], #2
-		mov	pc, lr
-
-3:		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-		strh	ip, [r1], #2
-		mov	ip, ip, lsr #16
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #8
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #24
-		bne	1b
-		strb	ip, [r1], #1
-		mov	pc, lr
-
-4:		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-1:		subs	r2, r2, #1
-		ldrne	r3, [r0]
-		orrne	ip, ip, r3, lsl #24
-		strne	ip, [r1], #4
-		movne	ip, r3, lsr #8
-		bne	1b
-		strb	ip, [r1], #1
-		mov	ip, ip, lsr #8
-		strh	ip, [r1], #2
-		mov	pc, lr
-
-ENTRY(outsl)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-		ands	ip, r1, #3
-		bne	2f
-
-1:		ldr	r3, [r1], #4
-		str	r3, [r0]
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-2:		bic	r1, r1, #3
-		cmp	ip, #2
-		ldr	ip, [r1], #4
-		mov	ip, ip, lsr #16
-		blt	3f
-		bgt	4f
-
-1:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #16
-		str	ip, [r0]
-		mov	ip, r3, lsr #16
-		subs	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-3:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #8
-		str	ip, [r0]
-		mov	ip, r3, lsr #24
-		subs	r2, r2, #1
-		bne	3b
-		mov	pc, lr
-
-4:		ldr	r3, [r1], #4
-		orr	ip, ip, r3, lsl #24
-		str	ip, [r0]
-		mov	ip, r3, lsr #8
-		subs	r2, r2, #1
-		bne	4b
-		mov	pc, lr
-
-		/* Nobody could say these are optimal, but not to worry. */
-
-ENTRY(outswb)
-		mov	r2, r2, lsr #1
-ENTRY(outsw)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		subs	r2, r2, #1
-		ldrgeh	r3, [r1], #2
-		strgeh	r3, [r0]
-		bgt	1b
-		mov	pc, lr
-
-ENTRY(inswb)
-		mov	r2, r2, lsr #1
-ENTRY(insw)
-		stmfd	sp!, {r4, r5, lr}
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-						@ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
-		subs	ip, r2, #8
-		blo	too_little
-						@ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
-		ands	lr, r1, #3		@ check alignment
-		beq	1f
-
-		ldrh	r3, [r0]
-		strh	r3, [r1], #2
-		sub	ip, ip, #1
-		cmn	ip, #8
-		blo	too_little
-
-1:		ldrh	r2, [r0]
-		ldrh	r3, [r0]
-		orr	r2, r2, r3, lsl #16
-		ldrh	r3, [r0]
-		ldrh	r4, [r0]
-		orr	r3, r3, r4, lsl #16
-		ldrh	r4, [r0]
-		ldrh	r5, [r0]
-		orr	r4, r4, r5, lsl #16
-		ldrh	r5, [r0]
-		ldrh	lr, [r0]
-		orr	r5, r5, lr, lsl #16
-		stmia	r1!, {r2, r3, r4, r5}
-		subs	ip, ip, #8
-						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
-		bhs	1b
-						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
-		cmn	ip, #4
-		ldrhsh	r2, [r0]		@ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
-		ldrhsh	r3, [r0]
-		orrhs	r2, r2, r3, lsl #16
-		ldrhsh	r3, [r0]
-		ldrhsh	r4, [r0]
-		orrhs	r3, r3, r4, lsl #16
-		stmhsia	r1!, {r2, r3}
-
-		tst	ip, #2
-		ldrneh	r2, [r0]		@ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
-		ldrneh	r3, [r0]
-		orrne	r2, r2, r3, lsl #16
-		strne	r2, [r1], #4
-
-		tst	ip, #1
-		ldrneh	r2, [r0]
-		strneh	r2, [r1], #2
-
-		ldmfd	sp!, {r4, r5, pc}
-
-too_little:	subs	r2, r2, #1
-		ldrgeh	r3, [r0]
-		strgeh	r3, [r1], #2
-		bgt	too_little
-
-		ldmfd	sp!, {r4, r5, pc}
-
-
-ENTRY(insb)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		teq	r2, #0
-		ldrneb	r3, [r0]
-		strneb	r3, [r1], #1
-		subne	r2, r2, #1
-		bne	1b
-		mov	pc, lr
-
-
-ENTRY(outsb)
-		add	r0, r0, #0xff000000
-		add	r0, r0, #0x00e00000
-1:		teq	r2, #0
-		ldrneb	r3, [r1], #1
-		strneb	r3, [r0]
-		subne	r2, r2, #1
-		bne	1b
-		mov	pc, lr
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/io-footbridge.S linux.18p17/arch/arm/lib/io-footbridge.S
--- linux.vanilla/arch/arm/lib/io-footbridge.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/io-footbridge.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,202 @@
+#include <linux/linkage.h>
+#include <asm/hardware.h>
+
+		.equ	pcio_high, PCIO_BASE & 0xff000000
+		.equ	pcio_low,  PCIO_BASE & 0x00ffffff
+
+		.macro	ioaddr, rd,rn
+		add	\rd, \rn, #pcio_high
+		.if	pcio_low
+		add	\rd, \rd, #pcio_low
+		.endif
+		.endm
+
+ENTRY(insl)
+		ioaddr	r0, r0
+		ands	ip, r1, #3
+		bne	2f
+
+1:		ldr	r3, [r0]
+		str	r3, [r1], #4
+		subs	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+2:		cmp	ip, #2
+		ldr	ip, [r0]
+		blt	4f
+		bgt	6f
+
+		strh	ip, [r1], #2
+		mov	ip, ip, lsr #16
+3:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #16
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #16
+		bne	3b
+		strh	ip, [r1], #2
+		mov	pc, lr
+
+4:		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+		strh	ip, [r1], #2
+		mov	ip, ip, lsr #16
+5:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #8
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #24
+		bne	5b
+		strb	ip, [r1], #1
+		mov	pc, lr
+
+6:		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+7:		subs	r2, r2, #1
+		ldrne	r3, [r0]
+		orrne	ip, ip, r3, lsl #24
+		strne	ip, [r1], #4
+		movne	ip, r3, lsr #8
+		bne	7b
+		strb	ip, [r1], #1
+		mov	ip, ip, lsr #8
+		strh	ip, [r1], #2
+		mov	pc, lr
+
+ENTRY(outsl)
+		ioaddr	r0, r0
+		ands	ip, r1, #3
+		bne	2f
+
+1:		ldr	r3, [r1], #4
+		str	r3, [r0]
+		subs	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+2:		bic	r1, r1, #3
+		cmp	ip, #2
+		ldr	ip, [r1], #4
+		mov	ip, ip, lsr #16
+		blt	4f
+		bgt	5f
+
+3:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #16
+		str	ip, [r0]
+		mov	ip, r3, lsr #16
+		subs	r2, r2, #1
+		bne	3b
+		mov	pc, lr
+
+4:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #8
+		str	ip, [r0]
+		mov	ip, r3, lsr #24
+		subs	r2, r2, #1
+		bne	4b
+		mov	pc, lr
+
+5:		ldr	r3, [r1], #4
+		orr	ip, ip, r3, lsl #24
+		str	ip, [r0]
+		mov	ip, r3, lsr #8
+		subs	r2, r2, #1
+		bne	5b
+		mov	pc, lr
+
+		/* Nobody could say these are optimal, but not to worry. */
+
+ENTRY(outswb)
+		mov	r2, r2, lsr #1
+ENTRY(outsw)
+		ioaddr	r0, r0
+1:		subs	r2, r2, #1
+		ldrgeh	r3, [r1], #2
+		strgeh	r3, [r0]
+		bgt	1b
+		mov	pc, lr
+
+ENTRY(inswb)
+		mov	r2, r2, lsr #1
+ENTRY(insw)
+		stmfd	sp!, {r4, r5, lr}
+		ioaddr	r0, r0
+						@ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
+		subs	ip, r2, #8
+		blo	too_little
+						@ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
+		ands	lr, r1, #3		@ check alignment
+		beq	1f
+
+		ldrh	r3, [r0]
+		strh	r3, [r1], #2
+		sub	ip, ip, #1
+		cmn	ip, #8
+		blo	too_little
+
+1:		ldrh	r2, [r0]
+		ldrh	r3, [r0]
+		orr	r2, r2, r3, lsl #16
+		ldrh	r3, [r0]
+		ldrh	r4, [r0]
+		orr	r3, r3, r4, lsl #16
+		ldrh	r4, [r0]
+		ldrh	r5, [r0]
+		orr	r4, r4, r5, lsl #16
+		ldrh	r5, [r0]
+		ldrh	lr, [r0]
+		orr	r5, r5, lr, lsl #16
+		stmia	r1!, {r2, r3, r4, r5}
+		subs	ip, ip, #8
+						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
+		bhs	1b
+						@ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
+		cmn	ip, #4
+		ldrhsh	r2, [r0]		@ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
+		ldrhsh	r3, [r0]
+		orrhs	r2, r2, r3, lsl #16
+		ldrhsh	r3, [r0]
+		ldrhsh	r4, [r0]
+		orrhs	r3, r3, r4, lsl #16
+		stmhsia	r1!, {r2, r3}
+
+		tst	ip, #2
+		ldrneh	r2, [r0]		@ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
+		ldrneh	r3, [r0]
+		orrne	r2, r2, r3, lsl #16
+		strne	r2, [r1], #4
+
+		tst	ip, #1
+		ldrneh	r2, [r0]
+		strneh	r2, [r1], #2
+
+		ldmfd	sp!, {r4, r5, pc}
+
+too_little:	subs	r2, r2, #1
+		ldrgeh	r3, [r0]
+		strgeh	r3, [r1], #2
+		bgt	too_little
+
+		ldmfd	sp!, {r4, r5, pc}
+
+
+ENTRY(insb)
+		ioaddr	r0, r0
+1:		teq	r2, #0
+		ldrneb	r3, [r0]
+		strneb	r3, [r1], #1
+		subne	r2, r2, #1
+		bne	1b
+		mov	pc, lr
+
+
+ENTRY(outsb)
+		ioaddr	r0, r0
+1:		teq	r2, #0
+		ldrneb	r3, [r1], #1
+		strneb	r3, [r0]
+		subne	r2, r2, #1
+		bne	1b
+		mov	pc, lr
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/io.c linux.18p17/arch/arm/lib/io.c
--- linux.vanilla/arch/arm/lib/io.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/io.c	Fri Sep 15 23:28:37 2000
@@ -1,3 +1,5 @@
+#include <linux/module.h>
+
 #include <asm/io.h>
 
 /*
@@ -18,7 +20,7 @@
  * Copy data from "real" memory space to IO memory space.
  * This needs to be optimized.
  */
-void _memcpy_toio(unsigned long to, void * from, unsigned long count)
+void _memcpy_toio(unsigned long to, const void * from, unsigned long count)
 {
 	while (count) {
 		count--;
@@ -40,3 +42,7 @@
 		dst++;
 	}
 }
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/ll_char_wr.S linux.18p17/arch/arm/lib/ll_char_wr.S
--- linux.vanilla/arch/arm/lib/ll_char_wr.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/ll_char_wr.S	Thu Jan  1 01:00:00 1970
@@ -1,158 +0,0 @@
-/*
- * linux/arch/arm/lib/ll_char_wr.S
- *
- * Copyright (C) 1995, 1996 Russell King.
- *
- * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
- *
- * 10-04-96	RMK	Various cleanups & reduced register usage.
- * 08-04-98	RMK	Shifts re-ordered
- */
-
-@ Regs: [] = corruptible
-@       {} = used
-@       () = do not use
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-		.text
-
-#define BOLD            0x01
-#define ITALIC          0x02
-#define UNDERLINE       0x04
-#define FLASH           0x08
-#define INVERSE         0x10
-
-LC0:		.word	SYMBOL_NAME(bytes_per_char_h)
-		.word	SYMBOL_NAME(video_size_row)
-		.word	SYMBOL_NAME(cmap_80)
-		.word	SYMBOL_NAME(con_charconvtable)
-
-ENTRY(ll_write_char)
-		stmfd	sp!, {r4 - r7, lr}
-@
-@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
-		eor	ip, r1, #UNDERLINE << 9
-/*
- * calculate colours
- */
-		tst	r1, #INVERSE << 9
-		moveq	r2, r1, lsr #16
-		moveq	r3, r1, lsr #24
-		movne	r2, r1, lsr #24
-		movne	r3, r1, lsr #16
-		and	r3, r3, #255
-		and	r2, r2, #255
-/*
- * calculate offset into character table
- */
-		mov	r1, r1, lsl #23
-		mov	r1, r1, lsr #20
-/*
- * calculate offset required for each row [maybe I should make this an argument to this fn.
- * Have to see what the register usage is like in the calling routines.
- */
-		adr	r4, LC0
-		ldmia	r4, {r4, r5, r6, lr}
-		ldr	r4, [r4]
-		ldr	r5, [r5]
-/*
- * Go to resolution-dependent routine...
- */
-		cmp	r4, #4
-		blt	Lrow1bpp
-		eor	r2, r3, r2			@ Create eor mask to change colour from bg
-		orr	r3, r3, r3, lsl #8		@ to fg.
-		orr	r3, r3, r3, lsl #16
-		add	r0, r0, r5, lsl #3		@ Move to bottom of character
-		add	r1, r1, #7
-		ldrb	r7, [r6, r1]
-		tst	ip, #UNDERLINE << 9
-		eoreq	r7, r7, #255
-		teq	r4, #8
-		beq	Lrow8bpplp
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
-		orr	r3, r3, r3, lsl #4
-Lrow4bpplp:	ldr	r7, [lr, r7, lsl #2]
-		mul	r7, r2, r7
-		tst	r1, #7				@ avoid using r7 directly after
-		eor	ip, r3, r7
-		str	ip, [r0, -r5]!
-		LOADREGS(eqfd, sp!, {r4 - r7, pc})
-		sub	r1, r1, #1
-		ldrb	r7, [r6, r1]
-		ldr	r7, [lr, r7, lsl #2]
-		mul	r7, r2, r7
-		tst	r1, #7				@ avoid using r7 directly after
-		eor	ip, r3, r7
-		str	ip, [r0, -r5]!
-		subne	r1, r1, #1
-		ldrneb	r7, [r6, r1]
-		bne	Lrow4bpplp
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
-Lrow8bpplp:	mov	ip, r7, lsr #4
-		ldr	ip, [lr, ip, lsl #2]
-		mul	r4, r2, ip
-		and	ip, r7, #15			@ avoid r4
-		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
-		mul	ip, r2, ip			@ avoid r4
-		eor	r4, r3, r4			@ avoid ip
-		tst	r1, #7				@ avoid ip
-		sub	r0, r0, r5			@ avoid ip
-		eor	ip, r3, ip
-		stmia	r0, {r4, ip}
-		LOADREGS(eqfd, sp!, {r4 - r7, pc})
-		sub	r1, r1, #1
-		ldrb	r7, [r6, r1]
-		mov	ip, r7, lsr #4
-		ldr	ip, [lr, ip, lsl #2]
-		mul	r4, r2, ip
-		and	ip, r7, #15			@ avoid r4
-		ldr	ip, [lr, ip, lsl #2]		@ avoid r4
-		mul	ip, r2, ip			@ avoid r4
-		eor	r4, r3, r4			@ avoid ip
-		tst	r1, #7				@ avoid ip
-		sub	r0, r0, r5			@ avoid ip
-		eor	ip, r3, ip
-		stmia	r0, {r4, ip}
-		subne	r1, r1, #1
-		ldrneb	r7, [r6, r1]
-		bne	Lrow8bpplp
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
-Lrow1bpp:	add	r6, r6, r1
-		ldmia	r6, {r4, r7}
-		tst	ip, #INVERSE << 9
-		mvnne	r4, r4
-		mvnne	r7, r7
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		mov	r4, r4, lsr #8
-		strb	r4, [r0], r5
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		strb	r7, [r0], r5
-		mov	r7, r7, lsr #8
-		tst	ip, #UNDERLINE << 9
-		mvneq	r7, r7
-		strb	r7, [r0], r5
-		LOADREGS(fd, sp!, {r4 - r7, pc})
-
-		.bss
-ENTRY(con_charconvtable)
-		.space	1024
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/loaders.S linux.18p17/arch/arm/lib/loaders.S
--- linux.vanilla/arch/arm/lib/loaders.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/loaders.S	Thu Jan  1 01:00:00 1970
@@ -1,53 +0,0 @@
-/*
- * linux/arch/arm/lib/loaders.S
- *
- * This file contains the ROM loaders for buggy cards
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-
-/*
- * Oak SCSI
- */
-
-ENTRY(oak_scsi_loader)
-			b	Loak_scsi_read
-			.word	0
-Loak_scsi_reset:	bic	r10, r11, #0x00ff0000
-			ldr	r2, [r10]
-			RETINSTR(mov,pc,lr)
-
-Loak_scsi_read:		mov	r2, r1, lsr #3
-			and	r2, r2, #15 << 9
-			bic	r10, r11, #0x00ff0000
-			ldr	r2, [r10, r2]
-			mov	r2, r1, lsl #20
-			ldrb	r0, [r11, r2, lsr #18]
-			ldr	r2, [r10]
-			RETINSTR(mov,pc,lr)
-
-ENTRY(atomwide_serial_loader)
-			b	Latomwide_serial_read
-			.word	0
-Latomwide_serial_reset:	mov	r2, #0x3c00
-			strb	r2, [r11, r2]
-			RETINSTR(mov,pc,lr)
-
-Latomwide_serial_read:	cmp	r1, #0x8000
-			RETINSTR(movhi,pc,lr)
-			add	r0, r1, #0x800
-			mov	r0, r0, lsr #11
-			mov	r3, #0x3c00
-			strb	r0, [r11, r3]
-			mov	r2, r1, lsl #21
-			ldrb	r0, [r11, r2, lsr #19]
-			strb	r2, [r11, r3]
-			RETINSTR(mov,pc,lr)
-
-/*
- * Cards we don't know about yet
- */
-ENTRY(noloader)
-			mov	r0, r0
-			mov	r0, #0
-			RETINSTR(mov,pc,lr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/memchr.S linux.18p17/arch/arm/lib/memchr.S
--- linux.vanilla/arch/arm/lib/memchr.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/memchr.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,33 @@
+/*
+ * linux/arch/arm/lib/memchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+@ I have this hunch that this one can be optimised further.
+@ Beware false optimisation of ldrb the byte BEYOND the end of buffer (could
+@ be beyond end of memory) before testing to see if length had been exhausted
+@ on the previous byte.
+ENTRY(memchr)				@ 2nd argument char
+		stmfd	sp!, {lr}
+		and	r1, r1, #0xff	@ cast 2nd argument to char
+		add	r2, r2, r0	@ Point to end (exclusive)
+
+1:		teq	r0, r2
+		beq	2f		@ No more to do
+		ldrb	r3, [r0], #1
+		teq	r3, r1
+		bne	1b
+		sub	r0, r0, #1
+		LOADREGS(fd, sp!, {pc})
+
+2:		mov	r0, #0		@ Got to end and not found.
+		LOADREGS(fd, sp!, {pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/memcpy.S linux.18p17/arch/arm/lib/memcpy.S
--- linux.vanilla/arch/arm/lib/memcpy.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/memcpy.S	Fri Sep 15 23:28:37 2000
@@ -1,20 +1,17 @@
 /*
- * linux/arch/arm/lib/segment.S
+ * linux/arch/arm/lib/memcpy.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
  *
- * Copyright (C) 1995, 1996 Russell King
- * Except memcpy/memmove routine.
  */
-
-#include <asm/assembler.h>
 #include <linux/linkage.h>
-
-#ifndef ENTRY
-#define ENTRY(x...)				\
-		.globl	_##x;			\
-_##x:
-#endif
+#include <asm/assembler.h>
+#include "constants.h"
 
 		.text
+
 #define ENTER	\
 		mov	ip,sp	;\
 		stmfd	sp!,{r4-r9,fp,ip,lr,pc}	;\
@@ -26,9 +23,10 @@
 #define EXITEQ	\
 		LOADREGS(eqea, fp, {r4 - r9, fp, sp, pc})
 
-# Prototype: void memcpy(void *to,const void *from,unsigned long n);
-# ARM3: cant use memcopy here!!!
-
+/*
+ * Prototype: void memcpy(void *to,const void *from,unsigned long n);
+ * ARM3: cant use memcopy here!!!
+ */
 ENTRY(memcpy)
 ENTRY(memmove)
 		ENTER
@@ -316,4 +314,5 @@
 		b	24b
 
 		.align
+
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/memset.S linux.18p17/arch/arm/lib/memset.S
--- linux.vanilla/arch/arm/lib/memset.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/memset.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,88 @@
+/*
+ * linux/arch/arm/lib/memset.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+		.align	5
+ENTRY(memset)
+		mov	r3, r0
+		cmp	r2, #16
+		blt	6f
+		ands	ip, r3, #3
+		beq	1f
+		cmp	ip, #2
+		strltb	r1, [r3], #1			@ Align destination
+		strleb	r1, [r3], #1
+		strb	r1, [r3], #1
+		rsb	ip, ip, #4
+		sub	r2, r2, ip
+1:		orr	r1, r1, r1, lsl #8
+		orr	r1, r1, r1, lsl #16
+		cmp	r2, #256
+		blt	4f
+		stmfd	sp!, {r4, r5, lr}
+		mov	r4, r1
+		mov	r5, r1
+		mov	lr, r1
+		mov	ip, r2, lsr #6
+		sub	r2, r2, ip, lsl #6
+2:		stmia	r3!, {r1, r4, r5, lr}		@ 64 bytes at a time.
+		stmia	r3!, {r1, r4, r5, lr}
+		stmia	r3!, {r1, r4, r5, lr}
+		stmia	r3!, {r1, r4, r5, lr}
+		subs	ip, ip, #1
+		bne	2b
+		teq	r2, #0
+		LOADREGS(eqfd, sp!, {r4, r5, pc})	@ Now <64 bytes to go.
+		tst	r2, #32
+		stmneia	r3!, {r1, r4, r5, lr}
+		stmneia	r3!, {r1, r4, r5, lr}
+		tst	r2, #16
+		stmneia	r3!, {r1, r4, r5, lr}
+		ldmia	sp!, {r4, r5}
+3:		tst	r2, #8
+		stmneia	r3!, {r1, lr}
+		tst	r2, #4
+		strne	r1, [r3], #4
+		tst	r2, #2
+		strneb	r1, [r3], #1
+		strneb	r1, [r3], #1
+		tst	r2, #1
+		strneb	r1, [r3], #1
+		LOADREGS(fd, sp!, {pc})
+
+4:		movs	ip, r2, lsr #3
+		beq	3b
+		sub	r2, r2, ip, lsl #3
+		stmfd	sp!, {lr}
+		mov	lr, r1
+		subs	ip, ip, #4
+5:		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		stmgeia	r3!, {r1, lr}
+		subges	ip, ip, #4
+		bge	5b
+		tst	ip, #2
+		stmneia	r3!, {r1, lr}
+		stmneia	r3!, {r1, lr}
+		tst	ip, #1
+		stmneia	r3!, {r1, lr}
+		teq	r2, #0
+		LOADREGS(eqfd, sp!, {pc})
+		b	3b
+
+6:		subs	r2, r2, #1
+		strgeb	r1, [r3], #1
+		bgt	6b
+		RETINSTR(mov, pc, lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/memzero.S linux.18p17/arch/arm/lib/memzero.S
--- linux.vanilla/arch/arm/lib/memzero.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/memzero.S	Mon Oct  2 10:10:30 2000
@@ -0,0 +1,80 @@
+/*
+ *  linux/arch/arm/lib/memzero.S
+ *
+ *  Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+	.text
+	.align	5
+	.word	0
+/*
+ * Align the pointer in r0.  r3 contains the number of bytes that we are
+ * mis-aligned by, and r1 is the number of bytes.  If r1 < 4, then we
+ * don't bother; we use byte stores instead.
+ */
+1:	subs	r1, r1, #4		@ 1 do we have enough
+	blt	5f			@ 1 bytes to align with?
+	cmp	r3, #2			@ 1
+	strltb	r2, [r0], #1		@ 1
+	strleb	r2, [r0], #1		@ 1
+	strb	r2, [r0], #1		@ 1
+	add	r1, r1, r3		@ 1 (r1 = r1 - (4 - r3))
+/*
+ * The pointer is now aligned and the length is adjusted.  Try doing the
+ * memzero again.
+ */
+
+ENTRY(__memzero)
+	mov	r2, #0			@ 1
+	ands	r3, r0, #3		@ 1 unaligned?
+	bne	1b			@ 1
+/*
+ * r3 = 0, and we know that the pointer in r0 is aligned to a word boundary.
+ */
+	cmp	r1, #16			@ 1 we can skip this chunk if we
+	blt	4f			@ 1 have < 16 bytes
+/*
+ * We need an extra register for this loop - save the return address and
+ * use the LR
+ */
+	str	lr, [sp, #-4]!		@ 1
+	mov	ip, r2			@ 1
+	mov	lr, r2			@ 1
+
+3:	subs	r1, r1, #64		@ 1 write 32 bytes out per loop
+	stmgeia	r0!, {r2, r3, ip, lr}	@ 4
+	stmgeia	r0!, {r2, r3, ip, lr}	@ 4
+	stmgeia	r0!, {r2, r3, ip, lr}	@ 4
+	stmgeia	r0!, {r2, r3, ip, lr}	@ 4
+	bgt	3b			@ 1
+	LOADREGS(eqfd, sp!, {pc})	@ 1/2 quick exit
+/*
+ * No need to correct the count; we're only testing bits from now on
+ */
+	tst	r1, #32			@ 1
+	stmneia	r0!, {r2, r3, ip, lr}	@ 4
+	stmneia	r0!, {r2, r3, ip, lr}	@ 4
+	tst	r1, #16			@ 1 16 bytes or more?
+	stmneia	r0!, {r2, r3, ip, lr}	@ 4
+	ldr	lr, [sp], #4		@ 1
+
+4:	tst	r1, #8			@ 1 8 bytes or more?
+	stmneia	r0!, {r2, r3}		@ 2
+	tst	r1, #4			@ 1 4 bytes or more?
+	strne	r2, [r0], #4		@ 1
+/*
+ * When we get here, we've got less than 4 bytes to zero.  We
+ * may have an unaligned pointer as well.
+ */
+5:	tst	r1, #2			@ 1 2 bytes or more?
+	strneb	r2, [r0], #1		@ 1
+	strneb	r2, [r0], #1		@ 1
+	tst	r1, #1			@ 1 a byte left over
+	strneb	r2, [r0], #1		@ 1
+	RETINSTR(mov,pc,lr)		@ 1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/semaphore.S linux.18p17/arch/arm/lib/semaphore.S
--- linux.vanilla/arch/arm/lib/semaphore.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/semaphore.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,34 @@
+/*
+ *  linux/arch/arm/lib/semaphore.S
+ *
+ *  Idea from i386 code, Copyright Linus Torvalds.
+ *  Converted for ARM by Russell King
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * The semaphore operations have a special calling sequence
+ * that allows us to keep the distruption of the main code
+ * path to a minimum.  These routines save and restore the
+ * registers that will be touched by __down etc.
+ */
+ENTRY(__down_failed)
+	stmfd	sp!, {r0 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down)
+	LOADREGS(fd, sp!, {r0 - r3, ip, pc})
+
+ENTRY(__down_interruptible_failed)
+	stmfd	sp!, {r1 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down_interruptible)
+	LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__down_trylock_failed)
+	stmfd	sp!, {r1 - r3, ip, lr}
+	bl	SYMBOL_NAME(__down_trylock)
+	LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__up_wakeup)
+	stmfd	sp!, {r0 - r3, ip, lr}
+	bl	SYMBOL_NAME(__up)
+	LOADREGS(fd, sp!, {r0 - r3, ip, pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/setbit.S linux.18p17/arch/arm/lib/setbit.S
--- linux.vanilla/arch/arm/lib/setbit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/setbit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/lib/setbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+@ Purpose  : Function to set a bit
+@ Prototype: int set_bit(int bit,int *addr)
+
+ENTRY(set_bit)
+                and     r2, r0, #7
+                mov     r3, #1
+                mov     r3, r3, lsl r2
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1, r0, lsr #3]
+		orr	r2, r2, r3
+		strb	r2, [r1, r0, lsr #3]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/strchr.S linux.18p17/arch/arm/lib/strchr.S
--- linux.vanilla/arch/arm/lib/strchr.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/strchr.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/strchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+ENTRY(strchr)
+		ldrb	r2, [r0], #1
+		stmfd	sp!,{lr}	@ StrongARM: LDR not followed by use
+1:		teq	r2, r1
+		teqne	r2, #0
+		ldrneb	r2, [r0], #1
+		bne	1b
+		teq	r2, r1		@ If they are the same we found char.
+		movne	r0, #0
+		subeq	r0, r0, #1	@ Even if we were looking for 0 :-)
+		LOADREGS(fd, sp!, {pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/string.S linux.18p17/arch/arm/lib/string.S
--- linux.vanilla/arch/arm/lib/string.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/string.S	Thu Jan  1 01:00:00 1970
@@ -1,170 +0,0 @@
-/*
- * linux/arch/arm/lib/string.S
- *
- * Copyright (C) 1995-1998 Russell King
- *
- * This is commonly used to clear the frame buffer and the frame
- * backing buffer.  As such, it will be rarely called with r2 < 32.
- *
- * Optimisations by Matthew Wilcox
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-		.text
-# Prototype: char *strrchr(const char *s,char c);
-
-/*
- * Prototype: void memzero(void *d, size_t n)
- */
-ENTRY(memzero)
-		mov	r2, r1
-		mov	r1, #0
-/*
- * Prototype: void memsetl(unsigned long *d, unsigned long c, size_t n)
- */
-ENTRY(memsetl)
-		teq	r2, #0
-		RETINSTR(moveq,pc,lr)
-		stmfd	sp!, {lr}
-		mov	lr, r1
-		mov	r3, r1
-		mov	ip, r1
-
-		@ r2 = {32 ... 4}
-
-1:		subs	r2, r2, #32
-		stmgeia	r0!, {r1, r3, ip, lr}
-		stmgeia	r0!, {r1, r3, ip, lr}
-		bgt	1b
-		LOADREGS(eqfd, sp!, {pc})
-
-		@ r2 can be {-4 ... -28}
-
-		cmp	r2, #-16
-		stmgeia	r0!, {r1, r3, ip, lr}
-		addlts	r2, r2, #16
-		LOADREGS(eqfd, sp!, {pc})
-
-		@ r2 can be {-4 ... -12}
-
-		cmp	r2, #-8
-		stmgeia	r0!, {r1, r3}
-		strne	r1, [r0]
-		LOADREGS(fd, sp!, {pc})
-
-		.global	__page_memcpy
-__page_memcpy:	stmfd	sp!, {r4, r5, lr}
-1:		subs	r2, r2, #4*8
-		ldmgeia	r1!, {r3, r4, r5, ip}
-		stmgeia	r0!, {r3, r4, r5, ip}
-		ldmgeia	r1!, {r3, r4, r5, ip}
-		stmgeia	r0!, {r3, r4, r5, ip}
-		bgt	1b
-		LOADREGS(fd, sp!, {r4, r5, pc})
-
-		.global	memset
-memset:		mov	r3, r0
-		cmp	r2, #16
-		blt	6f
-		ands	ip, r3, #3
-		beq	1f
-		cmp	ip, #2
-		strltb	r1, [r3], #1			@ Align destination
-		strleb	r1, [r3], #1
-		strb	r1, [r3], #1
-		rsb	ip, ip, #4
-		sub	r2, r2, ip
-1:		orr	r1, r1, r1, lsl #8
-		orr	r1, r1, r1, lsl #16
-		cmp	r2, #256
-		blt	4f
-		stmfd	sp!, {r4, r5, lr}
-		mov	r4, r1
-		mov	r5, r1
-		mov	lr, r1
-		mov	ip, r2, lsr #6
-		sub	r2, r2, ip, lsl #6
-2:		stmia	r3!, {r1, r4, r5, lr}		@ 64 bytes at a time.
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		stmia	r3!, {r1, r4, r5, lr}
-		subs	ip, ip, #1
-		bne	2b
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {r4, r5, pc})	@ Now <64 bytes to go.
-		tst	r2, #32
-		stmneia	r3!, {r1, r4, r5, lr}
-		stmneia	r3!, {r1, r4, r5, lr}
-		tst	r2, #16
-		stmneia	r3!, {r1, r4, r5, lr}
-		ldmia	sp!, {r4, r5}
-3:		tst	r2, #8
-		stmneia	r3!, {r1, lr}
-		tst	r2, #4
-		strne	r1, [r3], #4
-		tst	r2, #2
-		strneb	r1, [r3], #1
-		strneb	r1, [r3], #1
-		tst	r2, #1
-		strneb	r1, [r3], #1
-		LOADREGS(fd, sp!, {pc})
-
-4:		movs	ip, r2, lsr #3
-		beq	3b
-		sub	r2, r2, ip, lsl #3
-		stmfd	sp!, {lr}
-		mov	lr, r1
-		subs	ip, ip, #4
-5:		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		stmgeia	r3!, {r1, lr}
-		subges	ip, ip, #4
-		bge	5b
-		tst	ip, #2
-		stmneia	r3!, {r1, lr}
-		stmneia	r3!, {r1, lr}
-		tst	ip, #1
-		stmneia	r3!, {r1, lr}
-		teq	r2, #0
-		LOADREGS(eqfd, sp!, {pc})
-		b	3b
-
-6:		subs	r2, r2, #1
-		strgeb	r1, [r3], #1
-		bgt	6b
-		RETINSTR(mov, pc, lr)
-
-ENTRY(strrchr)
-		stmfd	sp!, {lr}
-		mov	r3, #0
-1:		ldrb	r2, [r0], #1
-		teq	r2, r1
-		moveq	r3, r0
-		teq	r2, #0
-		bne	1b
-		mov	r0, r3
-		LOADREGS(fd, sp!, {pc})
-
-ENTRY(strchr)
-		stmfd	sp!,{lr}
-		mov	r3, #0
-1:		ldrb	r2, [r0], #1
-		teq	r2, r1
-		teqne	r2, #0
-		bne	1b
-		teq	r2, #0
-		moveq	r0, #0
-		subne	r0, r0, #1
-		LOADREGS(fd, sp!, {pc})
-
-ENTRY(memchr)
-		stmfd	sp!, {lr}
-1:		ldrb	r3, [r0], #1
-		teq	r3, r1
-		beq	2f
-		subs	r2, r2, #1
-		bpl	1b
-2:		movne	r0, #0
-		subeq	r0, r0, #1
-		LOADREGS(fd, sp!, {pc})
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/strrchr.S linux.18p17/arch/arm/lib/strrchr.S
--- linux.vanilla/arch/arm/lib/strrchr.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/strrchr.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,29 @@
+/*
+ * linux/arch/arm/lib/strrchr.S
+ *
+ * Copyright (C) 1995-1999 Russell King
+ *
+ * ASM optimised string functions
+ *
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+		@ Technically this will fail a regression test that tries
+		@ strrchr (buffer, 0), by returning NULL rather than
+		@ buffer + strlen (buffer). But even glibc doesnt test this
+ENTRY(strrchr)	@ 2nd argument is int not char
+		stmfd	sp!, {lr}
+		mov	r3, r0
+		ldrb	r2, [r3], #1
+		mov	r0, #0		@ prime return result.
+1:		teq	r2, r1		@ Found one?
+		subeq	r0, r3, #1
+		teq	r2, #0
+		ldrneb	r2, [r3], #1	@ Assume that well be looping at least
+		bne	1b		@ twice, so put LDM outside loop.
+		LOADREGS(fd, sp!, {pc})
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/testchangebit.S linux.18p17/arch/arm/lib/testchangebit.S
--- linux.vanilla/arch/arm/lib/testchangebit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/testchangebit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testchangebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_change_bit)
+		add	r1, r1, r0, lsr #3
+		and	r3, r0, #7
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		eor	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/testclearbit.S linux.18p17/arch/arm/lib/testclearbit.S
--- linux.vanilla/arch/arm/lib/testclearbit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/testclearbit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testclearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_clear_bit)
+		add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7	@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		bic	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/testsetbit.S linux.18p17/arch/arm/lib/testsetbit.S
--- linux.vanilla/arch/arm/lib/testsetbit.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/lib/testsetbit.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/testsetbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+                .text
+
+ENTRY(test_and_set_bit)
+		add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7		@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS(ip)
+		DISABLEIRQS(ip)
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		orr	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS(ip)
+		RETINSTR(mov,pc,lr)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/uaccess-armo.S linux.18p17/arch/arm/lib/uaccess-armo.S
--- linux.vanilla/arch/arm/lib/uaccess-armo.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/uaccess-armo.S	Fri Sep 15 23:28:37 2000
@@ -30,7 +30,7 @@
 		.word	__arch_copy_to_user
 		.word	__arch_clear_user
 		.word	__arch_strncpy_from_user
-		.word	__arch_strlen_user
+		.word	__arch_strnlen_user
 
 
 @ In : r0 = x, r1 = addr, r2 = error
@@ -99,7 +99,7 @@
 		.word	uaccess_kernel_copy
 		.word	uaccess_kernel_clear
 		.word	uaccess_kernel_strncpy_from
-		.word	uaccess_kernel_strlen
+		.word	uaccess_kernel_strnlen
 
 @ In : r0 = x, r1 = addr, r2 = error
 @ Out: r2 = error
@@ -214,17 +214,20 @@
 2:		subs	r0, ip, r2
 		ldmfd	sp!, {pc}^
 
-/* Prototype: int uaccess_kernel_strlen(char *str)
+/* Prototype: int uaccess_kernel_strnlen(char *str, long n)
  * Purpose  : get length of a string in kernel memory
  * Params   : str - address of string in kernel memory
  * Returns  : length of string *including terminator*, or zero on error
  */
-uaccess_kernel_strlen:
+uaccess_kernel_strnlen:
 		stmfd	sp!, {lr}
 		mov	r2, r0
-1:		ldrb	r1, [r0], #1
-		teq	r1, #0
+1:		ldrb	r3, [r0], #1
+		teq	r3, #0
+		beq	2f
+		subs	r1, r1, #1
 		bne	1b
-		sub	r0, r0, r2
+		add	r0, r0, #1
+2:		sub	r0, r0, r2
 		ldmfd	sp!, {pc}^
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/lib/uaccess.S linux.18p17/arch/arm/lib/uaccess.S
--- linux.vanilla/arch/arm/lib/uaccess.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/lib/uaccess.S	Fri Sep 15 23:28:37 2000
@@ -42,11 +42,11 @@
 		rsb	ip, ip, #4
 		cmp	ip, #2
 		ldrb	r3, [r1], #1
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrgeb	r3, [r1], #1
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #1
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		sub	r2, r2, ip
 		b	.c2u_dest_aligned
 
@@ -69,8 +69,8 @@
 		addmi	ip, r2, #4
 		bmi	.c2u_0nowords
 		ldr	r3, [r1], #4
-USER(		strt	r3, [r0], #4)			// May fault
-		mov	ip, r0, lsl #32 - PAGE_SHIFT	// On each page, use a ld/st??t instruction
+USER(		strt	r3, [r0], #4)			@ May fault
+		mov	ip, r0, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
 		beq	.c2u_0fupi
@@ -84,31 +84,31 @@
 		blt	.c2u_0rem8lp
 
 .c2u_0cpy8lp:	ldmia	r1!, {r3 - r6}
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		ldmia	r1!, {r3 - r6}
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #32
 		bpl	.c2u_0cpy8lp
 .c2u_0rem8lp:	cmn	ip, #16
 		ldmgeia	r1!, {r3 - r6}
-		stmgeia	r0!, {r3 - r6}			// Shouldn't fault
+		stmgeia	r0!, {r3 - r6}			@ Shouldn't fault
 		tst	ip, #8
 		ldmneia	r1!, {r3 - r4}
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		ldrne	r3, [r1], #4
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_0fupi
 .c2u_0nowords:	teq	ip, #0
 		beq	.c2u_finished
 .c2u_nowords:	cmp	ip, #2
 		ldrb	r3, [r1], #1
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrgeb	r3, [r1], #1
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #1
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_not_enough:
@@ -129,7 +129,7 @@
 		mov	r3, r7, lsr #8
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #24
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -149,7 +149,7 @@
 		orr	r5, r5, r6, lsl #24
 		mov	r6, r6, lsr #8
 		orr	r6, r6, r7, lsl #24
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_1cpy8lp
 .c2u_1rem8lp:	tst	ip, #8
@@ -158,23 +158,23 @@
 		orrne	r3, r3, r4, lsl #24
 		movne	r4, r4, lsr #8
 		orrne	r4, r4, r7, lsl #24
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #8
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #24
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_1fupi
 .c2u_1nowords:	mov	r3, r7, lsr #8
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		movge	r3, r3, lsr #8
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		movgt	r3, r3, lsr #8
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_2fupi:	subs	r2, r2, #4
@@ -183,7 +183,7 @@
 		mov	r3, r7, lsr #16
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #16
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -203,7 +203,7 @@
 		orr	r5, r5, r6, lsl #16
 		mov	r6, r6, lsr #16
 		orr	r6, r6, r7, lsl #16
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_2cpy8lp
 .c2u_2rem8lp:	tst	ip, #8
@@ -212,23 +212,23 @@
 		orrne	r3, r3, r4, lsl #16
 		movne	r4, r4, lsr #16
 		orrne	r4, r4, r7, lsl #16
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #16
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #16
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_2fupi
 .c2u_2nowords:	mov	r3, r7, lsr #16
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		movge	r3, r3, lsr #8
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		ldrgtb	r3, [r1], #0
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 .c2u_3fupi:	subs	r2, r2, #4
@@ -237,7 +237,7 @@
 		mov	r3, r7, lsr #24
 		ldr	r7, [r1], #4
 		orr	r3, r3, r7, lsl #8
-USER(		strt	r3, [r0], #4)			// May fault
+USER(		strt	r3, [r0], #4)			@ May fault
 		mov	ip, r0, lsl #32 - PAGE_SHIFT
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
@@ -257,7 +257,7 @@
 		orr	r5, r5, r6, lsl #8
 		mov	r6, r6, lsr #24
 		orr	r6, r6, r7, lsl #8
-		stmia	r0!, {r3 - r6}			// Shouldn't fault
+		stmia	r0!, {r3 - r6}			@ Shouldn't fault
 		subs	ip, ip, #16
 		bpl	.c2u_3cpy8lp
 .c2u_3rem8lp:	tst	ip, #8
@@ -266,23 +266,23 @@
 		orrne	r3, r3, r4, lsl #8
 		movne	r4, r4, lsr #24
 		orrne	r4, r4, r7, lsl #8
-		stmneia	r0!, {r3 - r4}			// Shouldn't fault
+		stmneia	r0!, {r3 - r4}			@ Shouldn't fault
 		tst	ip, #4
 		movne	r3, r7, lsr #24
 		ldrne	r7, [r1], #4
 		orrne	r3, r3, r7, lsl #8
-		strnet	r3, [r0], #4			// Shouldn't fault
+		strnet	r3, [r0], #4			@ Shouldn't fault
 		ands	ip, ip, #3
 		beq	.c2u_3fupi
 .c2u_3nowords:	mov	r3, r7, lsr #24
 		teq	ip, #0
 		beq	.c2u_finished
 		cmp	ip, #2
-USER(		strbt	r3, [r0], #1)			// May fault
+USER(		strbt	r3, [r0], #1)			@ May fault
 		ldrge	r3, [r1], #0
-USER(		strgebt	r3, [r0], #1)			// May fault
+USER(		strgebt	r3, [r0], #1)			@ May fault
 		movgt	r3, r3, lsr #8
-USER(		strgtbt	r3, [r0], #1)			// May fault
+USER(		strgtbt	r3, [r0], #1)			@ May fault
 		b	.c2u_finished
 
 #ifndef TESTING
@@ -302,11 +302,11 @@
 .cfu_dest_not_aligned:
 		rsb	ip, ip, #4
 		cmp	ip, #2
-USER(		ldrbt	r3, [r1], #1)			// May fault
+USER(		ldrbt	r3, [r1], #1)			@ May fault
 		strb	r3, [r0], #1
-USER(		ldrgebt	r3, [r1], #1)			// May fault
+USER(		ldrgebt	r3, [r1], #1)			@ May fault
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #1)			// May fault
+USER(		ldrgtbt	r3, [r1], #1)			@ May fault
 		strgtb	r3, [r0], #1
 		sub	r2, r2, ip
 		b	.cfu_dest_aligned
@@ -330,7 +330,7 @@
 		bmi	.cfu_0nowords
 USER(		ldrt	r3, [r1], #4)
 		str	r3, [r0], #4
-		mov	ip, r1, lsl #32 - PAGE_SHIFT	// On each page, use a ld/st??t instruction
+		mov	ip, r1, lsl #32 - PAGE_SHIFT	@ On each page, use a ld/st??t instruction
 		rsb	ip, ip, #0
 		movs	ip, ip, lsr #32 - PAGE_SHIFT
 		beq	.cfu_0fupi
@@ -343,31 +343,31 @@
 		subs	ip, ip, #32
 		blt	.cfu_0rem8lp
 
-.cfu_0cpy8lp:	ldmia	r1!, {r3 - r6}			// Shouldn't fault
+.cfu_0cpy8lp:	ldmia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmia	r0!, {r3 - r6}
-		ldmia	r1!, {r3 - r6}			// Shouldn't fault
+		ldmia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmia	r0!, {r3 - r6}
 		subs	ip, ip, #32
 		bpl	.cfu_0cpy8lp
 .cfu_0rem8lp:	cmn	ip, #16
-		ldmgeia	r1!, {r3 - r6}			// Shouldn't fault
+		ldmgeia	r1!, {r3 - r6}			@ Shouldn't fault
 		stmgeia	r0!, {r3 - r6}
 		tst	ip, #8
-		ldmneia	r1!, {r3 - r4}			// Shouldn't fault
+		ldmneia	r1!, {r3 - r4}			@ Shouldn't fault
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
-		ldrnet	r3, [r1], #4			// Shouldn't fault
+		ldrnet	r3, [r1], #4			@ Shouldn't fault
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
 		beq	.cfu_0fupi
 .cfu_0nowords:	teq	ip, #0
 		beq	.cfu_finished
 .cfu_nowords:	cmp	ip, #2
-USER(		ldrbt	r3, [r1], #1)			// May fault
+USER(		ldrbt	r3, [r1], #1)			@ May fault
 		strb	r3, [r0], #1
-USER(		ldrgebt	r3, [r1], #1)			// May fault
+USER(		ldrgebt	r3, [r1], #1)			@ May fault
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #1)			// May fault
+USER(		ldrgtbt	r3, [r1], #1)			@ May fault
 		strgtb	r3, [r0], #1
 		b	.cfu_finished
 
@@ -380,7 +380,7 @@
 
 .cfu_src_not_aligned:
 		bic	r1, r1, #3
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		cmp	ip, #2
 		bgt	.cfu_3fupi
 		beq	.cfu_2fupi
@@ -388,7 +388,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_1nowords
 		mov	r3, r7, lsr #8
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #24
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -402,7 +402,7 @@
 		blt	.cfu_1rem8lp
 
 .cfu_1cpy8lp:	mov	r3, r7, lsr #8
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #24
 		mov	r4, r4, lsr #8
 		orr	r4, r4, r5, lsl #24
@@ -415,14 +415,14 @@
 		bpl	.cfu_1cpy8lp
 .cfu_1rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #8
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #24
 		movne	r4, r4, lsr #8
 		orrne	r4, r4, r7, lsl #24
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #8
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #24
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -442,7 +442,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_2nowords
 		mov	r3, r7, lsr #16
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #16
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -456,7 +456,7 @@
 		blt	.cfu_2rem8lp
 
 .cfu_2cpy8lp:	mov	r3, r7, lsr #16
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #16
 		mov	r4, r4, lsr #16
 		orr	r4, r4, r5, lsl #16
@@ -469,14 +469,14 @@
 		bpl	.cfu_2cpy8lp
 .cfu_2rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #16
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #16
 		movne	r4, r4, lsr #16
 		orrne	r4, r4, r7, lsl #16
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #16
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #16
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -488,7 +488,7 @@
 		strb	r3, [r0], #1
 		movge	r3, r3, lsr #8
 		strgeb	r3, [r0], #1
-USER(		ldrgtbt	r3, [r1], #0)			// May fault
+USER(		ldrgtbt	r3, [r1], #0)			@ May fault
 		strgtb	r3, [r0], #1
 		b	.cfu_finished
 
@@ -496,7 +496,7 @@
 		addmi	ip, r2, #4
 		bmi	.cfu_3nowords
 		mov	r3, r7, lsr #24
-USER(		ldrt	r7, [r1], #4)			// May fault
+USER(		ldrt	r7, [r1], #4)			@ May fault
 		orr	r3, r3, r7, lsl #8
 		str	r3, [r0], #4
 		mov	ip, r1, lsl #32 - PAGE_SHIFT
@@ -510,7 +510,7 @@
 		blt	.cfu_3rem8lp
 
 .cfu_3cpy8lp:	mov	r3, r7, lsr #24
-		ldmia	r1!, {r4 - r7}			// Shouldn't fault
+		ldmia	r1!, {r4 - r7}			@ Shouldn't fault
 		orr	r3, r3, r4, lsl #8
 		mov	r4, r4, lsr #24
 		orr	r4, r4, r5, lsl #8
@@ -523,14 +523,14 @@
 		bpl	.cfu_3cpy8lp
 .cfu_3rem8lp:	tst	ip, #8
 		movne	r3, r7, lsr #24
-		ldmneia	r1!, {r4, r7}			// Shouldn't fault
+		ldmneia	r1!, {r4, r7}			@ Shouldn't fault
 		orrne	r3, r3, r4, lsl #8
 		movne	r4, r4, lsr #24
 		orrne	r4, r4, r7, lsl #8
 		stmneia	r0!, {r3 - r4}
 		tst	ip, #4
 		movne	r3, r7, lsr #24
-USER(		ldrnet	r7, [r1], #4)			// May fault
+USER(		ldrnet	r7, [r1], #4)			@ May fault
 		orrne	r3, r3, r7, lsl #8
 		strne	r3, [r0], #4
 		ands	ip, ip, #3
@@ -540,7 +540,7 @@
 		beq	.cfu_finished
 		cmp	ip, #2
 		strb	r3, [r0], #1
-USER(		ldrget	r3, [r1], #0)			// May fault
+USER(		ldrget	r3, [r1], #0)			@ May fault
 		strgeb	r3, [r0], #1
 		movgt	r3, r3, lsr #8
 		strgtb	r3, [r0], #1
@@ -553,7 +553,8 @@
 		   data was copied.  */
 9001:		ldr	r0, [sp], #4
 		ldr	r1, [sp]
-		bl	SYMBOL_NAME(memzero)
+		teq	r1, #0
+		blne	SYMBOL_NAME(__memzero)
 		LOADREGS(fd,sp!, {r0, r4 - r7, pc})
 		.previous
 #endif
@@ -597,19 +598,22 @@
 9001:		LOADREGS(fd,sp!, {r0, pc})
 		.previous
 
-/* Prototype: int __arch_strlen_user(char *str)
+/* Prototype: int __arch_strnlen_user(char *str, long n)
  * Purpose  : get length of a string in user memory
  * Params   : str - address of string in user memory
  * Returns  : length of string *including terminator*, or zero on error
  */
-ENTRY(__arch_strlen_user)
+ENTRY(__arch_strnlen_user)
 		stmfd	sp!, {lr}
 		mov	r2, r0
 1:
-USER(		ldrbt	r1, [r0], #1)
-		teq	r1, #0
+USER(		ldrbt	r3, [r0], #1)
+		teq	r3, #0
+		beq	2f
+		subs	r1, r1, #1
 		bne	1b
-		sub	r0, r0, r2
+		add	r0, r0, #1
+2:		sub	r0, r0, r2
 		LOADREGS(fd,sp!, {pc})
 
 		.section .fixup,"ax"
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/Makefile linux.18p17/arch/arm/mm/Makefile
--- linux.vanilla/arch/arm/mm/Makefile	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/Makefile	Fri Sep 15 23:28:37 2000
@@ -7,13 +7,11 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
-all:	lib first_rule
-
-O_TARGET := mm.o
-O_OBJS   := init.o extable.o fault-$(PROCESSOR).o small_page.o
+O_TARGET	:= mm.o
+O_OBJS		:= init.o extable.o fault-$(PROCESSOR).o small_page.o
 
 ifeq ($(PROCESSOR),armo)
- O_OBJS += proc-arm2,3.o
+ O_OBJS		+= proc-arm2,3.o
 endif
 
 ifeq ($(PROCESSOR),armv)
@@ -22,11 +20,8 @@
 
 include $(TOPDIR)/Rules.make
 
-%.o: %.S
-	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
-
-.PHONY:	lib
-lib:;	@$(MAKE) -C ../lib constants.h
+.S.o:
+	$(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -traditional -c -o $*.o $<
 
 # Special dependencies
 fault-armv.o: fault-common.c
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/fault-armo.c linux.18p17/arch/arm/mm/fault-armo.c
--- linux.vanilla/arch/arm/mm/fault-armo.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/fault-armo.c	Fri Sep 15 23:28:37 2000
@@ -23,7 +23,6 @@
 #define FAULT_CODE_FORCECOW	0x80
 #define FAULT_CODE_PREFETCH	0x04
 #define FAULT_CODE_WRITE	0x02
-#define FAULT_CODE_USER		0x01
 
 #define DO_COW(m)		((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW))
 #define READ_FAULT(m)		(!((m) & FAULT_CODE_WRITE))
@@ -96,11 +95,11 @@
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
 #if 0
-	if (the memc mapping for this page exists - can check now...) {
+	if (the memc mapping for this page exists) {
 		printk ("Page in, but got abort (undefined instruction?)\n");
 		return 0;
 	}
 #endif
-	do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_PREFETCH, regs);
+	do_page_fault(addr, FAULT_CODE_PREFETCH, regs);
 	return 1;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/fault-armv.c linux.18p17/arch/arm/mm/fault-armv.c
--- linux.vanilla/arch/arm/mm/fault-armv.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/fault-armv.c	Fri Sep 15 23:28:37 2000
@@ -25,7 +25,6 @@
 #include <asm/unaligned.h>
 
 #define FAULT_CODE_READ		0x02
-#define FAULT_CODE_USER		0x01
 
 #define DO_COW(m)		(!((m) & FAULT_CODE_READ))
 #define READ_FAULT(m)		((m) & FAULT_CODE_READ)
@@ -377,16 +376,30 @@
 
 #endif
 
+#define BUG_PROC_MSG \
+  "Weirdness detected (%08X)\n" \
+  "Please read http://www.arm.linux.org.uk/state.html for more information"
+
 asmlinkage void
 do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs)
 {
-	if (user_mode(regs))
-		error_code |= FAULT_CODE_USER;
+	if (user_mode(regs)) {
+		if (addr == regs->ARM_pc) {
+			static int first = 1;
+			if (first) {
+				/*
+				 * I want statistical information on this problem!
+				 */
+				printk(KERN_ERR BUG_PROC_MSG, fsr);
+				first = 0;
+			}
+			return;
+		}
+	}
 
 #define DIE(signr,nam)\
 		force_sig(signr, current);\
 		die(nam, regs, fsr);\
-		do_exit(signr);\
 		break
 
 	switch (fsr & 15) {
@@ -395,10 +408,8 @@
 	 */
 	case 0:
 		force_sig(SIGSEGV, current);
-		if (!user_mode(regs)) {
+		if (!user_mode(regs))
 			die("vector exception", regs, fsr);
-			do_exit(SIGSEGV);
-		}
 		break;
 
 	/*
@@ -415,10 +426,9 @@
 	 */
 	case 13:
 		force_sig(SIGSEGV, current);
-		if (!user_mode(regs)) {
+		if (!user_mode(regs))
 			die("section permission fault", regs, fsr);
-			do_exit(SIGSEGV);
-		} else {
+		else {
 #ifdef CONFIG_DEBUG_USER
 			printk("%s: permission fault on section, "
 			       "address=0x%08lx, code %d\n",
@@ -476,6 +486,6 @@
 asmlinkage int
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
-	do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_READ, regs);
+	do_page_fault(addr, FAULT_CODE_READ, regs);
 	return 1;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/fault-common.c linux.18p17/arch/arm/mm/fault-common.c
--- linux.vanilla/arch/arm/mm/fault-common.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/fault-common.c	Fri Sep 15 23:28:37 2000
@@ -26,25 +26,14 @@
 	set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE));
 }
 
-static void
-kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs,
-		  struct task_struct *tsk, struct mm_struct *mm)
+/*
+ * This is useful to dump out the page tables associated with
+ * 'addr' in mm 'mm'.
+ */
+void show_pte(struct mm_struct *mm, unsigned long addr)
 {
-	char *reason;
-	/*
-	 * Oops. The kernel tried to access some bad page. We'll have to
-	 * terminate things with extreme prejudice.
-	 */
 	pgd_t *pgd;
 
-	if (addr < PAGE_SIZE)
-		reason = "NULL pointer dereference";
-	else
-		reason = "paging request";
-
-	printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
-		reason, addr);
-	printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
 	pgd = pgd_offset(mm, addr);
 	printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd));
 
@@ -73,11 +62,34 @@
 
 		pte = pte_offset(pmd, addr);
 		printk(", *pte = %08lx", pte_val(*pte));
+#ifdef CONFIG_CPU_32
 		printk(", *ppte = %08lx", pte_val(pte[-PTRS_PER_PTE]));
+#endif
 	} while(0);
 
 	printk("\n");
-	die("Oops", regs, mode);
+}
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+static void
+kernel_page_fault(unsigned long addr, int write_access, struct pt_regs *regs,
+		  struct task_struct *tsk, struct mm_struct *mm)
+{
+	char *reason;
+
+	if (addr < PAGE_SIZE)
+		reason = "NULL pointer dereference";
+	else
+		reason = "paging request";
+
+	printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
+		reason, addr);
+	printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
+	show_pte(mm, addr);
+	die("Oops", regs, write_access);
 
 	do_exit(SIGKILL);
 }
@@ -88,6 +100,7 @@
 	struct mm_struct *mm;
 	struct vm_area_struct *vma;
 	unsigned long fixup;
+	int fault;
 
 	tsk = current;
 	mm  = tsk->mm;
@@ -126,8 +139,12 @@
 	 * make sure we exit gracefully rather than endlessly redo
 	 * the fault.
 	 */
-	if (!handle_mm_fault(tsk, vma, addr & PAGE_MASK, DO_COW(mode)))
+survive:
+	fault = handle_mm_fault(tsk, vma, addr & PAGE_MASK, DO_COW(mode));
+	if (!fault)
 		goto do_sigbus;
+	if (fault < 0)
+		goto out_of_memory;
 
 	up(&mm->mmap_sem);
 	return;
@@ -140,7 +157,7 @@
 	up(&mm->mmap_sem);
 
 	/* User mode accesses just cause a SIGSEGV */
-	if (mode & FAULT_CODE_USER) {
+	if (user_mode(regs)) {
 		tsk->tss.error_code = mode;
 		tsk->tss.trap_no = 14;
 #ifdef CONFIG_DEBUG_USER
@@ -165,6 +182,23 @@
 	kernel_page_fault(addr, mode, regs, tsk, mm);
 	return;
 
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+	if (tsk->pid == 1) {
+		tsk->policy |= SCHED_YIELD;
+		schedule();
+		goto survive;
+	}
+	up(&mm->mmap_sem);
+	if (user_mode(regs)) {
+		printk("VM: killing process %s\n", tsk->comm);
+		do_exit(SIGKILL);
+	}
+	goto no_context;
+
 do_sigbus:
 	/*
 	 * We ran out of memory, or some other thing happened to us that made
@@ -181,7 +215,7 @@
 	force_sig(SIGBUS, tsk);
 
 	/* Kernel mode? Handle exceptions or die */
-	if (!(mode & FAULT_CODE_USER))
+	if (!user_mode(regs))
 		goto no_context;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/init.c linux.18p17/arch/arm/mm/init.c
--- linux.vanilla/arch/arm/mm/init.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/init.c	Fri Sep 15 23:28:37 2000
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/arm/mm/init.c
  *
- *  Copyright (C) 1995, 1996  Russell King
+ *  Copyright (C) 1995-1999  Russell King
  */
 
 #include <linux/config.h>
@@ -248,7 +248,7 @@
 			  (unsigned long)(&__netwinder_end),
 			  "netwinder");
 
-	if (!machine_is_ebsa285() && !machine_is_cats())
+	if (!machine_is_ebsa285() && !machine_is_cats() && !machine_is_co285())
 		free_area((unsigned long)(&__ebsa285_begin),
 			  (unsigned long)(&__ebsa285_end),
 			  "ebsa285/cats");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/ioremap.c linux.18p17/arch/arm/mm/ioremap.c
--- linux.vanilla/arch/arm/mm/ioremap.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/ioremap.c	Fri Sep 15 23:28:37 2000
@@ -28,7 +28,11 @@
  * flush this - flushing the area causes the bus to lock.
  */
 
+#include <linux/errno.h>
 #include <linux/vmalloc.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/io.h>
 
 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
@@ -111,23 +115,23 @@
  * 'flags' are the extra L_PTE_ flags that you want to specify for this
  * mapping.  See include/asm-arm/proc-armv/pgtable.h for more information.
  */
-void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
+void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
 {
 	void * addr;
 	struct vm_struct * area;
-	unsigned long offset;
+	unsigned long offset, last_addr;
+
+	/* Don't allow wraparound or zero size */
+	last_addr = phys_addr + size - 1;
+	if (!size || last_addr < phys_addr)
+		return NULL;
 
 	/*
 	 * Mappings have to be page-aligned
 	 */
 	offset = phys_addr & ~PAGE_MASK;
-	size = PAGE_ALIGN(size + offset);
-
-	/*
-	 * Don't allow mappings that wrap..
-	 */
-	if (!size || size > phys_addr + size)
-		return NULL;
+	phys_addr &= PAGE_MASK;
+	size = PAGE_ALIGN(last_addr) - phys_addr;
 
 	/*
 	 * Ok, go for it..
@@ -143,7 +147,7 @@
 	return (void *) (offset + (char *)addr);
 }
 
-void iounmap(void *addr)
+void __iounmap(void *addr)
 {
-	return vfree((void *) (PAGE_MASK & (unsigned long) addr));
+	vfree((void *) (PAGE_MASK & (unsigned long) addr));
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/mm-armv.c linux.18p17/arch/arm/mm/mm-armv.c
--- linux.vanilla/arch/arm/mm/mm-armv.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/mm-armv.c	Fri Sep 15 23:28:37 2000
@@ -70,3 +70,27 @@
 
 	return start_mem;
 }
+
+/*
+ * In order to soft-boot, we need to insert a 1:1 mapping in place of
+ * the user-mode pages.  This will then ensure that we have predictable
+ * results when turning the mmu off
+ */
+void setup_mm_for_reboot(char mode)
+{
+	pgd_t *pgd;
+	pmd_t pmd;
+	int i;
+
+	if (current->mm && current->mm->pgd)
+		pgd = current->mm->pgd;
+	else
+		pgd = init_mm.pgd;
+
+	for (i = 0; i < USER_PTRS_PER_PGD; i++) {
+		pmd_val(pmd) = (i << PAGE_SHIFT) |
+			PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
+			PMD_TYPE_SECT;
+		set_pmd(pmd_offset(pgd + i, i << PGDIR_SHIFT), pmd);
+	}
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/proc-arm2,3.S linux.18p17/arch/arm/mm/proc-arm2,3.S
--- linux.vanilla/arch/arm/mm/proc-arm2,3.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/proc-arm2,3.S	Fri Sep 15 23:28:37 2000
@@ -202,21 +202,17 @@
 LC0:		.word	SYMBOL_NAME(page_nr)
 /*
  * Function: arm2_switch_to (struct task_struct *prev, struct task_struct *next)
- *
  * Params  : prev	Old task structure
  *	   : next	New task structure for process to run
- *
  * Returns : prev
- *
  * Purpose : Perform a task switch, saving the old processes state, and restoring
  *	     the new.
- *
  * Notes   : We don't fiddle with the FP registers here - we postpone this until
  *	     the new task actually uses FP.  This way, we don't swap FP for tasks
  *	     that do not require it.
  */
 _arm2_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
 		mov	r4, r1
@@ -235,7 +231,7 @@
 		strb	r6, [r6]
 		subs	r1, r1, #8
 		bhi	1b
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: arm2_remap_memc (struct task_struct *tsk)
@@ -316,15 +312,11 @@
 _arm2_proc_fin:	movs	pc, lr
 /*
  * Function: arm3_switch_to (struct task_struct *prev, struct task_struct *next)
- *
  * Params  : prev	Old task structure
  *	   : next	New task structure for process to run
- *
  * Returns : prev
- *
  * Purpose : Perform a task switch, saving the old processes state, and restoring
  *	     the new.
- *
  * Notes   : We don't fiddle with the FP registers here - we postpone this until
  *	     the new task actually uses FP.  This way, we don't swap FP for tasks
  *	     that do not require it.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/proc-arm6,7.S linux.18p17/arch/arm/mm/proc-arm6,7.S
--- linux.vanilla/arch/arm/mm/proc-arm6,7.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/proc-arm6,7.S	Fri Sep 15 23:28:37 2000
@@ -1,13 +1,15 @@
 /*
- * linux/arch/arm/mm/arm6.S: MMU functions for ARM6
+ * linux/arch/arm/mm/proc-arm6,7.S: MMU functions for ARM6
  *
- * (C) 1997 Russell King
+ * (C) 1997-1999 Russell King
  *
  * These are the low level assembler for performing cache and TLB
  * functions on the ARM6 & ARM7.
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/procinfo.h>
+#include <asm/errno.h>
 #include "../lib/constants.h"
 
 /*
@@ -68,24 +70,21 @@
  *	     that do not require it.
  */
 _arm6_7_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		mrs	ip, cpsr
 		stmfd	sp!, {ip}			@ Save cpsr_SVC
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
-		ldr	r2, [r1, #TSK_ADDR_LIMIT]
-		teq	r2, #0
-		moveq	r2, #DOM_KERNELDOMAIN
-		movne	r2, #DOM_USERDOMAIN
+		ldr	r2, [r1, #TSS_DOMAIN]
+		ldr	r3, [r1, #TSS_MEMMAP]		@ Page table pointer
 		mcr	p15, 0, r2, c3, c0		@ Set domain reg
-		ldr	r2, [r1, #TSS_MEMMAP]		@ Page table pointer
 		mov	r1, #0
 		mcr	p15, 0, r1, c7, c0, 0		@ flush cache
-		mcr	p15, 0, r2, c2, c0, 0		@ update page table ptr
+		mcr	p15, 0, r3, c2, c0, 0		@ update page table ptr
 		mcr	p15, 0, r1, c5, c0, 0		@ flush TLBs
 		ldmfd	sp!, {ip}
 		msr	spsr, ip			@ Save tasks CPSR into SPSR for this return
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: arm6_7_data_abort ()
@@ -109,40 +108,44 @@
 _arm6_data_abort:
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
-		and	r1, r4, #15 << 24
-		add	pc, pc, r1, lsr #22		@ Now branch to the relevent processing routine
-		movs	pc, lr
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_earlyldrpost		@ ldr	rd, [rn], #m
-		b	Ldata_simple			@ ldr	rd, [rn, #m]	@ RegVal
-		b	Ldata_earlyldrpost		@ ldr	rd, [rn], rm
-		b	Ldata_simple			@ ldr	rd, [rn, rm]
-		b	Ldata_ldmstm			@ ldm*a	rn, <rlist>
-		b	Ldata_ldmstm			@ ldm*b	rn, <rlist>
-		b	Ldata_unknown
-		b	Ldata_unknown
-		b	Ldata_simple			@ ldc	rd, [rn], #m	@ Same as ldr	rd, [rn], #m
-		b	Ldata_simple			@ ldc	rd, [rn, #m]
-		b	Ldata_unknown
-Ldata_unknown:	@ Part of jumptable
-		ldr	r3, [sp, #15 * 4]		@ Get PC
-		str	r3, [sp, #-4]!
-		mov	r1, r1, lsr #2
-		mov	r3, r4
-		mov	r2, r0
-		adr	r0, Lukabttxt
-		bl	SYMBOL_NAME(panic)
-Lstop:		b	Lstop
+		and	r1, r4, #14 << 24
+		and	r2, r2, #2			@ check read/write bit
+		teq	r1, #8 << 24
+		bne	Ldata_simple
+
+Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
+		beq	Ldata_simple
+		mov	r7, #0x11
+		orr	r7, r7, r7, lsl #8
+		and	r0, r4, r7
+		and	r1, r4, r7, lsl #1
+		add	r0, r0, r1, lsr #1
+		and	r1, r4, r7, lsl #2
+		add	r0, r0, r1, lsr #2
+		and	r1, r4, r7, lsl #3
+		add	r0, r0, r1, lsr #3
+		add	r0, r0, r0, lsr #8
+		add	r0, r0, r0, lsr #4
+		and	r7, r0, #15			@ r7 = no. of registers to transfer.
+		and	r5, r4, #15 << 16		@ Get Rn
+		ldr	r0, [sp, r5, lsr #14]		@ Get register
+		tst	r4, #1 << 23			@ U bit
+		subne	r7, r0, r7, lsl #2
+		addeq	r7, r0, r7, lsl #2		@ Do correction (signed)
+Ldata_saver7:	str	r7, [sp, r5, lsr #14]		@ Put register
+Ldata_simple:	mrc	p15, 0, r0, c6, c0, 0		@ get FAR
+		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
+		and	r1, r1, #255
+		mov	pc, lr
 
 _arm7_data_abort:
 		ldr	r4, [r0]			@ read instruction causing problem
 		mov	r2, r4, lsr #19			@ r2 b1 = L
 		and	r1, r4, #15 << 24
+		and	r2, r2, #2			@ check read/write bit
 		add	pc, pc, r1, lsr #22		@ Now branch to the relevent processing routine
 		movs	pc, lr
+
 		b	Ldata_unknown
 		b	Ldata_unknown
 		b	Ldata_unknown
@@ -158,123 +161,26 @@
 		b	Ldata_simple			@ ldc	rd, [rn], #m	@ Same as ldr	rd, [rn], #m
 		b	Ldata_simple			@ ldc	rd, [rn, #m]
 		b	Ldata_unknown
+Ldata_unknown:	@ Part of jumptable
 		b	Ldata_unknown
 
-Ldata_ldmstm:	tst	r4, #1 << 21			@ check writeback bit
+Ldata_lateldrpreconst:
+		tst	r4, #1 << 21			@ check writeback bit
 		beq	Ldata_simple
-
-		mov	r7, #0x11
-		orr	r7, r7, r7, lsl #8
-		and	r0, r4, r7
-		and	r1, r4, r7, lsl #1
-		add	r0, r0, r1, lsr #1
-		and	r1, r4, r7, lsl #2
-		add	r0, r0, r1, lsr #2
-		and	r1, r4, r7, lsl #3
-		add	r0, r0, r1, lsr #3
-		add	r0, r0, r0, lsr #8
-		add	r0, r0, r0, lsr #4
-		and	r7, r0, #15			@ r7 = no. of registers to transfer.
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]		@ Get register
-		eor	r6, r4, r4, lsl #2
-		tst	r6, #1 << 23			@ Check inc/dec ^ writeback
-		rsbeq	r7, r7, #0
-		add	r7, r0, r7, lsl #2		@ Do correction (signed)
-		str	r7, [sp, r5, lsr #14]		@ Put register
-
-Ldata_simple:	and	r2, r2, #2			@ check read/write bit
-		mrc	p15, 0, r0, c6, c0, 0		@ get FAR
-		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
-		and	r1, r1, #15
-		mov	pc, lr
-
-Ldata_earlyldrpost:
-		tst	r2, #4
-		and	r2, r2, #2			@ check read/write bit
-		orrne	r2, r2, #1			@ T bit
-		mrc	p15, 0, r0, c6, c0, 0		@ get FAR
-		mrc	p15, 0, r1, c5, c0, 0		@ get FSR
-		and	r1, r1, #15
-		mov	pc, lr
-
 Ldata_lateldrpostconst:
 		movs	r1, r4, lsl #20			@ Get offset
-		beq	Ldata_earlyldrpost		@ if offset is zero, no effect
-		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_earlyldrpost
-
-Ldata_lateldrpreconst:
-		tst	r4, #1 << 21			@ check writeback bit
-		movnes	r1, r4, lsl #20			@ Get offset
 		beq	Ldata_simple
 		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1, lsr #20
-		addeq	r0, r0, r1, lsr #20
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
-
-Ldata_lateldrpostreg:
-		and	r5, r4, #15
-		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
-		mov	r3, r4, lsr #7
-		ands	r3, r3, #31
-		and	r6, r4, #0x70
-		orreq	r6, r6, #8
-		add	pc, pc, r6
-		mov	r0, r0
-
-		mov	r1, r1, lsl r3			@ 0: LSL #!0
-		b	1f
-		b	1f				@ 1: LSL #0
-		mov	r0, r0
-		b	1f				@ 2: MUL?
-		mov	r0, r0
-		b	1f				@ 3: MUL?
-		mov	r0, r0
-		mov	r1, r1, lsr r3			@ 4: LSR #!0
-		b	1f
-		mov	r1, r1, lsr #32			@ 5: LSR #32
-		b	1f
-		b	1f				@ 6: MUL?
-		mov	r0, r0
-		b	1f				@ 7: MUL?
-		mov	r0, r0
-		mov	r1, r1, asr r3			@ 8: ASR #!0
-		b	1f
-		mov	r1, r1, asr #32			@ 9: ASR #32
-		b	1f
-		b	1f				@ A: MUL?
-		mov	r0, r0
-		b	1f				@ B: MUL?
-		mov	r0, r0
-		mov	r1, r1, ror r3			@ C: ROR #!0
-		b	1f
-		mov	r1, r1, rrx			@ D: RRX
-		b	1f
-		mov	r0, r0				@ E: MUL?
-		mov	r0, r0
-		mov	r0, r0				@ F: MUL?
-
-
-1:		and	r5, r4, #15 << 16		@ Get Rn
-		ldr	r0, [sp, r5, lsr #14]
-		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_earlyldrpost
+		subne	r7, r0, r1, lsr #20
+		addeq	r7, r0, r1, lsr #20
+		b	Ldata_saver7
 
 Ldata_lateldrprereg:
 		tst	r4, #1 << 21			@ check writeback bit
 		beq	Ldata_simple
+Ldata_lateldrpostreg:
 		and	r5, r4, #15
 		ldr	r1, [sp, r5, lsl #2]		@ Get Rm
 		mov	r3, r4, lsr #7
@@ -320,10 +226,9 @@
 1:		and	r5, r4, #15 << 16		@ Get Rn
 		ldr	r0, [sp, r5, lsr #14]
 		tst	r4, #1 << 23			@ U bit
-		subne	r0, r0, r1
-		addeq	r0, r0, r1
-		str	r0, [sp, r5, lsr #14]		@ Put register
-		b	Ldata_simple
+		subne	r7, r0, r1
+		addeq	r7, r0, r1
+		b	Ldata_saver7
 
 /*
  * Function: arm6_7_check_bugs (void)
@@ -336,8 +241,22 @@
 		mrs	ip, cpsr
 		bic	ip, ip, #F_BIT
 		msr	cpsr, ip
+		mov	pc, lr
+
 _arm6_7_proc_init:
+		mov	pc, lr
+
 _arm6_7_proc_fin:
+		mrs	r0, cpsr
+		orr	r0, r0, #F_BIT | I_BIT
+		msr	cpsr, r0
+		mov	r0, #0x31			@ ....S..DP...M
+		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
+		mov	pc, lr
+
+ENTRY(cpu_arm6_do_idle)
+ENTRY(cpu_arm7_do_idle)
+		mov	r0, #-EINVAL
 		mov	pc, lr
 
 /*
@@ -379,23 +298,22 @@
 _arm6_7_set_pte:
 		str	r1, [r0], #-1024		@ linux version
 
+		eor	r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY
+
 		bic	r2, r1, #0xff0
 		bic	r2, r2, #3
 		orr	r2, r2, #HPTE_TYPE_SMALL
 
-		tst	r1, #LPTE_USER | LPTE_EXEC
+		tst	r1, #LPTE_USER | LPTE_EXEC	@ User or Exec?
 		orrne	r2, r2, #HPTE_AP_READ
 
-		tst	r1, #LPTE_WRITE
-		tstne	r1, #LPTE_DIRTY
-		orrne	r2, r2, #HPTE_AP_WRITE
-
-		tst	r1, #LPTE_PRESENT
-		tstne	r1, #LPTE_YOUNG
-		moveq	r2, #0
+		tst	r1, #LPTE_WRITE | LPTE_DIRTY	@ Write and Dirty?
+		orreq	r2, r2, #HPTE_AP_WRITE
+
+		tst	r1, #LPTE_PRESENT | LPTE_YOUNG	@ Present and Young
+		movne	r2, #0
 
 		str	r2, [r0]			@ hardware version
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
 		mov	pc, lr
 
 /*
@@ -403,24 +321,53 @@
  *
  * Notes   : This sets up everything for a reset
  */
-_arm6_7_reset:	mrs	r1, cpsr
-		orr	r1, r1, #F_BIT|I_BIT
-		msr	cpsr, r1
-		mov	r0, #0
-		mcr	p15, 0, r0, c7, c0, 0		@ flush cache
-		mcr	p15, 0, r0, c5, c0, 0		@ flush TLB
-		mov	r1, #F_BIT | I_BIT | 3
+_arm6_7_reset:
+		mov	r1, #0
+		mcr	p15, 0, r1, c7, c0, 0		@ flush cache
+		mcr	p15, 0, r1, c5, c0, 0		@ flush TLB
+		mov	r1, #0x30
+		mcr	p15, 0, r1, c1, c0, 0		@ turn off MMU etc
+		mov	pc, r0
+
+cpu_armvlsi_name:
+		.asciz	"ARM/VLSI"
+cpu_arm6_name:	.asciz	"ARM 6"
+cpu_arm610_name:
+		.asciz	"ARM 610"
+cpu_arm7_name:	.asciz	"ARM 7"
+cpu_arm710_name:
+		.asciz	"ARM 710"
+		.align
+
+		.section ".text.init", #alloc, #execinstr
+
+__arm6_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c0		@ flush caches on v3
+		mcr	p15, 0, r0, c5, c0		@ flush TLBs on v3
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mov	r0, #0x3d			@ ....S..DPWC.M
+		orr	r0, r0, #0x100
+		mov	pc, lr
+
+__arm7_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c0		@ flush caches on v3
+		mcr	p15, 0, r0, c5, c0		@ flush TLBs on v3
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mov	r0, #0x7d			@ ....S.LDPWC.M
+		orr	r0, r0, #0x100
 		mov	pc, lr
 
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_arm6_name:	.ascii	"arm6\0"
-		.align
-
-ENTRY(arm6_processor_functions)
-		.word	_arm6_name			@  0
+		.type	arm6_processor_functions, #object
+arm6_processor_functions:
+		.word	cpu_arm6_name			@  0
 		.word	_arm6_7_switch_to		@  4
 		.word	_arm6_data_abort		@  8
 		.word	_arm6_7_check_bugs		@ 12
@@ -441,16 +388,17 @@
 
 		.word	_arm6_7_flush_cache		@ 68
 		.word	_arm6_7_flush_cache		@ 72
+		.word	0
+		.word	cpu_arm6_do_idle
+		.size	arm6_processor_functions, . - arm6_processor_functions
 
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_arm7_name:	.ascii	"arm7\0"
-		.align
-
-ENTRY(arm7_processor_functions)
-		.word	_arm7_name			@  0
+		.type	arm7_processor_functions, #object
+arm7_processor_functions:
+		.word	cpu_arm7_name			@  0
 		.word	_arm6_7_switch_to		@  4
 		.word	_arm7_data_abort		@  8
 		.word	_arm6_7_check_bugs		@ 12
@@ -471,3 +419,93 @@
 
 		.word	_arm6_7_flush_cache		@ 68
 		.word	_arm6_7_flush_cache		@ 72
+		.word	0
+		.word	cpu_arm7_do_idle
+		.size	arm7_processor_functions, . - arm7_processor_functions
+
+		.type	cpu_arm6_info, #object
+cpu_arm6_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm6_name
+		.size	cpu_arm6_info, . - cpu_arm6_info
+
+		.type	cpu_arm610_info, #object
+cpu_arm610_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm610_name
+		.size	cpu_arm610_info, . - cpu_Arm610_info
+
+		.type	cpu_arm7_info, #object
+cpu_arm7_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm7_name
+		.size	cpu_arm7_info, . - cpu_arm7_info
+
+		.type	cpu_arm710_info, #object
+cpu_arm710_info:
+		.long	cpu_armvlsi_name
+		.long	cpu_arm710_name
+		.size	cpu_arm710_info, . - cpu_arm710_info
+
+		.type	cpu_arch_name, #object
+cpu_arch_name:	.asciz	"armv3"
+		.size	cpu_arch_name, . - cpu_arch_name
+
+		.type	cpu_elf_name, #object
+cpu_elf_name:	.asciz	"v3"
+		.size	cpu_elf_name, . - cpu_elf_name
+		.align
+
+		.section ".proc.info", #alloc, #execinstr
+
+		.type   __arm6_proc_info, #object
+__arm6_proc_info:
+		.long	0x41560600
+		.long	0xfffffff0
+		.long	0x00000c12
+		b	__arm6_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm6_info
+		.long	arm6_processor_functions
+		.size	__arm6_proc_info, . - __arm6_proc_info
+
+		.type   __arm610_proc_info, #object
+__arm610_proc_info:
+		.long	0x41560610
+		.long	0xfffffff0
+		.long	0x00000c12
+		b	__arm6_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm610_info
+		.long	arm6_processor_functions
+		.size	__arm610_proc_info, . - __arm610_proc_info
+
+		.type   __arm7_proc_info, #object
+__arm7_proc_info:
+		.long	0x41007000
+		.long	0xffffff00
+		.long	0x00000c12
+		b	__arm7_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm7_info
+		.long	arm7_processor_functions
+		.size	__arm7_proc_info, . - __arm7_proc_info
+
+		.type   __arm710_proc_info, #object
+__arm710_proc_info:
+		.long	0x41007100
+		.long	0xfff8ff00
+		.long	0x00000c12
+		b	__arm7_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_26BIT
+		.long	cpu_arm710_info
+		.long	arm7_processor_functions
+		.size	__arm710_proc_info, . - __arm710_proc_info
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/proc-sa110.S linux.18p17/arch/arm/mm/proc-sa110.S
--- linux.vanilla/arch/arm/mm/proc-sa110.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/mm/proc-sa110.S	Fri Sep 15 23:28:37 2000
@@ -1,13 +1,14 @@
 /*
- * linux/arch/arm/mm/sa110.S: MMU functions for SA110
+ * linux/arch/arm/mm/proc-sa110.S: MMU functions for SA110
  *
- * (C) 1997 Russell King
+ * (C) 1997-1999 Russell King
  *
  * These are the low level assembler for performing cache and TLB
- * functions on the sa110.
+ * functions on the StrongARM-110.
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/procinfo.h>
 #include <asm/hardware.h>
 #include "../lib/constants.h"
 
@@ -15,6 +16,7 @@
  * is larger than this, then we flush the whole cache
  */
 #define MAX_AREA_SIZE	32768
+#define FLUSH_OFFSET	32768
 
 		.data
 Lclean_switch:	.long	0
@@ -29,12 +31,12 @@
 		mov	r2, #1
 _sa110_flush_cache_all_r2:
 		ldr	r3, =Lclean_switch
+		ldr	ip, =FLUSH_BASE
 		ldr	r1, [r3]
 		ands	r1, r1, #1
 		eor	r1, r1, #1
 		str	r1, [r3]
-		ldr	ip, =FLUSH_BASE
-		addne	ip, ip, #32768
+		addne	ip, ip, #FLUSH_OFFSET
 		add	r1, ip, #16384			@ only necessary for 16k
 1:		ldr	r3, [ip], #32
 		teq	r1, ip
@@ -152,16 +154,17 @@
 _sa110_flush_ram_page:
 		mov	r1, #4096
 1:		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
-		mcr	p15, 0, r0, c7, c6, 1		@ flush D entry
 		add	r0, r0, #32
 		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
-		mcr	p15, 0, r0, c7, c6, 1		@ flush D entry
 		add	r0, r0, #32
-		subs	r1, r1, #64
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		add	r0, r0, #32
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		add	r0, r0, #32
+		subs	r1, r1, #128
 		bne	1b
 		mov	r0, #0
 		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
-		mcr	p15, 0, r0, c7, c5, 0		@ flush I cache
 		mov	pc, lr
 
 /*
@@ -179,7 +182,7 @@
  * Function: sa110_flush_tlb_area (unsigned long address, unsigned long end, int flags)
  * Params  : address	Area start address
  *	   : end	Area end address
- *	   : flags	b0 = I cache as well
+ *	   : flags	b0 = I-TLB as well
  * Purpose : flush a TLB entry
  */
 		.align	5
@@ -193,7 +196,7 @@
 		mcrlt	p15, 0, r0, c8, c6, 1		@ flush D TLB entry
 		addlt	r0, r0, #4096
 		blt	1b
-		tst	r2, #1
+		teq	r2, #0
 		mcrne	p15, 0, r3, c8, c5, 0		@ flush I TLB
 		mov	pc, lr
 
@@ -220,18 +223,15 @@
  */
 		.align	5
 _sa110_switch_to:
-		stmfd	sp!, {r4 - r9, fp, lr}		@ Store most regs on stack
+		stmfd	sp!, {r4 - r10, fp, lr}		@ Store most regs on stack
 		mrs	ip, cpsr
 		stmfd	sp!, {ip}			@ Save cpsr_SVC
 		ldr	r2, [r0, #TSS_MEMMAP]		@ Get old page tables
 		str	sp, [r0, #TSS_SAVE]		@ Save sp_SVC
 		ldr	sp, [r1, #TSS_SAVE]		@ Get saved sp_SVC
-		ldr	r4, [r1, #TSK_ADDR_LIMIT]
-		teq	r4, #0
-		moveq	r4, #DOM_KERNELDOMAIN
-		movne	r4, #DOM_USERDOMAIN
-		mcr	p15, 0, r4, c3, c0		@ Set segment
+		ldr	r5, [r1, #TSS_DOMAIN]
 		ldr	r4, [r1, #TSS_MEMMAP]		@ Page table pointer
+		mcr	p15, 0, r5, c3, c0		@ Set segment
 /*
  * Flushing the cache is nightmarishly slow, so we take any excuse
  * to get out of it.  If the old page table is the same as the new,
@@ -247,7 +247,7 @@
 		eor	r2, r2, #1
 		str	r2, [r3]
 		ldr	r2, =FLUSH_BASE
-		addne	r2, r2, #32768
+		addne	r2, r2, #FLUSH_OFFSET
 		add	r1, r2, #16384			@ only necessary for 16k
 1:		ldr	r3, [r2], #32
 		teq	r1, r2
@@ -259,7 +259,7 @@
 		mcr	p15, 0, r1, c8, c7, 0		@ flush TLBs
 2:		ldmfd	sp!, {ip}
 		msr	spsr, ip			@ Save tasks CPSR into SPSR for this return
-		ldmfd	sp!, {r4 - r9, fp, pc}^		@ Load all regs saved previously
+		ldmfd	sp!, {r4 - r10, fp, pc}^	@ Load all regs saved previously
 
 /*
  * Function: sa110_data_abort ()
@@ -288,7 +288,8 @@
  */
 		.align	5
 _sa110_set_pmd:	str	r1, [r0]
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
 		mov	pc, lr
 
 /*
@@ -317,7 +318,8 @@
 
 		str	r2, [r0]			@ hardware version
 		mov	r0, r0
-		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry	 (drain is done by TLB fns)
+		mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
+		mcr	p15, 0, r0, c7, c10, 4		@ drain WB
 		mov	pc, lr
 
 /*
@@ -330,36 +332,83 @@
 		mrs	ip, cpsr
 		bic	ip, ip, #F_BIT
 		msr	cpsr, ip
+		mov	pc, lr
 
 _sa110_proc_init:
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c1, 2		@ Enable clock switching
+		mov	pc, lr
+
 _sa110_proc_fin:
+		stmfd	sp!, {r1, lr}
+		mrs	r0, cpsr
+		orr	r0, r0, #F_BIT | I_BIT
+		msr	cpsr, r0
+		bl	_sa110_flush_cache_all		@ clean caches
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c2, 2		@ Disable clock switching
+		mrc	p15, 0, r0, c1, c0, 0
+		bic	r0, r0, #0x1000			@ ...i............
+		bic	r0, r0, #0x000e			@ ............wca.
+		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
+		ldmfd	sp!, {r1, pc}
+
+		.align	5
+ENTRY(cpu_sa110_do_idle)
+		mov	r0, #0
+		mcr	p15, 0, r0, c15, c2, 2		@ Disable clock switching
+		ldr	r1, =FLUSH_BASE+FLUSH_OFFSET*2	@ load from uncacheable loc
+		ldr	r1, [r1, #0]
+		b	1f
+
+		.align	5
+1:		mcr	p15, 0, r0, c15, c8, 2		@ Wait for interrupt
+		mcr	p15, 0, r0, c15, c1, 2		@ Enable clock switching
 		mov	pc, lr
 
 /*
  * Function: sa110_reset
  * Notes   : This sets up everything for a reset
  */
-_sa110_reset:	mrs	r1, cpsr
-		orr	r1, r1, #F_BIT | I_BIT
-		msr	cpsr, r1
-		stmfd	sp!, {r1, lr}
-		mov	r2, #1
-		bl	_sa110_flush_cache_all
-		bl	_sa110_flush_tlb_all
+_sa110_reset:
+		mov	ip, #0
 		mcr	p15, 0, ip, c7, c7, 0		@ flush I,D caches
+		mcr	p15, 0, ip, c7, c10, 4		@ drain WB
+		mcr	p15, 0, ip, c8, c7, 0		@ flush I & D tlbs
 		mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
-		bic	r0, r0, #0x1800
-		bic	r0, r0, #0x000f
-		ldmfd	sp!, {r1, pc}
+		bic	r0, r0, #0x000f			@ ............wcam
+		bic	r0, r0, #0x1100			@ ...i...s........
+		mcr	p15, 0, ip, c1, c0, 0
+		mov	pc, r0
 /*
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-_sa110_name:	.ascii	"sa110\0"
+
+cpu_manu_name:	.asciz	"Intel"
+ENTRY(cpu_sa110_name)
+		.asciz	"StrongARM-110"
 		.align
 
-ENTRY(sa110_processor_functions)
-		.word	_sa110_name			@  0
+		.section ".text.init", #alloc, #execinstr
+
+__sa110_setup:	mov	r0, #0
+		mcr	p15, 0, r0, c7, c7		@ flush I,D caches on v4
+		mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer on v4
+		mcr	p15, 0, r0, c8, c7		@ flush I,D TLBs on v4
+		mcr	p15, 0, r4, c2, c0		@ load page table pointer
+		mov	r0, #0x1f			@ Domains 0, 1 = client
+		mcr	p15, 0, r0, c3, c0		@ load domain access register
+		mrc	p15, 0, r0, c1, c0		@ get control register v4
+		bic	r0, r0, #0x0e00			@ ....??r.........
+		bic	r0, r0, #0x0002			@ ..............a.
+		orr	r0, r0, #0x003d			@ ..........DPWC.M
+		orr	r0, r0, #0x1100			@ ...I...S........
+		mov	pc, lr
+
+		.type	sa110_processor_functions, #object
+sa110_processor_functions:
+		.word	cpu_sa110_name			@  0
 		.word	_sa110_switch_to		@  4
 		.word	_sa110_data_abort		@  8
 		.word	_sa110_check_bugs		@ 12
@@ -381,3 +430,38 @@
 
 		.word	_sa110_cache_wback_area		@ 68
 		.word	_sa110_cache_purge_area		@ 72
+		.word	0
+		.word	cpu_sa110_do_idle
+		.size	sa110_processor_functions, . - sa110_processor_functions
+
+		.type	cpu_sa110_info, #object
+cpu_sa110_info:
+		.long	cpu_manu_name
+		.long	cpu_sa110_name
+		.size	cpu_sa110_info, . - cpu_sa110_info
+
+		.type	cpu_arch_name, #object
+cpu_arch_name:	.asciz	"armv4"
+		.size	cpu_arch_name, . - cpu_arch_name
+
+		.type	cpu_elf_name, #object
+cpu_elf_name:	.asciz	"v4"
+		.size	cpu_elf_name, . - cpu_elf_name
+		.align
+
+		.section ".proc.info", #alloc, #execinstr
+
+		.type	__sa110_proc_info,#object
+__sa110_proc_info:
+		.long	0x4401a100
+		.long	0xfffffff0
+		.long	0x00000c02
+		b	__sa110_setup
+		.long	cpu_arch_name
+		.long	cpu_elf_name
+		.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT
+		.long	cpu_sa110_info
+		.long	sa110_processor_functions
+		.size	__sa110_proc_info, . - __sa110_proc_info
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/ARM-gcc.h linux.18p17/arch/arm/nwfpe/ARM-gcc.h
--- linux.vanilla/arch/arm/nwfpe/ARM-gcc.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/ARM-gcc.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,128 @@
+
+/*
+-------------------------------------------------------------------------------
+One of the macros `BIGENDIAN' or `LITTLEENDIAN' must be defined.
+-------------------------------------------------------------------------------
+*/
+#define LITTLEENDIAN
+
+/*
+-------------------------------------------------------------------------------
+The macro `BITS64' can be defined to indicate that 64-bit integer types are
+supported by the compiler.
+-------------------------------------------------------------------------------
+*/
+#define BITS64
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines the most convenient type that holds
+integers of at least as many bits as specified.  For example, `uint8' should
+be the most convenient type that can hold unsigned integers of as many as
+8 bits.  The `flag' type must be able to hold either a 0 or 1.  For most
+implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+to the same as `int'.
+-------------------------------------------------------------------------------
+*/
+typedef char flag;
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef int uint16;
+typedef int int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+#ifdef BITS64
+typedef unsigned long long int bits64;
+typedef signed long long int sbits64;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines a type that holds integers
+of _exactly_ the number of bits specified.  For instance, for most
+implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+`unsigned short int' and `signed short int' (or `short int'), respectively.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned char bits8;
+typedef signed char sbits8;
+typedef unsigned short int bits16;
+typedef signed short int sbits16;
+typedef unsigned int bits32;
+typedef signed int sbits32;
+#ifdef BITS64
+typedef unsigned long long int uint64;
+typedef signed long long int int64;
+#endif
+
+#ifdef BITS64
+/*
+-------------------------------------------------------------------------------
+The `LIT64' macro takes as its argument a textual integer literal and if
+necessary ``marks'' the literal as having a 64-bit integer type.  For
+example, the Gnu C Compiler (`gcc') requires that 64-bit literals be
+appended with the letters `LL' standing for `long long', which is `gcc's
+name for the 64-bit integer type.  Some compilers may allow `LIT64' to be
+defined as the identity macro:  `#define LIT64( a ) a'.
+-------------------------------------------------------------------------------
+*/
+#define LIT64( a ) a##LL
+#endif
+
+/*
+-------------------------------------------------------------------------------
+The macro `INLINE' can be used before functions that should be inlined.  If
+a compiler does not support explicit inlining, this macro should be defined
+to be `static'.
+-------------------------------------------------------------------------------
+*/
+#define INLINE extern __inline__
+
+
+/* For use as a GCC soft-float library we need some special function names. */
+
+#ifdef __LIBFLOAT__
+
+/* Some 32-bit ops can be mapped straight across by just changing the name. */
+#define float32_add			__addsf3
+#define float32_sub			__subsf3
+#define float32_mul			__mulsf3
+#define float32_div			__divsf3
+#define int32_to_float32		__floatsisf
+#define float32_to_int32_round_to_zero	__fixsfsi
+#define float32_to_uint32_round_to_zero	__fixunssfsi
+
+/* These ones go through the glue code.  To avoid namespace pollution
+   we rename the internal functions too.  */
+#define float32_eq			___float32_eq
+#define float32_le			___float32_le
+#define float32_lt			___float32_lt
+
+/* All the 64-bit ops have to go through the glue, so we pull the same
+   trick.  */
+#define float64_add			___float64_add
+#define float64_sub			___float64_sub
+#define float64_mul			___float64_mul
+#define float64_div			___float64_div
+#define int32_to_float64		___int32_to_float64
+#define float64_to_int32_round_to_zero	___float64_to_int32_round_to_zero
+#define float64_to_uint32_round_to_zero	___float64_to_uint32_round_to_zero
+#define float64_to_float32		___float64_to_float32
+#define float32_to_float64		___float32_to_float64
+#define float64_eq			___float64_eq
+#define float64_le			___float64_le
+#define float64_lt			___float64_lt
+
+#if 0
+#define float64_add			__adddf3
+#define float64_sub			__subdf3
+#define float64_mul			__muldf3
+#define float64_div			__divdf3
+#define int32_to_float64		__floatsidf
+#define float64_to_int32_round_to_zero	__fixdfsi
+#define float64_to_uint32_round_to_zero	__fixunsdfsi
+#define float64_to_float32		__truncdfsf2
+#define float32_to_float64		__extendsfdf2
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/ChangeLog linux.18p17/arch/arm/nwfpe/ChangeLog
--- linux.vanilla/arch/arm/nwfpe/ChangeLog	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/ChangeLog	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,20 @@
+1998-11-23  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* README.FPE - fix typo in description of lfm/sfm instructions
+	* NOTES - Added file to describe known bugs/problems 
+	* fpmodule.c - Changed version number to 0.94
+
+1998-11-20  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* README.FPE - fix description of URD, NRM instructions
+	* TODO - remove URD, NRM instructions from TODO list
+	* single_cpdo.c - implement URD, NRM
+	* double_cpdo.c - implement URD, NRM
+	* extended_cpdo.c - implement URD, NRM
+
+1998-11-19  Scott Bambrough  <scottb@corelcomputer.com>
+
+	* ChangeLog - Added this file to track changes made.
+	* fpa11.c - added code to initialize register types to typeNone
+	* fpa11_cpdt.c - fixed bug in storeExtended (typeExtended changed to
+	  typeDouble in switch statement)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/Makefile linux.18p17/arch/arm/nwfpe/Makefile
--- linux.vanilla/arch/arm/nwfpe/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/Makefile	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,31 @@
+#
+# linux/arch/arm/nwfpe/Makefile
+#
+# Copyright (C) 1998, 1999 Philip Blundell
+#
+
+NWFPE_OBJS := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \
+	      fpmodule.o fpopcode.o softfloat.o \
+	      single_cpdo.o double_cpdo.o extended_cpdo.o
+
+ifeq ($(CONFIG_CPU_26),y)
+NWFPE_OBJS += entry26.o
+else
+NWFPE_OBJS += entry.o
+endif
+
+L_TARGET := math-emu.a
+
+ifeq ($(CONFIG_NWFPE),y)
+L_OBJS = $(NWFPE_OBJS)
+else
+  ifeq ($(CONFIG_NWFPE),m)
+    M_OBJS = nwfpe.o
+    MI_OBJS = $(NWFPE_OBJS)
+  endif
+endif    
+
+include $(TOPDIR)/Rules.make
+
+nwfpe.o: $(MI_OBJS) $(MIX_OBJS)
+	 $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/config.h linux.18p17/arch/arm/nwfpe/config.h
--- linux.vanilla/arch/arm/nwfpe/config.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/config.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,31 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#if 1
+#define C_SYMBOL_NAME(foo)	foo
+#else
+#define C_SYMBOL_NAME(foo)	_##foo
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/double_cpdo.c linux.18p17/arch/arm/nwfpe/double_cpdo.c
--- linux.vanilla/arch/arm/nwfpe/double_cpdo.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/double_cpdo.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,293 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+extern FPA11 *fpa11;
+
+float64 getDoubleConstant(unsigned int);
+
+float64 float64_exp(float64 Fm);
+float64 float64_ln(float64 Fm);
+float64 float64_sin(float64 rFm);
+float64 float64_cos(float64 rFm);
+float64 float64_arcsin(float64 rFm);
+float64 float64_arctan(float64 rFm);
+float64 float64_log(float64 rFm);
+float64 float64_tan(float64 rFm);
+float64 float64_arccos(float64 rFm);
+float64 float64_pow(float64 rFn,float64 rFm);
+float64 float64_pol(float64 rFn,float64 rFm);
+
+unsigned int DoubleCPDO(const unsigned int opcode)
+{
+   float64 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   //fp_printk("DoubleCPDO(0x%08x)\n",opcode);
+   
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getDoubleConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = float32_to_float64(fpa11->fpreg[Fm].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFm = fpa11->fpreg[Fm].fValue.fDouble;
+          break;
+
+        case typeExtended:
+            // !! patb
+	    //fp_printk("not implemented! why not?\n");
+            //!! ScottB
+            // should never get here, if extended involved
+            // then other operand should be promoted then
+            // ExtendedCPDO called.
+            break;
+
+        default: return 0;
+     }
+   }
+
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFn = fpa11->fpreg[Fn].fValue.fDouble;
+        break;
+        
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_mul(rFn,rFm);
+      break;
+
+   case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      break;
+
+      case MNF_CODE:
+      {
+         unsigned int *p = (unsigned int*)&rFm;
+         p[1] ^= 0x80000000;
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      }
+      break;
+
+      case ABS_CODE:
+      {
+         unsigned int *p = (unsigned int*)&rFm;
+         p[1] &= 0x7fffffff;
+         fpa11->fpreg[Fd].fValue.fDouble = rFm;
+      }
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = 
+             int32_to_float64(float64_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fDouble = float64_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeDouble;
+   return nRc;
+}
+
+#if 0
+float64 float64_exp(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_ln(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_sin(float64 rFm)
+{
+  return rFm;
+//series
+}
+
+float64 float64_cos(float64 rFm)
+{
+   return rFm;
+   //series
+}
+
+#if 0
+float64 float64_arcsin(float64 rFm)
+{
+//series
+}
+
+float64 float64_arctan(float64 rFm)
+{
+  //series
+}
+#endif
+
+float64 float64_log(float64 rFm)
+{
+  return float64_div(float64_ln(rFm),getDoubleConstant(7));
+}
+
+float64 float64_tan(float64 rFm)
+{
+  return float64_div(float64_sin(rFm),float64_cos(rFm));
+}
+
+float64 float64_arccos(float64 rFm)
+{
+return rFm;
+   //return float64_sub(halfPi,float64_arcsin(rFm));
+}
+
+float64 float64_pow(float64 rFn,float64 rFm)
+{
+  return float64_exp(float64_mul(rFm,float64_ln(rFn))); 
+}
+
+float64 float64_pol(float64 rFn,float64 rFm)
+{
+  return float64_arctan(float64_div(rFn,rFm)); 
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/entry.S linux.18p17/arch/arm/nwfpe/entry.S
--- linux.vanilla/arch/arm/nwfpe/entry.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/entry.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,126 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+	adrsvc	al, r9, ret_from_exception	@ r9  = normal FP return
+	adrsvc	al, lr, fpundefinstr		@ lr  = undefined instr return
+
+	get_current_task r10
+	mov	r8, #1
+	strb	r8, [r10, #TSK_USED_MATH]	@ set current->used_math
+	add	r10, r10, #TSS_FPESAVE		@ r10 = workspace
+	ldr	r4, .LC2
+	ldr	pc, [r4]			@ Call FP emulator entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator.  The emulator, if
+successful in its emulation, jumps to ret_from_exception (passed in
+r9) and the kernel takes care of returning control from the trap to
+the user code.  If the emulator is unable to emulate the instruction,
+it returns via _fpundefinstr (passed via lr) and the kernel halts the
+user program with a core dump.
+
+On entry to the emulator r10 points to an area of private FP workspace
+reserved in the thread structure for this process.  This is where the
+emulator saves its registers across calls.  The first word of this area
+is used as a flag to detect the first time a process uses floating point,
+so that the emulator startup cost can be avoided for tasks that don't
+want it.
+
+This routine does three things:
+
+1) It saves SP into a variable called userRegisters.  The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it.  See /usr/include/asm/proc/ptrace.h for details.  The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+3) If an instruction has been emulated successfully, it looks ahead at
+the next instruction.  If it is a floating point instruction, it
+executes the instruction, without returning to user space.  In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions.  GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions.  */
+
+	.globl	nwfpe_enter
+nwfpe_enter:
+	/* ?? Could put userRegisters and fpa11 into fixed regs during
+	   emulation.  This would reduce load/store overhead at the expense
+	   of stealing two regs from the register allocator.  Not sure if
+	   it's worth it.  */
+	ldr r4, =userRegisters
+        str sp, [r4]			@ save pointer to user regs
+	ldr r4, =fpa11
+	str r10, [r4]			@ store pointer to our state
+        mov r4, sp			@ use r4 for local pointer
+        mov r10, lr			@ save the failure-return addresses
+
+        ldr r5, [r4, #60]	 	@ get contents of PC;
+	ldr r0, [r5, #-4]		@ get actual instruction into r0
+emulate:
+	bl EmulateAll			@ emulate the instruction
+   	cmp r0, #0			@ was emulation successful
+        moveq pc, r10			@ no, return failure
+
+next:
+__x1:	ldrt	r6, [r5], #4		@ get the next instruction and
+					@ increment PC
+
+	and   r2, r6, #0x0F000000	@ test for FP insns
+        teq   r2, #0x0C000000
+        teqne r2, #0x0D000000
+        teqne r2, #0x0E000000
+        movne pc, r9			@ return ok if not a fp insn
+
+        str r5, [r4, #60]		@ update PC copy in regs
+
+        mov r0, r6			@ save a copy
+        ldr r1, [r4, #64]		@ fetch the condition codes
+   	bl  checkCondition		@ check the condition
+   	cmp r0, #0			@ r0 = 0 ==> condition failed
+
+        @ if condition code failed to match, next insn
+   	beq next			@ get the next instruction;
+   	    
+        mov r0, r6			@ prepare for EmulateAll()
+   	b emulate			@ if r0 != 0, goto EmulateAll
+
+	@ We need to be prepared for the instruction at __x1 to fault.
+	@ Emit the appropriate exception gunk to fix things up.
+	.section .fixup,"ax"
+	.align
+__f1:	mov	pc, r9
+	.previous
+	.section __ex_table,"a"
+	.align 3
+	.long	__x1, __f1
+	.previous
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/entry26.S linux.18p17/arch/arm/nwfpe/entry26.S
--- linux.vanilla/arch/arm/nwfpe/entry26.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/entry26.S	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,112 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "../lib/constants.h"
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+	mov	fp, #0
+	teqp	pc, #I_BIT | MODE_SVC
+	ldr	r4, .LC2
+	ldr	pc, [r4]		@ Call FP module USR entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator.  The emulator, if
+successful in its emulation, jumps to ret_from_exception and the
+kernel takes care of returning control from the trap to the user code.
+If the emulator is unable to emulate the instruction, it returns to
+fpundefinstr and the kernel halts the user program with a core dump.
+
+This routine does four things:
+
+1) It saves SP into a variable called userRegisters.  The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it.  See /usr/include/asm/proc/ptrace.h for details.  The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It locates the FP emulator work area within the TSS structure and
+points `fpa11' to it.
+
+3) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+4) If an instruction has been emulated successfully, it looks ahead at
+the next instruction.  If it is a floating point instruction, it
+executes the instruction, without returning to user space.  In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions.  GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions.  */
+
+	.globl	nwfpe_enter
+nwfpe_enter:
+	ldr	r4, =userRegisters
+	str	sp, [r4]		@ save pointer to user regs
+
+	mov	r10, sp, lsr #13	@ find workspace
+	mov	r10, r10, lsl #13
+	add	r10, r10, #TSS_FPESAVE
+
+	ldr	r4, =fpa11
+	str	r10, [r4]		@ store pointer to our state
+	mov	r4, sp			@ use r4 for local pointer
+
+	ldr	r5, [r4, #60]		@ get contents of PC
+	bic	r5, r5, #0xfc000003
+	ldr	r0, [r5, #-4]		@ get actual instruction into r0
+	bl	EmulateAll		@ emulate the instruction
+1:	cmp	r0, #0			@ was emulation successful
+	beq	fpundefinstr		@ no, return failure
+
+next:
+	ldrt	r6, [r5], #4		@ get the next instruction and
+					@ increment PC
+
+	and	r2, r6, #0x0F000000	@ test for FP insns
+	teq	r2, #0x0C000000
+	teqne	r2, #0x0D000000
+	teqne	r2, #0x0E000000
+	bne	ret_from_exception	@ return ok if not a fp insn
+
+	ldr	r9, [r4, #60]		@ get new condition codes
+	and	r9, r9, #0xfc000003
+	orr	r7, r5, r9
+	str	r7, [r4, #60]		@ update PC copy in regs
+
+	mov	r0, r6			@ save a copy
+	mov	r1, r9			@ fetch the condition codes
+	bl	checkCondition		@ check the condition
+	cmp	r0, #0			@ r0 = 0 ==> condition failed
+
+	@ if condition code failed to match, next insn
+	beq	next			@ get the next instruction;
+	    
+	mov	r0, r6			@ prepare for EmulateAll()
+	adr	lr, 1b
+	orr	lr, lr, #3
+	b	EmulateAll		@ if r0 != 0, goto EmulateAll
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/extended_cpdo.c linux.18p17/arch/arm/nwfpe/extended_cpdo.c
--- linux.vanilla/arch/arm/nwfpe/extended_cpdo.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/extended_cpdo.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,276 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+floatx80 getExtendedConstant(unsigned int);
+
+floatx80 floatx80_exp(floatx80 Fm);
+floatx80 floatx80_ln(floatx80 Fm);
+floatx80 floatx80_sin(floatx80 rFm);
+floatx80 floatx80_cos(floatx80 rFm);
+floatx80 floatx80_arcsin(floatx80 rFm);
+floatx80 floatx80_arctan(floatx80 rFm);
+floatx80 floatx80_log(floatx80 rFm);
+floatx80 floatx80_tan(floatx80 rFm);
+floatx80 floatx80_arccos(floatx80 rFm);
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm);
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm);
+
+unsigned int ExtendedCPDO(const unsigned int opcode)
+{
+   floatx80 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   //fp_printk("ExtendedCPDO(0x%08x)\n",opcode);
+   
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getExtendedConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+        break;
+        
+        case typeExtended:
+          rFm = fpa11->fpreg[Fm].fValue.fExtended;
+        break;
+        
+        default: return 0;
+     }
+   }
+   
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+        break;
+
+        case typeDouble:
+          rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+        break;
+        
+        case typeExtended:
+          rFn = fpa11->fpreg[Fn].fValue.fExtended;
+        break;
+        
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_mul(rFn,rFm);
+      break;
+
+      case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case MNF_CODE:
+         rFm.high ^= 0x8000;
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case ABS_CODE:
+         rFm.high &= 0x7fff;
+         fpa11->fpreg[Fd].fValue.fExtended = rFm;
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = 
+             int32_to_floatx80(floatx80_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fExtended = floatx80_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+   
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeExtended;
+   return nRc;
+}
+
+#if 0
+floatx80 floatx80_exp(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_ln(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_sin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_cos(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arcsin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arctan(floatx80 rFm)
+{
+  //series
+}
+
+floatx80 floatx80_log(floatx80 rFm)
+{
+  return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7));
+}
+
+floatx80 floatx80_tan(floatx80 rFm)
+{
+  return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm));
+}
+
+floatx80 floatx80_arccos(floatx80 rFm)
+{
+   //return floatx80_sub(halfPi,floatx80_arcsin(rFm));
+}
+
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm)
+{
+  return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn))); 
+}
+
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm)
+{
+  return floatx80_arctan(floatx80_div(rFn,rFm)); 
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11.c linux.18p17/arch/arm/nwfpe/fpa11.c
--- linux.vanilla/arch/arm/nwfpe/fpa11.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,206 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "fpa11.h"
+#include "milieu.h"
+#include "fpopcode.h"
+
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+/* forward declarations */
+unsigned int EmulateCPDO(const unsigned int);
+unsigned int EmulateCPDT(const unsigned int);
+unsigned int EmulateCPRT(const unsigned int);
+
+/* Emulator registers */
+FPA11 *fpa11;
+
+/* Reset the FPA11 chip.  Called to initialize and reset the emulator. */
+void resetFPA11(void)
+{
+  int i;
+  /* initialize the registers */
+  for (i=0;i<=7;i++)
+  {
+    fpa11->fpreg[i].fType = typeNone;
+  }
+  
+  /* FPSR: set system id to FP_EMULATOR, clear all other bits */
+  fpa11->fpsr = FP_EMULATOR;
+  
+  /* FPCR: set SB, AB and DA bits, clear all others */
+#if MAINTAIN_FPCR         
+  fpa11->fpcr = MASK_RESET;
+#endif
+}
+
+void SetRoundingMode(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+   fpa11->fpcr &= ~MASK_ROUNDING_MODE;
+#endif   
+   switch (opcode & MASK_ROUNDING_MODE)
+   {
+      default:
+      case ROUND_TO_NEAREST:
+         float_rounding_mode = float_round_nearest_even;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_NEAREST;
+#endif         
+      break;
+      
+      case ROUND_TO_PLUS_INFINITY:
+         float_rounding_mode = float_round_up;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
+#endif         
+      break;
+      
+      case ROUND_TO_MINUS_INFINITY:
+         float_rounding_mode = float_round_down;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
+#endif         
+      break;
+      
+      case ROUND_TO_ZERO:
+         float_rounding_mode = float_round_to_zero;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_TO_ZERO;
+#endif         
+      break;
+  }
+}
+
+void SetRoundingPrecision(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+   fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
+#endif   
+   switch (opcode & MASK_ROUNDING_PRECISION)
+   {
+      case ROUND_SINGLE:
+         floatx80_rounding_precision = 32;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_SINGLE;
+#endif         
+      break;
+      
+      case ROUND_DOUBLE:
+         floatx80_rounding_precision = 64;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_DOUBLE;
+#endif         
+      break;
+      
+      case ROUND_EXTENDED:
+         floatx80_rounding_precision = 80;
+#if MAINTAIN_FPCR         
+         fpa11->fpcr |= ROUND_EXTENDED;
+#endif         
+      break;
+      
+      default: floatx80_rounding_precision = 80;
+  }
+}
+
+/* Emulate the instruction in the opcode. */
+unsigned int EmulateAll(unsigned int opcode)
+{
+  unsigned int nRc = 0;
+
+  if (fpa11->initflag == 0)		/* good place for __builtin_expect */
+  {
+    resetFPA11();
+    SetRoundingMode(ROUND_TO_NEAREST);
+    SetRoundingPrecision(ROUND_EXTENDED);
+    fpa11->initflag = 1;
+  }
+
+  if (TEST_OPCODE(opcode,MASK_CPRT))
+  {
+    /* Emulate conversion opcodes. */
+    /* Emulate register transfer opcodes. */
+    /* Emulate comparison opcodes. */
+    nRc = EmulateCPRT(opcode);
+  }
+  else if (TEST_OPCODE(opcode,MASK_CPDO))
+  {
+    /* Emulate monadic arithmetic opcodes. */
+    /* Emulate dyadic arithmetic opcodes. */
+    nRc = EmulateCPDO(opcode);
+  }
+  else if (TEST_OPCODE(opcode,MASK_CPDT))
+  {
+    /* Emulate load/store opcodes. */
+    /* Emulate load/store multiple opcodes. */
+    nRc = EmulateCPDT(opcode);
+  }
+  else
+  {
+    /* Invalid instruction detected.  Return FALSE. */
+    nRc = 0;
+  }
+
+  return(nRc);
+}
+
+#if 0
+unsigned int EmulateAll1(unsigned int opcode)
+{
+  switch ((opcode >> 24) & 0xf)
+  {
+     case 0xc:
+     case 0xd:
+       if ((opcode >> 20) & 0x1)
+       {
+          switch ((opcode >> 8) & 0xf)
+          {
+             case 0x1: return PerformLDF(opcode); break;
+             case 0x2: return PerformLFM(opcode); break;
+             default: return 0;
+          }
+       }
+       else
+       {
+          switch ((opcode >> 8) & 0xf)
+          {
+             case 0x1: return PerformSTF(opcode); break;
+             case 0x2: return PerformSFM(opcode); break;
+             default: return 0;
+          }
+      }
+     break;
+     
+     case 0xe: 
+       if (opcode & 0x10)
+         return EmulateCPDO(opcode);
+       else
+         return EmulateCPRT(opcode);
+     break;
+  
+     default: return 0;
+  }
+}
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11.h linux.18p17/arch/arm/nwfpe/fpa11.h
--- linux.vanilla/arch/arm/nwfpe/fpa11.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,61 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPA11_H__
+#define __FPA11_H__
+
+/* includes */
+#include "fpsr.h"		/* FP control and status register definitions */
+#include "softfloat.h"
+
+#define		typeNone		0x00
+#define		typeSingle		0x01
+#define		typeDouble		0x02
+#define		typeExtended		0x03
+
+typedef struct tagFPREG {
+   unsigned int fType;
+   union {
+      float32  fSingle;
+      float64  fDouble;
+      floatx80 fExtended;
+   } fValue;
+} FPREG;
+
+/* FPA11 device model */
+typedef struct tagFPA11 {
+  int initflag;			/* this is special.  The kernel guarantees
+				   to set it to 0 when a thread is launched,
+				   so we can use it to detect whether this
+				   instance of the emulator needs to be
+				   initialised. */
+  FPREG fpreg[8];		/* 8 floating point registers */
+  FPSR fpsr;			/* floating point status register */
+  FPCR fpcr;			/* floating point control register */
+} FPA11;
+
+extern void resetFPA11(void);
+extern void SetRoundingMode(const unsigned int);
+extern void SetRoundingPrecision(const unsigned int);
+
+extern FPA11 *fpa11;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11.inl linux.18p17/arch/arm/nwfpe/fpa11.inl
--- linux.vanilla/arch/arm/nwfpe/fpa11.inl	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11.inl	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,47 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+
+/* Read and write floating point status register */
+extern __inline__ unsigned int readFPSR(void)
+{
+  return(fpa11->fpsr);
+}
+
+extern __inline__ void writeFPSR(FPSR reg)
+{
+  /* the sysid byte in the status register is readonly */
+  fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
+}
+
+/* Read and write floating point control register */
+extern __inline__ FPCR readFPCR(void)
+{
+  /* clear SB, AB and DA bits before returning FPCR */
+  return(fpa11->fpcr & ~MASK_RFC);
+}
+
+extern __inline__ void writeFPCR(FPCR reg)
+{
+  fpa11->fpcr &= ~MASK_WFC;		/* clear SB, AB and DA bits */
+  fpa11->fpcr |= (reg & MASK_WFC);	/* write SB, AB and DA bits */
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11_cpdo.c linux.18p17/arch/arm/nwfpe/fpa11_cpdo.c
--- linux.vanilla/arch/arm/nwfpe/fpa11_cpdo.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11_cpdo.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,117 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "fpa11.h"
+#include "fpopcode.h"
+
+unsigned int SingleCPDO(const unsigned int opcode);
+unsigned int DoubleCPDO(const unsigned int opcode);
+unsigned int ExtendedCPDO(const unsigned int opcode);
+
+unsigned int EmulateCPDO(const unsigned int opcode)
+{
+   unsigned int Fd, nType, nDest, nRc = 1;
+   
+   //fp_printk("EmulateCPDO(0x%08x)\n",opcode);
+
+   /* Get the destination size.  If not valid let Linux perform
+      an invalid instruction trap. */
+   nDest = getDestinationSize(opcode);
+   if (typeNone == nDest) return 0;
+   
+   SetRoundingMode(opcode);
+     
+   /* Compare the size of the operands in Fn and Fm.
+      Choose the largest size and perform operations in that size,
+      in order to make use of all the precision of the operands. 
+      If Fm is a constant, we just grab a constant of a size 
+      matching the size of the operand in Fn. */
+   if (MONADIC_INSTRUCTION(opcode))
+     nType = nDest;
+   else
+     nType = fpa11->fpreg[getFn(opcode)].fType;
+   
+   if (!CONSTANT_FM(opcode))
+   {
+     register unsigned int Fm = getFm(opcode);
+     if (nType < fpa11->fpreg[Fm].fType)
+     {
+        nType = fpa11->fpreg[Fm].fType;
+     }
+   }
+
+   switch (nType)
+   {
+      case typeSingle   : nRc = SingleCPDO(opcode);   break;
+      case typeDouble   : nRc = DoubleCPDO(opcode);   break;
+      case typeExtended : nRc = ExtendedCPDO(opcode); break;
+      default           : nRc = 0;
+   }
+
+   /* If the operation succeeded, check to see if the result in the
+      destination register is the correct size.  If not force it
+      to be. */
+   Fd = getFd(opcode);
+   nType = fpa11->fpreg[Fd].fType;
+   if ((0 != nRc) && (nDest != nType))
+   {
+     switch (nDest)
+     {
+       case typeSingle:
+       {
+         if (typeDouble == nType)
+           fpa11->fpreg[Fd].fValue.fSingle = 
+              float64_to_float32(fpa11->fpreg[Fd].fValue.fDouble);
+         else
+           fpa11->fpreg[Fd].fValue.fSingle = 
+              floatx80_to_float32(fpa11->fpreg[Fd].fValue.fExtended);
+       }
+       break;
+          
+       case typeDouble:
+       {
+         if (typeSingle == nType)
+           fpa11->fpreg[Fd].fValue.fDouble = 
+              float32_to_float64(fpa11->fpreg[Fd].fValue.fSingle);
+         else
+           fpa11->fpreg[Fd].fValue.fDouble = 
+              floatx80_to_float64(fpa11->fpreg[Fd].fValue.fExtended);
+       }
+       break;
+          
+       case typeExtended:
+       {
+         if (typeSingle == nType)
+           fpa11->fpreg[Fd].fValue.fExtended = 
+              float32_to_floatx80(fpa11->fpreg[Fd].fValue.fSingle);
+         else
+           fpa11->fpreg[Fd].fValue.fExtended = 
+              float64_to_floatx80(fpa11->fpreg[Fd].fValue.fDouble);
+       }
+       break;
+     }
+     
+     fpa11->fpreg[Fd].fType = nDest;
+   }
+   
+   return nRc;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11_cpdt.c linux.18p17/arch/arm/nwfpe/fpa11_cpdt.c
--- linux.vanilla/arch/arm/nwfpe/fpa11_cpdt.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11_cpdt.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,330 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+#include <asm/uaccess.h>
+
+extern __inline__
+void loadSingle(const unsigned int Fn,const unsigned int *pMem)
+{
+   fpa11->fpreg[Fn].fType = typeSingle;
+   get_user(fpa11->fpreg[Fn].fValue.fSingle, pMem);
+}
+
+extern __inline__
+void loadDouble(const unsigned int Fn,const unsigned int *pMem)
+{
+   unsigned int *p;
+   p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fDouble;
+   fpa11->fpreg[Fn].fType = typeDouble;
+   get_user(p[0], &pMem[1]);
+   get_user(p[1], &pMem[0]); /* sign & exponent */
+}   
+
+extern __inline__
+void loadExtended(const unsigned int Fn,const unsigned int *pMem)
+{
+   unsigned int *p;
+   p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fExtended;
+   fpa11->fpreg[Fn].fType = typeExtended;
+   get_user(p[0], &pMem[0]);  /* sign & exponent */
+   get_user(p[1], &pMem[2]);  /* ls bits */
+   get_user(p[2], &pMem[1]);  /* ms bits */
+}   
+
+extern __inline__
+void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
+{
+   register unsigned int *p;
+   unsigned long x;
+
+   p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+   get_user(x, &pMem[0]);
+   fpa11->fpreg[Fn].fType = (x >> 14) & 0x00000003;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle:
+      case typeDouble:
+      {
+         get_user(p[0], &pMem[2]);  /* Single */
+         get_user(p[1], &pMem[1]);  /* double msw */
+         p[2] = 0;        /* empty */
+      }
+      break; 
+   
+      case typeExtended:
+      {
+         get_user(p[1], &pMem[2]);
+         get_user(p[2], &pMem[1]);  /* msw */
+         p[0] = (x & 0x80003fff);      
+      }
+      break;
+   }
+}
+
+extern __inline__
+void storeSingle(const unsigned int Fn,unsigned int *pMem)
+{
+   float32 val;
+   register unsigned int *p = (unsigned int*)&val;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeDouble: 
+         val = float64_to_float32(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+
+      case typeExtended: 
+         val = floatx80_to_float32(fpa11->fpreg[Fn].fValue.fExtended);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fSingle;
+   }
+  
+   put_user(p[0], pMem);
+}   
+
+extern __inline__
+void storeDouble(const unsigned int Fn,unsigned int *pMem)
+{
+   float64 val;
+   register unsigned int *p = (unsigned int*)&val;
+
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+         val = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeExtended:
+         val = floatx80_to_float64(fpa11->fpreg[Fn].fValue.fExtended);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fDouble;
+   }
+   put_user(p[1], &pMem[0]);	/* msw */
+   put_user(p[0], &pMem[1]);	/* lsw */
+}   
+
+extern __inline__
+void storeExtended(const unsigned int Fn,unsigned int *pMem)
+{
+   floatx80 val;
+   register unsigned int *p = (unsigned int*)&val;
+   
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+         val = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeDouble: 
+         val = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+
+      default: val = fpa11->fpreg[Fn].fValue.fExtended;
+   }
+   
+   put_user(p[0], &pMem[0]); /* sign & exp */
+   put_user(p[1], &pMem[2]);
+   put_user(p[2], &pMem[1]); /* msw */
+}   
+
+extern __inline__
+void storeMultiple(const unsigned int Fn,unsigned int *pMem)
+{
+   register unsigned int nType, *p;
+   
+   p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+   nType = fpa11->fpreg[Fn].fType;
+   
+   switch (nType)
+   {
+      case typeSingle:
+      case typeDouble:
+      {
+	 put_user(p[0], &pMem[2]); /* single */
+	 put_user(p[1], &pMem[1]); /* double msw */
+	 put_user(nType << 14, &pMem[0]);
+      }
+      break; 
+   
+      case typeExtended:
+      {
+	 put_user(p[2], &pMem[1]); /* msw */
+	 put_user(p[1], &pMem[2]);
+	 put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
+      }
+      break;
+   }
+}
+
+unsigned int PerformLDF(const unsigned int opcode)
+{
+   unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+   
+   //fp_printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   switch (opcode & MASK_TRANSFER_LENGTH)
+   {
+      case TRANSFER_SINGLE  : loadSingle(getFd(opcode),pAddress);   break;
+      case TRANSFER_DOUBLE  : loadDouble(getFd(opcode),pAddress);   break;
+      case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
+      default: nRc = 0;
+   }
+   
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return nRc;
+}
+
+unsigned int PerformSTF(const unsigned int opcode)
+{
+   unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+   
+   //fp_printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+   SetRoundingMode(ROUND_TO_NEAREST);
+   
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   switch (opcode & MASK_TRANSFER_LENGTH)
+   {
+      case TRANSFER_SINGLE  : storeSingle(getFd(opcode),pAddress);   break;
+      case TRANSFER_DOUBLE  : storeDouble(getFd(opcode),pAddress);   break;
+      case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
+      default: nRc = 0;
+   }
+   
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return nRc;
+}
+
+unsigned int PerformLFM(const unsigned int opcode)
+{
+   unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   Fd = getFd(opcode);
+   for (i=getRegisterCount(opcode);i>0;i--)
+   {
+     loadMultiple(Fd,pAddress);
+     pAddress += 3; Fd++;
+     if (Fd == 8) Fd = 0;
+   }
+
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return 1;
+}
+
+unsigned int PerformSFM(const unsigned int opcode)
+{
+   unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+   
+   pBase = (unsigned int*)readRegister(getRn(opcode));
+   if (REG_PC == getRn(opcode)) pBase += 2;
+   
+   pFinal = pBase;
+   if (BIT_UP_SET(opcode))
+     pFinal += getOffset(opcode);
+   else
+     pFinal -= getOffset(opcode);
+
+   if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+   Fd = getFd(opcode);
+   for (i=getRegisterCount(opcode);i>0;i--)
+   {
+     storeMultiple(Fd,pAddress);
+     pAddress += 3; Fd++;
+     if (Fd == 8) Fd = 0;
+   }
+
+   if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+   return 1;
+}
+
+#if 1
+unsigned int EmulateCPDT(const unsigned int opcode)
+{
+  unsigned int nRc = 0;
+
+  //fp_printk("EmulateCPDT(0x%08x)\n",opcode);
+  
+  if (LDF_OP(opcode))
+  {
+    nRc = PerformLDF(opcode);
+  }
+  else if (LFM_OP(opcode))
+  {
+    nRc = PerformLFM(opcode);
+  }
+  else if (STF_OP(opcode))
+  {
+    nRc = PerformSTF(opcode);
+  } 
+  else if (SFM_OP(opcode))
+  {
+    nRc = PerformSFM(opcode);
+  }
+  else
+  {
+    nRc = 0;
+  }
+  
+  return nRc;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpa11_cprt.c linux.18p17/arch/arm/nwfpe/fpa11_cprt.c
--- linux.vanilla/arch/arm/nwfpe/fpa11_cprt.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpa11_cprt.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,313 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+extern flag floatx80_is_nan(floatx80);
+extern flag float64_is_nan( float64);
+extern flag float32_is_nan( float32);
+
+void SetRoundingMode(const unsigned int opcode);
+
+unsigned int PerformFLT(const unsigned int opcode);
+unsigned int PerformFIX(const unsigned int opcode);
+
+static unsigned int
+PerformComparison(const unsigned int opcode);
+
+unsigned int EmulateCPRT(const unsigned int opcode)
+{
+  unsigned int nRc = 1;
+
+  //fp_printk("EmulateCPRT(0x%08x)\n",opcode);
+
+  if (opcode & 0x800000)
+  {
+     /* This is some variant of a comparison (PerformComparison will
+	sort out which one).  Since most of the other CPRT
+	instructions are oddball cases of some sort or other it makes
+	sense to pull this out into a fast path.  */
+     return PerformComparison(opcode);
+  }
+
+  /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
+  switch ((opcode & 0x700000) >> 20)
+  {
+    case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
+    case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
+    
+    case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
+    case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
+
+#if 0
+    /* ?? Not at all sure about the mode checks here.  Linux never
+       calls the emulator from a non-USR fault but we always run in SVC
+       mode.  Is there even any point trying to emulate the way FPA11
+       behaves in this respect?
+
+       No - and I quote: 'The FPCR may only be present in some
+       implementations: it is there to control the hardware in an
+       implementation-specific manner, ...  The user mode of the
+       ARM is not permitted to use this register, and the WFC and
+       RFC instructions will trap if tried from user mode.'
+       Therefore, we do not provide the RFC and WFC instructions.
+        (rmk, 3/05/1999)
+     */
+    case  WFC_CODE >> 20:
+    {
+       int mode = 0;
+       __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+       nRc = (0x13 == mode) ? 1 : 0;	/* in SVC processor mode? */
+       if (nRc) writeFPCR(readRegister(getRd(opcode)));
+    }
+    break;
+    
+    case  RFC_CODE >> 20:
+    {
+       int mode = 0;
+       __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+       nRc = (0x13 == mode) ? 1 : 0;	/* in SVC processor mode? */
+       if (nRc) writeRegister(getRd(opcode),readFPCR()); break;
+    }
+    break;
+#endif
+
+    default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+unsigned int PerformFLT(const unsigned int opcode)
+{
+   unsigned int nRc = 1;
+   SetRoundingMode(opcode);
+   SetRoundingPrecision(opcode);
+   
+   switch (opcode & MASK_ROUNDING_PRECISION)
+   {
+      case ROUND_SINGLE:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeSingle;
+        fpa11->fpreg[getFn(opcode)].fValue.fSingle =
+	   int32_to_float32(readRegister(getRd(opcode)));
+      }
+      break;
+
+      case ROUND_DOUBLE:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeDouble;
+        fpa11->fpreg[getFn(opcode)].fValue.fDouble =
+            int32_to_float64(readRegister(getRd(opcode)));
+      }
+      break;
+        
+      case ROUND_EXTENDED:
+      {
+        fpa11->fpreg[getFn(opcode)].fType = typeExtended;
+        fpa11->fpreg[getFn(opcode)].fValue.fExtended =
+	   int32_to_floatx80(readRegister(getRd(opcode)));
+      }
+      break;
+      
+      default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+unsigned int PerformFIX(const unsigned int opcode)
+{
+   unsigned int nRc = 1;
+   unsigned int Fn = getFm(opcode);
+   
+   SetRoundingMode(opcode);
+
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle:
+      {
+         writeRegister(getRd(opcode),
+	               float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle));
+      }
+      break;
+
+      case typeDouble:
+      {
+         writeRegister(getRd(opcode),
+	               float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble));
+      }
+      break;
+      	               
+      case typeExtended:
+      {
+         writeRegister(getRd(opcode),
+	               floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended));
+      }
+      break;
+      
+      default: nRc = 0;
+  }
+  
+  return nRc;
+}
+
+   
+static unsigned int __inline__
+PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
+{
+   unsigned int flags = 0;
+
+   /* test for less than condition */
+   if (floatx80_lt(Fn,Fm))
+   {
+      flags |= CC_NEGATIVE;
+   }
+  
+   /* test for equal condition */
+   if (floatx80_eq(Fn,Fm))
+   {
+      flags |= CC_ZERO;
+   }
+
+   /* test for greater than or equal condition */
+   if (floatx80_lt(Fm,Fn))
+   {
+      flags |= CC_CARRY;
+   }
+   
+   writeConditionCodes(flags);
+   return 1;
+}
+
+/* This instruction sets the flags N, Z, C, V in the FPSR. */
+   
+static unsigned int PerformComparison(const unsigned int opcode)
+{
+   unsigned int Fn, Fm;
+   floatx80 rFn, rFm;
+   int e_flag = opcode & 0x400000;	/* 1 if CxFE */
+   int n_flag = opcode & 0x200000;	/* 1 if CNxx */
+   unsigned int flags = 0;
+
+   //fp_printk("PerformComparison(0x%08x)\n",opcode);
+
+   Fn = getFn(opcode);
+   Fm = getFm(opcode);
+
+   /* Check for unordered condition and convert all operands to 80-bit
+      format.
+      ?? Might be some mileage in avoiding this conversion if possible.
+      Eg, if both operands are 32-bit, detect this and do a 32-bit
+      comparison (cheaper than an 80-bit one).  */
+   switch (fpa11->fpreg[Fn].fType)
+   {
+      case typeSingle: 
+        //fp_printk("single.\n");
+	if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle))
+	   goto unordered;
+        rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+      break;
+
+      case typeDouble: 
+        //fp_printk("double.\n");
+	if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble))
+	   goto unordered;
+        rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+      break;
+      
+      case typeExtended: 
+        //fp_printk("extended.\n");
+	if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended))
+	   goto unordered;
+        rFn = fpa11->fpreg[Fn].fValue.fExtended;
+      break;
+      
+      default: return 0;
+   }
+
+   if (CONSTANT_FM(opcode))
+   {
+     //fp_printk("Fm is a constant: #%d.\n",Fm);
+     rFm = getExtendedConstant(Fm);
+     if (floatx80_is_nan(rFm))
+        goto unordered;
+   }
+   else
+   {
+     //fp_printk("Fm = r%d which contains a ",Fm);
+      switch (fpa11->fpreg[Fm].fType)
+      {
+         case typeSingle: 
+           //fp_printk("single.\n");
+	   if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle))
+	      goto unordered;
+           rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+         break;
+
+         case typeDouble: 
+           //fp_printk("double.\n");
+	   if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble))
+	      goto unordered;
+           rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+         break;
+      
+         case typeExtended: 
+           //fp_printk("extended.\n");
+	   if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended))
+	      goto unordered;
+           rFm = fpa11->fpreg[Fm].fValue.fExtended;
+         break;
+      
+         default: return 0;
+      }
+   }
+
+   if (n_flag)
+   {
+      rFm.high ^= 0x8000;
+   }
+
+   return PerformComparisonOperation(rFn,rFm);
+
+ unordered:
+   /* ?? The FPA data sheet is pretty vague about this, in particular
+      about whether the non-E comparisons can ever raise exceptions.
+      This implementation is based on a combination of what it says in
+      the data sheet, observation of how the Acorn emulator actually
+      behaves (and how programs expect it to) and guesswork.  */
+   flags |= CC_OVERFLOW;
+
+   if (BIT_AC & readFPSR()) flags |= CC_CARRY;
+
+   if (e_flag) float_raise(float_flag_invalid);
+
+   writeConditionCodes(flags);
+   return 1;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpmodule.c linux.18p17/arch/arm/nwfpe/fpmodule.c
--- linux.vanilla/arch/arm/nwfpe/fpmodule.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpmodule.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,185 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+    (c) Philip Blundell, 1998-1999
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+/* XXX */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/pgtable.h>
+/* XXX */
+
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpmodule.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+
+/* external data */
+extern FPA11 *fpa11;
+
+/* kernel symbols required for signal handling */
+typedef struct task_struct*	PTASK;
+
+#ifdef MODULE
+int fp_printk(const char *,...);
+void fp_send_sig(unsigned long sig, PTASK p, int priv);
+#if LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Scott Bambrough <scottb@corelcomputer.com>");
+MODULE_DESCRIPTION("NWFPE floating point emulator");
+#endif
+
+#else
+#define fp_printk	printk
+#define fp_send_sig	send_sig
+#define kern_fp_enter	fp_enter
+#endif
+
+/* kernel function prototypes required */
+void C_SYMBOL_NAME(fp_setup)(void);
+
+/* external declarations for saved kernel symbols */
+extern unsigned int C_SYMBOL_NAME(kern_fp_enter);
+
+/* forward declarations */
+extern void nwfpe_enter(void);
+
+/* Original value of fp_enter from kernel before patched by fpe_init. */ 
+static unsigned int orig_fp_enter;
+
+/* Address of user registers on the kernel stack. */
+unsigned int *userRegisters;
+
+void __init C_SYMBOL_NAME(fpe_version)(void)
+{
+  static const char szTitle[] = "<4>NetWinder Floating Point Emulator ";
+  static const char szVersion[] = "V0.94.1 ";
+  static const char szCopyright[] = "(c) 1998 Corel Computer Corp.\n";
+  C_SYMBOL_NAME(fp_printk)(szTitle);
+  C_SYMBOL_NAME(fp_printk)(szVersion);
+  C_SYMBOL_NAME(fp_printk)(szCopyright);
+}
+
+int __init fpe_init(void)
+{
+  /* Display title, version and copyright information. */
+  C_SYMBOL_NAME(fpe_version)();
+
+  /* Save pointer to the old FP handler and then patch ourselves in */
+  orig_fp_enter = C_SYMBOL_NAME(kern_fp_enter);
+  C_SYMBOL_NAME(kern_fp_enter) = (unsigned int)C_SYMBOL_NAME(nwfpe_enter);
+
+  return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+  return(fpe_init());
+}
+
+void cleanup_module(void)
+{
+  /* Restore the values we saved earlier. */
+  C_SYMBOL_NAME(kern_fp_enter) = orig_fp_enter;
+}
+#endif
+
+#define _ARM_pc 60
+#define _ARM_cpsr 64
+
+/*
+ScottB:  November 4, 1998
+
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+
+[1/1/99: Not quite true any more unfortunately.  There is Linux-specific
+code to access data in user space in some other source files at the 
+moment.  --philb]
+
+float_exception_flags is a global variable in SoftFloat.
+
+This function is called by the SoftFloat routines to raise a floating
+point exception.  We check the trap enable byte in the FPSR, and raise
+a SIGFPE exception if necessary.  If not the relevant bits in the 
+cumulative exceptions flag byte are set and we return.
+*/
+
+void float_raise(signed char flags)
+{
+  register unsigned int fpsr, cumulativeTraps;
+  
+#if 0
+  printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags,
+	 __builtin_return_address(0), userRegisters[15]);
+#endif
+
+  /* Keep SoftFloat exception flags up to date.  */
+  float_exception_flags |= flags;
+
+  /* Read fpsr and initialize the cumulativeTraps.  */
+  fpsr = readFPSR();
+  cumulativeTraps = 0;
+  
+  /* For each type of exception, the cumulative trap exception bit is only
+     set if the corresponding trap enable bit is not set.  */
+  if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
+     cumulativeTraps |= BIT_IXC;  
+  if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
+     cumulativeTraps |= BIT_UFC;  
+  if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
+     cumulativeTraps |= BIT_OFC;  
+  if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
+     cumulativeTraps |= BIT_DZC;  
+  if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
+     cumulativeTraps |= BIT_IOC;  
+
+  /* Set the cumulative exceptions flags.  */
+  if (cumulativeTraps)
+    writeFPSR(fpsr | cumulativeTraps);
+
+  /* Raise an exception if necessary.  */
+  if (fpsr & (flags << 16))
+    fp_send_sig(SIGFPE, current, 1);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpmodule.h linux.18p17/arch/arm/nwfpe/fpmodule.h
--- linux.vanilla/arch/arm/nwfpe/fpmodule.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpmodule.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,53 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPMODULE_H__
+#define __FPMODULE_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_CPU_32
+#define REG_ORIG_R0	17
+#define REG_CPSR	16
+#else
+#define REG_ORIG_R0	16
+#define REG_CPSR	15
+#endif
+
+#define REG_PC		15
+#define REG_LR		14
+#define REG_SP		13
+#define REG_IP		12
+#define REG_FP		11
+#define REG_R10		10
+#define REG_R9		9
+#define REG_R9		9
+#define REG_R8		8
+#define REG_R7		7
+#define REG_R6		6
+#define REG_R5		5
+#define REG_R4		4
+#define REG_R3		3
+#define REG_R2		2
+#define REG_R1		1
+#define REG_R0		0
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpmodule.inl linux.18p17/arch/arm/nwfpe/fpmodule.inl
--- linux.vanilla/arch/arm/nwfpe/fpmodule.inl	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpmodule.inl	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,88 @@
+/*
+   NetWinder Floating Point Emulator
+   (c) Corel Computer Corporation, 1998
+
+   Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Address of user registers on the kernel stack. */
+extern unsigned int *userRegisters;
+
+extern __inline__
+unsigned int readRegister(const unsigned int nReg)
+{
+	/* Note: The CPU thinks it has dealt with the current instruction.  As
+	   a result the program counter has been advanced to the next
+	   instruction, and points 4 bytes beyond the actual instruction
+	   that caused the invalid instruction trap to occur.  We adjust
+	   for this in this routine.  LDF/STF instructions with Rn = PC
+	   depend on the PC being correct, as they use PC+8 in their 
+	   address calculations. */
+	unsigned int val = userRegisters[nReg];
+
+	if (REG_PC == nReg)
+		val -= 4;
+
+	return val;
+}
+
+extern __inline__
+void writeRegister(const unsigned int nReg, const unsigned int val)
+{
+	userRegisters[nReg] = val;
+}
+
+extern __inline__
+unsigned int readCPSR(void)
+{
+	return (readRegister(REG_CPSR));
+}
+
+extern __inline__
+void writeCPSR(const unsigned int val)
+{
+	writeRegister(REG_CPSR, val);
+}
+
+extern __inline__
+unsigned int readConditionCodes(void)
+{
+#ifdef __FPEM_TEST__
+	return (0);
+#else
+	return (readCPSR() & CC_MASK);
+#endif
+}
+
+extern __inline__
+void writeConditionCodes(const unsigned int val)
+{
+	unsigned int rval;
+
+	/*
+	 * Operate directly on userRegisters since
+	 * the CPSR may be the PC register itself.
+	 */
+	rval = userRegisters[REG_CPSR] & ~CC_MASK;
+	userRegisters[REG_CPSR] = rval | (val & CC_MASK);
+}
+
+extern __inline__
+unsigned int readMemoryInt(unsigned int *pMem)
+{
+	return *pMem;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpopcode.c linux.18p17/arch/arm/nwfpe/fpopcode.c
--- linux.vanilla/arch/arm/nwfpe/fpopcode.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpopcode.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,164 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpsr.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+static floatx80 floatx80Constant[] = {
+  { 0x0000, 0x0000000000000000ULL},	/* extended 0.0 */
+  { 0x3fff, 0x8000000000000000ULL},	/* extended 1.0 */
+  { 0x4000, 0x8000000000000000ULL},	/* extended 2.0 */
+  { 0x4000, 0xc000000000000000ULL},	/* extended 3.0 */
+  { 0x4001, 0x8000000000000000ULL},	/* extended 4.0 */
+  { 0x4001, 0xa000000000000000ULL},	/* extended 5.0 */
+  { 0x3ffe, 0x8000000000000000ULL},	/* extended 0.5 */
+  { 0x4002, 0xa000000000000000ULL}	/* extended 10.0 */
+};  
+
+static float64 float64Constant[] = {
+  0x0000000000000000ULL,		/* double 0.0 */
+  0x3ff0000000000000ULL,		/* double 1.0 */
+  0x4000000000000000ULL,		/* double 2.0 */
+  0x4008000000000000ULL,		/* double 3.0 */
+  0x4010000000000000ULL,		/* double 4.0 */
+  0x4014000000000000ULL,		/* double 5.0 */
+  0x3fe0000000000000ULL,		/* double 0.5 */
+  0x4024000000000000ULL			/* double 10.0 */
+};  
+
+static float32 float32Constant[] = {
+  0x00000000,				/* single 0.0 */
+  0x3f800000,				/* single 1.0 */
+  0x40000000,				/* single 2.0 */
+  0x40400000,				/* single 3.0 */
+  0x40800000,				/* single 4.0 */
+  0x40a00000,				/* single 5.0 */
+  0x3f000000,				/* single 0.5 */
+  0x41200000				/* single 10.0 */
+};  
+
+floatx80 getExtendedConstant(const unsigned int nIndex)
+{
+   return floatx80Constant[nIndex];
+} 
+
+float64 getDoubleConstant(const unsigned int nIndex)
+{
+   return float64Constant[nIndex];
+} 
+
+float32 getSingleConstant(const unsigned int nIndex)
+{
+   return float32Constant[nIndex];
+} 
+
+unsigned int getTransferLength(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_TRANSFER_LENGTH)
+  {
+    case 0x00000000: nRc = 1; break; /* single precision */
+    case 0x00008000: nRc = 2; break; /* double precision */
+    case 0x00400000: nRc = 3; break; /* extended precision */
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getRegisterCount(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_REGISTER_COUNT)
+  {
+    case 0x00000000: nRc = 4; break;
+    case 0x00008000: nRc = 1; break;
+    case 0x00400000: nRc = 2; break;
+    case 0x00408000: nRc = 3; break;
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getRoundingPrecision(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_ROUNDING_PRECISION)
+  {
+    case 0x00000000: nRc = 1; break;
+    case 0x00000080: nRc = 2; break;
+    case 0x00080000: nRc = 3; break;
+    default: nRc = 0;
+  }
+  
+  return(nRc);
+}
+
+unsigned int getDestinationSize(const unsigned int opcode)
+{
+  unsigned int nRc;
+  
+  switch (opcode & MASK_DESTINATION_SIZE)
+  {
+    case 0x00000000: nRc = typeSingle; break;
+    case 0x00000080: nRc = typeDouble; break;
+    case 0x00080000: nRc = typeExtended; break;
+    default: nRc = typeNone;
+  }
+  
+  return(nRc);
+}
+
+/* contition code lookup table
+ index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ bit position in short is condition code: NZCV */
+unsigned short aCC[16] = {
+    0xF0F0, // EQ == Z set
+    0x0F0F, // NE
+    0xCCCC, // CS == C set
+    0x3333, // CC
+    0xFF00, // MI == N set
+    0x00FF, // PL
+    0xAAAA, // VS == V set
+    0x5555, // VC
+    0x0C0C, // HI == C set && Z clear
+    0xF3F3, // LS == C clear || Z set
+    0xAA55, // GE == (N==V)
+    0x55AA, // LT == (N!=V)
+    0x0A05, // GT == (!Z && (N==V))
+    0xF5FA, // LE == (Z || (N!=V))
+    0xFFFF, // AL always
+    0 // NV
+};
+
+unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes)
+{
+  return (aCC[opcode>>28] >> (ccodes>>28)) & 1;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpopcode.h linux.18p17/arch/arm/nwfpe/fpopcode.h
--- linux.vanilla/arch/arm/nwfpe/fpopcode.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpopcode.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,376 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPOPCODE_H__
+#define __FPOPCODE_H__
+
+/*
+ARM Floating Point Instruction Classes
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+|c o n d|1 1 0 P|U|u|W|L|   Rn  |v|  Fd |0|0|0|1|  o f f s e t  | CPDT
+|c o n d|1 1 0 P|U|w|W|L|   Rn  |x|  Fd |0|0|0|1|  o f f s e t  | CPDT
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+|c o n d|1 1 1 0|a|b|c|d|e|  Fn |j|  Fd |0|0|0|1|f|g|h|0|i|  Fm | CPDO
+|c o n d|1 1 1 0|a|b|c|L|e|  Fn |   Rd  |0|0|0|1|f|g|h|1|i|  Fm | CPRT
+|c o n d|1 1 1 0|a|b|c|1|e|  Fn |1|1|1|1|0|0|0|1|f|g|h|1|i|  Fm | comparisons
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 
+
+CPDT		data transfer instructions
+		LDF, STF, LFM, SFM
+		
+CPDO		dyadic arithmetic instructions
+		ADF, MUF, SUF, RSF, DVF, RDF,
+		POW, RPW, RMF, FML, FDV, FRD, POL
+
+CPDO		monadic arithmetic instructions
+		MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
+		SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
+		
+CPRT		joint arithmetic/data transfer instructions
+		FIX (arithmetic followed by load/store)
+		FLT (load/store followed by arithmetic)
+		CMF, CNF CMFE, CNFE (comparisons)
+		WFS, RFS (write/read floating point status register)
+		WFC, RFC (write/read floating point control register)
+
+cond		condition codes
+P		pre/post index bit: 0 = postindex, 1 = preindex
+U		up/down bit: 0 = stack grows down, 1 = stack grows up
+W		write back bit: 1 = update base register (Rn)
+L		load/store bit: 0 = store, 1 = load
+Rn		base register
+Rd		destination/source register		
+Fd		floating point destination register
+Fn		floating point source register
+Fm		floating point source register or floating point constant
+
+uv		transfer length (TABLE 1)
+wx		register count (TABLE 2)
+abcd		arithmetic opcode (TABLES 3 & 4)
+ef		destination size (rounding precision) (TABLE 5)
+gh		rounding mode (TABLE 6)
+j		dyadic/monadic bit: 0 = dyadic, 1 = monadic
+i 		constant bit: 1 = constant (TABLE 6)
+*/
+
+/*
+TABLE 1
++-------------------------+---+---+---------+---------+
+|  Precision              | u | v | FPSR.EP | length  |
++-------------------------+---+---+---------+---------+
+| Single                  | 0 ü 0 |    x    | 1 words |
+| Double                  | 1 ü 1 |    x    | 2 words |
+| Extended                | 1 ü 1 |    x    | 3 words |
+| Packed decimal          | 1 ü 1 |    0    | 3 words |
+| Expanded packed decimal | 1 ü 1 |    1    | 4 words |
++-------------------------+---+---+---------+---------+
+Note: x = don't care
+*/
+
+/*
+TABLE 2
++---+---+---------------------------------+
+| w | x | Number of registers to transfer |
++---+---+---------------------------------+
+| 0 ü 1 |  1                              |
+| 1 ü 0 |  2                              |
+| 1 ü 1 |  3                              |
+| 0 ü 0 |  4                              |
++---+---+---------------------------------+
+*/
+
+/*
+TABLE 3: Dyadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description           | Operation             |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | ADF      | Add                   | Fd := Fn + Fm         |
+| 0 | 0 | 0 | 1 | MUF      | Multiply              | Fd := Fn * Fm         |
+| 0 | 0 | 1 | 0 | SUF      | Subtract              | Fd := Fn - Fm         |
+| 0 | 0 | 1 | 1 | RSF      | Reverse subtract      | Fd := Fm - Fn         |
+| 0 | 1 | 0 | 0 | DVF      | Divide                | Fd := Fn / Fm         |
+| 0 | 1 | 0 | 1 | RDF      | Reverse divide        | Fd := Fm / Fn         |
+| 0 | 1 | 1 | 0 | POW      | Power                 | Fd := Fn ^ Fm         |
+| 0 | 1 | 1 | 1 | RPW      | Reverse power         | Fd := Fm ^ Fn         |
+| 1 | 0 | 0 | 0 | RMF      | Remainder             | Fd := IEEE rem(Fn/Fm) |
+| 1 | 0 | 0 | 1 | FML      | Fast Multiply         | Fd := Fn * Fm         |
+| 1 | 0 | 1 | 0 | FDV      | Fast Divide           | Fd := Fn / Fm         |
+| 1 | 0 | 1 | 1 | FRD      | Fast reverse divide   | Fd := Fm / Fn         |
+| 1 | 1 | 0 | 0 | POL      | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm)  |
+| 1 | 1 | 0 | 1 |          | undefined instruction | trap                  |
+| 1 | 1 | 1 | 0 |          | undefined instruction | trap                  |
+| 1 | 1 | 1 | 1 |          | undefined instruction | trap                  |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: POW, RPW, POL are deprecated, and are available for backwards
+      compatibility only.
+*/
+
+/*
+TABLE 4: Monadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description           | Operation             |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | MVF      | Move                  | Fd := Fm              |
+| 0 | 0 | 0 | 1 | MNF      | Move negated          | Fd := - Fm            |
+| 0 | 0 | 1 | 0 | ABS      | Absolute value        | Fd := abs(Fm)         |
+| 0 | 0 | 1 | 1 | RND      | Round to integer      | Fd := int(Fm)         |
+| 0 | 1 | 0 | 0 | SQT      | Square root           | Fd := sqrt(Fm)        |
+| 0 | 1 | 0 | 1 | LOG      | Log base 10           | Fd := log10(Fm)       |
+| 0 | 1 | 1 | 0 | LGN      | Log base e            | Fd := ln(Fm)          |
+| 0 | 1 | 1 | 1 | EXP      | Exponent              | Fd := e ^ Fm          |
+| 1 | 0 | 0 | 0 | SIN      | Sine                  | Fd := sin(Fm)         |
+| 1 | 0 | 0 | 1 | COS      | Cosine                | Fd := cos(Fm)         |
+| 1 | 0 | 1 | 0 | TAN      | Tangent               | Fd := tan(Fm)         |
+| 1 | 0 | 1 | 1 | ASN      | Arc Sine              | Fd := arcsin(Fm)      |
+| 1 | 1 | 0 | 0 | ACS      | Arc Cosine            | Fd := arccos(Fm)      |
+| 1 | 1 | 0 | 1 | ATN      | Arc Tangent           | Fd := arctan(Fm)      |
+| 1 | 1 | 1 | 0 | URD      | Unnormalized round    | Fd := int(Fm)         |
+| 1 | 1 | 1 | 1 | NRM      | Normalize             | Fd := norm(Fm)        |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
+      available for backwards compatibility only.
+*/
+
+/*
+TABLE 5
++-------------------------+---+---+
+|  Rounding Precision     | e | f |
++-------------------------+---+---+
+| IEEE Single precision   | 0 ü 0 |
+| IEEE Double precision   | 0 ü 1 |
+| IEEE Extended precision | 1 ü 0 |
+| undefined (trap)        | 1 ü 1 |
++-------------------------+---+---+
+*/
+
+/*
+TABLE 5
++---------------------------------+---+---+
+|  Rounding Mode                  | g | h |
++---------------------------------+---+---+
+| Round to nearest (default)      | 0 ü 0 |
+| Round toward plus infinity      | 0 ü 1 |
+| Round toward negative infinity  | 1 ü 0 |
+| Round toward zero               | 1 ü 1 |
++---------------------------------+---+---+
+*/
+
+/*
+===
+=== Definitions for load and store instructions
+===
+*/
+
+/* bit masks */
+#define BIT_PREINDEX	0x01000000
+#define BIT_UP		0x00800000
+#define BIT_WRITE_BACK	0x00200000
+#define BIT_LOAD	0x00100000
+
+/* masks for load/store */
+#define MASK_CPDT		0x0c000000  /* data processing opcode */
+#define MASK_OFFSET		0x000000ff
+#define MASK_TRANSFER_LENGTH	0x00408000
+#define MASK_REGISTER_COUNT	MASK_TRANSFER_LENGTH
+#define MASK_COPROCESSOR	0x00000f00
+
+/* Tests for transfer length */
+#define TRANSFER_SINGLE		0x00000000
+#define TRANSFER_DOUBLE		0x00008000
+#define TRANSFER_EXTENDED	0x00400000
+#define TRANSFER_PACKED		MASK_TRANSFER_LENGTH
+
+/* Get the coprocessor number from the opcode. */
+#define getCoprocessorNumber(opcode)	((opcode & MASK_COPROCESSOR) >> 8)
+
+/* Get the offset from the opcode. */
+#define getOffset(opcode)		(opcode & MASK_OFFSET)
+
+/* Tests for specific data transfer load/store opcodes. */
+#define TEST_OPCODE(opcode,mask)	(((opcode) & (mask)) == (mask))
+
+#define LOAD_OP(opcode)   TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
+#define STORE_OP(opcode)  ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
+
+#define LDF_OP(opcode)	(LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define LFM_OP(opcode)	(LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+#define STF_OP(opcode)	(STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define SFM_OP(opcode)	(STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+
+#define PREINDEXED(opcode)		((opcode & BIT_PREINDEX) != 0)
+#define POSTINDEXED(opcode)		((opcode & BIT_PREINDEX) == 0)
+#define BIT_UP_SET(opcode)		((opcode & BIT_UP) != 0)
+#define BIT_UP_CLEAR(opcode)		((opcode & BIT_DOWN) == 0)
+#define WRITE_BACK(opcode)		((opcode & BIT_WRITE_BACK) != 0)
+#define LOAD(opcode)			((opcode & BIT_LOAD) != 0)
+#define STORE(opcode)			((opcode & BIT_LOAD) == 0)
+
+/*
+===
+=== Definitions for arithmetic instructions
+===
+*/
+/* bit masks */
+#define BIT_MONADIC	0x00008000
+#define BIT_CONSTANT	0x00000008
+
+#define CONSTANT_FM(opcode)		((opcode & BIT_CONSTANT) != 0)
+#define MONADIC_INSTRUCTION(opcode)	((opcode & BIT_MONADIC) != 0)
+
+/* instruction identification masks */
+#define MASK_CPDO		0x0e000000  /* arithmetic opcode */
+#define MASK_ARITHMETIC_OPCODE	0x00f08000
+#define MASK_DESTINATION_SIZE	0x00080080
+
+/* dyadic arithmetic opcodes. */
+#define ADF_CODE	0x00000000
+#define MUF_CODE	0x00100000
+#define SUF_CODE	0x00200000
+#define RSF_CODE	0x00300000
+#define DVF_CODE	0x00400000
+#define RDF_CODE	0x00500000
+#define POW_CODE	0x00600000
+#define RPW_CODE	0x00700000
+#define RMF_CODE	0x00800000
+#define FML_CODE	0x00900000
+#define FDV_CODE	0x00a00000
+#define FRD_CODE	0x00b00000
+#define POL_CODE	0x00c00000
+/* 0x00d00000 is an invalid dyadic arithmetic opcode */
+/* 0x00e00000 is an invalid dyadic arithmetic opcode */
+/* 0x00f00000 is an invalid dyadic arithmetic opcode */
+
+/* monadic arithmetic opcodes. */
+#define MVF_CODE	0x00008000
+#define MNF_CODE	0x00108000
+#define ABS_CODE	0x00208000
+#define RND_CODE	0x00308000
+#define SQT_CODE	0x00408000
+#define LOG_CODE	0x00508000
+#define LGN_CODE	0x00608000
+#define EXP_CODE	0x00708000
+#define SIN_CODE	0x00808000
+#define COS_CODE	0x00908000
+#define TAN_CODE	0x00a08000
+#define ASN_CODE	0x00b08000
+#define ACS_CODE	0x00c08000
+#define ATN_CODE	0x00d08000
+#define URD_CODE	0x00e08000
+#define NRM_CODE	0x00f08000
+
+/*
+===
+=== Definitions for register transfer and comparison instructions
+===
+*/
+
+#define MASK_CPRT		0x0e000010  /* register transfer opcode */
+#define MASK_CPRT_CODE		0x00f00000
+#define FLT_CODE		0x00000000
+#define FIX_CODE		0x00100000
+#define WFS_CODE		0x00200000
+#define RFS_CODE		0x00300000
+#define WFC_CODE		0x00400000
+#define RFC_CODE		0x00500000
+#define CMF_CODE		0x00900000
+#define CNF_CODE		0x00b00000
+#define CMFE_CODE		0x00d00000
+#define CNFE_CODE		0x00f00000
+
+/*
+===
+=== Common definitions
+===
+*/
+
+/* register masks */
+#define MASK_Rd		0x0000f000
+#define MASK_Rn		0x000f0000
+#define MASK_Fd		0x00007000
+#define MASK_Fm		0x00000007
+#define MASK_Fn		0x00070000
+
+/* condition code masks */
+#define CC_MASK		0xf0000000
+#define CC_NEGATIVE	0x80000000
+#define CC_ZERO		0x40000000
+#define CC_CARRY	0x20000000
+#define CC_OVERFLOW	0x10000000
+#define CC_EQ		0x00000000
+#define CC_NE		0x10000000
+#define CC_CS		0x20000000
+#define CC_HS		CC_CS
+#define CC_CC		0x30000000
+#define CC_LO		CC_CC
+#define CC_MI		0x40000000
+#define CC_PL		0x50000000
+#define CC_VS		0x60000000
+#define CC_VC		0x70000000
+#define CC_HI		0x80000000
+#define CC_LS		0x90000000
+#define CC_GE		0xa0000000
+#define CC_LT		0xb0000000
+#define CC_GT		0xc0000000
+#define CC_LE		0xd0000000
+#define CC_AL		0xe0000000
+#define CC_NV		0xf0000000
+
+/* rounding masks/values */
+#define MASK_ROUNDING_MODE	0x00000060
+#define ROUND_TO_NEAREST	0x00000000
+#define ROUND_TO_PLUS_INFINITY	0x00000020
+#define ROUND_TO_MINUS_INFINITY	0x00000040
+#define ROUND_TO_ZERO		0x00000060
+
+#define MASK_ROUNDING_PRECISION	0x00080080
+#define ROUND_SINGLE		0x00000000
+#define ROUND_DOUBLE		0x00000080
+#define ROUND_EXTENDED		0x00080000
+
+/* Get the condition code from the opcode. */
+#define getCondition(opcode)		(opcode >> 28)
+
+/* Get the source register from the opcode. */
+#define getRn(opcode)			((opcode & MASK_Rn) >> 16)
+
+/* Get the destination floating point register from the opcode. */
+#define getFd(opcode)			((opcode & MASK_Fd) >> 12)
+
+/* Get the first source floating point register from the opcode. */
+#define getFn(opcode)		((opcode & MASK_Fn) >> 16)
+
+/* Get the second source floating point register from the opcode. */
+#define getFm(opcode)		(opcode & MASK_Fm)
+
+/* Get the destination register from the opcode. */
+#define getRd(opcode)		((opcode & MASK_Rd) >> 12)
+
+/* Get the rounding mode from the opcode. */
+#define getRoundingMode(opcode)		((opcode & MASK_ROUNDING_MODE) >> 5)
+
+float32 getSingleConstant(const unsigned int nIndex);
+float64 getDoubleConstant(const unsigned int nIndex);
+floatx80 getExtendedConstant(const unsigned int nIndex);
+
+unsigned int getRegisterCount(const unsigned int opcode);
+unsigned int getDestinationSize(const unsigned int opcode);
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/fpsr.h linux.18p17/arch/arm/nwfpe/fpsr.h
--- linux.vanilla/arch/arm/nwfpe/fpsr.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/fpsr.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,108 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPSR_H__
+#define __FPSR_H__
+
+/*
+The FPSR is a 32 bit register consisting of 4 parts, each exactly
+one byte.
+
+	SYSTEM ID
+	EXCEPTION TRAP ENABLE BYTE
+	SYSTEM CONTROL BYTE
+	CUMULATIVE EXCEPTION FLAGS BYTE
+	
+The FPCR is a 32 bit register consisting of bit flags.
+*/
+
+/* SYSTEM ID
+------------
+Note: the system id byte is read only  */
+
+typedef unsigned int FPSR;  /* type for floating point status register */
+typedef unsigned int FPCR;  /* type for floating point control register */
+
+#define MASK_SYSID		0xff000000
+#define BIT_HARDWARE		0x80000000
+#define FP_EMULATOR		0x01000000	/* System ID for emulator */ 
+#define FP_ACCELERATOR		0x81000000	/* System ID for FPA11 */
+
+/* EXCEPTION TRAP ENABLE BYTE
+----------------------------- */
+
+#define MASK_TRAP_ENABLE	0x00ff0000
+#define MASK_TRAP_ENABLE_STRICT	0x001f0000
+#define BIT_IXE		0x00100000   /* inexact exception enable */
+#define BIT_UFE		0x00080000   /* underflow exception enable */
+#define BIT_OFE		0x00040000   /* overflow exception enable */
+#define BIT_DZE		0x00020000   /* divide by zero exception enable */
+#define BIT_IOE		0x00010000   /* invalid operation exception enable */
+
+/* SYSTEM CONTROL BYTE
+---------------------- */
+
+#define MASK_SYSTEM_CONTROL	0x0000ff00
+#define MASK_TRAP_STRICT	0x00001f00
+
+#define BIT_AC	0x00001000	/* use alternative C-flag definition
+				   for compares */
+#define BIT_EP	0x00000800	/* use expanded packed decimal format */
+#define BIT_SO	0x00000400	/* select synchronous operation of FPA */
+#define BIT_NE	0x00000200	/* NaN exception bit */
+#define BIT_ND	0x00000100	/* no denormalized numbers bit */
+
+/* CUMULATIVE EXCEPTION FLAGS BYTE
+---------------------------------- */
+
+#define MASK_EXCEPTION_FLAGS		0x000000ff
+#define MASK_EXCEPTION_FLAGS_STRICT	0x0000001f
+
+#define BIT_IXC		0x00000010	/* inexact exception flag */
+#define BIT_UFC		0x00000008	/* underflow exception flag */
+#define BIT_OFC		0x00000004	/* overfloat exception flag */
+#define BIT_DZC		0x00000002	/* divide by zero exception flag */
+#define BIT_IOC		0x00000001	/* invalid operation exception flag */
+
+/* Floating Point Control Register
+----------------------------------*/
+
+#define BIT_RU		0x80000000	/* rounded up bit */
+#define BIT_IE		0x10000000	/* inexact bit */
+#define BIT_MO		0x08000000	/* mantissa overflow bit */
+#define BIT_EO		0x04000000	/* exponent overflow bit */
+#define BIT_SB		0x00000800	/* store bounce */
+#define BIT_AB		0x00000400	/* arithmetic bounce */
+#define BIT_RE		0x00000200	/* rounding exception */
+#define BIT_DA		0x00000100	/* disable FPA */
+
+#define MASK_OP		0x00f08010	/* AU operation code */
+#define MASK_PR		0x00080080	/* AU precision */
+#define MASK_S1		0x00070000	/* AU source register 1 */
+#define MASK_S2		0x00000007	/* AU source register 2 */
+#define MASK_DS		0x00007000	/* AU destination register */
+#define MASK_RM		0x00000060	/* AU rounding mode */
+#define MASK_ALU	0x9cfff2ff	/* only ALU can write these bits */
+#define MASK_RESET	0x00000d00	/* bits set on reset, all others cleared */
+#define MASK_WFC	MASK_RESET
+#define MASK_RFC	~MASK_RESET
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/milieu.h linux.18p17/arch/arm/nwfpe/milieu.h
--- linux.vanilla/arch/arm/nwfpe/milieu.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/milieu.h	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,48 @@
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Include common integer types and flags.
+-------------------------------------------------------------------------------
+*/
+#include "ARM-gcc.h"
+
+/*
+-------------------------------------------------------------------------------
+Symbolic Boolean literals.
+-------------------------------------------------------------------------------
+*/
+enum {
+    FALSE = 0,
+    TRUE  = 1
+};
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/single_cpdo.c linux.18p17/arch/arm/nwfpe/single_cpdo.c
--- linux.vanilla/arch/arm/nwfpe/single_cpdo.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/single_cpdo.c	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,259 @@
+/*
+    NetWinder Floating Point Emulator
+    (c) Corel Computer Corporation, 1998
+
+    Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+float32 getSingleConstant(unsigned int);
+
+float32 float32_exp(float32 Fm);
+float32 float32_ln(float32 Fm);
+float32 float32_sin(float32 rFm);
+float32 float32_cos(float32 rFm);
+float32 float32_arcsin(float32 rFm);
+float32 float32_arctan(float32 rFm);
+float32 float32_log(float32 rFm);
+float32 float32_tan(float32 rFm);
+float32 float32_arccos(float32 rFm);
+float32 float32_pow(float32 rFn,float32 rFm);
+float32 float32_pol(float32 rFn,float32 rFm);
+
+unsigned int SingleCPDO(const unsigned int opcode)
+{
+   float32 rFm, rFn;
+   unsigned int Fd, Fm, Fn, nRc = 1;
+
+   Fm = getFm(opcode);
+   if (CONSTANT_FM(opcode))
+   {
+     rFm = getSingleConstant(Fm);
+   }
+   else
+   {  
+     switch (fpa11->fpreg[Fm].fType)
+     {
+        case typeSingle:
+          rFm = fpa11->fpreg[Fm].fValue.fSingle;
+        break;
+        
+        default: return 0;
+     }
+   }
+
+   if (!MONADIC_INSTRUCTION(opcode))
+   {
+      Fn = getFn(opcode);
+      switch (fpa11->fpreg[Fn].fType)
+      {
+        case typeSingle:
+          rFn = fpa11->fpreg[Fn].fValue.fSingle;
+        break;
+
+        default: return 0;
+      }
+   }
+
+   Fd = getFd(opcode);
+   switch (opcode & MASK_ARITHMETIC_OPCODE)
+   {
+      /* dyadic opcodes */
+      case ADF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_add(rFn,rFm);
+      break;
+
+      case MUF_CODE:
+      case FML_CODE:
+        fpa11->fpreg[Fd].fValue.fSingle = float32_mul(rFn,rFm);
+      break;
+
+      case SUF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFn,rFm);
+      break;
+
+      case RSF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFm,rFn);
+      break;
+
+      case DVF_CODE:
+      case FDV_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFn,rFm);
+      break;
+
+      case RDF_CODE:
+      case FRD_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFm,rFn);
+      break;
+
+#if 0
+      case POW_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFn,rFm);
+      break;
+
+      case RPW_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFm,rFn);
+      break;
+#endif
+
+      case RMF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_rem(rFn,rFm);
+      break;
+
+#if 0
+      case POL_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_pol(rFn,rFm);
+      break;
+#endif
+
+      /* monadic opcodes */
+      case MVF_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case MNF_CODE:
+         rFm ^= 0x80000000;
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case ABS_CODE:
+         rFm &= 0x7fffffff;
+         fpa11->fpreg[Fd].fValue.fSingle = rFm;
+      break;
+
+      case RND_CODE:
+      case URD_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = 
+             int32_to_float32(float32_to_int32(rFm));
+      break;
+
+      case SQT_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sqrt(rFm);
+      break;
+
+#if 0
+      case LOG_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_log(rFm);
+      break;
+
+      case LGN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_ln(rFm);
+      break;
+
+      case EXP_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_exp(rFm);
+      break;
+
+      case SIN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_sin(rFm);
+      break;
+
+      case COS_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_cos(rFm);
+      break;
+
+      case TAN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_tan(rFm);
+      break;
+
+      case ASN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arcsin(rFm);
+      break;
+
+      case ACS_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arccos(rFm);
+      break;
+
+      case ATN_CODE:
+         fpa11->fpreg[Fd].fValue.fSingle = float32_arctan(rFm);
+      break;
+#endif
+
+      case NRM_CODE:
+      break;
+      
+      default:
+      {
+        nRc = 0;
+      }
+   }
+
+   if (0 != nRc) fpa11->fpreg[Fd].fType = typeSingle;
+   return nRc;
+}
+
+#if 0
+float32 float32_exp(float32 Fm)
+{
+//series
+}
+
+float32 float32_ln(float32 Fm)
+{
+//series
+}
+
+float32 float32_sin(float32 rFm)
+{
+//series
+}
+
+float32 float32_cos(float32 rFm)
+{
+//series
+}
+
+float32 float32_arcsin(float32 rFm)
+{
+//series
+}
+
+float32 float32_arctan(float32 rFm)
+{
+  //series
+}
+
+float32 float32_arccos(float32 rFm)
+{
+   //return float32_sub(halfPi,float32_arcsin(rFm));
+}
+
+float32 float32_log(float32 rFm)
+{
+  return float32_div(float32_ln(rFm),getSingleConstant(7));
+}
+
+float32 float32_tan(float32 rFm)
+{
+  return float32_div(float32_sin(rFm),float32_cos(rFm));
+}
+
+float32 float32_pow(float32 rFn,float32 rFm)
+{
+  return float32_exp(float32_mul(rFm,float32_ln(rFn))); 
+}
+
+float32 float32_pol(float32 rFn,float32 rFm)
+{
+  return float32_arctan(float32_div(rFn,rFm)); 
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/softfloat-macros linux.18p17/arch/arm/nwfpe/softfloat-macros
--- linux.vanilla/arch/arm/nwfpe/softfloat-macros	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/softfloat-macros	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,740 @@
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'.  If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1.  The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 32, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+    bits32 z;
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 32 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+    *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'.  If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1.  The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 64, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+    bits64 z;
+
+ __asm__("@shift64RightJamming -- start");   
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 64 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+ __asm__("@shift64RightJamming -- end");   
+    *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+_plus_ the number of bits given in `count'.  The shifted result is at most
+64 nonzero bits; this is stored at the location pointed to by `z0Ptr'.  The
+bits shifted off form a second 64-bit result as follows:  The _last_ bit
+shifted off is the most-significant bit of the extra result, and the other
+63 bits of the extra result are all zero if and only if _all_but_the_last_
+bits shifted off were all zero.  This extra result is stored in the location
+pointed to by `z1Ptr'.  The value of `count' can be arbitrarily large.
+    (This routine makes more sense if `a0' and `a1' are considered to form a
+fixed-point value with binary point between `a0' and `a1'.  This fixed-point
+value is shifted right by the number of bits given in `count', and the
+integer part of the result is returned at the location pointed to by
+`z0Ptr'.  The fractional part of the result may be slightly corrupted as
+described above, and is returned at the location pointed to by `z1Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift64ExtraRightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1 != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'.  Any bits shifted off are lost.  The value
+of `count' can be arbitrarily large; in particular, if `count' is greater
+than 128, the result will be 0.  The result is broken into two 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128Right(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count );
+        z0 = a0>>count;
+    }
+    else {
+        z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'.  If any nonzero bits are shifted off, they
+are ``jammed'' into the least significant bit of the result by setting the
+least significant bit to 1.  The value of `count' can be arbitrarily large;
+in particular, if `count' is greater than 128, the result will be either 0
+or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+nonzero.  The result is broken into two 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128RightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else if ( count < 128 ) {
+            z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+by 64 _plus_ the number of bits given in `count'.  The shifted result is
+at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+stored at the locations pointed to by `z0Ptr' and `z1Ptr'.  The bits shifted
+off form a third 64-bit result as follows:  The _last_ bit shifted off is
+the most-significant bit of the extra result, and the other 63 bits of the
+extra result are all zero if and only if _all_but_the_last_ bits shifted off
+were all zero.  This extra result is stored in the location pointed to by
+`z2Ptr'.  The value of `count' can be arbitrarily large.
+    (This routine makes more sense if `a0', `a1', and `a2' are considered
+to form a fixed-point value with binary point between `a1' and `a2'.  This
+fixed-point value is shifted right by the number of bits given in `count',
+and the integer part of the result is returned at the locations pointed to
+by `z0Ptr' and `z1Ptr'.  The fractional part of the result may be slightly
+corrupted as described above, and is returned at the location pointed to by
+`z2Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128ExtraRightJamming(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z2 = a2;
+        z1 = a1;
+        z0 = a0;
+    }
+    else {
+        if ( count < 64 ) {
+            z2 = a1<<negCount;
+            z1 = ( a0<<negCount ) | ( a1>>count );
+            z0 = a0>>count;
+        }
+        else {
+            if ( count == 64 ) {
+                z2 = a1;
+                z1 = a0;
+            }
+            else {
+                a2 |= a1;
+                if ( count < 128 ) {
+                    z2 = a0<<negCount;
+                    z1 = a0>>( count & 63 );
+                }
+                else {
+                    z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+                    z1 = 0;
+                }
+            }
+            z0 = 0;
+        }
+        z2 |= ( a2 != 0 );
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+number of bits given in `count'.  Any bits shifted off are lost.  The value
+of `count' must be less than 64.  The result is broken into two 64-bit
+pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift128Left(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1<<count;
+    *z0Ptr =
+        ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+by the number of bits given in `count'.  Any bits shifted off are lost.
+The value of `count' must be less than 64.  The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift192Left(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount;
+
+    z2 = a2<<count;
+    z1 = a1<<count;
+    z0 = a0<<count;
+    if ( 0 < count ) {
+        negCount = ( ( - count ) & 63 );
+        z1 |= a2>>negCount;
+        z0 |= a1>>negCount;
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+value formed by concatenating `b0' and `b1'.  Addition is modulo 2^128, so
+any carry out is lost.  The result is broken into two 64-bit pieces which
+are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z1;
+
+    z1 = a1 + b1;
+    *z1Ptr = z1;
+    *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+192-bit value formed by concatenating `b0', `b1', and `b2'.  Addition is
+modulo 2^192, so any carry out is lost.  The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 carry0, carry1;
+
+    z2 = a2 + b2;
+    carry1 = ( z2 < a2 );
+    z1 = a1 + b1;
+    carry0 = ( z1 < a1 );
+    z0 = a0 + b0;
+    z1 += carry1;
+    z0 += ( z1 < carry1 );
+    z0 += carry0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+128-bit value formed by concatenating `a0' and `a1'.  Subtraction is modulo
+2^128, so any borrow out (carry out) is lost.  The result is broken into two
+64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+`z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1 - b1;
+    *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+Subtraction is modulo 2^192, so any borrow out (carry out) is lost.  The
+result is broken into three 64-bit pieces which are stored at the locations
+pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 borrow0, borrow1;
+
+    z2 = a2 - b2;
+    borrow1 = ( a2 < b2 );
+    z1 = a1 - b1;
+    borrow0 = ( a1 < b1 );
+    z0 = a0 - b0;
+    z0 -= ( z1 < borrow1 );
+    z1 -= borrow1;
+    z0 -= borrow0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies `a' by `b' to obtain a 128-bit product.  The product is broken
+into two 64-bit pieces which are stored at the locations pointed to by
+`z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits32 aHigh, aLow, bHigh, bLow;
+    bits64 z0, zMiddleA, zMiddleB, z1;
+
+    aLow = a;
+    aHigh = a>>32;
+    bLow = b;
+    bHigh = b>>32;
+    z1 = ( (bits64) aLow ) * bLow;
+    zMiddleA = ( (bits64) aLow ) * bHigh;
+    zMiddleB = ( (bits64) aHigh ) * bLow;
+    z0 = ( (bits64) aHigh ) * bHigh;
+    zMiddleA += zMiddleB;
+    z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+    zMiddleA <<= 32;
+    z1 += zMiddleA;
+    z0 += ( z1 < zMiddleA );
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' by `b' to
+obtain a 192-bit product.  The product is broken into three 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+`z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128By64To192(
+     bits64 a0,
+     bits64 a1,
+     bits64 b,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2, more1;
+
+    mul64To128( a1, b, &z1, &z2 );
+    mul64To128( a0, b, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+product.  The product is broken into four 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128To256(
+     bits64 a0,
+     bits64 a1,
+     bits64 b0,
+     bits64 b1,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr,
+     bits64 *z3Ptr
+ )
+{
+    bits64 z0, z1, z2, z3;
+    bits64 more1, more2;
+
+    mul64To128( a1, b1, &z2, &z3 );
+    mul64To128( a1, b0, &z1, &more2 );
+    add128( z1, more2, 0, z2, &z1, &z2 );
+    mul64To128( a0, b0, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    mul64To128( a0, b1, &more1, &more2 );
+    add128( more1, more2, 0, z2, &more1, &z2 );
+    add128( z0, z1, 0, more1, &z0, &z1 );
+    *z3Ptr = z3;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the 64-bit integer quotient obtained by dividing
+`b' into the 128-bit value formed by concatenating `a0' and `a1'.  The
+divisor `b' must be at least 2^63.  If q is the exact quotient truncated
+toward zero, the approximation returned lies between q and q + 2 inclusive.
+If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+unsigned integer is returned.
+-------------------------------------------------------------------------------
+*/
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+    bits64 b0, b1;
+    bits64 rem0, rem1, term0, term1;
+    bits64 z;
+    if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+    b0 = b>>32;
+    z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+    mul64To128( b, z, &term0, &term1 );
+    sub128( a0, a1, term0, term1, &rem0, &rem1 );
+    while ( ( (sbits64) rem0 ) < 0 ) {
+        z -= LIT64( 0x100000000 );
+        b1 = b<<32;
+        add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+    }
+    rem0 = ( rem0<<32 ) | ( rem1>>32 );
+    z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the square root of the 32-bit significand given
+by `a'.  Considered as an integer, `a' must be at least 2^31.  If bit 0 of
+`aExp' (the least significant bit) is 1, the integer returned approximates
+2^31*sqrt(`a'/2^31), where `a' is considered an integer.  If bit 0 of `aExp'
+is 0, the integer returned approximates 2^31*sqrt(`a'/2^30).  In either
+case, the approximation returned lies strictly within +/-2 of the exact
+value.
+-------------------------------------------------------------------------------
+*/
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+    static const bits16 sqrtOddAdjustments[] = {
+        0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+        0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+    };
+    static const bits16 sqrtEvenAdjustments[] = {
+        0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+        0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+    };
+    int8 index;
+    bits32 z;
+
+    index = ( a>>27 ) & 15;
+    if ( aExp & 1 ) {
+        z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
+        z = ( ( a / z )<<14 ) + ( z<<15 );
+        a >>= 1;
+    }
+    else {
+        z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
+        z = a / z + z;
+        z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+        if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+    }
+    return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'.  If `a' is zero, 32 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros32( bits32 a )
+{
+    static const int8 countLeadingZerosHigh[] = {
+        8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < 0x10000 ) {
+        shiftCount += 16;
+        a <<= 16;
+    }
+    if ( a < 0x1000000 ) {
+        shiftCount += 8;
+        a <<= 8;
+    }
+    shiftCount += countLeadingZerosHigh[ a>>24 ];
+    return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'.  If `a' is zero, 64 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros64( bits64 a )
+{
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < ( (bits64) 1 )<<32 ) {
+        shiftCount += 32;
+    }
+    else {
+        a >>= 32;
+    }
+    shiftCount += countLeadingZeros32( a );
+    return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than the 128-bit value formed by concatenating `b0' and `b1'.  Otherwise,
+returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 != b0 ) || ( a1 != b1 );
+
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/softfloat-specialize linux.18p17/arch/arm/nwfpe/softfloat-specialize
--- linux.vanilla/arch/arm/nwfpe/softfloat-specialize	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/softfloat-specialize	Fri Sep 15 23:28:37 2000
@@ -0,0 +1,471 @@
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Underflow tininess-detection mode, statically initialized to default value.
+(The declaration in `softfloat.h' must match the `int8' type here.)
+-------------------------------------------------------------------------------
+*/
+int8 float_detect_tininess = float_tininess_after_rounding;
+
+/*
+-------------------------------------------------------------------------------
+Raises the exceptions specified by `flags'.  Floating-point traps can be
+defined here if desired.  It is currently not possible for such a trap to
+substitute a result value.  If traps are not implemented, this routine
+should be simply `float_exception_flags |= flags;'.
+
+ScottB:  November 4, 1998
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+-------------------------------------------------------------------------------
+void float_raise( int8 flags )
+{
+    float_exception_flags |= flags;
+}
+*/
+
+/*
+-------------------------------------------------------------------------------
+Internal canonical NaN format.
+-------------------------------------------------------------------------------
+*/
+typedef struct {
+    flag sign;
+    bits64 high, low;
+} commonNaNT;
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated single-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float32_default_nan 0xFFFFFFFF
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_nan( float32 a )
+{
+
+    return ( 0xFF000000 < (bits32) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_signaling_nan( float32 a )
+{
+
+    return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float32ToCommonNaN( float32 a )
+{
+    commonNaNT z;
+
+    if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a>>31;
+    z.low = 0;
+    z.high = ( (bits64) a )<<41;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the single-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+
+    return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two single-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float32 propagateFloat32NaN( float32 a, float32 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float32_is_nan( a );
+    aIsSignalingNaN = float32_is_signaling_nan( a );
+    bIsNaN = float32_is_nan( b );
+    bIsSignalingNaN = float32_is_signaling_nan( b );
+    a |= 0x00400000;
+    b |= 0x00400000;
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated double-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float64_default_nan LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_nan( float64 a )
+{
+
+    return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_signaling_nan( float64 a )
+{
+
+    return
+           ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+        && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float64ToCommonNaN( float64 a )
+{
+    commonNaNT z;
+
+    if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a>>63;
+    z.low = 0;
+    z.high = a<<12;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the double-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+
+    return
+          ( ( (bits64) a.sign )<<63 )
+        | LIT64( 0x7FF8000000000000 )
+        | ( a.high>>12 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two double-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float64 propagateFloat64NaN( float64 a, float64 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float64_is_nan( a );
+    aIsSignalingNaN = float64_is_signaling_nan( a );
+    bIsNaN = float64_is_nan( b );
+    bIsSignalingNaN = float64_is_signaling_nan( b );
+    a |= LIT64( 0x0008000000000000 );
+    b |= LIT64( 0x0008000000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated extended double-precision NaN.  The
+`high' and `low' values hold the most- and least-significant bits,
+respectively.
+-------------------------------------------------------------------------------
+*/
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low  LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_nan( floatx80 a )
+{
+
+    return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_signaling_nan( floatx80 a )
+{
+    //register int lr;
+    bits64 aLow;
+
+    //__asm__("mov %0, lr" : : "g" (lr));
+    //fp_printk("floatx80_is_signalling_nan() called from 0x%08x\n",lr);
+    aLow = a.low & ~ LIT64( 0x4000000000000000 );
+    return
+           ( ( a.high & 0x7FFF ) == 0x7FFF )
+        && (bits64) ( aLow<<1 )
+        && ( a.low == aLow );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point NaN `a' to the canonical NaN format.  If `a' is a signaling NaN, the
+invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT floatx80ToCommonNaN( floatx80 a )
+{
+    commonNaNT z;
+
+    if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a.high>>15;
+    z.low = 0;
+    z.high = a.low<<1;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the extended
+double-precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+    floatx80 z;
+
+    z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
+    z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two extended double-precision floating-point values `a' and `b', one
+of which is a NaN, and returns the appropriate NaN result.  If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = floatx80_is_nan( a );
+    aIsSignalingNaN = floatx80_is_signaling_nan( a );
+    bIsNaN = floatx80_is_nan( b );
+    bIsSignalingNaN = floatx80_is_signaling_nan( b );
+    a.low |= LIT64( 0xC000000000000000 );
+    b.low |= LIT64( 0xC000000000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated quadruple-precision NaN.  The `high' and
+`low' values hold the most- and least-significant bits, respectively.
+-------------------------------------------------------------------------------
+*/
+#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF )
+#define float128_default_nan_low  LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_nan( float128 a )
+{
+
+    return
+           ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+        && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_signaling_nan( float128 a )
+{
+
+    return
+           ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+        && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point NaN
+`a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float128ToCommonNaN( float128 a )
+{
+    commonNaNT z;
+
+    if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+    z.sign = a.high>>63;
+    shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the quadruple-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+    float128 z;
+
+    shift128Right( a.high, a.low, 16, &z.high, &z.low );
+    z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two quadruple-precision floating-point values `a' and `b', one of
+which is a NaN, and returns the appropriate NaN result.  If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float128 propagateFloat128NaN( float128 a, float128 b )
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float128_is_nan( a );
+    aIsSignalingNaN = float128_is_signaling_nan( a );
+    bIsNaN = float128_is_nan( b );
+    bIsSignalingNaN = float128_is_signaling_nan( b );
+    a.high |= LIT64( 0x0000800000000000 );
+    b.high |= LIT64( 0x0000800000000000 );
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+    if ( aIsNaN ) {
+        return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+    }
+    else {
+        return b;
+    }
+
+}
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/softfloat.c linux.18p17/arch/arm/nwfpe/softfloat.c
--- linux.vanilla/arch/arm/nwfpe/softfloat.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/softfloat.c	Fri Sep 15 23:28:38 2000
@@ -0,0 +1,4877 @@
+/*
+===============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#include "milieu.h"
+#include "softfloat.h"
+
+/*
+-------------------------------------------------------------------------------
+Floating-point rounding mode, extended double-precision rounding precision,
+and exception flags.
+-------------------------------------------------------------------------------
+*/
+int8 float_rounding_mode = float_round_nearest_even;
+int8 floatx80_rounding_precision = 80;
+int8 float_exception_flags = 0;
+
+/*
+-------------------------------------------------------------------------------
+Primitive arithmetic functions, including multi-word arithmetic, and
+division and square root approximations.  (Can be specialized to target if
+desired.)
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-macros"
+
+/*
+-------------------------------------------------------------------------------
+Functions and definitions to determine:  (1) whether tininess for underflow
+is detected before or after rounding by default, (2) what (if anything)
+happens when exceptions are raised, (3) how signaling NaNs are distinguished
+from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+are propagated from function inputs to output.  These details are target-
+specific.
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-specialize"
+
+/*
+-------------------------------------------------------------------------------
+Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+and 7, and returns the properly rounded 32-bit integer corresponding to the
+input.  If `zSign' is nonzero, the input is negated before being converted
+to an integer.  Bit 63 of `absZ' must be zero.  Ordinarily, the fixed-point
+input is simply rounded to an integer, with the inexact exception raised if
+the input cannot be represented exactly as an integer.  If the fixed-point
+input is too large, however, the invalid exception is raised and the largest
+positive or negative integer is returned.
+-------------------------------------------------------------------------------
+*/
+static int32 roundAndPackInt32( flag zSign, bits64 absZ )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    int32 z;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = absZ & 0x7F;
+    absZ = ( absZ + roundIncrement )>>7;
+    absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    z = absZ;
+    if ( zSign ) z = - z;
+    if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+        float_exception_flags |= float_flag_invalid;
+        return zSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+    return a & 0x007FFFFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+    return ( a>>23 ) & 0xFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+    return a>>31;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal single-precision floating-point value represented
+by the denormalized significand `aSig'.  The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( aSig ) - 8;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+single-precision floating-point value, returning the result.  After being
+shifted into the proper positions, the three fields are simply added
+together to form the result.  This means that any integer portion of `zSig'
+will be added into the exponent.  Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+#if 0
+   float32 f;
+   __asm__("@ packFloat32;
+   	    mov %0, %1, asl #31;
+   	    orr %0, %2, asl #23;
+   	    orr %0, %3"
+   	    : /* no outputs */
+   	    : "g" (f), "g" (zSign), "g" (zExp), "g" (zSig)
+   	    : "cc");
+   return f;
+#else
+    return ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig;
+#endif 
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input.  Ordinarily, the abstract
+value is simply rounded and packed into the single-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal single-
+precision floating-point number.
+    The input significand `zSig' has its binary point between bits 30
+and 29, which is 7 bits to the left of the usual location.  This shifted
+significand must be normalized or smaller.  If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding.  In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x7F;
+    if ( 0xFD <= (bits16) zExp ) {
+        if (    ( 0xFD < zExp )
+             || (    ( zExp == 0xFD )
+                  && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact );
+            return packFloat32( zSign, 0xFF, 0 ) - ( roundIncrement == 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < 0x80000000 );
+            shift32RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x7F;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>7;
+    zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input.  This routine is just like
+`roundAndPackFloat32' except that `zSig' does not have to be normalized in
+any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( zSig ) - 1;
+    return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+    return a & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+    return ( a>>52 ) & 0x7FF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+    return a>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal double-precision floating-point value represented
+by the denormalized significand `aSig'.  The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig ) - 11;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+double-precision floating-point value, returning the result.  After being
+shifted into the proper positions, the three fields are simply added
+together to form the result.  This means that any integer portion of `zSig'
+will be added into the exponent.  Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+    return ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input.  Ordinarily, the abstract
+value is simply rounded and packed into the double-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal double-
+precision floating-point number.
+    The input significand `zSig' has its binary point between bits 62
+and 61, which is 10 bits to the left of the usual location.  This shifted
+significand must be normalized or smaller.  If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding.  In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int16 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x200;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x3FF;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x3FF;
+    if ( 0x7FD <= (bits16) zExp ) {
+        if (    ( 0x7FD < zExp )
+             || (    ( zExp == 0x7FD )
+                  && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            //register int lr;
+            //__asm__("mov %0, lr" :: "g" (lr));
+            //fp_printk("roundAndPackFloat64 called from 0x%08x\n",lr);
+            float_raise( float_flag_overflow | float_flag_inexact );
+            return packFloat64( zSign, 0x7FF, 0 ) - ( roundIncrement == 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+            shift64RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x3FF;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>10;
+    zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input.  This routine is just like
+`roundAndPackFloat64' except that `zSig' does not have to be normalized in
+any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( zSig ) - 1;
+    return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+    return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+    return a.high & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the extended double-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+    return a.high>>15;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal extended double-precision floating-point value
+represented by the denormalized significand `aSig'.  The normalized exponent
+and significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig );
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+extended double-precision floating-point value, returning the result.
+-------------------------------------------------------------------------------
+*/
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+    floatx80 z;
+
+    z.low = zSig;
+    z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input.  Ordinarily, the abstract value is
+rounded and packed into the extended double-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal extended
+double-precision floating-point number.
+    If `roundingPrecision' is 32 or 64, the result is rounded to the same
+number of bits as single or double precision, respectively.  Otherwise, the
+result is rounded to the full precision of the extended double-precision
+format.
+    The input significand must be normalized or smaller.  If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding.  The
+handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ roundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+    int64 roundIncrement, roundMask, roundBits;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    if ( roundingPrecision == 80 ) goto precision80;
+    if ( roundingPrecision == 64 ) {
+        roundIncrement = LIT64( 0x0000000000000400 );
+        roundMask = LIT64( 0x00000000000007FF );
+    }
+    else if ( roundingPrecision == 32 ) {
+        roundIncrement = LIT64( 0x0000008000000000 );
+        roundMask = LIT64( 0x000000FFFFFFFFFF );
+    }
+    else {
+        goto precision80;
+    }
+    zSig0 |= ( zSig1 != 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = roundMask;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig0 & roundMask;
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+           ) {
+            goto overflow;
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ( zSig0 <= zSig0 + roundIncrement );
+            shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+            zExp = 0;
+            roundBits = zSig0 & roundMask;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+            if ( roundBits ) float_exception_flags |= float_flag_inexact;
+            zSig0 += roundIncrement;
+            if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            roundIncrement = roundMask + 1;
+            if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+                roundMask |= roundIncrement;
+            }
+            zSig0 &= ~ roundMask;
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( roundBits ) float_exception_flags |= float_flag_inexact;
+    zSig0 += roundIncrement;
+    if ( zSig0 < roundIncrement ) {
+        ++zExp;
+        zSig0 = LIT64( 0x8000000000000000 );
+    }
+    roundIncrement = roundMask + 1;
+    if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+        roundMask |= roundIncrement;
+    }
+    zSig0 &= ~ roundMask;
+    if ( zSig0 == 0 ) zExp = 0;
+    return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+    increment = ( (sbits64) zSig1 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig1;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig1;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || (    ( zExp == 0x7FFE )
+                  && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+                  && increment
+                )
+           ) {
+            roundMask = 0;
+ overflow:
+            float_raise( float_flag_overflow | float_flag_inexact );
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+            }
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ! increment
+                || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+            shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+            zExp = 0;
+            if ( isTiny && zSig1 ) float_raise( float_flag_underflow );
+            if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig1 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig1;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig1;
+                }
+            }
+            if ( increment ) {
+                ++zSig0;
+                zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+                if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            }
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+    if ( increment ) {
+        ++zSig0;
+        if ( zSig0 == 0 ) {
+            ++zExp;
+            zSig0 = LIT64( 0x8000000000000000 );
+        }
+        else {
+            zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+        }
+    }
+    else {
+        if ( zSig0 == 0 ) zExp = 0;
+    }
+    
+    return packFloatx80( zSign, zExp, zSig0 );
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent
+`zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input.  This routine is just like
+`roundAndPackFloatx80' except that the input significand does not have to be
+normalized.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ normalizeRoundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+    int8 shiftCount;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 );
+    shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    zExp -= shiftCount;
+    return
+        roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the least-significant 64 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+    return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the most-significant 48 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+    return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the quadruple-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+    return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the quadruple-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+    return a.high>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal quadruple-precision floating-point value
+represented by the denormalized significand formed by the concatenation of
+`aSig0' and `aSig1'.  The normalized exponent is stored at the location
+pointed to by `zExpPtr'.  The most significant 49 bits of the normalized
+significand are stored at the location pointed to by `zSig0Ptr', and the
+least significant 64 bits of the normalized significand are stored at the
+location pointed to by `zSig1Ptr'.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat128Subnormal(
+     bits64 aSig0,
+     bits64 aSig1,
+     int32 *zExpPtr,
+     bits64 *zSig0Ptr,
+     bits64 *zSig1Ptr
+ )
+{
+    int8 shiftCount;
+
+    if ( aSig0 == 0 ) {
+        shiftCount = countLeadingZeros64( aSig1 ) - 15;
+        if ( shiftCount < 0 ) {
+            *zSig0Ptr = aSig1>>( - shiftCount );
+            *zSig1Ptr = aSig1<<( shiftCount & 63 );
+        }
+        else {
+            *zSig0Ptr = aSig1<<shiftCount;
+            *zSig1Ptr = 0;
+        }
+        *zExpPtr = - shiftCount - 63;
+    }
+    else {
+        shiftCount = countLeadingZeros64( aSig0 ) - 15;
+        shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+        *zExpPtr = 1 - shiftCount;
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', the exponent `zExp', and the significand formed
+by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+floating-point value, returning the result.  After being shifted into the
+proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+added together to form the most significant 32 bits of the result.  This
+means that any integer portion of `zSig0' will be added into the exponent.
+Since a properly normalized significand will have an integer portion equal
+to 1, the `zExp' input should be 1 less than the desired result exponent
+whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+    float128 z;
+
+    z.low = zSig1;
+    z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0', `zSig1',
+and `zSig2', and returns the proper quadruple-precision floating-point value
+corresponding to the abstract input.  Ordinarily, the abstract value is
+simply rounded and packed into the quadruple-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly.  If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned.  If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal quadruple-
+precision floating-point number.
+    The input significand must be normalized or smaller.  If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding.  In the
+usual case that the input significand is normalized, `zExp' must be 1 less
+than the ``true'' floating-point exponent.  The handling of underflow and
+overflow follows the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128
+ roundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 )
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+
+    roundingMode = float_rounding_mode;
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    increment = ( (sbits64) zSig2 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig2;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig2;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) zExp ) {
+        if (    ( 0x7FFD < zExp )
+             || (    ( zExp == 0x7FFD )
+                  && eq128(
+                         LIT64( 0x0001FFFFFFFFFFFF ),
+                         LIT64( 0xFFFFFFFFFFFFFFFF ),
+                         zSig0,
+                         zSig1
+                     )
+                  && increment
+                )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact );
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return
+                    packFloat128(
+                        zSign,
+                        0x7FFE,
+                        LIT64( 0x0000FFFFFFFFFFFF ),
+                        LIT64( 0xFFFFFFFFFFFFFFFF )
+                    );
+            }
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( float_detect_tininess == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ! increment
+                || lt128(
+                       zSig0,
+                       zSig1,
+                       LIT64( 0x0001FFFFFFFFFFFF ),
+                       LIT64( 0xFFFFFFFFFFFFFFFF )
+                   );
+            shift128ExtraRightJamming(
+                zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+            zExp = 0;
+            if ( isTiny && zSig2 ) float_raise( float_flag_underflow );
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig2 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig2;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig2;
+                }
+            }
+        }
+    }
+    if ( zSig2 ) float_exception_flags |= float_flag_inexact;
+    if ( increment ) {
+        add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+        zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+    }
+    else {
+        if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+    }
+    return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand formed by the concatenation of `zSig0' and `zSig1', and
+returns the proper quadruple-precision floating-point value corresponding to
+the abstract input.  This routine is just like `roundAndPackFloat128' except
+that the input significand has fewer bits and does not have to be normalized
+in any way.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float128
+ normalizeRoundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+    int8 shiftCount;
+    bits64 zSig2;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 ) - 15;
+    if ( 0 <= shiftCount ) {
+        zSig2 = 0;
+        shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    }
+    else {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+    }
+    zExp -= shiftCount;
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the single-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( int32 a )
+{
+    flag zSign;
+
+    if ( a == 0 ) return 0;
+    if ( a == 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+    zSign = ( a < 0 );
+    return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the double-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 int32_to_float64( int32 a )
+{
+    flag aSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return 0;
+    aSign = ( a < 0 );
+    absA = aSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 21;
+    zSig = absA;
+    return packFloat64( aSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a'
+to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 int32_to_floatx80( int32 a )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 32;
+    zSig = absA;
+    return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the quadruple-precision floating-point format.  The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 int32_to_float128( int32 a )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig0;
+
+    if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 17;
+    zSig0 = absA;
+    return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32( float32 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    bits64 zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= 0x00800000;
+    shiftCount = 0xAF - aExp;
+    zSig = aSig;
+    zSig <<= 32;
+    if ( 0 < shiftCount ) shift64RightJamming( zSig, shiftCount, &zSig );
+    return roundAndPackInt32( aSign, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32_round_to_zero( float32 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    int32 z;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    shiftCount = aExp - 0x9E;
+    if ( 0 <= shiftCount ) {
+        if ( a == 0xCF000000 ) return 0x80000000;
+        float_raise( float_flag_invalid );
+        if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+        return 0x80000000;
+    }
+    else if ( aExp <= 0x7E ) {
+        if ( aExp | aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig = ( aSig | 0x00800000 )<<8;
+    z = aSig>>( - shiftCount );
+    if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return aSign ? - z : z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float32_to_float64( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a ) );
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float32_to_floatx80( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    aSig |= 0x00800000;
+    return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float32_to_float128( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the single-precision floating-point value `a' to an integer, and
+returns the result as a single-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float32 z;
+
+    aExp = extractFloat32Exp( a );
+    if ( 0x96 <= aExp ) {
+        if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+            return propagateFloat32NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x7E ) {
+        if ( (bits32) ( a<<1 ) == 0 ) return a;
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloat32Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+                return packFloat32( aSign, 0x7F, 0 );
+            }
+            break;
+         case float_round_down:
+            return aSign ? 0xBF800000 : 0;
+         case float_round_up:
+            return aSign ? 0x80000000 : 0x3F800000;
+        }
+        return packFloat32( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x96 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != a ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the single-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 6;
+    bSig <<= 6;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0xFF ) {
+            if ( aSig ) return propagateFloat32NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= 0x20000000;
+        }
+        shift32RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b );
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= 0x20000000;
+        }
+        shift32RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0xFF ) {
+            if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+        zSig = 0x40000000 + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= 0x20000000;
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits32) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the single-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 7;
+    bSig <<= 7;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0xFF ) {
+        if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat32( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return packFloat32( zSign ^ 1, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= 0x40000000;
+    }
+    shift32RightJamming( aSig, - expDiff, &aSig );
+    bSig |= 0x40000000;
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= 0x40000000;
+    }
+    shift32RightJamming( bSig, expDiff, &bSig );
+    aSig |= 0x40000000;
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the single-precision floating-point values `a'
+and `b'.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_add( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat32Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat32Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the single-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sub( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat32Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat32Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the single-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_mul( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig;
+    bits64 zSig64;
+    bits32 zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x7F;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+    zSig = zSig64;
+    if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+        zSig <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the single-precision floating-point value `a'
+by the corresponding value `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_div( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b );
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b );
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return packFloat32( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid );
+                return float32_default_nan;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x7D;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+    if ( ( zSig & 0x3F ) == 0 ) {
+        zSig |= ( ( (bits64) bSig ) * zSig != ( (bits64) aSig )<<32 );
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the single-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_rem( float32 a, float32 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits32 aSig, bSig;
+    bits32 q;
+    bits64 aSig64, bSig64, q64;
+    bits32 alternateASig;
+    sbits32 sigMean;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid );
+            return float32_default_nan;
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig |= 0x00800000;
+    bSig |= 0x00800000;
+    if ( expDiff < 32 ) {
+        aSig <<= 8;
+        bSig <<= 8;
+        if ( expDiff < 0 ) {
+            if ( expDiff < -1 ) return a;
+            aSig >>= 1;
+        }
+        q = ( bSig <= aSig );
+        if ( q ) aSig -= bSig;
+        if ( 0 < expDiff ) {
+            q = ( ( (bits64) aSig )<<32 ) / bSig;
+            q >>= 32 - expDiff;
+            bSig >>= 2;
+            aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+        }
+        else {
+            aSig >>= 2;
+            bSig >>= 2;
+        }
+    }
+    else {
+        if ( bSig <= aSig ) aSig -= bSig;
+        aSig64 = ( (bits64) aSig )<<40;
+        bSig64 = ( (bits64) bSig )<<40;
+        expDiff -= 64;
+        while ( 0 < expDiff ) {
+            q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+            q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+            aSig64 = - ( ( bSig * q64 )<<38 );
+            expDiff -= 62;
+        }
+        expDiff += 64;
+        q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+        q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+        q = q64>>( 64 - expDiff );
+        bSig <<= 6;
+        aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits32) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits32) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the single-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sqrt( float32 a )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits32 aSig, zSig;
+    bits64 rem, term;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, 0 );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid );
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return 0;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+    aSig = ( aSig | 0x00800000 )<<8;
+    zSig = estimateSqrt32( aExp, aSig ) + 2;
+    if ( ( zSig & 0x7F ) <= 5 ) {
+        if ( zSig < 2 ) {
+            zSig = 0xFFFFFFFF;
+        }
+        else {
+            aSig >>= aExp & 1;
+            term = ( (bits64) zSig ) * zSig;
+            rem = ( ( (bits64) aSig )<<32 ) - term;
+            while ( (sbits64) rem < 0 ) {
+                --zSig;
+                rem += ( ( (bits64) zSig )<<1 ) | 1;
+            }
+            zSig |= ( rem != 0 );
+        }
+    }
+    shift32RightJamming( zSig, 1, &zSig );
+    return roundAndPackFloat32( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq( float32 a, float32 b )
+{
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The invalid exception is raised
+if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq_signaling( float32 a, float32 b )
+{
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le_quiet( float32 a, float32 b )
+{
+    flag aSign, bSign;
+    //int16 aExp, bExp;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt_quiet( float32 a, float32 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x42C - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32_round_to_zero( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    shiftCount = 0x433 - aExp;
+    if ( shiftCount < 21 ) {
+        if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 52 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig |= LIT64( 0x0010000000000000 );
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement unsigned integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = 0; //extractFloat64Sign( a );
+    //if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x42C - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32_round_to_zero( float64 a )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    shiftCount = 0x433 - aExp;
+    if ( shiftCount < 21 ) {
+        if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 52 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig |= LIT64( 0x0010000000000000 );
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the single-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float64_to_float32( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+    bits32 zSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a ) );
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 22, &aSig );
+    zSig = aSig;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x381;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the extended double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float64_to_floatx80( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    return
+        packFloatx80(
+            aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the quadruple-precision floating-point format.  The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float64_to_float128( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the double-precision floating-point value `a' to an integer, and
+returns the result as a double-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float64 z;
+
+    aExp = extractFloat64Exp( a );
+    if ( 0x433 <= aExp ) {
+        if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+            return propagateFloat64NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x3FE ) {
+        if ( (bits64) ( a<<1 ) == 0 ) return a;
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloat64Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+                return packFloat64( aSign, 0x3FF, 0 );
+            }
+            break;
+         case float_round_down:
+            return aSign ? LIT64( 0xBFF0000000000000 ) : 0;
+         case float_round_up:
+            return
+            aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 );
+        }
+        return packFloat64( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x433 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != a ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the double-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 9;
+    bSig <<= 9;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FF ) {
+            if ( aSig ) return propagateFloat64NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b );
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FF ) {
+            if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+        zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= LIT64( 0x2000000000000000 );
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits64) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the double-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 10;
+    bSig <<= 10;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FF ) {
+        if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat64( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return packFloat64( zSign ^ 1, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( aSig, - expDiff, &aSig );
+    bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( bSig, expDiff, &bSig );
+    aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the double-precision floating-point values `a'
+and `b'.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_add( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat64Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat64Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the double-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sub( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat64Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat64Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the double-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_mul( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FF;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    zSig0 |= ( zSig1 != 0 );
+    if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+        zSig0 <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the double-precision floating-point value `a'
+by the corresponding value `b'.  The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_div( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    bits64 rem0, rem1;
+    bits64 term0, term1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b );
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b );
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return packFloat64( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid );
+                return float64_default_nan;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FD;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = estimateDiv128To64( aSig, 0, bSig );
+    if ( ( zSig & 0x1FF ) <= 2 ) {
+        mul64To128( bSig, zSig, &term0, &term1 );
+        sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+        while ( (sbits64) rem0 < 0 ) {
+            --zSig;
+            add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+        }
+        zSig |= ( rem1 != 0 );
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the double-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_rem( float64 a, float64 b )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits64 aSig, bSig;
+    bits64 q, alternateASig;
+    sbits64 sigMean;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid );
+            return float64_default_nan;
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        aSig >>= 1;
+    }
+    q = ( bSig <= aSig );
+    if ( q ) aSig -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        aSig = - ( ( bSig>>2 ) * q );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        bSig >>= 2;
+        aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    else {
+        aSig >>= 2;
+        bSig >>= 2;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits64) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits64) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the double-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sqrt( float64 a )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits64 aSig, zSig;
+    bits64 rem0, rem1, term0, term1; //, shiftedRem;
+    //float64 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, a );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid );
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return 0;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+    aSig |= LIT64( 0x0010000000000000 );
+    zSig = estimateSqrt32( aExp, aSig>>21 );
+    zSig <<= 31;
+    aSig <<= 9 - ( aExp & 1 );
+    zSig = estimateDiv128To64( aSig, 0, zSig ) + zSig + 2;
+    if ( ( zSig & 0x3FF ) <= 5 ) {
+        if ( zSig < 2 ) {
+            zSig = LIT64( 0xFFFFFFFFFFFFFFFF );
+        }
+        else {
+            aSig <<= 2;
+            mul64To128( zSig, zSig, &term0, &term1 );
+            sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+            while ( (sbits64) rem0 < 0 ) {
+                --zSig;
+                shortShift128Left( 0, zSig, 1, &term0, &term1 );
+                term1 |= 1;
+                add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+            }
+            zSig |= ( ( rem0 | rem1 ) != 0 );
+        }
+    }
+    shift64RightJamming( zSig, 1, &zSig );
+    return roundAndPackFloat64( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq( float64 a, float64 b )
+{
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise.  The invalid exception is raised
+if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq_signaling( float64 a, float64 b )
+{
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le_quiet( float64 a, float64 b )
+{
+    flag aSign, bSign;
+    //int16 aExp, bExp;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+    return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt_quiet( float64 a, float64 b )
+{
+    flag aSign, bSign;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+    return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic---which means in particular that the conversion
+is rounded according to the current rounding mode.  If `a' is a NaN, the
+largest positive integer is returned.  Otherwise, if the conversion
+overflows, the largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+    shiftCount = 0x4037 - aExp;
+    if ( shiftCount <= 0 ) shiftCount = 1;
+    shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic, except that the conversion is always rounded
+toward zero.  If `a' is a NaN, the largest positive integer is returned.
+Otherwise, if the conversion overflows, the largest integer with the same
+sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32_round_to_zero( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    shiftCount = 0x403E - aExp;
+    if ( shiftCount < 32 ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 63 < shiftCount ) {
+        if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the single-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 floatx80_to_float32( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat32( floatx80ToCommonNaN( a ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 33, &aSig );
+    if ( aExp || aSig ) aExp -= 0x3F81;
+    return roundAndPackFloat32( aSign, aExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the double-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 floatx80_to_float64( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig, zSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat64( floatx80ToCommonNaN( a ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shift64RightJamming( aSig, 1, &zSig );
+    if ( aExp || aSig ) aExp -= 0x3C01;
+    return roundAndPackFloat64( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the quadruple-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 floatx80_to_float128( floatx80 a )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+        return commonNaNToFloat128( floatx80ToCommonNaN( a ) );
+    }
+    shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the extended double-precision floating-point value `a' to an integer,
+and returns the result as an extended quadruple-precision floating-point
+value.  The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    floatx80 z;
+
+    aExp = extractFloatx80Exp( a );
+    if ( 0x403E <= aExp ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+            return propagateFloatx80NaN( a, a );
+        }
+        return a;
+    }
+    if ( aExp <= 0x3FFE ) {
+        if (    ( aExp == 0 )
+             && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+            return a;
+        }
+        float_exception_flags |= float_flag_inexact;
+        aSign = extractFloatx80Sign( a );
+        switch ( float_rounding_mode ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+               ) {
+                return
+                    packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+            }
+            break;
+         case float_round_down:
+            return
+                  aSign ?
+                      packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+                : packFloatx80( 0, 0, 0 );
+         case float_round_up:
+            return
+                  aSign ? packFloatx80( 1, 0, 0 )
+                : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+        }
+        return packFloatx80( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x403E - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = float_rounding_mode;
+    if ( roundingMode == float_round_nearest_even ) {
+        z.low += lastBitMask>>1;
+        if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z.low += roundBitsMask;
+        }
+    }
+    z.low &= ~ roundBitsMask;
+    if ( z.low == 0 ) {
+        ++z.high;
+        z.low = LIT64( 0x8000000000000000 );
+    }
+    if ( z.low != a.low ) float_exception_flags |= float_flag_inexact;
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the extended double-
+precision floating-point values `a' and `b'.  If `zSign' is true, the sum is
+negated before being returned.  `zSign' is ignored if the result is a NaN.
+The addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) --expDiff;
+        shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( aExp == 0 ) ++expDiff;
+        shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+                return propagateFloatx80NaN( a, b );
+            }
+            return a;
+        }
+        zSig1 = 0;
+        zSig0 = aSig + bSig;
+        if ( aExp == 0 ) {
+            normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+            goto roundAndPack;
+        }
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    
+    zSig0 = aSig + bSig;
+
+    if ( (sbits64) zSig0 < 0 ) goto roundAndPack; 
+ shiftRight1:
+    shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+    zSig0 |= LIT64( 0x8000000000000000 );
+    ++zExp;
+ roundAndPack:
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the extended
+double-precision floating-point values `a' and `b'.  If `zSign' is true,
+the difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    zSig1 = 0;
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloatx80( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) ++expDiff;
+    shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+    sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) --expDiff;
+    shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+    sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    return
+        normalizeRoundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the extended double-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_add( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+    
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return addFloatx80Sigs( a, b, aSign );
+    }
+    else {
+        return subFloatx80Sigs( a, b, aSign );
+    }
+    
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the extended double-precision floating-
+point values `a' and `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sub( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return subFloatx80Sigs( a, b, aSign );
+    }
+    else {
+        return addFloatx80Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the extended double-precision floating-
+point values `a' and `b'.  The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_mul( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        if ( ( bExp | bSig ) == 0 ) goto invalid;
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FFE;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    if ( 0 < (sbits64) zSig0 ) {
+        shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+        --zExp;
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the extended double-precision floating-point
+value `a' by the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_div( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    bits64 rem0, rem1, rem2, term0, term1, term2;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+            goto invalid;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return packFloatx80( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid );
+                z.low = floatx80_default_nan_low;
+                z.high = floatx80_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FFE;
+    rem1 = 0;
+    if ( bSig <= aSig ) {
+        shift128Right( aSig, 0, 1, &aSig, &rem1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+    mul64To128( bSig, zSig0, &term0, &term1 );
+    sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+    }
+    zSig1 = estimateDiv128To64( rem1, 0, bSig );
+    if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+        mul64To128( bSig, zSig1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+        }
+        zSig1 |= ( ( rem1 | rem2 ) != 0 );
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the extended double-precision floating-point value
+`a' with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_rem( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig;
+    bits64 q, term0, term1, alternateASig0, alternateASig1;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig0<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    bSig |= LIT64( 0x8000000000000000 );
+    zSign = aSign;
+    expDiff = aExp - bExp;
+    aSig1 = 0;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+        expDiff = 0;
+    }
+    q = ( bSig <= aSig0 );
+    if ( q ) aSig0 -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        mul64To128( bSig, q, &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+        while ( le128( term0, term1, aSig0, aSig1 ) ) {
+            ++q;
+            sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        }
+    }
+    else {
+        term1 = 0;
+        term0 = bSig;
+    }
+    sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+    if (    lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+         || (    eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+              && ( q & 1 ) )
+       ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+        zSign = ! zSign;
+    }
+    return
+        normalizeRoundAndPackFloatx80(
+            80, zSign, bExp + expDiff, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the extended double-precision floating-point
+value `a'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sqrt( floatx80 a )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    bits64 shiftedRem0, shiftedRem1;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid );
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+    zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+    zSig0 <<= 31;
+    aSig1 = 0;
+    shift128Right( aSig0, 0, ( aExp & 1 ) + 2, &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+    if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+    shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+        term1 |= 1;
+        add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+    }
+    shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+    zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+    if ( (bits64) ( zSig1<<1 ) <= 10 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( zSig0, zSig1, &term1, &term2 );
+        shortShift128Left( term1, term2, 1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+            term3 |= 1;
+            add192(
+                rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    return
+        roundAndPackFloatx80(
+            floatx80_rounding_precision, 0, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+equal to the corresponding value `b', and 0 otherwise.  The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq( floatx80 a, floatx80 b )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than or equal to the corresponding value `b', and 0 otherwise.  The
+comparison is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than the corresponding value `b', and 0 otherwise.  The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is equal
+to the corresponding value `b', and 0 otherwise.  The invalid exception is
+raised if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq_signaling( floatx80 a, floatx80 b )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs
+do not cause an exception.  Otherwise, the comparison is performed according
+to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le_quiet( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause
+an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt_quiet( floatx80 a, floatx80 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode.  If `a' is a NaN, the largest
+positive integer is returned.  Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32( float128 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+    if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+    aSig0 |= ( aSig1 != 0 );
+    shiftCount = 0x4028 - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+    return roundAndPackInt32( aSign, aSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero.  If
+`a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32_round_to_zero( float128 a )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1, savedASig;
+    int32 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    aSig0 |= ( aSig1 != 0 );
+    shiftCount = 0x402F - aExp;
+    if ( shiftCount < 17 ) {
+        if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+        goto invalid;
+    }
+    else if ( 48 < shiftCount ) {
+        if ( aExp || aSig0 ) float_exception_flags |= float_flag_inexact;
+        return 0;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    savedASig = aSig0;
+    aSig0 >>= shiftCount;
+    z = aSig0;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_exception_flags |= float_flag_invalid;
+        return aSign ? 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig0<<shiftCount ) != savedASig ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the single-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float128_to_float32( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+    bits32 zSig;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat32( float128ToCommonNaN( a ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    aSig0 |= ( aSig1 != 0 );
+    shift64RightJamming( aSig0, 18, &aSig0 );
+    zSig = aSig0;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x3F81;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the double-precision floating-point format.  The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float128_to_float64( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat64( float128ToCommonNaN( a ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    aSig0 |= ( aSig1 != 0 );
+    if ( aExp || aSig0 ) {
+        aSig0 |= LIT64( 0x4000000000000000 );
+        aExp -= 0x3C01;
+    }
+    return roundAndPackFloat64( aSign, aExp, aSig0 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the extended double-precision floating-point format.  The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float128_to_floatx80( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloatx80( float128ToCommonNaN( a ) );
+        }
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    else {
+        aSig0 |= LIT64( 0x0001000000000000 );
+    }
+    shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+    return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the quadruple-precision floating-point value `a' to an integer, and
+returns the result as a quadruple-precision floating-point value.  The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 a )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float128 z;
+
+    aExp = extractFloat128Exp( a );
+    if ( 0x402F <= aExp ) {
+        if ( 0x406F <= aExp ) {
+            if (    ( aExp == 0x7FFF )
+                 && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+               ) {
+                return propagateFloat128NaN( a, a );
+            }
+            return a;
+        }
+        lastBitMask = 1;
+        lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+        roundBitsMask = lastBitMask - 1;
+        z = a;
+        roundingMode = float_rounding_mode;
+        if ( roundingMode == float_round_nearest_even ) {
+            if ( lastBitMask ) {
+                add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+                if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+            }
+            else {
+                if ( (sbits64) z.low < 0 ) {
+                    ++z.high;
+                    if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+                }
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+            }
+        }
+        z.low &= ~ roundBitsMask;
+    }
+    else {
+        if ( aExp <= 0x3FFE ) {
+            if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+            float_exception_flags |= float_flag_inexact;
+            aSign = extractFloat128Sign( a );
+            switch ( float_rounding_mode ) {
+             case float_round_nearest_even:
+                if (    ( aExp == 0x3FFE )
+                     && (   extractFloat128Frac0( a )
+                          | extractFloat128Frac1( a ) )
+                   ) {
+                    return packFloat128( aSign, 0x3FFF, 0, 0 );
+                }
+                break;
+             case float_round_down:
+                return
+                      aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+                    : packFloat128( 0, 0, 0, 0 );
+             case float_round_up:
+                return
+                      aSign ? packFloat128( 1, 0, 0, 0 )
+                    : packFloat128( 0, 0x3FFF, 0, 0 );
+            }
+            return packFloat128( aSign, 0, 0, 0 );
+        }
+        lastBitMask = 1;
+        lastBitMask <<= 0x402F - aExp;
+        roundBitsMask = lastBitMask - 1;
+        z.low = 0;
+        z.high = a.high;
+        roundingMode = float_rounding_mode;
+        if ( roundingMode == float_round_nearest_even ) {
+            z.high += lastBitMask>>1;
+            if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+                z.high &= ~ lastBitMask;
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                z.high |= ( a.low != 0 );
+                z.high += roundBitsMask;
+            }
+        }
+        z.high &= ~ roundBitsMask;
+    }
+    if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+        float_exception_flags |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the quadruple-precision
+floating-point values `a' and `b'.  If `zSign' is true, the sum is negated
+before being returned.  `zSign' is ignored if the result is a NaN.  The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    int32 expDiff;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+                return propagateFloat128NaN( a, b );
+            }
+            return a;
+        }
+        add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+        if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 );
+        zSig2 = 0;
+        zSig0 |= LIT64( 0x0002000000000000 );
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    --zExp;
+    if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+    ++zExp;
+ shiftRight1:
+    shift128ExtraRightJamming(
+        zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the quadruple-
+precision floating-point values `a' and `b'.  If `zSign' is true, the
+difference is negated before being returned.  `zSign' is ignored if the
+result is a NaN.  The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+    int32 expDiff;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+            return propagateFloat128NaN( a, b );
+        }
+        float_raise( float_flag_invalid );
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig0 < aSig0 ) goto aBigger;
+    if ( aSig0 < bSig0 ) goto bBigger;
+    if ( bSig1 < aSig1 ) goto aBigger;
+    if ( aSig1 < bSig1 ) goto bBigger;
+    return packFloat128( float_rounding_mode == float_round_down, 0, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+    bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+    sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+    aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+    sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the quadruple-precision floating-point values
+`a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_add( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat128Sigs( a, b, aSign );
+    }
+    else {
+        return subFloat128Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the quadruple-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sub( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat128Sigs( a, b, aSign );
+    }
+    else {
+        return addFloat128Sigs( a, b, aSign );
+    }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the quadruple-precision floating-point
+values `a' and `b'.  The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_mul( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b );
+        }
+        if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    zExp = aExp + bExp - 0x4000;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+    mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+    add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zSig2 |= ( zSig3 != 0 );
+    if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+        ++zExp;
+    }
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the quadruple-precision floating-point value
+`a' by the corresponding value `b'.  The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_div( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+            goto invalid;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return packFloat128( zSign, 0, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+            if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid );
+                z.low = float128_default_nan_low;
+                z.high = float128_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero );
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = aExp - bExp + 0x3FFD;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+        shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+    mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+    sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+    }
+    zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+    if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+        mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+        sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the quadruple-precision floating-point value `a'
+with respect to the corresponding value `b'.  The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_rem( float128 a, float128 b )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig0, bSig1;
+    bits64 q, term0, term1, term2, allZero, alternateASig0, alternateASig1;
+    bits64 sigMean1;
+    sbits64 sigMean0;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid );
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return a;
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    expDiff = aExp - bExp;
+    if ( expDiff < -1 ) return a;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ),
+        aSig1,
+        15 - ( expDiff < 0 ),
+        &aSig0,
+        &aSig1
+    );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    q = le128( bSig0, bSig1, aSig0, aSig1 );
+    if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+        shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+        sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+        expDiff -= 61;
+    }
+    if ( -64 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        q >>= - expDiff;
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+        expDiff += 52;
+        if ( expDiff < 0 ) {
+            shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+        }
+        else {
+            shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+        }
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+    }
+    else {
+        shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+    }
+    do {
+        alternateASig0 = aSig0;
+        alternateASig1 = aSig1;
+        ++q;
+        sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    } while ( 0 <= (sbits64) aSig0 );
+    add128(
+        aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 );
+    if (    ( sigMean0 < 0 )
+         || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+    }
+    zSign = ( (sbits64) aSig0 < 0 );
+    if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+    return
+        normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the quadruple-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sqrt( float128 a )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1, zSig2;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    bits64 shiftedRem0, shiftedRem1;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid );
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+    zSig0 <<= 31;
+    shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+    if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+    shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+        term1 |= 1;
+        add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+    }
+    shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+    zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+    if ( ( zSig1 & 0x3FFF ) <= 5 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( zSig0, zSig1, &term1, &term2 );
+        shortShift128Left( term1, term2, 1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+            term3 |= 1;
+            add192(
+                rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq( float128 a, float128 b )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise.  The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise.  The invalid exception is
+raised if either operand is a NaN.  Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq_signaling( float128 a, float128 b )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid );
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+cause an exception.  Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le_quiet( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt_quiet( float128 a, float128 b )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid );
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/nwfpe/softfloat.h linux.18p17/arch/arm/nwfpe/softfloat.h
--- linux.vanilla/arch/arm/nwfpe/softfloat.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/nwfpe/softfloat.h	Fri Sep 15 23:28:38 2000
@@ -0,0 +1,290 @@
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#ifndef __SOFTFLOAT_H__
+#define __SOFTFLOAT_H__
+
+/*
+-------------------------------------------------------------------------------
+The macro `FLOATX80' must be defined to enable the extended double-precision
+floating-point format `floatx80'.  If this macro is not defined, the
+`floatx80' type will not be defined, and none of the functions that either
+input or output the `floatx80' type will be defined.  The same applies to
+the `FLOAT128' macro and the quadruple-precision format `float128'.
+-------------------------------------------------------------------------------
+*/
+#define FLOATX80
+/* #define FLOAT128 */
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point types.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned long int float32;
+typedef unsigned long long float64;
+#ifdef FLOATX80
+typedef struct {
+    unsigned short high;
+    unsigned long long low;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+    unsigned long long high, low;
+} float128;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point underflow tininess-detection mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_detect_tininess;
+enum {
+    float_tininess_after_rounding  = 0,
+    float_tininess_before_rounding = 1
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point rounding mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_rounding_mode;
+enum {
+    float_round_nearest_even = 0,
+    float_round_to_zero      = 1,
+    float_round_down         = 2,
+    float_round_up           = 3
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point exception flags.
+-------------------------------------------------------------------------------
+extern signed char float_exception_flags;
+enum {
+    float_flag_inexact   =  1,
+    float_flag_underflow =  2,
+    float_flag_overflow  =  4,
+    float_flag_divbyzero =  8,
+    float_flag_invalid   = 16
+};
+
+ScottB: November 4, 1998
+Changed the enumeration to match the bit order in the FPA11.
+*/
+
+extern signed char float_exception_flags;
+enum {
+    float_flag_invalid   =  1,
+    float_flag_divbyzero =  2,
+    float_flag_overflow  =  4,
+    float_flag_underflow =  8,
+    float_flag_inexact   = 16
+};
+
+/*
+-------------------------------------------------------------------------------
+Routine to raise any or all of the software IEC/IEEE floating-point
+exception flags.
+-------------------------------------------------------------------------------
+*/
+void float_raise( signed char );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE integer-to-floating-point conversion routines.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( signed int );
+float64 int32_to_float64( signed int );
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( signed int );
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( signed int );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float32_to_int32( float32 );
+signed int float32_to_int32_round_to_zero( float32 );
+float64 float32_to_float64( float32 );
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 );
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision operations.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 );
+float32 float32_add( float32, float32 );
+float32 float32_sub( float32, float32 );
+float32 float32_mul( float32, float32 );
+float32 float32_div( float32, float32 );
+float32 float32_rem( float32, float32 );
+float32 float32_sqrt( float32 );
+char float32_eq( float32, float32 );
+char float32_le( float32, float32 );
+char float32_lt( float32, float32 );
+char float32_eq_signaling( float32, float32 );
+char float32_le_quiet( float32, float32 );
+char float32_lt_quiet( float32, float32 );
+char float32_is_signaling_nan( float32 );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float64_to_int32( float64 );
+signed int float64_to_int32_round_to_zero( float64 );
+float32 float64_to_float32( float64 );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision operations.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 );
+float64 float64_add( float64, float64 );
+float64 float64_sub( float64, float64 );
+float64 float64_mul( float64, float64 );
+float64 float64_div( float64, float64 );
+float64 float64_rem( float64, float64 );
+float64 float64_sqrt( float64 );
+char float64_eq( float64, float64 );
+char float64_le( float64, float64 );
+char float64_lt( float64, float64 );
+char float64_eq_signaling( float64, float64 );
+char float64_le_quiet( float64, float64 );
+char float64_lt_quiet( float64, float64 );
+char float64_is_signaling_nan( float64 );
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int floatx80_to_int32( floatx80 );
+signed int floatx80_to_int32_round_to_zero( floatx80 );
+float32 floatx80_to_float32( floatx80 );
+float64 floatx80_to_float64( floatx80 );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision rounding precision.  Valid
+values are 32, 64, and 80.
+-------------------------------------------------------------------------------
+*/
+extern signed char floatx80_rounding_precision;
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision operations.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 );
+floatx80 floatx80_add( floatx80, floatx80 );
+floatx80 floatx80_sub( floatx80, floatx80 );
+floatx80 floatx80_mul( floatx80, floatx80 );
+floatx80 floatx80_div( floatx80, floatx80 );
+floatx80 floatx80_rem( floatx80, floatx80 );
+floatx80 floatx80_sqrt( floatx80 );
+char floatx80_eq( floatx80, floatx80 );
+char floatx80_le( floatx80, floatx80 );
+char floatx80_lt( floatx80, floatx80 );
+char floatx80_eq_signaling( floatx80, floatx80 );
+char floatx80_le_quiet( floatx80, floatx80 );
+char floatx80_lt_quiet( floatx80, floatx80 );
+char floatx80_is_signaling_nan( floatx80 );
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float128_to_int32( float128 );
+signed int float128_to_int32_round_to_zero( float128 );
+float32 float128_to_float32( float128 );
+float64 float128_to_float64( float128 );
+#ifdef FLOATX80
+floatx80 float128_to_floatx80( float128 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision operations.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 );
+float128 float128_add( float128, float128 );
+float128 float128_sub( float128, float128 );
+float128 float128_mul( float128, float128 );
+float128 float128_div( float128, float128 );
+float128 float128_rem( float128, float128 );
+float128 float128_sqrt( float128 );
+char float128_eq( float128, float128 );
+char float128_le( float128, float128 );
+char float128_lt( float128, float128 );
+char float128_eq_signaling( float128, float128 );
+char float128_le_quiet( float128, float128 );
+char float128_lt_quiet( float128, float128 );
+char float128_is_signaling_nan( float128 );
+
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/vmlinux-armo.lds linux.18p17/arch/arm/vmlinux-armo.lds
--- linux.vanilla/arch/arm/vmlinux-armo.lds	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/vmlinux-armo.lds	Tue Sep  5 22:20:06 2000
@@ -41,8 +41,16 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(32768);
   __init_end = .;
+
 
   __bss_start = .;		/* BSS */
   .bss : {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/vmlinux-armo.lds.in linux.18p17/arch/arm/vmlinux-armo.lds.in
--- linux.vanilla/arch/arm/vmlinux-armo.lds.in	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/vmlinux-armo.lds.in	Mon Oct  2 10:10:59 2000
@@ -0,0 +1,92 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+	. = TEXTADDR;
+
+	.init : {
+		__init_begin = .;	/* Init code and data		*/
+			*(.text.init)
+		__proc_info_begin = .;
+			*(.proc.info)
+		__proc_info_end = .;
+			*(.data.init)
+		. = ALIGN(16);
+		__setup_start = .;
+			*(.setup.init)
+		__setup_end = .;
+		__initcall_start = .;
+			*(.initcall.init)
+		__initcall_end = .;
+		. = ALIGN(32768);
+		__init_end = .;
+	}
+
+	.init.task : {
+		*(.init.task)
+	}
+
+	/DISCARD/ : {			/* Exit code and data		*/
+		*(.text.exit)
+		*(.data.exit)
+		*(.exitcall.exit)
+	}
+
+	.text : {
+		_text = .;		/* Text and read-only data	*/
+			*(.text)
+			*(.fixup)
+			*(.gnu.warning)
+			*(.text.lock)	/* out-of-line lock text	*/
+			*(.rodata)
+			*(.kstrtab)
+		. = ALIGN(16);		/* Exception table		*/
+		__start___ex_table = .;
+			*(__ex_table)
+		__stop___ex_table = .;
+
+		__start___ksymtab = .;	/* Kernel symbol table		*/
+			*(__ksymtab)
+		__stop___ksymtab = .;
+
+		*(.got)			/* Global offset table		*/
+
+		_etext = .;		/* End of text section		*/
+	}
+
+	.data : {			/* Data */
+		/*
+		 * The cacheline aligned data
+		 */
+		. = ALIGN(32);
+		*(.data.cacheline_aligned)
+
+		/*
+		 * and the usual data section
+		 */
+		*(.data)
+		CONSTRUCTORS
+
+		_edata = .;
+	}
+
+	.bss : {
+		__bss_start = .;	/* BSS				*/
+		*(.bss)
+		*(COMMON)
+		_end = . ;
+	}
+
+					/* Stabs debugging sections.  */
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/vmlinux-armv.lds linux.18p17/arch/arm/vmlinux-armv.lds
--- linux.vanilla/arch/arm/vmlinux-armv.lds	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/arm/vmlinux-armv.lds	Tue Sep  5 22:20:06 2000
@@ -41,8 +41,17 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+ 
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(4096);
   __init_end = .;
+
 
   __bss_start = .;		/* BSS */
   .bss : {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/vmlinux-armv.lds.in linux.18p17/arch/arm/vmlinux-armv.lds.in
--- linux.vanilla/arch/arm/vmlinux-armv.lds.in	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/arm/vmlinux-armv.lds.in	Mon Oct  2 10:10:59 2000
@@ -0,0 +1,106 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+OUTPUT_ARCH(arm)
+ENTRY(stext)
+SECTIONS
+{
+	. = TEXTADDR;
+	.init : {			/* Init code and data		*/
+		__init_begin = .;
+			*(.text.init)
+		__proc_info_begin = .;
+			*(.proc.info)
+		__proc_info_end = .;
+			*(.data.init)
+		. = ALIGN(16);
+		__setup_start = .;
+			*(.setup.init)
+		__setup_end = .;
+		__initcall_start = .;
+			*(.initcall.init)
+		__initcall_end = .;
+		. = ALIGN(4096);
+		__init_end = .;
+	}
+
+	/DISCARD/ : {			/* Exit code and data		*/
+		*(.text.exit)
+		*(.data.exit)
+		*(.exitcall.exit)
+	}
+
+	__ebsa285_begin = .;
+	.text.ebsa285 : { *(.text.ebsa285) }
+	.data.ebsa285 : { *(.data.ebsa285) }
+	. = ALIGN(4096);
+	__ebsa285_end = .;
+
+	__netwinder_begin = .;
+	.text.netwinder : { *(.text.netwinder) }
+	.data.netwinder : { *(.data.netwinder) }
+	. = ALIGN(4096);
+	__netwinder_end = .;
+
+	.text : {			/* Real text segment		*/
+		_text = .;		/* Text and read-only data	*/
+			*(.text)
+			*(.fixup)
+			*(.gnu.warning)
+			*(.text.lock)	/* out-of-line lock text */
+			*(.rodata)
+			*(.kstrtab)
+		. = ALIGN(16);		/* Exception table		*/
+		__start___ex_table = .;
+			*(__ex_table)
+		__stop___ex_table = .;
+
+		__start___ksymtab = .;	/* Kernel symbol table		*/
+			*(__ksymtab)
+		__stop___ksymtab = .;
+
+		*(.got)			/* Global offset table		*/
+
+		_etext = .;		/* End of text section		*/
+	}
+
+	. = ALIGN(8192);
+
+	.data : {
+		/*
+		 * first, the init task union, aligned
+		 * to an 8192 byte boundary.
+		 */
+		*(.init.task)
+
+		/*
+		 * then the cacheline aligned data
+		 */
+		. = ALIGN(32);
+		*(.data.cacheline_aligned)
+
+		/*
+		 * and the usual data section
+		 */
+		*(.data)
+		CONSTRUCTORS
+
+		_edata = .;
+	}
+
+	.bss : {
+		__bss_start = .;	/* BSS				*/
+		*(.bss)
+		*(COMMON)
+		_end = . ;
+	}
+					/* Stabs debugging sections.	*/
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/setup.S linux.18p17/arch/i386/boot/setup.S
--- linux.vanilla/arch/i386/boot/setup.S	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/i386/boot/setup.S	Fri Sep 15 23:35:27 2000
@@ -30,6 +30,10 @@
 ! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
 ! parsons) to avoid loadlin confusion, July 1997
 !
+!
+! A20 gating fiddled to work on AMD Elan AmSC4xx series by kira@linuxgrrls.org
+! july 1999
+!
 
 #define __ASSEMBLY__
 #include <linux/config.h>
@@ -548,6 +552,14 @@
 	lgdt	gdt_48		! load gdt with whatever appropriate
 
 ! that was painless, now we enable A20
+
+! if this is SC410 or a few other bits we need to do it with the 'fast' method
+
+        in      al,#0x92        ! read "System Control Port A"
+        or      al,#0x02        ! Set "Alternate Gate A20" - Bit
+        out     #0x92,al        ! write "System Control Port A"
+
+! do it the normal way too, so as not to upset normal machines
 
 	call	empty_8042
 	mov	al,#0xD1		! command write
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/config.in linux.18p17/arch/i386/config.in
--- linux.vanilla/arch/i386/config.in	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/config.in	Mon Sep 11 18:14:04 2000
@@ -33,6 +33,10 @@
   define_bool CONFIG_X86_GOOD_APIC y
 fi
 
+tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
+tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR
+tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID
+
 choice 'Maximum Physical Memory' \
 	"1GB		CONFIG_1GB \
 	 2GB		CONFIG_2GB" 1GB
@@ -109,7 +113,6 @@
   bool '   Enable PM at boot time' CONFIG_APM_DO_ENABLE
   bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
   bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
-  bool '   Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE
   bool '   RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT
   bool '   Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS
   bool '   Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF
@@ -176,7 +179,7 @@
 
 source drivers/char/Config.in
 
-# source drivers/usb/Config.in
+source drivers/usb/Config.in
 
 source fs/Config.in
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/defconfig linux.18p17/arch/i386/defconfig
--- linux.vanilla/arch/i386/defconfig	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/i386/defconfig	Sun Sep  3 14:00:02 2000
@@ -166,6 +166,7 @@
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
 # CONFIG_SCSI_AIC7XXX is not set
 # CONFIG_SCSI_IPS is not set
 # CONFIG_SCSI_ADVANSYS is not set
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/Makefile linux.18p17/arch/i386/kernel/Makefile
--- linux.vanilla/arch/i386/kernel/Makefile	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/kernel/Makefile	Mon Sep 11 18:14:04 2000
@@ -35,6 +35,30 @@
   endif
 endif
 
+ifeq ($(CONFIG_MICROCODE),y)
+OX_OBJS += microcode.o
+else
+  ifeq ($(CONFIG_MICROCODE),m)
+  MX_OBJS += microcode.o
+  endif
+endif
+
+ifeq ($(CONFIG_X86_MSR),y)
+OX_OBJS += msr.o
+else
+  ifeq ($(CONFIG_X86_MSR),m)
+  MX_OBJS += msr.o
+  endif
+endif
+
+ifeq ($(CONFIG_X86_CPUID),y)
+OX_OBJS += cpuid.o
+else
+  ifeq ($(CONFIG_X86_CPUID),m)
+  MX_OBJS += cpuid.o
+  endif
+endif
+
 ifdef CONFIG_APM
 OX_OBJS += apm.o
 endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/apm.c linux.18p17/arch/i386/kernel/apm.c
--- linux.vanilla/arch/i386/kernel/apm.c	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/i386/kernel/apm.c	Mon Sep 11 17:05:42 2000
@@ -170,18 +170,6 @@
 #include <asm/desc.h>
 #include <asm/softirq.h>
 
-/* 2.2-2.3 Compatability defines */
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define __setup(x, y)
-#define module_init(x)
-#define set_current_state(a) current->state = (a)
-#define create_proc_info_entry(n, m, b, g) \
-	{ \
-		struct proc_dir_entry *r = create_proc_entry(n, m, b); \
-		if (r) r->get_info = g; \
-	}
-
 EXPORT_SYMBOL(apm_register_callback);
 EXPORT_SYMBOL(apm_unregister_callback);
 
@@ -1438,7 +1426,7 @@
 	return 0;
 }
 
-void __init apm_setup(char *str, int *dummy)
+static int __init apm_setup(char *str)
 {
 	int	invert;
 
@@ -1458,6 +1446,7 @@
 		if (str != NULL)
 			str += strspn(str, ", \t");
 	}
+	return 1;
 }
 
 __setup("apm=", apm_setup);
@@ -1476,13 +1465,12 @@
 	&apm_bios_fops
 };
 
-#define APM_INIT_ERROR_RETURN	return
 
-void __init apm_init(void)
+static int __init apm_init(void)
 {
 	if (apm_bios_info.version == 0) {
 		printk(KERN_INFO "apm: BIOS not found.\n");
-		APM_INIT_ERROR_RETURN;
+		return -ENODEV;
 	}
 	printk(KERN_INFO
 		"apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
@@ -1492,7 +1480,7 @@
 		driver_version);
 	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
 		printk(KERN_INFO "apm: no 32 bit BIOS support\n");
-		APM_INIT_ERROR_RETURN;
+		return -ENODEV;
 	}
 
 	/*
@@ -1521,11 +1509,11 @@
 
 	if (apm_disabled) {
 		printk(KERN_NOTICE "apm: disabled on user request.\n");
-		APM_INIT_ERROR_RETURN;
+		return -ENODEV;
 	}
 	if ((smp_num_cpus > 1) && !power_off_enabled) {
 		printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
-		APM_INIT_ERROR_RETURN;
+		return -ENODEV;
 	}
 
 	/*
@@ -1573,7 +1561,7 @@
 	if (smp_num_cpus > 1) {
 		printk(KERN_NOTICE
 		   "apm: disabled - APM is not SMP safe (power off active).\n");
-		APM_INIT_ERROR_RETURN;
+		return 0;
 	}
 
 	init_timer(&apm_timer);
@@ -1586,6 +1574,7 @@
 #ifdef CONFIG_APM_CPU_IDLE
 	acpi_idle = apm_cpu_idle;
 #endif
+	return 0;
 }
 
 module_init(apm_init)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/bios32.c linux.18p17/arch/i386/kernel/bios32.c
--- linux.vanilla/arch/i386/kernel/bios32.c	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/i386/kernel/bios32.c	Sat Sep  2 23:44:17 2000
@@ -1055,7 +1055,6 @@
 	/*
 	 * Find and scan busses behind ServerWorks north bridge chips
 	 */
-	struct pci_bus *bus;
 	unsigned char busno, busmax;
 	pci_probe |= PCI_NO_PEER_FIXUP;
 	pci_read_config_byte(d, 0x44, &busno);
@@ -1070,7 +1069,6 @@
 	/*
 	 * Find and scan busses behind RCC LE north bridge chips
 	 */
-	struct pci_bus *bus;
 	unsigned char busno, busmax;
 	pci_probe |= PCI_NO_PEER_FIXUP;
 	pci_read_config_byte(d, 0xc8, &busno);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/bluesmoke.c linux.18p17/arch/i386/kernel/bluesmoke.c
--- linux.vanilla/arch/i386/kernel/bluesmoke.c	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/kernel/bluesmoke.c	Sun Sep 17 17:44:37 2000
@@ -22,7 +22,7 @@
 	if(mcgstl&(1<<0))	/* Recoverable ? */
 		recover=0;
 
-	printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x", smp_processor_id(), mcgstl, mcgsth);
+	printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl);
 	
 	for(i=0;i<banks;i++)
 	{
@@ -37,7 +37,7 @@
 			high&=~(1<<31);
 			if(high&(1<<27))
 			{
-				rdmsr(0x402+i*4, alow, ahigh);
+				rdmsr(0x403+i*4, alow, ahigh);
 				printk("[%08x%08x]", alow, ahigh);
 			}
 			if(high&(1<<26))
@@ -46,7 +46,10 @@
 				printk(" at %08x%08x", 
 					high, low);
 			}
-			wrmsr(0x401+i*4, low, high);
+			/* Clear it */
+			wrmsr(0x401+i*4, 0UL, 0UL);
+			/* Serialize */
+			mb();
 		}
 	}
 	
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/cpuid.c linux.18p17/arch/i386/kernel/cpuid.c
--- linux.vanilla/arch/i386/kernel/cpuid.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/i386/kernel/cpuid.c	Thu Oct 19 01:25:23 2000
@@ -0,0 +1,163 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2000 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+/*
+ * cpuid.c
+ *
+ * x86 CPUID access device
+ *
+ * This device is accessed by lseek() to the appropriate CPUID level
+ * and then read in chunks of 16 bytes.  A larger size means multiple
+ * reads of consecutive levels.
+ *
+ * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
+ * an SMP box will direct the access to CPU %d.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp.h>
+#include <linux/major.h>
+
+#include <asm/processor.h>
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_SMP
+
+struct cpuid_command {
+  int cpu;
+  u32 reg;
+  u32 *data;
+};
+
+static void cpuid_smp_cpuid(void *cmd_block)
+{
+  struct cpuid_command *cmd = (struct cpuid_command *) cmd_block;
+  
+  if ( cmd->cpu == smp_processor_id() )
+    cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], &cmd->data[3]);
+}
+
+extern inline void do_cpuid(int cpu, u32 reg, u32 *data)
+{
+  struct cpuid_command cmd;
+  
+  if ( cpu == smp_processor_id() ) {
+    cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
+  } else {
+    cmd.cpu  = cpu;
+    cmd.reg  = reg;
+    cmd.data = data;
+    
+    smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1);
+  }
+}
+#else /* ! CONFIG_SMP */
+
+extern inline void do_cpuid(int cpu, u32 reg, u32 *data)
+{
+  cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
+}
+
+#endif /* ! CONFIG_SMP */
+
+static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
+{
+  switch (orig) {
+  case 0:
+    file->f_pos = offset;
+    return file->f_pos;
+  case 1:
+    file->f_pos += offset;
+    return file->f_pos;
+  default:
+    return -EINVAL;	/* SEEK_END not supported */
+  }
+}
+
+static ssize_t cpuid_read(struct file * file, char * buf,
+			size_t count, loff_t *ppos)
+{
+  u32 *tmp = (u32 *)buf;
+  u32 data[4];
+  size_t rv;
+  u32 reg = *ppos;
+  int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
+  
+  if ( count % 16 )
+    return -EINVAL; /* Invalid chunk size */
+  
+  for ( rv = 0 ; count ; count -= 16 ) {
+    do_cpuid(cpu, reg, data);
+    if ( copy_to_user(tmp,&data,16) )
+      return -EFAULT;
+    tmp += 4;
+    *ppos = reg++;
+  }
+  
+  return ((char *)tmp) - buf;
+}
+
+static int cpuid_open(struct inode *inode, struct file *file)
+{
+  int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
+  struct cpuinfo_x86 *c = &(cpu_data)[cpu];
+
+  if ( !(cpu_online_map & (1UL << cpu)) )
+    return -ENXIO;		/* No such CPU */
+  if ( c->cpuid_level < 0 )
+    return -EIO;		/* CPUID not supported */
+  
+  return 0;
+}
+
+/*
+ * File operations we support
+ */
+static struct file_operations cpuid_fops = {
+  llseek:	cpuid_seek,
+  read:		cpuid_read,
+  open:		cpuid_open,
+};
+
+int __init cpuid_init(void)
+{
+  if (register_chrdev(CPUID_MAJOR, "cpu/cpuid", &cpuid_fops)) {
+    printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
+	   CPUID_MAJOR);
+    return -EBUSY;
+  }
+
+  return 0;
+}
+
+static void cpuid_exit(void)
+{
+}
+
+module_init(cpuid_init);
+module_exit(cpuid_exit);
+	
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
+MODULE_DESCRIPTION("x86 generic CPUID driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/i386_ksyms.c linux.18p17/arch/i386/kernel/i386_ksyms.c
--- linux.vanilla/arch/i386/kernel/i386_ksyms.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/i386/kernel/i386_ksyms.c	Tue Sep 12 13:14:57 2000
@@ -20,6 +20,7 @@
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
+extern spinlock_t rtc_lock;
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE)
 extern struct drive_info_struct drive_info;
@@ -119,3 +120,5 @@
 #ifdef CONFIG_VT
 EXPORT_SYMBOL(screen_info);
 #endif
+
+EXPORT_SYMBOL(rtc_lock);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/microcode.c linux.18p17/arch/i386/kernel/microcode.c
--- linux.vanilla/arch/i386/kernel/microcode.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/i386/kernel/microcode.c	Tue Sep  5 22:41:52 2000
@@ -0,0 +1,336 @@
+/*
+ *	CPU Microcode Update interface for Linux
+ *
+ *	Copyright (C) 2000 Tigran Aivazian
+ *
+ *	This driver allows to upgrade microcode on Intel processors
+ *	belonging to P6 family - PentiumPro, Pentium II, Pentium III etc.
+ *
+ *	Reference: Section 8.10 of Volume III, Intel Pentium III Manual, 
+ *	Order Number 243192 or download from:
+ *		
+ *	http://developer.intel.com/design/pentiumii/manuals/243192.htm
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	1.0	16 February 2000, Tigran Aivazian <tigran@sco.com>
+ *		Initial release.
+ *	1.01	18 February 2000, Tigran Aivazian <tigran@sco.com>
+ *		Added read() support + cleanups.
+ *	1.02	21 February 2000, Tigran Aivazian <tigran@sco.com>
+ *		Added 'device trimming' support. open(O_WRONLY) zeroes
+ *		and frees the saved copy of applied microcode.
+ *	1.03	29 February 2000, Tigran Aivazian <tigran@sco.com>
+ *		Made to use devfs (/dev/cpu/microcode) + cleanups.
+ *	1.04	06 June 2000, Simon Trimmer <simon@veritas.com>
+ *		Added misc device support (now uses both devfs and misc).
+ *		Added MICROCODE_IOCFREE ioctl to clear memory.
+ *	1.05	09 June 2000, Simon Trimmer <simon@veritas.com>
+ *		Messages for error cases (non intel & no suitable microcode).
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/miscdevice.h>
+#include <linux/smp.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+
+#define MICROCODE_MINOR 184
+#define MICROCODE_IOCFREE _IO('6',0)
+struct microcode {
+	unsigned int hdrver;
+	unsigned int rev;
+	unsigned int date;
+	unsigned int sig;
+	unsigned int cksum;
+	unsigned int ldrver;
+	unsigned int pf;
+	unsigned int reserved[5];
+	unsigned int bits[500];
+};
+
+#define MICROCODE_VERSION 	"1.05"
+
+MODULE_DESCRIPTION("Intel CPU (P6) microcode update driver");
+MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>");
+EXPORT_NO_SYMBOLS;
+
+/* VFS interface */
+static int microcode_open(struct inode *, struct file *);
+static int microcode_release(struct inode *, struct file *);
+static ssize_t microcode_read(struct file *, char *, size_t, loff_t *);
+static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *);
+static int microcode_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+
+/* internal helpers to do the work */
+static int do_microcode_update(void);
+static void do_update_one(void *);
+
+/*
+ *  Bits in microcode_status. (31 bits of room for future expansion)
+ */
+#define MICROCODE_IS_OPEN	0	/* set if device is in use */
+
+static unsigned long microcode_status;
+
+/* the actual array of microcode blocks, each 2048 bytes */
+static struct microcode *microcode;
+static unsigned int microcode_num;
+static char *mc_applied; /* holds an array of applied microcode blocks */
+static unsigned int mc_fsize;
+
+static struct file_operations microcode_fops = {
+	read:		microcode_read,
+	write:		microcode_write,
+	ioctl:		microcode_ioctl,
+	open:		microcode_open,
+	release:	microcode_release,
+};
+
+static struct miscdevice microcode_dev = {
+	minor: MICROCODE_MINOR,
+	name:	"microcode",
+	fops:	&microcode_fops,
+};
+
+int __init microcode_init(void)
+{
+	int error = 0;
+
+	if (misc_register(&microcode_dev) < 0) {
+		printk(KERN_WARNING 
+			"microcode: can't misc_register on minor=%d\n",
+			MICROCODE_MINOR);
+		error = 1;
+	}
+	printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", 
+			MICROCODE_VERSION);
+	return 0;
+}
+
+#ifdef MODULE
+static void microcode_exit(void)
+{
+	misc_deregister(&microcode_dev);
+	if (mc_applied)
+		kfree(mc_applied);
+	printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", 
+			MICROCODE_VERSION);
+}
+
+int init_module(void)
+{
+	return microcode_init();
+}
+void cleanup_module(void)
+{
+	microcode_exit();
+}
+#endif
+
+/*
+ * We enforce only one user at a time here with open/close.
+ */
+static int microcode_open(struct inode *inode, struct file *file)
+{
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	/* one at a time, please */
+	if (test_and_set_bit(MICROCODE_IS_OPEN, &microcode_status))
+		return -EBUSY;
+
+	return 0;
+}
+
+static int microcode_release(struct inode *inode, struct file *file)
+{
+	clear_bit(MICROCODE_IS_OPEN, &microcode_status);
+	return 0;
+}
+
+/* a pointer to 'struct update_req' is passed to the IPI handler = do_update_one()
+ * update_req[cpu].err is set to 1 if update failed on 'cpu', 0 otherwise
+ * if err==0, microcode[update_req[cpu].slot] points to applied block of microcode
+ */
+struct update_req {
+	int err;
+	int slot;
+} update_req[NR_CPUS];
+
+static int do_microcode_update(void)
+{
+	int i, error = 0, err;
+	struct microcode *m;
+
+	if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0)
+		panic("do_microcode_update(): timed out waiting for other CPUs\n");
+
+	do_update_one((void *)update_req);
+
+	for (i=0; i<smp_num_cpus; i++) {
+		err = update_req[i].err;
+		error += err;
+		if (!err) {
+			m = (struct microcode *)mc_applied + i;
+			memcpy(m, &microcode[update_req[i].slot], sizeof(struct microcode));
+		}
+	}
+	return error;
+}
+
+static void do_update_one(void *arg)
+{
+	int cpu_num = smp_processor_id();
+	struct cpuinfo_x86 *c = cpu_data + cpu_num;
+	struct update_req *req = (struct update_req *)arg + cpu_num;
+	unsigned int pf = 0, val[2], rev, sig;
+	int i,found=0;
+
+	req->err = 1; /* be pessimistic */
+
+	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6){
+		printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num );
+		return;
+	}
+
+	sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8);
+
+	if (c->x86_model >= 5) {
+		/* get processor flags from BBL_CR_OVRD MSR (0x17) */
+		rdmsr(0x17, val[0], val[1]);
+		pf = 1 << ((val[1] >> 18) & 7);
+	}
+
+	for (i=0; i<microcode_num; i++)
+		if (microcode[i].sig == sig && microcode[i].pf == pf &&
+		    microcode[i].ldrver == 1 && microcode[i].hdrver == 1) {
+
+			found=1;
+
+			rdmsr(0x8B, val[0], rev);
+			if (microcode[i].rev <= rev) {
+				printk(KERN_ERR 
+					"microcode: CPU%d not 'upgrading' to earlier revision"
+					" %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
+			} else { 
+				int sum = 0;
+				struct microcode *m = &microcode[i];
+				unsigned int *sump = (unsigned int *)(m+1);
+
+				while (--sump >= (unsigned int *)m)
+					sum += *sump;
+				if (sum != 0) {
+					printk(KERN_ERR "microcode: CPU%d aborting, "
+							"bad checksum\n", cpu_num);
+					break;
+				}
+
+				wrmsr(0x79, (unsigned int)(m->bits), 0);
+				__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
+				rdmsr(0x8B, val[0], val[1]);
+
+				req->err = 0;
+				req->slot = i;
+				printk(KERN_ERR "microcode: CPU%d updated from revision "
+						"%d to %d, date=%08x\n", 
+						cpu_num, rev, val[1], m->date);
+			}
+			break;
+		}
+
+	if(!found)
+		printk(KERN_ERR "microcode: found no data for CPU%d (sig=%x, pflags=%d)\n", cpu_num, sig, pf);
+}
+
+static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos)
+{
+	if (*ppos >= mc_fsize)
+		return 0;
+	if (*ppos + len > mc_fsize)
+		len = mc_fsize - *ppos;
+	if (copy_to_user(buf, mc_applied + *ppos, len))
+		return -EFAULT;
+	*ppos += len;
+	return len;
+}
+
+static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
+{
+	ssize_t ret;
+
+	if (len % sizeof(struct microcode) != 0) {
+		printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", 
+			sizeof(struct microcode));
+		return -EINVAL;
+	}
+	if (!mc_applied) {
+		mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode),
+				GFP_KERNEL);
+		if (!mc_applied) {
+			printk(KERN_ERR "microcode: out of memory for saved microcode\n");
+			return -ENOMEM;
+		}
+		memset(mc_applied, 0, mc_fsize);
+	}
+	
+	lock_kernel();
+	microcode_num = len/sizeof(struct microcode);
+	microcode = vmalloc(len);
+	if (!microcode) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+	if (copy_from_user(microcode, buf, len)) {
+		ret = -EFAULT;
+		goto out_fsize;
+	}
+	if(do_microcode_update()) {
+		ret = -EIO;
+		goto out_fsize;
+	} else {
+		mc_fsize = smp_num_cpus * sizeof(struct microcode);
+		ret = (ssize_t)len;
+	}
+out_fsize:
+	vfree(microcode);
+out_unlock:
+	unlock_kernel();
+	return ret;
+}
+
+static int microcode_ioctl(struct inode *inode, struct file *file, 
+		unsigned int cmd, unsigned long arg)
+{
+	switch(cmd) {
+		case MICROCODE_IOCFREE:
+			if (mc_applied) {
+				memset(mc_applied, 0, mc_fsize);
+				kfree(mc_applied);
+				mc_applied = NULL;
+				printk(KERN_WARNING 
+					"microcode: freed %d bytes\n", mc_fsize);
+				mc_fsize = 0;
+				return 0;
+			}
+			return -ENODATA;
+
+		default:
+			printk(KERN_ERR "microcode: unknown ioctl cmd=%d\n",
+					cmd);
+			return -EINVAL;
+	}
+	/* NOT REACHED */
+	return -EINVAL;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/msr.c linux.18p17/arch/i386/kernel/msr.c
--- linux.vanilla/arch/i386/kernel/msr.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/i386/kernel/msr.c	Thu Oct 19 01:25:23 2000
@@ -0,0 +1,270 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2000 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * msr.c
+ *
+ * x86 MSR access device
+ *
+ * This device is accessed by lseek() to the appropriate register number
+ * and then read/write in chunks of 8 bytes.  A larger size means multiple
+ * reads or writes of the same register.
+ *
+ * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on
+ * an SMP box will direct the access to CPU %d.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp.h>
+#include <linux/major.h>
+
+#include <asm/processor.h>
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/* Note: "err" is handled in a funny way below.  Otherwise one version
+   of gcc or another breaks. */
+
+extern inline int wrmsr_eio(u32 reg, u32 eax, u32 edx)
+{
+  int err;
+
+  asm volatile(
+	       "1:	wrmsr\n"
+	       "2:\n"
+	       ".section .fixup,\"ax\"\n"
+	       "3:	movl %4,%0\n"
+	       "	jmp 2b\n"
+	       ".previous\n"
+	       ".section __ex_table,\"a\"\n"
+	       "	.align 4\n"
+	       "	.long 1b,3b\n"
+	       ".previous"
+	       : "=&bDS" (err)
+	       : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0));
+
+  return err;
+}
+
+extern inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
+{
+  int err;
+
+  asm volatile(
+	       "1:	rdmsr\n"
+	       "2:\n"
+	       ".section .fixup,\"ax\"\n"
+	       "3:	movl %4,%0\n"
+	       "	jmp 2b\n"
+	       ".previous\n"
+	       ".section __ex_table,\"a\"\n"
+	       "	.align 4\n"
+	       "	.long 1b,3b\n"
+	       ".previous"
+	       : "=&bDS" (err), "=a" (*eax), "=d" (*edx)
+	       : "c" (reg), "i" (-EIO), "0" (0));
+
+  return err;
+}
+
+#ifdef CONFIG_SMP
+
+struct msr_command {
+  int cpu;
+  int err;
+  u32 reg;
+  u32 data[2];
+};
+
+static void msr_smp_wrmsr(void *cmd_block)
+{
+  struct msr_command *cmd = (struct msr_command *) cmd_block;
+  
+  if ( cmd->cpu == smp_processor_id() )
+    cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
+}
+
+static void msr_smp_rdmsr(void *cmd_block)
+{
+  struct msr_command *cmd = (struct msr_command *) cmd_block;
+  
+  if ( cmd->cpu == smp_processor_id() )
+    cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
+}
+
+extern inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
+{
+  struct msr_command cmd;
+
+  if ( cpu == smp_processor_id() ) {
+    return wrmsr_eio(reg, eax, edx);
+  } else {
+    cmd.cpu = cpu;
+    cmd.reg = reg;
+    cmd.data[0] = eax;
+    cmd.data[1] = edx;
+    
+    smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
+    return cmd.err;
+  }
+}
+
+extern inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
+{
+  struct msr_command cmd;
+
+  if ( cpu == smp_processor_id() ) {
+    return rdmsr_eio(reg, eax, edx);
+  } else {
+    cmd.cpu = cpu;
+    cmd.reg = reg;
+
+    smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
+    
+    *eax = cmd.data[0];
+    *edx = cmd.data[1];
+
+    return cmd.err;
+  }
+}
+
+#else /* ! CONFIG_SMP */
+
+extern inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
+{
+  return wrmsr_eio(reg, eax, edx);
+}
+
+extern inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
+{
+  return rdmsr_eio(reg, eax, edx);
+}
+
+#endif /* ! CONFIG_SMP */
+
+static loff_t msr_seek(struct file *file, loff_t offset, int orig)
+{
+  switch (orig) {
+  case 0:
+    file->f_pos = offset;
+    return file->f_pos;
+  case 1:
+    file->f_pos += offset;
+    return file->f_pos;
+  default:
+    return -EINVAL;	/* SEEK_END not supported */
+  }
+}
+
+static ssize_t msr_read(struct file * file, char * buf,
+			size_t count, loff_t *ppos)
+{
+  u32 *tmp = (u32 *)buf;
+  u32 data[2];
+  size_t rv;
+  u32 reg = *ppos;
+  int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
+  int err;
+
+  if ( count % 8 )
+    return -EINVAL; /* Invalid chunk size */
+  
+  for ( rv = 0 ; count ; count -= 8 ) {
+    err = do_rdmsr(cpu, reg, &data[0], &data[1]);
+    if ( err )
+      return err;
+    if ( copy_to_user(tmp,&data,8) )
+      return -EFAULT;
+    tmp += 2;
+  }
+
+  return ((char *)tmp) - buf;
+}
+
+static ssize_t msr_write(struct file * file, const char * buf,
+			 size_t count, loff_t *ppos)
+{
+  const u32 *tmp = (const u32 *)buf;
+  u32 data[2];
+  size_t rv;
+  u32 reg = *ppos;
+  int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
+  int err;
+
+  if ( count % 8 )
+    return -EINVAL; /* Invalid chunk size */
+  
+  for ( rv = 0 ; count ; count -= 8 ) {
+    if ( copy_from_user(&data,tmp,8) )
+      return -EFAULT;
+    err = do_wrmsr(cpu, reg, data[0], data[1]);
+    if ( err )
+      return err;
+    tmp += 2;
+  }
+
+  return ((char *)tmp) - buf;
+}
+
+static int msr_open(struct inode *inode, struct file *file)
+{
+  int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
+  struct cpuinfo_x86 *c = &(cpu_data)[cpu];
+  
+  if ( !(cpu_online_map & (1UL << cpu)) )
+    return -ENXIO;		/* No such CPU */
+  if ( !(c->x86_capability & X86_FEATURE_MSR) )
+    return -EIO;		/* MSR not supported */
+  
+  return 0;
+}
+
+/*
+ * File operations we support
+ */
+static struct file_operations msr_fops = {
+  llseek:	msr_seek,
+  read:		msr_read,
+  write:	msr_write,
+  open:		msr_open,
+};
+
+int __init msr_init(void)
+{
+  if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) {
+    printk(KERN_ERR "msr: unable to get major %d for msr\n",
+	   MSR_MAJOR);
+    return -EBUSY;
+  }
+  
+  return 0;
+}
+
+static void msr_exit(void)
+{
+}
+
+module_init(msr_init);
+module_exit(msr_exit);
+
+MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
+MODULE_DESCRIPTION("x86 generic MSR driver");
+EXPORT_NO_SYMBOLS;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/mtrr.c linux.18p17/arch/i386/kernel/mtrr.c
--- linux.vanilla/arch/i386/kernel/mtrr.c	Fri Apr 21 12:45:45 2000
+++ linux.18p17/arch/i386/kernel/mtrr.c	Wed Sep 27 21:05:41 2000
@@ -305,7 +305,7 @@
 static unsigned int ascii_buf_bytes = 0;
 #endif
 static unsigned int *usage_table = NULL;
-static spinlock_t main_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t main_lock  __attribute((unused)) = SPIN_LOCK_UNLOCKED;
 
 /*  Private functions  */
 #ifdef CONFIG_PROC_FS
@@ -337,7 +337,8 @@
 	if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
 	/* else fall through */
       case X86_VENDOR_CENTAUR:
-	return;
+	if(boot_cpu_data.x86 != 6)
+		return;
 	/*break;*/
     }
     /*  Save value of CR4 and clear Page Global Enable (bit 7)  */
@@ -361,6 +362,7 @@
     {
       case X86_VENDOR_AMD:
       case X86_VENDOR_INTEL:
+      case X86_VENDOR_CENTAUR:
 	/*  Disable MTRRs, and set the default type to uncached  */
 	rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
 	wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
@@ -384,8 +386,11 @@
 	if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
 	/* else fall through */
       case X86_VENDOR_CENTAUR:
-	__restore_flags (ctxt->flags);
-	return;
+	if(boot_cpu_data.x86 != 6)
+	{
+		__restore_flags (ctxt->flags);
+		return;
+	}
 	/*break;*/
     }
     /*  Flush caches and TLBs  */
@@ -396,6 +401,7 @@
     {
       case X86_VENDOR_AMD:
       case X86_VENDOR_INTEL:
+      case X86_VENDOR_CENTAUR:
 	wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
 	break;
       case X86_VENDOR_CYRIX:
@@ -434,9 +440,17 @@
 	/*break;*/
       case X86_VENDOR_CYRIX:
 	/*  Cyrix have 8 ARRs  */
+	return 8;
       case X86_VENDOR_CENTAUR:
         /*  and Centaur has 8 MCR's  */
-	return 8;
+	if(boot_cpu_data.x86==5)
+		return 8;
+	/*  the cyrix III has intel compatible MTRR */
+	if(boot_cpu_data.x86==6)
+	{
+		rdmsr (MTRRcap_MSR, config, dummy);
+		return (config & 0xff);
+	}
 	/*break;*/
     }
     return 0;
@@ -452,12 +466,15 @@
       case X86_VENDOR_AMD:
 	if (boot_cpu_data.x86 < 6) return 1; /* pre-Athlon CPUs */
 	/* else fall through */
+      case X86_VENDOR_CENTAUR:
+        if (boot_cpu_data.x86 == 5)
+        	return 1;	/* C6 */
+        /* CyrixIII is Intel like */
       case X86_VENDOR_INTEL:
 	rdmsr (MTRRcap_MSR, config, dummy);
 	return (config & (1<<10));
 	/*break;*/
       case X86_VENDOR_CYRIX:
-      case X86_VENDOR_CENTAUR:
 	return 1;
 	/*break;*/
     }
@@ -1135,7 +1152,7 @@
 	    printk ("mtrr: size: %lx  base: %lx\n", size, base);
 	    return -EINVAL;
 	}
-        if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR)
+        if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR && boot_cpu_data.x86 == 5)
 	{
 	    if (centaur_ctx->type_bits[type]==0) {
 		printk ("mtrr: type not supported\n");
@@ -1758,8 +1775,16 @@
 	get_free_region = cyrix_get_free_region;
 	break;
      case X86_VENDOR_CENTAUR:
-        get_mtrr = centaur_get_mcr;
-        set_mtrr_up = centaur_set_mcr_up;
+        if(boot_cpu_data.x86 == 5)
+        {
+        	get_mtrr = centaur_get_mcr;
+	        set_mtrr_up = centaur_set_mcr_up;
+	}
+	if(boot_cpu_data.x86 == 6)
+	{
+		get_mtrr = intel_get_mtrr;
+		set_mtrr_up = intel_set_mtrr_up;
+	}
         break;
     }
 }   /*  End Function mtrr_setup  */
@@ -1783,8 +1808,11 @@
       case X86_VENDOR_CYRIX:
 	cyrix_arr_init ();
 	break;
-      case X86_VENDOR_CENTAUR:
-        centaur_mcr_init ();
+      case X86_VENDOR_CENTAUR:		/* C6 and Cyrix III have different ones */
+      	if(boot_cpu_data.x86 == 5)
+	        centaur_mcr_init ();
+	if(boot_cpu_data.x86 == 6)
+		get_mtrr_state(&smp_mtrr_state);
         break;
     }
 }   /*  End Function mtrr_init_boot_cpu  */
@@ -1856,7 +1884,8 @@
 	cyrix_arr_init ();
 	break;
       case X86_VENDOR_CENTAUR:
-        centaur_mcr_init ();
+        if(boot_cpu_data.x86 == 5)
+        	centaur_mcr_init ();
         break;
     }
 #  endif  /*  !__SMP__  */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/setup.c linux.18p17/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/kernel/setup.c	Sat Oct 14 00:58:57 2000
@@ -16,19 +16,25 @@
  *  Intel Mobile Pentium II detection fix. Sean Gilley, June 1999.
  *
  *  IDT Winchip tweaks, misc clean ups.
- *	Dave Jones <dave@powertweak.com>, August 1999
+ *	Dave Jones <davej@suse.de>, August 1999
  *
  *	Added proper L2 cache detection for Coppermine
  *	Dragan Stancevic <visitor@valinux.com>, October 1999
  *
  *	Improved Intel cache detection.
- *	Dave Jones <dave@powertweak.com>, October 1999
+ *	Dave Jones <davej@suse.de>, October 1999
  *
  *	Added proper Cascades CPU and L2 cache detection for Cascades
  *	and 8-way type cache happy bunch from Intel:^)
  *	Dragan Stancevic <visitor@valinux.com>, May 2000
  *
  *	Transmeta CPU detection.  H. Peter Anvin <hpa@zytor.com>, May 2000
+ *
+ *	Cleaned up get_model_name(), AMD_model(), added display_cacheinfo().
+ *	Dave Jones <davej@suse.de>, September 2000
+ *
+ *	Added Cyrix III initial detection code
+ *	Alan Cox <alan@redhat.com>, Septembr 2000
  */
 
 /*
@@ -428,19 +434,16 @@
 	conswitchp = &dummy_con;
 #endif
 #endif
-	/*
-	 *	Check the bugs that will bite us before we get booting
-	 */
-
 }
 
+
 __initfunc(static int get_model_name(struct cpuinfo_x86 *c))
 {
-	unsigned int n, dummy, *v, ecx, edx;
+	unsigned int n, dummy, *v;
 
 	/* 
 	 * Actually we must have cpuid or we could never have
-	 * figured out that this was AMD/Cyrix/Transmeta
+	 * figured out that this was AMD/Centaur/Cyrix/Transmeta
 	 * from the vendor info :-).
 	 */
 
@@ -454,49 +457,44 @@
 	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
 	c->x86_model_id[48] = 0;
 	
-	switch(c->x86_vendor)
-	{
-	case X86_VENDOR_AMD:
-		/*  Set MTRR capability flag if appropriate  */
-		if(boot_cpu_data.x86 == 5) {
-			if((boot_cpu_data.x86_model == 9) ||
-			   ((boot_cpu_data.x86_model == 8) && 
-			    (boot_cpu_data.x86_mask >= 8)))
-				c->x86_capability |= X86_FEATURE_MTRR;
-		}
+	return 1;
+}
 
-		/* Fall through */
 
-	case X86_VENDOR_TRANSMETA:
-		/* Cache information */
-		if (n >= 0x80000005){
-			cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
-			printk("CPU: L1 I Cache: %dK  L1 D Cache: %dK\n",
-				ecx>>24, edx>>24);
-			c->x86_cache_size=(ecx>>24)+(edx>>24);
-		}
-		if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
-			boot_cpu_data.x86 == 6 && 
-			boot_cpu_data.x86_model== 3 &&
-			boot_cpu_data.x86_mask == 0)
-		{
-			/* AMD errata T13 (order #21922) */
-			printk("CPU: L2 Cache: 64K\n");
-			c->x86_cache_size = 64;
-		}
-		else if (n >= 0x80000006){
-			cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
-			printk("CPU: L2 Cache: %dK\n", ecx>>16);
-			c->x86_cache_size = ecx>>16;
-		}
-		break;
+__initfunc (static void display_cacheinfo(struct cpuinfo_x86 *c))
+{
+	unsigned int n, dummy, ecx, edx;
 
-	default:		/* Cyrix */
-		break;
+	cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
+
+	if (n >= 0x80000005){
+		cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
+		printk("CPU: L1 I Cache: %dK  L1 D Cache: %dK\n",
+			ecx>>24, edx>>24);
+		c->x86_cache_size=(ecx>>24)+(edx>>24);
+	}
+
+	/* Yes this can occur - the CyrixIII just has a large L1 */
+	if (n < 0x80000006)
+		return;	/* No function to get L2 info */
+
+	cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
+	c->x86_cache_size = ecx>>16;
+
+	/* AMD errata T13 (order #21922) */
+	if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+		boot_cpu_data.x86 == 6 && 
+		boot_cpu_data.x86_model== 3 &&
+		boot_cpu_data.x86_mask == 0)
+	{
+		c->x86_cache_size = 64;
 	}
-	return 1;
+
+	printk("CPU: L2 Cache: %dK\n", c->x86_cache_size);
 }
 
+
+
 __initfunc(static int amd_model(struct cpuinfo_x86 *c))
 {
 	u32 l, h;
@@ -517,7 +515,7 @@
 				/* Anyone with a K5 want to fill this in */				
 				break;
 			}
-			
+
 			/* K6 with old style WHCR */
 			if( c->x86_model < 8 ||
 				(c->x86_model== 8 && c->x86_mask < 8))
@@ -537,16 +535,17 @@
 					restore_flags(flags);
 					printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n",
 						mbytes);
-					
+
 				}
 				break;
 			}
-			if (c->x86_model == 8 || c->x86_model == 9)
+			if (c->x86_model == 8 || c->x86_model == 9 || c->x86_model == 13)
 			{
 				/* The more serious chips .. */
 				
 				if(mbytes>4092)
 					mbytes=4092;
+
 				rdmsr(0xC0000082, l, h);
 				if((l&0xFFFF0000)==0)
 				{
@@ -559,17 +558,35 @@
 					printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n",
 						mbytes);
 				}
+
+				/*  Set MTRR capability flag if appropriate  */
+				if((boot_cpu_data.x86_model == 9) ||
+				  ((boot_cpu_data.x86_model == 8) && 
+				   (boot_cpu_data.x86_mask >= 8)))
+					c->x86_capability |= X86_FEATURE_MTRR;
 				break;
 			}
 			break;
+
 		case 6:	/* An Athlon. We can trust the BIOS probably */
 		{
 			break;
 		}
-		
 	}
+
+	display_cacheinfo(c);
 	return r;
 }
+
+__initfunc(static void intel_model(struct cpuinfo_x86 *c))
+{
+	unsigned int *v = (unsigned int *) c->x86_model_id;
+	cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
+	cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+	c->x86_model_id[48] = 0;
+	printk("CPU: %s\n", c->x86_model_id);
+}
 			
 
 /*
@@ -691,18 +708,17 @@
 		 */
 		
 #ifdef CONFIG_PCI_QUIRKS
-                /* It isnt really a PCI quirk directly, but the cure is the
-       	           same. The MediaGX has deep magic SMM stuff that handles the
-                   SB emulation. It thows away the fifo on disable_dma() which
-                   is wrong and ruins the audio. 
-                   
-                   Bug2: VSA1 has a wrap bug so that using maximum sized DMA 
-                   causes bad things. According to NatSemi VSA2 has another
-                   bug to do with 'hlt'. I've not seen any boards using VSA2
-                   and X doesn't seem to support it either so who cares 8).
-                   VSA1 we work around however.
+		/* It isnt really a PCI quirk directly, but the cure is the
+		   same. The MediaGX has deep magic SMM stuff that handles the
+		   SB emulation. It thows away the fifo on disable_dma() which
+		   is wrong and ruins the audio. 
                    
-                  */
+		   Bug2: VSA1 has a wrap bug so that using maximum sized DMA 
+		   causes bad things. According to NatSemi VSA2 has another
+		   bug to do with 'hlt'. I've not seen any boards using VSA2
+		   and X doesn't seem to support it either so who cares 8).
+		   VSA1 we work around however.
+		*/
 
 		printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n");
                 isa_dma_bridge_buggy = 2;
@@ -759,12 +775,13 @@
 
 __initfunc(static void transmeta_model(struct cpuinfo_x86 *c))
 {
-	unsigned int cap_mask, uk, max, dummy, n, ecx, edx;
+	unsigned int cap_mask, uk, max, dummy;
 	unsigned int cms_rev1, cms_rev2;
 	unsigned int cpu_rev, cpu_freq, cpu_flags;
 	char cpu_info[65];
 
 	get_model_name(c);	/* Same as AMD/Cyrix */
+	display_cacheinfo(c);
 
 	/* Print CMS and CPU revision */
 	cpuid(0x80860000, &max, &dummy, &dummy, &dummy);
@@ -895,8 +912,9 @@
 {
 	int i;
 	char *p = NULL;
+	extern void mcheck_init(void);
 
-	c->loops_per_sec = loops_per_sec;
+	c->loops_per_jiffy = loops_per_jiffy;
 	c->x86_cache_size = -1;
 
 	get_cpu_vendor(c);
@@ -917,7 +935,7 @@
 		printk(KERN_INFO "CPU serial number disabled.\n");
 	}
 
-	mcheck_init(c);
+	mcheck_init();
 	
 	if (c->x86_vendor == X86_VENDOR_CYRIX) {
 		cyrix_model(c);
@@ -931,6 +949,14 @@
 		transmeta_model(c);
 		return;
 	}
+	
+	if(c->x86_vendor == X86_VENDOR_CENTAUR && c->x86==6)
+	{
+		/* The Cyrix III supports model naming and cache queries */
+		get_model_name(c);
+		display_cacheinfo(c);
+		return;
+	}
 
 	if (c->cpuid_level > 1) {
 		/* supports eax=2  call */
@@ -974,7 +1000,19 @@
 				break;
 		}
 	}
-
+	
+	/*
+	 *	Intel finally adopted the AMD/Cyrix extended id naming
+	 *	stuff
+	 */
+	 
+	if(c->x86_vendor ==X86_VENDOR_INTEL && c->x86 == 15)
+	{
+		c->x86 = 6;
+		intel_model(c);
+		return;
+	}
+	
 	for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
 		if (cpu_models[i].vendor == c->x86_vendor &&
 		    cpu_models[i].x86 == c->x86) {
@@ -986,14 +1024,13 @@
 			if ((cpu_models[i].vendor == X86_VENDOR_INTEL)
 			    && (cpu_models[i].x86 == 6)){ 
 				if(c->x86_model == 6 && c->x86_cache_size == 128) {
-                            		p = "Celeron (Mendocino)"; 
-                          	}
-			  	else { 
-                            	if (c->x86_model == 5 && c->x86_cache_size == 0) {
-				  	p = "Celeron (Covington)";
-			    	}
-                        }
-                    }
+					p = "Celeron (Mendocino)"; 
+				} else { 
+					if (c->x86_model == 5 && c->x86_cache_size == 0) {
+						p = "Celeron (Covington)";
+					}
+				}
+			}
 		}
 	}
 
@@ -1026,6 +1063,49 @@
 	"Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise", "Transmeta" };
 
 
+__initfunc(void setup_centaur(struct cpuinfo_x86 *c))
+{
+	u32 hv,lv;
+	
+	/* Centaur C6 Series */
+	if(c->x86==5)
+	{
+		rdmsr(0x107, lv, hv);
+		printk("Centaur FSR was 0x%X ",lv);
+		lv|=(1<<1 | 1<<2 | 1<<7);
+		/* lv|=(1<<6);	- may help too if the board can cope */
+		printk("now 0x%X\n", lv);
+		wrmsr(0x107, lv, hv);
+		/* Emulate MTRRs using Centaur's MCR. */
+		c->x86_capability |= X86_FEATURE_MTRR;
+
+		/* Disable TSC on C6 as per errata. */
+		if (c->x86_model ==4) {
+			printk ("Disabling bugged TSC.\n");
+			c->x86_capability &= ~X86_FEATURE_TSC;
+		}
+
+		/* Set 3DNow! on Winchip 2 and above. */
+		if (c->x86_model >=8)
+		    c->x86_capability |= X86_FEATURE_AMD3D;
+
+		c->x86_capability |=X86_FEATURE_CX8;
+	}
+	/* Cyrix III 'Samuel' CPU */
+	if(c->x86 == 6 && c->x86_model == 6)
+	{
+		rdmsr(0x1107, lv, hv);
+		lv|=(1<<1);	/* Report CX8 */
+		lv|=(1<<7);	/* PGE enable */
+		wrmsr(0x1107, lv, hv);
+		/* Cyrix III */
+		c->x86_capability |= X86_FEATURE_CX8;
+		rdmsr(0x80000001, lv, hv);
+		if(hv&(1<<31))
+			c->x86_capability |= X86_FEATURE_AMD3D;
+	}	
+}
+
 __initfunc(void print_cpu_info(struct cpuinfo_x86 *c))
 {
 	char *vendor = NULL;
@@ -1049,27 +1129,7 @@
 		printk("\n");
 
 	if(c->x86_vendor == X86_VENDOR_CENTAUR) {
-		u32 hv,lv;
-		rdmsr(0x107, lv, hv);
-		printk("Centaur FSR was 0x%X ",lv);
-		lv|=(1<<1 | 1<<2 | 1<<7);
-		/* lv|=(1<<6);	- may help too if the board can cope */
-		printk("now 0x%X\n", lv);
-		wrmsr(0x107, lv, hv);
-		/* Emulate MTRRs using Centaur's MCR. */
-		c->x86_capability |= X86_FEATURE_MTRR;
-
-		/* Disable TSC on C6 as per errata. */
-		if (c->x86_model ==4) {
-			printk ("Disabling bugged TSC.\n");
-			c->x86_capability &= ~X86_FEATURE_TSC;
-		}
-
-		/* Set 3DNow! on Winchip 2 and above. */
-		if (c->x86_model >=8)
-		    c->x86_capability |= X86_FEATURE_AMD3D;
-
-		c->x86_capability |=X86_FEATURE_CX8;
+		setup_centaur(c);
 	}
 }
 
@@ -1082,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;
@@ -1132,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";
@@ -1140,19 +1201,19 @@
 			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";
 			break;
 
 		    case X86_VENDOR_CENTAUR:
 			if (c->x86_model >=8)	/* Only Winchip2 and above */
-			    x86_cap_flags[31] = "3dnow";
+				x86_cap_flags[31] = "3dnow";
 			break;
 
 		    default:
@@ -1191,8 +1252,8 @@
 			if ( c->x86_capability & (1 << i) )
 				p += sprintf(p, " %s", x86_cap_flags[i]);
 		p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n",
-			     (c->loops_per_sec+2500)/500000,
-			     ((c->loops_per_sec+2500)/5000) % 100);
+			     c->loops_per_jiffy/(500000/HZ),
+			     (c->loops_per_jiffy/(5000/HZ)) % 100);
 	}
 	return p - buffer;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/smp.c linux.18p17/arch/i386/kernel/smp.c
--- linux.vanilla/arch/i386/kernel/smp.c	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/kernel/smp.c	Mon Oct  2 10:14:40 2000
@@ -658,7 +658,7 @@
 	return virt_to_phys(trampoline_base);
 }
 
-extern unsigned long i386_endbase __initdata;
+extern unsigned long i386_endbase;
 /*
  *	We are called very early to get the low memory for the
  *	SMP bootup trampoline page.
@@ -1596,12 +1596,12 @@
 		for(i=0;i<32;i++)
 		{
 			if (cpu_online_map&(1<<i))
-				bogosum+=cpu_data[i].loops_per_sec;
+				bogosum+=cpu_data[i].loops_per_jiffy;
 		}
 		printk(KERN_INFO "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_PRINTK(("Before bogocount - setting activated=1.\n"));
 		smp_activated=1;
 		smp_num_cpus=cpucount+1;
@@ -1813,7 +1813,7 @@
 void smp_flush_tlb(void)
 {
 	int cpu = smp_processor_id();
-	int stuck;
+	long long stuck;
 	unsigned long flags;
 
 	/*
@@ -1853,7 +1853,7 @@
 		 * guessed from the TSC calibration.
 		 */
 
-		stuck = 50000000 + cpu_data[cpu].loops_per_sec/2;
+		stuck = 50000000 + cpu_data[cpu].loops_per_jiffy*(HZ/2);
 		while (smp_invalidate_needed) {
 			/*
 			 * Take care of "crossing" invalidates
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/time.c linux.18p17/arch/i386/kernel/time.c
--- linux.vanilla/arch/i386/kernel/time.c	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/i386/kernel/time.c	Tue Sep 12 13:16:22 2000
@@ -77,6 +77,9 @@
 unsigned long fast_gettimeoffset_quotient=0;
 
 extern rwlock_t xtime_lock;
+extern volatile unsigned long lost_ticks;
+
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 
 static inline unsigned long do_fast_gettimeoffset(void)
 {
@@ -112,6 +115,8 @@
 
 #ifndef CONFIG_X86_TSC
 
+spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
+
 /* This function must be called with interrupts disabled 
  * It was inspired by Steve McCanne's microtime-i386 for BSD.  -- jrs
  * 
@@ -156,6 +161,8 @@
 	 */
 	unsigned long jiffies_t;
 
+	/* gets recalled with irq locally disabled */
+	spin_lock(&i8253_lock);
 	/* timer count may underflow right here */
 	outb_p(0x00, 0x43);	/* latch the count ASAP */
 
@@ -214,6 +221,7 @@
 		}
 	} else
 		jiffies_p = jiffies_t;
+	spin_unlock(&i8253_lock);
 
 	count_p = count;
 
@@ -237,7 +245,6 @@
  */
 void do_gettimeofday(struct timeval *tv)
 {
-	extern volatile unsigned long lost_ticks;
 	unsigned long flags;
 	unsigned long usec, sec;
 
@@ -271,6 +278,7 @@
 	 * would have done, and then undo it!
 	 */
 	tv->tv_usec -= do_gettimeoffset();
+	tv->tv_usec -= lost_ticks * (1000000 / HZ);
 
 	while (tv->tv_usec < 0) {
 		tv->tv_usec += 1000000;
@@ -301,6 +309,8 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 
+	/* gets recalled with irq locally disabled */
+	spin_lock(&rtc_lock);
 	save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
 	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
@@ -346,6 +356,7 @@
 	 */
 	CMOS_WRITE(save_control, RTC_CONTROL);
 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+	spin_unlock(&rtc_lock);
 
 	return retval;
 }
@@ -447,10 +458,19 @@
 
 		rdtscl(last_tsc_low);
 
+#if 0 /*
+       * SUBTLE: this is not necessary from here because it's implicit in the
+       * write xtime_lock.
+       */
+		spin_lock(&i8253_lock);
+#endif
 		outb_p(0x00, 0x43);     /* latch the count ASAP */
 
 		count = inb_p(0x40);    /* read the latched count */
 		count |= inb(0x40) << 8;
+#if 0
+		spin_unlock(&i8253_lock);
+#endif
 
 		count = ((LATCH-1) - count) * TICK_SIZE;
 		delay_at_last_interrupt = (count + LATCH/2) / LATCH;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/traps.c linux.18p17/arch/i386/kernel/traps.c
--- linux.vanilla/arch/i386/kernel/traps.c	Sat Sep  9 18:42:32 2000
+++ linux.18p17/arch/i386/kernel/traps.c	Mon Sep 11 17:51:59 2000
@@ -399,6 +399,7 @@
 	/* Ok, finally something we can handle */
 	tsk->tss.trap_no = 1;
 	tsk->tss.error_code = error_code;
+	tsk->tss.debugreg[DR_STATUS] = condition;
 	force_sig(SIGTRAP, tsk);
 	return;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/lib/delay.c linux.18p17/arch/i386/lib/delay.c
--- linux.vanilla/arch/i386/lib/delay.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/lib/delay.c	Sat Sep 30 14:41:43 2000
@@ -22,8 +22,7 @@
 	
 /*
  *	Do a udelay using the TSC for any CPU that happens
- *	to have one that we trust. This could be optimised to avoid
- *	the multiply per loop but its a delay loop so who are we kidding...
+ *	to have one that we trust.
  */
 
 static void __rdtsc_delay(unsigned long loops)
@@ -63,13 +62,19 @@
 		__loop_delay(loops);
 }
 
+/*
+ *	This could be optimised to avoid the multiply per loop but its a
+ *	delay loop so who are we kidding...  (we'd run into nasty 32-bit
+ *	issues with delays > 10 ms or HZ > 100 that way)
+ */
+
 void __const_udelay(unsigned long xloops)
 {
 	int d0;
 	__asm__("mull %0"
 		:"=d" (xloops), "=&a" (d0)
-		:"1" (xloops),"0" (current_cpu_data.loops_per_sec));
-        __delay(xloops);
+		:"1" (xloops),"0" (current_cpu_data.loops_per_jiffy));
+        __delay(xloops * HZ);
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/math-emu/get_address.c linux.18p17/arch/i386/math-emu/get_address.c
--- linux.vanilla/arch/i386/math-emu/get_address.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/math-emu/get_address.c	Fri Sep  1 13:15:46 2000
@@ -155,6 +155,7 @@
 { 
   struct desc_struct descriptor;
   unsigned long base_address, limit, address, seg_top;
+  unsigned short selector;
 
   segment--;
 
@@ -172,14 +173,16 @@
       /* fs and gs aren't used by the kernel, so they still have their
 	 user-space values. */
     case PREFIX_FS_-1:
-      /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+      /* The extra variable is needed here to get gcc to use a 16 bit register
 	 in the assembler statement. */
-      __asm__("mov %%fs,%0":"=r" ((unsigned short)addr->selector));
+      __asm__("mov %%fs,%0":"=r" (selector));
+      addr->selector = selector;
       break;
     case PREFIX_GS_-1:
-      /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+      /* The extra variable is needed here to get gcc to use a 16 bit register
 	 in the assembler statement. */
-      __asm__("mov %%gs,%0":"=r" ((unsigned short)addr->selector));
+      __asm__("mov %%gs,%0":"=r" (selector));
+      addr->selector = selector;
       break;
     default:
       addr->selector = PM_REG_(segment);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/math-emu/poly_tan.c linux.18p17/arch/i386/math-emu/poly_tan.c
--- linux.vanilla/arch/i386/math-emu/poly_tan.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/math-emu/poly_tan.c	Mon Sep 11 17:41:24 2000
@@ -89,7 +89,7 @@
 	{
 	  FPU_settag0(TAG_Valid);
 	  significand(st0_ptr) = 0x8a51e04daabda360LL;
-	  setexponent16(st0_ptr, 0x41 + EXTENDED_Ebias | SIGN_Negative);
+	  setexponent16(st0_ptr, (0x41 + EXTENDED_Ebias) | SIGN_Negative);
 	  return;
 	}
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/init.c linux.18p17/arch/i386/mm/init.c
--- linux.vanilla/arch/i386/mm/init.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/mm/init.c	Mon Sep 11 17:41:24 2000
@@ -384,7 +384,7 @@
 		printk(".\n");
 }
 
-extern unsigned long i386_endbase __initdata;
+extern unsigned long i386_endbase;
 
 __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
 {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/ioremap.c linux.18p17/arch/i386/mm/ioremap.c
--- linux.vanilla/arch/i386/mm/ioremap.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/mm/ioremap.c	Fri Sep  1 14:04:15 2000
@@ -110,7 +110,18 @@
 	 * Don't allow anybody to remap normal RAM that we're using..
 	 */
 	if (phys_addr < virt_to_phys(high_memory))
-		return NULL;
+           {
+		char *temp_addr, *temp_end;
+		int i;
+
+		temp_addr = __va(phys_addr);
+		temp_end = temp_addr + (size - 1);
+	      
+		for(i = MAP_NR(temp_addr); i < MAP_NR(temp_end); i++) {
+			if(!PageReserved(mem_map + i))
+				return NULL;
+		}
+	   }
 
 	/*
 	 * Mappings have to be page-aligned
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/vmlinux.lds.S linux.18p17/arch/i386/vmlinux.lds.S
--- linux.vanilla/arch/i386/vmlinux.lds.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/i386/vmlinux.lds.S	Tue Sep  5 22:20:06 2000
@@ -42,8 +42,16 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(4096);
   __init_end = .;
+
 
   . = ALIGN(32);
   .data.cacheline_aligned : { *(.data.cacheline_aligned) }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/Makefile linux.18p17/arch/m68k/Makefile
--- linux.vanilla/arch/m68k/Makefile	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/Makefile	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/bvme6000/config.c linux.18p17/arch/m68k/bvme6000/config.c
--- linux.vanilla/arch/m68k/bvme6000/config.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/bvme6000/config.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/config.in linux.18p17/arch/m68k/config.in
--- linux.vanilla/arch/m68k/config.in	Sun Jun 11 21:44:09 2000
+++ linux.18p17/arch/m68k/config.in	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/bindec.S linux.18p17/arch/m68k/fpsp040/bindec.S
--- linux.vanilla/arch/m68k/fpsp040/bindec.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/bindec.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/decbin.S linux.18p17/arch/m68k/fpsp040/decbin.S
--- linux.vanilla/arch/m68k/fpsp040/decbin.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/decbin.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/do_func.S linux.18p17/arch/m68k/fpsp040/do_func.S
--- linux.vanilla/arch/m68k/fpsp040/do_func.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/do_func.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/get_op.S linux.18p17/arch/m68k/fpsp040/get_op.S
--- linux.vanilla/arch/m68k/fpsp040/get_op.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/get_op.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/skeleton.S linux.18p17/arch/m68k/fpsp040/skeleton.S
--- linux.vanilla/arch/m68k/fpsp040/skeleton.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/skeleton.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/util.S linux.18p17/arch/m68k/fpsp040/util.S
--- linux.vanilla/arch/m68k/fpsp040/util.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/util.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/fpsp040/x_store.S linux.18p17/arch/m68k/fpsp040/x_store.S
--- linux.vanilla/arch/m68k/fpsp040/x_store.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/fpsp040/x_store.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/entry.S linux.18p17/arch/m68k/kernel/entry.S
--- linux.vanilla/arch/m68k/kernel/entry.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/entry.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/head.S linux.18p17/arch/m68k/kernel/head.S
--- linux.vanilla/arch/m68k/kernel/head.S	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/head.S	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/kgdb.c linux.18p17/arch/m68k/kernel/kgdb.c
--- linux.vanilla/arch/m68k/kernel/kgdb.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/kgdb.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/m68k_defs.c linux.18p17/arch/m68k/kernel/m68k_defs.c
--- linux.vanilla/arch/m68k/kernel/m68k_defs.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/m68k_defs.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/m68k_defs.h linux.18p17/arch/m68k/kernel/m68k_defs.h
--- linux.vanilla/arch/m68k/kernel/m68k_defs.h	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/m68k_defs.h	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/m68k_ksyms.c linux.18p17/arch/m68k/kernel/m68k_ksyms.c
--- linux.vanilla/arch/m68k/kernel/m68k_ksyms.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/m68k_ksyms.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/process.c linux.18p17/arch/m68k/kernel/process.c
--- linux.vanilla/arch/m68k/kernel/process.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/process.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/ptrace.c linux.18p17/arch/m68k/kernel/ptrace.c
--- linux.vanilla/arch/m68k/kernel/ptrace.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/ptrace.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/setup.c linux.18p17/arch/m68k/kernel/setup.c
--- linux.vanilla/arch/m68k/kernel/setup.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/setup.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/signal.c linux.18p17/arch/m68k/kernel/signal.c
--- linux.vanilla/arch/m68k/kernel/signal.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/signal.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/kernel/traps.c linux.18p17/arch/m68k/kernel/traps.c
--- linux.vanilla/arch/m68k/kernel/traps.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/kernel/traps.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/lib/Makefile linux.18p17/arch/m68k/lib/Makefile
--- linux.vanilla/arch/m68k/lib/Makefile	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/lib/Makefile	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/lib/ashldi3.c linux.18p17/arch/m68k/lib/ashldi3.c
--- linux.vanilla/arch/m68k/lib/ashldi3.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/lib/ashldi3.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/lib/lshrdi3.c linux.18p17/arch/m68k/lib/lshrdi3.c
--- linux.vanilla/arch/m68k/lib/lshrdi3.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/lib/lshrdi3.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/Makefile linux.18p17/arch/m68k/mac/Makefile
--- linux.vanilla/arch/m68k/mac/Makefile	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/Makefile	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/adb-bus.c linux.18p17/arch/m68k/mac/adb-bus.c
--- linux.vanilla/arch/m68k/mac/adb-bus.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/adb-bus.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/adb-misc.c linux.18p17/arch/m68k/mac/adb-misc.c
--- linux.vanilla/arch/m68k/mac/adb-misc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/adb-misc.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/bootparse.c linux.18p17/arch/m68k/mac/bootparse.c
--- linux.vanilla/arch/m68k/mac/bootparse.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/bootparse.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/config.c linux.18p17/arch/m68k/mac/config.c
--- linux.vanilla/arch/m68k/mac/config.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/config.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/debug.c linux.18p17/arch/m68k/mac/debug.c
--- linux.vanilla/arch/m68k/mac/debug.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/debug.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/iop.c linux.18p17/arch/m68k/mac/iop.c
--- linux.vanilla/arch/m68k/mac/iop.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/iop.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/mac_ksyms.c linux.18p17/arch/m68k/mac/mac_ksyms.c
--- linux.vanilla/arch/m68k/mac/mac_ksyms.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/mac_ksyms.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/mac_penguin.S linux.18p17/arch/m68k/mac/mac_penguin.S
--- linux.vanilla/arch/m68k/mac/mac_penguin.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/mac_penguin.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/macboing.c linux.18p17/arch/m68k/mac/macboing.c
--- linux.vanilla/arch/m68k/mac/macboing.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/macboing.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/macints.c linux.18p17/arch/m68k/mac/macints.c
--- linux.vanilla/arch/m68k/mac/macints.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/macints.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/mackeyb.c linux.18p17/arch/m68k/mac/mackeyb.c
--- linux.vanilla/arch/m68k/mac/mackeyb.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/mackeyb.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/oss.c linux.18p17/arch/m68k/mac/oss.c
--- linux.vanilla/arch/m68k/mac/oss.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/oss.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/psc.c linux.18p17/arch/m68k/mac/psc.c
--- linux.vanilla/arch/m68k/mac/psc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/psc.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/via.c linux.18p17/arch/m68k/mac/via.c
--- linux.vanilla/arch/m68k/mac/via.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mac/via.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/via6522.c linux.18p17/arch/m68k/mac/via6522.c
--- linux.vanilla/arch/m68k/mac/via6522.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/via6522.c	Thu Jan  1 01:00:00 1970
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mac/via6522.h linux.18p17/arch/m68k/mac/via6522.h
--- linux.vanilla/arch/m68k/mac/via6522.h	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mac/via6522.h	Thu Jan  1 01:00:00 1970
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/Makefile linux.18p17/arch/m68k/math-emu/Makefile
--- linux.vanilla/arch/m68k/math-emu/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/Makefile	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_arith.c linux.18p17/arch/m68k/math-emu/fp_arith.c
--- linux.vanilla/arch/m68k/math-emu/fp_arith.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_arith.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_arith.h linux.18p17/arch/m68k/math-emu/fp_arith.h
--- linux.vanilla/arch/m68k/math-emu/fp_arith.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_arith.h	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_cond.S linux.18p17/arch/m68k/math-emu/fp_cond.S
--- linux.vanilla/arch/m68k/math-emu/fp_cond.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_cond.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_decode.h linux.18p17/arch/m68k/math-emu/fp_decode.h
--- linux.vanilla/arch/m68k/math-emu/fp_decode.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_decode.h	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_emu.h linux.18p17/arch/m68k/math-emu/fp_emu.h
--- linux.vanilla/arch/m68k/math-emu/fp_emu.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_emu.h	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_entry.S linux.18p17/arch/m68k/math-emu/fp_entry.S
--- linux.vanilla/arch/m68k/math-emu/fp_entry.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_entry.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_log.c linux.18p17/arch/m68k/math-emu/fp_log.c
--- linux.vanilla/arch/m68k/math-emu/fp_log.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_log.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_move.S linux.18p17/arch/m68k/math-emu/fp_move.S
--- linux.vanilla/arch/m68k/math-emu/fp_move.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_move.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_movem.S linux.18p17/arch/m68k/math-emu/fp_movem.S
--- linux.vanilla/arch/m68k/math-emu/fp_movem.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_movem.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_scan.S linux.18p17/arch/m68k/math-emu/fp_scan.S
--- linux.vanilla/arch/m68k/math-emu/fp_scan.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_scan.S	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_trig.c linux.18p17/arch/m68k/math-emu/fp_trig.c
--- linux.vanilla/arch/m68k/math-emu/fp_trig.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_trig.c	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_trig.h linux.18p17/arch/m68k/math-emu/fp_trig.h
--- linux.vanilla/arch/m68k/math-emu/fp_trig.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_trig.h	Fri Oct 13 23:36:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/fp_util.S linux.18p17/arch/m68k/math-emu/fp_util.S
--- linux.vanilla/arch/m68k/math-emu/fp_util.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/fp_util.S	Fri Oct 13 23:36:55 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/math-emu/multi_arith.h linux.18p17/arch/m68k/math-emu/multi_arith.h
--- linux.vanilla/arch/m68k/math-emu/multi_arith.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/math-emu/multi_arith.h	Fri Oct 13 23:36:55 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mm/fault.c linux.18p17/arch/m68k/mm/fault.c
--- linux.vanilla/arch/m68k/mm/fault.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mm/fault.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mm/init.c linux.18p17/arch/m68k/mm/init.c
--- linux.vanilla/arch/m68k/mm/init.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mm/init.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mm/kmap.c linux.18p17/arch/m68k/mm/kmap.c
--- linux.vanilla/arch/m68k/mm/kmap.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mm/kmap.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mvme147/config.c linux.18p17/arch/m68k/mvme147/config.c
--- linux.vanilla/arch/m68k/mvme147/config.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mvme147/config.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mvme16x/Makefile linux.18p17/arch/m68k/mvme16x/Makefile
--- linux.vanilla/arch/m68k/mvme16x/Makefile	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mvme16x/Makefile	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mvme16x/config.c linux.18p17/arch/m68k/mvme16x/config.c
--- linux.vanilla/arch/m68k/mvme16x/config.c	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/mvme16x/config.c	Fri Oct 13 23:30:47 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mvme16x/mvme16x_ksyms.c linux.18p17/arch/m68k/mvme16x/mvme16x_ksyms.c
--- linux.vanilla/arch/m68k/mvme16x/mvme16x_ksyms.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/m68k/mvme16x/mvme16x_ksyms.c	Fri Oct 13 23:36:55 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/vmlinux.lds linux.18p17/arch/m68k/vmlinux.lds
--- linux.vanilla/arch/m68k/vmlinux.lds	Fri Apr 21 12:45:46 2000
+++ linux.18p17/arch/m68k/vmlinux.lds	Tue Sep  5 22:20:06 2000
@@ -35,6 +35,7 @@
 
   _edata = .;			/* End of data section */
 
+
   . = ALIGN(16);
   .data.cacheline_aligned : { *(.data.cacheline_aligned) }
 
@@ -42,6 +43,13 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(8192);
   __init_end = .;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/chrp_setup.c linux.18p17/arch/ppc/kernel/chrp_setup.c
--- linux.vanilla/arch/ppc/kernel/chrp_setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/chrp_setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/gemini_setup.c linux.18p17/arch/ppc/kernel/gemini_setup.c
--- linux.vanilla/arch/ppc/kernel/gemini_setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/gemini_setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/mbx_setup.c linux.18p17/arch/ppc/kernel/mbx_setup.c
--- linux.vanilla/arch/ppc/kernel/mbx_setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/mbx_setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/pmac_setup.c linux.18p17/arch/ppc/kernel/pmac_setup.c
--- linux.vanilla/arch/ppc/kernel/pmac_setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/pmac_setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/prep_setup.c linux.18p17/arch/ppc/kernel/prep_setup.c
--- linux.vanilla/arch/ppc/kernel/prep_setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/prep_setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/setup.c linux.18p17/arch/ppc/kernel/setup.c
--- linux.vanilla/arch/ppc/kernel/setup.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/setup.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/kernel/smp.c linux.18p17/arch/ppc/kernel/smp.c
--- linux.vanilla/arch/ppc/kernel/smp.c	Sat Sep  9 18:42:33 2000
+++ linux.18p17/arch/ppc/kernel/smp.c	Sun Oct 15 23:56:27 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/vmlinux.lds linux.18p17/arch/ppc/vmlinux.lds
--- linux.vanilla/arch/ppc/vmlinux.lds	Fri Apr 21 12:45:47 2000
+++ linux.18p17/arch/ppc/vmlinux.lds	Tue Sep  5 22:20:06 2000
@@ -71,8 +71,16 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(4096);
   __init_end = .;
+
 
   . = ALIGN(4096);
   __pmac_begin = .;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/vmlinux.lds linux.18p17/arch/s390/vmlinux.lds
--- linux.vanilla/arch/s390/vmlinux.lds	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/s390/vmlinux.lds	Tue Sep  5 22:20:06 2000
@@ -42,8 +42,16 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(4096);
   __init_end = .;
+
 
   . = ALIGN(32);
   .data.cacheline_aligned : { *(.data.cacheline_aligned) }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/defconfig linux.18p17/arch/sparc/defconfig
--- linux.vanilla/arch/sparc/defconfig	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/defconfig	Sat Oct 14 00:28:11 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/entry.S linux.18p17/arch/sparc/kernel/entry.S
--- linux.vanilla/arch/sparc/kernel/entry.S	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/entry.S	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/setup.c linux.18p17/arch/sparc/kernel/setup.c
--- linux.vanilla/arch/sparc/kernel/setup.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/setup.c	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/smp.c linux.18p17/arch/sparc/kernel/smp.c
--- linux.vanilla/arch/sparc/kernel/smp.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/smp.c	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sparc_ksyms.c linux.18p17/arch/sparc/kernel/sparc_ksyms.c
--- linux.vanilla/arch/sparc/kernel/sparc_ksyms.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc/kernel/sparc_ksyms.c	Sun Sep 17 17:57:13 2000
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.77.2.5 2000/07/17 18:44:26 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.77.2.6 2000/09/16 14:15:15 davem Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -65,6 +65,7 @@
 extern int __ashrdi3(int, int);
 extern int __ashldi3(int, int);
 extern int __lshrdi3(int, int);
+extern int __muldi3(int, int);
 
 extern void dump_thread(struct pt_regs *, struct user *);
 
@@ -272,6 +273,7 @@
 EXPORT_SYMBOL_NOVERS(__ashrdi3);
 EXPORT_SYMBOL_NOVERS(__ashldi3);
 EXPORT_SYMBOL_NOVERS(__lshrdi3);
+EXPORT_SYMBOL_NOVERS(__muldi3);
 
 EXPORT_SYMBOL_DOT(rem);
 EXPORT_SYMBOL_DOT(urem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sun4d_smp.c linux.18p17/arch/sparc/kernel/sun4d_smp.c
--- linux.vanilla/arch/sparc/kernel/sun4d_smp.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/sun4d_smp.c	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sun4m_smp.c linux.18p17/arch/sparc/kernel/sun4m_smp.c
--- linux.vanilla/arch/sparc/kernel/sun4m_smp.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/sun4m_smp.c	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sys_sunos.c linux.18p17/arch/sparc/kernel/sys_sunos.c
--- linux.vanilla/arch/sparc/kernel/sys_sunos.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/kernel/sys_sunos.c	Sun Sep 17 17:57:13 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.94.2.4 2000/01/17 21:28:27 davem Exp $
+/* $Id: sys_sunos.c,v 1.94.2.5 2000/09/16 14:15:15 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -707,7 +707,7 @@
 #define SMNT_SYS5         128
 
 struct sunos_fh_t {
-	char fh_data [NFS_FHSIZE];
+	char fh_data [NFS2_FHSIZE];
 };
 
 struct sunos_nfs_mount_args {
@@ -774,7 +774,7 @@
 
 	server.sin_family = AF_INET;
 	server.sin_addr = addr->sin_addr;
-	server.sin_port = NFS_PORT;
+	server.sin_port = NFS2_PORT;
 
 	/* Call sys_connect */
 	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/lib/Makefile linux.18p17/arch/sparc/lib/Makefile
--- linux.vanilla/arch/sparc/lib/Makefile	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/lib/Makefile	Sun Sep 17 17:57:13 2000
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.28.2.1 1999/09/28 16:47:36 davem Exp $
+# $Id: Makefile,v 1.28.2.2 2000/09/16 14:15:15 davem Exp $
 # Makefile for Sparc library files..
 #
 
@@ -6,7 +6,7 @@
         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
 	strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
 	copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \
-	ashldi3.o
+	ashldi3.o muldi3.o
 
 ifdef CONFIG_SMP
 OBJS += irqlock.o
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/lib/muldi3.S linux.18p17/arch/sparc/lib/muldi3.S
--- linux.vanilla/arch/sparc/lib/muldi3.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/arch/sparc/lib/muldi3.S	Fri Sep 15 22:10:43 2000
@@ -0,0 +1,76 @@
+/* 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.  */
+
+	.text
+	.align 4
+	.globl __muldi3
+__muldi3:
+	save  %sp, -104, %sp
+	wr  %g0, %i1, %y
+	sra  %i3, 0x1f, %g2
+	and  %i1, %g2, %g2
+	andcc  %g0, 0, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, 0, %g1
+	add  %g1, %g2, %l2
+	rd  %y, %o1
+	mov  %o1, %l3
+	mov  %i1, %o0
+	call  .umul
+	mov  %i2, %o1
+	mov  %o0, %l0
+	mov  %i0, %o0
+	call  .umul
+	mov  %i3, %o1
+	add  %l0, %o0, %l0
+	mov  %l2, %i0
+	add  %l2, %l0, %i0
+	ret 
+	restore  %g0, %l3, %o1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/mm/srmmu.c linux.18p17/arch/sparc/mm/srmmu.c
--- linux.vanilla/arch/sparc/mm/srmmu.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/mm/srmmu.c	Wed Sep 13 16:48:19 2000
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.187.2.9 2000/03/05 17:39:18 davem Exp $
+/* $Id: srmmu.c,v 1.187.2.10 2000/09/07 04:44:48 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -2056,8 +2056,8 @@
 			goto done;
 		inode = file->f_dentry->d_inode;
 		offset = (address & PAGE_MASK) - vma->vm_start;
-		vmaring = inode->i_mmap; 
-		do {
+		vmaring = inode->i_mmap_shared; 
+		if (vmaring) do {
 			/* Do not mistake ourselves as another mapping. */
 			if(vmaring == vma)
 				continue;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/mm/sun4c.c linux.18p17/arch/sparc/mm/sun4c.c
--- linux.vanilla/arch/sparc/mm/sun4c.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/mm/sun4c.c	Wed Sep 13 16:48:19 2000
@@ -1,4 +1,4 @@
-/* $Id: sun4c.c,v 1.173.2.5 1999/10/11 08:24:44 davem Exp $
+/* $Id: sun4c.c,v 1.173.2.6 2000/09/07 04:44:48 davem Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -2420,9 +2420,9 @@
 		inode = dentry->d_inode;
 	if (inode) {
 		unsigned long offset = (address & PAGE_MASK) - vma->vm_start;
-		struct vm_area_struct *vmaring = inode->i_mmap; 
+		struct vm_area_struct *vmaring = inode->i_mmap_shared; 
 		int alias_found = 0;
-		do {
+		if (vmaring) do {
 			unsigned long vaddr = vmaring->vm_start + offset;
 			unsigned long start;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/vmlinux.lds linux.18p17/arch/sparc/vmlinux.lds
--- linux.vanilla/arch/sparc/vmlinux.lds	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc/vmlinux.lds	Sun Sep 10 14:47:06 2000
@@ -40,8 +40,17 @@
   .text.init : { *(.text.init) }
   __init_text_end = .;
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(4096);
   __init_end = .;
+
+
   __bss_start = .;
   .sbss      : { *(.sbss) *(.scommon) }
   .bss       :
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/config.in linux.18p17/arch/sparc64/config.in
--- linux.vanilla/arch/sparc64/config.in	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc64/config.in	Fri Sep  1 14:13:19 2000
@@ -247,9 +247,10 @@
 			tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
 			tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
 		fi
-#		bool 'FDDI driver support' CONFIG_FDDI
-#		if [ "$CONFIG_FDDI" = "y" ]; then
-#		fi
+		bool 'FDDI driver support' CONFIG_FDDI
+		if [ "$CONFIG_FDDI" = "y" ]; then
+			tristate '  SysKonnect FDDI PCI support' CONFIG_SKFP
+		fi
 	fi
 	endmenu
 fi
@@ -271,6 +272,12 @@
     dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
   fi
 fi
+endmenu
+
+mainmenu_option next_comment
+comment 'XFree86 DRI support'
+bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
+dep_tristate '  Creator/Creator3D' CONFIG_DRM_FFB $CONFIG_DRM
 endmenu
 
 source fs/Config.in
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/defconfig linux.18p17/arch/sparc64/defconfig
--- linux.vanilla/arch/sparc64/defconfig	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc64/defconfig	Sat Oct 14 00:28:12 2000
@@ -31,8 +31,6 @@
 # CONFIG_FB_PM2_FIFO_DISCONNECT is not set
 CONFIG_FB_PM2_PCI=y
 CONFIG_FB_ATY=y
-# CONFIG_FB_MATROX is not set
-# CONFIG_FB_ATY128 is not set
 CONFIG_FB_SBUS=y
 CONFIG_FB_CREATOR=y
 CONFIG_FB_CGSIX=y
@@ -102,6 +100,7 @@
 CONFIG_PRINTER=m
 CONFIG_PRINTER_READBACK=y
 CONFIG_ENVCTRL=m
+CONFIG_DISPLAY7SEG=m
 
 #
 # Floppy, IDE, and other block devices
@@ -113,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
@@ -262,6 +262,8 @@
 CONFIG_NE2K_PCI=m
 CONFIG_EEXPRESS_PRO100=m
 CONFIG_SK98LIN=m
+CONFIG_FDDI=y
+CONFIG_SKFP=m
 
 #
 # Unix 98 PTY support
@@ -276,6 +278,12 @@
 CONFIG_VIDEO_BT848=y
 
 #
+# XFree86 DRI support
+#
+CONFIG_DRM=y
+CONFIG_DRM_FFB=m
+
+#
 # Filesystems
 #
 # CONFIG_QUOTA is not set
@@ -312,7 +320,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
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/entry.S linux.18p17/arch/sparc64/kernel/entry.S
--- linux.vanilla/arch/sparc64/kernel/entry.S	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc64/kernel/entry.S	Wed Sep 13 16:48:19 2000
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.103.2.5 2000/03/06 22:30:22 davem Exp $
+/* $Id: entry.S,v 1.103.2.6 2000/09/08 14:00:04 jj Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -958,9 +958,9 @@
 
 1:		b,pt		%xcc, ret_sys_call
 		 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0
-sparc_exit:	rdpr		%otherwin, %g1
-		rdpr		%pstate, %g2
+sparc_exit:	rdpr		%pstate, %g2
 		wrpr		%g2, PSTATE_IE, %pstate
+		rdpr		%otherwin, %g1
 		rdpr		%cansave, %g3
 		add		%g3, %g1, %g3
 		wrpr		%g3, 0x0, %cansave
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/ioctl32.c linux.18p17/arch/sparc64/kernel/ioctl32.c
--- linux.vanilla/arch/sparc64/kernel/ioctl32.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc64/kernel/ioctl32.c	Sun Sep 10 14:49:10 2000
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.62.2.13 2000/07/27 01:50:59 davem Exp $
+/* $Id: ioctl32.c,v 1.62.2.16 2000/09/05 00:10:54 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
@@ -41,6 +41,9 @@
 #include <linux/netdevice.h>
 #include <linux/smb_fs.h>
 #include <linux/blkdev.h>
+#include <linux/in6.h>
+#include <linux/ipv6_route.h>
+#include <net/sock.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -622,30 +625,64 @@
 
 };
 
+struct in6_rtmsg32 {
+	struct in6_addr		rtmsg_dst;
+	struct in6_addr		rtmsg_src;
+	struct in6_addr		rtmsg_gateway;
+	u32			rtmsg_type;
+	u16			rtmsg_dst_len;
+	u16			rtmsg_src_len;
+	u32			rtmsg_metric;
+	u32			rtmsg_info;
+	u32			rtmsg_flags;
+	s32			rtmsg_ifindex;
+};
+
+extern struct socket *sockfd_lookup(int fd, int *err);
+
 static inline int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
-	struct rtentry r;
+	int ret;
+	void *r = NULL;
+	struct in6_rtmsg r6;
+	struct rtentry r4;
 	char devname[16];
 	u32 rtdev;
-	int ret;
 	mm_segment_t old_fs = get_fs();
-	
-	ret = copy_from_user (&r.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr));
-	ret |= __get_user (r.rt_flags, &(((struct rtentry32 *)arg)->rt_flags));
-	ret |= __get_user (r.rt_metric, &(((struct rtentry32 *)arg)->rt_metric));
-	ret |= __get_user (r.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu));
-	ret |= __get_user (r.rt_window, &(((struct rtentry32 *)arg)->rt_window));
-	ret |= __get_user (r.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt));
-	ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev));
-	if (rtdev) {
-		ret |= copy_from_user (devname, (char *)A(rtdev), 15);
-		r.rt_dev = devname; devname[15] = 0;
-	} else
-		r.rt_dev = 0;
+	struct socket *mysock = sockfd_lookup(fd, &ret);
+
+	if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */
+		ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst),
+			3 * sizeof(struct in6_addr));
+		ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type));
+		ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len));
+		ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len));
+		ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric));
+		ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info));
+		ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags));
+		ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex));
+		
+		r = (void *)&r6;
+	} else { /* ipv4 */
+		ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr));
+		ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags));
+		ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric));
+		ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu));
+		ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window));
+		ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt));
+		ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev));
+		if (rtdev) {
+			ret |= copy_from_user (devname, (char *)A(rtdev), 15);
+			r4.rt_dev = devname; devname[15] = 0;
+		} else
+			r4.rt_dev = 0;
+
+		r = (void *)&r4;
+	}
 	if (ret)
 		return -EFAULT;
 	set_fs (KERNEL_DS);
-	ret = sys_ioctl (fd, cmd, (long)&r);
+	ret = sys_ioctl (fd, cmd, (unsigned long) r);
 	set_fs (old_fs);
 	return ret;
 }
@@ -1739,6 +1776,544 @@
 	return err;
 }
 
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+/* This really belongs in include/linux/drm.h -DaveM */
+#include "../../../drivers/char/drm/drm.h"
+
+typedef struct drm32_version {
+	int    version_major;	  /* Major version			    */
+	int    version_minor;	  /* Minor version			    */
+	int    version_patchlevel;/* Patch level			    */
+	int    name_len;	  /* Length of name buffer		    */
+	u32    name;		  /* Name of driver			    */
+	int    date_len;	  /* Length of date buffer		    */
+	u32    date;		  /* User-space buffer to hold date	    */
+	int    desc_len;	  /* Length of desc buffer		    */
+	u32    desc;		  /* User-space buffer to hold desc	    */
+} drm32_version_t;
+#define DRM32_IOCTL_VERSION    DRM_IOWR(0x00, drm32_version_t)
+
+static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_version_t *uversion = (drm32_version_t *)arg;
+	char *name_ptr, *date_ptr, *desc_ptr;
+	u32 tmp1, tmp2, tmp3;
+	drm_version_t kversion;
+	mm_segment_t old_fs;
+	int ret;
+
+	memset(&kversion, 0, sizeof(kversion));
+	if (get_user(kversion.name_len, &uversion->name_len) ||
+	    get_user(kversion.date_len, &uversion->date_len) ||
+	    get_user(kversion.desc_len, &uversion->desc_len) ||
+	    get_user(tmp1, &uversion->name) ||
+	    get_user(tmp2, &uversion->date) ||
+	    get_user(tmp3, &uversion->desc))
+		return -EFAULT;
+
+	name_ptr = (char *) A(tmp1);
+	date_ptr = (char *) A(tmp2);
+	desc_ptr = (char *) A(tmp3);
+
+	ret = -ENOMEM;
+	if (kversion.name_len && name_ptr) {
+		kversion.name = kmalloc(kversion.name_len, GFP_KERNEL);
+		if (!kversion.name)
+			goto out;
+	}
+	if (kversion.date_len && date_ptr) {
+		kversion.date = kmalloc(kversion.date_len, GFP_KERNEL);
+		if (!kversion.date)
+			goto out;
+	}
+	if (kversion.desc_len && desc_ptr) {
+		kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL);
+		if (!kversion.desc)
+			goto out;
+	}
+
+        old_fs = get_fs();
+	set_fs(KERNEL_DS);
+        ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion);
+        set_fs(old_fs);
+
+	if (!ret) {
+		if ((kversion.name &&
+		     copy_to_user(name_ptr, kversion.name, kversion.name_len)) ||
+		    (kversion.date &&
+		     copy_to_user(date_ptr, kversion.date, kversion.date_len)) ||
+		    (kversion.desc &&
+		     copy_to_user(desc_ptr, kversion.desc, kversion.desc_len)))
+			ret = -EFAULT;
+		if (put_user(kversion.version_major, &uversion->version_major) ||
+		    put_user(kversion.version_minor, &uversion->version_minor) ||
+		    put_user(kversion.version_patchlevel, &uversion->version_patchlevel) ||
+		    put_user(kversion.name_len, &uversion->name_len) ||
+		    put_user(kversion.date_len, &uversion->date_len) ||
+		    put_user(kversion.desc_len, &uversion->desc_len))
+			ret = -EFAULT;
+	}
+
+out:
+	if (kversion.name)
+		kfree(kversion.name);
+	if (kversion.date)
+		kfree(kversion.date);
+	if (kversion.desc)
+		kfree(kversion.desc);
+	return ret;
+}
+
+typedef struct drm32_unique {
+	int	unique_len;	  /* Length of unique			    */
+	u32	unique;		  /* Unique name for driver instantiation   */
+} drm32_unique_t;
+#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t)
+#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t)
+
+static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_unique_t *uarg = (drm32_unique_t *)arg;
+	drm_unique_t karg;
+	mm_segment_t old_fs;
+	char *uptr;
+	u32 tmp;
+	int ret;
+
+	if (get_user(karg.unique_len, &uarg->unique_len))
+		return -EFAULT;
+	karg.unique = NULL;
+
+	if (get_user(tmp, &uarg->unique))
+		return -EFAULT;
+
+	uptr = (char *) A(tmp);
+
+	if (uptr) {
+		karg.unique = kmalloc(karg.unique_len, GFP_KERNEL);
+		if (!karg.unique)
+			return -ENOMEM;
+		if (cmd == DRM32_IOCTL_SET_UNIQUE &&
+		    copy_from_user(karg.unique, uptr, karg.unique_len)) {
+			kfree(karg.unique);
+			return -EFAULT;
+		}
+	}
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (cmd == DRM32_IOCTL_GET_UNIQUE)
+		ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg);
+	else
+		ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg);
+        set_fs(old_fs);
+
+	if (!ret) {
+		if (cmd == DRM32_IOCTL_GET_UNIQUE &&
+		    uptr != NULL &&
+		    copy_to_user(uptr, karg.unique, karg.unique_len))
+			ret = -EFAULT;
+		if (put_user(karg.unique_len, &uarg->unique_len))
+			ret = -EFAULT;
+	}
+
+	if (karg.unique != NULL)
+		kfree(karg.unique);
+
+	return ret;
+}
+
+typedef struct drm32_map {
+	u32		offset;	 /* Requested physical address (0 for SAREA)*/
+	u32		size;	 /* Requested physical size (bytes)	    */
+	drm_map_type_t	type;	 /* Type of memory to map		    */
+	drm_map_flags_t flags;	 /* Flags				    */
+	u32		handle;  /* User-space: "Handle" to pass to mmap    */
+				 /* Kernel-space: kernel-virtual address    */
+	int		mtrr;	 /* MTRR slot used			    */
+				 /* Private data			    */
+} drm32_map_t;
+#define DRM32_IOCTL_ADD_MAP    DRM_IOWR(0x15, drm32_map_t)
+
+static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_map_t *uarg = (drm32_map_t *) arg;
+	drm_map_t karg;
+	mm_segment_t old_fs;
+	u32 tmp;
+	int ret;
+
+	ret  = get_user(karg.offset, &uarg->offset);
+	ret |= get_user(karg.size, &uarg->size);
+	ret |= get_user(karg.type, &uarg->type);
+	ret |= get_user(karg.flags, &uarg->flags);
+	ret |= get_user(tmp, &uarg->handle);
+	ret |= get_user(karg.mtrr, &uarg->mtrr);
+	if (ret)
+		return -EFAULT;
+
+	karg.handle = (void *) A(tmp);
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg);
+	set_fs(old_fs);
+
+	if (!ret) {
+		ret  = put_user(karg.offset, &uarg->offset);
+		ret |= put_user(karg.size, &uarg->size);
+		ret |= put_user(karg.type, &uarg->type);
+		ret |= put_user(karg.flags, &uarg->flags);
+		tmp = (u32) (long)karg.handle;
+		ret |= put_user(tmp, &uarg->handle);
+		ret |= put_user(karg.mtrr, &uarg->mtrr);
+		if (ret)
+			ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+typedef struct drm32_buf_info {
+	int	       count;	/* Entries in list			     */
+	u32	       list;    /* (drm_buf_desc_t *) */ 
+} drm32_buf_info_t;
+#define DRM32_IOCTL_INFO_BUFS  DRM_IOWR(0x18, drm32_buf_info_t)
+
+static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg;
+	drm_buf_desc_t *ulist;
+	drm_buf_info_t karg;
+	mm_segment_t old_fs;
+	int orig_count, ret;
+	u32 tmp;
+
+	if (get_user(karg.count, &uarg->count) ||
+	    get_user(tmp, &uarg->list))
+		return -EFAULT;
+
+	ulist = (drm_buf_desc_t *) A(tmp);
+
+	orig_count = karg.count;
+
+	karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL);
+	if (!karg.list)
+		return -EFAULT;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg);
+	set_fs(old_fs);
+
+	if (!ret) {
+		if (karg.count <= orig_count &&
+		    (copy_to_user(ulist, karg.list,
+				  karg.count * sizeof(drm_buf_desc_t))))
+			ret = -EFAULT;
+		if (put_user(karg.count, &uarg->count))
+			ret = -EFAULT;
+	}
+
+	kfree(karg.list);
+
+	return ret;
+}
+
+typedef struct drm32_buf_free {
+	int	       count;
+	u32	       list;	/* (int *) */
+} drm32_buf_free_t;
+#define DRM32_IOCTL_FREE_BUFS  DRM_IOW( 0x1a, drm32_buf_free_t)
+
+static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg;
+	drm_buf_free_t karg;
+	mm_segment_t old_fs;
+	int *ulist;
+	int ret;
+	u32 tmp;
+
+	if (get_user(karg.count, &uarg->count) ||
+	    get_user(tmp, &uarg->list))
+		return -EFAULT;
+
+	ulist = (int *) A(tmp);
+
+	karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL);
+	if (!karg.list)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int))))
+		goto out;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg);
+	set_fs(old_fs);
+
+out:
+	kfree(karg.list);
+
+	return ret;
+}
+
+typedef struct drm32_buf_pub {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	u32		  address;     /* Address of buffer (void *)	     */
+} drm32_buf_pub_t;
+
+typedef struct drm32_buf_map {
+	int	      count;	/* Length of buflist			    */
+	u32	      virtual;	/* Mmaped area in user-virtual (void *)	    */
+	u32 	      list;	/* Buffer information (drm_buf_pub_t *)	    */
+} drm32_buf_map_t;
+#define DRM32_IOCTL_MAP_BUFS   DRM_IOWR(0x19, drm32_buf_map_t)
+
+static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg;
+	drm32_buf_pub_t *ulist;
+	drm_buf_map_t karg;
+	mm_segment_t old_fs;
+	int orig_count, ret, i;
+	u32 tmp1, tmp2;
+
+	if (get_user(karg.count, &uarg->count) ||
+	    get_user(tmp1, &uarg->virtual) ||
+	    get_user(tmp2, &uarg->list))
+		return -EFAULT;
+
+	karg.virtual = (void *) A(tmp1);
+	ulist = (drm32_buf_pub_t *) A(tmp2);
+
+	orig_count = karg.count;
+
+	karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL);
+	if (!karg.list)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	for (i = 0; i < karg.count; i++) {
+		if (get_user(karg.list[i].idx, &ulist[i].idx) ||
+		    get_user(karg.list[i].total, &ulist[i].total) ||
+		    get_user(karg.list[i].used, &ulist[i].used) ||
+		    get_user(tmp1, &ulist[i].address))
+			goto out;
+
+		karg.list[i].address = (void *) A(tmp1);
+	}
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg);
+	set_fs(old_fs);
+
+	if (!ret) {
+		for (i = 0; i < orig_count; i++) {
+			tmp1 = (u32) (long) karg.list[i].address;
+			if (put_user(karg.list[i].idx, &ulist[i].idx) ||
+			    put_user(karg.list[i].total, &ulist[i].total) ||
+			    put_user(karg.list[i].used, &ulist[i].used) ||
+			    put_user(tmp1, &ulist[i].address)) {
+				ret = -EFAULT;
+				goto out;
+			}
+		}
+		if (put_user(karg.count, &uarg->count))
+			ret = -EFAULT;
+	}
+
+out:
+	kfree(karg.list);
+	return ret;
+}
+
+typedef struct drm32_dma {
+				/* Indices here refer to the offset into
+				   buflist in drm_buf_get_t.  */
+	int		context;	  /* Context handle		    */
+	int		send_count;	  /* Number of buffers to send	    */
+	u32		send_indices;	  /* List of handles to buffers (int *) */
+	u32		send_sizes;	  /* Lengths of data to send (int *) */
+	drm_dma_flags_t flags;		  /* Flags			    */
+	int		request_count;	  /* Number of buffers requested    */
+	int		request_size;	  /* Desired size for buffers	    */
+	u32		request_indices;  /* Buffer information (int *)	    */
+	u32		request_sizes;    /* (int *) */
+	int		granted_count;	  /* Number of buffers granted	    */
+} drm32_dma_t;
+#define DRM32_IOCTL_DMA	     DRM_IOWR(0x29, drm32_dma_t)
+
+/* RED PEN	The DRM layer blindly dereferences the send/request
+ * 		indice/size arrays even though they are userland
+ * 		pointers.  -DaveM
+ */
+static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_dma_t *uarg = (drm32_dma_t *) arg;
+	int *u_si, *u_ss, *u_ri, *u_rs;
+	drm_dma_t karg;
+	mm_segment_t old_fs;
+	int ret;
+	u32 tmp1, tmp2, tmp3, tmp4;
+
+	karg.send_indices = karg.send_sizes = NULL;
+	karg.request_indices = karg.request_sizes = NULL;
+
+	if (get_user(karg.context, &uarg->context) ||
+	    get_user(karg.send_count, &uarg->send_count) ||
+	    get_user(tmp1, &uarg->send_indices) ||
+	    get_user(tmp2, &uarg->send_sizes) ||
+	    get_user(karg.flags, &uarg->flags) ||
+	    get_user(karg.request_count, &uarg->request_count) ||
+	    get_user(karg.request_size, &uarg->request_size) ||
+	    get_user(tmp3, &uarg->request_indices) ||
+	    get_user(tmp4, &uarg->request_sizes) ||
+	    get_user(karg.granted_count, &uarg->granted_count))
+		return -EFAULT;
+
+	u_si = (int *) A(tmp1);
+	u_ss = (int *) A(tmp2);
+	u_ri = (int *) A(tmp3);
+	u_rs = (int *) A(tmp4);
+
+	if (karg.send_count) {
+		karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL);
+		karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL);
+
+		ret = -ENOMEM;
+		if (!karg.send_indices || !karg.send_sizes)
+			goto out;
+
+		ret = -EFAULT;
+		if (copy_from_user(karg.send_indices, u_si,
+				   (karg.send_count * sizeof(int))) ||
+		    copy_from_user(karg.send_sizes, u_ss,
+				   (karg.send_count * sizeof(int))))
+			goto out;
+	}
+
+	if (karg.request_count) {
+		karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL);
+		karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL);
+
+		ret = -ENOMEM;
+		if (!karg.request_indices || !karg.request_sizes)
+			goto out;
+
+		ret = -EFAULT;
+		if (copy_from_user(karg.request_indices, u_ri,
+				   (karg.request_count * sizeof(int))) ||
+		    copy_from_user(karg.request_sizes, u_rs,
+				   (karg.request_count * sizeof(int))))
+			goto out;
+	}
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg);
+	set_fs(old_fs);
+
+	if (!ret) {
+		if (put_user(karg.context, &uarg->context) ||
+		    put_user(karg.send_count, &uarg->send_count) ||
+		    put_user(karg.flags, &uarg->flags) ||
+		    put_user(karg.request_count, &uarg->request_count) ||
+		    put_user(karg.request_size, &uarg->request_size) ||
+		    put_user(karg.granted_count, &uarg->granted_count))
+			ret = -EFAULT;
+
+		if (karg.send_count) {
+			if (copy_to_user(u_si, karg.send_indices,
+					 (karg.send_count * sizeof(int))) ||
+			    copy_to_user(u_ss, karg.send_sizes,
+					 (karg.send_count * sizeof(int))))
+				ret = -EFAULT;
+		}
+		if (karg.request_count) {
+			if (copy_to_user(u_ri, karg.request_indices,
+					 (karg.request_count * sizeof(int))) ||
+			    copy_to_user(u_rs, karg.request_sizes,
+					 (karg.request_count * sizeof(int))))
+				ret = -EFAULT;
+		}
+	}
+
+out:
+	if (karg.send_indices)
+		kfree(karg.send_indices);
+	if (karg.send_sizes)
+		kfree(karg.send_sizes);
+	if (karg.request_indices)
+		kfree(karg.request_indices);
+	if (karg.request_sizes)
+		kfree(karg.request_sizes);
+
+	return ret;
+}
+
+typedef struct drm32_ctx_res {
+	int		count;
+	u32		contexts; /* (drm_ctx_t *) */
+} drm32_ctx_res_t;
+#define DRM32_IOCTL_RES_CTX    DRM_IOWR(0x26, drm32_ctx_res_t)
+
+static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg;
+	drm_ctx_t *ulist;
+	drm_ctx_res_t karg;
+	mm_segment_t old_fs;
+	int orig_count, ret;
+	u32 tmp;
+
+	karg.contexts = NULL;
+	if (get_user(karg.count, &uarg->count) ||
+	    get_user(tmp, &uarg->contexts))
+		return -EFAULT;
+
+	ulist = (drm_ctx_t *) A(tmp);
+
+	orig_count = karg.count;
+	if (karg.count && ulist) {
+		karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL);
+		if (!karg.contexts)
+			return -ENOMEM;
+		if (copy_from_user(karg.contexts, ulist,
+				   (karg.count * sizeof(drm_ctx_t)))) {
+			kfree(karg.contexts);
+			return -EFAULT;
+		}
+	}
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg);
+	set_fs(old_fs);
+
+	if (!ret) {
+		if (orig_count) {
+			if (copy_to_user(ulist, karg.contexts,
+					 (orig_count * sizeof(drm_ctx_t))))
+				ret = -EFAULT;
+		}
+		if (put_user(karg.count, &uarg->count))
+			ret = -EFAULT;
+	}
+
+	if (karg.contexts)
+		kfree(karg.contexts);
+
+	return ret;
+}
+
+#endif /* defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) */
+
 asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
 	struct file * filp;
@@ -1930,6 +2505,44 @@
 		error = do_smb_getmountuid(fd, cmd, arg);
 		goto out;
 
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+	case DRM32_IOCTL_VERSION:
+		error = drm32_version(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_GET_UNIQUE:
+		error = drm32_getsetunique(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_SET_UNIQUE:
+		error = drm32_getsetunique(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_ADD_MAP:
+		error = drm32_addmap(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_INFO_BUFS:
+		error = drm32_info_bufs(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_FREE_BUFS:
+		error = drm32_free_bufs(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_MAP_BUFS:
+		error = drm32_map_bufs(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_DMA:
+		error = drm32_dma(fd, cmd, arg);
+		goto out;
+
+	case DRM32_IOCTL_RES_CTX:
+		error = drm32_res_ctx(fd, cmd, arg);
+		goto out;
+#endif /* DRM */
+
 	/* List here exlicitly which ioctl's are known to have
 	 * compatable types passed or none at all...
 	 */
@@ -2486,6 +3099,30 @@
 
 	/* SMB ioctls which do not need any translations */
 	case SMB_IOC_NEWCONN:
+
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+	/* DRM ioctls which do not need any translations */
+	case DRM_IOCTL_GET_MAGIC:
+	case DRM_IOCTL_IRQ_BUSID:
+	case DRM_IOCTL_AUTH_MAGIC:
+	case DRM_IOCTL_BLOCK:
+	case DRM_IOCTL_UNBLOCK:
+	case DRM_IOCTL_CONTROL:
+	case DRM_IOCTL_ADD_BUFS:
+	case DRM_IOCTL_MARK_BUFS:
+	case DRM_IOCTL_ADD_CTX:
+	case DRM_IOCTL_RM_CTX:
+	case DRM_IOCTL_MOD_CTX:
+	case DRM_IOCTL_GET_CTX:
+	case DRM_IOCTL_SWITCH_CTX:
+	case DRM_IOCTL_NEW_CTX:
+	case DRM_IOCTL_ADD_DRAW:
+	case DRM_IOCTL_RM_DRAW:
+	case DRM_IOCTL_LOCK:
+	case DRM_IOCTL_UNLOCK:
+	case DRM_IOCTL_FINISH:
+#endif /* DRM */
+
 
 		error = sys_ioctl (fd, cmd, arg);
 		goto out;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/setup.c linux.18p17/arch/sparc64/kernel/setup.c
--- linux.vanilla/arch/sparc64/kernel/setup.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc64/kernel/setup.c	Sat Oct 14 00:28:12 2000
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.43.2.3 2000/03/03 23:50:37 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)
@@ -550,7 +550,7 @@
 	init_task.tss.kregs = &fake_swapper_regs;
 
 #ifdef CONFIG_IP_PNP
-	if (!ic_set_manually) {
+	if (ic_myaddr == INADDR_NONE) {
 		int chosen = prom_finddevice ("/chosen");
 		u32 cl, sv, gw;
 		
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/smp.c linux.18p17/arch/sparc64/kernel/smp.c
--- linux.vanilla/arch/sparc64/kernel/smp.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc64/kernel/smp.c	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sparc64_ksyms.c linux.18p17/arch/sparc64/kernel/sparc64_ksyms.c
--- linux.vanilla/arch/sparc64/kernel/sparc64_ksyms.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc64/kernel/sparc64_ksyms.c	Sun Sep 10 14:49:10 2000
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.58.2.8 2000/06/14 07:41:19 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.58.2.9 2000/09/05 00:10:54 davem Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -83,6 +83,7 @@
 extern int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
 extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
 extern void VISenter(void);
+extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space);
                 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
@@ -196,6 +197,9 @@
 EXPORT_SYMBOL(insl);
 EXPORT_SYMBOL(pci_dma_wsync);
 #endif
+
+/* I/O device mmaping on Sparc64. */
+EXPORT_SYMBOL(io_remap_page_range);
 
 /* Solaris/SunOS binary compatibility */
 EXPORT_SYMBOL(_sigpause_common);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sparc32.c linux.18p17/arch/sparc64/kernel/sys_sparc32.c
--- linux.vanilla/arch/sparc64/kernel/sys_sparc32.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/arch/sparc64/kernel/sys_sparc32.c	Thu Oct 19 01:15:54 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.107.2.9 2000/06/20 17:12:25 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107.2.14 2000/10/10 04:50:50 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -45,6 +45,9 @@
 #include <linux/stat.h>
 #include <linux/timex.h>
 #include <linux/filter.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -1377,7 +1380,7 @@
 
 static void *do_ncp_super_data_conv(void *raw_data)
 {
-	struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data;
+	struct ncp_mount_data news, *n = &news; 
 	struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data;
 
 	n->dir_mode = n32->dir_mode;
@@ -1387,6 +1390,7 @@
 	memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int)));
 	n->wdog_pid = n32->wdog_pid;
 	n->mounted_uid = n32->mounted_uid;
+	memcpy(raw_data, n, sizeof(struct ncp_mount_data)); 
 	return raw_data;
 }
 
@@ -1401,7 +1405,7 @@
 
 static void *do_smb_super_data_conv(void *raw_data)
 {
-	struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
+	struct smb_mount_data news, *s = &news;
 	struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
 
 	s->version = s32->version;
@@ -1410,6 +1414,7 @@
 	s->gid = s32->gid;
 	s->file_mode = s32->file_mode;
 	s->dir_mode = s32->dir_mode;
+	memcpy(raw_data, s, sizeof(struct smb_mount_data)); 
 	return raw_data;
 }
 
@@ -2665,42 +2670,86 @@
 extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
 				     char *optval, int optlen);
 
-asmlinkage int sys32_setsockopt(int fd, int level, int optname,
+static int do_set_attach_filter(int fd, int level, int optname,
 				char *optval, int optlen)
 {
-	if (optname == SO_ATTACH_FILTER) {
-		struct sock_fprog32 {
-			__u16 len;
-			__u32 filter;
-		} *fprog32 = (struct sock_fprog32 *)optval;
-		struct sock_fprog kfprog;
-		struct sock_filter *kfilter;
-		unsigned int fsize;
-		mm_segment_t old_fs;
-		__u32 uptr;
-		int ret;
+	struct sock_fprog32 {
+		__u16 len;
+		__u32 filter;
+	} *fprog32 = (struct sock_fprog32 *)optval;
+	struct sock_fprog kfprog;
+	struct sock_filter *kfilter;
+	unsigned int fsize;
+	mm_segment_t old_fs;
+	__u32 uptr;
+	int ret;
 
-		if (get_user(kfprog.len, &fprog32->len) ||
-		    __get_user(uptr, &fprog32->filter))
-			return -EFAULT;
-		kfprog.filter = (struct sock_filter *)A(uptr);
-		fsize = kfprog.len * sizeof(struct sock_filter);
-		kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL);
-		if (kfilter == NULL)
-			return -ENOMEM;
-		if (copy_from_user(kfilter, kfprog.filter, fsize)) {
-			kfree(kfilter);
-			return -EFAULT;
-		}
-		kfprog.filter = kfilter;
-		old_fs = get_fs();
-		set_fs(KERNEL_DS);
-		ret = sys_setsockopt(fd, level, optname,
-				     (char *)&kfprog, sizeof(kfprog));
-		set_fs(old_fs);
+	if (get_user(kfprog.len, &fprog32->len) ||
+	    __get_user(uptr, &fprog32->filter))
+		return -EFAULT;
+
+	kfprog.filter = (struct sock_filter *)A(uptr);
+	fsize = kfprog.len * sizeof(struct sock_filter);
+
+	kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL);
+	if (kfilter == NULL)
+		return -ENOMEM;
+
+	if (copy_from_user(kfilter, kfprog.filter, fsize)) {
 		kfree(kfilter);
-		return ret;
+		return -EFAULT;
 	}
+
+	kfprog.filter = kfilter;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_setsockopt(fd, level, optname,
+			     (char *)&kfprog, sizeof(kfprog));
+	set_fs(old_fs);
+
+	kfree(kfilter);
+
+	return ret;
+}
+
+static int do_set_icmpv6_filter(int fd, int level, int optname,
+				char *optval, int optlen)
+{
+	struct icmp6_filter kfilter;
+	mm_segment_t old_fs;
+	int ret, i;
+
+	if (copy_from_user(&kfilter, optval, sizeof(kfilter)))
+		return -EFAULT;
+
+
+	for (i = 0; i < 8; i += 2) {
+		u32 tmp = kfilter.data[i];
+
+		kfilter.data[i] = kfilter.data[i + 1];
+		kfilter.data[i + 1] = tmp;
+	}
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_setsockopt(fd, level, optname,
+			     (char *) &kfilter, sizeof(kfilter));
+	set_fs(old_fs);
+
+	return ret;
+}
+
+asmlinkage int sys32_setsockopt(int fd, int level, int optname,
+				char *optval, int optlen)
+{
+	if (optname == SO_ATTACH_FILTER)
+		return do_set_attach_filter(fd, level, optname,
+					    optval, optlen);
+	if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
+		return do_set_icmpv6_filter(fd, level, optname,
+					    optval, optlen);
+
 	return sys_setsockopt(fd, level, optname, optval, optlen);
 }
 
@@ -2875,7 +2924,7 @@
 /*
  * count32() counts the number of arguments/envelopes
  */
-static int count32(u32 * argv)
+static int count32(u32 * argv, int max)
 {
 	int i = 0;
 
@@ -2884,9 +2933,13 @@
 			u32 p; int error;
 
 			error = get_user(p,argv);
-			if (error) return error;
-			if (!p) break;
-			argv++; i++;
+			if (error)
+				return error;
+			if (!p)
+				break;
+			argv++;
+			if (++i > max)
+				return -E2BIG;
 		}
 	}
 	return i;
@@ -2903,18 +2956,24 @@
 {
 	u32 str;
 
-	if (!p) return 0;	/* bullet-proofing */
+	if ((long)p <= 0)
+		return p;	/* bullet-proofing */
 	while (argc-- > 0) {
 		int len;
 		unsigned long pos;
 
 		get_user(str, argv+argc);
-		if (!str) panic("VFS: argc is wrong");
-		len = strlen_user((char *)A(str));	/* includes the '\0' */
-		if (p < len)	/* this shouldn't happen - 128kB */
-			return 0;
-		p -= len; pos = p;
-		while (len) {
+		if (!str)
+			return -EFAULT;
+
+		len = strnlen_user((char *)A(str), p);	/* includes the '\0' */
+		if (!len)
+			return -EFAULT;
+		if (len > p)
+			return -E2BIG;
+		p -= len;
+		pos = p;
+		while (len > 0) {
 			char *pag;
 			int offset, bytes_to_copy;
 
@@ -2961,11 +3020,11 @@
 	bprm.java = 0;
 	bprm.loader = 0;
 	bprm.exec = 0;
-	if ((bprm.argc = count32(argv)) < 0) {
+	if ((bprm.argc = count32(argv, bprm.p / sizeof(u32))) < 0) {
 		dput(dentry);
 		return bprm.argc;
 	}
-	if ((bprm.envc = count32(envp)) < 0) {
+	if ((bprm.envc = count32(envp, bprm.p / sizeof(u32))) < 0) {
 		dput(dentry);
 		return bprm.envc;
 	}
@@ -2977,11 +3036,11 @@
 		bprm.exec = bprm.p;
 		bprm.p = copy_strings32(bprm.envc,envp,bprm.page,bprm.p);
 		bprm.p = copy_strings32(bprm.argc,argv,bprm.page,bprm.p);
-		if (!bprm.p)
-			retval = -E2BIG;
+		if ((long)bprm.p < 0)
+			retval = (int) bprm.p;
 	}
 
-	if(retval>=0)
+	if (retval >= 0)
 		retval = search_binary_handler(&bprm,regs);
 	if(retval>=0)
 		/* execve success */
@@ -4021,4 +4080,56 @@
 		ret = -EFAULT;
 
 	return ret;
+}
+
+struct __sysctl_args32 {
+	u32 name;
+	int nlen;
+	u32 oldval;
+	u32 oldlenp;
+	u32 newval;
+	u32 newlen;
+	u32 __unused[4];
+};
+
+extern int do_sysctl(int *name, int nlen,
+		     void *oldval, size_t *oldlenp,
+		     void *newval, size_t newlen);
+
+asmlinkage long sys32_sysctl(struct __sysctl_args32 *args)
+{
+	struct __sysctl_args32 tmp;
+	int error;
+	size_t oldlen, *oldlenp = NULL;
+	unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7;
+
+	if (copy_from_user(&tmp, args, sizeof(tmp)))
+		return -EFAULT;
+
+	if (tmp.oldval && tmp.oldlenp) {
+		/* Duh, this is ugly and might not work if sysctl_args
+		   is in read-only memory, but do_sysctl does indirectly
+		   a lot of uaccess in both directions and we'd have to
+		   basically copy the whole sysctl.c here, and
+		   glibc's __sysctl uses rw memory for the structure
+		   anyway.  */
+		if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) ||
+		    put_user(oldlen, (size_t *)addr))
+			return -EFAULT;
+		oldlenp = (size_t *)addr;
+	}
+
+	lock_kernel();
+	error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval),
+			  oldlenp, (void *)A(tmp.newval), tmp.newlen);
+	unlock_kernel();
+	if (oldlenp) {
+		if (!error) {
+			if (get_user(oldlen, (size_t *)addr) ||
+			    put_user(oldlen, (u32 *)A(tmp.oldlenp)))
+				error = -EFAULT;
+		}
+		copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused));
+	}
+	return error;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sunos32.c linux.18p17/arch/sparc64/kernel/sys_sunos32.c
--- linux.vanilla/arch/sparc64/kernel/sys_sunos32.c	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc64/kernel/sys_sunos32.c	Sun Sep 17 17:57:13 2000
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.22.2.3 2000/01/17 21:28:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.22.2.4 2000/09/16 14:15:15 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -672,7 +672,7 @@
 #define SMNT_SYS5         128
 
 struct sunos_fh_t {
-	char fh_data [NFS_FHSIZE];
+	char fh_data [NFS2_FHSIZE];
 };
 
 struct sunos_nfs_mount_args {
@@ -746,7 +746,7 @@
 
 	server.sin_family = AF_INET;
 	server.sin_addr = addr->sin_addr;
-	server.sin_port = NFS_PORT;
+	server.sin_port = NFS2_PORT;
 
 	/* Call sys_connect */
 	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/systbls.S linux.18p17/arch/sparc64/kernel/systbls.S
--- linux.vanilla/arch/sparc64/kernel/systbls.S	Fri Apr 21 12:45:48 2000
+++ linux.18p17/arch/sparc64/kernel/systbls.S	Sun Sep 10 14:49:10 2000
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.53.2.4 2000/03/24 00:03:28 davem Exp $
+/* $Id: systbls.S,v 1.53.2.5 2000/08/23 04:18:26 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -69,7 +69,7 @@
 	.word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 	.word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep
-/*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
+/*250*/	.word sys_mremap, sys32_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl
 	.word sys_aplib
 
 	/* Now the 64-bit native Linux syscall table. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/vmlinux.lds linux.18p17/arch/sparc64/vmlinux.lds
--- linux.vanilla/arch/sparc64/vmlinux.lds	Fri Apr 21 12:45:49 2000
+++ linux.18p17/arch/sparc64/vmlinux.lds	Sun Sep 10 14:47:06 2000
@@ -39,8 +39,17 @@
   __init_begin = .;
   .text.init : { *(.text.init) }
   .data.init : { *(.data.init) }
+  . = ALIGN(16);		/* __setup() commandline parameters */
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;		/* the init functions to be called */
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
   . = ALIGN(8192);
   __init_end = .;
+
+
   __bss_start = .;
   .sbss      : { *(.sbss) *(.scommon) }
   .bss       :
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/Makefile linux.18p17/drivers/Makefile
--- linux.vanilla/drivers/Makefile	Fri Apr 21 12:46:14 2000
+++ linux.18p17/drivers/Makefile	Sat Oct 14 00:03:45 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/Makefile linux.18p17/drivers/acorn/Makefile
--- linux.vanilla/drivers/acorn/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/acorn/Makefile	Fri Sep 15 23:31:12 2000
@@ -0,0 +1,12 @@
+#
+# Makefile for the Acorn-specific Linux kernel device drivers.
+#
+# 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 (not a .c file).
+
+SUB_DIRS     := block char net scsi
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/Config.in linux.18p17/drivers/acorn/block/Config.in
--- linux.vanilla/drivers/acorn/block/Config.in	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/Config.in	Fri Sep 15 23:31:12 2000
@@ -10,9 +10,12 @@
   dep_tristate '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE
 fi
 
-tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
-if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
-  bool '   Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
+  tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772
+  tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
+  if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
+    bool '   Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+  fi
 fi
 
 endmenu
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/Makefile linux.18p17/drivers/acorn/block/Makefile
--- linux.vanilla/drivers/acorn/block/Makefile	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/Makefile	Fri Sep 15 23:31:12 2000
@@ -14,13 +14,11 @@
 M_OBJS   :=
 MOD_LIST_NAME := ACORN_BLOCK_MODULES
 
-ifeq ($(CONFIG_ARCH_ARC),y)
-  ifeq ($(CONFIG_BLK_DEV_FD),y)
-    L_OBJS += fd1772.o fd1772dma.o
-  else
-    ifeq ($(CONFIG_BLK_DEV_FD),m)
-      M_OBJS += fd1772_mod.o
-    endif
+ifeq ($(CONFIG_BLK_DEV_FD1772),y)
+  L_OBJS += fd1772.o fd1772dma.o
+else
+  ifeq ($(CONFIG_BLK_DEV_FD1772),m)
+    M_OBJS += fd1772_mod.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/fd1772.c linux.18p17/drivers/acorn/block/fd1772.c
--- linux.vanilla/drivers/acorn/block/fd1772.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/fd1772.c	Fri Sep 15 23:31:12 2000
@@ -114,6 +114,10 @@
  *		  I wish I knew why that timer didn't work.....
  *
  *     16/11/96 - Fiddled and frigged for 2.0.18
+ *
+ * DAG 30/01/99 - Started frobbing for 2.2.1
+ * DAG 20/06/99 - A little more frobbing:
+       Included include/asm/uaccess.h for get_user/put_user
  */
 
 #include <linux/sched.h>
@@ -136,14 +140,16 @@
 #include <asm/dma.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
+#include <asm/ioc.h>
 #include <asm/irq.h>
-#include <asm/irq-no.h>
 #include <asm/pgtable.h>
 #include <asm/segment.h>
+#include <asm/uaccess.h>
+
 
 #define MAJOR_NR FLOPPY_MAJOR
 #define FLOPPY_DMA 0
-#include "blk.h"
+#include <linux/blk.h>
 
 /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with
  * little additional rework in this file). But I'm not yet sure if
@@ -1598,7 +1604,7 @@
 };
 
 
-int floppy_init(void)
+int fd1772_init(void)
 {
 	int i;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/fd1772dma.S linux.18p17/drivers/acorn/block/fd1772dma.S
--- linux.vanilla/drivers/acorn/block/fd1772dma.S	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/fd1772dma.S	Fri Sep 15 23:31:12 2000
@@ -4,45 +4,45 @@
 .text
 
 
-  .global _fdc1772_dataaddr
-_fdc1772_fiqdata:
+  .global fdc1772_dataaddr
+fdc1772_fiqdata:
 @ Number of bytes left to DMA
-  .global _fdc1772_bytestogo
-_fdc1772_bytestogo:
+  .global fdc1772_bytestogo
+fdc1772_bytestogo:
   .word 0
 @ Place to put/get data from in DMA
-  .global _fdc1772_dataaddr
-_fdc1772_dataaddr:
+  .global fdc1772_dataaddr
+fdc1772_dataaddr:
   .word 0
   
-  .global _fdc1772_fdc_int_done
-_fdc1772_fdc_int_done:
+  .global fdc1772_fdc_int_done
+fdc1772_fdc_int_done:
   .word 0
-  .global _fdc1772_comendstatus
-_fdc1772_comendstatus:
+  .global fdc1772_comendstatus
+fdc1772_comendstatus:
   .word 0
 
 @ We hang this off DMA channel 1
-    .global _fdc1772_comendhandler
-_fdc1772_comendhandler:
+    .global fdc1772_comendhandler
+fdc1772_comendhandler:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#2
   subeqs   pc,r14,#4        @ should I leave a space here
   orr      r9,r8,#0x10000   @ FDC base
-  adr      r8,_fdc1772_fdc_int_done
+  adr      r8,fdc1772_fdc_int_done
   ldrb     r10,[r9,#0]  @ FDC status
   mov      r9,#1        @ Got a FIQ flag
   stmia    r8,{r9,r10}
   subs     pc,r14,#4
 
 
-    .global _fdc1772_dma_read
-_fdc1772_dma_read:
+    .global fdc1772_dma_read
+fdc1772_dma_read:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#1
-  beq      _fdc1772_dma_read_notours
+  beq      fdc1772_dma_read_notours
   orr      r8,r8,#0x10000   @ FDC base
   ldrb     r10,[r8,#0xc]   @ Read from FDC data reg (also clears interrupt)
   ldmia    r11,{r8,r9}
@@ -51,19 +51,19 @@
   strplb   r10,[r9],#1     @ Store the data and increment the pointer
   stmplia  r11,{r8,r9}     @ Update count/pointers
   @ Handle any other interrupts if there are any
-_fdc1772_dma_read_notours:
+fdc1772_dma_read_notours:
   @ Cant branch because this code has been copied down to the FIQ vector
   ldr pc,[pc,#-4]
-  .word _fdc1772_comendhandler
-  .global _fdc1772_dma_read_end
-_fdc1772_dma_read_end:
+  .word fdc1772_comendhandler
+  .global fdc1772_dma_read_end
+fdc1772_dma_read_end:
 
-    .global _fdc1772_dma_write
-_fdc1772_dma_write:
+    .global fdc1772_dma_write
+fdc1772_dma_write:
   mov      r8,#IOC_BASE
   ldrb     r9,[r8,#0x34]    @ IOC FIQ status
   tst      r9,#1
-  beq      _fdc1772_dma_write_notours
+  beq      fdc1772_dma_write_notours
   orr      r8,r8,#0x10000   @ FDC base
   ldmia    r11,{r9,r10}
   subs     r9,r9,#1        @ One less byte to go
@@ -72,23 +72,23 @@
   strplb   r12,[r8,#0xc]   @ write it to FDC data reg
   stmplia  r11,{r9,r10}    @ Update count and pointer - should clear interrupt
   @ Handle any other interrupts
-_fdc1772_dma_write_notours:
+fdc1772_dma_write_notours:
   @ Cant branch because this code has been copied down to the FIQ vector
   ldr pc,[pc,#-4]
-  .word _fdc1772_comendhandler
+  .word fdc1772_comendhandler
 
-  .global _fdc1772_dma_write_end
-_fdc1772_dma_write_end:
+  .global fdc1772_dma_write_end
+fdc1772_dma_write_end:
   
 
 @ Setup the FIQ R11 to point to the data and store the count, address
 @ for this dma
 @ R0=count
 @ R1=address
-  .global _fdc1772_setupdma
-_fdc1772_setupdma:
+  .global fdc1772_setupdma
+fdc1772_setupdma:
 	@ The big job is flipping in and out of FIQ mode
-	adr	r2,_fdc1772_fiqdata	@ This is what we really came here for
+	adr	r2,fdc1772_fiqdata	@ This is what we really came here for
   stmia  r2,{r0,r1}
 	mov	r3, pc
 	teqp	pc,#0x0c000001	@ Disable FIQs, IRQs and switch to FIQ mode
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/ide-ics.c linux.18p17/drivers/acorn/block/ide-ics.c
--- linux.vanilla/drivers/acorn/block/ide-ics.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/ide-ics.c	Fri Sep 15 23:31:12 2000
@@ -4,8 +4,9 @@
  * Copyright (c) 1996,1997 Russell King.
  *
  * Changelog:
- *  08-06-1996	RMK	Created
- *  12-09-1997	RMK	Added interrupt enable/disable
+ *  08-Jun-1996	RMK	Created
+ *  12-Sep-1997	RMK	Added interrupt enable/disable
+ *  17-Apr-1999	RMK	Support for V6 EASI
  */
 
 #include <linux/module.h>
@@ -44,7 +45,8 @@
 #define ICS_ARCIN_V6_IDESTEPPING	4
 
 static const card_ids icside_cids[] = {
-	{ MANU_ICS, PROD_ICS_IDE },
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
 	{ 0xffff, 0xffff }
 };
 
@@ -81,6 +83,8 @@
 	icside_irqenable_arcin_v5,
 	icside_irqdisable_arcin_v5,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -106,9 +110,22 @@
 	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
 }
 
+/* Prototype: icside_irqprobe(struct expansion_card *ec)
+ * Purpose  : detect an active interrupt from card
+ */
+static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+	return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+	       inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
 static const expansioncard_ops_t icside_ops_arcin_v6 = {
 	icside_irqenable_arcin_v6,
 	icside_irqdisable_arcin_v6,
+	icside_irqpending_arcin_v6,
+	NULL,
 	NULL,
 	NULL
 };
@@ -157,7 +174,7 @@
 		printk ("icside: ***********************************\n");
 		printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
 		printk ("icside: ***********************************\n");
-		printk ("icside: please report this to: linux@arm.uk.linux.org\n");
+		printk ("icside: please report this to linux@arm.linux.org.uk\n");
 		printk ("icside: defaulting to ARCIN V5\n");
 		iftype = ics_if_arcin_v5;
 		break;
@@ -166,7 +183,9 @@
 	return iftype;
 }
 
-static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq)
+static int
+icside_register_port(unsigned long dataport, unsigned long ctrlport,
+		     int stepping, int irq, int dma)
 {
 	hw_regs_t hw;
 	int i;
@@ -179,6 +198,7 @@
 	}
 	hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport;
 	hw.irq = irq;
+	hw.dma = dma;
 
 	return ide_register_hw(&hw, NULL);
 }
@@ -189,7 +209,7 @@
  */
 static inline void icside_register (struct expansion_card *ec, int index)
 {
-	unsigned long port;
+	unsigned long port, slot_port;
 
 	result[index][0] = -1;
 	result[index][1] = -1;
@@ -201,44 +221,48 @@
 		break;
 
 	case ics_if_arcin_v5:
-		port = ecard_address (ec, ECARD_MEMC, 0);
-		ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
+		slot_port = ecard_address (ec, ECARD_MEMC, 0);
+		ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
 		ec->irqmask = 1;
-		ec->irq_data = (void *)port;
+		ec->irq_data = (void *)slot_port;
 		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
 
 		/*
 		 * Be on the safe side - disable interrupts
 		 */
-		inb (port + ICS_ARCIN_V5_INTROFFSET);
-		result[index][0] = icside_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
-							port + ICS_ARCIN_V5_IDEALTOFFSET,
+		inb (slot_port + ICS_ARCIN_V5_INTROFFSET);
+		result[index][0] = icside_register_port(slot_port + ICS_ARCIN_V5_IDEOFFSET,
+							slot_port + ICS_ARCIN_V5_IDEALTOFFSET,
 							ICS_ARCIN_V5_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		result[index][1] = -1;
 		break;
 
 	case ics_if_arcin_v6:
-		port = ecard_address (ec, ECARD_IOC, ECARD_FAST);
-		ec->irqaddr = ioaddr(port + ICS_ARCIN_V6_INTRSTAT_1);
-		ec->irqmask = 1;
+		slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+		port = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+		if (port == 0)
+			port = slot_port;
+		else
+			outb(1 << 5, slot_port);
+
 		ec->irq_data = (void *)port;
 		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
 
 		/*
 		 * Be on the safe side - disable interrupts
 		 */
-		inb (port + ICS_ARCIN_V6_INTROFFSET_1);
-		inb (port + ICS_ARCIN_V6_INTROFFSET_2);
+		inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+		inb(port + ICS_ARCIN_V6_INTROFFSET_2);
 
 		result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
 							port + ICS_ARCIN_V6_IDEALTOFFSET_1,
 							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
 							port + ICS_ARCIN_V6_IDEALTOFFSET_2,
 							ICS_ARCIN_V6_IDESTEPPING,
-							ec->irq);
+							ec->irq, ec->dma);
 		break;
 	}		
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/ide-rapide.c linux.18p17/drivers/acorn/block/ide-rapide.c
--- linux.vanilla/drivers/acorn/block/ide-rapide.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/ide-rapide.c	Fri Sep 15 23:31:12 2000
@@ -13,7 +13,6 @@
 #include <linux/blkdev.h>
 #include <linux/errno.h>
 #include <asm/ecard.h>
-#include <asm/ide.h>
 
 #include "../../block/ide.h"
 
@@ -28,14 +27,20 @@
 static inline int rapide_register(struct expansion_card *ec)
 {
 	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
-	ide_ioregspec_t spec;
+	hw_regs_t hw;
 
-	spec.base = port;
-	spec.ctrl = port + 0x206;
-	spec.offset = 1 << 4;
-	spec.irq = ec->irq;
+	int i;
 
-	return ide_register_port(&spec);
+	memset(&hw, 0, sizeof(hw));
+
+	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+		hw.io_ports[i] = (ide_ioreg_t)port;
+		port += 1 << 4;
+	}
+	hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
+	hw.irq = ec->irq;
+
+	return ide_register_hw(&hw, NULL);
 }
 
 int rapide_init(void)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/mfm.S linux.18p17/drivers/acorn/block/mfm.S
--- linux.vanilla/drivers/acorn/block/mfm.S	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/mfm.S	Fri Sep 15 23:31:12 2000
@@ -1,44 +1,43 @@
-@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400
-@   motherboard on ST506 podules.
-@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996
+@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes
+@   motherboard and on ST506 expansion podules.
+@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999
 
 #include <asm/assembler.h>
  
-_hdc63463_irqdata:
+hdc63463_irqdata:
 @ Controller base address
-  .global _hdc63463_baseaddress
-_hdc63463_baseaddress:
+  .global hdc63463_baseaddress
+hdc63463_baseaddress:
   .word 0
 
-  .global _hdc63463_irqpolladdress
-_hdc63463_irqpolladdress:
+  .global hdc63463_irqpolladdress
+hdc63463_irqpolladdress:
   .word 0
  
-  .global _hdc63463_irqpollmask
-_hdc63463_irqpollmask:
+  .global hdc63463_irqpollmask
+hdc63463_irqpollmask:
   .word 0
 
 @ where to read/write data  from the kernel data space
-  .global _hdc63463_dataptr
-_hdc63463_dataptr:
+  .global hdc63463_dataptr
+hdc63463_dataptr:
   .word 0
 
 @ Number of bytes left to transfer
-  .global _hdc63463_dataleft
-_hdc63463_dataleft:
+  .global hdc63463_dataleft
+hdc63463_dataleft:
   .word 0
 
 @ -------------------------------------------------------------------------
 @ hdc63463_writedma: DMA from host to controller
 @  internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
 @                      r3=data ptr, r4=data left, r5,r6=temporary
-  .global _hdc63463_writedma
-_hdc63463_writedma:
+  .global hdc63463_writedma
+hdc63463_writedma:
   stmfd sp!,{r4-r7}
-  adr r5,_hdc63463_irqdata
+  adr r5,hdc63463_irqdata
   ldmia r5,{r0,r1,r2,r3,r4}
 
-
 writedma_again:
 
   @ test number of remaining bytes to transfer
@@ -89,12 +88,12 @@
 
   @ If we were too slow we had better go through again - DAG - took out with new interrupt routine
   @ sub r0,r0,#32+8
-  @ adr r2,_hdc63463_irqdata
+  @ adr r2,hdc63463_irqdata
   @ ldr r2,[r2,#8]
   @ b writedma_again
 
 writedma_end:
-  adr r5,_hdc63463_irqdata+12
+  adr r5,hdc63463_irqdata+12
   stmia r5,{r3,r4}
   ldmfd sp!,{r4-r7}
   RETINSTR(mov,pc,lr)
@@ -103,10 +102,10 @@
 @ hdc63463_readdma: DMA from controller to host
 @  internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
 @                      r3=data ptr, r4=data left, r5,r6=temporary
-  .global _hdc63463_readdma
-_hdc63463_readdma:
+  .global hdc63463_readdma
+hdc63463_readdma:
   stmfd sp!,{r4-r7}
-  adr r5,_hdc63463_irqdata
+  adr r5,hdc63463_irqdata
   ldmia r5,{r0,r1,r2,r3,r4}
 
 readdma_again:
@@ -157,7 +156,7 @@
   @ b readdma_again
 
 readdma_end:
-  adr r5,_hdc63463_irqdata+12
+  adr r5,hdc63463_irqdata+12
   stmia r5,{r3,r4}
   ldmfd sp!,{r4-r7}
   RETINSTR(mov,pc,lr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/block/mfmhd.c linux.18p17/drivers/acorn/block/mfmhd.c
--- linux.vanilla/drivers/acorn/block/mfmhd.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/block/mfmhd.c	Fri Sep 15 23:31:12 2000
@@ -123,6 +123,7 @@
 #include <asm/dma.h>
 #include <asm/hardware.h>
 #include <asm/ecard.h>
+#include <asm/ioc.h>
 
 /*
  * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
@@ -261,7 +262,9 @@
 	void (*done) (int st);	/* done handler */
 } *cont = NULL;
 
+#if 0
 static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
+#endif
 
 int number_mfm_drives = 1;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/Config.in linux.18p17/drivers/acorn/char/Config.in
--- linux.vanilla/drivers/acorn/char/Config.in	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/char/Config.in	Thu Jan  1 01:00:00 1970
@@ -1,15 +0,0 @@
-if [ "$CONFIG_SERIAL" != "n" ]; then
-  tristate '   Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
-  tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL
-fi
-
-if [ "$CONFIG_MOUSE" = "y" ]; then
-  if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-    if [ "$CONFIG_ARCH_RPC" != "y" ]; then
-      define_bool CONFIG_KBDMOUSE y
-    else
-      define_bool CONFIG_RPCMOUSE y
-    fi
-  fi
-fi
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/Makefile linux.18p17/drivers/acorn/char/Makefile
--- linux.vanilla/drivers/acorn/char/Makefile	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/char/Makefile	Fri Sep 15 23:31:12 2000
@@ -9,20 +9,19 @@
 # parent makes..
 #
 
-L_TARGET := acorn-char.a
-M_OBJS   :=
-L_OBJS   :=
+L_TARGET 	:= acorn-char.a
+M_OBJS   	:=
+L_OBJS   	:=
 
-ifeq ($(MACHINE),rpc)
-  MOUSE_OBJS += mouse_rpc.o
-  L_OBJS += keyb_ps2.o
-endif
+L_OBJS_arc	:= keyb_arc.o
+L_OBJS_a5k	:= keyb_arc.o
+L_OBJS_rpc	:= keyb_ps2.o
 
-ifeq ($(CONFIG_MOUSE),y)
-  LX_OBJS += $(MOUSE_OBJS)
+ifeq ($(CONFIG_RPCMOUSE),y)
+  LX_OBJS += mouse_rpc.o
 else
-  ifeq ($(CONFIG_MOUSE),m)
-    MX_OBJS += $(MOUSE_OBJS)
+  ifeq ($(CONFIG_RPCMOUSE),m)
+    MX_OBJS += mouse_rpc.o
   endif
 endif
 
@@ -41,5 +40,7 @@
     M_OBJS += serial-dualsp.o
   endif
 endif
+
+L_OBJS += $(L_OBJS_$(MACHINE))
 
 include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/keyb_arc.c linux.18p17/drivers/acorn/char/keyb_arc.c
--- linux.vanilla/drivers/acorn/char/keyb_arc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/acorn/char/keyb_arc.c	Fri Sep 15 23:31:12 2000
@@ -0,0 +1,451 @@
+/*
+ * linux/arch/arm/drivers/char1/keyb_arc.c
+ *
+ * Acorn keyboard driver for ARM Linux.
+ *
+ * The Acorn keyboard appears to have a ***very*** buggy reset protocol -
+ * every reset behaves differently.  We try to get round this by attempting
+ * a few things...
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/tty.h>
+#include <linux/kbd_kern.h>
+#include <linux/delay.h>
+
+#include <asm/bitops.h>
+#include <asm/keyboard.h>
+#include <asm/irq.h>
+#include <asm/ioc.h>
+#include <asm/hardware.h>
+
+#include "../../char/mouse.h"
+
+extern void kbd_reset_kdown(void);
+
+#define VERSION 108
+
+#define KBD_REPORT_ERR
+#define KBD_REPORT_UNKN
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+static char kbd_txval[4];
+static unsigned char kbd_txhead, kbd_txtail;
+#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3)
+static int kbd_id = -1;
+static struct wait_queue *kbd_waitq;
+#ifdef CONFIG_KBDMOUSE
+static int mousedev;
+#endif
+
+/*
+ * Protocol codes to send the keyboard.
+ */
+#define HRST 0xff	/* reset keyboard */
+#define RAK1 0xfe	/* reset response */
+#define RAK2 0xfd	/* reset response */
+#define BACK 0x3f	/* Ack for first keyboard pair */
+#define SMAK 0x33	/* Last data byte ack (key scanning + mouse movement scanning) */
+#define MACK 0x32	/* Last data byte ack (mouse movement scanning) */
+#define SACK 0x31	/* Last data byte ack (key scanning) */
+#define NACK 0x30	/* Last data byte ack (no scanning, mouse data) */
+#define RQMP 0x22	/* Request mouse data */
+#define PRST 0x21	/* nothing */
+#define RQID 0x20	/* Request ID */
+
+#define UP_FLAG 1
+
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char a5kkbd_sysrq_xlate[] = 
+{
+    27,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+   '`',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+   '8',  '9',  '0',  '-',  '=',  'Ł',  127,    0,
+     0,    0,    0,  '/',  '*',  '#',    9,  'q',
+   'w',  'e',  'r',  't',  'y',  'u',  'i',  'o',
+   'p',  '[',  ']', '\\',  22,    23,   25,  '7',
+   '8',  '9',  '-',    0,  'a',  's',  'd',  'f',
+   'g',  'h',  'j',  'k',  'l',  ';', '\'',   13,
+   '4',  '5',  '6',  '+',    0,    0,  'z',  'x',
+   'c',  'v',  'b',  'n',  'm',  ',',  '.',  '/',
+     0,    0,  '1',  '2',  '3',    0,    0,  ' ',
+     0,    0,    0,    0,    0,  '0',  '.',   10,
+     0,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+     0,    0,    0,    0,    0,    0,    0,    0,
+};
+#endif
+
+/*
+ * This array converts the scancode that we get from the keyboard to the
+ * real rows/columns on the A5000 keyboard.  This might be keyboard specific...
+ *
+ * It is these values that we use to maintain the key down array.  That way, we
+ * should pick up on the ghost key presses (which is what happens when you press
+ * three keys, and the keyboard thinks you have pressed four!)
+ *
+ * Row 8 (0x80+c) is actually a column with one key per row.  It is isolated from
+ * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
+ *
+ * Illegal scancodes are denoted by an 0xff (in other words, we don't know about
+ * them, and can't process them for ghosts).  This does however, cause problems with
+ * autorepeat processing...
+ */
+static unsigned char scancode_2_colrow[256] = {
+  0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
+  0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
+  0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
+  0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
+  0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
+  0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
+  0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
+};
+
+#define BITS_PER_SHORT (8*sizeof(unsigned short))
+static unsigned short ghost_down[128/BITS_PER_SHORT];
+
+static void a5kkbd_key(unsigned int keycode, unsigned int up_flag)
+{
+	unsigned int real_keycode;
+
+	if (keycode > 0x72) {
+#ifdef KBD_REPORT_UNKN
+		printk ("kbd: unknown scancode 0x%04x\n", keycode);
+#endif
+		return;
+	}
+	if (keycode >= 0x70) {
+#ifdef CONFIG_KBDMOUSE
+		if (mousedev >= 0)
+			switch (keycode) {
+			case 0x70: /* Left mouse button */
+				busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0);
+				break;
+
+			case 0x71: /* Middle mouse button */
+				busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0);
+				break;
+
+			case 0x72:/* Right mouse button */
+				busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0);
+				break;
+			}
+#endif
+		return;
+	}
+
+	/*
+	 * We have to work out if we accept this key press as a real key, or
+	 * if it is a ghost.  IE. If you press three keys, the keyboard will think
+	 * that you've pressed a fourth: (@ = key down, # = ghost)
+	 *
+	 *   0 1 2 3 4 5 6 7
+	 *   | | | | | | | |
+	 * 0-+-+-+-+-+-+-+-+-
+	 *   | | | | | | | |
+	 * 1-+-@-+-+-+-@-+-+-
+	 *   | | | | | | | |
+	 * 2-+-+-+-+-+-+-+-+-
+	 *   | | | | | | | |
+	 * 3-+-@-+-+-+-#-+-+-
+	 *   | | | | | | | |
+	 *
+	 * This is what happens when you have a matrix keyboard...
+	 */
+
+	real_keycode = scancode_2_colrow[keycode];
+
+	if ((real_keycode & 0x80) == 0) {
+		int rr, kc = (real_keycode >> 4) & 7;
+		int cc;
+		unsigned short res, kdownkc;
+
+		kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
+
+		for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
+			if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
+			    	/*
+				 * we have found a second row with at least one key pressed in the
+			    	 * same column.
+			    	 */
+			    	for (cc = 0; res; res >>= 1)
+					cc += (res & 1);
+				if (cc > 1)
+					return; /* ignore it */
+			}
+		if (up_flag)
+			clear_bit (real_keycode, ghost_down);
+		else
+			set_bit (real_keycode, ghost_down);
+	}
+
+	handle_scancode(keycode, !up_flag);
+}
+
+static inline void a5kkbd_sendbyte(unsigned char val)
+{
+	kbd_txval[kbd_txhead] = val;
+	KBD_INCTXPTR(kbd_txhead);
+	enable_irq(IRQ_KEYBOARDTX);
+}
+
+static inline void a5kkbd_reset(void)
+{
+	int i;
+
+	for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++)
+		ghost_down[i] = 0;
+
+	kbd_reset_kdown();
+}
+
+void a5kkbd_leds(unsigned char leds)
+{
+	leds =  ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
+		((leds & (1<<VC_CAPSLOCK))?1:0);
+	a5kkbd_sendbyte(leds);
+}
+
+/* Keyboard states:
+ *  0 initial reset condition, receive HRST, send RRAK1
+ *  1 Sent RAK1, wait for RAK1, send RRAK2
+ *  2 Sent RAK2, wait for RAK2, send SMAK or RQID
+ *  3 Sent RQID, expect KBID, send SMAK
+ *  4 Sent SMAK, wait for anything
+ *  5 Wait for second keyboard nibble for key pressed
+ *  6 Wait for second keyboard nibble for key released
+ *  7 Wait for second part of mouse data
+ *
+ * This function returns 1 when we successfully enter the IDLE state
+ * (and hence need to do some keyboard processing).
+ */
+#define KBD_INITRST	0
+#define KBD_RAK1	1
+#define KBD_RAK2	2
+#define KBD_ID		3
+#define KBD_IDLE	4
+#define KBD_KEYDOWN	5
+#define KBD_KEYUP	6
+#define KBD_MOUSE	7
+
+static int handle_rawcode(unsigned int keyval)
+{
+	static signed char kbd_mousedx = 0;
+	       signed char kbd_mousedy;
+	static unsigned char kbd_state = KBD_INITRST;
+	static unsigned char kbd_keyhigh = 0;
+
+	if (keyval == HRST && kbd_state != KBD_INITRST && kbd_state != KBD_ID) {
+		a5kkbd_sendbyte (HRST);
+		a5kkbd_reset ();
+		kbd_state = KBD_INITRST;
+	} else switch(kbd_state) {
+	case KBD_INITRST:			/* hard reset - sent HRST */
+		if (keyval == HRST) {
+			a5kkbd_sendbyte (RAK1);
+			kbd_state = KBD_RAK1;
+		} else if (keyval == RAK1) {
+			/* Some A5000 keyboards are very fussy and don't follow Acorn's
+			 * specs - this appears to fix them, but them it might stop
+			 * them from being initialised.
+			 *  fix by Philip Blundell
+			 */
+			printk(KERN_DEBUG "keyboard sent early RAK1 -- ignored\n");
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_RAK1:				/* sent RAK1 - expect RAK1 and send RAK2 */
+		if (keyval == RAK1) {
+			a5kkbd_sendbyte (RAK2);
+			kbd_state = KBD_RAK2;
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_RAK2:				/* Sent RAK2 - expect RAK2 and send either RQID or SMAK */
+		if (keyval == RAK2) {
+			if (kbd_id == -1) {
+				a5kkbd_sendbyte (NACK);
+				a5kkbd_sendbyte (RQID);
+				kbd_state = KBD_ID;
+			} else {
+				a5kkbd_sendbyte (SMAK);
+				kbd_state = KBD_IDLE;
+			}
+		} else
+			goto kbd_wontreset;
+		break;
+
+	case KBD_ID:				/* Sent RQID - expect KBID */
+		if (keyval == HRST) {
+			kbd_id = -2;
+			a5kkbd_reset ();
+			a5kkbd_sendbyte (HRST);
+			kbd_state = KBD_INITRST;
+			wake_up (&kbd_waitq);
+		} else if ((keyval & 0xc0) == 0x80) {
+			kbd_id = keyval & 0x3f;
+			a5kkbd_sendbyte (SMAK);
+			kbd_state = KBD_IDLE;
+			wake_up (&kbd_waitq);
+		}
+		break;
+
+	case KBD_IDLE:				/* Send SMAK, ready for any reply */
+		switch (keyval & 0xf0) {
+		default:	/* 0x00 - 0x7f */
+			kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
+			kbd_state   = KBD_MOUSE;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0x80:
+		case 0x90:
+		case 0xa0:
+		case 0xb0:
+		    	if (kbd_id == -1)
+				kbd_id = keyval & 0x3f;
+			break;
+
+		case 0xc0:
+			kbd_keyhigh = keyval;
+			kbd_state   = KBD_KEYDOWN;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0xd0:
+			kbd_keyhigh = keyval;
+			kbd_state   = KBD_KEYUP;
+			a5kkbd_sendbyte (BACK);
+			break;
+
+		case 0xe0:
+		case 0xf0:
+			goto kbd_error;
+		}
+		break;
+
+	case KBD_KEYDOWN:
+		if ((keyval & 0xf0) != 0xc0)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+				a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), 0);
+		}
+		break;
+
+	case KBD_KEYUP:
+		if ((keyval & 0xf0) != 0xd0)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+				a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), UP_FLAG);
+		}
+		break;
+
+	case KBD_MOUSE:
+		if (keyval & 0x80)
+			goto kbd_error;
+		else {
+			kbd_state = KBD_IDLE;
+			a5kkbd_sendbyte (SMAK);
+			kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
+#ifdef CONFIG_KBDMOUSE
+			if (mousedev >= 0)
+				busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy);
+#endif
+		}
+	}
+	return kbd_state == KBD_IDLE ? 1 : 0;
+
+kbd_wontreset:
+#ifdef KBD_REPORT_ERR
+	printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n",
+		kbd_state, keyval);
+#endif
+	mdelay(1);
+	inb(IOC_KARTRX);
+	a5kkbd_sendbyte (HRST);
+	kbd_state = KBD_INITRST;
+	return 0;
+
+kbd_error:
+#ifdef KBD_REPORT_ERR
+	printk ("kbd: keyboard out of sync - resetting\n");
+#endif
+	a5kkbd_sendbyte (HRST);
+	kbd_state = KBD_INITRST;
+	return 0;
+}
+
+static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	kbd_pt_regs = regs;
+	if (handle_rawcode(inb(IOC_KARTRX)))
+		mark_bh (KEYBOARD_BH);
+}
+
+static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	outb (kbd_txval[kbd_txtail], IOC_KARTTX);
+	KBD_INCTXPTR(kbd_txtail);
+	if (kbd_txtail == kbd_txhead)
+		disable_irq(irq);
+}
+
+#ifdef CONFIG_KBDMOUSE
+static struct busmouse a5kkbd_mouse = {
+	6, "kbdmouse", NULL, NULL, 7
+};
+#endif
+
+__initfunc(void a5kkbd_init_hw (void))
+{
+	unsigned long flags;
+
+	save_flags_cli (flags);
+	if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0)
+		panic("Could not allocate keyboard transmit IRQ!");
+	disable_irq (IRQ_KEYBOARDTX);
+	if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0)
+		panic("Could not allocate keyboard receive IRQ!");
+	(void)inb(IOC_KARTRX);
+	restore_flags (flags);
+
+	a5kkbd_sendbyte (HRST);	/* send HRST (expect HRST) */
+
+	/* wait 1s for keyboard to initialise */
+	interruptible_sleep_on_timeout(&kbd_waitq, HZ);
+
+#ifdef CONFIG_KBDMOUSE
+	mousedev = register_busmouse(&a5kkbd_mouse);
+	if (mousedev < 0)
+		printk(KERN_ERR "Unable to register mouse driver\n");
+#endif
+
+	printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100);
+	if (kbd_id != -1)
+	      printk ("id=%d ", kbd_id);
+	printk ("English)\n");
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/keyb_ps2.c linux.18p17/drivers/acorn/char/keyb_ps2.c
--- linux.vanilla/drivers/acorn/char/keyb_ps2.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/char/keyb_ps2.c	Fri Sep 15 23:31:12 2000
@@ -25,6 +25,7 @@
 #include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 #include <asm/system.h>
 
 extern void kbd_reset_kdown(void);
@@ -221,14 +222,7 @@
 };
 #endif
 
-int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p)
-{
-	*uf_p = scancode & 0200;
-	*keycode_p = scancode & 0x7f;
-	return 1;
-}
-
-static void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
+static inline void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
 {
 	handle_scancode(keycode, !up_flag);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/mouse_rpc.c linux.18p17/drivers/acorn/char/mouse_rpc.c
--- linux.vanilla/drivers/acorn/char/mouse_rpc.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/char/mouse_rpc.c	Fri Sep 15 23:31:12 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/char/rpcmouse.c
+ * linux/drivers/char/mouse_rpc.c
  *
  * Copyright (C) 1996-1998 Russell King
  *
@@ -16,6 +16,7 @@
 #include <asm/hardware.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/iomd.h>
 
 #include "../../char/mouse.h"
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/char/serial-card.c linux.18p17/drivers/acorn/char/serial-card.c
--- linux.vanilla/drivers/acorn/char/serial-card.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/char/serial-card.c	Fri Sep 15 23:31:12 2000
@@ -33,14 +33,20 @@
 #ifdef MODULE
 static int __serial_ports[NUM_SERIALS];
 static int __serial_pcount;
+static int __serial_addr[NUM_SERIALS];
 static struct expansion_card *expcard[MAX_ECARDS];
 #define ADD_ECARD(ec,card) expcard[(card)] = (ec)
-#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port)
+#define ADD_PORT(port,addr)					\
+	do {							\
+		__serial_ports[__serial_pcount] = (port);	\
+		__serial_addr[__serial_pcount] = (addr);	\
+		__serial_pcount += 1;				\
+	} while (0)
 #undef MY_INIT
 #define MY_INIT init_module
 #else
 #define ADD_ECARD(ec,card)
-#define ADD_PORT(port)
+#define ADD_PORT(port,addr)
 #endif
 
 static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } };
@@ -75,12 +81,15 @@
 	cardaddr = MY_BASE_ADDRESS(ec);
 
 	for (port = 0; port < MY_NUMPORTS; port ++) {
+	    unsigned long address;
 	    int line;
 
-	    line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq);
+	    address = MY_PORT_ADDRESS(port, cardaddr);
+
+	    line = serial_register_onedev (address, ec->irq);
 	    if (line < 0)
 		break;
-	    ADD_PORT(line);
+	    ADD_PORT(line, address);
 	}
 
 	if (port) {
@@ -97,8 +106,10 @@
 {
     int i;
 
-    for (i = 0; i < __serial_pcount; i++)
-	unregister_serial (__serial_ports[i]);
+    for (i = 0; i < __serial_pcount; i++) {
+	unregister_serial(__serial_ports[i]);
+	release_region(__serial_addr[i], 8);
+    }
 
     for (i = 0; i < MAX_ECARDS; i++)
 	if (expcard[i])
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/net/ether1.c linux.18p17/drivers/acorn/net/ether1.c
--- linux.vanilla/drivers/acorn/net/ether1.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/net/ether1.c	Fri Sep 15 23:31:12 2000
@@ -71,7 +71,7 @@
 #define BUS_16 16
 #define BUS_8  8
 
-static const card_ids ether1_cids[] = {
+static const card_ids __init ether1_cids[] = {
 	{ MANU_ACORN, PROD_ACORN_ETHER1 },
 	{ 0xffff, 0xffff }
 };
@@ -128,7 +128,7 @@
 {
 	int used;
 
-	addr = IO_BASE + (addr << 2);
+	addr = ioaddr(addr);
 
 	__asm__ __volatile__(
 		"subs	%3, %3, #2
@@ -171,7 +171,7 @@
 {
 	int used;
 
-	addr = IO_BASE + (addr << 2);
+	addr = ioaddr(addr);
 
 	__asm__ __volatile__(
 		"subs	%3, %3, #2
@@ -659,12 +659,6 @@
 	/* Fill in the fields of the device structure with ethernet values */
 	ether_setup (dev);
 
-#ifndef CLAIM_IRQ_AT_OPEN
-	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) {
-		kfree (dev->priv);
-		return -EAGAIN;
-	}
-#endif
 	return 0;
 }	
     
@@ -751,6 +745,8 @@
 
 	if (ether1_init_for_open (dev))
 		printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
+	else
+		priv->restart = 0;
 
 	dev->start = 1;
 }
@@ -759,18 +755,16 @@
 ether1_open (struct device *dev)
 {
 	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
-#ifdef CLAIM_IRQ_AT_OPEN
+
 	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev))
 		return -EAGAIN;
-#endif
+
 	MOD_INC_USE_COUNT;
 
 	memset (&priv->stats, 0, sizeof (struct enet_statistics));
 
 	if (ether1_init_for_open (dev)) {
-#ifdef CLAIM_IRQ_AT_OPEN
 		free_irq (dev->irq, dev);
-#endif
 		MOD_DEC_USE_COUNT;
 		return -EAGAIN;
 	}
@@ -1080,12 +1074,10 @@
 static int
 ether1_close (struct device *dev)
 {
-#ifdef CLAIM_IRQ_AT_OPEN
-	free_irq (dev->irq, dev);
-#endif
-
 	ether1_reset (dev);
 
+	free_irq(dev->irq, dev);
+
 	dev->start = 0;
 	dev->tbusy = 0;
 
@@ -1117,56 +1109,46 @@
 
 #ifdef MODULE
 
-static char ethernames[MAX_ECARDS][9];
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+	struct expansion_card	*ec;
+	char			name[9];
+	struct device		dev;
+} ether_devs[MAX_ECARDS];
 
 int
 init_module (void)
 {
-	int i;
+	struct expansion_card *ec;
+	int i, ret = -ENODEV;
 
-	for (i = 0; i < MAX_ECARDS; i++) {
-		my_ethers[i] = NULL;
-		ec[i] = NULL;
-		strcpy (ethernames[i], "        ");
-	}
+	memset(ether_devs, 0, sizeof(ether_devs));
 
+	ecard_startfind ();
+	ec = ecard_find(0, ether1_cids);
 	i = 0;
 
-	ecard_startfind ();
+	while (ec && i < MAX_ECARDS) {
+		ecard_claim(ec);
 
-	do {
-		if ((ec[i] = ecard_find(0, ether1_cids)) == NULL)
+		ether_devs[i].ec	    = ec;
+		ether_devs[i].dev.irq	    = ec->irq;
+		ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+		ether_devs[i].dev.init	    = ether1_probe;
+		ether_devs[i].dev.name	    = ether_devs[i].name;
+
+		ret = register_netdev(&ether_devs[i].dev);
+
+		if (ret) {
+			ecard_release(ec);
+			ether_devs[i].ec = NULL;
 			break;
-
-		my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
-		memset (my_ethers[i], 0, sizeof (struct device));
-
-		my_ethers[i]->irq = ec[i]->irq;
-		my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST);
-		my_ethers[i]->init = ether1_probe;
-		my_ethers[i]->name = ethernames[i];
-
-		ecard_claim (ec[i]);
-
-		if (register_netdev (my_ethers[i]) != 0) {
-			for (i = 0; i < 4; i++) {
-				if (my_ethers[i]) {
-					kfree (my_ethers[i]);
-					my_ethers[i] = NULL;
-				}
-				if (ec[i]) {
-					ecard_release (ec[i]);
-					ec[i] = NULL;
-				}
-			}
-			return -EIO;
 		}
-		i++;
-	} while (i < MAX_ECARDS);
 
-	return i != 0 ? 0 : -ENODEV;
+		i += 1;
+		ec = ecard_find(0, ether1_cids);
+	}
+
+	return i != 0 ? 0 : ret;
 }
 
 void
@@ -1175,18 +1157,15 @@
 	int i;
 
 	for (i = 0; i < MAX_ECARDS; i++) {
-		if (my_ethers[i]) {
-			unregister_netdev (my_ethers[i]);
-			release_region (my_ethers[i]->base_addr, 16);
-			release_region (my_ethers[i]->base_addr + 0x800, 4096);
-#ifndef CLAIM_IRQ_AT_OPEN
-			free_irq (my_ethers[i]->irq, my_ethers[i]);
-#endif
-			my_ethers[i] = NULL;
-		}
-		if (ec[i]) {
-			ecard_release (ec[i]);
-			ec[i] = NULL;
+		if (ether_devs[i].ec) {
+			unregister_netdev(&ether_devs[i].dev);
+
+			release_region(ether_devs[i].dev.base_addr, 16);
+			release_region(ether_devs[i].dev.base_addr + 0x800, 4096);
+
+			ecard_release(ether_devs[i].ec);
+
+			ether_devs[i].ec = NULL;
 		}
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/net/ether3.c linux.18p17/drivers/acorn/net/ether3.c
--- linux.vanilla/drivers/acorn/net/ether3.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/net/ether3.c	Fri Sep 15 23:31:12 2000
@@ -33,11 +33,12 @@
  *				packet starts two bytes from the end of the
  *				buffer, it corrupts the receiver chain, and
  *				never updates the transmit status correctly.
- * TODO:
- *  When we detect a fatal error on the interface, we should restart it.
+ * 1.14	RMK	07/01/1998	Added initial code for ETHERB addressing.
+ * 1.15	RMK	30/04/1999	More fixes to the transmit routine for buggy
+ *				hardware.
  */
 
-static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n";
+static char *version = "ether3 ethernet driver (c) 1995-1999 R.M.King v1.15\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -66,7 +67,7 @@
 #include "ether3.h"
 
 static unsigned int net_debug = NET_DEBUG;
-static const card_ids ether3_cids[] = {
+static const card_ids __init ether3_cids[] = {
 	{ MANU_ANT2, PROD_ANT_ETHER3 },
 	{ MANU_ANT,  PROD_ANT_ETHER3 },
 	{ MANU_ANT,  PROD_ANT_ETHERB }, /* trial - will etherb work? */
@@ -77,9 +78,6 @@
 static int  ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt);
 static void ether3_tx(struct device *dev, struct dev_priv *priv);
 
-extern int inswb(int reg, void *buffer, int len);
-extern int outswb(int reg, void *buffer, int len);
-
 #define BUS_16		2
 #define BUS_8		1
 #define BUS_UNKNOWN	0
@@ -88,7 +86,7 @@
  * I'm not sure what address we should default to if the internal one
  * is corrupted...
  */
-unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+unsigned char def_eth_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 
 /* --------------------------------------------------------------------------- */
 
@@ -99,10 +97,20 @@
 
 /*
  * ether3 read/write.  Slow things down a bit...
+ * The SEEQ8005 doesn't like us writing to it's registers
+ * too quickly.
  */
-#define ether3_outb(v,r)	{ outb((v),(r)); udelay(1); }
-#define ether3_outw(v,r)	{ outw((v),(r)); udelay(1); }
+static inline void ether3_outb(int v, const int r)
+{
+	outb(v, r);
+	udelay(1);
+}
 
+static inline void ether3_outw(int v, const int r)
+{
+	outw(v, r);
+	udelay(1);
+}
 #define ether3_inb(r)		({ unsigned int __v = inb((r)); udelay(1); __v; })
 #define ether3_inw(r)		({ unsigned int __v = inw((r)); udelay(1); __v; })
 
@@ -138,7 +146,7 @@
  * write data to the buffer memory
  */
 #define ether3_writebuffer(dev,data,length)			\
-	outswb(REG_BUFWIN, (data), (length))
+	outsw(REG_BUFWIN, (data), (length) >> 1)
 
 #define ether3_writeword(dev,data)				\
 	outw((data), REG_BUFWIN)
@@ -153,7 +161,7 @@
  * read data from the buffer memory
  */
 #define ether3_readbuffer(dev,data,length)			\
-	inswb(REG_BUFWIN, (data), (length))
+	insw(REG_BUFWIN, (data), (length) >> 1)
 
 #define ether3_readword(dev)					\
 	inw(REG_BUFWIN)
@@ -249,7 +257,7 @@
 			}
 		} else {
 			if (bad != -1) {
-			    	if (bad != i - 1)
+				if (bad != i - 1)
 					printk(" - 0x%04X\n", i - 1);
 				printk("\n");
 				bad = -1;
@@ -335,7 +343,6 @@
 	for (i = 0; i < 6; i++)
 		ether3_outb(dev->dev_addr[i], REG_BUFWIN);
 
-	priv->tx_used	= 0;
 	priv->tx_head	= 0;
 	priv->tx_tail	= 0;
 	priv->regs.config2 |= CFG2_CTRLO;
@@ -471,6 +478,25 @@
 	return error;
 }
 
+__initfunc(static void
+ether3_get_dev(struct device *dev, struct expansion_card *ec))
+{
+	ecard_claim(ec);
+
+	dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
+	dev->irq = ec->irq;
+
+	if (ec->cid.manufacturer == MANU_ANT &&
+	    ec->cid.product == PROD_ANT_ETHERB) {
+		dev->base_addr += 0x200;
+	}
+
+	ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr);
+	ec->irqmask = 0xf0;
+
+	ether3_addr(dev->dev_addr, ec);
+}
+
 #ifndef MODULE
 __initfunc(int
 ether3_probe(struct device *dev))
@@ -485,12 +511,8 @@
 	if ((ec = ecard_find(0, ether3_cids)) == NULL)
 		return ENODEV;
 
-	dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
-	dev->irq = ec->irq;
-
-	ecard_claim(ec);
+	ether3_get_dev(dev, ec);
 
-	ether3_addr(dev->dev_addr, ec);
 	return ether3_probe1(dev);
 }
 #endif
@@ -581,33 +603,6 @@
 }
 
 /*
- * Allocate memory in transmitter ring buffer.
- */
-static int
-ether3_alloc_tx(struct dev_priv *priv, int length, int alloc)
-{
-	int start, head, tail;
-
-	tail = priv->tx_tail;
-	start = priv->tx_head;
-	head = start + length + 4;
-
-	if (head >= TX_END) {
-		if (tail > priv->tx_head)
-			return -1;
-		head -= TX_END - TX_START;
-		if (tail < head)
-			return -1;
-	} else if (start < tail && tail < head)
-		return -1;
-
-	if (alloc)
-		priv->tx_head = head;
-
-	return start;
-}
-
-/*
  * Transmit a packet
  */
 static int
@@ -622,7 +617,7 @@
 		if (!test_and_set_bit(0, (void *)&dev->tbusy)) {
 			unsigned long flags;
 			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-			int ptr;
+			unsigned int ptr, next_ptr;
 
 			length = (length + 1) & ~1;
 
@@ -633,23 +628,31 @@
 				return 0;
 			}
 
+			next_ptr = (priv->tx_head + 1) & 15;
+
 			save_flags_cli(flags);
 
-			ptr = ether3_alloc_tx(priv, length, 1);
-			if (ptr == -1)
+			if (priv->tx_tail == next_ptr) {
+				restore_flags(flags);
 				return 1;	/* unable to queue */
+			}
+
+			dev->trans_start = jiffies;
+			ptr		 = 0x600 * priv->tx_head;
+			priv->tx_head	 = next_ptr;
+			next_ptr	*= 0x600;
 
 #define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
 
-			ether3_setbuffer(dev, buffer_write, priv->tx_head);
+			ether3_setbuffer(dev, buffer_write, next_ptr);
 			ether3_writelong(dev, 0);
-
 			ether3_setbuffer(dev, buffer_write, ptr);
 			ether3_writelong(dev, 0);
 			ether3_writebuffer(dev, skb->data, length);
-
+			ether3_writeword(dev, htons(next_ptr));
+			ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16);
 			ether3_setbuffer(dev, buffer_write, ptr);
-			ether3_writeword(dev, htons(priv->tx_head));
+			ether3_writeword(dev, htons((ptr + length + 4)));
 			ether3_writeword(dev, TXHDR_FLAGS >> 16);
 			ether3_ledon(dev, priv);
 
@@ -658,11 +661,10 @@
 				ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND);
 			}
 
-			if (ether3_alloc_tx(priv, 2044, 0) != -1)
+			next_ptr = (priv->tx_head + 1) & 15;
+			if (priv->tx_tail != next_ptr)
 				dev->tbusy = 0;
 
-			dev->trans_start = jiffies;
-
 			restore_flags(flags);
 
 			dev_kfree_skb(skb);
@@ -689,7 +691,7 @@
 			ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
 		printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
 			ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
-		printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name,
+		printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name,
 			priv->tx_head, priv->tx_tail);
 		ether3_setbuffer(dev, buffer_read, priv->tx_tail);
 		printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev));
@@ -698,8 +700,9 @@
 		dev->tbusy = 0;
 		priv->regs.config2 |= CFG2_CTRLO;
 		priv->stats.tx_errors += 1;
-		ether3_outw(priv->regs.config2 , REG_CONFIG2);
+		ether3_outw(priv->regs.config2, REG_CONFIG2);
 		dev->trans_start = jiffies;
+		priv->tx_head = priv->tx_tail = 0;
 		goto retry;
 	}
 }
@@ -867,6 +870,7 @@
 ether3_tx(struct device *dev, struct dev_priv *priv)
 {
 	unsigned int tx_tail = priv->tx_tail;
+	int max_work = 14;
 
 	do {
 	    	unsigned long status;
@@ -874,7 +878,7 @@
     		/*
 	    	 * Read the packet header
     		 */
-	    	ether3_setbuffer(dev, buffer_read, tx_tail);
+	    	ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
     		status = ether3_readlong(dev);
 
 		/*
@@ -895,95 +899,72 @@
 			if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
 		}
 
-		tx_tail = htons(status & TX_NEXT);
-		if (tx_tail < TX_START || tx_tail >= TX_END) {
-			printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail);
-			tx_tail = TX_START;
-			priv->tx_head = TX_START;
-			priv->tx_tail = TX_END;
-		}
-	} while (1);
+		tx_tail = (tx_tail + 1) & 15;
+	} while (--max_work);
 
 	if (priv->tx_tail != tx_tail) {
 		priv->tx_tail = tx_tail;
-		if (priv->tx_used <= MAX_TX_BUFFERED) {
-			dev->tbusy = 0;
-			mark_bh(NET_BH);	/* Inform upper layers. */
-		}
+		dev->tbusy = 0;
+		mark_bh(NET_BH);	/* Inform upper layers. */
 	}
 }
 
 #ifdef MODULE
 
-char ethernames[MAX_ECARDS][9];
-
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+	struct expansion_card	*ec;
+	char			name[9];
+	struct device		dev;
+} ether_devs[MAX_ECARDS];
 
 int
 init_module(void)
 {
-	int i;
+	struct expansion_card *ec;
+	int i, ret = -ENODEV;
 
-	for(i = 0; i < MAX_ECARDS; i++) {
-		my_ethers[i] = NULL;
-		ec[i] = NULL;
-		strcpy(ethernames[i], "        ");
-	}
+	memset(ether_devs, 0, sizeof(ether_devs));
 
+	ecard_startfind ();
+	ec = ecard_find(0, ether3_cids);
 	i = 0;
 
-	ecard_startfind();
+	while (ec && i < MAX_ECARDS) {
+		ecard_claim(ec);
 
-	do {
-		if ((ec[i] = ecard_find(0, ether3_cids)) == NULL)
-			break;
-
-		my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
-		memset(my_ethers[i], 0, sizeof(struct device));
+		ether_devs[i].ec	    = ec;
+		ether_devs[i].dev.init	    = ether3_probe1;
+		ether_devs[i].dev.name	    = ether_devs[i].name;
+		ether3_get_dev(&ether_devs[i].dev, ec);
+
+		ret = register_netdev(&ether_devs[i].dev);
+
+		if (ret) {
+			ecard_release(ec);
+			ether_devs[i].ec = NULL;
+		} else
+			i += 1;
 
-		my_ethers[i]->irq = ec[i]->irq;
-		my_ethers[i]->base_addr= ecard_address(ec[i], ECARD_MEMC, 0);
-		my_ethers[i]->init = ether3_probe1;
-		my_ethers[i]->name = ethernames[i];
-
-		ether3_addr(my_ethers[i]->dev_addr, ec[i]);
-
-		ecard_claim(ec[i]);
-
-		if(register_netdev(my_ethers[i]) != 0) {
-			for (i = 0; i < 4; i++) {
-				if(my_ethers[i]) {
-					kfree(my_ethers[i]);
-					my_ethers[i] = NULL;
-				}
-				if(ec[i]) {
-					ecard_release(ec[i]);
-					ec[i] = NULL;
-				}
-			}
-			return -EIO;
-		}
-		i++;
+		ec = ecard_find(0, ether3_cids);
 	}
-	while(i < MAX_ECARDS);
 
-	return i != 0 ? 0 : -ENODEV;
+	return i != 0 ? 0 : ret;
 }
 
 void
 cleanup_module(void)
 {
 	int i;
+
 	for (i = 0; i < MAX_ECARDS; i++) {
-		if (my_ethers[i]) {
-		  	release_region(my_ethers[i]->base_addr, 128);
-			unregister_netdev(my_ethers[i]);
-			my_ethers[i] = NULL;
-		}
-		if (ec[i]) {
-			ecard_release(ec[i]);
-			ec[i] = NULL;
+		if (ether_devs[i].ec) {
+			unregister_netdev(&ether_devs[i].dev);
+
+			release_region(ether_devs[i].dev.base_addr, 128);
+
+			ecard_release(ether_devs[i].ec);
+
+			ether_devs[i].ec = NULL;
 		}
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/net/ether3.h linux.18p17/drivers/acorn/net/ether3.h
--- linux.vanilla/drivers/acorn/net/ether3.h	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/net/ether3.h	Fri Sep 15 23:31:12 2000
@@ -151,9 +151,8 @@
 	unsigned int config1;
 	unsigned int config2;
     } regs;
-    unsigned int tx_head;		/* address to insert next packet	 */
-    unsigned int tx_tail;		/* address of transmitting packet	 */
-    unsigned int tx_used;		/* number of 'slots' used		 */
+    unsigned char tx_head;		/* buffer nr to insert next packet	 */
+    unsigned char tx_tail;		/* buffer nr of transmitting packet	 */
     unsigned int rx_head;		/* address to fetch next packet from	 */
     struct enet_statistics stats;
     struct timer_list timer;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/net/etherh.c linux.18p17/drivers/acorn/net/etherh.c
--- linux.vanilla/drivers/acorn/net/etherh.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/net/etherh.c	Fri Sep 15 23:31:12 2000
@@ -37,6 +37,7 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -50,13 +51,16 @@
 #define DEBUG_INIT 2
 
 static unsigned int net_debug = NET_DEBUG;
-static const card_ids etherh_cids[] = {
+static const card_ids __init etherh_cids[] = {
 	{ MANU_I3, PROD_I3_ETHERLAN500 },
 	{ MANU_I3, PROD_I3_ETHERLAN600 },
 	{ MANU_I3, PROD_I3_ETHERLAN600A },
 	{ 0xffff, 0xffff }
 };
 
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("i3 EtherH driver");
+
 static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King v1.05\n";
 
 #define ETHERH500_DATAPORT	0x200	/* MEMC */
@@ -80,8 +84,8 @@
  * Read the ethernet address string from the on board rom.
  * This is an ascii string...
  */
-static int
-etherh_addr(char *addr, struct expansion_card *ec)
+__initfunc(static int
+etherh_addr(char *addr, struct expansion_card *ec))
 {
 	struct in_chunk_dir cd;
 	char *s;
@@ -216,10 +220,8 @@
 
 	if (ei_status.word16)
 		outsw (dma_addr, buf, count >> 1);
-#ifdef BIT8
 	else
 		outsb (dma_addr, buf, count);
-#endif
 
 	dma_start = jiffies;
 
@@ -268,11 +270,8 @@
 		insw (dma_addr, buf, count >> 1);
 		if (count & 1)
 			buf[count - 1] = inb (dma_addr);
-	}
-#ifdef BIT8
-	else
+	} else
 		insb (dma_addr, buf, count);
-#endif
 
 	outb (ENISR_RDC, addr + EN0_ISR);
 	ei_status.dmaing &= ~1;
@@ -307,10 +306,8 @@
 
 	if (ei_status.word16)
 		insw (dma_addr, hdr, sizeof (*hdr) >> 1);
-#ifdef BIT8
 	else
 		insb (dma_addr, hdr, sizeof (*hdr));
-#endif
 
 	outb (ENISR_RDC, addr + EN0_ISR);
 	ei_status.dmaing &= ~1;
@@ -355,8 +352,8 @@
 /*
  * This is the real probe routine.
  */
-static int
-etherh_probe1(struct device *dev)
+__initfunc(static int
+etherh_probe1(struct device *dev))
 {
 	static int version_printed;
 	unsigned int addr, i, reg0, tmp;
@@ -461,10 +458,13 @@
 	etherh_irq_enable,
 	etherh_irq_disable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
-static void etherh_initdev (ecard_t *ec, struct device *dev)
+__initfunc(static void
+etherh_initdev(ecard_t *ec, struct device *dev))
 {
 	ecard_claim (ec);
 	
@@ -492,27 +492,27 @@
 	}
 	ec->ops = &etherh_ops;
 
-	etherh_addr (dev->dev_addr, ec);
+	etherh_addr(dev->dev_addr, ec);
 }
 
 #ifndef MODULE
-int
-etherh_probe(struct device *dev)
+__initfunc(int
+etherh_probe(struct device *dev))
 {
 	if (!dev)
 		return ENODEV;
 
-	ecard_startfind ();
-
-	if (!dev->base_addr) {
+	if (!dev->base_addr || dev->base_addr == 0xffe0) {
 		struct expansion_card *ec;
 
+		ecard_startfind();
+
 		if ((ec = ecard_find (0, etherh_cids)) == NULL)
 			return ENODEV;
 
-		etherh_initdev (ec, dev);
+		etherh_initdev(ec, dev);
 	}
-	return etherh_probe1 (dev);
+	return etherh_probe1(dev);
 }
 #endif
 
@@ -529,12 +529,10 @@
 init_all_cards(void)
 {
 	struct device *dev = NULL;
-	struct expansion_card *boguscards[MAX_ETHERH_CARDS];
 	int i, found = 0;
 
 	for (i = 0; i < MAX_ETHERH_CARDS; i++) {
 		my_ethers[i] = NULL;
-		boguscards[i] = NULL;
 		ec[i] = NULL;
 		strcpy (ethernames[i], "        ");
 	}
@@ -571,7 +569,7 @@
 		if (register_netdev(dev) != 0) {
 			printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr);
 			if (ec[i]) {
-				boguscards[i] = ec[i];
+				ecard_release(ec[i]);
 				ec[i] = NULL;
 			}
 			continue;
@@ -582,12 +580,6 @@
 
 	if (dev)
 		kfree (dev);
-
-	for (i = 0; i < MAX_ETHERH_CARDS; i++)
-		if (boguscards[i]) {
-			boguscards[i]->ops = NULL;
-			ecard_release (boguscards[i]);
-		}
 
 	return found ? 0 : -ENODEV;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/Config.in linux.18p17/drivers/acorn/scsi/Config.in
--- linux.vanilla/drivers/acorn/scsi/Config.in	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/Config.in	Fri Sep 15 23:31:12 2000
@@ -7,11 +7,12 @@
   bool '  Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI
   dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI
   dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI
   dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI
 
-  comment 'The following drives are not fully supported'
+  comment 'The following drivers are not fully supported'
 
   dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI
   if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/Makefile linux.18p17/drivers/acorn/scsi/Makefile
--- linux.vanilla/drivers/acorn/scsi/Makefile	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/Makefile	Fri Sep 15 23:31:12 2000
@@ -24,6 +24,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_ARXESCSI),y)
+  L_OBJS += arxescsi.o
+  CONFIG_FAS216_BUILTIN=y
+else
+  ifeq ($(CONFIG_SCSI_ARXESCSI),m)
+    M_OBJS += arxescsi.o
+    CONFIG_FAS216_MODULE=y
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_CUMANA_1),y)
   L_OBJS += cumana_1.o
 else
@@ -34,12 +44,10 @@
 
 ifeq ($(CONFIG_SCSI_CUMANA_2),y)
   L_OBJS += cumana_2.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_CUMANA_2),m)
     M_OBJS += cumana_2.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
@@ -62,41 +70,39 @@
 
 ifeq ($(CONFIG_SCSI_POWERTECSCSI),y)
   L_OBJS += powertec.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_POWERTECSCSI),m)
     M_OBJS += powertec.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
 
 ifeq ($(CONFIG_SCSI_EESOXSCSI),y)
   L_OBJS += eesox.o
-  CONFIG_QUEUE_BUILTIN=y
   CONFIG_FAS216_BUILTIN=y
 else
   ifeq ($(CONFIG_SCSI_EESOXSCSI),m)
     M_OBJS += eesox.o
-    CONFIG_QUEUE_MODULE=y
     CONFIG_FAS216_MODULE=y
   endif
 endif
 
-ifeq ($(CONFIG_QUEUE_BUILTIN),y)
-  LX_OBJS += queue.o msgqueue.o
-else
-  ifeq ($(CONFIG_QUEUE_MODULE),y)
-    MX_OBJS += queue.o msgqueue.o
-  endif
-endif
-
 ifeq ($(CONFIG_FAS216_BUILTIN),y)
   LX_OBJS += fas216.o
+  CONFIG_QUEUE_BUILTIN=y
 else
   ifeq ($(CONFIG_FAS216_MODULE),y)
     MX_OBJS += fas216.o
+    CONFIG_QUEUE_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_QUEUE_BUILTIN),y)
+  LX_OBJS += queue.o msgqueue.o
+else
+  ifeq ($(CONFIG_QUEUE_MODULE),y)
+    MX_OBJS += queue.o msgqueue.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/acornscsi.c linux.18p17/drivers/acorn/scsi/acornscsi.c
--- linux.vanilla/drivers/acorn/scsi/acornscsi.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/acornscsi.c	Fri Sep 15 23:31:12 2000
@@ -21,6 +21,8 @@
  *  12-Oct-1997	RMK	Added catch for re-entering interrupt routine.
  *  15-Oct-1997	RMK	Improved handling of commands.
  *  27-Jun-1998	RMK	Changed asm/delay.h to linux/delay.h.
+ *  13-Dec-1998	RMK	Better abort code and command handling.  Extra state
+ *			transitions added to allow dodgy devices to work.
  */
 #define DEBUG_NO_WRITE	1
 #define DEBUG_QUEUES	2
@@ -35,7 +37,7 @@
 #define DEBUG_RESET	1024
 #define DEBUG_ALL	(DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
 			 DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
-			 DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE)
+			 DEBUG_DMA|DEBUG_QUEUES)
 
 /* DRIVER CONFIGURATION
  *
@@ -123,8 +125,8 @@
 #ifndef STRINGIFY
 #define STRINGIFY(x) #x
 #endif
-#define STR(x) STRINGIFY(x)
-#define NO_WRITE_STR STR(NO_WRITE)
+#define STRx(x) STRINGIFY(x)
+#define NO_WRITE_STR STRx(NO_WRITE)
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -142,6 +144,7 @@
 #include <asm/bitops.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/ecard.h>
 
 #include "../../scsi/scsi.h"
@@ -160,10 +163,6 @@
 #error "Yippee!  ABORT TAG is now defined!  Remove this error!"
 #endif
 
-#ifndef NO_IRQ
-#define NO_IRQ 255
-#endif
-
 #ifdef CONFIG_SCSI_ACORNSCSI_LINK
 #error SCSI2 LINKed commands not supported (yet)!
 #endif
@@ -186,26 +185,7 @@
 #define DMAC_BUFFER_SIZE	65536
 #endif
 
-/*
- * This is used to dump the previous states of the SBIC
- */
-static struct status_entry {
-	unsigned long	when;
-	unsigned char	ssr;
-	unsigned char	ph;
-	unsigned char	irq;
-	unsigned char	unused;
-} status[9][16];
-static unsigned char status_ptr[9];
-
-#define ADD_STATUS(_q,_ssr,_ph,_irq) \
-({							\
-	status[(_q)][status_ptr[(_q)]].when = jiffies;	\
-	status[(_q)][status_ptr[(_q)]].ssr  = (_ssr);	\
-	status[(_q)][status_ptr[(_q)]].ph   = (_ph);	\
-	status[(_q)][status_ptr[(_q)]].irq  = (_irq);	\
-	status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15;	\
-})
+#define STATUS_BUFFER_TO_PRINT	24
 
 unsigned int sdtr_period = SDTR_PERIOD;
 unsigned int sdtr_size   = SDTR_SIZE;
@@ -214,31 +194,31 @@
 	PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
-static int acornscsi_reconnect_finish (AS_Host *host);
-static void acornscsi_dma_cleanup (AS_Host *host);
-static void acornscsi_abortcmd (AS_Host *host, unsigned char tag);
+static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
+static int acornscsi_reconnect_finish(AS_Host *host);
+static void acornscsi_dma_cleanup(AS_Host *host);
+static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
 
 /* ====================================================================================
  * Miscellaneous
  */
 
 static inline void
-sbic_arm_write (unsigned int io_port, int reg, int value)
+sbic_arm_write(unsigned int io_port, int reg, int value)
 {
-    outb_t (reg, io_port);
-    outb_t (value, io_port + 4);
+    outb_t(reg, io_port);
+    outb_t(value, io_port + 4);
 }
 
 #define sbic_arm_writenext(io,val) \
-	outb_t ((val), (io) + 4)
+	outb_t((val), (io) + 4)
 
 static inline
-int sbic_arm_read (unsigned int io_port, int reg)
+int sbic_arm_read(unsigned int io_port, int reg)
 {
     if(reg == ASR)
 	   return inl_t(io_port) & 255;
-    outb_t (reg, io_port);
+    outb_t(reg, io_port);
     return inl_t(io_port + 4) & 255;
 }
 
@@ -247,129 +227,165 @@
 
 #ifdef USE_DMAC
 #define dmac_read(io_port,reg) \
-	inb ((io_port) + (reg))
+	inb((io_port) + (reg))
 
 #define dmac_write(io_port,reg,value) \
-	({ outb ((value), (io_port) + (reg)); })
+	({ outb((value), (io_port) + (reg)); })
 
 #define dmac_clearintr(io_port) \
-	({ outb (0, (io_port)); })
+	({ outb(0, (io_port)); })
 
 static inline
-unsigned int dmac_address (unsigned int io_port)
+unsigned int dmac_address(unsigned int io_port)
 {
-    return dmac_read (io_port, TXADRHI) << 16 |
-	   dmac_read (io_port, TXADRMD) << 8 |
-	   dmac_read (io_port, TXADRLO);
+    return dmac_read(io_port, TXADRHI) << 16 |
+	   dmac_read(io_port, TXADRMD) << 8 |
+	   dmac_read(io_port, TXADRLO);
 }
 
 static
-void acornscsi_dumpdma (AS_Host *host, char *where)
+void acornscsi_dumpdma(AS_Host *host, char *where)
 {
 	unsigned int mode, addr, len;
 
-	mode = dmac_read (host->dma.io_port, MODECON);
-	addr = dmac_address (host->dma.io_port);
-	len  = dmac_read (host->dma.io_port, TXCNTHI) << 8 |
-	       dmac_read (host->dma.io_port, TXCNTLO);
+	mode = dmac_read(host->dma.io_port, MODECON);
+	addr = dmac_address(host->dma.io_port);
+	len  = dmac_read(host->dma.io_port, TXCNTHI) << 8 |
+	       dmac_read(host->dma.io_port, TXCNTLO);
 
-	printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
+	printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
 		host->host->host_no, where,
 		mode, addr, (len + 1) & 0xffff,
-		dmac_read (host->dma.io_port, MASKREG));
+		dmac_read(host->dma.io_port, MASKREG));
 
-	printk ("DMA @%06x, ", host->dma.start_addr);
-	printk ("BH @%p +%04x, ", host->scsi.SCp.ptr,
+	printk("DMA @%06x, ", host->dma.start_addr);
+	printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
 		host->scsi.SCp.this_residual);
-	printk ("DT @+%04x ST @+%04x", host->dma.transferred,
+	printk("DT @+%04x ST @+%04x", host->dma.transferred,
 		host->scsi.SCp.scsi_xferred);
-	printk ("\n");
+	printk("\n");
 }
 #endif
 
 static
-unsigned long acornscsi_sbic_xfcount (AS_Host *host)
+unsigned long acornscsi_sbic_xfcount(AS_Host *host)
 {
     unsigned long length;
 
-    length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16;
-    length |= sbic_arm_readnext (host->scsi.io_port) << 8;
-    length |= sbic_arm_readnext (host->scsi.io_port);
+    length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16;
+    length |= sbic_arm_readnext(host->scsi.io_port) << 8;
+    length |= sbic_arm_readnext(host->scsi.io_port);
 
     return length;
 }
 
-static
-int acornscsi_sbic_issuecmd (AS_Host *host, int command)
+static int
+acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
 {
-    int asr;
+	int asr;
 
-    do {
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-    } while (asr & ASR_CIP);
+	do {
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+		if ((asr & stat_mask) == stat)
+			return 0;
+
+		udelay(1);
+	} while (--timeout);
+
+	printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
+
+	return -1;
+}
 
-    sbic_arm_write (host->scsi.io_port, CMND, command);
+static
+int acornscsi_sbic_issuecmd(AS_Host *host, int command)
+{
+    if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
+	return -1;
+
+    sbic_arm_write(host->scsi.io_port, CMND, command);
 
     return 0;
 }
 
 static void
-acornscsi_csdelay (unsigned int cs)
+acornscsi_csdelay(unsigned int cs)
 {
     unsigned long target_jiffies, flags;
 
     target_jiffies = jiffies + 1 + cs * HZ / 100;
 
-    save_flags (flags);
-    sti ();
+    save_flags(flags);
+    sti();
 
     while (time_before(jiffies, target_jiffies)) barrier();
 
-    restore_flags (flags);
+    restore_flags(flags);
 }
 
 static
-void acornscsi_resetcard (AS_Host *host)
+void acornscsi_resetcard(AS_Host *host)
 {
-    unsigned int i;
+    unsigned int i, timeout;
 
     /* assert reset line */
     host->card.page_reg = 0x80;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /* wait 3 cs.  SCSI standard says 25ms. */
-    acornscsi_csdelay (3);
+    acornscsi_csdelay(3);
 
     host->card.page_reg = 0;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /*
      * Should get a reset from the card
      */
-    while (!(inb (host->card.io_intr) & 8));
-    sbic_arm_read (host->scsi.io_port, ASR);
-    sbic_arm_read (host->scsi.io_port, SSR);
+    timeout = 1000;
+    do {
+	if (inb(host->card.io_intr) & 8)
+	    break;
+	udelay(1);
+    } while (--timeout);
+
+    if (timeout == 0)
+	printk("scsi%d: timeout while resetting card\n",
+		host->host->host_no);
+
+    sbic_arm_read(host->scsi.io_port, ASR);
+    sbic_arm_read(host->scsi.io_port, SSR);
 
     /* setup sbic - WD33C93A */
-    sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
-    sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+    sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+    sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
 
     /*
      * Command should cause a reset interrupt
      */
-    while (!(inb (host->card.io_intr) & 8));
-    sbic_arm_read (host->scsi.io_port, ASR);
-    if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01)
-	printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+    timeout = 1000;
+    do {
+	if (inb(host->card.io_intr) & 8)
+	    break;
+	udelay(1);
+    } while (--timeout);
+
+    if (timeout == 0)
+	printk("scsi%d: timeout while resetting card\n",
 		host->host->host_no);
 
-    sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
-    sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
-    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
-    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+    sbic_arm_read(host->scsi.io_port, ASR);
+    if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01)
+	printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+		host->host->host_no);
+
+    sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+    sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+    sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
 
     host->card.page_reg = 0x40;
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 
     /* setup dmac - uPC71071 */
     dmac_write(host->dma.io_port, INIT, 0);
@@ -391,7 +407,7 @@
     }
 
     /* wait 25 cs.  SCSI standard says 250ms. */
-    acornscsi_csdelay (25);
+    acornscsi_csdelay(25);
 }
 
 /*=============================================================================================
@@ -461,80 +477,101 @@
 };
 
 static
-void print_scsi_status (unsigned int ssr)
+void print_scsi_status(unsigned int ssr)
 {
     if (acornscsi_map[ssr] != -1)
-	printk ("%s:%s",
+	printk("%s:%s",
 		acornscsi_interrupttype[(ssr >> 4)],
 		acornscsi_interruptcode[acornscsi_map[ssr]]);
     else
-	printk ("%X:%X", ssr >> 4, ssr & 0x0f);    
+	printk("%X:%X", ssr >> 4, ssr & 0x0f);    
 }    
 #endif
 
 static
-void print_sbic_status (int asr, int ssr, int cmdphase)
+void print_sbic_status(int asr, int ssr, int cmdphase)
 {
 #ifdef CONFIG_ACORNSCSI_CONSTANTS
-    printk ("sbic: %c%c%c%c%c%c ",
+    printk("sbic: %c%c%c%c%c%c ",
 	    asr & ASR_INT ? 'I' : 'i',
 	    asr & ASR_LCI ? 'L' : 'l',
 	    asr & ASR_BSY ? 'B' : 'b',
 	    asr & ASR_CIP ? 'C' : 'c',
 	    asr & ASR_PE  ? 'P' : 'p',
 	    asr & ASR_DBR ? 'D' : 'd');
-    printk ("scsi: ");
-    print_scsi_status (ssr);
-    printk (" ph %02X\n", cmdphase);
+    printk("scsi: ");
+    print_scsi_status(ssr);
+    printk(" ph %02X\n", cmdphase);
 #else
-    printk ("sbic: %02X scsi: %X:%X ph: %02X\n",
+    printk("sbic: %02X scsi: %X:%X ph: %02X\n",
 	    asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
 #endif
 }
 
-static
-void acornscsi_dumplog (AS_Host *host, int target)
+static void
+acornscsi_dumplogline(AS_Host *host, int target, int line)
 {
-    unsigned int prev;
-    do {
-	signed int statptr;
+	unsigned long prev;
+	signed int ptr;
 
-	printk ("%c:", target == 8 ? 'H' : ('0' + target));
-	statptr = status_ptr[target] - 10;
+	ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
+	if (ptr < 0)
+		ptr += STATUS_BUFFER_SIZE;
 
-	if (statptr < 0)
-	    statptr += 16;
+	printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
+		line == 0 ? "ph" : line == 1 ? "ssr" : "int");
 
-	prev = status[target][statptr].when;
+	prev = host->status[target][ptr].when;
 
-	for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) {
-	    if (status[target][statptr].when) {
-#ifdef CONFIG_ACORNSCSI_CONSTANTS
-		printk ("%c%02X:S=",
-			status[target][statptr].irq ? '-' : ' ',
-			status[target][statptr].ph);
-		print_scsi_status (status[target][statptr].ssr);
-#else
-		printk ("%c%02X:%02X",
-			status[target][statptr].irq ? '-' : ' ',
-			status[target][statptr].ph,
-			status[target][statptr].ssr);
-#endif
-		printk ("+%02ld",
-			(status[target][statptr].when - prev) < 100 ?
-				(status[target][statptr].when - prev) : 99);
-		prev = status[target][statptr].when;
-	    }
+	for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+		unsigned long time_diff;
+
+		if (!host->status[target][ptr].when)
+			continue;
+
+		switch (line) {
+		case 0:
+			printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
+					 host->status[target][ptr].ph);
+			break;
+
+		case 1:
+			printk(" %02X", host->status[target][ptr].ssr);
+			break;
+
+		case 2:
+			time_diff = host->status[target][ptr].when - prev;
+			prev = host->status[target][ptr].when;
+			if (time_diff == 0)
+				printk("==^");
+			else if (time_diff >= 100)
+				printk("   ");
+			else
+				printk(" %02ld", time_diff);
+			break;
+		}
 	}
-	printk ("\n");
+
+	printk("\n");
+}
+
+static
+void acornscsi_dumplog(AS_Host *host, int target)
+{
+    do {
+	acornscsi_dumplogline(host, target, 0);
+	acornscsi_dumplogline(host, target, 1);
+	acornscsi_dumplogline(host, target, 2);
+
 	if (target == 8)
 	    break;
+
 	target = 8;
     } while (1);
 }
 
 static
-char acornscsi_target (AS_Host *host)
+char acornscsi_target(AS_Host *host)
 {
 	if (host->SCpnt)
 		return '0' + host->SCpnt->target;
@@ -542,7 +579,7 @@
 }
 
 /*
- * Prototype: cmdtype_t acornscsi_cmdtype (int command)
+ * Prototype: cmdtype_t acornscsi_cmdtype(int command)
  * Purpose  : differentiate READ from WRITE from other commands
  * Params   : command - command to interpret
  * Returns  : CMD_READ	- command reads data,
@@ -550,7 +587,7 @@
  *	      CMD_MISC	- everything else
  */
 static inline
-cmdtype_t acornscsi_cmdtype (int command)
+cmdtype_t acornscsi_cmdtype(int command)
 {
     switch (command) {
     case WRITE_6:  case WRITE_10:  case WRITE_12:
@@ -563,7 +600,7 @@
 }
 
 /*
- * Prototype: int acornscsi_datadirection (int command)
+ * Prototype: int acornscsi_datadirection(int command)
  * Purpose  : differentiate between commands that have a DATA IN phase
  *	      and a DATA OUT phase
  * Params   : command - command to interpret
@@ -571,7 +608,7 @@
  *	      DATADIR_IN  - data in phase expected
  */
 static
-datadir_t acornscsi_datadirection (int command)
+datadir_t acornscsi_datadirection(int command)
 {
     switch (command) {
     case CHANGE_DEFINITION:	case COMPARE:		case COPY:
@@ -605,13 +642,13 @@
 };
 
 /*
- * Prototype: int acornscsi_getperiod (unsigned char syncxfer)
+ * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
  * Purpose  : period for the synchronous transfer setting
  * Params   : syncxfer SYNCXFER register value
  * Returns  : period in ns.
  */
 static
-int acornscsi_getperiod (unsigned char syncxfer)
+int acornscsi_getperiod(unsigned char syncxfer)
 {
     int i;
 
@@ -626,14 +663,14 @@
 }
 
 /*
- * Prototype: int round_period (unsigned int period)
+ * Prototype: int round_period(unsigned int period)
  * Purpose  : return index into above table for a required REQ period
  * Params   : period - time (ns) for REQ
  * Returns  : table index
  * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
  */
 static inline
-int round_period (unsigned int period)
+int round_period(unsigned int period)
 {
     int i;
 
@@ -646,7 +683,7 @@
 }
 
 /*
- * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+ * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
  * Purpose  : calculate value for 33c93s SYNC register
  * Params   : period - time (ns) for REQ
  *	      offset - offset in bytes between REQ/ACK
@@ -654,7 +691,7 @@
  * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
  */
 static
-unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
 {
     return sync_xfer_table[round_period(period)].reg_value |
 		((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
@@ -664,14 +701,14 @@
  * Command functions
  */
 /*
- * Function: acornscsi_kick (AS_Host *host)
+ * Function: acornscsi_kick(AS_Host *host)
  * Purpose : kick next command to interface
  * Params  : host - host to send command to
  * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
  * Notes   : interrupts are always disabled!
  */
 static
-intr_ret_t acornscsi_kick (AS_Host *host)
+intr_ret_t acornscsi_kick(AS_Host *host)
 {
     int from_queue = 0;
     Scsi_Cmnd *SCpnt;
@@ -682,7 +719,7 @@
 
     /* retrieve next command */
     if (!SCpnt) {
-	SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns);
+	SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
 	if (!SCpnt)
 	    return INTR_IDLE;
 
@@ -690,11 +727,11 @@
     }
 
     if (host->scsi.disconnectable && host->SCpnt) {
-	queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+	queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
 	host->scsi.disconnectable = 0;
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n",
-		host->host->host_no, acornscsi_target (host)));
+	DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
+		host->host->host_no, acornscsi_target(host)));
 #endif
 	host->SCpnt = NULL;
     }
@@ -703,9 +740,9 @@
      * If we have an interrupt pending, then we may have been reselected.
      * In this case, we don't want to write to the registers
      */
-    if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
-	sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target);
-	sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN);
+    if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+	sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->target);
+	sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN);
     }
 
     /*
@@ -717,9 +754,10 @@
     host->scsi.SCp = SCpnt->SCp;
     host->dma.xfer_setup = 0;
     host->dma.xfer_required = 0;
+    host->dma.xfer_done = 0;
 
 #if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
-    DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n",
+    DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
 	    host->host->host_no, '0' + SCpnt->target,
 	    SCpnt->cmnd[0]));
 #endif
@@ -736,11 +774,11 @@
 	    SCpnt->tag = SCpnt->device->current_tag;
 	} else
 #endif
-	    set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+	    set_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
 
 	host->stats.removes += 1;
 
-	switch (acornscsi_cmdtype (SCpnt->cmnd[0])) {
+	switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
 	case CMD_WRITE:
 	    host->stats.writes += 1;
 	    break;
@@ -757,25 +795,25 @@
 }    
 
 /*
- * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+ * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
  * Purpose : complete processing for command
  * Params  : host   - interface that completed
  *	     result - driver byte of result
  */
 static
-void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
 {
     Scsi_Cmnd *SCpnt = *SCpntp;
 
     /* clean up */
-    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+    sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
 
     host->stats.fins += 1;
 
     if (SCpnt) {
 	*SCpntp = NULL;
 
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 
 	SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
 
@@ -787,35 +825,63 @@
 	 * It doesn't appear to be set to something meaningful by the higher
 	 * levels all the time.
 	 */
-	if (host->scsi.SCp.ptr && result == DID_OK &&
-		acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) {
-	    switch (status_byte (SCpnt->result)) {
-	    case CHECK_CONDITION:
-	    case COMMAND_TERMINATED:
-	    case BUSY:
-	    case QUEUE_FULL:
-	    case RESERVATION_CONFLICT:
-		break;
+	if (result == DID_OK) {
+		int xfer_warn = 0;
 
-	    default:
-		printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
-			host->host->host_no, SCpnt->result);
-		print_command (SCpnt->cmnd);
-		acornscsi_dumpdma (host, "done");
-	 	acornscsi_dumplog (host, SCpnt->target);
-		SCpnt->result &= 0xffff;
-		SCpnt->result |= DID_ERROR << 16;
-	    }
+		if (SCpnt->underflow == 0) {
+			if (host->scsi.SCp.ptr &&
+			    acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
+				xfer_warn = 1;
+		} else {
+			if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
+			    host->scsi.SCp.scsi_xferred != host->dma.transferred)
+				xfer_warn = 1;
+		}
+
+		/* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
+		 *  Targets which break data transfers into multiple
+		 *  connections shall end each successful connection
+		 *  (except possibly the last) with a SAVE DATA
+		 *  POINTER - DISCONNECT message sequence.
+		 *
+		 * This makes it difficult to ensure that a transfer has
+		 * completed.  If we reach the end of a transfer during
+		 * the command, then we can only have finished the transfer.
+		 * therefore, if we seem to have some data remaining, this
+		 * is not a problem.
+		 */
+		if (host->dma.xfer_done)
+			xfer_warn = 0;
+
+		if (xfer_warn) {
+		    switch (status_byte(SCpnt->result)) {
+		    case CHECK_CONDITION:
+		    case COMMAND_TERMINATED:
+		    case BUSY:
+		    case QUEUE_FULL:
+		    case RESERVATION_CONFLICT:
+			break;
+
+		    default:
+			printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
+				host->host->host_no, SCpnt->result);
+			print_command(SCpnt->cmnd);
+			acornscsi_dumpdma(host, "done");
+		 	acornscsi_dumplog(host, SCpnt->target);
+			SCpnt->result &= 0xffff;
+			SCpnt->result |= DID_ERROR << 16;
+		    }
+		}
 	}
 
 	if (!SCpnt->scsi_done)
-	    panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
+	    panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
 
-	clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+	clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
 
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     } else
-	printk ("scsi%d: null command in acornscsi_done", host->host->host_no);
+	printk("scsi%d: null command in acornscsi_done", host->host->host_no);
 
     host->scsi.phase = PHASE_IDLE;
 }
@@ -828,7 +894,7 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
+void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
 {
     SCp->ptr += length;
     SCp->this_residual -= length;
@@ -839,13 +905,15 @@
 	    SCp->buffers_residual--;
 	    SCp->ptr = (char *)SCp->buffer->address;
 	    SCp->this_residual = SCp->buffer->length;
-	} else
+	} else {
 	    SCp->ptr = NULL;
+	    host->dma.xfer_done = 1;
+	}
     }
 }
 
 /*
- * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
  *				unsigned int start_addr, unsigned int length)
  * Purpose  : read data from DMA RAM
  * Params   : host - host to transfer from
@@ -855,16 +923,16 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_read (AS_Host *host, char *ptr,
+void acornscsi_data_read(AS_Host *host, char *ptr,
 				 unsigned int start_addr, unsigned int length)
 {
-    extern void __acornscsi_in (int port, char *buf, int len);
+    extern void __acornscsi_in(int port, char *buf, int len);
     unsigned int page, offset, len = length;
 
     page = (start_addr >> 12);
     offset = start_addr & ((1 << 12) - 1);
 
-    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 
     while (len > 0) {
 	unsigned int this_len;
@@ -874,7 +942,7 @@
 	else
 	    this_len = len;
 
-	__acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len);
+	__acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len);
 
 	offset += this_len;
 	ptr += this_len;
@@ -883,14 +951,14 @@
 	if (offset == (1 << 12)) {
 	    offset = 0;
 	    page ++;
-	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 	}
     }
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 }
 
 /*
- * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
  *				unsigned int start_addr, unsigned int length)
  * Purpose  : write data to DMA RAM
  * Params   : host - host to transfer from
@@ -900,16 +968,16 @@
  * Notes    : this will only be one SG entry or less
  */
 static
-void acornscsi_data_write (AS_Host *host, char *ptr,
+void acornscsi_data_write(AS_Host *host, char *ptr,
 				 unsigned int start_addr, unsigned int length)
 {
-    extern void __acornscsi_out (int port, char *buf, int len);
+    extern void __acornscsi_out(int port, char *buf, int len);
     unsigned int page, offset, len = length;
 
     page = (start_addr >> 12);
     offset = start_addr & ((1 << 12) - 1);
 
-    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 
     while (len > 0) {
 	unsigned int this_len;
@@ -919,7 +987,7 @@
 	else
 	    this_len = len;
 
-	__acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len);
+	__acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len);
 
 	offset += this_len;
 	ptr += this_len;
@@ -928,10 +996,10 @@
 	if (offset == (1 << 12)) {
 	    offset = 0;
 	    page ++;
-	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	    outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
 	}
     }
-    outb (host->card.page_reg, host->card.io_page);
+    outb(host->card.page_reg, host->card.io_page);
 }
 
 /* =========================================================================================
@@ -939,25 +1007,25 @@
  */
 #ifdef USE_DMAC
 /*
- * Prototype: void acornscsi_dmastop (AS_Host *host)
+ * Prototype: void acornscsi_dmastop(AS_Host *host)
  * Purpose  : stop all DMA
  * Params   : host - host on which to stop DMA
  * Notes    : This is called when leaving DATA IN/OUT phase,
  *	      or when interface is RESET
  */
 static inline
-void acornscsi_dma_stop (AS_Host *host)
+void acornscsi_dma_stop(AS_Host *host)
 {
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
 #if (DEBUG & DEBUG_DMA)
-    DBG(host->SCpnt, acornscsi_dumpdma (host, "stop"));
+    DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
 #endif
 }
 
 /*
- * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+ * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
  * Purpose : setup DMA controller for data transfer
  * Params  : host - host to setup
  *	     direction - data transfer direction
@@ -965,19 +1033,19 @@
  *	     while we're in a DATA I/O phase
  */
 static
-void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
 {
     unsigned int address, length, mode;
 
     host->dma.direction = direction;
 
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
 
     if (direction == DMA_OUT) {
 #if (DEBUG & DEBUG_NO_WRITE)
 	if (NO_WRITE & (1 << host->SCpnt->target)) {
-	    printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
+		    host->host->host_no, acornscsi_target(host));
 	    return;
 	}
 #endif
@@ -988,7 +1056,7 @@
     /*
      * Allocate some buffer space, limited to half the buffer size
      */
-    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
     if (length) {
 	host->dma.start_addr = address = host->dma.free_addr;
 	host->dma.free_addr = (host->dma.free_addr + length) &
@@ -998,27 +1066,27 @@
 	 * Transfer data to DMA memory
 	 */
 	if (direction == DMA_OUT)
-	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
 				length);
 
 	length -= 1;
-	dmac_write (host->dma.io_port, TXCNTLO, length);
-	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
-	dmac_write (host->dma.io_port, TXADRLO, address);
-	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
-	dmac_write (host->dma.io_port, TXADRHI, 0);
-	dmac_write (host->dma.io_port, MODECON, mode);
-	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	dmac_write(host->dma.io_port, TXCNTLO, length);
+	dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write(host->dma.io_port, TXADRLO, address);
+	dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write(host->dma.io_port, TXADRHI, 0);
+	dmac_write(host->dma.io_port, MODECON, mode);
+	dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 
 #if (DEBUG & DEBUG_DMA)
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "strt"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
 #endif
 	host->dma.xfer_setup = 1;
     }
 }
 
 /*
- * Function: void acornscsi_dma_cleanup (AS_Host *host)
+ * Function: void acornscsi_dma_cleanup(AS_Host *host)
  * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
  * Params  : host - host to finish
  * Notes   : This is called when a command is:
@@ -1026,10 +1094,10 @@
  *	   : This must not return until all transfers are completed.
  */
 static
-void acornscsi_dma_cleanup (AS_Host *host)
+void acornscsi_dma_cleanup(AS_Host *host)
 {
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
     /*
      * Check for a pending transfer
@@ -1037,7 +1105,7 @@
     if (host->dma.xfer_required) {
 	host->dma.xfer_required = 0;
 	if (host->dma.direction == DMA_IN)
-	    acornscsi_data_read (host, host->dma.xfer_ptr,
+	    acornscsi_data_read(host, host->dma.xfer_ptr,
 				 host->dma.xfer_start, host->dma.xfer_length);
     }
 
@@ -1056,17 +1124,17 @@
 	/*
 	 * Calculate number of bytes transferred from DMA.
 	 */
-	transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+	transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
 	host->dma.transferred += transferred;
 
 	if (host->dma.direction == DMA_IN)
-	    acornscsi_data_read (host, host->scsi.SCp.ptr,
+	    acornscsi_data_read(host, host->scsi.SCp.ptr,
 				 host->dma.start_addr, transferred);
 
 	/*
 	 * Update SCSI pointers
 	 */
-	acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+	acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
 #if (DEBUG & DEBUG_DMA)
 	DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
 #endif
@@ -1074,7 +1142,7 @@
 }
 
 /*
- * Function: void acornscsi_dmacintr (AS_Host *host)
+ * Function: void acornscsi_dmacintr(AS_Host *host)
  * Purpose : handle interrupts from DMAC device
  * Params  : host - host to process
  * Notes   : If reading, we schedule the read to main memory &
@@ -1084,21 +1152,21 @@
  *	   : Called whenever DMAC finished it's current transfer.
  */
 static
-void acornscsi_dma_intr (AS_Host *host)
+void acornscsi_dma_intr(AS_Host *host)
 {
     unsigned int address, length, transferred;
 
 #if (DEBUG & DEBUG_DMA)
-    DBG(host->SCpnt, acornscsi_dumpdma (host, "inti"));
+    DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
 #endif
 
-    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
-    dmac_clearintr (host->dma.io_intr_clear);
+    dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr(host->dma.io_intr_clear);
 
     /*
      * Calculate amount transferred via DMA
      */
-    transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+    transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
     host->dma.transferred += transferred;
 
     /*
@@ -1111,12 +1179,12 @@
 	host->dma.xfer_required = 1;
     }
 
-    acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+    acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
 
     /*
      * Allocate some buffer space, limited to half the on-board RAM size
      */
-    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
     if (length) {
 	host->dma.start_addr = address = host->dma.free_addr;
 	host->dma.free_addr = (host->dma.free_addr + length) &
@@ -1126,19 +1194,19 @@
 	 * Transfer data to DMA memory
 	 */
 	if (host->dma.direction == DMA_OUT)
-	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+	    acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
 				length);
 
 	length -= 1;
-	dmac_write (host->dma.io_port, TXCNTLO, length);
-	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
-	dmac_write (host->dma.io_port, TXADRLO, address);
-	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
-	dmac_write (host->dma.io_port, TXADRHI, 0);
-	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	dmac_write(host->dma.io_port, TXCNTLO, length);
+	dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write(host->dma.io_port, TXADRLO, address);
+	dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write(host->dma.io_port, TXADRHI, 0);
+	dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 
 #if (DEBUG & DEBUG_DMA)
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "into"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
 #endif
     } else {
 	host->dma.xfer_setup = 0;
@@ -1149,48 +1217,48 @@
 	 * attention condition.  We continue giving one byte until
 	 * the device recognises the attention.
 	 */
-	if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) {
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) {
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 
-	    dmac_write (host->dma.io_port, TXCNTLO, 0);
-	    dmac_write (host->dma.io_port, TXCNTHI, 0);
-	    dmac_write (host->dma.io_port, TXADRLO, 0);
-	    dmac_write (host->dma.io_port, TXADRMD, 0);
-	    dmac_write (host->dma.io_port, TXADRHI, 0);
-	    dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	    dmac_write(host->dma.io_port, TXCNTLO, 0);
+	    dmac_write(host->dma.io_port, TXCNTHI, 0);
+	    dmac_write(host->dma.io_port, TXADRLO, 0);
+	    dmac_write(host->dma.io_port, TXADRMD, 0);
+	    dmac_write(host->dma.io_port, TXADRHI, 0);
+	    dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
 	}
 #endif
     }
 }
 
 /*
- * Function: void acornscsi_dma_xfer (AS_Host *host)
+ * Function: void acornscsi_dma_xfer(AS_Host *host)
  * Purpose : transfer data between AcornSCSI and memory
  * Params  : host - host to process
  */
 static
-void acornscsi_dma_xfer (AS_Host *host)
+void acornscsi_dma_xfer(AS_Host *host)
 {
     host->dma.xfer_required = 0;
 
     if (host->dma.direction == DMA_IN)
-	acornscsi_data_read (host, host->dma.xfer_ptr,
+	acornscsi_data_read(host, host->dma.xfer_ptr,
 				host->dma.xfer_start, host->dma.xfer_length);
 }
 
 /*
- * Function: void acornscsi_dma_adjust (AS_Host *host)
+ * Function: void acornscsi_dma_adjust(AS_Host *host)
  * Purpose : adjust DMA pointers & count for bytes transfered to
  *	     SBIC but not SCSI bus.
  * Params  : host - host to adjust DMA count for
  */
 static
-void acornscsi_dma_adjust (AS_Host *host)
+void acornscsi_dma_adjust(AS_Host *host)
 {
     if (host->dma.xfer_setup) {
 	signed long transferred;
 #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
-	DBG(host->SCpnt, acornscsi_dumpdma (host, "adji"));
+	DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
 #endif
 	/*
 	 * Calculate correct DMA address - DMA is ahead of SCSI bus while
@@ -1205,17 +1273,17 @@
 	 */
 	transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
 	if (transferred < 0)
-	    printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
-		    host->host->host_no, acornscsi_target (host), transferred);
+	    printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
+		    host->host->host_no, acornscsi_target(host), transferred);
 	else if (transferred == 0)
 	    host->dma.xfer_setup = 0;
 	else {
 	    transferred += host->dma.start_addr;
-	    dmac_write (host->dma.io_port, TXADRLO, transferred);
-	    dmac_write (host->dma.io_port, TXADRMD, transferred >> 8);
-	    dmac_write (host->dma.io_port, TXADRHI, transferred >> 16);
+	    dmac_write(host->dma.io_port, TXADRLO, transferred);
+	    dmac_write(host->dma.io_port, TXADRMD, transferred >> 8);
+	    dmac_write(host->dma.io_port, TXADRHI, transferred >> 16);
 #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
-	    DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo"));
+	    DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
 #endif
 	}
     }
@@ -1225,66 +1293,88 @@
 /* =========================================================================================
  * Data I/O
  */
+static int
+acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
+{
+	unsigned int asr, timeout = max_timeout;
+	int my_ptr = *ptr;
+
+	while (my_ptr < len) {
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+		if (asr & ASR_DBR) {
+			timeout = max_timeout;
+
+			sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]);
+		} else if (asr & ASR_INT)
+			break;
+		else if (--timeout == 0)
+			break;
+		udelay(1);
+	}
+
+	*ptr = my_ptr;
+
+	return (timeout == 0) ? -1 : 0;
+}
+
 /*
- * Function: void acornscsi_sendcommand (AS_Host *host)
+ * Function: void acornscsi_sendcommand(AS_Host *host)
  * Purpose : send a command to a target
  * Params  : host - host which is connected to target
  */
-static
-void acornscsi_sendcommand (AS_Host *host)
+static void
+acornscsi_sendcommand(AS_Host *host)
 {
     Scsi_Cmnd *SCpnt = host->SCpnt;
-    unsigned int asr;
-    unsigned char *cmdptr, *cmdend;
 
-    sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
-    sbic_arm_writenext (host->scsi.io_port, 0);
-    sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
-
-    cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command;
-    cmdend = SCpnt->cmnd + SCpnt->cmd_len;
-
-    while (cmdptr < cmdend) {
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	if (asr & ASR_DBR)
-	    sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++);
-	else if (asr & ASR_INT)
-	    break;
-    }
-    if (cmdptr >= cmdend)
-	host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd;
+    sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+    sbic_arm_writenext(host->scsi.io_port, 0);
+    sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
+
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+
+    if (acornscsi_write_pio(host, SCpnt->cmnd,
+	(int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
+	printk("scsi%d: timeout while sending command\n", host->host->host_no);
+
     host->scsi.phase = PHASE_COMMAND;
 }
 
 static
-void acornscsi_sendmessage (AS_Host *host)
+void acornscsi_sendmessage(AS_Host *host)
 {
-    unsigned int message_length = msgqueue_msglength (&host->scsi.msgs);
-    int msgnr;
+    unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
+    unsigned int msgnr;
     struct message *msg;
 
 #if (DEBUG & DEBUG_MESSAGES)
-    printk ("scsi%d.%c: sending message ",
-	    host->host->host_no, acornscsi_target (host));
+    printk("scsi%d.%c: sending message ",
+	    host->host->host_no, acornscsi_target(host));
 #endif
 
     switch (message_length) {
     case 0:
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
-	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-	sbic_arm_write (host->scsi.io_port, DATA, NOP);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
+
+	sbic_arm_write(host->scsi.io_port, DATA, NOP);
+
 	host->scsi.last_message = NOP;
 #if (DEBUG & DEBUG_MESSAGES)
-	printk ("NOP");
+	printk("NOP");
 #endif
 	break;
 
     case 1:
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
 	msg = msgqueue_getmsg(&host->scsi.msgs, 0);
-	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-	sbic_arm_write (host->scsi.io_port, DATA, msg->msg[0]);
+
+	acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
+
+	sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]);
+
 	host->scsi.last_message = msg->msg[0];
 #if (DEBUG & DEBUG_MESSAGES)
 	print_msg(msg->msg);
@@ -1300,86 +1390,85 @@
 	 *  initiator.  This provides an interlock so that the
 	 *  initiator can determine which message byte is rejected.
 	 */
-	sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
-	sbic_arm_writenext (host->scsi.io_port, 0);
-	sbic_arm_writenext (host->scsi.io_port, message_length);
-	acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+	sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+	sbic_arm_writenext(host->scsi.io_port, 0);
+	sbic_arm_writenext(host->scsi.io_port, message_length);
+	acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
 
 	msgnr = 0;
 	while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
-	    unsigned int asr, i;
+	    unsigned int i;
 #if (DEBUG & DEBUG_MESSAGES)
-	    print_msg (msg);
+	    print_msg(msg);
 #endif
-	    for (i = 0; i < msg->length;) {
-		asr = sbic_arm_read (host->scsi.io_port, ASR);
-		if (asr & ASR_DBR)
-		    sbic_arm_write (host->scsi.io_port, DATA, msg->msg[i++]);
-		if (asr & ASR_INT)
-		    break;
-	    }
+	    i = 0;
+	    if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
+		printk("scsi%d: timeout while sending message\n", host->host->host_no);
+
 	    host->scsi.last_message = msg->msg[0];
 	    if (msg->msg[0] == EXTENDED_MESSAGE)
 		host->scsi.last_message |= msg->msg[2] << 8;
-	    if (asr & ASR_INT)
+
+	    if (i != msg->length)
 		break;
 	}
 	break;
     }
 #if (DEBUG & DEBUG_MESSAGES)
-    printk ("\n");
+    printk("\n");
 #endif
 }
 
 /*
- * Function: void acornscsi_readstatusbyte (AS_Host *host)
+ * Function: void acornscsi_readstatusbyte(AS_Host *host)
  * Purpose : Read status byte from connected target
  * Params  : host - host connected to target
  */
 static
-void acornscsi_readstatusbyte (AS_Host *host)
+void acornscsi_readstatusbyte(AS_Host *host)
 {
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT);
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-
-    host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
+    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
+    host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA);
 }
 
 /*
- * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host)
+ * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
  * Purpose : Read one message byte from connected target
  * Params  : host - host connected to target
  */
 static
-unsigned char acornscsi_readmessagebyte (AS_Host *host)
+unsigned char acornscsi_readmessagebyte(AS_Host *host)
 {
     unsigned char message;
 
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+    acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
 
-    message = sbic_arm_read (host->scsi.io_port, DATA);
+    message = sbic_arm_read(host->scsi.io_port, DATA);
 
     /* wait for MSGIN-XFER-PAUSED */
-    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
-    sbic_arm_read (host->scsi.io_port, SSR);
+    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
+
+    sbic_arm_read(host->scsi.io_port, SSR);
 
     return message;
 }
 
 /*
- * Function: void acornscsi_message (AS_Host *host)
+ * Function: void acornscsi_message(AS_Host *host)
  * Purpose : Read complete message from connected target & action message
  * Params  : host - host connected to target
  */
 static
-void acornscsi_message (AS_Host *host)
+void acornscsi_message(AS_Host *host)
 {
     unsigned char message[16];
     unsigned int msgidx = 0, msglen = 1;
 
     do {
-	message[msgidx] = acornscsi_readmessagebyte (host);
+	message[msgidx] = acornscsi_readmessagebyte(host);
 
 	switch (msgidx) {
 	case 0:
@@ -1395,17 +1484,17 @@
 	}
 	msgidx += 1;
 	if (msgidx < msglen) {
-	    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+	    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
 
 	    /* wait for next msg-in */
-	    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
-	    sbic_arm_read (host->scsi.io_port, SSR);
+	    acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
+	    sbic_arm_read(host->scsi.io_port, SSR);
 	}
     } while (msgidx < msglen);
 
 #if (DEBUG & DEBUG_MESSAGES)
     printk("scsi%d.%c: message in: ",
-	    host->host->host_no, acornscsi_target (host));
+	    host->host->host_no, acornscsi_target(host));
     print_msg(message);
     printk("\n");
 #endif
@@ -1419,7 +1508,7 @@
 	 */
 	if (message[0] == SIMPLE_QUEUE_TAG)
 	    host->scsi.reconnected.tag = message[1];
-	if (acornscsi_reconnect_finish (host))
+	if (acornscsi_reconnect_finish(host))
 	    host->scsi.phase = PHASE_MSGIN;
     }
 
@@ -1429,7 +1518,7 @@
     case COMMAND_COMPLETE:
 	if (host->scsi.phase != PHASE_STATUSIN) {
 	    printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
-		    host->host->host_no, acornscsi_target (host));
+		    host->host->host_no, acornscsi_target(host));
 	    acornscsi_dumplog(host, host->SCpnt->target);
 	}
 	host->scsi.phase = PHASE_DONE;
@@ -1443,7 +1532,7 @@
 	 *  direct the initiator to copy the active data pointer to
 	 *  the saved data pointer for the current I/O process.
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->SCpnt->SCp = host->scsi.SCp;
 	host->SCpnt->SCp.sent_command = 0;
 	host->scsi.phase = PHASE_MSGIN;
@@ -1459,7 +1548,7 @@
 	 *  status pointers shall be restored to the beginning of
 	 *  the present command and status areas.'
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->scsi.SCp = host->SCpnt->SCp;
 	host->scsi.phase = PHASE_MSGIN;
 	break;
@@ -1474,7 +1563,7 @@
 	 *  message.  When reconnection is completed, the most recent
 	 *  saved pointer values are restored.'
 	 */
-	acornscsi_dma_cleanup (host);
+	acornscsi_dma_cleanup(host);
 	host->scsi.phase = PHASE_DISCONNECT;
 	break;
 
@@ -1493,8 +1582,8 @@
 	/*
 	 * If we have any messages waiting to go out, then assert ATN now
 	 */
-	if (msgqueue_msglength (&host->scsi.msgs))
-	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+	if (msgqueue_msglength(&host->scsi.msgs))
+	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
 
 	switch (host->scsi.last_message) {
 #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
@@ -1507,21 +1596,21 @@
 	     *  message is received, it shall respond with a MESSAGE REJECT
 	     *  message and accept the I/O process as if it were untagged.
 	     */
-	    printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
+		    host->host->host_no, acornscsi_target(host));
 	    host->SCpnt->device->tagged_queue = 0;
-	    set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
+	    set_bit(host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
 	    break;
 #endif
 	case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
 	    /*
 	     * Target can't handle synchronous transfers
 	     */
-	    printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
-		    host->host->host_no, acornscsi_target (host));
+	    printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
+		    host->host->host_no, acornscsi_target(host));
 	    host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA;
 	    host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS;
-	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
 	    break;
 
 	default:
@@ -1535,8 +1624,8 @@
 
     case SIMPLE_QUEUE_TAG:
 	/* tag queue reconnect... message[1] = queue tag.  Print something to indicate something happened! */
-	printk ("scsi%d.%c: reconnect queue tag %02X\n",
-		host->host->host_no, acornscsi_target (host),
+	printk("scsi%d.%c: reconnect queue tag %02X\n",
+		host->host->host_no, acornscsi_target(host),
 		message[1]);
 	break;
 
@@ -1552,26 +1641,26 @@
 		 * and the target retries fail, then we fallback to asynchronous mode
 		 */
 		host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED;
-		printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
+		printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
 			host->host->host_no, acornscsi_target(host),
 			message[4], message[3] * 4);
 		host->device[host->SCpnt->target].sync_xfer =
-			calc_sync_xfer (message[3] * 4, message[4]);
+			calc_sync_xfer(message[3] * 4, message[4]);
 	    } else {
 		unsigned char period, length;
 		/*
 		 * Target requested synchronous transfers.  The agreement is only
 		 * to be in operation AFTER the target leaves message out phase.
 		 */
-		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-		period = max (message[3], sdtr_period / 4);
-		length = min (message[4], sdtr_size);
-		msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
+		acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+		period = max(message[3], sdtr_period / 4);
+		length = min(message[4], sdtr_size);
+		msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
 				 EXTENDED_SDTR, period, length);
 		host->device[host->SCpnt->target].sync_xfer =
-			calc_sync_xfer (period * 4, length);
+			calc_sync_xfer(period * 4, length);
 	    }
-	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
 	    break;
 #else
 	    /* We do not accept synchronous transfers.  Respond with a
@@ -1584,9 +1673,9 @@
 	     * to a wide data transfer request.
 	     */
 	default:
-	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	    msgqueue_flush (&host->scsi.msgs);
-	    msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	    acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+	    msgqueue_flush(&host->scsi.msgs);
+	    msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
 	    break;
 	}
 	break;
@@ -1607,19 +1696,19 @@
 	     * if there are more linked commands available.
 	     */
 	    if (!host->SCpnt->next_link) {
-		printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
-			instance->host_no, acornscsi_target (host), host->SCpnt->tag);
-		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-		msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+		printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
+			instance->host_no, acornscsi_target(host), host->SCpnt->tag);
+		acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+		msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
 	    } else {
 		Scsi_Cmnd *SCpnt = host->SCpnt;
 
-		acornscsi_dma_cleanup (host);
+		acornscsi_dma_cleanup(host);
 
 		host->SCpnt = host->SCpnt->next_link;
 		host->SCpnt->tag = SCpnt->tag;
 		SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
-		SCpnt->done (SCpnt);
+		SCpnt->done(SCpnt);
 
 		/* initialise host->SCpnt->SCp */
 	    }
@@ -1628,42 +1717,42 @@
 #endif
 
     default: /* reject message */
-	printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
-		host->host->host_no, acornscsi_target (host),
+	printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
+		host->host->host_no, acornscsi_target(host),
 		message[0]);
-	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	msgqueue_flush (&host->scsi.msgs);
-	msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+	msgqueue_flush(&host->scsi.msgs);
+	msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
 	host->scsi.phase = PHASE_MSGIN;
 	break;
     }
-    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
 }
 
 /*
- * Function: int acornscsi_buildmessages (AS_Host *host)
+ * Function: int acornscsi_buildmessages(AS_Host *host)
  * Purpose : build the connection messages for a host
  * Params  : host - host to add messages to
  */
 static
-void acornscsi_buildmessages (AS_Host *host)
+void acornscsi_buildmessages(AS_Host *host)
 {
 #if 0
     /* does the device need resetting? */
     if (cmd_reset) {
-	msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET);
+	msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
 	return;
     }
 #endif
 
-    msgqueue_addmsg (&host->scsi.msgs, 1,
+    msgqueue_addmsg(&host->scsi.msgs, 1,
 		     IDENTIFY(host->device[host->SCpnt->target].disconnect_ok,
 			     host->SCpnt->lun));
 
 #if 0
     /* does the device need the current command aborted */
     if (cmd_aborted) {
-	acornscsi_abortcmd (host->SCpnt->tag);
+	acornscsi_abortcmd(host->SCpnt->tag);
 	return;
     }
 #endif
@@ -1678,14 +1767,14 @@
 	    tag_type = HEAD_OF_QUEUE_TAG;
 	else
 	    tag_type = SIMPLE_QUEUE_TAG;
-	msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
+	msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
     }
 #endif
 
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) {
 	host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST;
-	msgqueue_addmsg (&host->scsi.msgs, 5,
+	msgqueue_addmsg(&host->scsi.msgs, 5,
 			 EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
 			 sdtr_period / 4, sdtr_size);
     }
@@ -1693,29 +1782,29 @@
 }
 
 /*
- * Function: int acornscsi_starttransfer (AS_Host *host)
+ * Function: int acornscsi_starttransfer(AS_Host *host)
  * Purpose : transfer data to/from connected target
  * Params  : host - host to which target is connected
  * Returns : 0 if failure
  */
 static
-int acornscsi_starttransfer (AS_Host *host)
+int acornscsi_starttransfer(AS_Host *host)
 {
     int residual;
 
     if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
-	printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
-		host->host->host_no, acornscsi_target (host));
+	printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
+		host->host->host_no, acornscsi_target(host));
 	return 0;
     }
 
     residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred;
 
-    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
-    sbic_arm_writenext (host->scsi.io_port, residual >> 16);
-    sbic_arm_writenext (host->scsi.io_port, residual >> 8);
-    sbic_arm_writenext (host->scsi.io_port, residual);
-    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+    sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+    sbic_arm_writenext(host->scsi.io_port, residual >> 16);
+    sbic_arm_writenext(host->scsi.io_port, residual >> 8);
+    sbic_arm_writenext(host->scsi.io_port, residual);
+    acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
     return 1;
 }
 
@@ -1723,7 +1812,7 @@
  * Connection & Disconnection
  */
 /*
- * Function : acornscsi_reconnect (AS_Host *host)
+ * Function : acornscsi_reconnect(AS_Host *host)
  * Purpose  : reconnect a previously disconnected command
  * Params   : host - host specific data
  * Remarks  : SCSI spec says:
@@ -1731,27 +1820,27 @@
  *		 of saved pointers upon reconnection of the I/O process'
  */
 static
-int acornscsi_reconnect (AS_Host *host)
+int acornscsi_reconnect(AS_Host *host)
 {
     unsigned int target, lun, ok = 0;
 
-    target = sbic_arm_read (host->scsi.io_port, SOURCEID);
+    target = sbic_arm_read(host->scsi.io_port, SOURCEID);
 
     if (!(target & 8))
-	printk (KERN_ERR "scsi%d: invalid source id after reselection "
+	printk(KERN_ERR "scsi%d: invalid source id after reselection "
 		"- device fault?\n",
 		host->host->host_no);
 
     target &= 7;
 
     if (host->SCpnt && !host->scsi.disconnectable) {
-	printk (KERN_ERR "scsi%d.%d: reconnected while command in "
+	printk(KERN_ERR "scsi%d.%d: reconnected while command in "
 		"progress to target %d?\n",
 		host->host->host_no, target, host->SCpnt->target);
 	host->SCpnt = NULL;
     }
 
-    lun = sbic_arm_read (host->scsi.io_port, DATA) & 7;
+    lun = sbic_arm_read(host->scsi.io_port, DATA) & 7;
 
     host->scsi.reconnected.target = target;
     host->scsi.reconnected.lun = lun;
@@ -1761,7 +1850,7 @@
 	host->SCpnt->target == target && host->SCpnt->lun == lun)
 	ok = 1;
 
-    if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun))
+    if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
 	ok = 1;
 
     ADD_STATUS(target, 0x81, host->scsi.phase, 0);
@@ -1770,26 +1859,28 @@
 	host->scsi.phase = PHASE_RECONNECTED;
     } else {
 	/* this doesn't seem to work */
-	printk (KERN_ERR "scsi%d.%c: reselected with no command "
+	printk(KERN_ERR "scsi%d.%c: reselected with no command "
 		"to reconnect with\n",
 		host->host->host_no, '0' + target);
-	acornscsi_dumplog (host, target);
-	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
-	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
-	host->scsi.phase = PHASE_ABORTED;
+	acornscsi_dumplog(host, target);
+	acornscsi_abortcmd(host, 0);
+	if (host->SCpnt) {
+	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+	    host->SCpnt = NULL;
+	}
     }
-    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+    acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
     return !ok;
 }
 
 /*
- * Function: int acornscsi_reconect_finish (AS_Host *host)
+ * Function: int acornscsi_reconect_finish(AS_Host *host)
  * Purpose : finish reconnecting a command
  * Params  : host - host to complete
  * Returns : 0 if failed
  */
 static
-int acornscsi_reconnect_finish (AS_Host *host)
+int acornscsi_reconnect_finish(AS_Host *host)
 {
     if (host->scsi.disconnectable && host->SCpnt) {
 	host->scsi.disconnectable = 0;
@@ -1797,45 +1888,44 @@
 	    host->SCpnt->lun	== host->scsi.reconnected.lun &&
 	    host->SCpnt->tag	== host->scsi.reconnected.tag) {
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	    DBG(host->SCpnt, printk ("scsi%d.%c: reconnected",
-		    host->host->host_no, acornscsi_target (host)));
+	    DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
+		    host->host->host_no, acornscsi_target(host)));
 #endif
 	} else {
-	    queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+	    queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	    DBG(host->SCpnt, printk ("scsi%d.%c: had to move command "
+	    DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
 		    "to disconnected queue\n",
-		    host->host->host_no, acornscsi_target (host)));
+		    host->host->host_no, acornscsi_target(host)));
 #endif
 	    host->SCpnt = NULL;
 	}
     }
     if (!host->SCpnt) {
-	host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected,
+	host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
 				host->scsi.reconnected.target,
 				host->scsi.reconnected.lun,
 				host->scsi.reconnected.tag);
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	DBG(host->SCpnt, printk ("scsi%d.%c: had to get command",
-		host->host->host_no, acornscsi_target (host)));
+	DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
+		host->host->host_no, acornscsi_target(host)));
 #endif
     }
 
-    if (!host->SCpnt) {
-	acornscsi_abortcmd (host, host->scsi.reconnected.tag);
-	host->scsi.phase = PHASE_ABORTED;
-    } else {
+    if (!host->SCpnt)
+	acornscsi_abortcmd(host, host->scsi.reconnected.tag);
+    else {
 	/*
 	 * Restore data pointer from SAVED pointers.
 	 */
 	host->scsi.SCp = host->SCpnt->SCp;
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-	printk (", data pointers: [%p, %X]",
+	printk(", data pointers: [%p, %X]",
 		host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
 #endif
     }
 #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
-    printk ("\n");
+    printk("\n");
 #endif
 
     host->dma.transferred = host->scsi.SCp.scsi_xferred;
@@ -1844,47 +1934,48 @@
 }
 
 /*
- * Function: void acornscsi_disconnect_unexpected (AS_Host *host)
+ * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
  * Purpose : handle an unexpected disconnect
  * Params  : host - host on which disconnect occurred
  */
 static
-void acornscsi_disconnect_unexpected (AS_Host *host)
+void acornscsi_disconnect_unexpected(AS_Host *host)
 {
-    printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n",
-	    host->host->host_no, acornscsi_target (host));
+    printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
+	    host->host->host_no, acornscsi_target(host));
 #if (DEBUG & DEBUG_ABORT)
-    acornscsi_dumplog (host, 8);
+    acornscsi_dumplog(host, 8);
 #endif
 
-    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 }
 
 /*
- * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag)
+ * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
  * Purpose : abort a currently executing command
  * Params  : host - host with connected command to abort
  *	     tag  - tag to abort
  */
 static
-void acornscsi_abortcmd (AS_Host *host, unsigned char tag)
+void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
 {
-    sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN);
+    host->scsi.phase = PHASE_ABORTED;
+    sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN);
 
-    msgqueue_flush (&host->scsi.msgs);
+    msgqueue_flush(&host->scsi.msgs);
 #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
     if (tag)
-	msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag);
+	msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
     else
 #endif
-	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+	msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
 }
 
 /* ==========================================================================================
  * Interrupt routines.
  */
 /*
- * Function: int acornscsi_sbicintr (AS_Host *host)
+ * Function: int acornscsi_sbicintr(AS_Host *host)
  * Purpose : handle interrupts from SCSI device
  * Params  : host - host to process
  * Returns : INTR_PROCESS if expecting another SBIC interrupt
@@ -1892,15 +1983,15 @@
  *	     INTR_NEXT_COMMAND if we have finished processing the command
  */
 static
-intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq)
+intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
 {
     unsigned int asr, ssr;
 
-    asr = sbic_arm_read (host->scsi.io_port, ASR);
+    asr = sbic_arm_read(host->scsi.io_port, ASR);
     if (!(asr & ASR_INT))
 	return INTR_IDLE;
 
-    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+    ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
 #if (DEBUG & DEBUG_PHASES)
     print_sbic_status(asr, ssr, host->scsi.phase);
@@ -1913,23 +2004,23 @@
 
     switch (ssr) {
     case 0x00:				/* reset state - not advanced			*/
-	printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
+	printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
 		host->host->host_no);
 	/* setup sbic - WD33C93A */
-	sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
-	sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+	sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+	sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
 	return INTR_IDLE;
 
     case 0x01:				/* reset state - advanced			*/
-	sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
-	sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
-	sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
-	sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
-	msgqueue_flush (&host->scsi.msgs);
+	sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+	sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+	sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+	sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+	msgqueue_flush(&host->scsi.msgs);
 	return INTR_IDLE;
 
     case 0x41:				/* unexpected disconnect aborted command	*/
-	acornscsi_disconnect_unexpected (host);
+	acornscsi_disconnect_unexpected(host);
 	return INTR_NEXT_COMMAND;
     }
 
@@ -1939,35 +2030,35 @@
 	case 0x11:			/* -> PHASE_CONNECTED				*/
 	    /* BUS FREE -> SELECTION */
 	    host->scsi.phase = PHASE_CONNECTED;
-	    msgqueue_flush (&host->scsi.msgs);
+	    msgqueue_flush(&host->scsi.msgs);
 	    host->dma.transferred = host->scsi.SCp.scsi_xferred;
 	    /* 33C93 gives next interrupt indicating bus phase */
-	    asr = sbic_arm_read (host->scsi.io_port, ASR);
+	    asr = sbic_arm_read(host->scsi.io_port, ASR);
 	    if (!(asr & ASR_INT))
 		break;
-	    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	    ssr = sbic_arm_read(host->scsi.io_port, SSR);
 	    ADD_STATUS(8, ssr, host->scsi.phase, 1);
 	    ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1);
 	    goto connected;
 	    
 	case 0x42:			/* select timed out				*/
 					/* -> PHASE_IDLE				*/
-	    acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT);
+	    acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
 	    return INTR_NEXT_COMMAND;
 
 	case 0x81:			/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
 	    /* BUS FREE -> RESELECTION */
 	    host->origSCpnt = host->SCpnt;
 	    host->SCpnt = NULL;
-	    msgqueue_flush (&host->scsi.msgs);
-	    acornscsi_reconnect (host);
+	    msgqueue_flush(&host->scsi.msgs);
+	    acornscsi_reconnect(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	}
 	return INTR_PROCESSING;
 
@@ -1977,12 +2068,12 @@
 #ifdef NONSTANDARD
 	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    /* SELECTION -> COMMAND */
-	    acornscsi_sendcommand (host);
+	    acornscsi_sendcommand(host);
 	    break;
 
 	case 0x8b:			/* -> PHASE_STATUS				*/
 	    /* SELECTION -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 #endif
@@ -1990,55 +2081,57 @@
 	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* SELECTION ->MESSAGE OUT */
 	    host->scsi.phase = PHASE_MSGOUT;
-	    acornscsi_buildmessages (host);
-	    acornscsi_sendmessage (host);
+	    acornscsi_buildmessages(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	/* these should not happen */
 	case 0x85:			/* target disconnected				*/
-	    acornscsi_done (host, &host->SCpnt, DID_ERROR);
+	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_MSGOUT:			/* STATE: connected & sent IDENTIFY message	*/
 	/*
-	 * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase
+	 * SCSI standard says that MESSAGE OUT phases can be followed by a
+	 * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
 	 */
 	switch (ssr) {
-	case 0x8a:
+	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	case 0x1a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    /* MESSAGE OUT -> COMMAND */
-	    acornscsi_sendcommand (host);
+	    acornscsi_sendcommand(host);
 	    break;
 
+	case 0x8b:			/* -> PHASE_STATUS				*/
 	case 0x1b:			/* -> PHASE_STATUS				*/
 	    /* MESSAGE OUT -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
-        case 0x4f:
+	case 0x4f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    /* MESSAGE OUT -> MESSAGE IN */
-	    acornscsi_message (host);
+	    acornscsi_message(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2047,43 +2140,43 @@
 	case 0x18:			/* -> PHASE_DATAOUT				*/
 	    /* COMMAND -> DATA OUT */
 	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
-		acornscsi_abortcmd (host, host->SCpnt->tag);
-	    acornscsi_dma_setup (host, DMA_OUT);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+		acornscsi_abortcmd(host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_OUT);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAOUT;
 	    return INTR_IDLE;
 
 	case 0x19:			/* -> PHASE_DATAIN				*/
 	    /* COMMAND -> DATA IN */
 	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
-		acornscsi_abortcmd (host, host->SCpnt->tag);
-	    acornscsi_dma_setup (host, DMA_IN);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+		acornscsi_abortcmd(host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_IN);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAIN;
 	    return INTR_IDLE;
 
 	case 0x1b:			/* -> PHASE_STATUS				*/
 	    /* COMMAND -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	    /* COMMAND -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    /* COMMAND -> MESSAGE IN */
-	    acornscsi_message (host);
+	    acornscsi_message(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2094,19 +2187,19 @@
 	    host->scsi.phase = PHASE_IDLE;
 	    host->stats.disconnects += 1;
 	} else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_NEXT_COMMAND;
 
     case PHASE_IDLE:			/* STATE: disconnected				*/
 	if (ssr == 0x81)		/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
-	    acornscsi_reconnect (host);
+	    acornscsi_reconnect(host);
 	else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2119,54 +2212,54 @@
 	 * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
 	 * reconnect I_T_L command
 	 */
-	if (ssr != 0x8f && !acornscsi_reconnect_finish (host))
+	if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
 	    return INTR_IDLE;
 	ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq);
 	switch (ssr) {
 	case 0x88:			/* data out phase				*/
 					/* -> PHASE_DATAOUT				*/
 	    /* MESSAGE IN -> DATA OUT */
-	    acornscsi_dma_setup (host, DMA_OUT);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_OUT);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAOUT;
 	    return INTR_IDLE;
 
 	case 0x89:			/* data in phase				*/
 					/* -> PHASE_DATAIN				*/
 	    /* MESSAGE IN -> DATA IN */
-	    acornscsi_dma_setup (host, DMA_IN);
-	    if (!acornscsi_starttransfer (host))
-		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup(host, DMA_IN);
+	    if (!acornscsi_starttransfer(host))
+		acornscsi_abortcmd(host, host->SCpnt->tag);
 	    host->scsi.phase = PHASE_DATAIN;
 	    return INTR_IDLE;
 
 	case 0x8a:			/* command out					*/
 	    /* MESSAGE IN -> COMMAND */
-	    acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
+	    acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
 	    break;
 
 	case 0x8b:			/* status in					*/
 					/* -> PHASE_STATUSIN				*/
 	    /* MESSAGE IN -> STATUS */
-	    acornscsi_readstatusbyte (host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x8e:			/* message out					*/
 					/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE IN -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x8f:			/* message in					*/
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2177,41 +2270,45 @@
 	 */
 	switch (ssr) {
 	case 0x19:			/* -> PHASE_DATAIN				*/
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	case 0x89:			/* -> PHASE_DATAIN				*/
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	    return INTR_IDLE;
 
-	case 0x4b:			/* -> PHASE_STATUSIN				*/
 	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x8b:			/* -> PHASE_STATUSIN				*/
 	    /* DATA IN -> STATUS */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_readstatusbyte (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* DATA IN -> MESSAGE OUT */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_sendmessage (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* message in					*/
 	case 0x4f:			/* message in					*/
+	case 0x8f:			/* message in					*/
 	    /* DATA IN -> MESSAGE IN */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2222,58 +2319,69 @@
 	 */
 	switch (ssr) {
 	case 0x18:			/* -> PHASE_DATAOUT				*/
-	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	case 0x88:			/* -> PHASE_DATAOUT				*/
+	    acornscsi_abortcmd(host, host->SCpnt->tag);
 	    return INTR_IDLE;
 
-	case 0x4b:			/* -> PHASE_STATUSIN				*/
 	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x8b:			/* -> PHASE_STATUSIN				*/
 	    /* DATA OUT -> STATUS */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_readstatusbyte (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_readstatusbyte(host);
 	    host->scsi.phase = PHASE_STATUSIN;
 	    break;
 
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* DATA OUT -> MESSAGE OUT */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_sendmessage (host);
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* message in					*/
 	case 0x4f:			/* message in					*/
+	case 0x8f:			/* message in					*/
 	    /* DATA OUT -> MESSAGE IN */
 	    host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
-					  acornscsi_sbic_xfcount (host);
-	    acornscsi_dma_stop (host);
-	    acornscsi_dma_adjust (host);
-	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+					  acornscsi_sbic_xfcount(host);
+	    acornscsi_dma_stop(host);
+	    acornscsi_dma_adjust(host);
+	    acornscsi_message(host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_STATUSIN:		/* STATE: status in complete			*/
-	if (ssr == 0x1f)		/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	switch (ssr) {
+	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	case 0x8f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
 	    /* STATUS -> MESSAGE IN */
-	    acornscsi_message (host);
-	else if (ssr == 0x1e)		/* -> PHASE_MSGOUT				*/
+	    acornscsi_message(host);
+	    break;
+
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* STATUS -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
-	else {
-	    printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_sendmessage(host);
+	    break;
+
+	default:
+	    printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
@@ -2281,78 +2389,93 @@
 	switch (ssr) {
 	case 0x1e:			/* -> PHASE_MSGOUT				*/
 	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
 	    /* MESSAGE IN -> MESSAGE OUT */
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
 	case 0x2f:
 	case 0x4f:
 	case 0x8f:
-	    acornscsi_message (host);
+	    acornscsi_message(host);
+	    break;
+
+	case 0x85:
+	    printk("scsi%d.%c: strange message in disconnection\n",
+		host->host->host_no, acornscsi_target(host));
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_done(host, &host->SCpnt, DID_ERROR);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_DONE:			/* STATE: received status & message		*/
 	switch (ssr) {
 	case 0x85:			/* -> PHASE_IDLE				*/
-	    acornscsi_done (host, &host->SCpnt, DID_OK);
+	    acornscsi_done(host, &host->SCpnt, DID_OK);
 	    return INTR_NEXT_COMMAND;
 
+	case 0x1e:
 	case 0x8e:
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     case PHASE_ABORTED:
 	switch (ssr) {
 	case 0x85:
-	    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+	    if (host->SCpnt)
+		acornscsi_done(host, &host->SCpnt, DID_ABORT);
+	    else {
+		clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
+			  host->busyluns);
+		host->scsi.phase = PHASE_IDLE;
+	    }
 	    return INTR_NEXT_COMMAND;
 
 	case 0x1e:
 	case 0x2e:
 	case 0x4e:
 	case 0x8e:
-	    acornscsi_sendmessage (host);
+	    acornscsi_sendmessage(host);
 	    break;
 
 	default:
-	    printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
-		    host->host->host_no, acornscsi_target (host), ssr);
-	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target(host), ssr);
+	    acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
 	}
 	return INTR_PROCESSING;
 
     default:
-	printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
-		host->host->host_no, acornscsi_target (host), ssr);
-	acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
+		host->host->host_no, acornscsi_target(host), ssr);
+	acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
     }
     return INTR_PROCESSING;
 }
 
 /*
- * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+ * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
  * Purpose  : handle interrupts from Acorn SCSI card
  * Params   : irq    - interrupt number
  *	      dev_id - device specific data (AS_Host structure)
  *	      regs   - processor registers when interrupt occurred
  */
 static
-void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
     AS_Host *host = (AS_Host *)dev_id;
     intr_ret_t ret;
@@ -2360,21 +2483,21 @@
     int in_irq = 0;
 
     if (host->scsi.interrupt)
-	printk ("scsi%d: interrupt re-entered\n", host->host->host_no);
+	printk("scsi%d: interrupt re-entered\n", host->host->host_no);
     host->scsi.interrupt = 1;
 
     do {
 	ret = INTR_IDLE;
 
-	iostatus = inb (host->card.io_intr);
+	iostatus = inb(host->card.io_intr);
 
 	if (iostatus & 2) {
-	    acornscsi_dma_intr (host);
-	    iostatus = inb (host->card.io_intr);
+	    acornscsi_dma_intr(host);
+	    iostatus = inb(host->card.io_intr);
 	}
 
 	if (iostatus & 8)
-	    ret = acornscsi_sbicintr (host, in_irq);
+	    ret = acornscsi_sbicintr(host, in_irq);
 
 	/*
 	 * If we have a transfer pending, start it.
@@ -2382,10 +2505,10 @@
 	 * it's data
 	 */
 	if (host->dma.xfer_required)
-	    acornscsi_dma_xfer (host);
+	    acornscsi_dma_xfer(host);
 
 	if (ret == INTR_NEXT_COMMAND)
-	    ret = acornscsi_kick (host);
+	    ret = acornscsi_kick(host);
 
 	in_irq = 1;
     } while (ret != INTR_IDLE);
@@ -2398,29 +2521,29 @@
  */
 
 /*
- * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
  * Purpose  : queues a SCSI command
  * Params   : cmd  - SCSI command
  *	      done - function called on completion, with pointer to command descriptor
  * Returns  : 0, or < 0 on error.
  */
-int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
     AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
 
     if (!done) {
 	/* there should be some way of rejecting errors like this without panicing... */
-	panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]",
+	panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
 		SCpnt->host->host_no, SCpnt);
 	return -EINVAL;
     }
 
 #if (DEBUG & DEBUG_NO_WRITE)
-    if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
-	printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
+    if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
+	printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
 	    SCpnt->host->host_no, '0' + SCpnt->target);
 	SCpnt->result = DID_NO_CONNECT << 16;
-	done (SCpnt);
+	done(SCpnt);
 	return 0;
     }
 #endif
@@ -2429,7 +2552,7 @@
     SCpnt->host_scribble = NULL;
     SCpnt->result = 0;
     SCpnt->tag = 0;
-    SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]);
+    SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
     SCpnt->SCp.sent_command = 0;
     SCpnt->SCp.scsi_xferred = 0;
     SCpnt->SCp.Status = 0;
@@ -2452,21 +2575,21 @@
     {
 	unsigned long flags;
 
-	if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) {
+	if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
 	    SCpnt->result = DID_ERROR << 16;
-	    done (SCpnt);
+	    done(SCpnt);
 	    return 0;
 	}
-	save_flags_cli (flags);
+	save_flags_cli(flags);
 	if (host->scsi.phase == PHASE_IDLE)
-	    acornscsi_kick (host);
-	restore_flags (flags);
+	    acornscsi_kick(host);
+	restore_flags(flags);
     }
     return 0;
 }
 
 /*
- * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+ * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
  * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
  * Params   : SCpntp1 - pointer to command to return
  *	      SCpntp2 - pointer to command to check
@@ -2474,7 +2597,7 @@
  * Returns  : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
  */
 static inline
-void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
 {
     Scsi_Cmnd *SCpnt = *SCpntp1;
 
@@ -2482,80 +2605,203 @@
 	*SCpntp1 = NULL;
 
 	SCpnt->result = result;
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     }
 
     if (SCpnt == *SCpntp2)
 	*SCpntp2 = NULL;
 }
 
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt)
+ * Purpose  : abort a command on this host
+ * Params   : SCpnt - command to abort
+ * Returns  : our abort status
+ */
+static enum res_abort
+acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt)
+{
+	enum res_abort res = res_not_running;
+
+	if (queue_removecmd(&host->queues.issue, SCpnt)) {
+		/*
+		 * The command was on the issue queue, and has not been
+		 * issued yet.  We can remove the command from the queue,
+		 * and acknowledge the abort.  Neither the devices nor the
+		 * interface know about the command.
+		 */
+//#if (DEBUG & DEBUG_ABORT)
+		printk("on issue queue ");
+//#endif
+		res = res_success;
+	} else if (queue_removecmd(&host->queues.disconnected, SCpnt)) {
+		/*
+		 * The command was on the disconnected queue.  Simply
+		 * acknowledge the abort condition, and when the target
+		 * reconnects, we will give it an ABORT message.  The
+		 * target should then disconnect, and we will clear
+		 * the busylun bit.
+		 */
+//#if (DEBUG & DEBUG_ABORT)
+		printk("on disconnected queue ");
+//#endif
+		res = res_success;
+	} else if (host->SCpnt == SCpnt) {
+		unsigned long flags;
+
+//#if (DEBUG & DEBUG_ABORT)
+		printk("executing ");
+//#endif
+
+		save_flags(flags);
+		cli();
+		switch (host->scsi.phase) {
+		/*
+		 * If the interface is idle, and the command is 'disconnectable',
+		 * then it is the same as on the disconnected queue.  We simply
+		 * remove all traces of the command.  When the target reconnects,
+		 * we will give it an ABORT message since the command could not
+		 * be found.  When the target finally disconnects, we will clear
+		 * the busylun bit.
+		 */
+		case PHASE_IDLE:
+			if (host->scsi.disconnectable) {
+				host->scsi.disconnectable = 0;
+				host->SCpnt = NULL;
+				res = res_success;
+			}
+			break;
+
+		/*
+		 * If the command has connected and done nothing further,
+		 * simply force a disconnect.  We also need to clear the
+		 * busylun bit.
+		 */
+		case PHASE_CONNECTED:
+			sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT);
+			host->SCpnt = NULL;
+			res = res_success_clear;
+			break;
+
+		default:
+			acornscsi_abortcmd(host, host->SCpnt->tag);
+			res = res_snooze;
+		}
+		restore_flags(flags);
+	} else if (host->origSCpnt == SCpnt) {
+		/*
+		 * The command will be executed next, but a command
+		 * is currently using the interface.  This is similar to
+		 * being on the issue queue, except the busylun bit has
+		 * been set.
+		 */
+		host->origSCpnt = NULL;
+//#if (DEBUG & DEBUG_ABORT)
+		printk("waiting for execution ");
+//#endif
+		res = res_success_clear;
+	} else
+		printk("unknown ");
+
+	return res;
+}
+
 /*
- * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt)
+ * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt)
  * Purpose  : abort a command on this host
  * Params   : SCpnt - command to abort
  * Returns  : one of SCSI_ABORT_ macros
  */
-int acornscsi_abort (Scsi_Cmnd *SCpnt)
+int acornscsi_abort(Scsi_Cmnd *SCpnt)
 {
-    AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
-    int result = SCSI_ABORT_NOT_RUNNING;
+	AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
+	int result;
 
-    host->stats.aborts += 1;
+	host->stats.aborts += 1;
 
 #if (DEBUG & DEBUG_ABORT)
-    {
-	int asr, ssr;
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	{
+		int asr, ssr;
+		asr = sbic_arm_read(host->scsi.io_port, ASR);
+		ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
-	printk (KERN_WARNING "acornscsi_abort: ");
-	print_sbic_status(asr, ssr, host->scsi.phase);
-	acornscsi_dumplog (host, SCpnt->target);
-    }
+		printk(KERN_WARNING "acornscsi_abort: ");
+		print_sbic_status(asr, ssr, host->scsi.phase);
+		acornscsi_dumplog(host, SCpnt->target);
+	}
 #endif
 
-    if (queue_removecmd (&host->queues.issue, SCpnt)) {
-	SCpnt->result = DID_ABORT << 16;
-	SCpnt->scsi_done (SCpnt);
-#if (DEBUG & DEBUG_ABORT)
-	printk ("scsi%d: command on issue queue\n", host->host->host_no);
-#endif
-	result = SCSI_ABORT_SUCCESS;
-    } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) {
-	printk ("scsi%d: command on disconnected queue\n", host->host->host_no);
-	result = SCSI_ABORT_SNOOZE;
-    } else if (host->SCpnt == SCpnt) {
-	acornscsi_abortcmd (host, host->SCpnt->tag);
-	printk ("scsi%d: command executing\n", host->host->host_no);
-	result = SCSI_ABORT_SNOOZE;
-    } else if (host->origSCpnt == SCpnt) {
-	host->origSCpnt = NULL;
-	SCpnt->result = DID_ABORT << 16;
-	SCpnt->scsi_done (SCpnt);
-#if (DEBUG & DEBUG_ABORT)
-	printk ("scsi%d: command waiting for execution\n", host->host->host_no);
-#endif
-	result = SCSI_ABORT_SUCCESS;
-    }
+	printk("scsi%d: ", host->host->host_no);
+
+	switch (acornscsi_do_abort(host, SCpnt)) {
+	/*
+	 * We managed to find the command and cleared it out.
+	 * We do not expect the command to be executing on the
+	 * target, but we have set the busylun bit.
+	 */
+	case res_success_clear:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("clear ");
+//#endif
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+
+	/*
+	 * We found the command, and cleared it out.  Either
+	 * the command is still known to be executing on the
+	 * target, or the busylun bit is not set.
+	 */
+	case res_success:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("success\n");
+//#endif
+		SCpnt->result = DID_ABORT << 16;
+		SCpnt->scsi_done(SCpnt);
+		result = SCSI_ABORT_SUCCESS;
+		break;
 
-    if (result == SCSI_ABORT_NOT_RUNNING) {
-	printk ("scsi%d: abort(): command not running\n", host->host->host_no);
-	acornscsi_dumplog (host, SCpnt->target);
+	/*
+	 * We did find the command, but unfortunately we couldn't
+	 * unhook it from ourselves.  Wait some more, and if it
+	 * still doesn't complete, reset the interface.
+	 */
+	case res_snooze:
+//#if (DEBUG & DEBUG_ABORT)
+		printk("snooze\n");
+//#endif
+		result = SCSI_ABORT_SNOOZE;
+		break;
+
+	/*
+	 * The command could not be found (either because it completed,
+	 * or it got dropped.
+	 */
+	default:
+	case res_not_running:
+		acornscsi_dumplog(host, SCpnt->target);
 #if (DEBUG & DEBUG_ABORT)
-	result = SCSI_ABORT_SNOOZE;
+		result = SCSI_ABORT_SNOOZE;
+#else
+		result = SCSI_ABORT_NOT_RUNNING;
 #endif
-    }
-    return result;
+//#if (DEBUG & DEBUG_ABORT)
+		printk("not running\n");
+//#endif
+		break;
+	}
+
+	return result;
 }
 
 /*
- * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
  * Purpose  : reset a command on this host/reset this host
  * Params   : SCpnt  - command causing reset
  *	      result - what type of reset to perform
  * Returns  : one of SCSI_RESET_ macros
  */
-int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
 {
     AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
     Scsi_Cmnd *SCptr;
@@ -2566,16 +2812,16 @@
     {
 	int asr, ssr;
 
-	asr = sbic_arm_read (host->scsi.io_port, ASR);
-	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	asr = sbic_arm_read(host->scsi.io_port, ASR);
+	ssr = sbic_arm_read(host->scsi.io_port, SSR);
 
-	printk (KERN_WARNING "acornscsi_reset: ");
+	printk(KERN_WARNING "acornscsi_reset: ");
 	print_sbic_status(asr, ssr, host->scsi.phase);
-	acornscsi_dumplog (host, SCpnt->target);
+	acornscsi_dumplog(host, SCpnt->target);
     }
 #endif
 
-    acornscsi_dma_stop (host);
+    acornscsi_dma_stop(host);
 
     SCptr = host->SCpnt;
 
@@ -2583,19 +2829,19 @@
      * do hard reset.  This resets all devices on this host, and so we
      * must set the reset status on all commands.
      */
-    acornscsi_resetcard (host);
+    acornscsi_resetcard(host);
 
     /*
      * report reset on commands current connected/disconnected
      */
-    acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET);
+    acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET);
 
-    while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL)
-	acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET);
+    while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
+	acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET);
 
     if (SCpnt) {
 	SCpnt->result = DID_RESET << 16;
-	SCpnt->scsi_done (SCpnt);
+	SCpnt->scsi_done(SCpnt);
     }
 
     return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
@@ -2607,19 +2853,19 @@
 static struct expansion_card *ecs[MAX_ECARDS];
 
 /*
- * Prototype: void acornscsi_init (AS_Host *host)
+ * Prototype: void acornscsi_init(AS_Host *host)
  * Purpose  : initialise the AS_Host structure for one interface & setup hardware
  * Params   : host - host to setup
  */
 static
-void acornscsi_init (AS_Host *host)
+void acornscsi_init(AS_Host *host)
 {
-    memset (&host->stats, 0, sizeof (host->stats));
-    queue_initialise (&host->queues.issue);
-    queue_initialise (&host->queues.disconnected);
-    msgqueue_initialise (&host->scsi.msgs);
+    memset(&host->stats, 0, sizeof (host->stats));
+    queue_initialise(&host->queues.issue);
+    queue_initialise(&host->queues.disconnected);
+    msgqueue_initialise(&host->scsi.msgs);
 
-    acornscsi_resetcard (host);
+    acornscsi_resetcard(host);
 }
 
 int acornscsi_detect(Scsi_Host_Template * tpnt)
@@ -2634,7 +2880,7 @@
     for (i = 0; i < MAX_ECARDS; i++)
 	ecs[i] = NULL;
 
-    ecard_startfind ();
+    ecard_startfind();
 
     while(1) {
 	ecs[count] = ecard_find(0, acornscsi_cids);
@@ -2642,37 +2888,37 @@
 	    break;
 
 	if (ecs[count]->irq == 0xff) {
-	    printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
+	    printk("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
 	    continue;
 	}
 
 	ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
 
-	instance = scsi_register (tpnt, sizeof(AS_Host));
+	instance = scsi_register(tpnt, sizeof(AS_Host));
 	host = (AS_Host *)instance->hostdata;
 
-	instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
+	instance->io_port = ecard_address(ecs[count], ECARD_MEMC, 0);
 	instance->irq = ecs[count]->irq;
 
 	host->host		= instance;
-	host->scsi.io_port	= ioaddr (instance->io_port + 0x800);
+	host->scsi.io_port	= ioaddr(instance->io_port + 0x800);
 	host->scsi.irq		= instance->irq;
 	host->card.io_intr	= POD_SPACE(instance->io_port) + 0x800;
 	host->card.io_page	= POD_SPACE(instance->io_port) + 0xc00;
-	host->card.io_ram	= ioaddr (instance->io_port);
+	host->card.io_ram	= ioaddr(instance->io_port);
 	host->dma.io_port	= instance->io_port + 0xc00;
 	host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800;
 
 	ecs[count]->irqaddr	= (char *)ioaddr(host->card.io_intr);
 	ecs[count]->irqmask	= 0x0a;
 
-	request_region (instance->io_port + 0x800,  2, "acornscsi(sbic)");
-	request_region (host->card.io_intr,  1, "acornscsi(intr)");
-	request_region (host->card.io_page,  1, "acornscsi(page)");
+	request_region(instance->io_port + 0x800,  2, "acornscsi(sbic)");
+	request_region(host->card.io_intr,  1, "acornscsi(intr)");
+	request_region(host->card.io_page,  1, "acornscsi(page)");
 #ifdef USE_DMAC
-	request_region (host->dma.io_port, 256, "acornscsi(dmac)");
+	request_region(host->dma.io_port, 256, "acornscsi(dmac)");
 #endif
-	request_region (instance->io_port, 2048, "acornscsi(ram)");
+	request_region(instance->io_port, 2048, "acornscsi(ram)");
 
 	if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) {
 	    printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n",
@@ -2680,7 +2926,7 @@
 	    host->scsi.irq = NO_IRQ;
 	}
 
-	acornscsi_init (host);
+	acornscsi_init(host);
 
 	++count;
     }
@@ -2688,12 +2934,12 @@
 }
 
 /*
- * Function: int acornscsi_release (struct Scsi_Host *host)
+ * Function: int acornscsi_release(struct Scsi_Host *host)
  * Purpose : release all resources used by this adapter
  * Params  : host - driver structure to release
  * Returns : nothing of any consequence
  */
-int acornscsi_release (struct Scsi_Host *instance)
+int acornscsi_release(struct Scsi_Host *instance)
 {
     AS_Host *host = (AS_Host *)instance->hostdata;
     int i;
@@ -2701,30 +2947,30 @@
     /*
      * Put card into RESET state
      */
-    outb (0x80, host->card.io_page);
+    outb(0x80, host->card.io_page);
 
     if (host->scsi.irq != NO_IRQ)
-	free_irq (host->scsi.irq, host);
+	free_irq(host->scsi.irq, host);
 
-    release_region (instance->io_port + 0x800, 2);
-    release_region (host->card.io_intr, 1);
-    release_region (host->card.io_page, 1);
-    release_region (host->dma.io_port, 256);
-    release_region (instance->io_port, 2048);
+    release_region(instance->io_port + 0x800, 2);
+    release_region(host->card.io_intr, 1);
+    release_region(host->card.io_page, 1);
+    release_region(host->dma.io_port, 256);
+    release_region(instance->io_port, 2048);
 
     for (i = 0; i < MAX_ECARDS; i++)
-	if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
-	    ecard_release (ecs[i]);
+	if (ecs[i] && instance->io_port == ecard_address(ecs[i], ECARD_MEMC, 0))
+	    ecard_release(ecs[i]);
 
-    msgqueue_free (&host->scsi.msgs);
-    queue_free (&host->queues.disconnected);
-    queue_free (&host->queues.issue);
+    msgqueue_free(&host->scsi.msgs);
+    queue_free(&host->queues.disconnected);
+    queue_free(&host->queues.issue);
 
     return 0;
 }
 
 /*
- * Function: char *acornscsi_info (struct Scsi_Host *host)
+ * Function: char *acornscsi_info(struct Scsi_Host *host)
  * Purpose : return a string describing this interface
  * Params  : host - host to give information on
  * Returns : a constant string
@@ -2736,7 +2982,7 @@
 
     p = string;
     
-    p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d"
+    p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d"
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     " SYNC"
 #endif
@@ -2772,7 +3018,7 @@
 
     host  = (AS_Host *)instance->hostdata;
     
-    p += sprintf (p, "AcornSCSI driver v%d.%d.%d"
+    p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     " SYNC"
 #endif
@@ -2787,14 +3033,14 @@
 #endif
 		"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
 
-    p += sprintf (p,	"SBIC: WD33C93A  Address: %08X  IRQ : %d\n",
+    p += sprintf(p,	"SBIC: WD33C93A  Address: %08X  IRQ : %d\n",
 			host->scsi.io_port, host->scsi.irq);
 #ifdef USE_DMAC
-    p += sprintf (p,	"DMAC: uPC71071  Address: %08X  IRQ : %d\n\n",
+    p += sprintf(p,	"DMAC: uPC71071  Address: %08X  IRQ : %d\n\n",
 			host->dma.io_port, host->scsi.irq);
 #endif
 
-    p += sprintf (p,	"Statistics:\n"
+    p += sprintf(p,	"Statistics:\n"
 			"Queued commands: %-10u    Issued commands: %-10u\n"
 			"Done commands  : %-10u    Reads          : %-10u\n"
 			"Writes         : %-10u    Others         : %-10u\n"
@@ -2809,47 +3055,47 @@
     for (devidx = 0; devidx < 9; devidx ++) {
 	unsigned int statptr, prev;
 
-	p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
-	statptr = status_ptr[devidx] - 10;
+	p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+	statptr = host->status_ptr[devidx] - 10;
 
 	if ((signed int)statptr < 0)
-	    statptr += 16;
+	    statptr += STATUS_BUFFER_SIZE;
 
-	prev = status[devidx][statptr].when;
+	prev = host->status[devidx][statptr].when;
 
-	for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) {
-	    if (status[devidx][statptr].when) {
-		p += sprintf (p, "%c%02X:%02X+%2ld",
-			status[devidx][statptr].irq ? '-' : ' ',
-			status[devidx][statptr].ph,
-			status[devidx][statptr].ssr,
-			(status[devidx][statptr].when - prev) < 100 ?
-				(status[devidx][statptr].when - prev) : 99);
-		prev = status[devidx][statptr].when;
+	for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+	    if (host->status[devidx][statptr].when) {
+		p += sprintf(p, "%c%02X:%02X+%2ld",
+			host->status[devidx][statptr].irq ? '-' : ' ',
+			host->status[devidx][statptr].ph,
+			host->status[devidx][statptr].ssr,
+			(host->status[devidx][statptr].when - prev) < 100 ?
+				(host->status[devidx][statptr].when - prev) : 99);
+		prev = host->status[devidx][statptr].when;
 	    }
 	}
     }
 
-    p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
+    p += sprintf(p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
 
     for (scd = instance->host_queue; scd; scd = scd->next) {
 	int len;
 
-	proc_print_scsidevice (scd, p, &len, 0);
+	proc_print_scsidevice(scd, p, &len, 0);
 	p += len;
 
-	p += sprintf (p, "Extensions: ");
+	p += sprintf(p, "Extensions: ");
 
 	if (scd->tagged_supported)
-	    p += sprintf (p, "TAG %sabled [%d] ",
+	    p += sprintf(p, "TAG %sabled [%d] ",
 			  scd->tagged_queue ? "en" : "dis", scd->current_tag);
-	p += sprintf (p, "\nTransfers: ");
+	p += sprintf(p, "\nTransfers: ");
 	if (host->device[scd->id].sync_xfer & 15)
-	    p += sprintf (p, "sync, offset %d, %d ns\n",
+	    p += sprintf(p, "sync, offset %d, %d ns\n",
 			  host->device[scd->id].sync_xfer & 15,
-			  acornscsi_getperiod (host->device[scd->id].sync_xfer));
+			  acornscsi_getperiod(host->device[scd->id].sync_xfer));
 	else
-	    p += sprintf (p, "async\n");
+	    p += sprintf(p, "async\n");
 
 	pos = p - buffer;
 	if (pos + begin < offset) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/acornscsi.h linux.18p17/drivers/acorn/scsi/acornscsi.h
--- linux.vanilla/drivers/acorn/scsi/acornscsi.h	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/acornscsi.h	Fri Sep 15 23:31:12 2000
@@ -291,6 +291,27 @@
 #include "queue.h"
 #include "msgqueue.h"
 
+#define STATUS_BUFFER_SIZE	32
+/*
+ * This is used to dump the previous states of the SBIC
+ */
+struct status_entry {
+	unsigned long	when;
+	unsigned char	ssr;
+	unsigned char	ph;
+	unsigned char	irq;
+	unsigned char	unused;
+};
+
+#define ADD_STATUS(_q,_ssr,_ph,_irq) \
+({									\
+	host->status[(_q)][host->status_ptr[(_q)]].when = jiffies;	\
+	host->status[(_q)][host->status_ptr[(_q)]].ssr  = (_ssr);	\
+	host->status[(_q)][host->status_ptr[(_q)]].ph   = (_ph);	\
+	host->status[(_q)][host->status_ptr[(_q)]].irq  = (_irq);	\
+	host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \
+})
+
 /*
  * AcornSCSI host specific data
  */
@@ -303,7 +324,7 @@
     /* driver information */
     struct {
 	unsigned int	io_port;		/* base address of WD33C93		*/
-	unsigned char	irq;			/* interrupt				*/
+	unsigned int	irq;			/* interrupt				*/
 	phase_t		phase;			/* current phase			*/
 
 	struct {
@@ -361,6 +382,7 @@
 	char		*xfer_ptr;		/* pointer to area			*/
 	unsigned char	xfer_required:1;	/* set if we need to transfer something	*/
 	unsigned char	xfer_setup:1;		/* set if DMA is setup			*/
+	unsigned char	xfer_done:1;		/* set if DMA reached end of BH list	*/
     } dma;
 
     /* card info */
@@ -370,6 +392,9 @@
 	unsigned int	io_ram;			/* base address of RAM access		*/
 	unsigned char	page_reg;		/* current setting of page reg		*/
     } card;
+
+    unsigned char status_ptr[9];
+    struct status_entry status[9][STATUS_BUFFER_SIZE];
 } AS_Host;
 
 #endif /* ndef HOSTS_C */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/arxescsi.c linux.18p17/drivers/acorn/scsi/arxescsi.c
--- linux.vanilla/drivers/acorn/scsi/arxescsi.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/acorn/scsi/arxescsi.c	Fri Sep 15 23:31:12 2000
@@ -0,0 +1,395 @@
+/*
+ * linux/arch/arm/drivers/scsi/cumana_2.c
+ *
+ * Copyright (C) 1997,1998 Russell King
+ *
+ * This driver is based on experimentation.  Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ *  30-08-1997	RMK	0.0.0	Created, READONLY version as cumana_2.c
+ *  22-01-1998	RMK	0.0.1	Updated to 2.1.80
+ *  15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.
+ *  11-06-1998 		0.0.2   Changed to support ARXE 16-bit SCSI card, enabled writing
+ *  				by Stefan Hanske
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "arxescsi.h"
+#include "fas216.h"
+
+/* Hmm - this should go somewhere else */
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+
+/* Configuration */
+#define ARXESCSI_XTALFREQ		24
+#define ARXESCSI_ASYNC_PERIOD		200
+#define ARXESCSI_SYNC_DEPTH		0
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define ARXESCSI_LIST		{ MANU_ARXE, PROD_ARXE_SCSI }
+
+/*
+ * Version
+ */
+#define VER_MAJOR	0
+#define VER_MINOR	0
+#define VER_PATCH	2
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+static struct proc_dir_entry proc_scsi_arxescsi = {
+	PROC_SCSI_QLOGICFAS, 6, "arxescsi",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*
+ * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params  : host      - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ *	     min_type  - minimum DMA support that we must have for this transfer
+ * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+ */
+static fasdmatype_t
+arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+		       fasdmadir_t direction, fasdmatype_t min_type)
+{
+	/*
+	 * We don't do real DMA
+	 */
+	return fasdma_pseudo;
+}
+
+
+
+/* Faster transfer routines, written by SH to speed up the loops */
+
+static __inline__ unsigned char getb(unsigned int address, unsigned int reg)
+{
+	unsigned char value;
+
+	__asm__ __volatile__(
+	"ldrb	%0, [%1, %2, lsl #5]"
+	: "=r" (value)
+	: "r" (address), "r" (reg) );
+	return value;
+}
+
+static __inline__ unsigned int getw(unsigned int address, unsigned int reg)
+{
+	unsigned int value;
+	
+	__asm__ __volatile__(
+	"ldr	%0, [%1, %2, lsl #5]\n\t"
+	"mov	%0, %0, lsl #16\n\t"
+	"mov	%0, %0, lsr #16"
+	: "=r" (value)
+	: "r" (address), "r" (reg) );
+	return value;
+}
+
+static __inline__ void putw(unsigned int address, unsigned int reg, unsigned long value)
+{
+	__asm__ __volatile__(
+	"mov	%0, %0, lsl #16\n\t"
+	"str	%0, [%1, %2, lsl #5]"
+	:
+	: "r" (value), "r" (address), "r" (reg) );
+}
+
+
+/*
+ * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params  : host      - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ *	     transfer  - minimum number of bytes we expect to transfer
+ */
+void arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+			fasdmadir_t direction, int transfer)
+{
+	ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+	unsigned int length, io, error=0;
+	unsigned char *addr;
+
+	length = SCp->this_residual;
+	addr = SCp->ptr;
+	io = __ioaddr(host->io_port);
+
+	if (direction == DMA_OUT) {
+		while (length > 0) {
+			unsigned long word;
+
+
+			word = *addr | *(addr + 1) << 8;
+			if (getb(io, 4) & STAT_INT)
+				break;
+
+			if (!(getb(io, 48) & CSTATUS_IRQ))
+				continue;
+
+			putw(io, 16, word);
+			if (length > 1) {
+				addr += 2;
+				length -= 2;
+			} else {
+				addr += 1;
+				length -= 1;
+			}
+		}
+	}
+	else {
+		if (transfer && (transfer & 255)) {
+			while (length >= 256) {
+				if (getb(io, 4) & STAT_INT) {
+					error=1;
+					break;
+				}
+	    
+				if (!(getb(io, 48) & CSTATUS_IRQ))
+					continue;
+
+				insw(info->dmaarea, addr, 256 >> 1);
+				addr += 256;
+				length -= 256;
+			}
+		}
+
+		if (!(error))
+			while (length > 0) {
+				unsigned long word;
+
+				if (getb(io, 4) & STAT_INT)
+					break;
+
+				if (!(getb(io, 48) & CSTATUS_IRQ))
+					continue;
+
+				word = getw(io, 16);
+				*addr++ = word;
+				if (--length > 0) {
+					*addr++ = word >> 8;
+					length --;
+				}
+			}
+	}
+}
+
+/*
+ * Function: int arxescsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params  : host  - host
+ *	     SCpnt - command
+ */
+static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+	/*
+	 * no DMA to stop
+	 */
+}
+
+/*
+ * Function: int arxescsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises ARXE SCSI driver
+ * Params  : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int arxescsi_detect(Scsi_Host_Template *tpnt)
+{
+	static const card_ids arxescsi_cids[] = { ARXESCSI_LIST, { 0xffff, 0xffff} };
+	int count = 0;
+	struct Scsi_Host *host;
+  
+	tpnt->proc_dir = &proc_scsi_arxescsi;
+	memset(ecs, 0, sizeof (ecs));
+
+	ecard_startfind();
+
+	while (1) {
+	    	ARXEScsi_Info *info;
+
+		ecs[count] = ecard_find(0, arxescsi_cids);
+		if (!ecs[count])
+			break;
+
+		ecard_claim(ecs[count]);
+		
+		host = scsi_register(tpnt, sizeof (ARXEScsi_Info));
+		if (!host) {
+			ecard_release(ecs[count]);
+			break;
+		}
+
+		host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0) + 0x0800;
+		host->irq = NO_IRQ;
+		host->dma_channel = NO_DMA;
+		host->can_queue = 0; /* no command queueing */
+		info = (ARXEScsi_Info *)host->hostdata;
+
+		info->info.scsi.io_port		= host->io_port;
+		info->info.scsi.irq		= host->irq;
+		info->info.scsi.io_shift	= 3;
+		info->info.ifcfg.clockrate	= ARXESCSI_XTALFREQ;
+		info->info.ifcfg.select_timeout = 255;
+		info->info.ifcfg.asyncperiod	= ARXESCSI_ASYNC_PERIOD;
+		info->info.ifcfg.sync_max_depth	= ARXESCSI_SYNC_DEPTH;
+		info->info.ifcfg.cntl3		= CNTL3_FASTSCSI | CNTL3_FASTCLK;
+		info->info.ifcfg.disconnect_ok	= 0;
+		info->info.ifcfg.wide_max_size	= 0;
+		info->info.dma.setup		= arxescsi_dma_setup;
+		info->info.dma.pseudo		= arxescsi_dma_pseudo;
+		info->info.dma.stop		= arxescsi_dma_stop;
+		info->dmaarea			= host->io_port + 128;
+		info->cstatus			= host->io_port + 384;
+		
+		ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(host->io_port);
+		ecs[count]->irqmask = CSTATUS_IRQ;
+
+		request_region(host->io_port      , 120, "arxescsi-fas");
+		request_region(host->io_port + 128, 384, "arxescsi-dma");
+
+		printk("scsi%d: Has no interrupts - using polling mode\n",
+		       host->host_no);
+
+		fas216_init(host);
+		++count;
+	}
+	return count;
+}
+
+/*
+ * Function: int arxescsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params  : host - driver host structure to return info for.
+ * Returns : nothing
+ */
+int arxescsi_release(struct Scsi_Host *host)
+{
+	int i;
+
+	fas216_release(host);
+
+	release_region(host->io_port, 120);
+	release_region(host->io_port + 128, 384);
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ecs[i] && host->io_port == (ecard_address(ecs[i], ECARD_MEMC, 0) + 0x0800))
+			ecard_release(ecs[i]);
+	return 0;
+}
+
+/*
+ * Function: const char *arxescsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params  : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *arxescsi_info(struct Scsi_Host *host)
+{
+	ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+	static char string[100], *p;
+
+	p = string;
+	p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d scsi %s",
+		     host->hostt->name, host->io_port, host->irq,
+		     VER_MAJOR, VER_MINOR, VER_PATCH,
+		     info->info.scsi.type);
+
+	return string;
+}
+
+/*
+ * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+ *					 int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ *	     the /proc filesystem.
+ * Params  : buffer - a buffer to write information to
+ *	     start  - a pointer into this buffer set by this routine to the start
+ *		      of the required information.
+ *	     offset - offset into information that we have read upto.
+ *	     length - length of buffer
+ *	     host_no - host number to return information for
+ *	     inout  - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+			    int length, int host_no, int inout)
+{
+	int pos, begin;
+	struct Scsi_Host *host = scsi_hostlist;
+	ARXEScsi_Info *info;
+	Scsi_Device *scd;
+
+	while (host) {
+		if (host->host_no == host_no)
+			break;
+		host = host->next;
+	}
+	if (!host)
+		return 0;
+
+	info = (ARXEScsi_Info *)host->hostdata;
+	if (inout == 1)
+		return -EINVAL;
+
+	begin = 0;
+	pos = sprintf(buffer,
+			"ARXE 16-bit SCSI driver version %d.%d.%d\n",
+			VER_MAJOR, VER_MINOR, VER_PATCH);
+	pos += sprintf(buffer + pos,
+			"Address: %08lX          IRQ : %d\n"
+			"FAS    : %s\n\n"
+			"Statistics:\n",
+			host->io_port, host->irq, info->info.scsi.type);
+
+	pos += fas216_print_stats(&info->info, buffer + pos);
+
+	pos += sprintf (buffer+pos, "\nAttached devices:\n");
+
+	for (scd = host->host_queue; scd; scd = scd->next) {
+		pos += fas216_print_device(&info->info, scd, buffer + pos);
+
+		if (pos + begin < offset) {
+			begin += pos;
+			pos = 0;
+		}
+		if (pos + begin > offset + length)
+			break;
+	}
+
+	*start = buffer + (offset - begin);
+	pos -= offset - begin;
+	if (pos > length)
+		pos = length;
+
+	return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ARXEScsi;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/arxescsi.h linux.18p17/drivers/acorn/scsi/arxescsi.h
--- linux.vanilla/drivers/acorn/scsi/arxescsi.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/acorn/scsi/arxescsi.h	Fri Sep 15 23:31:12 2000
@@ -0,0 +1,80 @@
+/*
+ * ARXE SCSI card driver
+ *
+ * Copyright (C) 1997 Russell King
+ * Changes to support ARXE 16-bit SCSI card by Stefan Hanske
+ */
+#ifndef ARXE_SCSI_H
+#define ARXE_SCSI_H
+
+#define MANU_ARXE 	0x0041
+#define PROD_ARXE_SCSI	0x00be
+
+extern int arxescsi_detect (Scsi_Host_Template *);
+extern int arxescsi_release (struct Scsi_Host *);
+extern const char *arxescsi_info (struct Scsi_Host *);
+extern int arxescsi_proc_info (char *buffer, char **start, off_t offset,
+				int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE	1
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN	1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID		7
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+#include "fas216.h"
+#endif
+
+#define ARXEScsi {							\
+proc_info:	arxescsi_proc_info,						\
+name:		"ARXE SCSI card",						\
+detect:		arxescsi_detect,		/* detect		*/	\
+release:	arxescsi_release,		/* release		*/	\
+info:		arxescsi_info,			/* info			*/	\
+command:	fas216_command,			/* command		*/	\
+queuecommand:	fas216_queue_command,		/* queuecommand		*/	\
+abort:		fas216_abort,			/* abort		*/	\
+reset:		fas216_reset,			/* reset		*/	\
+bios_param:	scsicam_bios_param,		/* biosparam		*/	\
+can_queue:	CAN_QUEUE,			/* can queue		*/	\
+this_id:	SCSI_ID,			/* scsi host id		*/	\
+sg_tablesize:	SG_ALL,				/* sg_tablesize		*/	\
+cmd_per_lun:	CMD_PER_LUN,			/* cmd per lun		*/	\
+use_clustering:	DISABLE_CLUSTERING						\
+	}
+
+#ifndef HOSTS_C
+
+typedef struct {
+    FAS216_Info info;
+
+    /* other info... */
+    unsigned int	cstatus;	/* card status register	*/
+    unsigned int	dmaarea;	/* Pseudo DMA area	*/
+} ARXEScsi_Info;
+
+#define CSTATUS_IRQ	(1 << 0)
+#define CSTATUS_DRQ	(1 << 0)
+
+#endif /* HOSTS_C */
+
+#endif /* ARXE_SCSI_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/cumana_2.c linux.18p17/drivers/acorn/scsi/cumana_2.c
--- linux.vanilla/drivers/acorn/scsi/cumana_2.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/cumana_2.c	Fri Sep 15 23:31:12 2000
@@ -4,12 +4,12 @@
  * Copyright (C) 1997-1998 Russell King
  *
  * Changelog:
- *  30-08-1997	RMK	0.0.0	Created, READONLY version
- *  22-01-1998	RMK	0.0.1	Updated to 2.1.80
+ *  30-08-1997	RMK	0.0.0	Created, READONLY version.
+ *  22-01-1998	RMK	0.0.1	Updated to 2.1.80.
  *  15-04-1998	RMK	0.0.1	Only do PIO if FAS216 will allow it.
- *  02-05-1998	RMK	0.0.2	Updated & added DMA support
+ *  02-05-1998	RMK	0.0.2	Updated & added DMA support.
  *  27-06-1998	RMK		Changed asm/delay.h to linux/delay.h
- *  18-08-1998	RMK	0.0.3	Fixed synchronous transfer depth
+ *  18-08-1998	RMK	0.0.3	Fixed synchronous transfer depth.
  */
 
 #include <linux/module.h>
@@ -117,6 +117,8 @@
 	cumanascsi_2_irqenable,
 	cumanascsi_2_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -364,6 +366,7 @@
 		info->info.ifcfg.sync_max_depth	= CUMANASCSI2_SYNC_DEPTH;
 		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= cumanascsi_2_dma_setup;
 		info->info.dma.pseudo		= cumanascsi_2_dma_pseudo;
 		info->info.dma.stop		= cumanascsi_2_dma_stop;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/eesox.c linux.18p17/drivers/acorn/scsi/eesox.c
--- linux.vanilla/drivers/acorn/scsi/eesox.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/eesox.c	Fri Sep 15 23:31:12 2000
@@ -38,9 +38,6 @@
 #include "../../scsi/hosts.h"
 #include "eesox.h"
 
-#define NO_IRQ	255
-#define NO_DMA	255
-
 /* Configuration */
 #define EESOX_XTALFREQ		40
 #define EESOX_ASYNC_PERIOD	200
@@ -123,6 +120,8 @@
 	eesoxscsi_irqenable,
 	eesoxscsi_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -379,6 +378,7 @@
 		info->info.ifcfg.sync_max_depth	= EESOX_SYNC_DEPTH;
 		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= eesoxscsi_dma_setup;
 		info->info.dma.pseudo		= eesoxscsi_dma_pseudo;
 		info->info.dma.stop		= eesoxscsi_dma_stop;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/fas216.c linux.18p17/drivers/acorn/scsi/fas216.c
--- linux.vanilla/drivers/acorn/scsi/fas216.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/fas216.c	Fri Sep 15 23:31:12 2000
@@ -24,9 +24,10 @@
  *  02-05-1998	RMK	Added extra checks in fas216_reset
  *  24-05-1998	RMK	Fixed synchronous transfers with period >= 200ns
  *  27-06-1998	RMK	Changed asm/delay.h to linux/delay.h
+ *  26-08-1998	RMK	Improved message support wrt MESSAGE_REJECT
+ *  17-01-2000	RMK	Added Command->Message In transition
  *
  * Todo:
- *  - tighten up the MESSAGE_REJECT support.
  *  - allow individual devices to enable sync xfers.
  */
 
@@ -57,7 +58,7 @@
 
 #define VER_MAJOR	0
 #define VER_MINOR	0
-#define VER_PATCH	4
+#define VER_PATCH	6
 
 #define SCSI2_TAG
 
@@ -86,6 +87,8 @@
  */
 #define SCSI2_SYNC
 
+#define SCSI2_WIDE
+
 #undef DEBUG_CONNECT
 #undef DEBUG_BUSSERVICE
 #undef DEBUG_FUNCTIONDONE
@@ -98,11 +101,16 @@
 
 static void fas216_dumpstate(FAS216_Info *info)
 {
+	unsigned char is, stat, inst;
+
+	is   = inb(REG_IS(info));
+	stat = inb(REG_STAT(info));
+	inst = inb(REG_INST(info));
+	
 	printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X"
 	       " INST=%02X IS=%02X CFIS=%02X",
 		inb(REG_CTCL(info)), inb(REG_CTCM(info)),
-		inb(REG_CMD(info)),  inb(REG_STAT(info)),
-		inb(REG_INST(info)), inb(REG_IS(info)),
+		inb(REG_CMD(info)),  stat, inst, is,
 		inb(REG_CFIS(info)));
 	printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n",
 		inb(REG_CNTL1(info)), inb(REG_CNTL2(info)),
@@ -117,43 +125,27 @@
 	if (used++)
 		return;
 
-	printk("FAS216_Info=\n");
-	printk("  { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n",
-		info->magic_start, info->host, info->SCpnt,
-		info->origSCpnt);
-	printk("    scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n",
-		info->scsi.io_port, info->scsi.io_shift, info->scsi.irq,
-		info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2],
-		info->scsi.cfg[3]);
-	printk("           type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n",
-		info->scsi.type, info->scsi.phase,
-		info->scsi.reconnected.target,
+	printk("FAS216 Info (%s) =\n", info->scsi.type);
+	printk("  SCpnt=%p origSCpnt=%p\n", info->SCpnt, info->origSCpnt);
+	printk("  phase=%X reconnected={ target=%d lun=%d tag=%d }\n",
+		info->scsi.phase, info->scsi.reconnected.target,
 		info->scsi.reconnected.lun, info->scsi.reconnected.tag);
-	printk("           SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
+	printk("  SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
 		info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
 		info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual);
-	printk("      msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n",
-		info->scsi.async_stp, info->scsi.last_message,
+	printk("  async_stp=%X disconnectable=%d aborting=%d\n",
+		info->scsi.async_stp,
 		info->scsi.disconnectable, info->scsi.aborting);
-	printk("    stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n"
-	       "            disconnects=%X aborts=%X resets=%X }\n",
-		info->stats.queues, info->stats.removes, info->stats.fins,
-		info->stats.reads, info->stats.writes, info->stats.miscs,
-		info->stats.disconnects, info->stats.aborts, info->stats.resets);
-	printk("    ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n",
-		info->ifcfg.clockrate, info->ifcfg.select_timeout,
-		info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
 	for (i = 0; i < 8; i++) {
-		printk("    busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n",
-			i, info->busyluns[i], i,
+		printk("  dev[%d]={ busy=%d disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
+			i, info->busyluns[i],
 			info->device[i].disconnect_ok, info->device[i].stp,
-			info->device[i].sof, info->device[i].negstate);
+			info->device[i].sof, info->device[i].sync_state);
 	}
-	printk("    dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
+	printk("  dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
 		info->dma.transfer_type, info->dma.setup,
 		info->dma.pseudo, info->dma.stop);
-	printk("    internal_done=%X magic_end=%lX }\n",
-		info->internal_done, info->magic_end);
+	printk("  internal_done=%X\n", info->internal_done);
 }
 
 #ifdef CHECK_STRUCTURE
@@ -192,19 +184,19 @@
 static const char *fas216_drv_phase(FAS216_Info *info)
 {
 	switch (info->scsi.phase) {
-	case PHASE_IDLE:	return "idle";
-	case PHASE_SELECTION:	return "selection";
-	case PHASE_MESSAGESENT:	return "message sent";
-	case PHASE_RECONNECTED:	return "reconnected";
-	case PHASE_DATAOUT:	return "data out";
-	case PHASE_DATAIN:	return "data in";
-	case PHASE_MSGOUT:	return "message out";
-	case PHASE_MSGIN:	return "message in";
-	case PHASE_AFTERMSGOUT:	return "after message out";
-	case PHASE_STATUS:	return "status";
-	case PHASE_DISCONNECT:	return "disconnect";
-	case PHASE_DONE:	return "done";
-	default:		return "???";
+	case PHASE_IDLE:		return "idle";
+	case PHASE_SELECTION:		return "selection";
+	case PHASE_COMMAND:		return "command";
+	case PHASE_RECONNECTED:		return "reconnected";
+	case PHASE_DATAOUT:		return "data out";
+	case PHASE_DATAIN:		return "data in";
+	case PHASE_MSGIN:		return "message in";
+	case PHASE_MSGIN_DISCONNECT:	return "disconnect";
+	case PHASE_MSGOUT_EXPECT:	return "expect message out";
+	case PHASE_MSGOUT:		return "message out";
+	case PHASE_STATUS:		return "status";
+	case PHASE_DONE:		return "done";
+	default:			return "???";
 	}
 }
 
@@ -262,6 +254,37 @@
 	return clock;
 }
 
+/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos)
+ * Purpose : retrieve a last message from the list, using position in fifo
+ * Params  : info - interface to search
+ *         : pos  - current fifo position
+ */
+static inline unsigned short
+fas216_get_last_msg(FAS216_Info *info, int pos)
+{
+	unsigned short packed_msg = NOP;
+	struct message *msg;
+	int msgnr = 0;
+
+	while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+		if (pos >= msg->fifo)
+			break;
+	}
+
+	if (msg) {
+		if (msg->msg[0] == EXTENDED_MESSAGE)
+			packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8;
+		else
+			packed_msg = msg->msg[0];
+	}
+
+#ifdef DEBUG_MESSAGES
+	printk("Message: %04X found at position %02X\n",
+		packed_msg, pos);
+#endif
+	return packed_msg;
+}
+
 /* Function: int fas216_syncperiod(FAS216_Info *info, int ns)
  * Purpose : Calculate value to be loaded into the STP register
  *           for a given period in ns
@@ -303,6 +326,252 @@
 		outb(info->scsi.cfg[2], REG_CNTL3(info));
 }
 
+typedef enum {
+	mstat_accept,
+	mstat_reject,
+	mstat_unrecognised
+} msgin_stat_t;
+
+/* Synchronous transfer support
+ *
+ * Note: The SCSI II r10 spec says (5.6.12):
+ *
+ *  (2)  Due to historical problems with early host adapters that could
+ *  not accept an SDTR message, some targets may not initiate synchronous
+ *  negotiation after a power cycle as required by this standard.  Host
+ *  adapters that support synchronous mode may avoid the ensuing failure
+ *  modes when the target is independently power cycled by initiating a
+ *  synchronous negotiation on each REQUEST SENSE and INQUIRY command.
+ *  This approach increases the SCSI bus overhead and is not recommended
+ *  for new implementations.  The correct method is to respond to an
+ *  SDTR message with a MESSAGE REJECT message if the either the
+ *  initiator or target devices does not support synchronous transfers
+ *  or does not want to negotiate for synchronous transfers at the time.
+ *  Using the correct method assures compatibility with wide data
+ *  transfers and future enhancements.
+ *
+ * We will always initiate a synchronous transfer negociation request on
+ * every INQUIRY or REQUEST SENSE message, unless the target itself has
+ * at some point performed a synchronous transfer negociation request, or
+ * we have synchronous transfers disabled for this device.
+ */
+
+/* Function: void fas216_handlesync(FAS216_Info *info, char *msg)
+ * Purpose : Handle a synchronous transfer message from the target
+ * Params  : info - state structure for interface
+ *         : msg  - message from target
+ */
+static msgin_stat_t
+fas216_handlesync(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { sync, async, none, reject } res = none;
+	msgin_stat_t mstat;
+
+#ifdef SCSI2_SYNC
+	switch (msg[0]) {
+	case MESSAGE_REJECT:
+		/* Synchronous transfer request failed.
+		 * Note: SCSI II r10:
+		 *
+		 *  SCSI devices that are capable of synchronous
+		 *  data transfers shall not respond to an SDTR
+		 *  message with a MESSAGE REJECT message.
+		 *
+		 * Hence, if we get this condition, we disable
+		 * negociation for this device.
+		 */
+		if (dev->sync_state == neg_inprogress) {
+			dev->sync_state = neg_invalid;
+			res = async;
+		}
+		break;
+
+	case EXTENDED_MESSAGE:
+		switch (dev->sync_state) {
+		/* We don't accept synchronous transfer requests.
+		 * Respond with a MESSAGE_REJECT to prevent a
+		 * synchronous transfer agreement from being reached.
+		 */
+		case neg_invalid:
+			res = reject;
+			break;
+
+		/* We were not negociating a synchronous transfer,
+		 * but the device sent us a negociation request.
+		 * Honour the request by sending back a SDTR
+		 * message containing our capability, limited by
+		 * the targets capability.
+		 */
+		default:
+			outb(CMD_SETATN, REG_CMD(info));
+			if (msg[4] > info->ifcfg.sync_max_depth)
+				msg[4] = info->ifcfg.sync_max_depth;
+			if (msg[3] < 1000 / info->ifcfg.clockrate)
+				msg[3] = 1000 / info->ifcfg.clockrate;
+
+			msgqueue_flush(&info->scsi.msgs);
+			msgqueue_addmsg(&info->scsi.msgs, 5,
+					EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+					msg[3], msg[4]);
+			info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+			/* This is wrong.  The agreement is not in effect
+			 * until this message is accepted by the device
+			 */
+			dev->sync_state = neg_targcomplete;
+			res = sync;
+			break;
+
+		/* We initiated the synchronous transfer negociation,
+		 * and have successfully received a response from the
+		 * target.  The synchronous transfer agreement has been
+		 * reached.  Note: if the values returned are out of our
+		 * bounds, we must reject the message.
+		 */
+		case neg_inprogress:
+			res = reject;
+			if (msg[4] <= info->ifcfg.sync_max_depth &&
+			    msg[3] >= 1000 / info->ifcfg.clockrate) {
+				dev->sync_state = neg_complete;
+				res = sync;
+			}
+			break;
+		}
+	}
+#else
+	res = reject;
+#endif
+
+	mstat = mstat_accept;
+
+	switch (res) {
+	case sync:
+		dev->period = msg[3];
+		dev->sof    = msg[4];
+		dev->stp    = fas216_syncperiod(info, msg[3] * 4);
+		fas216_set_sync(info, info->SCpnt->target);
+		break;
+
+	case reject:
+		mstat = mstat_reject;
+
+	case async:
+		dev->period = info->ifcfg.asyncperiod / 4;
+		dev->sof    = 0;
+		dev->stp    = info->scsi.async_stp;
+		fas216_set_sync(info, info->SCpnt->target);
+		break;
+
+	case none:
+		mstat = mstat_unrecognised;
+		break;
+	}
+
+	return mstat;
+}
+
+/* Function: void fas216_handlewide(FAS216_Info *info, char *msg)
+ * Purpose : Handle a wide transfer message from the target
+ * Params  : info - state structure for interface
+ *         : msg  - message from target
+ */
+static msgin_stat_t
+fas216_handlewide(FAS216_Info *info, char *msg)
+{
+	struct fas216_device *dev = &info->device[info->SCpnt->target];
+	enum { wide, bit8, none, reject } res = none;
+	msgin_stat_t mstat;
+
+#ifdef SCSI2_WIDE
+	switch (msg[0]) {
+	case MESSAGE_REJECT:
+		/* Wide transfer request failed.
+		 * Note: SCSI II r10:
+		 *
+		 *  SCSI devices that are capable of wide
+		 *  data transfers shall not respond to a
+		 *  WDTR message with a MESSAGE REJECT message.
+		 *
+		 * Hence, if we get this condition, we never
+		 * reattempt negociation for this device.
+		 */
+		if (dev->wide_state == neg_inprogress) {
+			dev->wide_state = neg_invalid;
+			res = bit8;
+		}
+		break;
+
+	case EXTENDED_MESSAGE:
+		switch (dev->wide_state) {
+		/* We don't accept wide data transfer requests.
+		 * Respond with a MESSAGE REJECT to prevent a
+		 * wide data transfer agreement from being reached.
+		 */
+		case neg_invalid:
+			res = reject;
+			break;
+
+		/* We were not negociating a wide data transfer,
+		 * but the device sent is a negociation request.
+		 * Honour the request by sending back a WDTR
+		 * message containing our capability, limited by
+		 * the targets capability.
+		 */
+		default:
+			outb(CMD_SETATN, REG_CMD(info));
+			if (msg[3] > info->ifcfg.wide_max_size)
+				msg[3] = info->ifcfg.wide_max_size;
+
+			msgqueue_flush(&info->scsi.msgs);
+			msgqueue_addmsg(&info->scsi.msgs, 4,
+					EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+					msg[3]);
+			info->scsi.phase = PHASE_MSGOUT_EXPECT;
+			res = wide;
+			break;
+
+		/* We initiated the wide data transfer negociation,
+		 * and have successfully received a response from the
+		 * target.  The synchronous transfer agreement has been
+		 * reached.  Note: if the values returned are out of our
+		 * bounds, we must reject the message.
+		 */
+		case neg_inprogress:
+			res = reject;
+			if (msg[3] <= info->ifcfg.wide_max_size) {
+				dev->wide_state = neg_complete;
+				res = wide;
+			}
+			break;
+		}
+	}
+#else
+	res = reject;
+#endif
+
+	mstat = mstat_accept;
+
+	switch (res) {
+	case wide:
+		dev->wide_xfer = msg[3];
+		break;
+
+	case reject:
+		mstat = mstat_reject;
+
+	case bit8:
+		dev->wide_xfer = 0;
+		break;
+
+	case none:
+		mstat = mstat_unrecognised;
+		break;
+	}
+
+	return mstat;
+}
+
 /* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
  * Purpose : update data pointers after transfer suspended/paused
  * Params  : info              - interface's local pointer to update
@@ -338,6 +607,9 @@
 	residual -= bytes_transferred;
 	ptr += bytes_transferred;
 
+	if (residual == 0)
+		ptr = NULL;
+
 	info->scsi.SCp.ptr = ptr;
 	info->scsi.SCp.this_residual = residual;
 }
@@ -353,35 +625,18 @@
 {
 	unsigned int residual;
 	char *ptr;
-	int correction;
 
 	fas216_checkmagic(info, "fas216_pio");
 
 	residual = info->scsi.SCp.this_residual;
 	ptr = info->scsi.SCp.ptr;
 
-	if (direction == DMA_OUT) {
-	    	while (residual > 0) {
-			if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
-				outb(*ptr++, REG_FF(info));
-				residual -= 1;
-			} else if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		}
-		correction = inb(REG_CFIS(info)) & CFIS_CF;
-	} else {
-	    	while (residual > 0) {
-			if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
-				*ptr++ = inb(REG_FF(info));
-				residual -= 1;
-			} else if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		}
-		correction = 0;
-	}
+	if (direction == DMA_OUT)
+		outb(*ptr++, REG_FF(info));
+	else
+		*ptr++ = inb(REG_FF(info));
 
-	ptr -= correction;
-	residual += correction;
+	residual -= 1;
 
 	if (residual == 0) {
 		if (info->scsi.SCp.buffers_residual) {
@@ -549,10 +804,11 @@
 
 	switch (info->scsi.phase) {
 	case PHASE_SELECTION:			/* while selecting - no target		*/
+	case PHASE_SELSTEPS:
 		fas216_done(info, DID_NO_CONNECT);
 		break;
 
-	case PHASE_DISCONNECT:			/* message in - disconnecting		*/
+	case PHASE_MSGIN_DISCONNECT:		/* message in - disconnecting		*/
 		outb(CMD_ENABLESEL, REG_CMD(info));
 		info->scsi.disconnectable = 1;
 		info->scsi.reconnected.tag = 0;
@@ -564,8 +820,8 @@
 		fas216_done(info, DID_OK);
 		break;
 
-	case PHASE_AFTERMSGOUT:			/* message out - possible ABORT message	*/
-		if (info->scsi.last_message == ABORT) {
+	case PHASE_MSGOUT:			/* message out - possible ABORT message	*/
+		if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) {
 			info->scsi.aborting = 0;
 			fas216_done(info, DID_ABORT);
 			break;
@@ -592,14 +848,17 @@
 
 	fas216_checkmagic(info, "fas216_reselected_intr");
 
-	if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
+	if ((info->scsi.phase == PHASE_SELECTION ||
+	     info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) {
 		Scsi_Cmnd *SCpnt = info->SCpnt;
 
 		info->origSCpnt = SCpnt;
 		info->SCpnt = NULL;
 
-		if (info->device[SCpnt->target].negstate == syncneg_sent)
-			info->device[SCpnt->target].negstate = syncneg_start;
+		if (info->device[SCpnt->target].wide_state == neg_inprogress)
+			info->device[SCpnt->target].wide_state = neg_wait;
+		if (info->device[SCpnt->target].sync_state == neg_inprogress)
+			info->device[SCpnt->target].sync_state = neg_wait;
 	}
 
 #ifdef DEBUG_CONNECT
@@ -607,15 +866,14 @@
 		fas216_target(info), info->scsi.phase);
 #endif
 
-	msgqueue_flush(&info->scsi.msgs);
-
 	if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
 		printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
 			info->host->host_no);
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-		info->scsi.phase = PHASE_MSGOUT;
 		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		return;
 	}
 
@@ -636,13 +894,14 @@
 
 	if (!ok) {
 		/*
-		 * Something went wrong - abort the command on
-		 * the target.  Should this be INITIATOR_ERROR ?
+		 * Something went wrong - send an initiator error to
+		 * the target.
 		 */
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
 		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		return;
 	}
 
@@ -672,17 +931,20 @@
 	if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
 		ok = 1;
 
+	msgqueue_flush(&info->scsi.msgs);
 	if (ok) {
 		info->scsi.phase = PHASE_RECONNECTED;
 		outb(target, REG_SDID(info));
 	} else {
 		/*
-		 * Our command structure not found - abort the command on the target
-		 * Should this be INITIATOR_ERROR ?
+		 * Our command structure not found - abort the
+		 * command on the target.  Since we have no
+		 * record of this command, we can't send
+		 * an INITIATOR DETECTED ERROR message.
 		 */
 		outb(CMD_SETATN, REG_CMD(info));
 		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 	}
 	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
@@ -733,8 +995,14 @@
 	}
 	if (!info->SCpnt) {
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT;
+		msgqueue_flush(&info->scsi.msgs);
+#if 0
+		if (info->scsi.reconnected.tag)
+			msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag);
+		else
+#endif
+			msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		info->scsi.aborting = 1;
 	} else {
 		/*
@@ -751,50 +1019,91 @@
 #endif
 }
 
-/* Function: void fas216_message(FAS216_Info *info)
- * Purpose : handle a function done interrupt from FAS216 chip
- * Params  : info - interface which caused function done interrupt
- */
-static void fas216_message(FAS216_Info *info)
+static int fas216_wait_cmd(FAS216_Info *info, int cmd)
 {
-	unsigned char message[16];
+	int tout;
+	int stat;
+
+	outb(cmd, REG_CMD(info));
+
+	for (tout = 1000; tout; tout -= 1) {
+		stat = inb(REG_STAT(info));
+		if (stat & STAT_INT)
+			break;
+		udelay(1);
+	}
+
+	return stat;
+}
+
+static int fas216_get_msg_byte(FAS216_Info *info)
+{
+	int stat;
+
+	stat = fas216_wait_cmd(info, CMD_MSGACCEPTED);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	inb(REG_INST(info));
+
+	stat = fas216_wait_cmd(info, CMD_TRANSFERINFO);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	inb(REG_INST(info));
+
+	return inb(REG_FF(info));
+
+timedout:
+	printk("scsi%d.%c: timed out waiting for message byte\n",
+		info->host->host_no, fas216_target(info));
+	return -1;
+
+unexpected_phase_change:
+	printk("scsi%d.%c: unexpected phase change: status = %02X\n",
+		info->host->host_no, fas216_target(info), stat);
+
+	return -2;
+}
+
+static msgin_stat_t fas216___message(FAS216_Info *info)
+{
+	unsigned char *message = info->scsi.message;
 	unsigned int msglen = 1;
+	msgin_stat_t mstat = mstat_unrecognised;
+	signed int msgbyte = 0;
 
 	fas216_checkmagic(info, "fas216_message");
 
 	message[0] = inb(REG_FF(info));
 
 	if (message[0] == EXTENDED_MESSAGE) {
-		int tout;
-		outb(CMD_MSGACCEPTED, REG_CMD(info));
-		for (tout = 1000000; tout; tout--)
-			if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		inb(REG_INST(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		for (tout = 1000000; tout; tout--)
-			if (inb(REG_STAT(info)) & STAT_INT)
-				break;
-		inb(REG_INST(info));
+		msgbyte = fas216_get_msg_byte(info);
 
-		message[1] = inb(REG_FF(info));
+		if (msgbyte >= 0) {
+			message[1] = msgbyte;
 
-		for (msglen = 2; msglen < message[1] + 2; msglen++) {
-			outb(CMD_MSGACCEPTED, REG_CMD(info));
-			for (tout = 1000000; tout; tout--)
-				if (inb(REG_STAT(info)) & STAT_INT)
-					break;
-			inb(REG_INST(info));
-			outb(CMD_TRANSFERINFO, REG_CMD(info));
-			for (tout = 1000000; tout; tout--)
-				if (inb(REG_STAT(info)) & STAT_INT)
-					break;
-			inb(REG_INST(info));
+			for (msglen = 2; msglen < message[1] + 2; msglen++) {
+				msgbyte = fas216_get_msg_byte(info);
 
-			message[msglen] = inb(REG_FF(info));
+				if (msgbyte >= 0)
+					message[msglen] = msgbyte;
+				else
+					break;
+			}
 		}
 	}
 
+	info->scsi.msglen = msglen;
+
 #ifdef DEBUG_MESSAGES
 	{
 		int i;
@@ -806,6 +1115,19 @@
 		printk("\n");
 	}
 #endif
+
+	/*
+	 * check the length of the message.
+	 * messages 0x00, 0x02-0x1f, 0x80-0xff are one-byte
+	 * messages 0x20-0x30 are two-byte
+	 * check extended messages below...
+	 */
+	if ((((message[0] < 0x20 && message[0] != EXTENDED_MESSAGE) ||
+	      (message[0] >= 0x80)) && msglen != 1) ||
+	    ((message[0] >= 0x20 && message[0] < 0x30) && msglen != 2))
+		return mstat_unrecognised;
+
+
 	if (info->scsi.phase == PHASE_RECONNECTED) {
 		if (message[0] == SIMPLE_QUEUE_TAG)
 			info->scsi.reconnected.tag = message[1];
@@ -815,19 +1137,29 @@
 
 	switch (message[0]) {
 	case COMMAND_COMPLETE:
-		printk("fas216: command complete with no status in MESSAGE_IN?\n");
+		printk(KERN_ERR "scsi%d.%c: command complete with no "
+			"status in MESSAGE_IN?\n",
+			info->host->host_no, fas216_target(info));
+		mstat = mstat_accept;
 		break;
 
 	case SAVE_POINTERS:
 		/*
 		 * Save current data pointer to SAVED data pointer
+		 * SCSI II standard says that we must not acknowledge
+		 * this until we have really saved pointers.
+		 * NOTE: we DO NOT save the command nor status pointers
+		 * as required by the SCSI II standard.  These always
+		 * point to the start of their respective areas.
 		 */
 		info->SCpnt->SCp = info->scsi.SCp;
+		info->SCpnt->SCp.sent_command = 0;
 #if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
 		printk("scsi%d.%c: save data pointers: [%p, %X]\n",
 			info->host->host_no, fas216_target(info),
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
+		mstat = mstat_accept;
 		break;
 
 	case RESTORE_POINTERS:
@@ -840,85 +1172,103 @@
 			info->host->host_no, fas216_target(info),
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
+		mstat = mstat_accept;
 		break;
 
 	case DISCONNECT:
-		info->scsi.phase = PHASE_DISCONNECT;
+		info->scsi.phase = PHASE_MSGIN_DISCONNECT;
+		mstat = mstat_accept;
 		break;
 
 	case MESSAGE_REJECT:
-		printk("scsi%d.%c: reject, last message %04X\n",
-			info->host->host_no, fas216_target(info),
-			info->scsi.last_message);
+		mstat = mstat_accept;
+		switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) {
+		case EXTENDED_MESSAGE | EXTENDED_SDTR << 8:
+			mstat = fas216_handlesync(info, message);
+			break;
+
+		case EXTENDED_MESSAGE | EXTENDED_WDTR << 8:
+			mstat = fas216_handlewide(info, message);
+			break;
+
+		default:
+			printk("scsi%d.%c: reject, last message %04X\n",
+				info->host->host_no, fas216_target(info),
+				fas216_get_last_msg(info, info->scsi.msgin_fifo));
+		}
+		break;
+
+	case NOP:
+		mstat = mstat_accept;
 		break;
 
 	case SIMPLE_QUEUE_TAG:
-		/* handled above */
+		/* handled above - print a warning since this is untested */
 		printk("scsi%d.%c: reconnect queue tag %02X\n",
 			info->host->host_no, fas216_target(info),
 			message[1]);
+		mstat = mstat_accept;
 		break;
 
 	case EXTENDED_MESSAGE:
-		switch (message[2]) {
-		case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
-			switch (info->device[info->SCpnt->target].negstate) {
-			case syncneg_invalid:
-				msgqueue_flush(&info->scsi.msgs);
-				outb(CMD_SETATN, REG_CMD(info));
-				msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-				info->scsi.phase = PHASE_MSGOUT;
+		if (msglen >= 3) {
+			switch (message[2]) {
+			case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+				mstat = fas216_handlesync(info, message);
+				break;
+
+			case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
+				mstat = fas216_handlewide(info, message);
 				break;
 
 			default:
-				if (message[4] > info->ifcfg.sync_max_depth)
-					message[4] = info->ifcfg.sync_max_depth;
-				if (message[3] < 1000 / info->ifcfg.clockrate)
-					message[3] = 1000 / info->ifcfg.clockrate;
-
-				outb(CMD_SETATN, REG_CMD(info));
-				msgqueue_addmsg(&info->scsi.msgs, 5,
-						EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
-						message[3], message[4]);
-				info->scsi.phase = PHASE_MSGOUT;
-			case syncneg_sent:
-				info->device[info->SCpnt->target].negstate = syncneg_complete;
-				info->device[info->SCpnt->target].period = message[3];
-				info->device[info->SCpnt->target].sof = message[4];
-				info->device[info->SCpnt->target].stp =
-					fas216_syncperiod(info, message[3] * 4);
-				printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n",
-					info->host->host_no, fas216_target(info), message[4], message[3] * 4);
-				fas216_set_sync(info, info->SCpnt->target);
 				break;
 			}
-			break;
-
-		case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
-			/* We don't do wide transfers - reject message */
-		default:
-			printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
-				info->host->host_no, fas216_target(info),
-				message[2]);
-			msgqueue_flush(&info->scsi.msgs);
-			outb(CMD_SETATN, REG_CMD(info));
-			msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-			info->scsi.phase = PHASE_MSGOUT;
-			break;
 		}
 		break;
 
 	default:
-		printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
-			info->host->host_no, fas216_target(info),
-			message[0]);
-		msgqueue_flush(&info->scsi.msgs);
+		break;
+	}
+	return mstat;
+}
+
+/* Function: void fas216_message(FAS216_Info *info)
+ * Purpose : handle a function done interrupt from FAS216 chip
+ * Params  : info - interface which caused function done interrupt
+ */
+static void fas216_message(FAS216_Info *info)
+{
+	int i;
+
+	switch (fas216___message(info)) {
+	case mstat_unrecognised:
+		printk("scsi%d.%c: unrecognised message, rejecting\n",
+			info->host->host_no, fas216_target(info));
+		printk("scsi%d.%c: message was", info->host->host_no,
+			fas216_target(info));
+		for (i = 0; i < info->scsi.msglen; i++)
+			printk("%s%02X", i & 31 ? " " : "\n  ",
+				info->scsi.message[i]);
+		printk("\n");
+
+	case mstat_reject:
+		/*
+		 * Something strange seems to be happening here -
+		 * I can't use SETATN since the chip gives me an
+		 * invalid command interrupt when I do.  Weird.
+		 */
+outb(CMD_NOP, REG_CMD(info));
+fas216_dumpstate(info);
 		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
 		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-		info->scsi.phase = PHASE_MSGOUT;
-		break;
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+fas216_dumpstate(info);
+
+	case mstat_accept:
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
 	}
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
 
 /* Function: void fas216_send_command(FAS216_Info *info)
@@ -935,308 +1285,266 @@
 	outb(CMD_FLUSHFIFO, REG_CMD(info));
 
 	/* load command */
-	for (i = 0; i < info->SCpnt->cmd_len; i++)
+	for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++)
 		outb(info->SCpnt->cmnd[i], REG_FF(info));
 
 	outb(CMD_TRANSFERINFO, REG_CMD(info));
+
+	info->scsi.phase = PHASE_COMMAND;
 }
 
-/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in selection phase
+/* Function: void fas216_send_messageout(FAS216_Info *info, int start)
+ * Purpose : handle bus service to send a message
  * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
+ * Note    : We do not allow the device to change the data direction!
  */
-static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
+static void fas216_send_messageout(FAS216_Info *info, int start)
 {
-	fas216_checkmagic(info, "fas216_busservice_selection");
-
-	switch (stat & STAT_BUSMASK) {
-	case STAT_DATAOUT:	/* data out phase			*/
-		fas216_starttransfer(info, DMA_OUT, 1);
-		return 1;
-
-	case STAT_DATAIN:		/* data in phase		*/
-		fas216_starttransfer(info, DMA_IN, 0);
-		return 1;
+	unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs);
 
-	case STAT_STATUS:		/* status phase			*/
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	fas216_checkmagic(info, "fas216_send_messageout");
 
-	case STAT_MESGIN:		/* message in phase		*/
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
-
-	case STAT_MESGOUT:{		/* message out phase		*/
-		char *msg;
-		int start = 1, msglen;
+	outb(CMD_FLUSHFIFO, REG_CMD(info));
 
-		/* load message bytes, but don't forget to miss the first
-		 * byte!
-		 */
-		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+	if (tot_msglen) {
+		struct message *msg;
+		int msgnr = 0;
+#ifdef DEBUG_MESSAGES
+		printk("scsi%d.%c: message out: ",
+		       info->host->host_no, fas216_target(info));
+#endif
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
 			int i;
+#ifdef DEBUG_MESSAGES
+			printk("{ ");
+#endif
+			for (i = start; i < msg->length; i++) {
+#ifdef DEBUG_MESSAGES
+				printk("%02X ", msg->msg[i]);
+#endif
+				outb(msg->msg[i], REG_FF(info));
+			}
+#ifdef DEBUG_MESSAGES
+			printk("} ");
+#endif
 
-			for (i = start; i < msglen; i++)
-				outb(msg[i], REG_FF(info));
+			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
 			start = 0;
 		}
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		info->scsi.phase = PHASE_MESSAGESENT;
-		return 1;
-	}
-	default:
-		return 0;
-	}
-}
-
-/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service after the IDENTIFY message has been sent
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- */
-static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_messagesent");
-
-	switch (stat & STAT_BUSMASK) {
-	case STAT_MESGIN:		/* message in phase		*/
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+#ifdef DEBUG_MESSAGES
+		printk("\n");
+#endif
+	} else
+		outb(NOP, REG_FF(info));
 
-	case STAT_COMMAND:		/* command phase		*/
-		fas216_send_command(info);
-		return 1;
+	outb(CMD_TRANSFERINFO, REG_CMD(info));
 
-	default:
-		return 0;
-	}
+	info->scsi.phase = PHASE_MSGOUT;
 }
 
-/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in a data in/out phase.
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
+/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+ * Purpose : handle a bus service interrupt from FAS216 chip
+ * Params  : info - interface which caused bus service interrupt
+ *           stat - Status register contents
+ *           ssr  - SCSI Status register contents
  */
-static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+static void
+fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
 {
-	fas216_checkmagic(info, "fas216_busservice_dataphase");
+	fas216_checkmagic(info, "fas216_busservice_intr");
 
-	switch (stat & STAT_BUSMASK) {
-	case STAT_DATAIN:		/* continue data in phase	*/
-		if (info->scsi.phase == PHASE_DATAIN) {
-			fas216_starttransfer(info, DMA_IN, 0);
-			return 1;
-		} else
-			return 0;
+#ifdef DEBUG_BUSSERVICE
+	printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
+		info->host->host_no, fas216_target(info), stat, ssr,
+		info->scsi.phase);
+#endif
 
-	case STAT_DATAOUT:		/* continue data out phase	*/
-		if (info->scsi.phase == PHASE_DATAOUT) {
-			fas216_starttransfer(info, DMA_OUT, 0);
-			return 1;
-		} else
-			return 0;
+	switch (info->scsi.phase) {
+	case PHASE_SELECTION:
+		if ((ssr & IS_BITS) != 1)
+			goto bad_is;
+		break;
 
-	case STAT_STATUS:		/* status in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	case PHASE_SELSTEPS:
+		switch (ssr & IS_BITS) {
+		case IS_SELARB:
+		case IS_MSGBYTESENT:
+			goto bad_is;
+
+		case IS_NOTCOMMAND:
+		case IS_EARLYPHASE:
+			if ((stat & STAT_BUSMASK) == STAT_MESGIN)
+				break;
+			goto bad_is;
 
-	case STAT_MESGIN:		/* message in phase		*/
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+		case IS_COMPLETE:
+			break;
+		}
 
 	default:
-		return 0;
+		break;
 	}
-}
 
-/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in after a reconnection
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_reconnected");
+	outb(CMD_NOP, REG_CMD(info));
 
-	switch (stat & STAT_BUSMASK) {
-	case STAT_MESGIN:
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		return 1;
+	/*
+	 * s2r10 5.1.9.2 para 5
+	 * If the target accepts the messages sent during a message out
+	 * phase, it will change to a new phase.
+	 */
+	if (info->scsi.phase == PHASE_MSGOUT &&
+	    (stat & STAT_BUSMASK) != STAT_MESGOUT)
+		msgqueue_flush(&info->scsi.msgs);
 
-	case STAT_STATUS:
+#define STATE(st,ph) ((ph) << 3 | (st))
+	/*
+	 * This table describes the legal SCSI state transitions,
+	 * as described by the SCSI II spec.
+	 */
+	switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) {
+						/* Reselmsgin   -> Data In	*/
+	case STATE(STAT_DATAIN, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		return 1;
+	case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_DATAIN):  /* Data In      -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_MSGOUT):  /* Message Out  -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command      -> Data In      */
+	case STATE(STAT_DATAIN, PHASE_MSGIN):   /* Message In   -> Data In      */
+		fas216_starttransfer(info, DMA_IN, 0);
+		return;
+
+	case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out     -> Data Out     */
+		fas216_starttransfer(info, DMA_OUT, 0);
+		return;
 
-	case STAT_DATAOUT:		/* data out phase		*/
+						/* Reselmsgin   -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
+	case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out  -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command      -> Data Out     */
+	case STATE(STAT_DATAOUT, PHASE_MSGIN):  /* Message In   -> Data Out     */
 		fas216_starttransfer(info, DMA_OUT, 1);
-		return 1;
+		return;
 
-	case STAT_DATAIN:		/* data in phase		*/
+						/* Reselmsgin   -> Status       */
+	case STATE(STAT_STATUS, PHASE_RECONNECTED):
 		fas216_finish_reconnect(info);
-		fas216_starttransfer(info, DMA_IN, 0);
-		return 1;
-
-	default:
-		return 0;
-	}
-}
-
-/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service to send a message
- * Params  : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note    : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
-{
-	fas216_checkmagic(info, "fas216_busservice_messageout");
-
-	if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
-		printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
-		       info->host->host_no, fas216_target(info));
-		return 0;
-	} else {
-		unsigned int msglen = msgqueue_msglength(&info->scsi.msgs);
+		goto status;
+	case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out     -> Status       */
+	case STATE(STAT_STATUS, PHASE_DATAIN):  /* Data In      -> Status       */
+		fas216_stoptransfer(info);
+	case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status       */
+	case STATE(STAT_STATUS, PHASE_MSGOUT):  /* Message Out  -> Status       */
+	case STATE(STAT_STATUS, PHASE_COMMAND): /* Command      -> Status       */
+	case STATE(STAT_STATUS, PHASE_MSGIN):   /* Message In   -> Status       */
+	status:
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		info->scsi.phase = PHASE_STATUS;
+		return;
 
+	case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out     -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_DATAIN):  /* Data In      -> Message In   */
+		fas216_stoptransfer(info);
+	case STATE(STAT_MESGIN, PHASE_COMMAND):	/* Command	-> Message In	*/
+	case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_MSGOUT):  /* Message Out  -> Message In   */
+		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
 		outb(CMD_FLUSHFIFO, REG_CMD(info));
-
-		if (msglen == 0)
-			outb(NOP, REG_FF(info));
-		else {
-			char *msg;
-
-			while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-				int i;
-
-				for (i = 0; i < msglen; i++)
-					outb(msg[i], REG_FF(info));
-			}
-		}
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		info->scsi.phase = PHASE_AFTERMSGOUT;
-		return 1;
-	}
-}
-
-/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
- * Purpose : handle a bus service interrupt from FAS216 chip
- * Params  : info - interface which caused bus service interrupt
- *           stat - Status register contents
- *           ssr  - SCSI Status register contents
- */
-static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
-{
-	fas216_checkmagic(info, "fas216_busservice_intr");
-
-#ifdef DEBUG_BUSSERVICE
-	printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
-		info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
-#endif
-	switch (ssr & IS_BITS) {
-	case IS_MSGBYTESENT:		/* select with ATN and stop steps completed	*/
-	case IS_COMPLETE:			/* last action completed		*/
-		outb(CMD_NOP, REG_CMD(info));
-
-		switch (info->scsi.phase) {
-		case PHASE_SELECTION:		/* while selecting - selected target	*/
-			if (!fas216_busservice_selection(info, stat))
-				printk("scsi%d.%c: bus phase %s after connect?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_MESSAGESENT:
-			if (!fas216_busservice_messagesent(info, stat))
-				printk("scsi%d.%c: bus phase %s after message sent?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_DATAIN:		/* while transfering data in		*/
-		case PHASE_DATAOUT:		/* while transfering data out		*/
-			if (!fas216_busservice_dataphase(info, stat))
-				printk("scsi%d.%c: bus phase %s after %s?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat), fas216_drv_phase(info));
-			break;
-
-		case PHASE_RECONNECTED:		/* newly reconnected device		*/
-			/*
-			 * Command reconnected - if MESGIN, get message - it may be
-			 * the tag.  If not, get command out of the disconnected queue
-			 */
-			if (!fas216_busservice_reconnected(info, stat))
-				printk("scsi%d.%c: bus phase %s after reconnect?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
-
-		case PHASE_MSGIN:
-		case PHASE_AFTERMSGOUT:
-			switch (stat & STAT_BUSMASK) {
-			case STAT_MESGIN:
-				info->scsi.phase = PHASE_MSGIN;
-				outb(CMD_TRANSFERINFO, REG_CMD(info));
-				break;
+		info->scsi.phase = PHASE_MSGIN;
+		return;
 
-			case STAT_COMMAND:	/* command phase			*/
-				fas216_send_command(info);
-				info->scsi.phase = PHASE_SELECTION;
-				break;
+						/* Reselmsgin   -> Message In   */
+	case STATE(STAT_MESGIN, PHASE_RECONNECTED):
+	case STATE(STAT_MESGIN, PHASE_MSGIN):
+		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		return;
 
-			default:
-				printk("scsi%d.%c: bus phase %s after %s?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat),
-					fas216_drv_phase(info));
-			}
-			break;
+						/* Reselmsgin   -> Command      */
+	case STATE(STAT_COMMAND, PHASE_RECONNECTED):
+		fas216_finish_reconnect(info);
+	case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out  -> Command      */
+	case STATE(STAT_COMMAND, PHASE_MSGIN):  /* Message In   -> Command      */
+		fas216_send_command(info);
+		info->scsi.phase = PHASE_COMMAND;
+		return;
+						/* Selection    -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_SELECTION):
+		fas216_send_messageout(info, 1);
+		return;
+						/* Any          -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT):
+		fas216_send_messageout(info, 0);
+		return;
 
-		case PHASE_MSGOUT:
-			if (!fas216_busservice_messageout(info, stat))
-				printk("scsi%d.%c: bus phase %s instead of message out?\n",
-					info->host->host_no, fas216_target(info),
-					fas216_bus_phase(stat));
-			break;
+	/* Error recovery rules.
+	 *   These either attempt to abort or retry the operation.
+	 * TODO: we need more of these
+	 */
+	case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command      -> Command      */
+		/* error - we've sent out all the command bytes
+		 * we have.
+		 * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS
+		 * to include the command bytes sent for this to work
+		 * correctly.
+		 */
+		printk(KERN_ERR "scsi%d.%c: "
+			"target trying to receive more command bytes\n",
+			info->host->host_no, fas216_target(info));
+		outb(CMD_SETATN, REG_CMD(info));
+		outb(15, REG_STCL(info));
+		outb(0, REG_STCM(info));
+		outb(0, REG_STCH(info));
+		outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info));
+		msgqueue_flush(&info->scsi.msgs);
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		return;
 
-		case PHASE_DISCONNECT:
-			printk("scsi%d.%c: disconnect message received, but bus service %s?\n",
-				info->host->host_no, fas216_target(info),
-				fas216_bus_phase(stat));
+						/* Selection    -> Message Out  */
+	case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
+	case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out  -> Message Out  */
+		/* If we get another message out phase, this
+		 * usually means some parity error occurred.
+		 * Resend complete set of messages.  If we have
+		 * more than 1 byte to send, we need to assert
+		 * ATN again.
+		 */
+		if (msgqueue_msglength(&info->scsi.msgs) > 1)
 			outb(CMD_SETATN, REG_CMD(info));
-			msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
-			info->scsi.phase = PHASE_MSGOUT;
-			info->scsi.aborting = 1;
-			outb(CMD_TRANSFERINFO, REG_CMD(info));
-			break;
 
-		default:
-			printk("scsi%d.%c: internal phase %s for bus service?"
-				"  What do I do with this?\n",
-				info->host->host_no, fas216_target(info),
-				fas216_drv_phase(info));
-		}
-		break;
+		fas216_send_messageout(info, 0);
+		return;
+	}
 
-	default:
-		printk("scsi%d.%c: bus service at step %d?\n",
+	if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) {
+		printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n",
 			info->host->host_no, fas216_target(info),
-			ssr & IS_BITS);
+			fas216_bus_phase(stat));
+		msgqueue_flush(&info->scsi.msgs);
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		info->scsi.aborting = 1;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		return;
 	}
+	printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n",
+		info->host->host_no, fas216_target(info),
+		fas216_bus_phase(stat),
+		fas216_drv_phase(info));
+	print_debug_list();
+	return;
+
+bad_is:
+	printk("scsi%d.%c: bus service at step %d?\n",
+		info->host->host_no, fas216_target(info),
+		ssr & IS_BITS);
+	print_debug_list();
+
+	fas216_done(info, DID_ERROR);
 }
 
 /* Function: void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
@@ -1269,6 +1577,7 @@
 	case PHASE_MSGIN:			/* message in phase			*/
 	case PHASE_RECONNECTED:			/* reconnected command			*/
 		if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+			info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
 			fas216_message(info);
 			break;
 		}
@@ -1300,10 +1609,11 @@
 
 	if (stat & STAT_INT) {
 		if (isr & INST_BUSRESET)
-			printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
-		else if (isr & INST_ILLEGALCMD)
+			printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no);
+		else if (isr & INST_ILLEGALCMD) {
 			printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
-		else if (isr & INST_DISCONNECT)
+			fas216_dumpstate(info);
+		} else if (isr & INST_DISCONNECT)
 			fas216_disconnect_intr(info);
 		else if (isr & INST_RESELECTED)		/* reselected			*/
 			fas216_reselected_intr(info);
@@ -1327,7 +1637,7 @@
 static void fas216_kick(FAS216_Info *info)
 {
 	Scsi_Cmnd *SCpnt;
-	int i, msglen, from_queue = 0;
+	int tot_msglen, from_queue = 0;
 
 	fas216_checkmagic(info, "fas216_kick");
 
@@ -1380,7 +1690,8 @@
 
 	if (from_queue) {
 #ifdef SCSI2_TAG
-		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) {
+		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE &&
+		    SCpnt->cmnd[0] != INQUIRY) {
 		    SCpnt->device->current_tag += 1;
 			if (SCpnt->device->current_tag == 0)
 			    SCpnt->device->current_tag = 1;
@@ -1409,6 +1720,7 @@
 
 	/* build outgoing message bytes */
 	msgqueue_flush(&info->scsi.msgs);
+
 	if (info->device[SCpnt->target].disconnect_ok)
 		msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
 	else
@@ -1418,15 +1730,29 @@
 	if (SCpnt->tag)
 		msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
 
-	/* add synchronous negociation */
-	if (SCpnt->cmnd[0] == REQUEST_SENSE &&
-	    info->device[SCpnt->target].negstate == syncneg_start) {
-		info->device[SCpnt->target].negstate = syncneg_sent;
+#ifdef SCSI2_WIDE
+	if (info->device[SCpnt->target].wide_state == neg_wait) {
+		info->device[SCpnt->target].wide_state = neg_inprogress;
+		msgqueue_addmsg(&info->scsi.msgs, 4,
+				EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+				info->ifcfg.wide_max_size);
+	}
+#ifdef SCSI2_SYNC
+	else
+#endif
+#endif
+#ifdef SCSI2_SYNC
+	if ((info->device[SCpnt->target].sync_state == neg_wait ||
+	     info->device[SCpnt->target].sync_state == neg_complete) &&
+	    (SCpnt->cmnd[0] == REQUEST_SENSE ||
+	     SCpnt->cmnd[0] == INQUIRY)) {
+		info->device[SCpnt->target].sync_state = neg_inprogress;
 		msgqueue_addmsg(&info->scsi.msgs, 5,
 				EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
 				1000 / info->ifcfg.clockrate,
 				info->ifcfg.sync_max_depth);
 	}
+#endif
 
 	/* following what the ESP driver says */
 	outb(0, REG_STCL(info));
@@ -1444,25 +1770,29 @@
 	/* synchronous transfers */
 	fas216_set_sync(info, SCpnt->target);
 
-	msglen = msgqueue_msglength(&info->scsi.msgs);
+	tot_msglen = msgqueue_msglength(&info->scsi.msgs);
 
-	if (msglen == 1 || msglen == 3) {
+	if (tot_msglen == 1 || tot_msglen == 3) {
 		/*
 		 * We have an easy message length to send...
 		 */
-		char *msg;
+		struct message *msg;
+		int msgnr = 0, i;
+
+		info->scsi.phase = PHASE_SELSTEPS;
 
 		/* load message bytes */
-		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
-			for (i = 0; i < msglen; i++)
-				outb(msg[i], REG_FF(info));
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+			for (i = 0; i < msg->length; i++)
+				outb(msg->msg[i], REG_FF(info));
+			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
 		}
 
 		/* load command */
 		for (i = 0; i < SCpnt->cmd_len; i++)
 			outb(SCpnt->cmnd[i], REG_FF(info));
 
-		if (msglen == 1)
+		if (tot_msglen == 1)
 			outb(CMD_SELECTATN, REG_CMD(info));
 		else
 			outb(CMD_SELECTATN3, REG_CMD(info));
@@ -1471,17 +1801,11 @@
 		 * We have an unusual number of message bytes to send.
 		 *  Load first byte into fifo, and issue SELECT with ATN and
 		 *  stop steps.
-		 * Note: we only peek at t his message - we need the rest
-		 * later on!
 		 */
-		int thismsg;
-		char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg);
+		struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
 
-		if (!msg || thismsg < 1)
-			printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n",
-				info->host->host_no, fas216_target(info), msglen);
-		else
-			outb(msg[0], REG_FF(info));
+		outb(msg->msg[0], REG_FF(info));
+		msg->fifo = 1;
 
 		outb(CMD_SELECTATNSTOP, REG_CMD(info));
 	}
@@ -1525,11 +1849,15 @@
 		/*
 		 * In theory, this should not happen, but just in case it does.
 		 */
-		if (info->scsi.SCp.ptr && result == DID_OK) {
+		if (info->scsi.SCp.ptr &&
+		    info->scsi.SCp.this_residual &&
+		    result == DID_OK) {
 			switch (SCpnt->cmnd[0]) {
 			case INQUIRY:
 			case START_STOP:
 			case READ_CAPACITY:
+			case TEST_UNIT_READY:
+			case MODE_SENSE:
 				break;
 
 			default:
@@ -1579,6 +1907,7 @@
 int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	unsigned long flags;
 
 	fas216_checkmagic(info, "fas216_queue_command");
 
@@ -1618,27 +1947,18 @@
 	info->stats.queues += 1;
 	SCpnt->tag = 0;
 
-	if (info->scsi.irq != NO_IRQ) {
-		unsigned long flags;
-
-		/* add command into execute queue and let it complete under
-		 * the drivers interrupts.
-		 */
-		if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
-			SCpnt->result = DID_ERROR << 16;
-			done(SCpnt);
-		}
-		save_flags_cli(flags);
-		if (!info->SCpnt || info->scsi.disconnectable)
-			fas216_kick(info);
-		restore_flags(flags);
-	} else {
-		/* no interrupts to rely on - we'll have to handle the
-		 * command ourselves.  For now, we give up.
-		 */
+	/* add command into execute queue and let it complete under
+	 * whatever scheme we're using.
+	 */
+	if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
 		SCpnt->result = DID_ERROR << 16;
 		done(SCpnt);
 	}
+	save_flags_cli(flags);
+	if (!info->SCpnt || info->scsi.disconnectable)
+		fas216_kick(info);
+	restore_flags(flags);
+
 	return 0;
 }
 
@@ -1672,15 +1992,28 @@
 
 	/*
 	 * This wastes time, since we can't return until the command is
-	 * complete. We can't seep either since we may get re-entered!
+	 * complete. We can't sleep either since we may get re-entered!
 	 * However, we must re-enable interrupts, or else we'll be
 	 * waiting forever.
 	 */
 	save_flags(flags);
 	sti();
 
-	while (!info->internal_done)
-		barrier();
+	while (!info->internal_done) {
+		/*
+		 * If we don't have an IRQ, then we must
+		 * poll the card for it's interrupt, and
+		 * use that to call this driver's interrupt
+		 * routine.  That way, we keep the command
+		 * progressing.
+		 */
+		if (info->scsi.irq == NO_IRQ) {
+			sti();
+			while (!(inb(REG_STAT(info)) & STAT_INT));
+			cli();
+			fas216_intr(info->host);
+		}
+	}
 
 	restore_flags(flags);
 
@@ -1688,7 +2021,7 @@
 }
 
 /* Prototype: void fas216_reportstatus(Scsi_Cmnd **SCpntp1,
- *				       Scsi_Cmnd **SCpntp2, int result)
+ *				       Scsi_Cmnd **SCpntp2, int result, int no_report)
  * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
  * Params   : SCpntp1 - pointer to command to return
  *	      SCpntp2 - pointer to command to check
@@ -1697,7 +2030,7 @@
  *	      structure as *SCpntp2.
  */
 static void fas216_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2,
-				int result)
+				int result, int no_report)
 {
 	Scsi_Cmnd *SCpnt = *SCpntp1;
 
@@ -1705,7 +2038,8 @@
 		*SCpntp1 = NULL;
 
 		SCpnt->result = result;
-		SCpnt->scsi_done(SCpnt);
+		if (!no_report || SCpnt != *SCpntp2)
+			SCpnt->scsi_done(SCpnt);
 	}
 
 	if (SCpnt == *SCpntp2)
@@ -1752,6 +2086,95 @@
 	return FAILED;
 }
 
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+ * Purpose  : abort a command on this host
+ * Params   : SCpnt - command to abort
+ * Returns  : abort status
+ */
+static enum res_abort
+fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+	enum res_abort res = res_not_running;
+
+	if (queue_removecmd(&info->queues.issue, SCpnt)) {
+		/*
+		 * The command was on the issue queue, and has not been
+		 * issued yet.  We can remove the command from the queue,
+		 * and acknowledge the abort.  Neither the devices nor the
+		 * interface know about the command.
+		 */
+		printk("on issue queue ");
+
+		res = res_success;
+	} else if (queue_removecmd(&info->queues.disconnected, SCpnt)) {
+		/*
+		 * The command was on the disconnected queue.  Simply
+		 * acknowledge the abort condition, and when the target
+		 * reconnects, we will give it an ABORT message.  The
+		 * target should then disconnect, and we will clear
+		 * the busylun bit.
+		 */
+		printk("on disconnected queue ");
+
+		res = res_success;
+	} else if (info->SCpnt == SCpnt) {
+		unsigned long flags;
+
+		printk("executing ");
+
+		save_flags(flags);
+		cli();
+		switch (info->scsi.phase) {
+		/*
+		 * If the interface is idle, and the command is 'disconnectable',
+		 * then it is the same as on the disconnected queue.  We simply
+		 * remove all traces of the command.  When the target reconnects,
+		 * we will give it an ABORT message since the command could not
+		 * be found.  When the target finally disconnects, we will clear
+		 * the busylun bit.
+		 */
+		case PHASE_IDLE:
+			if (info->scsi.disconnectable) {
+				info->scsi.disconnectable = 0;
+				info->SCpnt = NULL;
+				res = res_success;
+			}
+			break;
+
+		/*
+		 * If the command has connected and done nothing futher,
+		 * simply force a disconnect.  We also need to clear the
+		 * busylun bit.
+		 */
+		case PHASE_SELECTION:
+//			info->SCpnt = NULL;
+//			res = res_success_clear;
+//			break;
+
+		default:
+			res = res_snooze;
+			break;
+		}
+		restore_flags(flags);
+	} else if (info->origSCpnt == SCpnt) {
+		/*
+		 * The command will be executed next, but a command
+		 * is currently using the interface.  This is similar to
+		 * being on the issue queue, except the busylun bit has
+		 * been set.
+		 */
+		info->origSCpnt = NULL;
+		printk("waiting for execution ");
+		res = res_success_clear;
+	} else
+		printk("unknown ");
+
+	return res;
+}
+
 /* Function: int fas216_abort(Scsi_Cmnd *SCpnt)
  * Purpose : abort a command if something horrible happens.
  * Params  : SCpnt - Command that is believed to be causing a problem.
@@ -1769,46 +2192,51 @@
 	print_debug_list();
 	fas216_dumpstate(info);
 	fas216_dumpinfo(info);
-	printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
 
-	do {
-		/* If command is waiting in the issue queue, then we can
-		 * simply remove the command and return abort status
-		 */
-		if (queue_removecmd(&info->queues.issue, SCpnt)) {
-			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done(SCpnt);
-			printk("command on issue queue");
-			result = SCSI_ABORT_SUCCESS;
-			break;
-		}
+	printk(KERN_WARNING "scsi%d: abort ", info->host->host_no);
 
-		/* If the command is on the disconencted queue, we need to
-		 * reconnect to the device
-		 */
-		if (queue_cmdonqueue(&info->queues.disconnected, SCpnt))
-			printk("command on disconnected queue");
+	switch (fas216_do_abort(info, SCpnt)) {
+	/*
+	 * We managed to find the command and cleared it out.
+	 * We do not expect the command to be executing on the
+	 * target, but we have set the busylun bit.
+	 */
+	case res_success_clear:
+		printk("clear ");
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
 
-		/* If the command is connected, we need to flag that the
-		 * command needs to be aborted
-		 */
-		if (info->SCpnt == SCpnt)
-			printk("command executing");
+	/*
+	 * We found the command, and cleared it out.  Either
+	 * the command is still known to be executing on the
+	 * target, or the busylun bit is not set.
+	 */
+	case res_success:
+		printk("success\n");
+		SCpnt->result = DID_ABORT << 16;
+		SCpnt->scsi_done(SCpnt);
+		result = SCSI_ABORT_SUCCESS;
+		break;
 
-		/* If the command is pending for execution, then again
-		 * this is simple - we remove it and report abort status
-		 */
-		if (info->origSCpnt == SCpnt) {
-			info->origSCpnt = NULL;
-			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done(SCpnt);
-			printk("command waiting for execution");
-			result = SCSI_ABORT_SUCCESS;
-			break;
-		}
-	} while (0);
+	/*
+	 * We did find the command, but unfortunately we couldn't
+	 * unhook it from ourselves.  Wait some more, and if it
+	 * still doesn't complete, reset the interface.
+	 */
+	case res_snooze:
+		printk("snooze\n");
+		result = SCSI_ABORT_SNOOZE;
+		break;
 
-	printk("\n");
+	/*
+	 * The command could not be found (either because it completed,
+	 * or it got dropped.
+	 */
+	default:
+	case res_not_running:
+		result = SCSI_ABORT_SNOOZE;
+		printk("not running\n");
+		break;
+	}
 
 	return result;
 }
@@ -1819,7 +2247,7 @@
  */
 static void fas216_reset_state(FAS216_Info *info)
 {
-	syncneg_t negstate;
+	neg_t sync_state, wide_state;
 	int i;
 
 	fas216_checkmagic(info, "fas216_reset_state");
@@ -1833,26 +2261,37 @@
 	info->scsi.reconnected.lun = 0;
 	info->scsi.reconnected.tag = 0;
 	info->scsi.disconnectable = 0;
-	info->scsi.last_message = 0;
 	info->scsi.aborting = 0;
 	info->scsi.phase = PHASE_IDLE;
-	info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+	info->scsi.async_stp =
+			fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+	if (info->ifcfg.wide_max_size == 0)
+		wide_state = neg_invalid;
+	else
+#ifdef SCSI2_WIDE
+		wide_state = neg_wait;
+#else
+		wide_state = neg_invalid;
+#endif
 
 	if (info->host->dma_channel == NO_DMA || !info->dma.setup)
-		negstate = syncneg_invalid;
+		sync_state = neg_invalid;
 	else
 #ifdef SCSI2_SYNC
-		negstate = syncneg_start;
+		sync_state = neg_wait;
 #else
-		negstate = syncneg_invalid;
+		sync_state = neg_invalid;
 #endif
 
 	for (i = 0; i < 8; i++) {
-		info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
-		info->device[i].negstate = negstate;
-		info->device[i].period = info->ifcfg.asyncperiod / 4;
-		info->device[i].stp = info->scsi.async_stp;
-		info->device[i].sof = 0;
+		info->device[i].disconnect_ok	= info->ifcfg.disconnect_ok;
+		info->device[i].sync_state	= sync_state;
+		info->device[i].wide_state	= wide_state;
+		info->device[i].period		= info->ifcfg.asyncperiod / 4;
+		info->device[i].stp		= info->scsi.async_stp;
+		info->device[i].sof		= 0;
+		info->device[i].wide_xfer	= 0;
 	}
 }
 
@@ -1887,6 +2326,7 @@
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 	Scsi_Cmnd *SCptr;
 	int result = 0;
+	int synchronous = reset_flags & SCSI_RESET_SYNCHRONOUS;
 
 	fas216_checkmagic(info, "fas216_reset");
 
@@ -1908,9 +2348,11 @@
 	info->stats.resets += 1;
 
 	print_debug_list();
-	printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+	printk(KERN_WARNING "scsi%d: reset ", info->host->host_no);
 	if (SCpnt)
-		printk(" for target %d ", SCpnt->target);
+		printk("for target %d ", SCpnt->target);
+
+	printk("\n");
 
 	outb(info->scsi.cfg[3], REG_CNTL3(info));
 
@@ -1944,10 +2386,10 @@
 	/*
 	 * Signal all commands in progress have been reset
 	 */
-	fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16);
+	fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16, synchronous);
 
 	while ((SCptr = queue_remove(&info->queues.disconnected)) != NULL)
-		fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16);
+		fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16, synchronous);
 
 	if (SCpnt) {
 		/*
@@ -1960,11 +2402,10 @@
 		queue_removecmd(&info->queues.issue, SCpnt);
 
 		SCpnt->result = DID_RESET << 16;
-		SCpnt->scsi_done(SCpnt);
+		if (!synchronous)
+			SCpnt->scsi_done(SCpnt);
 	}
 
-	printk("\n");
-
 	return result | SCSI_RESET_SUCCESS;
 }
 
@@ -2083,6 +2524,49 @@
 	return 0;
 }
 
+int fas216_print_stats(FAS216_Info *info, char *buffer)
+{
+	return sprintf(buffer,
+			"Queued commands: %-10u   Issued commands: %-10u\n"
+			"Done commands  : %-10u   Reads          : %-10u\n"
+			"Writes         : %-10u   Others         : %-10u\n"
+			"Disconnects    : %-10u   Aborts         : %-10u\n"
+			"Resets         : %-10u\n",
+			info->stats.queues,	 info->stats.removes,
+			info->stats.fins,	 info->stats.reads,
+			info->stats.writes,	 info->stats.miscs,
+			info->stats.disconnects, info->stats.aborts,
+			info->stats.resets);
+}
+
+int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer)
+{
+	struct fas216_device *dev = &info->device[scd->id];
+	int len = 0;
+	char *p;
+
+	proc_print_scsidevice(scd, buffer, &len, 0);
+	p = buffer + len;
+
+	p += sprintf(p, "  Extensions: ");
+
+	if (scd->tagged_supported)
+		p += sprintf(p, "TAG %sabled [%d] ",
+			     scd->tagged_queue ? "en" : "dis",
+			     scd->current_tag);
+
+	p += sprintf(p, "\n  Transfers : %d-bit ",
+		     8 << dev->wide_xfer);
+
+	if (dev->sof)
+		p += sprintf(p, "sync offset %d, %d ns\n",
+				dev->sof, dev->period * 4);
+	else
+		p += sprintf(p, "async\n");
+
+	return p - buffer;
+}
+
 EXPORT_SYMBOL(fas216_init);
 EXPORT_SYMBOL(fas216_abort);
 EXPORT_SYMBOL(fas216_reset);
@@ -2094,7 +2578,8 @@
 EXPORT_SYMBOL(fas216_eh_device_reset);
 EXPORT_SYMBOL(fas216_eh_bus_reset);
 EXPORT_SYMBOL(fas216_eh_host_reset);
-
+EXPORT_SYMBOL(fas216_print_stats);
+EXPORT_SYMBOL(fas216_print_device);
 
 #ifdef MODULE
 int init_module(void)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/fas216.h linux.18p17/drivers/acorn/scsi/fas216.h
--- linux.vanilla/drivers/acorn/scsi/fas216.h	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/fas216.h	Fri Sep 15 23:31:12 2000
@@ -40,6 +40,7 @@
 #define CMD_TRANSFERINFO	0x10
 #define CMD_INITCMDCOMPLETE	0x11
 #define CMD_MSGACCEPTED		0x12
+#define CMD_PADBYTES		0x18
 #define CMD_SETATN		0x1a
 #define CMD_RSETATN		0x1b
 
@@ -171,15 +172,17 @@
 typedef enum {
 	PHASE_IDLE,					/* we're not planning on doing anything	*/
 	PHASE_SELECTION,				/* selecting a device			*/
+	PHASE_SELSTEPS,					/* selection with command steps		*/
+	PHASE_COMMAND,					/* command sent				*/
 	PHASE_MESSAGESENT,				/* selected, and we're sending cmd	*/
 	PHASE_RECONNECTED,				/* reconnected				*/
 	PHASE_DATAOUT,					/* data out to device			*/
 	PHASE_DATAIN,					/* data in from device			*/
 	PHASE_MSGIN,					/* message in from device		*/
-	PHASE_MSGOUT,					/* message out to device		*/
-	PHASE_AFTERMSGOUT,				/* after message out phase		*/
+	PHASE_MSGIN_DISCONNECT,				/* disconnecting from bus		*/
+	PHASE_MSGOUT,					/* after message out phase		*/
+	PHASE_MSGOUT_EXPECT,				/* expecting message out		*/
 	PHASE_STATUS,					/* status from device			*/
-	PHASE_DISCONNECT,				/* disconnecting from bus		*/
 	PHASE_DONE					/* Command complete			*/
 } phase_t;
 
@@ -197,13 +200,15 @@
 } fasdmatype_t;
 
 typedef enum {
-	syncneg_start,					/* Negociate with device for Sync xfers	*/
-	syncneg_sent,					/* Sync Xfer negociation sent		*/
-	syncneg_complete,				/* Sync Xfer complete			*/
-	syncneg_invalid					/* Sync Xfer not supported		*/
-} syncneg_t;
+	neg_wait,					/* Negociate with device		*/
+	neg_inprogress,					/* Negociation sent			*/
+	neg_complete,					/* Negociation complete			*/
+	neg_targcomplete,				/* Target completed negociation		*/
+	neg_invalid					/* Negociation not supported		*/
+} neg_t;
 
 #define MAGIC	0x441296bdUL
+#define NR_MSGS	8
 
 typedef struct {
 	unsigned long		magic_start;
@@ -231,7 +236,9 @@
 		MsgQueue_t	msgs;			/* message queue for connected device	*/
 
 		unsigned int	async_stp;		/* Async transfer STP value		*/
-		unsigned short	last_message;		/* last message to be sent		*/
+		unsigned char	msgin_fifo;		/* bytes in fifo at time of message in	*/
+		unsigned char	message[256];		/* last message received from device	*/
+		unsigned int	msglen;			/* length of last message received	*/
 
 		unsigned char	disconnectable:1;	/* this command can be disconnected	*/
 		unsigned char	aborting:1;		/* aborting command			*/
@@ -255,6 +262,7 @@
 		unsigned char	clockrate;		/* clock rate of FAS device (MHz)	*/
 		unsigned char	select_timeout;		/* timeout (R5)				*/
 		unsigned char	sync_max_depth;		/* Synchronous xfer max fifo depth	*/
+		unsigned char	wide_max_size;		/* Maximum wide transfer size		*/
 		unsigned char	cntl3;			/* Control Reg 3			*/
 		unsigned int	asyncperiod;		/* Async transfer period (ns)		*/
 		unsigned int	disconnect_ok:1;	/* Disconnects allowed?			*/
@@ -267,12 +275,14 @@
 	} queues;
 
 	/* per-device info */
-	struct {
+	struct fas216_device {
 		unsigned char	disconnect_ok:1;	/* device can disconnect		*/
-		unsigned int	period;			/* sync xfer period (*4ns)		*/
+		unsigned char	period;			/* sync xfer period in (*4ns)		*/
 		unsigned char	stp;			/* synchronous transfer period		*/
 		unsigned char	sof;			/* synchronous offset register		*/
-		syncneg_t	negstate;		/* synchronous transfer mode		*/
+		unsigned char	wide_xfer;		/* currently negociated wide transfer	*/
+		neg_t		sync_state;		/* synchronous transfer mode		*/
+		neg_t		wide_state;		/* wide transfer mode			*/
 	} device[8];
 	unsigned char	busyluns[8];			/* array of bits indicating LUNs busy	*/
 
@@ -339,6 +349,9 @@
  * Returns : 0 on success
  */
 extern int fas216_release (struct Scsi_Host *instance);
+
+extern int fas216_print_stats(FAS216_Info *info, char *buffer);
+extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer);
 
 /* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
  * Purpose : abort this command
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/msgqueue.c linux.18p17/drivers/acorn/scsi/msgqueue.c
--- linux.vanilla/drivers/acorn/scsi/msgqueue.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/msgqueue.c	Fri Sep 15 23:31:12 2000
@@ -83,45 +83,25 @@
 	int length = 0;
 
 	for (mq = msgq->qe; mq; mq = mq->next)
-		length += mq->length;
+		length += mq->msg.length;
 
 	return length;
 }
 
 /*
- * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return a message & its length
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+ * Purpose : return a message
  * Params  : msgq   - queue to obtain message from
- *	     length - pointer to int for message length
+ *	   : msgno  - message number
  * Returns : pointer to message string, or NULL
  */
-char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
+struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
 {
 	struct msgqueue_entry *mq;
 
-	if ((mq = msgq->qe) != NULL) {
-		msgq->qe = mq->next;
-		mqe_free(msgq, mq);
-		*length = mq->length;
-	}
-
-	return mq ? mq->msg : NULL;
-}
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params  : msgq   - queue to obtain message from
- *         : length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
-{
-	struct msgqueue_entry *mq = msgq->qe;
-
-	*length = mq ? mq->length : 0;
+	for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--);
 
-	return mq ? mq->msg : NULL;
+	return mq ? &mq->msg : NULL;
 }
 
 /*
@@ -143,10 +123,11 @@
 
 		va_start(ap, length);
 		for (i = 0; i < length; i++)
-			mq->msg[i] = va_arg(ap, unsigned char);
+			mq->msg.msg[i] = va_arg(ap, unsigned char);
 		va_end(ap);
 
-		mq->length = length;
+		mq->msg.length = length;
+		mq->msg.fifo = 0;
 		mq->next = NULL;
 
 		mqp = &msgq->qe;
@@ -178,8 +159,7 @@
 EXPORT_SYMBOL(msgqueue_initialise);
 EXPORT_SYMBOL(msgqueue_free);
 EXPORT_SYMBOL(msgqueue_msglength);
-EXPORT_SYMBOL(msgqueue_getnextmsg);
-EXPORT_SYMBOL(msgqueue_peeknextmsg);
+EXPORT_SYMBOL(msgqueue_getmsg);
 EXPORT_SYMBOL(msgqueue_addmsg);
 EXPORT_SYMBOL(msgqueue_flush);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/msgqueue.h linux.18p17/drivers/acorn/scsi/msgqueue.h
--- linux.vanilla/drivers/acorn/scsi/msgqueue.h	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/msgqueue.h	Fri Sep 15 23:31:12 2000
@@ -6,9 +6,14 @@
 #ifndef MSGQUEUE_H
 #define MSGQUEUE_H
 
-struct msgqueue_entry {
+struct message {
     char msg[8];
     int length;
+    int fifo;
+};
+
+struct msgqueue_entry {
+    struct message msg;
     struct msgqueue_entry *next;
 };
 
@@ -21,60 +26,51 @@
 } MsgQueue_t;
 
 /*
- * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
  * Purpose : initialise a message queue
  * Params  : msgq - queue to initialise
  */
-extern void msgqueue_initialise (MsgQueue_t *msgq);
+extern void msgqueue_initialise(MsgQueue_t *msgq);
 
 /*
- * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
  * Purpose : free a queue
  * Params  : msgq - queue to free
  */
-extern void msgqueue_free (MsgQueue_t *msgq);
+extern void msgqueue_free(MsgQueue_t *msgq);
 
 /*
- * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
  * Purpose : calculate the total length of all messages on the message queue
  * Params  : msgq - queue to examine
  * Returns : number of bytes of messages in queue
  */
-extern int msgqueue_msglength (MsgQueue_t *msgq);
+extern int msgqueue_msglength(MsgQueue_t *msgq);
 
 /*
- * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
  * Purpose : return a message & its length
  * Params  : msgq   - queue to obtain message from
- *	     length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length);
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params  : msgq   - queue to obtain message from
- *         : length - pointer to int for message length
+ *         : msgno  - message number
  * Returns : pointer to message string, or NULL
  */
-extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length);
+extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno);
 
 /*
- * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
  * Purpose : add a message onto a message queue
  * Params  : msgq   - queue to add message on
  *	     length - length of message
  *	     ...    - message bytes
  * Returns : != 0 if successful
  */
-extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...);
+extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...);
 
 /*
- * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
  * Purpose : flush all messages from message queue
  * Params  : msgq - queue to flush
  */
-extern void msgqueue_flush (MsgQueue_t *msgq);
+extern void msgqueue_flush(MsgQueue_t *msgq);
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/powertec.c linux.18p17/drivers/acorn/scsi/powertec.c
--- linux.vanilla/drivers/acorn/scsi/powertec.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/powertec.c	Fri Sep 15 23:31:12 2000
@@ -114,6 +114,8 @@
 	powertecscsi_irqenable,
 	powertecscsi_irqdisable,
 	NULL,
+	NULL,
+	NULL,
 	NULL
 };
 
@@ -271,8 +273,9 @@
 		info->info.ifcfg.select_timeout	= 255;
 		info->info.ifcfg.asyncperiod	= POWERTEC_ASYNC_PERIOD;
 		info->info.ifcfg.sync_max_depth	= POWERTEC_SYNC_DEPTH;
-		info->info.ifcfg.cntl3		= CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+		info->info.ifcfg.cntl3		= /*CNTL3_BS8 |*/ CNTL3_FASTSCSI | CNTL3_FASTCLK;
 		info->info.ifcfg.disconnect_ok	= 1;
+		info->info.ifcfg.wide_max_size	= 0;
 		info->info.dma.setup		= powertecscsi_dma_setup;
 		info->info.dma.pseudo		= NULL;
 		info->info.dma.stop		= powertecscsi_dma_stop;
@@ -443,31 +446,12 @@
 			host->io_port, host->irq, host->dma_channel,
 			info->info.scsi.type, info->control.terms ? "on" : "off");
 
-	pos += sprintf(buffer+pos,
-			"Queued commands: %-10u   Issued commands: %-10u\n"
-			"Done commands  : %-10u   Reads          : %-10u\n"
-			"Writes         : %-10u   Others         : %-10u\n"
-			"Disconnects    : %-10u   Aborts         : %-10u\n"
-			"Resets         : %-10u\n",
-			info->info.stats.queues,      info->info.stats.removes,
-			info->info.stats.fins,        info->info.stats.reads,
-			info->info.stats.writes,      info->info.stats.miscs,
-			info->info.stats.disconnects, info->info.stats.aborts,
-			info->info.stats.resets);
+	pos += fas216_print_stats(&info->info, buffer + pos);
 
-	pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+	pos += sprintf (buffer+pos, "\nAttached devices:\n");
 
 	for (scd = host->host_queue; scd; scd = scd->next) {
-		int len;
-
-		proc_print_scsidevice (scd, buffer, &len, pos);
-		pos += len;
-		pos += sprintf (buffer+pos, "Extensions: ");
-		if (scd->tagged_supported)
-			pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
-				    scd->tagged_queue ? "en" : "dis",
-				    scd->current_tag);
-		pos += sprintf (buffer+pos, "\n");
+		pos += fas216_print_device(&info->info, scd, buffer + pos);
 
 		if (pos + begin < offset) {
 			begin += pos;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/acorn/scsi/queue.c linux.18p17/drivers/acorn/scsi/queue.c
--- linux.vanilla/drivers/acorn/scsi/queue.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/acorn/scsi/queue.c	Fri Sep 15 23:31:12 2000
@@ -55,6 +55,7 @@
 			q->magic = QUEUE_MAGIC_FREE;
 			q->SCpnt = NULL;
 		}
+		q -= 1;
 		q->next = NULL;
 	}
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ap1000/ap.c linux.18p17/drivers/ap1000/ap.c
--- linux.vanilla/drivers/ap1000/ap.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/ap1000/ap.c	Tue Sep 12 12:55:07 2000
@@ -20,7 +20,6 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/module.h>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ap1000/ddv.c linux.18p17/drivers/ap1000/ddv.c
--- linux.vanilla/drivers/ap1000/ddv.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/ap1000/ddv.c	Tue Sep 12 12:55:07 2000
@@ -16,7 +16,6 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/mm.h>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Config.in linux.18p17/drivers/block/Config.in
--- linux.vanilla/drivers/block/Config.in	Sun Jun 11 21:44:11 2000
+++ linux.18p17/drivers/block/Config.in	Mon Sep 11 18:06:33 2000
@@ -40,6 +40,7 @@
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
           bool '     OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
           if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
+			bool '     ALI 15X3 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_ALI15X3
             bool '     Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
             bool '     NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
             bool '     VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
@@ -138,6 +139,10 @@
 if [ "$CONFIG_PCI" = "y" ]; then
   tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
 fi
+if [ "$CONFIG_PCI" = "y" ]; then
+  tristate 'Compaq CISS Array support' CONFIG_BLK_CPQ_CISS_DA
+fi
+
 
 if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
   define_bool CONFIG_BLK_DEV_HD y
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/DAC960.c linux.18p17/drivers/block/DAC960.c
--- linux.vanilla/drivers/block/DAC960.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/DAC960.c	Sun Sep 10 14:46:08 2000
@@ -19,8 +19,8 @@
 */
 
 
-#define DAC960_DriverVersion			"2.2.8"
-#define DAC960_DriverDate			"19 August 2000"
+#define DAC960_DriverVersion			"2.2.9"
+#define DAC960_DriverDate			"7 September 2000"
 
 
 #include <linux/version.h>
@@ -310,13 +310,8 @@
 
 static void DAC960_WaitForCommand(DAC960_Controller_T *Controller)
 {
-  WaitQueue_T WaitQueueEntry = { current, NULL };
-  add_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry);
-  current->state = TASK_UNINTERRUPTIBLE;
-  spin_unlock(&io_request_lock);
-  schedule();
-  current->state = TASK_RUNNING;
-  remove_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry);
+  spin_unlock_irq(&io_request_lock);
+  __wait_event(Controller->CommandWaitQueue, Controller->FreeCommands);
   spin_lock_irq(&io_request_lock);
 }
 
@@ -5053,7 +5048,8 @@
 	}
       Geometry.start =
 	Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].start_sect;
-      return copy_to_user(UserGeometry, &Geometry, sizeof(DiskGeometry_T));
+      return (copy_to_user(UserGeometry, &Geometry,
+			   sizeof(DiskGeometry_T)) ? -EFAULT : 0);
     case BLKGETSIZE:
       /* Get Device Size. */
       if ((long *) Argument == NULL) return -EINVAL;
@@ -5161,8 +5157,8 @@
 	ControllerInfo.PCI_Address = Controller->PCI_Address;
 	strcpy(ControllerInfo.ModelName, Controller->ModelName);
 	strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion);
-	return copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
-			    sizeof(DAC960_ControllerInfo_T));
+	return (copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
+			     sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0);
       }
     case DAC960_IOCTL_V1_EXECUTE_COMMAND:
       {
@@ -6484,7 +6480,7 @@
   unsigned char CommandBuffer[80];
   int Length;
   if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
-  copy_from_user(CommandBuffer, Buffer, Count);
+  if (copy_from_user(CommandBuffer, Buffer, Count)) return -EFAULT;
   CommandBuffer[Count] = '\0';
   Length = strlen(CommandBuffer);
   if (CommandBuffer[Length-1] == '\n')
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Makefile linux.18p17/drivers/block/Makefile
--- linux.vanilla/drivers/block/Makefile	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/Makefile	Mon Sep 11 18:06:33 2000
@@ -142,6 +142,10 @@
 IDE_OBJS += pdc4030.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
+IDE_OBJS += alim15x3.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_TRM290),y)
 IDE_OBJS += trm290.o
 endif
@@ -243,6 +247,14 @@
 else
   ifeq ($(CONFIG_BLK_CPQ_DA),m)
   M_OBJS += cpqarray.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_CPQ_CISS_DA),y)
+L_OBJS += cciss.o
+else
+  ifeq ($(CONFIG_BLK_CPQ_CISS_DA),m)
+  M_OBJS += cciss.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/acsi.c linux.18p17/drivers/block/acsi.c
--- linux.vanilla/drivers/block/acsi.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/acsi.c	Sat Oct 14 00:07:14 2000
@@ -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)
@@ -411,7 +402,7 @@
  * 6), the timeout is based on the 'jiffies' variable to provide exact
  * timeouts for device probing etc.
  * If interrupts are disabled, the number of tries is based on the
- * 'loops_per_sec' variable. A rough estimation is sufficient here...
+ * 'loops_per_jiffy' variable. A rough estimation is sufficient here...
  */
 
 #define INT_LEVEL													\
@@ -424,12 +415,12 @@
 
 {
 	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 {
-		long tries = loops_per_sec / HZ / 8 * timeout;
+		long tries = loops_per_jiffy / 8 * timeout;
 		while( --tries >= 0 )
 			if (!(mfp.par_dt_reg & 0x20)) return( 1 );
 	}		
@@ -441,12 +432,12 @@
 
 {
 	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 {
-		long tries = loops_per_sec * timeout / HZ / 8;
+		long tries = loops_per_jiffy / 8 * timeout;
 		while( tries-- >= 0 )
 			if (mfp.par_dt_reg & 0x20) return( 1 );
 	}		
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/acsi_slm.c linux.18p17/drivers/block/acsi_slm.c
--- linux.vanilla/drivers/block/acsi_slm.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/acsi_slm.c	Sat Oct 14 00:07:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/alim15x3.c linux.18p17/drivers/block/alim15x3.c
--- linux.vanilla/drivers/block/alim15x3.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/alim15x3.c	Mon Sep 11 18:06:33 2000
@@ -0,0 +1,743 @@
+/*
+ *
+ *  Copyright (C) 1998-99 Michel Aubry, Maintainer
+ *  Copyright (C) 1998-99 Andrzej Krzysztofowicz, Maintainer
+ *  Copyright (C) 1998-99 Andre Hedrick, Integrater and Maintainer
+ *
+ *  (U)DMA capable version of ali 1543(C), 1535(D)
+ *
+ */
+
+/*  ======================================================
+ *  linux/drivers/block/alim15x3.c
+ *  version: 1.0 beta3 (Sep. 6, 1999)
+ *	e-mail your problems to cjtsai@ali.com.tw
+ *
+ *  Note: if you want to combine the code to kernel 2.3.x
+ *  you should ---
+ *		change:		//#define ALI_KERNEL_2_3_X
+ *		to:			#define ALI_KERNEL_2_3_X
+ *  and replace the "alim15x3.c" exists in kernel 2.3.x
+ *  with this file. I am not very sure that this file can 
+ *  support all 2.3.x. Because I only test it in kernel
+ *  version 2.3.16. So, before you replace the file, backup
+ *	the old one.
+ * ======================================================= */
+
+/* ===================== important =======================
+ *  for 2.2.x, you should use "//#define ALI_KERNEL_2_3_X"
+ *  for 2.3.x, you should use "#define ALI_KERNEL_2_3_X"
+ * ======================================================= */
+//#define ALI_KERNEL_2_3_X
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#if defined(ALI_KERNEL_2_3_X)
+#include <linux/ide.h>
+#else
+#include "ide.h"
+#endif
+
+#include "ide_modes.h"
+
+static int m5229_revision = -1;
+static int chip_is_1543c_e = 0;
+static int cable_80_pin[2] = { 0, 0 };
+
+/* This is fun.  -DaveM */
+#define IDE_SETXFER			0x03
+#define IDE_SETFEATURE		0xef
+
+#define IDE_DMA2_ENABLE		0x22	// dma
+#define IDE_DMA1_ENABLE		0x21
+#define IDE_DMA0_ENABLE		0x20
+
+#define IDE_UDMA4_ENABLE	0x44	// ultra 66
+#define IDE_UDMA3_ENABLE	0x43
+
+#define IDE_UDMA2_ENABLE	0x42	// ultra 33
+#define IDE_UDMA1_ENABLE	0x41
+#define IDE_UDMA0_ENABLE	0x40
+
+
+
+#if defined(ALI_KERNEL_2_3_X)
+// =====================   Support 2.3.x   =====================
+
+
+#define DISPLAY_ALI_TIMINGS
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int dummy);
+extern int (*ali_display_info)(char *, char **, off_t, int, int);  /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+char *fifo[4] = {
+	"FIFO Off",
+	"FIFO On ",
+	"DMA mode",
+	"PIO mode" };
+
+char *udmaT[8] = {
+	"1.5T",
+	"  2T",
+	"2.5T",
+	"  3T",
+	"3.5T",
+	"  4T",
+	"  6T",
+	"  8T"
+};
+
+char *channel_status[8] = {
+	"OK            ",
+	"busy          ",
+	"DRQ           ",
+	"DRQ busy      ",
+	"error         ",
+	"error busy    ",
+	"error DRQ     ",
+	"error DRQ busy"
+};
+#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name)
+{
+	byte confreg0 = 0, confreg1 =0, progif = 0;
+	int errors = 0;
+
+	if (pci_read_config_byte(dev, 0x50, &confreg1))
+		goto veryspecialsettingserror;
+	if (!(confreg1 & 0x02))
+		if (pci_write_config_byte(dev, 0x50, confreg1 | 0x02))
+			goto veryspecialsettingserror;
+
+	if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif))
+		goto veryspecialsettingserror;
+	if (!(progif & 0x40)) {
+		/*
+		 * The way to enable them is to set progif
+		 * writable at 0x4Dh register, and set bit 6
+		 * of progif to 1:
+		 */
+		if (pci_read_config_byte(dev, 0x4d, &confreg0))
+			goto veryspecialsettingserror;
+		if (confreg0 & 0x80)
+			if (pci_write_config_byte(dev, 0x4d, confreg0 & ~0x80))
+				goto veryspecialsettingserror;
+		if (pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x40))
+			goto veryspecialsettingserror;
+		if (confreg0 & 0x80)
+			if (pci_write_config_byte(dev, 0x4d, confreg0))
+				errors++;
+	}
+
+	if ((pci_read_config_byte(dev, PCI_CLASS_PROG, &progif)) || (!(progif & 0x40)))
+		goto veryspecialsettingserror;
+
+	printk("%s: enabled read of IDE channels state (en/dis-abled) %s.\n",
+		name, errors ? "with Error(s)" : "Succeeded" );
+	return 0;
+
+veryspecialsettingserror:
+	printk("%s: impossible to enable read of IDE channels state (en/dis-abled)!\n", name);
+	return 0;
+}
+
+static void ali15x3_tune_drive (ide_drive_t *drive, byte pio)
+{
+	ide_pio_data_t d;
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev = hwif->pci_dev;
+	int s_time, a_time, c_time;
+	byte s_clc, a_clc, r_clc;
+	unsigned long flags;
+	int bus_speed = ide_system_bus_speed();
+	int port = hwif->index ? 0x5c : 0x58;
+
+	pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+	s_time = ide_pio_timings[pio].setup_time;
+	a_time = ide_pio_timings[pio].active_time;
+	if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
+		s_clc = 0;
+	if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
+		a_clc = 0;
+	c_time = ide_pio_timings[pio].cycle_time;
+
+#if 0
+	if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
+		r_clc = 0;
+#endif
+
+	if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
+		r_clc = 1;
+	} else {
+		if (r_clc >= 16)
+		r_clc = 0;
+	}
+	save_flags(flags);
+	cli();
+	pci_write_config_byte(dev, port, s_clc);
+	pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
+	restore_flags(flags);
+
+	/*
+	 * setup   active  rec
+	 * { 70,   165,    365 },   PIO Mode 0
+	 * { 50,   125,    208 },   PIO Mode 1
+	 * { 30,   100,    110 },   PIO Mode 2
+	 * { 30,   80,     70  },   PIO Mode 3 with IORDY
+	 * { 25,   70,     25  },   PIO Mode 4 with IORDY  ns
+	 * { 20,   50,     30  }    PIO Mode 5 with IORDY (nonstandard)
+	 */
+}
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int dummy)
+{
+	byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
+	unsigned int bibma;
+	byte c0, c1;
+	byte rev, tmp;
+	char *p = buffer;
+	char *q;
+
+	/* fetch rev. */
+	pci_read_config_byte(bmide_dev, 0x08, &rev);
+	if (rev >= 0xc1)	/* M1543C or newer */
+		udmaT[7] = " ???";
+	else
+		fifo[3]  = "   ???  ";
+
+	/* first fetch bibma: */
+	pci_read_config_dword(bmide_dev, 0x20, &bibma);
+	bibma = (bibma & 0xfff0) ;
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte
+	 * registers to investigate:
+	 */
+	c0 = inb((unsigned short)bibma + 0x02);
+	c1 = inb((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p,
+		"\n                                Ali M15x3 Chipset.\n");
+	p += sprintf(p,
+		"                                ------------------\n");
+	pci_read_config_byte(bmide_dev, 0x78, &reg53h);
+	p += sprintf(p, "PCI Clock: %d.\n", reg53h);
+
+	pci_read_config_byte(bmide_dev, 0x53, &reg53h);
+	p += sprintf(p,
+		"CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
+		(reg53h & 0x02) ? "Yes" : "No ",
+		(reg53h & 0x01) ? "Yes" : "No " );
+	pci_read_config_byte(bmide_dev, 0x74, &reg53h);
+	p += sprintf(p,
+		"FIFO Status: contains %d Words, runs%s%s\n\n",
+		(reg53h & 0x3f),
+		(reg53h & 0x40) ? " OVERWR" : "",
+		(reg53h & 0x80) ? " OVERRD." : "." );
+
+	p += sprintf(p,
+		"-------------------primary channel-------------------secondary channel---------\n\n");
+
+	pci_read_config_byte(bmide_dev, 0x09, &reg53h);
+	p += sprintf(p,
+		"channel status:       %s                               %s\n",
+		(reg53h & 0x20) ? "On " : "Off",
+		(reg53h & 0x10) ? "On " : "Off" );
+
+	p += sprintf(p,
+		"both channels togth:  %s                               %s\n",
+		(c0&0x80) ? "No " : "Yes",
+		(c1&0x80) ? "No " : "Yes" );
+
+	pci_read_config_byte(bmide_dev, 0x76, &reg53h);
+	p += sprintf(p,
+		"Channel state:        %s                    %s\n",
+		channel_status[reg53h & 0x07],
+		channel_status[(reg53h & 0x70) >> 4] );
+
+	pci_read_config_byte(bmide_dev, 0x58, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5c, &reg5yh);
+	p += sprintf(p,
+		"Add. Setup Timing:    %dT                                %dT\n",
+		(reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
+		(reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
+
+	pci_read_config_byte(bmide_dev, 0x59, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5d, &reg5yh);
+	p += sprintf(p,
+		"Command Act. Count:   %dT                                %dT\n"
+		"Command Rec. Count:   %dT                               %dT\n\n",
+		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8, 
+		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
+
+	p += sprintf(p,
+		"----------------drive0-----------drive1------------drive0-----------drive1------\n\n");
+	p += sprintf(p,
+		"DMA enabled:      %s              %s               %s              %s\n",
+		(c0&0x20) ? "Yes" : "No ",
+		(c0&0x40) ? "Yes" : "No ",
+		(c1&0x20) ? "Yes" : "No ",
+		(c1&0x40) ? "Yes" : "No " );
+
+	pci_read_config_byte(bmide_dev, 0x54, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x55, &reg5yh);
+	q = "FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n";
+	if (rev < 0xc1) {
+		if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
+			p += sprintf(p, q, 8, 8, 8, 8);
+		} else {
+			p += sprintf(p, q,
+				(reg5xh & 0x03) + 12,
+				((reg5xh & 0x30)>>4) + 12,
+				(reg5yh & 0x03) + 12,
+				((reg5yh & 0x30)>>4) + 12 );
+		}
+	} else {
+		p += sprintf(p, q,
+			(tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4,
+			(tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4,
+			(tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4,
+			(tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4 );
+	}
+
+#if 0
+	p += sprintf(p, 
+		"FIFO threshold:   %2d Words         %2d Words          %2d Words         %2d Words\n",
+		(reg5xh & 0x03) + 12,
+		((reg5xh & 0x30)>>4) + 12,
+		(reg5yh & 0x03) + 12,
+		((reg5yh & 0x30)>>4) + 12 );
+#endif
+
+	p += sprintf(p,
+		"FIFO mode:        %s         %s          %s         %s\n",
+		fifo[((reg5xh & 0x0c) >> 2)],
+		fifo[((reg5xh & 0xc0) >> 6)],
+		fifo[((reg5yh & 0x0c) >> 2)],
+		fifo[((reg5yh & 0xc0) >> 6)] );
+
+	pci_read_config_byte(bmide_dev, 0x5a, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x5b, &reg5xh1);
+	pci_read_config_byte(bmide_dev, 0x5e, &reg5yh);
+	pci_read_config_byte(bmide_dev, 0x5f, &reg5yh1);
+
+	p += sprintf(p,/*
+		"------------------drive0-----------drive1------------drive0-----------drive1------\n")*/
+		"Dt RW act. Cnt    %2dT              %2dT               %2dT              %2dT\n"
+		"Dt RW rec. Cnt    %2dT              %2dT               %2dT              %2dT\n\n",
+		(reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+		(reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
+		(reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+		(reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
+		(reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+		(reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
+		(reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
+		(reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
+
+	p += sprintf(p,
+		"-----------------------------------UDMA Timings--------------------------------\n\n");
+
+	pci_read_config_byte(bmide_dev, 0x56, &reg5xh);
+	pci_read_config_byte(bmide_dev, 0x57, &reg5yh);
+	p += sprintf(p,
+		"UDMA:             %s               %s                %s               %s\n"
+		"UDMA timings:     %s             %s              %s             %s\n\n",
+		(reg5xh & 0x08) ? "OK" : "No",
+		(reg5xh & 0x80) ? "OK" : "No",
+		(reg5yh & 0x08) ? "OK" : "No",
+		(reg5yh & 0x80) ? "OK" : "No",
+		udmaT[(reg5xh & 0x07)],
+		udmaT[(reg5xh & 0x70) >> 4],
+		udmaT[reg5yh & 0x07],
+		udmaT[(reg5yh & 0x70) >> 4] );
+
+	return p-buffer; /* => must be less than 4k! */
+}
+#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+
+// ===================   End Support 2.3.x   ===================
+#endif	// defined(ALI_KERNEL_2_3_X)
+
+
+
+static __inline__ unsigned char dma2_bits_to_command(unsigned char bits)
+{
+	if(bits & 0x04)
+		return IDE_DMA2_ENABLE;
+	if(bits & 0x02)
+		return IDE_DMA1_ENABLE;
+	return IDE_DMA0_ENABLE;
+}
+
+static __inline__ unsigned char udma2_bits_to_command(unsigned char bits)
+{
+	if(bits & 0x10)
+		return IDE_UDMA4_ENABLE;
+	if(bits & 0x08)
+		return IDE_UDMA3_ENABLE;
+	if(bits & 0x04)
+		return IDE_UDMA2_ENABLE;
+	if(bits & 0x02)
+		return IDE_UDMA1_ENABLE;
+	return IDE_UDMA0_ENABLE;
+}
+
+static __inline__ int wait_for_ready(ide_drive_t *drive)
+{
+	int timeout = 20000;
+	byte stat;
+
+	while(--timeout) {
+		stat = GET_STAT();	// printk("STAT(%2x) ", stat);
+		
+		if(!(stat & BUSY_STAT)) {
+			if((stat & READY_STAT) || (stat & ERR_STAT))
+				break;
+		}
+		udelay(150);
+	}
+	if((stat & ERR_STAT) || timeout <= 0)
+		return 1;
+	return 0;
+}
+
+static void ali15x3_do_setfeature(ide_drive_t *drive, byte command)
+{
+	unsigned long flags;
+	byte old_select;
+
+	save_flags(flags);
+	cli();
+		
+	old_select = IN_BYTE(IDE_SELECT_REG);	//  save old selected device
+	OUT_BYTE(drive->select.all, IDE_SELECT_REG);	// "SELECT "
+
+	OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG);	// "SETXFER "	
+	OUT_BYTE(command, IDE_NSECTOR_REG);	// "CMND "
+	
+	if(wait_for_ready(drive))	// "wait "
+		goto out;
+
+	OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG);	// "SETFEATURE "
+	(void) wait_for_ready(drive);	// "wait "
+
+out:	
+	OUT_BYTE(old_select, IDE_SELECT_REG);	// restore to old "selected device"
+	restore_flags(flags);
+}
+
+static void ali15x3_dma2_enable(ide_drive_t *drive, unsigned long dma_base)
+{
+	byte unit = (drive->select.b.unit & 0x01);
+	byte bits = (drive->id->dma_mword | drive->id->dma_1word) & 0x07;
+	byte tmpbyte;
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long flags;
+	int m5229_udma_setting_index = hwif->channel? 0x57 : 0x56;		
+	
+/*	if((((drive->id->dma_mword & 0x0007) << 8) !=
+	   (drive->id->dma_mword & 0x0700)))*/
+		ali15x3_do_setfeature(drive, dma2_bits_to_command(bits));
+
+	// clear "ultra enable" bit
+	pci_read_config_byte(hwif->pci_dev, m5229_udma_setting_index, &tmpbyte);
+	if(unit)
+		tmpbyte &= 0x7f;
+	else
+		tmpbyte &= 0xf7;
+	save_flags(flags);
+	cli();
+	pci_write_config_byte(hwif->pci_dev, m5229_udma_setting_index, tmpbyte);
+	restore_flags(flags);
+	drive->id->dma_ultra = 0x00;
+
+	/* Enable DMA*/
+	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+	printk(" ALI15X3: MultiWord DMA enabled\n");
+}
+
+static void ali15x3_udma_enable(ide_drive_t *drive, unsigned long dma_base)
+{	
+	byte unit = (drive->select.b.unit & 0x01);
+	byte bits = drive->id->dma_ultra & 0x1f;	
+	byte tmpbyte;
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long flags;
+	unsigned char udma_mode = 0;
+	int m5229_udma_setting_index = hwif->channel? 0x57 : 0x56;
+
+	if(bits & 0x18) {	// 00011000, disk: ultra66
+		if(m5229_revision < 0xc2) {		// controller: ultra33
+			bits = 0x04;	// 00000100, use ultra33, mode 2			
+			drive->id->dma_ultra &= ~0xFF00;
+			drive->id->dma_ultra |= 0x0004;			
+		}
+		else {	// controller: ultra66
+			// Try to detect word93 bit13 and 80-pin cable (from host view)
+			if( ! ( (drive->id->word93 & 0x2000) && cable_80_pin[hwif->channel] ) ) {	
+				bits = 0x04;	// 00000100, use ultra33, mode 2
+				drive->id->dma_ultra &= ~0xFF00;
+				drive->id->dma_ultra |= 0x0004;
+			}
+		}
+	}
+
+	// set feature only if we are not in that mode
+/*	if(((drive->id->dma_ultra & 0x001f) << 8) !=
+	   (drive->id->dma_ultra & 0x1f00))*/
+		ali15x3_do_setfeature(drive, udma_mode = udma2_bits_to_command(bits));
+	udma_mode &= 0x0f;	// get UDMA mode
+
+	/* Enable DMA and UltraDMA */
+	outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+	// m5229 ultra
+	pci_read_config_byte(hwif->pci_dev, m5229_udma_setting_index, &tmpbyte);
+	
+	tmpbyte &= (0x0f << ( (1-unit) << 2) );	// clear bit0~3 or bit 4~7
+	// enable ultra dma and set timing
+	tmpbyte |= ( (0x08 | (4-udma_mode) ) << (unit << 2) );
+	// set to m5229
+	save_flags(flags);
+	cli();		
+	pci_write_config_byte(hwif->pci_dev, m5229_udma_setting_index, tmpbyte);
+	restore_flags(flags);
+
+	if(udma_mode >= 3) { // ultra 66
+		pci_read_config_byte(hwif->pci_dev, 0x4b, &tmpbyte);
+		tmpbyte |= 1;			
+		save_flags(flags);
+		cli();
+		pci_write_config_byte(hwif->pci_dev, 0x4b, tmpbyte);		
+		restore_flags(flags);
+	}
+
+	printk(" ALI15X3: Ultra DMA enabled\n");
+}
+
+static int ali15x3_dma_onoff(ide_drive_t *drive, int enable)
+{
+	if(enable) {
+		ide_hwif_t *hwif = HWIF(drive);
+		unsigned long dma_base = hwif->dma_base;
+		struct hd_driveid *id = drive->id;
+				
+		if((id->field_valid & 0x0004) && 
+		   (id->dma_ultra & 0x001f)) {
+			// 1543C_E, in ultra mode, some WDC "harddisk" will cause "CRC" errors
+			// (even if no CRC problem), so we try to use "DMA" here
+			if( m5229_revision <= 0x20)
+				ali15x3_dma2_enable(drive, dma_base);	/* Normal MultiWord DMA modes. */
+			else if( (m5229_revision < 0xC2) && 
+			   ( (drive->media!=ide_disk) || (chip_is_1543c_e && strstr(id->model, "WDC ")) ) )
+				ali15x3_dma2_enable(drive, dma_base);	/* Normal MultiWord DMA modes. */
+			else	// >= 0xC2 or 0xC1 with no problem
+				ali15x3_udma_enable(drive, dma_base);	/* UltraDMA modes. */
+		} else {
+			ali15x3_dma2_enable(drive, dma_base);	/* Normal MultiWord DMA modes. */		
+		}
+	}
+
+	drive->using_dma = enable;	// on, off
+	return 0;
+}
+
+static int ali15x3_config_drive_for_dma(ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+
+	if( (m5229_revision<=0x20) && (drive->media!=ide_disk) )
+		return hwif->dmaproc(ide_dma_off_quietly, drive);	
+
+	/* Even if the drive is not _currently_ in a DMA
+	 * mode, we succeed, and we'll enable it manually
+	 * below in alim15x3_dma_onoff */
+	if((id != NULL) && 
+	   (id->capability & 1) &&	// dma supported
+	   hwif->autodma ) {		
+		if(id->field_valid & 0x0004)	// word 88 is valid
+			if(id->dma_ultra & 0x001F)	// 00011111 => ultra dma mode 0~4
+				return hwif->dmaproc(ide_dma_on, drive);
+
+		if(id->field_valid & 0x0002)	// words 64~70 is valid
+			if((id->dma_mword & 0x0007)	// word 63, multiword DMA mode 0~2
+			 || (id->dma_1word & 0x0007)) // word 62, single word DMA mode 0~2
+				return hwif->dmaproc(ide_dma_on, drive);
+	}
+
+	return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return ali15x3_config_drive_for_dma(drive);
+		case ide_dma_on:
+		case ide_dma_off:
+		case ide_dma_off_quietly:
+			return ali15x3_dma_onoff(drive, (func == ide_dma_on));					
+		case ide_dma_write:
+			if( (m5229_revision < 0xC2) && (drive->media != ide_disk) )
+				return 1;	/* try PIO instead of DMA */
+			break;
+		default:
+	}
+
+	return ide_dmaproc(func, drive);	// use standard DMA stuff
+}
+
+void __init ide_init_ali15x3 (ide_hwif_t *hwif)
+{	
+	unsigned long flags;
+	byte tmp_m5229_revision;
+
+	struct pci_dev *dev_m1533, *dev_m5229 = hwif->pci_dev;
+	byte ideic, inmir, tmpbyte;	
+	byte irq_routing_table[] = { -1,  9, 3, 10, 4,  5, 7,  6,
+								  1, 11, 0, 12, 0, 14, 0, 15 };
+
+	hwif->irq = hwif->channel ? 15 : 14;	
+
+	// look for ISA bridge m1533
+	for (dev_m1533=pci_devices; dev_m1533; dev_m1533=dev_m1533->next)
+		if(dev_m1533->vendor==PCI_VENDOR_ID_AL && 
+		   dev_m1533->device==PCI_DEVICE_ID_AL_M1533)
+			break;	// we find it
+
+	pci_read_config_byte(dev_m1533, 0x58, &ideic);	// read IDE interface control
+	ideic = ideic & 0x03;	// bit0, bit1
+	
+	/* get IRQ for IDE Controller */
+	if ((hwif->channel && ideic == 0x03) || (!hwif->channel && !ideic)) { // get SIRQ1 routing table
+		pci_read_config_byte(dev_m1533, 0x44, &inmir);	
+		inmir = inmir & 0x0f;
+		hwif->irq = irq_routing_table[inmir];
+	}	
+	else if (hwif->channel && !(ideic & 0x01)) { // get SIRQ2 routing table
+		pci_read_config_byte(dev_m1533, 0x75, &inmir);
+		inmir = inmir & 0x0f;
+		hwif->irq = irq_routing_table[inmir];
+	}
+	
+	// read m5229 revision to a temp variable
+	pci_read_config_byte(dev_m5229, PCI_REVISION_ID, &tmp_m5229_revision);
+
+	if(m5229_revision == -1)	{	// only do it one time.
+		m5229_revision = 0;	// change the default to 0	
+		printk("\n************************************\n");
+		printk("*    ALi IDE driver (1.0 beta3)    *\n");
+		printk("*       Chip Revision is %02X        *\n", tmp_m5229_revision);
+		printk("*  Maximum capability is ");
+		if(tmp_m5229_revision >= 0xC2)
+			printk("- UDMA 66 *\n");
+		else if(tmp_m5229_revision >= 0xC0)
+			printk("- UDMA 33 *\n");
+		else if(tmp_m5229_revision == 0x20)
+			printk("  -  DMA  *\n");
+		else
+			printk("  -  PIO  *\n");
+		printk("************************************\n\n");	
+
+		// support CD-ROM DMA mode
+		pci_read_config_byte(dev_m5229, 0x53, &tmpbyte);
+		tmpbyte = (tmpbyte & (~0x02)) | 0x01;
+		save_flags(flags);
+		cli();		
+		pci_write_config_byte(dev_m5229, 0x53, tmpbyte);
+		restore_flags(flags);
+	}
+	
+	if ( (hwif->dma_base) && (tmp_m5229_revision>=0x20) ) {
+		
+#if defined(ALI_KERNEL_2_3_X)
+		if(tmp_m5229_revision>=0xC2)
+			hwif->udma_four = 1;
+		else
+			hwif->udma_four = 0;
+#endif
+		
+		if(m5229_revision == 0) {	// only do it one time
+			m5229_revision = tmp_m5229_revision;	// keep it
+
+			if(m5229_revision >= 0xC2) {	// 1543C-B?, 1535, 1535D, 1553
+				// Note 1: not all "motherboard" support this detection
+				// Note 2: if no udma 66 device, the detection may "error".
+				//		   but in this case, we will not set the device to 
+				//		   ultra 66, the detection result is not important
+				save_flags(flags);
+				cli();
+				// enable "Cable Detection", m5229, 0x4b, bit3
+				pci_read_config_byte(dev_m5229, 0x4b, &tmpbyte);
+				pci_write_config_byte(dev_m5229, 0x4b, tmpbyte | 0x08);
+
+				// set south-bridge's enable bit, m1533, 0x79
+				pci_read_config_byte(dev_m1533, 0x79, &tmpbyte);
+				if(m5229_revision==0xC2)		// 1543C-B0 (m1533, 0x79, bit 2)
+					pci_write_config_byte(dev_m1533, 0x79, tmpbyte | 0x04);
+				else if	(m5229_revision==0xC3)	// 1553/1535 (m1533, 0x79, bit 1)
+					pci_write_config_byte(dev_m1533, 0x79, tmpbyte | 0x02);
+				restore_flags(flags);
+								
+				// Ultra66 cable detection (from Host View)
+				// m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
+				pci_read_config_byte(dev_m5229, 0x4a, &tmpbyte);
+				if(! (tmpbyte & 0x01))	// 0x4a, bit0 is 0 => 
+					cable_80_pin[0] = 1;	// primary channel has 80-pin (from host view)
+				if(! (tmpbyte & 0x02))	// 0x4a, bit1 is 0 =>				
+					cable_80_pin[1] = 1;	// secondary channel has 80-pin (from host view)					
+			} else {
+				// revision 0x20 (1543-E, 1543-F)
+				// revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E)
+				// clear CD-ROM DMA write bit, m5229, 0x4b, bit 7
+				pci_read_config_byte(dev_m5229, 0x4b, &tmpbyte);
+				save_flags(flags);
+				cli();
+				pci_write_config_byte(dev_m5229, 0x4b, tmpbyte & 0x7F);	// clear bit 7
+				restore_flags(flags);
+
+				// check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010
+				pci_read_config_byte(dev_m1533, 0x5e, &tmpbyte);
+				chip_is_1543c_e = ( (tmpbyte & 0x1e) == 0x12 ) ? 1: 0;
+			}			
+		}
+		
+		// M1543 or newer for DMAing
+		hwif->dmaproc = &ali15x3_dmaproc;
+		hwif->autodma = 1;
+	} else {
+		// if both channel use PIO, then chip revision is not important.
+		// we should't assign m5229_revision here (assign it will affect
+		// the chip detection procedure above --- if(! m5229_revision)
+		hwif->autodma = 0;			
+		hwif->drives[0].autotune = 1;
+		hwif->drives[1].autotune = 1;
+	}
+	
+#if defined(ALI_KERNEL_2_3_X) 
+	hwif->tuneproc = &ali15x3_tune_drive;
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+	bmide_dev = hwif->pci_dev;
+	ali_display_info = &ali_get_info;
+#endif  /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */	
+#endif  /* defined(ALI_KERNEL_2_3_X) */	
+	
+	return;
+}
\ No newline at end of file
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/buddha.c linux.18p17/drivers/block/buddha.c
--- linux.vanilla/drivers/block/buddha.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/buddha.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss.c linux.18p17/drivers/block/cciss.c
--- linux.vanilla/drivers/block/cciss.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/cciss.c	Fri Sep  1 13:34:06 2000
@@ -0,0 +1,1934 @@
+/*
+ *    Disk Array driver for Compaq SMART2 Controllers
+ *    Copyright 2000 Compaq Computer Corporation
+ *
+ *    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, GOOD TITLE or
+ *    NON INFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    Questions/Comments/Bugfixes to arrays@compaq.com
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/capability.h>
+#include <linux/hdreg.h>
+#include <asm/uaccess.h>
+#include <asm/spinlock.h>
+#include <asm/io.h>
+
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+
+#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
+#define DRIVER_NAME "Compaq CISS Driver (v 1.0.0)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(1,0,0)
+
+/* Embedded module documentation macros - see modules.h */
+MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5300");
+
+#include "cciss_cmd.h"
+#include "cciss.h"
+#include <linux/cciss_ioctl.h>
+
+#define NR_PRODUCTS (sizeof(products)/sizeof(struct board_type))
+
+/*  board_id = Subsystem Device ID & Vendor ID
+ *  product = Marketing Name for the board
+ *  access = Address of the struct of function pointers 
+ */
+static struct board_type products[] = {
+	{ 0x40700E11, "Smart Array 5300",	&SA5_access },
+};
+
+/* How long to wait (in millesconds) for board to go into simple mode */
+#define MAX_CONFIG_WAIT 1000 
+
+#define READ_AHEAD 	 128
+#define NR_CMDS		 128 /* #commands that can be outstanding */
+#define MAX_CTLR 8
+static int nr_ctlr =0; 
+static ctlr_info_t *hba[MAX_CTLR] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static struct proc_dir_entry *proc_cciss = NULL;
+
+static void do_cciss_request(int i);
+/*
+ * This is a hack.  This driver eats a major number for each controller, and
+ * sets blkdev[xxx].request_fn to each one of these so the real request
+ * function knows what controller its working with.
+ */
+#define DO_CCISS_REQUEST(x) { do_cciss_request(x); }
+
+static void do_cciss_request0(void) DO_CCISS_REQUEST(0);
+static void do_cciss_request1(void) DO_CCISS_REQUEST(1);
+static void do_cciss_request2(void) DO_CCISS_REQUEST(2);
+static void do_cciss_request3(void) DO_CCISS_REQUEST(3);
+static void do_cciss_request4(void) DO_CCISS_REQUEST(4);
+static void do_cciss_request5(void) DO_CCISS_REQUEST(5);
+static void do_cciss_request6(void) DO_CCISS_REQUEST(6);
+static void do_cciss_request7(void) DO_CCISS_REQUEST(7);
+
+static int cciss_open(struct inode *inode, struct file *filep);
+static int cciss_release(struct inode *inode, struct file *filep);
+static int cciss_ioctl(struct inode *inode, struct file *filep, 
+		unsigned int cmd, unsigned long arg);
+
+static int revalidate_allvol(kdev_t dev);
+static int revalidate_logvol(kdev_t dev, int maxusage);
+static int frevalidate_logvol(kdev_t dev);
+
+static void cciss_getgeometry(int cntl_num);
+
+static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c);
+static void start_io( ctlr_info_t *h);
+
+static struct file_operations cciss_fops  = {
+        NULL,                        /* lseek - default */
+        block_read,                  /* read - general block-dev read */
+        block_write,                 /* write - general block-dev write */
+        NULL,                        /* readdir - bad */
+        NULL,                        /* select */
+        cciss_ioctl,                  /* ioctl */
+        NULL,                        /* mmap */
+        cciss_open,                     /* open code */
+        NULL,
+        cciss_release,                  /* release */
+        block_fsync,                  /* fsync */
+        NULL,                        /* fasync */
+        NULL,                   /* Disk change */
+        frevalidate_logvol,     /* revalidate */
+};
+
+/*
+ * Report information about this controller.
+ */
+static int cciss_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+        off_t pos = 0;
+        off_t len = 0;
+        int size, i, ctlr;
+        ctlr_info_t *h = (ctlr_info_t*)data;
+        drive_info_struct *drv;
+
+        ctlr = h->ctlr;
+        size = sprintf(buffer, "%s:  Compaq %s Controller\n"
+                "       Board ID: %08lx\n"
+		"       Firmware Version: %c%c%c%c\n"
+                "       Memory Address: %08lx\n"
+                "       IRQ: 0x%x\n"
+                "       Logical drives: %d\n"
+                "       Current Q depth: %d\n"
+		"       Current # commands on controller %d\n"
+                "       Max Q depth since init: %d\n"
+		"       Max # commands on controller since init: %d\n"
+		"       Max SG entries since init: %d\n\n",
+                h->devname,
+                h->product_name,
+                (unsigned long)h->board_id,
+		h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3],
+                (unsigned long)h->vaddr,
+                (unsigned int)h->intr,
+                h->num_luns, 
+                h->Qdepth, h->commands_outstanding,
+		h->maxQsinceinit, h->max_outstanding, h->maxSG);
+
+        pos += size; len += size;
+	for(i=0; i<h->num_luns; i++) {
+                drv = &h->drv[i];
+                size = sprintf(buffer+len, "cciss/c%dd%d: blksz=%d nr_blocks=%d\n",
+                                ctlr, i, drv->block_size, drv->nr_blocks);
+                pos += size; len += size;
+        }
+
+	size = sprintf(buffer+len, "nr_allocs = %d\nnr_frees = %d\n",
+                        h->nr_allocs, h->nr_frees);
+        pos += size; len += size;
+
+        *eof = 1;
+        *start = buffer+offset;
+        len -= offset;
+        if (len>length)
+                len = length;
+        return len;
+}
+
+/*
+ * Get us a file in /proc/cciss that says something about each controller.
+ * Create /proc/cciss if it doesn't exist yet.
+ */
+static void cciss_procinit(int i)
+{
+        struct proc_dir_entry *pd;
+
+        if (proc_cciss == NULL) {
+                proc_cciss = create_proc_entry("cciss", S_IFDIR|S_IRUGO|S_IXUGO,                                                                &proc_root);
+                if (!proc_cciss) 
+			return;
+        }
+
+        pd = create_proc_entry(hba[i]->devname, S_IFREG|S_IRUGO, proc_cciss);
+        if (!pd) 
+		return;
+        pd->read_proc = cciss_proc_get_info;
+        pd->data = hba[i];
+}
+
+/* 
+ * For operations that cannot sleep, a command block is allocated at init, 
+ * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
+ * which ones are free or in use.  For operations that can wait for kmalloc 
+ * to possible sleep, this routine can be called with a NULL pointer. 
+ * cmd_free() MUST be called with a NULL pointer if cmd_alloc was. 
+ */ 
+static CommandList_struct * cmd_alloc(ctlr_info_t *h)
+{
+	CommandList_struct *c;
+	int i; 
+	u64bit temp64;
+
+	if (h == NULL)
+	{
+		c = (CommandList_struct *)kmalloc(sizeof(CommandList_struct), 
+			GFP_KERNEL);
+        	if(c==NULL)
+                 	return NULL;
+		memset(c, 0, sizeof(CommandList_struct));
+
+		c->err_info = (ErrorInfo_struct *)kmalloc(
+					sizeof(ErrorInfo_struct), GFP_KERNEL);
+	
+		if (c->err_info == NULL)
+		{
+			kfree(c);
+			return NULL;
+		}
+		memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+	} else /* get it out of the controllers pool */ 
+	{
+	     	do {
+                	i = find_first_zero_bit(h->cmd_pool_bits, NR_CMDS);
+                        if (i == NR_CMDS)
+                                return NULL;
+                } while(test_and_set_bit(i%32, h->cmd_pool_bits+(i/32)) != 0);
+#ifdef CCISS_DEBUG
+		printk(KERN_DEBUG "cciss: using command buffer %d\n", i);
+#endif
+                c = h->cmd_pool + i;
+		memset(c, 0, sizeof(CommandList_struct));			
+		c->err_info = h->errinfo_pool + i;
+		memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+                h->nr_allocs++;
+        }
+
+
+	temp64.val = (__u64) virt_to_bus(c->err_info);
+	c->ErrDesc.Addr.lower = temp64.val32.lower;
+	c->ErrDesc.Addr.upper = temp64.val32.upper;
+	c->ErrDesc.Len = sizeof(ErrorInfo_struct);
+       	c->busaddr = virt_to_bus(c);
+        return c;
+
+
+}
+
+/* 
+ * Frees a command block that was previously allocated with cmd_alloc(). 
+ */
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c)
+{
+	int i;
+
+	if( h == NULL)
+	{ 
+		kfree(c->err_info);
+		kfree(c);	
+	} else 
+	{
+		i = c - h->cmd_pool;
+		clear_bit(i%32, h->cmd_pool_bits+(i/32));
+                h->nr_frees++;
+        }
+}
+
+/*  
+ * fills in the disk information. 
+ */
+static void cciss_geninit( struct gendisk *g)
+{
+	int ctlr = -1; 
+	drive_info_struct *drv;
+	int i,j;
+
+	/* find the controller this gendisk data is from */ 
+	for(i=0; i < nr_ctlr; i++)
+	{
+		if ( g == &(hba[i]->gendisk))
+		{
+			ctlr = i;
+			break;
+		}
+	}
+	if ( ctlr < 0) 
+	{
+		printk(KERN_WARNING "cciss: gendisk data invalid\n");
+		return;
+	}
+	/* Loop through each real device */ 
+	g->nr_real = 0; 
+	for(i=0; i< NWD; i++)
+	{
+		drv = &(hba[ctlr]->drv[i]);
+		if( !(drv->nr_blocks))
+			continue;
+		hba[ctlr]->hd[i << NWD_SHIFT].nr_sects = 
+		hba[ctlr]->sizes[i << NWD_SHIFT] = drv->nr_blocks;
+
+		/* for each partition */ 
+		for(j=0; j<MAX_PART; j++)
+		{
+			hba[ctlr]->blocksizes[(i<<NWD_SHIFT) + j] = 1024; 
+
+			hba[ctlr]->hardsizes[ (i<<NWD_SHIFT) + j] = 
+				drv->block_size;
+		}
+		g->nr_real++;
+	}
+}
+/*
+ * Open.  Make sure the device is really there.
+ */
+static int cciss_open(struct inode *inode, struct file *filep)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss_open %x (%x:%x)\n", inode->i_rdev, ctlr, dsk);
+#endif /* CCISS_DEBUG */ 
+
+	if (ctlr > MAX_CTLR || hba[ctlr] == NULL)
+		return -ENXIO;
+
+	if (!suser() && hba[ctlr]->sizes[ MINOR(inode->i_rdev)] == 0)
+		return -ENXIO;
+
+	/*
+	 * Root is allowed to open raw volume zero even if its not configured
+	 * so array config can still work.  I don't think I really like this,
+	 * but I'm already using way to many device nodes to claim another one
+	 * for "raw controller".
+	 */
+	if (suser()
+		&& (hba[ctlr]->sizes[MINOR(inode->i_rdev)] == 0) 
+		&& (MINOR(inode->i_rdev)!= 0))
+		return -ENXIO;
+
+	hba[ctlr]->drv[dsk].usage_count++;
+	hba[ctlr]->usage_count++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+/*
+ * Close.  Sync first.
+ */
+static int cciss_release(struct inode *inode, struct file *filep)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss_release %x (%x:%x)\n", inode->i_rdev, ctlr, dsk);
+#endif /* CCISS_DEBUG */
+
+	fsync_dev(inode->i_rdev);
+
+	hba[ctlr]->drv[dsk].usage_count--;
+	hba[ctlr]->usage_count--;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * ioctl 
+ */
+static int cciss_ioctl(struct inode *inode, struct file *filep, 
+		unsigned int cmd, unsigned long arg)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+	int diskinfo[4];
+	struct hd_geometry *geo = (struct hd_geometry *)arg;
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg);
+#endif /* CCISS_DEBUG */ 
+	
+	switch(cmd) {
+	case HDIO_GETGEO:
+		if (hba[ctlr]->drv[dsk].cylinders) {
+			diskinfo[0] = hba[ctlr]->drv[dsk].heads;
+			diskinfo[1] = hba[ctlr]->drv[dsk].sectors;
+			diskinfo[2] = hba[ctlr]->drv[dsk].cylinders;
+		} else {
+			diskinfo[0] = 0xff;
+			diskinfo[1] = 0x3f;
+			diskinfo[2] = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f);		}
+		put_user(diskinfo[0], &geo->heads);
+		put_user(diskinfo[1], &geo->sectors);
+		put_user(diskinfo[2], &geo->cylinders);
+		put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect, &geo->start);
+		return 0;
+	case BLKGETSIZE:
+		if (!arg) return -EINVAL;
+		put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects, (long*)arg);
+		return 0;
+	case BLKRASET:
+		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+		if (!(inode->i_rdev)) return -EINVAL;
+		if (arg>0xff) return -EINVAL;
+		read_ahead[MAJOR(inode->i_rdev)] = arg;
+		return 0;
+	case BLKRAGET:
+		if (!arg) return -EINVAL;
+		put_user(read_ahead[MAJOR(inode->i_rdev)], (int*)arg);
+		return 0;
+	case BLKRRPART:
+		return revalidate_logvol(inode->i_rdev, 1);
+
+	case CCISS_GETPCIINFO:
+	{
+		cciss_pci_info_struct pciinfo;
+
+		if (!arg) return -EINVAL;
+		pciinfo.bus = hba[ctlr]->pci_bus;
+		pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+		pciinfo.board_id = hba[ctlr]->board_id;
+		copy_to_user_ret((void *) arg, &pciinfo,  
+			sizeof( cciss_pci_info_struct),  -EFAULT);
+		return(0);
+	}	
+	case CCISS_GETINTINFO:
+	{
+		cciss_coalint_struct intinfo;
+		ctlr_info_t *c = hba[ctlr];
+
+		if (!arg) return -EINVAL;
+		intinfo.delay = readl(&c->cfgtable->HostWrite.CoalIntDelay);
+		intinfo.count = readl(&c->cfgtable->HostWrite.CoalIntCount);
+		copy_to_user_ret((void *) arg, &intinfo,
+                        sizeof( cciss_coalint_struct),  -EFAULT);
+                return(0);
+        }
+	case CCISS_SETINTINFO:
+        {
+                cciss_coalint_struct intinfo;
+                ctlr_info_t *c = hba[ctlr];
+		unsigned long flags;
+		int i;
+
+		if (!arg) return -EINVAL;	
+		if (!capable(CAP_SYS_ADMIN)) return -EPERM;
+		copy_from_user_ret(&intinfo, (void *) arg, 
+			sizeof( cciss_coalint_struct),  -EFAULT);
+		if ( (intinfo.delay == 0 ) && (intinfo.count == 0))
+
+		{
+//			printk("cciss_ioctl: delay and count cannot be 0\n");
+			return( -EINVAL);
+		}
+		spin_lock_irqsave(&io_request_lock, flags);
+		/* Can only safely update if no commands outstanding */ 
+		if (c->commands_outstanding > 0 )
+		{
+//			printk("cciss_ioctl: cannot change coalasing "
+//				"%d commands outstanding on controller\n", 
+//					c->commands_outstanding);
+			spin_unlock_irqrestore(&io_request_lock, flags);
+			return(-EINVAL);
+		}
+		/* Update the field, and then ring the doorbell */ 
+		writel( intinfo.delay, 
+			&(c->cfgtable->HostWrite.CoalIntDelay));
+		writel( intinfo.count, 
+                        &(c->cfgtable->HostWrite.CoalIntCount));
+		writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL);
+
+		for(i=0;i<MAX_CONFIG_WAIT;i++)
+		{
+			if (!(readl(c->vaddr + SA5_DOORBELL) 
+					& CFGTBL_ChangeReq))
+				break;
+			/* delay and try again */
+			udelay(1000);
+		}	
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		if (i >= MAX_CONFIG_WAIT)
+			return( -EFAULT);
+                return(0);
+        }
+	case CCISS_GETNODENAME:
+        {
+                NodeName_type NodeName;
+                ctlr_info_t *c = hba[ctlr];
+		int i; 
+
+		if (!arg) return -EINVAL;
+		for(i=0;i<16;i++)
+			NodeName[i] = readb(&c->cfgtable->ServerName[i]);
+                copy_to_user_ret((void *) arg, NodeName,
+                        sizeof( NodeName_type),  -EFAULT);
+                return(0);
+        }
+	case CCISS_SETNODENAME:
+	{
+		NodeName_type NodeName;
+		ctlr_info_t *c = hba[ctlr];
+		unsigned long flags;
+		int i;
+
+		if (!arg) return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN)) return -EPERM;
+		
+		copy_from_user_ret(NodeName, (void *) arg, 
+			sizeof( NodeName_type),  -EFAULT);
+
+		spin_lock_irqsave(&io_request_lock, flags);
+
+			/* Update the field, and then ring the doorbell */ 
+		for(i=0;i<16;i++)
+			writeb( NodeName[i], &c->cfgtable->ServerName[i]);
+			
+		writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL);
+
+		for(i=0;i<MAX_CONFIG_WAIT;i++)
+		{
+			if (!(readl(c->vaddr + SA5_DOORBELL) 
+					& CFGTBL_ChangeReq))
+				break;
+			/* delay and try again */
+			udelay(1000);
+		}	
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		if (i >= MAX_CONFIG_WAIT)
+			return( -EFAULT);
+                return(0);
+        }
+
+	case CCISS_GETHEARTBEAT:
+        {
+                Heartbeat_type heartbeat;
+                ctlr_info_t *c = hba[ctlr];
+
+		if (!arg) return -EINVAL;
+                heartbeat = readl(&c->cfgtable->HeartBeat);
+                copy_to_user_ret((void *) arg, &heartbeat,
+                        sizeof( Heartbeat_type),  -EFAULT);
+                return(0);
+        }
+	case CCISS_GETBUSTYPES:
+        {
+                BusTypes_type BusTypes;
+                ctlr_info_t *c = hba[ctlr];
+
+		if (!arg) return -EINVAL;
+                BusTypes = readl(&c->cfgtable->BusTypes);
+                copy_to_user_ret((void *) arg, &BusTypes,
+                        sizeof( BusTypes_type),  -EFAULT);
+                return(0);
+        }
+	case CCISS_GETFIRMVER:
+        {
+		FirmwareVer_type firmware;
+
+		if (!arg) return -EINVAL;
+		memcpy(firmware, hba[ctlr]->firm_ver, 4);
+
+                copy_to_user_ret((void *) arg, firmware,
+                        sizeof( FirmwareVer_type),  -EFAULT);
+                return(0);
+        }
+        case CCISS_GETDRIVVER:
+        {
+		DriverVer_type DriverVer = DRIVER_VERSION;
+
+                if (!arg) return -EINVAL;
+
+                copy_to_user_ret((void *) arg, &DriverVer,
+                        sizeof( DriverVer_type),  -EFAULT);
+                return(0);
+        }
+
+	case CCISS_REVALIDVOLS:
+                return( revalidate_allvol(inode->i_rdev));
+	
+	case CCISS_PASSTHRU:
+	{
+		IOCTL_Command_struct iocommand;
+		ctlr_info_t *h = hba[ctlr];
+		CommandList_struct *c;
+		char 	*buff = NULL;
+		u64bit	temp64;
+		unsigned long flags;
+
+		if (!arg) return -EINVAL;
+	
+		if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+
+		copy_from_user_ret(&iocommand, (void *) arg,
+                        sizeof( IOCTL_Command_struct),  -EFAULT); 
+		if((iocommand.buf_size < 1) && 
+				(iocommand.Request.Type.Direction != XFER_NONE))
+		{	
+			return -EINVAL;
+		} 
+		/* Check kmalloc limits */
+		if(iocommand.buf_size > 128000)
+			return -EINVAL;
+		if(iocommand.buf_size > 0)
+		{
+			buff =  kmalloc(iocommand.buf_size, GFP_KERNEL);
+			if( buff == NULL) 
+				return -EFAULT;
+		}
+		if (iocommand.Request.Type.Direction == XFER_WRITE)
+		{
+			/* Copy the data into the buffer we created */ 
+			copy_from_user_ret(buff, iocommand.buf,
+                        	iocommand.buf_size,  -EFAULT);
+		}
+		if ((c = cmd_alloc(NULL)) == NULL)
+		{
+			if(buff!=NULL)
+				kfree(buff);
+			return -ENOMEM;
+		}
+			// Fill in the command type 
+		c->cmd_type = CMD_IOCTL_PEND;
+			// Fill in Command Header 
+		c->Header.ReplyQueue = 0;  // unused in simple mode
+		if( iocommand.buf_size > 0) 	// buffer to fill 
+		{
+			c->Header.SGList = 1;
+			c->Header.SGTotal= 1;
+		} else	// no buffers to fill  
+		{
+			c->Header.SGList = 0;
+                	c->Header.SGTotal= 0;
+		}
+		c->Header.LUN = iocommand.LUN_info;
+		c->Header.Tag.lower = c->busaddr;  // use the kernel address the cmd block for tag
+		
+		// Fill in Request block 
+		c->Request = iocommand.Request; 
+	
+		// Fill in the scatter gather information
+		if (iocommand.buf_size > 0 ) 
+		{	
+			temp64.val = (__u64) virt_to_bus(buff);
+			c->SG[0].Addr.lower = temp64.val32.lower;
+			c->SG[0].Addr.upper = temp64.val32.upper;
+			c->SG[0].Len = iocommand.buf_size;
+			c->SG[0].Ext = 0;  // we are not chaining
+		}
+		/* Put the request on the tail of the request queue */
+		spin_lock_irqsave(&io_request_lock, flags);
+		addQ(&h->reqQ, c);
+		h->Qdepth++;
+		start_io(h);
+		spin_unlock_irqrestore(&io_request_lock, flags);
+
+		/* Wait for completion */
+		while(c->cmd_type != CMD_IOCTL_DONE)
+		{
+			schedule_timeout(1);
+			rmb();
+		}
+
+		/* Copy the error information out */ 
+		iocommand.error_info = *(c->err_info);
+		if ( copy_to_user((void *) arg, &iocommand,
+                        sizeof( IOCTL_Command_struct) ) < 0 )
+		{
+			cmd_free(NULL, c);
+			if (buff != NULL) 
+				kfree(buff);
+			return( -EFAULT);	
+		} 	
+
+		if (iocommand.Request.Type.Direction == XFER_READ)
+                {
+                        /* Copy the data out of the buffer we created */
+                        if (copy_to_user(iocommand.buf, buff, 
+                        	iocommand.buf_size) < 0) 
+			{
+			             cmd_free(NULL, c);
+			             kfree(buff);
+			}
+                }
+		cmd_free(NULL, c);
+                if (buff != NULL)
+                	kfree(buff);
+                return(0);
+	} 
+
+	RO_IOCTLS(inode->i_rdev, arg);
+
+	default:
+		return -EBADRQC;
+	}
+	
+}
+
+/* Borrowed and adapted from sd.c */
+static int revalidate_logvol(kdev_t dev, int maxusage)
+{
+        int ctlr, target;
+        struct gendisk *gdev;
+        unsigned long flags;
+        int max_p;
+        int start;
+        int i;
+
+        target = MINOR(dev) >> NWD_SHIFT;
+        ctlr = MAJOR(dev) - MAJOR_NR;
+        gdev = &(hba[ctlr]->gendisk);
+
+        spin_lock_irqsave(&io_request_lock, flags);
+        if (hba[ctlr]->drv[target].usage_count > maxusage) {
+                spin_unlock_irqrestore(&io_request_lock, flags);
+                printk(KERN_WARNING "cpqarray: Device busy for "
+                        "revalidation (usage=%d)\n",
+                        hba[ctlr]->drv[target].usage_count);
+                return -EBUSY;
+        }
+        hba[ctlr]->drv[target].usage_count++;
+        spin_unlock_irqrestore(&io_request_lock, flags);
+
+        max_p = gdev->max_p;
+        start = target << gdev->minor_shift;
+
+        for(i=max_p; i>=0; i--) {
+                int minor = start+i;
+                kdev_t devi = MKDEV(MAJOR_NR + ctlr, minor);
+                struct super_block *sb = get_super(devi);
+                sync_dev(devi);
+                if (sb) invalidate_inodes(sb);
+                invalidate_buffers(devi);
+                gdev->part[minor].start_sect = 0;
+                gdev->part[minor].nr_sects = 0;
+
+                /* reset the blocksize so we can read the partition table */
+                blksize_size[MAJOR_NR+ctlr][minor] = 1024;
+        }
+	gdev->part[start].nr_sects =  hba[ctlr]->drv[target].nr_blocks;
+        resetup_one_dev(gdev, target);
+        hba[ctlr]->drv[target].usage_count--;
+        return 0;
+}
+
+static int frevalidate_logvol(kdev_t dev)
+{
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss: frevalidate has been called\n");
+#endif /* CCISS_DEBUG */ 
+	return revalidate_logvol(dev, 0);
+}
+
+/*
+ * revalidate_allvol is for online array config utilities.  After a
+ * utility reconfigures the drives in the array, it can use this function
+ * (through an ioctl) to make the driver zap any previous disk structs for
+ * that controller and get new ones.
+ *
+ * Right now I'm using the getgeometry() function to do this, but this
+ * function should probably be finer grained and allow you to revalidate one
+ * particualar logical volume (instead of all of them on a particular
+ * controller).
+ */
+static int revalidate_allvol(kdev_t dev)
+{
+	int ctlr, i;
+	unsigned long flags;
+
+	ctlr = MAJOR(dev) - MAJOR_NR;
+        if (MINOR(dev) != 0)
+                return -ENXIO;
+
+        spin_lock_irqsave(&io_request_lock, flags);
+        if (hba[ctlr]->usage_count > 1) {
+                spin_unlock_irqrestore(&io_request_lock, flags);
+                printk(KERN_WARNING "cciss: Device busy for volume"
+                        " revalidation (usage=%d)\n", hba[ctlr]->usage_count);
+                return -EBUSY;
+        }
+        spin_unlock_irqrestore(&io_request_lock, flags);
+        hba[ctlr]->usage_count++;
+
+        /*
+         * Set the partition and block size structures for all volumes
+         * on this controller to zero.  We will reread all of this data
+         */
+	memset(hba[ctlr]->hd,         0, sizeof(struct hd_struct) * 256);
+        memset(hba[ctlr]->sizes,      0, sizeof(int) * 256);
+        memset(hba[ctlr]->blocksizes, 0, sizeof(int) * 256);
+        memset(hba[ctlr]->hardsizes,  0, sizeof(int) * 256);
+        memset(hba[ctlr]->drv,        0, sizeof(drive_info_struct)
+						* CISS_MAX_LUN);
+        hba[ctlr]->gendisk.nr_real = 0;
+
+        /*
+         * Tell the array controller not to give us any interupts while
+         * we check the new geometry.  Then turn interrupts back on when
+         * we're done.
+         */
+        hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_OFF);
+        cciss_getgeometry(ctlr);
+        hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_ON);
+
+        cciss_geninit(&(hba[ctlr]->gendisk));
+        for(i=0; i<NWD; i++)
+                if (hba[ctlr]->sizes[ i<<NWD_SHIFT ])
+                        revalidate_logvol(dev+(i<<NWD_SHIFT), 2);
+
+        hba[ctlr]->usage_count--;
+        return 0;
+}
+
+
+
+/*
+ *   Wait polling for a command to complete.
+ *   The memory mapped FIFO is polled for the completion.
+ *   Used only at init time, interrupts disabled.
+ */
+static unsigned long pollcomplete(int ctlr)
+{
+        unsigned long done;
+        int i;
+
+        /* Wait (up to 2 seconds) for a command to complete */
+
+        for (i = 200000; i > 0; i--) {
+                done = hba[ctlr]->access.command_completed(hba[ctlr]);
+                if (done == FIFO_EMPTY) {
+                        udelay(10);     /* a short fixed delay */
+                } else
+                        return (done);
+        }
+        /* Invalid address to tell caller we ran out of time */
+        return 1;
+}
+/*
+ * Send a command to the controller, and wait for it to complete.  
+ * Only used at init time. 
+ */
+static int sendcmd(
+	__u8	cmd,
+	int	ctlr,
+	void	*buff,
+	size_t	size,
+	unsigned int use_unit_num,
+	unsigned int log_unit,
+	__u8	page_code )
+{
+	CommandList_struct *c;
+	int i;
+	unsigned long complete;
+	ctlr_info_t *info_p= hba[ctlr];
+	u64bit temp64;
+
+	c = cmd_alloc(info_p);
+	if (c == NULL)
+	{
+		printk(KERN_WARNING "cciss: unable to get memory");
+		return(IO_ERROR);
+	}
+	// Fill in Command Header 
+	c->Header.ReplyQueue = 0;  // unused in simple mode
+	if( buff != NULL) 	// buffer to fill 
+	{
+		c->Header.SGList = 1;
+		c->Header.SGTotal= 1;
+	} else	// no buffers to fill  
+	{
+		c->Header.SGList = 0;
+                c->Header.SGTotal= 0;
+	}
+	c->Header.Tag.lower = c->busaddr;  // use the kernel address the cmd block for tag
+	// Fill in Request block 	
+	switch(cmd)
+	{
+		case  CISS_INQUIRY:
+			/* If the logical unit number is 0 then, this is going
+				to controller so It's a physical command
+				mode = 0 target = 0.
+				So we have nothing to write. 
+				Otherwise 
+				mode = 1  target = LUNID
+			*/
+			if(use_unit_num != 0)
+			{
+				c->Header.LUN.LogDev.VolId=
+                                	hba[ctlr]->drv[log_unit].LunID;
+                        	c->Header.LUN.LogDev.Mode = 1;
+			}
+			/* are we trying to read a vital product page */
+			if(page_code != 0)
+			{
+				c->Request.CDB[1] = 0x01;
+				c->Request.CDB[2] = page_code;
+			}
+			c->Request.CDBLen = 6;
+			c->Request.Type.Type =  TYPE_CMD; // It is a command. 
+			c->Request.Type.Attribute = ATTR_SIMPLE;  
+			c->Request.Type.Direction = XFER_READ; // Read 
+			c->Request.Timeout = 0; // Don't time out 
+			c->Request.CDB[0] =  CISS_INQUIRY;
+			c->Request.CDB[4] = size  & 0xFF;  
+		break;
+		case CISS_REPORT_LOG:
+                        /* Talking to controller so It's a physical command
+                                mode = 00 target = 0.
+                                So we have nothing to write.
+                        */
+                        c->Request.CDBLen = 12;
+                        c->Request.Type.Type =  TYPE_CMD; // It is a command.
+                        c->Request.Type.Attribute = ATTR_SIMPLE; 
+                        c->Request.Type.Direction = XFER_READ; // Read
+                        c->Request.Timeout = 0; // Don't time out
+                        c->Request.CDB[0] = CISS_REPORT_LOG;
+                        c->Request.CDB[6] = (size >> 24) & 0xFF;  //MSB
+                        c->Request.CDB[7] = (size >> 16) & 0xFF;
+                        c->Request.CDB[8] = (size >> 8) & 0xFF;
+                        c->Request.CDB[9] = size & 0xFF;
+                break;
+
+		case CCISS_READ_CAPACITY:
+			c->Header.LUN.LogDev.VolId= 
+				hba[ctlr]->drv[log_unit].LunID;
+			c->Header.LUN.LogDev.Mode = 1;
+			c->Request.CDBLen = 10;
+                        c->Request.Type.Type =  TYPE_CMD; // It is a command.
+                        c->Request.Type.Attribute = ATTR_SIMPLE; 
+                        c->Request.Type.Direction = XFER_READ; // Read
+                        c->Request.Timeout = 0; // Don't time out
+                        c->Request.CDB[0] = CCISS_READ_CAPACITY;
+		break;
+		default:
+			printk(KERN_WARNING
+				"cciss:  Unknown Command 0x%c sent attempted\n",
+				  cmd);
+			cmd_free(info_p, c);
+			return(IO_ERROR);
+	};
+	// Fill in the scatter gather information
+	if (size > 0 ) 
+	{
+		temp64.val = (__u64) virt_to_bus(buff);
+		c->SG[0].Addr.lower = temp64.val32.lower;
+		c->SG[0].Addr.upper = temp64.val32.upper;
+		c->SG[0].Len = size;
+		c->SG[0].Ext = 0;  // we are not chaining
+	}
+	/*
+         * Disable interrupt
+         */
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss: turning intr off\n");
+#endif /* CCISS_DEBUG */ 
+        info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF);
+	
+	/* Make sure there is room in the command FIFO */
+        /* Actually it should be completely empty at this time. */
+        for (i = 200000; i > 0; i--) 
+	{
+		/* if fifo isn't full go */
+                if (!(info_p->access.fifo_full(info_p))) 
+		{
+			
+                        break;
+                }
+                udelay(10);
+                printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full,"
+                        " waiting!\n", ctlr);
+        }
+        /*
+         * Send the cmd
+         */
+        info_p->access.submit_command(info_p, c);
+        complete = pollcomplete(ctlr);
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss: command completed\n");
+#endif /* CCISS_DEBUG */
+
+	if (complete != 1) {
+		if ( (complete & CISS_ERROR_BIT)
+		     && (complete & ~CISS_ERROR_BIT) == c->busaddr)
+		     {
+			/* if data overrun or underun on Report command 
+				ignore it 
+			*/
+			if (((c->Request.CDB[0] == CISS_REPORT_LOG) ||
+			     (c->Request.CDB[0] == CISS_INQUIRY)) &&
+				((c->err_info->CommandStatus == 
+					CMD_DATA_OVERRUN) || 
+				 (c->err_info->CommandStatus == 
+					CMD_DATA_UNDERRUN)
+			 	))
+			{
+				complete = c->busaddr;
+			} else
+			{
+				printk(KERN_WARNING "ciss ciss%d: sendcmd"
+				" Error %x \n", ctlr, 
+					c->err_info->CommandStatus); 
+				printk(KERN_WARNING "ciss ciss%d: sendcmd"
+				" offensive info\n"
+				"  size %x\n   num %x   value %x\n", ctlr,
+				  c->err_info->MoreErrInfo.Invalid_Cmd.offense_size,
+				  c->err_info->MoreErrInfo.Invalid_Cmd.offense_num,
+				  c->err_info->MoreErrInfo.Invalid_Cmd.offense_value);
+				cmd_free(info_p,c);
+				return(IO_ERROR);
+			}
+		}
+                if (complete != c->busaddr) {
+                        printk( KERN_WARNING "cciss cciss%d: SendCmd "
+                      "Invalid command list address returned! (%lx)\n",
+                                ctlr, complete);
+                        cmd_free(info_p, c);
+                        return (IO_ERROR);
+                }
+        } else {
+                printk( KERN_WARNING
+                        "cciss cciss%d: SendCmd Timeout out, "
+                        "No command list address returned!\n",
+                        ctlr);
+                cmd_free(info_p, c);
+                return (IO_ERROR);
+        }
+	cmd_free(info_p, c);
+        return (IO_OK);
+} 
+/*
+ * Map (physical) PCI mem into (virtual) kernel space
+ */
+static ulong remap_pci_mem(ulong base, ulong size)
+{
+        ulong page_base        = ((ulong) base) & PAGE_MASK;
+        ulong page_offs        = ((ulong) base) - page_base;
+        ulong page_remapped    = (ulong) ioremap(page_base, page_offs+size);
+
+        return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+}
+
+/*
+ * Enqueuing and dequeuing functions for cmdlists.
+ */
+static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c)
+{
+        if (*Qptr == NULL) {
+                *Qptr = c;
+                c->next = c->prev = c;
+        } else {
+                c->prev = (*Qptr)->prev;
+                c->next = (*Qptr);
+                (*Qptr)->prev->next = c;
+                (*Qptr)->prev = c;
+        }
+}
+
+static inline CommandList_struct *removeQ(CommandList_struct **Qptr, 
+						CommandList_struct *c)
+{
+        if (c && c->next != c) {
+                if (*Qptr == c) *Qptr = c->next;
+                c->prev->next = c->next;
+                c->next->prev = c->prev;
+        } else {
+                *Qptr = NULL;
+        }
+        return c;
+}
+
+/* 
+ * Takes jobs of the Q and sends them to the hardware, then puts it on 
+ * the Q to wait for completion. 
+ */ 
+static void start_io( ctlr_info_t *h)
+{
+	CommandList_struct *c;
+	
+	while(( c = h->reqQ) != NULL )
+	{
+		/* can't do anything if fifo is full */
+		if ((h->access.fifo_full(h)))
+		{
+			printk(KERN_WARNING "cciss: fifo full \n");
+			return;
+		}
+		/* Get the frist entry from the Request Q */ 
+		removeQ(&(h->reqQ), c);
+		h->Qdepth--;
+	
+		/* Tell the controller execute command */ 
+		h->access.submit_command(h, c);
+		
+		/* Put job onto the completed Q */ 
+		addQ (&(h->cmpQ), c); 
+	}
+}
+
+static inline void complete_buffers( struct buffer_head *bh, int status)
+{
+	struct buffer_head *xbh;
+	
+	while(bh)
+	{
+		xbh = bh->b_reqnext; 
+		bh->b_reqnext = NULL; 
+		bh->b_end_io(bh, status);
+		bh = xbh;
+	}
+} 
+/* checks the status of the job and calls complete buffers to mark all 
+ * buffers for the completed job. 
+ */ 
+static inline void complete_command( CommandList_struct *cmd, int timeout)
+{
+	int status = 1;
+	
+	if (timeout)
+		status = 0; 
+	if(cmd->err_info->CommandStatus != 0) 
+	{ /* an error has occured */ 
+		switch(cmd->err_info->CommandStatus)
+		{
+			case CMD_TARGET_STATUS:
+				printk(KERN_WARNING "cciss: cmd %p has "
+					" completed with errors\n", cmd);
+				if( cmd->err_info->ScsiStatus)
+                		{
+                    			printk(KERN_WARNING "cciss: cmd %p "
+					"has SCSI Status = %x\n",
+                        			cmd,  
+						cmd->err_info->ScsiStatus);
+                		}
+
+			break;
+			case CMD_DATA_UNDERRUN:
+				printk(KERN_WARNING "cciss: cmd %p has"
+					" completed with data underrun "
+					"reported\n", cmd);
+			break;
+			case CMD_DATA_OVERRUN:
+				printk(KERN_WARNING "cciss: cmd %p has"
+					" completed with data overrun "
+					"reported\n", cmd);
+			break;
+			case CMD_INVALID:
+				printk(KERN_WARNING "cciss: cmd %p is "
+					"reported invalid\n", cmd);
+				status = 0;
+			break;
+			case CMD_PROTOCOL_ERR:
+                                printk(KERN_WARNING "cciss: cmd %p has "
+					"protocol error \n", cmd);
+                                status = 0;
+                        break;
+			case CMD_HARDWARE_ERR:
+                                printk(KERN_WARNING "cciss: cmd %p had " 
+                                        " hardware error\n", cmd);
+                                status = 0;
+                        break;
+			case CMD_CONNECTION_LOST:
+				printk(KERN_WARNING "cciss: cmd %p had "
+					"connection lost\n", cmd);
+				status=0;
+			break;
+			case CMD_ABORTED:
+				printk(KERN_WARNING "cciss: cmd %p was "
+					"aborted\n", cmd);
+				status=0;
+			break;
+			case CMD_ABORT_FAILED:
+				printk(KERN_WARNING "cciss: cmd %p reports "
+					"abort failed\n", cmd);
+				status=0;
+			break;
+			case CMD_UNSOLICITED_ABORT:
+				printk(KERN_WARNING "cciss: cmd %p aborted "
+					"do to an unsolicited abort\n", cmd);
+				status=0;
+			break;
+			case CMD_TIMEOUT:
+				printk(KERN_WARNING "cciss: cmd %p timedout\n",
+					cmd);
+				status=0;
+			break;
+			default:
+				printk(KERN_WARNING "cciss: cmd %p returned "
+					"unknown status %x\n", cmd, 
+						cmd->err_info->CommandStatus); 
+				status=0;
+		}
+	}
+	complete_buffers(cmd->bh, status);
+}
+/* 
+ * Get a request and submit it to the controller. 
+ * Currently we do one request at a time.  Ideally we would like to send
+ * everything to the controller on the first call, but there is a danger
+ * of holding the io_request_lock for to long.  
+ */
+static void do_cciss_request(int ctlr)
+{
+	ctlr_info_t *h= hba[ctlr];
+	CommandList_struct *c;
+	int log_unit, start_blk, seg, sect;
+	char *lastdataend;
+	struct buffer_head *bh;
+	struct request *creq;
+	u64bit temp64;
+
+	/* loop till creq is null or current_request is inactive */
+	creq = blk_dev[MAJOR_NR+ctlr].current_request;
+	if ((creq == NULL) || (creq->rq_status == RQ_INACTIVE))
+	{
+		/* nothing to do... restart processing and return */ 
+		start_io(h);
+		return;
+	}
+	if ((ctlr != (MAJOR(creq->rq_dev)-MAJOR_NR)) || (ctlr > nr_ctlr)
+		|| (h == NULL))
+	{
+#ifdef CCISS_DEBUG
+		printk(KERN_WARNING "cciss: doreq cmd of %d, %x at %p\n",
+			ctlr, creq->rq_dev, creq);
+#endif /* CCISS_DEBUG */
+		complete_buffers(creq->bh, 0);
+		return;
+	}
+	if (( c = cmd_alloc(h)) == NULL)
+	{
+		start_io(h);
+		return;
+	}
+	c->cmd_type = CMD_RWREQ;      
+	bh = c->bh = creq->bh;
+	
+	/* fill in the request */ 
+	log_unit = MINOR(creq->rq_dev) >> NWD_SHIFT; 
+	c->Header.ReplyQueue = 0;  // unused in simple mode
+	c->Header.Tag.lower = c->busaddr;  // use the physical address the cmd block for tag
+	c->Header.LUN.LogDev.VolId= hba[ctlr]->drv[log_unit].LunID;
+	c->Header.LUN.LogDev.Mode = 1;
+	c->Request.CDBLen = 10; // 12 byte commands not in FW yet;
+	c->Request.Type.Type =  TYPE_CMD; // It is a command. 
+	c->Request.Type.Attribute = ATTR_SIMPLE; 
+	c->Request.Type.Direction = 
+		(creq->cmd == READ) ? XFER_READ: XFER_WRITE; 
+	c->Request.Timeout = 0; // Don't time out	
+	c->Request.CDB[0] = (creq->cmd == READ) ? CCISS_READ : CCISS_WRITE;
+	start_blk = hba[ctlr]->hd[MINOR(creq->rq_dev)].start_sect + creq->sector;
+	if (bh == NULL)
+		panic("cciss: bh== NULL?");
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",(int) creq->sector,
+		(int) creq->nr_sectors);	
+#endif /* CCISS_DEBUG */
+	seg = 0; 
+	lastdataend = NULL;
+	sect = 0;
+	while(bh)
+	{
+		sect += bh->b_size/512;
+		if (bh->b_size % 512)
+		{
+			printk(KERN_CRIT "cciss:  Oh Man.  %d+%d, size=%d\n", 
+				(int) creq->sector, sect, (int) bh->b_size);
+			panic("b_size 512 != 0\n");
+		}
+		if (bh->b_data == lastdataend)
+		{  // tack it on to the last segment 
+			c->SG[seg-1].Len +=bh->b_size;
+			lastdataend += bh->b_size;
+		} else
+		{
+			c->SG[seg].Len = bh->b_size;
+			temp64.val = (__u64) virt_to_bus(bh->b_data);
+			c->SG[seg].Addr.lower = temp64.val32.lower;
+			c->SG[seg].Addr.upper = temp64.val32.upper;
+			c->SG[0].Ext = 0;  // we are not chaining
+			lastdataend = bh->b_data + bh->b_size;
+			if( ++seg == MAXSGENTRIES)
+			{
+				break; 
+			}
+		}
+		bh = bh->b_reqnext;
+	}
+	/* track how many SG entries we are using */ 
+	if( seg > h->maxSG)
+		h->maxSG = seg; 
+
+	/* adjusting the remaining request, if any */ 
+	creq-> sector+= sect;
+	creq->nr_sectors -= sect; 
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n", sect, seg);
+#endif /* CCISS_DEBUG */
+
+	c->Header.SGList = c->Header.SGTotal = seg;
+	c->Request.CDB[1]= 0;
+	c->Request.CDB[2]= (start_blk >> 24) & 0xff;	//MSB
+	c->Request.CDB[3]= (start_blk >> 16) & 0xff;
+	c->Request.CDB[4]= (start_blk >>  8) & 0xff;
+	c->Request.CDB[5]= start_blk & 0xff;
+	c->Request.CDB[6]= 0; // (sect >> 24) & 0xff; MSB
+	// c->Request.CDB[7]= (sect >> 16) & 0xff; 
+	c->Request.CDB[7]= (sect >>  8) & 0xff; 
+	c->Request.CDB[8]= sect & 0xff; 
+	c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+
+	/* check to see if we going to complete the entire request */ 
+	/* if so, mark this request as Done and ready the next one */ 
+	if (creq->nr_sectors)
+	{
+#ifdef CCISS_DEBUG
+		printk(KERN_DEBUG "cciss: More to do on the same request %p %ld\n", 
+			creq, creq->nr_sectors);
+#endif /* CCISS_DEBUG */
+
+		creq->bh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+	} else
+	{
+#ifdef CCISS_DEBUG
+		printk(KERN_DEBUG "cciss: Done with %p, queueing %p\n", creq, creq->next);
+#endif /* CCISS_DEBUG */
+
+		creq->rq_status = RQ_INACTIVE;
+		blk_dev[MAJOR_NR+ctlr].current_request = creq->next;
+		wake_up(&wait_for_request);
+	}
+	addQ(&(h->reqQ),c);
+	h->Qdepth++;
+	if(h->Qdepth > h->maxQsinceinit)
+		h->maxQsinceinit = h->Qdepth; 
+	start_io(h);
+}
+
+static void do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ctlr_info_t *h = dev_id;
+	CommandList_struct *c;
+	unsigned long flags;
+	__u32 a, a1;
+
+
+	/* Is this interrupt for us? */
+	if ( h->access.intr_pending(h) == 0)
+		return;
+
+	/*
+	 * If there are completed commands in the completion queue,
+	 * we had better do something about it.
+	 */
+	spin_lock_irqsave(&io_request_lock, flags);
+	while( h->access.intr_pending(h))
+	{
+		while((a = h->access.command_completed(h)) != FIFO_EMPTY) 
+		{
+			a1 = a;
+			a &= ~3;
+			if ((c = h->cmpQ) == NULL)
+			{  
+				printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1);
+				continue;	
+			} 
+			while(c->busaddr != a) {
+				c = c->next;
+				if (c == h->cmpQ) 
+					break;
+			}
+			/*
+			 * If we've found the command, take it off the
+			 * completion Q and free it
+			 */
+			 if (c->busaddr == a) {
+				removeQ(&h->cmpQ, c);
+				if (c->cmd_type == CMD_RWREQ) {
+					complete_command(c, 0);
+					cmd_free(h, c);
+				} else if (c->cmd_type == CMD_IOCTL_PEND) {
+					c->cmd_type = CMD_IOCTL_DONE;
+				}
+				continue;
+			}
+		}
+	}
+	/*
+	 * See if we can queue up some more IO
+	 */
+	do_cciss_request(h->ctlr);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+/* 
+ *  We cannot read the structure directly, for portablity we must use 
+ *   the io functions.
+ *   This is for debug only. 
+ */
+#ifdef CCISS_DEBUG
+static void print_cfg_table( CfgTable_struct *tb)
+{
+	int i;
+	char temp_name[17];
+
+	printk("Controller Configuration information\n");
+	printk("------------------------------------\n");
+	for(i=0;i<4;i++)
+		temp_name[i] = readb(&(tb->Signature[i]));
+	temp_name[4]='\0';
+	printk("   Signature = %s\n", temp_name); 
+	printk("   Spec Number = %d\n", readl(&(tb->SpecValence)));
+	printk("   Transport methods supported = 0x%x\n", 
+				readl(&(tb-> TransportSupport)));
+	printk("   Transport methods active = 0x%x\n", 
+				readl(&(tb->TransportActive)));
+	printk("   Requested transport Method = 0x%x\n", 
+			readl(&(tb->HostWrite.TransportRequest)));
+	printk("   Coalese Interrupt Delay = 0x%x\n", 
+			readl(&(tb->HostWrite.CoalIntDelay)));
+	printk("   Coalese Interrupt Count = 0x%x\n", 
+			readl(&(tb->HostWrite.CoalIntCount)));
+	printk("   Max outstanding commands = 0x%d\n", 
+			readl(&(tb->CmdsOutMax)));
+	printk("   Bus Types = 0x%x\n", readl(&(tb-> BusTypes)));
+	for(i=0;i<16;i++)
+		temp_name[i] = readb(&(tb->ServerName[i]));
+	temp_name[16] = '\0';
+	printk("   Server Name = %s\n", temp_name);
+	printk("   Heartbeat Counter = 0x%x\n\n\n", 
+			readl(&(tb->HeartBeat)));
+}
+#endif /* CCISS_DEBUG */
+
+static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+{
+	ushort vendor_id, device_id, command;
+	unchar cache_line_size, latency_timer;
+	unchar irq, revision;
+	uint addr[6];
+	__u32 board_id;
+	struct pci_dev *pdev;
+
+	int i;
+
+	pdev = pci_find_slot(bus, device_fn);
+	vendor_id = pdev->vendor;
+	device_id = pdev->device;
+	irq = pdev->irq;
+
+	for(i=0; i<6; i++)
+		addr[i] = pdev->base_address[i];
+	(void) pcibios_read_config_word(bus, device_fn,
+					PCI_COMMAND,&command);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CLASS_REVISION,&revision);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CACHE_LINE_SIZE, &cache_line_size);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_LATENCY_TIMER, &latency_timer);
+
+	(void) pcibios_read_config_dword(bus, device_fn, 0x2c, &board_id);
+
+#ifdef CCISS_DEBUG
+	printk("vendor_id = %x\n", vendor_id);
+	printk("device_id = %x\n", device_id);
+	printk("command = %x\n", command);
+	for(i=0; i<6; i++)
+		printk("addr[%d] = %x\n", i, addr[i]);
+	printk("revision = %x\n", revision);
+	printk("irq = %x\n", irq);
+	printk("cache_line_size = %x\n", cache_line_size);
+	printk("latency_timer = %x\n", latency_timer);
+	printk("board_id = %x\n", board_id);
+#endif /* CCISS_DEBUG */ 
+
+	c->intr = irq;
+
+	/*
+	 * Memory base addr is first addr , the second points to the config
+         *   table
+	 */
+	c->paddr = addr[0];
+	c->vaddr = remap_pci_mem(c->paddr, 128);
+	c->cfgtable = (CfgTable_struct *) remap_pci_mem(addr[1], 
+						sizeof(CfgTable_struct));
+	c->board_id = board_id;
+
+#ifdef CCISS_DEBUG
+	print_cfg_table(c->cfgtable); 
+#endif /* CCISS_DEBUG */
+	for(i=0; i<NR_PRODUCTS; i++) {
+		if (board_id == products[i].board_id) {
+			c->product_name = products[i].product_name;
+			c->access = *(products[i].access);
+			break;
+		}
+	}
+	if (i == NR_PRODUCTS) {
+		printk(KERN_WARNING "cciss: Sorry, I don't know how"
+			" to access the Smart Array controller %08lx\n", 
+				(unsigned long)board_id);
+		return -1;
+	}
+#ifdef CCISS_DEBUG
+	printk("Trying to put board into Simple mode\n");
+#endif /* CCISS_DEBUG */ 
+	c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
+	/* Update the field, and then ring the doorbell */ 
+	writel( CFGTBL_Trans_Simple, 
+		&(c->cfgtable->HostWrite.TransportRequest));
+	writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL);
+
+	for(i=0;i<MAX_CONFIG_WAIT;i++)
+	{
+		if (!(readl(c->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
+			break;
+		/* delay and try again */
+		udelay(1000);
+	}	
+
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "I counter got to %d %x\n", i, readl(c->vaddr + SA5_DOORBELL));
+#endif /* CCISS_DEBUG */
+#ifdef CCISS_DEBUG
+	print_cfg_table(c->cfgtable);	
+#endif /* CCISS_DEBUG */ 
+
+	if (!(readl(&(c->cfgtable->TransportActive)) & CFGTBL_Trans_Simple))
+	{
+		printk(KERN_WARNING "cciss: unable to get board into"
+					" simple mode\n");
+		return -1;
+	}
+	return 0;
+
+}
+/* 
+ * Scans PCI space for any controllers that this driver can control. 
+ */
+static int cciss_pci_detect(void)
+{
+
+	int index;
+	unchar bus=0, dev_fn=0;
+	
+		for(index=0; ; index++) {
+			if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ,
+			 	PCI_DEVICE_ID_COMPAQ_CISS, 
+					index, &bus, &dev_fn))
+				break;
+			printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n",
+				PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn);
+			if (index == 1000000) break;
+			if (nr_ctlr == 8) {
+				printk(KERN_WARNING "cciss: This driver"
+				" supports a maximum of 8 controllers.\n");
+				break;
+			}
+			hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+			if(hba[nr_ctlr]==NULL)
+			{
+				printk(KERN_ERR "cciss: out of memory.\n");
+				continue;
+			}
+			memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
+			if (cciss_pci_init(hba[nr_ctlr], bus, dev_fn) != 0)
+			{
+				kfree(hba[nr_ctlr]);
+				continue;
+			}
+			sprintf(hba[nr_ctlr]->devname, "cciss%d", nr_ctlr);
+			hba[nr_ctlr]->ctlr = nr_ctlr;
+			hba[nr_ctlr]->pci_bus = bus;
+			hba[nr_ctlr]->pci_dev_fn = dev_fn;
+			nr_ctlr++;
+
+		}
+	return nr_ctlr;
+
+}
+
+/* 
+ * Gets information about the local volumes attached to the controller. 
+ */ 
+static void cciss_getgeometry(int cntl_num)
+{
+	ReportLunData_struct *ld_buff;
+	ReadCapdata_struct *size_buff;
+	InquiryData_struct *inq_buff;
+	int return_code;
+	int i;
+	int listlength = 0;
+	int lunid = 0;
+	int block_size;
+	int total_size; 
+
+	ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
+	if (ld_buff == NULL)
+	{
+		printk(KERN_ERR "cciss: out of memory\n");
+		return;
+	}
+	memset(ld_buff, 0, sizeof(ReportLunData_struct));
+	size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL);
+        if (size_buff == NULL)
+        {
+                printk(KERN_ERR "cciss: out of memory\n");
+		kfree(ld_buff);
+                return;
+        }
+	inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL);
+        if (inq_buff == NULL)
+        {
+                printk(KERN_ERR "cciss: out of memory\n");
+                kfree(ld_buff);
+		kfree(size_buff);
+                return;
+        }
+	/* Get the firmware version */ 
+	return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, 
+		sizeof(InquiryData_struct), 0, 0 ,0 );
+	if (return_code == IO_OK)
+	{
+		hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32];
+		hba[cntl_num]->firm_ver[1] = inq_buff->data_byte[33];
+		hba[cntl_num]->firm_ver[2] = inq_buff->data_byte[34];
+		hba[cntl_num]->firm_ver[3] = inq_buff->data_byte[35];
+	} else /* send command failed */
+	{
+		printk(KERN_WARNING "cciss: unable to determine firmware"
+			" version of controller\n");
+	}
+	/* Get the number of logical volumes */ 
+	return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff, 
+			sizeof(ReportLunData_struct), 0, 0, 0 );
+
+	if( return_code == IO_OK)
+	{
+#ifdef CCISS_DEBUG
+		printk("LUN Data\n--------------------------\n");
+#endif /* CCISS_DEBUG */ 
+
+		listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24;
+		listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16;
+		listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8;	
+		listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]);
+	} else /* reading number of logical volumes failed */
+	{
+		printk(KERN_WARNING "cciss: report logical volume"
+			" command failed\n");
+		listlength = 0;
+	}
+	hba[cntl_num]->num_luns = listlength / 8; // 8 bytes pre entry
+	if (hba[cntl_num]->num_luns > CISS_MAX_LUN)
+	{
+		printk(KERN_ERR "ciss:  only %d number of logical volumes supported\n",
+			CISS_MAX_LUN);
+		hba[cntl_num]->num_luns = CISS_MAX_LUN;
+	}
+#ifdef CCISS_DEBUG
+	printk(KERN_DEBUG "Length = %x %x %x %x = %d\n", ld_buff->LUNListLength[0],
+		ld_buff->LUNListLength[1], ld_buff->LUNListLength[2],
+		ld_buff->LUNListLength[3],  hba[cntl_num]->num_luns);
+#endif /* CCISS_DEBUG */
+	for(i=0; i<  hba[cntl_num]->num_luns ; i++)
+	{
+	  	lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24;
+        	lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16;
+        	lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8;
+        	lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]);
+		hba[cntl_num]->drv[i].LunID = lunid;
+
+#ifdef CCISS_DEBUG
+	  	printk(KERN_DEBUG "LUN[%d]:  %x %x %x %x = %x\n", i, 
+		ld_buff->LUN[i][0], ld_buff->LUN[i][1],ld_buff->LUN[i][2], 
+		ld_buff->LUN[i][3], hba[cntl_num]->drv[i].LunID);
+#endif /* CCISS_DEBUG */
+
+	  	memset(size_buff, 0, sizeof(ReadCapdata_struct));
+	  	return_code = sendcmd(CCISS_READ_CAPACITY, cntl_num, size_buff, 
+				sizeof( ReadCapdata_struct), 1, i, 0 );
+	  	if (return_code == IO_OK)
+		{
+			total_size = (0xff & 
+				(unsigned int)(size_buff->total_size[0])) << 24;
+			total_size |= (0xff & 
+				(unsigned int)(size_buff->total_size[1])) << 16;
+			total_size |= (0xff & 
+				(unsigned int)(size_buff->total_size[2])) << 8;
+			total_size |= (0xff & (unsigned int)
+				(size_buff->total_size[3])); 
+			total_size++; // command returns highest block address
+
+			block_size = (0xff & 
+				(unsigned int)(size_buff->block_size[0])) << 24;
+                	block_size |= (0xff & 
+				(unsigned int)(size_buff->block_size[1])) << 16;
+                	block_size |= (0xff & 
+				(unsigned int)(size_buff->block_size[2])) << 8;
+                	block_size |= (0xff & 
+				(unsigned int)(size_buff->block_size[3]));
+		} else /* read capacity command failed */ 
+		{
+			printk(KERN_WARNING "cciss: read capacity failed\n");
+			total_size = block_size = 0; 
+		}	
+		printk("      blocks= %d block_size= %d\n", total_size,
+					block_size);
+
+		/* Execute the command to read the disk geometry */
+		memset(inq_buff, 0, sizeof(InquiryData_struct));
+		return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff,
+                	sizeof(InquiryData_struct), 1, i ,0xC1 );
+	  	if (return_code == IO_OK)
+		{
+			if(inq_buff->data_byte[8] == 0xFF)
+			{
+			   printk(KERN_WARNING "cciss: reading geometry failed, volume does not support reading geometry\n");
+
+                           hba[cntl_num]->drv[i].block_size = block_size;
+                           hba[cntl_num]->drv[i].nr_blocks = total_size;
+                           hba[cntl_num]->drv[i].heads = 255;
+                           hba[cntl_num]->drv[i].sectors = 32; // Sectors per track
+                           hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32;                	} else
+			{
+
+		 	   hba[cntl_num]->drv[i].block_size = block_size;
+                           hba[cntl_num]->drv[i].nr_blocks = total_size;
+                           hba[cntl_num]->drv[i].heads = 
+					inq_buff->data_byte[6]; 
+                           hba[cntl_num]->drv[i].sectors = 
+					inq_buff->data_byte[7]; 
+			   hba[cntl_num]->drv[i].cylinders = 
+					(inq_buff->data_byte[4] & 0xff) << 8;
+			   hba[cntl_num]->drv[i].cylinders += 
+                                        inq_buff->data_byte[5];
+			}
+		}
+		else /* Get geometry failed */
+		{
+			printk(KERN_WARNING "cciss: reading geometry failed, continuing with default geometry\n"); 
+
+			hba[cntl_num]->drv[i].block_size = block_size;
+			hba[cntl_num]->drv[i].nr_blocks = total_size;
+			hba[cntl_num]->drv[i].heads = 255;
+			hba[cntl_num]->drv[i].sectors = 32; // Sectors per track 
+			hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32;
+		}
+		printk(KERN_INFO "      heads= %d, sectors= %d, cylinders= %d\n\n",
+			hba[cntl_num]->drv[i].heads, 
+			hba[cntl_num]->drv[i].sectors,
+			hba[cntl_num]->drv[i].cylinders);
+
+	}
+	kfree(ld_buff);
+	kfree(size_buff);
+}	
+
+/*
+ *  This is it.  Find all the controllers and register them.  I really hate
+ *  stealing all these major device numbers.
+ *  returns the number of block devices registered.
+ */
+int cciss_init(void)
+{
+	int num_cntlrs_reg = 0;
+	int i;
+
+	void (*request_fns[MAX_CTLR])(void) = {
+                do_cciss_request0, do_cciss_request1,
+                do_cciss_request2, do_cciss_request3,
+                do_cciss_request4, do_cciss_request5,
+                do_cciss_request6, do_cciss_request7,
+        };
+
+	/* detect controllers */
+	cciss_pci_detect();
+
+	if (nr_ctlr == 0)
+		return(num_cntlrs_reg);
+
+	printk(KERN_INFO DRIVER_NAME "\n");
+	printk(KERN_INFO "Found %d controller(s)\n", nr_ctlr);
+	for(i=0;i<nr_ctlr;i++)
+	{
+		if( register_blkdev(MAJOR_NR+i, hba[i]->devname, &cciss_fops))
+		{
+			printk(KERN_ERR "cciss:  Unable to get major number "
+				"%d for %s\n", MAJOR_NR+i, hba[i]->devname);
+			continue;
+		}
+		/* make sure the board interrupts are off */
+		hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
+		if( request_irq(hba[i]->intr, do_cciss_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i]))
+		{
+			printk(KERN_ERR "ciss: Unable to get irq %d for %s\n",
+				hba[i]->intr, hba[i]->devname);
+			unregister_blkdev( MAJOR_NR+i, hba[i]->devname);
+			continue;
+		}
+		num_cntlrs_reg++;
+		hba[i]->cmd_pool_bits = (__u32*)kmalloc(
+                                ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL);
+		hba[i]->cmd_pool = (CommandList_struct *)kmalloc(
+                                NR_CMDS * sizeof(CommandList_struct), 
+					GFP_KERNEL);
+		hba[i]->errinfo_pool = (ErrorInfo_struct *)kmalloc(
+                                NR_CMDS * sizeof( ErrorInfo_struct), 
+					GFP_KERNEL);
+		if((hba[i]->cmd_pool_bits == NULL) 
+			|| (hba[i]->cmd_pool == NULL)
+			|| (hba[i]->errinfo_pool == NULL))
+                {
+                        nr_ctlr = i;
+			if(hba[i]->cmd_pool_bits)
+                                kfree(hba[i]->cmd_pool_bits);
+                        if(hba[i]->cmd_pool)
+                                kfree(hba[i]->cmd_pool);
+			if(hba[i]->errinfo_pool)
+				kfree(hba[i]->errinfo_pool);
+                        free_irq(hba[i]->intr, hba[i]);
+                        unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+                        num_cntlrs_reg--;
+                        printk( KERN_ERR "cciss: out of memory");
+			return(num_cntlrs_reg);
+		}
+
+		/* command and error info recs zeroed out before 
+			they are used */
+                memset(hba[i]->cmd_pool_bits, 0, 
+			((NR_CMDS+31)/32)*sizeof(__u32));
+
+#ifdef CCISS_DEBUG	
+		printk(KERN_DEBUG "Scanning for drives on controller cciss%d\n",i);
+#endif /* CCISS_DEBUG */
+
+		cciss_getgeometry(i);
+
+		/* Turn the interrupts on so we can service requests */
+		hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
+
+		cciss_procinit(i);
+		/* Fill in the gendisk data */ 	
+		hba[i]->gendisk.major = MAJOR_NR + i;
+		hba[i]->gendisk.major_name = "cciss";
+		hba[i]->gendisk.minor_shift = NWD_SHIFT;
+		hba[i]->gendisk.max_p = MAX_PART;
+		hba[i]->gendisk.max_nr = NWD;
+		hba[i]->gendisk.init = cciss_geninit;
+		hba[i]->gendisk.part = hba[i]->hd;
+		hba[i]->gendisk.sizes = hba[i]->sizes;
+		hba[i]->gendisk.nr_real = hba[i]->num_luns;
+
+		/* fill in the other Kernel structs */ 
+		blk_dev[MAJOR_NR+i].request_fn = request_fns[i];
+		blksize_size[MAJOR_NR+i] = hba[i]->blocksizes;
+		hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes;
+		read_ahead[MAJOR_NR+i] = READ_AHEAD;
+
+		/* Get on the disk list */ 
+		hba[i]->gendisk.next = gendisk_head;
+		gendisk_head = &(hba[i]->gendisk); 
+	}
+	return(nr_ctlr);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+
+/* This is a bit of a hack... */
+int init_module(void)
+{
+	int i,j;
+
+	if (cciss_init() == 0) /* all the block dev numbers already used */
+		return -EIO;	  /* or no controllers were found */
+	for(i=0; i<nr_ctlr; i++)
+	{
+		cciss_geninit(&(hba[i]->gendisk));
+		for(j=0; j<NWD; j++)
+		{
+			if(hba[i]->sizes[j<<NWD_SHIFT])
+				resetup_one_dev(&(hba[i]->gendisk), j);
+		}
+	}
+	return 0;
+}
+void cleanup_module(void)
+{
+	int i;
+	struct gendisk *g;
+
+	for(i=0; i<nr_ctlr; i++)
+	{
+		/* Turn board interrupts off */ 
+		hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
+		free_irq(hba[i]->intr, hba[i]);
+		iounmap((void*)hba[i]->vaddr);
+		unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+		remove_proc_entry(hba[i]->devname, proc_cciss);	
+
+		/* remove it from the disk list */ 
+		if (gendisk_head == &(hba[i]->gendisk))
+		{
+			gendisk_head = hba[i]->gendisk.next;
+		} else 
+		{
+			for(g=gendisk_head; g ; g=g->next)
+			{
+				if(g->next == &(hba[i]->gendisk))
+				{
+					g->next = hba[i]->gendisk.next;
+				}
+			}
+		}
+		remove_proc_entry("cciss", &proc_root);
+		kfree(hba[i]->cmd_pool);
+		kfree(hba[i]->errinfo_pool);
+                kfree(hba[i]->cmd_pool_bits);
+		kfree(hba[i]);
+	}
+}
+
+#endif /* MODULE */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss.h linux.18p17/drivers/block/cciss.h
--- linux.vanilla/drivers/block/cciss.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/cciss.h	Sun Sep  3 00:10:45 2000
@@ -0,0 +1,201 @@
+#ifndef CCISS_H
+#define CCISS_H
+
+#include <linux/genhd.h>
+
+#include "cciss_cmd.h"
+
+
+#define NWD		16
+#define NWD_SHIFT	4
+#define MAX_PART	16
+
+#define IO_OK		0
+#define IO_ERROR	1
+
+#define MAJOR_NR COMPAQ_CISS_MAJOR 
+
+struct ctlr_info;
+typedef struct ctlr_info ctlr_info_t;
+
+struct access_method {
+	void (*submit_command)(ctlr_info_t *h, CommandList_struct *c);
+	void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
+	unsigned long (*fifo_full)(ctlr_info_t *h);
+	unsigned long (*intr_pending)(ctlr_info_t *h);
+	unsigned long (*command_completed)(ctlr_info_t *h);
+};
+typedef struct _drive_info_struct
+{
+ 	__u32   LunID;	
+	int 	usage_count;
+	int 	nr_blocks;
+	int	block_size;
+	int 	heads;
+	int	sectors;
+	int 	cylinders;
+} drive_info_struct;
+
+struct ctlr_info 
+{
+	int	ctlr;
+	char	devname[8];
+	char    *product_name;
+	char	firm_ver[4]; // Firmware version 
+	unchar  pci_bus;
+        unchar  pci_dev_fn;
+	__u32	board_id;
+	__u32	vaddr;
+	__u32	paddr;	
+	CfgTable_struct *cfgtable;
+	int	intr;
+
+	int 	max_commands;
+	int	commands_outstanding;
+	int 	max_outstanding; /* Debug */ 
+	int	num_luns;
+	int	usage_count;  /* number of opens all all minor devices */
+
+	// information about each logical volume
+	drive_info_struct drv[CISS_MAX_LUN];
+
+	struct access_method access;
+
+	/* queue and queue Info */ 
+	CommandList_struct *reqQ;
+	CommandList_struct  *cmpQ;
+	unsigned int Qdepth;
+	unsigned int maxQsinceinit;
+	unsigned int maxSG;
+
+	//* pointers to command and error info pool */ 
+	CommandList_struct 	*cmd_pool;
+	ErrorInfo_struct 	*errinfo_pool;
+        __u32   		*cmd_pool_bits;
+	int			nr_allocs;
+	int			nr_frees; 
+
+	// Disk structures we need to pass back
+	struct gendisk   gendisk;
+	   // indexed by minor numbers
+	struct hd_struct hd[256];
+	int              sizes[256];
+	int              blocksizes[256];
+	int              hardsizes[256];
+};
+
+/*  Defining the diffent access_menthods */
+/*
+ * Memory mapped FIFO interface (SMART 53xx cards)
+ */
+#define SA5_DOORBELL	0x20
+#define SA5_REQUEST_PORT_OFFSET	0x40
+#define SA5_REPLY_INTR_MASK_OFFSET	0x34
+#define SA5_REPLY_PORT_OFFSET		0x44
+#define SA5_INTR_STATUS		0x30
+
+#define SA5_INTR_OFF		0x08
+#define SA5_INTR_PENDING	0x08
+#define FIFO_EMPTY		0xffffffff	
+
+#define  CISS_ERROR_BIT		0x02
+
+#define CCISS_INTR_ON 	1 
+#define CCISS_INTR_OFF	0
+/* 
+	Send the command to the hardware 
+*/
+static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) 
+{
+#ifdef CCISS_DEBUG
+	 printk("Sending %x - down to controller\n", c->busaddr );
+#endif /* CCISS_DEBUG */ 
+         writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
+	 h->commands_outstanding++;
+	 if ( h->commands_outstanding > h->max_outstanding)
+		h->max_outstanding = h->commands_outstanding;
+}
+
+/*  
+ *  This card is the oposite of the other cards.  
+ *   0 turns interrupts on... 
+ *   0x08 turns them off... 
+ */
+static void SA5_intr_mask(ctlr_info_t *h, unsigned long val)
+{
+	if (val) 
+	{ /* Turn interrupts on */
+		writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+	} else /* Turn them off */
+	{
+        	writel( SA5_INTR_OFF, 
+			h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+	}
+}
+/*
+ *  Returns true if fifo is full.  
+ * 
+ */ 
+static unsigned long SA5_fifo_full(ctlr_info_t *h)
+{
+	if( h->commands_outstanding >= h->max_commands)
+		return(1);
+	else 
+		return(0);
+
+}
+/* 
+ *   returns value read from hardware. 
+ *     returns FIFO_EMPTY if there is nothing to read 
+ */ 
+static unsigned long SA5_completed(ctlr_info_t *h)
+{
+	unsigned long register_value 
+		= readl(h->vaddr + SA5_REPLY_PORT_OFFSET);
+	if(register_value != FIFO_EMPTY)
+	{
+		h->commands_outstanding--;
+#ifdef CCISS_DEBUG
+		printk("cciss:  Read %lx back from board\n", register_value);
+#endif /* CCISS_DEBUG */ 
+	} 
+#ifdef CCISS_DEBUG
+	else
+	{
+		printk("cciss:  FIFO Empty read\n");
+	}
+#endif 
+	return ( register_value); 
+
+}
+/*
+ *	Returns true if an interrupt is pending.. 
+ */
+static unsigned long SA5_intr_pending(ctlr_info_t *h)
+{
+	unsigned long register_value  = 
+		readl(h->vaddr + SA5_INTR_STATUS);
+#ifdef CCISS_DEBUG
+	printk("cciss: intr_pending %lx\n", register_value);
+#endif  /* CCISS_DEBUG */
+	if( register_value &  SA5_INTR_PENDING) 
+		return  1;	
+	return 0 ;
+}
+
+
+static struct access_method SA5_access = {
+	SA5_submit_command,
+	SA5_intr_mask,
+	SA5_fifo_full,
+	SA5_intr_pending,
+	SA5_completed,
+};
+
+struct board_type {
+	__u32	board_id;
+	char	*product_name;
+	struct access_method *access;
+};
+#endif /* CCISS_H */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss_cmd.h linux.18p17/drivers/block/cciss_cmd.h
--- linux.vanilla/drivers/block/cciss_cmd.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/cciss_cmd.h	Fri Sep  1 13:32:19 2000
@@ -0,0 +1,254 @@
+#ifndef CCISS_CMD_H
+#define CCISS_CMD_H
+//###########################################################################
+//DEFINES
+//###########################################################################
+#define CISS_VERSION "1.00"
+
+//general boundary defintions
+#define SENSEINFOBYTES          32//note that this value may vary between host implementations
+#define MAXSGENTRIES            31
+#define MAXREPLYQS              256
+
+//Command Status value
+#define CMD_SUCCESS             0x0000
+#define CMD_TARGET_STATUS       0x0001
+#define CMD_DATA_UNDERRUN       0x0002
+#define CMD_DATA_OVERRUN        0x0003
+#define CMD_INVALID             0x0004
+#define CMD_PROTOCOL_ERR        0x0005
+#define CMD_HARDWARE_ERR        0x0006
+#define CMD_CONNECTION_LOST     0x0007
+#define CMD_ABORTED             0x0008
+#define CMD_ABORT_FAILED        0x0009
+#define CMD_UNSOLICITED_ABORT   0x000A
+#define CMD_TIMEOUT             0x000B
+#define CMD_UNABORTABLE		0x000C
+
+//transfer direction
+#define XFER_NONE               0x00
+#define XFER_WRITE              0x01
+#define XFER_READ               0x02
+#define XFER_RSVD               0x03
+
+//task attribute
+#define ATTR_UNTAGGED           0x00
+#define ATTR_SIMPLE             0x04
+#define ATTR_HEADOFQUEUE        0x05
+#define ATTR_ORDERED            0x06
+#define ATTR_ACA                0x07
+
+//cdb type
+#define TYPE_CMD				0x00
+#define TYPE_MSG				0x01
+
+//config space register offsets
+#define CFG_VENDORID            0x00
+#define CFG_DEVICEID            0x02
+#define CFG_I2OBAR              0x10
+#define CFG_MEM1BAR             0x14
+
+//i2o space register offsets
+#define I2O_IBDB_SET            0x20
+#define I2O_IBDB_CLEAR          0x70
+#define I2O_INT_STATUS          0x30
+#define I2O_INT_MASK            0x34
+#define I2O_IBPOST_Q            0x40
+#define I2O_OBPOST_Q            0x44
+
+//Configuration Table
+#define CFGTBL_ChangeReq        0x00000001l
+#define CFGTBL_AccCmds          0x00000001l
+
+#define CFGTBL_Trans_Simple     0x00000002l
+
+#define CFGTBL_BusType_Ultra2   0x00000001l
+#define CFGTBL_BusType_Ultra3   0x00000002l
+#define CFGTBL_BusType_Fibre1G  0x00000100l
+#define CFGTBL_BusType_Fibre2G  0x00000200l
+typedef struct _vals32
+{
+        __u32   lower;
+        __u32   upper;
+} vals32;
+
+typedef union _u64bit
+{
+   vals32	val32;
+   __u64	val;
+} u64bit;
+
+// Type defs used in the following structs
+#define BYTE __u8
+#define WORD __u16
+#define HWORD __u16
+#define DWORD __u32
+#define QWORD vals32 
+
+//###########################################################################
+//STRUCTURES
+//###########################################################################
+#define CISS_MAX_LUN	16	
+// SCSI-3 Cmmands 
+
+#pragma pack(1)	
+
+#define CISS_INQUIRY 0x12
+//Date returned
+typedef struct _InquiryData_struct
+{
+  BYTE data_byte[36];
+} InquiryData_struct;
+
+#define CISS_REPORT_LOG 0xc2    /* Report Logical LUNs */
+// Data returned
+typedef struct _ReportLUNdata_struct
+{
+  BYTE LUNListLength[4];
+  DWORD reserved;
+  BYTE LUN[CISS_MAX_LUN][8];
+} ReportLunData_struct;
+
+#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */ 
+typedef struct _ReadCapdata_struct
+{
+  BYTE total_size[4];	// Total size in blocks
+  BYTE block_size[4];	// Size of blocks in bytes
+} ReadCapdata_struct;
+
+// 12 byte commands not implemented in firmware yet. 
+// #define CCISS_READ 	0xa8	// Read(12)
+// #define CCISS_WRITE	0xaa	// Write(12)
+ #define CCISS_READ   0x28    // Read(10)
+ #define CCISS_WRITE  0x2a    // Write(10)
+
+//Command List Structure
+typedef union _SCSI3Addr_struct {
+   struct {
+    BYTE Bus:6;
+    BYTE Mode:2;        // b00
+    BYTE Dev;
+  } PeripDev;
+   struct {
+    BYTE DevMSB:6;
+    BYTE Mode:2;        // b01
+    BYTE DevLSB;
+  } LogDev;
+   struct {
+    BYTE Targ:6;
+    BYTE Mode:2;        // b10
+    BYTE Dev:5;
+    BYTE Bus:3;
+  } LogUnit;
+} SCSI3Addr_struct;
+
+typedef struct _PhysDevAddr_struct {
+  DWORD             TargetId:24;
+  DWORD             Bus:6;
+  DWORD             Mode:2;
+  SCSI3Addr_struct  Target[2]; //2 level target device addr
+} PhysDevAddr_struct;
+  
+typedef struct _LogDevAddr_struct {
+  DWORD            VolId:30;
+  DWORD            Mode:2;
+  BYTE             reserved[4];
+} LogDevAddr_struct;
+
+typedef union _LUNAddr_struct {
+  BYTE               LunAddrBytes[8];
+  SCSI3Addr_struct   SCSI3Lun[4];
+  PhysDevAddr_struct PhysDev;
+  LogDevAddr_struct  LogDev;
+} LUNAddr_struct;
+
+typedef struct _CommandListHeader_struct {
+  BYTE              ReplyQueue;
+  BYTE              SGList;
+  HWORD             SGTotal;
+  QWORD             Tag;
+  LUNAddr_struct    LUN;
+} CommandListHeader_struct;
+typedef struct _RequestBlock_struct {
+  BYTE   CDBLen;
+  struct {
+    BYTE Type:3;
+    BYTE Attribute:3;
+    BYTE Direction:2;
+  } Type;
+  HWORD  Timeout;
+  BYTE   CDB[16];
+} RequestBlock_struct;
+typedef struct _ErrDescriptor_struct {
+  QWORD  Addr;
+  DWORD  Len;
+} ErrDescriptor_struct;
+typedef struct _SGDescriptor_struct {
+  QWORD  Addr;
+  DWORD  Len;
+  DWORD  Ext;
+} SGDescriptor_struct;
+
+typedef union _MoreErrInfo_struct{
+  struct {
+    BYTE  Reserved[3];
+    BYTE  Type;
+    DWORD ErrorInfo;
+  }Common_Info;
+  struct{
+    BYTE  Reserved[2];
+    BYTE  offense_size;//size of offending entry
+    BYTE  offense_num; //byte # of offense 0-base
+    DWORD offense_value;
+  }Invalid_Cmd;
+}MoreErrInfo_struct;
+typedef struct _ErrorInfo_struct {
+  BYTE               ScsiStatus;
+  BYTE               SenseLen;
+  HWORD              CommandStatus;
+  DWORD              ResidualCnt;
+  MoreErrInfo_struct MoreErrInfo;
+  BYTE               SenseInfo[SENSEINFOBYTES];
+} ErrorInfo_struct;
+
+/* Command types */
+#define CMD_RWREQ       0x00
+#define CMD_IOCTL_PEND  0x01
+#define CMD_IOCTL_DONE  0x02
+
+typedef struct _CommandList_struct {
+  CommandListHeader_struct Header;
+  RequestBlock_struct      Request;
+  ErrDescriptor_struct     ErrDesc;
+  SGDescriptor_struct      SG[MAXSGENTRIES];
+	/* information associated with the command */ 
+  __u32			   busaddr; /* physical addres of this record */
+  ErrorInfo_struct * 	   err_info; /* pointer to the allocated mem */ 
+  int			   cmd_type; 
+  struct _CommandList_struct *prev;
+  struct _CommandList_struct *next;
+  struct buffer_head *	   bh;
+} CommandList_struct;
+
+//Configuration Table Structure
+typedef struct _HostWrite_struct {
+  DWORD TransportRequest;
+  DWORD Reserved;
+  DWORD CoalIntDelay;
+  DWORD CoalIntCount;
+} HostWrite_struct;
+
+typedef struct _CfgTable_struct {
+  BYTE             Signature[4];
+  DWORD            SpecValence;
+  DWORD            TransportSupport;
+  DWORD            TransportActive;
+  HostWrite_struct HostWrite;
+  DWORD            CmdsOutMax;
+  DWORD            BusTypes;
+  DWORD            Reserved; 
+  BYTE             ServerName[16];
+  DWORD            HeartBeat;
+} CfgTable_struct;
+#pragma pack()	 
+#endif // CCISS_CMD_H
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cpqarray.c linux.18p17/drivers/block/cpqarray.c
--- linux.vanilla/drivers/block/cpqarray.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/cpqarray.c	Wed Sep 13 16:20:43 2000
@@ -41,8 +41,14 @@
 
 #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 
-#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.9)"
-#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,9)
+#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.10)"
+#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,10)
+
+/* Embedded module documentation macros - see modules.h */
+/* Original author Chris Frantz - Compaq Computer Corporation */
+MODULE_AUTHOR("Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq Smart2 Array Controllers");
+
 #define MAJOR_NR COMPAQ_SMART2_MAJOR
 #include <linux/blk.h>
 #include <linux/blkdev.h>
@@ -617,6 +623,8 @@
 
 	int i;
 
+	c->pci_bus = bus;
+	c->pci_dev_fn = device_fn;
 	pdev = pci_find_slot(bus, device_fn);
 	vendor_id = pdev->vendor;
 	device_id = pdev->device;
@@ -663,6 +671,7 @@
 	c->vaddr = remap_pci_mem(c->paddr, 128);
 	c->board_id = board_id;
 
+	
 	for(i=0; i<NR_PRODUCTS; i++) {
 		if (board_id == products[i].board_id) {
 			c->product_name = products[i].product_name;
@@ -752,6 +761,8 @@
 		hba[nr_ctlr]->access = *(products[j].access);
 		hba[nr_ctlr]->ctlr = nr_ctlr;
 		hba[nr_ctlr]->board_id = board_id;
+		hba[nr_ctlr]->pci_bus = 0;  /* not PCI */
+		hba[nr_ctlr]->pci_dev_fn = 0; /* not PCI */
 
 DBGINFO(
 	printk("i = %d, j = %d\n", i, j);
@@ -1174,7 +1185,20 @@
 		if (!arg) return -EINVAL;
 		put_user(DRIVER_VERSION, (unsigned long*)arg);
 		return 0;
+	case IDAGETPCIINFO:
+	{
+		
+		ida_pci_info_struct pciinfo;
 
+		if (!arg) return -EINVAL;
+		pciinfo.bus = hba[ctlr]->pci_bus;
+		pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+		pciinfo.board_id = hba[ctlr]->board_id;
+		copy_to_user_ret((void *) arg, &pciinfo,  
+			sizeof( ida_pci_info_struct),  -EFAULT);
+		return(0);
+	}	
+	
 	RO_IOCTLS(inode->i_rdev, arg);
 
 	default:
@@ -1358,6 +1382,8 @@
 	ctlr_info_t *info_p = hba[ctlr];
 
 	c = cmd_alloc(info_p);
+	if (!c)
+		return IO_ERROR;
 	c->ctlr = ctlr;
 	c->hdr.unit = log_unit;
 	c->hdr.prio = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cpqarray.h linux.18p17/drivers/block/cpqarray.h
--- linux.vanilla/drivers/block/cpqarray.h	Fri Apr 21 23:13:37 2000
+++ linux.18p17/drivers/block/cpqarray.h	Thu Oct 19 01:40:16 2000
@@ -87,6 +87,8 @@
 	int	log_drives;
 	int	phys_drives;
 
+	unsigned char  pci_bus;     /* 0 if EISA */
+        unsigned char  pci_dev_fn;  /* 0 if EISA */
 	__u32	board_id;
 	char	*product_name;	
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/falconide.c linux.18p17/drivers/block/falconide.c
--- linux.vanilla/drivers/block/falconide.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/falconide.c	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/floppy.c linux.18p17/drivers/block/floppy.c
--- linux.vanilla/drivers/block/floppy.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/floppy.c	Sat Sep 23 13:24:12 2000
@@ -2257,6 +2257,7 @@
 static void request_done(int uptodate)
 {
 	int block;
+	unsigned long flags;
 
 	probing = 0;
 	reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
@@ -2275,6 +2276,7 @@
 			DRS->maxtrack = 1;
 
 		/* unlock chained buffers */
+		spin_lock_irqsave(&io_request_lock, flags);
 		while (current_count_sectors && CURRENT &&
 		       current_count_sectors >= CURRENT->current_nr_sectors){
 			current_count_sectors -= CURRENT->current_nr_sectors;
@@ -2282,6 +2284,7 @@
 			CURRENT->sector += CURRENT->current_nr_sectors;
 			end_request(1);
 		}
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		if (current_count_sectors && CURRENT){
 			/* "unlock" last subsector */
 			CURRENT->buffer += current_count_sectors <<9;
@@ -2305,7 +2308,9 @@
 			DRWE->last_error_sector = CURRENT->sector;
 			DRWE->last_error_generation = DRS->generation;
 		}
+		spin_lock_irqsave(&io_request_lock, flags);
 		end_request(0);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	}
 }
 
@@ -2313,6 +2318,13 @@
 static void rw_interrupt(void)
 {
 	int nr_sectors, ssize, eoc, heads;
+
+	if (R_HEAD >= 2) {
+	    /* some Toshiba floppy controllers occasionnally seem to
+	     * return bogus interrupts after read/write operations, which
+	     * can be recognized by a bad head number (>= 2) */
+	     return;
+	}  
 
 	if (!DRS->first_read_date)
 		DRS->first_read_date = jiffies;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/gayle.c linux.18p17/drivers/block/gayle.c
--- linux.vanilla/drivers/block/gayle.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/gayle.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/genhd.c linux.18p17/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/genhd.c	Sat Sep 16 23:12:17 2000
@@ -131,6 +131,10 @@
 			return buf;
 		}
 	}
+	if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <=
+            COMPAQ_CISS_MAJOR+7) {
+          return raid_name (hd, minor, COMPAQ_CISS_MAJOR, buf);
+        }
 	if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <=
             COMPAQ_SMART2_MAJOR+7) {
           return raid_name (hd, minor, COMPAQ_SMART2_MAJOR, buf);
@@ -1512,7 +1516,7 @@
 	char buf[MAX_DISKNAME_LEN];
 
 	if (first_time)
-		printk("Partition check:\n");
+		printk(KERN_INFO "Partition check:\n");
 	first_time = 0;
 	first_sector = hd->part[MINOR(dev)].start_sect;
 
@@ -1525,7 +1529,7 @@
 		return;
 	}
 
-	printk(" %s:", disk_name(hd, MINOR(dev), buf));
+	printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
 #ifdef CONFIG_MSDOS_PARTITION
 	if (msdos_partition(hd, dev, first_sector))
 		return;
@@ -1622,6 +1626,9 @@
 {
 	extern void console_map_init(void);
 	extern void cpqarray_init(void);
+#ifdef CONFIG_BLK_CPQ_CISS_DA
+	extern int  cciss_init(void);
+#endif 
 #ifdef CONFIG_PARPORT
 	extern int parport_init(void);
 #endif
@@ -1654,6 +1661,9 @@
 #endif
 #ifdef CONFIG_BLK_CPQ_DA
 	cpqarray_init();
+#endif
+#ifdef CONFIG_BLK_CPQ_CISS_DA
+        cciss_init();
 #endif
 #ifdef CONFIG_NET
 	net_dev_init();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ida_ioctl.h linux.18p17/drivers/block/ida_ioctl.h
--- linux.vanilla/drivers/block/ida_ioctl.h	Fri Apr 21 23:13:37 2000
+++ linux.18p17/drivers/block/ida_ioctl.h	Thu Oct 19 01:40:16 2000
@@ -33,7 +33,14 @@
 #define IDAGETCTLRSIG		0x29293030
 #define IDAREVALIDATEVOLS	0x30303131
 #define IDADRIVERVERSION	0x31313232
+#define IDAGETPCIINFO		0x32323333
 
+typedef struct _ida_pci_info_struct
+{
+	unsigned char 	bus;
+	unsigned char 	dev_fn;
+	__u32 		board_id;
+} ida_pci_info_struct;
 /*
  * Normally, the ioctl determines the logical unit for this command by
  * the major,minor number of the fd passed to ioctl.  If you need to send
@@ -60,7 +67,7 @@
 
 	union ctlr_cmds {
 		drv_info_t		drv;
-		unsigned char		buf[512];
+		unsigned char		buf[1024];
 
 		id_ctlr_t		id_ctlr;
 		drv_param_t		drv_param;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ide-cd.c linux.18p17/drivers/block/ide-cd.c
--- linux.vanilla/drivers/block/ide-cd.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/ide-cd.c	Mon Oct  2 10:21:52 2000
@@ -210,17 +210,17 @@
  *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
  *
  * 4.50  Oct 19, 1998  -- New maintainers!
- *                         Jens Axboe <axboe@image.dk>
+ *                         Jens Axboe <axboe@suse.de>
  *                         Chris Zwilling <chris@cloudnet.com>
  *
- * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
+ * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@suse.de>
  *                      - ide_cdrom_reset enabled since the ide subsystem
- *                         handles resets fine now. <axboe@image.dk>
+ *                         handles resets fine now. <axboe@suse.de>
  *                      - Transfer size fix for Samsung CD-ROMs, thanks to
  *                        "Ville Hallik" <ville.hallik@mail.ee>.
  *                      - other minor stuff.
  *
- * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
+ * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@suse.de>
  *                      - Detect DVD-ROM/RAM drives
  *
  * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
@@ -1827,6 +1827,20 @@
 	return cdrom_queue_packet_command(drive, &pc);
 }
 
+static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end)
+{
+	struct request_sense sense;
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof (pc));
+	pc.sense = &sense;
+
+	pc.c[0] = GPCMD_PLAY_AUDIO_MSF;
+	lba_to_msf(lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+	lba_to_msf(lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+
+	return cdrom_queue_packet_command(drive, &pc);
+}
 
 static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
 				struct atapi_toc_entry **ent)
@@ -1930,10 +1944,38 @@
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct cdrom_info *info = drive->driver_data;
+	int stat;
 
 	switch (cmd) {
+	/*
+	 * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_MSF, since
+	 * atapi doesn't support it
+	 */
+	case CDROMPLAYTRKIND: {
+		int lba_start, lba_end;
+		struct cdrom_ti *ti = (struct cdrom_ti *)arg;
+		struct atapi_toc_entry *first_toc, *last_toc;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk0, &first_toc);
+		if (stat)
+			return stat;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc);
+		if (stat)
+			return stat;
+
+		if (ti->cdti_trk1 != CDROM_LEADOUT)
+			++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
+
+		if (lba_end <= lba_start)
+			return -EINVAL;
+
+		return cdrom_play_audio(drive, lba_start, lba_end);
+	}
+
 	case CDROMREADTOCHDR: {
-		int stat;
 		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
 		struct atapi_toc *toc;
 
@@ -1949,7 +1991,6 @@
 	}
 
 	case CDROMREADTOCENTRY: {
-		int stat;
 		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
 		struct atapi_toc_entry *toce;
 
@@ -2275,7 +2316,7 @@
 		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
 	if (cap.audio_play)
 		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
-	if (cap.mechtype == 0)
+	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
 		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
 
 #if ! STANDARD_ATAPI
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ide-dma.c linux.18p17/drivers/block/ide-dma.c
--- linux.vanilla/drivers/block/ide-dma.c	Sun Jun 11 21:44:12 2000
+++ linux.18p17/drivers/block/ide-dma.c	Mon Sep 11 18:06:33 2000
@@ -433,6 +433,12 @@
 			request_region(dma_base+16, extra, name);
 		dma_base += hwif->channel ? 8 : 0;
 		hwif->dma_extra = extra;
+		
+		/* ====== ALI M5229 ================================*/
+		if( hwif->pci_dev->device == PCI_DEVICE_ID_AL_M5229)
+			outb(inb(dma_base+2) & 0x60, dma_base+2);           
+		/* =================================================*/
+		
 		if (inb(dma_base+2) & 0x80) {
 			printk("%s: simplex device:  DMA disabled\n", name);
 			dma_base = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ide-pci.c linux.18p17/drivers/block/ide-pci.c
--- linux.vanilla/drivers/block/ide-pci.c	Sun Jun 11 21:44:12 2000
+++ linux.18p17/drivers/block/ide-pci.c	Mon Sep 11 23:03:25 2000
@@ -30,6 +30,7 @@
 #define DEVID_PIIX3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
 #define DEVID_PIIX4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
 #define DEVID_VP_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
+#define DEVID_VP_OLDIDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_0})
 #define DEVID_PDC20246	((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246})
 #define DEVID_RZ1000	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1000})
 #define DEVID_RZ1001	((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH,  PCI_DEVICE_ID_PCTECH_RZ1001})
@@ -39,6 +40,7 @@
 #define DEVID_OPTI621	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C621})
 #define DEVID_OPTI621V	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    PCI_DEVICE_ID_OPTI_82C558})
 #define DEVID_OPTI621X	((ide_pci_devid_t){PCI_VENDOR_ID_OPTI,    0xd568})  /* from datasheets */
+#define DEVID_ALI15X3   ((ide_pci_devid_t){PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M5229})
 #define DEVID_TRM290	((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM,  PCI_DEVICE_ID_TEKRAM_DC290})
 #define DEVID_NS87410	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87410})
 #define DEVID_NS87415	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87415})
@@ -52,6 +54,13 @@
 
 #define IDE_IGNORE	((void *)-1)
 
+#ifdef CONFIG_BLK_DEV_ALI15X3
+extern void ide_init_ali15x3(ide_hwif_t *);
+#define INIT_ALI15X3  &ide_init_ali15x3
+#else
+#define INIT_ALI15X3  NULL
+#endif
+
 #ifdef CONFIG_BLK_DEV_TRM290
 extern void ide_init_trm290(ide_hwif_t *);
 #define INIT_TRM290	&ide_init_trm290
@@ -145,6 +154,7 @@
 	{DEVID_HT6565,	"HT6565",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_OPTI621,	"OPTI621",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_OPTI621X,"OPTI621X",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	ON_BOARD,	0 },
+	{DEVID_ALI15X3, "ALI15X3",  INIT_ALI15X3,   {{0x00,0x00,0x00}, {0x00,0x00,0x00}},   ON_BOARD,   0 },
 	{DEVID_TRM290,	"TRM290",	INIT_TRM290,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_NS87415,	"NS87415",	INIT_NS87415,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_AEC6210,	"AEC6210",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	OFF_BOARD,	0 },
@@ -451,6 +461,19 @@
 		devid.vid = dev->vendor;
 		devid.did = dev->device;
 		if (IDE_PCI_DEVID_EQ(devid, DEVID_450NX)) {
+			autodma_default = 0;
+			break;
+		}
+		/*
+		 *	Don't try and tune a VIA 82C586 or 586A
+		 */
+		if (IDE_PCI_DEVID_EQ(devid, DEVID_VP_IDE))
+		{
+			autodma_default = 0;
+			break;
+		}
+		if (IDE_PCI_DEVID_EQ(devid, DEVID_VP_OLDIDE))
+		{
 			autodma_default = 0;
 			break;
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ide.c linux.18p17/drivers/block/ide.c
--- linux.vanilla/drivers/block/ide.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/ide.c	Sat Oct 14 01:00:34 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ll_rw_blk.c linux.18p17/drivers/block/ll_rw_blk.c
--- linux.vanilla/drivers/block/ll_rw_blk.c	Sun Jun 11 21:44:12 2000
+++ linux.18p17/drivers/block/ll_rw_blk.c	Thu Oct 19 01:19:35 2000
@@ -312,7 +312,7 @@
 	output.queue_ID			= elevator->queue_ID;
 	output.read_latency		= elevator->read_latency;
 	output.write_latency		= elevator->write_latency;
-	output.max_bomb_segments	= elevator->max_bomb_segments;
+	output.max_bomb_segments	= 0;
 
 	ret = -EFAULT;
 	if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
@@ -336,12 +336,9 @@
 		goto out;
 	if (input.write_latency < 0)
 		goto out;
-	if (input.max_bomb_segments <= 0)
-		goto out;
 
 	elevator->read_latency		= input.read_latency;
 	elevator->write_latency		= input.write_latency;
-	elevator->max_bomb_segments	= input.max_bomb_segments;
 
 	ret = 0;
  out:
@@ -362,26 +359,15 @@
 	return -EINVAL;
 }
 
-static inline int seek_to_not_starving_chunk(struct request ** req, int * lat)
+static inline struct request * seek_to_not_starving_chunk(struct request * req)
 {
-	struct request * tmp = *req;
-	int found = 0, pos = 0;
-	int last_pos = 0, __lat = *lat;
+	struct request * tmp = req;
 
-	do {
-		if (tmp->elevator_latency <= 0)
-		{
-			*req = tmp;
-			found = 1;
-			last_pos = pos;
-			if (last_pos >= __lat)
-				break;
-		}
-		pos += tmp->nr_segments;
-	} while ((tmp = tmp->next));
-	*lat -= last_pos;
+	while ((tmp = tmp->next))
+		if ((tmp->cmd != READ && tmp->cmd != WRITE) || !tmp->elevator_latency)
+			req = tmp;
 
-	return found;
+	return req;
 }
 
 #define CASE_COALESCE_BUT_FIRST_REQUEST_MAYBE_BUSY	\
@@ -421,56 +407,31 @@
 	     case COMPAQ_SMART2_MAJOR+4:	\
 	     case COMPAQ_SMART2_MAJOR+5:	\
 	     case COMPAQ_SMART2_MAJOR+6:	\
-	     case COMPAQ_SMART2_MAJOR+7:
+	     case COMPAQ_SMART2_MAJOR+7:	\
+	     case COMPAQ_CISS_MAJOR+0:        \
+             case COMPAQ_CISS_MAJOR+1:        \
+             case COMPAQ_CISS_MAJOR+2:        \
+             case COMPAQ_CISS_MAJOR+3:        \
+             case COMPAQ_CISS_MAJOR+4:        \
+             case COMPAQ_CISS_MAJOR+5:        \
+             case COMPAQ_CISS_MAJOR+6:        \
+             case COMPAQ_CISS_MAJOR+7:
 
 #define elevator_starve_rest_of_queue(req)			\
 do {								\
 	struct request * tmp = (req);				\
-	for ((tmp) = (tmp)->next; (tmp); (tmp) = (tmp)->next)	\
-		(tmp)->elevator_latency--;			\
+	for ((tmp) = (tmp)->next; (tmp); (tmp) = (tmp)->next) {	\
+		if ((tmp)->cmd != READ && (tmp)->cmd != WRITE)	\
+			continue; 				\
+		if (--(tmp)->elevator_latency < 0)		\
+			panic("elevator_starve_rest_of_queue");	\
+	}							\
 } while (0)
 
 static inline void elevator_queue(struct request * req,
-				  struct request * tmp,
-				  int latency,
-				  struct blk_dev_struct * dev,
-				  struct request ** queue_head)
+				  struct request * tmp)
 {
-	struct request * __tmp;
-	int starving, __latency;
-
-	starving = seek_to_not_starving_chunk(&tmp, &latency);
-	__tmp = tmp;
-	__latency = latency;
-
-	for (;; tmp = tmp->next)
-	{
-		if ((latency -= tmp->nr_segments) <= 0)
-		{
-			tmp = __tmp;
-			latency = __latency - tmp->nr_segments;
-
-			if (starving)
-				break;
-
-			switch (MAJOR(req->rq_dev))
-			{
-			CASE_COALESCE_BUT_FIRST_REQUEST_MAYBE_BUSY
-				if (tmp == dev->current_request)
-			default:
-					goto link;
-			CASE_COALESCE_ALSO_FIRST_REQUEST
-			}
-
-			latency += tmp->nr_segments;
-			req->next = tmp;
-			*queue_head = req;
-			goto after_link;
-		}
-
-		if (!tmp->next)
-			break;
-
+	for (tmp = seek_to_not_starving_chunk(tmp); tmp->next; tmp = tmp->next) {
 		{
 			const int after_current = IN_ORDER(tmp,req);
 			const int before_next = IN_ORDER(req,tmp->next);
@@ -485,13 +446,9 @@
 		}
 	}
 
- link:
 	req->next = tmp->next;
 	tmp->next = req;
 
- after_link:
-	req->elevator_latency = latency;
-
 	elevator_starve_rest_of_queue(req);
 }
 
@@ -513,7 +470,6 @@
 	short		 disk_index;
 	unsigned long flags;
 	int queue_new_request = 0;
-	int latency;
 
 	switch (major) {
 		case DAC960_MAJOR+0:
@@ -544,7 +500,7 @@
 	if (disk_index >= 0 && disk_index < 4)
 		drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
 
-	latency = get_request_latency(&dev->elevator, req->cmd);
+	req->elevator_latency = get_request_latency(&dev->elevator, req->cmd);
 
 	/*
 	 * We use the goto to reduce locking complexity
@@ -556,19 +512,20 @@
 		mark_buffer_clean(req->bh);
 	if (!(tmp = *current_request)) {
 		req->next = NULL;
-		req->elevator_latency = latency;
 		*current_request = req;
 		if (dev->current_request != &dev->plug)
 			queue_new_request = 1;
 		goto out;
 	}
-	elevator_queue(req, tmp, latency, dev, current_request);
+	elevator_queue(req, tmp);
 
 /* for SCSI devices, call request_fn unconditionally */
 	if (scsi_blk_major(major) ||
             (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) ||
             (major >= COMPAQ_SMART2_MAJOR+0 &&
-             major <= COMPAQ_SMART2_MAJOR+7))
+             major <= COMPAQ_SMART2_MAJOR+7) ||
+	     (major >= COMPAQ_CISS_MAJOR+0 &&
+             major <= COMPAQ_CISS_MAJOR+7))
 		queue_new_request = 1;
 
 out:
@@ -610,28 +567,13 @@
 	wake_up (&wait_for_request);
 }
 
-#define read_pendings(req)			\
-({						\
-	int __ret = 0;				\
-	struct request * tmp = (req);		\
-	do {					\
-		if (tmp->cmd == READ)		\
-		{				\
-			__ret = 1;		\
-			break;			\
-		}				\
-		tmp = tmp->next;		\
-	} while (tmp);				\
-	__ret;					\
-})
-
 void make_request(int major, int rw, struct buffer_head * bh)
 {
 	unsigned int sector, count;
 	struct request * req, * prev;
 	int rw_ahead, max_req, max_sectors, max_segments;
 	unsigned long flags;
-	int latency, starving;
+	int back, front;
 
 	count = bh->b_size >> 9;
 	sector = bh->b_rsector;
@@ -708,8 +650,6 @@
 	max_sectors = get_max_sectors(bh->b_rdev);
 	max_segments = get_max_segments(bh->b_rdev);
 
-	latency = get_request_latency(&blk_dev[major].elevator, rw);
-
 	/*
 	 * Now we acquire the request spinlock, we have to be mega careful
 	 * not to schedule or do something nonatomic
@@ -733,33 +673,31 @@
 		 * entry may be busy being processed and we thus can't change it.
 		 */
 		if (req == blk_dev[major].current_request)
-		{
 	        	if (!(req = req->next))
 				break;
-			latency -= req->nr_segments;
-		}
 		/* fall through */
 	     CASE_COALESCE_ALSO_FIRST_REQUEST
 
-		/* avoid write-bombs to not hurt iteractiveness of reads */
-		if (rw != READ && read_pendings(req))
-			max_segments = blk_dev[major].elevator.max_bomb_segments;
-
-		starving = seek_to_not_starving_chunk(&req, &latency);
+		req = seek_to_not_starving_chunk(req);
 		prev = NULL;
+		back = front = 0;
 		do {
-			if (req->sem)
-				continue;
 			if (req->cmd != rw)
 				continue;
+			if (req->rq_dev != bh->b_rdev)
+				continue;
+			if (req->sector + req->nr_sectors == sector)
+				back = 1;
+			else if (req->sector - count == sector)
+				front = 1;
+
 			if (req->nr_sectors + count > max_sectors)
 				continue;
-			if (req->rq_dev != bh->b_rdev)
+			if (req->sem)
 				continue;
+
 			/* Can we add it to the end of this request? */
-			if (req->sector + req->nr_sectors == sector) {
-				if (latency - req->nr_segments < 0)
-					break;
+			if (back) {
 				if (req->bhtail->b_data + req->bhtail->b_size
 				    != bh->b_data) {
 					if (req->nr_segments < max_segments)
@@ -770,16 +708,18 @@
 				req->bhtail = bh;
 			    	req->nr_sectors += count;
 
-				/* latency stuff */
-				if ((latency -= req->nr_segments) < req->elevator_latency)
-					req->elevator_latency = latency;
 				elevator_starve_rest_of_queue(req);
 
 				/* Can we now merge this req with the next? */
 				attempt_merge(req, max_sectors, max_segments);
 			/* or to the beginning? */
-			} else if (req->sector - count == sector) {
-				if (!prev && starving)
+			} else if (front) {
+				/*
+				 * Check that we didn't seek on a starving request,
+				 * that could happen only at the first pass, thus
+				 * do that only if prev is NULL.
+				 */
+				if (!prev && ((req->cmd != READ && req->cmd != WRITE) || !req->elevator_latency))
 					break;
 				if (bh->b_data + bh->b_size
 				    != req->bh->b_data) {
@@ -795,8 +735,8 @@
 			    	req->nr_sectors += count;
 
 				/* latency stuff */
-				if (latency < --req->elevator_latency)
-					req->elevator_latency = latency;
+				--req->elevator_latency;
+
 				elevator_starve_rest_of_queue(req);
 
 				if (prev)
@@ -809,7 +749,7 @@
 		    	return;
 
 		} while (prev = req,
-			 (latency -= req->nr_segments) >= 0 && (req = req->next) != NULL);
+			 !front && !back && (req = req->next) != NULL);
 	}
 
 /* find an unused request. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/macide.c linux.18p17/drivers/block/macide.c
--- linux.vanilla/drivers/block/macide.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/macide.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/q40ide.c linux.18p17/drivers/block/q40ide.c
--- linux.vanilla/drivers/block/q40ide.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/q40ide.c	Fri Oct 13 23:59:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/rd.c linux.18p17/drivers/block/rd.c
--- linux.vanilla/drivers/block/rd.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/block/rd.c	Mon Oct  2 10:09:50 2000
@@ -653,7 +653,9 @@
 
 #define OF(args)  args
 
+#ifndef memzero
 #define memzero(s, n)     memset ((s), 0, (n))
+#endif
 
 
 typedef unsigned char  uch;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/rz1000.c linux.18p17/drivers/block/rz1000.c
--- linux.vanilla/drivers/block/rz1000.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/rz1000.c	Fri Sep 29 10:11:45 2000
@@ -86,9 +86,9 @@
 {
 	struct pci_dev *dev = NULL;
 
-	while (dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev)))
 		init_rz1000 (dev, "RZ1000");
-	while (dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev)))
 		init_rz1000 (dev, "RZ1001");
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/swim_iop.c linux.18p17/drivers/block/swim_iop.c
--- linux.vanilla/drivers/block/swim_iop.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/block/swim_iop.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/z2ram.c linux.18p17/drivers/block/z2ram.c
--- linux.vanilla/drivers/block/z2ram.c	Fri Apr 21 12:45:50 2000
+++ linux.18p17/drivers/block/z2ram.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/cdrom/cdrom.c linux.18p17/drivers/cdrom/cdrom.c
--- linux.vanilla/drivers/cdrom/cdrom.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/cdrom/cdrom.c	Mon Oct  2 10:21:52 2000
@@ -82,17 +82,17 @@
   Thanks to Grant R. Guenther <grant@torque.net> for spotting this bug.
   -- Made a few things more pedanticly correct.
 
-2.50 Oct 19, 1998 - Jens Axboe <axboe@image.dk>
+2.50 Oct 19, 1998 - Jens Axboe <axboe@suse.de>
   -- New maintainers! Erik was too busy to continue the work on the driver,
-  so now Chris Zwilling <chris@cloudnet.com> and Jens Axboe <axboe@image.dk>
+  so now Chris Zwilling <chris@cloudnet.com> and Jens Axboe <axboe@suse.de>
   will do their best to follow in his footsteps
   
-  2.51 Dec 20, 1998 - Jens Axboe <axboe@image.dk>
+  2.51 Dec 20, 1998 - Jens Axboe <axboe@suse.de>
   -- Check if drive is capable of doing what we ask before blindly changing
   cdi->options in various ioctl.
   -- Added version to proc entry.
   
-  2.52 Jan 16, 1999 - Jens Axboe <axboe@image.dk>
+  2.52 Jan 16, 1999 - Jens Axboe <axboe@suse.de>
   -- Fixed an error in open_for_data where we would sometimes not return
   the correct error value. Thanks Huba Gaspar <huba@softcell.hu>.
   -- Fixed module usage count - usage was based on /proc/sys/dev
@@ -101,7 +101,7 @@
   dev would be removed even though it was used. cdrom.c just illuminated
   that bug.
   
-  2.53 Feb 22, 1999 - Jens Axboe <axboe@image.dk>
+  2.53 Feb 22, 1999 - Jens Axboe <axboe@suse.de>
   -- Fixup of several ioctl calls, in particular CDROM_SET_OPTIONS has
   been "rewritten" because capabilities and options aren't in sync. They
   should be...
@@ -111,16 +111,16 @@
   -- Added CDROM_GET_CAPABILITY ioctl. This relieves userspace programs
   from parsing /proc/sys/dev/cdrom/info.
   
-  2.54 Mar 15, 1999 - Jens Axboe <axboe@image.dk>
+  2.54 Mar 15, 1999 - Jens Axboe <axboe@suse.de>
   -- Check capability mask from low level driver when counting tracks as
   per suggestion from Corey J. Scotts <cstotts@blue.weeg.uiowa.edu>.
   
-  2.55 Apr 25, 1999 - Jens Axboe <axboe@image.dk>
+  2.55 Apr 25, 1999 - Jens Axboe <axboe@suse.de>
   -- autoclose was mistakenly checked against CDC_OPEN_TRAY instead of
   CDC_CLOSE_TRAY.
   -- proc info didn't mask against capabilities mask.
   
-  3.00 Aug 5, 1999 - Jens Axboe <axboe@image.dk>
+  3.00 Aug 5, 1999 - Jens Axboe <axboe@suse.de>
   -- Unified audio ioctl handling across CD-ROM drivers. A lot of the
   code was duplicated before. Drives that support the generic packet
   interface are now being fed packets from here instead.
@@ -138,13 +138,13 @@
   -- CDROM_SEND_PACKET ioctl added. The infrastructure was in place for
   doing this anyway, with the generic_packet addition.
   
-  3.01 Aug 6, 1999 - Jens Axboe <axboe@image.dk>
+  3.01 Aug 6, 1999 - Jens Axboe <axboe@suse.de>
   -- Fix up the sysctl handling so that the option flags get set
   correctly.
   -- Fix up ioctl handling so the device specific ones actually get
   called :).
   
-  3.02 Aug 8, 1999 - Jens Axboe <axboe@image.dk>
+  3.02 Aug 8, 1999 - Jens Axboe <axboe@suse.de>
   -- Fixed volume control on SCSI drives (or others with longer audio
   page).
   -- Fixed a couple of DVD minors. Thanks to Andrew T. Veliath
@@ -153,7 +153,7 @@
   DVD patches for ide-cd and while I rearranged and unified them, the
   interface is still the same.
   
-  3.03 Sep 1, 1999 - Jens Axboe <axboe@image.dk>
+  3.03 Sep 1, 1999 - Jens Axboe <axboe@suse.de>
   -- Moved the rest of the audio ioctls from the CD-ROM drivers here. Only
   CDROMREADTOCENTRY and CDROMREADTOCHDR are left.
   -- Moved the CDROMREADxxx ioctls in here.
@@ -164,7 +164,7 @@
   drivers are updated as well.
   -- Various other cleanups.
 
-  3.04 Sep 12, 1999 - Jens Axboe <axboe@image.dk>
+  3.04 Sep 12, 1999 - Jens Axboe <axboe@suse.de>
   -- Fixed a couple of possible memory leaks (if an operation failed and
   we didn't free the buffer before returning the error).
   -- Integrated Uniform CD Changer handling from Richard Sharman
@@ -178,7 +178,7 @@
   -- Export cdrom_mode_sense and cdrom_mode_select.
   -- init_cdrom_command() for setting up a cgc command.
   
-  3.05 Oct 24, 1999 - Jens Axboe <axboe@image.dk>
+  3.05 Oct 24, 1999 - Jens Axboe <axboe@suse.de>
   -- Changed the interface for CDROM_SEND_PACKET. Before it was virtually
   impossible to send the drive data in a sensible way.
   -- Lowered stack usage in mmc_ioctl(), dvd_read_disckey(), and
@@ -187,7 +187,7 @@
   -- Fixed CDDA ripping with cdda2wav - accept much larger requests of
   number of frames and split the reads in blocks of 8.
 
-  3.06 Dec 13, 1999 - Jens Axboe <axboe@image.dk>
+  3.06 Dec 13, 1999 - Jens Axboe <axboe@suse.de>
   -- Added support for changing the region of DVD drives.
   -- Added sense data to generic command.
 
@@ -1106,6 +1106,8 @@
 		cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n");
 		setup_report_key(&cgc, 0, 8);
 		memset(&rpc_state, 0, sizeof(rpc_state_t));
+		cgc.buffer = (char *) &rpc_state;
+		cgc.buffer = (char *) &rpc_state;
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
 			return ret;
@@ -1604,7 +1606,7 @@
 			return -EDRIVE_CANT_DO_THIS;
 		keeplocked = arg ? 1 : 0;
 		/* don't unlock the door on multiple opens */
-		if ((cdi->use_count != 1) && !arg)
+		if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN))
 			return -EBUSY;
 		return cdo->lock_door(cdi, arg);
 		}
@@ -1966,19 +1968,6 @@
 		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
 		return 0;
 		}
-	case CDROMPLAYTRKIND: {
-		struct cdrom_ti ti;
-
-		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
-		IOCTL_IN(arg, struct cdrom_ti, ti);
-
-		cgc.cmd[0] = GPCMD_PLAY_AUDIO_TI;
-		cgc.cmd[4] = ti.cdti_trk0;
-		cgc.cmd[5] = ti.cdti_ind0;
-		cgc.cmd[7] = ti.cdti_trk1;
-		cgc.cmd[8] = ti.cdti_ind1;
-		return cdo->generic_packet(cdi, &cgc);
-		}
 	case CDROMPLAYMSF: {
 		struct cdrom_msf msf;
 		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
@@ -2565,6 +2554,7 @@
  * Note: only the top-level directory needs to do this; if
  * a lower level is referenced, the parent will be as well.
  */
+#ifdef CONFIG_PROC_FS
 static void cdrom_procfs_modcount(struct inode *inode, int fill)
 {
 	if (fill) {
@@ -2573,6 +2563,7 @@
 		MOD_DEC_USE_COUNT;
 	}
 }
+#endif
 
 static void cdrom_sysctl_register(void)
 {
@@ -2582,7 +2573,9 @@
 		return;
 
 	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1);
+#ifdef CONFIG_PROC_FS
 	cdrom_root_table->child->de->fill_inode = &cdrom_procfs_modcount;
+#endif
 
 	/* set the defaults */
 	cdrom_sysctl_settings.autoclose = autoclose;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Config.in linux.18p17/drivers/char/Config.in
--- linux.vanilla/drivers/char/Config.in	Sun Jun 11 21:44:12 2000
+++ linux.18p17/drivers/char/Config.in	Sat Sep 23 13:13:41 2000
@@ -66,7 +66,7 @@
     bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
   fi
 fi
-
+     
 bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
 if [ "$CONFIG_MOUSE" = "y" ]; then
 	mainmenu_option next_comment
@@ -106,6 +106,7 @@
          bool '       Fan Tachometer' CONFIG_WDT_501_FAN
      fi
   fi
+  tristate '   WDT PCI Watchdog timer' CONFIG_WDTPCI
   tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
   tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
@@ -120,7 +121,23 @@
 if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
   bool 'Tadpole ANA H8 Support'  CONFIG_H8
 fi
+if [ "$CONFIG_PCI" = "y" ]; then
+  tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG
+fi
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP n
+   if [ "$CONFIG_AGP" != "n" ]; then
+      bool '   Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
+      bool '   Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
+      bool '   VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA
+      bool '   AMD Irongate support' CONFIG_AGP_AMD
+      bool '   Generic SiS support' CONFIG_AGP_SIS
+      bool '   ALI M1541 support' CONFIG_AGP_ALI
+   fi
+   source drivers/char/drm/Config.in
+fi
+      
 mainmenu_option next_comment
 comment 'Video For Linux'
 
@@ -139,11 +156,12 @@
     hex '  Aztech/Packard Bell I/O port (0x350 or 0x358)' CONFIG_RADIO_AZTECH_PORT 350
   fi
   dep_tristate 'ADS Cadet AM/FM Tuner' CONFIG_RADIO_CADET $CONFIG_VIDEO_DEV
-  dep_tristate 'Miro PCM20 Radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV
   dep_tristate 'GemTek Radio Card support' CONFIG_RADIO_GEMTEK $CONFIG_VIDEO_DEV
   if [ "$CONFIG_RADIO_GEMTEK" = "y" ]; then
     hex '  GemTek i/o port (0x20c, 0x24c, 0x248, 0x30c, or 0x34c)' CONFIG_RADIO_GEMTEK_PORT 34c
   fi
+  dep_tristate 'Maestro on board radio' CONFIG_RADIO_MAESTRO $CONFIG_VIDEO_DEV
+  dep_tristate 'Miro PCM20 Radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV
   dep_tristate 'Trust FM Radio' CONFIG_RADIO_TRUST $CONFIG_VIDEO_DEV
   if [ "$CONFIG_RADIO_TRUST" = "y" ]; then
     hex '  Trust FM Radio I/O port (0x350 or 0x358)' CONFIG_RADIO_TRUST_PORT 350
@@ -160,18 +178,18 @@
     dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    dep_tristate 'CPiA Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV
+    dep_tristate 'CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV
     if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then
-      if [ "$CONFIG_PARPORT" != "n" ]; then
-        dep_tristate 'CPiA Parallel Port Lowlevel Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT
-        if [ "$CONFIG_VIDEO_CPIA_PP" != "n" ]; then
-          bool 'CPiA Parallel Port DMA Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP_DMA
-        fi
+      if [ "CONFIG_PARPORT_1284" != "n" ]; then
+        dep_tristate '  CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT
+      fi
+      if [ "$CONFIG_USB" != "n" ]; then
+        dep_tristate '  CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB
       fi
     fi
   fi
   dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
-  if [ "$CONFIG_PMAC" = "y" ]; then
+  if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
     dep_tristate 'PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV
   fi
   dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/Makefile linux.18p17/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/char/Makefile	Sat Sep 30 14:40:19 2000
@@ -11,21 +11,23 @@
 
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) rio ftape joystick
+ALL_SUB_DIRS := $(SUB_DIRS) rio ftape joystick drm agp
 
 #
 # This file contains the font map for the default (hardware) font
 #
 FONTMAPFILE = cp437.uni
 
-L_TARGET := char.a
+O_TARGET := char.o
 M_OBJS   :=
-L_OBJS   := tty_io.o n_tty.o tty_ioctl.o mem.o random.o
-LX_OBJS  := pty.o misc.o
+O_OBJS   := tty_io.o n_tty.o tty_ioctl.o mem.o random.o
+OX_OBJS  := pty.o misc.o
+obj-y 	 :=
+obj-m	 :=
 
 ifdef CONFIG_VT
-L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o
-LX_OBJS += console.o selection.o
+O_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o
+OX_OBJS += console.o selection.o
 endif
 
 ifeq ($(CONFIG_SERIAL),y)
@@ -33,7 +35,7 @@
     ifeq ($(CONFIG_SGI_SERIAL),)
       ifeq ($(CONFIG_DECSTATION),)
 	ifeq ($(CONFIG_BAGET_MIPS),)
-      LX_OBJS += serial.o
+      OX_OBJS += serial.o
     endif
   endif
     endif
@@ -56,28 +58,28 @@
 ifndef CONFIG_BAGET_MIPS
 ifndef CONFIG_SUN_KEYBOARD
 ifdef CONFIG_VT
-LX_OBJS += keyboard.o
-endif
+OX_OBJS += keyboard.o
  ifneq ($(ARCH),m68k)
   ifneq ($(ARCH),s390)
-   L_OBJS += pc_keyb.o defkeymap.o
+   O_OBJS += pc_keyb.o defkeymap.o
  endif
  endif
+endif
 else
 ifdef CONFIG_PCI
-L_OBJS += defkeymap.o
-LX_OBJS += keyboard.o
+O_OBJS += defkeymap.o
+OX_OBJS += keyboard.o
 endif
 endif
 
 ifdef CONFIG_MAGIC_SYSRQ
-LX_OBJS += sysrq.o
+OX_OBJS += sysrq.o
 endif
 endif
 endif
 
 ifeq ($(CONFIG_ATARI_DSP56K),y)
-L_OBJS += dsp56k.o
+O_OBJS += dsp56k.o
 S = y
 else
   ifeq ($(CONFIG_ATARI_DSP56K),m)
@@ -87,7 +89,7 @@
 endif
 
 ifeq ($(CONFIG_ROCKETPORT),y)
-L_OBJS += rocket.o
+O_OBJS += rocket.o
 else
   ifeq ($(CONFIG_ROCKETPORT),m)
   M_OBJS += rocket.o
@@ -95,7 +97,7 @@
 endif
 
 ifeq ($(CONFIG_MOXA_SMARTIO),y)
-L_OBJS += mxser.o
+O_OBJS += mxser.o
 else
   ifeq ($(CONFIG_MOXA_SMARTIO),m)
   M_OBJS += mxser.o
@@ -103,7 +105,7 @@
 endif
 
 ifeq ($(CONFIG_MOXA_INTELLIO),y)
-L_OBJS += moxa.o
+O_OBJS += moxa.o
 else
   ifeq ($(CONFIG_MOXA_INTELLIO),m)
   M_OBJS += moxa.o
@@ -111,7 +113,7 @@
 endif
 
 ifeq ($(CONFIG_DIGI),y)
-L_OBJS += pcxx.o
+O_OBJS += pcxx.o
 else
   ifeq ($(CONFIG_DIGI),m)
   M_OBJS += pcxx.o
@@ -119,7 +121,7 @@
 endif
 
 ifeq ($(CONFIG_DIGIEPCA),y)
-L_OBJS += epca.o
+O_OBJS += epca.o
 else
   ifeq ($(CONFIG_DIGIEPCA),m)
   M_OBJS += epca.o
@@ -127,7 +129,7 @@
 endif
 
 ifeq ($(CONFIG_CYCLADES),y)
-L_OBJS += cyclades.o
+O_OBJS += cyclades.o
 else
   ifeq ($(CONFIG_CYCLADES),m)
   M_OBJS += cyclades.o
@@ -135,7 +137,7 @@
 endif
 
 ifeq ($(CONFIG_STALLION),y)
-L_OBJS += stallion.o
+O_OBJS += stallion.o
 else
   ifeq ($(CONFIG_STALLION),m)
   M_OBJS += stallion.o
@@ -143,7 +145,7 @@
 endif
 
 ifeq ($(CONFIG_ISTALLION),y)
-L_OBJS += istallion.o
+O_OBJS += istallion.o
 else
   ifeq ($(CONFIG_ISTALLION),m)
   M_OBJS += istallion.o
@@ -151,7 +153,7 @@
 endif
 
 ifeq ($(CONFIG_COMPUTONE),y)
-L_OBJS += ip2.o ip2main.o
+O_OBJS += ip2.o ip2main.o
 else
   ifeq ($(CONFIG_COMPUTONE),m)
   M_OBJS += ip2.o ip2main.o
@@ -159,7 +161,7 @@
 endif
 
 ifeq ($(CONFIG_RISCOM8),y)
-L_OBJS += riscom8.o
+O_OBJS += riscom8.o
 else
   ifeq ($(CONFIG_RISCOM8),m)
   M_OBJS += riscom8.o
@@ -167,7 +169,7 @@
 endif
 
 ifeq ($(CONFIG_ISI),y)
-L_OBJS += isicom.o
+O_OBJS += isicom.o
 else
   ifeq ($(CONFIG_ISI),m)
   M_OBJS += isicom.o
@@ -175,7 +177,7 @@
 endif
 
 ifeq ($(CONFIG_ESPSERIAL),y)
-L_OBJS += esp.o
+O_OBJS += esp.o
 else
   ifeq ($(CONFIG_ESPSERIAL),m)
   M_OBJS += esp.o
@@ -191,23 +193,17 @@
 endif
 
 ifeq ($(CONFIG_SPECIALIX),y)
-L_OBJS += specialix.o
+O_OBJS += specialix.o
 else
   ifeq ($(CONFIG_SPECIALIX),m)
   M_OBJS += specialix.o
   endif
 endif
 
-ifeq ($(CONFIG_SX),y)
-L_OBJS += sx.o generic_serial.o
-else
-  ifeq ($(CONFIG_SX),m)
-  M_OBJS += sx.o  generic_serial.o
-  endif
-endif
+obj-$(CONFIG_SX) += sx.o generic_serial.o
 
 ifeq ($(CONFIG_RIO),y)
-L_OBJS += rio/rio.o  generic_serial.o
+O_OBJS += rio/rio.o generic_serial.o
 SUB_DIRS += rio
 MOD_SUB_DIRS += rio
 else
@@ -218,7 +214,7 @@
 endif
 
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
-L_OBJS += atixlmouse.o
+O_OBJS += atixlmouse.o
 else
   ifeq ($(CONFIG_ATIXL_BUSMOUSE),m)
   M_OBJS += atixlmouse.o
@@ -226,7 +222,7 @@
 endif
 
 ifeq ($(CONFIG_BUSMOUSE),y)
-L_OBJS += busmouse.o
+O_OBJS += busmouse.o
 else
   ifeq ($(CONFIG_BUSMOUSE),m)
   M_OBJS += busmouse.o
@@ -234,7 +230,7 @@
 endif
 
 ifeq ($(CONFIG_PRINTER),y)
-L_OBJS += lp.o
+O_OBJS += lp.o
 else
   ifeq ($(CONFIG_PRINTER),m)
   M_OBJS += lp.o
@@ -242,7 +238,7 @@
 endif
 
 ifeq ($(CONFIG_JOYSTICK),y)
-L_OBJS += joystick/js.o
+O_OBJS += joystick/js.o
 SUB_DIRS += joystick
 MOD_SUB_DIRS += joystick
 else
@@ -252,7 +248,7 @@
 endif
 
 ifeq ($(CONFIG_DTLK),y)
-L_OBJS += dtlk.o
+O_OBJS += dtlk.o
 else
   ifeq ($(CONFIG_DTLK),m)
   M_OBJS += dtlk.o
@@ -260,7 +256,7 @@
 endif
 
 ifeq ($(CONFIG_MS_BUSMOUSE),y)
-L_OBJS += msbusmouse.o
+O_OBJS += msbusmouse.o
 else
   ifeq ($(CONFIG_MS_BUSMOUSE),m)
   M_OBJS += msbusmouse.o
@@ -268,7 +264,7 @@
 endif
 
 ifeq ($(CONFIG_82C710_MOUSE),y)
-L_OBJS += qpmouse.o
+O_OBJS += qpmouse.o
 else
   ifeq ($(CONFIG_82C710_MOUSE),m)
   M_OBJS += qpmouse.o
@@ -276,7 +272,7 @@
 endif
 
 ifeq ($(CONFIG_SOFT_WATCHDOG),y)
-L_OBJS += softdog.o
+O_OBJS += softdog.o
 else
   ifeq ($(CONFIG_SOFT_WATCHDOG),m)
   M_OBJS += softdog.o
@@ -284,7 +280,7 @@
 endif
 
 ifeq ($(CONFIG_PCWATCHDOG),y)
-L_OBJS += pcwd.o
+O_OBJS += pcwd.o
 else
   ifeq ($(CONFIG_PCWATCHDOG),m)
   M_OBJS += pcwd.o
@@ -292,7 +288,7 @@
 endif
 
 ifeq ($(CONFIG_ACQUIRE_WDT),y)
-L_OBJS += acquirewdt.o
+O_OBJS += acquirewdt.o
 else
   ifeq ($(CONFIG_ACQUIRE_WDT),m)
   M_OBJS += acquirewdt.o
@@ -300,7 +296,7 @@
 endif
 
 ifeq ($(CONFIG_60XX_WDT),y)
-L_OBJS += sbc60xxwdt.o
+O_OBJS += sbc60xxwdt.o
 else
   ifeq ($(CONFIG_60XX_WDT),m)
   M_OBJS += sbc60xxwdt.o
@@ -308,7 +304,7 @@
 endif
 
 ifeq ($(CONFIG_MIXCOMWD),y)
-L_OBJS += mixcomwd.o
+O_OBJS += mixcomwd.o
 else
   ifeq ($(CONFIG_MIXCOMWD),m)
   M_OBJS += mixcomwd.o
@@ -316,7 +312,7 @@
 endif
 
 ifeq ($(CONFIG_AMIGAMOUSE),y)
-L_OBJS += amigamouse.o
+O_OBJS += amigamouse.o
 else
   ifeq ($(CONFIG_AMIGAMOUSE),m)
     M_OBJS += amigamouse.o
@@ -324,7 +320,7 @@
 endif
 
 ifeq ($(CONFIG_ATARIMOUSE),y)
-L_OBJS += atarimouse.o
+O_OBJS += atarimouse.o
 else
   ifeq ($(CONFIG_ATARIMOUSE),m)
     M_OBJS += atarimouse.o
@@ -332,7 +328,7 @@
 endif
 
 ifeq ($(CONFIG_ADBMOUSE),y)
-L_OBJS += adbmouse.o
+O_OBJS += adbmouse.o
 else
   ifeq ($(CONFIG_ADBMOUSE),m)
     M_OBJS += adbmouse.o
@@ -340,7 +336,7 @@
 endif
 
 ifeq ($(CONFIG_PC110_PAD),y)
-L_OBJS += pc110pad.o
+O_OBJS += pc110pad.o
 else
   ifeq ($(CONFIG_PC110_PAD),m)
     M_OBJS += pc110pad.o
@@ -348,20 +344,28 @@
 endif
 
 ifeq ($(CONFIG_WDT),y)
-L_OBJS += wdt.o
+O_OBJS += wdt.o
 else
   ifeq ($(CONFIG_WDT),m)
     M_OBJS += wdt.o
   endif
 endif
 
+ifeq ($(CONFIG_WDTPCI),y)
+O_OBJS += wdt_pci.o
+else
+  ifeq ($(CONFIG_WDTPCI),m)
+    M_OBJS += wdt_pci.o
+  endif
+endif
+
 ifeq ($(CONFIG_RTC),y)
-L_OBJS += rtc.o
+O_OBJS += rtc.o
 endif
 
 ifeq ($(CONFIG_NVRAM),y)
   ifeq ($(CONFIG_PPC),)
-  L_OBJS += nvram.o
+  O_OBJS += nvram.o
   endif
 else
   ifeq ($(CONFIG_NVRAM),m)
@@ -371,8 +375,27 @@
   endif
 endif
 
+ifeq ($(CONFIG_AGP),y)
+O_OBJS += agp/agp.o
+ALL_SUB_DIRS += agp
+SUB_DIRS += agp
+else
+  ifeq ($(CONFIG_AGP),m)
+  ALL_SUB_DIRS += agp
+  MOD_SUB_DIRS += agp
+  endif
+endif
+
+ifeq ($(CONFIG_INTEL_RNG),y)
+O_OBJS += i810_rng.o
+else
+  ifeq ($(CONFIG_INTEL_RNG),m)
+  M_OBJS += i810_rng.o
+  endif
+endif
+    
 ifeq ($(CONFIG_VIDEO_DEV),y)
-LX_OBJS += videodev.o
+OX_OBJS += videodev.o
 else
   ifeq ($(CONFIG_VIDEO_DEV),m)
     MX_OBJS += videodev.o
@@ -388,7 +411,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_BT848),y)
-L_OBJS += bttv.o tuner.o
+O_OBJS += bttv.o tuner.o
 L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_BT848),m)
@@ -398,7 +421,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_MSP3400),y)
-L_OBJS += msp3400.o 
+O_OBJS += msp3400.o 
 L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_MSP3400),m)
@@ -408,7 +431,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_SAA5249),y)
-L_OBJS += saa5249.o
+O_OBJS += saa5249.o
 L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_SAA5249),m)
@@ -418,7 +441,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_BWQCAM),y)
-L_OBJS += bw-qcam.o
+O_OBJS += bw-qcam.o
 else
   ifeq ($(CONFIG_VIDEO_BWQCAM),m)
     M_OBJS += bw-qcam.o
@@ -426,7 +449,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_CQCAM),y)
-L_OBJS += c-qcam.o
+O_OBJS += c-qcam.o
 else
   ifeq ($(CONFIG_VIDEO_CQCAM),m)
     M_OBJS += c-qcam.o
@@ -434,7 +457,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_ZORAN),y)
-L_OBJS += buz.o
+O_OBJS += buz.o
 L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_ZORAN),m)
@@ -444,7 +467,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_LML33),y)
-L_OBJS += bt856.o bt819.o
+O_OBJS += bt856.o bt819.o
 else
   ifeq ($(CONFIG_VIDEO_LML33),m)
     M_OBJS += bt856.o bt819.o
@@ -452,7 +475,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_BUZ),y)
-L_OBJS += saa7111.o saa7185.o
+O_OBJS += saa7111.o saa7185.o
 else
   ifeq ($(CONFIG_VIDEO_BUZ),m)
     M_OBJS += saa7111.o saa7185.o
@@ -460,7 +483,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_PMS),y)
-L_OBJS += pms.o
+O_OBJS += pms.o
 else
   ifeq ($(CONFIG_VIDEO_PMS),m)
   M_OBJS += pms.o
@@ -468,7 +491,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_PLANB),y)
-L_OBJS += planb.o
+O_OBJS += planb.o
 else
   ifeq ($(CONFIG_VIDEO_PLANB),m)
   M_OBJS += planb.o
@@ -476,7 +499,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_VINO),y)
-L_OBJS += vino.o
+O_OBJS += vino.o
 else
   ifeq ($(CONFIG_VIDEO_VINO),m)
   M_OBJS += vino.o
@@ -484,7 +507,7 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_CPIA),y)
-LX_OBJS += cpia.o
+OX_OBJS += cpia.o
 else
   ifeq ($(CONFIG_VIDEO_CPIA),m)
     MX_OBJS += cpia.o
@@ -492,15 +515,23 @@
 endif
 
 ifeq ($(CONFIG_VIDEO_CPIA_PP),y)
-L_OBJS += cpia_pp.o
+O_OBJS += cpia_pp.o
 else
   ifeq ($(CONFIG_VIDEO_CPIA_PP),m)
     M_OBJS += cpia_pp.o
   endif
 endif
 
+ifeq ($(CONFIG_VIDEO_CPIA_USB),y)
+O_OBJS += cpia_usb.o
+else
+  ifeq ($(CONFIG_VIDEO_CPIA_USB),m)
+    M_OBJS += cpia_usb.o
+  endif
+endif
+
 ifeq ($(CONFIG_RADIO_AZTECH),y)
-L_OBJS += radio-aztech.o
+O_OBJS += radio-aztech.o
 else
   ifeq ($(CONFIG_RADIO_AZTECH),m)
   M_OBJS += radio-aztech.o
@@ -508,7 +539,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_SF16FMI),y)
-L_OBJS += radio-sf16fmi.o
+O_OBJS += radio-sf16fmi.o
 else
   ifeq ($(CONFIG_RADIO_SF16FMI),m)
   M_OBJS += radio-sf16fmi.o
@@ -516,7 +547,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_RTRACK),y)
-L_OBJS += radio-aimslab.o
+O_OBJS += radio-aimslab.o
 else
   ifeq ($(CONFIG_RADIO_RTRACK),m)
   M_OBJS += radio-aimslab.o
@@ -524,7 +555,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_RTRACK2),y)
-L_OBJS += radio-rtrack2.o
+O_OBJS += radio-rtrack2.o
 else
   ifeq ($(CONFIG_RADIO_RTRACK2),m)
   M_OBJS += radio-rtrack2.o
@@ -532,7 +563,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_TYPHOON),y)
-L_OBJS += radio-typhoon.o
+O_OBJS += radio-typhoon.o
 else
   ifeq ($(CONFIG_RADIO_TYPHOON),m)
   M_OBJS += radio-typhoon.o
@@ -540,7 +571,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_ZOLTRIX),y)
-L_OBJS += radio-zoltrix.o
+O_OBJS += radio-zoltrix.o
 else
   ifeq ($(CONFIG_RADIO_ZOLTRIX),m)
   M_OBJS += radio-zoltrix.o
@@ -548,7 +579,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_CADET),y)
-L_OBJS += radio-cadet.o
+O_OBJS += radio-cadet.o
 else
   ifeq ($(CONFIG_RADIO_CADET),m)
   M_OBJS += radio-cadet.o
@@ -556,15 +587,23 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_MIROPCM20),y)
-L_OBJS += radio-miropcm20.o
+O_OBJS += radio-miropcm20.o
 else
   ifeq ($(CONFIG_RADIO_MIROPCM20),m)
   M_OBJS += radio-miropcm20.o
   endif
 endif
 
+ifeq ($(CONFIG_RADIO_MAESTRO),y)
+O_OBJS += radio-maestro.o
+else
+  ifeq ($(CONFIG_RADIO_MAESTRO),m)
+  M_OBJS += radio-maestro.o
+  endif
+endif
+
 ifeq ($(CONFIG_RADIO_GEMTEK),y)
-L_OBJS += radio-gemtek.o
+O_OBJS += radio-gemtek.o
 else
   ifeq ($(CONFIG_RADIO_GEMTEK),m)
   M_OBJS += radio-gemtek.o
@@ -572,7 +611,7 @@
 endif                                             
 
 ifeq ($(CONFIG_RADIO_TRUST),y)
-L_OBJS += radio-trust.o
+O_OBJS += radio-trust.o
 else
   ifeq ($(CONFIG_RADIO_TRUST),m)
   M_OBJS += radio-trust.o
@@ -580,7 +619,7 @@
 endif                                             
 
 ifeq ($(CONFIG_QIC02_TAPE),y)
-L_OBJS += tpqic02.o
+O_OBJS += tpqic02.o
 else
   ifeq ($(CONFIG_QIC02_TAPE),m)
   M_OBJS += tpqic02.o
@@ -588,7 +627,7 @@
 endif
 
 ifeq ($(CONFIG_FTAPE),y)
-L_OBJS       += ftape/ftape.o
+O_OBJS       += ftape/ftape.o
 SUB_DIRS     += ftape
 ifneq ($(CONFIG_ZFTAPE),n)
 MOD_SUB_DIRS += ftape
@@ -600,11 +639,23 @@
 endif
 
 ifdef CONFIG_H8
-LX_OBJS += h8.o
+OX_OBJS += h8.o
+endif
+
+
+ifeq ($(CONFIG_DRM),y)
+SUB_DIRS += drm
+O_OBJS += drm/drm.o
+MOD_SUB_DIRS += drm
+else
+  ifeq ($(CONFIG_DRM),m)
+  MOD_SUB_DIRS += drm
+  endif
 endif
 
+
 ifeq ($(L_I2C),y)
-LX_OBJS += i2c.o
+OX_OBJS += i2c.o
 else
   ifeq ($(M_I2C),y)
   MX_OBJS += i2c.o
@@ -615,7 +666,7 @@
 ifeq ($(CONFIG_HFMODEM),y)
 ALL_SUB_DIRS += hfmodem
 SUB_DIRS += hfmodem
-L_OBJS += hfmodem/hfmodem.o
+O_OBJS += hfmodem/hfmodem.o
 else
   ifeq ($(CONFIG_HFMODEM),m)
   ALL_SUB_DIRS += hfmodem
@@ -625,17 +676,26 @@
 endif
 
 ifeq ($(CONFIG_DZ),y)
-  L_OBJS += dz.o
+  O_OBJS += dz.o
 endif
 
 
 ifeq ($(CONFIG_TOSHIBA),y)
-L_OBJS += toshiba.o
+O_OBJS += toshiba.o
 else
   ifeq ($(CONFIG_TOSHIBA),m)
   M_OBJS += toshiba.o
   endif
 endif
+
+# remove objects from modular that are also built in
+obj-m           := $(filter-out $(obj-y), $(obj-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS          += $(filter-out $(export-objs), $(obj-y))
+OX_OBJS         += $(filter     $(export-objs), $(obj-y))
+M_OBJS          += $(sort $(filter     $(module-list), $(obj-m)))
 
 
 include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/Makefile linux.18p17/drivers/char/agp/Makefile
--- linux.vanilla/drivers/char/agp/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/Makefile	Fri Sep  1 14:06:07 2000
@@ -0,0 +1,22 @@
+#
+# Makefile for the agpgart device driver.  This driver adds a user
+# space ioctl interface to use agp memory.  It also adds a kernel interface
+# that other drivers could use to manipulate agp memory.
+
+O_TARGET	:= agp.o
+
+ifeq ($(CONFIG_AGP),y)
+  O_OBJS += agpgart_fe.o
+  OX_OBJS += agpgart_be.o
+else
+  ifeq ($(CONFIG_AGP), m)
+    MI_OBJS += agpgart_fe.o
+    MIX_OBJS += agpgart_be.o
+    M_OBJS += agpgart.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+agpgart.o: agpgart_be.o agpgart_fe.o
+	$(LD) $(LD_RFLAG) -r -o $@ agpgart_be.o agpgart_fe.o
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agp.h linux.18p17/drivers/char/agp/agp.h
--- linux.vanilla/drivers/char/agp/agp.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/agp.h	Thu Oct 19 01:22:51 2000
@@ -0,0 +1,287 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_BACKEND_PRIV_H
+#define _AGP_BACKEND_PRIV_H 1
+
+#include <asm/io.h>
+
+enum aper_size_type {
+	U8_APER_SIZE,
+	U16_APER_SIZE,
+	U32_APER_SIZE,
+	LVL2_APER_SIZE,
+	FIXED_APER_SIZE
+};
+
+typedef struct _gatt_mask {
+	unsigned long mask;
+	u32 type;
+	/* totally device specific, for integrated chipsets that 
+	 * might have different types of memory masks.  For other
+	 * devices this will probably be ignored */
+} gatt_mask;
+
+typedef struct _aper_size_info_8 {
+	int size;
+	int num_entries;
+	int page_order;
+	u8 size_value;
+} aper_size_info_8;
+
+typedef struct _aper_size_info_16 {
+	int size;
+	int num_entries;
+	int page_order;
+	u16 size_value;
+} aper_size_info_16;
+
+typedef struct _aper_size_info_32 {
+	int size;
+	int num_entries;
+	int page_order;
+	u32 size_value;
+} aper_size_info_32;
+
+typedef struct _aper_size_info_lvl2 {
+	int size;
+	int num_entries;
+	u32 size_value;
+} aper_size_info_lvl2;
+
+typedef struct _aper_size_info_fixed {
+	int size;
+	int num_entries;
+	int page_order;
+} aper_size_info_fixed;
+
+struct agp_bridge_data {
+	agp_version *version;
+	void *aperture_sizes;
+	void *previous_size;
+	void *current_size;
+	void *dev_private_data;
+	struct pci_dev *dev;
+	gatt_mask *masks;
+	unsigned long *gatt_table;
+	unsigned long *gatt_table_real;
+	unsigned long scratch_page;
+	unsigned long gart_bus_addr;
+	unsigned long gatt_bus_addr;
+	u32 mode;
+	enum chipset_type type;
+	enum aper_size_type size_type;
+	u32 *key_list;
+	atomic_t current_memory_agp;
+	atomic_t agp_in_use;
+	int max_memory_agp;	/* in number of pages */
+	int needs_scratch_page;
+	int aperture_size_idx;
+	int num_aperture_sizes;
+	int num_of_masks;
+	int capndx;
+
+	/* Links to driver specific functions */
+
+	int (*fetch_size) (void);
+	int (*configure) (void);
+	void (*agp_enable) (u32);
+	void (*cleanup) (void);
+	void (*tlb_flush) (agp_memory *);
+	unsigned long (*mask_memory) (unsigned long, int);
+	void (*cache_flush) (void);
+	int (*create_gatt_table) (void);
+	int (*free_gatt_table) (void);
+	int (*insert_memory) (agp_memory *, off_t, int);
+	int (*remove_memory) (agp_memory *, off_t, int);
+	agp_memory *(*alloc_by_type) (size_t, int);
+	void (*free_by_type) (agp_memory *);
+};
+
+#define OUTREG32(mmap, addr, val)   writel((val),(mmap + (addr)))
+#define OUTREG16(mmap, addr, val)   writew((val),(mmap + (addr)))
+#define OUTREG8 (mmap, addr, val)   writeb((val),(mmap + (addr)))
+
+#define INREG32(mmap, addr)         readl(mmap + (addr))
+#define INREG16(mmap, addr)         readw(mmap + (addr))
+#define INREG8 (mmap, addr)         readb(mmap + (addr))
+
+#define CACHE_FLUSH	agp_bridge.cache_flush
+#define A_SIZE_8(x)	((aper_size_info_8 *) x)
+#define A_SIZE_16(x)	((aper_size_info_16 *) x)
+#define A_SIZE_32(x)	((aper_size_info_32 *) x)
+#define A_SIZE_LVL2(x)  ((aper_size_info_lvl2 *) x)
+#define A_SIZE_FIX(x)	((aper_size_info_fixed *) x)
+#define A_IDX8()	(A_SIZE_8(agp_bridge.aperture_sizes) + i)
+#define A_IDX16()	(A_SIZE_16(agp_bridge.aperture_sizes) + i)
+#define A_IDX32()	(A_SIZE_32(agp_bridge.aperture_sizes) + i)
+#define A_IDXLVL2()	(A_SIZE_LVL2(agp_bridge.aperture_sizes) + i)
+#define A_IDXFIX()	(A_SIZE_FIX(agp_bridge.aperture_sizes) + i)
+#define MAXKEY		(4096 * 32)
+
+#ifndef min
+#define min(a,b)	(((a)<(b))?(a):(b))
+#endif
+
+#define arraysize(x)            (sizeof(x)/sizeof(*(x)))
+
+#define AGPGART_MODULE_NAME	"agpgart"
+#define PFX			AGPGART_MODULE_NAME ": "
+
+#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page)
+
+#ifndef PCI_DEVICE_ID_VIA_82C691_0
+#define PCI_DEVICE_ID_VIA_82C691_0      0x0691
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C691_1
+#define PCI_DEVICE_ID_VIA_82C691_1      0x8691
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_0
+#define PCI_DEVICE_ID_INTEL_810_0       0x7120
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_840_0
+#define PCI_DEVICE_ID_INTEL_840_0		0x1a21
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_DC100_0
+#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_E_0
+#define PCI_DEVICE_ID_INTEL_810_E_0     0x7124
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443GX_0
+#define PCI_DEVICE_ID_INTEL_82443GX_0   0x71a0
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_1
+#define PCI_DEVICE_ID_INTEL_810_1       0x7121
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_DC100_1
+#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_810_E_1
+#define PCI_DEVICE_ID_INTEL_810_E_1     0x7125
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_815_0
+#define PCI_DEVICE_ID_INTEL_815_0       0x1130
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_815_1
+#define PCI_DEVICE_ID_INTEL_815_1       0x1132
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443GX_1
+#define PCI_DEVICE_ID_INTEL_82443GX_1   0x71a1
+#endif
+#ifndef PCI_DEVICE_ID_AMD_IRONGATE_0
+#define PCI_DEVICE_ID_AMD_IRONGATE_0    0x7006
+#endif
+#ifndef PCI_VENDOR_ID_AL
+#define PCI_VENDOR_ID_AL		0x10b9
+#endif
+#ifndef PCI_DEVICE_ID_AL_M1541_0
+#define PCI_DEVICE_ID_AL_M1541_0	0x1541
+#endif
+#ifndef PCI_DEVICE_ID_SI_630
+#define PCI_DEVICE_ID_SI_630		0x0630
+#endif
+#ifndef PCI_DEVICE_ID_SI_540
+#define PCI_DEVICE_ID_SI_540		0x0540
+#endif
+#ifndef PCI_DEVICE_ID_SI_620
+#define PCI_DEVICE_ID_SI_620		0x0620
+#endif
+#ifndef PCI_DEVICE_ID_SI_530
+#define PCI_DEVICE_ID_SI_530		0x0530
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8501_0
+#define PCI_DEVICE_ID_VIA_8501_0	0x0501
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8371_0
+#define PCI_DEVICE_ID_VIA_8371_0	0x0391
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8363_0
+#define PCI_DEVICE_ID_VIA_8363_0	0x0305
+#endif
+
+
+/* intel register */
+#define INTEL_APBASE    0x10
+#define INTEL_APSIZE    0xb4
+#define INTEL_ATTBASE   0xb8
+#define INTEL_AGPCTRL   0xb0
+#define INTEL_NBXCFG    0x50
+#define INTEL_ERRSTS    0x91
+
+/* intel i840 registers */
+#define INTEL_I840_MCHCFG   0x50
+#define INTEL_I840_ERRSTS	0xc8
+
+/* intel i810 registers */
+#define I810_GMADDR 0x10
+#define I810_MMADDR 0x14
+#define I810_PTE_BASE          0x10000
+#define I810_PTE_MAIN_UNCACHED 0x00000000
+#define I810_PTE_LOCAL         0x00000002
+#define I810_PTE_VALID         0x00000001
+#define I810_SMRAM_MISCC       0x70
+#define I810_GFX_MEM_WIN_SIZE  0x00010000
+#define I810_GFX_MEM_WIN_32M   0x00010000
+#define I810_GMS               0x000000c0
+#define I810_GMS_DISABLE       0x00000000
+#define I810_PGETBL_CTL        0x2020
+#define I810_PGETBL_ENABLED    0x00000001
+#define I810_DRAM_CTL          0x3000
+#define I810_DRAM_ROW_0        0x00000001
+#define I810_DRAM_ROW_0_SDRAM  0x00000001
+
+/* VIA register */
+#define VIA_APBASE      0x10
+#define VIA_GARTCTRL    0x80
+#define VIA_APSIZE      0x84
+#define VIA_ATTBASE     0x88
+
+/* SiS registers */
+#define SIS_APBASE      0x10
+#define SIS_ATTBASE     0x90
+#define SIS_APSIZE      0x94
+#define SIS_TLBCNTRL    0x97
+#define SIS_TLBFLUSH    0x98
+
+/* AMD registers */
+#define AMD_APBASE      0x10
+#define AMD_MMBASE      0x14
+#define AMD_APSIZE      0xac
+#define AMD_MODECNTL    0xb0
+#define AMD_MODECNTL2   0xb2
+#define AMD_GARTENABLE  0x02	/* In mmio region (16-bit register) */
+#define AMD_ATTBASE     0x04	/* In mmio region (32-bit register) */
+#define AMD_TLBFLUSH    0x0c	/* In mmio region (32-bit register) */
+#define AMD_CACHEENTRY  0x10	/* In mmio region (32-bit register) */
+
+/* ALi registers */
+#define ALI_APBASE	0x10
+#define ALI_AGPCTRL	0xb8
+#define ALI_ATTBASE	0xbc
+#define ALI_TLBCTRL	0xc0
+
+#endif				/* _AGP_BACKEND_PRIV_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agp_backend.h linux.18p17/drivers/char/agp/agp_backend.h
--- linux.vanilla/drivers/char/agp/agp_backend.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/agp_backend.h	Fri Sep  1 14:14:15 2000
@@ -0,0 +1,231 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_BACKEND_H
+#define _AGP_BACKEND_H 1
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define AGPGART_VERSION_MAJOR 0
+#define AGPGART_VERSION_MINOR 99
+
+enum chipset_type {
+	NOT_SUPPORTED,
+	INTEL_GENERIC,
+	INTEL_LX,
+	INTEL_BX,
+	INTEL_GX,
+	INTEL_I810,
+	INTEL_I840,
+	VIA_GENERIC,
+	VIA_VP3,
+	VIA_MVP3,
+	VIA_MVP4,
+	VIA_APOLLO_SUPER,
+	VIA_APOLLO_PRO,
+	SIS_GENERIC,
+	AMD_GENERIC,
+	AMD_IRONGATE,
+	ALI_M1541,
+	ALI_GENERIC
+};
+
+typedef struct _agp_version {
+	u16 major;
+	u16 minor;
+} agp_version;
+
+typedef struct _agp_kern_info {
+	agp_version version;
+	struct pci_dev *device;
+	enum chipset_type chipset;
+	unsigned long mode;
+	off_t aper_base;
+	size_t aper_size;
+	int max_memory;		/* In pages */
+	int current_memory;
+} agp_kern_info;
+
+/* 
+ * The agp_memory structure has information
+ * about the block of agp memory allocated.
+ * A caller may manipulate the next and prev
+ * pointers to link each allocated item into
+ * a list.  These pointers are ignored by the 
+ * backend.  Everything else should never be
+ * written to, but the caller may read any of
+ * the items to detrimine the status of this
+ * block of agp memory.
+ * 
+ */
+
+typedef struct _agp_memory {
+	int key;
+	struct _agp_memory *next;
+	struct _agp_memory *prev;
+	size_t page_count;
+	int num_scratch_pages;
+	unsigned long *memory;
+	off_t pg_start;
+	u32 type;
+	u32 physical;
+	u8 is_bound;
+	u8 is_flushed;
+} agp_memory;
+
+#define AGP_NORMAL_MEMORY 0
+
+extern void agp_free_memory(agp_memory *);
+
+/*
+ * agp_free_memory :
+ * 
+ * This function frees memory associated with
+ * an agp_memory pointer.  It is the only function
+ * that can be called when the backend is not owned
+ * by the caller.  (So it can free memory on client
+ * death.)
+ * 
+ * It takes an agp_memory pointer as an argument.
+ * 
+ */
+
+extern agp_memory *agp_allocate_memory(size_t, u32);
+
+/*
+ * agp_allocate_memory :
+ * 
+ * This function allocates a group of pages of
+ * a certain type.
+ * 
+ * It takes a size_t argument of the number of pages, and
+ * an u32 argument of the type of memory to be allocated.  
+ * Every agp bridge device will allow you to allocate 
+ * AGP_NORMAL_MEMORY which maps to physical ram.  Any other
+ * type is device dependant.
+ * 
+ * It returns NULL whenever memory is unavailable.
+ * 
+ */
+
+extern void agp_copy_info(agp_kern_info *);
+
+/*
+ * agp_copy_info :
+ * 
+ * This function copies information about the
+ * agp bridge device and the state of the agp
+ * backend into an agp_kern_info pointer.
+ * 
+ * It takes an agp_kern_info pointer as an
+ * argument.  The caller should insure that
+ * this pointer is valid.
+ * 
+ */
+
+extern int agp_bind_memory(agp_memory *, off_t);
+
+/*
+ * agp_bind_memory :
+ * 
+ * This function binds an agp_memory structure
+ * into the graphics aperture translation table.
+ * 
+ * It takes an agp_memory pointer and an offset into
+ * the graphics aperture translation table as arguments
+ * 
+ * It returns -EINVAL if the pointer == NULL.
+ * It returns -EBUSY if the area of the table
+ * requested is already in use.
+ * 
+ */
+
+extern int agp_unbind_memory(agp_memory *);
+
+/* 
+ * agp_unbind_memory :
+ * 
+ * This function removes an agp_memory structure
+ * from the graphics aperture translation table.
+ * 
+ * It takes an agp_memory pointer as an argument.
+ * 
+ * It returns -EINVAL if this piece of agp_memory
+ * is not currently bound to the graphics aperture
+ * translation table or if the agp_memory 
+ * pointer == NULL
+ * 
+ */
+
+extern void agp_enable(u32);
+
+/* 
+ * agp_enable :
+ * 
+ * This function initializes the agp point-to-point
+ * connection.
+ * 
+ * It takes an agp mode register as an argument
+ * 
+ */
+
+extern int agp_backend_acquire(void);
+
+/*
+ * agp_backend_acquire :
+ * 
+ * This Function attempts to acquire the agp
+ * backend.
+ * 
+ * returns -EBUSY if agp is in use,
+ * returns 0 if the caller owns the agp backend
+ */
+
+extern void agp_backend_release(void);
+
+/*
+ * agp_backend_release :
+ * 
+ * This Function releases the lock on the agp
+ * backend.
+ * 
+ * The caller must insure that the graphics
+ * aperture translation table is read for use
+ * by another entity.  (Ensure that all memory
+ * it bound is unbound.)
+ * 
+ */
+
+/* Don't call this directly!! used in initialization */
+extern int agp_init(void);
+
+#endif				/* _AGP_BACKEND_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart.h linux.18p17/drivers/char/agp/agpgart.h
--- linux.vanilla/drivers/char/agp/agpgart.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/agpgart.h	Sun Sep  3 00:12:57 2000
@@ -0,0 +1,224 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_H
+#define _AGP_H 1
+
+#define AGPIOC_BASE       'A'
+#define AGPIOC_INFO       _IOR (AGPIOC_BASE, 0, agp_info*)
+#define AGPIOC_ACQUIRE    _IO  (AGPIOC_BASE, 1)
+#define AGPIOC_RELEASE    _IO  (AGPIOC_BASE, 2)
+#define AGPIOC_SETUP      _IOW (AGPIOC_BASE, 3, agp_setup*)
+#define AGPIOC_RESERVE    _IOW (AGPIOC_BASE, 4, agp_region*)
+#define AGPIOC_PROTECT    _IOW (AGPIOC_BASE, 5, agp_region*)
+#define AGPIOC_ALLOCATE   _IOWR(AGPIOC_BASE, 6, agp_allocate*)
+#define AGPIOC_DEALLOCATE _IOW (AGPIOC_BASE, 7, int)
+#define AGPIOC_BIND       _IOW (AGPIOC_BASE, 8, agp_bind*)
+#define AGPIOC_UNBIND     _IOW (AGPIOC_BASE, 9, agp_unbind*)
+
+#define AGP_DEVICE      "/dev/agpgart"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef __KERNEL__
+#include <linux/types.h>
+#include <asm/types.h>
+
+typedef struct _agp_version {
+	__u16 major;
+	__u16 minor;
+} agp_version;
+
+typedef struct _agp_info {
+	agp_version version;	/* version of the driver        */
+	__u32 bridge_id;	/* bridge vendor/device         */
+	__u32 agp_mode;		/* mode info of bridge          */
+	off_t aper_base;	/* base of aperture             */
+	size_t aper_size;	/* size of aperture             */
+	size_t pg_total;	/* max pages (swap + system)    */
+	size_t pg_system;	/* max pages (system)           */
+	size_t pg_used;		/* current pages used           */
+} agp_info;
+
+typedef struct _agp_setup {
+	__u32 agp_mode;		/* mode info of bridge          */
+} agp_setup;
+
+/*
+ * The "prot" down below needs still a "sleep" flag somehow ...
+ */
+typedef struct _agp_segment {
+	off_t pg_start;		/* starting page to populate    */
+	size_t pg_count;	/* number of pages              */
+	int prot;		/* prot flags for mmap          */
+} agp_segment;
+
+typedef struct _agp_region {
+	pid_t pid;		/* pid of process               */
+	size_t seg_count;	/* number of segments           */
+	struct _agp_segment *seg_list;
+} agp_region;
+
+typedef struct _agp_allocate {
+	int key;		/* tag of allocation            */
+	size_t pg_count;	/* number of pages              */
+	__u32 type;		/* 0 == normal, other devspec   */
+   	__u32 physical;         /* device specific (some devices  
+				 * need a phys address of the     
+				 * actual page behind the gatt    
+				 * table)                        */
+} agp_allocate;
+
+typedef struct _agp_bind {
+	int key;		/* tag of allocation            */
+	off_t pg_start;		/* starting page to populate    */
+} agp_bind;
+
+typedef struct _agp_unbind {
+	int key;		/* tag of allocation            */
+	__u32 priority;		/* priority for paging out      */
+} agp_unbind;
+
+#else				/* __KERNEL__ */
+
+#define AGPGART_MINOR 175
+
+#define AGP_UNLOCK()      	up(&(agp_fe.agp_mutex));
+#define AGP_LOCK()    		down(&(agp_fe.agp_mutex));
+#define AGP_LOCK_INIT() 	sema_init(&(agp_fe.agp_mutex), 1)
+
+#ifndef _AGP_BACKEND_H
+typedef struct _agp_version {
+	u16 major;
+	u16 minor;
+} agp_version;
+
+#endif
+
+typedef struct _agp_info {
+	agp_version version;	/* version of the driver        */
+	u32 bridge_id;		/* bridge vendor/device         */
+	u32 agp_mode;		/* mode info of bridge          */
+	off_t aper_base;	/* base of aperture             */
+	size_t aper_size;	/* size of aperture             */
+	size_t pg_total;	/* max pages (swap + system)    */
+	size_t pg_system;	/* max pages (system)           */
+	size_t pg_used;		/* current pages used           */
+} agp_info;
+
+typedef struct _agp_setup {
+	u32 agp_mode;		/* mode info of bridge          */
+} agp_setup;
+
+/*
+ * The "prot" down below needs still a "sleep" flag somehow ...
+ */
+typedef struct _agp_segment {
+	off_t pg_start;		/* starting page to populate    */
+	size_t pg_count;	/* number of pages              */
+	int prot;		/* prot flags for mmap          */
+} agp_segment;
+
+typedef struct _agp_segment_priv {
+	off_t pg_start;
+	size_t pg_count;
+	pgprot_t prot;
+} agp_segment_priv;
+
+typedef struct _agp_region {
+	pid_t pid;		/* pid of process               */
+	size_t seg_count;	/* number of segments           */
+	struct _agp_segment *seg_list;
+} agp_region;
+
+typedef struct _agp_allocate {
+	int key;		/* tag of allocation            */
+	size_t pg_count;	/* number of pages              */
+	u32 type;		/* 0 == normal, other devspec   */
+	u32 physical;           /* device specific (some devices  
+				 * need a phys address of the     
+				 * actual page behind the gatt    
+				 * table)                        */
+} agp_allocate;
+
+typedef struct _agp_bind {
+	int key;		/* tag of allocation            */
+	off_t pg_start;		/* starting page to populate    */
+} agp_bind;
+
+typedef struct _agp_unbind {
+	int key;		/* tag of allocation            */
+	u32 priority;		/* priority for paging out      */
+} agp_unbind;
+
+typedef struct _agp_client {
+	struct _agp_client *next;
+	struct _agp_client *prev;
+	pid_t pid;
+	int num_segments;
+	agp_segment_priv **segments;
+} agp_client;
+
+typedef struct _agp_controller {
+	struct _agp_controller *next;
+	struct _agp_controller *prev;
+	pid_t pid;
+	int num_clients;
+	agp_memory *pool;
+	agp_client *clients;
+} agp_controller;
+
+#define AGP_FF_ALLOW_CLIENT		0
+#define AGP_FF_ALLOW_CONTROLLER 	1
+#define AGP_FF_IS_CLIENT		2
+#define AGP_FF_IS_CONTROLLER		3
+#define AGP_FF_IS_VALID 		4
+
+typedef struct _agp_file_private {
+	struct _agp_file_private *next;
+	struct _agp_file_private *prev;
+	pid_t my_pid;
+	u32 access_flags;
+} agp_file_private;
+
+struct agp_front_data {
+	struct semaphore agp_mutex;
+	agp_controller *current_controller;
+	agp_controller *controllers;
+	agp_file_private *file_priv_list;
+	u8 used_by_controller;
+	u8 backend_acquired;
+};
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _AGP_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart_be.c linux.18p17/drivers/char/agp/agpgart_be.c
--- linux.vanilla/drivers/char/agp/agpgart_be.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/agpgart_be.c	Tue Sep 26 17:00:25 2000
@@ -0,0 +1,2493 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/miscdevice.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/page.h>
+
+#include <linux/agp_backend.h>
+#include "agp.h"
+
+MODULE_AUTHOR("Jeff Hartmann <jhartmann@precisioninsight.com>");
+MODULE_PARM(agp_try_unsupported, "1i");
+EXPORT_SYMBOL(agp_free_memory);
+EXPORT_SYMBOL(agp_allocate_memory);
+EXPORT_SYMBOL(agp_copy_info);
+EXPORT_SYMBOL(agp_bind_memory);
+EXPORT_SYMBOL(agp_unbind_memory);
+EXPORT_SYMBOL(agp_enable);
+EXPORT_SYMBOL(agp_backend_acquire);
+EXPORT_SYMBOL(agp_backend_release);
+
+static void flush_cache(void);
+
+static struct agp_bridge_data agp_bridge;
+static int agp_try_unsupported __initdata = 0;
+
+
+static inline void flush_cache(void)
+{
+#if defined(__i386__)
+	asm volatile ("wbinvd":::"memory");
+#elif defined(__alpha__)
+	/* ??? I wonder if we'll really need to flush caches, or if the
+	   core logic can manage to keep the system coherent.  The ARM
+	   speaks only of using `cflush' to get things in memory in
+	   preparation for power failure.
+
+	   If we do need to call `cflush', we'll need a target page,
+	   as we can only flush one page at a time.  */
+	mb();
+#else
+#error "Please define flush_cache."
+#endif
+}
+
+#ifdef CONFIG_SMP
+static atomic_t cpus_waiting;
+
+static void ipi_handler(void *null)
+{
+	flush_cache();
+	atomic_dec(&cpus_waiting);
+	while (atomic_read(&cpus_waiting) > 0)
+		barrier();
+}
+
+static void smp_flush_cache(void)
+{
+	atomic_set(&cpus_waiting, smp_num_cpus - 1);
+	if (smp_call_function(ipi_handler, NULL, 1, 0) != 0)
+		panic(PFX "timed out waiting for the other CPUs!\n");
+	flush_cache();
+	while (atomic_read(&cpus_waiting) > 0)
+		barrier();
+}
+#define global_cache_flush smp_flush_cache
+#else				/* CONFIG_SMP */
+#define global_cache_flush flush_cache
+#endif				/* CONFIG_SMP */
+
+int agp_backend_acquire(void)
+{
+	atomic_inc(&agp_bridge.agp_in_use);
+
+	if (atomic_read(&agp_bridge.agp_in_use) != 1) {
+		atomic_dec(&agp_bridge.agp_in_use);
+		return -EBUSY;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+void agp_backend_release(void)
+{
+	atomic_dec(&agp_bridge.agp_in_use);
+	MOD_DEC_USE_COUNT;
+}
+
+/* 
+ * Basic Page Allocation Routines -
+ * These routines handle page allocation
+ * and by default they reserve the allocated 
+ * memory.  They also handle incrementing the
+ * current_memory_agp value, Which is checked
+ * against a maximum value.
+ */
+
+static unsigned long agp_alloc_page(void)
+{
+	void *pt;
+
+	pt = (void *) __get_free_page(GFP_KERNEL);
+	if (pt == NULL) {
+		return 0;
+	}
+	atomic_inc(&mem_map[MAP_NR(pt)].count);
+	set_bit(PG_locked, &mem_map[MAP_NR(pt)].flags);
+	atomic_inc(&agp_bridge.current_memory_agp);
+	return (unsigned long) pt;
+}
+
+static void agp_destroy_page(unsigned long page)
+{
+	void *pt = (void *) page;
+
+	if (pt == NULL) {
+		return;
+	}
+	atomic_dec(&mem_map[MAP_NR(pt)].count);
+	clear_bit(PG_locked, &mem_map[MAP_NR(pt)].flags);
+#if 0
+   	wake_up(&mem_map[MAP_NR(pt)].wait);
+#endif
+	free_page((unsigned long) pt);
+	atomic_dec(&agp_bridge.current_memory_agp);
+}
+
+/* End Basic Page Allocation Routines */
+
+/* 
+ * Generic routines for handling agp_memory structures -
+ * They use the basic page allocation routines to do the
+ * brunt of the work.
+ */
+
+
+static void agp_free_key(int key)
+{
+
+	if (key < 0) {
+		return;
+	}
+	if (key < MAXKEY) {
+		clear_bit(key, agp_bridge.key_list);
+	}
+}
+
+static int agp_get_key(void)
+{
+	int bit;
+
+	bit = find_first_zero_bit(agp_bridge.key_list, MAXKEY);
+	if (bit < MAXKEY) {
+		set_bit(bit, agp_bridge.key_list);
+		return bit;
+	}
+	return -1;
+}
+
+static agp_memory *agp_create_memory(int scratch_pages)
+{
+	agp_memory *new;
+
+	new = kmalloc(sizeof(agp_memory), GFP_KERNEL);
+
+	if (new == NULL) {
+		return NULL;
+	}
+	memset(new, 0, sizeof(agp_memory));
+	new->key = agp_get_key();
+
+	if (new->key < 0) {
+		kfree(new);
+		return NULL;
+	}
+	new->memory = vmalloc(PAGE_SIZE * scratch_pages);
+
+	if (new->memory == NULL) {
+		agp_free_key(new->key);
+		kfree(new);
+		return NULL;
+	}
+	new->num_scratch_pages = scratch_pages;
+	return new;
+}
+
+void agp_free_memory(agp_memory * curr)
+{
+	int i;
+
+	if (curr == NULL) {
+		return;
+	}
+	if (curr->is_bound == TRUE) {
+		agp_unbind_memory(curr);
+	}
+	if (curr->type != 0) {
+		agp_bridge.free_by_type(curr);
+		return;
+	}
+	if (curr->page_count != 0) {
+		for (i = 0; i < curr->page_count; i++) {
+			curr->memory[i] &= ~(0x00000fff);
+			agp_destroy_page((unsigned long)
+					 phys_to_virt(curr->memory[i]));
+		}
+	}
+	agp_free_key(curr->key);
+	vfree(curr->memory);
+	kfree(curr);
+	MOD_DEC_USE_COUNT;
+}
+
+#define ENTRIES_PER_PAGE		(PAGE_SIZE / sizeof(unsigned long))
+
+agp_memory *agp_allocate_memory(size_t page_count, u32 type)
+{
+	int scratch_pages;
+	agp_memory *new;
+	int i;
+
+	if ((atomic_read(&agp_bridge.current_memory_agp) + page_count) >
+	    agp_bridge.max_memory_agp) {
+		return NULL;
+	}
+
+	if (type != 0) {
+		new = agp_bridge.alloc_by_type(page_count, type);
+		return new;
+	}
+      	/* We always increase the module count, since free auto-decrements
+	 * it
+	 */
+
+      	MOD_INC_USE_COUNT;
+
+	scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
+
+	new = agp_create_memory(scratch_pages);
+
+	if (new == NULL) {
+	      	MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	for (i = 0; i < page_count; i++) {
+		new->memory[i] = agp_alloc_page();
+
+		if (new->memory[i] == 0) {
+			/* Free this structure */
+			agp_free_memory(new);
+			return NULL;
+		}
+		new->memory[i] =
+		    agp_bridge.mask_memory(
+				   virt_to_phys((void *) new->memory[i]),
+						  type);
+		new->page_count++;
+	}
+
+	return new;
+}
+
+/* End - Generic routines for handling agp_memory structures */
+
+static int agp_return_size(void)
+{
+	int current_size;
+	void *temp;
+
+	temp = agp_bridge.current_size;
+
+	switch (agp_bridge.size_type) {
+	case U8_APER_SIZE:
+		current_size = A_SIZE_8(temp)->size;
+		break;
+	case U16_APER_SIZE:
+		current_size = A_SIZE_16(temp)->size;
+		break;
+	case U32_APER_SIZE:
+		current_size = A_SIZE_32(temp)->size;
+		break;
+	case LVL2_APER_SIZE:
+		current_size = A_SIZE_LVL2(temp)->size;
+		break;
+	case FIXED_APER_SIZE:
+		current_size = A_SIZE_FIX(temp)->size;
+		break;
+	default:
+		current_size = 0;
+		break;
+	}
+
+	return current_size;
+}
+
+/* Routine to copy over information structure */
+
+void agp_copy_info(agp_kern_info * info)
+{
+	memset(info, 0, sizeof(agp_kern_info));
+	info->version.major = agp_bridge.version->major;
+	info->version.minor = agp_bridge.version->minor;
+	info->device = agp_bridge.dev;
+	info->chipset = agp_bridge.type;
+	info->mode = agp_bridge.mode;
+	info->aper_base = agp_bridge.gart_bus_addr;
+	info->aper_size = agp_return_size();
+	info->max_memory = agp_bridge.max_memory_agp;
+	info->current_memory = atomic_read(&agp_bridge.current_memory_agp);
+}
+
+/* End - Routine to copy over information structure */
+
+/*
+ * Routines for handling swapping of agp_memory into the GATT -
+ * These routines take agp_memory and insert them into the GATT.
+ * They call device specific routines to actually write to the GATT.
+ */
+
+int agp_bind_memory(agp_memory * curr, off_t pg_start)
+{
+	int ret_val;
+
+	if ((curr == NULL) || (curr->is_bound == TRUE)) {
+		return -EINVAL;
+	}
+	ret_val = agp_bridge.insert_memory(curr, pg_start, curr->type);
+
+	if (ret_val != 0) {
+		return ret_val;
+	}
+	curr->is_bound = TRUE;
+	curr->pg_start = pg_start;
+	return 0;
+}
+
+int agp_unbind_memory(agp_memory * curr)
+{
+	int ret_val;
+
+	if (curr == NULL) {
+		return -EINVAL;
+	}
+	if (curr->is_bound != TRUE) {
+		return -EINVAL;
+	}
+	ret_val = agp_bridge.remove_memory(curr, curr->pg_start, curr->type);
+
+	if (ret_val != 0) {
+		return ret_val;
+	}
+	curr->is_bound = FALSE;
+	curr->pg_start = 0;
+	return 0;
+}
+
+/* End - Routines for handling swapping of agp_memory into the GATT */
+
+/* 
+ * Driver routines - start
+ * Currently this module supports the 
+ * i810, 440lx, 440bx, 440gx, via vp3, via mvp3,
+ * amd irongate, ALi M1541 and generic support for the
+ * SiS chipsets.
+ */
+
+/* Generic Agp routines - Start */
+
+static void agp_generic_agp_enable(u32 mode)
+{
+	struct pci_dev *device = NULL;
+	u32 command, scratch, cap_id;
+	u8 cap_ptr;
+
+	pci_read_config_dword(agp_bridge.dev,
+			      agp_bridge.capndx + 4,
+			      &command);
+
+	/*
+	 * PASS1: go throu all devices that claim to be
+	 *        AGP devices and collect their data.
+	 */
+
+	while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8,
+					device)) != NULL) {
+		pci_read_config_dword(device, 0x04, &scratch);
+
+		if (!(scratch & 0x00100000))
+			continue;
+
+		pci_read_config_byte(device, 0x34, &cap_ptr);
+
+		if (cap_ptr != 0x00) {
+			do {
+				pci_read_config_dword(device,
+						      cap_ptr, &cap_id);
+
+				if ((cap_id & 0xff) != 0x02)
+					cap_ptr = (cap_id >> 8) & 0xff;
+			}
+			while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
+		}
+		if (cap_ptr != 0x00) {
+			/*
+			 * Ok, here we have a AGP device. Disable impossible 
+			 * settings, and adjust the readqueue to the minimum.
+			 */
+
+			pci_read_config_dword(device, cap_ptr + 4, &scratch);
+
+			/* adjust RQ depth */
+			command =
+			    ((command & ~0xff000000) |
+			     min((mode & 0xff000000),
+				 min((command & 0xff000000),
+				     (scratch & 0xff000000))));
+
+			/* disable SBA if it's not supported */
+			if (!((command & 0x00000200) &&
+			      (scratch & 0x00000200) &&
+			      (mode & 0x00000200)))
+				command &= ~0x00000200;
+
+			/* disable FW if it's not supported */
+			if (!((command & 0x00000010) &&
+			      (scratch & 0x00000010) &&
+			      (mode & 0x00000010)))
+				command &= ~0x00000010;
+
+			if (!((command & 4) &&
+			      (scratch & 4) &&
+			      (mode & 4)))
+				command &= ~0x00000004;
+
+			if (!((command & 2) &&
+			      (scratch & 2) &&
+			      (mode & 2)))
+				command &= ~0x00000002;
+
+			if (!((command & 1) &&
+			      (scratch & 1) &&
+			      (mode & 1)))
+				command &= ~0x00000001;
+		}
+	}
+	/*
+	 * PASS2: Figure out the 4X/2X/1X setting and enable the
+	 *        target (our motherboard chipset).
+	 */
+
+	if (command & 4) {
+		command &= ~3;	/* 4X */
+	}
+	if (command & 2) {
+		command &= ~5;	/* 2X */
+	}
+	if (command & 1) {
+		command &= ~6;	/* 1X */
+	}
+	command |= 0x00000100;
+
+	pci_write_config_dword(agp_bridge.dev,
+			       agp_bridge.capndx + 8,
+			       command);
+
+	/*
+	 * PASS3: Go throu all AGP devices and update the
+	 *        command registers.
+	 */
+
+	while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8,
+					device)) != NULL) {
+		pci_read_config_dword(device, 0x04, &scratch);
+
+		if (!(scratch & 0x00100000))
+			continue;
+
+		pci_read_config_byte(device, 0x34, &cap_ptr);
+
+		if (cap_ptr != 0x00) {
+			do {
+				pci_read_config_dword(device,
+						      cap_ptr, &cap_id);
+
+				if ((cap_id & 0xff) != 0x02)
+					cap_ptr = (cap_id >> 8) & 0xff;
+			}
+			while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
+		}
+		if (cap_ptr != 0x00)
+			pci_write_config_dword(device, cap_ptr + 8, command);
+	}
+}
+
+static int agp_generic_create_gatt_table(void)
+{
+	char *table;
+	char *table_end;
+	int size;
+	int page_order;
+	int num_entries;
+	int i;
+	void *temp;
+
+	/* The generic routines can't handle 2 level gatt's */
+	if (agp_bridge.size_type == LVL2_APER_SIZE) {
+		return -EINVAL;
+	}
+
+	table = NULL;
+	i = agp_bridge.aperture_size_idx;
+	temp = agp_bridge.current_size;
+	size = page_order = num_entries = 0;
+
+	if (agp_bridge.size_type != FIXED_APER_SIZE) {
+		do {
+			switch (agp_bridge.size_type) {
+			case U8_APER_SIZE:
+				size = A_SIZE_8(temp)->size;
+				page_order =
+				    A_SIZE_8(temp)->page_order;
+				num_entries =
+				    A_SIZE_8(temp)->num_entries;
+				break;
+			case U16_APER_SIZE:
+				size = A_SIZE_16(temp)->size;
+				page_order = A_SIZE_16(temp)->page_order;
+				num_entries = A_SIZE_16(temp)->num_entries;
+				break;
+			case U32_APER_SIZE:
+				size = A_SIZE_32(temp)->size;
+				page_order = A_SIZE_32(temp)->page_order;
+				num_entries = A_SIZE_32(temp)->num_entries;
+				break;
+				/* This case will never really happen. */
+			case FIXED_APER_SIZE:
+			case LVL2_APER_SIZE:
+			default:
+				size = page_order = num_entries = 0;
+				break;
+			}
+
+			table = (char *) __get_free_pages(GFP_KERNEL,
+							  page_order);
+
+			if (table == NULL) {
+				i++;
+				switch (agp_bridge.size_type) {
+				case U8_APER_SIZE:
+					agp_bridge.current_size = A_IDX8();
+					break;
+				case U16_APER_SIZE:
+					agp_bridge.current_size = A_IDX16();
+					break;
+				case U32_APER_SIZE:
+					agp_bridge.current_size = A_IDX32();
+					break;
+					/* This case will never really 
+					 * happen. 
+					 */
+				case FIXED_APER_SIZE:
+				case LVL2_APER_SIZE:
+				default:
+					agp_bridge.current_size =
+					    agp_bridge.current_size;
+					break;
+				}
+			} else {
+				agp_bridge.aperture_size_idx = i;
+			}
+		} while ((table == NULL) &&
+			 (i < agp_bridge.num_aperture_sizes));
+	} else {
+		size = ((aper_size_info_fixed *) temp)->size;
+		page_order = ((aper_size_info_fixed *) temp)->page_order;
+		num_entries = ((aper_size_info_fixed *) temp)->num_entries;
+		table = (char *) __get_free_pages(GFP_KERNEL, page_order);
+	}
+
+	if (table == NULL) {
+		return -ENOMEM;
+	}
+	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+	for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) {
+		set_bit(PG_reserved, &mem_map[i].flags);
+	}
+
+   	agp_bridge.gatt_table = (unsigned long *) table;
+	agp_bridge.gatt_table_real = (unsigned long *) table;
+	CACHE_FLUSH();
+
+	if (agp_bridge.gatt_table == NULL) {
+		for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) {
+			clear_bit(PG_reserved, &mem_map[i].flags);
+		}
+
+		free_pages((unsigned long) table, page_order);
+
+		return -ENOMEM;
+	}
+	agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real);
+
+	for (i = 0; i < num_entries; i++) {
+		agp_bridge.gatt_table[i] =
+		    (unsigned long) agp_bridge.scratch_page;
+	}
+
+	return 0;
+}
+
+static int agp_generic_free_gatt_table(void)
+{
+	int i;
+	int page_order;
+	char *table, *table_end;
+	void *temp;
+
+	temp = agp_bridge.current_size;
+
+	switch (agp_bridge.size_type) {
+	case U8_APER_SIZE:
+		page_order = A_SIZE_8(temp)->page_order;
+		break;
+	case U16_APER_SIZE:
+		page_order = A_SIZE_16(temp)->page_order;
+		break;
+	case U32_APER_SIZE:
+		page_order = A_SIZE_32(temp)->page_order;
+		break;
+	case FIXED_APER_SIZE:
+		page_order = A_SIZE_FIX(temp)->page_order;
+		break;
+	case LVL2_APER_SIZE:
+		/* The generic routines can't deal with 2 level gatt's */
+		return -EINVAL;
+		break;
+	default:
+		page_order = 0;
+		break;
+	}
+
+	/* Do not worry about freeing memory, because if this is
+	 * called, then all agp memory is deallocated and removed
+	 * from the table.
+	 */
+
+	table = (char *) agp_bridge.gatt_table_real;
+	table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+	for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) {
+		clear_bit(PG_reserved, &mem_map[i].flags);
+	}
+
+	free_pages((unsigned long) agp_bridge.gatt_table_real, page_order);
+	return 0;
+}
+
+static int agp_generic_insert_memory(agp_memory * mem,
+				     off_t pg_start, int type)
+{
+	int i, j, num_entries;
+	void *temp;
+
+	temp = agp_bridge.current_size;
+
+	switch (agp_bridge.size_type) {
+	case U8_APER_SIZE:
+		num_entries = A_SIZE_8(temp)->num_entries;
+		break;
+	case U16_APER_SIZE:
+		num_entries = A_SIZE_16(temp)->num_entries;
+		break;
+	case U32_APER_SIZE:
+		num_entries = A_SIZE_32(temp)->num_entries;
+		break;
+	case FIXED_APER_SIZE:
+		num_entries = A_SIZE_FIX(temp)->num_entries;
+		break;
+	case LVL2_APER_SIZE:
+		/* The generic routines can't deal with 2 level gatt's */
+		return -EINVAL;
+		break;
+	default:
+		num_entries = 0;
+		break;
+	}
+
+	if (type != 0 || mem->type != 0) {
+		/* The generic routines know nothing of memory types */
+		return -EINVAL;
+	}
+	if ((pg_start + mem->page_count) > num_entries) {
+		return -EINVAL;
+	}
+	j = pg_start;
+
+	while (j < (pg_start + mem->page_count)) {
+		if (!PGE_EMPTY(agp_bridge.gatt_table[j])) {
+			return -EBUSY;
+		}
+		j++;
+	}
+
+	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+		agp_bridge.gatt_table[j] = mem->memory[i];
+	}
+	/* Do cache flush all the time since the table is not 
+	 * mapped uncached */
+   	CACHE_FLUSH();
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static int agp_generic_remove_memory(agp_memory * mem, off_t pg_start,
+				     int type)
+{
+	int i;
+
+	if (type != 0 || mem->type != 0) {
+		/* The generic routines know nothing of memory types */
+		return -EINVAL;
+	}
+	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+		agp_bridge.gatt_table[i] =
+		    (unsigned long) agp_bridge.scratch_page;
+	}
+
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static agp_memory *agp_generic_alloc_by_type(size_t page_count, int type)
+{
+	return NULL;
+}
+
+static void agp_generic_free_by_type(agp_memory * curr)
+{
+	if (curr->memory != NULL) {
+		vfree(curr->memory);
+	}
+	agp_free_key(curr->key);
+	kfree(curr);
+}
+
+void agp_enable(u32 mode)
+{
+	agp_bridge.agp_enable(mode);
+}
+
+/* End - Generic Agp routines */
+
+#ifdef CONFIG_AGP_I810
+
+static aper_size_info_fixed intel_i810_sizes[] =
+{
+	{64, 16384, 4},
+     /* The 32M mode still requires a 64k gatt */
+	{32, 8192, 4}
+};
+
+#define AGP_DCACHE_MEMORY 1
+#define AGP_PHYS_MEMORY   2
+
+static gatt_mask intel_i810_masks[] =
+{
+	{I810_PTE_VALID, 0},
+	{(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY},
+	{I810_PTE_VALID, 0}
+};
+
+static struct _intel_i810_private {
+	struct pci_dev *i810_dev;	/* device one */
+	volatile u8 *registers;
+	int num_dcache_entries;
+} intel_i810_private;
+
+static int intel_i810_fetch_size(void)
+{
+	u32 smram_miscc;
+	aper_size_info_fixed *values;
+
+	pci_read_config_dword(agp_bridge.dev, I810_SMRAM_MISCC, &smram_miscc);
+	values = A_SIZE_FIX(agp_bridge.aperture_sizes);
+
+	if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) {
+		printk(KERN_WARNING PFX "i810 is disabled\n");
+		return 0;
+	}
+	if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) {
+		agp_bridge.previous_size =
+		    agp_bridge.current_size = (void *) (values + 1);
+		agp_bridge.aperture_size_idx = 1;
+		return values[1].size;
+	} else {
+		agp_bridge.previous_size =
+		    agp_bridge.current_size = (void *) (values);
+		agp_bridge.aperture_size_idx = 0;
+		return values[0].size;
+	}
+
+	return 0;
+}
+
+static int intel_i810_configure(void)
+{
+	aper_size_info_fixed *current_size;
+	u32 temp;
+	int i;
+
+	current_size = A_SIZE_FIX(agp_bridge.current_size);
+
+	pci_read_config_dword(intel_i810_private.i810_dev, I810_MMADDR, &temp);
+	temp &= 0xfff80000;
+
+	intel_i810_private.registers =
+	    (volatile u8 *) ioremap(temp, 128 * 4096);
+
+	if ((INREG32(intel_i810_private.registers, I810_DRAM_CTL)
+	     & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) {
+		/* This will need to be dynamically assigned */
+		printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n");
+		intel_i810_private.num_dcache_entries = 1024;
+	}
+	pci_read_config_dword(intel_i810_private.i810_dev, I810_GMADDR, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+	OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL,
+		 agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED);
+	CACHE_FLUSH();
+
+	if (agp_bridge.needs_scratch_page == TRUE) {
+		for (i = 0; i < current_size->num_entries; i++) {
+			OUTREG32(intel_i810_private.registers,
+				 I810_PTE_BASE + (i * 4),
+				 agp_bridge.scratch_page);
+		}
+	}
+	return 0;
+}
+
+static void intel_i810_cleanup(void)
+{
+	OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, 0);
+	iounmap((void *) intel_i810_private.registers);
+}
+
+static void intel_i810_tlbflush(agp_memory * mem)
+{
+	return;
+}
+
+static void intel_i810_agp_enable(u32 mode)
+{
+	return;
+}
+
+static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start,
+				     int type)
+{
+	int i, j, num_entries;
+	void *temp;
+
+	temp = agp_bridge.current_size;
+	num_entries = A_SIZE_FIX(temp)->num_entries;
+
+	if ((pg_start + mem->page_count) > num_entries) {
+		return -EINVAL;
+	}
+	for (j = pg_start; j < (pg_start + mem->page_count); j++) {
+		if (!PGE_EMPTY(agp_bridge.gatt_table[j])) {
+			return -EBUSY;
+		}
+	}
+
+	if (type != 0 || mem->type != 0) {
+		if ((type == AGP_DCACHE_MEMORY) &&
+		    (mem->type == AGP_DCACHE_MEMORY)) {
+			/* special insert */
+			CACHE_FLUSH();
+			for (i = pg_start;
+			     i < (pg_start + mem->page_count); i++) {
+				OUTREG32(intel_i810_private.registers,
+					 I810_PTE_BASE + (i * 4),
+					 (i * 4096) | I810_PTE_LOCAL |
+					 I810_PTE_VALID);
+			}
+			CACHE_FLUSH();
+			agp_bridge.tlb_flush(mem);
+			return 0;
+		}
+	        if((type == AGP_PHYS_MEMORY) &&
+		   (mem->type == AGP_PHYS_MEMORY)) {
+		   goto insert;
+		}
+		return -EINVAL;
+	}
+
+insert:
+   	CACHE_FLUSH();
+	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+		OUTREG32(intel_i810_private.registers,
+			 I810_PTE_BASE + (j * 4), mem->memory[i]);
+	}
+	CACHE_FLUSH();
+
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static int intel_i810_remove_entries(agp_memory * mem, off_t pg_start,
+				     int type)
+{
+	int i;
+
+   	CACHE_FLUSH();
+	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+		OUTREG32(intel_i810_private.registers,
+			 I810_PTE_BASE + (i * 4),
+			 agp_bridge.scratch_page);
+	}
+   	CACHE_FLUSH();
+
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type)
+{
+	agp_memory *new;
+
+	if (type == AGP_DCACHE_MEMORY) {
+		if (pg_count != intel_i810_private.num_dcache_entries) {
+			return NULL;
+		}
+		new = agp_create_memory(1);
+
+		if (new == NULL) {
+			return NULL;
+		}
+		new->type = AGP_DCACHE_MEMORY;
+		new->page_count = pg_count;
+		new->num_scratch_pages = 0;
+		vfree(new->memory);
+	   	MOD_INC_USE_COUNT;
+		return new;
+	}
+	if(type == AGP_PHYS_MEMORY) {
+		/* The I810 requires a physical address to program
+		 * it's mouse pointer into hardware.  However the
+		 * Xserver still writes to it through the agp
+		 * aperture
+		 */
+	   	if (pg_count != 1) {
+		   	return NULL;
+		}
+	   	new = agp_create_memory(1);
+
+		if (new == NULL) {
+			return NULL;
+		}
+	   	MOD_INC_USE_COUNT;
+		new->memory[0] = agp_alloc_page();
+
+		if (new->memory[0] == 0) {
+			/* Free this structure */
+			agp_free_memory(new);
+			return NULL;
+		}
+		new->memory[0] =
+		    agp_bridge.mask_memory(
+				   virt_to_phys((void *) new->memory[0]),
+						  type);
+		new->page_count = 1;
+	   	new->num_scratch_pages = 1;
+	   	new->type = AGP_PHYS_MEMORY;
+	        new->physical = virt_to_phys((void *) new->memory[0]);
+	   	return new;
+	}
+   
+	return NULL;
+}
+
+static void intel_i810_free_by_type(agp_memory * curr)
+{
+	agp_free_key(curr->key);
+   	if(curr->type == AGP_PHYS_MEMORY) {
+	   	agp_destroy_page((unsigned long)
+				 phys_to_virt(curr->memory[0]));
+		vfree(curr->memory);
+	}
+	kfree(curr);
+   	MOD_DEC_USE_COUNT;
+}
+
+static unsigned long intel_i810_mask_memory(unsigned long addr, int type)
+{
+	/* Type checking must be done elsewhere */
+	return addr | agp_bridge.masks[type].mask;
+}
+
+static int __init intel_i810_setup(struct pci_dev *i810_dev)
+{
+	intel_i810_private.i810_dev = i810_dev;
+
+	agp_bridge.masks = intel_i810_masks;
+	agp_bridge.num_of_masks = 2;
+	agp_bridge.aperture_sizes = (void *) intel_i810_sizes;
+	agp_bridge.size_type = FIXED_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 2;
+	agp_bridge.dev_private_data = (void *) &intel_i810_private;
+	agp_bridge.needs_scratch_page = TRUE;
+	agp_bridge.configure = intel_i810_configure;
+	agp_bridge.fetch_size = intel_i810_fetch_size;
+	agp_bridge.cleanup = intel_i810_cleanup;
+	agp_bridge.tlb_flush = intel_i810_tlbflush;
+	agp_bridge.mask_memory = intel_i810_mask_memory;
+	agp_bridge.agp_enable = intel_i810_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = intel_i810_insert_entries;
+	agp_bridge.remove_memory = intel_i810_remove_entries;
+	agp_bridge.alloc_by_type = intel_i810_alloc_by_type;
+	agp_bridge.free_by_type = intel_i810_free_by_type;
+
+	return 0;
+}
+
+#endif /* CONFIG_AGP_I810 */
+
+#ifdef CONFIG_AGP_INTEL
+
+static int intel_fetch_size(void)
+{
+	int i;
+	u16 temp;
+	aper_size_info_16 *values;
+
+	pci_read_config_word(agp_bridge.dev, INTEL_APSIZE, &temp);
+	values = A_SIZE_16(agp_bridge.aperture_sizes);
+
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if (temp == values[i].size_value) {
+			agp_bridge.previous_size =
+			    agp_bridge.current_size = (void *) (values + i);
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+static void intel_tlbflush(agp_memory * mem)
+{
+	pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2200);
+	pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280);
+}
+
+static void intel_cleanup(void)
+{
+	u16 temp;
+	aper_size_info_16 *previous_size;
+
+	previous_size = A_SIZE_16(agp_bridge.previous_size);
+	pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp);
+	pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9));
+	pci_write_config_word(agp_bridge.dev, INTEL_APSIZE,
+			      previous_size->size_value);
+}
+
+static int intel_configure(void)
+{
+	u32 temp;
+	u16 temp2;
+	aper_size_info_16 *current_size;
+
+	current_size = A_SIZE_16(agp_bridge.current_size);
+
+	/* aperture size */
+	pci_write_config_word(agp_bridge.dev, INTEL_APSIZE,
+			      current_size->size_value);
+
+	/* address to map to */
+	pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+	/* attbase - aperture base */
+	pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE,
+			       agp_bridge.gatt_bus_addr);
+
+	/* agpctrl */
+	pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280);
+
+	/* paccfg/nbxcfg */
+	pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2);
+	pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG,
+			      (temp2 & ~(1 << 10)) | (1 << 9));
+	/* clear any possible error conditions */
+	pci_write_config_byte(agp_bridge.dev, INTEL_ERRSTS + 1, 7);
+	return 0;
+}
+
+static int intel_840_configure(void)
+{
+	u32 temp;
+	u16 temp2;
+	aper_size_info_16 *current_size;
+
+	current_size = A_SIZE_16(agp_bridge.current_size);
+
+	/* aperture size */
+	pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE,
+			      (char)current_size->size_value); 
+
+	/* address to map to */
+	pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+	/* attbase - aperture base */
+	pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE,
+			       agp_bridge.gatt_bus_addr); 
+
+	/* agpctrl */
+	pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); 
+
+	/* mcgcfg */
+	pci_read_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, &temp2);
+	pci_write_config_word(agp_bridge.dev, INTEL_I840_MCHCFG,
+			      temp2 | (1 << 9));
+	/* clear any possible error conditions */
+	pci_write_config_word(agp_bridge.dev, INTEL_I840_ERRSTS, 0xc000); 
+	return 0;
+}
+
+static unsigned long intel_mask_memory(unsigned long addr, int type)
+{
+	/* Memory type is ignored */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+
+/* Setup function */
+static gatt_mask intel_generic_masks[] =
+{
+	{0x00000017, 0}
+};
+
+static aper_size_info_16 intel_generic_sizes[7] =
+{
+	{256, 65536, 6, 0},
+	{128, 32768, 5, 32},
+	{64, 16384, 4, 48},
+	{32, 8192, 3, 56},
+	{16, 4096, 2, 60},
+	{8, 2048, 1, 62},
+	{4, 1024, 0, 63}
+};
+
+static int __init intel_generic_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = intel_generic_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) intel_generic_sizes;
+	agp_bridge.size_type = U16_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = intel_configure;
+	agp_bridge.fetch_size = intel_fetch_size;
+	agp_bridge.cleanup = intel_cleanup;
+	agp_bridge.tlb_flush = intel_tlbflush;
+	agp_bridge.mask_memory = intel_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = agp_generic_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+static int __init intel_840_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = intel_generic_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) intel_generic_sizes;
+	agp_bridge.size_type = U16_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = intel_840_configure;
+	agp_bridge.fetch_size = intel_fetch_size;
+	agp_bridge.cleanup = intel_cleanup;
+	agp_bridge.tlb_flush = intel_tlbflush;
+	agp_bridge.mask_memory = intel_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = agp_generic_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_INTEL */
+
+#ifdef CONFIG_AGP_VIA
+
+static int via_fetch_size(void)
+{
+	int i;
+	u8 temp;
+	aper_size_info_8 *values;
+
+	values = A_SIZE_8(agp_bridge.aperture_sizes);
+	pci_read_config_byte(agp_bridge.dev, VIA_APSIZE, &temp);
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if (temp == values[i].size_value) {
+			agp_bridge.previous_size =
+			    agp_bridge.current_size = (void *) (values + i);
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+static int via_configure(void)
+{
+	u32 temp;
+	aper_size_info_8 *current_size;
+
+	current_size = A_SIZE_8(agp_bridge.current_size);
+	/* aperture size */
+	pci_write_config_byte(agp_bridge.dev, VIA_APSIZE,
+			      current_size->size_value);
+	/* address to map too */
+	pci_read_config_dword(agp_bridge.dev, VIA_APBASE, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+	/* GART control register */
+	pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f);
+
+	/* attbase - aperture GATT base */
+	pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE,
+			    (agp_bridge.gatt_bus_addr & 0xfffff000) | 3);
+	return 0;
+}
+
+static void via_cleanup(void)
+{
+	aper_size_info_8 *previous_size;
+
+	previous_size = A_SIZE_8(agp_bridge.previous_size);
+	pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE, 0);
+	pci_write_config_byte(agp_bridge.dev, VIA_APSIZE,
+			      previous_size->size_value);
+}
+
+static void via_tlbflush(agp_memory * mem)
+{
+	pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000008f);
+	pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f);
+}
+
+static unsigned long via_mask_memory(unsigned long addr, int type)
+{
+	/* Memory type is ignored */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+static aper_size_info_8 via_generic_sizes[7] =
+{
+	{256, 65536, 6, 0},
+	{128, 32768, 5, 128},
+	{64, 16384, 4, 192},
+	{32, 8192, 3, 224},
+	{16, 4096, 2, 240},
+	{8, 2048, 1, 248},
+	{4, 1024, 0, 252}
+};
+
+static gatt_mask via_generic_masks[] =
+{
+	{0x00000000, 0}
+};
+
+static int __init via_generic_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = via_generic_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) via_generic_sizes;
+	agp_bridge.size_type = U8_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = via_configure;
+	agp_bridge.fetch_size = via_fetch_size;
+	agp_bridge.cleanup = via_cleanup;
+	agp_bridge.tlb_flush = via_tlbflush;
+	agp_bridge.mask_memory = via_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = agp_generic_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_VIA */
+
+#ifdef CONFIG_AGP_SIS
+
+static int sis_fetch_size(void)
+{
+	u8 temp_size;
+	int i;
+	aper_size_info_8 *values;
+
+	pci_read_config_byte(agp_bridge.dev, SIS_APSIZE, &temp_size);
+	values = A_SIZE_8(agp_bridge.aperture_sizes);
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if ((temp_size == values[i].size_value) ||
+		    ((temp_size & ~(0x03)) ==
+		     (values[i].size_value & ~(0x03)))) {
+			agp_bridge.previous_size =
+			    agp_bridge.current_size = (void *) (values + i);
+
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+
+static void sis_tlbflush(agp_memory * mem)
+{
+	pci_write_config_byte(agp_bridge.dev, SIS_TLBFLUSH, 0x02);
+}
+
+static int sis_configure(void)
+{
+	u32 temp;
+	aper_size_info_8 *current_size;
+
+	current_size = A_SIZE_8(agp_bridge.current_size);
+	pci_write_config_byte(agp_bridge.dev, SIS_TLBCNTRL, 0x05);
+	pci_read_config_dword(agp_bridge.dev, SIS_APBASE, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+	pci_write_config_dword(agp_bridge.dev, SIS_ATTBASE,
+			       agp_bridge.gatt_bus_addr);
+	pci_write_config_byte(agp_bridge.dev, SIS_APSIZE,
+			      current_size->size_value);
+	return 0;
+}
+
+static void sis_cleanup(void)
+{
+	aper_size_info_8 *previous_size;
+
+	previous_size = A_SIZE_8(agp_bridge.previous_size);
+	pci_write_config_byte(agp_bridge.dev, SIS_APSIZE,
+			      (previous_size->size_value & ~(0x03)));
+}
+
+static unsigned long sis_mask_memory(unsigned long addr, int type)
+{
+	/* Memory type is ignored */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+static aper_size_info_8 sis_generic_sizes[7] =
+{
+	{256, 65536, 6, 99},
+	{128, 32768, 5, 83},
+	{64, 16384, 4, 67},
+	{32, 8192, 3, 51},
+	{16, 4096, 2, 35},
+	{8, 2048, 1, 19},
+	{4, 1024, 0, 3}
+};
+
+static gatt_mask sis_generic_masks[] =
+{
+	{0x00000000, 0}
+};
+
+static int __init sis_generic_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = sis_generic_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) sis_generic_sizes;
+	agp_bridge.size_type = U8_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = sis_configure;
+	agp_bridge.fetch_size = sis_fetch_size;
+	agp_bridge.cleanup = sis_cleanup;
+	agp_bridge.tlb_flush = sis_tlbflush;
+	agp_bridge.mask_memory = sis_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = agp_generic_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+}
+
+#endif /* CONFIG_AGP_SIS */
+
+#ifdef CONFIG_AGP_AMD
+
+typedef struct _amd_page_map {
+	unsigned long *real;
+	unsigned long *remapped;
+} amd_page_map;
+
+static struct _amd_irongate_private {
+	volatile u8 *registers;
+	amd_page_map **gatt_pages;
+	int num_tables;
+} amd_irongate_private;
+
+static int amd_create_page_map(amd_page_map *page_map)
+{
+	int i;
+
+	page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
+	if (page_map->real == NULL) {
+		return -ENOMEM;
+	}
+	set_bit(PG_reserved, &mem_map[MAP_NR(page_map->real)].flags);
+	page_map->remapped = page_map->real;
+
+	for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
+		page_map->remapped[i] = agp_bridge.scratch_page;
+	}
+	CACHE_FLUSH();
+
+	return 0;
+}
+
+static void amd_free_page_map(amd_page_map *page_map)
+{
+	clear_bit(PG_reserved, 
+		  &mem_map[MAP_NR(page_map->real)].flags);
+	free_page((unsigned long) page_map->real);
+}
+
+static void amd_free_gatt_pages(void)
+{
+	int i;
+	amd_page_map **tables;
+	amd_page_map *entry;
+
+	tables = amd_irongate_private.gatt_pages;
+	for(i = 0; i < amd_irongate_private.num_tables; i++) {
+		entry = tables[i];
+		if (entry != NULL) {
+			if (entry->real != NULL) {
+				amd_free_page_map(entry);
+			}
+			kfree(entry);
+		}
+	}
+	kfree(tables);
+}
+
+static int amd_create_gatt_pages(int nr_tables)
+{
+	amd_page_map **tables;
+	amd_page_map *entry;
+	int retval = 0;
+	int i;
+
+	tables = kmalloc((nr_tables + 1) * sizeof(amd_page_map *), 
+			 GFP_KERNEL);
+	if (tables == NULL) {
+		return -ENOMEM;
+	}
+	memset(tables, 0, sizeof(amd_page_map *) * (nr_tables + 1));
+	for (i = 0; i < nr_tables; i++) {
+		entry = kmalloc(sizeof(amd_page_map), GFP_KERNEL);
+		if (entry == NULL) {
+			retval = -ENOMEM;
+			break;
+		}
+		memset(entry, 0, sizeof(amd_page_map));
+		tables[i] = entry;
+		retval = amd_create_page_map(entry);
+		if (retval != 0) break;
+	}
+	amd_irongate_private.num_tables = nr_tables;
+	amd_irongate_private.gatt_pages = tables;
+
+	if (retval != 0) amd_free_gatt_pages();
+
+	return retval;
+}
+
+/* Since we don't need contigious memory we just try
+ * to get the gatt table once
+ */
+
+#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
+#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \
+	GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr))
+#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) 
+#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\
+	GET_PAGE_DIR_IDX(addr)]->remapped)
+
+static int amd_create_gatt_table(void)
+{
+	aper_size_info_lvl2 *value;
+	amd_page_map page_dir;
+	unsigned long addr;
+	int retval;
+	u32 temp;
+	int i;
+
+	value = A_SIZE_LVL2(agp_bridge.current_size);
+	retval = amd_create_page_map(&page_dir);
+	if (retval != 0) {
+		return retval;
+	}
+
+	retval = amd_create_gatt_pages(value->num_entries / 1024);
+	if (retval != 0) {
+		amd_free_page_map(&page_dir);
+		return retval;
+	}
+
+	agp_bridge.gatt_table_real = page_dir.real;
+	agp_bridge.gatt_table = page_dir.remapped;
+	agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real);
+
+	/* Get the address for the gart region.
+	 * This is a bus address even on the alpha, b/c its
+	 * used to program the agp master not the cpu
+	 */
+
+	pci_read_config_dword(agp_bridge.dev, AMD_APBASE, &temp);
+	addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+	agp_bridge.gart_bus_addr = addr;
+
+	/* Calculate the agp offset */
+	for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) {
+		page_dir.remapped[GET_PAGE_DIR_OFF(addr)] =
+			virt_to_bus(amd_irongate_private.gatt_pages[i]->real);
+		page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001;
+	}
+
+	return 0;
+}
+
+static int amd_free_gatt_table(void)
+{
+	amd_page_map page_dir;
+   
+	page_dir.real = agp_bridge.gatt_table_real;
+	page_dir.remapped = agp_bridge.gatt_table;
+
+	amd_free_gatt_pages();
+	amd_free_page_map(&page_dir);
+	return 0;
+}
+
+static int amd_irongate_fetch_size(void)
+{
+	int i;
+	u32 temp;
+	aper_size_info_lvl2 *values;
+
+	pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp);
+	temp = (temp & 0x0000000e);
+	values = A_SIZE_LVL2(agp_bridge.aperture_sizes);
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if (temp == values[i].size_value) {
+			agp_bridge.previous_size =
+			    agp_bridge.current_size = (void *) (values + i);
+
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+static int amd_irongate_configure(void)
+{
+	aper_size_info_lvl2 *current_size;
+	u32 temp;
+	u16 enable_reg;
+
+	current_size = A_SIZE_LVL2(agp_bridge.current_size);
+
+	/* Get the memory mapped registers */
+	pci_read_config_dword(agp_bridge.dev, AMD_MMBASE, &temp);
+	temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+	amd_irongate_private.registers = (volatile u8 *) ioremap(temp, 4096);
+
+	/* Write out the address of the gatt table */
+	OUTREG32(amd_irongate_private.registers, AMD_ATTBASE,
+		 agp_bridge.gatt_bus_addr);
+
+	/* Write the Sync register */
+	pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL, 0x80);
+   
+   	/* Set indexing mode */
+   	pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL2, 0x00);
+
+	/* Write the enable register */
+	enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE);
+	enable_reg = (enable_reg | 0x0004);
+	OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg);
+
+	/* Write out the size register */
+	pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp);
+	temp = (((temp & ~(0x0000000e)) | current_size->size_value)
+		| 0x00000001);
+	pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp);
+
+	/* Flush the tlb */
+	OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001);
+
+	return 0;
+}
+
+static void amd_irongate_cleanup(void)
+{
+	aper_size_info_lvl2 *previous_size;
+	u32 temp;
+	u16 enable_reg;
+
+	previous_size = A_SIZE_LVL2(agp_bridge.previous_size);
+
+	enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE);
+	enable_reg = (enable_reg & ~(0x0004));
+	OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg);
+
+	/* Write back the previous size and disable gart translation */
+	pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp);
+	temp = ((temp & ~(0x0000000f)) | previous_size->size_value);
+	pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp);
+	iounmap((void *) amd_irongate_private.registers);
+}
+
+/*
+ * This routine could be implemented by taking the addresses
+ * written to the GATT, and flushing them individually.  However
+ * currently it just flushes the whole table.  Which is probably
+ * more efficent, since agp_memory blocks can be a large number of
+ * entries.
+ */
+
+static void amd_irongate_tlbflush(agp_memory * temp)
+{
+	OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001);
+}
+
+static unsigned long amd_irongate_mask_memory(unsigned long addr, int type)
+{
+	/* Only type 0 is supported by the irongate */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+static int amd_insert_memory(agp_memory * mem,
+			     off_t pg_start, int type)
+{
+	int i, j, num_entries;
+	unsigned long *cur_gatt;
+	unsigned long addr;
+
+	num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries;
+
+	if (type != 0 || mem->type != 0) {
+		return -EINVAL;
+	}
+	if ((pg_start + mem->page_count) > num_entries) {
+		return -EINVAL;
+	}
+
+	j = pg_start;
+	while (j < (pg_start + mem->page_count)) {
+		addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr;
+		cur_gatt = GET_GATT(addr);
+		if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) {
+			return -EBUSY;
+		}
+		j++;
+	}
+
+	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+		addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr;
+		cur_gatt = GET_GATT(addr);
+		cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i];
+	}
+
+	CACHE_FLUSH();
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static int amd_remove_memory(agp_memory * mem, off_t pg_start,
+			     int type)
+{
+	int i;
+	unsigned long *cur_gatt;
+	unsigned long addr;
+
+	if (type != 0 || mem->type != 0) {
+		return -EINVAL;
+	}
+	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+		addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr;
+		cur_gatt = GET_GATT(addr);
+		cur_gatt[GET_GATT_OFF(addr)] = 
+			(unsigned long) agp_bridge.scratch_page;
+	}
+
+	CACHE_FLUSH();
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static aper_size_info_lvl2 amd_irongate_sizes[7] =
+{
+	{2048, 524288, 0x0000000c},
+	{1024, 262144, 0x0000000a},
+	{512, 131072, 0x00000008},
+	{256, 65536, 0x00000006},
+	{128, 32768, 0x00000004},
+	{64, 16384, 0x00000002},
+	{32, 8192, 0x00000000}
+};
+
+static gatt_mask amd_irongate_masks[] =
+{
+	{0x00000001, 0}
+};
+
+static int __init amd_irongate_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = amd_irongate_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) amd_irongate_sizes;
+	agp_bridge.size_type = LVL2_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = (void *) &amd_irongate_private;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = amd_irongate_configure;
+	agp_bridge.fetch_size = amd_irongate_fetch_size;
+	agp_bridge.cleanup = amd_irongate_cleanup;
+	agp_bridge.tlb_flush = amd_irongate_tlbflush;
+	agp_bridge.mask_memory = amd_irongate_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = amd_create_gatt_table;
+	agp_bridge.free_gatt_table = amd_free_gatt_table;
+	agp_bridge.insert_memory = amd_insert_memory;
+	agp_bridge.remove_memory = amd_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_AMD */
+
+#ifdef CONFIG_AGP_ALI
+
+static int ali_fetch_size(void)
+{
+	int i;
+	u32 temp;
+	aper_size_info_32 *values;
+
+	pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp);
+	temp &= ~(0xfffffff0);
+	values = A_SIZE_32(agp_bridge.aperture_sizes);
+
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if (temp == values[i].size_value) {
+			agp_bridge.previous_size =
+			    agp_bridge.current_size = (void *) (values + i);
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+static void ali_tlbflush(agp_memory * mem)
+{
+	u32 temp;
+
+	pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp);
+	pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL,
+			       ((temp & 0xffffff00) | 0x00000090));
+	pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL,
+			       ((temp & 0xffffff00) | 0x00000010));
+}
+
+static void ali_cleanup(void)
+{
+	aper_size_info_32 *previous_size;
+	u32 temp;
+
+	previous_size = A_SIZE_32(agp_bridge.previous_size);
+
+	pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp);
+	pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL,
+			       ((temp & 0xffffff00) | 0x00000090));
+	pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE,
+			       previous_size->size_value);
+}
+
+static int ali_configure(void)
+{
+	u32 temp;
+	aper_size_info_32 *current_size;
+
+	current_size = A_SIZE_32(agp_bridge.current_size);
+
+	/* aperture size and gatt addr */
+	pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE,
+		    agp_bridge.gatt_bus_addr | current_size->size_value);
+
+	/* tlb control */
+	pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp);
+	pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL,
+			       ((temp & 0xffffff00) | 0x00000010));
+
+	/* address to map to */
+	pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp);
+	agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+	return 0;
+}
+
+static unsigned long ali_mask_memory(unsigned long addr, int type)
+{
+	/* Memory type is ignored */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+
+/* Setup function */
+static gatt_mask ali_generic_masks[] =
+{
+	{0x00000000, 0}
+};
+
+static aper_size_info_32 ali_generic_sizes[7] =
+{
+	{256, 65536, 6, 10},
+	{128, 32768, 5, 9},
+	{64, 16384, 4, 8},
+	{32, 8192, 3, 7},
+	{16, 4096, 2, 6},
+	{8, 2048, 1, 4},
+	{4, 1024, 0, 3}
+};
+
+static int __init ali_generic_setup (struct pci_dev *pdev)
+{
+	agp_bridge.masks = ali_generic_masks;
+	agp_bridge.num_of_masks = 1;
+	agp_bridge.aperture_sizes = (void *) ali_generic_sizes;
+	agp_bridge.size_type = U32_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 7;
+	agp_bridge.dev_private_data = NULL;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = ali_configure;
+	agp_bridge.fetch_size = ali_fetch_size;
+	agp_bridge.cleanup = ali_cleanup;
+	agp_bridge.tlb_flush = ali_tlbflush;
+	agp_bridge.mask_memory = ali_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = agp_generic_insert_memory;
+	agp_bridge.remove_memory = agp_generic_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+
+	return 0;
+	
+	(void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_ALI */
+
+
+/* per-chipset initialization data.
+ * note -- all chipsets for a single vendor MUST be grouped together
+ */
+static struct {
+	unsigned short device_id; /* first, to make table easier to read */
+	unsigned short vendor_id;
+	enum chipset_type chipset;
+	const char *vendor_name;
+	const char *chipset_name;
+	int (*chipset_setup) (struct pci_dev *pdev);
+} agp_bridge_info[] __initdata = {
+
+#ifdef CONFIG_AGP_ALI
+	{ PCI_DEVICE_ID_AL_M1541_0,
+		PCI_VENDOR_ID_AL,
+		ALI_M1541,
+		"Ali",
+		"M1541",
+		ali_generic_setup },
+	{ 0,
+		PCI_VENDOR_ID_AL,
+		ALI_GENERIC,
+		"Ali",
+		"Generic",
+		ali_generic_setup },
+#endif /* CONFIG_AGP_ALI */
+
+#ifdef CONFIG_AGP_AMD
+	{ PCI_DEVICE_ID_AMD_IRONGATE_0,
+		PCI_VENDOR_ID_AMD,
+		AMD_IRONGATE,
+		"AMD",
+		"Irongate",
+		amd_irongate_setup },
+	{ 0,
+		PCI_VENDOR_ID_AMD,
+		AMD_GENERIC,
+		"AMD",
+		"Generic",
+		amd_irongate_setup },
+#endif /* CONFIG_AGP_AMD */
+
+#ifdef CONFIG_AGP_INTEL
+	{ PCI_DEVICE_ID_INTEL_82443LX_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_LX,
+		"Intel",
+		"440LX",
+		intel_generic_setup },
+	{ PCI_DEVICE_ID_INTEL_82443BX_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_BX,
+		"Intel",
+		"440BX",
+		intel_generic_setup },
+	{ PCI_DEVICE_ID_INTEL_82443GX_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_GX,
+		"Intel",
+		"440GX",
+		intel_generic_setup },
+	{ PCI_DEVICE_ID_INTEL_840_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_I840,
+		"Intel",
+		"i840",
+		intel_840_setup },
+	{ 0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_GENERIC,
+		"Intel",
+		"Generic",
+		intel_generic_setup },
+#endif /* CONFIG_AGP_INTEL */
+
+#ifdef CONFIG_AGP_SIS
+	{ PCI_DEVICE_ID_SI_630,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"630",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_540,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"540",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_620,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"620",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_530,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"530",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_630,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"Generic",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_540,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"Generic",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_620,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"Generic",
+		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_530,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"Generic",
+		sis_generic_setup },
+	{ 0,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"Generic",
+		sis_generic_setup },
+#endif /* CONFIG_AGP_SIS */
+
+#ifdef CONFIG_AGP_VIA
+	{ PCI_DEVICE_ID_VIA_8501_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_MVP4,
+		"Via",
+		"MVP4",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_82C597_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_VP3,
+		"Via",
+		"VP3",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_82C598_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_MVP3,
+		"Via",
+		"MVP3",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_8371_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_KX133,
+		"Via",
+		"Apollo KX133",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_8363_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_KT133,
+		"Via",
+		"Apollo KT133",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_82C691_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_PRO,
+		"Via",
+		"Apollo Pro",
+		via_generic_setup },
+	{ 0,
+		PCI_VENDOR_ID_VIA,
+		VIA_GENERIC,
+		"Via",
+		"Generic",
+		via_generic_setup },
+#endif /* CONFIG_AGP_VIA */
+
+	{ 0, }, /* dummy final entry, always present */
+};
+
+
+/* scan table above for supported devices */
+static int __init agp_lookup_host_bridge (struct pci_dev *pdev)
+{
+	int i;
+	
+	for (i = 0; i < arraysize (agp_bridge_info); i++)
+		if (pdev->vendor == agp_bridge_info[i].vendor_id)
+			break;
+
+	if (i >= arraysize (agp_bridge_info)) {
+		printk (KERN_DEBUG PFX "unsupported bridge\n");
+		return -ENODEV;
+	}
+
+	while ((i < arraysize (agp_bridge_info)) &&
+	       (agp_bridge_info[i].vendor_id == pdev->vendor)) {
+		if (pdev->device == agp_bridge_info[i].device_id) {
+			printk (KERN_INFO PFX "Detected %s %s chipset\n",
+				agp_bridge_info[i].vendor_name,
+				agp_bridge_info[i].chipset_name);
+			agp_bridge.type = agp_bridge_info[i].chipset;
+			return agp_bridge_info[i].chipset_setup (pdev);
+		}
+		
+		i++;
+	}
+
+	i--; /* point to vendor generic entry (device_id == 0) */
+
+	/* try init anyway, if user requests it AND
+	 * there is a 'generic' bridge entry for this vendor */
+	if (agp_try_unsupported && agp_bridge_info[i].device_id == 0) {
+		printk(KERN_WARNING PFX "Trying generic %s routines"
+		       " for device id: %x\n",
+		       agp_bridge_info[i].vendor_name, pdev->device);
+		agp_bridge.type = agp_bridge_info[i].chipset;
+		return agp_bridge_info[i].chipset_setup (pdev);
+	}
+
+	printk(KERN_ERR PFX "Unsupported %s chipset,"
+	       " you might want to try "
+	       "agp_try_unsupported=1.\n",
+	       agp_bridge_info[i].vendor_name);
+	return -ENODEV;
+}
+
+
+/* Supported Device Scanning routine */
+
+static int __init agp_find_supported_device(void)
+{
+	struct pci_dev *dev = NULL;
+	u8 cap_ptr = 0x00;
+	u32 cap_id, scratch;
+
+	if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) == NULL)
+		return -ENODEV;
+
+	agp_bridge.dev = dev;
+
+	/* Need to test for I810 here */
+#ifdef CONFIG_AGP_I810
+	if (dev->vendor == PCI_VENDOR_ID_INTEL) {
+		struct pci_dev *i810_dev;
+
+		switch (dev->device) {
+		case PCI_DEVICE_ID_INTEL_810_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					       PCI_DEVICE_ID_INTEL_810_1,
+						   NULL);
+			if (i810_dev == NULL) {
+				printk(KERN_ERR PFX "Detected an Intel i810,"
+				       " but could not find the secondary"
+				       " device.\n");
+				return -ENODEV;
+			}
+			printk(KERN_INFO PFX "Detected an Intel "
+			       "i810 Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i810_setup (i810_dev);
+
+		case PCI_DEVICE_ID_INTEL_810_DC100_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					 PCI_DEVICE_ID_INTEL_810_DC100_1,
+						   NULL);
+			if (i810_dev == NULL) {
+				printk(KERN_ERR PFX "Detected an Intel i810 "
+				       "DC100, but could not find the "
+				       "secondary device.\n");
+				return -ENODEV;
+			}
+			printk(KERN_INFO PFX "Detected an Intel i810 "
+			       "DC100 Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i810_setup(i810_dev);
+
+		case PCI_DEVICE_ID_INTEL_810_E_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					     PCI_DEVICE_ID_INTEL_810_E_1,
+						   NULL);
+			if (i810_dev == NULL) {
+				printk(KERN_ERR PFX "Detected an Intel i810 E"
+				    ", but could not find the secondary "
+				       "device.\n");
+				return -ENODEV;
+			}
+			printk(KERN_INFO PFX "Detected an Intel i810 E "
+			       "Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i810_setup(i810_dev);
+		 case PCI_DEVICE_ID_INTEL_815_0:
+		   /* The i815 can operate either as an i810 style
+		    * integrated device, or as an AGP4X motherboard.
+		    *
+		    * This only addresses the first mode:
+		    */
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+						   PCI_DEVICE_ID_INTEL_815_1,
+						   NULL);
+			if (i810_dev == NULL) {
+				printk(KERN_ERR PFX "agpgart: Detected an "
+				       "Intel i815, but could not find the"
+				       " secondary device.\n");
+				agp_bridge.type = NOT_SUPPORTED;
+				return -ENODEV;
+			}
+			printk(KERN_INFO PFX "agpgart: Detected an Intel i815 "
+			       "Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i810_setup(i810_dev);
+
+		 default:
+			break;
+		}
+	}
+#endif /* CONFIG_AGP_I810 */
+
+	/* find capndx */
+	pci_read_config_dword(dev, 0x04, &scratch);
+	if (!(scratch & 0x00100000))
+		return -ENODEV;
+
+	pci_read_config_byte(dev, 0x34, &cap_ptr);
+	if (cap_ptr != 0x00) {
+		do {
+			pci_read_config_dword(dev, cap_ptr, &cap_id);
+
+			if ((cap_id & 0xff) != 0x02)
+				cap_ptr = (cap_id >> 8) & 0xff;
+		}
+		while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
+	}
+	if (cap_ptr == 0x00)
+		return -ENODEV;
+	agp_bridge.capndx = cap_ptr;
+
+	/* Fill in the mode register */
+	pci_read_config_dword(agp_bridge.dev,
+			      agp_bridge.capndx + 4,
+			      &agp_bridge.mode);
+
+	/* probe for known chipsets */
+	return agp_lookup_host_bridge (dev);
+}
+
+struct agp_max_table {
+	int mem;
+	int agp;
+};
+
+static struct agp_max_table maxes_table[9] __initdata =
+{
+	{0, 0},
+	{32, 4},
+	{64, 28},
+	{128, 96},
+	{256, 204},
+	{512, 440},
+	{1024, 942},
+	{2048, 1920},
+	{4096, 3932}
+};
+
+static int __init agp_find_max (void)
+{
+	long memory, index, result;
+
+	memory = virt_to_phys(high_memory) >> 20;
+	index = 1;
+
+	while ((memory > maxes_table[index].mem) &&
+	       (index < 8)) {
+		index++;
+	}
+
+	result = maxes_table[index - 1].agp +
+	   ( (memory - maxes_table[index - 1].mem)  *
+	     (maxes_table[index].agp - maxes_table[index - 1].agp)) /
+	   (maxes_table[index].mem - maxes_table[index - 1].mem);
+
+	printk(KERN_INFO PFX "Maximum main memory to use "
+	       "for agp memory: %ldM\n", result);
+	result = result << (20 - PAGE_SHIFT);
+        return result;
+}
+
+#define AGPGART_VERSION_MAJOR 0
+#define AGPGART_VERSION_MINOR 99
+
+static agp_version agp_current_version =
+{
+	AGPGART_VERSION_MAJOR,
+	AGPGART_VERSION_MINOR
+};
+
+static int __init agp_backend_initialize(void)
+{
+	int size_value, rc, got_gatt=0, got_keylist=0;
+
+	memset(&agp_bridge, 0, sizeof(struct agp_bridge_data));
+	agp_bridge.type = NOT_SUPPORTED;
+	agp_bridge.max_memory_agp = agp_find_max();
+	agp_bridge.version = &agp_current_version;
+
+	rc = agp_find_supported_device();
+	if (rc) {
+		/* not KERN_ERR because error msg should have already printed */
+		printk(KERN_DEBUG PFX "no supported devices found.\n");
+		return rc;
+	}
+
+	if (agp_bridge.needs_scratch_page == TRUE) {
+		agp_bridge.scratch_page = agp_alloc_page();
+
+		if (agp_bridge.scratch_page == 0) {
+			printk(KERN_ERR PFX "unable to get memory for "
+			       "scratch page.\n");
+			return -ENOMEM;
+		}
+		agp_bridge.scratch_page =
+		    virt_to_phys((void *) agp_bridge.scratch_page);
+		agp_bridge.scratch_page =
+		    agp_bridge.mask_memory(agp_bridge.scratch_page, 0);
+	}
+
+	size_value = agp_bridge.fetch_size();
+
+	if (size_value == 0) {
+		printk(KERN_ERR PFX "unable to detrimine aperture size.\n");
+		rc = -EINVAL;
+		goto err_out;
+	}
+	if (agp_bridge.create_gatt_table()) {
+		printk(KERN_ERR PFX "unable to get memory for graphics "
+		       "translation table.\n");
+		rc = -ENOMEM;
+		goto err_out;
+	}
+	got_gatt = 1;
+	
+	agp_bridge.key_list = vmalloc(PAGE_SIZE * 4);
+	if (agp_bridge.key_list == NULL) {
+		printk(KERN_ERR PFX "error allocating memory for key lists.\n");
+		rc = -ENOMEM;
+		goto err_out;
+	}
+	got_keylist = 1;
+	
+	/* FIXME vmalloc'd memory not guaranteed contiguous */
+	memset(agp_bridge.key_list, 0, PAGE_SIZE * 4);
+
+	if (agp_bridge.configure()) {
+		printk(KERN_ERR PFX "error configuring host chipset.\n");
+		rc = -EINVAL;
+		goto err_out;
+	}
+
+	printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n",
+	       size_value, agp_bridge.gart_bus_addr);
+
+	return 0;
+
+err_out:
+	if (agp_bridge.needs_scratch_page == TRUE) {
+		agp_bridge.scratch_page &= ~(0x00000fff);
+		agp_destroy_page((unsigned long)
+				 phys_to_virt(agp_bridge.scratch_page));
+	}
+	if (got_gatt)
+		agp_bridge.free_gatt_table();
+	if (got_keylist)
+		vfree(agp_bridge.key_list);
+	return rc;
+}
+
+
+/* cannot be __exit b/c as it could be called from __init code */
+static void agp_backend_cleanup(void)
+{
+	agp_bridge.cleanup();
+	agp_bridge.free_gatt_table();
+	vfree(agp_bridge.key_list);
+
+	if (agp_bridge.needs_scratch_page == TRUE) {
+		agp_bridge.scratch_page &= ~(0x00000fff);
+		agp_destroy_page((unsigned long)
+				 phys_to_virt(agp_bridge.scratch_page));
+	}
+}
+
+extern int agp_frontend_initialize(void);
+extern void agp_frontend_cleanup(void);
+
+__initfunc(int agp_init(void))
+{
+	int ret_val;
+
+	printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Jeff Hartmann\n",
+	       AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR);
+
+	ret_val = agp_backend_initialize();
+	if (ret_val)
+		return ret_val;
+
+	ret_val = agp_frontend_initialize();
+	if (ret_val) {
+		agp_backend_cleanup();
+		return ret_val;
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return agp_init();
+}
+
+void cleanup_module(void)
+{
+   	agp_frontend_cleanup();
+	agp_backend_cleanup();
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart_fe.c linux.18p17/drivers/char/agp/agpgart_fe.c
--- linux.vanilla/drivers/char/agp/agpgart_fe.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/agp/agpgart_fe.c	Tue Sep 26 17:00:25 2000
@@ -0,0 +1,1098 @@
+/*
+ * AGPGART module frontend version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/miscdevice.h>
+#include <linux/agp_backend.h>
+#include "agpgart.h"
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/mman.h>
+
+#include "agp.h"
+
+static struct agp_front_data agp_fe;
+
+static agp_memory *agp_find_mem_by_key(int key)
+{
+	agp_memory *curr;
+
+	if (agp_fe.current_controller == NULL) {
+		return NULL;
+	}
+	curr = agp_fe.current_controller->pool;
+
+	while (curr != NULL) {
+		if (curr->key == key) {
+			return curr;
+		}
+		curr = curr->next;
+	}
+
+	return NULL;
+}
+
+static void agp_remove_from_pool(agp_memory * temp)
+{
+	agp_memory *prev;
+	agp_memory *next;
+
+	/* Check to see if this is even in the memory pool */
+
+	if (agp_find_mem_by_key(temp->key) != NULL) {
+		next = temp->next;
+		prev = temp->prev;
+
+		if (prev != NULL) {
+			prev->next = next;
+			if (next != NULL) {
+				next->prev = prev;
+			}
+		} else {
+			/* This is the first item on the list */
+			if (next != NULL) {
+				next->prev = NULL;
+			}
+			agp_fe.current_controller->pool = next;
+		}
+	}
+}
+
+/*
+ * Routines for managing each client's segment list -
+ * These routines handle adding and removing segments
+ * to each auth'ed client.
+ */
+
+static agp_segment_priv *agp_find_seg_in_client(const agp_client * client,
+						unsigned long offset,
+					    int size, pgprot_t page_prot)
+{
+	agp_segment_priv *seg;
+	int num_segments, pg_start, pg_count, i;
+
+	pg_start = offset / 4096;
+	pg_count = size / 4096;
+	seg = *(client->segments);
+	num_segments = client->num_segments;
+
+	for (i = 0; i < client->num_segments; i++) {
+		if ((seg[i].pg_start == pg_start) &&
+		    (seg[i].pg_count == pg_count) &&
+		    (pgprot_val(seg[i].prot) == pgprot_val(page_prot))) {
+			return seg + i;
+		}
+	}
+
+	return NULL;
+}
+
+static void agp_remove_seg_from_client(agp_client * client)
+{
+	if (client->segments != NULL) {
+		if (*(client->segments) != NULL) {
+			kfree(*(client->segments));
+		}
+		kfree(client->segments);
+	}
+}
+
+static void agp_add_seg_to_client(agp_client * client,
+			       agp_segment_priv ** seg, int num_segments)
+{
+	agp_segment_priv **prev_seg;
+
+	prev_seg = client->segments;
+
+	if (prev_seg != NULL) {
+		agp_remove_seg_from_client(client);
+	}
+	client->num_segments = num_segments;
+	client->segments = seg;
+}
+
+/* Originally taken from linux/mm/mmap.c from the array
+ * protection_map.
+ * The original really should be exported to modules, or 
+ * some routine which does the conversion for you 
+ */
+
+static const pgprot_t my_protect_map[16] =
+{
+	__P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
+	__S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
+};
+
+static pgprot_t agp_convert_mmap_flags(int prot)
+{
+#define _trans(x,bit1,bit2) \
+((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
+
+	unsigned long prot_bits;
+	pgprot_t temp;
+
+	prot_bits = _trans(prot, PROT_READ, VM_READ) |
+	    _trans(prot, PROT_WRITE, VM_WRITE) |
+	    _trans(prot, PROT_EXEC, VM_EXEC);
+
+	prot_bits |= VM_SHARED;
+
+	temp = my_protect_map[prot_bits & 0x0000000f];
+
+	return temp;
+}
+
+static int agp_create_segment(agp_client * client, agp_region * region)
+{
+	agp_segment_priv **ret_seg;
+	agp_segment_priv *seg;
+	agp_segment *user_seg;
+	int i;
+
+	seg = kmalloc((sizeof(agp_segment_priv) * region->seg_count),
+		      GFP_KERNEL);
+	if (seg == NULL) {
+		kfree(region->seg_list);
+		return -ENOMEM;
+	}
+	memset(seg, 0, (sizeof(agp_segment_priv) * region->seg_count));
+	user_seg = region->seg_list;
+
+	for (i = 0; i < region->seg_count; i++) {
+		seg[i].pg_start = user_seg[i].pg_start;
+		seg[i].pg_count = user_seg[i].pg_count;
+		seg[i].prot = agp_convert_mmap_flags(user_seg[i].prot);
+	}
+	ret_seg = kmalloc(sizeof(void *), GFP_KERNEL);
+	if (ret_seg == NULL) {
+		kfree(region->seg_list);
+		kfree(seg);
+		return -ENOMEM;
+	}
+	*ret_seg = seg;
+	kfree(region->seg_list);
+	agp_add_seg_to_client(client, ret_seg, region->seg_count);
+	return 0;
+}
+
+/* End - Routines for managing each client's segment list */
+
+/* This function must only be called when current_controller != NULL */
+static void agp_insert_into_pool(agp_memory * temp)
+{
+	agp_memory *prev;
+
+	prev = agp_fe.current_controller->pool;
+
+	if (prev != NULL) {
+		prev->prev = temp;
+		temp->next = prev;
+	}
+	agp_fe.current_controller->pool = temp;
+}
+
+
+/* File private list routines */
+
+agp_file_private *agp_find_private(pid_t pid)
+{
+	agp_file_private *curr;
+
+	curr = agp_fe.file_priv_list;
+
+	while (curr != NULL) {
+		if (curr->my_pid == pid) {
+			return curr;
+		}
+		curr = curr->next;
+	}
+
+	return NULL;
+}
+
+void agp_insert_file_private(agp_file_private * priv)
+{
+	agp_file_private *prev;
+
+	prev = agp_fe.file_priv_list;
+
+	if (prev != NULL) {
+		prev->prev = priv;
+	}
+	priv->next = prev;
+	agp_fe.file_priv_list = priv;
+}
+
+void agp_remove_file_private(agp_file_private * priv)
+{
+	agp_file_private *next;
+	agp_file_private *prev;
+
+	next = priv->next;
+	prev = priv->prev;
+
+	if (prev != NULL) {
+		prev->next = next;
+
+		if (next != NULL) {
+			next->prev = prev;
+		}
+	} else {
+		if (next != NULL) {
+			next->prev = NULL;
+		}
+		agp_fe.file_priv_list = next;
+	}
+}
+
+/* End - File flag list routines */
+
+/* 
+ * Wrappers for agp_free_memory & agp_allocate_memory 
+ * These make sure that internal lists are kept updated.
+ */
+static void agp_free_memory_wrap(agp_memory * memory)
+{
+	agp_remove_from_pool(memory);
+	agp_free_memory(memory);
+}
+
+static agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type)
+{
+	agp_memory *memory;
+
+	memory = agp_allocate_memory(pg_count, type);
+   	printk(KERN_DEBUG "memory : %p\n", memory);
+	if (memory == NULL) {
+		return NULL;
+	}
+	agp_insert_into_pool(memory);
+	return memory;
+}
+
+/* Routines for managing the list of controllers -
+ * These routines manage the current controller, and the list of
+ * controllers
+ */
+
+static agp_controller *agp_find_controller_by_pid(pid_t id)
+{
+	agp_controller *controller;
+
+	controller = agp_fe.controllers;
+
+	while (controller != NULL) {
+		if (controller->pid == id) {
+			return controller;
+		}
+		controller = controller->next;
+	}
+
+	return NULL;
+}
+
+static agp_controller *agp_create_controller(pid_t id)
+{
+	agp_controller *controller;
+
+	controller = kmalloc(sizeof(agp_controller), GFP_KERNEL);
+
+	if (controller == NULL) {
+		return NULL;
+	}
+	memset(controller, 0, sizeof(agp_controller));
+	controller->pid = id;
+
+	return controller;
+}
+
+static int agp_insert_controller(agp_controller * controller)
+{
+	agp_controller *prev_controller;
+
+	prev_controller = agp_fe.controllers;
+	controller->next = prev_controller;
+
+	if (prev_controller != NULL) {
+		prev_controller->prev = controller;
+	}
+	agp_fe.controllers = controller;
+
+	return 0;
+}
+
+static void agp_remove_all_clients(agp_controller * controller)
+{
+	agp_client *client;
+	agp_client *temp;
+
+	client = controller->clients;
+
+	while (client) {
+		agp_file_private *priv;
+
+		temp = client;
+		agp_remove_seg_from_client(temp);
+		priv = agp_find_private(temp->pid);
+
+		if (priv != NULL) {
+			clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
+			clear_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+		}
+		client = client->next;
+		kfree(temp);
+	}
+}
+
+static void agp_remove_all_memory(agp_controller * controller)
+{
+	agp_memory *memory;
+	agp_memory *temp;
+
+	memory = controller->pool;
+
+	while (memory) {
+		temp = memory;
+		memory = memory->next;
+		agp_free_memory_wrap(temp);
+	}
+}
+
+static int agp_remove_controller(agp_controller * controller)
+{
+	agp_controller *prev_controller;
+	agp_controller *next_controller;
+
+	prev_controller = controller->prev;
+	next_controller = controller->next;
+
+	if (prev_controller != NULL) {
+		prev_controller->next = next_controller;
+		if (next_controller != NULL) {
+			next_controller->prev = prev_controller;
+		}
+	} else {
+		if (next_controller != NULL) {
+			next_controller->prev = NULL;
+		}
+		agp_fe.controllers = next_controller;
+	}
+
+	agp_remove_all_memory(controller);
+	agp_remove_all_clients(controller);
+
+	if (agp_fe.current_controller == controller) {
+		agp_fe.current_controller = NULL;
+		agp_fe.backend_acquired = FALSE;
+		agp_backend_release();
+	}
+	kfree(controller);
+	return 0;
+}
+
+static void agp_controller_make_current(agp_controller * controller)
+{
+	agp_client *clients;
+
+	clients = controller->clients;
+
+	while (clients != NULL) {
+		agp_file_private *priv;
+
+		priv = agp_find_private(clients->pid);
+
+		if (priv != NULL) {
+			set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+			set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+		}
+		clients = clients->next;
+	}
+
+	agp_fe.current_controller = controller;
+}
+
+static void agp_controller_release_current(agp_controller * controller,
+				      agp_file_private * controller_priv)
+{
+	agp_client *clients;
+
+	clear_bit(AGP_FF_IS_VALID, &controller_priv->access_flags);
+	clients = controller->clients;
+
+	while (clients != NULL) {
+		agp_file_private *priv;
+
+		priv = agp_find_private(clients->pid);
+
+		if (priv != NULL) {
+			clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
+		}
+		clients = clients->next;
+	}
+
+	agp_fe.current_controller = NULL;
+	agp_fe.used_by_controller = FALSE;
+	agp_backend_release();
+}
+
+/* 
+ * Routines for managing client lists -
+ * These routines are for managing the list of auth'ed clients.
+ */
+
+static agp_client *agp_find_client_in_controller(agp_controller * controller,
+						 pid_t id)
+{
+	agp_client *client;
+
+	if (controller == NULL) {
+		return NULL;
+	}
+	client = controller->clients;
+
+	while (client != NULL) {
+		if (client->pid == id) {
+			return client;
+		}
+		client = client->next;
+	}
+
+	return NULL;
+}
+
+static agp_controller *agp_find_controller_for_client(pid_t id)
+{
+	agp_controller *controller;
+
+	controller = agp_fe.controllers;
+
+	while (controller != NULL) {
+		if ((agp_find_client_in_controller(controller, id)) != NULL) {
+			return controller;
+		}
+		controller = controller->next;
+	}
+
+	return NULL;
+}
+
+static agp_client *agp_find_client_by_pid(pid_t id)
+{
+	agp_client *temp;
+
+	if (agp_fe.current_controller == NULL) {
+		return NULL;
+	}
+	temp = agp_find_client_in_controller(agp_fe.current_controller, id);
+	return temp;
+}
+
+static void agp_insert_client(agp_client * client)
+{
+	agp_client *prev_client;
+
+	prev_client = agp_fe.current_controller->clients;
+	client->next = prev_client;
+
+	if (prev_client != NULL) {
+		prev_client->prev = client;
+	}
+	agp_fe.current_controller->clients = client;
+	agp_fe.current_controller->num_clients++;
+}
+
+static agp_client *agp_create_client(pid_t id)
+{
+	agp_client *new_client;
+
+	new_client = kmalloc(sizeof(agp_client), GFP_KERNEL);
+
+	if (new_client == NULL) {
+		return NULL;
+	}
+	memset(new_client, 0, sizeof(agp_client));
+	new_client->pid = id;
+	agp_insert_client(new_client);
+	return new_client;
+}
+
+static int agp_remove_client(pid_t id)
+{
+	agp_client *client;
+	agp_client *prev_client;
+	agp_client *next_client;
+	agp_controller *controller;
+
+	controller = agp_find_controller_for_client(id);
+
+	if (controller == NULL) {
+		return -EINVAL;
+	}
+	client = agp_find_client_in_controller(controller, id);
+
+	if (client == NULL) {
+		return -EINVAL;
+	}
+	prev_client = client->prev;
+	next_client = client->next;
+
+	if (prev_client != NULL) {
+		prev_client->next = next_client;
+		if (next_client != NULL) {
+			next_client->prev = prev_client;
+		}
+	} else {
+		if (next_client != NULL) {
+			next_client->prev = NULL;
+		}
+		controller->clients = next_client;
+	}
+
+	controller->num_clients--;
+	agp_remove_seg_from_client(client);
+	kfree(client);
+	return 0;
+}
+
+/* End - Routines for managing client lists */
+
+/* File Operations */
+
+static int agp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	int size;
+	int current_size;
+	unsigned long offset;
+	agp_client *client;
+	agp_file_private *priv = (agp_file_private *) file->private_data;
+	agp_kern_info kerninfo;
+
+	AGP_LOCK();
+
+	if (agp_fe.backend_acquired != TRUE) {
+		AGP_UNLOCK();
+		return -EPERM;
+	}
+	if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags))) {
+		AGP_UNLOCK();
+		return -EPERM;
+	}
+	agp_copy_info(&kerninfo);
+	size = vma->vm_end - vma->vm_start;
+	current_size = kerninfo.aper_size;
+	current_size = current_size * 0x100000;
+	offset = vma->vm_offset;
+
+	if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) {
+		if ((size + offset) > current_size) {
+			AGP_UNLOCK();
+			return -EINVAL;
+		}
+		client = agp_find_client_by_pid(current->pid);
+
+		if (client == NULL) {
+			AGP_UNLOCK();
+			return -EPERM;
+		}
+		if (!agp_find_seg_in_client(client, offset,
+					    size, vma->vm_page_prot)) {
+			AGP_UNLOCK();
+			return -EINVAL;
+		}
+		if (remap_page_range(vma->vm_start,
+				     (kerninfo.aper_base + offset),
+				     size, vma->vm_page_prot)) {
+			AGP_UNLOCK();
+			return -EAGAIN;
+		}
+		AGP_UNLOCK();
+		return 0;
+	}
+	if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
+		if (size != current_size) {
+			AGP_UNLOCK();
+			return -EINVAL;
+		}
+		if (remap_page_range(vma->vm_start, kerninfo.aper_base,
+				     size, vma->vm_page_prot)) {
+			AGP_UNLOCK();
+			return -EAGAIN;
+		}
+		AGP_UNLOCK();
+		return 0;
+	}
+	AGP_UNLOCK();
+	return -EPERM;
+}
+
+static int agp_release(struct inode *inode, struct file *file)
+{
+	agp_file_private *priv = (agp_file_private *) file->private_data;
+
+	AGP_LOCK();
+
+	if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
+		agp_controller *controller;
+
+		controller = agp_find_controller_by_pid(priv->my_pid);
+
+		if (controller != NULL) {
+			if (controller == agp_fe.current_controller) {
+				agp_controller_release_current(controller,
+							       priv);
+			}
+			agp_remove_controller(controller);
+		}
+	}
+	if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) {
+		agp_remove_client(priv->my_pid);
+	}
+	agp_remove_file_private(priv);
+	kfree(priv);
+	MOD_DEC_USE_COUNT;
+	AGP_UNLOCK();
+	return 0;
+}
+
+static int agp_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	agp_file_private *priv;
+	agp_client *client;
+	int rc = -ENXIO;
+
+	AGP_LOCK();
+	MOD_INC_USE_COUNT;
+
+	if (minor != AGPGART_MINOR)
+		goto err_out;
+
+	priv = kmalloc(sizeof(agp_file_private), GFP_KERNEL);
+	if (priv == NULL)
+		goto err_out_nomem;
+
+	memset(priv, 0, sizeof(agp_file_private));
+	set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags);
+	priv->my_pid = current->pid;
+
+	if ((current->uid == 0) || (current->suid == 0)) {
+		/* Root priv, can be controller */
+		set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags);
+	}
+	client = agp_find_client_by_pid(current->pid);
+
+	if (client != NULL) {
+		set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
+		set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+	}
+	file->private_data = (void *) priv;
+	agp_insert_file_private(priv);
+	AGP_UNLOCK();
+	return 0;
+
+err_out_nomem:
+	rc = -ENOMEM;
+err_out:
+	MOD_DEC_USE_COUNT;
+	AGP_UNLOCK();
+	return rc;
+}
+
+
+static long long agp_lseek(struct file *file, long long offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static ssize_t agp_read(struct file *file, char *buf,
+			size_t count, loff_t * ppos)
+{
+	return -EINVAL;
+}
+
+static ssize_t agp_write(struct file *file, const char *buf,
+			 size_t count, loff_t * ppos)
+{
+	return -EINVAL;
+}
+
+static int agpioc_info_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_info userinfo;
+	agp_kern_info kerninfo;
+
+	agp_copy_info(&kerninfo);
+
+	userinfo.version.major = kerninfo.version.major;
+	userinfo.version.minor = kerninfo.version.minor;
+	userinfo.bridge_id = kerninfo.device->vendor |
+	    (kerninfo.device->device << 16);
+	userinfo.agp_mode = kerninfo.mode;
+	userinfo.aper_base = kerninfo.aper_base;
+	userinfo.aper_size = kerninfo.aper_size;
+	userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory;
+	userinfo.pg_used = kerninfo.current_memory;
+
+	if (copy_to_user((void *) arg, &userinfo, sizeof(agp_info))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int agpioc_acquire_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_controller *controller;
+	if (!(test_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags))) {
+		return -EPERM;
+	}
+	if (agp_fe.current_controller != NULL) {
+		return -EBUSY;
+	}
+	if ((agp_backend_acquire()) == 0) {
+		agp_fe.backend_acquired = TRUE;
+	} else {
+		return -EBUSY;
+	}
+
+	controller = agp_find_controller_by_pid(priv->my_pid);
+
+	if (controller != NULL) {
+		agp_controller_make_current(controller);
+	} else {
+		controller = agp_create_controller(priv->my_pid);
+
+		if (controller == NULL) {
+			agp_fe.backend_acquired = FALSE;
+			agp_backend_release();
+			return -ENOMEM;
+		}
+		agp_insert_controller(controller);
+		agp_controller_make_current(controller);
+	}
+
+	set_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags);
+	set_bit(AGP_FF_IS_VALID, &priv->access_flags);
+	return 0;
+}
+
+static int agpioc_release_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_controller_release_current(agp_fe.current_controller, priv);
+	return 0;
+}
+
+static int agpioc_setup_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_setup mode;
+
+	if (copy_from_user(&mode, (void *) arg, sizeof(agp_setup))) {
+		return -EFAULT;
+	}
+	agp_enable(mode.agp_mode);
+	return 0;
+}
+
+static int agpioc_reserve_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_region reserve;
+	agp_client *client;
+	agp_file_private *client_priv;
+
+
+	if (copy_from_user(&reserve, (void *) arg, sizeof(agp_region))) {
+		return -EFAULT;
+	}
+	client = agp_find_client_by_pid(reserve.pid);
+
+	if (reserve.seg_count == 0) {
+		/* remove a client */
+		client_priv = agp_find_private(reserve.pid);
+
+		if (client_priv != NULL) {
+			set_bit(AGP_FF_IS_CLIENT,
+				&client_priv->access_flags);
+			set_bit(AGP_FF_IS_VALID,
+				&client_priv->access_flags);
+		}
+		if (client == NULL) {
+			/* client is already removed */
+			return 0;
+		}
+		return agp_remove_client(reserve.pid);
+	} else {
+		agp_segment *segment;
+
+		segment = kmalloc((sizeof(agp_segment) * reserve.seg_count),
+				  GFP_KERNEL);
+
+		if (segment == NULL) {
+			return -ENOMEM;
+		}
+		if (copy_from_user(segment, (void *) reserve.seg_list,
+				   GFP_KERNEL)) {
+			kfree(segment);
+			return -EFAULT;
+		}
+		reserve.seg_list = segment;
+
+		if (client == NULL) {
+			/* Create the client and add the segment */
+			client = agp_create_client(reserve.pid);
+
+			if (client == NULL) {
+				kfree(segment);
+				return -ENOMEM;
+			}
+			client_priv = agp_find_private(reserve.pid);
+
+			if (client_priv != NULL) {
+				set_bit(AGP_FF_IS_CLIENT,
+					&client_priv->access_flags);
+				set_bit(AGP_FF_IS_VALID,
+					&client_priv->access_flags);
+			}
+			return agp_create_segment(client, &reserve);
+		} else {
+			return agp_create_segment(client, &reserve);
+		}
+	}
+	/* Will never really happen */
+	return -EINVAL;
+}
+
+static int agpioc_protect_wrap(agp_file_private * priv, unsigned long arg)
+{
+	/* This function is not currently implemented */
+	return -EINVAL;
+}
+
+static int agpioc_allocate_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_memory *memory;
+	agp_allocate alloc;
+
+	if (copy_from_user(&alloc, (void *) arg, sizeof(agp_allocate))) {
+		return -EFAULT;
+	}
+	memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type);
+
+	if (memory == NULL) {
+		return -ENOMEM;
+	}
+	alloc.key = memory->key;
+	alloc.physical = memory->physical;
+
+	if (copy_to_user((void *) arg, &alloc, sizeof(agp_allocate))) {
+		agp_free_memory_wrap(memory);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int agpioc_deallocate_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_memory *memory;
+
+	memory = agp_find_mem_by_key((int) arg);
+
+	if (memory == NULL) {
+		return -EINVAL;
+	}
+	agp_free_memory_wrap(memory);
+	return 0;
+}
+
+static int agpioc_bind_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_bind bind_info;
+	agp_memory *memory;
+
+	if (copy_from_user(&bind_info, (void *) arg, sizeof(agp_bind))) {
+		return -EFAULT;
+	}
+	memory = agp_find_mem_by_key(bind_info.key);
+
+	if (memory == NULL) {
+		return -EINVAL;
+	}
+	return agp_bind_memory(memory, bind_info.pg_start);
+}
+
+static int agpioc_unbind_wrap(agp_file_private * priv, unsigned long arg)
+{
+	agp_memory *memory;
+	agp_unbind unbind;
+
+	if (copy_from_user(&unbind, (void *) arg, sizeof(agp_unbind))) {
+		return -EFAULT;
+	}
+	memory = agp_find_mem_by_key(unbind.key);
+
+	if (memory == NULL) {
+		return -EINVAL;
+	}
+	return agp_unbind_memory(memory);
+}
+
+static int agp_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	agp_file_private *curr_priv = (agp_file_private *) file->private_data;
+	int ret_val = -ENOTTY;
+
+	AGP_LOCK();
+
+	if ((agp_fe.current_controller == NULL) &&
+	    (cmd != AGPIOC_ACQUIRE)) {
+		ret_val = -EINVAL;
+	   	goto ioctl_out;
+	}
+	if ((agp_fe.backend_acquired != TRUE) &&
+	    (cmd != AGPIOC_ACQUIRE)) {
+		ret_val = -EBUSY;
+	   	goto ioctl_out;
+	}
+	if (cmd != AGPIOC_ACQUIRE) {
+		if (!(test_bit(AGP_FF_IS_CONTROLLER,
+			       &curr_priv->access_flags))) {
+			ret_val = -EPERM;
+		   	goto ioctl_out;
+		}
+		/* Use the original pid of the controller,
+		 * in case it's threaded */
+
+		if (agp_fe.current_controller->pid != curr_priv->my_pid) {
+			ret_val = -EBUSY;
+		   	goto ioctl_out;
+		}
+	}
+	switch (cmd) {
+	case AGPIOC_INFO:
+		{
+			ret_val = agpioc_info_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_ACQUIRE:
+		{
+			ret_val = agpioc_acquire_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_RELEASE:
+		{
+			ret_val = agpioc_release_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_SETUP:
+		{
+			ret_val = agpioc_setup_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_RESERVE:
+		{
+			ret_val = agpioc_reserve_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_PROTECT:
+		{
+			ret_val = agpioc_protect_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_ALLOCATE:
+		{
+			ret_val = agpioc_allocate_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_DEALLOCATE:
+		{
+			ret_val = agpioc_deallocate_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_BIND:
+		{
+			ret_val = agpioc_bind_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	case AGPIOC_UNBIND:
+		{
+			ret_val = agpioc_unbind_wrap(curr_priv, arg);
+		   	goto ioctl_out;
+		}
+	}
+   
+ioctl_out:
+	AGP_UNLOCK();
+	return ret_val;
+}
+
+static struct file_operations agp_fops =
+{
+	llseek:		agp_lseek,
+	read:		agp_read,
+	write:		agp_write,
+	ioctl:		agp_ioctl,
+	mmap:		agp_mmap,
+	open:		agp_open,
+	release:	agp_release,
+};
+
+static struct miscdevice agp_miscdev =
+{
+	AGPGART_MINOR,
+	AGPGART_MODULE_NAME,
+	&agp_fops
+};
+
+int __init agp_frontend_initialize(void)
+{
+	memset(&agp_fe, 0, sizeof(struct agp_front_data));
+	AGP_LOCK_INIT();
+
+	if (misc_register(&agp_miscdev)) {
+		printk(KERN_ERR PFX "unable to get minor: %d\n", AGPGART_MINOR);
+		return -EIO;
+	}
+	return 0;
+}
+
+void agp_frontend_cleanup(void)
+{
+	misc_deregister(&agp_miscdev);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/amiga_ser.c linux.18p17/drivers/char/amiga_ser.c
--- linux.vanilla/drivers/char/amiga_ser.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/amiga_ser.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/amikeyb.c linux.18p17/drivers/char/amikeyb.c
--- linux.vanilla/drivers/char/amikeyb.c	Fri Apr 21 12:45:51 2000
+++ linux.18p17/drivers/char/amikeyb.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_MFPser.c linux.18p17/drivers/char/atari_MFPser.c
--- linux.vanilla/drivers/char/atari_MFPser.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_MFPser.c	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_MFPser.h linux.18p17/drivers/char/atari_MFPser.h
--- linux.vanilla/drivers/char/atari_MFPser.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_MFPser.h	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_MIDI.c linux.18p17/drivers/char/atari_MIDI.c
--- linux.vanilla/drivers/char/atari_MIDI.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_MIDI.c	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_MIDI.h linux.18p17/drivers/char/atari_MIDI.h
--- linux.vanilla/drivers/char/atari_MIDI.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_MIDI.h	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_SCC.README linux.18p17/drivers/char/atari_SCC.README
--- linux.vanilla/drivers/char/atari_SCC.README	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_SCC.README	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_SCC.c linux.18p17/drivers/char/atari_SCC.c
--- linux.vanilla/drivers/char/atari_SCC.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_SCC.c	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/atari_SCC.h linux.18p17/drivers/char/atari_SCC.h
--- linux.vanilla/drivers/char/atari_SCC.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/atari_SCC.h	Sat Oct 14 00:06:42 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/bttv.c linux.18p17/drivers/char/bttv.c
--- linux.vanilla/drivers/char/bttv.c	Sat Sep  9 18:42:34 2000
+++ linux.18p17/drivers/char/bttv.c	Fri Sep 29 22:17:48 2000
@@ -95,7 +95,6 @@
 /* Anybody who uses more than four? */
 #define BTTV_MAX 4
 
-static int find_vga(void);
 static void bt848_set_risc_jmps(struct bttv *btv);
 
 static unsigned int vidmem=0;   /* manually set video mem address */
@@ -559,7 +558,7 @@
         /* TurboTV */
         { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}},
         /* Newer Hauppauge (bt878) */
-	{ 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}},
+	{ 4, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}},
         /* MIRO PCTV pro */
         { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}},
 	/* ADS Technologies Channel Surfer TV (and maybe TV+FM) */
@@ -1196,6 +1195,7 @@
 		*(ro++)=cpu_to_le32(btv->bus_vbi_even);
 		*(re++)=cpu_to_le32(BT848_RISC_JUMP);
 		*(re++)=cpu_to_le32(btv->bus_vbi_odd);
+		return;
 	}
 	if (ncr < 0) {	/* bitmap was pased */
 		memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE);
@@ -1307,8 +1307,8 @@
 
 	tvn=&tvnorms[btv->win.norm];
 	
-        btv->win.cropheight=tvn->sheight;
-        btv->win.cropwidth=tvn->swidth;
+/*        btv->win.cropheight=tvn->sheight;
+          btv->win.cropwidth=tvn->swidth; set somewhere else now */
 
 /*
 	if (btv->win.cropwidth>tvn->cropwidth)
@@ -1413,6 +1413,62 @@
  *	Set TSA5522 synthesizer frequency in 1/16 Mhz steps
  */
 
+static void reset_cropwin(struct bttv * btv)
+{
+        btv->win.cropx=0;
+        btv->win.cropy=0;
+        btv->win.cropheight=tvnorms[btv->win.norm].sheight;
+        btv->win.cropwidth=tvnorms[btv->win.norm].swidth;
+}                       
+
+static void clip_cropwin(struct bttv * btv, u16 width, u16 height)
+{
+        /* don't allow values beyond max */
+
+        if (btv->win.cropwidth>tvnorms[btv->win.norm].swidth)
+                btv->win.cropwidth=tvnorms[btv->win.norm].swidth;
+
+	if (btv->win.cropheight>tvnorms[btv->win.norm].sheight)
+	        btv->win.cropheight=tvnorms[btv->win.norm].sheight;
+
+        /* horizontal: downscaling only (bt848 limitation)*/
+	if (width>btv->win.cropwidth)
+                btv->win.cropwidth=width;
+
+        /* horizontal scaling is kinda limited by the registers
+         probably irrelevant */
+        if (btv->win.cropwidth>((65535UL+4096UL)*width)/4096UL)
+                btv->win.cropwidth = ((65535UL+4096UL)*width)/4096UL;
+
+        /* vertical: downscaling only (not mentioned in the specs
+         but doesnt work anyways */
+        if (height>btv->win.cropheight)
+                btv->win.cropheight = height;
+
+        if (btv->win.interlace == 0)
+        {
+                if (btv->win.cropheight>127UL*height)
+                        btv->win.cropheight = 127UL*height;
+/*                if (((127UL*512UL-1023UL)*height)/512UL>btv->win.cropheight)
+                  btv->win.cropheight = (127UL*512UL-1023UL)*height/512UL;*/
+        } else
+        {
+                if (btv->win.cropheight>2UL*127UL*height)
+                        btv->win.cropheight = 2UL*127UL*height;
+                btv->win.cropheight = (btv->win.cropheight/2)*2;  /*even number */
+/*                if (((127UL*512UL-1023UL)*height)/256UL>btv->win.cropheight)
+                        btv->win.cropheight = (127UL*512UL-1023UL)*height/256UL;*/
+        }
+        
+        if (btv->win.cropx>tvnorms[btv->win.norm].swidth-btv->win.cropwidth)
+                btv->win.cropx = tvnorms[btv->win.norm].swidth-btv->win.cropwidth;
+
+        if (btv->win.cropy>tvnorms[btv->win.norm].sheight-btv->win.cropheight)
+                btv->win.cropy = tvnorms[btv->win.norm].sheight-btv->win.cropheight;
+
+        return;
+}
+
 static void set_freq(struct bttv *btv, unsigned short freq)
 {
 	int fixme = freq; /* XXX */
@@ -1496,6 +1552,10 @@
 	 *	like QCIF has meaning as a capture.
 	 */
 	 
+
+        clip_cropwin(btv, mp->width, mp->height);
+        bt848_set_geo(btv, mp->width, mp->height, mp->format, 1);
+
 	/*
 	 *	Ok load up the BT848
 	 */
@@ -1602,8 +1662,6 @@
         audio(btv, AUDIO_UNMUTE);
         for (i=users=0; i<bttv_num; i++)
                 users+=bttvs[i].user;
-        if (users==1)
-                find_vga();
         btv->fbuffer=NULL;
         if (!btv->fbuffer)
                 btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
@@ -1721,6 +1779,7 @@
 			b.type = VID_TYPE_CAPTURE|
 				VID_TYPE_TELETEXT|
 				VID_TYPE_OVERLAY|
+                                VID_TYPE_SUBCAPTURE|
 				VID_TYPE_CLIPPING|
 				VID_TYPE_FRAMERAM|
 				VID_TYPE_SCALES|
@@ -1780,7 +1839,8 @@
 			btv->win.norm = v.norm;
                         make_vbitab(btv);
 			bt848_set_winsize(btv);
-			btv->channel=v.channel;
+                        reset_cropwin(btv);
+ 			btv->channel=v.channel;
 			return 0;
 		}
 		case VIDIOCGTUNER:
@@ -1905,6 +1965,7 @@
 			on=(btv->cap&3);
 			
 			bt848_cap(btv,0);
+                        clip_cropwin(btv, vw.width, vw.height);
 			bt848_set_winsize(btv);
 
 			/*
@@ -2226,6 +2287,8 @@
 				return -EIO;
                         if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING)
                                 return -EBUSY;
+
+                     
 		        return vgrab(btv, &vm);
 		}
 		
@@ -2263,7 +2326,38 @@
 			return 0;
 		}
 		
-	        case BTTV_BURST_ON:
+                case VIDIOCGCAPTURE:
+                {
+                        struct video_capture vc;
+                        vc.x=btv->win.cropx;
+                        vc.y=btv->win.cropy;
+                        vc.width=btv->win.cropwidth;
+                        vc.height=btv->win.cropheight;
+                        vc.decimation=0; /* not implemented at the moment */
+                        vc.flags=0; /* dito */
+                        if(copy_to_user((void *)arg, (void *)&vc, sizeof(vc)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+                case VIDIOCSCAPTURE:
+                {
+                        struct video_capture vc;
+                        if(copy_from_user((void *) &vc, (void *) arg, sizeof(vc)))
+                             return -EFAULT;
+                        if (vc.x>tvnorms[btv->win.norm].swidth||
+                            vc.y>tvnorms[btv->win.norm].sheight||
+                            vc.width>tvnorms[btv->win.norm].swidth||
+                            vc.height>tvnorms[btv->win.norm].sheight)
+                                return -EFAULT;
+                        btv->win.cropx=vc.x;
+                        btv->win.cropy=(vc.y>>1)<<1; // make even
+                        btv->win.cropwidth=vc.width;
+                        btv->win.cropheight=vc.height;                        
+                        return 0;
+                }
+
+                case BTTV_BURST_ON:
 		{
 			tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2;
 			tvnorms[0].hdelayx1=186-BURSTOFFSET;
@@ -2576,234 +2670,6 @@
 };
 
 
-struct vidbases 
-{
-	unsigned short vendor, device;
-	char *name;
-	uint badr;
-};
-
-static struct vidbases vbs[] = {
-        { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D,
-                "Alliance AT3D", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222,
-                "ATI MACH64 CT", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
-		"ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
- 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT,
-                "ATI MACH64 GT", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
-		"DEC DC21030", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
-		"Matrox Millennium", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2,
-		"Matrox Millennium II", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP,
-		"Matrox Millennium II AGP", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_MATROX, 0x0521, "Matrox G200", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
-		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128_2, 
-		"Number Nine Imagine 128 Series 2", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128,
-                "Riva128", PCI_BASE_ADDRESS_1},
-};
-
-
-/* DEC TGA offsets stolen from XFree-3.2 */
-
-static uint dec_offsets[4] = {
-	0x200000,
-	0x804000,
-	0,
-	0x1004000
-};
-
-#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases))
-
-/* Scan for PCI display adapter
-   if more than one card is present the last one is used for now */
-
-#if LINUX_VERSION_CODE >= 0x020100
-
-static int find_vga(void)
-{
-	unsigned short badr;
-	int found = 0, i, tga_type;
-	unsigned int vidadr=0;
-	struct pci_dev *dev;
-
-
-	for (dev = pci_devices; dev != NULL; dev = dev->next) 
-	{
-		if (dev->class != PCI_CLASS_NOT_DEFINED_VGA &&
-			((dev->class) >> 16 != PCI_BASE_CLASS_DISPLAY))
-		{
-			continue;
-		}
-		if (PCI_FUNC(dev->devfn) != 0)
-			continue;
-
-		badr=0;
-		printk(KERN_INFO "bttv: PCI display adapter: ");
-		for (i=0; i<NR_CARDS; i++) 
-		{
-			if (dev->vendor == vbs[i].vendor) 
-			{
-				if (vbs[i].device) 
-					if (vbs[i].device!=dev->device)
-						continue;
-				printk("%s.\n", vbs[i].name);
-				badr=vbs[i].badr;
-				break;
-			}
-		}
-		if (!badr) 
-		{
-			printk(KERN_ERR "bttv: Unknown video memory base address.\n");
-			continue;
-		}
-		pci_read_config_dword(dev, badr, &vidadr);
-		if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
-		{
-			printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
-			printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
-			continue;
-		}
-		vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
-		if (!vidadr) 
-		{
-			printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!");
-			continue;
-		}
-     
-		if (dev->vendor == PCI_VENDOR_ID_DEC &&
-			dev->device == PCI_DEVICE_ID_DEC_TGA) 
-		{
-			tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
-			if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
-			{
-				printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
-				found--;
-			}
-			vidadr+=dec_offsets[tga_type];
-		}
-		DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
-		DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", dev->devfn));
-		found++;
-	}
-  
-	if (vidmem)
-	{
-		vidadr=vidmem<<20;
-		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
-		found=1;
-	}
-	for (i=0; i<BTTV_MAX; i++)
-		bttvs[i].win.vidadr=vidadr;
-
-	return found;
-}
-
-#else
-static int find_vga(void)
-{
-	unsigned int devfn, class, vendev;
-	unsigned short vendor, device, badr;
-	int found=0, bus=0, i, tga_type;
-	unsigned int vidadr=0;
-
-
-	for (devfn = 0; devfn < 0xff; devfn++) 
-	{
-		if (PCI_FUNC(devfn) != 0)
-			continue;
-		pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
-		if (vendev == 0xffffffff || vendev == 0x00000000) 
-			continue;
-		pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
-		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
-		pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
-		class = class >> 16;
-/*		if (class == PCI_CLASS_DISPLAY_VGA) {*/
-		if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
-			/* Number 9 GXE64Pro needs this */
-			class == PCI_CLASS_NOT_DEFINED_VGA) 
-		{
-			badr=0;
-			printk(KERN_INFO "bttv: PCI display adapter: ");
-			for (i=0; i<NR_CARDS; i++) 
-			{
-				if (vendor==vbs[i].vendor) 
-				{
-					if (vbs[i].device) 
-						if (vbs[i].device!=device)
-							continue;
-					printk("%s.\n", vbs[i].name);
-					badr=vbs[i].badr;
-					break;
-				}
-			}
-                        if (NR_CARDS == i)
-                            printk("UNKNOWN.\n");
-			if (!badr) 
-			{
-				printk(KERN_ERR "bttv: Unknown video memory base address.\n");
-				continue;
-			}
-			pcibios_read_config_dword(bus, devfn, badr, &vidadr);
-			if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
-			{
-				printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
-				printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
-				continue;
-			}
-			vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
-			if (!vidadr) 
-			{
-				printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!\n");
-				continue;
-			}
-      
-			if (vendor==PCI_VENDOR_ID_DEC)
-				if (device==PCI_DEVICE_ID_DEC_TGA) 
-			{
-				tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
-				if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
-				{
-					printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
-					found--;
-				}
-				vidadr+=dec_offsets[tga_type];
-			}
-
-			DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
-			DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
-			found++;
-		}
-	}
-  
-	if (vidmem)
-	{
-                if (vidmem < 0x1000)
-                        vidadr=vidmem<<20;
-                else
-                        vidadr=vidmem;
-		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
-		found=1;
-	}
-	for (i=0; i<BTTV_MAX; i++)
-		bttvs[i].win.vidadr=vidadr;
-
-	return found;
-}
-#endif
-
-
 #define  TRITON_PCON	           0x50 
 #define  TRITON_BUS_CONCURRENCY   (1<<0)
 #define  TRITON_STREAMING	  (1<<1)
@@ -2811,8 +2677,6 @@
 #define  TRITON_PEER_CONCURRENCY  (1<<3)
   
 
-#if LINUX_VERSION_CODE >= 0x020100
-
 static void handle_chipset(void)
 {
 	struct pci_dev *dev = NULL;
@@ -2876,78 +2740,6 @@
 #endif
 	}
 }
-#else
-static void handle_chipset(void)
-{
-	int index;
-  
-	for (index = 0; index < 8; index++)
-	{
-		unsigned char bus, devfn;
-		unsigned char b;
-    
-		/* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */
-		
-		if (!pcibios_find_device(PCI_VENDOR_ID_SI, 
-					 PCI_DEVICE_ID_SI_496, 
-					 index, &bus, &devfn))
-		{
-			printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n");
-		}			
-
-		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, 
-					 PCI_DEVICE_ID_INTEL_82441,
-					 index, &bus, &devfn)) 
-		{
-			pcibios_read_config_byte(bus, devfn, 0x53, &b);
-			DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
-			DEBUG(printk("bufcon=0x%02x\n",b));
-		}
-
-		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
-			    index, &bus, &devfn)) 
-		{
-			printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
-			triton1=BT848_INT_ETBF;
-			
-#if 0			
-			/* The ETBF bit SHOULD make all this unnecessary */
-			/* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
-			{   
-			        unsigned char bo;
-
-				pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
-				bo=b;
-				DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
-
-				if(!(b & TRITON_BUS_CONCURRENCY)) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
-					b |= TRITON_BUS_CONCURRENCY;
-				}
-
-				if(b & TRITON_PEER_CONCURRENCY) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
-					b &= ~TRITON_PEER_CONCURRENCY;
-				}
-				if(!(b & TRITON_STREAMING)) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
-					b |=  TRITON_STREAMING;
-				}
-
-				if (b!=bo) 
-				{
-					pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); 
-					printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
-				}
-			}
-#endif
-		}
-	}
-}
-#endif
 
 static void init_tea6300(struct i2c_bus *bus) 
 {
@@ -3444,6 +3236,7 @@
 					btv->grf = btv->grf_next;
                                         btv->risc_jmp[5]=cpu_to_le32(btv->gro);
 					btv->risc_jmp[11]=cpu_to_le32(btv->gre);
+                                        clip_cropwin(btv, btv->gwidth, btv->gheight);
 					bt848_set_geo(btv, btv->gwidth,
 						      btv->gheight,
 						      btv->gfmt, 0);
@@ -3451,7 +3244,9 @@
 					bt848_set_risc_jmps(btv);
 					btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI);
 					btand(~BT848_VSCALE_COMB, BT848_O_VSCALE_HI);
-                                        bt848_set_geo(btv, btv->win.width, 
+					clip_cropwin(btv, btv->win.width, 
+                                                        btv->win.height);
+					bt848_set_geo(btv, btv->win.width, 
 						      btv->win.height,
 						      btv->win.color_fmt, 0);
 				}
@@ -3463,7 +3258,8 @@
 			        btv->risc_jmp[5]=cpu_to_le32(btv->gro);
 				btv->risc_jmp[11]=cpu_to_le32(btv->gre);
 				btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP);
-				bt848_set_geo(btv, btv->gwidth, btv->gheight,
+                                clip_cropwin(btv, btv->gwidth, btv->gheight);
+                                bt848_set_geo(btv, btv->gwidth, btv->gheight,
 					      btv->gfmt, 0);
 			}
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/bttv.h linux.18p17/drivers/char/bttv.h
--- linux.vanilla/drivers/char/bttv.h	Wed May  3 22:26:11 2000
+++ linux.18p17/drivers/char/bttv.h	Thu Oct 19 01:41:36 2000
@@ -40,7 +40,7 @@
 #define VBIBUF_SIZE     65536
 
 /* maximum needed buffer size for extended VBI frame mode capturing */
-#define BTTV_MAX_FBUF	0x190000
+#define BTTV_MAX_FBUF   0x208000
 
 #ifdef __KERNEL__
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/buz.c linux.18p17/drivers/char/buz.c
--- linux.vanilla/drivers/char/buz.c	Fri Apr 21 12:45:51 2000
+++ linux.18p17/drivers/char/buz.c	Thu Aug 31 15:14:49 2000
@@ -201,6 +201,7 @@
 				mem_map_reserve(MAP_NR(mem + off));
 			DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem)));
 		} else {
+			v4l_fbuffer_free(zr);
 			return -ENOBUFS;
 		}
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/cpia.c linux.18p17/drivers/char/cpia.c
--- linux.vanilla/drivers/char/cpia.c	Sun Jun 11 21:44:12 2000
+++ linux.18p17/drivers/char/cpia.c	Tue Sep  5 23:08:25 2000
@@ -3,9 +3,9 @@
  *
  * Supports CPiA based Video Camera's.
  *
- * (C) 1999 Peter Pregler,
- *          Scott J. Bertin,
- *          Johannes Erdfelt
+ * (C) Copyright 1999-2000 Peter Pregler,
+ * (C) Copyright 1999-2000 Scott J. Bertin,
+ * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,28 +22,28 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/* #define _CPIA_DEBUG_		define for verbose debug output */
 #include <linux/config.h>
 
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/init.h>
-#include <linux/wrapper.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
-#include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
-#include <linux/videodev.h>
+#include <linux/pagemap.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
+#include <linux/wrapper.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
 
-#undef _CPIA_DEBUG_		/* define for verbose debug output */
-#include <linux/cpia.h>
+#include "cpia.h"
 
 #ifdef CONFIG_VIDEO_CPIA_PP
 extern int cpia_pp_init(void);
@@ -53,7 +53,7 @@
 #endif
 
 #ifdef MODULE
-MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <jerdfelt@valinux.com>");
 MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
 MODULE_SUPPORTED_DEVICE("video");
 #endif
@@ -97,6 +97,7 @@
 #define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
 
 #define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
 #define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
 #define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
 #define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
@@ -127,6 +128,7 @@
 #define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
 #define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
 #define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
 
 #define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
 #define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
@@ -135,117 +137,7 @@
 #define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
 #define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
 #define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
-
-struct cam_params {
-	struct {
-		u8 firmwareVersion;
-		u8 firmwareRevision;
-		u8 vcVersion;
-		u8 vcRevision;
-	} version;
-	struct {
-		u16 vendor;
-		u16 product;
-		u16 deviceRevision;
-	} pnpID;
-	struct {
-		u8 vpVersion;
-		u8 vpRevision;
-		u16 cameraHeadID;
-	} vpVersion;
-	struct {
-		u8 systemState;
-		u8 grabState;
-		u8 streamState;
-		u8 fatalError;
-		u8 cmdError;
-		u8 debugFlags;
-		u8 vpStatus;
-		u8 errorCode;
-	} status;
-	struct {
-		u8 brightness;
-		u8 contrast;
-		u8 saturation;
-	} colourParams;
-	struct {
-		u8 gainMode;
-		u8 expMode;
-		u8 compMode;
-		u8 centreWeight;
-		u8 gain;
-		u8 fineExp;
-		u8 coarseExpLo;
-		u8 coarseExpHi;
-		u8 redComp;
-		u8 green1Comp;
-		u8 green2Comp;
-		u8 blueComp;
-	} exposure;
-	struct {
-		u8 balanceMode;
-		u8 redGain;
-		u8 greenGain;
-		u8 blueGain;
-	} colourBalance;
-	struct {
-		u8 divisor;
-		u8 baserate;
-	} sensorFps;
-	struct {
-		u8 gain1;
-		u8 gain2;
-		u8 gain4;
-		u8 gain8;
-	} apcor;
-	struct {
-		u8 flickerMode;
-		u8 coarseJump;
-		u8 allowableOverExposure;
-	} flickerControl;
-	struct {
-		u8 gain1;
-		u8 gain2;
-		u8 gain4;
-		u8 gain8;
-	} vlOffset;
-	struct {
-		u8 mode;
-		u8 decimation;
-	} compression;
-	struct {
-		u8 frTargeting;
-		u8 targetFR;
-		u8 targetQ;
-	} compressionTarget;
-	struct {
-		u8 yThreshold;
-		u8 uvThreshold;
-	} yuvThreshold;
-	struct {
-		u8 hysteresis;
-		u8 threshMax;
-		u8 smallStep;
-		u8 largeStep;
-		u8 decimationHysteresis;
-		u8 frDiffStepThresh;
-		u8 qDiffStepThresh;
-		u8 decimationThreshMod;
-	} compressionParams;
-	struct {
-		u8 videoSize;		/* CIF/QCIF */
-		u8 subSample;
-		u8 yuvOrder;
-	} format;
-	struct {
-		u8 colStart;		/* skip first 8*colStart pixels */
-		u8 colEnd;		/* finish at 8*colEnd pixels */
-		u8 rowStart;		/* skip first 4*rowStart lines */
-		u8 rowEnd;		/* finish at 4*rowEnd lines */
-	} roi;
-	u8 ecpTiming;
-	u8 streamStartLine;
-};
+#define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
 
 enum {
 	FRAME_READY,		/* Ready to grab into */
@@ -254,24 +146,6 @@
 	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
 };
 
-#define FRAME_NUM	2	/* double buffering for now */
-struct cpia_frame {
-	u8 *data;
-	int count;
-	int width;
-	int height;
-	volatile int state;
-};
-
-enum v4l_camstates {
-	CPIA_V4L_IDLE = 0,
-	CPIA_V4L_ERROR,
-	CPIA_V4L_COMMAND,
-	CPIA_V4L_GRABBING,
-	CPIA_V4L_STREAMING,
-	CPIA_V4L_STREAMING_PAUSED,
-};
-
 #define COMMAND_NONE			0x0000
 #define COMMAND_SETCOMPRESSION		0x0001
 #define COMMAND_SETCOMPRESSIONTARGET	0x0002
@@ -288,45 +162,7 @@
 #define COMMAND_SETAPCOR		0x1000
 #define COMMAND_SETFLICKERCTRL		0x2000
 #define COMMAND_SETVLOFFSET		0x4000
-
-struct cam_data {
-	int index;			/* which camera is this */
-        struct semaphore busy_lock;     /* guard against SMP multithreading */
-	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
-	void *lowlevel_data;		/* private data for lowlevel driver */
-	u8 *raw_image;			/* buffer for raw image data */
-	struct cpia_frame decompressed_frame;
-                                        /* buffer to hold decompressed frame */
-	int image_size;		        /* sizeof last decompressed image */
-	int open_count;			/* # of process that have camera open */
-				/* camera status */
-	int fps;			/* actual fps reported by the camera */
-	int transfer_rate;		/* transfer rate from camera in kB/s */
-	u8 mainsFreq;			/* for flicker control */
-
-				/* proc interface */
-	struct semaphore param_lock;	/* params lock for this camera */
-	struct cam_params params;	/* camera settings */
-	struct proc_dir_entry *proc_entry;	/* /proc/cpia/videoX */
-	
-					/* v4l */
-	int video_size;			/* VIDEO_SIZE_ */
-	volatile enum v4l_camstates camstate;	/* v4l layer status */
-	struct video_device vdev;	/* v4l videodev */
-	struct video_picture vp;	/* v4l camera settings */
-	struct video_window vw;		/* v4l capture area */
-
-				/* mmap interface */
-	int curframe;			/* the current frame to grab into */
-	u8 *frame_buf;			/* frame buffer data */
-        struct cpia_frame frame[FRAME_NUM];
-				/* FRAME_NUM-buffering, so we need a array */
-
-        int first_frame;
-	volatile u32 cmd_queue;		/* queued commands */
-};
-
-static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL };
+#define COMMAND_SETLIGHTS               0x8000 /* GA 04/14/00 */
 
 /* Developer's Guide Table 5 p 3-34
  * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
@@ -338,6 +174,7 @@
 /* forward declaration of local function */
 static void reset_camera_struct(struct cam_data *cam);
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 /**********************************************************************
  *
  * Memory management
@@ -357,64 +194,134 @@
 	pte_t *ptep, pte;
 
 	pgd = pgd_offset(current->mm, adr);
-	if (pgd_none(*pgd)) return 0;
+	if (pgd_none(*pgd))
+		return 0;
 	pmd = pmd_offset(pgd, adr);
-	if (pmd_none(*pmd)) return 0;
+	if (pmd_none(*pmd))
+		return 0;
 	ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
 	pte = *ptep;
-	if(pte_present(pte))
-		return 
-		  virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+	if (pte_present(pte))
+		return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
 	return 0;
 }
 
-static inline unsigned long kvirt_to_phys(unsigned long adr) 
+static inline unsigned long kvirt_to_pa(unsigned long adr) 
 {
 	return uvirt_to_phys(VMALLOC_VMADDR(adr));
 }
 
-static void * rvmalloc(unsigned long size)
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet - jerdfelt
+ *
+ **********************************************************************/
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none(*pgd)) {
+		pmd = pmd_offset(pgd, adr);
+		if (!pmd_none(*pmd)) {
+			ptep = pte_offset(pmd, adr);
+			pte = *ptep;
+			if (pte_present(pte))
+				ret = page_address(pte_page(pte)) |
+				      (adr & (PAGE_SIZE-1));
+		}
+	}
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	return ret;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
+
+static void *rvmalloc(unsigned long size)
 {
-	void * mem;
+	void *mem;
 	unsigned long adr, page;
 
+	/* Round it off to PAGE_SIZE */
 	size += (PAGE_SIZE - 1);
 	size &= ~(PAGE_SIZE - 1);
 
-	mem=vmalloc(size);
-	if (mem) {
-		/* Clear the ram out, no junk to the user */
-		memset(mem, 0, size);
-		adr=(unsigned long) mem;
-		while (size > 0) {
-			page = kvirt_to_phys(adr);
-			mem_map_reserve(MAP_NR(phys_to_virt(page)));
-			adr+=PAGE_SIZE;
-			if (size > PAGE_SIZE) size-=PAGE_SIZE;
-			else  size=0;
-		}
+	mem = vmalloc(size);
+	if (!mem)
+		return NULL;
+
+	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		mem_map_reserve(MAP_NR(phys_to_virt(page)));
+#else
+		mem_map_reserve(MAP_NR(__va(page)));
+#endif
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
+
 	return mem;
 }
 
-static void rvfree(void * mem, unsigned long size)
+static void rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr, page;
-        
+
+	if (!mem)
+		return;
+
 	size += (PAGE_SIZE - 1);
 	size &= ~(PAGE_SIZE - 1);
 
-	if (mem) {
-		adr=(unsigned long) mem;
-		while (size > 0) {
-			page = kvirt_to_phys(adr);
-			mem_map_unreserve(MAP_NR(phys_to_virt(page)));
-			adr+=PAGE_SIZE;
-			if (size > PAGE_SIZE) size-=PAGE_SIZE;
-			else size=0;
-		}
-		vfree(mem);
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+#else
+		mem_map_unreserve(MAP_NR(__va(page)));
+#endif
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
+	vfree(mem);
 }
 
 /**********************************************************************
@@ -422,6 +329,7 @@
  * /proc interface
  *
  **********************************************************************/
+#ifdef CONFIG_PROC_FS
 static struct proc_dir_entry *cpia_proc_root=NULL;
 
 static int cpia_read_proc(char *page, char **start, off_t off,
@@ -432,7 +340,7 @@
 	struct cam_data *cam = data;
 	char tmpstr[20];
 
-	/* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?)
+	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
 
 	out += sprintf(out, "read-only\n-----------------------\n");
@@ -467,22 +375,23 @@
 	               cam->params.status.vpStatus);
 	out += sprintf(out, "error_code:               %#04x\n",
 	               cam->params.status.errorCode);
+	/* GA 04/14/00 - QX3 specific entries */
+	if (cam->params.qx3.qx3_detected) {
+		out += sprintf(out, "button:                   %4d\n",
+		               cam->params.qx3.button);
+		out += sprintf(out, "cradled:                  %4d\n",
+		               cam->params.qx3.cradled);
+	}
 	out += sprintf(out, "video_size:               %s\n",
 	               cam->params.format.videoSize == VIDEOSIZE_CIF ?
 		       "CIF " : "QCIF");
-	out += sprintf(out, "sub_sample:               %s\n",
-	               cam->params.format.subSample == SUBSAMPLE_420 ?
-		       "420" : "422");
-	out += sprintf(out, "yuv_order:                %s\n",
-	               cam->params.format.yuvOrder == YUVORDER_YUYV ?
-		       "YUYV" : "UYVY");
 	out += sprintf(out, "roi:                      (%3d, %3d) to (%3d, %3d)\n",
 	               cam->params.roi.colStart*8,
 	               cam->params.roi.rowStart*4,
 	               cam->params.roi.colEnd*8,
 	               cam->params.roi.rowEnd*4);
-	out += sprintf(out, "actual_fps:               %d\n", cam->fps);
-	out += sprintf(out, "transfer_rate:            %dkB/s\n",
+	out += sprintf(out, "actual_fps:               %3d\n", cam->fps);
+	out += sprintf(out, "transfer_rate:            %4dkB/s\n",
 	               cam->transfer_rate);
 	
 	out += sprintf(out, "\nread-write\n");
@@ -490,13 +399,13 @@
 	               "       max   default  comment\n");
 	out += sprintf(out, "brightness:             %8d  %8d  %8d  %8d\n",
 	               cam->params.colourParams.brightness, 0, 100, 50);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits contrast to 80 */
 		tmp = 80;
-	} else {
+	else
 		tmp = 96;
-	}
+
 	out += sprintf(out, "contrast:               %8d  %8d  %8d  %8d"
 	               "  steps of 8\n",
 	               cam->params.colourParams.contrast, 0, tmp, 48);
@@ -510,21 +419,20 @@
 	               2*cam->params.streamStartLine, 0,
 		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
 		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+	out += sprintf(out, "sub_sample:             %8s  %8s  %8s  %8s\n",
+	               cam->params.format.subSample == SUBSAMPLE_420 ?
+		       "420" : "422", "420", "422", "422");
+	out += sprintf(out, "yuv_order:              %8s  %8s  %8s  %8s\n",
+	               cam->params.format.yuvOrder == YUVORDER_YUYV ?
+		       "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
 	out += sprintf(out, "ecp_timing:             %8s  %8s  %8s  %8s\n",
 	               cam->params.ecpTiming ? "slow" : "normal", "slow",
 		       "normal", "normal");
 
-	switch(cam->params.colourBalance.balanceMode) {
-	case 1:
-	  // FIXME case 3:
-		sprintf(tmpstr, "manual");
-		break;
-	case 2:
+	if (cam->params.colourBalance.balanceModeIsAuto) {
 		sprintf(tmpstr, "auto");
-		break;
-	default:
-		sprintf(tmpstr, "unknown");
-		break;
+	} else {
+		sprintf(tmpstr, "manual");
 	}
 	out += sprintf(out, "color_balance_mode:     %8s  %8s  %8s"
 		       "  %8s\n",  tmpstr, "manual", "auto", "auto");
@@ -535,20 +443,20 @@
 	out += sprintf(out, "blue_gain:              %8d  %8d  %8d  %8d\n",
 	               cam->params.colourBalance.blueGain, 0, 212, 92);
 
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits gain to 2 */
 		sprintf(tmpstr, "%8d  %8d", 1, 2);
-	} else {
+	else
 		sprintf(tmpstr, "%8d  %8d", 1, 8);
-	}
-	if(cam->params.exposure.gainMode == 0) {
+
+	if (cam->params.exposure.gainMode == 0)
 		out += sprintf(out, "max_gain:                unknown  %18s"
 		               "  %8d  powers of 2\n", tmpstr, 2); 
-	} else {
+	else
 		out += sprintf(out, "max_gain:               %8d  %18s  %8d  powers of 2\n", 
 		               1<<(cam->params.exposure.gainMode-1), tmpstr, 2);
-	}
+
 	switch(cam->params.exposure.expMode) {
 	case 1:
 	case 3:
@@ -568,22 +476,22 @@
 	               "off", "on", "on");
 	out += sprintf(out, "gain:                   %8d  %8d  max_gain  %8d  1,2,4,8 possible\n",
 	               1<<cam->params.exposure.gain, 1, 1);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits fineExp to 127 */
 		tmp = 255;
-	} else {
+	else
 		tmp = 511;
-	}
+
 	out += sprintf(out, "fine_exp:               %8d  %8d  %8d  %8d\n",
 	               cam->params.exposure.fineExp*2, 0, tmp, 0);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits coarseExpHi to 0 */
 		tmp = 255;
-	} else {
+	else
 		tmp = 65535;
-	}
+
 	out += sprintf(out, "coarse_exp:             %8d  %8d  %8d"
 		       "  %8d\n", cam->params.exposure.coarseExpLo+
 		       256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
@@ -637,9 +545,9 @@
 		break;
 	}
 	out += sprintf(out, "    none,auto,manual      auto\n");
-	out += sprintf(out, "decimation:             %8s  %8s  %8s  %8s\n",
+	out += sprintf(out, "decimation_enable:      %8s  %8s  %8s  %8s\n",
         	       cam->params.compression.decimation == 
-		       DECIMATION_ENAB ? "on":"off", "off", "off",
+		       DECIMATION_ENAB ? "on":"off", "off", "on",
 		       "off");
 	out += sprintf(out, "compression_target:    %9s %9s %9s %9s\n",
 	               cam->params.compressionTarget.frTargeting  == 
@@ -647,13 +555,13 @@
 		       "framerate":"quality",
 		       "framerate", "quality", "quality");
 	out += sprintf(out, "target_framerate:       %8d  %8d  %8d  %8d\n",
-	               cam->params.compressionTarget.targetFR, 0, 30, 7);
+	               cam->params.compressionTarget.targetFR, 1, 30, 15);
 	out += sprintf(out, "target_quality:         %8d  %8d  %8d  %8d\n",
-	               cam->params.compressionTarget.targetQ, 0, 255, 10);
+	               cam->params.compressionTarget.targetQ, 1, 64, 5);
 	out += sprintf(out, "y_threshold:            %8d  %8d  %8d  %8d\n",
-	               cam->params.yuvThreshold.yThreshold, 0, 31, 15);
+	               cam->params.yuvThreshold.yThreshold, 0, 31, 6);
 	out += sprintf(out, "uv_threshold:           %8d  %8d  %8d  %8d\n",
-	               cam->params.yuvThreshold.uvThreshold, 0, 31, 15);
+	               cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
 	out += sprintf(out, "hysteresis:             %8d  %8d  %8d  %8d\n",
 	               cam->params.compressionParams.hysteresis, 0, 255, 3);
 	out += sprintf(out, "threshold_max:          %8d  %8d  %8d  %8d\n",
@@ -674,15 +582,25 @@
 	out += sprintf(out, "decimation_thresh_mod:  %8d  %8d  %8d  %8d\n",
 	               cam->params.compressionParams.decimationThreshMod,
 		       0, 255, 2);
-	
+
+	/* GA 04/14/00 - QX3 specific entries */
+	if (cam->params.qx3.qx3_detected) {
+		out += sprintf(out, "toplight:               %8s  %8s  %8s  %8s\n", 
+		               cam->params.qx3.toplight ? "on" : "off",
+			       "off", "on", "off");
+		out += sprintf(out, "bottomlight:            %8s  %8s  %8s  %8s\n", 
+		               cam->params.qx3.bottomlight ? "on" : "off",
+			       "off", "on", "off");
+	}
+
 	len = out - page;
 	len -= off;
 	if (len < count) {
 		*eof = 1;
 		if (len <= 0) return 0;
-	} else {
+	} else
 		len = count;
-	}
+
 	*start = page + off;
 	return len;
 }
@@ -692,16 +610,14 @@
 {
 	struct cam_data *cam = data;
 	struct cam_params new_params;
-	int retval, len, colon_found;
+	int retval, find_colon;
 	int size = count;
-	char *p;
 	unsigned long val;
 	u32 command_flags = 0;
 	u8 new_mains;
 	
-	if(down_interruptible(&cam->param_lock)) {
+	if (down_interruptible(&cam->param_lock))
 		return -ERESTARTSYS;
-	}
 	
 	/*
 	 * Skip over leading whitespace
@@ -714,106 +630,112 @@
 	memcpy(&new_params, &cam->params, sizeof(struct cam_params));
 	new_mains = cam->mainsFreq;
 	
-#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0)
+#define MATCH(x) \
+	({ \
+		int _len = strlen(x), _ret, _colon_found; \
+		_ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
+		if (_ret) { \
+			buffer += _len; \
+			count -= _len; \
+			if (find_colon) { \
+				_colon_found = 0; \
+				while (count && (*buffer == ' ' || *buffer == '\t' || \
+				       (!_colon_found && *buffer == ':'))) { \
+					if (*buffer == ':')  \
+						_colon_found = 1; \
+					--count; \
+					++buffer; \
+				} \
+				if (!count || !_colon_found) \
+					retval = -EINVAL; \
+				find_colon = 0; \
+			} \
+		} \
+		_ret; \
+	})
 #define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
                                new_params.version.firmwareRevision == (y))
 #define VALUE \
-	simple_strtoul(buffer, &p, 0); \
-	if(p == buffer) { \
-		retval = -EINVAL; \
-	} else { \
-		count -= p-buffer; \
-		buffer = p; \
-	}
-#define FIND_VALUE \
-	buffer += len;\
-	count -= len; \
-	colon_found=0; \
-	while(count && (*buffer == ' ' || *buffer == '\t' || \
-	                (!colon_found && *buffer == ':'))) { \
-		if(*buffer == ':') colon_found = 1; \
-		--count; \
-		++buffer; \
-	} \
-	if(!count || !colon_found) { \
-		retval = -EINVAL; \
-	}
-#define FIND_END \
-	if(retval == 0) { \
-		while(count && isspace(*buffer) && *buffer != '\n'){ \
-			--count; \
-			++buffer; \
+	({ \
+		char *_p; \
+		unsigned long int _ret; \
+		_ret = simple_strtoul(buffer, &_p, 0); \
+		if (_p == buffer) \
+			retval = -EINVAL; \
+		else { \
+			count -= _p - buffer; \
+			buffer = _p; \
 		} \
-		if(count) { \
-			if(*buffer != '\n' && *buffer != ';') { \
-				retval = -EINVAL; \
-			} else { \
-				--count; \
-				++buffer; \
-			} \
-		} \
-	}
+		_ret; \
+	})
+
 	retval = 0;
-	while(count && !retval) {
-		if(MATCH("brightness")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100)
-					new_params.colourParams.brightness=val;
-				else retval = -EINVAL;
+	while (count && !retval) {
+		find_colon = 1;
+		if (MATCH("brightness")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100)
+					new_params.colourParams.brightness = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("contrast")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100) {
+		} else if (MATCH("contrast")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100) {
 					/* contrast is in steps of 8, so round*/
 					val = ((val + 3) / 8) * 8;
 					/* 1-02 firmware limits contrast to 80*/
-					if(FIRMWARE_VERSION(1,2) && val > 80)
+					if (FIRMWARE_VERSION(1,2) && val > 80)
 						val = 80;
+
 					new_params.colourParams.contrast = val;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("saturation")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100)
-					new_params.colourParams.saturation=val;
-				else retval = -EINVAL;
+		} else if (MATCH("saturation")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100)
+					new_params.colourParams.saturation = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("sensor_fps")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("sensor_fps")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				/* find values so that sensorFPS is minimized,
 				 * but >= val */
-				if(val > 30) {
+				if (val > 30)
 					retval = -EINVAL;
-				} else if(val > 25) {
+				else if (val > 25) {
 					new_params.sensorFps.divisor = 0;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 15) {
+				} else if (val > 15) {
 					new_params.sensorFps.divisor = 0;
 					new_params.sensorFps.baserate = 0;
-				} else if(val > 12) {
+				} else if (val > 12) {
 					new_params.sensorFps.divisor = 1;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 7) {
+				} else if (val > 7) {
 					new_params.sensorFps.divisor = 1;
 					new_params.sensorFps.baserate = 0;
-				} else if(val > 6) {
+				} else if (val > 6) {
 					new_params.sensorFps.divisor = 2;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 3) {
+				} else if (val > 3) {
 					new_params.sensorFps.divisor = 2;
 					new_params.sensorFps.baserate = 0;
 				} else {
@@ -825,89 +747,100 @@
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
 			}
 			command_flags |= COMMAND_SETSENSORFPS;
-			FIND_END
-		} else if(MATCH("stream_start_line")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("stream_start_line")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				int max_line = 288;
-				if(new_params.format.videoSize==VIDEOSIZE_QCIF)
+
+				if (new_params.format.videoSize == VIDEOSIZE_QCIF)
 					max_line = 144;
-				if(val <= max_line)
+				if (val <= max_line)
 					new_params.streamStartLine = val/2;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("ecp_timing")) {
-			FIND_VALUE
-			if(!retval && MATCH("normal")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("sub_sample")) {
+			if (!retval && MATCH("420"))
+				new_params.format.subSample = SUBSAMPLE_420;
+			else if (!retval && MATCH("422"))
+				new_params.format.subSample = SUBSAMPLE_422;
+			else
+				retval = -EINVAL;
+
+			command_flags |= COMMAND_SETFORMAT;
+		} else if (MATCH("yuv_order")) {
+			if (!retval && MATCH("YUYV"))
+				new_params.format.yuvOrder = YUVORDER_YUYV;
+			else if (!retval && MATCH("UYVY"))
+				new_params.format.yuvOrder = YUVORDER_UYVY;
+			else
+				retval = -EINVAL;
+
+			command_flags |= COMMAND_SETFORMAT;
+		} else if (MATCH("ecp_timing")) {
+			if (!retval && MATCH("normal"))
 				new_params.ecpTiming = 0;
-			} else if(!retval && MATCH("slow")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("slow"))
 				new_params.ecpTiming = 1;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETECPTIMING;
-			FIND_END
-		} else if(MATCH("color_balance_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
-				new_params.colourBalance.balanceMode=1;
-			} else if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
-				new_params.colourBalance.balanceMode=2;
-			} else {
+		} else if (MATCH("color_balance_mode")) {
+			if (!retval && MATCH("manual"))
+				new_params.colourBalance.balanceModeIsAuto = 0;
+			else if (!retval && MATCH("auto"))
+				new_params.colourBalance.balanceModeIsAuto = 1;
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("red_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
+		} else if (MATCH("red_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
 					new_params.colourBalance.redGain = val;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("green_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
-					new_params.colourBalance.greenGain=val;
-				else retval = -EINVAL;
+		} else if (MATCH("green_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
+					new_params.colourBalance.greenGain = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("blue_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
+		} else if (MATCH("blue_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
 					new_params.colourBalance.blueGain = val;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("max_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("max_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				/* 1-02 firmware limits gain to 2 */
-				if(FIRMWARE_VERSION(1,2) && val > 2)
+				if (FIRMWARE_VERSION(1,2) && val > 2)
 					val = 2;
 				switch(val) {
 				case 1:
@@ -928,44 +861,32 @@
 				}
 			}
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("exposure_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("exposure_mode")) {
+			if (!retval && MATCH("auto"))
 				new_params.exposure.expMode = 2;
-			} else if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
-				if(new_params.exposure.expMode == 2)
+			else if (!retval && MATCH("manual")) {
+				if (new_params.exposure.expMode == 2)
 					new_params.exposure.expMode = 3;
 				new_params.flickerControl.flickerMode = 0;
 				command_flags |= COMMAND_SETFLICKERCTRL;
-			} else {
+			} else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("centre_weight")) {
-			FIND_VALUE
-			if(!retval && MATCH("on")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("centre_weight")) {
+			if (!retval && MATCH("on"))
 				new_params.exposure.centreWeight = 1;
-			} else if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("off"))
 				new_params.exposure.centreWeight = 2;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				switch(val) {
 				case 1:
 					new_params.exposure.gain = 0;
@@ -996,35 +917,36 @@
 					break;
 				}
 				command_flags |= COMMAND_SETEXPOSURE;
-				if(new_params.exposure.gain >
-				   new_params.exposure.gainMode-1)
+				if (new_params.exposure.gain >
+				    new_params.exposure.gainMode-1)
 					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("fine_exp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 256) {
+		} else if (MATCH("fine_exp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 256) {
 					/* 1-02 firmware limits fineExp to 127*/
-					if(FIRMWARE_VERSION(1,2) && val > 127)
+					if (FIRMWARE_VERSION(1,2) && val > 127)
 						val = 127;
 					new_params.exposure.fineExp = val;
 					new_params.exposure.expMode = 1;
 					command_flags |= COMMAND_SETEXPOSURE;
 					new_params.flickerControl.flickerMode = 0;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("coarse_exp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 65536) {
+		} else if (MATCH("coarse_exp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 65536) {
 					/* 1-02 firmware limits
 					 * coarseExp to 255 */
-					if(FIRMWARE_VERSION(1,2) && val > 255)
+					if (FIRMWARE_VERSION(1,2) && val > 255)
 						val = 255;
 					new_params.exposure.coarseExpLo =
 						val & 0xff;
@@ -1034,353 +956,398 @@
 					command_flags |= COMMAND_SETEXPOSURE;
 					new_params.flickerControl.flickerMode = 0;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("red_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 220 && val <= 255) {
+		} else if (MATCH("red_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 220 && val <= 255) {
 					new_params.exposure.redComp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("green1_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 214 && val <= 255) {
+		} else if (MATCH("green1_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 214 && val <= 255) {
 					new_params.exposure.green1Comp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("green2_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 214 && val <= 255) {
+		} else if (MATCH("green2_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 214 && val <= 255) {
 					new_params.exposure.green2Comp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("blue_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 230 && val <= 255) {
+		} else if (MATCH("blue_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 230 && val <= 255) {
 					new_params.exposure.blueComp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain1")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain1")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain1 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain1 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain2")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain2")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain2 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain2 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain4")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain4")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain4 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain4 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain8")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain8")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain8 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain8 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("vl_offset_gain1")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain1 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain1")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain1 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain2")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain2 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain2")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain2 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain4")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain4 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain4")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain4 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain8")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain8 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain8")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain8 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("flicker_control")) {
-			FIND_VALUE
-			if(!retval && MATCH("on")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("flicker_control")) {
+			if (!retval && MATCH("on")) {
 				new_params.flickerControl.flickerMode = 1;
 				new_params.exposure.expMode = 2;
 				command_flags |= COMMAND_SETEXPOSURE;
-			} else if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+			} else if (!retval && MATCH("off"))
 				new_params.flickerControl.flickerMode = 0;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETFLICKERCTRL;
-			FIND_END
-		} else if(MATCH("mains_frequency")) {
-			FIND_VALUE
-			if(!retval && MATCH("50")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("mains_frequency")) {
+			if (!retval && MATCH("50")) {
 				new_mains = 0;
 				new_params.flickerControl.coarseJump = 
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
-			} else if(!retval && MATCH("60")) {
-				buffer += len;
-				count -= len;
+			} else if (!retval && MATCH("60")) {
 				new_mains = 1;
 				new_params.flickerControl.coarseJump = 
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
-			} else {
+			} else
 				retval = -EINVAL;
-			}
-			FIND_END
-		} else if(MATCH("allowable_overexposure")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) {
+		} else if (MATCH("allowable_overexposure")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff) {
 					new_params.flickerControl.
 						allowableOverExposure = val;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("compression_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("none")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("compression_mode")) {
+			if (!retval && MATCH("none"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_NONE;
-			} else if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("auto"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_AUTO;
-			} else if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("manual"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_MANUAL;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSION;
-			FIND_END
-		} else if(MATCH("decimation")) {
-			FIND_VALUE
-			if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("decimation_enable")) {
+			if (!retval && MATCH("off"))
 				new_params.compression.decimation = 0;
-			} else {
+			else if (!retval && MATCH("on"))
+				new_params.compression.decimation = 1;
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSION;
-			FIND_END
-		} else if(MATCH("compression_target")) {
-			FIND_VALUE
-			if(!retval && MATCH("quality")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("compression_target")) {
+			if (!retval && MATCH("quality"))
 				new_params.compressionTarget.frTargeting = 
 					CPIA_COMPRESSION_TARGET_QUALITY;
-			} else if(!retval && MATCH("framerate")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("framerate"))
 				new_params.compressionTarget.frTargeting = 
 					CPIA_COMPRESSION_TARGET_FRAMERATE;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("target_framerate")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval)
-				new_params.compressionTarget.targetFR = val;
+		} else if (MATCH("target_framerate")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if(val > 0 && val <= 30)
+					new_params.compressionTarget.targetFR = val;
+				else
+					retval = -EINVAL;
+			}
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("target_quality")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) new_params.compressionTarget.targetQ = val;
+		} else if (MATCH("target_quality")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if(val > 0 && val <= 64)
+					new_params.compressionTarget.targetQ = val;
+				else 
+					retval = -EINVAL;
+			}
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("y_threshold")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 32)
-					new_params.yuvThreshold.yThreshold=val;
-				else retval = -EINVAL;
+		} else if (MATCH("y_threshold")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 32)
+					new_params.yuvThreshold.yThreshold = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETYUVTHRESH;
-			FIND_END
-		} else if(MATCH("uv_threshold")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 32)
-					new_params.yuvThreshold.uvThreshold=val;
-				else retval = -EINVAL;
+		} else if (MATCH("uv_threshold")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 32)
+					new_params.yuvThreshold.uvThreshold = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETYUVTHRESH;
-			FIND_END
-		} else if(MATCH("hysteresis")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.hysteresis=val;
-				else retval = -EINVAL;
+		} else if (MATCH("hysteresis")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.hysteresis = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("threshold_max")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.threshMax=val;
-				else retval = -EINVAL;
+		} else if (MATCH("threshold_max")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.threshMax = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("small_step")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.smallStep=val;
-				else retval = -EINVAL;
+		} else if (MATCH("small_step")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.smallStep = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("large_step")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.largeStep=val;
-				else retval = -EINVAL;
+		} else if (MATCH("large_step")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.largeStep = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("decimation_hysteresis")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        decimationHysteresis = val;
-				else retval = -EINVAL;
+		} else if (MATCH("decimation_hysteresis")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.decimationHysteresis = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("fr_diff_step_thresh")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        frDiffStepThresh = val;
-				else retval = -EINVAL;
+		} else if (MATCH("fr_diff_step_thresh")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.frDiffStepThresh = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("q_diff_step_thresh")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        qDiffStepThresh = val;
-				else retval = -EINVAL;
-			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("decimation_thresh_mod")) {
+		} else if (MATCH("q_diff_step_thresh")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.qDiffStepThresh = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        decimationThreshMod = val;
-				else retval = -EINVAL;
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+		} else if (MATCH("decimation_thresh_mod")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.decimationThreshMod = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
+		} else if (MATCH("toplight")) { /* GA 4/14/00 */
+		        if (!retval && MATCH("on"))
+			  new_params.qx3.toplight = 1;
+
+			else if (!retval && MATCH("off")) 
+			  new_params.qx3.toplight = 0;
+
+			else 
+			  retval = -EINVAL;
+                        command_flags |= COMMAND_SETLIGHTS;
+                        
+		} else if (MATCH("bottomlight")) { /* GA 4/14/00 */
+		        if (!retval && MATCH("on"))
+			  new_params.qx3.bottomlight = 1;
+
+			else if (!retval && MATCH("off")) 
+			  new_params.qx3.bottomlight = 0;
+
+			else 
+			  retval = -EINVAL;
+                        command_flags |= COMMAND_SETLIGHTS;
+                        
 		} else {
+			DBG("No match found\n");
 			retval = -EINVAL;
 		}
+
+		if (!retval) {
+			while (count && isspace(*buffer) && *buffer != '\n') {
+				--count;
+				++buffer;
+			}
+			if (count) {
+				if (*buffer != '\n' && *buffer != ';')
+					retval = -EINVAL;
+				else {
+					--count;
+					++buffer;
+				}
+			}
+		}
 	}
 #undef MATCH	
 #undef FIRMWARE_VERSION
 #undef VALUE
 #undef FIND_VALUE
 #undef FIND_END
-	if(retval == 0) {
-		if(command_flags & COMMAND_SETCOLOURPARAMS) {
+	if (!retval) {
+		if (command_flags & COMMAND_SETCOLOURPARAMS) {
 			/* Adjust cam->vp to reflect these changes */
 			cam->vp.brightness =
 				new_params.colourParams.brightness*65535/100;
@@ -1394,9 +1361,8 @@
 		cam->mainsFreq = new_mains;
 		cam->cmd_queue |= command_flags;
 		retval = size;
-	} else {
+	} else
 		DBG("error: %d\n", retval);
-	}
 	
 	up(&cam->param_lock);
 	
@@ -1408,17 +1374,21 @@
 	char name[7];
 	struct proc_dir_entry *ent;
 	
-	if(cpia_proc_root == NULL || cam == NULL) {
+	if (!cpia_proc_root || !cam)
 		return;
-	}
+
 	sprintf(name, "video%d", cam->vdev.minor);
 	
 	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
-	if (!ent) return;
+	if (!ent)
+		return;
+
 	ent->data = cam;
 	ent->read_proc = cpia_read_proc;
 	ent->write_proc = cpia_write_proc;
-	ent->size = 3623;
+	ent->size = 3736;
+	if (cam->params.qx3.qx3_detected)
+		ent->size += 188;
 	cam->proc_entry = ent;
 }
 
@@ -1426,16 +1396,43 @@
 {
 	char name[7];
 	
-	if(cam == NULL || cam->proc_entry == NULL) return;
+	if (!cam || !cam->proc_entry)
+		return;
 	
 	sprintf(name, "video%d", cam->vdev.minor);
 	remove_proc_entry(name, cpia_proc_root);
 	cam->proc_entry = NULL;
 }
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ * We use it here to manage the module use counts.
+ */
+static void proc_cpia_modcount(struct inode *inode, int fill)
+{
+#ifdef MODULE
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+#endif
+}
+#endif
+
 static void proc_cpia_create(void)
 {
 	cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0);
+
+	if (cpia_proc_root) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		cpia_proc_root->fill_inode = &proc_cpia_modcount;
+#else
+		cpia_proc_root->owner = THIS_MODULE;
+#endif
+	} else
+		LOG("Unable to initialise /proc/cpia\n");
 }
 
 #ifdef MODULE
@@ -1444,6 +1441,7 @@
 	remove_proc_entry("cpia", 0);
 }
 #endif
+#endif /* CONFIG_PROC_FS */
 
 /* ----------------------- debug functions ---------------------- */
 
@@ -1473,39 +1471,41 @@
 {
 	/* return the best match, where 'best' is as always
 	 * the largest that is not bigger than what is requested. */
-	if(width>=352 && height>=288) {
+	if (width>=352 && height>=288)
 		return VIDEOSIZE_352_288; /* CIF */
-	}
-	if(width>=320 && height>=240) {
+
+	if (width>=320 && height>=240)
 		return VIDEOSIZE_320_240; /* SIF */
-	}
-	if(width>=288 && height>=216) {
+
+	if (width>=288 && height>=216)
 		return VIDEOSIZE_288_216;
-	}
-	if(width>=256 && height>=192) {
+
+	if (width>=256 && height>=192)
 		return VIDEOSIZE_256_192;
-	}
-	if(width>=224 && height>=168) {
+
+	if (width>=224 && height>=168)
 		return VIDEOSIZE_224_168;
-	}
-	if(width>=192 && height>=144) {
+
+	if (width>=192 && height>=144)
 		return VIDEOSIZE_192_144;
-	}
-	if(width>=176 && height>=144) {
+
+	if (width>=176 && height>=144)
 		return VIDEOSIZE_176_144; /* QCIF */
-	}
-	if(width>=160 && height>=120) {
+
+	if (width>=160 && height>=120)
 		return VIDEOSIZE_160_120; /* QSIF */
-	}
-	if(width>=128 && height>=96) {
+
+	if (width>=128 && height>=96)
 		return VIDEOSIZE_128_96;
-	}
-	if(width>=64 && height>=48) {
+
+	if (width>=88 && height>=72)
+		return VIDEOSIZE_88_72;
+
+	if (width>=64 && height>=48)
 		return VIDEOSIZE_64_48;
-	}
-	if(width>=48 && height>=48) {
+
+	if (width>=48 && height>=48)
 		return VIDEOSIZE_48_48;
-	}
 
 	return -1;
 }
@@ -1608,6 +1608,16 @@
 		cam->params.roi.rowEnd=30;
 		cam->params.streamStartLine = 60;
 		break;
+	case VIDEOSIZE_88_72:
+		cam->vw.width = 88;
+		cam->vw.height = 72;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=5;
+		cam->params.roi.colEnd=16;
+		cam->params.roi.rowStart=9;
+		cam->params.roi.rowEnd=27;
+		cam->params.streamStartLine = 60;
+		break;
 	case VIDEOSIZE_64_48:
 		cam->vw.width = 64;
 		cam->vw.height = 48;
@@ -1635,27 +1645,29 @@
 	return;
 }
 
-static int allocate_frame_buf(struct cam_data *cam) {
+static int allocate_frame_buf(struct cam_data *cam)
+{
 	int i;
 
-	cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE);
-	if (!cam->frame_buf) {
+	cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
+	if (!cam->frame_buf)
 		return -ENOBUFS;
-	}
-	for( i=0; i<FRAME_NUM; i++ ) {
-		cam->frame[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE;
-	}
+
+	for (i = 0; i < FRAME_NUM; i++)
+		cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
+
 	return 0;
 }
 
-static int free_frame_buf(struct cam_data *cam) {
+static int free_frame_buf(struct cam_data *cam)
+{
 	int i;
 	
 	rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
-	cam->frame_buf=0;
-	for( i=0; i<FRAME_NUM; i++ ) {
+	cam->frame_buf = 0;
+	for (i=0; i < FRAME_NUM; i++)
 		cam->frame[i].data = NULL;
-	}
+
 	return 0;
 }
 
@@ -1663,9 +1675,9 @@
 static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
 {
 	int i;
-	for( i=0; i<FRAME_NUM; i++ ) {
-		frame[i].state=FRAME_UNUSED;
-	}
+
+	for (i=0; i < FRAME_NUM; i++)
+		frame[i].state = FRAME_UNUSED;
 	return;
 }
 
@@ -1693,6 +1705,10 @@
 		down(&cam->param_lock);
 		datasize=8;
 		break;
+        case CPIA_COMMAND_ReadMCPorts: /* GA 4/14/00 */
+        case CPIA_COMMAND_ReadVCRegs:
+                datasize = 4;
+                break;
 	default:
 		datasize=0;
 		break;
@@ -1708,12 +1724,12 @@
 	cmd[7] = 0;
 
 	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-	if(retval) {
-		LOG("%x - failed\n", command);
+	if (retval) {
+		DBG("%x - failed, retval=%d\n", command, retval);
 		if (command == CPIA_COMMAND_GetColourParams ||
 		    command == CPIA_COMMAND_GetColourBalance ||
 		    command == CPIA_COMMAND_GetExposure)
-                	up(&cam->param_lock);
+			up(&cam->param_lock);
 	} else {
 		switch(command) {
 		case CPIA_COMMAND_GetCPIAVersion:
@@ -1765,9 +1781,48 @@
 			cam->params.exposure.green1Comp = data[5];
 			cam->params.exposure.green2Comp = data[6];
 			cam->params.exposure.blueComp = data[7];
+			/* If the *Comp parameters are wacko, generate
+			 * a warning, and reset them back to default
+			 * values.             - rich@annexia.org
+			 */
+			if (cam->params.exposure.redComp < 220 ||
+			    cam->params.exposure.redComp > 255 ||
+			    cam->params.exposure.green1Comp < 214 ||
+			    cam->params.exposure.green1Comp > 255 ||
+			    cam->params.exposure.green2Comp < 214 ||
+			    cam->params.exposure.green2Comp > 255 ||
+			    cam->params.exposure.blueComp < 230 ||
+			    cam->params.exposure.blueComp > 255)
+			  {
+			    printk (KERN_WARNING "*_comp parameters have gone AWOL (%d/%d/%d/%d) - reseting them\n",
+				    cam->params.exposure.redComp,
+				    cam->params.exposure.green1Comp,
+				    cam->params.exposure.green2Comp,
+				    cam->params.exposure.blueComp);
+			    cam->params.exposure.redComp = 220;
+			    cam->params.exposure.green1Comp = 214;
+			    cam->params.exposure.green2Comp = 214;
+			    cam->params.exposure.blueComp = 230;
+			  }
 			up(&cam->param_lock);
 			break;
-		default:
+
+                case CPIA_COMMAND_ReadMCPorts: /* GA 04/14/00 */
+                  if (!cam->params.qx3.qx3_detected) break;
+
+		  /* test button press */ 
+		  cam->params.qx3.button = ((data[1] & 0x02) == 0);
+		  if (cam->params.qx3.button) {
+		    /* button pressed - unlock the latch */
+		    do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
+		    do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
+		  }
+
+		  /* test whether microscope is cradled */
+		  cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
+		  break;
+
+ 		default:
 			break;
 		}
 	}
@@ -1782,6 +1837,7 @@
 {
 	int retval;
 	u8 cmd[8], data[8];
+
 	cmd[0] = command>>8;
 	cmd[1] = command&0xff;
 	cmd[2] = a;
@@ -1800,29 +1856,186 @@
 	data[7] = l;
 
 	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-	if(retval) {
+	if (retval)
 		LOG("%x - failed\n", command);
+
+	return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+                      int linesize, int mmap_kludge)
+{
+	int y, u, v, r, g, b, y1;
+
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		r = ((*(rgb+1-linesize)) & 0x7c) << 1;
+		g = ((*(rgb-linesize)) & 0xe0) >> 4 |
+		    ((*(rgb+1-linesize)) & 0x03) << 6;
+		b = ((*(rgb-linesize)) & 0x1f) << 3;
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u - 53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_RGB565:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		r = (*(rgb+1-linesize)) & 0xf8;
+		g = ((*(rgb-linesize)) & 0xe0) >> 3 |
+		    ((*(rgb+1-linesize)) & 0x07) << 5;
+		b = ((*(rgb-linesize)) & 0x1f) << 3;
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u - 53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_RGB24:
+	case VIDEO_PALETTE_RGB32:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		if (mmap_kludge) {
+			r = *(rgb+2-linesize);
+			g = *(rgb+1-linesize);
+			b = *(rgb-linesize);
+		} else {
+			r = *(rgb-linesize);
+			g = *(rgb+1-linesize);
+			b = *(rgb+2-linesize);
+		}
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u + -53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		y = *yuv++;
+		u = *(rgb+1-linesize);
+		y1 = *yuv;
+		v = *(rgb+3-linesize);
+		/* Just to avoid compiler warnings */
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	case VIDEO_PALETTE_UYVY:
+		u = *(rgb-linesize);
+		y = *yuv++;
+		v = *(rgb+2-linesize);
+		y1 = *yuv;
+		/* Just to avoid compiler warnings */
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	case VIDEO_PALETTE_GREY:
+	default:
+		y = *yuv++;
+		y1 = *yuv;
+		/* Just to avoid compiler warnings */
+		u = 0;
+		v = 0;
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	}
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+		*rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+		*rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+		*rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+		*rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+		return 4;
+	case VIDEO_PALETTE_RGB565:
+		*rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+		*rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+		*rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+		*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+		return 4;
+	case VIDEO_PALETTE_RGB24:
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
+		return 6;
+	case VIDEO_PALETTE_RGB32:
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			rgb++;
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			rgb++;
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
+		return 8;
+	case VIDEO_PALETTE_GREY:
+		*rgb++ = y;
+		*rgb = y1;
+		return 2;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		*rgb++ = y;
+		*rgb++ = u;
+		*rgb++ = y1;
+		*rgb = v;
+		return 4;
+	case VIDEO_PALETTE_UYVY:
+		*rgb++ = u;
+		*rgb++ = y;
+		*rgb++ = v;
+		*rgb = y1;
+		return 4;
+	default:
+		DBG("Empty: %d\n", out_fmt);
+		return 0;
 	}
-	return retval;
 }
 
-/**********************************************************************
- *
- * Colorspace conversion
- *
- **********************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-
 static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
-                      int in_uyvy)
+                      int in_uyvy, int mmap_kludge)
 {
 	int y, u, v, r, g, b, y1;
+
 	switch(out_fmt) {
 	case VIDEO_PALETTE_RGB555:
 	case VIDEO_PALETTE_RGB565:
 	case VIDEO_PALETTE_RGB24:
 	case VIDEO_PALETTE_RGB32:
-		if(in_uyvy) {
+		if (in_uyvy) {
 			u = *yuv++ - 128;
 			y = (*yuv++ - 16) * 76310;
 			v = *yuv++ - 128;
@@ -1862,21 +2075,40 @@
 		*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
 		return 4;
 	case VIDEO_PALETTE_RGB24:
-		*rgb++ = LIMIT(b+y);
-		*rgb++ = LIMIT(g+y);
-		*rgb++ = LIMIT(r+y);
-		*rgb++ = LIMIT(b+y1);
-		*rgb++ = LIMIT(g+y1);
-		*rgb = LIMIT(r+y1);
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
 		return 6;
 	case VIDEO_PALETTE_RGB32:
-		*rgb++ = LIMIT(b+y);
-		*rgb++ = LIMIT(g+y);
-		*rgb++ = LIMIT(r+y);
-		rgb++;
-		*rgb++ = LIMIT(b+y1);
-		*rgb++ = LIMIT(g+y1);
-		*rgb = LIMIT(r+y1);
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			rgb++;
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			rgb++;
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
 		return 8;
 	case VIDEO_PALETTE_GREY:
 		*rgb++ = y;
@@ -1896,11 +2128,13 @@
 		*rgb = y1;
 		return 4;
 	default:
+		DBG("Empty: %d\n", out_fmt);
 		return 0;
 	}
 }
 
-static int skipcount(int count, int fmt) {
+static int skipcount(int count, int fmt)
+{
 	switch(fmt) {
 	case VIDEO_PALETTE_GREY:
 		return count;
@@ -1922,66 +2156,68 @@
 static int parse_picture(struct cam_data *cam, int size)
 {
 	u8 *obuf, *ibuf, *end_obuf;
-	int ll, in_uyvy, compressed, origsize, out_fmt;
+	int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
+	int rows, cols, linesize, subsample_422;
 
 	/* make sure params don't change while we are decoding */
 	down(&cam->param_lock);
-	
+
 	obuf = cam->decompressed_frame.data;
 	end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
 	ibuf = cam->raw_image;
 	origsize = size;
 	out_fmt = cam->vp.palette;
-	
-	if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+
+	if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
 		LOG("header not found\n");
 		up(&cam->param_lock);
 		return -1;
 	}
 
-	if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+	if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
 		LOG("wrong video size\n");
 		up(&cam->param_lock);
 		return -1;
 	}
 	
-	if(ibuf[17] != SUBSAMPLE_422) {
+	if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
 		LOG("illegal subtype %d\n",ibuf[17]);
 		up(&cam->param_lock);
 		return -1;
 	}
+	subsample_422 = ibuf[17] == SUBSAMPLE_422;
 	
-	if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+	if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
 		LOG("illegal yuvorder %d\n",ibuf[18]);
 		up(&cam->param_lock);
 		return -1;
 	}
 	in_uyvy = ibuf[18] == YUVORDER_UYVY;
 	
-#if 0
-	/* FIXME: ROI mismatch occurs when switching capture sizes */
-	if((ibuf[24] != cam->params.roi.colStart) ||
-	   (ibuf[25] != cam->params.roi.colEnd) ||
-	   (ibuf[26] != cam->params.roi.rowStart) ||
-	   (ibuf[27] != cam->params.roi.rowEnd)) {
+	if ((ibuf[24] != cam->params.roi.colStart) ||
+	    (ibuf[25] != cam->params.roi.colEnd) ||
+	    (ibuf[26] != cam->params.roi.rowStart) ||
+	    (ibuf[27] != cam->params.roi.rowEnd)) {
 		LOG("ROI mismatch\n");
 		up(&cam->param_lock);
 		return -1;
 	}
-#endif
+	cols = 8*(ibuf[25] - ibuf[24]);
+	rows = 4*(ibuf[27] - ibuf[26]);
 	
-	if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+	if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
 		LOG("illegal compression %d\n",ibuf[28]);
 		up(&cam->param_lock);
 		return -1;
 	}
 	compressed = (ibuf[28] == COMPRESSED);
 	
-	if(ibuf[29] != NO_DECIMATION) {
-		LOG("decimation not supported\n");
+	if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
+		LOG("illegal decimation %d\n",ibuf[29]);
 		up(&cam->param_lock);
 		return -1;
 	}
+	decimation = (ibuf[29] == DECIMATION_ENAB);
 	
 	cam->params.yuvThreshold.yThreshold = ibuf[30];
 	cam->params.yuvThreshold.uvThreshold = ibuf[31];
@@ -1994,98 +2230,135 @@
 	cam->params.status.vpStatus = ibuf[38];
 	cam->params.status.errorCode = ibuf[39];
 	cam->fps = ibuf[41];
+	up(&cam->param_lock);
 	
-	ibuf += 64;
-	size -= 64;
+	linesize = skipcount(cols, out_fmt);
+	ibuf += FRAME_HEADER_SIZE;
+	size -= FRAME_HEADER_SIZE;
 	ll = ibuf[0] | (ibuf[1] << 8);
 	ibuf += 2;
-	while(size > 0) {
+	even_line = 1;
+
+	while (size > 0) {
 		size -= (ll+2);
-		if(size < 0) {
+		if (size < 0) {
 			LOG("Insufficient data in buffer\n");
-			up(&cam->param_lock);
 			return -1;
 		}
-		while(ll > 1) {
-			if(!compressed || (compressed && !(*ibuf & 1))) {
-				obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy);
-				ibuf += 4;
-				ll -= 4;
+
+		while (ll > 1) {
+			if (!compressed || (compressed && !(*ibuf & 1))) {
+				if(subsample_422 || even_line) {
+					obuf += yuvconvert(ibuf, obuf, out_fmt,
+			                	   in_uyvy, cam->mmap_kludge);
+					ibuf += 4;
+					ll -= 4;
+				} else {
+					/* SUBSAMPLE_420 on an odd line */
+					obuf += convert420(ibuf, obuf,
+					                   out_fmt, linesize,
+			                		   cam->mmap_kludge);
+					ibuf += 2;
+					ll -= 2;
+				}
 			} else {
 				/*skip compressed interval from previous frame*/
-				int skipsize = skipcount(*ibuf >> 1, out_fmt);
-				obuf += skipsize;
-				if( obuf > end_obuf ) {
-					LOG("Insufficient data in buffer\n");
-					up(&cam->param_lock);
+				obuf += skipcount(*ibuf >> 1, out_fmt);
+				if (obuf > end_obuf) {
+					LOG("Insufficient buffer size\n");
 					return -1;
 				}
 				++ibuf;
 				ll--;
 			}
 		}
-		if(ll == 1) {
-			if(*ibuf != EOL) {
+		if (ll == 1) {
+			if (*ibuf != EOL) {
 				LOG("EOL not found giving up after %d/%d"
 				    " bytes\n", origsize-size, origsize);
-				up(&cam->param_lock);
 				return -1;
 			}
 
-			ibuf++; /* skip over EOL */
+			++ibuf; /* skip over EOL */
 
-			if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+			if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
 			   (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
 			 	size -= 4;
 				break;
 			}
 
-			if(size > 1) {
+			if(decimation) {
+				/* skip the odd lines for now */
+				obuf += linesize;
+			}
+			
+			if (size > 1) {
 				ll = ibuf[0] | (ibuf[1] << 8);
 				ibuf += 2; /* skip over line length */
 			}
+			
+			if(!decimation)
+				even_line = !even_line;
 		} else {
 			LOG("line length was not 1 but %d after %d/%d bytes\n",
 			    ll, origsize-size, origsize);
-			up(&cam->param_lock);
 			return -1;
 		}
 	}
 	
+	if(decimation) {
+		/* interpolate odd rows */
+		int i, j;
+		u8 *prev, *next;
+		prev = cam->decompressed_frame.data;
+		obuf = prev+linesize;
+		next = obuf+linesize;
+		for(i=1; i<rows-1; i+=2) {
+			for(j=0; j<linesize; ++j) {
+				*obuf++ = ((int)*prev++ + *next++) / 2;
+			}
+			prev += linesize;
+			obuf += linesize;
+			next += linesize;
+		}
+		/* last row is odd, just copy previous row */
+		memcpy(obuf, prev, linesize);
+	}
+	
 	cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
 
-	up(&cam->param_lock);
-	
 	return cam->decompressed_frame.count;
 }
 
 /* InitStreamCap wrapper to select correct start line */
-static inline int init_stream_cap(struct cam_data *cam) {
+static inline int init_stream_cap(struct cam_data *cam)
+{
 	return do_command(cam, CPIA_COMMAND_InitStreamCap,
 	                  0, cam->params.streamStartLine, 0, 0);
 }
 
 /* update various camera modes and settings */
-static void dispatch_commands(struct cam_data *cam ) {
+static void dispatch_commands(struct cam_data *cam)
+{
 	down(&cam->param_lock);
-	if( cam->cmd_queue==COMMAND_NONE ) {
+	if (cam->cmd_queue==COMMAND_NONE) {
 		up(&cam->param_lock);
 		return;
 	}
 	DEB_BYTE(cam->cmd_queue);
 	DEB_BYTE(cam->cmd_queue>>8);
-	if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) {
+	if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
 		do_command(cam, CPIA_COMMAND_SetColourParams,
 		           cam->params.colourParams.brightness,
 		           cam->params.colourParams.contrast,
 		           cam->params.colourParams.saturation, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
 		do_command(cam, CPIA_COMMAND_SetCompression,
 		           cam->params.compression.mode,
 			   cam->params.compression.decimation, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETFORMAT ) {
+
+	if (cam->cmd_queue & COMMAND_SETFORMAT) {
 		do_command(cam, CPIA_COMMAND_SetFormat,
 	        	   cam->params.format.videoSize,
 	        	   cam->params.format.subSample,
@@ -2095,22 +2368,23 @@
 	        	   cam->params.roi.rowStart, cam->params.roi.rowEnd);
 		cam->first_frame = 1;
 	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
 		do_command(cam, CPIA_COMMAND_SetCompressionTarget,
 		           cam->params.compressionTarget.frTargeting,
 		           cam->params.compressionTarget.targetFR,
 		           cam->params.compressionTarget.targetQ, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) {
+
+	if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
 		do_command(cam, CPIA_COMMAND_SetYUVThresh,
 		           cam->params.yuvThreshold.yThreshold,
 		           cam->params.yuvThreshold.uvThreshold, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETECPTIMING ) {
+
+	if (cam->cmd_queue & COMMAND_SETECPTIMING)
 		do_command(cam, CPIA_COMMAND_SetECPTiming,
 		           cam->params.ecpTiming, 0, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
 		do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
 		            0, 0, 0, 0,
 		            cam->params.compressionParams.hysteresis,
@@ -2121,8 +2395,8 @@
 		            cam->params.compressionParams.frDiffStepThresh,
 		            cam->params.compressionParams.qDiffStepThresh,
 		            cam->params.compressionParams.decimationThreshMod);
-	}
-	if( cam->cmd_queue & COMMAND_SETEXPOSURE ) {
+
+	if (cam->cmd_queue & COMMAND_SETEXPOSURE)
 		do_command_extended(cam, CPIA_COMMAND_SetExposure,
 		                    cam->params.exposure.gainMode,
 		                    cam->params.exposure.expMode,
@@ -2136,150 +2410,191 @@
 		                    cam->params.exposure.green1Comp,
 		                    cam->params.exposure.green2Comp,
 		                    cam->params.exposure.blueComp);
+
+	if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
+		if (cam->params.colourBalance.balanceModeIsAuto) {
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   2, 0, 0, 0);
+		} else {
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   1,
+				   cam->params.colourBalance.redGain,
+				   cam->params.colourBalance.greenGain,
+				   cam->params.colourBalance.blueGain);
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   3, 0, 0, 0);
+		}
 	}
-	if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) {
-		do_command(cam, CPIA_COMMAND_SetColourBalance,
-		           cam->params.colourBalance.balanceMode,
-		           cam->params.colourBalance.redGain,
-		           cam->params.colourBalance.greenGain,
-		           cam->params.colourBalance.blueGain);
-	}
-	if( cam->cmd_queue & COMMAND_SETSENSORFPS ) {
+
+	if (cam->cmd_queue & COMMAND_SETSENSORFPS)
 		do_command(cam, CPIA_COMMAND_SetSensorFPS,
 		           cam->params.sensorFps.divisor,
 		           cam->params.sensorFps.baserate, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETAPCOR ) {
+
+	if (cam->cmd_queue & COMMAND_SETAPCOR)
 		do_command(cam, CPIA_COMMAND_SetApcor,
 		           cam->params.apcor.gain1,
 		           cam->params.apcor.gain2,
 		           cam->params.apcor.gain4,
 		           cam->params.apcor.gain8);
-	}
-	if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) {
+
+	if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
 		do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
 		           cam->params.flickerControl.flickerMode,
 		           cam->params.flickerControl.coarseJump,
 		           cam->params.flickerControl.allowableOverExposure, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETVLOFFSET ) {
+
+	if (cam->cmd_queue & COMMAND_SETVLOFFSET)
 		do_command(cam, CPIA_COMMAND_SetVLOffset,
 		           cam->params.vlOffset.gain1,
 		           cam->params.vlOffset.gain2,
 		           cam->params.vlOffset.gain4,
 		           cam->params.vlOffset.gain8);
-	}
-	if( cam->cmd_queue & COMMAND_PAUSE ) {
+
+	if (cam->cmd_queue & COMMAND_PAUSE)
 		do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_RESUME ) {
-		init_stream_cap( cam );
-	}
+
+	if (cam->cmd_queue & COMMAND_RESUME)
+		init_stream_cap(cam);
+
+	/* GA 04/14/00 */
+	if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
+	  {
+	    int p1 = (cam->params.qx3.bottomlight == 0) << 1;
+            int p2 = (cam->params.qx3.toplight == 0) << 3;
+            do_command(cam, CPIA_COMMAND_WriteVCReg,  0x90, 0x8F, 0x50, 0);
+            do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
+	  }
+
 	up(&cam->param_lock);
-	cam->cmd_queue=COMMAND_NONE;
+	cam->cmd_queue = COMMAND_NONE;
 	return;
 }
 
 /* kernel thread function to read image from camera */
 static void fetch_frame(void *data)
 {
-	int image_size;
+	int image_size, retry;
 	struct cam_data *cam = (struct cam_data *)data;
 	unsigned long oldjif, rate, diff;
 
-        /* load first frame always uncompressed */
-        if( cam->first_frame &&
-            cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
-                do_command(cam, CPIA_COMMAND_SetCompression,
-                           CPIA_COMPRESSION_NONE,
-                           NO_DECIMATION, 0, 0);
-        }
-
-        /* init camera upload */
-        if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS,
-                      0, 0, 0)) goto end;
-        if(do_command(cam, CPIA_COMMAND_GrabFrame, 0,
-                      cam->params.streamStartLine, 0, 0)) goto end;
-
-	/* loop until image ready */
-	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
-	while(cam->params.status.streamState != STREAM_READY) {
-		if(current->need_resched) {schedule();}
-		current->state=TASK_INTERRUPTIBLE;
-		schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */
-		if(signal_pending(current)) {
-			goto end;
-		}
-		do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
-	}
-
-	/* grab image from camera */
-	if(current->need_resched) {schedule();}
-	oldjif = jiffies;
-	image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
-	if(image_size<=0) {
-		DBG("read frame failed\n");
-		goto end;
-	}
-	rate = image_size * HZ / 1024;
-	diff = jiffies-oldjif;
-	rate = diff==0 ? rate : rate/diff; /* unlikely but possible */
-	/* Keep track of transfer_rate as a runnung average over 3 frames
-	 * to smooth out any inconsistencies */
-	cam->transfer_rate = (2*cam->transfer_rate + rate) / 3;
-
-	/* camera idle now so dispatch queued commands */
-	dispatch_commands( cam );
-
-	/* Update our knowledge of the camera state - FIXME: necessary? */
-	do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
-	do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
-	
-	/* decompress and convert image to by copying it from
-	*  raw_image to decompressed_frame
-	*/
-	if(current->need_resched) {schedule();}
-	cam->image_size = parse_picture(cam, image_size);
-	if( cam->image_size <= 0 ) {
-		DBG("parse frame failed\n");
-		goto end;
-	}
-
-	/* FIXME: this only works for double buffering */
-	if( cam->frame[cam->curframe].state == FRAME_READY ) {
-		memcpy(cam->frame[cam->curframe].data,
-		       cam->decompressed_frame.data,
-		       cam->decompressed_frame.count);
-		cam->frame[cam->curframe].state = FRAME_DONE;
-	} else {
-		cam->decompressed_frame.state = FRAME_DONE;
-	}
+	/* Allow up to two bad images in a row to be read and
+	 * ignored before an error is reported */
+	for (retry = 0; retry < 3; ++retry) {
+		if (retry)
+			DBG("retry=%d\n", retry);
+
+		if (!cam->ops)
+			continue;
+
+		/* load first frame always uncompressed */
+		if (cam->first_frame &&
+		    cam->params.compression.mode != CPIA_COMPRESSION_NONE)
+			do_command(cam, CPIA_COMMAND_SetCompression,
+				   CPIA_COMPRESSION_NONE,
+				   NO_DECIMATION, 0, 0);
+
+		/* init camera upload */
+		if (do_command(cam, CPIA_COMMAND_SetGrabMode,
+			       CPIA_GRAB_CONTINUOUS, 0, 0, 0))
+			continue;
+
+		if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+			       cam->params.streamStartLine, 0, 0))
+			continue;
+
+		if (cam->ops->wait_for_stream_ready) {
+			/* loop until image ready */
+			do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+			while (cam->params.status.streamState != STREAM_READY) {
+				if (current->need_resched)
+					schedule();
+
+				current->state = TASK_INTERRUPTIBLE;
+
+				/* sleep for 10 ms, hopefully ;) */
+				schedule_timeout(10*HZ/1000);
+				if (signal_pending(current))
+					return;
+
+				do_command(cam, CPIA_COMMAND_GetCameraStatus,
+				           0, 0, 0, 0);
+			}
+		}
+
+		/* grab image from camera */
+		if (current->need_resched)
+			schedule();
+
+		oldjif = jiffies;
+		image_size = cam->ops->streamRead(cam->lowlevel_data,
+						  cam->raw_image, 0);
+		if (image_size <= 0) {
+			DBG("streamRead failed: %d\n", image_size);
+			continue;
+		}
+
+		rate = image_size * HZ / 1024;
+		diff = jiffies-oldjif;
+		cam->transfer_rate = diff==0 ? rate : rate/diff;
+			/* diff==0 ? unlikely but possible */
+
+		/* camera idle now so dispatch queued commands */
+		dispatch_commands(cam);
+
+		/* Update our knowledge of the camera state - FIXME: necessary? */
+		do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+		do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+		do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); /* GA */
+
+
+		/* decompress and convert image to by copying it from
+		 * raw_image to decompressed_frame
+		 */
+		if (current->need_resched)
+			schedule();
+
+		cam->image_size = parse_picture(cam, image_size);
+		if (cam->image_size <= 0)
+			DBG("parse_picture failed %d\n", cam->image_size);
+		else
+			break;
+	}
+
+	if (retry < 3) {
+		/* FIXME: this only works for double buffering */
+		if (cam->frame[cam->curframe].state == FRAME_READY) {
+			memcpy(cam->frame[cam->curframe].data,
+			       cam->decompressed_frame.data,
+			       cam->decompressed_frame.count);
+			cam->frame[cam->curframe].state = FRAME_DONE;
+		} else
+			cam->decompressed_frame.state = FRAME_DONE;
 
 #if 0
-	if( cam->first_frame &&
-	    cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
-		cam->first_frame = 0;
-		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
-	}
+		if (cam->first_frame &&
+		    cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
+			cam->first_frame = 0;
+			cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+		}
 #else
-	if( cam->first_frame ) {
-		cam->first_frame = 0;
-		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
-		cam->cmd_queue |= COMMAND_SETEXPOSURE;
-	}
+		if (cam->first_frame) {
+			cam->first_frame = 0;
+			cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+			cam->cmd_queue |= COMMAND_SETEXPOSURE;
+		}
 #endif
-end:
-	return;
+	}
 }
 
 static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
 {
 	int retval = 0;
 
-	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
-		if( (retval = allocate_frame_buf(cam)) ) {
+	if (!cam->frame_buf) {
+		/* we do lazy allocation */
+		if ((retval = allocate_frame_buf(cam)))
 			return retval;
-		}
 	}
 	
 	/* FIXME: the first frame seems to be captured by the camera
@@ -2287,25 +2602,30 @@
 	   that one, the next one is generated with our settings
 	   (exposure, color balance, ...)
 	*/
-	if(cam->first_frame) {
-	  cam->curframe = vm->frame;
-	  cam->frame[cam->curframe].state = FRAME_READY;
-	  fetch_frame(cam);
-	  if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+	if (cam->first_frame) {
+		cam->curframe = vm->frame;
+		cam->frame[cam->curframe].state = FRAME_READY;
+		fetch_frame(cam);
+		if (cam->frame[cam->curframe].state != FRAME_DONE)
+			retval = -EIO;
 	}
 	cam->curframe = vm->frame;
-        cam->frame[cam->curframe].state = FRAME_READY;
-        fetch_frame(cam);
-        if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+	cam->frame[cam->curframe].state = FRAME_READY;
+	fetch_frame(cam);
+	if (cam->frame[cam->curframe].state != FRAME_DONE)
+		retval=-EIO;
 
 	return retval;
 }
   
 static int goto_high_power(struct cam_data *cam)
 {
-	if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1;
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
-	if(cam->params.status.systemState == HI_POWER_STATE) {
+	if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
+		return -1;
+	mdelay(100);		/* windows driver does it too */
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+		return -1;
+	if (cam->params.status.systemState == HI_POWER_STATE) {
 		DBG("camera now in HIGH power state\n");
 		return 0;
 	}
@@ -2315,14 +2635,15 @@
 
 static int goto_low_power(struct cam_data *cam)
 {
-	if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1;
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
-	if(cam->params.status.systemState == LO_POWER_STATE) {
+	if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
+		return -1;
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+		return -1;
+	if (cam->params.status.systemState == LO_POWER_STATE) {
 		DBG("camera now in LOW power state\n");
 		return 0;
 	}
 	printstatus(cam);
-	mdelay(100);		/* windows driver does it too */
 	return -1;
 }
 
@@ -2344,21 +2665,23 @@
 	     cam->params.colourBalance.redGain,
 	     cam->params.colourBalance.greenGain,
 	     cam->params.colourBalance.blueGain);
-
-	return;
 }
 
 static void set_camera_state(struct cam_data *cam)
 {
-	if(cam->params.colourBalance.balanceMode != 1) {
+	if(cam->params.colourBalance.balanceModeIsAuto) {
+		do_command(cam, CPIA_COMMAND_SetColourBalance, 
+	        	   2, 0, 0, 0);
+	} else {
 		do_command(cam, CPIA_COMMAND_SetColourBalance, 
 	        	   1,
 	        	   cam->params.colourBalance.redGain,
 	        	   cam->params.colourBalance.greenGain,
 	        	   cam->params.colourBalance.blueGain);
+		do_command(cam, CPIA_COMMAND_SetColourBalance, 
+	        	   3, 0, 0, 0);
 	}
-	if(cam->params.colourBalance.balanceMode == 0)
-		cam->params.colourBalance.balanceMode = 2;
+
 
 	do_command_extended(cam, CPIA_COMMAND_SetExposure,
 			    cam->params.exposure.gainMode, 1, 1,
@@ -2367,19 +2690,19 @@
 	                    cam->params.exposure.fineExp,
 	                    cam->params.exposure.coarseExpLo,
 	                    cam->params.exposure.coarseExpHi,
-	                    cam->params.exposure.redComp,
-	                    cam->params.exposure.green1Comp,
-	                    cam->params.exposure.green2Comp,
-	                    cam->params.exposure.blueComp);
+			    cam->params.exposure.redComp,
+			    cam->params.exposure.green1Comp,
+			    cam->params.exposure.green2Comp,
+			    cam->params.exposure.blueComp);
 	do_command_extended(cam, CPIA_COMMAND_SetExposure,
 			    0, 3, 0, 0,
 			    0, 0, 0, 0, 0, 0, 0, 0);
 
-	if(cam->params.exposure.gainMode == 0)
+	if (!cam->params.exposure.gainMode)
 		cam->params.exposure.gainMode = 2;
-	if(cam->params.exposure.expMode == 0)
+	if (!cam->params.exposure.expMode)
 		cam->params.exposure.expMode = 2;
-	if(cam->params.exposure.centreWeight == 0)
+	if (!cam->params.exposure.centreWeight)
 		cam->params.exposure.centreWeight = 1;
 	
 	cam->cmd_queue = COMMAND_SETCOMPRESSION |
@@ -2407,32 +2730,25 @@
 {
 	/* GetCPIAVersion */
 	do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
-	printk(KERN_INFO "  CPIA Version: %d.%02d (%d.%d)\n",
-	       cam->params.version.firmwareVersion,
-	       cam->params.version.firmwareRevision,
-	       cam->params.version.vcVersion,
-	       cam->params.version.vcRevision);
 
 	/* GetPnPID */
 	do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
-	printk(KERN_INFO "  CPIA PnP-ID: %04x:%04x:%04x\n",
-	       cam->params.pnpID.vendor, cam->params.pnpID.product,
-	       cam->params.pnpID.deviceRevision);
 }
 
 /* initialize camera */
 static int reset_camera(struct cam_data *cam)
 {
 	/* Start the camera in low power mode */
-	if(goto_low_power(cam)) {
-		if( cam->params.status.systemState != 0x4 ) {
+	if (goto_low_power(cam)) {
+		if (cam->params.status.systemState != WARM_BOOT_STATE)
 			return -ENODEV;
-		}
+
 		/* FIXME: this is just dirty trial and error */
 		reset_camera_struct(cam);
 		goto_high_power(cam);
 		do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
-		if(goto_low_power(cam)) return -NODEV;
+		if (goto_low_power(cam))
+			return -NODEV;
 	}
 	
 	/* procedure described in developer's guide p3-28 */
@@ -2440,11 +2756,15 @@
 	/* Check the firmware version FIXME: should we check PNPID? */
 	cam->params.version.firmwareVersion = 0;
 	get_version_information(cam);
-	if(cam->params.version.firmwareVersion != 1) {
+	if (cam->params.version.firmwareVersion != 1)
 		return -ENODEV;
-	}
+	
+	/* GA 04/14/00 - set QX3 detected flag */
+	cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
+					cam->params.pnpID.product == 0x0001);
 
-	/* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */
+	/* The fatal error checking should be done after
+	 * the camera powers up (developer's guide p 3-38) */
 
 	/* Set streamState before transition to high power to avoid bug
 	 * in firmware 1-02 */
@@ -2452,40 +2772,38 @@
 	           STREAM_NOT_READY, 0);
 	
 	/* GotoHiPower */
-	if(goto_high_power(cam))
+	if (goto_high_power(cam))
 		return -ENODEV;
 
-	
 	/* Check the camera status */
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
 		return -EIO;
-	if(cam->params.status.fatalError) {
-		DBG("fatal_error:              %#04x\n",cam->params.status.fatalError);
-		DBG("vp_status:                %#04x\n",cam->params.status.vpStatus);
-		if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+
+	if (cam->params.status.fatalError) {
+		DBG("fatal_error:              %#04x\n",
+		    cam->params.status.fatalError);
+		DBG("vp_status:                %#04x\n",
+		    cam->params.status.vpStatus);
+		if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
 			/* Fatal error in camera */
 			return -EIO;
-		} else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){
+		} else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
 			/* Firmware 1-02 may do this for parallel port cameras,
 			 * just clear the flags (developer's guide p 3-38) */
 			do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
 			           FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
 		}
 	}
-		
+	
 	/* Check the camera status again */
-	if(cam->params.status.fatalError) {
-		if(cam->params.status.fatalError)
+	if (cam->params.status.fatalError) {
+		if (cam->params.status.fatalError)
 			return -EIO;
 	}
 	
 	/* VPVersion can't be retrieved before the camera is in HiPower,
 	 * so get it here instead of in get_version_information. */
 	do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
-	printk(KERN_INFO "  VP-Version: %d.%d %04x\n",
-	       cam->params.vpVersion.vpVersion,
-	       cam->params.vpVersion.vpRevision,
-	       cam->params.vpVersion.cameraHeadID);
 
 	/* set camera to a known state */
 	set_camera_state(cam);
@@ -2499,54 +2817,60 @@
 	int i;
 	struct cam_data *cam = dev->priv;
 
-	DBG("cpia_open\n");
-
-	if(cam->open_count > 0) {
-		DBG("Camera[%d] already open\n",cam->index);
+	if (!cam) {
+		DBG("Internal error, cam_data not found!\n");
+		return -EBUSY;
+	}
+	    
+	if (cam->open_count > 0) {
+		DBG("Camera already open\n");
 		return -EBUSY;
 	}
 	
-	if(cam->raw_image == NULL) {
-		if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){
+	if (!cam->raw_image) {
+		cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
+		if (!cam->raw_image)
 			return -ENOMEM;
-		}
 	}
-	if(cam->decompressed_frame.data == NULL) {
-		cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE);
-		if(cam->decompressed_frame.data == NULL){
+
+	if (!cam->decompressed_frame.data) {
+		cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
+		if (!cam->decompressed_frame.data) {
 			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-			cam->raw_image=NULL;
+			cam->raw_image = NULL;
 			return -ENOMEM;
 		}
 	}
 	
 	/* open cpia */
-	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+	if (cam->ops->open(cam->lowlevel_data)) {
 		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
+		cam->decompressed_frame.data = NULL;
 		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
+		cam->raw_image = NULL;
 		return -ENODEV;
 	}
 	
 	/* reset the camera */
-	if((i = reset_camera(cam)) != 0) {
+	if ((i = reset_camera(cam)) != 0) {
 		cam->ops->close(cam->lowlevel_data);
 		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
+		cam->decompressed_frame.data = NULL;
 		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
+		cam->raw_image = NULL;
 		return i;
 	}
 	
 	/* Set ownership of /proc/cpia/videoX to current user */
-	if(cam->proc_entry != NULL)
+	if(cam->proc_entry)
 		cam->proc_entry->uid = current->uid;
-	
+
 	/* set mark for loading first frame uncompressed */
 	cam->first_frame = 1;
-	cam->busy_lock = MUTEX;
-        
+
+	/* init it to something */
+	cam->mmap_kludge = 0;
+	
 	++cam->open_count;
 #ifdef MODULE
 	MOD_INC_USE_COUNT;
@@ -2554,78 +2878,51 @@
 	return 0;
 }
 
-/* FIXME */
 static void cpia_close(struct video_device *dev)
 {
 	struct cam_data *cam;
 
-	DBG("cpia_close\n");
 	cam = dev->priv;
 
-	if(cam->ops == NULL) {
-		if(--cam->open_count == 0) {
-			int i;
-			video_unregister_device(dev);
-			/* Need a lock when adding/removing cameras */
-			lock_kernel();
-			if(cam->raw_image) {
-				rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-				cam->raw_image=0;
-			}
-			if(cam->decompressed_frame.data) {
-				rvfree(cam->decompressed_frame.data,
-				       CPIA_MAX_FRAME_SIZE);
-				cam->decompressed_frame.data=0;
-			}
-			if(cam->frame_buf) {
-				free_frame_buf(cam);
-			}
-			for(i=0; i<CPIA_MAXCAMS; ++i) {
-				if(camera[i] == cam) break;
-			}
-			kfree(cam);
-			if(i != CPIA_MAXCAMS) { /* should always be true */
-				camera[i] = NULL;
-			}
-			unlock_kernel();
-		}
-#ifdef MODULE
-		MOD_DEC_USE_COUNT;
-#endif
-		return;
-	}
+	if (cam->ops) {
+	        /* Return ownership of /proc/cpia/videoX to root */
+		if(cam->proc_entry)
+			cam->proc_entry->uid = 0;
 	
-	/* Return ownership of /proc/cpia/videoX to root */
-	if(cam->proc_entry != NULL)
-		cam->proc_entry->uid = 0;
-	
-	/* save camera state for later open (developers guide ch 3.5.3) */
-	save_camera_state(cam);
+		/* save camera state for later open (developers guide ch 3.5.3) */
+		save_camera_state(cam);
 
-	/* GotoLoPower */
-	goto_low_power(cam);
+		/* GotoLoPower */
+		goto_low_power(cam);
 
-	/* cleanup internal state stuff */
-	free_frames(cam->frame);
+		/* Update the camera ststus */
+		do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
 
-	/* Update the camera ststus */
-	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+		/* cleanup internal state stuff */
+		free_frames(cam->frame);
 
-	/* close cpia */
-	cam->ops->close(cam->lowlevel_data);
+		/* close cpia */
+		cam->ops->close(cam->lowlevel_data);
+	}
 
-	if(--cam->open_count == 0) {
+	if (--cam->open_count == 0) {
 		/* clean up capture-buffers */
-		if(cam->raw_image) {
+		if (cam->raw_image) {
 			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-			cam->raw_image = 0;
+			cam->raw_image = NULL;
 		}
-		if(cam->decompressed_frame.data) {
+
+		if (cam->decompressed_frame.data) {
 			rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-			cam->decompressed_frame.data = 0;
+			cam->decompressed_frame.data = NULL;
 		}
-		if(cam->frame_buf) {
+
+		if (cam->frame_buf)
 			free_frame_buf(cam);
+
+		if (!cam->ops) {
+			video_unregister_device(dev);
+			kfree(cam);
 		}
 	}
 	
@@ -2637,26 +2934,27 @@
 }
 
 static long cpia_read(struct video_device *dev, char *buf,
-                     unsigned long count, int noblock)
+                      unsigned long count, int noblock)
 {
 	struct cam_data *cam = dev->priv;
 
 	/* make this _really_ smp and multithredi-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
 
 	if (!buf) {
 		DBG("buf NULL\n");
 		up(&cam->busy_lock);
 		return -EINVAL;
 	}
-	if(count == 0) {
+
+	if (!count) {
 		DBG("count 0\n");
 		up(&cam->busy_lock);
 		return 0;
 	}
-	if(!cam->ops) {
+
+	if (!cam->ops) {
 		DBG("ops NULL\n");
 		up(&cam->busy_lock);
 		return -ENODEV;
@@ -2664,8 +2962,9 @@
 
 	/* upload frame */
 	cam->decompressed_frame.state = FRAME_READY;
+	cam->mmap_kludge=0;
 	fetch_frame(cam);
-	if( cam->decompressed_frame.state != FRAME_DONE ) {
+	if (cam->decompressed_frame.state != FRAME_DONE) {
 		DBG("upload failed %d/%d\n", cam->decompressed_frame.count,
 		    cam->decompressed_frame.state);
 		up(&cam->busy_lock);
@@ -2674,12 +2973,13 @@
 	cam->decompressed_frame.state = FRAME_UNUSED;
 
 	/* copy data to user space */
-	if(cam->decompressed_frame.count > count) {
-		DBG("count wrong\n");
+	if (cam->decompressed_frame.count > count) {
+		DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
+		    count);
 		up(&cam->busy_lock);
 		return -EFAULT;
 	}
-	if(copy_to_user(buf, cam->decompressed_frame.data,
+	if (copy_to_user(buf, cam->decompressed_frame.data,
 	                cam->decompressed_frame.count)) {
 		DBG("copy_to_user failed\n");
 		up(&cam->busy_lock);
@@ -2695,31 +2995,34 @@
 	struct cam_data *cam = dev->priv;
 	int retval = 0;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	/* make this _really_ smp-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
+
 	//DBG("cpia_ioctl: %u\n", ioctlnr);
 
 	switch (ioctlnr) {
-
 	/* query capabilites */
 	case VIDIOCGCAP:
 	{
 		struct video_capability b;
 
 		DBG("VIDIOCGCAP\n");
-		strcpy(b.name, "CPiA Parport Camera");
+		strcpy(b.name, "CPiA Camera");
 		b.type = VID_TYPE_CAPTURE;
 		b.channels = 1;
 		b.audios = 0;
-		b.maxwidth = 352;         /* VIDEOSIZE_CIF */
+		b.maxwidth = 352;	/* VIDEOSIZE_CIF */
 		b.maxheight = 288;
-		b.minwidth = 48;         /* VIDEOSIZE_48_48 */
+		b.minwidth = 48;	/* VIDEOSIZE_48_48 */
 		b.minheight = 48;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			retval = -EFAULT;
+
 		break;
 	}
 
@@ -2782,13 +3085,15 @@
 			retval = -EFAULT;
 			break;
 		}
+
 		/* check validity */
 		DBG("palette: %d\n", vp.palette);
 		DBG("depth: %d\n", vp.depth);
-		if ( !valid_mode(vp.palette, vp.depth) ) {
+		if (!valid_mode(vp.palette, vp.depth)) {
 			retval = -EINVAL;
 			break;
 		}
+
 		down(&cam->param_lock);
 		/* brightness, colour, contrast need no check 0-65535 */
 		memcpy( &cam->vp, &vp, sizeof(vp) );
@@ -2799,12 +3104,13 @@
 		/* contrast is in steps of 8, so round */
 		cam->params.colourParams.contrast =
 			((cam->params.colourParams.contrast + 3) / 8) * 8;
-		if(cam->params.version.firmwareVersion == 1 &&
-		   cam->params.version.firmwareRevision == 2 &&
-		   cam->params.colourParams.contrast > 80) {
+		if (cam->params.version.firmwareVersion == 1 &&
+		    cam->params.version.firmwareRevision == 2 &&
+		    cam->params.colourParams.contrast > 80) {
 			/* 1-02 firmware limits contrast to 80 */
 			cam->params.colourParams.contrast = 80;
 		}
+
 		/* queue command to update camera */
 		cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
 		up(&cam->param_lock);
@@ -2845,9 +3151,10 @@
 		* is requested by the user???
 		*/
 		down(&cam->param_lock);
-		if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) {
+		if (vw.width != cam->vw.width || vw.height != cam->vw.height) {
 			int video_size = match_videosize(vw.width, vw.height);
-			if(video_size < 0) {
+
+			if (video_size < 0) {
 				retval = -EINVAL;
 				up(&cam->param_lock);
 				break;
@@ -2863,7 +3170,7 @@
 
 		/* setformat ignored by camera during streaming,
 		 * so stop/dispatch/start */
-		if(cam->cmd_queue & COMMAND_SETFORMAT) {
+		if (cam->cmd_queue & COMMAND_SETFORMAT) {
 			DBG("\n");
 			dispatch_commands(cam);
 		}
@@ -2882,10 +3189,10 @@
 		memset(&vm, 0, sizeof(vm));
 		vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
 		vm.frames = FRAME_NUM;
-		for( i=0; i<FRAME_NUM; i++ ) {
-			vm.offsets[i] = CPIA_MAX_FRAME_SIZE*i;
-		}
-		if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+		for (i = 0; i < FRAME_NUM; i++)
+			vm.offsets[i] = CPIA_MAX_FRAME_SIZE * i;
+
+		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
 			retval = -EFAULT;
 
 		break;
@@ -2900,7 +3207,7 @@
 			retval = -EFAULT;
 			break;
 		}
-#if 0
+#if 1
 		DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
 		    vm.width, vm.height);
 #endif
@@ -2913,6 +3220,8 @@
 		cam->vp.palette = vm.format;
 		switch(vm.format) {
 		case VIDEO_PALETTE_GREY:
+			cam->vp.depth = 8;
+			break;
 		case VIDEO_PALETTE_RGB555:
 		case VIDEO_PALETTE_RGB565:
 		case VIDEO_PALETTE_YUV422:
@@ -2930,15 +3239,16 @@
 			retval = -EINVAL;
 			break;
 		}
-		if(retval != 0) break;
+		if (retval)
+			break;
 
 		/* set video size */
 		video_size = match_videosize(vm.width, vm.height);
-		if(cam->video_size<0) {
+		if (cam->video_size < 0) {
 			retval = -EINVAL;
 			break;
 		}
-		if( video_size != cam->video_size ) {
+		if (video_size != cam->video_size) {
 			cam->video_size = video_size;
 			set_vw_size(cam);
 			cam->cmd_queue |= COMMAND_SETFORMAT;
@@ -2949,6 +3259,7 @@
 		    cam->vw.width, cam->vw.height);
 #endif
 		/* according to v4l-spec we must start streaming here */
+		cam->mmap_kludge = 1;
 		retval = capture_frame(cam, &vm);
 
 		break;
@@ -2964,7 +3275,7 @@
 		}
 		//DBG("VIDIOCSYNC: %d\n", frame);
 
-		if (frame<0||frame>=FRAME_NUM) {
+		if (frame<0 || frame >= FRAME_NUM) {
 			retval = -EINVAL;
 			break;
 		}
@@ -2982,7 +3293,7 @@
 			//DBG("VIDIOCSYNC: %d synced\n", frame);
 			break;
 		}
-		if(retval == -EINTR) {
+		if (retval == -EINTR) {
 			/* FIXME - xawtv does not handle this nice */
 			retval = 0;
 		}
@@ -3040,49 +3351,57 @@
 {
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
-	struct cam_data *cam;
+	struct cam_data *cam = dev->priv;
 	int retval;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	DBG("cpia_mmap: %ld\n", size);
 
-	if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){
+	if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
 		return -EINVAL;
-	}
-
-	cam = dev->priv;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	/* make this _really_ smp-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
 
-	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
-		if( (retval = allocate_frame_buf(cam)) ) {
+	if (!cam->frame_buf) {	/* we do lazy allocation */
+		if ((retval = allocate_frame_buf(cam))) {
 			up(&cam->busy_lock);
 			return retval;
 		}
 	}
+
 	pos = (unsigned long)(cam->frame_buf);
 	while (size > 0) {
-		page = kvirt_to_phys(pos);
+		page = kvirt_to_pa(pos);
 		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
 			up(&cam->busy_lock);
 			return -EAGAIN;
 		}
-		start+=PAGE_SIZE;
-		pos+=PAGE_SIZE;
-		if (size > PAGE_SIZE) size-=PAGE_SIZE;
-		else size=0;
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
 
 	DBG("cpia_mmap: %ld\n", size);
 	up(&cam->busy_lock);
+
 	return 0;
 }
 
 int cpia_video_init(struct video_device *vdev)
 {
+#ifdef CONFIG_PROC_FS
 	create_proc_cpia_cam(vdev->priv);
+#endif
 	return 0;
 }
 
@@ -3124,7 +3443,7 @@
 	cam->params.exposure.green1Comp = 214;
 	cam->params.exposure.green2Comp = 214;
 	cam->params.exposure.blueComp = 230;
-	cam->params.colourBalance.balanceMode = 2;	/* auto color balance */
+	cam->params.colourBalance.balanceModeIsAuto = 1;
 	cam->params.colourBalance.redGain = 32;
 	cam->params.colourBalance.greenGain = 6;
 	cam->params.colourBalance.blueGain = 92;
@@ -3151,16 +3470,15 @@
 	cam->params.compressionParams.decimationThreshMod = 2;
 	/* End of default values from Software Developer's Guide */
 	
-	/* Start with a reasonable transfer rate */
-	cam->transfer_rate = 500;
+	cam->transfer_rate = 0;
 	
 	/* Set Sensor FPS to 15fps. This seems better than 30fps
 	 * for indoor lighting. */
 	cam->params.sensorFps.divisor = 1;
 	cam->params.sensorFps.baserate = 1;
 	
-	cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */
-	cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */
+	cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
+	cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
 	
 	cam->params.format.subSample = SUBSAMPLE_422;
 	cam->params.format.yuvOrder = YUVORDER_YUYV;
@@ -3168,8 +3486,14 @@
 	cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
 	cam->params.compressionTarget.frTargeting =
 		CPIA_COMPRESSION_TARGET_QUALITY;
-	cam->params.compressionTarget.targetFR = 7; /* FIXME? */
-	cam->params.compressionTarget.targetQ = 10; /* FIXME? */
+	cam->params.compressionTarget.targetFR = 15; /* From windows driver */
+	cam->params.compressionTarget.targetQ = 5; /* From windows driver */
+	
+	cam->params.qx3.qx3_detected = 0;
+	cam->params.qx3.toplight = 0;
+	cam->params.qx3.bottomlight = 0;
+	cam->params.qx3.button = 0;
+	cam->params.qx3.cradled = 0;
 
 	cam->video_size = VIDEOSIZE_CIF;
 	
@@ -3179,7 +3503,7 @@
 	cam->vp.contrast = 32768;    /* 50% */
 	cam->vp.whiteness = 0;       /* not used -> grayscale only */
 	cam->vp.depth = 0;           /* FIXME: to be set by user? */
-	cam->vp.palette = 0;         /* FIXME: to be set by user? */
+	cam->vp.palette = VIDEO_PALETTE_RGB24;         /* FIXME: to be set by user? */
 
 	cam->vw.x = 0;
 	cam->vw.y = 0;
@@ -3201,11 +3525,13 @@
                                struct cpia_camera_ops *ops )
 {
 	int i;
+
 	/* Default everything to 0 */
 	memset(cam, 0, sizeof(struct cam_data));
 
 	cam->ops = ops;
-	cam->param_lock = MUTEX;
+	init_MUTEX(&cam->param_lock);
+	init_MUTEX(&cam->busy_lock);
 
 	reset_camera_struct(cam);
 
@@ -3215,7 +3541,7 @@
 	cam->vdev.priv = cam;
 	
 	cam->curframe = 0;
-	for( i=0; i<FRAME_NUM; i++) {
+	for (i = 0; i < FRAME_NUM; i++) {
 		cam->frame[i].width = 0;
 		cam->frame[i].height = 0;
 		cam->frame[i].state = FRAME_UNUSED;
@@ -3225,95 +3551,84 @@
 	cam->decompressed_frame.height = 0;
 	cam->decompressed_frame.state = FRAME_UNUSED;
 	cam->decompressed_frame.data = NULL;
-
-	return;
 }
 
-int cpia_register_camera(struct cpia_camera_ops *ops)
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
 {
-	int i;
-	struct cam_data *cam;
+        struct cam_data *camera;
+	
 	/* Need a lock when adding/removing cameras.  This doesn't happen
 	 * often and doesn't take very long, so grabbing the kernel lock
 	 * should be OK. */
-	lock_kernel();
-	for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */
-	if(i == CPIA_MAXCAMS) {
-		unlock_kernel();
-		return -ENODEV;
-	}
 	
-	if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
+	if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
 		unlock_kernel();
-		return -ENOMEM;
+		return NULL;
 	}
 	
-	init_camera_struct( camera[i], ops );
-	camera[i]->index = i;
-
+	init_camera_struct( camera, ops );
+	camera->lowlevel_data = lowlevel;
+	
 	/* register v4l device */
-	if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) {
-		kfree(camera[i]);
-		camera[i] = NULL;
+	if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) {
+		kfree(camera);
 		unlock_kernel();
 		printk(KERN_DEBUG "video_register_device failed\n");
-		return -ENODEV;
+		return NULL;
 	}
 
-	unlock_kernel();
-	
-	/* FIXME */
-#if 0
-	cam = camera[i];
+	/* get version information from camera: open/reset/close */
+
 	/* open cpia */
-	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
-		return -ENODEV;
-	}
+	if (camera->ops->open(camera->lowlevel_data))
+		return camera;
 	
 	/* reset the camera */
-	if((i = reset_camera(cam)) != 0) {
-		cam->ops->close(cam->lowlevel_data);
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
-		return i;
+	if (reset_camera(camera) != 0) {
+		camera->ops->close(camera->lowlevel_data);
+		return camera;
 	}
-#endif	
-	return i;
+
+	/* close cpia */
+	camera->ops->close(camera->lowlevel_data);
+
+	printk(KERN_INFO "  CPiA Version: %d.%02d (%d.%d)\n",
+	       camera->params.version.firmwareVersion,
+	       camera->params.version.firmwareRevision,
+	       camera->params.version.vcVersion,
+	       camera->params.version.vcRevision);
+	printk(KERN_INFO "  CPiA PnP-ID: %04x:%04x:%04x\n",
+	       camera->params.pnpID.vendor,
+	       camera->params.pnpID.product,
+	       camera->params.pnpID.deviceRevision);
+	printk(KERN_INFO "  VP-Version: %d.%d %04x\n",
+	       camera->params.vpVersion.vpVersion,
+	       camera->params.vpVersion.vpRevision,
+	       camera->params.vpVersion.cameraHeadID);
+
+	return camera;
 }
 
-void cpia_unregister_camera(int camnr)
+void cpia_unregister_camera(struct cam_data *cam)
 {
-	struct cam_data *cam;
-	if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return;
-	cam = camera[camnr];
-	
-	DBG("unregistering video\n");
-	/* FIXME: Is this safe if the device is open? */
-	video_unregister_device(&cam->vdev);
-	
-	/* Need a lock when adding/removing cameras.  This doesn't happen
-	 * often and doesn't take very long, so grabbing the kernel lock
-	 * should be OK. */
-	lock_kernel();
+	if (!cam->open_count) {
+		DBG("unregistering video\n");
+		video_unregister_device(&cam->vdev);
+	} else {
+		LOG("/dev/video%d removed while open, "
+		    "deferring video_unregister_device\n", cam->vdev.minor);
+		DBG("camera open -- setting ops to NULL\n");
+		cam->ops = NULL;
+	}
 	
+#ifdef CONFIG_PROC_FS
 	DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
 	destroy_proc_cpia_cam(cam);
-	
-	if(cam->open_count == 0) {
+#endif	
+	if (!cam->open_count) {
 		DBG("freeing camera\n");
-		kfree(camera[camnr]);
-		camera[camnr] = NULL;
-	} else {
-		DBG("camera open -- setting ops to NULL\n");
-		cam->ops = NULL;
+		kfree(cam);
 	}
-	unlock_kernel();
 }
 
 /****************************************************************************
@@ -3327,7 +3642,9 @@
 {
 	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
 	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
 	proc_cpia_create();
+#endif
 #ifdef CONFIG_KMOD
 #ifdef CONFIG_VIDEO_CPIA_PP_MODULE
 	request_module("cpia_pp");
@@ -3341,14 +3658,9 @@
 
 void cleanup_module(void)
 {
-	int i;
-	for(i=0; i<CPIA_MAXCAMS; ++i) {
-		if(camera[i]) {
-			video_unregister_device(&camera[i]->vdev);
-		}
-	}
-	
+#ifdef CONFIG_PROC_FS
 	proc_cpia_destroy();
+#endif
 }
 
 #else
@@ -3357,25 +3669,25 @@
 {
 	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
 	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
 	proc_cpia_create();
+#endif
 
-#ifdef MODULE
+#ifdef CONFIG_VIDEO_CPIA_PP
+	cpia_pp_init();
+#endif
 #ifdef CONFIG_KMOD
 #ifdef CONFIG_VIDEO_CPIA_PP_MODULE
 	request_module("cpia_pp");
 #endif
+
 #ifdef CONFIG_VIDEO_CPIA_USB_MODULE
 	request_module("cpia_usb");
 #endif
 #endif	/* CONFIG_KMOD */
-#else	/* !MODULE */
-#ifdef CONFIG_VIDEO_CPIA_PP
-	cpia_pp_init();
-#endif
 #ifdef CONFIG_VIDEO_CPIA_USB
 	cpia_usb_init();
 #endif
-#endif	/* !MODULE */
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/cpia.h linux.18p17/drivers/char/cpia.h
--- linux.vanilla/drivers/char/cpia.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/cpia.h	Thu Oct 19 01:40:50 2000
@@ -0,0 +1,430 @@
+#ifndef cpia_h
+#define cpia_h
+
+/*
+ * CPiA Parallel Port Video4Linux driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman,
+ *                    Peter Pregler,
+ *                    Scott J. Bertin,
+ *                    VLSI Vision Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define CPIA_MAJ_VER	1
+#define CPIA_MIN_VER    0
+#define CPIA_PATCH_VER	0
+
+#define CPIA_PP_MAJ_VER       1
+#define CPIA_PP_MIN_VER       0
+#define CPIA_PP_PATCH_VER     0
+
+#define CPIA_MAX_FRAME_SIZE_UNALIGNED	(352 * 288 * 4)   /* CIF at RGB32 */
+#define CPIA_MAX_FRAME_SIZE	((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
+
+#ifdef __KERNEL__
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+
+struct cpia_camera_ops
+{
+	/* open sets privdata to point to structure for this camera.
+         * Returns negative value on error, otherwise 0.
+	 */
+	int (*open)(void *privdata);
+	
+	/* Registers callback function cb to be called with cbdata
+	 * when an image is ready.  If cb is NULL, only single image grabs
+	 * should be used.  cb should immediately call streamRead to read
+	 * the data or data may be lost. Returns negative value on error,
+	 * otherwise 0.
+	 */
+	int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
+	                        void *cbdata);
+	
+	/* transferCmd sends commands to the camera.  command MUST point to
+	 * an  8 byte buffer in kernel space. data can be NULL if no extra
+	 * data is needed.  The size of the data is given by the last 2
+	 * bytes of comand.  data must also point to memory in kernel space.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*transferCmd)(void *privdata, u8 *command, u8 *data);
+
+	/* streamStart initiates stream capture mode.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*streamStart)(void *privdata);
+	
+	/* streamStop terminates stream capture mode.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*streamStop)(void *privdata);
+        
+	/* streamRead reads a frame from the camera.  buffer points to a
+         * buffer large enough to hold a complete frame in kernel space.
+         * noblock indicates if this should be a non blocking read.
+	 * Returns the number of bytes read, or negative value on error.
+         */
+	int (*streamRead)(void *privdata, u8 *buffer, int noblock);
+	
+	/* close disables the device until open() is called again.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*close)(void *privdata);
+	
+	/* If wait_for_stream_ready is non-zero, wait until the streamState
+	 * is STREAM_READY before calling streamRead.
+	 */
+	int wait_for_stream_ready;
+};
+
+struct cpia_frame {
+	u8 *data;
+	int count;
+	int width;
+	int height;
+	volatile int state;
+};
+
+struct cam_params {
+	struct {
+		u8 firmwareVersion;
+		u8 firmwareRevision;
+		u8 vcVersion;
+		u8 vcRevision;
+	} version;
+	struct {
+		u16 vendor;
+		u16 product;
+		u16 deviceRevision;
+	} pnpID;
+	struct {
+		u8 vpVersion;
+		u8 vpRevision;
+		u16 cameraHeadID;
+	} vpVersion;
+	struct {
+		u8 systemState;
+		u8 grabState;
+		u8 streamState;
+		u8 fatalError;
+		u8 cmdError;
+		u8 debugFlags;
+		u8 vpStatus;
+		u8 errorCode;
+	} status;
+	struct {
+		u8 brightness;
+		u8 contrast;
+		u8 saturation;
+	} colourParams;
+	struct {
+		u8 gainMode;
+		u8 expMode;
+		u8 compMode;
+		u8 centreWeight;
+		u8 gain;
+		u8 fineExp;
+		u8 coarseExpLo;
+		u8 coarseExpHi;
+		u8 redComp;
+		u8 green1Comp;
+		u8 green2Comp;
+		u8 blueComp;
+	} exposure;
+	struct {
+		u8 balanceModeIsAuto;
+		u8 redGain;
+		u8 greenGain;
+		u8 blueGain;
+	} colourBalance;
+	struct {
+		u8 divisor;
+		u8 baserate;
+	} sensorFps;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} apcor;
+	struct {
+		u8 flickerMode;
+		u8 coarseJump;
+		u8 allowableOverExposure;
+	} flickerControl;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} vlOffset;
+	struct {
+		u8 mode;
+		u8 decimation;
+	} compression;
+	struct {
+		u8 frTargeting;
+		u8 targetFR;
+		u8 targetQ;
+	} compressionTarget;
+	struct {
+		u8 yThreshold;
+		u8 uvThreshold;
+	} yuvThreshold;
+	struct {
+		u8 hysteresis;
+		u8 threshMax;
+		u8 smallStep;
+		u8 largeStep;
+		u8 decimationHysteresis;
+		u8 frDiffStepThresh;
+		u8 qDiffStepThresh;
+		u8 decimationThreshMod;
+	} compressionParams;
+	struct {
+		u8 videoSize;		/* CIF/QCIF */
+		u8 subSample;
+		u8 yuvOrder;
+	} format;
+
+        struct {     /* GA 04/14/00 - Intel QX3 specific data */
+	        u8 qx3_detected;        /* a QX3 is present */
+	        u8 toplight;            /* top light lit , R/W */
+	        u8 bottomlight;         /* bottom light lit, R/W */
+	        u8 button;              /* snapshot button pressed (R/O) */
+	        u8 cradled;             /* microscope is in cradle (R/O) */
+	} qx3;
+
+	struct {
+		u8 colStart;		/* skip first 8*colStart pixels */
+		u8 colEnd;		/* finish at 8*colEnd pixels */
+		u8 rowStart;		/* skip first 4*rowStart lines */
+		u8 rowEnd;		/* finish at 4*rowEnd lines */
+	} roi;
+	u8 ecpTiming;
+	u8 streamStartLine;
+};
+
+enum v4l_camstates {
+	CPIA_V4L_IDLE = 0,
+	CPIA_V4L_ERROR,
+	CPIA_V4L_COMMAND,
+	CPIA_V4L_GRABBING,
+	CPIA_V4L_STREAMING,
+	CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define FRAME_NUM	2	/* double buffering for now */
+
+struct cam_data {
+	struct cam_data **previous;
+	struct cam_data *next;
+
+        struct semaphore busy_lock;     /* guard against SMP multithreading */
+	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
+	void *lowlevel_data;		/* private data for lowlevel driver */
+	u8 *raw_image;			/* buffer for raw image data */
+	struct cpia_frame decompressed_frame;
+                                        /* buffer to hold decompressed frame */
+	int image_size;		        /* sizeof last decompressed image */
+	int open_count;			/* # of process that have camera open */
+
+				/* camera status */
+	int fps;			/* actual fps reported by the camera */
+	int transfer_rate;		/* transfer rate from camera in kB/s */
+	u8 mainsFreq;			/* for flicker control */
+
+				/* proc interface */
+	struct semaphore param_lock;	/* params lock for this camera */
+	struct cam_params params;	/* camera settings */
+	struct proc_dir_entry *proc_entry;	/* /proc/cpia/videoX */
+	
+					/* v4l */
+	int video_size;			/* VIDEO_SIZE_ */
+	volatile enum v4l_camstates camstate;	/* v4l layer status */
+	struct video_device vdev;	/* v4l videodev */
+	struct video_picture vp;	/* v4l camera settings */
+	struct video_window vw;		/* v4l capture area */
+
+				/* mmap interface */
+	int curframe;			/* the current frame to grab into */
+	u8 *frame_buf;			/* frame buffer data */
+        struct cpia_frame frame[FRAME_NUM];
+				/* FRAME_NUM-buffering, so we need a array */
+
+	int first_frame;
+	int mmap_kludge;		/* 'wrong' byte order for mmap */
+	volatile u32 cmd_queue;		/* queued commands */
+};
+
+/* cpia_register_camera is called by low level driver for each camera.
+ * A unique camera number is returned, or a negative value on error */
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
+
+/* cpia_unregister_camera is called by low level driver when a camera
+ * is removed.  This must not fail. */
+void cpia_unregister_camera(struct cam_data *cam);
+
+/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
+ * one byte 16bit DMA alignment
+ */
+#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
+
+/* constant value's */
+#define MAGIC_0		0x19
+#define MAGIC_1		0x68
+#define DATA_IN		0xC0
+#define DATA_OUT	0x40
+#define VIDEOSIZE_QCIF	0	/* 176x144 */
+#define VIDEOSIZE_CIF	1	/* 352x288 */
+#define VIDEOSIZE_SIF	2	/* 320x240 */
+#define VIDEOSIZE_QSIF	3	/* 160x120 */
+#define VIDEOSIZE_48_48		4 /* where no one has gone before, iconsize! */
+#define VIDEOSIZE_64_48		5
+#define VIDEOSIZE_128_96	6
+#define VIDEOSIZE_160_120	VIDEOSIZE_QSIF
+#define VIDEOSIZE_176_144	VIDEOSIZE_QCIF
+#define VIDEOSIZE_192_144	7
+#define VIDEOSIZE_224_168	8
+#define VIDEOSIZE_256_192	9
+#define VIDEOSIZE_288_216	10
+#define VIDEOSIZE_320_240	VIDEOSIZE_SIF
+#define VIDEOSIZE_352_288	VIDEOSIZE_CIF
+#define VIDEOSIZE_88_72		11 /* quarter CIF */
+#define SUBSAMPLE_420	0
+#define SUBSAMPLE_422	1
+#define YUVORDER_YUYV	0
+#define YUVORDER_UYVY	1
+#define NOT_COMPRESSED	0
+#define COMPRESSED	1
+#define NO_DECIMATION	0
+#define DECIMATION_ENAB	1
+#define EOI		0xff	/* End Of Image */
+#define EOL		0xfd	/* End Of Line */
+#define FRAME_HEADER_SIZE	64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE	0
+#define CPIA_GRAB_CONTINUOUS	1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE	0
+#define CPIA_COMPRESSION_AUTO	1
+#define CPIA_COMPRESSION_MANUAL	2
+#define CPIA_COMPRESSION_TARGET_QUALITY         0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE	0
+#define GRABSTATE	1
+#define STREAMSTATE	2
+#define FATALERROR	3
+#define CMDERROR	4
+#define DEBUGFLAGS	5
+#define VPSTATUS	6
+#define ERRORCODE	7
+
+/* SystemState */
+#define UNINITIALISED_STATE	0
+#define PASS_THROUGH_STATE	1
+#define LO_POWER_STATE		2
+#define HI_POWER_STATE		3
+#define WARM_BOOT_STATE		4
+
+/* GrabState */
+#define GRAB_IDLE		0
+#define GRAB_ACTIVE		1
+#define GRAB_DONE		2
+
+/* StreamState */
+#define STREAM_NOT_READY	0
+#define STREAM_READY		1
+#define STREAM_OPEN		2
+#define STREAM_PAUSED		3
+#define STREAM_FINISHED		4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG	  1
+#define SYSTEM_FLAG	  2
+#define INT_CTRL_FLAG	  4
+#define PROCESS_FLAG	  8
+#define COM_FLAG	 16
+#define VP_CTRL_FLAG	 32
+#define CAPTURE_FLAG	 64
+#define DEBUG_FLAG	128
+
+/* VPStatus */
+#define VP_STATE_OK			0x00
+
+#define VP_STATE_FAILED_VIDEOINIT	0x01
+#define VP_STATE_FAILED_AECACBINIT	0x02
+#define VP_STATE_AEC_MAX		0x04
+#define VP_STATE_ACB_BMAX		0x08
+
+#define VP_STATE_ACB_RMIN		0x10
+#define VP_STATE_ACB_GMIN		0x20
+#define VP_STATE_ACB_RMAX		0x40
+#define VP_STATE_ACB_GMAX		0x80
+
+/* ErrorCode */
+#define ERROR_FLICKER_BELOW_MIN_EXP     0x01 /*flicker exposure got below minimum exposure */
+
+#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args)
+#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args)
+
+#ifdef _CPIA_DEBUG_
+#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args)
+#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args)
+#else
+#define DBG(fmn,args...) do {} while(0)
+#endif
+
+#define DEB_BYTE(p)\
+  DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
+      (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
+        (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#define ADD_TO_LIST(l, drv) \
+  {\
+    lock_kernel();\
+    (drv)->next = l;\
+    (drv)->previous = &(l);\
+    (l) = drv;\
+    unlock_kernel();\
+  } while(0)
+
+#define REMOVE_FROM_LIST(drv) \
+  {\
+    if ((drv)->previous != NULL) {\
+      lock_kernel();\
+      if ((drv)->next != NULL)\
+        (drv)->next->previous = (drv)->previous;\
+      *((drv)->previous) = (drv)->next;\
+      (drv)->previous = NULL;\
+      (drv)->next = NULL;\
+      unlock_kernel();\
+    }\
+  } while (0)
+
+
+#endif /* __KERNEL__ */
+
+#endif /* cpia_h */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/cpia_pp.c linux.18p17/drivers/char/cpia_pp.c
--- linux.vanilla/drivers/char/cpia_pp.c	Fri Apr 21 12:45:51 2000
+++ linux.18p17/drivers/char/cpia_pp.c	Fri Sep  1 13:51:28 2000
@@ -3,10 +3,9 @@
  *
  * Supports CPiA based parallel port Video Camera's.
  *
- * (C) 1999 Bas Huisman <bhuism@cs.utwente.nl>
- *          Scott J. Bertin <sbertin@mindspring.com>,
- *          Peter Pregler <Peter_Pregler@email.com>
- *          Johannes Erdfelt
+ * (C) Copyright 1999 Bas Huisman <bhuism@cs.utwente.nl>
+ * (C) Copyright 1999-2000 Scott J. Bertin <sbertin@mindspring.com>,
+ * (C) Copyright 1999-2000 Peter Pregler <Peter_Pregler@email.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,6 +23,11 @@
  */
 
 #include <linux/config.h>
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+#undef CONFIG_VIDEO_CPIA_PP_DMA
+#endif
 
 #include <linux/module.h>
 #include <linux/init.h>
@@ -32,6 +36,8 @@
 #include <linux/parport.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/smp_lock.h>
+
 #ifdef CONFIG_VIDEO_CPIA_PP_DMA
 #include <asm/dma.h>
 #endif
@@ -41,6 +47,7 @@
 #endif
 
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 /* If this is a module and parport_pc is not, some parport_pc_* functions
  * are not directly availbale.  parport.h messes this up.
  * This fixes what we need.
@@ -67,11 +74,12 @@
 #define parport_disable_irq(p) parport_pc_disable_irq(p)
 #endif				/* parport_enable_irq */
 #endif				/* !PARPORT_NEED_GENERIC_OPS */
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */
 
-#undef _CPIA_DEBUG_		/* define for verbose debug output */
-#include <linux/cpia.h>
+/* #define _CPIA_DEBUG_		define for verbose debug output */
+#include "cpia.h"
 
-static int cpia_pp_open(int camnr, void **privdata);
+static int cpia_pp_open(void *privdata);
 static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
                                     void *cbdata);
 static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
@@ -126,6 +134,8 @@
 #define ECP_FIFO_SIZE	16
 #define DMA_BUFFER_SIZE               PAGE_SIZE
 	/* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */
+#define PARPORT_CHUNK_SIZE	PAGE_SIZE/* >=2.3.x */
+				/* we read this many bytes at once */
 
 #define GetECRMasked(port,mask)	(parport_read_econtrol(port) & (mask))
 #define GetStatus(port)		((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8))
@@ -139,7 +149,6 @@
 #define ClearControlMasked(port,mask)	SetControl(port,GetControl(port)&~(mask));
 #define FrobControlBit(port,mask,value)	SetControl(port,(GetControl(port)&~(mask))|((value)&(mask)));
 
-#define PP_MAXCAMS	(PARPORT_MAX<CPIA_MAXCAMS?PARPORT_MAX:CPIA_MAXCAMS)
 #define PACKET_LENGTH 	8
 
 /* Magic numbers for defining port-device mappings */
@@ -149,18 +158,20 @@
 #define PPCPIA_PARPORT_NONE -1
 
 #ifdef MODULE
-static int parport_nr[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC};
-static char *parport[PP_MAXCAMS] = {NULL,};
+static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static char *parport[PARPORT_MAX] = {NULL,};
 
+MODULE_AUTHOR("B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com>");
 MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
-MODULE_PARM(parport, "1-" __MODULE_STRING(PP_MAXCAMS) "s");
+MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s");
 MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
 #else
-static int parport_nr[PP_MAXCAMS] __initdata =
-	{[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC};
+static int parport_nr[PARPORT_MAX] __initdata =
+	{[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
 static int parport_ptr = 0;
 #endif
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 enum comstates { CPIA_FORWARD, CPIA_REVERSE };	//fixme
 enum camstates {
 	CPIA_PHASE_idle = 0,
@@ -187,16 +198,22 @@
 	"CPIA_PHASE_secpwrite",
 };
 #endif
+#endif
+
 struct pp_cam_entry {
 	struct pardevice *pdev;
 	struct parport *port;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	enum comstates state;
 	enum camstates camstate;
+#endif
 	struct tq_struct cb_task;
 	int open_count;
-	int camnr;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	struct wait_queue *wq_stream;
-	
+#else
+	wait_queue_head_t wq_stream;
+#endif	
 	/* image state flags */
 	int image_ready;	/* we got an interrupt */
 	int image_complete;	/* we have seen 4 EOI */
@@ -220,8 +237,6 @@
 #endif
 };
 
-static struct pp_cam_entry *camera[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = NULL};
-
 static struct cpia_camera_ops cpia_pp_ops = 
 {
 	cpia_pp_open,
@@ -230,9 +245,12 @@
 	cpia_pp_streamStart,
 	cpia_pp_streamStop,
 	cpia_pp_streamRead,
-	cpia_pp_close
+	cpia_pp_close,
+	1
 };
 
+static struct cam_data *cam_list;
+
 #ifdef _CPIA_DEBUG_
 #define DEB_PORT(port) { \
 u8 controll = GetControl(port); \
@@ -258,17 +276,18 @@
 
 /* FIXME */
 static void cpia_parport_enable_irq( struct parport *port ) {
-  parport_enable_irq(port);
-  mdelay(10);
-  return;
+	parport_enable_irq(port);
+	mdelay(10);
+	return;
 }
 
 static void cpia_parport_disable_irq( struct parport *port ) {
-  parport_disable_irq(port);
-  mdelay(10);
-  return;
+	parport_disable_irq(port);
+	mdelay(10);
+	return;
 }
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 static int while_out(struct pp_cam_entry *cam)
 {
 	struct parport *port = cam->port;
@@ -709,7 +728,7 @@
 	release_dma_lock(flags);
 	return cam->readbytes;
 }
-#endif
+#endif /* CONFIG_VIDEO_CPIA_PP_DMA */
 
 /****************************************************************************
  *
@@ -779,6 +798,7 @@
 
 	return written;
 }
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */
 
 /****************************************************************************
  *
@@ -787,10 +807,13 @@
  ***************************************************************************/
 static void EndTransferMode(struct pp_cam_entry *cam)
 {
-	if (!cam) return;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if (cam->state == CPIA_REVERSE)  Reverse2Forward(cam);
 	Valid1284Termination(cam);
 	if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+#else
+	parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+#endif
 }
 
 /****************************************************************************
@@ -800,6 +823,7 @@
  ***************************************************************************/
 static int ForwardSetup(struct pp_cam_entry *cam)
 {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if(cam->stream_irq) cpia_parport_disable_irq(cam->port);
 	if (!Negotiate2SetupPhase(cam, 0)) {
 		if (!ECPSetupPhase(cam)) {
@@ -812,6 +836,22 @@
 	}
 	EndTransferMode(cam);
 	return -1;
+#else
+	int retry;
+	
+	/* After some commands the camera needs extra time before
+	 * it will respond again, so we try up to 3 times */
+	for(retry=0; retry<3; ++retry) {
+		if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) {
+			break;
+		}
+	}
+	if(retry == 3) {
+		DBG("Unable to negotiate ECP mode\n");
+		return -1;
+	}
+	return 0;
+#endif
 }
 
 /****************************************************************************
@@ -821,6 +861,7 @@
  ***************************************************************************/
 static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
 {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if(cam->stream_irq) cpia_parport_disable_irq(cam->port);
 	if (!Negotiate2SetupPhase(cam, extensibility)) {
 		if (!ECPSetupPhase(cam)) {
@@ -837,6 +878,28 @@
 	}
 	EndTransferMode(cam);
 	return -1;
+#else
+	int retry;
+	int mode = IEEE1284_MODE_ECP;
+	if(extensibility) mode = 8|3|IEEE1284_EXT_LINK;
+
+	/* After some commands the camera needs extra time before
+	 * it will respond again, so we try up to 3 times */
+	for(retry=0; retry<3; ++retry) {
+		if(!parport_negotiate(cam->port, mode)) {
+			break;
+		}
+	}
+	if(retry == 3) {
+		if(extensibility)
+			DBG("Unable to negotiate extensibility mode\n");
+		else
+			DBG("Unable to negotiate ECP mode\n");
+		return -1;
+	}
+	if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP;
+	return 0;
+#endif
 }
 
 #ifdef CONFIG_VIDEO_CPIA_PP_DMA
@@ -967,7 +1030,7 @@
         }
 	return;
 }
-#endif
+#endif /* CONFIG_VIDEO_CPIA_PP_DMA */
 
 /****************************************************************************
  *
@@ -975,6 +1038,7 @@
  *
  ***************************************************************************/
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b)
 {
 	struct pp_cam_entry *cam = handle;
@@ -992,7 +1056,7 @@
 		}
 		return;
 	}
-#endif
+#endif /* CONFIG_VIDEO_CPIA_PP_DMA */
 	if (cam->camstate == CPIA_PHASE_ecpread) return;
 	if( cam->camstate != CPIA_PHASE_idle )
 		DBG("got IRQ(%d) when in %s\n",
@@ -1012,6 +1076,7 @@
         }
 	return;
 }
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */
 
 /****************************************************************************
  *
@@ -1021,15 +1086,27 @@
 static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
 {
 	int retval=0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+	int size_written;
+#endif
 	if (packet == NULL) {
 		return -EINVAL;
 	}
 	if (ForwardSetup(cam)) {
+		DBG("Write failed in setup\n");
 		return -EIO;
 	}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if (SimECPWriteBuffer(cam, packet, size) != size) {
 		retval = -EIO;
 	}
+#else
+	size_written = parport_write(cam->port, packet, size);
+	if(size_written != size) {
+		DBG("Write failed, wrote %d/%d\n", size_written, size);
+		retval = -EIO;
+	}
+#endif
 	EndTransferMode(cam);
 	return retval;
 }
@@ -1048,9 +1125,15 @@
 	if (ReverseSetup(cam, 0)) {
 		return -EIO;
 	}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if (SimECPReadBuffer(cam, packet, size) != size) {
 		retval = -EIO;
 	}
+#else
+	if(parport_read(cam->port, packet, size) != size) {
+		retval = -EIO;
+	}
+#endif
 	EndTransferMode(cam);
 	return retval;
 }
@@ -1068,7 +1151,6 @@
 	cam->image_ready=0;
 	//if (ReverseSetup(cam,1)) return -EIO;
 	if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
-
 	return 0;
 }
 
@@ -1089,6 +1171,20 @@
 	return 0;
 }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
+{
+	int bytes_read, new_bytes;
+	for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
+		new_bytes = parport_read(port, buffer+bytes_read,
+			                 len-bytes_read);
+		if(new_bytes < 0) break;
+	}
+	return bytes_read;
+}
+
+#endif
+
 /****************************************************************************
  *
  *  cpia_pp_streamRead
@@ -1098,6 +1194,10 @@
 {
 	struct pp_cam_entry *cam = privdata;
 	int read_bytes = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+	int i, endseen, block_size, new_bytes;
+#endif
+
 	if(cam == NULL) {
 		DBG("Internal driver error: cam is NULL\n");
 		return -EINVAL;
@@ -1120,8 +1220,12 @@
 			DBG("%d\n", cam->image_ready);
 		}
 	} else {
-		if (ReverseSetup(cam, 1)) return -EIO;
+		if (ReverseSetup(cam, 1)) {
+			DBG("unable to ReverseSetup\n");
+			return -EIO;
+		}
 	}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	read_bytes = ECPReadBuffer(cam, buffer, CPIA_MAX_IMAGE_SIZE);
 	if( 1/*!cam->streaming*/) {
 		EndTransferMode(cam);
@@ -1131,6 +1235,36 @@
 			DBG("incomplete image: %ld / %d / %d\n",
 			    jiffies, cam->image_complete, read_bytes);
 	}
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
+	endseen = 0;
+	block_size = PARPORT_CHUNK_SIZE;
+	while( !cam->image_complete ) {
+		if(current->need_resched)  schedule();
+		
+		new_bytes = cpia_pp_read(cam->port, buffer, block_size );
+		if( new_bytes <= 0 ) {
+			break;
+		}
+		i=-1;
+		while(++i<new_bytes && endseen<4) {
+	        	if(*buffer==EOI) {
+	                	endseen++;
+	                } else {
+	                	endseen=0;
+	                }
+			buffer++;
+		}
+		read_bytes += i;
+		if( endseen==4 ) {
+			cam->image_complete=1;
+			break;
+		}
+		if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
+			block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
+		}
+	}
+	EndTransferMode(cam);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
 	return cam->image_complete ? read_bytes : -EIO;
 }
 
@@ -1145,6 +1279,7 @@
 	int retval=0;
 	int databytes;
 	struct pp_cam_entry *cam = privdata;
+
 	if(cam == NULL) {
 		DBG("Internal driver error: cam is NULL\n");
 		return -EINVAL;
@@ -1155,6 +1290,7 @@
 	}
 	databytes = (((int)command[7])<<8) | command[6];
 	if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
+		DBG("Error writing command\n");
 		return err;
 	}
 	if(command[0] == DATA_IN) {
@@ -1163,9 +1299,9 @@
 			DBG("Internal driver error: data is NULL\n");
 			return -EINVAL;
 		}
-		err = ReadPacket(cam, buffer, 8);
-		if(err < 0) {
+		if((err = ReadPacket(cam, buffer, 8)) < 0) {
 			return err;
+			DBG("Error reading command result\n");
 		}
 		memcpy(data, buffer, databytes);
 	} else if(command[0] == DATA_OUT) {
@@ -1174,7 +1310,10 @@
 				DBG("Internal driver error: data is NULL\n");
 				retval = -EINVAL;
 			} else {
-				WritePacket(cam, data, databytes);
+				if((err=WritePacket(cam, data, databytes)) < 0){
+					DBG("Error writing command data\n");
+					return err;
+				}
 			}
 		}
 	} else {
@@ -1189,29 +1328,19 @@
  *  cpia_pp_open
  *
  ***************************************************************************/
-static int cpia_pp_open(int camnr, void **privdata)
+static int cpia_pp_open(void *privdata)
 {
-	struct pp_cam_entry *cam=NULL;
-	int i;
-	for(i=0; i<PP_MAXCAMS; ++i) {
-		cam=camera[i];
-		if(cam == NULL) {
-			DBG("Trying to open non-existent camera[%d]\n", camnr);
-			return -ENXIO;
-		}
-		if(cam->camnr == camnr) {
-			DBG("Found camera[%d]\n", camnr);
-			break;
-		}
-	}
+	struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;
 	
-	*privdata = cam;
+	if (cam == NULL)
+		return -EINVAL;
 	
 	if(cam->open_count == 0) {
 		if (parport_claim(cam->pdev)) {
 			DBG("failed to claim the port\n");
 			return -EBUSY;
 		}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 		parport_write_econtrol(cam->port, PARPORT_MODE_PCECR);
 		parport_frob_econtrol(cam->port,
 		                      ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn,
@@ -1222,6 +1351,15 @@
 #endif
 		cam->stream_irq=0;
 		cpia_parport_disable_irq(cam->port);
+#else
+		parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+		parport_data_forward(cam->port);
+		parport_write_control(cam->port, PARPORT_CONTROL_SELECT);
+		udelay(50);
+		parport_write_control(cam->port,
+		                      PARPORT_CONTROL_SELECT
+		                      | PARPORT_CONTROL_INIT);
+#endif
 	}
 	
 	++cam->open_count;
@@ -1266,6 +1404,7 @@
 	MOD_DEC_USE_COUNT;
 #endif
 	if (--cam->open_count == 0) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 		if (cam->port->irq != PARPORT_IRQ_NONE) {
 			cpia_parport_disable_irq(cam->port);
 		}
@@ -1278,11 +1417,12 @@
 			interruptible_sleep_on(&cam->wq_dma);
 		}
 		restore_flags(flags);
-#endif
+#endif /* CONFIG_VIDEO_CPIA_PP_DMA */
 		if (waitqueue_active(&cam->wq_stream)) { /* FIXME */
 			wake_up(&cam->wq_stream);
 		}
 		
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */
 		parport_release(cam->pdev);
 	}
 	return 0;
@@ -1293,47 +1433,55 @@
  *  cpia_pp_register
  *
  ***************************************************************************/
-static int cpia_pp_register(int nr, struct parport *port)
+static int cpia_pp_register(struct parport *port)
 {
 	struct pardevice *pdev = NULL;
-	int camnr;
+	struct pp_cam_entry *cam;
+	struct cam_data *cpia;
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	if (!(port->modes & PARPORT_MODE_PCECP)) {
+#else
+	if (!(port->modes & PARPORT_MODE_ECP) &&
+	    !(port->modes & PARPORT_MODE_TRISTATE)) {
+#endif
 		LOG("port is not ECP capable\n");
 		return -ENXIO;
 	}
 
-	if((camnr = cpia_register_camera(&cpia_pp_ops)) < 0) {
-		LOG("failed to cpia_register_camera\n");
-		return -ENXIO;
-	}
-	camera[nr] = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
-	if (camera[nr] == NULL) {
+	cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
+	if (cam == NULL) {
 		LOG("failed to allocate camera structure\n");
-		cpia_unregister_camera(camnr);
 		return -ENOMEM;
 	}
-	memset(camera[nr],0,sizeof(struct pp_cam_entry));
+	memset(cam,0,sizeof(struct pp_cam_entry));
 	
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+	pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
+	                               cpia_pp_irq_handler, 0, cam);
+#else
 	pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
-	                               cpia_pp_irq_handler, 0, camera[nr]);
+	                               NULL, 0, cam);
+#endif
 
 	if (!pdev) {
 		LOG("failed to parport_register_device\n");
-		cpia_unregister_camera(camnr);
-		kfree(camera[nr]);
+		kfree(cam);
 		return -ENXIO;
 	}
 
-	camera[nr]->pdev = pdev;
-	camera[nr]->port = port;
-	camera[nr]->state = CPIA_FORWARD;
-	camera[nr]->camstate = CPIA_PHASE_idle;
-	camera[nr]->camnr = camnr;
-	init_waitqueue(&camera[nr]->wq_stream);
+	cam->pdev = pdev;
+	cam->port = port;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+	cam->state = CPIA_FORWARD;
+	cam->camstate = CPIA_PHASE_idle;
+	init_waitqueue(&cam->wq_stream);
+#else
+	init_waitqueue_head(&cam->wq_stream);
+#endif
 
-	camera[nr]->streaming = 0;
-	camera[nr]->stream_irq = 0;
+	cam->streaming = 0;
+	cam->stream_irq = 0;
 
 #ifdef CONFIG_VIDEO_CPIA_PP_DMA
 	if (pdev->port->irq != PARPORT_IRQ_NONE &&
@@ -1341,66 +1489,151 @@
 		if(request_dma(pdev->port->dma, "cpia_pp")) {
 			LOG("failed to register dma %d\n", pdev->port->dma);
 			parport_unregister_device(pdev);
-			cpia_unregister_camera(camnr);
-			kfree(camera[nr]);
+			kfree(cam);
 			return -ENXIO;
 		}
-		camera[nr]->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA);
-		if(camera[nr]->dma_buf == NULL) {
-			free_dma(camera[nr]->pdev->port->dma);
-			LOG("failed to allocate dma buffer, using FIFO mode\n");
+		cam->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA);
+		if(cam->dma_buf == NULL) {
+			free_dma(cam->pdev->port->dma);
+			LOG("failed to allocate dma buffer, using PIO mode\n");
 		} else {
-			init_waitqueue(&camera[nr]->wq_dma);
+			init_waitqueue(&cam->wq_dma);
 			printk(KERN_INFO "  using DMA mode (irq %d, DMA %d)\n", pdev->port->irq, pdev->port->dma);
 		}
-		memset(camera[nr]->dma_buf, 0, DMA_BUFFER_SIZE);
+		memset(cam->dma_buf, 0, DMA_BUFFER_SIZE);
 	} else {
 		printk(KERN_INFO "  using PIO mode\n");
 	}
 #endif
 
+	if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) {
+		LOG("failed to cpia_register_camera\n");
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+		if (cam->dma_buf) {
+			free_dma(cam->pdev->port->dma);
+			kfree(cam->dma_buf);
+		}
+#endif
+		parport_unregister_device(pdev);
+		kfree(cam);
+		return -ENXIO;
+	}
+	ADD_TO_LIST(cam_list, cpia);
+
 	return 0;
 }
 
+static void cpia_pp_detach (struct parport *port)
+{
+	struct cam_data *cpia;
+
+	for(cpia = cam_list; cpia != NULL; cpia = cpia->next) {
+		struct pp_cam_entry *cam = cpia->lowlevel_data;
+		if (cam && cam->port->number == port->number) {
+			REMOVE_FROM_LIST(cpia);
+			
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	                if (cam->dma_buf) {
+	                        free_dma(cam->pdev->port->dma);
+        	                kfree(cam->dma_buf);
+                	}
+#endif
+
+			cpia_unregister_camera(cpia);
+			
+			if(cam->open_count > 0) {
+				cpia_pp_close(cam);
+			}
+
+			parport_unregister_device(cam->pdev);
+		
+			kfree(cam);
+			cpia->lowlevel_data = NULL;
+			break;
+		}
+	}
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+static void cpia_pp_attach (struct parport *port)
+{
+	unsigned int i;
+
+	switch (parport_nr[0])
+	{
+	case PPCPIA_PARPORT_UNSPEC:
+	case PPCPIA_PARPORT_AUTO:
+		if (port->probe_info[0].class != PARPORT_CLASS_MEDIA ||
+		    port->probe_info[0].cmdset == NULL ||
+		    strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0)
+			return;
+
+		cpia_pp_register(port);
+
+		break;
+
+	default:
+		for (i = 0; i < PARPORT_MAX; ++i) {
+			if (port->number == parport_nr[i]) {
+				cpia_pp_register(port);
+				break;
+			}
+		}
+		break;
+	}
+}
+
+static struct parport_driver cpia_pp_driver = {
+	"cpia_pp",
+	cpia_pp_attach,
+	cpia_pp_detach,
+	NULL
+};
+#endif
+
 int cpia_pp_init(void)
 {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 	struct parport *port;
 	int i, count = 0;
+#endif
 
 	printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, 
 	       CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
 
-	switch (parport_nr[0]) {
-	case PPCPIA_PARPORT_OFF:
+	if(parport_nr[0] == PPCPIA_PARPORT_OFF) {
 		printk("  disabled\n");
 		return 0;
-
+	}
+	
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+	switch (parport_nr[0]) {
 	case PPCPIA_PARPORT_UNSPEC:
 	case PPCPIA_PARPORT_AUTO:
 		for (port = parport_enumerate(); port; port = port->next) {
 
 #if defined(CONFIG_PNP_PARPORT) || \
     (defined(MODULE) && defined(CONFIG_PNP_PARPORT_MODULE))
-			if (port->probe_info.class != PARPORT_CLASS_MEDIA ||
-			    port->probe_info.cmdset == NULL ||
-			    strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){
-				continue;
+			if(port->probe_info.model) {
+				if (port->probe_info.class != PARPORT_CLASS_MEDIA ||
+				    port->probe_info.cmdset == NULL ||
+				    strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){
+					continue;
+				}
 			}
 #endif
-
-			if (cpia_pp_register(count, port) == 0 &&
-			    ++count == PP_MAXCAMS) {
-				break;
+			if (!cpia_pp_register(port)) {
+				++count;
 			}
 		}
 		break;
 
 	default:
-		for (i = 0; i < PP_MAXCAMS; i++) {
+		for (i = 0; i < PARPORT_MAX; i++) {
 			for (port = parport_enumerate(); port;
 			     port = port->next) {
 				if (port->number == parport_nr[i]) {
-					if (!cpia_pp_register(i, port)) {
+					if (!cpia_pp_register(port)) {
 						count++;
 					}
 					break;
@@ -1414,6 +1647,13 @@
 	if (count == 0) {
 		return -ENODEV;
 	}
+#else /* kernel version >= 2.3.0 */
+	if (parport_register_driver (&cpia_pp_driver)) {
+		LOG ("unable to register with parport\n");
+		return -EIO;
+	}
+#endif /* kernel version >= 2.3.0 */
+
 	return 0;
 }
 
@@ -1426,7 +1666,7 @@
 			parport_nr[0] = PPCPIA_PARPORT_AUTO;
 		} else {
 			int n;
-			for (n = 0; n < PP_MAXCAMS && parport[n]; n++) {
+			for (n = 0; n < PARPORT_MAX && parport[n]; n++) {
 				if (!strncmp(parport[n], "none", 4)) {
 					parport_nr[n] = PPCPIA_PARPORT_NONE;
 				} else {
@@ -1443,31 +1683,23 @@
 		}
 	}
 #if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE)
-	request_module("parport_probe");
+	if(parport_enumerate() && !parport_enumerate()->probe_info.model) {
+		request_module("parport_probe");
+	}
 #endif
 	return cpia_pp_init();
 }
 
 void cleanup_module(void)
 {
-	int i;
-	for (i = 0; camera[i] != NULL && i < PP_MAXCAMS; i++) {
-		while(camera[i]->open_count > 0) {
-			LOG("You forgot to close camera %d, will do it for you\n", camera[i]->camnr);
-			cpia_pp_close(camera[i]);
-		}
-		
-#ifdef CONFIG_VIDEO_CPIA_PP_DMA
-		if (camera[i]->dma_buf) {
-			free_dma(camera[i]->pdev->port->dma);
-			kfree(camera[i]->dma_buf);
-		}
-#endif
-
-		cpia_unregister_camera(camera[i]->camnr);
-		parport_unregister_device(camera[i]->pdev);
-		kfree(camera[i]);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+	parport_unregister_driver (&cpia_pp_driver);
+#else
+	while(cam_list != NULL) {
+		struct pp_cam_entry *cam = cam_list->lowlevel_data;
+		cpia_pp_detach(cam->port);
 	}
+#endif
 	return;
 }
 
@@ -1482,7 +1714,7 @@
 		}
 	} else if (!strncmp(str, "parport", 7)) {
 		int n = simple_strtoul(str + 7, NULL, 10);
-		if (parport_ptr < PP_MAXCAMS) {
+		if (parport_ptr < PARPORT_MAX) {
 			parport_nr[parport_ptr++] = n;
 		} else {
 			LOG("too many ports, %s ignored.\n", str);
@@ -1493,5 +1725,8 @@
 		parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
 	}
 }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
+__setup("cpia_pp=", cpia_pp_setup);
+#endif
 
 #endif /* !MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/cpia_usb.c linux.18p17/drivers/char/cpia_usb.c
--- linux.vanilla/drivers/char/cpia_usb.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/cpia_usb.c	Fri Sep  1 13:51:28 2000
@@ -0,0 +1,636 @@
+/*
+ * cpia_usb CPiA USB driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * Copyright (C) 1999        Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
+ * Copyright (C) 1999, 2000  Johannes Erdfelt <jerdfelt@valinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+
+#include "cpia.h"
+
+#define USB_REQ_CPIA_GRAB_FRAME			0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME		0xC2
+#define  WAIT_FOR_NEXT_FRAME			0
+#define  FORCE_FRAME_UPLOAD			1
+
+#define FRAMES_PER_DESC		10
+#define FRAME_SIZE_PER_DESC	960	/* Shouldn't be hardcoded */
+#define CPIA_NUMSBUF		2
+#define STREAM_BUF_SIZE		(PAGE_SIZE * 4)
+#define SCRATCH_BUF_SIZE	(STREAM_BUF_SIZE * 2)
+
+struct cpia_sbuf {
+	char *data;
+	urb_t *urb;
+};
+
+#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
+enum framebuf_status {
+	FRAME_EMPTY,
+	FRAME_READING,
+	FRAME_READY,
+	FRAME_ERROR,
+};
+
+struct framebuf {
+	int length;
+	enum framebuf_status status;
+	u8 data[FRAMEBUF_LEN];
+	struct framebuf *next;
+};
+
+struct usb_cpia {
+	/* Device structure */
+	struct usb_device *dev;
+
+	unsigned char iface;
+	wait_queue_head_t wq_stream;
+
+	int cursbuf;		/* Current receiving sbuf */
+	struct cpia_sbuf sbuf[CPIA_NUMSBUF];		/* Double buffering */
+
+	int streaming;
+	int open;
+	int present;
+	struct framebuf *buffers[3];
+	struct framebuf *curbuff, *workbuff;
+};
+
+static int cpia_usb_open(void *privdata);
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+			             void *cbdata);
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_usb_streamStart(void *privdata);
+static int cpia_usb_streamStop(void *privdata);
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
+static int cpia_usb_close(void *privdata);
+
+#define ABOUT "USB driver for Vision CPiA based cameras"
+
+static struct cpia_camera_ops cpia_usb_ops = {
+	cpia_usb_open,
+	cpia_usb_registerCallback,
+	cpia_usb_transferCmd,
+	cpia_usb_streamStart,
+	cpia_usb_streamStop,
+	cpia_usb_streamRead,
+	cpia_usb_close,
+	0
+};
+
+static struct cam_data *cam_list;
+
+static void cpia_usb_complete(struct urb *urb)
+{
+	int i;
+	char *cdata;
+	struct usb_cpia *ucpia;
+
+	if (!urb || !urb->context)
+		return;
+
+	ucpia = (struct usb_cpia *) urb->context;
+
+	if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open)
+		return;
+
+	if (ucpia->workbuff->status == FRAME_EMPTY) {
+		ucpia->workbuff->status = FRAME_READING;
+		ucpia->workbuff->length = 0;
+	}
+ 		  
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		if (st)
+			printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st);
+
+		if (FRAMEBUF_LEN < ucpia->workbuff->length + n) {
+			printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n);
+			return;
+		}
+	    
+		if (n) {
+			if ((ucpia->workbuff->length > 0) || 
+			    (0x19 == cdata[0] && 0x68 == cdata[1])) {
+				memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n);
+				ucpia->workbuff->length += n;
+			} else
+				DBG("Ignoring packet!\n");
+		} else {
+			if (ucpia->workbuff->length > 4 &&
+			    0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] &&
+			    0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] &&
+			    0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] &&
+			    0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) {
+				ucpia->workbuff->status = FRAME_READY;
+				ucpia->curbuff = ucpia->workbuff;
+				ucpia->workbuff = ucpia->workbuff->next;
+				ucpia->workbuff->status = FRAME_EMPTY;
+				ucpia->workbuff->length = 0;
+		  
+				if (waitqueue_active(&ucpia->wq_stream))
+					wake_up_interruptible(&ucpia->wq_stream);
+			}
+		}
+	}
+}
+
+static int cpia_usb_open(void *privdata)
+{
+	struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+	urb_t *urb;
+	int ret, retval = 0, fx, err;
+  
+	if (!ucpia)
+		return -EINVAL;
+
+	ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+	if (!ucpia->sbuf[0].data)
+		return -EINVAL;
+
+	ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+	if (!ucpia->sbuf[1].data) {
+		retval = -EINVAL;
+		goto error_0;
+	}
+	
+	ret = usb_set_interface(ucpia->dev, ucpia->iface, 3);
+	if (ret < 0) {
+		printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret);
+		retval = -EBUSY;
+		goto error_all;
+	}
+
+	ucpia->buffers[0]->status = FRAME_EMPTY;
+	ucpia->buffers[0]->length = 0;
+	ucpia->buffers[1]->status = FRAME_EMPTY;
+	ucpia->buffers[1]->length = 0;
+	ucpia->buffers[2]->status = FRAME_EMPTY;
+	ucpia->buffers[2]->length = 0;
+	ucpia->curbuff = ucpia->buffers[0];
+	ucpia->workbuff = ucpia->buffers[1];
+
+	/* We double buffer the Iso lists */
+	urb = usb_alloc_urb(FRAMES_PER_DESC);
+	if (!urb) {
+		printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+		retval = -ENOMEM;
+		goto error_all;
+	}
+
+	ucpia->sbuf[0].urb = urb;
+	urb->dev = ucpia->dev;
+	urb->context = ucpia;
+	urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+	urb->transfer_flags = USB_ISO_ASAP;
+	urb->transfer_buffer = ucpia->sbuf[0].data;
+	urb->complete = cpia_usb_complete;
+	urb->number_of_packets = FRAMES_PER_DESC;
+	urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+	for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+		urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+		urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+	}
+
+	urb = usb_alloc_urb(FRAMES_PER_DESC);
+	if (!urb) {
+		printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+		retval = -ENOMEM;
+		goto error_all;
+	}
+
+	ucpia->sbuf[1].urb = urb;
+	urb->dev = ucpia->dev;
+	urb->context = ucpia;
+	urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+	urb->transfer_flags = USB_ISO_ASAP;
+	urb->transfer_buffer = ucpia->sbuf[1].data;
+	urb->complete = cpia_usb_complete;
+	urb->number_of_packets = FRAMES_PER_DESC;
+	urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+	for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+		urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+		urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+	}
+
+	ucpia->sbuf[1].urb->next = ucpia->sbuf[0].urb;
+	ucpia->sbuf[0].urb->next = ucpia->sbuf[1].urb;
+	
+	err = usb_submit_urb(ucpia->sbuf[0].urb);
+	if (err)
+		printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n",
+			err);
+	err = usb_submit_urb(ucpia->sbuf[1].urb);
+	if (err)
+		printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n",
+			err);
+
+	ucpia->streaming = 1;
+	ucpia->open = 1;
+
+	return 0;
+
+error_all:
+	kfree (ucpia->sbuf[1].data);
+error_0:
+	kfree (ucpia->sbuf[0].data);
+	
+	return retval;
+}
+
+//
+// convenience functions
+//
+
+/****************************************************************************
+ *
+ *  WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size)
+{
+	if (!packet)
+		return -EINVAL;
+
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			 packet[1] + (packet[0] << 8),
+			 USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			 packet[2] + (packet[3] << 8), 
+			 packet[4] + (packet[5] << 8), buf, size, HZ);
+}
+
+/****************************************************************************
+ *
+ *  ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size)
+{
+	if (!packet || size <= 0)
+		return -EINVAL;
+
+	return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			 packet[1] + (packet[0] << 8),
+			 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			 packet[2] + (packet[3] << 8), 
+			 packet[4] + (packet[5] << 8), buf, size, HZ);
+}
+
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+	int err = 0;
+	int databytes;
+	struct usb_cpia *ucpia = (struct usb_cpia *)privdata;
+	struct usb_device *udev = ucpia->dev;
+
+	if (!udev) {
+		DBG("Internal driver error: udev is NULL\n");
+		return -EINVAL;
+	}
+
+	if (!command) {
+		DBG("Internal driver error: command is NULL\n");
+		return -EINVAL;
+	}
+
+	databytes = (((int)command[7])<<8) | command[6];
+
+	if (command[0] == DATA_IN) {
+		u8 buffer[8];
+
+		if (!data) {
+			DBG("Internal driver error: data is NULL\n");
+			return -EINVAL;
+		}
+
+		err = ReadPacket(udev, command, buffer, 8);
+		if (err < 0)
+			return err;
+
+		memcpy(data, buffer, databytes);
+	} else if(command[0] == DATA_OUT)
+		WritePacket(udev, command, data, databytes);
+	else {
+		DBG("Unexpected first byte of command: %x\n", command[0]);
+		err = -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+	void *cbdata)
+{
+	return -ENODEV;
+}
+
+static int cpia_usb_streamStart(void *privdata)
+{
+	return -ENODEV;
+}
+
+static int cpia_usb_streamStop(void *privdata)
+{
+	return -ENODEV;
+}
+
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
+{
+	struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+	struct framebuf *mybuff;
+
+	if (!ucpia || !ucpia->present)
+		return -1;
+  
+	if (ucpia->curbuff->status != FRAME_READY)
+		interruptible_sleep_on(&ucpia->wq_stream);
+	else
+		DBG("Frame already waiting!\n");
+
+	mybuff = ucpia->curbuff;
+
+	if (!mybuff)
+		return -1;
+  
+	if (mybuff->status != FRAME_READY || mybuff->length < 4) {
+		DBG("Something went wrong!\n");
+		return -1;
+	}
+
+	memcpy(frame, mybuff->data, mybuff->length);
+	mybuff->status = FRAME_EMPTY;
+  
+/*   DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n",  */
+/*       mybuff->length, frame[0], frame[1], */
+/*       frame[mybuff->length-4], frame[mybuff->length-3],  */
+/*       frame[mybuff->length-2], frame[mybuff->length-1]); */
+
+	return mybuff->length;
+}
+
+static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try)
+{
+	if (!ucpia->streaming)
+		return;
+
+	ucpia->streaming = 0;
+
+	/* Set packet size to 0 */
+	if (try) {
+		int ret;
+
+		ret = usb_set_interface(ucpia->dev, ucpia->iface, 0);
+		if (ret < 0) {
+			printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret);
+			return;
+		}
+	}
+
+	/* Unschedule all of the iso td's */
+	if (ucpia->sbuf[1].urb) {
+		usb_unlink_urb(ucpia->sbuf[1].urb);
+		usb_free_urb(ucpia->sbuf[1].urb);
+		ucpia->sbuf[1].urb = NULL;
+	}
+
+	if (ucpia->sbuf[0].urb) {
+		usb_unlink_urb(ucpia->sbuf[0].urb);
+		usb_free_urb(ucpia->sbuf[0].urb);
+		ucpia->sbuf[0].urb = NULL;
+	}
+}
+
+static int cpia_usb_close(void *privdata)
+{
+	struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+
+	ucpia->open = 0;
+
+	cpia_usb_free_resources(ucpia, 1);
+
+	if (!ucpia->present)
+		kfree(ucpia);
+
+	return 0;
+}
+
+int cpia_usb_init(void)
+{
+	/* return -ENODEV; */
+	return 0;
+}
+
+/* Probing and initializing */
+
+static void *cpia_probe(struct usb_device *udev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_cpia *ucpia;
+	struct cam_data *cam;
+	int ret;
+  
+	/* A multi-config CPiA camera? */
+	if (udev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	interface = &udev->actconfig->interface[ifnum].altsetting[0];
+
+	/* Is it a CPiA? */
+	if (!((udev->descriptor.idVendor == 0x0553 && 
+	     udev->descriptor.idProduct == 0x0002) ||
+	    (udev->descriptor.idVendor == 0x0813 &&
+	     udev->descriptor.idProduct == 0x0001))) /* GA 04/14/00 */
+	  return NULL;
+
+	/* We found a CPiA */
+	printk(KERN_INFO "USB CPiA camera found\n");
+
+	ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL);
+	if (!ucpia) {
+		printk(KERN_ERR "couldn't kmalloc cpia struct\n");
+		return NULL;
+	}
+
+	memset(ucpia, 0, sizeof(*ucpia));
+
+	ucpia->dev = udev;
+	ucpia->iface = interface->bInterfaceNumber;
+	init_waitqueue_head(&ucpia->wq_stream);
+
+	ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0]));
+	if (!ucpia->buffers[0]) {
+		printk(KERN_ERR "couldn't vmalloc frame buffer 0\n");
+		goto fail_alloc_0;
+	}
+
+	ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1]));
+	if (!ucpia->buffers[1]) {
+		printk(KERN_ERR "couldn't vmalloc frame buffer 1\n");
+		goto fail_alloc_1;
+	}
+
+	ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2]));
+	if (!ucpia->buffers[2]) {
+		printk(KERN_ERR "couldn't vmalloc frame buffer 2\n");
+		goto fail_alloc_2;
+	}
+
+	ucpia->buffers[0]->next = ucpia->buffers[1];
+	ucpia->buffers[1]->next = ucpia->buffers[2];
+	ucpia->buffers[2]->next = ucpia->buffers[0];
+
+	ret = usb_set_interface(udev, ucpia->iface, 0);
+	if (ret < 0) {
+		printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret);
+		/* goto fail_all; */
+	}
+
+	/* Before register_camera, important */
+	ucpia->present = 1;
+  
+	cam = cpia_register_camera(&cpia_usb_ops, ucpia);
+	if (!cam) {
+		LOG("failed to cpia_register_camera\n");
+		goto fail_all;
+	}
+
+	ADD_TO_LIST(cam_list, cam);
+
+	return cam;
+
+fail_all:
+	vfree(ucpia->buffers[2]);
+	ucpia->buffers[2] = NULL;
+fail_alloc_2:
+	vfree(ucpia->buffers[1]);
+	ucpia->buffers[1] = NULL;
+fail_alloc_1:
+	vfree(ucpia->buffers[0]);
+	ucpia->buffers[0] = NULL;
+fail_alloc_0:
+
+	return NULL;
+}
+
+static void cpia_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver cpia_driver = {
+	"cpia",
+	cpia_probe,
+	cpia_disconnect,
+	{ NULL, NULL }
+};
+
+/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */
+/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */
+/* will do it for us as well as passing a udev structure - jerdfelt */
+static void cpia_disconnect(struct usb_device *udev, void *ptr)
+{
+	struct cam_data *cam = (struct cam_data *) ptr;
+	struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data;
+  
+	REMOVE_FROM_LIST(cam);
+
+	/* Don't even try to reset the altsetting if we're disconnected */
+	cpia_usb_free_resources(ucpia, 0);
+
+	ucpia->present = 0;
+
+	cpia_unregister_camera(cam);
+
+	ucpia->curbuff->status = FRAME_ERROR;
+
+	if (waitqueue_active(&ucpia->wq_stream))
+		wake_up_interruptible(&ucpia->wq_stream);
+
+	usb_driver_release_interface(&cpia_driver,
+				     &udev->actconfig->interface[0]);
+
+	ucpia->curbuff = ucpia->workbuff = NULL;
+
+	if (ucpia->buffers[2]) {
+		vfree(ucpia->buffers[2]);
+		ucpia->buffers[2] = NULL;
+	}
+
+	if (ucpia->buffers[1]) {
+		vfree(ucpia->buffers[1]);
+		ucpia->buffers[1] = NULL;
+	}
+
+	if (ucpia->buffers[0]) {
+		vfree(ucpia->buffers[0]);
+		ucpia->buffers[0] = NULL;
+	}
+
+	if (!ucpia->open)
+		kfree(ucpia);
+}
+
+int usb_cpia_init(void)
+{
+	cam_list = NULL;
+
+	return usb_register(&cpia_driver);
+}
+
+void usb_cpia_cleanup(void)
+{
+/*
+	struct cam_data *cam;
+
+	while ((cam = cam_list) != NULL)
+		cpia_disconnect(NULL, cam);
+*/
+
+	usb_deregister(&cpia_driver);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_cpia_init();
+}
+
+void cleanup_module(void)
+{
+	usb_cpia_cleanup();
+}
+
+#else /* !MODULE */
+
+__initfunc(void cpia_usb_setup(char *str, int *ints))
+{
+}
+
+#endif /* !MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/Config.in linux.18p17/drivers/char/drm/Config.in
--- linux.vanilla/drivers/char/drm/Config.in	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/Config.in	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,15 @@
+#
+# drm device configuration
+#
+# This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.x.
+#
+
+tristate 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
+if [ "$CONFIG_DRM" != "n" ]; then
+    tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
+    tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
+    tristate '  ATI Rage 128' CONFIG_DRM_R128
+    dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
+    dep_tristate '  Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP
+fi
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/Makefile linux.18p17/drivers/char/drm/Makefile
--- linux.vanilla/drivers/char/drm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/Makefile	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,88 @@
+#
+# Makefile for the drm device driver.  This driver provides support for
+# the Direct Rendering Infrastructure (DRI) in XFree86 4.x.
+#
+
+# drm.o is a fake target -- it is never built
+# The real targets are in the module-list
+O_TARGET	:= drm.o
+module-list     := gamma.o tdfx.o r128.o ffb.o mga.o i810.o
+export-objs     := $(patsubst %.o,%_drv.o,$(module-list))
+
+# libs-objs are included in every module so that radical changes to the
+# architecture of the DRM support library can be made at a later time.
+#
+# The downside is that each module is larger, and a system that uses
+# more than one module (i.e., a dual-head system) will use more memory
+# (but a system that uses exactly one module will use the same amount of
+# memory).
+#
+# The upside is that if the DRM support library ever becomes insufficient
+# for new families of cards, a new library can be implemented for those new
+# cards without impacting the drivers for the old cards.  This is significant,
+# because testing architectural changes to old cards may be impossible, and
+# may delay the implementation of a better architecture.  We've traded slight
+# memory waste (in the dual-head case) for greatly improved long-term
+# maintainability.
+#
+lib-objs   := init.o memory.o proc.o auth.o context.o drawable.o bufs.o
+lib-objs   += lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o
+
+ifeq ($(CONFIG_AGP),y)
+ lib-objs  += agpsupport.o
+else
+ ifeq ($(CONFIG_AGP),m)
+  lib-objs  += agpsupport.o
+ endif
+endif
+
+gamma-objs := $(lib-objs) gamma_drv.o gamma_dma.o
+tdfx-objs  := $(lib-objs) tdfx_drv.o                tdfx_context.o
+r128-objs  := $(lib-objs) r128_drv.o  r128_dma.o    r128_context.o r128_bufs.o
+ffb-objs   := $(lib-objs) ffb_drv.o                 ffb_context.o
+mga-objs   := $(lib-objs) mga_drv.o   mga_dma.o     mga_context.o  mga_bufs.o \
+	      mga_state.o
+i810-objs  := $(lib-objs) i810_drv.o  i810_dma.o    i810_context.o i810_bufs.o
+
+obj-$(CONFIG_DRM_GAMMA) += gamma.o $(gamma-objs)
+obj-$(CONFIG_DRM_TDFX)  += tdfx.o  $(tdfx-objs)
+obj-$(CONFIG_DRM_R128)  += r128.o  $(r128-objs)
+obj-$(CONFIG_DRM_FFB)   += ffb.o   $(ffb-objs)
+
+ifneq ($CONFIG_AGP),)
+obj-$(CONFIG_DRM_MGA)   += mga.o   $(mga-objs)
+obj-$(CONFIG_DRM_I810)  += i810.o  $(i810-objs)
+endif
+
+# Take module names out of obj-y and int-m
+
+obj-y           := $(filter-out $(module-list), $(obj-y))
+int-m           := $(filter-out $(module-list), $(obj-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS          := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS         := $(filter     $(export-objs), $(obj-y))
+M_OBJS          := $(sort $(filter     $(module-list), $(obj-m)))
+MI_OBJS         := $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS        := $(sort $(filter     $(export-objs), $(int-m)))
+
+include $(TOPDIR)/Rules.make
+
+gamma.o: $(gamma-objs)
+	$(LD) -r -o $@ $(gamma-objs)
+
+tdfx.o: $(tdfx-objs)
+	$(LD) -r -o $@ $(tdfx-objs)
+
+mga.o: $(mga-objs)
+	$(LD) -r -o $@ $(mga-objs)
+
+i810.o: $(i810-objs)
+	$(LD) -r -o $@ $(i810-objs)
+
+r128.o: $(r128-objs)
+	$(LD) -r -o $@ $(r128-objs)
+
+ffb.o: $(ffb-objs)
+	$(LD) -r -o $@ $(ffb-objs)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/README.drm linux.18p17/drivers/char/drm/README.drm
--- linux.vanilla/drivers/char/drm/README.drm	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/README.drm	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,41 @@
+
+The Direct Rendering Manager (drm) is a device-independent kernel-level
+device driver that provides support for the XFree86 Direct Rendering
+Infrastructure (DRI).
+
+The DRM supports the Direct Rendering Infrastructure (DRI) in four major
+ways:
+
+    1. The DRM provides synchronized access to the graphics hardware via
+       the use of an optimized two-tiered lock.
+
+    2. The DRM enforces the DRI security policy for access to the graphics
+       hardware by only allowing authenticated X11 clients access to
+       restricted regions of memory.
+
+    3. The DRM provides a generic DMA engine, complete with multiple
+       queues and the ability to detect the need for an OpenGL context
+       switch.
+
+    4. The DRM is extensible via the use of small device-specific modules
+       that rely extensively on the API exported by the DRM module.
+
+
+Documentation on the DRI is available from:
+    http://precisioninsight.com/piinsights.html
+
+For specific information about kernel-level support, see:
+
+    The Direct Rendering Manager, Kernel Support for the Direct Rendering
+    Infrastructure
+    http://precisioninsight.com/dr/drm.html
+
+    Hardware Locking for the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/locking.html
+
+    A Security Analysis of the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/security.html
+
+
+
+$XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/README.drm,v 1.2 1999/09/27 14:59:24 dawes Exp $
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/agpsupport.c linux.18p17/drivers/char/drm/agpsupport.c
--- linux.vanilla/drivers/char/drm/agpsupport.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/agpsupport.c	Tue Sep 26 17:00:25 2000
@@ -0,0 +1,330 @@
+/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*-
+ * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/module.h>
+
+drm_agp_func_t drm_agp = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+/* The C standard says that 'void *' is not guaranteed to hold a function
+   pointer, so we use this union to define a generic pointer that is
+   guaranteed to hold any of the function pointers we care about. */
+typedef union {
+	void          (*free_memory)(agp_memory *);
+	agp_memory    *(*allocate_memory)(size_t, u32);
+	int           (*bind_memory)(agp_memory *, off_t);
+	int           (*unbind_memory)(agp_memory *);
+	void          (*enable)(u32);
+	int           (*acquire)(void);
+	void          (*release)(void);
+	void          (*copy_info)(agp_kern_info *);
+	unsigned long address;
+} drm_agp_func_u;
+
+typedef struct drm_agp_fill {
+        const char     *name;
+	drm_agp_func_u *f;
+} drm_agp_fill_t;
+
+static drm_agp_fill_t drm_agp_fill[] = {
+	{ __MODULE_STRING(agp_free_memory),
+	   (drm_agp_func_u *)&drm_agp.free_memory     },
+	{ __MODULE_STRING(agp_allocate_memory), 
+	   (drm_agp_func_u *)&drm_agp.allocate_memory },
+	{ __MODULE_STRING(agp_bind_memory),     
+	   (drm_agp_func_u *)&drm_agp.bind_memory     },
+	{ __MODULE_STRING(agp_unbind_memory),   
+	   (drm_agp_func_u *)&drm_agp.unbind_memory   },
+	{ __MODULE_STRING(agp_enable),          
+	   (drm_agp_func_u *)&drm_agp.enable          },
+	{ __MODULE_STRING(agp_backend_acquire), 
+	   (drm_agp_func_u *)&drm_agp.acquire         },
+	{ __MODULE_STRING(agp_backend_release), 
+	   (drm_agp_func_u *)&drm_agp.release         },
+	{ __MODULE_STRING(agp_copy_info),       
+	   (drm_agp_func_u *)&drm_agp.copy_info       },
+	{ NULL, NULL }
+};
+
+int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	agp_kern_info    *kern;
+	drm_agp_info_t   info;
+
+	if (!dev->agp->acquired || !drm_agp.copy_info) return -EINVAL;
+
+	kern                   = &dev->agp->agp_info;
+	info.agp_version_major = kern->version.major;
+	info.agp_version_minor = kern->version.minor;
+	info.mode              = kern->mode;
+	info.aperture_base     = kern->aper_base;
+	info.aperture_size     = kern->aper_size * 1024 * 1024;
+	info.memory_allowed    = kern->max_memory << PAGE_SHIFT;
+	info.memory_used       = kern->current_memory << PAGE_SHIFT;
+	info.id_vendor         = kern->device->vendor;
+	info.id_device         = kern->device->device;
+
+	copy_to_user_ret((drm_agp_info_t *)arg, &info, sizeof(info), -EFAULT);
+	return 0;
+}
+
+int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int              retcode;
+
+	if (dev->agp->acquired || !drm_agp.acquire) return -EINVAL;
+	if ((retcode = (*drm_agp.acquire)())) return retcode;
+	dev->agp->acquired = 1;
+	return 0;
+}
+
+int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+
+	if (!dev->agp->acquired || !drm_agp.release) return -EINVAL;
+	(*drm_agp.release)();
+	dev->agp->acquired = 0;
+	return 0;
+	
+}
+
+int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_mode_t   mode;
+
+	if (!dev->agp->acquired || !drm_agp.enable) return -EINVAL;
+
+	copy_from_user_ret(&mode, (drm_agp_mode_t *)arg, sizeof(mode),
+			   -EFAULT);
+	
+	dev->agp->mode    = mode.mode;
+	(*drm_agp.enable)(mode.mode);
+	dev->agp->base    = dev->agp->agp_info.aper_base;
+	dev->agp->enabled = 1;
+	return 0;
+}
+
+int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	agp_memory       *memory;
+	unsigned long    pages;
+	u32 		 type;
+	if (!dev->agp->acquired) return -EINVAL;
+	copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request),
+			   -EFAULT);
+	if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS)))
+		return -ENOMEM;
+   
+   	memset(entry, 0, sizeof(*entry));
+
+	pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+	type = (u32) request.type;
+
+	if (!(memory = drm_alloc_agp(pages, type))) {
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -ENOMEM;
+	}
+	
+	entry->handle    = (unsigned long)memory->memory;
+	entry->memory    = memory;
+	entry->bound     = 0;
+	entry->pages     = pages;
+	entry->prev      = NULL;
+	entry->next      = dev->agp->memory;
+	if (dev->agp->memory) dev->agp->memory->prev = entry;
+	dev->agp->memory = entry;
+
+	request.handle   = entry->handle;
+        request.physical = memory->physical;
+
+	if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) {
+		dev->agp->memory       = entry->next;
+		dev->agp->memory->prev = NULL;
+		drm_free_agp(memory, pages);
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev,
+					   unsigned long handle)
+{
+	drm_agp_mem_t *entry;
+
+	for (entry = dev->agp->memory; entry; entry = entry->next) {
+		if (entry->handle == handle) return entry;
+	}
+	return NULL;
+}
+
+int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+
+	if (!dev->agp->acquired) return -EINVAL;
+	copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request),
+			   -EFAULT);
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (!entry->bound) return -EINVAL;
+	return drm_unbind_agp(entry->memory);
+}
+
+int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+	int               retcode;
+	int               page;
+	
+	if (!dev->agp->acquired || !drm_agp.bind_memory) return -EINVAL;
+	copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request),
+			   -EFAULT);
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) return -EINVAL;
+	page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE;
+	if ((retcode = drm_bind_agp(entry->memory, page))) return retcode;
+	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
+	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", 
+		  dev->agp->base, entry->bound);
+	return 0;
+}
+
+int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	
+	if (!dev->agp->acquired) return -EINVAL;
+	copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request),
+			   -EFAULT);
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) drm_unbind_agp(entry->memory);
+   
+	if (entry->prev) entry->prev->next = entry->next;
+	else             dev->agp->memory  = entry->next;
+	if (entry->next) entry->next->prev = entry->prev;
+	drm_free_agp(entry->memory, entry->pages);
+	drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+	return 0;
+}
+
+drm_agp_head_t *drm_agp_init(void)
+{
+	drm_agp_fill_t *fill;
+	drm_agp_head_t *head         = NULL;
+	int            agp_available = 1;
+
+	for (fill = &drm_agp_fill[0]; fill->name; fill++) {
+		char *n  = (char *)fill->name;
+		*fill->f = (drm_agp_func_u)get_module_symbol(NULL, n);
+		DRM_DEBUG("%s resolves to 0x%08lx\n", n, (*fill->f).address);
+		if (!(*fill->f).address) agp_available = 0;
+	}
+   
+	DRM_DEBUG("agp_available = %d\n", agp_available);
+
+	if (agp_available) {
+		if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
+			return NULL;
+		memset((void *)head, 0, sizeof(*head));
+		(*drm_agp.copy_info)(&head->agp_info);
+		head->memory = NULL;
+		switch (head->agp_info.chipset) {
+		case INTEL_GENERIC:    head->chipset = "Intel";            break;
+		case INTEL_LX:         head->chipset = "Intel 440LX";      break;
+		case INTEL_BX:         head->chipset = "Intel 440BX";      break;
+		case INTEL_GX:         head->chipset = "Intel 440GX";      break;
+		case INTEL_I810:       head->chipset = "Intel i810";       break;
+		case VIA_GENERIC:      head->chipset = "VIA";              break;
+		case VIA_VP3:          head->chipset = "VIA VP3";          break;
+		case VIA_MVP3:         head->chipset = "VIA MVP3";         break;
+		case VIA_APOLLO_PRO:   head->chipset = "VIA Apollo Pro";   break;
+		case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; break;
+		case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; break;
+		case SIS_GENERIC:      head->chipset = "SiS";              break;
+		case AMD_GENERIC:      head->chipset = "AMD";              break;
+		case AMD_IRONGATE:     head->chipset = "AMD Irongate";     break;
+		case ALI_GENERIC:      head->chipset = "ALi";              break;
+		case ALI_M1541:        head->chipset = "ALi M1541";        break;
+		default:
+		}
+		DRM_INFO("AGP %d.%d on %s @ 0x%08lx %dMB\n",
+			 head->agp_info.version.major,
+			 head->agp_info.version.minor,
+			 head->chipset,
+			 head->agp_info.aper_base,
+			 head->agp_info.aper_size);
+	}
+	return head;
+}
+
+void drm_agp_uninit(void)
+{
+	drm_agp_fill_t *fill;
+	
+	for (fill = &drm_agp_fill[0]; fill->name; fill++) {
+#if LINUX_VERSION_CODE >= 0x020400
+		if ((*fill->f).address) put_module_symbol((*fill->f).address);
+#endif
+		(*fill->f).address = 0;
+	}
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/auth.c linux.18p17/drivers/char/drm/auth.c
--- linux.vanilla/drivers/char/drm/auth.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/auth.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,160 @@
+/* auth.c -- IOCTLs for authentication -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_hash_magic(drm_magic_t magic)
+{
+	return magic & (DRM_HASH_SIZE-1);
+}
+
+static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_file_t	  *retval = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash	  = drm_hash_magic(magic);
+
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
+		if (pt->magic == magic) {
+			retval = pt->priv;
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+	return retval;
+}
+
+int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic)
+{
+	int		  hash;
+	drm_magic_entry_t *entry;
+	
+	DRM_DEBUG("%d\n", magic);
+	
+	hash	     = drm_hash_magic(magic);
+	entry	     = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
+	if (!entry) return -ENOMEM;
+	entry->magic = magic;
+	entry->priv  = priv;
+	entry->next  = NULL;
+
+	down(&dev->struct_sem);
+	if (dev->magiclist[hash].tail) {
+		dev->magiclist[hash].tail->next = entry;
+		dev->magiclist[hash].tail	= entry;
+	} else {
+		dev->magiclist[hash].head	= entry;
+		dev->magiclist[hash].tail	= entry;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_remove_magic(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_magic_entry_t *prev = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash;
+	
+	DRM_DEBUG("%d\n", magic);
+	hash = drm_hash_magic(magic);
+	
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
+		if (pt->magic == magic) {
+			if (dev->magiclist[hash].head == pt) {
+				dev->magiclist[hash].head = pt->next;
+			}
+			if (dev->magiclist[hash].tail == pt) {
+				dev->magiclist[hash].tail = prev;
+			}
+			if (prev) {
+				prev->next = pt->next;
+			}
+			up(&dev->struct_sem);
+			return 0;
+		}
+	}
+	up(&dev->struct_sem);
+
+	drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+	
+	return -EINVAL;
+}
+
+int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	static drm_magic_t sequence = 0;
+	static spinlock_t  lock	    = SPIN_LOCK_UNLOCKED;
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+
+				/* Find unique magic */
+	if (priv->magic) {
+		auth.magic = priv->magic;
+	} else {
+		spin_lock(&lock);
+		do {
+			if (!sequence) ++sequence; /* reserve 0 */
+			auth.magic = sequence++;
+		} while (drm_find_file(dev, auth.magic));
+		spin_unlock(&lock);
+		priv->magic = auth.magic;
+		drm_add_magic(dev, priv, auth.magic);
+	}
+	
+	DRM_DEBUG("%u\n", auth.magic);
+	copy_to_user_ret((drm_auth_t *)arg, &auth, sizeof(auth), -EFAULT);
+	return 0;
+}
+
+int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+	drm_file_t	   *file;
+
+	copy_from_user_ret(&auth, (drm_auth_t *)arg, sizeof(auth), -EFAULT);
+	DRM_DEBUG("%u\n", auth.magic);
+	if ((file = drm_find_file(dev, auth.magic))) {
+		file->authenticated = 1;
+		drm_remove_magic(dev, auth.magic);
+		return 0;
+	}
+	return -EINVAL;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/bufs.c linux.18p17/drivers/char/drm/bufs.c
--- linux.vanilla/drivers/char/drm/bufs.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/bufs.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,538 @@
+/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "linux/un.h"
+
+				/* Compute order.  Can be made faster. */
+int drm_order(unsigned long size)
+{
+	int	      order;
+	unsigned long tmp;
+
+	for (order = 0, tmp = size; tmp >>= 1; ++order);
+	if (size & ~(1 << order)) ++order;
+	return order;
+}
+
+int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map;
+	
+	if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */
+
+	map	     = drm_alloc(sizeof(*map), DRM_MEM_MAPS);
+	if (!map) return -ENOMEM;
+	if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EFAULT;
+	}
+
+	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
+		  map->offset, map->size, map->type);
+	if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+	map->mtrr   = -1;
+	map->handle = 0;
+
+	switch (map->type) {
+	case _DRM_REGISTERS:
+	case _DRM_FRAME_BUFFER:
+#ifndef __sparc__
+		if (map->offset + map->size < map->offset
+		    || map->offset < virt_to_phys(high_memory)) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -EINVAL;
+		}
+#endif
+#ifdef CONFIG_MTRR
+		if (map->type == _DRM_FRAME_BUFFER
+		    || (map->flags & _DRM_WRITE_COMBINING)) {
+			map->mtrr = mtrr_add(map->offset, map->size,
+					     MTRR_TYPE_WRCOMB, 1);
+		}
+#endif
+		map->handle = drm_ioremap(map->offset, map->size);
+		break;
+			
+
+	case _DRM_SHM:
+		map->handle = (void *)drm_alloc_pages(drm_order(map->size)
+						      - PAGE_SHIFT,
+						      DRM_MEM_SAREA);
+		DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size),
+			  map->handle);
+		if (!map->handle) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -ENOMEM;
+		}
+		map->offset = (unsigned long)map->handle;
+		if (map->flags & _DRM_CONTAINS_LOCK) {
+			dev->lock.hw_lock = map->handle; /* Pointer to lock */
+		}
+		break;
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	case _DRM_AGP:
+		map->offset = map->offset + dev->agp->base;
+		break;
+#endif
+	default:
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+
+	down(&dev->struct_sem);
+	if (dev->maplist) {
+		++dev->map_count;
+		dev->maplist = drm_realloc(dev->maplist,
+					   (dev->map_count-1)
+					   * sizeof(*dev->maplist),
+					   dev->map_count
+					   * sizeof(*dev->maplist),
+					   DRM_MEM_MAPS);
+	} else {
+		dev->map_count = 1;
+		dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist),
+					 DRM_MEM_MAPS);
+	}
+	dev->maplist[dev->map_count-1] = map;
+	up(&dev->struct_sem);
+
+	copy_to_user_ret((drm_map_t *)arg, map, sizeof(*map), -EFAULT);
+	if (map->type != _DRM_SHM) {
+		copy_to_user_ret(&((drm_map_t *)arg)->handle,
+				 &map->offset,
+				 sizeof(map->offset),
+				 -EFAULT);
+	}		
+	return 0;
+}
+
+int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	copy_to_user_ret((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				copy_to_user_ret(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark),
+						 -EFAULT);
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	copy_to_user_ret((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+	
+	return 0;
+}
+
+int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		copy_from_user_ret(&idx,
+				   &request.list[i],
+				   sizeof(idx),
+				   -EFAULT);
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	if (request.count >= dma->buf_count) {
+		down(&current->mm->mmap_sem);
+		virtual = do_mmap(filp, 0, dma->byte_count,
+				  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+		up(&current->mm->mmap_sem);
+		if (virtual > -1024UL) {
+				/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	copy_to_user_ret((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+
+	return retcode;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/context.c linux.18p17/drivers/char/drm/context.c
--- linux.vanilla/drivers/char/drm/context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/context.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,308 @@
+/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx)
+{
+	DRM_DEBUG("\n");
+	
+	if (atomic_read(&q->use_count) != 1
+	    || atomic_read(&q->finalization)
+	    || atomic_read(&q->block_count)) {
+		DRM_ERROR("New queue is already in use: u%d f%d b%d\n",
+			  atomic_read(&q->use_count),
+			  atomic_read(&q->finalization),
+			  atomic_read(&q->block_count));
+	}
+		  
+	atomic_set(&q->finalization,  0);
+	atomic_set(&q->block_count,   0);
+	atomic_set(&q->block_read,    0);
+	atomic_set(&q->block_write,   0);
+	atomic_set(&q->total_queued,  0);
+	atomic_set(&q->total_flushed, 0);
+	atomic_set(&q->total_locks,   0);
+
+	init_waitqueue_head(&q->write_queue);
+	init_waitqueue_head(&q->read_queue);
+	init_waitqueue_head(&q->flush_queue);
+
+	q->flags = ctx->flags;
+
+	drm_waitlist_create(&q->waitlist, dev->dma->buf_count);
+
+	return 0;
+}
+
+
+/* drm_alloc_queue:
+PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not
+	disappear (so all deallocation must be done after IOCTLs are off)
+     2) dev->queue_count < dev->queue_slots
+     3) dev->queuelist[i].use_count == 0 and
+	dev->queuelist[i].finalization == 0 if i not in use 
+POST: 1) dev->queuelist[i].use_count == 1
+      2) dev->queue_count < dev->queue_slots */
+		
+static int drm_alloc_queue(drm_device_t *dev)
+{
+	int	    i;
+	drm_queue_t *queue;
+	int	    oldslots;
+	int	    newslots;
+				/* Check for a free queue */
+	for (i = 0; i < dev->queue_count; i++) {
+		atomic_inc(&dev->queuelist[i]->use_count);
+		if (atomic_read(&dev->queuelist[i]->use_count) == 1
+		    && !atomic_read(&dev->queuelist[i]->finalization)) {
+			DRM_DEBUG("%d (free)\n", i);
+			return i;
+		}
+		atomic_dec(&dev->queuelist[i]->use_count);
+	}
+				/* Allocate a new queue */
+	down(&dev->struct_sem);
+	
+	queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES);
+	memset(queue, 0, sizeof(*queue));
+	atomic_set(&queue->use_count, 1);
+	
+	++dev->queue_count;
+	if (dev->queue_count >= dev->queue_slots) {
+		oldslots = dev->queue_slots * sizeof(*dev->queuelist);
+		if (!dev->queue_slots) dev->queue_slots = 1;
+		dev->queue_slots *= 2;
+		newslots = dev->queue_slots * sizeof(*dev->queuelist);
+
+		dev->queuelist = drm_realloc(dev->queuelist,
+					     oldslots,
+					     newslots,
+					     DRM_MEM_QUEUES);
+		if (!dev->queuelist) {
+			up(&dev->struct_sem);
+			DRM_DEBUG("out of memory\n");
+			return -ENOMEM;
+		}
+	}
+	dev->queuelist[dev->queue_count-1] = queue;
+	
+	up(&dev->struct_sem);
+	DRM_DEBUG("%d (new)\n", dev->queue_count - 1);
+	return dev->queue_count - 1;
+}
+
+int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+
+int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Init kernel's context and get a new one. */
+		drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+		ctx.handle = drm_alloc_queue(dev);
+	}
+	drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+	DRM_DEBUG("%d\n", ctx.handle);
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+
+	if (DRM_BUFCOUNT(&q->waitlist)) {
+		atomic_dec(&q->use_count);
+		return -EBUSY;
+	}
+	
+	q->flags = ctx.flags;
+	
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	ctx.flags = q->flags;
+	atomic_dec(&q->use_count);
+	
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	
+	return 0;
+}
+
+int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return drm_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+	drm_buf_t	*buf;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	atomic_inc(&q->finalization); /* Mark queue in finalization state */
+	atomic_sub(2, &q->use_count); /* Mark queue as unused (pending
+					 finalization) */
+
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) {
+			clear_bit(0, &dev->interrupt_flag);
+			return -EINTR;
+		}
+	}
+				/* Remove queued buffers */
+	while ((buf = drm_waitlist_get(&q->waitlist))) {
+		drm_free_buffer(dev, buf);
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	
+				/* Wakeup blocked processes */
+	wake_up_interruptible(&q->read_queue);
+	wake_up_interruptible(&q->write_queue);
+	wake_up_interruptible(&q->flush_queue);
+	
+				/* Finalization over.  Queue is made
+				   available when both use_count and
+				   finalization become 0, which won't
+				   happen until all the waiting processes
+				   stop waiting. */
+	atomic_dec(&q->finalization);
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ctxbitmap.c linux.18p17/drivers/char/drm/ctxbitmap.c
--- linux.vanilla/drivers/char/drm/ctxbitmap.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/ctxbitmap.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,85 @@
+/* ctxbitmap.c -- Context bitmap management -*- linux-c -*-
+ * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com
+ * 
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle)
+{
+	if (ctx_handle < 0) goto failed;
+
+	if (ctx_handle < DRM_MAX_CTXBITMAP) {
+		clear_bit(ctx_handle, dev->ctx_bitmap);
+		return;
+	}
+failed:
+       	DRM_ERROR("Attempt to free invalid context handle: %d\n",
+		  ctx_handle);
+       	return;
+}
+
+int drm_ctxbitmap_next(drm_device_t *dev)
+{
+	int bit;
+
+	bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
+	if (bit < DRM_MAX_CTXBITMAP) {
+		set_bit(bit, dev->ctx_bitmap);
+	   	DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit);
+		return bit;
+	}
+	return -1;
+}
+
+int drm_ctxbitmap_init(drm_device_t *dev)
+{
+	int i;
+   	int temp;
+
+	dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, 
+						      DRM_MEM_CTXBITMAP);
+	if(dev->ctx_bitmap == NULL) {
+		return -ENOMEM;
+	}
+	memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE);
+	for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+		temp = drm_ctxbitmap_next(dev);
+	   	DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp);
+	}
+
+	return 0;
+}
+
+void drm_ctxbitmap_cleanup(drm_device_t *dev)
+{
+	drm_free((void *)dev->ctx_bitmap, PAGE_SIZE,
+		 DRM_MEM_CTXBITMAP);
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/dma.c linux.18p17/drivers/char/drm/dma.c
--- linux.vanilla/drivers/char/drm/dma.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/dma.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,540 @@
+/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinuxa.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+void drm_dma_setup(drm_device_t *dev)
+{
+	int i;
+	
+	dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER);
+	memset(dev->dma, 0, sizeof(*dev->dma));
+	for (i = 0; i <= DRM_MAX_ORDER; i++)
+		memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0]));
+}
+
+void drm_dma_takedown(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma = dev->dma;
+	int		  i, j;
+
+	if (!dma) return;
+	
+				/* Clear dma buffers */
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].seg_count) {
+			DRM_DEBUG("order %d: buf_count = %d,"
+				  " seg_count = %d\n",
+				  i,
+				  dma->bufs[i].buf_count,
+				  dma->bufs[i].seg_count);
+			for (j = 0; j < dma->bufs[i].seg_count; j++) {
+				drm_free_pages(dma->bufs[i].seglist[j],
+					       dma->bufs[i].page_order,
+					       DRM_MEM_DMA);
+			}
+			drm_free(dma->bufs[i].seglist,
+				 dma->bufs[i].seg_count
+				 * sizeof(*dma->bufs[0].seglist),
+				 DRM_MEM_SEGS);
+		}
+	   	if(dma->bufs[i].buf_count) {
+		   	for(j = 0; j < dma->bufs[i].buf_count; j++) {
+			   if(dma->bufs[i].buflist[j].dev_private) {
+			      drm_free(dma->bufs[i].buflist[j].dev_private,
+				       dma->bufs[i].buflist[j].dev_priv_size,
+				       DRM_MEM_BUFS);
+			   }
+			}
+		   	drm_free(dma->bufs[i].buflist,
+				 dma->bufs[i].buf_count *
+				 sizeof(*dma->bufs[0].buflist),
+				 DRM_MEM_BUFS);
+		   	drm_freelist_destroy(&dma->bufs[i].freelist);
+		}
+	}
+	
+	if (dma->buflist) {
+		drm_free(dma->buflist,
+			 dma->buf_count * sizeof(*dma->buflist),
+			 DRM_MEM_BUFS);
+	}
+
+	if (dma->pagelist) {
+		drm_free(dma->pagelist,
+			 dma->page_count * sizeof(*dma->pagelist),
+			 DRM_MEM_PAGES);
+	}
+	drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER);
+	dev->dma = NULL;
+}
+
+#if DRM_DMA_HISTOGRAM
+/* This is slow, but is useful for debugging. */
+int drm_histogram_slot(unsigned long count)
+{
+	int value = DRM_DMA_HISTOGRAM_INITIAL;
+	int slot;
+
+	for (slot = 0;
+	     slot < DRM_DMA_HISTOGRAM_SLOTS;
+	     ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) {
+		if (count < value) return slot;
+	}
+	return DRM_DMA_HISTOGRAM_SLOTS - 1;
+}
+
+void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf)
+{
+	cycles_t queued_to_dispatched;
+	cycles_t dispatched_to_completed;
+	cycles_t completed_to_freed;
+	int	 q2d, d2c, c2f, q2c, q2f;
+	
+	if (buf->time_queued) {
+		queued_to_dispatched	= (buf->time_dispatched
+					   - buf->time_queued);
+		dispatched_to_completed = (buf->time_completed
+					   - buf->time_dispatched);
+		completed_to_freed	= (buf->time_freed
+					   - buf->time_completed);
+
+		q2d = drm_histogram_slot(queued_to_dispatched);
+		d2c = drm_histogram_slot(dispatched_to_completed);
+		c2f = drm_histogram_slot(completed_to_freed);
+
+		q2c = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed);
+		q2f = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed
+					 + completed_to_freed);
+		
+		atomic_inc(&dev->histo.total);
+		atomic_inc(&dev->histo.queued_to_dispatched[q2d]);
+		atomic_inc(&dev->histo.dispatched_to_completed[d2c]);
+		atomic_inc(&dev->histo.completed_to_freed[c2f]);
+		
+		atomic_inc(&dev->histo.queued_to_completed[q2c]);
+		atomic_inc(&dev->histo.queued_to_freed[q2f]);
+
+	}
+	buf->time_queued     = 0;
+	buf->time_dispatched = 0;
+	buf->time_completed  = 0;
+	buf->time_freed	     = 0;
+}
+#endif
+
+void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if (!buf) return;
+	
+	buf->waiting  = 0;
+	buf->pending  = 0;
+	buf->pid      = 0;
+	buf->used     = 0;
+#if DRM_DMA_HISTOGRAM
+	buf->time_completed = get_cycles();
+#endif
+	if (waitqueue_active(&buf->dma_wait)) {
+		wake_up_interruptible(&buf->dma_wait);
+	} else {
+				/* If processes are waiting, the last one
+				   to wake will put the buffer on the free
+				   list.  If no processes are waiting, we
+				   put the buffer on the freelist here. */
+		drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf);
+	}
+}
+
+void drm_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+	for (i = 0; i < dma->buf_count; i++) {
+		if (dma->buflist[i]->pid == pid) {
+			switch (dma->buflist[i]->list) {
+			case DRM_LIST_NONE:
+				drm_free_buffer(dev, dma->buflist[i]);
+				break;
+			case DRM_LIST_WAIT:
+				dma->buflist[i]->list = DRM_LIST_RECLAIM;
+				break;
+			default:
+				/* Buffer already on hardware. */
+				break;
+			}
+		}
+	}
+}
+
+int drm_context_switch(drm_device_t *dev, int old, int new)
+{
+	char	    buf[64];
+	drm_queue_t *q;
+
+	atomic_inc(&dev->total_ctx);
+
+	if (test_and_set_bit(0, &dev->context_flag)) {
+		DRM_ERROR("Reentering -- FIXME\n");
+		return -EBUSY;
+	}
+
+#if DRM_DMA_HISTOGRAM
+	dev->ctx_start = get_cycles();
+#endif
+	
+	DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+	if (new >= dev->queue_count) {
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (new == dev->last_context) {
+		clear_bit(0, &dev->context_flag);
+		return 0;
+	}
+	
+	q = dev->queuelist[new];
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+		atomic_dec(&q->use_count);
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (drm_flags & DRM_FLAG_NOCTX) {
+		drm_context_switch_complete(dev, new);
+	} else {
+		sprintf(buf, "C %d %d\n", old, new);
+		drm_write_string(dev, buf);
+	}
+	
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+int drm_context_switch_complete(drm_device_t *dev, int new)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+	dev->last_switch  = jiffies;
+	
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("Lock isn't held after context switch\n");
+	}
+
+	if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("Cannot free lock\n");
+		}
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+						      - dev->ctx_start)]);
+		   
+#endif
+	clear_bit(0, &dev->context_flag);
+	wake_up_interruptible(&dev->context_wait);
+	
+	return 0;
+}
+
+void drm_clear_next_buffer(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dma->next_buffer = NULL;
+	if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) {
+		wake_up_interruptible(&dma->next_queue->flush_queue);
+	}
+	dma->next_queue	 = NULL;
+}
+
+
+int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long))
+{
+	int	   i;
+	int	   candidate = -1;
+	int	   j	     = jiffies;
+
+	if (!dev) {
+		DRM_ERROR("No device\n");
+		return -1;
+	}
+	if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) {
+				/* This only happens between the time the
+				   interrupt is initialized and the time
+				   the queues are initialized. */
+		return -1;
+	}
+
+				/* Doing "while locked" DMA? */
+	if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) {
+		return DRM_KERNEL_CONTEXT;
+	}
+
+				/* If there are buffers on the last_context
+				   queue, and we have not been executing
+				   this context very long, continue to
+				   execute this context. */
+	if (dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j
+	    && DRM_WAITCOUNT(dev, dev->last_context)) {
+		return dev->last_context;
+	}
+
+				/* Otherwise, find a candidate */
+	for (i = dev->last_checked + 1; i < dev->queue_count; i++) {
+		if (DRM_WAITCOUNT(dev, i)) {
+			candidate = dev->last_checked = i;
+			break;
+		}
+	}
+
+	if (candidate < 0) {
+		for (i = 0; i < dev->queue_count; i++) {
+			if (DRM_WAITCOUNT(dev, i)) {
+				candidate = dev->last_checked = i;
+				break;
+			}
+		}
+	}
+
+	if (wrapper
+	    && candidate >= 0
+	    && candidate != dev->last_context
+	    && dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j) {
+		if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) {
+			del_timer(&dev->timer);
+			dev->timer.function = wrapper;
+			dev->timer.data	    = (unsigned long)dev;
+			dev->timer.expires  = dev->last_switch+DRM_TIME_SLICE;
+			add_timer(&dev->timer);
+		}
+		return -1;
+	}
+
+	return candidate;
+}
+
+
+int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d)
+{
+	int		  i;
+	drm_queue_t	  *q;
+	drm_buf_t	  *buf;
+	int		  idx;
+	int		  while_locked = 0;
+	drm_device_dma_t  *dma = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+	DRM_DEBUG("%d\n", d->send_count);
+
+	if (d->flags & _DRM_DMA_WHILE_LOCKED) {
+		int context = dev->lock.hw_lock->lock;
+		
+		if (!_DRM_LOCK_IS_HELD(context)) {
+			DRM_ERROR("No lock held during \"while locked\""
+				  " request\n");
+			return -EINVAL;
+		}
+		if (d->context != _DRM_LOCKING_CONTEXT(context)
+		    && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) {
+			DRM_ERROR("Lock held by %d while %d makes"
+				  " \"while locked\" request\n",
+				  _DRM_LOCKING_CONTEXT(context),
+				  d->context);
+			return -EINVAL;
+		}
+		q = dev->queuelist[DRM_KERNEL_CONTEXT];
+		while_locked = 1;
+	} else {
+		q = dev->queuelist[d->context];
+	}
+
+
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->block_write)) {
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&q->write_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			if (!atomic_read(&q->block_write)) break;
+			schedule();
+			if (signal_pending(current)) {
+				atomic_dec(&q->use_count);
+				return -EINTR;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->write_queue, &entry);
+	}
+	
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer %d on list %d\n",
+				  current->pid, buf->idx, buf->list);
+		}
+		buf->used	  = d->send_sizes[i];
+		buf->while_locked = while_locked;
+		buf->context	  = d->context;
+		if (!buf->used) {
+			DRM_ERROR("Queueing 0 length buffer\n");
+		}
+		if (buf->pending) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		if (buf->waiting) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		buf->waiting = 1;
+		if (atomic_read(&q->use_count) == 1
+		    || atomic_read(&q->finalization)) {
+			drm_free_buffer(dev, buf);
+		} else {
+			drm_waitlist_put(&q->waitlist, buf);
+			atomic_inc(&q->total_queued);
+		}
+	}
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d,
+					int order)
+{
+	int		  i;
+	drm_buf_t	  *buf;
+	drm_device_dma_t  *dma = dev->dma;
+	
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = drm_freelist_get(&dma->bufs[order].freelist,
+				       d->flags & _DRM_DMA_WAIT);
+		if (!buf) break;
+		if (buf->pending || buf->waiting) {
+			DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n",
+				  buf->idx,
+				  buf->pid,
+				  buf->waiting,
+				  buf->pending);
+		}
+		buf->pid     = current->pid;
+		copy_to_user_ret(&d->request_indices[i],
+				 &buf->idx,
+				 sizeof(buf->idx),
+				 -EFAULT);
+		copy_to_user_ret(&d->request_sizes[i],
+				 &buf->total,
+				 sizeof(buf->total),
+				 -EFAULT);
+		++d->granted_count;
+	}
+	return 0;
+}
+
+
+int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma)
+{
+	int		  order;
+	int		  retcode = 0;
+	int		  tmp_order;
+	
+	order = drm_order(dma->request_size);
+
+	dma->granted_count = 0;
+	retcode		   = drm_dma_get_buffers_of_order(dev, dma, order);
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_SMALLER_OK)) {
+		for (tmp_order = order - 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order >= DRM_MIN_ORDER;
+		     --tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_LARGER_OK)) {
+		for (tmp_order = order + 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order <= DRM_MAX_ORDER;
+		     ++tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drawable.c linux.18p17/drivers/char/drm/drawable.c
--- linux.vanilla/drivers/char/drm/drawable.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/drawable.c	Fri Sep  1 14:12:32 2000
@@ -0,0 +1,50 @@
+/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_draw_t draw;
+
+	draw.handle = 0;	/* NOOP */
+	DRM_DEBUG("%d\n", draw.handle);
+	copy_to_user_ret((drm_draw_t *)arg, &draw, sizeof(draw), -EFAULT);
+	return 0;
+}
+
+int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	return 0;		/* NOOP */
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drm.h linux.18p17/drivers/char/drm/drm.h
--- linux.vanilla/drivers/char/drm/drm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/drm.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,367 @@
+/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ * Acknowledgements:
+ * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
+ *
+ */
+
+#ifndef _DRM_H_
+#define _DRM_H_
+
+#include <asm/ioctl.h>		/* For _IO* macros */
+
+#define DRM_PROC_DEVICES "/proc/devices"
+#define DRM_PROC_MISC	 "/proc/misc"
+#define DRM_PROC_DRM	 "/proc/drm"
+#define DRM_DEV_DRM	 "/dev/drm"
+#define DRM_DEV_MODE	 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
+#define DRM_DEV_UID	 0
+#define DRM_DEV_GID	 0
+
+
+#define DRM_NAME	"drm"	  /* Name in kernel, /dev, and /proc	    */
+#define DRM_MIN_ORDER	5	  /* At least 2^5 bytes = 32 bytes	    */
+#define DRM_MAX_ORDER	22	  /* Up to 2^22 bytes = 4MB		    */
+#define DRM_RAM_PERCENT 10	  /* How much system ram can we lock?	    */
+
+#define _DRM_LOCK_HELD	0x80000000 /* Hardware lock is held		    */
+#define _DRM_LOCK_CONT	0x40000000 /* Hardware lock is contended	    */
+#define _DRM_LOCK_IS_HELD(lock)	   ((lock) & _DRM_LOCK_HELD)
+#define _DRM_LOCK_IS_CONT(lock)	   ((lock) & _DRM_LOCK_CONT)
+#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+typedef unsigned long drm_handle_t;
+typedef unsigned int  drm_context_t;
+typedef unsigned int  drm_drawable_t;
+typedef unsigned int  drm_magic_t;
+
+/* Warning: If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well */
+
+typedef struct drm_clip_rect {
+           unsigned short x1;
+           unsigned short y1;
+           unsigned short x2;
+           unsigned short y2;
+} drm_clip_rect_t;
+
+/* Seperate include files for the i810/mga/r128 specific structures */
+#include "mga_drm.h"
+#include "i810_drm.h"
+#include "r128_drm.h"
+
+typedef struct drm_version {
+	int    version_major;	  /* Major version			    */
+	int    version_minor;	  /* Minor version			    */
+	int    version_patchlevel;/* Patch level			    */
+	size_t name_len;	  /* Length of name buffer		    */
+	char   *name;		  /* Name of driver			    */
+	size_t date_len;	  /* Length of date buffer		    */
+	char   *date;		  /* User-space buffer to hold date	    */
+	size_t desc_len;	  /* Length of desc buffer		    */
+	char   *desc;		  /* User-space buffer to hold desc	    */
+} drm_version_t;
+
+typedef struct drm_unique {
+	size_t unique_len;	  /* Length of unique			    */
+	char   *unique;		  /* Unique name for driver instantiation   */
+} drm_unique_t;
+
+typedef struct drm_list {
+	int		 count;	  /* Length of user-space structures	    */
+	drm_version_t	 *version;
+} drm_list_t;
+
+typedef struct drm_block {
+	int		 unused;
+} drm_block_t;
+
+typedef struct drm_control {
+	enum {
+		DRM_ADD_COMMAND,
+		DRM_RM_COMMAND,
+		DRM_INST_HANDLER,
+		DRM_UNINST_HANDLER
+	}		 func;
+	int		 irq;
+} drm_control_t;
+
+typedef enum drm_map_type {
+	_DRM_FRAME_BUFFER = 0,	  /* WC (no caching), no core dump	    */
+	_DRM_REGISTERS	  = 1,	  /* no caching, no core dump		    */
+	_DRM_SHM	  = 2,	  /* shared, cached			    */
+	_DRM_AGP          = 3	  /* AGP/GART                               */
+} drm_map_type_t;
+
+typedef enum drm_map_flags {
+	_DRM_RESTRICTED	     = 0x01, /* Cannot be mapped to user-virtual    */
+	_DRM_READ_ONLY	     = 0x02,
+	_DRM_LOCKED	     = 0x04, /* shared, cached, locked		    */
+	_DRM_KERNEL	     = 0x08, /* kernel requires access		    */
+	_DRM_WRITE_COMBINING = 0x10, /* use write-combining if available    */
+	_DRM_CONTAINS_LOCK   = 0x20  /* SHM page that contains lock	    */
+} drm_map_flags_t;
+
+typedef struct drm_map {
+	unsigned long	offset;	 /* Requested physical address (0 for SAREA)*/
+	unsigned long	size;	 /* Requested physical size (bytes)	    */
+	drm_map_type_t	type;	 /* Type of memory to map		    */
+	drm_map_flags_t flags;	 /* Flags				    */
+	void		*handle; /* User-space: "Handle" to pass to mmap    */
+				 /* Kernel-space: kernel-virtual address    */
+	int		mtrr;	 /* MTRR slot used			    */
+				 /* Private data			    */
+} drm_map_t;
+
+typedef enum drm_lock_flags {
+	_DRM_LOCK_READY	     = 0x01, /* Wait until hardware is ready for DMA */
+	_DRM_LOCK_QUIESCENT  = 0x02, /* Wait until hardware quiescent	     */
+	_DRM_LOCK_FLUSH	     = 0x04, /* Flush this context's DMA queue first */
+	_DRM_LOCK_FLUSH_ALL  = 0x08, /* Flush all DMA queues first	     */
+				/* These *HALT* flags aren't supported yet
+				   -- they will be used to support the
+				   full-screen DGA-like mode. */
+	_DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues   */
+	_DRM_HALT_CUR_QUEUES = 0x20  /* Halt all current queues		     */
+} drm_lock_flags_t;
+
+typedef struct drm_lock {
+	int		 context;
+	drm_lock_flags_t flags;
+} drm_lock_t;
+
+typedef enum drm_dma_flags {	      /* These values *MUST* match xf86drm.h */
+				      /* Flags for DMA buffer dispatch	     */
+	_DRM_DMA_BLOCK	      = 0x01, /* Block until buffer dispatched.
+					 Note, the buffer may not yet have
+					 been processed by the hardware --
+					 getting a hardware lock with the
+					 hardware quiescent will ensure
+					 that the buffer has been
+					 processed.			     */
+	_DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held	     */
+	_DRM_DMA_PRIORITY     = 0x04, /* High priority dispatch		     */
+
+				      /* Flags for DMA buffer request	     */
+	_DRM_DMA_WAIT	      = 0x10, /* Wait for free buffers		     */
+	_DRM_DMA_SMALLER_OK   = 0x20, /* Smaller-than-requested buffers ok   */
+	_DRM_DMA_LARGER_OK    = 0x40  /* Larger-than-requested buffers ok    */
+} drm_dma_flags_t;
+
+typedef struct drm_buf_desc {
+	int	      count;	 /* Number of buffers of this size	     */
+	int	      size;	 /* Size in bytes			     */
+	int	      low_mark;	 /* Low water mark			     */
+	int	      high_mark; /* High water mark			     */
+	enum {
+		_DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA  */
+		_DRM_AGP_BUFFER = 0x02  /* Buffer is in agp space            */
+	}	      flags;
+	unsigned long agp_start; /* Start address of where the agp buffers
+				  * are in the agp aperture */
+} drm_buf_desc_t;
+
+typedef struct drm_buf_info {
+	int	       count;	/* Entries in list			     */
+	drm_buf_desc_t *list;
+} drm_buf_info_t;
+
+typedef struct drm_buf_free {
+	int	       count;
+	int	       *list;
+} drm_buf_free_t;
+
+typedef struct drm_buf_pub {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	void		  *address;    /* Address of buffer		     */
+} drm_buf_pub_t;
+
+typedef struct drm_buf_map {
+	int	      count;	/* Length of buflist			    */
+	void	      *virtual;	/* Mmaped area in user-virtual		    */
+	drm_buf_pub_t *list;	/* Buffer information			    */
+} drm_buf_map_t;
+
+typedef struct drm_dma {
+				/* Indices here refer to the offset into
+				   buflist in drm_buf_get_t.  */
+	int		context;	  /* Context handle		    */
+	int		send_count;	  /* Number of buffers to send	    */
+	int		*send_indices;	  /* List of handles to buffers	    */
+	int		*send_sizes;	  /* Lengths of data to send	    */
+	drm_dma_flags_t flags;		  /* Flags			    */
+	int		request_count;	  /* Number of buffers requested    */
+	int		request_size;	  /* Desired size for buffers	    */
+	int		*request_indices; /* Buffer information		    */
+	int		*request_sizes;
+	int		granted_count;	  /* Number of buffers granted	    */
+} drm_dma_t;
+
+typedef enum {
+	_DRM_CONTEXT_PRESERVED = 0x01,
+	_DRM_CONTEXT_2DONLY    = 0x02
+} drm_ctx_flags_t;
+
+typedef struct drm_ctx {
+	drm_context_t	handle;
+	drm_ctx_flags_t flags;
+} drm_ctx_t;
+
+typedef struct drm_ctx_res {
+	int		count;
+	drm_ctx_t	*contexts;
+} drm_ctx_res_t;
+
+typedef struct drm_draw {
+	drm_drawable_t	handle;
+} drm_draw_t;
+
+typedef struct drm_auth {
+	drm_magic_t	magic;
+} drm_auth_t;
+
+typedef struct drm_irq_busid {
+	int irq;
+	int busnum;
+	int devnum;
+	int funcnum;
+} drm_irq_busid_t;
+
+typedef struct drm_agp_mode {
+	unsigned long mode;
+} drm_agp_mode_t;
+
+				/* For drm_agp_alloc -- allocated a buffer */
+typedef struct drm_agp_buffer {
+	unsigned long size;	/* In bytes -- will round to page boundary */
+	unsigned long handle;	/* Used for BIND/UNBIND ioctls */
+	unsigned long type;     /* Type of memory to allocate  */
+        unsigned long physical; /* Physical used by i810       */
+} drm_agp_buffer_t;
+
+				/* For drm_agp_bind */
+typedef struct drm_agp_binding {
+	unsigned long handle;   /* From drm_agp_buffer */
+	unsigned long offset;	/* In bytes -- will round to page boundary */
+} drm_agp_binding_t;
+
+typedef struct drm_agp_info {
+	int            agp_version_major;
+	int            agp_version_minor;
+	unsigned long  mode;
+	unsigned long  aperture_base;  /* physical address */
+	unsigned long  aperture_size;  /* bytes */
+	unsigned long  memory_allowed; /* bytes */
+	unsigned long  memory_used;
+
+				/* PCI information */
+	unsigned short id_vendor;
+	unsigned short id_device;
+} drm_agp_info_t;
+
+#define DRM_IOCTL_BASE	     'd'
+#define DRM_IOCTL_NR(n)	     _IOC_NR(n)
+#define DRM_IO(nr)	     _IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,size)     _IOR(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOW(nr,size)     _IOW(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOWR(nr,size)    _IOWR(DRM_IOCTL_BASE,nr,size)
+
+
+#define DRM_IOCTL_VERSION    DRM_IOWR(0x00, drm_version_t)
+#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t)
+#define DRM_IOCTL_GET_MAGIC  DRM_IOR( 0x02, drm_auth_t)
+#define DRM_IOCTL_IRQ_BUSID  DRM_IOWR(0x03, drm_irq_busid_t)
+
+#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t)
+#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t)
+#define DRM_IOCTL_BLOCK	     DRM_IOWR(0x12, drm_block_t)
+#define DRM_IOCTL_UNBLOCK    DRM_IOWR(0x13, drm_block_t)
+#define DRM_IOCTL_CONTROL    DRM_IOW( 0x14, drm_control_t)
+#define DRM_IOCTL_ADD_MAP    DRM_IOWR(0x15, drm_map_t)
+#define DRM_IOCTL_ADD_BUFS   DRM_IOWR(0x16, drm_buf_desc_t)
+#define DRM_IOCTL_MARK_BUFS  DRM_IOW( 0x17, drm_buf_desc_t)
+#define DRM_IOCTL_INFO_BUFS  DRM_IOWR(0x18, drm_buf_info_t)
+#define DRM_IOCTL_MAP_BUFS   DRM_IOWR(0x19, drm_buf_map_t)
+#define DRM_IOCTL_FREE_BUFS  DRM_IOW( 0x1a, drm_buf_free_t)
+
+#define DRM_IOCTL_ADD_CTX    DRM_IOWR(0x20, drm_ctx_t)
+#define DRM_IOCTL_RM_CTX     DRM_IOWR(0x21, drm_ctx_t)
+#define DRM_IOCTL_MOD_CTX    DRM_IOW( 0x22, drm_ctx_t)
+#define DRM_IOCTL_GET_CTX    DRM_IOWR(0x23, drm_ctx_t)
+#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, drm_ctx_t)
+#define DRM_IOCTL_NEW_CTX    DRM_IOW( 0x25, drm_ctx_t)
+#define DRM_IOCTL_RES_CTX    DRM_IOWR(0x26, drm_ctx_res_t)
+#define DRM_IOCTL_ADD_DRAW   DRM_IOWR(0x27, drm_draw_t)
+#define DRM_IOCTL_RM_DRAW    DRM_IOWR(0x28, drm_draw_t)
+#define DRM_IOCTL_DMA	     DRM_IOWR(0x29, drm_dma_t)
+#define DRM_IOCTL_LOCK	     DRM_IOW( 0x2a, drm_lock_t)
+#define DRM_IOCTL_UNLOCK     DRM_IOW( 0x2b, drm_lock_t)
+#define DRM_IOCTL_FINISH     DRM_IOW( 0x2c, drm_lock_t)
+
+#define DRM_IOCTL_AGP_ACQUIRE DRM_IO(  0x30)
+#define DRM_IOCTL_AGP_RELEASE DRM_IO(  0x31)
+#define DRM_IOCTL_AGP_ENABLE  DRM_IOW( 0x32, drm_agp_mode_t)
+#define DRM_IOCTL_AGP_INFO    DRM_IOR( 0x33, drm_agp_info_t)
+#define DRM_IOCTL_AGP_ALLOC   DRM_IOWR(0x34, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_FREE    DRM_IOW( 0x35, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_BIND    DRM_IOW( 0x36, drm_agp_binding_t)
+#define DRM_IOCTL_AGP_UNBIND  DRM_IOW( 0x37, drm_agp_binding_t)
+
+/* Mga specific ioctls */
+#define DRM_IOCTL_MGA_INIT    DRM_IOW( 0x40, drm_mga_init_t)
+#define DRM_IOCTL_MGA_SWAP    DRM_IOW( 0x41, drm_mga_swap_t)
+#define DRM_IOCTL_MGA_CLEAR   DRM_IOW( 0x42, drm_mga_clear_t)
+#define DRM_IOCTL_MGA_ILOAD   DRM_IOW( 0x43, drm_mga_iload_t)
+#define DRM_IOCTL_MGA_VERTEX  DRM_IOW( 0x44, drm_mga_vertex_t)
+#define DRM_IOCTL_MGA_FLUSH   DRM_IOW( 0x45, drm_lock_t )
+#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t)
+
+/* I810 specific ioctls */
+#define DRM_IOCTL_I810_INIT    DRM_IOW( 0x40, drm_i810_init_t)
+#define DRM_IOCTL_I810_VERTEX  DRM_IOW( 0x41, drm_i810_vertex_t)
+#define DRM_IOCTL_I810_CLEAR   DRM_IOW( 0x42, drm_i810_clear_t)
+#define DRM_IOCTL_I810_FLUSH   DRM_IO ( 0x43)
+#define DRM_IOCTL_I810_GETAGE  DRM_IO ( 0x44)
+#define DRM_IOCTL_I810_GETBUF  DRM_IOWR(0x45, drm_i810_dma_t)
+#define DRM_IOCTL_I810_SWAP    DRM_IO ( 0x46)
+#define DRM_IOCTL_I810_COPY    DRM_IOW( 0x47, drm_i810_copy_t)
+#define DRM_IOCTL_I810_DOCOPY  DRM_IO ( 0x48)
+
+/* Rage 128 specific ioctls */
+#define DRM_IOCTL_R128_INIT	DRM_IOW( 0x40, drm_r128_init_t)
+#define DRM_IOCTL_R128_RESET	DRM_IO(  0x41)
+#define DRM_IOCTL_R128_FLUSH	DRM_IO(  0x42)
+#define DRM_IOCTL_R128_IDLE	DRM_IO(  0x43)
+#define DRM_IOCTL_R128_PACKET	DRM_IOW( 0x44, drm_r128_packet_t)
+#define DRM_IOCTL_R128_VERTEX	DRM_IOW( 0x45, drm_r128_vertex_t)
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drmP.h linux.18p17/drivers/char/drm/drmP.h
--- linux.vanilla/drivers/char/drm/drmP.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/drmP.h	Thu Oct 19 01:44:30 2000
@@ -0,0 +1,743 @@
+/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _DRM_P_H_
+#define _DRM_P_H_
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/pci.h>
+#include <linux/wrapper.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>	/* For (un)lock_kernel */
+#include <asm/io.h>
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+#include <linux/types.h>
+#include <linux/agp_backend.h>
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <linux/tqueue.h>
+#include <linux/poll.h>
+#endif
+#include "drm.h"
+
+#define DRM_DEBUG_CODE 2	  /* Include debugging code (if > 1, then
+				     also include looping detection. */
+#define DRM_DMA_HISTOGRAM 1	  /* Make histogram of DMA latency. */
+
+#define DRM_HASH_SIZE	      16 /* Size of key hash table		  */
+#define DRM_KERNEL_CONTEXT    0	 /* Change drm_resctx if changed	  */
+#define DRM_RESERVED_CONTEXTS 1	 /* Change drm_resctx if changed	  */
+#define DRM_LOOPING_LIMIT     5000000
+#define DRM_BSZ		      1024 /* Buffer size for /dev/drm? output	  */
+#define DRM_TIME_SLICE	      (HZ/20)  /* Time slice for GLXContexts	  */
+#define DRM_LOCK_SLICE	      1	/* Time slice for lock, in jiffies	  */
+
+#define DRM_FLAG_DEBUG	  0x01
+#define DRM_FLAG_NOCTX	  0x02
+
+#define DRM_MEM_DMA	   0
+#define DRM_MEM_SAREA	   1
+#define DRM_MEM_DRIVER	   2
+#define DRM_MEM_MAGIC	   3
+#define DRM_MEM_IOCTLS	   4
+#define DRM_MEM_MAPS	   5
+#define DRM_MEM_VMAS	   6
+#define DRM_MEM_BUFS	   7
+#define DRM_MEM_SEGS	   8
+#define DRM_MEM_PAGES	   9
+#define DRM_MEM_FILES	  10
+#define DRM_MEM_QUEUES	  11
+#define DRM_MEM_CMDS	  12
+#define DRM_MEM_MAPPINGS  13
+#define DRM_MEM_BUFLISTS  14
+#define DRM_MEM_AGPLISTS  15
+#define DRM_MEM_TOTALAGP  16
+#define DRM_MEM_BOUNDAGP  17
+#define DRM_MEM_CTXBITMAP 18
+
+#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
+
+				/* Backward compatibility section */
+				/* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */
+#ifndef _PAGE_PWT
+#define _PAGE_PWT _PAGE_WT
+#endif
+				/* Wait queue declarations changed in 2.3.1 */
+#ifndef DECLARE_WAITQUEUE
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL }
+typedef struct wait_queue *wait_queue_head_t;
+#define init_waitqueue_head(q) *q = NULL;
+#endif
+
+				/* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */
+#ifndef _PAGE_PSE
+#define _PAGE_PSE _PAGE_4M
+#endif
+
+				/* vm_offset changed to vm_pgoff in 2.3.25 */
+#if LINUX_VERSION_CODE < 0x020319
+#define VM_OFFSET(vma) ((vma)->vm_offset)
+#else
+#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+#endif
+
+				/* *_nopage return values defined in 2.3.26 */
+#ifndef NOPAGE_SIGBUS
+#define NOPAGE_SIGBUS 0
+#endif
+#ifndef NOPAGE_OOM
+#define NOPAGE_OOM 0
+#endif
+
+				/* Generic cmpxchg added in 2.3.x */
+#ifndef __HAVE_ARCH_CMPXCHG
+				/* Include this here so that driver can be
+                                   used with older kernels. */
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+				      unsigned long new, int size)
+{
+	unsigned long prev;
+	switch (size) {
+	case 1:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 2:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 4:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	}
+	return old;
+}
+
+#define cmpxchg(ptr,o,n)						\
+  ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),		\
+				 (unsigned long)(n),sizeof(*(ptr))))
+#endif
+
+				/* Macros to make printk easier */
+#define DRM_ERROR(fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg)
+#define DRM_MEM_ERROR(area, fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \
+	       drm_mem_stats[area].name , ##arg)
+#define DRM_INFO(fmt, arg...)  printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg)
+
+#if DRM_DEBUG_CODE
+#define DRM_DEBUG(fmt, arg...)						  \
+	do {								  \
+		if (drm_flags&DRM_FLAG_DEBUG)				  \
+			printk(KERN_DEBUG				  \
+			       "[" DRM_NAME ":" __FUNCTION__ "] " fmt ,	  \
+			       ##arg);					  \
+	} while (0)
+#else
+#define DRM_DEBUG(fmt, arg...)		 do { } while (0)
+#endif
+
+#define DRM_PROC_LIMIT (PAGE_SIZE-80)
+
+#define DRM_PROC_PRINT(fmt, arg...)	   \
+   len += sprintf(&buf[len], fmt , ##arg); \
+   if (len > DRM_PROC_LIMIT) return len;
+
+#define DRM_PROC_PRINT_RET(ret, fmt, arg...)	    \
+   len += sprintf(&buf[len], fmt , ##arg);	    \
+   if (len > DRM_PROC_LIMIT) { ret; return len; }
+
+				/* Internal types and structures */
+#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define DRM_MIN(a,b) ((a)<(b)?(a):(b))
+#define DRM_MAX(a,b) ((a)>(b)?(a):(b))
+
+#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
+#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
+#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
+
+typedef int drm_ioctl_t(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+typedef struct drm_ioctl_desc {
+	drm_ioctl_t	     *func;
+	int		     auth_needed;
+	int		     root_only;
+} drm_ioctl_desc_t;
+
+typedef struct drm_devstate {
+	pid_t		  owner;	/* X server pid holding x_lock */
+	
+} drm_devstate_t;
+
+typedef struct drm_magic_entry {
+	drm_magic_t	       magic;
+	struct drm_file	       *priv;
+	struct drm_magic_entry *next;
+} drm_magic_entry_t;
+
+typedef struct drm_magic_head {
+	struct drm_magic_entry *head;
+	struct drm_magic_entry *tail;
+} drm_magic_head_t;
+
+typedef struct drm_vma_entry {
+	struct vm_area_struct *vma;
+	struct drm_vma_entry  *next;
+	pid_t		      pid;
+} drm_vma_entry_t;
+
+typedef struct drm_buf {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  order;       /* log-base-2(total)		     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	unsigned long	  offset;      /* Byte offset (used internally)	     */
+	void		  *address;    /* Address of buffer		     */
+	unsigned long	  bus_address; /* Bus address of buffer		     */
+	struct drm_buf	  *next;       /* Kernel-only: used for free list    */
+	__volatile__ int  waiting;     /* On kernel DMA queue		     */
+	__volatile__ int  pending;     /* On hardware DMA queue		     */
+	wait_queue_head_t dma_wait;    /* Processes waiting		     */
+	pid_t		  pid;	       /* PID of holding process	     */
+	int		  context;     /* Kernel queue for this buffer	     */
+	int		  while_locked;/* Dispatch this buffer while locked  */
+	enum {
+		DRM_LIST_NONE	 = 0,
+		DRM_LIST_FREE	 = 1,
+		DRM_LIST_WAIT	 = 2,
+		DRM_LIST_PEND	 = 3,
+		DRM_LIST_PRIO	 = 4,
+		DRM_LIST_RECLAIM = 5
+	}		  list;	       /* Which list we're on		     */
+
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  time_queued;	   /* Queued to kernel DMA queue     */
+	cycles_t	  time_dispatched; /* Dispatched to hardware	     */
+	cycles_t	  time_completed;  /* Completed by hardware	     */
+	cycles_t	  time_freed;	   /* Back on freelist		     */
+#endif
+
+	int		  dev_priv_size; /* Size of buffer private stoarge   */
+	void		  *dev_private;  /* Per-buffer private storage       */
+} drm_buf_t;
+
+#if DRM_DMA_HISTOGRAM
+#define DRM_DMA_HISTOGRAM_SLOTS		  9
+#define DRM_DMA_HISTOGRAM_INITIAL	 10
+#define DRM_DMA_HISTOGRAM_NEXT(current)	 ((current)*10)
+typedef struct drm_histogram {
+	atomic_t	  total;
+	
+	atomic_t	  queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  dma[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  schedule[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  ctx[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lacq[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lhld[DRM_DMA_HISTOGRAM_SLOTS];
+} drm_histogram_t;
+#endif
+
+				/* bufs is one longer than it has to be */
+typedef struct drm_waitlist {
+	int		  count;	/* Number of possible buffers	   */
+	drm_buf_t	  **bufs;	/* List of pointers to buffers	   */
+	drm_buf_t	  **rp;		/* Read pointer			   */
+	drm_buf_t	  **wp;		/* Write pointer		   */
+	drm_buf_t	  **end;	/* End pointer			   */
+	spinlock_t	  read_lock;
+	spinlock_t	  write_lock;
+} drm_waitlist_t;
+
+typedef struct drm_freelist {
+	int		  initialized; /* Freelist in use		   */
+	atomic_t	  count;       /* Number of free buffers	   */
+	drm_buf_t	  *next;       /* End pointer			   */
+	
+	wait_queue_head_t waiting;     /* Processes waiting on free bufs   */
+	int		  low_mark;    /* Low water mark		   */
+	int		  high_mark;   /* High water mark		   */
+	atomic_t	  wfh;	       /* If waiting for high mark	   */
+} drm_freelist_t;
+
+typedef struct drm_buf_entry {
+	int		  buf_size;
+	int		  buf_count;
+	drm_buf_t	  *buflist;
+	int		  seg_count;
+	int		  page_order;
+	unsigned long	  *seglist;
+
+	drm_freelist_t	  freelist;
+} drm_buf_entry_t;
+
+typedef struct drm_hw_lock {
+	__volatile__ unsigned int lock;
+	char			  padding[60]; /* Pad to cache line */
+} drm_hw_lock_t;
+
+typedef struct drm_file {
+	int		  authenticated;
+	int		  minor;
+	pid_t		  pid;
+	uid_t		  uid;
+	drm_magic_t	  magic;
+	unsigned long	  ioctl_count;
+	struct drm_file	  *next;
+	struct drm_file	  *prev;
+	struct drm_device *dev;
+} drm_file_t;
+
+
+typedef struct drm_queue {
+	atomic_t	  use_count;	/* Outstanding uses (+1)	    */
+	atomic_t	  finalization;	/* Finalization in progress	    */
+	atomic_t	  block_count;	/* Count of processes waiting	    */
+	atomic_t	  block_read;	/* Queue blocked for reads	    */
+	wait_queue_head_t read_queue;	/* Processes waiting on block_read  */
+	atomic_t	  block_write;	/* Queue blocked for writes	    */
+	wait_queue_head_t write_queue;	/* Processes waiting on block_write */
+	atomic_t	  total_queued;	/* Total queued statistic	    */
+	atomic_t	  total_flushed;/* Total flushes statistic	    */
+	atomic_t	  total_locks;	/* Total locks statistics	    */
+	drm_ctx_flags_t	  flags;	/* Context preserving and 2D-only   */
+	drm_waitlist_t	  waitlist;	/* Pending buffers		    */
+	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+} drm_queue_t;
+
+typedef struct drm_lock_data {
+	drm_hw_lock_t	  *hw_lock;	/* Hardware lock		   */
+	pid_t		  pid;		/* PID of lock holder (0=kernel)   */
+	wait_queue_head_t lock_queue;	/* Queue of blocked processes	   */
+	unsigned long	  lock_time;	/* Time of last lock in jiffies	   */
+} drm_lock_data_t;
+
+typedef struct drm_device_dma {
+				/* Performance Counters */
+	atomic_t	  total_prio;	/* Total DRM_DMA_PRIORITY	   */
+	atomic_t	  total_bytes;	/* Total bytes DMA'd		   */
+	atomic_t	  total_dmas;	/* Total DMA buffers dispatched	   */
+	
+	atomic_t	  total_missed_dma;  /* Missed drm_do_dma	    */
+	atomic_t	  total_missed_lock; /* Missed lock in drm_do_dma   */
+	atomic_t	  total_missed_free; /* Missed drm_free_this_buffer */
+	atomic_t	  total_missed_sched;/* Missed drm_dma_schedule	    */
+
+	atomic_t	  total_tried;	/* Tried next_buffer		    */
+	atomic_t	  total_hit;	/* Sent next_buffer		    */
+	atomic_t	  total_lost;	/* Lost interrupt		    */
+
+	drm_buf_entry_t	  bufs[DRM_MAX_ORDER+1];
+	int		  buf_count;
+	drm_buf_t	  **buflist;	/* Vector of pointers info bufs	   */
+	int		  seg_count; 
+	int		  page_count;
+	unsigned long	  *pagelist;
+	unsigned long	  byte_count;
+	enum {
+	   _DRM_DMA_USE_AGP = 0x01
+	} flags;
+
+				/* DMA support */
+	drm_buf_t	  *this_buffer;	/* Buffer being sent		   */
+	drm_buf_t	  *next_buffer; /* Selected buffer to send	   */
+	drm_queue_t	  *next_queue;	/* Queue from which buffer selected*/
+	wait_queue_head_t waiting;	/* Processes waiting on free bufs  */
+} drm_device_dma_t;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+typedef struct drm_agp_mem {
+	unsigned long      handle;
+	agp_memory         *memory;
+	unsigned long      bound; /* address */
+	int                pages;
+	struct drm_agp_mem *prev;
+	struct drm_agp_mem *next;
+} drm_agp_mem_t;
+
+typedef struct drm_agp_head {
+	agp_kern_info      agp_info;
+	const char         *chipset;
+	drm_agp_mem_t      *memory;
+	unsigned long      mode;
+	int                enabled;
+	int                acquired;
+	unsigned long      base;
+   	int 		   agp_mtrr;
+} drm_agp_head_t;
+
+typedef struct {
+	void       (*free_memory)(agp_memory *);
+	agp_memory *(*allocate_memory)(size_t, u32);
+	int        (*bind_memory)(agp_memory *, off_t);
+	int        (*unbind_memory)(agp_memory *);
+	void       (*enable)(u32);
+	int        (*acquire)(void);
+	void       (*release)(void);
+	void       (*copy_info)(agp_kern_info *);
+} drm_agp_func_t;
+
+extern drm_agp_func_t drm_agp;
+#endif
+
+typedef struct drm_device {
+	const char	  *name;	/* Simple driver name		   */
+	char		  *unique;	/* Unique identifier: e.g., busid  */
+	int		  unique_len;	/* Length of unique field	   */
+	dev_t		  device;	/* Device number for mknod	   */
+	char		  *devname;	/* For /proc/interrupts		   */
+	
+	int		  blocked;	/* Blocked due to VC switch?	   */
+	struct proc_dir_entry *root;	/* Root for this device's entries  */
+
+				/* Locks */
+	spinlock_t	  count_lock;	/* For inuse, open_count, buf_use  */
+	struct semaphore  struct_sem;	/* For others			   */
+
+				/* Usage Counters */
+	int		  open_count;	/* Outstanding files open	   */
+	atomic_t	  ioctl_count;	/* Outstanding IOCTLs pending	   */
+	atomic_t	  vma_count;	/* Outstanding vma areas open	   */
+	int		  buf_use;	/* Buffers in use -- cannot alloc  */
+	atomic_t	  buf_alloc;	/* Buffer allocation in progress   */
+
+				/* Performance Counters */
+	atomic_t	  total_open;
+	atomic_t	  total_close;
+	atomic_t	  total_ioctl;
+	atomic_t	  total_irq;	/* Total interruptions		   */
+	atomic_t	  total_ctx;	/* Total context switches	   */
+	
+	atomic_t	  total_locks;
+	atomic_t	  total_unlocks;
+	atomic_t	  total_contends;
+	atomic_t	  total_sleeps;
+
+				/* Authentication */
+	drm_file_t	  *file_first;
+	drm_file_t	  *file_last;
+	drm_magic_head_t  magiclist[DRM_HASH_SIZE];
+
+				/* Memory management */
+	drm_map_t	  **maplist;	/* Vector of pointers to regions   */
+	int		  map_count;	/* Number of mappable regions	   */
+
+	drm_vma_entry_t	  *vmalist;	/* List of vmas (for debugging)	   */
+	drm_lock_data_t	  lock;		/* Information on hardware lock	   */
+
+				/* DMA queues (contexts) */
+	int		  queue_count;	/* Number of active DMA queues	   */
+	int		  queue_reserved; /* Number of reserved DMA queues */
+	int		  queue_slots;	/* Actual length of queuelist	   */
+	drm_queue_t	  **queuelist;	/* Vector of pointers to DMA queues */
+	drm_device_dma_t  *dma;		/* Optional pointer for DMA support */
+
+				/* Context support */
+	int		  irq;		/* Interrupt used by board	   */
+	__volatile__ long context_flag;	/* Context swapping flag	   */
+	__volatile__ long interrupt_flag; /* Interruption handler flag	   */
+	__volatile__ long dma_flag;	/* DMA dispatch flag		   */
+	struct timer_list timer;	/* Timer for delaying ctx switch   */
+	wait_queue_head_t context_wait; /* Processes waiting on ctx switch */
+	int		  last_checked;	/* Last context checked for DMA	   */
+	int		  last_context;	/* Last current context		   */
+	unsigned long	  last_switch;	/* jiffies at last context switch  */
+	struct tq_struct  tq;
+	cycles_t	  ctx_start;
+	cycles_t	  lck_start;
+#if DRM_DMA_HISTOGRAM
+	drm_histogram_t	  histo;
+#endif
+	
+				/* Callback to X server for context switch
+				   and for heavy-handed reset. */
+	char		  buf[DRM_BSZ]; /* Output buffer		   */
+	char		  *buf_rp;	/* Read pointer			   */
+	char		  *buf_wp;	/* Write pointer		   */
+	char		  *buf_end;	/* End pointer			   */
+	struct fasync_struct *buf_async;/* Processes waiting for SIGIO	   */
+	wait_queue_head_t buf_readers;	/* Processes waiting to read	   */
+	wait_queue_head_t buf_writers;	/* Processes waiting to ctx switch */
+	
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	drm_agp_head_t    *agp;
+#endif
+	unsigned long     *ctx_bitmap;
+	void		  *dev_private;
+} drm_device_t;
+
+
+				/* Internal function definitions */
+
+				/* Misc. support (init.c) */
+extern int	     drm_flags;
+extern void	     drm_parse_options(char *s);
+extern int           drm_cpu_valid(void);
+
+
+				/* Device support (fops.c) */
+extern int	     drm_open_helper(struct inode *inode, struct file *filp,
+				     drm_device_t *dev);
+extern int	     drm_flush(struct file *filp);
+extern int	     drm_release(struct inode *inode, struct file *filp);
+extern int	     drm_fasync(int fd, struct file *filp, int on);
+extern ssize_t	     drm_read(struct file *filp, char *buf, size_t count,
+			      loff_t *off);
+extern int	     drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int  drm_poll(struct file *filp, struct poll_table_struct *wait);
+
+				/* Mapping support (vm.c) */
+#if LINUX_VERSION_CODE < 0x020317
+extern unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+				   unsigned long address,
+				   int write_access);
+extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+#else
+				/* Return type changed in 2.3.23 */
+extern struct page *drm_vm_nopage(struct vm_area_struct *vma,
+				  unsigned long address,
+				  int write_access);
+extern struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+extern struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+#endif
+extern void	     drm_vm_open(struct vm_area_struct *vma);
+extern void	     drm_vm_close(struct vm_area_struct *vma);
+extern int	     drm_mmap_dma(struct file *filp,
+				  struct vm_area_struct *vma);
+extern int	     drm_mmap(struct file *filp, struct vm_area_struct *vma);
+extern struct vm_operations_struct drm_vm_ops;
+extern struct vm_operations_struct drm_vm_shm_ops;
+extern struct vm_operations_struct drm_vm_dma_ops;
+
+				/* Proc support (proc.c) */
+extern int	     drm_proc_init(drm_device_t *dev);
+extern int	     drm_proc_cleanup(void);
+
+				/* Memory management support (memory.c) */
+extern void	     drm_mem_init(void);
+extern int	     drm_mem_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+extern void	     *drm_alloc(size_t size, int area);
+extern void	     *drm_realloc(void *oldpt, size_t oldsize, size_t size,
+				  int area);
+extern char	     *drm_strdup(const char *s, int area);
+extern void	     drm_strfree(const char *s, int area);
+extern void	     drm_free(void *pt, size_t size, int area);
+extern unsigned long drm_alloc_pages(int order, int area);
+extern void	     drm_free_pages(unsigned long address, int order,
+				    int area);
+extern void	     *drm_ioremap(unsigned long offset, unsigned long size);
+extern void	     drm_ioremapfree(void *pt, unsigned long size);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+extern agp_memory    *drm_alloc_agp(int pages, u32 type);
+extern int           drm_free_agp(agp_memory *handle, int pages);
+extern int           drm_bind_agp(agp_memory *handle, unsigned int start);
+extern int           drm_unbind_agp(agp_memory *handle);
+#endif
+
+
+				/* Buffer management support (bufs.c) */
+extern int	     drm_order(unsigned long size);
+extern int	     drm_addmap(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_infobufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_markbufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_freebufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_mapbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+
+
+				/* Buffer list management support (lists.c) */
+extern int	     drm_waitlist_create(drm_waitlist_t *bl, int count);
+extern int	     drm_waitlist_destroy(drm_waitlist_t *bl);
+extern int	     drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf);
+extern drm_buf_t     *drm_waitlist_get(drm_waitlist_t *bl);
+
+extern int	     drm_freelist_create(drm_freelist_t *bl, int count);
+extern int	     drm_freelist_destroy(drm_freelist_t *bl);
+extern int	     drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl,
+				      drm_buf_t *buf);
+extern drm_buf_t     *drm_freelist_get(drm_freelist_t *bl, int block);
+
+				/* DMA support (gen_dma.c) */
+extern void	     drm_dma_setup(drm_device_t *dev);
+extern void	     drm_dma_takedown(drm_device_t *dev);
+extern void	     drm_free_buffer(drm_device_t *dev, drm_buf_t *buf);
+extern void	     drm_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int	     drm_context_switch(drm_device_t *dev, int old, int new);
+extern int	     drm_context_switch_complete(drm_device_t *dev, int new);
+extern void	     drm_clear_next_buffer(drm_device_t *dev);
+extern int	     drm_select_queue(drm_device_t *dev,
+				      void (*wrapper)(unsigned long));
+extern int	     drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma);
+extern int	     drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma);
+#if DRM_DMA_HISTOGRAM
+extern int	     drm_histogram_slot(unsigned long count);
+extern void	     drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf);
+#endif
+
+
+				/* Misc. IOCTL support (ioctl.c) */
+extern int	     drm_irq_busid(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_getunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_setunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Context IOCTL support (context.c) */
+extern int	     drm_resctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_modctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_getctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_switchctx(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_newctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_rmctx(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+
+
+				/* Drawable IOCTL support (drawable.c) */
+extern int	     drm_adddraw(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_rmdraw(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+
+
+				/* Authentication IOCTL support (auth.c) */
+extern int	     drm_add_magic(drm_device_t *dev, drm_file_t *priv,
+				   drm_magic_t magic);
+extern int	     drm_remove_magic(drm_device_t *dev, drm_magic_t magic);
+extern int	     drm_getmagic(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_authmagic(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Locking IOCTL support (lock.c) */
+extern int	     drm_block(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+extern int	     drm_unblock(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_lock_take(__volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_lock_transfer(drm_device_t *dev,
+				       __volatile__ unsigned int *lock,
+				       unsigned int context);
+extern int	     drm_lock_free(drm_device_t *dev,
+				   __volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_finish(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_flush_unblock(drm_device_t *dev, int context,
+				       drm_lock_flags_t flags);
+extern int	     drm_flush_block_and_flush(drm_device_t *dev, int context,
+					       drm_lock_flags_t flags);
+
+				/* Context Bitmap support (ctxbitmap.c) */
+extern int	     drm_ctxbitmap_init(drm_device_t *dev);
+extern void	     drm_ctxbitmap_cleanup(drm_device_t *dev);
+extern int	     drm_ctxbitmap_next(drm_device_t *dev);
+extern void	     drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* AGP/GART support (agpsupport.c) */
+extern drm_agp_head_t *drm_agp_init(void);
+extern void           drm_agp_uninit(void);
+extern int            drm_agp_acquire(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern int            drm_agp_release(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern int            drm_agp_enable(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_info(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_alloc(struct inode *inode, struct file *filp,
+				    unsigned int cmd, unsigned long arg);
+extern int            drm_agp_free(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_unbind(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_bind(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+#endif
+#endif
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drm_syms.c linux.18p17/drivers/char/drm/drm_syms.c
--- linux.vanilla/drivers/char/drm/drm_syms.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/drm_syms.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,149 @@
+#include <linux/config.h>
+#include "drmP.h"
+
+/* Misc. support (init.c) */
+EXPORT_SYMBOL(drm_flags);
+EXPORT_SYMBOL(drm_parse_options);
+EXPORT_SYMBOL(drm_cpu_valid);
+
+/* Device support (fops.c) */
+EXPORT_SYMBOL(drm_open_helper);
+EXPORT_SYMBOL(drm_flush);
+EXPORT_SYMBOL(drm_release);
+EXPORT_SYMBOL(drm_fasync);
+EXPORT_SYMBOL(drm_read);
+EXPORT_SYMBOL(drm_write_string);
+EXPORT_SYMBOL(drm_poll);
+
+/* Mapping support (vm.c) */
+#if LINUX_VERSION_CODE < 0x020317
+EXPORT_SYMBOL(drm_vm_nopage);
+EXPORT_SYMBOL(drm_vm_shm_nopage);
+EXPORT_SYMBOL(drm_vm_dma_nopage);
+#else
+/* Return type changed in 2.3.23 */
+EXPORT_SYMBOL(drm_vm_nopage);
+EXPORT_SYMBOL(drm_vm_shm_nopage);
+EXPORT_SYMBOL(drm_vm_dma_nopage);
+#endif
+
+EXPORT_SYMBOL(drm_vm_open);
+EXPORT_SYMBOL(drm_vm_close);
+EXPORT_SYMBOL(drm_mmap_dma);
+EXPORT_SYMBOL(drm_mmap);
+EXPORT_SYMBOL(drm_vm_ops);
+EXPORT_SYMBOL(drm_vm_shm_ops);
+EXPORT_SYMBOL(drm_vm_dma_ops);
+
+/* Proc support (proc.c) */
+EXPORT_SYMBOL(drm_proc_init);
+EXPORT_SYMBOL(drm_proc_cleanup);
+
+/* Memory management support (memory.c) */
+EXPORT_SYMBOL(drm_mem_init);
+EXPORT_SYMBOL(drm_mem_info);
+EXPORT_SYMBOL(drm_alloc);
+EXPORT_SYMBOL(drm_realloc);
+EXPORT_SYMBOL(drm_strdup);
+EXPORT_SYMBOL(drm_strfree);
+EXPORT_SYMBOL(drm_free);
+EXPORT_SYMBOL(drm_alloc_pages);
+EXPORT_SYMBOL(drm_free_pages);
+EXPORT_SYMBOL(drm_ioremap);
+EXPORT_SYMBOL(drm_ioremapfree);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+EXPORT_SYMBOL(drm_alloc_agp);
+EXPORT_SYMBOL(drm_free_agp);
+EXPORT_SYMBOL(drm_bind_agp);
+EXPORT_SYMBOL(drm_unbind_agp);
+#endif
+
+/* Buffer management support (bufs.c) */
+EXPORT_SYMBOL(drm_order);
+EXPORT_SYMBOL(drm_addmap);
+EXPORT_SYMBOL(drm_addbufs);
+EXPORT_SYMBOL(drm_infobufs);
+EXPORT_SYMBOL(drm_markbufs);
+EXPORT_SYMBOL(drm_freebufs);
+EXPORT_SYMBOL(drm_mapbufs);
+
+/* Buffer list management support (lists.c) */
+EXPORT_SYMBOL(drm_waitlist_create);
+EXPORT_SYMBOL(drm_waitlist_destroy);
+EXPORT_SYMBOL(drm_waitlist_put);
+EXPORT_SYMBOL(drm_waitlist_get);
+EXPORT_SYMBOL(drm_freelist_create);
+EXPORT_SYMBOL(drm_freelist_destroy);
+EXPORT_SYMBOL(drm_freelist_put);
+EXPORT_SYMBOL(drm_freelist_get);
+
+/* DMA support (gen_dma.c) */
+EXPORT_SYMBOL(drm_dma_setup);
+EXPORT_SYMBOL(drm_dma_takedown);
+EXPORT_SYMBOL(drm_free_buffer);
+EXPORT_SYMBOL(drm_reclaim_buffers);
+EXPORT_SYMBOL(drm_context_switch);
+EXPORT_SYMBOL(drm_context_switch_complete);
+EXPORT_SYMBOL(drm_clear_next_buffer);
+EXPORT_SYMBOL(drm_select_queue);
+EXPORT_SYMBOL(drm_dma_enqueue);
+EXPORT_SYMBOL(drm_dma_get_buffers);
+#if DRM_DMA_HISTOGRAM
+EXPORT_SYMBOL(drm_histogram_slot);
+EXPORT_SYMBOL(drm_histogram_compute);
+#endif
+
+/* Misc. IOCTL support (ioctl.c) */
+EXPORT_SYMBOL(drm_irq_busid);
+EXPORT_SYMBOL(drm_getunique);
+EXPORT_SYMBOL(drm_setunique);
+
+/* Context IOCTL support (context.c) */
+EXPORT_SYMBOL(drm_resctx);
+EXPORT_SYMBOL(drm_addctx);
+EXPORT_SYMBOL(drm_modctx);
+EXPORT_SYMBOL(drm_getctx);
+EXPORT_SYMBOL(drm_switchctx);
+EXPORT_SYMBOL(drm_newctx);
+EXPORT_SYMBOL(drm_rmctx);
+
+/* Drawable IOCTL support (drawable.c) */
+EXPORT_SYMBOL(drm_adddraw);
+EXPORT_SYMBOL(drm_rmdraw);
+
+/* Authentication IOCTL support (auth.c) */
+EXPORT_SYMBOL(drm_add_magic);
+EXPORT_SYMBOL(drm_remove_magic);
+EXPORT_SYMBOL(drm_getmagic);
+EXPORT_SYMBOL(drm_authmagic);
+
+/* Locking IOCTL support (lock.c) */
+EXPORT_SYMBOL(drm_block);
+EXPORT_SYMBOL(drm_unblock);
+EXPORT_SYMBOL(drm_lock_take);
+EXPORT_SYMBOL(drm_lock_transfer);
+EXPORT_SYMBOL(drm_lock_free);
+EXPORT_SYMBOL(drm_finish);
+EXPORT_SYMBOL(drm_flush_unblock);
+EXPORT_SYMBOL(drm_flush_block_and_flush);
+
+/* Context Bitmap support (ctxbitmap.c) */
+EXPORT_SYMBOL(drm_ctxbitmap_init);
+EXPORT_SYMBOL(drm_ctxbitmap_cleanup);
+EXPORT_SYMBOL(drm_ctxbitmap_next);
+EXPORT_SYMBOL(drm_ctxbitmap_free);
+
+/* AGP/GART support (agpsupport.c) */
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+EXPORT_SYMBOL(drm_agp);
+EXPORT_SYMBOL(drm_agp_init);
+EXPORT_SYMBOL(drm_agp_uninit);
+EXPORT_SYMBOL(drm_agp_acquire);
+EXPORT_SYMBOL(drm_agp_release);
+EXPORT_SYMBOL(drm_agp_enable);
+EXPORT_SYMBOL(drm_agp_info);
+EXPORT_SYMBOL(drm_agp_alloc);
+EXPORT_SYMBOL(drm_agp_free);
+EXPORT_SYMBOL(drm_agp_unbind);
+EXPORT_SYMBOL(drm_agp_bind);
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ffb_context.c linux.18p17/drivers/char/drm/ffb_context.c
--- linux.vanilla/drivers/char/drm/ffb_context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/ffb_context.c	Sun Sep 10 14:49:10 2000
@@ -0,0 +1,531 @@
+/* $Id: ffb_context.c,v 1.4.2.1 2000/09/05 00:10:45 davem Exp $
+ * ffb_context.c: Creator/Creator3D DRI/DRM context switching.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ *
+ * Almost entirely stolen from tdfx_context.c, see there
+ * for authors.
+ */
+
+#define __NO_VERSION__
+#include <linux/sched.h>
+#include <asm/upa.h>
+
+#include "drmP.h"
+
+#include "ffb_drv.h"
+
+static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+	int i;
+
+	for (i = 0; i < FFB_MAX_CTXS; i++) {
+		if (fpriv->hw_state[i] == NULL)
+			break;
+	}
+	if (i == FFB_MAX_CTXS)
+		return -1;
+
+	fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL);
+	if (fpriv->hw_state[i] == NULL)
+		return -1;
+
+	fpriv->hw_state[i]->is_2d_only = is_2d_only;
+
+	/* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */
+	return i + 1;
+}
+
+static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		ctx->drawop = upa_readl(&ffb->drawop);
+		ctx->ppc = upa_readl(&ffb->ppc);
+		ctx->wid = upa_readl(&ffb->wid);
+		ctx->fg = upa_readl(&ffb->fg);
+		ctx->bg = upa_readl(&ffb->bg);
+		ctx->xclip = upa_readl(&ffb->xclip);
+		ctx->fbc = upa_readl(&ffb->fbc);
+		ctx->rop = upa_readl(&ffb->rop);
+		ctx->cmp = upa_readl(&ffb->cmp);
+		ctx->matchab = upa_readl(&ffb->matchab);
+		ctx->magnab = upa_readl(&ffb->magnab);
+		ctx->pmask = upa_readl(&ffb->pmask);
+		ctx->xpmask = upa_readl(&ffb->xpmask);
+		ctx->lpat = upa_readl(&ffb->lpat);
+		ctx->fontxy = upa_readl(&ffb->fontxy);
+		ctx->fontw = upa_readl(&ffb->fontw);
+		ctx->fontinc = upa_readl(&ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+
+		for (i = 0; i < 32; i++)
+			ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+		ctx->ucsr = upa_readl(&ffb->ucsr);
+		return;
+	}
+
+	/* Fetch drawop. */
+	ctx->drawop = upa_readl(&ffb->drawop);
+
+	/* If we were saving the vertex registers, this is where
+	 * we would do it.  We would save 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Capture rendering attributes. */
+
+	ctx->ppc = upa_readl(&ffb->ppc);		/* Pixel Processor Control */
+	ctx->wid = upa_readl(&ffb->wid);		/* Current WID */
+	ctx->fg = upa_readl(&ffb->fg);			/* Constant FG color */
+	ctx->bg = upa_readl(&ffb->bg);			/* Constant BG color */
+	ctx->consty = upa_readl(&ffb->consty);		/* Constant Y */
+	ctx->constz = upa_readl(&ffb->constz);		/* Constant Z */
+	ctx->xclip = upa_readl(&ffb->xclip);		/* X plane clip */
+	ctx->dcss = upa_readl(&ffb->dcss);		/* Depth Cue Scale Slope */
+	ctx->vclipmin = upa_readl(&ffb->vclipmin);	/* Primary XY clip, minimum */
+	ctx->vclipmax = upa_readl(&ffb->vclipmax);	/* Primary XY clip, maximum */
+	ctx->vclipzmin = upa_readl(&ffb->vclipzmin);	/* Primary Z clip, minimum */
+	ctx->vclipzmax = upa_readl(&ffb->vclipzmax);	/* Primary Z clip, maximum */
+	ctx->dcsf = upa_readl(&ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	ctx->dcsb = upa_readl(&ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	ctx->dczf = upa_readl(&ffb->dczf);		/* Depth Cue Scale Z Front */
+	ctx->dczb = upa_readl(&ffb->dczb);		/* Depth Cue Scale Z Back */
+	ctx->blendc = upa_readl(&ffb->blendc);		/* Alpha Blend Control */
+	ctx->blendc1 = upa_readl(&ffb->blendc1);	/* Alpha Blend Color 1 */
+	ctx->blendc2 = upa_readl(&ffb->blendc2);	/* Alpha Blend Color 2 */
+	ctx->fbc = upa_readl(&ffb->fbc);		/* Frame Buffer Control */
+	ctx->rop = upa_readl(&ffb->rop);		/* Raster Operation */
+	ctx->cmp = upa_readl(&ffb->cmp);		/* Compare Controls */
+	ctx->matchab = upa_readl(&ffb->matchab);	/* Buffer A/B Match Ops */
+	ctx->matchc = upa_readl(&ffb->matchc);		/* Buffer C Match Ops */
+	ctx->magnab = upa_readl(&ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	ctx->magnc = upa_readl(&ffb->magnc);		/* Buffer C Magnitude Ops */
+	ctx->pmask = upa_readl(&ffb->pmask);		/* RGB Plane Mask */
+	ctx->xpmask = upa_readl(&ffb->xpmask);		/* X Plane Mask */
+	ctx->ypmask = upa_readl(&ffb->ypmask);		/* Y Plane Mask */
+	ctx->zpmask = upa_readl(&ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min);
+	ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max);
+	ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min);
+	ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max);
+	ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min);
+	ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max);
+	ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min);
+	ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max);
+
+	ctx->lpat = upa_readl(&ffb->lpat);		/* Line Pattern */
+	ctx->fontxy = upa_readl(&ffb->fontxy);		/* XY Font Coordinate */
+	ctx->fontw = upa_readl(&ffb->fontw);		/* Font Width */
+	ctx->fontinc = upa_readl(&ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		ctx->dcss1 = upa_readl(&ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		ctx->dcss2 = upa_readl(&ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		ctx->dcss2 = upa_readl(&ffb->dcss3);	/* Depth Cue Scale Slope 3 */
+		ctx->dcs2  = upa_readl(&ffb->dcs2);	/* Depth Cue Scale 2 */
+		ctx->dcs3  = upa_readl(&ffb->dcs3);	/* Depth Cue Scale 3 */
+		ctx->dcs4  = upa_readl(&ffb->dcs4);	/* Depth Cue Scale 4 */
+		ctx->dcd2  = upa_readl(&ffb->dcd2);	/* Depth Cue Depth 2 */
+		ctx->dcd3  = upa_readl(&ffb->dcd3);	/* Depth Cue Depth 3 */
+		ctx->dcd4  = upa_readl(&ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+	}
+
+	/* Save the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register. */
+	ctx->ucsr = upa_readl(&ffb->ucsr);
+}
+
+static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		upa_writel(ctx->drawop, &ffb->drawop);
+
+		/* If we were restoring the vertex registers, this is where
+		 * we would do it.  We would restore 32 32-bit words starting
+		 * at ffb->suvtx.
+		 */
+
+		upa_writel(ctx->ppc, &ffb->ppc);
+		upa_writel(ctx->wid, &ffb->wid);
+		upa_writel(ctx->fg,  &ffb->fg);
+		upa_writel(ctx->bg, &ffb->bg);
+		upa_writel(ctx->xclip, &ffb->xclip);
+		upa_writel(ctx->fbc, &ffb->fbc);
+		upa_writel(ctx->rop, &ffb->rop);
+		upa_writel(ctx->cmp, &ffb->cmp);
+		upa_writel(ctx->matchab, &ffb->matchab);
+		upa_writel(ctx->magnab, &ffb->magnab);
+		upa_writel(ctx->pmask, &ffb->pmask);
+		upa_writel(ctx->xpmask, &ffb->xpmask);
+		upa_writel(ctx->lpat, &ffb->lpat);
+		upa_writel(ctx->fontxy, &ffb->fontxy);
+		upa_writel(ctx->fontw, &ffb->fontw);
+		upa_writel(ctx->fontinc, &ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+
+		for (i = 0; i < 32; i++)
+			upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+		upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+		return;
+	}
+
+	/* Restore drawop. */
+	upa_writel(ctx->drawop, &ffb->drawop);
+
+	/* If we were restoring the vertex registers, this is where
+	 * we would do it.  We would restore 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Restore rendering attributes. */
+
+	upa_writel(ctx->ppc, &ffb->ppc);		/* Pixel Processor Control */
+	upa_writel(ctx->wid, &ffb->wid);		/* Current WID */
+	upa_writel(ctx->fg, &ffb->fg);			/* Constant FG color */
+	upa_writel(ctx->bg, &ffb->bg);			/* Constant BG color */
+	upa_writel(ctx->consty, &ffb->consty);		/* Constant Y */
+	upa_writel(ctx->constz, &ffb->constz);		/* Constant Z */
+	upa_writel(ctx->xclip, &ffb->xclip);		/* X plane clip */
+	upa_writel(ctx->dcss, &ffb->dcss);		/* Depth Cue Scale Slope */
+	upa_writel(ctx->vclipmin, &ffb->vclipmin);	/* Primary XY clip, minimum */
+	upa_writel(ctx->vclipmax, &ffb->vclipmax);	/* Primary XY clip, maximum */
+	upa_writel(ctx->vclipzmin, &ffb->vclipzmin);	/* Primary Z clip, minimum */
+	upa_writel(ctx->vclipzmax, &ffb->vclipzmax);	/* Primary Z clip, maximum */
+	upa_writel(ctx->dcsf, &ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	upa_writel(ctx->dcsb, &ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	upa_writel(ctx->dczf, &ffb->dczf);		/* Depth Cue Scale Z Front */
+	upa_writel(ctx->dczb, &ffb->dczb);		/* Depth Cue Scale Z Back */
+	upa_writel(ctx->blendc, &ffb->blendc);		/* Alpha Blend Control */
+	upa_writel(ctx->blendc1, &ffb->blendc1);	/* Alpha Blend Color 1 */
+	upa_writel(ctx->blendc2, &ffb->blendc2);	/* Alpha Blend Color 2 */
+	upa_writel(ctx->fbc, &ffb->fbc);		/* Frame Buffer Control */
+	upa_writel(ctx->rop, &ffb->rop);		/* Raster Operation */
+	upa_writel(ctx->cmp, &ffb->cmp);		/* Compare Controls */
+	upa_writel(ctx->matchab, &ffb->matchab);	/* Buffer A/B Match Ops */
+	upa_writel(ctx->matchc, &ffb->matchc);		/* Buffer C Match Ops */
+	upa_writel(ctx->magnab, &ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	upa_writel(ctx->magnc, &ffb->magnc);		/* Buffer C Magnitude Ops */
+	upa_writel(ctx->pmask, &ffb->pmask);		/* RGB Plane Mask */
+	upa_writel(ctx->xpmask, &ffb->xpmask);		/* X Plane Mask */
+	upa_writel(ctx->ypmask, &ffb->ypmask);		/* Y Plane Mask */
+	upa_writel(ctx->zpmask, &ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min);
+	upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max);
+	upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min);
+	upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max);
+	upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min);
+	upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max);
+	upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min);
+	upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max);
+
+	upa_writel(ctx->lpat, &ffb->lpat);		/* Line Pattern */
+	upa_writel(ctx->fontxy, &ffb->fontxy);		/* XY Font Coordinate */
+	upa_writel(ctx->fontw, &ffb->fontw);		/* Font Width */
+	upa_writel(ctx->fontinc, &ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		upa_writel(ctx->dcss1, &ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		upa_writel(ctx->dcss2, &ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		upa_writel(ctx->dcss3, &ffb->dcss2);	/* Depth Cue Scale Slope 3 */
+		upa_writel(ctx->dcs2, &ffb->dcs2);	/* Depth Cue Scale 2 */
+		upa_writel(ctx->dcs3, &ffb->dcs3);	/* Depth Cue Scale 3 */
+		upa_writel(ctx->dcs4, &ffb->dcs4);	/* Depth Cue Scale 4 */
+		upa_writel(ctx->dcd2, &ffb->dcd2);	/* Depth Cue Depth 2 */
+		upa_writel(ctx->dcd3, &ffb->dcd3);	/* Depth Cue Depth 3 */
+		upa_writel(ctx->dcd4, &ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			/* Unfortunately, there is a hardware bug on
+			 * the FFB2+ chips which prevents a normal write
+			 * to the stencil control register from working
+			 * as it should.
+			 *
+			 * The state controlled by the FFB stencilctl register
+			 * really gets transferred to the per-buffer instances
+			 * of the stencilctl register in the 3DRAM chips.
+			 *
+			 * The bug is that FFB does not update buffer C correctly,
+			 * so we have to do it by hand for them.
+			 */
+
+			/* This will update buffers A and B. */
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+
+			/* Force FFB to use buffer C 3dram regs. */
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+
+			/* Now restore the correct FBC controls. */
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+	}
+
+	/* Restore the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register.
+	 * The only state we really preserve here is the picking
+	 * control.
+	 */
+	upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+}
+
+#define FFB_UCSR_FB_BUSY       0x01000000
+#define FFB_UCSR_RP_BUSY       0x02000000
+#define FFB_UCSR_ALL_BUSY      (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY)
+
+static void FFBWait(ffb_fbcPtr ffb)
+{
+	int limit = 100000;
+
+	do {
+		u32 regval = upa_readl(&ffb->ucsr);
+
+		if ((regval & FFB_UCSR_ALL_BUSY) == 0)
+			break;
+	} while (--limit);
+}
+
+int ffb_context_switch(drm_device_t *dev, int old, int new)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+
+        atomic_inc(&dev->total_ctx);
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context ||
+	    dev->last_context == 0) {
+		dev->last_context = new;
+                return 0;
+	}
+        
+	FFBWait(fpriv->regs);
+	ffb_save_context(fpriv, old);
+	ffb_restore_context(fpriv, old, new);
+	FFBWait(fpriv->regs);
+        
+	dev->last_context = new;
+
+        return 0;
+}
+
+int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+
+int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	int idx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY));
+	if (idx < 0)
+		return -ENFILE;
+
+	DRM_DEBUG("%d\n", ctx.handle);
+	ctx.handle = idx;
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0)
+		hwctx->is_2d_only = 0;
+	else
+		hwctx->is_2d_only = 1;
+
+	return 0;
+}
+
+int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if (hwctx->is_2d_only != 0)
+		ctx.flags = _DRM_CONTEXT_2DONLY;
+	else
+		ctx.flags = 0;
+
+	copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+
+	return 0;
+}
+
+int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return ffb_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	return 0;
+}
+
+int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	int idx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	idx = ctx.handle - 1;
+	if (idx < 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	if (fpriv->hw_state[idx] != NULL) {
+		kfree(fpriv->hw_state[idx]);
+		fpriv->hw_state[idx] = NULL;
+	}
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ffb_drv.c linux.18p17/drivers/char/drm/ffb_drv.c
--- linux.vanilla/drivers/char/drm/ffb_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/ffb_drv.c	Sun Sep 10 14:49:10 2000
@@ -0,0 +1,881 @@
+/* $Id: ffb_drv.c,v 1.6.2.2 2000/09/07 03:51:06 davem Exp $
+ * ffb_drv.c: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+#include "drmP.h"
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <asm/oplib.h>
+#include <asm/upa.h>
+
+#include "ffb_drv.h"
+
+#include <linux/agp_backend.h>
+
+#define FFB_NAME	"ffb"
+#define FFB_DESC	"Creator/Creator3D"
+#define FFB_DATE	"20000517"
+#define FFB_MAJOR	0
+#define FFB_MINOR	0
+#define FFB_PATCHLEVEL	1
+
+/* Forward declarations. */
+int  ffb_init(void);
+void ffb_cleanup(void);
+static int  ffb_version(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+static int  ffb_open(struct inode *inode, struct file *filp);
+static int  ffb_release(struct inode *inode, struct file *filp);
+static int  ffb_ioctl(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+static int  ffb_lock(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg);
+static int  ffb_unlock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/* From ffb_context.c */
+extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_context_switch(drm_device_t *, int, int);
+
+static struct file_operations ffb_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:		THIS_MODULE,
+#endif
+	open:		ffb_open,
+	flush:		drm_flush,
+	release:	ffb_release,
+	ioctl:		ffb_ioctl,
+	mmap:		ffb_mmap,
+	read:		drm_read,
+	fasync:		drm_fasync,
+	poll:		drm_poll,
+};
+
+/* This is just a template, we make a new copy for each FFB
+ * we discover at init time so that each one gets a unique
+ * misc device minor number.
+ */
+static struct miscdevice ffb_misc = {
+	minor:	MISC_DYNAMIC_MINOR,
+	name:	FFB_NAME,
+	fops:	&ffb_fops,
+};
+
+static drm_ioctl_desc_t ffb_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { ffb_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 }, /* XXX */
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	
+	/* The implementation is currently a nop just like on tdfx.
+	 * Later we can do something more clever. -DaveM
+	 */
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { ffb_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { ffb_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { ffb_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { ffb_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { ffb_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { ffb_resctx,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { ffb_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { ffb_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls)
+
+#ifdef MODULE
+static char *ffb = NULL;
+#endif
+
+MODULE_AUTHOR("David S. Miller (davem@redhat.com)");
+MODULE_DESCRIPTION("Sun Creator/Creator3D DRI");
+
+static int ffb_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+
+	/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+	
+	/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+	/* Clear map area information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+				drm_ioremapfree(map->handle, map->size);
+				break;
+
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+
+			default:
+				break;
+			};
+
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+drm_device_t **ffb_dev_table;
+static int ffb_dev_table_size;
+
+static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance)
+{
+	volatile unsigned char *strap_bits;
+	unsigned char val;
+
+#if LINUX_VERSION_CODE < 0x020300
+	strap_bits = (volatile unsigned char *)
+		__va(ffb_priv->card_phys_base + 0x00200000UL);
+#else
+	strap_bits = (volatile unsigned char *)
+		(ffb_priv->card_phys_base + 0x00200000UL);
+#endif
+
+	/* Don't ask, you have to read the value twice for whatever
+	 * reason to get correct contents.
+	 */
+	val = upa_readb(strap_bits);
+	val = upa_readb(strap_bits);
+	switch (val & 0x78) {
+	case (0x0 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb1_prototype;
+		printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance);
+		break;
+	case (0x0 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb1_standard;
+		printk("ffb%d: Detected FFB1\n", instance);
+		break;
+	case (0x0 << 5) | (0x3 << 3):
+		ffb_priv->ffb_type = ffb1_speedsort;
+		printk("ffb%d: Detected FFB1-SpeedSort\n", instance);
+		break;
+	case (0x1 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_prototype;
+		printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance);
+		break;
+	case (0x1 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Detected FFB2/vertical\n", instance);
+		break;
+	case (0x1 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_vertical_plus;
+		printk("ffb%d: Detected FFB2+/vertical\n", instance);
+		break;
+	case (0x2 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2/horizontal\n", instance);
+		break;
+	case (0x2 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2+/horizontal\n", instance);
+		break;
+	default:
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val);
+		break;
+	};
+}
+
+static int ffb_init_one(int prom_node, int instance)
+{
+	struct linux_prom64_registers regs[2*PROMREG_MAX];
+	drm_device_t *dev;
+	ffb_dev_priv_t *ffb_priv;
+	int ret, i;
+
+	dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	memset(dev, 0, sizeof(*dev));
+	spin_lock_init(&dev->count_lock);
+	sema_init(&dev->struct_sem, 1);
+
+	ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+	ffb_priv->prom_node = prom_node;
+	if (prom_getproperty(ffb_priv->prom_node, "reg",
+			     (void *)regs, sizeof(regs)) <= 0) {
+		kfree(dev);
+		return -EINVAL;
+	}
+	ffb_priv->card_phys_base = regs[0].phys_addr;
+#if LINUX_VERSION_CODE < 0x020300
+	ffb_priv->regs = (ffb_fbcPtr)
+		__va(regs[0].phys_addr + 0x00600000UL);
+#else
+	ffb_priv->regs = (ffb_fbcPtr)
+		(regs[0].phys_addr + 0x00600000UL);
+#endif
+	get_ffb_type(ffb_priv, instance);
+	for (i = 0; i < FFB_MAX_CTXS; i++)
+		ffb_priv->hw_state[i] = NULL;
+
+	ffb_dev_table[instance] = dev;
+
+#ifdef MODULE
+	drm_parse_options(ffb);
+#endif
+
+	memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc));
+	ret = misc_register(&ffb_priv->miscdev);
+	if (ret) {
+		ffb_dev_table[instance] = NULL;
+		kfree(dev);
+		return ret;
+	}
+
+	dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor);
+	dev->name = FFB_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n",
+		 FFB_NAME,
+		 FFB_MAJOR,
+		 FFB_MINOR,
+		 FFB_PATCHLEVEL,
+		 FFB_DATE,
+		 ffb_priv->miscdev.minor,
+		 ffb_priv->card_phys_base);
+	
+	return 0;
+}
+
+static int ffb_init_dev_table(void)
+{
+	int root, node;
+	int total = 0;
+
+	root = prom_getchild(prom_root_node);
+	for (node = prom_searchsiblings(root, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb"))
+		total++;
+
+	ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL);
+	if (!ffb_dev_table)
+		return -ENOMEM;
+
+	ffb_dev_table_size = total;
+
+	return 0;
+}
+
+int ffb_init(void)
+{
+	int root, node, instance, ret;
+
+	ret = ffb_init_dev_table();
+	if (ret)
+		return ret;
+
+	instance = 0;
+	root = prom_getchild(prom_root_node);
+	for (node = prom_searchsiblings(root, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) {
+		ret = ffb_init_one(node, instance);
+		if (ret)
+			return ret;
+		instance++;
+	}
+
+	return 0;
+}
+
+void ffb_cleanup(void)
+{
+	int instance;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	for (instance = 0; instance < ffb_dev_table_size; instance++) {
+		drm_device_t *dev = ffb_dev_table[instance];
+		ffb_dev_priv_t *ffb_priv;
+
+		if (!dev)
+			continue;
+
+		ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+		if (misc_deregister(&ffb_priv->miscdev)) {
+			DRM_ERROR("Cannot unload module\n");
+		} else {
+			DRM_INFO("Module unloaded\n");
+		}
+		ffb_takedown(dev);
+		kfree(dev);
+		ffb_dev_table[instance] = NULL;
+	}
+	kfree(ffb_dev_table);
+	ffb_dev_table = NULL;
+	ffb_dev_table_size = 0;
+}
+
+static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_version_t version;
+	int len, ret;
+
+	ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version));
+	if (ret)
+		return -EFAULT;
+
+	version.version_major		= FFB_MAJOR;
+	version.version_minor		= FFB_MINOR;
+	version.version_patchlevel	= FFB_PATCHLEVEL;
+
+	len = strlen(FFB_NAME);
+	if (len > version.name_len)
+		len = version.name_len;
+	version.name_len = len;
+	if (len && version.name) {
+		ret = copy_to_user(version.name, FFB_NAME, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DATE);
+	if (len > version.date_len)
+		len = version.date_len;
+	version.date_len = len;
+	if (len && version.date) {
+		ret = copy_to_user(version.date, FFB_DATE, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DESC);
+	if (len > version.desc_len)
+		len = version.desc_len;
+	version.desc_len = len;
+	if (len && version.desc) {
+		ret = copy_to_user(version.desc, FFB_DESC, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version));
+	if (ret)
+		ret = -EFAULT;
+
+	return ret;
+}
+
+static int ffb_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	return 0;
+}
+
+static int ffb_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t *dev;
+	int minor, i;
+	int ret = 0;
+
+	minor = MINOR(inode->i_rdev);
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_dev_priv_t *ffb_priv;
+
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+
+	if (i >= ffb_dev_table_size)
+		return -EINVAL;
+
+	dev = ffb_dev_table[i];
+	if (!dev)
+		return -EINVAL;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	ret = drm_open_helper(inode, filp, dev);
+	if (!ret) {
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return ffb_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return ret;
+}
+
+static int ffb_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev;
+	int ret = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (dev->lock.hw_lock != NULL
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+		int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock);
+		int idx;
+
+		/* We have to free up the rogue hw context state
+		 * holding error or else we will leak it.
+		 */
+		idx = context - 1;
+		if (fpriv->hw_state[idx] != NULL) {
+			kfree(fpriv->hw_state[idx]);
+			fpriv->hw_state[idx] = NULL;
+		}
+	}
+
+	ret = drm_release(inode, filp);
+
+	if (!ret) {
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			ret = ffb_takedown(dev);
+			unlock_kernel();
+			return ret;
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return ret;
+}
+
+static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+	int		 ret;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= FFB_IOCTL_COUNT) {
+		ret = -EINVAL;
+	} else {
+		ioctl	  = &ffb_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			ret = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			ret = -EACCES;
+		} else {
+			ret = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+
+	return ret;
+}
+
+static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv	= filp->private_data;
+        drm_device_t      *dev	= priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret	= 0;
+        drm_lock_t        lock;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+	add_wait_queue(&dev->lock.lock_queue, &entry);
+	for (;;) {
+		if (!dev->lock.hw_lock) {
+			/* Device has been unregistered */
+			ret = -EINTR;
+			break;
+		}
+		if (drm_lock_take(&dev->lock.hw_lock->lock,
+				  lock.context)) {
+			dev->lock.pid       = current->pid;
+			dev->lock.lock_time = jiffies;
+			atomic_inc(&dev->total_locks);
+			break;  /* Got lock */
+		}
+                        
+		/* Contention */
+		atomic_inc(&dev->total_sleeps);
+		current->state = TASK_INTERRUPTIBLE;
+		current->policy |= SCHED_YIELD;
+		schedule();
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&dev->lock.lock_queue, &entry);
+
+        if (!ret &&
+	    (dev->last_context != lock.context))
+		ffb_context_switch(dev, dev->last_context, lock.context);
+
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+        return ret;
+}
+
+int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+	unsigned int old, new, prev, ctx;
+	int ret;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+	
+	if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+
+	/* We no longer really hold it, but if we are the next
+	 * agent to request it then we should just be able to
+	 * take it immediately and not eat the ioctl.
+	 */
+	dev->lock.pid = 0;
+	{
+		__volatile__ unsigned int *plock = &dev->lock.hw_lock->lock;
+
+		do {
+			old  = *plock;
+			new  = ctx;
+			prev = cmpxchg(plock, old, new);
+		} while (prev != old);
+	}
+
+	wake_up_interruptible(&dev->lock.lock_queue);
+	
+	return 0;
+}
+
+static void align_fb_mapping(struct vm_area_struct *vma)
+{
+	unsigned long j, alignment;
+
+	j = vma->vm_end - vma->vm_start;
+	for (alignment = (4 * 1024 * 1024); alignment > PAGE_SIZE; alignment >>= 3)
+		if (j >= alignment)
+			break;
+	if (alignment > PAGE_SIZE) {
+		j = alignment;
+		alignment = j - (vma->vm_start & (j - 1));
+		if (alignment != j) {
+			struct vm_area_struct *vmm = find_vma(current->mm,vma->vm_start);
+
+			if (!vmm || vmm->vm_start >= vma->vm_end + alignment) {
+				vma->vm_start += alignment;
+				vma->vm_end += alignment;
+			}
+		}
+	}
+}
+
+/* The problem here is, due to virtual cache aliasing,
+ * we must make sure the shared memory area lands in the
+ * same dcache line for both the kernel and all drm clients.
+ */
+static void align_shm_mapping(struct vm_area_struct *vma, unsigned long kvirt)
+{
+	kvirt &= PAGE_SIZE;
+	if ((vma->vm_start & PAGE_SIZE) != kvirt) {
+		struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
+
+		if (!vmm || vmm->vm_start >= vma->vm_end + PAGE_SIZE) {
+			vma->vm_start += PAGE_SIZE;
+			vma->vm_end += PAGE_SIZE;
+		}
+	}
+}
+
+extern struct vm_operations_struct drm_vm_ops;
+extern struct vm_operations_struct drm_vm_shm_ops;
+
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	ffb_dev_priv_t	*ffb_priv;
+	int		i, minor;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	lock_kernel();
+	minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+	ffb_priv = NULL;
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+	if (i >= ffb_dev_table_size) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	/* We don't support/need dma mappings, so... */
+	if (!VM_OFFSET(vma)) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long off;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		off = (map->offset & 0xffffffff);
+		if (off == VM_OFFSET(vma))
+			break;
+	}
+
+	if (i >= dev->map_count) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	if (!map ||
+	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
+		unlock_kernel();
+		return -EPERM;
+	}
+	if (map->size != (vma->vm_end - vma->vm_start)) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	/* Set read-only attribute before mappings are created
+	 * so it works for fb/reg maps too.
+	 */
+	if (map->flags & _DRM_READ_ONLY)
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+		align_fb_mapping(vma);
+		/* FALLTHROUGH */
+
+	case _DRM_REGISTERS:
+		/* In order to handle 32-bit drm apps/xserver we
+		 * play a trick.  The mappings only really specify
+		 * the 32-bit offset from the cards 64-bit base
+		 * address, and we just add in the base here.
+		 */
+		vma->vm_flags |= VM_IO;
+		if (io_remap_page_range(vma->vm_start,
+					ffb_priv->card_phys_base + VM_OFFSET(vma),
+					vma->vm_end - vma->vm_start,
+					vma->vm_page_prot, 0)) {
+			unlock_kernel();
+			return -EAGAIN;
+		}
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		align_shm_mapping(vma, (unsigned long)dev->lock.hw_lock);
+		vma->vm_ops = &drm_vm_shm_ops;
+
+		/* Don't let this area swap.  Change when
+		 * DRM_KERNEL advisory is supported.
+		 */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		unlock_kernel();
+		return -EINVAL;	/* This should never happen. */
+	};
+	unlock_kernel();
+
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file = filp; /* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+module_init(ffb_init);
+module_exit(ffb_cleanup);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ffb_drv.h linux.18p17/drivers/char/drm/ffb_drv.h
--- linux.vanilla/drivers/char/drm/ffb_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/ffb_drv.h	Sun Sep 10 14:49:10 2000
@@ -0,0 +1,286 @@
+/* $Id: ffb_drv.h,v 1.1.2.1 2000/09/05 00:10:45 davem Exp $
+ * ffb_drv.h: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+/* 2.2.x compat items. */
+#ifndef upa_readb
+#define upa_readb(x)		(*(volatile u8 *)(x))
+#define upa_readw(x)		(*(volatile u16 *)(x))
+#define upa_readl(x)		(*(volatile u32 *)(x))
+#define upa_writeb(val,x)	((*(volatile u8 *)(x)) = (val))
+#define upa_writew(val,x)	((*(volatile u16 *)(x)) = (val))
+#define upa_writel(val,x)	((*(volatile u32 *)(x)) = (val))
+#endif /* !defined(upa_readb) */
+
+/* Auxilliary clips. */
+typedef struct  {
+	volatile unsigned int min;
+	volatile unsigned int max;
+} ffb_auxclip, *ffb_auxclipPtr;
+
+/* FFB register set. */
+typedef struct _ffb_fbc {
+	/* Next vertex registers, on the right we list which drawops
+	 * use said register and the logical name the register has in
+	 * that context.
+	 */					/* DESCRIPTION		DRAWOP(NAME)	*/
+/*0x00*/unsigned int		pad1[3];	/* Reserved				*/
+/*0x0c*/volatile unsigned int	alpha;		/* ALPHA Transparency			*/
+/*0x10*/volatile unsigned int	red;		/* RED					*/
+/*0x14*/volatile unsigned int	green;		/* GREEN				*/
+/*0x18*/volatile unsigned int	blue;		/* BLUE					*/
+/*0x1c*/volatile unsigned int	z;		/* DEPTH				*/
+/*0x20*/volatile unsigned int	y;		/* Y			triangle(DOYF)	*/
+						/*                      aadot(DYF)	*/
+						/*                      ddline(DYF)	*/
+						/*                      aaline(DYF)	*/
+/*0x24*/volatile unsigned int	x;		/* X			triangle(DOXF)	*/
+						/*                      aadot(DXF)	*/
+						/*                      ddline(DXF)	*/
+						/*                      aaline(DXF)	*/
+/*0x28*/unsigned int		pad2[2];	/* Reserved				*/
+/*0x30*/volatile unsigned int	ryf;		/* Y (alias to DOYF)	ddline(RYF)	*/
+						/*			aaline(RYF)	*/
+						/*			triangle(RYF)	*/
+/*0x34*/volatile unsigned int	rxf;		/* X			ddline(RXF)	*/
+						/*			aaline(RXF)	*/
+						/*			triangle(RXF)	*/
+/*0x38*/unsigned int		pad3[2];	/* Reserved				*/
+/*0x40*/volatile unsigned int	dmyf;		/* Y (alias to DOYF)	triangle(DMYF)	*/
+/*0x44*/volatile unsigned int	dmxf;		/* X			triangle(DMXF)	*/
+/*0x48*/unsigned int		pad4[2];	/* Reserved				*/
+/*0x50*/volatile unsigned int	ebyi;		/* Y (alias to RYI)	polygon(EBYI)	*/
+/*0x54*/volatile unsigned int	ebxi;		/* X			polygon(EBXI)	*/
+/*0x58*/unsigned int		pad5[2];	/* Reserved				*/
+/*0x60*/volatile unsigned int	by;		/* Y			brline(RYI)	*/
+						/*			fastfill(OP)	*/
+						/*			polygon(YI)	*/
+						/*			rectangle(YI)	*/
+						/*			bcopy(SRCY)	*/
+						/*			vscroll(SRCY)	*/
+/*0x64*/volatile unsigned int	bx;		/* X			brline(RXI)	*/
+						/*			polygon(XI)	*/
+						/*			rectangle(XI)	*/
+						/*			bcopy(SRCX)	*/
+						/*			vscroll(SRCX)	*/
+						/*			fastfill(GO)	*/
+/*0x68*/volatile unsigned int	dy;		/* destination Y	fastfill(DSTY)	*/
+						/*			bcopy(DSRY)	*/
+						/*			vscroll(DSRY)	*/
+/*0x6c*/volatile unsigned int	dx;		/* destination X	fastfill(DSTX)	*/
+						/*			bcopy(DSTX)	*/
+						/*			vscroll(DSTX)	*/
+/*0x70*/volatile unsigned int	bh;		/* Y (alias to RYI)	brline(DYI)	*/
+						/*			dot(DYI)	*/
+						/*			polygon(ETYI)	*/
+						/* Height		fastfill(H)	*/
+						/*			bcopy(H)	*/
+						/*			vscroll(H)	*/
+						/* Y count		fastfill(NY)	*/
+/*0x74*/volatile unsigned int	bw;		/* X			dot(DXI)	*/
+						/*			brline(DXI)	*/
+						/*			polygon(ETXI)	*/
+						/*			fastfill(W)	*/
+						/*			bcopy(W)	*/
+						/*			vscroll(W)	*/
+						/*			fastfill(NX)	*/
+/*0x78*/unsigned int		pad6[2];	/* Reserved				*/
+/*0x80*/unsigned int		pad7[32];	/* Reserved				*/
+	
+	/* Setup Unit's vertex state register */
+/*100*/	volatile unsigned int	suvtx;
+/*104*/	unsigned int		pad8[63];	/* Reserved				*/
+	
+	/* Frame Buffer Control Registers */
+/*200*/	volatile unsigned int	ppc;		/* Pixel Processor Control		*/
+/*204*/	volatile unsigned int	wid;		/* Current WID				*/
+/*208*/	volatile unsigned int	fg;		/* FG data				*/
+/*20c*/	volatile unsigned int	bg;		/* BG data				*/
+/*210*/	volatile unsigned int	consty;		/* Constant Y				*/
+/*214*/	volatile unsigned int	constz;		/* Constant Z				*/
+/*218*/	volatile unsigned int	xclip;		/* X Clip				*/
+/*21c*/	volatile unsigned int	dcss;		/* Depth Cue Scale Slope		*/
+/*220*/	volatile unsigned int	vclipmin;	/* Viewclip XY Min Bounds		*/
+/*224*/	volatile unsigned int	vclipmax;	/* Viewclip XY Max Bounds		*/
+/*228*/	volatile unsigned int	vclipzmin;	/* Viewclip Z Min Bounds		*/
+/*22c*/	volatile unsigned int	vclipzmax;	/* Viewclip Z Max Bounds		*/
+/*230*/	volatile unsigned int	dcsf;		/* Depth Cue Scale Front Bound		*/
+/*234*/	volatile unsigned int	dcsb;		/* Depth Cue Scale Back Bound		*/
+/*238*/	volatile unsigned int	dczf;		/* Depth Cue Z Front			*/
+/*23c*/	volatile unsigned int	dczb;		/* Depth Cue Z Back			*/
+/*240*/	unsigned int		pad9;		/* Reserved				*/
+/*244*/	volatile unsigned int	blendc;		/* Alpha Blend Control			*/
+/*248*/	volatile unsigned int	blendc1;	/* Alpha Blend Color 1			*/
+/*24c*/	volatile unsigned int	blendc2;	/* Alpha Blend Color 2			*/
+/*250*/	volatile unsigned int	fbramitc;	/* FB RAM Interleave Test Control	*/
+/*254*/	volatile unsigned int	fbc;		/* Frame Buffer Control			*/
+/*258*/	volatile unsigned int	rop;		/* Raster OPeration			*/
+/*25c*/	volatile unsigned int	cmp;		/* Frame Buffer Compare			*/
+/*260*/	volatile unsigned int	matchab;	/* Buffer AB Match Mask			*/
+/*264*/	volatile unsigned int	matchc;		/* Buffer C(YZ) Match Mask		*/
+/*268*/	volatile unsigned int	magnab;		/* Buffer AB Magnitude Mask		*/
+/*26c*/	volatile unsigned int	magnc;		/* Buffer C(YZ) Magnitude Mask		*/
+/*270*/	volatile unsigned int	fbcfg0;		/* Frame Buffer Config 0		*/
+/*274*/	volatile unsigned int	fbcfg1;		/* Frame Buffer Config 1		*/
+/*278*/	volatile unsigned int	fbcfg2;		/* Frame Buffer Config 2		*/
+/*27c*/	volatile unsigned int	fbcfg3;		/* Frame Buffer Config 3		*/
+/*280*/	volatile unsigned int	ppcfg;		/* Pixel Processor Config		*/
+/*284*/	volatile unsigned int	pick;		/* Picking Control			*/
+/*288*/	volatile unsigned int	fillmode;	/* FillMode				*/
+/*28c*/	volatile unsigned int	fbramwac;	/* FB RAM Write Address Control		*/
+/*290*/	volatile unsigned int	pmask;		/* RGB PlaneMask			*/
+/*294*/	volatile unsigned int	xpmask;		/* X PlaneMask				*/
+/*298*/	volatile unsigned int	ypmask;		/* Y PlaneMask				*/
+/*29c*/	volatile unsigned int	zpmask;		/* Z PlaneMask				*/
+/*2a0*/	ffb_auxclip		auxclip[4]; 	/* Auxilliary Viewport Clip		*/
+	
+	/* New 3dRAM III support regs */
+/*2c0*/	volatile unsigned int	rawblend2;
+/*2c4*/	volatile unsigned int	rawpreblend;
+/*2c8*/	volatile unsigned int	rawstencil;
+/*2cc*/	volatile unsigned int	rawstencilctl;
+/*2d0*/	volatile unsigned int	threedram1;
+/*2d4*/	volatile unsigned int	threedram2;
+/*2d8*/	volatile unsigned int	passin;
+/*2dc*/	volatile unsigned int	rawclrdepth;
+/*2e0*/	volatile unsigned int	rawpmask;
+/*2e4*/	volatile unsigned int	rawcsrc;
+/*2e8*/	volatile unsigned int	rawmatch;
+/*2ec*/	volatile unsigned int	rawmagn;
+/*2f0*/	volatile unsigned int	rawropblend;
+/*2f4*/	volatile unsigned int	rawcmp;
+/*2f8*/	volatile unsigned int	rawwac;
+/*2fc*/	volatile unsigned int	fbramid;
+	
+/*300*/	volatile unsigned int	drawop;		/* Draw OPeration			*/
+/*304*/	unsigned int		pad10[2];	/* Reserved				*/
+/*30c*/	volatile unsigned int	lpat;		/* Line Pattern control			*/
+/*310*/	unsigned int		pad11;		/* Reserved				*/
+/*314*/	volatile unsigned int	fontxy;		/* XY Font coordinate			*/
+/*318*/	volatile unsigned int	fontw;		/* Font Width				*/
+/*31c*/	volatile unsigned int	fontinc;	/* Font Increment			*/
+/*320*/	volatile unsigned int	font;		/* Font bits				*/
+/*324*/	unsigned int		pad12[3];	/* Reserved				*/
+/*330*/	volatile unsigned int	blend2;
+/*334*/	volatile unsigned int	preblend;
+/*338*/	volatile unsigned int	stencil;
+/*33c*/	volatile unsigned int	stencilctl;
+
+/*340*/	unsigned int		pad13[4];	/* Reserved				*/
+/*350*/	volatile unsigned int	dcss1;		/* Depth Cue Scale Slope 1		*/
+/*354*/	volatile unsigned int	dcss2;		/* Depth Cue Scale Slope 2		*/
+/*358*/	volatile unsigned int	dcss3;		/* Depth Cue Scale Slope 3		*/
+/*35c*/	volatile unsigned int	widpmask;
+/*360*/	volatile unsigned int	dcs2;
+/*364*/	volatile unsigned int	dcs3;
+/*368*/	volatile unsigned int	dcs4;
+/*36c*/	unsigned int		pad14;		/* Reserved				*/
+/*370*/	volatile unsigned int	dcd2;
+/*374*/	volatile unsigned int	dcd3;
+/*378*/	volatile unsigned int	dcd4;
+/*37c*/	unsigned int		pad15;		/* Reserved				*/
+/*380*/	volatile unsigned int	pattern[32];	/* area Pattern				*/
+/*400*/	unsigned int		pad16[8];	/* Reserved				*/
+/*420*/	volatile unsigned int	reset;		/* chip RESET				*/
+/*424*/	unsigned int		pad17[247];	/* Reserved				*/
+/*800*/	volatile unsigned int	devid;		/* Device ID				*/
+/*804*/	unsigned int		pad18[63];	/* Reserved				*/
+/*900*/	volatile unsigned int	ucsr;		/* User Control & Status Register	*/
+/*904*/	unsigned int		pad19[31];	/* Reserved				*/
+/*980*/	volatile unsigned int	mer;		/* Mode Enable Register			*/
+/*984*/	unsigned int		pad20[1439];	/* Reserved				*/
+} ffb_fbc, *ffb_fbcPtr;
+
+struct ffb_hw_context {
+	int is_2d_only;
+
+	unsigned int ppc;
+	unsigned int wid;
+	unsigned int fg;
+	unsigned int bg;
+	unsigned int consty;
+	unsigned int constz;
+	unsigned int xclip;
+	unsigned int dcss;
+	unsigned int vclipmin;
+	unsigned int vclipmax;
+	unsigned int vclipzmin;
+	unsigned int vclipzmax;
+	unsigned int dcsf;
+	unsigned int dcsb;
+	unsigned int dczf;
+	unsigned int dczb;
+	unsigned int blendc;
+	unsigned int blendc1;
+	unsigned int blendc2;
+	unsigned int fbc;
+	unsigned int rop;
+	unsigned int cmp;
+	unsigned int matchab;
+	unsigned int matchc;
+	unsigned int magnab;
+	unsigned int magnc;
+	unsigned int pmask;
+	unsigned int xpmask;
+	unsigned int ypmask;
+	unsigned int zpmask;
+	unsigned int auxclip0min;
+	unsigned int auxclip0max;
+	unsigned int auxclip1min;
+	unsigned int auxclip1max;
+	unsigned int auxclip2min;
+	unsigned int auxclip2max;
+	unsigned int auxclip3min;
+	unsigned int auxclip3max;
+	unsigned int drawop;
+	unsigned int lpat;
+	unsigned int fontxy;
+	unsigned int fontw;
+	unsigned int fontinc;
+	unsigned int area_pattern[32];
+	unsigned int ucsr;
+	unsigned int stencil;
+	unsigned int stencilctl;
+	unsigned int dcss1;
+	unsigned int dcss2;
+	unsigned int dcss3;
+	unsigned int dcs2;
+	unsigned int dcs3;
+	unsigned int dcs4;
+	unsigned int dcd2;
+	unsigned int dcd3;
+	unsigned int dcd4;
+	unsigned int mer;
+};
+
+#define FFB_MAX_CTXS	32
+
+enum ffb_chip_type {
+	ffb1_prototype = 0,	/* Early pre-FCS FFB */
+	ffb1_standard,		/* First FCS FFB, 100Mhz UPA, 66MHz gclk */
+	ffb1_speedsort,		/* Second FCS FFB, 100Mhz UPA, 75MHz gclk */
+	ffb2_prototype,		/* Early pre-FCS vertical FFB2 */
+	ffb2_vertical,		/* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk,
+				   75(SingleBuffer)/83(DoubleBuffer) MHz fclk */
+	ffb2_vertical_plus,	/* Second FCS FFB2/vertical, same timings */
+	ffb2_horizontal,	/* First FCS FFB2/horizontal, same timings as FFB2/vert */
+	ffb2_horizontal_plus,	/* Second FCS FFB2/horizontal, same timings */
+	afb_m3,			/* FCS Elite3D, 3 float chips */
+	afb_m6			/* FCS Elite3D, 6 float chips */
+};
+
+typedef struct ffb_dev_priv {
+	/* Misc software state. */
+	int			prom_node;
+	enum ffb_chip_type	ffb_type;
+	u64			card_phys_base;
+	struct miscdevice 	miscdev;
+
+	/* Controller registers. */
+	ffb_fbcPtr		regs;
+
+	/* Context table. */
+	struct ffb_hw_context	*hw_state[FFB_MAX_CTXS];
+} ffb_dev_priv_t;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/fops.c linux.18p17/drivers/char/drm/fops.c
--- linux.vanilla/drivers/char/drm/fops.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/fops.c	Fri Sep  1 14:35:00 2000
@@ -0,0 +1,238 @@
+/* fops.c -- File operations for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/poll.h>
+
+/* drm_open is called whenever a process opens /dev/drm. */
+
+int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev)
+{
+	kdev_t	     minor = MINOR(inode->i_rdev);
+	drm_file_t   *priv;
+
+	if (filp->f_flags & O_EXCL)   return -EBUSY; /* No exclusive opens */
+	if (!drm_cpu_valid())         return -EINVAL;
+
+	DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
+
+	priv		    = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
+	memset(priv, 0, sizeof(*priv));
+	filp->private_data  = priv;
+	priv->uid	    = current->euid;
+	priv->pid	    = current->pid;
+	priv->minor	    = minor;
+	priv->dev	    = dev;
+	priv->ioctl_count   = 0;
+	priv->authenticated = capable(CAP_SYS_ADMIN);
+
+	down(&dev->struct_sem);
+	if (!dev->file_last) {
+		priv->next	= NULL;
+		priv->prev	= NULL;
+		dev->file_first = priv;
+		dev->file_last	= priv;
+	} else {
+		priv->next	     = NULL;
+		priv->prev	     = dev->file_last;
+		dev->file_last->next = priv;
+		dev->file_last	     = priv;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_flush(struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+	return 0;
+}
+
+/* drm_release is called whenever a process closes /dev/drm*.  Linux calls
+   this only if any mappings have been closed. */
+
+int drm_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	}
+	drm_reclaim_buffers(dev, priv->pid);
+
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+	
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+	
+	return 0;
+}
+
+int drm_fasync(int fd, struct file *filp, int on)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      retcode;
+	
+	DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device);
+	retcode = fasync_helper(fd, filp, on, &dev->buf_async);
+	if (retcode < 0) return retcode;
+	return 0;
+}
+
+
+/* The drm_read and drm_write_string code (especially that which manages
+   the circular buffer), is based on Alessandro Rubini's LINUX DEVICE
+   DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */
+
+ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      left;
+	int	      avail;
+	int	      send;
+	int	      cur;
+
+	DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp);
+	
+	while (dev->buf_rp == dev->buf_wp) {
+		DRM_DEBUG("  sleeping\n");
+		if (filp->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		interruptible_sleep_on(&dev->buf_readers);
+		if (signal_pending(current)) {
+			DRM_DEBUG("  interrupted\n");
+			return -ERESTARTSYS;
+		}
+		DRM_DEBUG("  awake\n");
+	}
+
+	left  = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	avail = DRM_BSZ - left;
+	send  = DRM_MIN(avail, count);
+
+	while (send) {
+		if (dev->buf_wp > dev->buf_rp) {
+			cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp);
+		} else {
+			cur = DRM_MIN(send, dev->buf_end - dev->buf_rp);
+		}
+		copy_to_user_ret(buf, dev->buf_rp, cur, -EINVAL);
+		dev->buf_rp += cur;
+		if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf;
+		send -= cur;
+	}
+	
+	wake_up_interruptible(&dev->buf_writers);
+	return DRM_MIN(avail, count);;
+}
+
+int drm_write_string(drm_device_t *dev, const char *s)
+{
+	int left   = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	int send   = strlen(s);
+	int count;
+
+	DRM_DEBUG("%d left, %d to send (%p, %p)\n",
+		  left, send, dev->buf_rp, dev->buf_wp);
+	
+	if (left == 1 || dev->buf_wp != dev->buf_rp) {
+		DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n",
+			  left,
+			  dev->buf_wp,
+			  dev->buf_rp);
+	}
+
+	while (send) {
+		if (dev->buf_wp >= dev->buf_rp) {
+			count = DRM_MIN(send, dev->buf_end - dev->buf_wp);
+			if (count == left) --count; /* Leave a hole */
+		} else {
+			count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1);
+		}
+		strncpy(dev->buf_wp, s, count);
+		dev->buf_wp += count;
+		if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf;
+		send -= count;
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO);
+#else
+				/* Type of first parameter changed in
+                                   Linux 2.4.0-test2... */
+	if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
+#endif
+	DRM_DEBUG("waking\n");
+	wake_up_interruptible(&dev->buf_readers);
+	return 0;
+}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+	drm_file_t   *priv = filp->private_data;
+	drm_device_t *dev  = priv->dev;
+
+	poll_wait(filp, &dev->buf_readers, wait);
+	if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/gamma_dma.c linux.18p17/drivers/char/drm/gamma_dma.c
--- linux.vanilla/drivers/char/drm/gamma_dma.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/gamma_dma.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,824 @@
+/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+
+/* WARNING!!! MAGIC NUMBER!!!  The number of regions already added to the
+   kernel must be specified here.  Currently, the number is 2.	This must
+   match the order the X server uses for instantiating register regions ,
+   or must be passed in a new ioctl. */
+#define GAMMA_REG(reg)						   \
+	(2							   \
+	 + ((reg < 0x1000)					   \
+	    ? 0							   \
+	    : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3))))
+
+#define GAMMA_OFF(reg)						   \
+	((reg < 0x1000)						   \
+	 ? reg							   \
+	 : ((reg < 0x10000)					   \
+	    ? (reg - 0x1000)					   \
+	    : ((reg < 0x11000)					   \
+	       ? (reg - 0x10000)				   \
+	       : (reg - 0x11000))))
+
+#define GAMMA_BASE(reg)	 ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle)
+#define GAMMA_ADDR(reg)	 (GAMMA_BASE(reg) + GAMMA_OFF(reg))
+#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg)
+#define GAMMA_READ(reg)	 GAMMA_DEREF(reg)
+#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0)
+
+#define GAMMA_BROADCASTMASK    0x9378
+#define GAMMA_COMMANDINTENABLE 0x0c48
+#define GAMMA_DMAADDRESS       0x0028
+#define GAMMA_DMACOUNT	       0x0030
+#define GAMMA_FILTERMODE       0x8c00
+#define GAMMA_GCOMMANDINTFLAGS 0x0c50
+#define GAMMA_GCOMMANDMODE     0x0c40
+#define GAMMA_GCOMMANDSTATUS   0x0c60
+#define GAMMA_GDELAYTIMER      0x0c38
+#define GAMMA_GDMACONTROL      0x0060
+#define GAMMA_GINTENABLE       0x0808
+#define GAMMA_GINTFLAGS	       0x0810
+#define GAMMA_INFIFOSPACE      0x0018
+#define GAMMA_OUTFIFOWORDS     0x0020
+#define GAMMA_OUTPUTFIFO       0x2000
+#define GAMMA_SYNC	       0x8c40
+#define GAMMA_SYNC_TAG	       0x0188
+
+static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address,
+				      unsigned long length)
+{
+	GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address));
+	while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4)
+		;
+	GAMMA_WRITE(GAMMA_DMACOUNT, length / 4);
+}
+
+static inline void gamma_dma_quiescent_single(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_quiescent_dual(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_BROADCASTMASK, 3);
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+				/* Read from first MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+	
+				/* Read from second MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_ready(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+}
+
+static inline int gamma_dma_is_ready(drm_device_t *dev)
+{
+	return !GAMMA_READ(GAMMA_DMACOUNT);
+}
+
+static void gamma_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+	drm_device_dma_t *dma = dev->dma;
+	
+	atomic_inc(&dev->total_irq);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */
+	GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8);
+	GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001);
+	if (gamma_dma_is_ready(dev)) {
+				/* Free previous buffer */
+		if (test_and_set_bit(0, &dev->dma_flag)) {
+			atomic_inc(&dma->total_missed_free);
+			return;
+		}
+		if (dma->this_buffer) {
+			drm_free_buffer(dev, dma->this_buffer);
+			dma->this_buffer = NULL;
+		}
+		clear_bit(0, &dev->dma_flag);
+
+				/* Dispatch new buffer */
+		queue_task(&dev->tq, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+/* Only called by gamma_dma_schedule. */
+static int gamma_do_dma(drm_device_t *dev, int locked)
+{
+	unsigned long	 address;
+	unsigned long	 length;
+	drm_buf_t	 *buf;
+	int		 retcode = 0;
+	drm_device_dma_t *dma = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 dma_start, dma_stop;
+#endif
+
+	if (test_and_set_bit(0, &dev->dma_flag)) {
+		atomic_inc(&dma->total_missed_dma);
+		return -EBUSY;
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	dma_start = get_cycles();
+#endif
+
+	if (!dma->next_buffer) {
+		DRM_ERROR("No next_buffer\n");
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	buf	= dma->next_buffer;
+	address = (unsigned long)buf->address;
+	length	= buf->used;
+
+	DRM_DEBUG("context %d, buffer %d (%ld bytes)\n",
+		  buf->context, buf->idx, length);
+
+	if (buf->list == DRM_LIST_RECLAIM) {
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	if (!length) {
+		DRM_ERROR("0 length buffer\n");
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return 0;
+	}
+	
+	if (!gamma_dma_is_ready(dev)) {
+		clear_bit(0, &dev->dma_flag);
+		return -EBUSY;
+	}
+
+	if (buf->while_locked) {
+		if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+			DRM_ERROR("Dispatching buffer %d from pid %d"
+				  " \"while locked\", but no lock held\n",
+				  buf->idx, buf->pid);
+		}
+	} else {
+		if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock,
+					      DRM_KERNEL_CONTEXT)) {
+			atomic_inc(&dma->total_missed_lock);
+			clear_bit(0, &dev->dma_flag);
+			return -EBUSY;
+		}
+	}
+
+	if (dev->last_context != buf->context
+	    && !(dev->queuelist[buf->context]->flags
+		 & _DRM_CONTEXT_PRESERVED)) {
+				/* PRE: dev->last_context != buf->context */
+		if (drm_context_switch(dev, dev->last_context, buf->context)) {
+			drm_clear_next_buffer(dev);
+			drm_free_buffer(dev, buf);
+		}
+		retcode = -EBUSY;
+		goto cleanup;
+			
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+	}
+
+	drm_clear_next_buffer(dev);
+	buf->pending	 = 1;
+	buf->waiting	 = 0;
+	buf->list	 = DRM_LIST_PEND;
+#if DRM_DMA_HISTOGRAM
+	buf->time_dispatched = get_cycles();
+#endif
+
+	gamma_dma_dispatch(dev, address, length);
+	drm_free_buffer(dev, dma->this_buffer);
+	dma->this_buffer = buf;
+
+	atomic_add(length, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+
+	if (!buf->while_locked && !dev->context_flag && !locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+cleanup:
+
+	clear_bit(0, &dev->dma_flag);
+
+#if DRM_DMA_HISTOGRAM
+	dma_stop = get_cycles();
+	atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]);
+#endif
+
+	return retcode;
+}
+
+static void gamma_dma_schedule_timer_wrapper(unsigned long dev)
+{
+	gamma_dma_schedule((drm_device_t *)dev, 0);
+}
+
+static void gamma_dma_schedule_tq_wrapper(void *dev)
+{
+	gamma_dma_schedule(dev, 0);
+}
+
+int gamma_dma_schedule(drm_device_t *dev, int locked)
+{
+	int		 next;
+	drm_queue_t	 *q;
+	drm_buf_t	 *buf;
+	int		 retcode   = 0;
+	int		 processed = 0;
+	int		 missed;
+	int		 expire	   = 20;
+	drm_device_dma_t *dma	   = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 schedule_start;
+#endif
+
+	if (test_and_set_bit(0, &dev->interrupt_flag)) {
+				/* Not reentrant */
+		atomic_inc(&dma->total_missed_sched);
+		return -EBUSY;
+	}
+	missed = atomic_read(&dma->total_missed_sched);
+
+#if DRM_DMA_HISTOGRAM
+	schedule_start = get_cycles();
+#endif
+
+again:
+	if (dev->context_flag) {
+		clear_bit(0, &dev->interrupt_flag);
+		return -EBUSY;
+	}
+	if (dma->next_buffer) {
+				/* Unsent buffer that was previously
+				   selected, but that couldn't be sent
+				   because the lock could not be obtained
+				   or the DMA engine wasn't ready.  Try
+				   again. */
+		atomic_inc(&dma->total_tried);
+		if (!(retcode = gamma_do_dma(dev, locked))) {
+			atomic_inc(&dma->total_hit);
+			++processed;
+		}
+	} else {
+		do {
+			next = drm_select_queue(dev,
+					     gamma_dma_schedule_timer_wrapper);
+			if (next >= 0) {
+				q   = dev->queuelist[next];
+				buf = drm_waitlist_get(&q->waitlist);
+				dma->next_buffer = buf;
+				dma->next_queue	 = q;
+				if (buf && buf->list == DRM_LIST_RECLAIM) {
+					drm_clear_next_buffer(dev);
+					drm_free_buffer(dev, buf);
+				}
+			}
+		} while (next >= 0 && !dma->next_buffer);
+		if (dma->next_buffer) {
+			if (!(retcode = gamma_do_dma(dev, locked))) {
+				++processed;
+			}
+		}
+	}
+
+	if (--expire) {
+		if (missed != atomic_read(&dma->total_missed_sched)) {
+			atomic_inc(&dma->total_lost);
+			if (gamma_dma_is_ready(dev)) goto again;
+		}
+		if (processed && gamma_dma_is_ready(dev)) {
+			atomic_inc(&dma->total_lost);
+			processed = 0;
+			goto again;
+		}
+	}
+	
+	clear_bit(0, &dev->interrupt_flag);
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles()
+							   - schedule_start)]);
+#endif
+	return retcode;
+}
+
+static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d)
+{
+	unsigned long	  address;
+	unsigned long	  length;
+	int		  must_free = 0;
+	int		  retcode   = 0;
+	int		  i;
+	int		  idx;
+	drm_buf_t	  *buf;
+	drm_buf_t	  *last_buf = NULL;
+	drm_device_dma_t  *dma	    = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+				/* Turn off interrupt handling */
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) return -EINTR;
+	}
+	if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) {
+		while (!drm_lock_take(&dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT)) {
+			schedule();
+			if (signal_pending(current)) {
+				clear_bit(0, &dev->interrupt_flag);
+				return -EINTR;
+			}
+		}
+		++must_free;
+	}
+	atomic_inc(&dma->total_prio);
+
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			continue;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			DRM_ERROR("Process %d using %d's buffer on list %d\n",
+				  current->pid, buf->pid, buf->list);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+				/* This isn't a race condition on
+				   buf->list, since our concern is the
+				   buffer reclaim during the time the
+				   process closes the /dev/drm? handle, so
+				   it can't also be doing DMA. */
+		buf->list	  = DRM_LIST_PRIO;
+		buf->used	  = d->send_sizes[i];
+		buf->context	  = d->context;
+		buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED;
+		address		  = (unsigned long)buf->address;
+		length		  = buf->used;
+		if (!length) {
+			DRM_ERROR("0 length buffer\n");
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->waiting) {
+			DRM_ERROR("Sending waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		buf->pending = 1;
+		
+		if (dev->last_context != buf->context
+		    && !(dev->queuelist[buf->context]->flags
+			 & _DRM_CONTEXT_PRESERVED)) {
+			add_wait_queue(&dev->context_wait, &entry);
+			current->state = TASK_INTERRUPTIBLE;
+				/* PRE: dev->last_context != buf->context */
+			drm_context_switch(dev, dev->last_context,
+					   buf->context);
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+			schedule();
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&dev->context_wait, &entry);
+			if (signal_pending(current)) {
+				retcode = -EINTR;
+				goto cleanup;
+			}
+			if (dev->last_context != buf->context) {
+				DRM_ERROR("Context mismatch: %d %d\n",
+					  dev->last_context,
+					  buf->context);
+			}
+		}
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = get_cycles();
+		buf->time_dispatched = buf->time_queued;
+#endif
+		gamma_dma_dispatch(dev, address, length);
+		atomic_add(length, &dma->total_bytes);
+		atomic_inc(&dma->total_dmas);
+		
+		if (last_buf) {
+			drm_free_buffer(dev, last_buf);
+		}
+		last_buf = buf;
+	}
+
+
+cleanup:
+	if (last_buf) {
+		gamma_dma_ready(dev);
+		drm_free_buffer(dev, last_buf);
+	}
+	
+	if (must_free && !dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	return retcode;
+}
+
+static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	drm_buf_t	  *last_buf = NULL;
+	int		  retcode   = 0;
+	drm_device_dma_t  *dma	    = dev->dma;
+
+	if (d->flags & _DRM_DMA_BLOCK) {
+		last_buf = dma->buflist[d->send_indices[d->send_count-1]];
+		add_wait_queue(&last_buf->dma_wait, &entry);
+	}
+	
+	if ((retcode = drm_dma_enqueue(dev, d))) {
+		if (d->flags & _DRM_DMA_BLOCK)
+			remove_wait_queue(&last_buf->dma_wait, &entry);
+		return retcode;
+	}
+	
+	gamma_dma_schedule(dev, 0);
+	
+	if (d->flags & _DRM_DMA_BLOCK) {
+		DRM_DEBUG("%d waiting\n", current->pid);
+		current->state = TASK_INTERRUPTIBLE;
+		for (;;) {
+			if (!last_buf->waiting
+			    && !last_buf->pending)
+				break; /* finished */
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		DRM_DEBUG("%d running\n", current->pid);
+		remove_wait_queue(&last_buf->dma_wait, &entry);
+		if (!retcode
+		    || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) {
+			if (!waitqueue_active(&last_buf->dma_wait)) {
+				drm_free_buffer(dev, last_buf);
+			}
+		}
+		if (retcode) {
+			DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n",
+				  d->context,
+				  last_buf->waiting,
+				  last_buf->pending,
+				  DRM_WAITCOUNT(dev, d->context),
+				  last_buf->idx,
+				  last_buf->list,
+				  last_buf->pid,
+				  current->pid);
+		}
+	}
+	return retcode;
+}
+
+int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_device_dma_t  *dma	    = dev->dma;
+	int		  retcode   = 0;
+	drm_dma_t	  d;
+
+	copy_from_user_ret(&d, (drm_dma_t *)arg, sizeof(d), -EFAULT);
+	DRM_DEBUG("%d %d: %d send, %d req\n",
+		  current->pid, d.context, d.send_count, d.request_count);
+
+	if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) {
+		DRM_ERROR("Process %d using context %d\n",
+			  current->pid, d.context);
+		return -EINVAL;
+	}
+	if (d.send_count < 0 || d.send_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n",
+			  current->pid, d.send_count, dma->buf_count);
+		return -EINVAL;
+	}
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+			  current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	if (d.send_count) {
+		if (d.flags & _DRM_DMA_PRIORITY)
+			retcode = gamma_dma_priority(dev, &d);
+		else 
+			retcode = gamma_dma_send_buffers(dev, &d);
+	}
+
+	d.granted_count = 0;
+
+	if (!retcode && d.request_count) {
+		retcode = drm_dma_get_buffers(dev, &d);
+	}
+
+	DRM_DEBUG("%d returning, granted = %d\n",
+		  current->pid, d.granted_count);
+	copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT);
+
+	return retcode;
+}
+
+int gamma_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	dev->tq.next	      = NULL;
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = gamma_dma_schedule_tq_wrapper;
+	dev->tq.data	      = dev;
+
+
+				/* Before installing handler */
+	GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0);
+	GAMMA_WRITE(GAMMA_GDMACONTROL, 0);
+	
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   gamma_dma_service,
+				   0,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+
+				/* After installing handler */
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0x2001);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	   0x39090);
+	
+	return 0;
+}
+
+int gamma_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+	
+	DRM_DEBUG("%d\n", irq);
+	
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	    0);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0);
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0);
+	free_irq(irq, dev);
+
+	return 0;
+}
+
+
+int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+	
+	copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = gamma_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = gamma_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+	drm_queue_t	  *q;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  start;
+
+	dev->lck_start = start = get_cycles();
+#endif
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0 || lock.context >= dev->queue_count)
+		return -EINVAL;
+	q = dev->queuelist[lock.context];
+	
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+
+	if (!ret) {
+		if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+		    != lock.context) {
+			long j = jiffies - dev->lock.lock_time;
+
+			if (j > 0 && j <= DRM_LOCK_SLICE) {
+				/* Can't take lock if we just had it and
+				   there is contention. */
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout(j);
+			}
+		}
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				atomic_inc(&q->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+
+	drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */
+	
+	if (!ret) {
+		if (lock.flags & _DRM_LOCK_READY)
+			gamma_dma_ready(dev);
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+			if (gamma_found() == 1) {
+				gamma_dma_quiescent_single(dev);
+			} else {
+				gamma_dma_quiescent_dual(dev);
+			}
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+	
+	return ret;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/gamma_drv.c linux.18p17/drivers/char/drm/gamma_drv.c
--- linux.vanilla/drivers/char/drm/gamma_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/gamma_drv.c	Sat Sep 30 22:50:20 2000
@@ -0,0 +1,575 @@
+/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#include <linux/agp_backend.h>
+
+static void __attribute__((unused)) unused(void)
+{
+	agp_enable(0);
+}
+
+#ifndef PCI_DEVICE_ID_3DLABS_GAMMA
+#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_3DLABS_MX
+#define PCI_DEVICE_ID_3DLABS_MX 0x0006
+#endif
+
+#define GAMMA_NAME	 "gamma"
+#define GAMMA_DESC	 "3dlabs GMX 2000"
+#define GAMMA_DATE	 "20000719"
+#define GAMMA_MAJOR	 1
+#define GAMMA_MINOR	 0
+#define GAMMA_PATCHLEVEL 0
+
+static drm_device_t	      gamma_device;
+
+static struct file_operations gamma_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:   THIS_MODULE,
+#endif
+	open:	 gamma_open,
+	flush:	 drm_flush,
+	release: gamma_release,
+	ioctl:	 gamma_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      gamma_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  GAMMA_NAME,
+	fops:  &gamma_fops,
+};
+
+static drm_ioctl_desc_t	      gamma_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { gamma_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]    = { gamma_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]   = { drm_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]  = { drm_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]  = { drm_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]   = { drm_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]  = { drm_freebufs,	  1, 0 },
+	
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { drm_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { drm_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { drm_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { drm_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { drm_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { drm_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	     = { gamma_dma,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { gamma_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { gamma_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls)
+
+#ifdef MODULE
+static char		      *gamma = NULL;
+#endif
+static int 		      devices = 0;
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("3dlabs GMX 2000");
+MODULE_PARM(gamma, "s");
+MODULE_PARM(devices, "i");
+MODULE_PARM_DESC(devices,
+		 "devices=x, where x is the number of MX chips on card\n");
+#ifndef MODULE
+/* gamma_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_options.
+ */
+ 
+
+static int __init gamma_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("gamma=", gamma_options);
+#endif
+
+static int gamma_setup(drm_device_t *dev)
+{
+	int i;
+	
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+			
+	DRM_DEBUG("\n");
+			
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+			
+	return 0;
+}
+
+
+static int gamma_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) gamma_irq_uninstall(dev);
+	
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+	
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int gamma_found(void)
+{
+	return devices;
+}
+
+int gamma_find_devices(void)
+{
+	struct pci_dev *d = NULL, *one = NULL, *two = NULL;
+
+	d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d);
+	if (!d) return 0;
+
+	one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d);
+	if (!one) return 0;
+
+	/* Make sure it's on the same card, if not - no MX's found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0;
+
+	two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one);
+	if (!two) return 1;
+
+	/* Make sure it's on the same card, if not - only 1 MX found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1;
+
+	/* Two MX's found - we don't currently support more than 2 */
+	return 2;
+}
+
+/* gamma_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int gamma_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+	
+#ifdef MODULE
+	drm_parse_options(gamma);
+#endif
+	devices = gamma_find_devices();
+	if (devices == 0) return -1;
+
+	if ((retcode = misc_register(&gamma_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor);
+	dev->name   = GAMMA_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n",
+		 GAMMA_NAME,
+		 GAMMA_MAJOR,
+		 GAMMA_MINOR,
+		 GAMMA_PATCHLEVEL,
+		 GAMMA_DATE,
+		 gamma_misc.minor,
+		 devices);
+	
+	return 0;
+}
+
+/* gamma_cleanup is called via cleanup_module at module unload time. */
+
+static void gamma_cleanup(void)
+{
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	if (misc_deregister(&gamma_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	gamma_takedown(dev);
+}
+
+module_init(gamma_init);
+module_exit(gamma_cleanup);
+
+
+int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	copy_from_user_ret(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version),
+			   -EFAULT);
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		copy_to_user_ret(name, value, len, -EFAULT); \
+	}
+
+	version.version_major	   = GAMMA_MAJOR;
+	version.version_minor	   = GAMMA_MINOR;
+	version.version_patchlevel = GAMMA_PATCHLEVEL;
+
+	DRM_COPY(version.name, GAMMA_NAME);
+	DRM_COPY(version.date, GAMMA_DATE);
+	DRM_COPY(version.desc, GAMMA_DESC);
+
+	copy_to_user_ret((drm_version_t *)arg,
+			 &version,
+			 sizeof(version),
+			 -EFAULT);
+	return 0;
+}
+
+int gamma_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &gamma_device;
+	int	      retcode = 0;
+	
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return gamma_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int gamma_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return gamma_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= GAMMA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &gamma_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+
+int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+	
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	gamma_dma_schedule(dev, 1);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+	
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/gamma_drv.h linux.18p17/drivers/char/drm/gamma_drv.h
--- linux.vanilla/drivers/char/drm/gamma_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/gamma_drv.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,58 @@
+/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _GAMMA_DRV_H_
+#define _GAMMA_DRV_H_
+
+				/* gamma_drv.c */
+extern int  gamma_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_open(struct inode *inode, struct file *filp);
+extern int  gamma_release(struct inode *inode, struct file *filp);
+extern int  gamma_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  gamma_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  gamma_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* gamma_dma.c */
+extern int  gamma_dma_schedule(drm_device_t *dev, int locked);
+extern int  gamma_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  gamma_irq_install(drm_device_t *dev, int irq);
+extern int  gamma_irq_uninstall(drm_device_t *dev);
+extern int  gamma_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_find_devices(void);
+extern int  gamma_found(void);
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_bufs.c linux.18p17/drivers/char/drm/i810_bufs.c
--- linux.vanilla/drivers/char/drm/i810_bufs.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_bufs.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,334 @@
+/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include "linux/un.h"
+
+int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+   
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+		buf->offset = offset;
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t), 
+					     DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_i810_buf_priv_t);
+	   	memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t));
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+      
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	copy_to_user_ret((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+   
+	atomic_dec(&dev->buf_alloc);
+	dma->flags = _DRM_DMA_USE_AGP;
+	return 0;
+}
+
+int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return i810_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return -EINVAL;
+}
+
+int i810_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				copy_to_user_ret(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark),
+						 -EFAULT);
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	copy_to_user_ret((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+	
+	return 0;
+}
+
+int i810_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int i810_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		copy_from_user_ret(&idx,
+				   &request.list[i],
+				   sizeof(idx),
+				   -EFAULT);
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_context.c linux.18p17/drivers/char/drm/i810_context.c
--- linux.vanilla/drivers/char/drm/i810_context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_context.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,203 @@
+/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+
+static int i810_alloc_queue(drm_device_t *dev)
+{
+   	int temp = drm_ctxbitmap_next(dev);
+   	DRM_DEBUG("i810_alloc_queue: %d\n", temp);
+	return temp;
+}
+
+int i810_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                i810_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int i810_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+int i810_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = i810_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the i810 */
+	return 0;
+}
+
+int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int i810_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return i810_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int i810_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	i810_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int i810_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+   	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_dma.c linux.18p17/drivers/char/drm/i810_dma.c
--- linux.vanilla/drivers/char/drm/i810_dma.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_dma.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,1417 @@
+/* i810_dma.c -- DMA support for the i810 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include <linux/interrupt.h>	/* For task queue support */
+
+/* in case we don't have a 2.3.99-pre6 kernel or later: */
+#ifndef VM_DONTCOPY
+#define VM_DONTCOPY 0
+#endif
+
+#define I810_BUF_FREE		2
+#define I810_BUF_CLIENT		1
+#define I810_BUF_HARDWARE      	0
+
+#define I810_BUF_UNMAPPED 0
+#define I810_BUF_MAPPED   1
+
+#define I810_REG(reg)		2
+#define I810_BASE(reg)		((unsigned long) \
+				dev->maplist[I810_REG(reg)]->handle)
+#define I810_ADDR(reg)		(I810_BASE(reg) + reg)
+#define I810_DEREF(reg)		*(__volatile__ int *)I810_ADDR(reg)
+#define I810_READ(reg)		I810_DEREF(reg)
+#define I810_WRITE(reg,val) 	do { I810_DEREF(reg) = val; } while (0)
+#define I810_DEREF16(reg)	*(__volatile__ u16 *)I810_ADDR(reg)
+#define I810_READ16(reg)	I810_DEREF16(reg)
+#define I810_WRITE16(reg,val)	do { I810_DEREF16(reg) = val; } while (0)
+
+#define RING_LOCALS	unsigned int outring, ringmask; volatile char *virt;
+
+#define BEGIN_LP_RING(n) do {				\
+	if (I810_VERBOSE)				\
+		DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n",	\
+			  n, __FUNCTION__);		\
+	if (dev_priv->ring.space < n*4) 		\
+		i810_wait_ring(dev, n*4);		\
+	dev_priv->ring.space -= n*4;			\
+	outring = dev_priv->ring.tail;			\
+	ringmask = dev_priv->ring.tail_mask;		\
+	virt = dev_priv->ring.virtual_start;		\
+} while (0)
+
+#define ADVANCE_LP_RING() do {					\
+	if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n");	\
+	dev_priv->ring.tail = outring;				\
+	I810_WRITE(LP_RING + RING_TAIL, outring);		\
+} while(0)
+
+#define OUT_RING(n) do {						\
+	if (I810_VERBOSE) DRM_DEBUG("   OUT_RING %x\n", (int)(n));	\
+	*(volatile unsigned int *)(virt + outring) = n;			\
+	outring += 4;							\
+	outring &= ringmask;						\
+} while (0);
+
+static inline void i810_print_status_page(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+	u32 *temp = (u32 *)dev_priv->hw_status_page;
+   	int i;
+
+   	DRM_DEBUG(  "hw_status: Interrupt Status : %x\n", temp[0]);
+   	DRM_DEBUG(  "hw_status: LpRing Head ptr : %x\n", temp[1]);
+   	DRM_DEBUG(  "hw_status: IRing Head ptr : %x\n", temp[2]);
+      	DRM_DEBUG(  "hw_status: Reserved : %x\n", temp[3]);
+   	DRM_DEBUG(  "hw_status: Driver Counter : %d\n", temp[5]);
+   	for(i = 6; i < dma->buf_count + 6; i++) {
+	   	DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]);
+	}
+}
+
+static drm_buf_t *i810_freelist_get(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+   	int 		 used;
+   
+	/* Linear search might not be the best solution */
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+		/* In use is already a pointer */
+	   	used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, 
+			       I810_BUF_CLIENT);
+	   	if(used == I810_BUF_FREE) {
+			return buf;
+		}
+	}
+   	return NULL;
+}
+
+/* This should only be called if the buffer is not sent to the hardware
+ * yet, the hardware updates in use for us once its on the ring buffer.
+ */
+
+static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	int used;
+   
+   	/* In use is already a pointer */
+   	used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE);
+   	if(used != I810_BUF_CLIENT) {
+	   	DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx);
+	   	return -EINVAL;
+	}
+   
+   	return 0;
+}
+
+static struct file_operations i810_buffer_fops = {
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 i810_mmap_buffers,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	    *priv	  = filp->private_data;
+	drm_device_t	    *dev;
+	drm_i810_private_t  *dev_priv;
+	drm_buf_t           *buf;
+	drm_i810_buf_priv_t *buf_priv;
+
+	lock_kernel();
+	dev	 = priv->dev;
+	dev_priv = dev->dev_private;
+	buf      = dev_priv->mmap_buffer;
+	buf_priv = buf->dev_private;
+   
+	vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+	vma->vm_file = filp;
+   
+   	buf_priv->currently_mapped = I810_BUF_MAPPED;
+	unlock_kernel();
+
+	if (remap_page_range(vma->vm_start,
+			     VM_OFFSET(vma),
+			     vma->vm_end - vma->vm_start,
+			     vma->vm_page_prot)) return -EAGAIN;
+	return 0;
+}
+
+static int i810_map_buffer(drm_buf_t *buf, struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	struct file_operations *old_fops;
+	int retcode = 0;
+
+	if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL;
+
+	if(VM_DONTCOPY != 0) {
+		down(&current->mm->mmap_sem);
+		old_fops = filp->f_op;
+		filp->f_op = &i810_buffer_fops;
+		dev_priv->mmap_buffer = buf;
+		buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, 
+						    PROT_READ|PROT_WRITE,
+						    MAP_SHARED, 
+						    buf->bus_address);
+		dev_priv->mmap_buffer = NULL;
+   		filp->f_op = old_fops;
+		if ((unsigned long)buf_priv->virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed int)buf_priv->virtual;
+			buf_priv->virtual = 0;
+		}
+   		up(&current->mm->mmap_sem);
+	} else {
+		buf_priv->virtual = buf_priv->kernel_virtual;
+   		buf_priv->currently_mapped = I810_BUF_MAPPED;
+	}
+	return retcode;
+}
+
+static int i810_unmap_buffer(drm_buf_t *buf)
+{
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	int retcode = 0;
+
+	if(VM_DONTCOPY != 0) {
+		if(buf_priv->currently_mapped != I810_BUF_MAPPED) 
+			return -EINVAL;
+		down(&current->mm->mmap_sem);
+#if LINUX_VERSION_CODE < 0x020399
+        	retcode = do_munmap((unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#else
+        	retcode = do_munmap(current->mm, 
+				    (unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#endif
+   		up(&current->mm->mmap_sem);
+	}
+   	buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+   	buf_priv->virtual = 0;
+
+	return retcode;
+}
+
+static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, 
+			       struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_buf_t	  *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	int retcode = 0;
+
+	buf = i810_freelist_get(dev);
+	if (!buf) {
+		retcode = -ENOMEM;
+	   	DRM_DEBUG("%s retcode %d\n", __FUNCTION__, retcode);
+		goto out_get_buf;
+	}
+   
+	retcode = i810_map_buffer(buf, filp);
+	if(retcode) {
+		i810_freelist_put(dev, buf);
+	   	DRM_DEBUG("mapbuf failed in %s retcode %d\n", 
+			  __FUNCTION__, retcode);
+	   	goto out_get_buf;
+	}
+	buf->pid     = priv->pid;
+	buf_priv = buf->dev_private;	
+	d->granted = 1;
+   	d->request_idx = buf->idx;
+   	d->request_size = buf->total;
+   	d->virtual = buf_priv->virtual;
+
+out_get_buf:
+	return retcode;
+}
+
+static unsigned long i810_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+   
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) 
+		return 0;
+	
+	atomic_inc(&mem_map[MAP_NR((void *) address)].count);
+	set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags);
+   
+	return address;
+}
+
+static void i810_free_page(drm_device_t *dev, unsigned long page)
+{
+	if(page == 0UL) 
+		return;
+	
+	atomic_dec(&mem_map[MAP_NR((void *) page)].count);
+	clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags);
+	wake_up(&mem_map[MAP_NR((void *) page)].wait);
+	free_page(page);
+	return;
+}
+
+static int i810_dma_cleanup(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if(dev->dev_private) {
+		int i;
+	   	drm_i810_private_t *dev_priv = 
+	     		(drm_i810_private_t *) dev->dev_private;
+	   
+	   	if(dev_priv->ring.virtual_start) {
+		   	drm_ioremapfree((void *) dev_priv->ring.virtual_start,
+					dev_priv->ring.Size);
+		}
+	   	if(dev_priv->hw_status_page != 0UL) {
+		   	i810_free_page(dev, dev_priv->hw_status_page);
+		   	/* Need to rewrite hardware status page */
+		   	I810_WRITE(0x02080, 0x1ffff000);
+		}
+	   	drm_free(dev->dev_private, sizeof(drm_i810_private_t), 
+			 DRM_MEM_DRIVER);
+	   	dev->dev_private = NULL;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			drm_buf_t *buf = dma->buflist[ i ];
+			drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+			drm_ioremapfree(buf_priv->kernel_virtual, buf->total);
+		}
+	}
+   	return 0;
+}
+
+static int i810_wait_ring(drm_device_t *dev, int n)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+   	int iters = 0;
+   	unsigned long end;
+	unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+
+	end = jiffies + (HZ*3);
+   	while (ring->space < n) {
+	   	int i;
+	
+	   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+	   	ring->space = ring->head - (ring->tail+8);
+		if (ring->space < 0) ring->space += ring->Size;
+	   
+		if (ring->head != last_head)
+		   end = jiffies + (HZ*3);
+	  
+	   	iters++;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("space: %d wanted %d\n", ring->space, n);
+		   	DRM_ERROR("lockup\n");
+		   	goto out_wait_ring;
+		}
+
+	   	for (i = 0 ; i < 2000 ; i++) ;
+	}
+
+out_wait_ring:   
+   	return iters;
+}
+
+static void i810_kernel_lost_context(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+      
+   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+     	ring->tail = I810_READ(LP_RING + RING_TAIL);
+     	ring->space = ring->head - (ring->tail+8);
+     	if (ring->space < 0) ring->space += ring->Size;
+}
+
+static int i810_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	int my_idx = 24;
+   	u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx);
+   	int i;
+   
+   	if(dma->buf_count > 1019) {
+	   	/* Not enough space in the status page for the freelist */
+	   	return -EINVAL;
+	}
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+	   	buf_priv->in_use = hw_status++;
+	   	buf_priv->my_use_idx = my_idx;
+	   	my_idx += 4;
+
+	   	*buf_priv->in_use = I810_BUF_FREE;
+
+		buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, 
+						       buf->total);
+	}
+	return 0;
+}
+
+static int i810_dma_initialize(drm_device_t *dev, 
+			       drm_i810_private_t *dev_priv,
+			       drm_i810_init_t *init)
+{
+	drm_map_t *sarea_map;
+
+   	dev->dev_private = (void *) dev_priv;
+   	memset(dev_priv, 0, sizeof(drm_i810_private_t));
+
+   	if (init->ring_map_idx >= dev->map_count ||
+	    init->buffer_map_idx >= dev->map_count) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("ring_map or buffer_map are invalid\n");
+	   	return -EINVAL;
+	}
+   
+   	dev_priv->ring_map_idx = init->ring_map_idx;
+   	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_i810_sarea_t *) 
+		((u8 *)sarea_map->handle + 
+		 init->sarea_priv_offset);
+
+   	atomic_set(&dev_priv->flush_done, 0);
+	init_waitqueue_head(&dev_priv->flush_queue);
+   	
+   	dev_priv->ring.Start = init->ring_start;
+   	dev_priv->ring.End = init->ring_end;
+   	dev_priv->ring.Size = init->ring_size;
+
+   	dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + 
+						   init->ring_start, 
+						   init->ring_size);
+
+   	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+   
+   	if (dev_priv->ring.virtual_start == NULL) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("can not ioremap virtual address for"
+			  " ring buffer\n");
+	   	return -ENOMEM;
+	}
+
+	dev_priv->w = init->w;
+	dev_priv->h = init->h;
+	dev_priv->pitch = init->pitch;
+	dev_priv->back_offset = init->back_offset;
+	dev_priv->depth_offset = init->depth_offset;
+
+	dev_priv->front_di1 = init->front_offset | init->pitch_bits;
+	dev_priv->back_di1 = init->back_offset | init->pitch_bits;
+	dev_priv->zi1 = init->depth_offset | init->pitch_bits;
+	
+   
+   	/* Program Hardware Status Page */
+   	dev_priv->hw_status_page = i810_alloc_page(dev);
+   	memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE);
+   	if(dev_priv->hw_status_page == 0UL) {
+		i810_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate hardware status page\n");
+		return -ENOMEM;
+	}
+   	DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page);
+   
+   	I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page));
+   	DRM_DEBUG("Enabled hardware status page\n");
+   
+   	/* Now we need to init our freelist */
+   	if(i810_freelist_init(dev) != 0) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("Not enough space in the status page for"
+			  " the freelist\n");
+	   	return -ENOMEM;
+	}
+   	return 0;
+}
+
+int i810_dma_init(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t *priv = filp->private_data;
+   	drm_device_t *dev = priv->dev;
+   	drm_i810_private_t *dev_priv;
+   	drm_i810_init_t init;
+   	int retcode = 0;
+	
+   	copy_from_user_ret(&init, (drm_i810_init_t *)arg, 
+			   sizeof(init), -EFAULT);
+	
+   	switch(init.func) {
+	 	case I810_INIT_DMA:
+	   		dev_priv = drm_alloc(sizeof(drm_i810_private_t), 
+					     DRM_MEM_DRIVER);
+	   		if(dev_priv == NULL) return -ENOMEM;
+	   		retcode = i810_dma_initialize(dev, dev_priv, &init);
+	   	break;
+	 	case I810_CLEANUP_DMA:
+	   		retcode = i810_dma_cleanup(dev);
+	   	break;
+	 	default:
+	   		retcode = -EINVAL;
+	   	break;
+	}
+   
+   	return retcode;
+}
+
+
+
+/* Most efficient way to verify state for the i810 is as it is
+ * emitted.  Non-conformant state is silently dropped.
+ *
+ * Use 'volatile' & local var tmp to force the emitted values to be
+ * identical to the verified ones.
+ */
+static void i810EmitContextVerified( drm_device_t *dev, 
+				     volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_CTX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_COLOR_FACTOR );
+	OUT_RING( code[I810_CTXREG_CF1] );
+
+	OUT_RING( GFX_OP_STIPPLE );
+	OUT_RING( code[I810_CTXREG_ST1] );
+
+	for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		} 
+	}
+
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+static void i810EmitTexVerified( drm_device_t *dev, 
+				 volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_TEX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_MAP_INFO );
+	OUT_RING( code[I810_TEXREG_MI1] );
+	OUT_RING( code[I810_TEXREG_MI2] );
+	OUT_RING( code[I810_TEXREG_MI3] );
+
+	for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		}
+	} 
+		
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+
+/* Need to do some additional checking when setting the dest buffer.
+ */
+static void i810EmitDestVerified( drm_device_t *dev, 
+				  volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 );
+
+	tmp = code[I810_DESTREG_DI1];
+	if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) {
+		OUT_RING( CMD_OP_DESTBUFFER_INFO );
+		OUT_RING( tmp );
+	} else
+	   DRM_DEBUG("bad di1 %x (allow %x or %x)\n",
+		     tmp, dev_priv->front_di1, dev_priv->back_di1);
+
+	/* invarient:
+	 */
+	OUT_RING( CMD_OP_Z_BUFFER_INFO );
+	OUT_RING( dev_priv->zi1 );
+
+	OUT_RING( GFX_OP_DESTBUFFER_VARS );
+	OUT_RING( code[I810_DESTREG_DV1] );
+
+	OUT_RING( GFX_OP_DRAWRECT_INFO );
+	OUT_RING( code[I810_DESTREG_DR1] );
+	OUT_RING( code[I810_DESTREG_DR2] );
+	OUT_RING( code[I810_DESTREG_DR3] );
+	OUT_RING( code[I810_DESTREG_DR4] );
+	OUT_RING( 0 );
+
+	ADVANCE_LP_RING();
+}
+
+
+
+static void i810EmitState( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (dirty & I810_UPLOAD_BUFFERS) {
+		i810EmitDestVerified( dev, sarea_priv->BufferState );
+		sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS;
+	}
+
+	if (dirty & I810_UPLOAD_CTX) {
+		i810EmitContextVerified( dev, sarea_priv->ContextState );
+		sarea_priv->dirty &= ~I810_UPLOAD_CTX;
+	}
+
+	if (dirty & I810_UPLOAD_TEX0) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[0] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX0;
+	}
+
+	if (dirty & I810_UPLOAD_TEX1) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[1] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX1;
+	}
+}
+
+
+
+/* need to verify 
+ */
+static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, 
+				     unsigned int clear_color,
+				     unsigned int clear_zval )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int i;
+	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox ; i++, pbox++) {
+		unsigned int x = pbox->x1;
+		unsigned int y = pbox->y1;
+		unsigned int width = (pbox->x2 - x) * cpp;
+		unsigned int height = pbox->y2 - y;
+		unsigned int start = y * pitch + x * cpp;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+
+	   	if ( flags & I810_FRONT ) {	    
+		   	DRM_DEBUG("clear front\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_BACK ) {
+			DRM_DEBUG("clear back\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->back_offset + start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_DEPTH ) {
+			DRM_DEBUG("clear depth\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->depth_offset + start );
+			OUT_RING( clear_zval );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+	}
+}
+
+static void i810_dma_dispatch_swap( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int ofs = dev_priv->back_offset;
+	int i;
+	RING_LOCALS;
+
+	DRM_DEBUG("swapbuffers\n");
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox; i++, pbox++) 
+	{
+		unsigned int w = pbox->x2 - pbox->x1;
+		unsigned int h = pbox->y2 - pbox->y1;
+		unsigned int dst = pbox->x1*cpp + pbox->y1*pitch;
+		unsigned int start = ofs + dst;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+ 
+	   	DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+			  pbox[i].x1, pbox[i].y1,
+			  pbox[i].x2, pbox[i].y2);
+
+		BEGIN_LP_RING( 6 );
+		OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 );
+		OUT_RING( pitch | (0xCC << 16));
+		OUT_RING( (h << 16) | (w * cpp));
+		OUT_RING( dst );
+		OUT_RING( pitch );	
+		OUT_RING( start );
+		ADVANCE_LP_RING();
+	}
+}
+
+
+static void i810_dma_dispatch_vertex(drm_device_t *dev, 
+				     drm_buf_t *buf,
+				     int discard,
+				     int used)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	drm_clip_rect_t *box = sarea_priv->boxes;
+   	int nbox = sarea_priv->nbox;
+	unsigned long address = (unsigned long)buf->bus_address;
+	unsigned long start = address - dev->agp->base;     
+	int i = 0, u;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	if (nbox > I810_NR_SAREA_CLIPRECTS) 
+		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	if (discard) {
+		u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+			    I810_BUF_HARDWARE);
+		if(u != I810_BUF_CLIENT) {
+			DRM_DEBUG("xxxx 2\n");
+		}
+	}
+
+	if (used > 4*1024) 
+		used = 0;
+
+	if (sarea_priv->dirty)
+	   i810EmitState( dev );
+
+  	DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", 
+		  address, used, nbox);
+
+   	dev_priv->counter++;
+   	DRM_DEBUG(  "dispatch counter : %ld\n", dev_priv->counter);
+   	DRM_DEBUG(  "i810_dma_dispatch\n");
+   	DRM_DEBUG(  "start : %lx\n", start);
+	DRM_DEBUG(  "used : %d\n", used);
+   	DRM_DEBUG(  "start + used - 4 : %ld\n", start + used - 4);
+
+	if (buf_priv->currently_mapped == I810_BUF_MAPPED) {
+		*(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE |
+					     sarea_priv->vertex_prim |
+					     ((used/4)-2));
+		
+		if (used & 4) {
+			*(u32 *)((u32)buf_priv->virtual + used) = 0;
+			used += 4;
+		}
+
+		i810_unmap_buffer(buf);
+	}
+		   
+	if (used) {
+		do {
+			if (i < nbox) {
+				BEGIN_LP_RING(4);
+				OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | 
+					  SC_ENABLE );
+				OUT_RING( GFX_OP_SCISSOR_INFO );
+				OUT_RING( box[i].x1 | (box[i].y1<<16) );
+				OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) );
+				ADVANCE_LP_RING();
+			}
+			
+			BEGIN_LP_RING(4);
+			OUT_RING( CMD_OP_BATCH_BUFFER );
+			OUT_RING( start | BB1_PROTECTED );
+			OUT_RING( start + used - 4 );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+			
+		} while (++i < nbox);
+	}
+
+	BEGIN_LP_RING(10);
+	OUT_RING( CMD_STORE_DWORD_IDX );
+	OUT_RING( 20 );
+	OUT_RING( dev_priv->counter );
+	OUT_RING( 0 );
+
+	if (discard) {
+		OUT_RING( CMD_STORE_DWORD_IDX );
+		OUT_RING( buf_priv->my_use_idx );
+		OUT_RING( I810_BUF_FREE );
+		OUT_RING( 0 );
+	}
+
+      	OUT_RING( CMD_REPORT_HEAD );
+	OUT_RING( 0 );
+   	ADVANCE_LP_RING();
+}
+
+
+/* Interrupts are only for flushing */
+static void i810_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+   	u16 temp;
+   
+	atomic_inc(&dev->total_irq);
+      	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+	else
+	   return;
+ 
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+}
+
+static void i810_dma_task_queue(void *device)
+{
+	drm_device_t *dev = (drm_device_t *) device;
+      	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+
+   	atomic_set(&dev_priv->flush_done, 1);
+   	wake_up_interruptible(&dev_priv->flush_queue);
+}
+
+int i810_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+	u16 temp;
+   
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+   	DRM_DEBUG(  "Interrupt Install : %d\n", irq);
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	dev->tq.next	      = NULL;
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = i810_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+   	temp = I810_READ16(I810REG_HWSTAM);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_HWSTAM, temp);
+   	
+      	temp = I810_READ16(I810REG_INT_MASK_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+      	I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */
+
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   i810_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	temp = temp | 0x0003;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp); /* Enable bp & user interrupts */
+	return 0;
+}
+
+int i810_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+   	u16 temp;
+
+
+/*  	return 0; */
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+
+   	DRM_DEBUG(  "Interrupt UnInstall: %d\n", irq);	
+	DRM_DEBUG("%d\n", irq);
+   
+   	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+   
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp);                     /* Disable all interrupts */
+
+   	free_irq(irq, dev);
+
+	return 0;
+}
+
+int i810_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+   
+   	DRM_DEBUG(  "i810_control\n");
+
+	copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = i810_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = i810_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline void i810_dma_emit_flush(drm_device_t *dev)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(2);
+      	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+      	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static inline void i810_dma_quiescent_emit(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(4);
+   	OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE );
+   	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( 0 );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+   	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static void i810_dma_quiescent(drm_device_t *dev)
+{
+      	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	unsigned long end;      
+
+   	if(dev_priv == NULL) {
+	   	return;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	current->state = TASK_INTERRUPTIBLE;
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   
+   	for (;;) {
+	      	i810_dma_quiescent_emit(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+   
+   	return;
+}
+
+static int i810_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	unsigned long end;
+   	int i, ret = 0;      
+
+   	if(dev_priv == NULL) {
+	   	return 0;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	current->state = TASK_INTERRUPTIBLE;
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   	for (;;) {
+	      	i810_dma_emit_flush(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	ret = -EINTR; /* Can't restart */
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, 
+				   I810_BUF_FREE);
+
+		if (used == I810_BUF_HARDWARE)
+			DRM_DEBUG("reclaimed from HARDWARE\n");
+		if (used == I810_BUF_CLIENT)
+			DRM_DEBUG("still on client HARDWARE\n");
+	}
+
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if (!dev->dev_private) return;
+	if (!dma->buflist) return;
+
+        i810_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		if (buf->pid == pid && buf_priv) {
+			int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+					   I810_BUF_FREE);
+
+			if (used == I810_BUF_CLIENT)
+				DRM_DEBUG("reclaimed from client\n");
+		   	if(buf_priv->currently_mapped == I810_BUF_MAPPED)
+		     		buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+		}
+	}
+}
+
+int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+   
+   	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0) {
+		return -EINVAL;
+	}
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			current->state = TASK_INTERRUPTIBLE;
+		   	DRM_DEBUG("Calling lock schedule\n");
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+	
+	if (!ret) {
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   DRM_DEBUG("fred\n");
+		   i810_dma_quiescent(dev);
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+	return ret;
+}
+
+int i810_flush_ioctl(struct inode *inode, struct file *filp, 
+		     unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t	  *priv	  = filp->private_data;
+   	drm_device_t	  *dev	  = priv->dev;
+   
+   	DRM_DEBUG("i810_flush_ioctl\n");
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_flush_ioctl called without lock held\n");
+		return -EINVAL;
+	}
+
+   	i810_flush_queue(dev);
+   	return 0;
+}
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_i810_vertex_t vertex;
+
+	copy_from_user_ret(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex),
+			   -EFAULT);
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n",
+		  vertex.idx, vertex.used, vertex.discard);
+
+	i810_dma_dispatch_vertex( dev, 
+				  dma->buflist[ vertex.idx ], 
+				  vertex.discard, vertex.used );
+
+   	atomic_add(vertex.used, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+	sarea_priv->last_enqueue = dev_priv->counter-1;
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+   
+	return 0;
+}
+
+
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_i810_clear_t clear;
+
+   	copy_from_user_ret(&clear, (drm_i810_clear_t *)arg, sizeof(clear), 
+			   -EFAULT);
+   
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_clear( dev, clear.flags, 
+				 clear.clear_color, 
+				 clear.clear_depth );
+   	return 0;
+}
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+   
+	DRM_DEBUG("i810_swap_bufs\n");
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_swap_buf called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_swap( dev );
+   	return 0;
+}
+
+int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+   	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+      	sarea_priv->last_dispatch = (int) hw_status[5];
+	return 0;
+}
+
+int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	int		  retcode   = 0;
+	drm_i810_dma_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+	DRM_DEBUG("getbuf\n");
+   	copy_from_user_ret(&d, (drm_i810_dma_t *)arg, sizeof(d), -EFAULT);
+   
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+	
+	d.granted = 0;
+
+	retcode = i810_dma_get_buffer(dev, &d, filp);
+
+	DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n",
+		  current->pid, retcode, d.granted);
+
+	copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT);   
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return retcode;
+}
+
+int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_i810_copy_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_buf_t *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	drm_device_dma_t *dma = dev->dma;
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+   
+   	copy_from_user_ret(&d, (drm_i810_copy_t *)arg, sizeof(d), -EFAULT);
+
+	if(d.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[ d.idx ];
+   	buf_priv = buf->dev_private;
+	if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM;
+
+   	copy_from_user_ret(buf_priv->virtual, d.address, d.used, -EFAULT);
+
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return 0;
+}
+
+int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	if(VM_DONTCOPY == 0) return 1;
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_drm.h linux.18p17/drivers/char/drm/i810_drm.h
--- linux.vanilla/drivers/char/drm/i810_drm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_drm.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,194 @@
+#ifndef _I810_DRM_H_
+#define _I810_DRM_H_
+
+/* WARNING: These defines must be the same as what the Xserver uses.
+ * if you change them, you must change the defines in the Xserver.
+ */
+
+#ifndef _I810_DEFINES_
+#define _I810_DEFINES_
+
+#define I810_DMA_BUF_ORDER		12
+#define I810_DMA_BUF_SZ 		(1<<I810_DMA_BUF_ORDER)
+#define I810_DMA_BUF_NR 		256
+#define I810_NR_SAREA_CLIPRECTS 	8
+
+/* Each region is a minimum of 64k, and there are at most 64 of them.
+ */
+#define I810_NR_TEX_REGIONS 64
+#define I810_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+#define I810_UPLOAD_TEX0IMAGE  0x1 /* handled clientside */
+#define I810_UPLOAD_TEX1IMAGE  0x2 /* handled clientside */
+#define I810_UPLOAD_CTX        0x4
+#define I810_UPLOAD_BUFFERS    0x8
+#define I810_UPLOAD_TEX0       0x10
+#define I810_UPLOAD_TEX1       0x20
+#define I810_UPLOAD_CLIPRECTS  0x40
+
+
+/* Indices into buf.Setup where various bits of state are mirrored per
+ * context and per buffer.  These can be fired at the card as a unit,
+ * or in a piecewise fashion as required.
+ */
+
+/* Destbuffer state 
+ *    - backbuffer linear offset and pitch -- invarient in the current dri
+ *    - zbuffer linear offset and pitch -- also invarient
+ *    - drawing origin in back and depth buffers.
+ *
+ * Keep the depth/back buffer state here to acommodate private buffers
+ * in the future.
+ */
+#define I810_DESTREG_DI0  0	/* CMD_OP_DESTBUFFER_INFO (2 dwords) */
+#define I810_DESTREG_DI1  1
+#define I810_DESTREG_DV0  2	/* GFX_OP_DESTBUFFER_VARS (2 dwords) */
+#define I810_DESTREG_DV1  3
+#define I810_DESTREG_DR0  4	/* GFX_OP_DRAWRECT_INFO (4 dwords) */
+#define I810_DESTREG_DR1  5
+#define I810_DESTREG_DR2  6
+#define I810_DESTREG_DR3  7
+#define I810_DESTREG_DR4  8
+#define I810_DEST_SETUP_SIZE 10
+
+/* Context state
+ */
+#define I810_CTXREG_CF0   0	/* GFX_OP_COLOR_FACTOR */
+#define I810_CTXREG_CF1   1	
+#define I810_CTXREG_ST0   2     /* GFX_OP_STIPPLE */
+#define I810_CTXREG_ST1   3
+#define I810_CTXREG_VF    4	/* GFX_OP_VERTEX_FMT */
+#define I810_CTXREG_MT    5	/* GFX_OP_MAP_TEXELS */
+#define I810_CTXREG_MC0   6	/* GFX_OP_MAP_COLOR_STAGES - stage 0 */
+#define I810_CTXREG_MC1   7     /* GFX_OP_MAP_COLOR_STAGES - stage 1 */
+#define I810_CTXREG_MC2   8	/* GFX_OP_MAP_COLOR_STAGES - stage 2 */
+#define I810_CTXREG_MA0   9	/* GFX_OP_MAP_ALPHA_STAGES - stage 0 */
+#define I810_CTXREG_MA1   10	/* GFX_OP_MAP_ALPHA_STAGES - stage 1 */
+#define I810_CTXREG_MA2   11	/* GFX_OP_MAP_ALPHA_STAGES - stage 2 */
+#define I810_CTXREG_SDM   12	/* GFX_OP_SRC_DEST_MONO */
+#define I810_CTXREG_FOG   13	/* GFX_OP_FOG_COLOR */
+#define I810_CTXREG_B1    14	/* GFX_OP_BOOL_1 */
+#define I810_CTXREG_B2    15	/* GFX_OP_BOOL_2 */
+#define I810_CTXREG_LCS   16	/* GFX_OP_LINEWIDTH_CULL_SHADE_MODE */
+#define I810_CTXREG_PV    17	/* GFX_OP_PV_RULE -- Invarient! */
+#define I810_CTXREG_ZA    18	/* GFX_OP_ZBIAS_ALPHAFUNC */
+#define I810_CTXREG_AA    19	/* GFX_OP_ANTIALIAS */
+#define I810_CTX_SETUP_SIZE 20 
+
+/* Texture state (per tex unit)
+ */
+#define I810_TEXREG_MI0  0	/* GFX_OP_MAP_INFO (4 dwords) */
+#define I810_TEXREG_MI1  1	
+#define I810_TEXREG_MI2  2	
+#define I810_TEXREG_MI3  3	
+#define I810_TEXREG_MF   4	/* GFX_OP_MAP_FILTER */
+#define I810_TEXREG_MLC  5	/* GFX_OP_MAP_LOD_CTL */
+#define I810_TEXREG_MLL  6	/* GFX_OP_MAP_LOD_LIMITS */
+#define I810_TEXREG_MCS  7	/* GFX_OP_MAP_COORD_SETS ??? */
+#define I810_TEX_SETUP_SIZE 8
+
+#define I810_FRONT   0x1
+#define I810_BACK    0x2
+#define I810_DEPTH   0x4
+
+
+typedef struct _drm_i810_init {
+	enum {
+		I810_INIT_DMA = 0x01,
+		I810_CLEANUP_DMA = 0x02
+	} func;
+	int ring_map_idx;
+	int buffer_map_idx;
+	int sarea_priv_offset;
+	unsigned int ring_start;
+	unsigned int ring_end;
+	unsigned int ring_size;
+	unsigned int front_offset;
+	unsigned int back_offset;
+	unsigned int depth_offset;
+	unsigned int w;
+	unsigned int h;
+	unsigned int pitch;
+	unsigned int pitch_bits; 
+} drm_i810_init_t;
+
+/* Warning: If you change the SAREA structure you must change the Xserver
+ * structure as well */
+
+typedef struct _drm_i810_tex_region {
+	unsigned char next, prev; /* indices to form a circular LRU  */
+	unsigned char in_use;	/* owned by a client, or free? */
+	int age;		/* tracked by clients to update local LRU's */
+} drm_i810_tex_region_t;
+
+typedef struct _drm_i810_sarea {
+   	unsigned int ContextState[I810_CTX_SETUP_SIZE];
+   	unsigned int BufferState[I810_DEST_SETUP_SIZE];
+   	unsigned int TexState[2][I810_TEX_SETUP_SIZE];
+   	unsigned int dirty;
+
+	unsigned int nbox;
+	drm_clip_rect_t boxes[I810_NR_SAREA_CLIPRECTS];
+
+	/* Maintain an LRU of contiguous regions of texture space.  If
+	 * you think you own a region of texture memory, and it has an
+	 * age different to the one you set, then you are mistaken and
+	 * it has been stolen by another client.  If global texAge
+	 * hasn't changed, there is no need to walk the list.
+	 *
+	 * These regions can be used as a proxy for the fine-grained
+	 * texture information of other clients - by maintaining them
+	 * in the same lru which is used to age their own textures,
+	 * clients have an approximate lru for the whole of global
+	 * texture space, and can make informed decisions as to which
+	 * areas to kick out.  There is no need to choose whether to
+	 * kick out your own texture or someone else's - simply eject
+	 * them all in LRU order.  
+	 */
+   
+	drm_i810_tex_region_t texList[I810_NR_TEX_REGIONS+1]; 
+				/* Last elt is sentinal */
+        int texAge;		/* last time texture was uploaded */
+        int last_enqueue;	/* last time a buffer was enqueued */
+	int last_dispatch;	/* age of the most recently dispatched buffer */
+	int last_quiescent;     /*  */
+	int ctxOwner;		/* last context to upload state */
+
+	int vertex_prim;
+
+} drm_i810_sarea_t;
+
+typedef struct _drm_i810_clear {
+	int clear_color;
+	int clear_depth;
+	int flags;
+} drm_i810_clear_t;
+
+
+
+/* These may be placeholders if we have more cliprects than
+ * I810_NR_SAREA_CLIPRECTS.  In that case, the client sets discard to
+ * false, indicating that the buffer will be dispatched again with a
+ * new set of cliprects.
+ */
+typedef struct _drm_i810_vertex {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	int discard;		/* client is finished with the buffer? */
+} drm_i810_vertex_t;
+
+typedef struct _drm_i810_copy_t {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	void *address;		/* Address to copy from */
+} drm_i810_copy_t;
+
+typedef struct drm_i810_dma {
+	void *virtual;
+	int request_idx;
+	int request_size;
+	int granted;
+} drm_i810_dma_t;
+
+#endif /* _I810_DRM_H_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_drv.c linux.18p17/drivers/char/drm/i810_drv.c
--- linux.vanilla/drivers/char/drm/i810_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_drv.c	Sat Sep 30 22:50:36 2000
@@ -0,0 +1,653 @@
+/* i810_drv.c -- I810 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ * 
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "i810_drv.h"
+
+#include <linux/agp_backend.h>
+
+static void __attribute__((unused)) unused(void)
+{
+	agp_enable(0);
+}
+
+#define I810_NAME	 "i810"
+#define I810_DESC	 "Intel I810"
+#define I810_DATE	 "20000719"
+#define I810_MAJOR	 1
+#define I810_MINOR	 1
+#define I810_PATCHLEVEL	 0
+
+static drm_device_t	      i810_device;
+drm_ctx_t		      i810_res_ctx;
+
+static struct file_operations i810_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:   THIS_MODULE,
+#endif
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+static struct miscdevice      i810_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  I810_NAME,
+	fops:  &i810_fops,
+};
+
+static drm_ioctl_desc_t	      i810_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { i810_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { i810_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { i810_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { i810_markbufs,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { i810_infobufs,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { i810_freebufs,  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { i810_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { i810_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { i810_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { i810_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { i810_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { i810_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { i810_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { i810_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { i810_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)]   = { i810_dma_init,   1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)]  = { i810_clear_bufs, 1, 0 },
+      	[DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)]  = { i810_flush_ioctl,1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf,     1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)]   = { i810_swap_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)]   = { i810_copybuf,    1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy,     1, 0 },
+};
+
+#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls)
+
+#ifdef MODULE
+static char		      *i810 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Intel I810");
+MODULE_PARM(i810, "s");
+
+#ifndef MODULE
+/* i810_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init i810_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("i810=", i810_options);
+#endif
+
+static int i810_setup(drm_device_t *dev)
+{
+	int i;
+	
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+			
+	DRM_DEBUG("\n");
+			
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+			
+	return 0;
+}
+
+
+static int i810_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) i810_irq_uninstall(dev);
+	
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+		
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+		
+		if (dev->agp->acquired && drm_agp.release)
+			(*drm_agp.release)();
+		
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+/* i810_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int i810_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+	
+#ifdef MODULE
+	drm_parse_options(i810);
+#endif
+	DRM_DEBUG("doing misc_register\n");
+	if ((retcode = misc_register(&i810_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", I810_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, i810_misc.minor);
+	dev->name   = I810_NAME;
+
+   	DRM_DEBUG("doing mem init\n");
+	drm_mem_init();
+	DRM_DEBUG("doing proc init\n");
+	drm_proc_init(dev);
+	DRM_DEBUG("doing agp init\n");
+	dev->agp    = drm_agp_init();
+   	if(dev->agp == NULL) {
+	   	DRM_INFO("The i810 drm module requires the agpgart module"
+			 " to function correctly\nPlease load the agpgart"
+			 " module before you load the i810 module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&i810_misc);
+	   	i810_takedown(dev);
+	   	return -ENOMEM;
+	}
+	DRM_DEBUG("doing ctxbitmap init\n");
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&i810_misc);
+		i810_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 I810_NAME,
+		 I810_MAJOR,
+		 I810_MINOR,
+		 I810_PATCHLEVEL,
+		 I810_DATE,
+		 i810_misc.minor);
+
+	return 0;
+}
+
+/* i810_cleanup is called via cleanup_module at module unload time. */
+
+static void i810_cleanup(void)
+{
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	if (misc_deregister(&i810_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	i810_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(i810_init);
+module_exit(i810_cleanup);
+
+
+int i810_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	copy_from_user_ret(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version),
+			   -EFAULT);
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		copy_to_user_ret(name, value, len, -EFAULT); \
+	}
+
+	version.version_major	   = I810_MAJOR;
+	version.version_minor	   = I810_MINOR;
+	version.version_patchlevel = I810_PATCHLEVEL;
+
+	DRM_COPY(version.name, I810_NAME);
+	DRM_COPY(version.date, I810_DATE);
+	DRM_COPY(version.desc, I810_DESC);
+
+	copy_to_user_ret((drm_version_t *)arg,
+			 &version,
+			 sizeof(version),
+			 -EFAULT);
+	return 0;
+}
+
+int i810_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &i810_device;
+	int	      retcode = 0;
+	
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return i810_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int i810_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	i810_reclaim_buffers(dev, priv->pid);
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	i810_reclaim_buffers(dev, priv->pid);
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+	
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+		return i810_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= I810_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &i810_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int i810_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+	
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+   	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+	
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/i810_drv.h linux.18p17/drivers/char/drm/i810_drv.h
--- linux.vanilla/drivers/char/drm/i810_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/i810_drv.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,225 @@
+/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _I810_DRV_H_
+#define _I810_DRV_H_
+
+typedef struct drm_i810_buf_priv {
+   	u32 *in_use;
+   	int my_use_idx;
+	int currently_mapped;
+	void *virtual;
+	void *kernel_virtual;
+	int map_count;
+   	struct vm_area_struct *vma;
+} drm_i810_buf_priv_t;
+
+typedef struct _drm_i810_ring_buffer{
+	int tail_mask;
+	unsigned long Start;
+	unsigned long End;
+	unsigned long Size;
+	u8 *virtual_start;
+	int head;
+	int tail;
+	int space;
+} drm_i810_ring_buffer_t;
+
+typedef struct drm_i810_private {
+   	int ring_map_idx;
+   	int buffer_map_idx;
+
+   	drm_i810_ring_buffer_t ring;
+	drm_i810_sarea_t *sarea_priv;
+
+      	unsigned long hw_status_page;
+   	unsigned long counter;
+
+   	atomic_t flush_done;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+	drm_buf_t *mmap_buffer;
+
+	
+	u32 front_di1, back_di1, zi1;
+	
+	int back_offset;
+	int depth_offset;
+	int w, h;
+	int pitch;
+} drm_i810_private_t;
+
+				/* i810_drv.c */
+extern int  i810_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_open(struct inode *inode, struct file *filp);
+extern int  i810_release(struct inode *inode, struct file *filp);
+extern int  i810_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* i810_dma.c */
+extern int  i810_dma_schedule(drm_device_t *dev, int locked);
+extern int  i810_getbuf(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_irq_install(drm_device_t *dev, int irq);
+extern int  i810_irq_uninstall(drm_device_t *dev);
+extern int  i810_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_dma_init(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_flush_ioctl(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg);
+extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int  i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+			unsigned long arg);
+extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma);
+extern int i810_copybuf(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int i810_docopy(struct inode *inode, struct file *filp, 
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_bufs.c */
+extern int  i810_addbufs(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int  i810_infobufs(struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_context.c */
+extern int  i810_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  i810_context_switch(drm_device_t *dev, int old, int new);
+extern int  i810_context_switch_complete(drm_device_t *dev, int new);
+
+#define I810_VERBOSE 0
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg);
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+#define GFX_OP_USER_INTERRUPT 		((0<<29)|(2<<23))
+#define GFX_OP_BREAKPOINT_INTERRUPT	((0<<29)|(1<<23))
+#define CMD_REPORT_HEAD			(7<<23)
+#define CMD_STORE_DWORD_IDX		((0x21<<23) | 0x1)
+#define CMD_OP_BATCH_BUFFER  ((0x0<<29)|(0x30<<23)|0x1)
+
+#define INST_PARSER_CLIENT   0x00000000
+#define INST_OP_FLUSH        0x02000000
+#define INST_FLUSH_MAP_CACHE 0x00000001
+
+
+#define BB1_START_ADDR_MASK   (~0x7)
+#define BB1_PROTECTED         (1<<0)
+#define BB1_UNPROTECTED       (0<<0)
+#define BB2_END_ADDR_MASK     (~0x7)
+
+#define I810REG_HWSTAM		0x02098
+#define I810REG_INT_IDENTITY_R	0x020a4
+#define I810REG_INT_MASK_R 	0x020a8
+#define I810REG_INT_ENABLE_R	0x020a0
+
+#define LP_RING     		0x2030
+#define HP_RING     		0x2040
+#define RING_TAIL      		0x00
+#define TAIL_ADDR		0x000FFFF8
+#define RING_HEAD      		0x04
+#define HEAD_WRAP_COUNT     	0xFFE00000
+#define HEAD_WRAP_ONE       	0x00200000
+#define HEAD_ADDR           	0x001FFFFC
+#define RING_START     		0x08
+#define START_ADDR          	0x00FFFFF8
+#define RING_LEN       		0x0C
+#define RING_NR_PAGES       	0x000FF000 
+#define RING_REPORT_MASK    	0x00000006
+#define RING_REPORT_64K     	0x00000002
+#define RING_REPORT_128K    	0x00000004
+#define RING_NO_REPORT      	0x00000000
+#define RING_VALID_MASK     	0x00000001
+#define RING_VALID          	0x00000001
+#define RING_INVALID        	0x00000000
+
+#define GFX_OP_SCISSOR         ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define SC_UPDATE_SCISSOR       (0x1<<1)
+#define SC_ENABLE_MASK          (0x1<<0)
+#define SC_ENABLE               (0x1<<0)
+
+#define GFX_OP_SCISSOR_INFO    ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
+#define SCI_YMIN_MASK      (0xffff<<16)
+#define SCI_XMIN_MASK      (0xffff<<0)
+#define SCI_YMAX_MASK      (0xffff<<16)
+#define SCI_XMAX_MASK      (0xffff<<0)
+
+#define GFX_OP_COLOR_FACTOR      ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
+#define GFX_OP_STIPPLE           ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define GFX_OP_MAP_INFO          ((0x3<<29)|(0x1d<<24)|0x2)
+#define GFX_OP_DESTBUFFER_VARS   ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
+#define GFX_OP_DRAWRECT_INFO     ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_PRIMITIVE         ((0x3<<29)|(0x1f<<24))
+
+#define CMD_OP_Z_BUFFER_INFO     ((0x0<<29)|(0x16<<23))
+#define CMD_OP_DESTBUFFER_INFO   ((0x0<<29)|(0x15<<23))
+
+#define BR00_BITBLT_CLIENT   0x40000000
+#define BR00_OP_COLOR_BLT    0x10000000
+#define BR00_OP_SRC_COPY_BLT 0x10C00000
+#define BR13_SOLID_PATTERN   0x80000000
+
+
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/init.c linux.18p17/drivers/char/drm/init.c
--- linux.vanilla/drivers/char/drm/init.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/init.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,113 @@
+/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int			      drm_flags		= 0;
+
+/* drm_parse_option parses a single option.  See description for
+   drm_parse_options for details. */
+
+static void drm_parse_option(char *s)
+{
+	char *c, *r;
+	
+	DRM_DEBUG("\"%s\"\n", s);
+	if (!s || !*s) return;
+	for (c = s; *c && *c != ':'; c++); /* find : or \0 */
+	if (*c) r = c + 1; else r = NULL;  /* remember remainder */
+	*c = '\0';			   /* terminate */
+	if (!strcmp(s, "noctx")) {
+		drm_flags |= DRM_FLAG_NOCTX;
+		DRM_INFO("Server-mediated context switching OFF\n");
+		return;
+	}
+	if (!strcmp(s, "debug")) {
+		drm_flags |= DRM_FLAG_DEBUG;
+		DRM_INFO("Debug messages ON\n");
+		return;
+	}
+	DRM_ERROR("\"%s\" is not a valid option\n", s);
+	return;
+}
+
+/* drm_parse_options parse the insmod "drm=" options, or the command-line
+ * options passed to the kernel via LILO.  The grammar of the format is as
+ * follows:
+ *
+ * drm		::= 'drm=' option_list
+ * option_list	::= option [ ';' option_list ]
+ * option	::= 'device:' major
+ *		|   'debug' 
+ *		|   'noctx'
+ * major	::= INTEGER
+ *
+ * Note that 's' contains option_list without the 'drm=' part.
+ *
+ * device=major,minor specifies the device number used for /dev/drm
+ *	  if major == 0 then the misc device is used
+ *	  if major == 0 and minor == 0 then dynamic misc allocation is used
+ * debug=on specifies that debugging messages will be printk'd
+ * debug=trace specifies that each function call will be logged via printk
+ * debug=off turns off all debugging options
+ *
+ */
+
+void drm_parse_options(char *s)
+{
+	char *h, *t, *n;
+	
+	DRM_DEBUG("\"%s\"\n", s ?: "");
+	if (!s || !*s) return;
+
+	for (h = t = n = s; h && *h; h = n) {
+		for (; *t && *t != ';'; t++);	       /* find ; or \0 */
+		if (*t) n = t + 1; else n = NULL;      /* remember next */
+		*t = '\0';			       /* terminate */
+		drm_parse_option(h);		       /* parse */
+	}
+}
+
+/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0
+ * otherwise. */
+
+int drm_cpu_valid(void)
+{
+#if defined(__i386__)
+	if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */
+#endif
+#if defined(__sparc__) && !defined(__sparc_v9__)
+	if (1)
+		return 0; /* No cmpxchg before v9 sparc. */
+#endif
+	return 1;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ioctl.c linux.18p17/drivers/char/drm/ioctl.c
--- linux.vanilla/drivers/char/drm/ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/ioctl.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,91 @@
+/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
+ * Created: Fri Jan  8 09:01:26 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_irq_busid_t p;
+	struct pci_dev	*dev;
+
+	copy_from_user_ret(&p, (drm_irq_busid_t *)arg, sizeof(p), -EFAULT);
+	dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum));
+	if (dev) p.irq = dev->irq;
+	else	 p.irq = 0;
+	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
+		  p.busnum, p.devnum, p.funcnum, p.irq);
+	copy_to_user_ret((drm_irq_busid_t *)arg, &p, sizeof(p), -EFAULT);
+	return 0;
+}
+
+int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT);
+	if (u.unique_len >= dev->unique_len) {
+		copy_to_user_ret(u.unique, dev->unique, dev->unique_len,
+				 -EFAULT);
+	}
+	u.unique_len = dev->unique_len;
+	copy_to_user_ret((drm_unique_t *)arg, &u, sizeof(u), -EFAULT);
+	return 0;
+}
+
+int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	if (dev->unique_len || dev->unique) return -EBUSY;
+
+	copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT);
+	if (!u.unique_len) return -EINVAL;
+	
+	dev->unique_len = u.unique_len;
+	dev->unique	= drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER);
+	copy_from_user_ret(dev->unique, u.unique, dev->unique_len,
+			   -EFAULT);
+	dev->unique[dev->unique_len] = '\0';
+
+	dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2,
+				 DRM_MEM_DRIVER);
+	sprintf(dev->devname, "%s@%s", dev->name, dev->unique);
+
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/lists.c linux.18p17/drivers/char/drm/lists.c
--- linux.vanilla/drivers/char/drm/lists.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/lists.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,245 @@
+/* lists.c -- Buffer list handling routines -*- linux-c -*-
+ * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_waitlist_create(drm_waitlist_t *bl, int count)
+{
+	DRM_DEBUG("%d\n", count);
+	if (bl->count) return -EINVAL;
+	
+	bl->count      = count;
+	bl->bufs       = drm_alloc((bl->count + 2) * sizeof(*bl->bufs),
+				   DRM_MEM_BUFLISTS);
+	bl->rp	       = bl->bufs;
+	bl->wp	       = bl->bufs;
+	bl->end	       = &bl->bufs[bl->count+1];
+	bl->write_lock = SPIN_LOCK_UNLOCKED;
+	bl->read_lock  = SPIN_LOCK_UNLOCKED;
+	return 0;
+}
+
+int drm_waitlist_destroy(drm_waitlist_t *bl)
+{
+	DRM_DEBUG("\n");
+	if (bl->rp != bl->wp) return -EINVAL;
+	if (bl->bufs) drm_free(bl->bufs,
+			       (bl->count + 2) * sizeof(*bl->bufs),
+			       DRM_MEM_BUFLISTS);
+	bl->count = 0;
+	bl->bufs  = NULL;
+	bl->rp	  = NULL;
+	bl->wp	  = NULL;
+	bl->end	  = NULL;
+	return 0;
+}
+
+int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf)
+{							
+	int	      left;
+	unsigned long flags;
+
+	left = DRM_LEFTCOUNT(bl);
+	DRM_DEBUG("put %d (%d left, rp = %p, wp = %p)\n",
+		  buf->idx, left, bl->rp, bl->wp);
+	if (!left) {
+		DRM_ERROR("Overflow while adding buffer %d from pid %d\n",
+			  buf->idx, buf->pid);
+		return -EINVAL;
+	}
+#if DRM_DMA_HISTOGRAM
+	buf->time_queued = get_cycles();
+#endif
+	buf->list	 = DRM_LIST_WAIT;
+	
+	spin_lock_irqsave(&bl->write_lock, flags);
+	*bl->wp = buf;
+	if (++bl->wp >= bl->end) bl->wp = bl->bufs;
+	spin_unlock_irqrestore(&bl->write_lock, flags);
+	
+	return 0;
+}
+
+drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl)
+{
+	drm_buf_t     *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bl->read_lock, flags);
+	buf = *bl->rp;
+	if (bl->rp == bl->wp) {
+		spin_unlock_irqrestore(&bl->read_lock, flags);
+		return NULL;
+	}				     
+	if (++bl->rp >= bl->end) bl->rp = bl->bufs;
+	spin_unlock_irqrestore(&bl->read_lock, flags);
+	
+	DRM_DEBUG("get %d\n", buf->idx);
+	return buf;
+}
+
+int drm_freelist_create(drm_freelist_t *bl, int count)
+{
+	DRM_DEBUG("\n");
+	atomic_set(&bl->count, 0);
+	bl->next      = NULL;
+	init_waitqueue_head(&bl->waiting);
+	bl->low_mark  = 0;
+	bl->high_mark = 0;
+	atomic_set(&bl->wfh,   0);
+	++bl->initialized;
+	return 0;
+}
+
+int drm_freelist_destroy(drm_freelist_t *bl)
+{
+	DRM_DEBUG("\n");
+	atomic_set(&bl->count, 0);
+	bl->next = NULL;
+	return 0;
+}
+
+int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
+{
+	drm_buf_t        *old, *prev;
+	int		 count = 0;
+	drm_device_dma_t *dma  = dev->dma;
+
+	if (!dma) {
+		DRM_ERROR("No DMA support\n");
+		return 1;
+	}
+
+	if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) {
+		DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n",
+		  buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh),
+		  buf->waiting, buf->pending);
+	if (!bl) return 1;
+#if DRM_DMA_HISTOGRAM
+	buf->time_freed = get_cycles();
+	drm_histogram_compute(dev, buf);
+#endif
+	buf->list	= DRM_LIST_FREE;
+	do {
+		old       = bl->next;
+		buf->next = old;
+		prev      = cmpxchg(&bl->next, old, buf);
+		if (++count > DRM_LOOPING_LIMIT) {
+			DRM_ERROR("Looping\n");
+			return 1;
+		}
+	} while (prev != old);
+	atomic_inc(&bl->count);
+	if (atomic_read(&bl->count) > dma->buf_count) {
+		DRM_ERROR("%d of %d buffers free after addition of %d\n",
+			  atomic_read(&bl->count), dma->buf_count, buf->idx);
+		return 1;
+	}
+				/* Check for high water mark */
+	if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) {
+		atomic_set(&bl->wfh, 0);
+		wake_up_interruptible(&bl->waiting);
+	}
+	return 0;
+}
+
+static drm_buf_t *drm_freelist_try(drm_freelist_t *bl)
+{
+	drm_buf_t         *old, *new, *prev;
+	drm_buf_t	  *buf;
+	int		  count = 0;
+
+	if (!bl) return NULL;
+	
+				/* Get buffer */
+	do {
+		old = bl->next;
+		if (!old) return NULL;
+		new  = bl->next->next;
+		prev = cmpxchg(&bl->next, old, new);
+		if (++count > DRM_LOOPING_LIMIT) {
+			DRM_ERROR("Looping\n");
+			return NULL;
+		}
+	} while (prev != old);
+	atomic_dec(&bl->count);
+	
+	buf	  = old;
+	buf->next = NULL;
+	buf->list = DRM_LIST_NONE;
+	DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n",
+		  buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh),
+		  buf->waiting, buf->pending);
+	if (buf->waiting || buf->pending) {
+		DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	
+	return buf;
+}
+
+drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block)
+{
+	drm_buf_t	  *buf	= NULL;
+	DECLARE_WAITQUEUE(entry, current);
+
+	if (!bl || !bl->initialized) return NULL;
+	
+				/* Check for low water mark */
+	if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */
+		atomic_set(&bl->wfh, 1);
+	if (atomic_read(&bl->wfh)) {
+		DRM_DEBUG("Block = %d, count = %d, wfh = %d\n",
+			  block, atomic_read(&bl->count),
+			  atomic_read(&bl->wfh));
+		if (block) {
+			add_wait_queue(&bl->waiting, &entry);
+			current->state = TASK_INTERRUPTIBLE;
+			for (;;) {
+				if (!atomic_read(&bl->wfh)
+				    && (buf = drm_freelist_try(bl))) break;
+				schedule();
+				if (signal_pending(current)) break;
+			}
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&bl->waiting, &entry);
+		}
+		return buf;
+	}
+		
+	DRM_DEBUG("Count = %d, wfh = %d\n",
+		  atomic_read(&bl->count), atomic_read(&bl->wfh));
+	return drm_freelist_try(bl);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/lock.c linux.18p17/drivers/char/drm/lock.c
--- linux.vanilla/drivers/char/drm/lock.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/lock.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,225 @@
+/* lock.c -- IOCTLs for locking -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_block(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	DRM_DEBUG("%d attempts\n", context);
+	do {
+		old = *lock;
+		if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
+		else			  new = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCKING_CONTEXT(old) == context) {
+		if (old & _DRM_LOCK_HELD) {
+			if (context != DRM_KERNEL_CONTEXT) {
+				DRM_ERROR("%d holds heavyweight lock\n",
+					  context);
+			}
+			return 0;
+		}
+	}
+	if (new == (context | _DRM_LOCK_HELD)) {
+				/* Have lock */
+		DRM_DEBUG("%d\n", context);
+		return 1;
+	}
+	DRM_DEBUG("%d unable to get lock held by %d\n",
+		  context, _DRM_LOCKING_CONTEXT(old));
+	return 0;
+}
+
+/* This takes a lock forcibly and hands it to context.	Should ONLY be used
+   inside *_unlock to give lock to kernel before calling *_dma_schedule. */
+int drm_lock_transfer(drm_device_t *dev,
+		      __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	DRM_DEBUG("%d => %d\n", _DRM_LOCKING_CONTEXT(old), context);
+	return 1;
+}
+
+int drm_lock_free(drm_device_t *dev,
+		  __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+	pid_t        pid = dev->lock.pid;
+
+	DRM_DEBUG("%d\n", context);
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = 0;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
+		DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
+			  context,
+			  _DRM_LOCKING_CONTEXT(old),
+			  pid);
+		return 1;
+	}
+	wake_up_interruptible(&dev->lock.lock_queue);
+	return 0;
+}
+
+static int drm_flush_queue(drm_device_t *dev, int context)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		atomic_inc(&q->block_write);
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&q->flush_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			if (!DRM_BUFCOUNT(&q->waitlist)) break;
+			schedule();
+			if (signal_pending(current)) {
+				ret = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->flush_queue, &entry);
+	}
+	atomic_dec(&q->use_count);
+	atomic_inc(&q->total_flushed);
+		
+				/* NOTE: block_write is still incremented!
+				   Use drm_flush_unlock_queue to decrement. */
+	return ret;
+}
+
+static int drm_flush_unblock_queue(drm_device_t *dev, int context)
+{
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		if (atomic_read(&q->block_write)) {
+			atomic_dec(&q->block_write);
+			wake_up_interruptible(&q->write_queue);
+		}
+	}
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_flush_block_and_flush(drm_device_t *dev, int context,
+			      drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_queue(dev, i);
+		}
+	}
+	return ret;
+}
+
+int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_unblock_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_unblock_queue(dev, i);
+		}
+	}
+		
+	return ret;
+}
+
+int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	int		  ret	  = 0;
+	drm_lock_t	  lock;
+
+	DRM_DEBUG("\n");
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+	drm_flush_unblock(dev, lock.context, lock.flags);
+	return ret;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/memory.c linux.18p17/drivers/char/drm/memory.c
--- linux.vanilla/drivers/char/drm/memory.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/memory.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,449 @@
+/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
+ * Created: Thu Feb  4 14:00:34 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+
+typedef struct drm_mem_stats {
+	const char	  *name;
+	int		  succeed_count;
+	int		  free_count;
+	int		  fail_count;
+	unsigned long	  bytes_allocated;
+	unsigned long	  bytes_freed;
+} drm_mem_stats_t;
+
+static spinlock_t	  drm_mem_lock	    = SPIN_LOCK_UNLOCKED;
+static unsigned long	  drm_ram_available = 0; /* In pages */
+static unsigned long	  drm_ram_used	    = 0;
+static drm_mem_stats_t	  drm_mem_stats[]   = {
+	[DRM_MEM_DMA]	    = { "dmabufs"  },
+	[DRM_MEM_SAREA]	    = { "sareas"   },
+	[DRM_MEM_DRIVER]    = { "driver"   },
+	[DRM_MEM_MAGIC]	    = { "magic"	   },
+	[DRM_MEM_IOCTLS]    = { "ioctltab" },
+	[DRM_MEM_MAPS]	    = { "maplist"  },
+	[DRM_MEM_VMAS]	    = { "vmalist"  },
+	[DRM_MEM_BUFS]	    = { "buflist"  },
+	[DRM_MEM_SEGS]	    = { "seglist"  },
+	[DRM_MEM_PAGES]	    = { "pagelist" },
+	[DRM_MEM_FILES]	    = { "files"	   },
+	[DRM_MEM_QUEUES]    = { "queues"   },
+	[DRM_MEM_CMDS]	    = { "commands" },
+	[DRM_MEM_MAPPINGS]  = { "mappings" },
+	[DRM_MEM_BUFLISTS]  = { "buflists" },
+	[DRM_MEM_AGPLISTS]  = { "agplist"  },
+	[DRM_MEM_TOTALAGP]  = { "totalagp" },
+	[DRM_MEM_BOUNDAGP]  = { "boundagp" },
+	[DRM_MEM_CTXBITMAP] = { "ctxbitmap"},
+	{ NULL, 0, }		/* Last entry must be null */
+};
+
+void drm_mem_init(void)
+{
+	drm_mem_stats_t *mem;
+	struct sysinfo	si;
+	
+	for (mem = drm_mem_stats; mem->name; ++mem) {
+		mem->succeed_count   = 0;
+		mem->free_count	     = 0;
+		mem->fail_count	     = 0;
+		mem->bytes_allocated = 0;
+		mem->bytes_freed     = 0;
+	}
+	
+	si_meminfo(&si);
+#if LINUX_VERSION_CODE < 0x020317
+				/* Changed to page count in 2.3.23 */
+	drm_ram_available = si.totalram >> PAGE_SHIFT;
+#else
+	drm_ram_available = si.totalram;
+#endif
+	drm_ram_used	  = 0;
+}
+
+/* drm_mem_info is called whenever a process reads /dev/drm/mem. */
+
+static int _drm_mem_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_mem_stats_t *pt;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("		  total counts			"
+		       " |    outstanding  \n");
+	DRM_PROC_PRINT("type	   alloc freed fail	bytes	   freed"
+		       " | allocs      bytes\n\n");
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "system", 0, 0, 0,
+		       drm_ram_available << (PAGE_SHIFT - 10));
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "locked", 0, 0, 0, drm_ram_used >> 10);
+	DRM_PROC_PRINT("\n");
+	for (pt = drm_mem_stats; pt->name; pt++) {
+		DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n",
+			       pt->name,
+			       pt->succeed_count,
+			       pt->free_count,
+			       pt->fail_count,
+			       pt->bytes_allocated,
+			       pt->bytes_freed,
+			       pt->succeed_count - pt->free_count,
+			       (long)pt->bytes_allocated
+			       - (long)pt->bytes_freed);
+	}
+	
+	return len;
+}
+
+int drm_mem_info(char *buf, char **start, off_t offset, int len,
+		 int *eof, void *data)
+{
+	int ret;
+	
+	spin_lock(&drm_mem_lock);
+	ret = _drm_mem_info(buf, start, offset, len, eof, data);
+	spin_unlock(&drm_mem_lock);
+	return ret;
+}
+
+void *drm_alloc(size_t size, int area)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(area, "Allocating 0 bytes\n");
+		return NULL;
+	}
+	
+	if (!(pt = kmalloc(size, GFP_KERNEL))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
+{
+	void *pt;
+	
+	if (!(pt = drm_alloc(size, area))) return NULL;
+	if (oldpt && oldsize) {
+		memcpy(pt, oldpt, oldsize);
+		drm_free(oldpt, oldsize, area);
+	}
+	return pt;
+}
+
+char *drm_strdup(const char *s, int area)
+{
+	char *pt;
+	int	 length = s ? strlen(s) : 0;
+	
+	if (!(pt = drm_alloc(length+1, area))) return NULL;
+	strcpy(pt, s);
+	return pt;
+}
+
+void drm_strfree(const char *s, int area)
+{
+	unsigned int size;
+	
+	if (!s) return;
+	
+	size = 1 + (s ? strlen(s) : 0);
+	drm_free((void *)s, size, area);
+}
+
+void drm_free(void *pt, size_t size, int area)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n");
+	else	 kfree(pt);
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[area].bytes_freed += size;
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+unsigned long drm_alloc_pages(int order, int area)
+{
+	unsigned long address;
+	unsigned long bytes	  = PAGE_SIZE << order;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	spin_lock(&drm_mem_lock);
+	if ((drm_ram_used >> PAGE_SHIFT)
+	    > (DRM_RAM_PERCENT * drm_ram_available) / 100) {
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_unlock(&drm_mem_lock);
+	
+	address = __get_free_pages(GFP_KERNEL, order);
+	if (!address) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += bytes;
+	drm_ram_used		            += bytes;
+	spin_unlock(&drm_mem_lock);
+	
+	
+				/* Zero outside the lock */
+	memset((void *)address, 0, bytes);
+	
+				/* Reserve */
+	for (addr = address, sz = bytes;
+	     sz > 0;
+	     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+		mem_map_reserve(MAP_NR(addr));
+	}
+	
+	return address;
+}
+
+void drm_free_pages(unsigned long address, int order, int area)
+{
+	unsigned long bytes = PAGE_SIZE << order;
+	int		  alloc_count;
+	int		  free_count;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	if (!address) {
+		DRM_MEM_ERROR(area, "Attempt to free address 0\n");
+	} else {
+				/* Unreserve */
+		for (addr = address, sz = bytes;
+		     sz > 0;
+		     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+			mem_map_unreserve(MAP_NR(addr));
+		}
+		free_pages(address, order);
+	}
+	
+	spin_lock(&drm_mem_lock);
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_freed += bytes;
+	drm_ram_used			-= bytes;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+void *drm_ioremap(unsigned long offset, unsigned long size)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Mapping 0 bytes at 0x%08lx\n", offset);
+		return NULL;
+	}
+	
+	if (!(pt = ioremap(offset, size))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void drm_ioremapfree(void *pt, unsigned long size)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt)
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Attempt to free NULL pointer\n");
+	else
+		iounmap(pt);
+	
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size;
+	free_count  = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count;
+	alloc_count =	drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+agp_memory *drm_alloc_agp(int pages, u32 type)
+{
+	agp_memory *handle;
+
+	if (!pages) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n");
+		return NULL;
+	}
+	
+	if (drm_agp.allocate_memory) {
+		if ((handle = (*drm_agp.allocate_memory)(pages,
+							 type))) {
+			spin_lock(&drm_mem_lock);
+			++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+			drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated
+				+= pages << PAGE_SHIFT;
+			spin_unlock(&drm_mem_lock);
+			return handle;
+		}
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return NULL;
+}
+
+int drm_free_agp(agp_memory *handle, int pages)
+{
+	int           alloc_count;
+	int           free_count;
+	int           retval = -EINVAL;
+
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+			      "Attempt to free NULL AGP handle\n");
+		return retval;;
+	}
+	
+	if (drm_agp.free_memory) {
+		(*drm_agp.free_memory)(handle);
+		spin_lock(&drm_mem_lock);
+		free_count  = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count;
+		alloc_count =   drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed
+			+= pages << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		if (free_count > alloc_count) {
+			DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+				      "Excess frees: %d frees, %d allocs\n",
+				      free_count, alloc_count);
+		}
+		return 0;
+	}
+	return retval;
+}
+
+int drm_bind_agp(agp_memory *handle, unsigned int start)
+{
+	int retcode = -EINVAL;
+
+   DRM_DEBUG("drm_bind_agp called\n");
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to bind NULL AGP handle\n");
+		return retcode;
+	}
+
+   DRM_DEBUG("drm_agp.bind_memory : %p\n", drm_agp.bind_memory);
+	if (drm_agp.bind_memory) {
+		if (!(retcode = (*drm_agp.bind_memory)(handle, start))) {
+			spin_lock(&drm_mem_lock);
+			++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+			drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated
+				+= handle->page_count << PAGE_SHIFT;
+			spin_unlock(&drm_mem_lock);
+		   DRM_DEBUG("drm_agp.bind_memory: retcode %d\n", retcode);
+			return retcode;
+		}
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return retcode;
+}
+
+int drm_unbind_agp(agp_memory *handle)
+{
+	int alloc_count;
+	int free_count;
+	int retcode = -EINVAL;
+	
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to unbind NULL AGP handle\n");
+		return retcode;
+	}
+
+	if (drm_agp.unbind_memory) {
+		int c = handle->page_count;
+		if ((retcode = (*drm_agp.unbind_memory)(handle)))
+			return retcode;
+		spin_lock(&drm_mem_lock);
+		free_count  = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count;
+		alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed += c << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		if (free_count > alloc_count) {
+			DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+				      "Excess frees: %d frees, %d allocs\n",
+				      free_count, alloc_count);
+		}
+	}
+	return retcode;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_bufs.c linux.18p17/drivers/char/drm/mga_bufs.c
--- linux.vanilla/drivers/char/drm/mga_bufs.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_bufs.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,639 @@
+/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "linux/un.h"
+
+
+int mga_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+
+		DRM_DEBUG("offset : %ld\n", offset);
+
+		buf->offset = offset; /* Hrm */
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t), DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_mga_buf_priv_t);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+      
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+
+	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
+
+	dma->byte_count += byte_count;
+
+	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	copy_to_user_ret((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+   
+	atomic_dec(&dev->buf_alloc);
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	DRM_DEBUG("dma->flags : %x\n", dma->flags);
+
+	return 0;
+}
+
+int mga_addbufs_pci(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+   	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	copy_to_user_ret((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return mga_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return mga_addbufs_pci(inode, filp, cmd, arg);
+}
+
+int mga_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				copy_to_user_ret(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark),
+						 -EFAULT);
+				copy_to_user_ret(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark),
+						 -EFAULT);
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	copy_to_user_ret((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+	
+	return 0;
+}
+
+int mga_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int mga_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		copy_from_user_ret(&idx,
+				   &request.list[i],
+				   sizeof(idx),
+				   -EFAULT);
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int mga_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		DRM_DEBUG("Busy\n");
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	DRM_DEBUG("mga_mapbufs\n");
+   	DRM_DEBUG("dma->flags : %x\n", dma->flags);
+   
+	if (request.count >= dma->buf_count) {
+		if(dma->flags & _DRM_DMA_USE_AGP) {
+			drm_mga_private_t *dev_priv = dev->dev_private;
+			drm_map_t *map = NULL;
+	 
+			map = dev->maplist[dev_priv->buffer_map_idx];
+			if (!map) {
+				DRM_DEBUG("map is null\n");
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			DRM_DEBUG("map->offset : %lx\n", map->offset);
+			DRM_DEBUG("map->size : %lx\n", map->size);
+			DRM_DEBUG("map->type : %d\n", map->type);
+			DRM_DEBUG("map->flags : %x\n", map->flags);
+			DRM_DEBUG("map->handle : %p\n", map->handle);
+			DRM_DEBUG("map->mtrr : %d\n", map->mtrr);
+			down(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size, 
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED, 
+					  (unsigned long)map->offset);
+			up(&current->mm->mmap_sem);
+		} else {
+			down(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+      
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+   
+	copy_to_user_ret((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+
+	DRM_DEBUG("retcode : %d\n", retcode);
+
+	return retcode;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_context.c linux.18p17/drivers/char/drm/mga_context.c
--- linux.vanilla/drivers/char/drm/mga_context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_context.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,203 @@
+/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	   Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+static int mga_alloc_queue(drm_device_t *dev)
+{
+   	int temp = drm_ctxbitmap_next(dev);
+   	DRM_DEBUG("mga_alloc_queue: %d\n", temp);
+	return temp;
+}
+
+int mga_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                mga_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int mga_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+int mga_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = mga_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the mga */
+	return 0;
+}
+
+int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int mga_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return mga_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int mga_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	mga_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int mga_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+      	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_dma.c linux.18p17/drivers/char/drm/mga_dma.c
--- linux.vanilla/drivers/char/drm/mga_dma.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_dma.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,1115 @@
+/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+#define MGA_REG(reg)		2
+#define MGA_BASE(reg)		((unsigned long) \
+				((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle)
+#define MGA_ADDR(reg)		(MGA_BASE(reg) + reg)
+#define MGA_DEREF(reg)		*(__volatile__ int *)MGA_ADDR(reg)
+#define MGA_READ(reg)		MGA_DEREF(reg)
+#define MGA_WRITE(reg,val) 	do { MGA_DEREF(reg) = val; } while (0)
+
+#define PDEA_pagpxfer_enable 	     0x2
+
+static int mga_flush_queue(drm_device_t *dev);
+
+static unsigned long mga_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+   
+	DRM_DEBUG("%s\n", __FUNCTION__);
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) {
+		return 0;
+	}
+	atomic_inc(&mem_map[MAP_NR((void *) address)].count);
+	set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags);
+   
+	return address;
+}
+
+static void mga_free_page(drm_device_t *dev, unsigned long page)
+{
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if(page == 0UL) {
+		return;
+	}
+	atomic_dec(&mem_map[MAP_NR((void *) page)].count);
+	clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags);
+	wake_up(&mem_map[MAP_NR((void *) page)].wait);
+	free_page(page);
+	return;
+}
+
+static void mga_delay(void)
+{
+   	return;
+}
+
+void mga_flush_write_combine(void)
+{
+   	int xchangeDummy;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	__asm__ volatile(" push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy));
+   	__asm__ volatile(" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;"
+			 " movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ;"
+			 " pop %%eax" : /* no outputs */ :  /* no inputs */ );
+}
+
+/* These are two age tags that will never be sent to
+ * the hardware */
+#define MGA_BUF_USED 	0xffffffff
+#define MGA_BUF_FREE	0
+
+static int mga_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	int i;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	if(dev_priv->head == NULL) return -ENOMEM;
+   	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
+   	dev_priv->head->age = MGA_BUF_USED;
+   
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		item = drm_alloc(sizeof(drm_mga_freelist_t),
+				 DRM_MEM_DRIVER);
+	   	if(item == NULL) return -ENOMEM;
+	   	memset(item, 0, sizeof(drm_mga_freelist_t));
+	  	item->age = MGA_BUF_FREE;
+	   	item->prev = dev_priv->head;
+	   	item->next = dev_priv->head->next;
+	   	if(dev_priv->head->next != NULL)
+			dev_priv->head->next->prev = item;
+	   	if(item->next == NULL) dev_priv->tail = item;
+	   	item->buf = buf;
+	   	buf_priv->my_freelist = item;
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+	   	dev_priv->head->next = item;
+	}
+   
+   	return 0;
+}
+
+static void mga_freelist_cleanup(drm_device_t *dev)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	drm_mga_freelist_t *prev;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	item = dev_priv->head;
+   	while(item) {
+	   	prev = item;
+	   	item = item->next;
+	   	drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	}
+   
+   	dev_priv->head = dev_priv->tail = NULL;
+}
+
+/* Frees dispatch lock */
+static inline void mga_dma_quiescent(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma      = dev->dma;
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	unsigned long end;
+	int i;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+	end = jiffies + (HZ*3);
+    	while(1) {
+		if(!test_and_set_bit(MGA_IN_DISPATCH, 
+				     &dev_priv->dispatch_status)) {
+			break;
+		}
+	   	if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n", 
+				  atomic_read(&dev->total_irq), 
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup\n"); 
+			goto out_nolock;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();
+	}
+	end = jiffies + (HZ*3);
+    	DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS));
+    	while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+		if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n", 
+				  atomic_read(&dev->total_irq), 
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup\n"); 
+			goto out_status;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();	  
+	}
+    	sarea_priv->dirty |= MGA_DMA_FLUSH;
+
+out_status:
+    	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+out_nolock:
+}
+
+static void mga_reset_freelist(drm_device_t *dev)
+{
+   	drm_device_dma_t  *dma      = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+	int i;
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		buf_priv->my_freelist->age = MGA_BUF_FREE;
+	}
+}
+
+/* Least recently used :
+ * These operations are not atomic b/c they are protected by the 
+ * hardware lock */
+
+drm_buf_t *mga_freelist_get(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv = 
+     		(drm_mga_private_t *) dev->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *next;
+	static int failed = 0;
+
+	DRM_DEBUG("%s : tail->age : %d last_prim_age : %d\n", __FUNCTION__,
+	       dev_priv->tail->age, dev_priv->last_prim_age);
+   
+	if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) {
+		DRM_DEBUG("I'm waiting on the freelist!!! %d\n", 
+		       dev_priv->last_prim_age);
+	   	set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+		current->state = TASK_INTERRUPTIBLE;
+	   	add_wait_queue(&dev_priv->buf_queue, &entry);
+	   	for (;;) {
+		   	mga_dma_schedule(dev, 0);
+		   	if(!test_bit(MGA_IN_GETBUF, 
+				     &dev_priv->dispatch_status)) 
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	schedule();
+		   	if (signal_pending(current)) {
+				clear_bit(MGA_IN_GETBUF,
+					  &dev_priv->dispatch_status);
+			   	goto failed_getbuf;
+			}
+		}
+	   	current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->buf_queue, &entry);
+	}
+   
+   	if(dev_priv->tail->age < dev_priv->last_prim_age) {
+		prev = dev_priv->tail->prev;
+	   	next = dev_priv->tail;
+	   	prev->next = NULL;
+	   	next->prev = next->next = NULL;
+	   	dev_priv->tail = prev;
+	   	next->age = MGA_BUF_USED;
+		failed = 0;
+	   	return next->buf;
+	}
+
+failed_getbuf:
+	failed++;
+   	return NULL;
+}
+
+int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+      	drm_mga_private_t *dev_priv = 
+     		(drm_mga_private_t *) dev->dev_private;
+   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *next;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	if(buf_priv->my_freelist->age == MGA_BUF_USED) {
+		/* Discarded buffer, put it on the tail */
+		next = buf_priv->my_freelist;
+		next->age = MGA_BUF_FREE;
+		prev = dev_priv->tail;
+		prev->next = next;
+		next->prev = prev;
+		next->next = NULL;
+		dev_priv->tail = next;
+		DRM_DEBUG("Discarded\n");
+	} else {
+		/* Normally aged buffer, put it on the head + 1,
+		 * as the real head is a sentinal element
+		 */
+		next = buf_priv->my_freelist;
+		head = dev_priv->head;
+		prev = head->next;
+		head->next = next;
+		prev->prev = next;
+		next->prev = head;
+		next->next = prev;
+	}
+   
+   	return 0;
+}
+
+static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init)
+{
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_prim_buf_t *prim_buffer;
+   	int i, temp, size_of_buf;
+   	int offset = init->reserved_map_agpstart;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+   	dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) / 
+				  PAGE_SIZE) * PAGE_SIZE;
+   	size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS;
+	dev_priv->warp_ucode_size = init->warp_ucode_size;
+   	dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) * 
+					(MGA_NUM_PRIM_BUFS + 1), 
+					DRM_MEM_DRIVER);
+   	if(dev_priv->prim_bufs == NULL) {
+		DRM_ERROR("Unable to allocate memory for prim_buf\n");
+		return -ENOMEM;
+	}
+   	memset(dev_priv->prim_bufs, 
+	       0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1));
+   
+   	temp = init->warp_ucode_size + dev_priv->primary_size;
+	temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+	   
+	dev_priv->ioremap = drm_ioremap(dev->agp->base + offset, 
+					temp);
+	if(dev_priv->ioremap == NULL) {
+		DRM_DEBUG("Ioremap failed\n");
+		return -ENOMEM;
+	}
+   	init_waitqueue_head(&dev_priv->wait_queue);
+   
+   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+	   	prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t), 
+					DRM_MEM_DRIVER);
+	   	if(prim_buffer == NULL) return -ENOMEM;
+	   	memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t));
+	   	prim_buffer->phys_head = offset + dev->agp->base;
+	   	prim_buffer->current_dma_ptr = 
+			prim_buffer->head = 
+			(u32 *) (dev_priv->ioremap + 
+				 offset - 
+				 init->reserved_map_agpstart);
+	   	prim_buffer->num_dwords = 0;
+	   	prim_buffer->max_dwords = size_of_buf / sizeof(u32);
+	   	prim_buffer->max_dwords -= 5; /* Leave room for the softrap */
+	   	prim_buffer->sec_used = 0;
+	   	prim_buffer->idx = i;
+		prim_buffer->prim_age = i + 1;
+	   	offset = offset + size_of_buf;
+	   	dev_priv->prim_bufs[i] = prim_buffer;
+	}
+	dev_priv->current_prim_idx = 0;
+        dev_priv->next_prim = 
+		dev_priv->last_prim = 
+		dev_priv->current_prim =
+        	dev_priv->prim_bufs[0];
+	dev_priv->next_prim_age = 2;	
+	dev_priv->last_prim_age = 1;
+   	set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status);
+   	return 0;
+}
+
+void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim)
+{
+       	drm_mga_private_t *dev_priv = dev->dev_private;
+      	drm_device_dma_t  *dma	    = dev->dma;
+       	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ 	int use_agp = PDEA_pagpxfer_enable;
+	unsigned long end;
+   	int i;
+   	int next_idx;
+       	PRIMLOCALS;
+
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+   	dev_priv->last_prim = prim;
+   
+ 	/* We never check for overflow, b/c there is always room */
+    	PRIMPTR(prim);
+   	if(num_dwords <= 0) {
+		DRM_DEBUG("num_dwords == 0 when dispatched\n");
+		goto out_prim_wait;
+	}
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+       	PRIMOUTREG( MGAREG_DMAPAD, 0);
+   	PRIMOUTREG( MGAREG_SOFTRAP, 0);
+    	PRIMFINISH(prim);
+
+	end = jiffies + (HZ*3);
+    	if(sarea_priv->dirty & MGA_DMA_FLUSH) {
+		DRM_DEBUG("Dma top flush\n");	   
+		while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n", 
+					  atomic_read(&dev->total_irq), 
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup in fire primary "
+					  "(Dma Top Flush)\n");
+				goto out_prim_wait;
+			}
+	      
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+		sarea_priv->dirty &= ~(MGA_DMA_FLUSH);
+	} else {
+		DRM_DEBUG("Status wait\n");
+		while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n", 
+					  atomic_read(&dev->total_irq), 
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup in fire primary "
+					  "(Status Wait)\n");
+				goto out_prim_wait;
+			}
+	   
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+	}
+
+   	mga_flush_write_combine();
+    	atomic_inc(&dev_priv->pending_bufs);
+       	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+ 	MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp);
+   	prim->num_dwords = 0;
+	sarea_priv->last_enqueue = prim->prim_age;
+    
+   	next_idx = prim->idx + 1;
+    	if(next_idx >= MGA_NUM_PRIM_BUFS) 
+		next_idx = 0;
+
+    	dev_priv->next_prim = dev_priv->prim_bufs[next_idx];
+	return;
+
+ out_prim_wait:
+	prim->num_dwords = 0;
+	prim->sec_used = 0;
+	clear_bit(MGA_BUF_IN_USE, &prim->buffer_status);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status);
+	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+}
+
+int mga_advance_primary(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+   	drm_mga_prim_buf_t *prim_buffer;
+   	drm_device_dma_t  *dma      = dev->dma;
+   	int next_prim_idx;
+   	int ret = 0;
+   
+   	/* This needs to reset the primary buffer if available,
+	 * we should collect stats on how many times it bites
+	 * it's tail */
+	DRM_DEBUG("%s\n", __FUNCTION__);
+   
+   	next_prim_idx = dev_priv->current_prim_idx + 1;
+   	if(next_prim_idx >= MGA_NUM_PRIM_BUFS)
+     		next_prim_idx = 0;
+   	prim_buffer = dev_priv->prim_bufs[next_prim_idx];
+	set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+   
+      	/* In use is cleared in interrupt handler */
+   
+   	if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) {
+	   	add_wait_queue(&dev_priv->wait_queue, &entry);
+		current->state = TASK_INTERRUPTIBLE;
+
+	   	for (;;) {
+		   	mga_dma_schedule(dev, 0);
+		   	if(!test_and_set_bit(MGA_BUF_IN_USE, 
+					     &prim_buffer->buffer_status)) 
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	atomic_inc(&dma->total_missed_sched);
+		   	schedule();
+		   	if (signal_pending(current)) {
+			   	ret = -ERESTARTSYS;
+			   	break;
+			}
+		}
+	   	current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->wait_queue, &entry);
+	   	if(ret) return ret;
+	}
+	clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+   	/* This primary buffer is now free to use */
+   	prim_buffer->current_dma_ptr = prim_buffer->head;
+   	prim_buffer->num_dwords = 0;
+   	prim_buffer->sec_used = 0;
+	prim_buffer->prim_age = dev_priv->next_prim_age++;
+	if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) {
+	   mga_flush_queue(dev);
+	   mga_dma_quiescent(dev);
+	   mga_reset_freelist(dev);
+	   prim_buffer->prim_age = (dev_priv->next_prim_age += 2);
+	}
+
+	/* Reset all buffer status stuff */
+	clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status);
+
+   	dev_priv->current_prim = prim_buffer;
+   	dev_priv->current_prim_idx = next_prim_idx;
+   	return 0;
+}
+
+/* More dynamic performance decisions */
+static inline int mga_decide_to_fire(drm_device_t *dev)
+{
+   	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+      	drm_device_dma_t  *dma	    = dev->dma;
+
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) {
+	   	atomic_inc(&dma->total_prio);
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	atomic_inc(&dma->total_prio);
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	atomic_inc(&dma->total_prio);
+	   	return 1;
+	}
+   
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) {
+		if(test_bit(MGA_BUF_SWAP_PENDING, 
+			    &dev_priv->next_prim->buffer_status)) {
+			atomic_inc(&dma->total_dmas);
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) {
+			atomic_inc(&dma->total_hit);
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) {
+			atomic_inc(&dma->total_missed_free);
+			return 1;
+		}
+	}
+
+   	atomic_inc(&dma->total_tried);
+   	return 0;
+}
+
+int mga_dma_schedule(drm_device_t *dev, int locked)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+      	drm_device_dma_t  *dma	    = dev->dma;
+	int retval = 0;
+
+   	if (test_and_set_bit(0, &dev->dma_flag)) {
+		atomic_inc(&dma->total_missed_dma);
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+   
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) || 
+	   test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) ||
+	   test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+		locked = 1;
+	}
+   
+   	if (!locked && 
+	    !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
+	   	atomic_inc(&dma->total_missed_lock);
+	   	clear_bit(0, &dev->dma_flag);
+		DRM_DEBUG("Not locked\n");
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+   	DRM_DEBUG("I'm locked\n");
+
+   	if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) {
+	   	/* Fire dma buffer */
+	   	if(mga_decide_to_fire(dev)) {
+		   	DRM_DEBUG("idx :%d\n", dev_priv->next_prim->idx);
+			clear_bit(MGA_BUF_FORCE_FIRE, 
+				  &dev_priv->next_prim->buffer_status);
+		   	if(dev_priv->current_prim == dev_priv->next_prim) {
+				/* Schedule overflow for a later time */
+				set_bit(MGA_BUF_NEEDS_OVERFLOW,
+					&dev_priv->next_prim->buffer_status);
+			}
+		   	mga_fire_primary(dev, dev_priv->next_prim);
+		} else {
+			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+		}
+	} else {
+		DRM_DEBUG("I can't get the dispatch lock\n");
+	}
+   	
+	if (!locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+sch_out_wakeup:
+      	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	   atomic_read(&dev_priv->pending_bufs) == 0) {
+		/* Everything has been processed by the hardware */
+		clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		wake_up_interruptible(&dev_priv->flush_queue);
+	}
+
+	if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+	   dev_priv->tail->age < dev_priv->last_prim_age) {
+		clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+		DRM_DEBUG("Waking up buf queue\n");
+		wake_up_interruptible(&dev_priv->buf_queue);
+	} else if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+	   	DRM_DEBUG("Not waking buf_queue on %d %d\n", 
+			  atomic_read(&dev->total_irq), 
+			  dev_priv->last_prim_age);
+	}
+
+   	clear_bit(0, &dev->dma_flag);
+	return retval;
+}
+
+static void mga_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+    	drm_device_t	 *dev = (drm_device_t *)device;
+    	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+    	drm_mga_prim_buf_t *last_prim_buffer;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+    	atomic_inc(&dev->total_irq);
+	if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return;
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+   	last_prim_buffer = dev_priv->last_prim;
+    	last_prim_buffer->num_dwords = 0;
+    	last_prim_buffer->sec_used = 0;
+	dev_priv->sarea_priv->last_dispatch = 
+		dev_priv->last_prim_age = last_prim_buffer->prim_age;
+      	clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+      	clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status);
+      	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+      	atomic_dec(&dev_priv->pending_bufs);
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+}
+
+static void mga_dma_task_queue(void *device)
+{
+	DRM_DEBUG("%s\n", __FUNCTION__);
+	mga_dma_schedule((drm_device_t *)device, 0);
+}
+
+int mga_dma_cleanup(drm_device_t *dev)
+{
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if(dev->dev_private) {
+		drm_mga_private_t *dev_priv = 
+			(drm_mga_private_t *) dev->dev_private;
+      
+		if(dev_priv->ioremap) {
+			int temp = (dev_priv->warp_ucode_size + 
+				    dev_priv->primary_size + 
+				    PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+
+			drm_ioremapfree((void *) dev_priv->ioremap, temp);
+		}
+	   	if(dev_priv->status_page != NULL) {
+		   	iounmap(dev_priv->status_page);
+		}
+	   	if(dev_priv->real_status_page != 0UL) {
+		   	mga_free_page(dev, dev_priv->real_status_page);
+		}
+	   	if(dev_priv->prim_bufs != NULL) {
+		   	int i;
+		   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+			   	if(dev_priv->prim_bufs[i] != NULL) {
+			     		drm_free(dev_priv->prim_bufs[i],
+						 sizeof(drm_mga_prim_buf_t),
+						 DRM_MEM_DRIVER);
+				}
+			}
+		   	drm_free(dev_priv->prim_bufs, sizeof(void *) *
+				 (MGA_NUM_PRIM_BUFS + 1), 
+				 DRM_MEM_DRIVER);
+		}
+		if(dev_priv->head != NULL) {
+		   	mga_freelist_cleanup(dev);
+		}
+
+
+		drm_free(dev->dev_private, sizeof(drm_mga_private_t), 
+			 DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) {
+	drm_mga_private_t *dev_priv;
+	drm_map_t *sarea_map = NULL;
+	int i;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
+	if(dev_priv == NULL) return -ENOMEM;
+	dev->dev_private = (void *) dev_priv;
+
+	memset(dev_priv, 0, sizeof(drm_mga_private_t));
+
+	if((init->reserved_map_idx >= dev->map_count) ||
+	   (init->buffer_map_idx >= dev->map_count)) {
+		mga_dma_cleanup(dev);
+		DRM_DEBUG("reserved_map or buffer_map are invalid\n");
+		return -EINVAL;
+	}
+   
+	dev_priv->reserved_map_idx = init->reserved_map_idx;
+	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_mga_sarea_t *) 
+		((u8 *)sarea_map->handle + 
+		 init->sarea_priv_offset);
+
+	/* Scale primary size to the next page */
+	dev_priv->chipset = init->chipset;
+	dev_priv->frontOffset = init->frontOffset;
+	dev_priv->backOffset = init->backOffset;
+	dev_priv->depthOffset = init->depthOffset;
+	dev_priv->textureOffset = init->textureOffset;
+	dev_priv->textureSize = init->textureSize;
+	dev_priv->cpp = init->cpp;
+	dev_priv->sgram = init->sgram;
+	dev_priv->stride = init->stride;
+
+	dev_priv->mAccess = init->mAccess;
+   	init_waitqueue_head(&dev_priv->flush_queue);
+	init_waitqueue_head(&dev_priv->buf_queue);
+	dev_priv->WarpPipe = -1;
+
+   	DRM_DEBUG("chipset: %d ucode_size: %d backOffset: %x depthOffset: %x\n",
+		  dev_priv->chipset, dev_priv->warp_ucode_size, 
+		  dev_priv->backOffset, dev_priv->depthOffset);
+   	DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n",
+		  dev_priv->cpp, dev_priv->sgram, dev_priv->stride, 
+		  dev_priv->mAccess);
+   
+	memcpy(&dev_priv->WarpIndex, &init->WarpIndex, 
+	       sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES);
+
+   	for (i = 0 ; i < MGA_MAX_WARP_PIPES ; i++) 
+		DRM_DEBUG("warp pipe %d: installed: %d phys: %lx size: %x\n",
+			  i, 
+			  dev_priv->WarpIndex[i].installed,
+			  dev_priv->WarpIndex[i].phys_addr,
+			  dev_priv->WarpIndex[i].size);
+
+   	if(mga_init_primary_bufs(dev, init) != 0) {
+		DRM_ERROR("Can not initialize primary buffers\n");
+		mga_dma_cleanup(dev);
+		return -ENOMEM;
+	}
+   	dev_priv->real_status_page = mga_alloc_page(dev);
+      	if(dev_priv->real_status_page == 0UL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate status page\n");
+		return -ENOMEM;
+	}
+
+   	dev_priv->status_page = 
+		ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page),
+				PAGE_SIZE);
+
+   	if(dev_priv->status_page == NULL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not remap status page\n");
+		return -ENOMEM;
+	}
+
+   	/* Write status page when secend or softrap occurs */
+   	MGA_WRITE(MGAREG_PRIMPTR, 
+		  virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003);
+      
+
+	/* Private is now filled in, initialize the hardware */
+	{
+		PRIMLOCALS;
+		PRIMGETPTR( dev_priv );
+	   	   
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0x0100);
+		PRIMOUTREG(MGAREG_SOFTRAP, 0);
+		/* Poll for the first buffer to insure that
+		 * the status register will be correct
+		 */
+	   
+		mga_flush_write_combine();
+	   	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+
+		MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) | 
+					   PDEA_pagpxfer_enable));
+	   
+	   	while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ;
+	}
+
+	if(mga_freelist_init(dev) != 0) {
+	   	DRM_ERROR("Could not initialize freelist\n");
+	   	mga_dma_cleanup(dev);
+	   	return -ENOMEM;
+	}
+	return 0;
+}
+
+int mga_dma_init(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_init_t init;
+   
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	copy_from_user_ret(&init, (drm_mga_init_t *)arg, sizeof(init), -EFAULT);
+   
+	switch(init.func) {
+	case MGA_INIT_DMA:
+		return mga_dma_initialize(dev, &init);
+	case MGA_CLEANUP_DMA:
+		return mga_dma_cleanup(dev);
+	}
+
+	return -EINVAL;
+}
+
+int mga_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+	DRM_DEBUG("install irq handler %d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+	dev->tq.next	      = NULL;
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = mga_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+	MGA_WRITE(MGAREG_IEN, 0);
+   				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   mga_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+				/* After installing handler */
+   	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0x00000001);
+	return 0;
+}
+
+int mga_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+   	DRM_DEBUG("remove irq handler %d\n", irq);
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0);
+	free_irq(irq, dev);
+	return 0;
+}
+
+int mga_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+   
+	copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
+
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		return mga_irq_install(dev, ctl.irq);
+	case DRM_UNINST_HANDLER:
+		return mga_irq_uninstall(dev);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mga_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	int ret = 0;
+
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+
+   	if(dev_priv == NULL) {
+	   	return 0;
+	}
+   
+   	if(dev_priv->next_prim->num_dwords != 0) {
+   		current->state = TASK_INTERRUPTIBLE;
+   		add_wait_queue(&dev_priv->flush_queue, &entry);
+		set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		mga_dma_schedule(dev, 0);
+   		for (;;) {
+	   		if (!test_bit(MGA_IN_FLUSH, 
+				      &dev_priv->dispatch_status)) 
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+	      		schedule();
+	      		if (signal_pending(current)) {
+		   		ret = -EINTR; /* Can't restart */
+				clear_bit(MGA_IN_FLUSH, 
+					  &dev_priv->dispatch_status);
+		   		break;
+			}
+		}
+   		current->state = TASK_RUNNING;
+   		remove_wait_queue(&dev_priv->flush_queue, &entry);
+	}
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if(dev->dev_private == NULL) return;
+	if(dma->buflist == NULL) return;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+        mga_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+
+		/* Only buffers that need to get reclaimed ever 
+		 * get set to free 
+		 */
+		if (buf->pid == pid  && buf_priv) {
+			if(buf_priv->my_freelist->age == MGA_BUF_USED) 
+		     		buf_priv->my_freelist->age = MGA_BUF_FREE;
+		}
+	}
+}
+
+int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+   
+   	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+	       lock.context, current->pid, dev->lock.hw_lock->lock,
+	       lock.flags);
+
+	if (lock.context < 0) {
+		return -EINVAL;
+	}
+   
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+	
+	if (!ret) {
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   mga_flush_queue(dev);
+		   mga_dma_quiescent(dev);
+		}
+	}
+   
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+	return ret;
+}
+		
+int mga_flush_ioctl(struct inode *inode, struct file *filp, 
+		    unsigned int cmd, unsigned long arg)
+{
+       	drm_file_t	  *priv	  = filp->private_data;
+    	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+   	DRM_DEBUG("%s\n", __FUNCTION__);
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_flush_ioctl called without lock held\n");
+		return -EINVAL;
+	}
+
+   	if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) {
+		drm_mga_prim_buf_t *temp_buf;
+
+		temp_buf = dev_priv->current_prim;
+
+		if(temp_buf && temp_buf->num_dwords) {
+			set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status);
+			mga_advance_primary(dev);
+ 		}
+		mga_dma_schedule(dev, 1);
+	}
+   	if(lock.flags & _DRM_LOCK_QUIESCENT) {
+		mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+	}
+
+    	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_drm.h linux.18p17/drivers/char/drm/mga_drm.h
--- linux.vanilla/drivers/char/drm/mga_drm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_drm.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,269 @@
+/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRM_H_
+#define _MGA_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmMga.h)
+ */
+#ifndef _MGA_DEFINES_
+#define _MGA_DEFINES_
+
+#define MGA_F  0x1		/* fog */
+#define MGA_A  0x2		/* alpha */
+#define MGA_S  0x4		/* specular */
+#define MGA_T2 0x8		/* multitexture */
+
+#define MGA_WARP_TGZ            0
+#define MGA_WARP_TGZF           (MGA_F)
+#define MGA_WARP_TGZA           (MGA_A)
+#define MGA_WARP_TGZAF          (MGA_F|MGA_A)
+#define MGA_WARP_TGZS           (MGA_S)
+#define MGA_WARP_TGZSF          (MGA_S|MGA_F)
+#define MGA_WARP_TGZSA          (MGA_S|MGA_A)
+#define MGA_WARP_TGZSAF         (MGA_S|MGA_F|MGA_A)
+#define MGA_WARP_T2GZ           (MGA_T2)
+#define MGA_WARP_T2GZF          (MGA_T2|MGA_F)
+#define MGA_WARP_T2GZA          (MGA_T2|MGA_A)
+#define MGA_WARP_T2GZAF         (MGA_T2|MGA_A|MGA_F)
+#define MGA_WARP_T2GZS          (MGA_T2|MGA_S)
+#define MGA_WARP_T2GZSF         (MGA_T2|MGA_S|MGA_F)
+#define MGA_WARP_T2GZSA         (MGA_T2|MGA_S|MGA_A)
+#define MGA_WARP_T2GZSAF        (MGA_T2|MGA_S|MGA_F|MGA_A)
+
+#define MGA_MAX_G400_PIPES 16
+#define MGA_MAX_G200_PIPES  8	/* no multitex */
+#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES
+
+#define MGA_CARD_TYPE_G200 1
+#define MGA_CARD_TYPE_G400 2
+
+#define MGA_FRONT   0x1
+#define MGA_BACK    0x2
+#define MGA_DEPTH   0x4
+
+/* 3d state excluding texture units:
+ */
+#define MGA_CTXREG_DSTORG   0	/* validated */
+#define MGA_CTXREG_MACCESS  1	
+#define MGA_CTXREG_PLNWT    2	
+#define MGA_CTXREG_DWGCTL    3	
+#define MGA_CTXREG_ALPHACTRL 4
+#define MGA_CTXREG_FOGCOLOR  5
+#define MGA_CTXREG_WFLAG     6
+#define MGA_CTXREG_TDUAL0    7
+#define MGA_CTXREG_TDUAL1    8
+#define MGA_CTXREG_FCOL      9
+#define MGA_CTX_SETUP_SIZE   10
+
+/* 2d state
+ */
+#define MGA_2DREG_PITCH 	0
+#define MGA_2D_SETUP_SIZE 	1
+
+/* Each texture unit has a state:
+ */
+#define MGA_TEXREG_CTL        0
+#define MGA_TEXREG_CTL2       1
+#define MGA_TEXREG_FILTER     2
+#define MGA_TEXREG_BORDERCOL  3
+#define MGA_TEXREG_ORG        4 /* validated */
+#define MGA_TEXREG_ORG1       5
+#define MGA_TEXREG_ORG2       6
+#define MGA_TEXREG_ORG3       7
+#define MGA_TEXREG_ORG4       8
+#define MGA_TEXREG_WIDTH      9
+#define MGA_TEXREG_HEIGHT     10
+#define MGA_TEX_SETUP_SIZE    11
+
+/* What needs to be changed for the current vertex dma buffer?
+ */
+#define MGA_UPLOAD_CTX        0x1
+#define MGA_UPLOAD_TEX0       0x2
+#define MGA_UPLOAD_TEX1       0x4
+#define MGA_UPLOAD_PIPE       0x8
+#define MGA_UPLOAD_TEX0IMAGE  0x10 /* handled client-side */
+#define MGA_UPLOAD_TEX1IMAGE  0x20 /* handled client-side */
+#define MGA_UPLOAD_2D 	      0x40
+#define MGA_WAIT_AGE          0x80 /* handled client-side */
+#define MGA_UPLOAD_CLIPRECTS  0x100 /* handled client-side */
+#define MGA_DMA_FLUSH	      0x200 /* set when someone gets the lock
+                                       quiescent */
+
+/* 32 buffers of 64k each, total 2 meg.
+ */
+#define MGA_DMA_BUF_ORDER     16
+#define MGA_DMA_BUF_SZ        (1<<MGA_DMA_BUF_ORDER)
+#define MGA_DMA_BUF_NR        31
+
+/* Keep these small for testing.
+ */
+#define MGA_NR_SAREA_CLIPRECTS 8
+
+/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
+ * regions, subject to a minimum region size of (1<<16) == 64k. 
+ *
+ * Clients may subdivide regions internally, but when sharing between
+ * clients, the region size is the minimum granularity. 
+ */
+
+#define MGA_CARD_HEAP 0
+#define MGA_AGP_HEAP  1
+#define MGA_NR_TEX_HEAPS 2
+#define MGA_NR_TEX_REGIONS 16
+#define MGA_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+typedef struct _drm_mga_warp_index {
+   	int installed;
+   	unsigned long phys_addr;
+   	int size;
+} drm_mga_warp_index_t;
+
+typedef struct drm_mga_init {
+   	enum { 
+	   	MGA_INIT_DMA = 0x01,
+	       	MGA_CLEANUP_DMA = 0x02
+	} func;
+   	int reserved_map_agpstart;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	int sarea_priv_offset;
+   	int primary_size;
+   	int warp_ucode_size;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+        unsigned int agpTextureOffset;
+        unsigned int agpTextureSize;
+   	unsigned int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int chipset;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_WARP_PIPES];
+	unsigned int mAccess;
+} drm_mga_init_t;
+
+/* Warning: if you change the sarea structure, you must change the Xserver
+ * structures as well */
+
+typedef struct _drm_mga_tex_region {
+	unsigned char next, prev;	
+	unsigned char in_use;	
+	unsigned int age;			
+} drm_mga_tex_region_t;
+
+typedef struct _drm_mga_sarea {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex dma buffer.
+	 */
+   	unsigned int ContextState[MGA_CTX_SETUP_SIZE];
+   	unsigned int ServerState[MGA_2D_SETUP_SIZE];
+   	unsigned int TexState[2][MGA_TEX_SETUP_SIZE];
+   	unsigned int WarpPipe;
+   	unsigned int dirty;
+
+   	unsigned int nbox;
+   	drm_clip_rect_t boxes[MGA_NR_SAREA_CLIPRECTS];
+
+
+	/* Information about the most recently used 3d drawable.  The
+	 * client fills in the req_* fields, the server fills in the 
+	 * exported_ fields and puts the cliprects into boxes, above.
+	 *
+	 * The client clears the exported_drawable field before
+	 * clobbering the boxes data.
+	 */
+        unsigned int req_drawable;	 /* the X drawable id */
+	unsigned int req_draw_buffer;	 /* MGA_FRONT or MGA_BACK */
+
+        unsigned int exported_drawable;	 
+	unsigned int exported_index; 
+        unsigned int exported_stamp;	
+        unsigned int exported_buffers;	 
+        unsigned int exported_nfront;
+        unsigned int exported_nback;
+	int exported_back_x, exported_front_x, exported_w;	
+	int exported_back_y, exported_front_y, exported_h;
+   	drm_clip_rect_t exported_boxes[MGA_NR_SAREA_CLIPRECTS];
+   
+	/* Counters for aging textures and for client-side throttling.
+	 */
+        unsigned int last_enqueue;	/* last time a buffer was enqueued */
+	unsigned int last_dispatch;	/* age of the most recently dispatched buffer */
+	unsigned int last_quiescent;     /*  */
+
+
+	/* LRU lists for texture memory in agp space and on the card
+	 */
+	drm_mga_tex_region_t texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS+1];
+	unsigned int texAge[MGA_NR_TEX_HEAPS];
+	
+	/* Mechanism to validate card state.
+	 */
+   	int ctxOwner;
+} drm_mga_sarea_t;	
+
+/* Device specific ioctls:
+ */
+typedef struct _drm_mga_clear {
+	unsigned int clear_color;
+	unsigned int clear_depth;
+	unsigned int flags;
+} drm_mga_clear_t;
+
+typedef struct _drm_mga_swap {
+   	int dummy;
+} drm_mga_swap_t;
+
+typedef struct _drm_mga_iload {
+	int idx;
+	int length;
+	unsigned int destOrg;
+} drm_mga_iload_t;
+
+typedef struct _drm_mga_vertex {
+   	int idx;		/* buffer to queue */
+	int used;		/* bytes in use */
+	int discard;		/* client finished with buffer?  */
+} drm_mga_vertex_t;
+
+typedef struct _drm_mga_indices {
+   	int idx;		/* buffer to queue */
+	unsigned int start;		
+	unsigned int end;		
+	int discard;		/* client finished with buffer?  */
+} drm_mga_indices_t;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_drv.c linux.18p17/drivers/char/drm/mga_drv.c
--- linux.vanilla/drivers/char/drm/mga_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_drv.c	Sat Sep 30 22:50:49 2000
@@ -0,0 +1,666 @@
+/* mga_drv.c -- Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ * 
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "mga_drv.h"
+
+#include <linux/agp_backend.h>
+
+static void __attribute__((unused)) unused(void)
+{
+	agp_enable(0);
+}
+
+#define MGA_NAME	 "mga"
+#define MGA_DESC	 "Matrox g200/g400"
+#define MGA_DATE	 "20000719"
+#define MGA_MAJOR	 1
+#define MGA_MINOR	 0
+#define MGA_PATCHLEVEL	 0
+
+static drm_device_t	      mga_device;
+drm_ctx_t		      mga_res_ctx;
+
+static struct file_operations mga_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:   THIS_MODULE,
+#endif
+	open:	 mga_open,
+	flush:	 drm_flush,
+	release: mga_release,
+	ioctl:	 mga_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+   	poll:	 drm_poll,
+};
+
+static struct miscdevice      mga_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  MGA_NAME,
+	fops:  &mga_fops,
+};
+
+static drm_ioctl_desc_t	      mga_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { mga_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { mga_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { mga_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { mga_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { mga_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]    = { mga_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { mga_freebufs,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { mga_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { mga_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { mga_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { mga_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { mga_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { mga_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { mga_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	      = { mga_dma,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { mga_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { mga_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)]    = { mga_dma_init,    1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)]    = { mga_swap_bufs,   1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)]   = { mga_clear_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)]   = { mga_iload,       1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)]  = { mga_vertex,      1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)]   = { mga_flush_ioctl, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices,     1, 0 },
+};
+
+#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls)
+
+#ifdef MODULE
+static char		      *mga = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Matrox g200/g400");
+MODULE_PARM(mga, "s");
+
+#ifndef MODULE
+/* mga_options is called by the kernel to parse command-line options passed
+ * via the boot-loader (e.g., LILO).  It calls the insmod option routine,
+ * drm_parse_drm.
+ */
+
+static int __init mga_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("mga=", mga_options);
+#endif
+
+static int mga_setup(drm_device_t *dev)
+{
+	int i;
+	
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+			
+	DRM_DEBUG("\n");
+			
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+			
+	return 0;
+}
+
+
+static int mga_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) mga_irq_uninstall(dev);
+	
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+		
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+		
+		if (dev->agp->acquired && drm_agp.release)
+			(*drm_agp.release)();
+		
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+/* mga_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int mga_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+	
+#ifdef MODULE
+	drm_parse_options(mga);
+#endif
+	DRM_DEBUG("doing misc_register\n");
+	if ((retcode = misc_register(&mga_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, mga_misc.minor);
+	dev->name   = MGA_NAME;
+
+   	DRM_DEBUG("doing mem init\n");
+	drm_mem_init();
+	DRM_DEBUG("doing proc init\n");
+	drm_proc_init(dev);
+	DRM_DEBUG("doing agp init\n");
+	dev->agp    = drm_agp_init();
+      	if(dev->agp == NULL) {
+	   	DRM_INFO("The mga drm module requires the agpgart module"
+		         " to function correctly\nPlease load the agpgart"
+		         " module before you load the mga module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&mga_misc);
+	   	mga_takedown(dev);
+	   	return -ENOMEM;
+	}
+#ifdef CONFIG_MTRR
+   	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size * 1024 * 1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+	DRM_DEBUG("doing ctxbitmap init\n");
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&mga_misc);
+		mga_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 MGA_NAME,
+		 MGA_MAJOR,
+		 MGA_MINOR,
+		 MGA_PATCHLEVEL,
+		 MGA_DATE,
+		 mga_misc.minor);
+
+	return 0;
+}
+
+/* mga_cleanup is called via cleanup_module at module unload time. */
+
+static void mga_cleanup(void)
+{
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	if (misc_deregister(&mga_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	mga_dma_cleanup(dev);
+#ifdef CONFIG_MTRR
+   	if(dev->agp && dev->agp->agp_mtrr) {
+	   	int retval;
+	   	retval = mtrr_del(dev->agp->agp_mtrr, 
+				  dev->agp->agp_info.aper_base,
+				  dev->agp->agp_info.aper_size * 1024*1024);
+	   	DRM_DEBUG("mtrr_del = %d\n", retval);
+	}
+#endif
+
+	mga_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(mga_init);
+module_exit(mga_cleanup);
+
+
+int mga_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	copy_from_user_ret(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version),
+			   -EFAULT);
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		copy_to_user_ret(name, value, len, -EFAULT); \
+	}
+
+	version.version_major	   = MGA_MAJOR;
+	version.version_minor	   = MGA_MINOR;
+	version.version_patchlevel = MGA_PATCHLEVEL;
+
+	DRM_COPY(version.name, MGA_NAME);
+	DRM_COPY(version.date, MGA_DATE);
+	DRM_COPY(version.desc, MGA_DESC);
+
+	copy_to_user_ret((drm_version_t *)arg,
+			 &version,
+			 sizeof(version),
+			 -EFAULT);
+	return 0;
+}
+
+int mga_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &mga_device;
+	int	      retcode = 0;
+	
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return mga_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int mga_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	mga_reclaim_buffers(dev, priv->pid);
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	mga_reclaim_buffers(dev, priv->pid);
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+	
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+	   	return mga_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= MGA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &mga_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int mga_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+	
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	mga_dma_schedule(dev, 1);
+
+	if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+			  DRM_KERNEL_CONTEXT)) {
+	   DRM_ERROR("\n");
+	}
+
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_drv.h linux.18p17/drivers/char/drm/mga_drv.h
--- linux.vanilla/drivers/char/drm/mga_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_drv.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,445 @@
+/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRV_H_
+#define _MGA_DRV_H_
+
+#define MGA_BUF_IN_USE         0
+#define MGA_BUF_SWAP_PENDING   1
+#define MGA_BUF_FORCE_FIRE     2
+#define MGA_BUF_NEEDS_OVERFLOW 3
+
+typedef struct {
+	u32 buffer_status;
+   	unsigned int num_dwords;
+   	unsigned int max_dwords;
+   	u32 *current_dma_ptr;
+   	u32 *head;
+   	u32 phys_head;
+	unsigned int prim_age;
+   	int sec_used;
+   	int idx;
+} drm_mga_prim_buf_t;
+
+typedef struct _drm_mga_freelist {
+   	unsigned int age;
+   	drm_buf_t *buf;
+   	struct _drm_mga_freelist *next;
+   	struct _drm_mga_freelist *prev;
+} drm_mga_freelist_t;
+
+#define MGA_IN_DISPATCH   0
+#define MGA_IN_FLUSH      1
+#define MGA_IN_WAIT       2
+#define MGA_IN_GETBUF	  3
+
+typedef struct _drm_mga_private {
+   	u32 dispatch_status;
+	unsigned int next_prim_age;
+	__volatile__ unsigned int last_prim_age;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	drm_mga_sarea_t *sarea_priv;
+   	int primary_size;
+   	int warp_ucode_size;
+   	int chipset;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+   	int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int use_agp;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES];
+	unsigned int WarpPipe;
+   	atomic_t pending_bufs;
+   	void *status_page;
+   	unsigned long real_status_page;
+   	u8 *ioremap;
+   	drm_mga_prim_buf_t **prim_bufs;
+   	drm_mga_prim_buf_t *next_prim;
+   	drm_mga_prim_buf_t *last_prim;
+   	drm_mga_prim_buf_t *current_prim;
+   	int current_prim_idx;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *tail;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+      	wait_queue_head_t wait_queue;	/* Processes waiting until interrupt */
+	wait_queue_head_t buf_queue;    /* Processes waiting for a free buf */
+	/* Some validated register values:
+	 */	
+	u32 mAccess;
+} drm_mga_private_t;
+
+				/* mga_drv.c */
+extern int  mga_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_open(struct inode *inode, struct file *filp);
+extern int  mga_release(struct inode *inode, struct file *filp);
+extern int  mga_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* mga_dma.c */
+extern int  mga_dma_schedule(drm_device_t *dev, int locked);
+extern int  mga_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_irq_install(drm_device_t *dev, int irq);
+extern int  mga_irq_uninstall(drm_device_t *dev);
+extern int  mga_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+/* mga_dma_init does init and release */
+extern int mga_dma_init(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int mga_dma_cleanup(drm_device_t *dev);
+extern int mga_flush_ioctl(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern void mga_flush_write_combine(void);
+extern unsigned int mga_create_sync_tag(drm_device_t *dev);
+extern drm_buf_t *mga_freelist_get(drm_device_t *dev);
+extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf);
+extern int mga_advance_primary(drm_device_t *dev);
+extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid);
+
+
+				/* mga_bufs.c */
+extern int  mga_addbufs(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int  mga_infobufs(struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+				/* mga_state.c */
+extern int  mga_clear_bufs(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  mga_swap_bufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_iload(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_vertex(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_indices(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+				/* mga_context.c */
+extern int  mga_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  mga_context_switch(drm_device_t *dev, int old, int new);
+extern int  mga_context_switch_complete(drm_device_t *dev, int new);
+
+
+typedef enum {
+	TT_GENERAL,
+	TT_BLIT,
+	TT_VECTOR,
+	TT_VERTEX
+} transferType_t;
+
+typedef struct {
+   	drm_mga_freelist_t *my_freelist;
+	int discard;
+	int dispatched;
+} drm_mga_buf_priv_t;
+
+#define DWGREG0 	0x1c00
+#define DWGREG0_END 	0x1dff
+#define DWGREG1		0x2c00
+#define DWGREG1_END	0x2dff
+
+#define ISREG0(r)	(r >= DWGREG0 && r <= DWGREG0_END)
+#define ADRINDEX0(r)	(u8)((r - DWGREG0) >> 2)
+#define ADRINDEX1(r)	(u8)(((r - DWGREG1) >> 2) | 0x80)
+#define ADRINDEX(r)	(ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r)) 
+
+#define MGA_VERBOSE 0
+#define MGA_NUM_PRIM_BUFS 	8
+
+#define PRIMLOCALS	u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \
+			int outcount, num_dwords
+
+#define PRIM_OVERFLOW(dev, dev_priv, length) do {			\
+	drm_mga_prim_buf_t *tmp_buf =					\
+ 		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if( test_bit(MGA_BUF_NEEDS_OVERFLOW,				\
+		  &tmp_buf->buffer_status)) {				\
+ 		mga_advance_primary(dev);				\
+ 		mga_dma_schedule(dev, 1);				\
+ 	} else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length ||\
+ 	    tmp_buf->sec_used > MGA_DMA_BUF_NR/2) {			\
+		set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status);	\
+ 		mga_advance_primary(dev);				\
+ 		mga_dma_schedule(dev, 1);				\
+	}								\
+} while(0)
+
+#define PRIMGETPTR(dev_priv) do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if(MGA_VERBOSE)							\
+		DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__);		\
+	dma_ptr = tmp_buf->current_dma_ptr;				\
+	num_dwords = tmp_buf->num_dwords;				\
+	phys_head = tmp_buf->phys_head;					\
+	outcount = 0;							\
+} while(0)
+
+#define PRIMPTR(prim_buf) do {					\
+	if(MGA_VERBOSE)						\
+		DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__);	\
+	dma_ptr = prim_buf->current_dma_ptr;			\
+	num_dwords = prim_buf->num_dwords;			\
+	phys_head = prim_buf->phys_head;			\
+	outcount = 0;						\
+} while(0)
+
+#define PRIMFINISH(prim_buf) do {				\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	        \
+        }							\
+	prim_buf->num_dwords = num_dwords;			\
+	prim_buf->current_dma_ptr = dma_ptr;			\
+} while(0)
+
+#define PRIMADVANCE(dev_priv)	do {				\
+drm_mga_prim_buf_t *tmp_buf = 					\
+	dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	\
+        }							\
+	tmp_buf->num_dwords = num_dwords;      			\
+	tmp_buf->current_dma_ptr = dma_ptr;    			\
+} while (0)
+
+#define PRIMUPDATE(dev_priv)	do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	tmp_buf->sec_used++;						\
+} while (0)
+
+#define AGEBUF(dev_priv, buf_priv)	do {				\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	buf_priv->my_freelist->age = tmp_buf->prim_age;			\
+} while (0)
+
+
+#define PRIMOUTREG(reg, val) do {					\
+	tempIndex[outcount]=ADRINDEX(reg);				\
+	dma_ptr[1+outcount] = val;					\
+	if (MGA_VERBOSE)						\
+		DRM_DEBUG("   PRIMOUT %d: 0x%x -- 0x%x\n",		\
+		       num_dwords + 1 + outcount, ADRINDEX(reg), val);	\
+	if( ++outcount == 4) {						\
+		outcount = 0;						\
+		dma_ptr[0] = *(u32 *)tempIndex;				\
+		dma_ptr+=5;						\
+		num_dwords += 5;					\
+	}								\
+}while (0)
+
+/* A reduced set of the mga registers.
+ */
+
+#define MGAREG_MGA_EXEC 			0x0100
+#define MGAREG_ALPHACTRL 			0x2c7c
+#define MGAREG_AR0 				0x1c60
+#define MGAREG_AR1 				0x1c64
+#define MGAREG_AR2 				0x1c68
+#define MGAREG_AR3 				0x1c6c
+#define MGAREG_AR4 				0x1c70
+#define MGAREG_AR5 				0x1c74
+#define MGAREG_AR6 				0x1c78
+#define MGAREG_CXBNDRY				0x1c80
+#define MGAREG_CXLEFT 				0x1ca0
+#define MGAREG_CXRIGHT				0x1ca4
+#define MGAREG_DMAPAD 				0x1c54
+#define MGAREG_DSTORG 				0x2cb8
+#define MGAREG_DWGCTL 				0x1c00
+#define MGAREG_DWGSYNC				0x2c4c
+#define MGAREG_FCOL 				0x1c24
+#define MGAREG_FIFOSTATUS 			0x1e10
+#define MGAREG_FOGCOL 				0x1cf4
+#define MGAREG_FXBNDRY				0x1c84
+#define MGAREG_FXLEFT 				0x1ca8
+#define MGAREG_FXRIGHT				0x1cac
+#define MGAREG_ICLEAR 				0x1e18
+#define MGAREG_IEN 				0x1e1c
+#define MGAREG_LEN 				0x1c5c
+#define MGAREG_MACCESS				0x1c04
+#define MGAREG_PITCH 				0x1c8c
+#define MGAREG_PLNWT 				0x1c1c
+#define MGAREG_PRIMADDRESS 			0x1e58
+#define MGAREG_PRIMEND				0x1e5c
+#define MGAREG_PRIMPTR				0x1e50
+#define MGAREG_SECADDRESS 			0x2c40
+#define MGAREG_SECEND 				0x2c44
+#define MGAREG_SETUPADDRESS 			0x2cd0
+#define MGAREG_SETUPEND 			0x2cd4
+#define MGAREG_SOFTRAP				0x2c48
+#define MGAREG_SRCORG 				0x2cb4
+#define MGAREG_STATUS 				0x1e14
+#define MGAREG_STENCIL				0x2cc8
+#define MGAREG_STENCILCTL 			0x2ccc
+#define MGAREG_TDUALSTAGE0 			0x2cf8
+#define MGAREG_TDUALSTAGE1 			0x2cfc
+#define MGAREG_TEXBORDERCOL 			0x2c5c
+#define MGAREG_TEXCTL 				0x2c30
+#define MGAREG_TEXCTL2				0x2c3c
+#define MGAREG_TEXFILTER 			0x2c58
+#define MGAREG_TEXHEIGHT 			0x2c2c
+#define MGAREG_TEXORG 				0x2c24
+#define MGAREG_TEXORG1				0x2ca4
+#define MGAREG_TEXORG2				0x2ca8
+#define MGAREG_TEXORG3				0x2cac
+#define MGAREG_TEXORG4				0x2cb0
+#define MGAREG_TEXTRANS 			0x2c34
+#define MGAREG_TEXTRANSHIGH 			0x2c38
+#define MGAREG_TEXWIDTH 			0x2c28
+#define MGAREG_WACCEPTSEQ 			0x1dd4
+#define MGAREG_WCODEADDR 			0x1e6c
+#define MGAREG_WFLAG 				0x1dc4
+#define MGAREG_WFLAG1 				0x1de0
+#define MGAREG_WFLAGNB				0x1e64
+#define MGAREG_WFLAGNB1 			0x1e08
+#define MGAREG_WGETMSB				0x1dc8
+#define MGAREG_WIADDR 				0x1dc0
+#define MGAREG_WIADDR2				0x1dd8
+#define MGAREG_WMISC 				0x1e70
+#define MGAREG_WVRTXSZ				0x1dcc
+#define MGAREG_YBOT 				0x1c9c
+#define MGAREG_YDST 				0x1c90
+#define MGAREG_YDSTLEN				0x1c88
+#define MGAREG_YDSTORG				0x1c94
+#define MGAREG_YTOP 				0x1c98
+#define MGAREG_ZORG 				0x1c0c
+
+#define PDEA_pagpxfer_enable			0x2
+
+#define WIA_wmode_suspend			0x0
+#define WIA_wmode_start 			0x3
+#define WIA_wagp_agp				0x4
+
+#define DC_opcod_line_open 			0x0
+#define DC_opcod_autoline_open 			0x1
+#define DC_opcod_line_close 			0x2
+#define DC_opcod_autoline_close 		0x3
+#define DC_opcod_trap 				0x4
+#define DC_opcod_texture_trap 			0x6
+#define DC_opcod_bitblt 			0x8
+#define DC_opcod_iload 				0x9
+#define DC_atype_rpl 				0x0
+#define DC_atype_rstr 				0x10
+#define DC_atype_zi 				0x30
+#define DC_atype_blk 				0x40
+#define DC_atype_i 				0x70
+#define DC_linear_xy 				0x0 		
+#define DC_linear_linear 			0x80 		
+#define DC_zmode_nozcmp 			0x0
+#define DC_zmode_ze 				0x200
+#define DC_zmode_zne 				0x300
+#define DC_zmode_zlt 				0x400
+#define DC_zmode_zlte 				0x500
+#define DC_zmode_zgt 				0x600
+#define DC_zmode_zgte 				0x700
+#define DC_solid_disable 			0x0 		
+#define DC_solid_enable 			0x800 		
+#define DC_arzero_disable 			0x0 		
+#define DC_arzero_enable 			0x1000 		
+#define DC_sgnzero_disable 			0x0 		
+#define DC_sgnzero_enable 			0x2000 		
+#define DC_shftzero_disable 			0x0 		
+#define DC_shftzero_enable 			0x4000 		
+#define DC_bop_SHIFT 				16 		
+#define DC_trans_SHIFT 				20 		
+#define DC_bltmod_bmonolef 			0x0
+#define DC_bltmod_bmonowf 			0x8000000
+#define DC_bltmod_bplan 			0x2000000
+#define DC_bltmod_bfcol 			0x4000000
+#define DC_bltmod_bu32bgr 			0x6000000
+#define DC_bltmod_bu32rgb 			0xe000000
+#define DC_bltmod_bu24bgr 			0x16000000
+#define DC_bltmod_bu24rgb 			0x1e000000
+#define DC_pattern_disable 			0x0 		
+#define DC_pattern_enable 			0x20000000 	
+#define DC_transc_disable 			0x0 		
+#define DC_transc_enable 			0x40000000 	
+#define DC_clipdis_disable 			0x0 		
+#define DC_clipdis_enable 			0x80000000 	
+
+#define SETADD_mode_vertlist                   	0x0 	
+
+
+#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | 		\
+		       DC_sgnzero_enable | DC_shftzero_enable | 	\
+		       (0xC << DC_bop_SHIFT) | DC_clipdis_enable | 	\
+		       DC_solid_enable | DC_transc_enable)
+	  
+
+#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy |	\
+		      DC_solid_disable | DC_arzero_disable | 		\
+		      DC_sgnzero_enable | DC_shftzero_enable | 		\
+		      (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | 	\
+		      DC_pattern_disable | DC_transc_disable | 		\
+		      DC_clipdis_enable)				\
+
+#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\
+		       DC_arzero_enable | DC_sgnzero_enable |		\
+		       DC_atype_i)
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/mga_state.c linux.18p17/drivers/char/drm/mga_state.c
--- linux.vanilla/drivers/char/drm/mga_state.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/mga_state.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,1067 @@
+/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ * 	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "drm.h"
+
+static void mgaEmitClipRect(drm_mga_private_t * dev_priv,
+			    drm_clip_rect_t * box)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	/* This takes 10 dwords */
+	PRIMGETPTR(dev_priv);
+
+	/* Force reset of dwgctl (eliminates clip disable) */
+#if 0
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+#else
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+	PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+	PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+#endif
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1));
+	PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / 2);
+	PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / 2);
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaEmitContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	/* This takes a max of 15 dwords */
+	PRIMGETPTR(dev_priv);
+
+	PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]);
+	PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]);
+	PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]);
+	PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]);
+	PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]);
+	PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset);	/* invarient */
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]);
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+	} else {
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	}
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitTex(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 20 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(0x2d00 + 24 * 4, regs[MGA_TEXREG_WIDTH]);
+
+	PRIMOUTREG(0x2d00 + 34 * 4, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG400EmitTex0(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	int multitex = sarea_priv->WarpPipe & MGA_T2;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes a max of 30 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(0x2d00 + 49 * 4, 0);
+
+	PRIMOUTREG(0x2d00 + 57 * 4, 0);
+	PRIMOUTREG(0x2d00 + 53 * 4, 0);
+	PRIMOUTREG(0x2d00 + 61 * 4, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	if (!multitex) {
+		PRIMOUTREG(0x2d00 + 52 * 4, 0x40);
+		PRIMOUTREG(0x2d00 + 60 * 4, 0x40);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	}
+
+	PRIMOUTREG(0x2d00 + 54 * 4, regs[MGA_TEXREG_WIDTH] | 0x40);
+	PRIMOUTREG(0x2d00 + 62 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define TMC_map1_enable 		0x80000000
+
+static void mgaG400EmitTex1(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[1];
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 25 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2,
+		   regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(0x2d00 + 49 * 4, 0);
+
+	PRIMOUTREG(0x2d00 + 57 * 4, 0);
+	PRIMOUTREG(0x2d00 + 53 * 4, 0);
+	PRIMOUTREG(0x2d00 + 61 * 4, 0);
+	PRIMOUTREG(0x2d00 + 52 * 4, regs[MGA_TEXREG_WIDTH] | 0x40);
+
+	PRIMOUTREG(0x2d00 + 60 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define EMIT_PIPE 50
+static void mgaG400EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	float fParam = 12800.0f;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 50 dwords */
+
+	/* Establish vertex size.  
+	 */
+	PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	if (pipe & MGA_T2) {
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000);
+	} else {
+		if (dev_priv->WarpPipe & MGA_T2) {
+			/* Flush the WARP pipe */
+			PRIMOUTREG(MGAREG_YDST, 0);
+			PRIMOUTREG(MGAREG_FXLEFT, 0);
+			PRIMOUTREG(MGAREG_FXRIGHT, 1);
+			PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD);
+
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+			PRIMOUTREG(MGAREG_TEXCTL2, 0 | 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+		}
+
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000);
+	}
+
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(MGAREG_WFLAG1, 0);
+	PRIMOUTREG(0x2d00 + 56 * 4, *((u32 *) (&fParam)));
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMOUTREG(0x2d00 + 49 * 4, 0);	/* Tex stage 0 */
+	PRIMOUTREG(0x2d00 + 57 * 4, 0);	/* Tex stage 0 */
+	PRIMOUTREG(0x2d00 + 53 * 4, 0);	/* Tex stage 1 */
+	PRIMOUTREG(0x2d00 + 61 * 4, 0);	/* Tex stage 1 */
+
+	PRIMOUTREG(0x2d00 + 54 * 4, 0x40);	/* Tex stage 0 : w */
+	PRIMOUTREG(0x2d00 + 62 * 4, 0x40);	/* Tex stage 0 : h */
+	PRIMOUTREG(0x2d00 + 52 * 4, 0x40);	/* Tex stage 1 : w */
+	PRIMOUTREG(0x2d00 + 60 * 4, 0x40);	/* Tex stage 1 : h */
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR2,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 15 dwords */
+
+	PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_WVRTXSZ, 7);
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(0x2d00 + 24 * 4, 0);	/* tex w/h */
+
+	PRIMOUTREG(0x2d00 + 25 * 4, 0x100);
+	PRIMOUTREG(0x2d00 + 34 * 4, 0);	/* tex w/h */
+	PRIMOUTREG(0x2d00 + 42 * 4, 0xFFFF);
+	PRIMOUTREG(0x2d00 + 60 * 4, 0xFFFF);
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+
+	PRIMADVANCE( dev_priv );
+}
+
+static void mgaEmitState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		int multitex = sarea_priv->WarpPipe & MGA_T2;
+
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG400EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG400EmitTex0(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+
+		if ((dirty & MGA_UPLOAD_TEX1) && multitex) {
+			mgaG400EmitTex1(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX1;
+		}
+	} else {
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG200EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG200EmitTex(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+	}
+}
+
+
+/* Disallow all write destinations except the front and backbuffer.
+ */
+static int mgaVerifyContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset &&
+	    regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) {
+		DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n",
+			  regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset,
+			  dev_priv->backOffset);
+		regs[MGA_CTXREG_DSTORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Disallow texture reads from PCI space.
+ */
+static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) {
+		DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n",
+			  sarea_priv->TexState[unit][MGA_TEXREG_ORG],
+			  unit);
+		sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mgaVerifyState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	int rv = 0;
+
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	if (dirty & MGA_UPLOAD_CTX)
+		rv |= mgaVerifyContext(dev_priv);
+
+	if (dirty & MGA_UPLOAD_TEX0)
+		rv |= mgaVerifyTex(dev_priv, 0);
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		if (dirty & MGA_UPLOAD_TEX1)
+			rv |= mgaVerifyTex(dev_priv, 1);
+
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES);
+	} else {
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES);
+	}
+
+	return rv == 0;
+}
+
+static int mgaVerifyIload(drm_mga_private_t * dev_priv,
+			  unsigned long bus_address,
+			  unsigned int dstOrg, int length)
+{
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (dstOrg < dev_priv->textureOffset ||
+	    dstOrg + length >
+	    (dev_priv->textureOffset + dev_priv->textureSize)) {
+		return -EINVAL;
+	}
+	if (length % 64) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* This copies a 64 byte aligned agp region to the frambuffer
+ * with a standard blit, the ioctl needs to do checking */
+
+static void mga_dma_dispatch_tex_blit(drm_device_t * dev,
+				      unsigned long bus_address,
+				      int length, unsigned int destOrg)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	int use_agp = PDEA_pagpxfer_enable | 0x00000001;
+	u16 y2;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	y2 = length / 64;
+
+	PRIM_OVERFLOW(dev, dev_priv, 30);
+	PRIMGETPTR(dev_priv);
+
+	PRIMOUTREG(MGAREG_DSTORG, destOrg);
+	PRIMOUTREG(MGAREG_MACCESS, 0x00000000);
+	DRM_DEBUG("srcorg : %lx\n", bus_address | use_agp);
+	PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp);
+	PRIMOUTREG(MGAREG_AR5, 64);
+
+	PRIMOUTREG(MGAREG_PITCH, 64);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	PRIMOUTREG(MGAREG_AR0, 63);
+	PRIMOUTREG(MGAREG_AR3, 0);
+	PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16));
+	PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2);
+
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned long address = (unsigned long) buf->bus_address;
+	int length = buf->used;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	int primary_needed;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	DRM_DEBUG("dispatch vertex %d addr 0x%lx, "
+		  "length 0x%x nbox %d dirty %x\n",
+		  buf->idx, address, length,
+		  sarea_priv->nbox, sarea_priv->dirty);
+
+	DRM_DEBUG("used : %d, total : %d\n", buf->used, buf->total);
+
+	if (buf->used) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).  
+		 */
+		buf_priv->dispatched = 1;
+		primary_needed = (50 + 15 + 15 + 30 + 25 +
+				  10 + 15 * MGA_NR_SAREA_CLIPRECTS);
+		PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+		mgaEmitState(dev_priv);
+		do {
+			if (i < sarea_priv->nbox) {
+				DRM_DEBUG("idx %d Emit box %d/%d:"
+					  "%d,%d - %d,%d\n",
+					  buf->idx,
+					  i, sarea_priv->nbox,
+					  sarea_priv->boxes[i].x1,
+					  sarea_priv->boxes[i].y1,
+					  sarea_priv->boxes[i].x2,
+					  sarea_priv->boxes[i].y2);
+
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SECADDRESS,
+				   ((u32) address) | TT_VERTEX);
+			PRIMOUTREG(MGAREG_SECEND,
+				   (((u32) (address + length)) | use_agp));
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+
+
+}
+
+
+static void mga_dma_dispatch_indices(drm_device_t * dev,
+				     drm_buf_t * buf,
+				     unsigned int start, unsigned int end)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int address = (unsigned int) buf->bus_address;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	int primary_needed;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	DRM_DEBUG("dispatch indices %d addr 0x%x, "
+		  "start 0x%x end 0x%x nbox %d dirty %x\n",
+		  buf->idx, address, start, end,
+		  sarea_priv->nbox, sarea_priv->dirty);
+
+	if (start != end) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).  
+		 */
+		buf_priv->dispatched = 1;
+		primary_needed = (50 + 15 + 15 + 30 + 25 +
+				  10 + 15 * MGA_NR_SAREA_CLIPRECTS);
+		PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+		mgaEmitState(dev_priv);
+
+		do {
+			if (i < sarea_priv->nbox) {
+				DRM_DEBUG("idx %d Emit box %d/%d:"
+					  "%d,%d - %d,%d\n",
+					  buf->idx,
+					  i, sarea_priv->nbox,
+					  sarea_priv->boxes[i].x1,
+					  sarea_priv->boxes[i].y1,
+					  sarea_priv->boxes[i].x2,
+					  sarea_priv->boxes[i].y2);
+
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SETUPADDRESS,
+				   ((address + start) |
+				    SETADD_mode_vertlist));
+			PRIMOUTREG(MGAREG_SETUPEND,
+				   ((address + end) | use_agp));
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+}
+
+
+static void mga_dma_dispatch_clear(drm_device_t * dev, int flags,
+				   unsigned int clear_color,
+				   unsigned int clear_zval)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	unsigned int cmd;
+	int i;
+	int primary_needed;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (dev_priv->sgram)
+		cmd = MGA_CLEAR_CMD | DC_atype_blk;
+	else
+		cmd = MGA_CLEAR_CMD | DC_atype_rstr;
+
+	primary_needed = nbox * 70;
+	if (primary_needed == 0)
+		primary_needed = 70;
+	PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+	PRIMGETPTR(dev_priv);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int height = pbox[i].y2 - pbox[i].y1;
+
+		DRM_DEBUG("dispatch clear %d,%d-%d,%d flags %x!\n",
+			  pbox[i].x1, pbox[i].y1, pbox[i].x2,
+			  pbox[i].y2, flags);
+
+		if (flags & MGA_FRONT) {
+			DRM_DEBUG("clear front\n");
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_BACK) {
+			DRM_DEBUG("clear back\n");
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_DEPTH) {
+			DRM_DEBUG("clear depth\n");
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_zval);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_swap(drm_device_t * dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int i;
+	int primary_needed;
+	PRIMLOCALS;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	primary_needed = nbox * 5;
+	primary_needed += 65;
+	PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+	PRIMGETPTR(dev_priv);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7100);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+
+	PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+	PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess);
+	PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset);
+	PRIMOUTREG(MGAREG_AR5, dev_priv->stride / 2);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int h = pbox[i].y2 - pbox[i].y1;
+		unsigned int start = pbox[i].y1 * dev_priv->stride / 2;
+
+		DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+			  pbox[i].x1, pbox[i].y1, pbox[i].x2, pbox[i].y2);
+
+		PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1);
+		PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1);
+		PRIMOUTREG(MGAREG_FXBNDRY,
+			   pbox[i].x1 | ((pbox[i].x2 - 1) << 16));
+		PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC,
+			   (pbox[i].y1 << 16) | h);
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMADVANCE(dev_priv);
+}
+
+int mga_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_clear_t clear;
+
+	copy_from_user_ret(&clear, (drm_mga_clear_t *) arg, sizeof(clear),
+			   -EFAULT);
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_clear(dev, clear.flags,
+			       clear.clear_color, clear.clear_depth);
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_swap_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_swap(dev);
+	PRIMUPDATE(dev_priv);
+	set_bit(MGA_BUF_SWAP_PENDING,
+		&dev_priv->current_prim->buffer_status);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_iload(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_iload_t iload;
+	unsigned long bus_address;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	DRM_DEBUG("Starting Iload\n");
+	copy_from_user_ret(&iload, (drm_mga_iload_t *) arg, sizeof(iload),
+			   -EFAULT);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_iload called without lock held\n");
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[iload.idx];
+	buf_priv = buf->dev_private;
+	bus_address = buf->bus_address;
+	DRM_DEBUG("bus_address %lx, length %d, destorg : %x\n",
+		  bus_address, iload.length, iload.destOrg);
+
+	if (mgaVerifyIload(dev_priv,
+			   bus_address, iload.destOrg, iload.length)) {
+		mga_freelist_put(dev, buf);
+		return -EINVAL;
+	}
+
+	sarea_priv->dirty |= MGA_UPLOAD_CTX;
+
+	mga_dma_dispatch_tex_blit(dev, bus_address, iload.length,
+				  iload.destOrg);
+	AGEBUF(dev_priv, buf_priv);
+	buf_priv->discard = 1;
+	mga_freelist_put(dev, buf);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_vertex_t vertex;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	copy_from_user_ret(&vertex, (drm_mga_vertex_t *) arg,
+			   sizeof(vertex), -EFAULT);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("mga_vertex\n");
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	buf->used = vertex.used;
+	buf_priv->discard = vertex.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (vertex.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		DRM_DEBUG("bad state\n");
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_vertex(dev, buf);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+int mga_indices(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_indices_t indices;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	copy_from_user_ret(&indices, (drm_mga_indices_t *) arg,
+			   sizeof(indices), -EFAULT);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_indices called without lock held\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("mga_indices\n");
+
+	buf = dma->buflist[indices.idx];
+	buf_priv = buf->dev_private;
+
+	buf_priv->discard = indices.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (indices.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_indices(dev, buf, indices.start, indices.end);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+
+static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d)
+{
+	int i;
+	drm_buf_t *buf;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = mga_freelist_get(dev);
+		if (!buf)
+			break;
+		buf->pid = current->pid;
+		copy_to_user_ret(&d->request_indices[i],
+				 &buf->idx, sizeof(buf->idx), -EFAULT);
+		copy_to_user_ret(&d->request_sizes[i],
+				 &buf->total, sizeof(buf->total), -EFAULT);
+		++d->granted_count;
+	}
+	return 0;
+}
+
+int mga_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int retcode = 0;
+	drm_dma_t d;
+	DRM_DEBUG("%s\n", __FUNCTION__);
+
+	copy_from_user_ret(&d, (drm_dma_t *) arg, sizeof(d), -EFAULT);
+	DRM_DEBUG("%d %d: %d send, %d req\n",
+		  current->pid, d.context, d.send_count, d.request_count);
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_dma called without lock held\n");
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if (d.send_count != 0) {
+		DRM_ERROR
+		    ("Process %d trying to send %d buffers via drmDMA\n",
+		     current->pid, d.send_count);
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR
+		    ("Process %d trying to get %d buffers (of %d max)\n",
+		     current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if (d.request_count) {
+		retcode = mga_dma_get_buffers(dev, &d);
+	}
+
+	DRM_DEBUG("%d returning, granted = %d\n",
+		  current->pid, d.granted_count);
+	copy_to_user_ret((drm_dma_t *) arg, &d, sizeof(d), -EFAULT);
+	return retcode;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/proc.c linux.18p17/drivers/char/drm/proc.c
--- linux.vanilla/drivers/char/drm/proc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/proc.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,577 @@
+/* proc.c -- /proc support for DRM -*- linux-c -*-
+ * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static struct proc_dir_entry *drm_root	   = NULL;
+static struct proc_dir_entry *drm_dev_root = NULL;
+static char		     drm_slot_name[64];
+
+static int	   drm_name_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+static int	   drm_vm_info(char *buf, char **start, off_t offset,
+			       int len, int *eof, void *data);
+static int	   drm_clients_info(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data);
+static int	   drm_queues_info(char *buf, char **start, off_t offset,
+				   int len, int *eof, void *data);
+static int	   drm_bufs_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+#if DRM_DEBUG_CODE
+static int	   drm_vma_info(char *buf, char **start, off_t offset,
+				int len, int *eof, void *data);
+#endif
+#if DRM_DMA_HISTOGRAM
+static int	   drm_histo_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+#endif
+
+struct drm_proc_list {
+	const char *name;
+	int	   (*f)(char *, char **, off_t, int, int *, void *);
+} drm_proc_list[] = {
+	{ "name",    drm_name_info    },
+	{ "mem",     drm_mem_info     },
+	{ "vm",	     drm_vm_info      },
+	{ "clients", drm_clients_info },
+	{ "queues",  drm_queues_info  },
+	{ "bufs",    drm_bufs_info    },
+#if DRM_DEBUG_CODE
+	{ "vma",     drm_vma_info     },
+#endif
+#if DRM_DMA_HISTOGRAM
+	{ "histo",   drm_histo_info   },
+#endif
+};
+#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0]))
+
+int drm_proc_init(drm_device_t *dev)
+{
+	struct proc_dir_entry *ent;
+	int		      i, j;
+
+	drm_root = create_proc_entry("dri", S_IFDIR, NULL);
+	if (!drm_root) {
+		DRM_ERROR("Cannot create /proc/dri\n");
+		return -1;
+	}
+
+				/* Instead of doing this search, we should
+				   add some global support for /proc/dri. */
+	for (i = 0; i < 8; i++) {
+		sprintf(drm_slot_name, "dri/%d", i);
+		drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
+		if (!drm_dev_root) {
+			DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
+			remove_proc_entry("dri", NULL);
+		}
+		if (drm_dev_root->nlink == 2) break;
+		drm_dev_root = NULL;
+	}
+	if (!drm_dev_root) {
+		DRM_ERROR("Cannot find slot in /proc/dri\n");
+		return -1;
+	}
+
+	for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+		ent = create_proc_entry(drm_proc_list[i].name,
+					S_IFREG|S_IRUGO, drm_dev_root);
+		if (!ent) {
+			DRM_ERROR("Cannot create /proc/%s/%s\n",
+				  drm_slot_name, drm_proc_list[i].name);
+			for (j = 0; j < i; j++)
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			remove_proc_entry(drm_slot_name, NULL);
+			remove_proc_entry("dri", NULL);
+			return -1;
+		}
+		ent->read_proc = drm_proc_list[i].f;
+		ent->data      = dev;
+	}
+
+	return 0;
+}
+
+
+int drm_proc_cleanup(void)
+{
+	int i;
+	
+	if (drm_root) {
+		if (drm_dev_root) {
+			for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			}
+			remove_proc_entry(drm_slot_name, NULL);
+		}
+		remove_proc_entry("dri", NULL);
+		remove_proc_entry(DRM_NAME, NULL);
+	}
+	drm_root = drm_dev_root = NULL;
+	return 0;
+}
+
+static int drm_name_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	if (dev->unique) {
+		DRM_PROC_PRINT("%s 0x%x %s\n",
+			       dev->name, dev->device, dev->unique);
+	} else {
+		DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device);
+	}
+	return len;
+}
+
+static int _drm_vm_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_map_t    *map;
+				/* Hardcoded from _DRM_FRAME_BUFFER,
+                                   _DRM_REGISTERS, _DRM_SHM, and
+                                   _DRM_AGP. */
+	const char   *types[] = { "FB", "REG", "SHM", "AGP" };
+	const char   *type;
+	int	     i;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("slot	 offset	      size type flags	 "
+		       "address mtrr\n\n");
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->type < 0 || map->type > 3) type = "??";
+		else				    type = types[map->type];
+		DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s  0x%02x 0x%08lx ",
+			       i,
+			       map->offset,
+			       map->size,
+			       type,
+			       map->flags,
+			       (unsigned long)map->handle);
+		if (map->mtrr < 0) {
+			DRM_PROC_PRINT("none\n");
+		} else {
+			DRM_PROC_PRINT("%4d\n", map->mtrr);
+		}
+	}
+
+	return len;
+}
+
+static int drm_vm_info(char *buf, char **start, off_t offset, int len,
+		       int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vm_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_queues_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     i;
+	drm_queue_t  *q;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("  ctx/flags   use   fin"
+		       "   blk/rw/rwf  wait    flushed	   queued"
+		       "      locks\n\n");
+	for (i = 0; i < dev->queue_count; i++) {
+		q = dev->queuelist[i];
+		atomic_inc(&q->use_count);
+		DRM_PROC_PRINT_RET(atomic_dec(&q->use_count),
+				   "%5d/0x%03x %5d %5d"
+				   " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n",
+				   i,
+				   q->flags,
+				   atomic_read(&q->use_count),
+				   atomic_read(&q->finalization),
+				   atomic_read(&q->block_count),
+				   atomic_read(&q->block_read) ? 'r' : '-',
+				   atomic_read(&q->block_write) ? 'w' : '-',
+				   waitqueue_active(&q->read_queue) ? 'r':'-',
+				   waitqueue_active(&q->write_queue) ? 'w':'-',
+				   waitqueue_active(&q->flush_queue) ? 'f':'-',
+				   DRM_BUFCOUNT(&q->waitlist),
+				   atomic_read(&q->total_flushed),
+				   atomic_read(&q->total_queued),
+				   atomic_read(&q->total_locks));
+		atomic_dec(&q->use_count);
+	}
+	
+	return len;
+}
+
+static int drm_queues_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_queues_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+/* drm_bufs_info is called whenever a process reads
+   /dev/drm/<dev>/bufs. */
+
+static int _drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma)	return 0;
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT(" o     size count  free	 segs pages    kB\n\n");
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].buf_count)
+			DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n",
+				       i,
+				       dma->bufs[i].buf_size,
+				       dma->bufs[i].buf_count,
+				       atomic_read(&dma->bufs[i]
+						   .freelist.count),
+				       dma->bufs[i].seg_count,
+				       dma->bufs[i].seg_count
+				       *(1 << dma->bufs[i].page_order),
+				       (dma->bufs[i].seg_count
+					* (1 << dma->bufs[i].page_order))
+				       * PAGE_SIZE / 1024);
+	}
+	DRM_PROC_PRINT("\n");
+	for (i = 0; i < dma->buf_count; i++) {
+		if (i && !(i%32)) DRM_PROC_PRINT("\n");
+		DRM_PROC_PRINT(" %d", dma->buflist[i]->list);
+	}
+	DRM_PROC_PRINT("\n");
+
+	return len;
+}
+
+static int drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_bufs_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_clients_info(char *buf, char **start, off_t offset, int len,
+			     int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_file_t   *priv;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("a dev	pid    uid	magic	  ioctls\n\n");
+	for (priv = dev->file_first; priv; priv = priv->next) {
+		DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
+			       priv->authenticated ? 'y' : 'n',
+			       priv->minor,
+			       priv->pid,
+			       priv->uid,
+			       priv->magic,
+			       priv->ioctl_count);
+	}
+
+	return len;
+}
+
+static int drm_clients_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_clients_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+#if DRM_DEBUG_CODE
+
+#define DRM_VMA_VERBOSE 0
+
+static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t	      *dev = (drm_device_t *)data;
+	drm_vma_entry_t	      *pt;
+	struct vm_area_struct *vma;
+#if DRM_VMA_VERBOSE
+	unsigned long	      i;
+	unsigned long	      address;
+	pgd_t		      *pgd;
+	pmd_t		      *pmd;
+	pte_t		      *pte;
+#endif
+#if defined(__i386__)
+	unsigned int	      pgprot;
+#endif
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n",
+		       atomic_read(&dev->vma_count),
+		       high_memory, virt_to_phys(high_memory));
+	for (pt = dev->vmalist; pt; pt = pt->next) {
+		if (!(vma = pt->vma)) continue;
+		DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx",
+			       pt->pid,
+			       vma->vm_start,
+			       vma->vm_end,
+			       vma->vm_flags & VM_READ	   ? 'r' : '-',
+			       vma->vm_flags & VM_WRITE	   ? 'w' : '-',
+			       vma->vm_flags & VM_EXEC	   ? 'x' : '-',
+			       vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+			       vma->vm_flags & VM_LOCKED   ? 'l' : '-',
+			       vma->vm_flags & VM_IO	   ? 'i' : '-',
+			       VM_OFFSET(vma));
+		
+#if defined(__i386__)
+		pgprot = pgprot_val(vma->vm_page_prot);
+		DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c",
+			       pgprot & _PAGE_PRESENT  ? 'p' : '-',
+			       pgprot & _PAGE_RW       ? 'w' : 'r',
+			       pgprot & _PAGE_USER     ? 'u' : 's',
+			       pgprot & _PAGE_PWT      ? 't' : 'b',
+			       pgprot & _PAGE_PCD      ? 'u' : 'c',
+			       pgprot & _PAGE_ACCESSED ? 'a' : '-',
+			       pgprot & _PAGE_DIRTY    ? 'd' : '-',
+			       pgprot & _PAGE_PSE      ? 'm' : 'k',
+			       pgprot & _PAGE_GLOBAL   ? 'g' : 'l' );
+#endif		
+		DRM_PROC_PRINT("\n");
+#if 0
+		for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) {
+			pgd = pgd_offset(vma->vm_mm, i);
+			pmd = pmd_offset(pgd, i);
+			pte = pte_offset(pmd, i);
+			if (pte_present(*pte)) {
+				address = __pa(pte_page(*pte))
+					+ (i & (PAGE_SIZE-1));
+				DRM_PROC_PRINT("      0x%08lx -> 0x%08lx"
+					       " %c%c%c%c%c\n",
+					       i,
+					       address,
+					       pte_read(*pte)  ? 'r' : '-',
+					       pte_write(*pte) ? 'w' : '-',
+					       pte_exec(*pte)  ? 'x' : '-',
+					       pte_dirty(*pte) ? 'd' : '-',
+					       pte_young(*pte) ? 'a' : '-' );
+			} else {
+				DRM_PROC_PRINT("      0x%08lx\n", i);
+			}
+		}
+#endif
+	}
+	
+	return len;
+}
+
+static int drm_vma_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vma_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
+
+
+#if DRM_DMA_HISTOGRAM
+static int _drm_histo_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+	unsigned long	 slot_value = DRM_DMA_HISTOGRAM_INITIAL;
+	unsigned long	 prev_value = 0;
+	drm_buf_t	 *buffer;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	DRM_PROC_PRINT("general statistics:\n");
+	DRM_PROC_PRINT("total	 %10u\n", atomic_read(&dev->histo.total));
+	DRM_PROC_PRINT("open	 %10u\n", atomic_read(&dev->total_open));
+	DRM_PROC_PRINT("close	 %10u\n", atomic_read(&dev->total_close));
+	DRM_PROC_PRINT("ioctl	 %10u\n", atomic_read(&dev->total_ioctl));
+	DRM_PROC_PRINT("irq	 %10u\n", atomic_read(&dev->total_irq));
+	DRM_PROC_PRINT("ctx	 %10u\n", atomic_read(&dev->total_ctx));
+	
+	DRM_PROC_PRINT("\nlock statistics:\n");
+	DRM_PROC_PRINT("locks	 %10u\n", atomic_read(&dev->total_locks));
+	DRM_PROC_PRINT("unlocks	 %10u\n", atomic_read(&dev->total_unlocks));
+	DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends));
+	DRM_PROC_PRINT("sleeps	 %10u\n", atomic_read(&dev->total_sleeps));
+
+
+	if (dma) {
+		DRM_PROC_PRINT("\ndma statistics:\n");
+		DRM_PROC_PRINT("prio	 %10u\n",
+			       atomic_read(&dma->total_prio));
+		DRM_PROC_PRINT("bytes	 %10u\n",
+			       atomic_read(&dma->total_bytes));
+		DRM_PROC_PRINT("dmas	 %10u\n",
+			       atomic_read(&dma->total_dmas));
+		DRM_PROC_PRINT("missed:\n");
+		DRM_PROC_PRINT("  dma	 %10u\n",
+			       atomic_read(&dma->total_missed_dma));
+		DRM_PROC_PRINT("  lock	 %10u\n",
+			       atomic_read(&dma->total_missed_lock));
+		DRM_PROC_PRINT("  free	 %10u\n",
+			       atomic_read(&dma->total_missed_free));
+		DRM_PROC_PRINT("  sched	 %10u\n",
+			       atomic_read(&dma->total_missed_sched));
+		DRM_PROC_PRINT("tried	 %10u\n",
+			       atomic_read(&dma->total_tried));
+		DRM_PROC_PRINT("hit	 %10u\n",
+			       atomic_read(&dma->total_hit));
+		DRM_PROC_PRINT("lost	 %10u\n",
+			       atomic_read(&dma->total_lost));
+		
+		buffer = dma->next_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("next_buffer    none\n");
+		}
+		buffer = dma->this_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("this_buffer    none\n");
+		}
+	}
+	
+
+	DRM_PROC_PRINT("\nvalues:\n");
+	if (dev->lock.hw_lock) {
+		DRM_PROC_PRINT("lock	       0x%08x\n",
+			       dev->lock.hw_lock->lock);
+	} else {
+		DRM_PROC_PRINT("lock		     none\n");
+	}
+	DRM_PROC_PRINT("context_flag   0x%08lx\n", dev->context_flag);
+	DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag);
+	DRM_PROC_PRINT("dma_flag       0x%08lx\n", dev->dma_flag);
+
+	DRM_PROC_PRINT("queue_count    %10d\n",	 dev->queue_count);
+	DRM_PROC_PRINT("last_context   %10d\n",	 dev->last_context);
+	DRM_PROC_PRINT("last_switch    %10lu\n", dev->last_switch);
+	DRM_PROC_PRINT("last_checked   %10d\n",	 dev->last_checked);
+		
+	
+	DRM_PROC_PRINT("\n		       q2d	  d2c	     c2f"
+		       "	q2c	   q2f	      dma	 sch"
+		       "	ctx	  lacq	     lhld\n\n");
+	for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) {
+		DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u"
+			       " %10u %10u %10u %10u %10u\n",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1
+			       ? prev_value : slot_value ,
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_dispatched[i]),
+			       atomic_read(&dev->histo
+					   .dispatched_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .completed_to_freed[i]),
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .queued_to_freed[i]),
+			       atomic_read(&dev->histo.dma[i]),
+			       atomic_read(&dev->histo.schedule[i]),
+			       atomic_read(&dev->histo.ctx[i]),
+			       atomic_read(&dev->histo.lacq[i]),
+			       atomic_read(&dev->histo.lhld[i]));
+		prev_value = slot_value;
+		slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value);
+	}
+	return len;
+}
+
+static int drm_histo_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_histo_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_bufs.c linux.18p17/drivers/char/drm/r128_bufs.c
--- linux.vanilla/drivers/char/drm/r128_bufs.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_bufs.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,309 @@
+/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+#include "linux/un.h"
+
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+int r128_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		     unsigned long arg)
+{
+	drm_file_t       *priv = filp->private_data;
+	drm_device_t     *dev  = priv->dev;
+	drm_device_dma_t *dma  = dev->dma;
+	drm_buf_desc_t    request;
+	drm_buf_entry_t  *entry;
+	drm_buf_t        *buf;
+	unsigned long     offset;
+	unsigned long     agp_offset;
+	int               count;
+	int               order;
+	int               size;
+	int               alignment;
+	int               page_order;
+	int               total;
+	int               byte_count;
+	int               i;
+
+	if (!dma) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	count      = request.count;
+	order      = drm_order(request.size);
+	size       = 1 << order;
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total      = PAGE_SIZE << page_order;
+
+	byte_count = 0;
+	agp_offset = dev->agp->base + request.agp_start;
+
+	DRM_DEBUG("count:      %d\n",  count);
+	DRM_DEBUG("order:      %d\n",  order);
+	DRM_DEBUG("size:       %d\n",  size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment:  %d\n",  alignment);
+	DRM_DEBUG("page_order: %d\n",  page_order);
+	DRM_DEBUG("total:      %d\n",  total);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset            = 0;
+
+	for (offset = 0;
+	     entry->buf_count < count;
+	     offset += alignment, ++entry->buf_count) {
+		buf          = &entry->buflist[entry->buf_count];
+		buf->idx     = dma->buf_count + entry->buf_count;
+		buf->total   = alignment;
+		buf->order   = order;
+		buf->used    = 0;
+		buf->offset  = (dma->byte_count + offset);
+		buf->address = (void *)(agp_offset + offset);
+		buf->next    = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid     = 0;
+
+		buf->dev_priv_size = sizeof(drm_r128_buf_priv_t);
+		buf->dev_private   = drm_alloc(sizeof(drm_r128_buf_priv_t),
+					       DRM_MEM_BUFS);
+		memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed  = 0;
+		buf->time_freed      = 0;
+#endif
+
+		byte_count += PAGE_SIZE << page_order;
+
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	copy_to_user_ret((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+#endif
+
+int r128_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_buf_desc_t		request;
+
+	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	copy_from_user_ret(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (request.flags & _DRM_AGP_BUFFER)
+		return r128_addbufs_agp(inode, filp, cmd, arg);
+	else
+#endif
+		return -EINVAL;
+}
+
+int r128_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_device_dma_t	*dma		= dev->dma;
+	int			 retcode	= 0;
+	const int		 zero		= 0;
+	unsigned long		 virtual;
+	unsigned long		 address;
+	drm_buf_map_t		 request;
+	int			 i;
+
+	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	copy_from_user_ret(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request),
+			   -EFAULT);
+
+	if (request.count >= dma->buf_count) {
+		if (dma->flags & _DRM_DMA_USE_AGP) {
+			drm_map_t *map;
+
+			map = dev_priv->agp_vertbufs;
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			down(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size, 
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED, 
+					  (unsigned long)map->offset);
+			up(&current->mm->mmap_sem);
+		} else {
+			down(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	copy_to_user_ret((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request),
+			 -EFAULT);
+
+	return retcode;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_context.c linux.18p17/drivers/char/drm/r128_context.c
--- linux.vanilla/drivers/char/drm/r128_context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_context.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,207 @@
+/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+extern drm_ctx_t r128_res_ctx;
+
+static int r128_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int r128_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                r128_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int r128_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+
+int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+
+int r128_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = r128_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		r128_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int r128_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return r128_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int r128_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	r128_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int r128_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_dma.c linux.18p17/drivers/char/drm/r128_dma.c
--- linux.vanilla/drivers/char/drm/r128_dma.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_dma.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,922 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+#include <linux/delay.h>
+
+
+
+#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_REMAPFREE(_m)                                                    \
+	do {                                                                \
+		if ((_m)->handle && (_m)->size)                             \
+			drm_ioremapfree((_m)->handle, (_m)->size);          \
+	} while (0)
+
+#define DO_FIND_MAP(_m, _o)                                                 \
+	do {                                                                \
+		int _i;                                                     \
+		for (_i = 0; _i < dev->map_count; _i++) {                   \
+			if (dev->maplist[_i]->offset == _o) {               \
+				_m = dev->maplist[_i];                      \
+				break;                                      \
+			}                                                   \
+		}                                                           \
+	} while (0)
+
+
+#define R128_MAX_VBUF_AGE	0x10000000
+#define R128_VB_AGE_REG		R128_GUI_SCRATCH_REG0
+
+int R128_READ_PLL(drm_device_t *dev, int addr)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
+	return R128_READ(R128_CLOCK_CNTL_DATA);
+}
+
+static void r128_flush_write_combine(void)
+{
+	int xchangeDummy;
+
+	__asm__ volatile("push %%eax ;"
+			 "xchg %%eax, %0 ;"
+			 "pop %%eax" : : "m" (xchangeDummy));
+	__asm__ volatile("push %%eax ;"
+			 "push %%ebx ;"
+			 "push %%ecx ;"
+			 "push %%edx ;"
+			 "movl $0,%%eax ;"
+			 "cpuid ;"
+			 "pop %%edx ;"
+			 "pop %%ecx ;"
+			 "pop %%ebx ;"
+			 "pop %%eax" : /* no outputs */ :  /* no inputs */ );
+}
+
+static void r128_status(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+
+	printk("GUI_STAT           = 0x%08x\n",
+	       (unsigned int)R128_READ(R128_GUI_STAT));
+	printk("PM4_STAT           = 0x%08x\n",
+	       (unsigned int)R128_READ(R128_PM4_STAT));
+	printk("PM4_BUFFER_DL_WPTR = 0x%08x\n",
+	       (unsigned int)R128_READ(R128_PM4_BUFFER_DL_WPTR));
+	printk("PM4_BUFFER_DL_RPTR = 0x%08x\n",
+	       (unsigned int)R128_READ(R128_PM4_BUFFER_DL_RPTR));
+}
+
+static int r128_do_cleanup_cce(drm_device_t *dev)
+{
+	if (dev->dev_private) {
+		drm_r128_private_t *dev_priv = dev->dev_private;
+
+		if (!dev_priv->is_pci) {
+			DO_REMAPFREE(dev_priv->agp_ring);
+			DO_REMAPFREE(dev_priv->agp_read_ptr);
+			DO_REMAPFREE(dev_priv->agp_vertbufs);
+			DO_REMAPFREE(dev_priv->agp_indbufs);
+			DO_REMAPFREE(dev_priv->agp_textures);
+		}
+
+		drm_free(dev->dev_private, sizeof(drm_r128_private_t),
+			 DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+static int r128_do_init_cce(drm_device_t *dev, drm_r128_init_t *init)
+{
+	drm_r128_private_t *dev_priv;
+        int                 i;
+
+	dev_priv = drm_alloc(sizeof(drm_r128_private_t), DRM_MEM_DRIVER);
+	if (dev_priv == NULL) return -ENOMEM;
+	dev->dev_private = (void *)dev_priv;
+
+	memset(dev_priv, 0, sizeof(drm_r128_private_t));
+
+	dev_priv->is_pci         = init->is_pci;
+
+	dev_priv->usec_timeout   = init->usec_timeout;
+	if (dev_priv->usec_timeout < 1 ||
+	    dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT) {
+		drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->cce_mode       = init->cce_mode;
+	dev_priv->cce_fifo_size  = init->cce_fifo_size;
+	dev_priv->cce_is_bm_mode =
+		((init->cce_mode == R128_PM4_192BM) ||
+		 (init->cce_mode == R128_PM4_128BM_64INDBM) ||
+		 (init->cce_mode == R128_PM4_64BM_128INDBM) ||
+		 (init->cce_mode == R128_PM4_64BM_64VCBM_64INDBM));
+	dev_priv->cce_secure     = init->cce_secure;
+
+	if (dev_priv->cce_is_bm_mode && dev_priv->is_pci) {
+		drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	for (i = 0; i < dev->map_count; i++) {
+		if (dev->maplist[i]->type == _DRM_SHM) {
+			dev_priv->sarea = dev->maplist[i];
+			break;
+		}
+	}
+
+	DO_FIND_MAP(dev_priv->fb,           init->fb_offset);
+	if (!dev_priv->is_pci) {
+		DO_FIND_MAP(dev_priv->agp_ring,     init->agp_ring_offset);
+		DO_FIND_MAP(dev_priv->agp_read_ptr, init->agp_read_ptr_offset);
+		DO_FIND_MAP(dev_priv->agp_vertbufs, init->agp_vertbufs_offset);
+		DO_FIND_MAP(dev_priv->agp_indbufs,  init->agp_indbufs_offset);
+		DO_FIND_MAP(dev_priv->agp_textures, init->agp_textures_offset);
+	}
+	DO_FIND_MAP(dev_priv->mmio,         init->mmio_offset);
+
+	dev_priv->sarea_priv =
+		(drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle +
+				     init->sarea_priv_offset);
+
+	if (!dev_priv->is_pci) {
+		DO_REMAP(dev_priv->agp_ring);
+		DO_REMAP(dev_priv->agp_read_ptr);
+		DO_REMAP(dev_priv->agp_vertbufs);
+#if 0
+		DO_REMAP(dev_priv->agp_indirectbufs);
+		DO_REMAP(dev_priv->agp_textures);
+#endif
+
+		dev_priv->ring_size     = init->ring_size;
+		dev_priv->ring_sizel2qw = drm_order(init->ring_size/8);
+		dev_priv->ring_entries  = init->ring_size/sizeof(u32);
+		dev_priv->ring_read_ptr = ((__volatile__ u32 *)
+					   dev_priv->agp_read_ptr->handle);
+		dev_priv->ring_start    = (u32 *)dev_priv->agp_ring->handle;
+		dev_priv->ring_end      = ((u32 *)dev_priv->agp_ring->handle
+					   + dev_priv->ring_entries);
+	}
+
+	dev_priv->submit_age    = 0;
+	R128_WRITE(R128_VB_AGE_REG, dev_priv->submit_age);
+
+	return 0;
+}
+
+int r128_init_cce(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+	drm_r128_init_t    init;
+
+	copy_from_user_ret(&init, (drm_r128_init_t *)arg, sizeof(init),
+			   -EFAULT);
+
+	switch (init.func) {
+	case R128_INIT_CCE:
+		return r128_do_init_cce(dev, &init);
+	case R128_CLEANUP_CCE:
+		return r128_do_cleanup_cce(dev);
+	}
+
+	return -EINVAL;
+}
+
+static void r128_mark_vertbufs_done(drm_device_t *dev)
+{
+	drm_device_dma_t   *dma      = dev->dma;
+	int                 i;
+
+	for (i = 0; i < dma->buf_count; i++) {
+		drm_buf_t           *buf      = dma->buflist[i];
+		drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+		buf_priv->age = 0;
+	}
+}
+
+static int r128_do_pixcache_flush(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32                 tmp;
+	int                 i;
+
+	tmp = R128_READ(R128_PC_NGUI_CTLSTAT) | R128_PC_FLUSH_ALL;
+	R128_WRITE(R128_PC_NGUI_CTLSTAT, tmp);
+
+	for (i = 0; i < dev_priv->usec_timeout; i++) {
+		if (!(R128_READ(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY))
+			return 0;
+		udelay(1);
+	}
+
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_fifo(drm_device_t *dev, int entries)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 i;
+
+	for (i = 0; i < dev_priv->usec_timeout; i++) {
+		int slots = R128_READ(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK;
+		if (slots >= entries) return 0;
+		udelay(1);
+	}
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_idle(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 i, ret;
+
+	if (!(ret = r128_do_wait_for_fifo(dev, 64))) return ret;
+
+	for (i = 0; i < dev_priv->usec_timeout; i++) {
+		if (!(R128_READ(R128_GUI_STAT) & R128_GUI_ACTIVE)) {
+			(void)r128_do_pixcache_flush(dev);
+			return 0;
+		}
+		udelay(1);
+	}
+	return -EBUSY;
+}
+
+int r128_do_engine_reset(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32                 clock_cntl_index, mclk_cntl, gen_reset_cntl;
+
+	(void)r128_do_pixcache_flush(dev);
+
+	clock_cntl_index = R128_READ(R128_CLOCK_CNTL_INDEX);
+	mclk_cntl        = R128_READ_PLL(dev, R128_MCLK_CNTL);
+
+	R128_WRITE_PLL(R128_MCLK_CNTL,
+		       mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);
+
+	gen_reset_cntl   = R128_READ(R128_GEN_RESET_CNTL);
+
+	R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI);
+	(void)R128_READ(R128_GEN_RESET_CNTL);
+	R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl & ~R128_SOFT_RESET_GUI);
+	(void)R128_READ(R128_GEN_RESET_CNTL);
+
+	R128_WRITE_PLL(R128_MCLK_CNTL,    mclk_cntl);
+	R128_WRITE(R128_CLOCK_CNTL_INDEX, clock_cntl_index);
+	R128_WRITE(R128_GEN_RESET_CNTL,   gen_reset_cntl);
+
+	/* For CCE ring buffer only */
+	if (dev_priv->cce_is_bm_mode) {
+		R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0);
+		R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0);
+		*dev_priv->ring_read_ptr = 0;
+		dev_priv->sarea_priv->ring_write = 0;
+	}
+
+	/* Reset the CCE mode */
+	(void)r128_do_wait_for_idle(dev);
+	R128_WRITE(R128_PM4_BUFFER_CNTL,
+		   dev_priv->cce_mode | dev_priv->ring_sizel2qw);
+	(void)R128_READ(R128_PM4_BUFFER_ADDR); /* as per the sample code */
+	R128_WRITE(R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN);
+
+	r128_mark_vertbufs_done(dev);
+	return 0;
+}
+
+int r128_eng_reset(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+	    dev->lock.pid != current->pid) {
+		DRM_ERROR("r128_eng_reset called without holding the lock\n");
+		return -EINVAL;
+	}
+
+	return r128_do_engine_reset(dev);
+}
+
+static int r128_do_engine_flush(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32                 tmp;
+
+	tmp = R128_READ(R128_PM4_BUFFER_DL_WPTR);
+	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, tmp | R128_PM4_BUFFER_DL_DONE);
+
+	return 0;
+}
+
+int r128_eng_flush(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+	    dev->lock.pid != current->pid) {
+		DRM_ERROR("r128_eng_flush called without holding the lock\n");
+		return -EINVAL;
+	}
+
+	return r128_do_engine_flush(dev);
+}
+
+static int r128_do_cce_wait_for_fifo(drm_device_t *dev, int entries)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 i;
+
+	for (i = 0; i < dev_priv->usec_timeout; i++) {
+		int slots = R128_READ(R128_PM4_STAT) & R128_PM4_FIFOCNT_MASK;
+		if (slots >= entries) return 0;
+		udelay(1);
+	}
+	return -EBUSY;
+}
+
+int r128_do_cce_wait_for_idle(drm_device_t *dev)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 i;
+
+	if (dev_priv->cce_is_bm_mode) {
+		for (i = 0; i < dev_priv->usec_timeout; i++) {
+			if (*dev_priv->ring_read_ptr == dev_priv->sarea_priv->ring_write) {
+				int pm4stat = R128_READ(R128_PM4_STAT);
+				if ((pm4stat & R128_PM4_FIFOCNT_MASK) >= dev_priv->cce_fifo_size &&
+				    !(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) {
+					return r128_do_pixcache_flush(dev);
+				}
+			}
+			udelay(1);
+		}
+		return -EBUSY;
+	} else {
+		int ret = r128_do_cce_wait_for_fifo(dev, dev_priv->cce_fifo_size);
+		if (ret < 0) return ret;
+
+		for (i = 0; i < dev_priv->usec_timeout; i++) {
+			int pm4stat = R128_READ(R128_PM4_STAT);
+			if (!(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) {
+				return r128_do_pixcache_flush(dev);
+			}
+			udelay(1);
+		}
+		return -EBUSY;
+	}
+}
+
+int r128_cce_idle(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+        drm_file_t         *priv     = filp->private_data;
+        drm_device_t       *dev      = priv->dev;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+	    dev->lock.pid != current->pid) {
+		DRM_ERROR("r128_wait_idle called without holding the lock\n");
+		return -EINVAL;
+	}
+
+	return r128_do_cce_wait_for_idle(dev);
+}
+
+static int r128_submit_packets_ring_secure(drm_device_t *dev,
+					   u32 *commands, int *count)
+{
+	drm_r128_private_t *dev_priv  = dev->dev_private;
+	int                 write     = dev_priv->sarea_priv->ring_write;
+	int                *write_ptr = dev_priv->ring_start + write;
+	int                 c         = *count;
+	u32                 tmp       = 0;
+	int                 psize     = 0;
+	int                 writing   = 1;
+	int                 timeout;
+
+	while (c > 0) {
+		tmp = *commands++;
+		if (!psize) {
+			writing = 1;
+
+			if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) {
+				if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) {
+					if ((tmp & R128_CCE_PACKET0_REG_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP >> 2)) {
+						writing = 0;
+					}
+				}
+				psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+			} else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) {
+				if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) {
+					if ((tmp & R128_CCE_PACKET1_REG0_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP >> 2)) {
+						writing = 0;
+					}
+				} else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <=
+					   (0x1004 << 9)) {
+					if ((tmp & R128_CCE_PACKET1_REG1_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP << 9)) {
+						writing = 0;
+					}
+				}
+				psize = 3;
+			} else {
+				psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+			}
+		}
+		psize--;
+
+		if (writing) {
+			write++;
+			*write_ptr++ = tmp;
+		}
+		if (write >= dev_priv->ring_entries) {
+			write     = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+		timeout = 0;
+		while (write == *dev_priv->ring_read_ptr) {
+			(void)R128_READ(R128_PM4_BUFFER_DL_RPTR);
+			if (timeout++ >= dev_priv->usec_timeout)
+				return -EBUSY;
+			udelay(1);
+		}
+		c--;
+	}
+
+	if (write < 32)
+	    memcpy(dev_priv->ring_end,
+		   dev_priv->ring_start,
+		   write * sizeof(u32));
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write);
+
+	*count = 0;
+
+	return 0;
+}
+
+static int r128_submit_packets_pio_secure(drm_device_t *dev,
+					  u32 *commands, int *count)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32                 tmp      = 0;
+	int                 psize    = 0;
+	int                 writing  = 1;
+	int                 addr     = R128_PM4_FIFO_DATA_EVEN;
+	int                 ret;
+
+	while (*count > 0) {
+		tmp = *commands++;
+		if (!psize) {
+			writing = 1;
+
+			if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) {
+				if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) {
+					if ((tmp & R128_CCE_PACKET0_REG_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP >> 2)) {
+						writing = 0;
+					}
+				}
+				psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+			} else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) {
+				if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) {
+					if ((tmp & R128_CCE_PACKET1_REG0_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP >> 2)) {
+						writing = 0;
+					}
+				} else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <=
+					   (0x1004 << 9)) {
+					if ((tmp & R128_CCE_PACKET1_REG1_MASK) !=
+					    (R128_PM4_VC_FPU_SETUP << 9)) {
+						writing = 0;
+					}
+				}
+				psize = 3;
+			} else {
+				psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+			}
+		}
+		psize--;
+
+		if (writing) {
+			if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0)
+				return ret;
+			R128_WRITE(addr, tmp);
+			addr ^= 0x0004;
+		}
+
+		*count -= 1;
+	}
+
+	if (addr == R128_PM4_FIFO_DATA_ODD) {
+		if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0) return ret;
+		R128_WRITE(addr, R128_CCE_PACKET2);
+	}
+
+	return 0;
+}
+
+static int r128_submit_packets_ring(drm_device_t *dev,
+				    u32 *commands, int *count)
+{
+	drm_r128_private_t *dev_priv  = dev->dev_private;
+	int                 write     = dev_priv->sarea_priv->ring_write;
+	int                *write_ptr = dev_priv->ring_start + write;
+	int                 c         = *count;
+	int                 timeout;
+
+	while (c > 0) {
+		write++;
+		*write_ptr++ = *commands++;
+		if (write >= dev_priv->ring_entries) {
+			write     = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+		timeout = 0;
+		while (write == *dev_priv->ring_read_ptr) {
+			(void)R128_READ(R128_PM4_BUFFER_DL_RPTR);
+			if (timeout++ >= dev_priv->usec_timeout)
+				return -EBUSY;
+			udelay(1);
+		}
+		c--;
+	}
+
+	if (write < 32)
+	    memcpy(dev_priv->ring_end,
+		   dev_priv->ring_start,
+		   write * sizeof(u32));
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write);
+
+	*count = 0;
+
+	return 0;
+}
+
+static int r128_submit_packets_pio(drm_device_t *dev,
+				   u32 *commands, int *count)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 ret;
+
+	while (*count > 1) {
+		if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret;
+		R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++);
+		R128_WRITE(R128_PM4_FIFO_DATA_ODD,  *commands++);
+		*count -= 2;
+	}
+
+	if (*count) {
+		if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret;
+		R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++);
+		R128_WRITE(R128_PM4_FIFO_DATA_ODD,  R128_CCE_PACKET2);
+		*count = 0;
+	}
+
+	return 0;
+}
+
+static int r128_do_submit_packets(drm_device_t *dev, u32 *buffer, int count)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int                 c = count;
+	int                 ret;
+
+	if (dev_priv->cce_is_bm_mode) {
+		int left = 0;
+
+		if (c >= dev_priv->ring_entries) {
+			c    = dev_priv->ring_entries-1;
+			left = count - c;
+		}
+
+		/* Since this is only used by the kernel we can use the
+                   insecure ring buffer submit packet routine */
+		ret = r128_submit_packets_ring(dev, buffer, &c);
+
+		c += left;
+	} else {
+		/* Since this is only used by the kernel we can use the
+                   insecure PIO submit packet routine */
+		ret = r128_submit_packets_pio(dev, buffer, &c);
+	}
+
+	if (ret < 0) return ret;
+	else         return c;
+}
+
+int r128_submit_pkt(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg)
+{
+        drm_file_t         *priv     = filp->private_data;
+        drm_device_t       *dev      = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_packet_t   packet;
+	u32                *buffer;
+	int                 c;
+	int                 size;
+	int                 ret = 0;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+	    dev->lock.pid != current->pid) {
+		DRM_ERROR("r128_submit_pkt called without holding the lock\n");
+		return -EINVAL;
+	}
+
+	copy_from_user_ret(&packet, (drm_r128_packet_t *)arg, sizeof(packet),
+			   -EFAULT);
+
+	c    = packet.count;
+	size = c * sizeof(*buffer);
+
+	if (dev_priv->cce_is_bm_mode) {
+		int left = 0;
+
+		if (c >= dev_priv->ring_entries) {
+			c    = dev_priv->ring_entries-1;
+			size = c * sizeof(*buffer);
+			left = packet.count - c;
+		}
+
+		if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM;
+		copy_from_user_ret(buffer, packet.buffer, size, -EFAULT);
+
+		if (dev_priv->cce_secure)
+			ret = r128_submit_packets_ring_secure(dev, buffer, &c);
+		else
+			ret = r128_submit_packets_ring(dev, buffer, &c);
+
+		c += left;
+	} else {
+		if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM;
+		copy_from_user_ret(buffer, packet.buffer, size, -EFAULT);
+
+		if (dev_priv->cce_secure)
+			ret = r128_submit_packets_pio_secure(dev, buffer, &c);
+		else
+			ret = r128_submit_packets_pio(dev, buffer, &c);
+	}
+
+	kfree(buffer);
+
+	packet.count = c;
+	copy_to_user_ret((drm_r128_packet_t *)arg, &packet, sizeof(packet),
+			 -EFAULT);
+
+	if (ret)        return ret;
+	else if (c > 0) return -EAGAIN;
+
+	return 0;
+}
+
+static int r128_send_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v)
+{
+	drm_device_dma_t    *dma      = dev->dma;
+	drm_r128_private_t  *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t           *buf;
+	int                  i, ret;
+	u32                  cce[2];
+
+	/* Make sure we have valid data */
+	for (i = 0; i < v->send_count; i++) {
+		int idx = v->send_indices[i];
+
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  v->send_indices[i], i);
+			return -EINVAL;
+		}
+	}
+
+	/* Wait for idle, if we've wrapped to make sure that all pending
+           buffers have been processed */
+	if (dev_priv->submit_age == R128_MAX_VBUF_AGE) {
+		if ((ret = r128_do_cce_wait_for_idle(dev)) < 0) return ret;
+		dev_priv->submit_age = 0;
+		r128_mark_vertbufs_done(dev);
+	}
+
+	/* Make sure WC cache has been flushed (if in PIO mode) */
+	if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine();
+
+	/* FIXME: Add support for sending vertex buffer to the CCE here
+	   instead of in client code.  The v->prim holds the primitive
+	   type that should be drawn.  Loop over the list buffers in
+	   send_indices[] and submit a packet for each VB.
+
+	   This will require us to loop over the clip rects here as
+	   well, which implies that we extend the kernel driver to allow
+	   cliprects to be stored here.  Note that the cliprects could
+	   possibly come from the X server instead of the client, but
+	   this will require additional changes to the DRI to allow for
+	   this optimization. */
+
+	/* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */
+	cce[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0);
+	cce[1] = dev_priv->submit_age;
+	if ((ret = r128_do_submit_packets(dev, cce, 2)) < 0) {
+		/* Until we add support for sending VBs to the CCE in
+		   this routine, we can recover from this error.  After
+		   we add that support, we won't be able to easily
+		   recover, so we will probably have to implement
+		   another mechanism for handling timeouts from packets
+		   submitted directly by the kernel. */
+		return ret;
+	}
+
+	/* Now that the submit packet request has succeeded, we can mark
+           the buffers as pending */
+	for (i = 0; i < v->send_count; i++) {
+		buf = dma->buflist[v->send_indices[i]];
+		buf->pending = 1;
+
+		buf_priv      = buf->dev_private;
+		buf_priv->age = dev_priv->submit_age;
+	}
+
+	dev_priv->submit_age++;
+
+	return 0;
+}
+
+static drm_buf_t *r128_freelist_get(drm_device_t *dev)
+{
+	drm_device_dma_t    *dma      = dev->dma;
+	drm_r128_private_t  *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t           *buf;
+	int                  i, t;
+
+	/* FIXME: Optimize -- use freelist code */
+
+	for (i = 0; i < dma->buf_count; i++) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		if (buf->pid == 0) return buf;
+	}
+
+	for (t = 0; t < dev_priv->usec_timeout; t++) {
+		u32 done_age = R128_READ(R128_VB_AGE_REG);
+
+		for (i = 0; i < dma->buf_count; i++) {
+			buf = dma->buflist[i];
+			buf_priv = buf->dev_private;
+			if (buf->pending && buf_priv->age <= done_age) {
+				/* The buffer has been processed, so it
+                                   can now be used */
+				buf->pending = 0;
+				return buf;
+			}
+		}
+		udelay(1);
+	}
+
+	r128_status(dev);
+	return NULL;
+}
+
+
+static int r128_get_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v)
+{
+	drm_buf_t *buf;
+	int        i;
+
+	for (i = v->granted_count; i < v->request_count; i++) {
+		buf = r128_freelist_get(dev);
+		if (!buf) break;
+		buf->pid = current->pid;
+		copy_to_user_ret(&v->request_indices[i],
+				 &buf->idx,
+				 sizeof(buf->idx),
+				 -EFAULT);
+		copy_to_user_ret(&v->request_sizes[i],
+				 &buf->total,
+				 sizeof(buf->total),
+				 -EFAULT);
+		++v->granted_count;
+	}
+	return 0;
+}
+
+int r128_vertex_buf(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	   *priv     = filp->private_data;
+	drm_device_t	   *dev	     = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t   *dma	     = dev->dma;
+	int		    retcode  = 0;
+	drm_r128_vertex_t   v;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+	    dev->lock.pid != current->pid) {
+		DRM_ERROR("r128_vertex_buf called without holding the lock\n");
+		return -EINVAL;
+	}
+
+	if (!dev_priv || dev_priv->is_pci) {
+		DRM_ERROR("r128_vertex_buf called with a PCI card\n");
+		return -EINVAL;
+	}
+
+	copy_from_user_ret(&v, (drm_r128_vertex_t *)arg, sizeof(v), -EFAULT);
+	DRM_DEBUG("%d: %d send, %d req\n",
+		  current->pid, v.send_count, v.request_count);
+
+	if (v.send_count < 0 || v.send_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n",
+			  current->pid, v.send_count, dma->buf_count);
+		return -EINVAL;
+	}
+	if (v.request_count < 0 || v.request_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+			  current->pid, v.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	if (v.send_count) {
+		retcode = r128_send_vertbufs(dev, &v);
+	}
+
+	v.granted_count = 0;
+
+	if (!retcode && v.request_count) {
+		retcode = r128_get_vertbufs(dev, &v);
+	}
+
+	DRM_DEBUG("%d returning, granted = %d\n",
+		  current->pid, v.granted_count);
+	copy_to_user_ret((drm_r128_vertex_t *)arg, &v, sizeof(v), -EFAULT);
+
+	return retcode;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_drm.h linux.18p17/drivers/char/drm/r128_drm.h
--- linux.vanilla/drivers/char/drm/r128_drm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_drm.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,111 @@
+/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRM_H_
+#define _R128_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmR128.h)
+ */
+typedef struct drm_r128_init {
+	enum { 
+		R128_INIT_CCE    = 0x01,
+		R128_CLEANUP_CCE = 0x02
+	} func;
+	int sarea_priv_offset;
+	int is_pci;
+	int cce_mode;
+	int cce_fifo_size;
+	int cce_secure;
+	int ring_size;
+	int usec_timeout;
+
+	int fb_offset;
+	int agp_ring_offset;
+	int agp_read_ptr_offset;
+	int agp_vertbufs_offset;
+	int agp_indbufs_offset;
+	int agp_textures_offset;
+	int mmio_offset;
+} drm_r128_init_t;
+
+typedef struct drm_r128_packet {
+	unsigned long *buffer;
+	int            count;
+	int            flags;
+} drm_r128_packet_t;
+
+typedef enum drm_r128_prim {
+	_DRM_R128_PRIM_NONE		= 0x0001,
+	_DRM_R128_PRIM_POINT		= 0x0002,
+	_DRM_R128_PRIM_LINE		= 0x0004,
+	_DRM_R128_PRIM_POLY_LINE	= 0x0008,
+	_DRM_R128_PRIM_TRI_LIST		= 0x0010,
+	_DRM_R128_PRIM_TRI_FAN		= 0x0020,
+	_DRM_R128_PRIM_TRI_STRIP	= 0x0040,
+	_DRM_R128_PRIM_TRI_TYPE2	= 0x0080
+} drm_r128_prim_t;
+
+typedef struct drm_r128_vertex {
+				/* Indices here refer to the offset into
+				   buflist in drm_buf_get_t.  */
+	int		send_count;	  /* Number of buffers to send	    */
+	int		*send_indices;	  /* List of handles to buffers	    */
+	int		*send_sizes;	  /* Lengths of data to send	    */
+	drm_r128_prim_t	prim;		  /* Primitive type		    */
+	int		request_count;	  /* Number of buffers requested    */
+	int		*request_indices; /* Buffer information		    */
+	int		*request_sizes;
+	int		granted_count;	  /* Number of buffers granted	    */
+} drm_r128_vertex_t;
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (r128_sarea.h)
+ */
+#define R128_LOCAL_TEX_HEAP       0
+#define R128_AGP_TEX_HEAP         1
+#define R128_NR_TEX_HEAPS         2
+#define R128_NR_TEX_REGIONS      64
+#define R128_LOG_TEX_GRANULARITY 16
+
+typedef struct drm_tex_region {
+	unsigned char next, prev;       
+	unsigned char in_use;   
+	int age;                        
+} drm_tex_region_t;
+
+typedef struct drm_r128_sarea {
+	drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1];
+	int              tex_age[R128_NR_TEX_HEAPS];
+	int              ctx_owner;
+	int              ring_write;
+} drm_r128_sarea_t;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_drv.c linux.18p17/drivers/char/drm/r128_drv.c
--- linux.vanilla/drivers/char/drm/r128_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_drv.c	Sat Sep 30 22:51:05 2000
@@ -0,0 +1,731 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *          Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+
+#include <linux/agp_backend.h>
+
+static void __attribute__((unused)) unused(void)
+{
+	agp_enable(0);
+}
+
+#define R128_NAME	 "r128"
+#define R128_DESC	 "ATI Rage 128"
+#define R128_DATE	 "20000719"
+#define R128_MAJOR	 1
+#define R128_MINOR	 0
+#define R128_PATCHLEVEL  0
+
+static drm_device_t	      r128_device;
+drm_ctx_t	              r128_res_ctx;
+
+static struct file_operations r128_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:   THIS_MODULE,
+#endif
+	open:	 r128_open,
+	flush:	 drm_flush,
+	release: r128_release,
+	ioctl:	 r128_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      r128_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  R128_NAME,
+	fops:  &r128_fops,
+};
+
+static drm_ioctl_desc_t	      r128_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { r128_version,	   0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,   0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	   0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,   0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { r128_addbufs,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { drm_markbufs,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { drm_infobufs,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]    = { r128_mapbufs,	   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { drm_freebufs,    1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { r128_addctx,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { r128_rmctx,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { r128_modctx,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { r128_getctx,	   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { r128_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { r128_newctx,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { r128_resctx,	   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { r128_lock,	   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { r128_unlock,	   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	   1, 0 },
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+#endif
+
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)]   = { r128_init_cce,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)]  = { r128_eng_reset,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_FLUSH)]  = { r128_eng_flush,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)] = { r128_submit_pkt, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_IDLE)]   = { r128_cce_idle,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_vertex_buf, 1, 0 },
+};
+#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls)
+
+#ifdef MODULE
+static char		      *r128 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("r128");
+MODULE_PARM(r128, "s");
+
+#ifndef MODULE
+/* r128_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init r128_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("r128=", r128_options);
+#endif
+
+static int r128_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	r128_res_ctx.handle=-1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int r128_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired && drm_agp.release)
+			(*drm_agp.release)();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+#endif
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* r128_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int r128_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(r128);
+#endif
+
+	if ((retcode = misc_register(&r128_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", R128_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, r128_misc.minor);
+	dev->name   = R128_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+      	if (dev->agp == NULL) {
+	   	DRM_ERROR("Cannot initialize agpgart module.\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&r128_misc);
+	   	r128_takedown(dev);
+	   	return -ENOMEM;
+	}
+
+#ifdef CONFIG_MTRR
+	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size*1024*1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+#endif
+
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&r128_misc);
+		r128_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 R128_NAME,
+		 R128_MAJOR,
+		 R128_MINOR,
+		 R128_PATCHLEVEL,
+		 R128_DATE,
+		 r128_misc.minor);
+
+	return 0;
+}
+
+/* r128_cleanup is called via cleanup_module at module unload time. */
+
+static void r128_cleanup(void)
+{
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&r128_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	r128_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(r128_init);
+module_exit(r128_cleanup);
+
+
+int r128_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	copy_from_user_ret(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version),
+			   -EFAULT);
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		copy_to_user_ret(name, value, len, -EFAULT); \
+	}
+
+	version.version_major	   = R128_MAJOR;
+	version.version_minor	   = R128_MINOR;
+	version.version_patchlevel = R128_PATCHLEVEL;
+
+	DRM_COPY(version.name, R128_NAME);
+	DRM_COPY(version.date, R128_DATE);
+	DRM_COPY(version.desc, R128_DESC);
+
+	copy_to_user_ret((drm_version_t *)arg,
+			 &version,
+			 sizeof(version),
+			 -EFAULT);
+	return 0;
+}
+
+int r128_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &r128_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return r128_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	
+	return retcode;
+}
+
+int r128_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return r128_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	
+	unlock_kernel();
+	return retcode;
+}
+
+/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int r128_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= R128_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &r128_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int r128_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+#if 0
+				/* dev->queue_count == 0 right now for
+                                   r128.  FIXME? */
+        if (lock.context < 0 || lock.context >= dev->queue_count)
+                return -EINVAL;
+#endif
+
+        if (!ret) {
+#if 0
+                if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+                    != lock.context) {
+                        long j = jiffies - dev->lock.lock_time;
+
+                        if (lock.context == r128_res_ctx.handle &&
+				j >= 0 && j < DRM_LOCK_SLICE) {
+                                /* Can't take lock if we just had it and
+                                   there is contention. */
+                                DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n",
+					lock.context, current->pid, j,
+					dev->lock.lock_time, jiffies);
+                                current->state = TASK_INTERRUPTIBLE;
+				current->policy |= SCHED_YIELD;
+                                schedule_timeout(DRM_LOCK_SLICE-j);
+				DRM_DEBUG("jiffies=%d\n", jiffies);
+                        }
+                }
+#endif
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        current->state = TASK_INTERRUPTIBLE;
+#if 1
+			current->policy |= SCHED_YIELD;
+#endif
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+#if 0
+	if (!ret && dev->last_context != lock.context &&
+		lock.context != r128_res_ctx.handle &&
+		dev->last_context != r128_res_ctx.handle) {
+		add_wait_queue(&dev->context_wait, &entry);
+	        current->state = TASK_INTERRUPTIBLE;
+                /* PRE: dev->last_context != lock.context */
+	        r128_context_switch(dev, dev->last_context, lock.context);
+		/* POST: we will wait for the context
+                   switch and will dispatch on a later call
+                   when dev->last_context == lock.context
+                   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+                   TIME! */
+		current->policy |= SCHED_YIELD;
+	        schedule();
+	        current->state = TASK_RUNNING;
+	        remove_wait_queue(&dev->context_wait, &entry);
+	        if (signal_pending(current)) {
+	                ret = -EINTR;
+	        } else if (dev->last_context != lock.context) {
+			DRM_ERROR("Context mismatch: %d %d\n",
+                        	dev->last_context, lock.context);
+	        }
+	}
+#endif
+
+        if (!ret) {
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+#if 0
+                        r128_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int r128_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/r128_drv.h linux.18p17/drivers/char/drm/r128_drv.h
--- linux.vanilla/drivers/char/drm/r128_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/r128_drv.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,224 @@
+/* r128_drv.h -- Private header for r128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *          Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRV_H_
+#define _R128_DRV_H_
+
+typedef struct drm_r128_private {
+	int               is_pci;
+
+	int               cce_mode;
+	int               cce_fifo_size;
+	int               cce_is_bm_mode;
+	int               cce_secure;
+
+	drm_r128_sarea_t *sarea_priv;
+
+	__volatile__ u32 *ring_read_ptr;
+
+	u32              *ring_start;
+	u32              *ring_end;
+	int               ring_size;
+	int               ring_sizel2qw;
+	int               ring_entries;
+
+	int               submit_age;
+
+	int               usec_timeout;
+
+	drm_map_t        *sarea;
+	drm_map_t        *fb;
+	drm_map_t        *agp_ring;
+	drm_map_t        *agp_read_ptr;
+	drm_map_t        *agp_vertbufs;
+	drm_map_t        *agp_indbufs;
+	drm_map_t        *agp_textures;
+	drm_map_t        *mmio;
+} drm_r128_private_t;
+
+typedef struct drm_r128_buf_priv {
+	u32               age;
+} drm_r128_buf_priv_t;
+
+				/* r128_drv.c */
+extern int  r128_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  r128_open(struct inode *inode, struct file *filp);
+extern int  r128_release(struct inode *inode, struct file *filp);
+extern int  r128_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  r128_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* r128_dma.c */
+extern int r128_init_cce(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int r128_eng_reset(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int r128_eng_flush(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int r128_submit_pkt(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int r128_cce_idle(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int r128_vertex_buf(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+
+				/* r128_bufs.c */
+extern int r128_addbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int r128_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+				/* r128_context.c */
+extern int  r128_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  r128_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  r128_context_switch(drm_device_t *dev, int old, int new);
+extern int  r128_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Rage 128 kernel driver.
+ */
+
+#define R128_PC_NGUI_CTLSTAT	0x0184
+#       define R128_PC_FLUSH_ALL	0x00ff
+#       define R128_PC_BUSY		(1 << 31)
+
+#define R128_CLOCK_CNTL_INDEX	0x0008
+#define R128_CLOCK_CNTL_DATA	0x000c
+#       define R128_PLL_WR_EN		(1 << 7)
+
+#define R128_MCLK_CNTL		0x000f
+#       define R128_FORCE_GCP		(1 << 16)
+#       define R128_FORCE_PIPE3D_CP	(1 << 17)
+#       define R128_FORCE_RCP		(1 << 18)
+
+#define R128_GEN_RESET_CNTL	0x00f0
+#       define R128_SOFT_RESET_GUI	(1 <<  0)
+
+#define R128_PM4_BUFFER_CNTL	0x0704
+#       define R128_PM4_NONPM4			(0  << 28)
+#       define R128_PM4_192PIO			(1  << 28)
+#       define R128_PM4_192BM			(2  << 28)
+#       define R128_PM4_128PIO_64INDBM		(3  << 28)
+#       define R128_PM4_128BM_64INDBM		(4  << 28)
+#       define R128_PM4_64PIO_128INDBM		(5  << 28)
+#       define R128_PM4_64BM_128INDBM		(6  << 28)
+#       define R128_PM4_64PIO_64VCBM_64INDBM	(7  << 28)
+#       define R128_PM4_64BM_64VCBM_64INDBM	(8  << 28)
+#       define R128_PM4_64PIO_64VCPIO_64INDPIO	(15 << 28)
+
+
+#define R128_PM4_BUFFER_DL_RPTR	0x0710
+#define R128_PM4_BUFFER_DL_WPTR	0x0714
+#       define R128_PM4_BUFFER_DL_DONE		(1 << 31)
+
+#define R128_PM4_VC_FPU_SETUP	0x071c
+
+#define R128_PM4_STAT		0x07b8
+#       define R128_PM4_FIFOCNT_MASK		0x0fff
+#       define R128_PM4_BUSY			(1 << 16)
+#       define R128_PM4_GUI_ACTIVE		(1 << 31)
+
+#define R128_PM4_BUFFER_ADDR	0x07f0
+#define R128_PM4_MICRO_CNTL	0x07fc
+#       define R128_PM4_MICRO_FREERUN		(1 << 30)
+
+#define R128_PM4_FIFO_DATA_EVEN	0x1000
+#define R128_PM4_FIFO_DATA_ODD	0x1004
+
+#define R128_GUI_SCRATCH_REG0	0x15e0
+#define R128_GUI_SCRATCH_REG1	0x15e4
+#define R128_GUI_SCRATCH_REG2	0x15e8
+#define R128_GUI_SCRATCH_REG3	0x15ec
+#define R128_GUI_SCRATCH_REG4	0x15f0
+#define R128_GUI_SCRATCH_REG5	0x15f4
+
+#define R128_GUI_STAT		0x1740
+#       define R128_GUI_FIFOCNT_MASK		0x0fff
+#       define R128_GUI_ACTIVE			(1 << 31)
+
+
+/* CCE command packets */
+#define R128_CCE_PACKET0	0x00000000
+#define R128_CCE_PACKET1	0x40000000
+#define R128_CCE_PACKET2	0x80000000
+#       define R128_CCE_PACKET_MASK		0xC0000000
+#       define R128_CCE_PACKET_COUNT_MASK	0x3fff0000
+#       define R128_CCE_PACKET0_REG_MASK	0x000007ff
+#       define R128_CCE_PACKET1_REG0_MASK	0x000007ff
+#       define R128_CCE_PACKET1_REG1_MASK	0x003ff800
+
+
+#define R128_MAX_USEC_TIMEOUT	100000	/* 100 ms */
+
+
+#define R128_BASE(reg)		((u32)(dev_priv->mmio->handle))
+#define R128_ADDR(reg)		(R128_BASE(reg) + reg)
+
+#define R128_DEREF(reg)		*(__volatile__ int *)R128_ADDR(reg)
+#define R128_READ(reg)		R128_DEREF(reg)
+#define R128_WRITE(reg,val)	do { R128_DEREF(reg) = val; } while (0)
+
+#define R128_DEREF8(reg)	*(__volatile__ char *)R128_ADDR(reg)
+#define R128_READ8(reg)		R128_DEREF8(reg)
+#define R128_WRITE8(reg,val)	do { R128_DEREF8(reg) = val; } while (0)
+
+#define R128_WRITE_PLL(addr,val)                                              \
+do {                                                                          \
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \
+	R128_WRITE(R128_CLOCK_CNTL_DATA, (val));                              \
+} while (0)
+
+extern int R128_READ_PLL(drm_device_t *dev, int addr);
+
+#define R128CCE0(p,r,n)   ((p) | ((n) << 16) | ((r) >> 2))
+#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2))
+#define R128CCE2(p)       ((p))
+#define R128CCE3(p,n)     ((p) | ((n) << 16))
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/tdfx_context.c linux.18p17/drivers/char/drm/tdfx_context.c
--- linux.vanilla/drivers/char/drm/tdfx_context.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/tdfx_context.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,209 @@
+/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
+ * Created: Thu Oct  7 10:50:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+extern drm_ctx_t tdfx_res_ctx;
+
+static int tdfx_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int tdfx_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                tdfx_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int tdfx_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+
+int tdfx_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			copy_to_user_ret(&res.contexts[i],
+					 &i,
+					 sizeof(i),
+					 -EFAULT);
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+	return 0;
+}
+
+
+int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	if ((ctx.handle = tdfx_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = tdfx_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+   
+	copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int tdfx_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		tdfx_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int tdfx_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+	return 0;
+}
+
+int tdfx_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	return tdfx_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	tdfx_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int tdfx_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t      *priv   = filp->private_data;
+	drm_device_t    *dev    = priv->dev;
+	drm_ctx_t	ctx;
+
+	copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/tdfx_drv.c linux.18p17/drivers/char/drm/tdfx_drv.c
--- linux.vanilla/drivers/char/drm/tdfx_drv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/tdfx_drv.c	Sat Sep 30 22:51:19 2000
@@ -0,0 +1,690 @@
+/* tdfx.c -- tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:38:32 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+#include <linux/agp_backend.h>
+
+static void __attribute__((unused)) unused(void)
+{
+	agp_enable(0);
+}
+
+#define TDFX_NAME	 "tdfx"
+#define TDFX_DESC	 "3dfx Banshee/Voodoo3+"
+#define TDFX_DATE	 "20000719"
+#define TDFX_MAJOR	 1
+#define TDFX_MINOR	 0
+#define TDFX_PATCHLEVEL  0
+
+static drm_device_t	      tdfx_device;
+drm_ctx_t	              tdfx_res_ctx;
+
+static struct file_operations tdfx_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+				/* This started being used approx. 2.3.34 */
+	owner:   THIS_MODULE,
+#endif
+	open:	 tdfx_open,
+	flush:	 drm_flush,
+	release: tdfx_release,
+	ioctl:	 tdfx_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      tdfx_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  TDFX_NAME,
+	fops:  &tdfx_fops,
+};
+
+static drm_ioctl_desc_t	      tdfx_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { tdfx_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { tdfx_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { tdfx_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { tdfx_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { tdfx_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { tdfx_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { tdfx_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { tdfx_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { tdfx_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { tdfx_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = {drm_agp_acquire, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = {drm_agp_release, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = {drm_agp_enable,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = {drm_agp_info,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = {drm_agp_alloc,   1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = {drm_agp_free,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = {drm_agp_unbind,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = {drm_agp_bind,    1, 1},
+#endif
+};
+#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls)
+
+#ifdef MODULE
+static char		      *tdfx = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("tdfx");
+MODULE_PARM(tdfx, "s");
+
+#ifndef MODULE
+/* tdfx_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init tdfx_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("tdfx=", tdfx_options);
+#endif
+
+static int tdfx_setup(drm_device_t *dev)
+{
+	int i;
+	
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	tdfx_res_ctx.handle=-1;
+			
+	DRM_DEBUG("\n");
+			
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+			
+	return 0;
+}
+
+
+static int tdfx_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *temp;
+		drm_agp_mem_t *temp_next;
+	   
+		temp = dev->agp->memory;
+		while(temp != NULL) {
+			temp_next = temp->next;
+			drm_free_agp(temp->memory, temp->pages);
+			drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS);
+			temp = temp_next;
+		}
+		if (dev->agp->acquired) (*drm_agp.release)();
+	}
+#endif
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+/* tdfx_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int tdfx_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+	
+#ifdef MODULE
+	drm_parse_options(tdfx);
+#endif
+
+	if ((retcode = misc_register(&tdfx_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", TDFX_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, tdfx_misc.minor);
+	dev->name   = TDFX_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+#endif
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&tdfx_misc);
+		tdfx_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 TDFX_NAME,
+		 TDFX_MAJOR,
+		 TDFX_MINOR,
+		 TDFX_PATCHLEVEL,
+		 TDFX_DATE,
+		 tdfx_misc.minor);
+	
+	return 0;
+}
+
+/* tdfx_cleanup is called via cleanup_module at module unload time. */
+
+static void tdfx_cleanup(void)
+{
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	if (misc_deregister(&tdfx_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	tdfx_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(tdfx_init);
+module_exit(tdfx_cleanup);
+
+
+int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	copy_from_user_ret(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version),
+			   -EFAULT);
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		copy_to_user_ret(name, value, len, -EFAULT); \
+	}
+
+	version.version_major	   = TDFX_MAJOR;
+	version.version_minor	   = TDFX_MINOR;
+	version.version_patchlevel = TDFX_PATCHLEVEL;
+
+	DRM_COPY(version.name, TDFX_NAME);
+	DRM_COPY(version.date, TDFX_DATE);
+	DRM_COPY(version.desc, TDFX_DESC);
+
+	copy_to_user_ret((drm_version_t *)arg,
+			 &version,
+			 sizeof(version),
+			 -EFAULT);
+	return 0;
+}
+
+int tdfx_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &tdfx_device;
+	int	      retcode = 0;
+	
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return tdfx_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int tdfx_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return tdfx_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* tdfx_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int tdfx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= TDFX_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &tdfx_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+#if 0
+				/* dev->queue_count == 0 right now for
+                                   tdfx.  FIXME? */
+        if (lock.context < 0 || lock.context >= dev->queue_count)
+                return -EINVAL;
+#endif
+        
+        if (!ret) {
+#if 0
+                if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+                    != lock.context) {
+                        long j = jiffies - dev->lock.lock_time;
+
+                        if (lock.context == tdfx_res_ctx.handle &&
+				j >= 0 && j < DRM_LOCK_SLICE) {
+                                /* Can't take lock if we just had it and
+                                   there is contention. */
+                                DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n",
+					lock.context, current->pid, j, 
+					dev->lock.lock_time, jiffies);
+                                current->state = TASK_INTERRUPTIBLE;
+				current->policy |= SCHED_YIELD;
+                                schedule_timeout(DRM_LOCK_SLICE-j);
+				DRM_DEBUG("jiffies=%d\n", jiffies);
+                        }
+                }
+#endif
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+                        
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        current->state = TASK_INTERRUPTIBLE;
+#if 1
+			current->policy |= SCHED_YIELD;
+#endif
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+#if 0
+	if (!ret && dev->last_context != lock.context &&
+		lock.context != tdfx_res_ctx.handle &&
+		dev->last_context != tdfx_res_ctx.handle) {
+		add_wait_queue(&dev->context_wait, &entry);
+	        current->state = TASK_INTERRUPTIBLE;
+                /* PRE: dev->last_context != lock.context */
+	        tdfx_context_switch(dev, dev->last_context, lock.context);
+		/* POST: we will wait for the context
+                   switch and will dispatch on a later call
+                   when dev->last_context == lock.context
+                   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+                   TIME! */
+		current->policy |= SCHED_YIELD;
+	        schedule();
+	        current->state = TASK_RUNNING;
+	        remove_wait_queue(&dev->context_wait, &entry);
+	        if (signal_pending(current)) {
+	                ret = -EINTR;
+	        } else if (dev->last_context != lock.context) {
+			DRM_ERROR("Context mismatch: %d %d\n",
+                        	dev->last_context, lock.context);
+	        }
+	}
+#endif
+
+        if (!ret) {
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+#if 0
+                        tdfx_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+        
+        return ret;
+}
+
+
+int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+	
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+	
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/tdfx_drv.h linux.18p17/drivers/char/drm/tdfx_drv.h
--- linux.vanilla/drivers/char/drm/tdfx_drv.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/tdfx_drv.h	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,67 @@
+/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:40:04 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#ifndef _TDFX_DRV_H_
+#define _TDFX_DRV_H_
+
+				/* tdfx_drv.c */
+extern int  tdfx_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  tdfx_open(struct inode *inode, struct file *filp);
+extern int  tdfx_release(struct inode *inode, struct file *filp);
+extern int  tdfx_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  tdfx_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* tdfx_context.c */
+
+extern int  tdfx_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  tdfx_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  tdfx_context_switch(drm_device_t *dev, int old, int new);
+extern int  tdfx_context_switch_complete(drm_device_t *dev, int new);
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/vm.c linux.18p17/drivers/char/drm/vm.c
--- linux.vanilla/drivers/char/drm/vm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/drm/vm.c	Fri Sep  1 14:12:33 2000
@@ -0,0 +1,319 @@
+/* vm.c -- Memory mapping for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+struct vm_operations_struct   drm_vm_ops = {
+	nopage:	 drm_vm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_shm_ops = {
+	nopage:	 drm_vm_shm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_dma_ops = {
+	nopage:	 drm_vm_dma_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+			    unsigned long address,
+			    int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_nopage(struct vm_area_struct *vma,
+			   unsigned long address,
+			   int write_access)
+#endif
+{
+	DRM_DEBUG("0x%08lx, %d\n", address, write_access);
+
+	return NOPAGE_SIGBUS;		/* Disallow mremap */
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dev->lock.hw_lock)    return NOPAGE_OOM;  /* Nothing allocated */
+
+	offset	 = address - vma->vm_start;
+	page	 = offset >> PAGE_SHIFT;
+	physical = (unsigned long)dev->lock.hw_lock + offset;
+	atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return mem_map + MAP_NR(physical);
+#endif
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (!dma)		   return NOPAGE_SIGBUS; /* Error */
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dma->pagelist)	   return NOPAGE_OOM ; /* Nothing allocated */
+
+	offset	 = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
+	page	 = offset >> PAGE_SHIFT;
+	physical = dma->pagelist[page] + (offset & (~PAGE_MASK));
+	atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return mem_map + MAP_NR(physical);
+#endif
+}
+
+void drm_vm_open(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *vma_entry;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+	atomic_inc(&dev->vma_count);
+#if LINUX_VERSION_CODE < 0x020333
+				/* The map can exist after the fd is closed. */
+	MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+
+
+#if DRM_DEBUG_CODE
+	vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
+	if (vma_entry) {
+		down(&dev->struct_sem);
+		vma_entry->vma	= vma;
+		vma_entry->next = dev->vmalist;
+		vma_entry->pid	= current->pid;
+		dev->vmalist	= vma_entry;
+		up(&dev->struct_sem);
+	}
+#endif
+}
+
+void drm_vm_close(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *pt, *prev;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+	atomic_dec(&dev->vma_count);
+
+#if DRM_DEBUG_CODE
+	down(&dev->struct_sem);
+	for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
+		if (pt->vma == vma) {
+			if (prev) {
+				prev->next = pt->next;
+			} else {
+				dev->vmalist = pt->next;
+			}
+			drm_free(pt, sizeof(*pt), DRM_MEM_VMAS);
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+#endif
+}
+
+int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev;
+	drm_device_dma_t *dma;
+	unsigned long	 length	 = vma->vm_end - vma->vm_start;
+	
+	lock_kernel();
+	dev	 = priv->dev;
+	dma	 = dev->dma;
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+				/* Length must match exact page count */
+	if ((length >> PAGE_SHIFT) != dma->page_count) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	unlock_kernel();
+
+	vma->vm_ops   = &drm_vm_dma_ops;
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+	
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	int		i;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma);
+
+				/* A sequential search of a linked list is
+				   fine here because: 1) there will only be
+				   about 5-10 entries in the list and, 2) a
+				   DRI client only has to do this mapping
+				   once, so it doesn't have to be optimized
+				   for performance, even if the list was a
+				   bit longer. */
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->offset == VM_OFFSET(vma)) break;
+	}
+	
+	if (i >= dev->map_count) return -EINVAL;
+	if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+		return -EPERM;
+
+				/* Check for valid size. */
+	if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
+	
+	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
+		vma->vm_flags &= VM_MAYWRITE;
+#if defined(__i386__)
+		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
+#else
+				/* Ye gads this is ugly.  With more thought
+                                   we could move this up higher and use
+                                   `protection_map' instead.  */
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+#endif
+	}
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+	case _DRM_REGISTERS:
+	case _DRM_AGP:
+		if (VM_OFFSET(vma) >= __pa(high_memory)) {
+#if defined(__i386__)
+			if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
+				pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+				pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
+			}
+#endif
+			vma->vm_flags |= VM_IO;	/* not in core dump */
+		}
+		if (remap_page_range(vma->vm_start,
+				     VM_OFFSET(vma),
+				     vma->vm_end - vma->vm_start,
+				     vma->vm_page_prot))
+				return -EAGAIN;
+		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
+			  " offset = 0x%lx\n",
+			  map->type,
+			  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		vma->vm_ops = &drm_vm_shm_ops;
+				/* Don't let this area swap.  Change when
+				   DRM_KERNEL advisory is supported. */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		return -EINVAL;	/* This should never happen. */
+	}
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/dtlk.c linux.18p17/drivers/char/dtlk.c
--- linux.vanilla/drivers/char/dtlk.c	Sat Sep  9 18:42:35 2000
+++ linux.18p17/drivers/char/dtlk.c	Mon Oct  2 10:14:40 2000
@@ -61,7 +61,7 @@
 #include <linux/mm.h>		/* for verify_area */
 #include <linux/errno.h>	/* for -EBUSY */
 #include <linux/ioport.h>	/* for check_region, request_region */
-#include <linux/delay.h>	/* for loops_per_sec */
+#include <linux/delay.h>	/* for loops_per_jiffy */
 #include <asm/segment.h>	/* for put_user_byte */
 #include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
 #include <asm/uaccess.h>	/* for get_user, etc. */
@@ -154,7 +154,7 @@
 	if (minor != DTLK_MINOR || !dtlk_has_indexing)
 		return -EINVAL;
 
-	for (retries = 0; retries < loops_per_sec / 10; retries++) {
+	for (retries = 0; retries < 10000000; retries++) {
 		while (i < count && dtlk_readable()) {
 			ch = dtlk_read_lpc();
 			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
@@ -168,7 +168,7 @@
 			break;
 		dtlk_delay(10);
 	}
-	if (retries == loops_per_sec)
+	if (retries == 10000000)
 		printk(KERN_ERR "dtlk_read times out\n");
 	TRACE_RET;
 	return -EAGAIN;
@@ -222,7 +222,7 @@
 				   up to 250 usec for the RDY bit to
 				   go nonzero. */
 				for (retries = 0;
-				     retries < loops_per_sec / 4000;
+				     retries < 25000;
 				     retries++)
 					if (inb_p(dtlk_port_tts) &
 					    TTS_WRITABLE)
@@ -479,7 +479,7 @@
 for (i = 0; i < 10; i++)			\
   {						\
     buffer[b++] = inb_p(dtlk_port_lpc);		\
-    __delay(loops_per_sec/1000000);             \
+    udelay(1);					\
   }
 				char buffer[1000];
 				int b = 0, i, j;
@@ -490,7 +490,7 @@
 				LOOK
 				    dtlk_write_bytes("\0012I\r", 4);
 				buffer[b++] = 0;
-				__delay(50 * loops_per_sec / 1000);
+				mdelay(50);
 				outb_p(0xff, dtlk_port_lpc);
 				buffer[b++] = 0;
 				LOOK
@@ -509,12 +509,12 @@
 for (i = 0; i < 10; i++)			\
   {						\
     buffer[b++] = inb_p(dtlk_port_tts);		\
-    __delay(loops_per_sec/1000000);  /* 1 us */ \
+    udelay(1);					\
   }
 				char buffer[1000];
 				int b = 0, i, j;
 
-				__delay(loops_per_sec / 100);	/* 10 ms */
+				mdelay(10);
 				LOOK
 				    outb_p(0x03, dtlk_port_tts);
 				buffer[b++] = 0;
@@ -646,7 +646,7 @@
 	/* acknowledging a read takes 3-4
 	   usec.  Here, we wait up to 20 usec
 	   for the acknowledgement */
-	retries = (loops_per_sec * 20) / 1000000;
+	retries = 2000;
 	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
 	if (retries == 0)
 		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
@@ -704,7 +704,7 @@
 	/* the RDY bit goes zero 2-3 usec after writing, and goes
 	   1 again 180-190 usec later.  Here, we wait up to 10
 	   usec for the RDY bit to go zero. */
-	for (retries = 0; retries < loops_per_sec / 100000; retries++)
+	for (retries = 0; retries < 1000; retries++)
 		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
 			break;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/epca.c linux.18p17/drivers/char/epca.c
--- linux.vanilla/drivers/char/epca.c	Sat Sep  9 18:42:35 2000
+++ linux.18p17/drivers/char/epca.c	Mon Oct  2 10:14:40 2000
@@ -1681,7 +1681,7 @@
 		memory.
 	------------------------------------------------------------------*/
 
-	ulong flags, save_loops_per_sec; 
+	ulong flags;
 	int crd;
 	struct board_info *bd;
 	unsigned char board_id = 0;
@@ -1825,9 +1825,10 @@
 	/* --------------------------------------------------------------------- 
 	   loops_per_sec hasn't been set at this point :-(, so fake it out... 
 	   I set it, so that I can use the __delay() function.
+	   
+	   We don't use __delay(), so we don't need to fake it.
+	   
 	------------------------------------------------------------------------ */
-	save_loops_per_sec = loops_per_sec;
-	loops_per_sec = 13L * 500000L;
 
 	save_flags(flags);
 	cli();
@@ -1960,7 +1961,6 @@
 	if (tty_register_driver(&pc_info))
 		panic("Couldn't register Digi PC/ info ");
 
-	loops_per_sec = save_loops_per_sec;  /* reset it to what it should be */
 
 	/* -------------------------------------------------------------------
 	   Start up the poller to check for events on all enabled boards
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/i810_rng.c linux.18p17/drivers/char/i810_rng.c
--- linux.vanilla/drivers/char/i810_rng.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/i810_rng.c	Mon Sep 11 17:33:27 2000
@@ -0,0 +1,486 @@
+/*
+
+	Hardware driver for Intel i810 Random Number Generator (RNG)
+	Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+	Driver Web site:  http://gtf.org/garzik/drivers/i810_rng/
+
+
+
+	Based on:
+	Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet
+		May 1999 Order Number: 290658-002 R
+
+	Intel 82802 Firmware Hub: Random Number Generator
+	Programmer's Reference Manual
+		December 1999 Order Number: 298029-001 R
+
+	Intel 82802 Firmware HUB Random Number Generator Driver
+	Copyright (c) 2000 Matt Sottek <msottek@quiknet.com>
+
+	Special thanks to Matt Sottek.  I did the "guts", he
+	did the "brains" and all the testing.  (Anybody wanna send
+	me an i810 or i820?)
+
+	----------------------------------------------------------
+
+	This software may be used and distributed according to the terms
+        of the GNU Public License, incorporated herein by reference.
+
+	----------------------------------------------------------
+
+	From the firmware hub datasheet:
+
+	The Firmware Hub integrates a Random Number Generator (RNG)
+	using thermal noise generated from inherently random quantum
+	mechanical properties of silicon. When not generating new random
+	bits the RNG circuitry will enter a low power state. Intel will
+	provide a binary software driver to give third party software
+	access to our RNG for use as a security feature. At this time,
+	the RNG is only to be used with a system in an OS-present state.
+
+	----------------------------------------------------------
+
+	Theory of operation:
+
+	Character driver.  Using the standard open()
+	and read() system calls, you can read random data from
+	the i810 RNG device.  This data is NOT CHECKED by any
+	fitness tests, and could potentially be bogus (if the
+	hardware is faulty or has been tampered with).
+
+	/dev/intel_rng is char device major 10, minor 183.
+
+
+	----------------------------------------------------------
+
+	Driver notes:
+
+	* In order to unload the i810_rng module, you must first
+	make sure all users of the character device have closed
+
+	* FIXME:  Currently only one open() of the character device is allowed.
+	If another user tries to open() the device, they will get an
+	-EBUSY error.  Instead, this really should either support
+	multiple simultaneous users of the character device (not hard),
+	or simply block open() until the current user of the chrdev
+	calls close().
+
+	* FIXME: support poll()
+
+	* FIXME: should we be crazy and support mmap()?
+
+	----------------------------------------------------------
+
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/spinlock.h>
+#include <linux/random.h>
+#include <linux/sysctl.h>
+#include <linux/miscdevice.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+/*
+ * core module and version information
+ */
+#define RNG_VERSION "0.6.2-2.2.x"
+#define RNG_MODULE_NAME "i810_rng"
+#define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
+#define PFX RNG_MODULE_NAME ": "
+
+
+/*
+ * debugging macros
+ */
+#undef RNG_DEBUG /* define to 1 to enable copious debugging info */
+
+#ifdef RNG_DEBUG
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define RNG_NDEBUG 0        /* define to 1 to disable lightweight runtime checks */
+#if RNG_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+        if(!(expr)) {                                   \
+        printk( "Assertion failed! %s,%s,%s,line=%d\n", \
+        #expr,__FILE__,__FUNCTION__,__LINE__);          \
+        }
+#endif
+
+
+/*
+ * misc helper macros
+ */
+#define arraysize(x)            (sizeof(x)/sizeof(*(x)))
+
+
+/*
+ * RNG registers (offsets from rng_mem)
+ */
+#define RNG_HW_STATUS			0
+#define		RNG_PRESENT		0x40
+#define		RNG_ENABLED		0x01
+#define RNG_STATUS			1
+#define		RNG_DATA_PRESENT	0x01
+#define RNG_DATA			2
+
+#define RNG_ADDR			0xFFBC015F
+#define RNG_ADDR_LEN			3
+
+#define RNG_MISCDEV_MINOR		183 /* official */
+
+
+/*
+ * various RNG status variables.  they are globals
+ * as we only support a single RNG device
+ */
+static int rng_allocated;		/* is someone using the RNG region? */
+static int rng_hw_enabled;		/* is the RNG h/w enabled? */
+static int rng_use_count;		/* number of times RNG has been enabled */
+static void *rng_mem;			/* token to our ioremap'd RNG register area */
+static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; /* hardware lock */
+static int rng_open;			/* boolean, 0 (false) if chrdev is closed, 1 (true) if open */
+
+/*
+ * inlined helper functions for accessing RNG registers
+ */
+static inline u8 rng_hwstatus (void)
+{
+	assert (rng_mem != NULL);
+	return readb (rng_mem + RNG_HW_STATUS);
+}
+
+
+static inline void rng_hwstatus_set (u8 hw_status)
+{
+	assert (rng_mem != NULL);
+	writeb (hw_status, rng_mem + RNG_HW_STATUS);
+}
+
+
+static inline int rng_data_present (void)
+{
+	assert (rng_mem != NULL);
+	assert (rng_hw_enabled == 1);
+
+	return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0;
+}
+
+
+static inline int rng_data_read (void)
+{
+	assert (rng_mem != NULL);
+	assert (rng_hw_enabled == 1);
+
+	return readb (rng_mem + RNG_DATA);
+}
+
+
+/*
+ * rng_enable - enable or disable the RNG hardware
+ */
+static int rng_enable (int enable)
+{
+	int rc = 0;
+	u8 hw_status;
+
+	DPRINTK ("ENTER\n");
+
+	spin_lock (&rng_lock);
+
+	hw_status = rng_hwstatus ();
+
+	if (enable) {
+		rng_hw_enabled = 1;
+		rng_use_count++;
+		MOD_INC_USE_COUNT;
+	} else {
+		rng_use_count--;
+		if (rng_use_count == 0)
+			rng_hw_enabled = 0;
+		MOD_DEC_USE_COUNT;
+	}
+
+	if (rng_hw_enabled && ((hw_status & RNG_ENABLED) == 0)) {
+		rng_hwstatus_set (hw_status | RNG_ENABLED);
+		printk (KERN_INFO PFX "RNG h/w enabled\n");
+	}
+
+	else if (!rng_hw_enabled && (hw_status & RNG_ENABLED)) {
+		rng_hwstatus_set (hw_status & ~RNG_ENABLED);
+		printk (KERN_INFO PFX "RNG h/w disabled\n");
+	}
+
+	spin_unlock (&rng_lock);
+
+	if ((!!enable) != (!!(rng_hwstatus () & RNG_ENABLED))) {
+		printk (KERN_ERR PFX "Unable to %sable the RNG\n",
+			enable ? "en" : "dis");
+		rc = -EIO;
+	}
+
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static int rng_dev_open (struct inode *inode, struct file *filp)
+{
+	int rc = -EINVAL;
+
+	MOD_INC_USE_COUNT;
+
+	if ((filp->f_mode & FMODE_READ) == 0)
+		goto err_out;
+	if (filp->f_mode & FMODE_WRITE)
+		goto err_out;
+
+	spin_lock (&rng_lock);
+
+	/* only allow one open of this device, exit with -EBUSY if already open */
+	/* FIXME: we should sleep on a semaphore here, unless O_NONBLOCK */
+	if (rng_open) {
+		spin_unlock (&rng_lock);
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	rng_open = 1;
+
+	spin_unlock (&rng_lock);
+
+	if (rng_enable(1) != 0) {
+		spin_lock (&rng_lock);
+		rng_open = 0;
+		spin_unlock (&rng_lock);
+		rc = -EIO;
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	MOD_DEC_USE_COUNT;
+	return rc;
+}
+
+
+static int rng_dev_release (struct inode *inode, struct file *filp)
+{
+
+	if (rng_enable(0) != 0)
+		return -EIO;
+
+	spin_lock (&rng_lock);
+	rng_open = 0;
+	spin_unlock (&rng_lock);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static ssize_t rng_dev_read (struct file *filp, char * buf, size_t size,
+			     loff_t *offp)
+{
+	int have_data, copied = 0;
+	u8 data=0;
+	u8 *page;
+
+	if (size < 1)
+		return 0;
+
+	page = (unsigned char *) get_free_page (GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+read_loop:
+	/* using the fact that read() can return >0 but
+	 * less than the requested amount, we simply
+	 * read up to PAGE_SIZE or buffer size, whichever
+	 * is smaller, and return that data.
+	 */
+	if ((copied == size) || (copied == PAGE_SIZE)) {
+		size_t tmpsize = (copied == size) ? size : PAGE_SIZE;
+		int rc = copy_to_user (buf, page, tmpsize);
+		free_page ((long)page);
+		if (rc) return rc;
+		return tmpsize;
+	}
+
+	spin_lock (&rng_lock);
+
+	have_data = 0;
+	if (rng_data_present ()) {
+		data = rng_data_read ();
+		have_data = 1;
+	}
+
+	spin_unlock (&rng_lock);
+
+	if (have_data) {
+		page[copied] = data;
+		copied++;
+	} else {
+		if (filp->f_flags & O_NONBLOCK) {
+			free_page ((long)page);
+			return -EAGAIN;
+		}
+	}
+
+	if (current->need_resched)
+		schedule ();
+
+	if (signal_pending (current)) {
+		free_page ((long)page);
+		return -ERESTARTSYS;
+	}
+
+	goto read_loop;
+}
+
+
+/*
+ * rng_init_one - look for and attempt to init a single RNG
+ */
+static int __init rng_init_one (struct pci_dev *dev)
+{
+	int rc;
+	u8 hw_status;
+
+	DPRINTK ("ENTER\n");
+
+	if (rng_allocated) {
+		printk (KERN_ERR PFX "this driver only supports one RNG\n");
+		DPRINTK ("EXIT, returning -EBUSY\n");
+		return -EBUSY;
+	}
+
+	rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN);
+	if (rng_mem == NULL) {
+		printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
+		DPRINTK ("EXIT, returning -EBUSY\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	/* Check for Intel 82802 */
+	hw_status = rng_hwstatus ();
+	if ((hw_status & RNG_PRESENT) == 0) {
+		printk (KERN_ERR PFX "RNG not detected\n");
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	rng_allocated = 1;
+
+	rc = rng_enable (0);
+	if (rc) {
+		printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
+		goto err_out;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out:
+	if (rng_mem)
+		iounmap (rng_mem);
+	return rc;
+}
+
+
+/*
+ * Data for PCI driver interface
+ */
+
+MODULE_AUTHOR("Jeff Garzik, Matt Sottek");
+MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver");
+
+
+static struct file_operations rng_chrdev_ops = {
+	open:		rng_dev_open,
+	release:	rng_dev_release,
+	read:		rng_dev_read,
+};
+
+
+static struct miscdevice rng_miscdev = {
+	RNG_MISCDEV_MINOR,
+	RNG_MODULE_NAME,
+	&rng_chrdev_ops,
+};
+
+
+/*
+ * rng_init - initialize RNG module
+ */
+int __init rng_init (void)
+{
+	int rc;
+	struct pci_dev *pdev;
+	
+	pdev = pci_find_device (0x8086, 0x2418, NULL);
+	if (!pdev)
+		pdev = pci_find_device (0x8086, 0x2428, NULL);
+	if (!pdev)
+		return -ENODEV;
+
+	DPRINTK ("ENTER\n");
+
+	rc = rng_init_one(pdev);
+	if (rc) {
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		return rc;
+	}
+
+	rc = misc_register (&rng_miscdev);
+	if (rc) {
+		if (rng_mem)
+			iounmap (rng_mem);
+		DPRINTK ("EXIT, returning %d\n", rc);
+		return rc;
+	}
+
+	printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void) { return rng_init (); }
+
+/*
+ * rng_init - shutdown RNG module
+ */
+void cleanup_module (void)
+{
+	DPRINTK ("ENTER\n");
+	
+	iounmap (rng_mem);
+
+	rng_hwstatus_set (rng_hwstatus() & ~RNG_ENABLED);
+
+	misc_deregister (&rng_miscdev);
+
+	DPRINTK ("EXIT\n");
+}
+
+#endif /* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/isicom.c linux.18p17/drivers/char/isicom.c
--- linux.vanilla/drivers/char/isicom.c	Fri Apr 21 12:46:03 2000
+++ linux.18p17/drivers/char/isicom.c	Sat Sep 23 13:34:38 2000
@@ -16,6 +16,31 @@
  *
  *	10/6/99 sameer			Merged the ISA and PCI drivers to
  *					a new unified driver.
+ *
+ *	3/9/99	sameer			Added support for ISI4616 cards.
+ *
+ *	16/9/99	sameer			We do not force RTS low anymore.
+ *					This is to prevent the firmware 
+ *					from getting confused.
+ *
+ *	26/10/99 sameer			Cosmetic changes:The driver now
+ *					dumps the Port Count information
+ *					along with I/O address and IRQ.
+ *
+ *	13/12/99 sameer			Fixed the problem with IRQ sharing.
+ *
+ *	10/5/00  sameer			Fixed isicom_shutdown_board()
+ *					to not lower DTR on all the ports
+ *					when the last port on the card is 
+ *					closed.
+ *
+ *	10/5/00  sameer			Signal mask setup command added
+ *					to  isicom_setup_port and 
+ *					isicom_shutdown_port.
+ *
+ *	24/5/00 sameer			The driver is now SMP aware.
+ *					
+ *
  *	***********************************************************
  *
  *	To use this driver you also need the support package. You 
@@ -51,6 +76,7 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/spinlock.h>
 
 #include <linux/pci.h>
 
@@ -67,9 +93,11 @@
 				0x2058
 			};
 
+#define	DEVID_COUNT	(sizeof(device_id) / sizeof(device_id[0]))
+
 static int isicom_refcount = 0;
 static int prev_card = 3;	/*	start servicing isi_card[0]	*/
-static struct isi_board * irq_to_board[16] = { NULL, };
+/*static struct isi_board * irq_to_board[16] = { NULL, };*/
 static struct tty_driver isicom_normal, isicom_callout;
 static struct tty_struct * isicom_table[PORT_COUNT] = { NULL, };
 static struct termios * isicom_termios[PORT_COUNT] = { NULL, };
@@ -84,6 +112,7 @@
 static char re_schedule = 1;
 #ifdef ISICOM_DEBUG
 static unsigned long tx_count = 0;
+static unsigned long intr_count = 0;
 #endif
 
 static int ISILoad_open(struct inode *inode, struct file *filp);
@@ -140,6 +169,70 @@
 		return 1;
 }
 
+
+extern inline unsigned char valid_irq(int irq)
+{
+	if	((irq != 2) &&
+		(irq != 3) &&
+		(irq != 4) &&
+		(irq != 5) &&
+		(irq != 7) &&
+		(irq != 10) &&
+		(irq != 11) &&
+		(irq != 12) &&
+		(irq != 15))
+		return 0;
+	else
+		return 1;
+}
+
+static unsigned short reset_card(int base_addr, unsigned char read_portcount)
+{
+	unsigned int	i;
+	unsigned short	sig, portcount;
+
+	inw(base_addr + 0x8);
+			
+	for(i = jiffies+HZ/100; time_before(jiffies, i););
+				
+	outw(0, base_addr + 0x8); /* Reset */
+
+	for(i = jiffies+HZ; time_before(jiffies, i););
+
+	sig = (inw(base_addr + 0x4)) & 0xff;
+
+	if ((sig!= 0xa5) &&
+		(sig != 0xbb) &&
+		(sig != 0xcc) &&
+		(sig != 0xdd)) 
+		return 0;
+
+	if (read_portcount) {
+
+		for(i = jiffies+HZ/100; time_before(jiffies, i););
+
+		portcount = inw(base_addr + 0x2);
+		if ((portcount != 0) && 
+			(portcount != 4) && 
+			(portcount != 8) &&
+			(portcount != 16))
+			return 0;
+		else
+			sig |= (portcount << 8);
+	}
+
+	if (!(inw(base_addr + 0xe) & 0x1)) {
+#ifdef ISICOM_DEBUG				
+		printk("\nbase+0xe=0x%x", inw(base_addr + 0xe));
+#endif				
+		printk("\nISILoad:ISA Card reset failure"
+			"(Possible bad I/O Port Address 0x%x).\n",
+			base_addr);
+		return 0;
+	}
+	return sig;	
+}
+
 static int ISILoad_open(struct inode *inode, struct file *filp)
 {
 #ifdef ISICOM_DEBUG	
@@ -152,7 +245,7 @@
 static int ISILoad_release(struct inode *inode, struct file *filp)
 {
 #ifdef ISICOM_DEBUG
-	printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n",);
+	printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n");
 #endif	
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -161,7 +254,7 @@
 static int ISILoad_ioctl(struct inode *inode, struct file *filp,
 		         unsigned int cmd, unsigned long arg)
 {
-	unsigned int card, i, j, signature, status, portcount = 0;
+	unsigned int card, i, signature, status;
 	unsigned short word_count, base;
 	bin_frame frame;
 	/* exec_record exec_rec; */
@@ -182,46 +275,18 @@
 			if (!capable(CAP_SYS_ADMIN))
 				return -EPERM;
 			printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%x ",card+1,base);
-								
-			inw(base+0x8);
-			
-			for(i=jiffies+HZ/100;time_before(jiffies, i););
-				
-			outw(0,base+0x8); /* Reset */
-			
-			for(j=1;j<=3;j++) {
-				for(i=jiffies+HZ;time_before(jiffies, i););
-				printk(".");
-			}	
-			signature=(inw(base+0x4)) & 0xff;	
-			if (isi_card[card].isa) {
-					
-				if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {
-#ifdef ISICOM_DEBUG				
-					printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
-#endif				
-					printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
-					return -EIO;					
-				}
-			}	
-			else {
-				portcount = inw(base+0x2);
-				if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) {	
-#ifdef ISICOM_DEBUG
-					printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
-#endif
-					printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
-					return -EIO;
-				}
-			}	
-			switch(signature) {
+							
+			signature = reset_card(base, 
+					(isi_card[card].isa ? 0 : 1));
+
+			switch(signature & 0xff) {
 			case	0xa5:
 			case	0xbb:
 			case	0xdd:	
 					if (isi_card[card].isa) 
 						isi_card[card].port_count = 8;
 					else {
-						if (portcount == 4)
+						if ((signature & 0xff00) == 0x0400)
 							isi_card[card].port_count = 4;
 						else
 							isi_card[card].port_count = 8;
@@ -229,17 +294,20 @@
 				     	isi_card[card].shift_count = 12;
 				     	break;
 				        
-			case	0xcc:	isi_card[card].port_count = 16;
+			case	0xcc:	/*	ISI616EM	*/
+			case	0xee:	/*	ISI4616		*/
+					isi_card[card].port_count = 16;
 					isi_card[card].shift_count = 11;
 					break;  			
 					
-			default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
+			default: printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
 #ifdef ISICOM_DEBUG			
-				 printk("Sig=0x%x\n",signature);
+				 printk("Sig=0x%x\n", signature);
 #endif				 
 				 return -EIO;
 			}
 			printk("-Done\n");
+			signature &= 0xff;
 			return put_user(signature,(unsigned int*)arg);
 						
 	case	MIOCTL_LOAD_FIRMWARE:
@@ -266,8 +334,14 @@
 				return -EIO;
 				
 			if ((status=inw(base+0x4))!=0) {
-				printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", 
-				card+1, frame.addr, frame.count, status);
+				printk(KERN_WARNING "ISILoad:"
+					"Card%d rejected load header:"
+					"\nAddress:0x%x \nCount:0x%x"
+					" \nStatus:0x%x \n", 
+					card+1, 
+					frame.addr, 
+					frame.count, 
+					status);
 				return -EIO;
 			}
 			outsw(base, (void *) frame.bin_data, word_count);
@@ -280,7 +354,10 @@
 				return -EIO;
 				
 			if ((status=inw(base+0x4))!=0) {
-				printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status);
+				printk(KERN_ERR "ISILoad:Card%d"
+				" got out of sync.Card Status:0x%x\n",
+				card+1, 
+				status);
 				return -EIO;
 			}	
 			return 0;
@@ -346,6 +423,9 @@
 			outw(0x0, base+0x4);    /* for ISI4608 cards */
 							
 			isi_card[card].status |= FIRMWARE_LOADED;
+			printk(KERN_INFO "ISICOM: Card%d with %d ports at 0x%x using irq %d.\n", 
+				card+1,isi_card[card].port_count, 
+				isi_card[card].base, isi_card[card].irq); 
 			return 0;	
 			
 	default:
@@ -363,8 +443,10 @@
  *	ISICOM Driver specific routines ...
  *
  */
- 
-static inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev, 
+#ifndef ISIOCM_DEBUG 
+static 
+#endif
+inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev, 
 					const char * routine)
 {
 #ifdef ISICOM_DEBUG 
@@ -391,14 +473,15 @@
 } 
 
 /*	Transmitter	*/
-
-static void isicom_tx(unsigned long _data)
+#ifndef ISICOM_DEBUG
+static 
+#endif
+void isicom_tx(unsigned long _data)
 {
 	short count = (BOARD_COUNT-1), card, base;
-	short txcount, wait, wrd, residue, word_count, cnt;
+	short txcount, wrd, residue, word_count, cnt;
 	struct isi_port * port;
 	struct tty_struct * tty;
-	unsigned long flags;
 	
 #ifdef ISICOM_DEBUG
 	++tx_count;
@@ -416,54 +499,64 @@
 		
 	prev_card = card;
 	
-	count = isi_card[card].port_count;
+	
 	port = isi_card[card].ports;
 	base = isi_card[card].base;
-	for (;count > 0;count--, port++) {
+	for (count = isi_card[card].port_count; count > 0; count--, port++) {
+		
+		if (!lock_card_at_interrupt(&isi_card[card])) {
+#ifdef ISICOM_DEBUG
+			printk(KERN_DEBUG "ISICOM: isicom_tx:"
+				"Card(0x%x) found busy.\n",
+				card);
+#endif
+			continue;
+		}
+
 		/* port not active or tx disabled to force flow control */
-		if (!(port->status & ISI_TXOK))
+		if (!(port->flags & ASYNC_INITIALIZED) ||
+			!(port->status & ISI_TXOK)) {
+			unlock_card(&isi_card[card]);
 			continue;
+		}
 		
 		tty = port->tty;
-		save_flags(flags); cli();
+
 		txcount = MIN(TX_SIZE, port->xmit_cnt);
 		if ((txcount <= 0) || tty->stopped || tty->hw_stopped) {
-			restore_flags(flags);
-			continue;
-		}
-		wait = 200;	
-		while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
-		if (wait <= 0) {
-			restore_flags(flags);
-#ifdef ISICOM_DEBUG
-			printk(KERN_DEBUG "ISICOM: isicom_tx:Card(0x%x) found busy.\n",
-				card);
-#endif
+			unlock_card(&isi_card[card]);
 			continue;
 		}
+
 		if (!(inw(base + 0x02) & (1 << port->channel))) {
-			restore_flags(flags);
+			unlock_card(&isi_card[card]);
 #ifdef ISICOM_DEBUG					
-			printk(KERN_DEBUG "ISICOM: isicom_tx: cannot tx to 0x%x:%d.\n",
-					base, port->channel + 1);
+			printk(KERN_DEBUG "ISICOM: isicom_tx:"
+					"Card(0x%x) cannot accept data"
+					" for channel %d!\n",
+					base,
+					port->channel + 1);
 #endif					
 			continue;		
 		}
 #ifdef ISICOM_DEBUG
 		printk(KERN_DEBUG "ISICOM: txing %d bytes, port%d.\n", 
-				txcount, port->channel+1); 
+				txcount,
+				port->channel+1); 
 #endif	
-		outw((port->channel << isi_card[card].shift_count) | txcount
-					, base);
+		outw((port->channel << isi_card[card].shift_count) | txcount,
+			base);
 		residue = NO;
-		wrd = 0;			
+		wrd = 0;
 		while (1) {
 			cnt = MIN(txcount, (SERIAL_XMIT_SIZE - port->xmit_tail));
 			if (residue == YES) {
 				residue = NO;
 				if (cnt > 0) {
-					wrd |= (port->xmit_buf[port->xmit_tail] << 8);
-					port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+					wrd |= (port->xmit_buf[port->xmit_tail]
+						 << 8);
+					port->xmit_tail = (port->xmit_tail + 1)
+						 & (SERIAL_XMIT_SIZE - 1);
 					port->xmit_cnt--;
 					txcount--;
 					cnt--;
@@ -477,14 +570,15 @@
 			if (cnt <= 0) break;
 			word_count = cnt >> 1;
 			outsw(base, port->xmit_buf+port->xmit_tail, word_count);
-			port->xmit_tail = (port->xmit_tail + (word_count << 1)) &
-						(SERIAL_XMIT_SIZE - 1);
+			port->xmit_tail = (port->xmit_tail + (word_count << 1))
+				& (SERIAL_XMIT_SIZE - 1);
 			txcount -= (word_count << 1);
 			port->xmit_cnt -= (word_count << 1);
 			if (cnt & 0x0001) {
 				residue = YES;
 				wrd = port->xmit_buf[port->xmit_tail];
-				port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+				port->xmit_tail = (port->xmit_tail + 1) &
+						(SERIAL_XMIT_SIZE - 1);
 				port->xmit_cnt--;
 				txcount--;
 			}
@@ -495,7 +589,7 @@
 			port->status &= ~ISI_TXOK;
 		if (port->xmit_cnt <= WAKEUP_CHARS)
 			schedule_bh(port);
-		restore_flags(flags);
+		unlock_card(&isi_card[card]);
 	}	
 
 		/*	schedule another tx for hopefully in about 10ms	*/	
@@ -532,11 +626,13 @@
 	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 	wake_up_interruptible(&tty->write_wait);
-	wake_up_interruptible(&tty->poll_wait);
 } 		
  		
 /* main interrupt handler routine */ 		
-static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs)
 {
 	struct isi_board * card;
 	struct isi_port * port;
@@ -545,26 +641,34 @@
 	unsigned char channel;
 	short byte_count;
 	
-	/*
-	 *      find the source of interrupt
-	 */
-	 
-	for(count = 0; count < BOARD_COUNT; count++) { 
-		card = &isi_card[count];
-		if (card->base != 0) {
-			if (((card->isa == YES) && (card->irq == irq)) || 
-				((card->isa == NO) && (card->irq == irq) && (inw(card->base+0x0e) & 0x02)))
-				break;
-		}
-		card = NULL;
-	}
+#ifdef	ISICOM_DEBUG
+	++intr_count;
+#endif
+	
+	card = (struct isi_board *) dev_id;
 
 	if (!card || !(card->status & FIRMWARE_LOADED)) {
-/*		printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq);*/
+#ifdef ISICOM_DEBUG 
+		printk(KERN_DEBUG "ISICOM: interrupt:"
+				" not handling irq%d!.\n", 
+				irq);
+#endif
 		return;
 	}
-	
+
 	base = card->base;
+
+	/*
+	 *	If this is a PCI card, did it really interrupt us ?
+	 */
+	if ((card->isa == NO) && (!inw(base+0x0e) & 0x02))
+		return;
+
+	spin_lock(&card->card_lock);
+#ifdef ISICOM_DEBUG 
+	printk(KERN_DEBUG "ISICOM: isicom_interrupt: acquired card spinlock\n");
+
+#endif	
 	if (card->isa == NO) {
 	/*
 	 *      disable any interrupts from the PCI card and lower the
@@ -582,12 +686,14 @@
 	printk(KERN_DEBUG "ISICOM:Intr:(0x%x:%d).\n", base, channel+1);
 #endif	
 	if ((channel+1) > card->port_count) {
-		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n",
-				base, channel+1);
+		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x):"
+					" %d(channel) > port_count.\n",
+					base, channel+1);
 		if (card->isa)
 			ClearInterrupt(base);
 		else
 			outw(0x0000, base+0x04); /* enable interrupts */		
+		spin_unlock(&card->card_lock);
 		return;			
 	}
 	port = card->ports + channel;
@@ -596,6 +702,7 @@
 			ClearInterrupt(base);
 		else
 			outw(0x0000, base+0x04); /* enable interrupts */
+		spin_unlock(&card->card_lock);
 		return;
 	}	
 		
@@ -606,6 +713,7 @@
 		switch(header & 0xff) {
 			case 0:	/* Change in EIA signals */
 				
+
 				if (port->flags & ASYNC_CHECK_CD) {
 					if (port->status & ISI_DCD) {
 						if (!(header & ISI_DCD)) {
@@ -613,7 +721,6 @@
 #ifdef ISICOM_DEBUG						
 							printk(KERN_DEBUG "ISICOM: interrupt: DCD->low.\n");
 #endif							
-							port->status &= ~ISI_DCD;
 							if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
 								(port->flags & ASYNC_CALLOUT_NOHUP)))
 								queue_task(&port->hangup_tq,
@@ -626,42 +733,39 @@
 #ifdef ISICOM_DEBUG
 							printk(KERN_DEBUG "ISICOM: interrupt: DCD->high.\n");
 #endif							
-							port->status |= ISI_DCD;
 							wake_up_interruptible(&port->open_wait);
 						}
 					}
 				}
-				else {
-					if (header & ISI_DCD) 
-						port->status |= ISI_DCD;
-					else
-						port->status &= ~ISI_DCD;
-				}	
 				
+				if (header & ISI_DCD) 
+					port->status |= ISI_DCD;
+				else
+					port->status &= ~ISI_DCD;
+
 				if (port->flags & ASYNC_CTS_FLOW) {
 					if (port->tty->hw_stopped) {
 						if (header & ISI_CTS) {
 							port->tty->hw_stopped = 0;
-							/* start tx ing */
-							port->status |= (ISI_TXOK | ISI_CTS);
+							/* start tx ing 
+							port->status |= (ISI_TXOK | ISI_CTS);*/
 							schedule_bh(port);
 						}
 					}
 					else {
 						if (!(header & ISI_CTS)) {
-							port->tty->hw_stopped = 1;
-							/* stop tx ing */
-							port->status &= ~(ISI_TXOK | ISI_CTS);
+						/*	port->tty->hw_stopped = 1;*/
+							/* stop tx ing 
+							port->status &= ~(ISI_TXOK | ISI_CTS);*/
 						}
 					}
 				}
-				else {
-					if (header & ISI_CTS) 
-						port->status |= ISI_CTS;
-					else
-						port->status &= ~ISI_CTS;
-				}
 				
+				if (header & ISI_CTS) 
+					port->status |= ISI_CTS;
+				else
+					port->status &= ~ISI_CTS;
+
 				if (header & ISI_DSR) 
 					port->status |= ISI_DSR;
 				else
@@ -687,7 +791,8 @@
 				break;
 				
 			case 2:	/* Statistics		 */
-				printk(KERN_DEBUG "ISICOM: isicom_interrupt: stats!!!.\n");			
+				printk(KERN_DEBUG "ISICOM: isicom_interrupt:"
+					" stats!!!.\n");
 				break;
 				
 			default:
@@ -714,8 +819,9 @@
 		tty->flip.count += count;
 		
 		if (byte_count > 0) {
-			printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d): Flip buffer overflow! dropping bytes...\n",
-					base, channel+1);
+			printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d):"
+				" Flip buffer overflow! dropping bytes...\n",
+				base, channel+1);
 			while(byte_count > 0) { /* drain out unread xtra data */
 				inw(base);
 				byte_count -= 2;
@@ -726,20 +832,25 @@
 	if (card->isa == YES)
 		ClearInterrupt(base);
 	else
-		outw(0x0000, base+0x04); /* enable interrupts */	
+		outw(0x0000, base+0x04); /* enable interrupts */
+	spin_unlock(&card->card_lock);
 	return;
 } 
-
- /* called with interrupts disabled */ 
-static void isicom_config_port(struct isi_port * port)
+#ifndef ISICOM_DEBUG
+static 
+#endif
+void isicom_config_port(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	struct tty_struct * tty;
 	unsigned long baud;
-	unsigned short channel_setup, wait, base = card->base;
+	unsigned short channel_setup, base = card->base;
 	unsigned short channel = port->channel, shift_count = card->shift_count;
 	unsigned char flow_ctrl;
 	
+#ifdef	ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM:isicom_config_port:start\n");
+#endif
 	if (!(tty = port->tty) || !tty->termios)
 		return;
 	baud = C_BAUD(tty);
@@ -776,40 +887,47 @@
 	else  
 		raise_dtr(port);
 		
-	wait = 100;	
-	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
-	if (!wait) {
-		printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at channel setup.\n");
-		return;
-	}			 
-	outw(0x8000 | (channel << shift_count) |0x03, base);
-	outw(linuxb_to_isib[baud] << 8 | 0x03, base);
-	channel_setup = 0;
-	switch(C_CSIZE(tty)) {
-		case CS5:
-			channel_setup |= ISICOM_CS5;
-			break;
-		case CS6:
-			channel_setup |= ISICOM_CS6;
-			break;
-		case CS7:
-			channel_setup |= ISICOM_CS7;
-			break;
-		case CS8:
-			channel_setup |= ISICOM_CS8;
-			break;
-	}
+	if (lock_card(card)) {
+		outw(0x8000 | (channel << shift_count) |0x03, base);
+		outw(linuxb_to_isib[baud] << 8 | 0x03, base);
+		channel_setup = 0;
+		switch(C_CSIZE(tty)) {
+			case CS5:
+				channel_setup |= ISICOM_CS5;
+				break;
+			case CS6:
+				channel_setup |= ISICOM_CS6;
+				break;
+			case CS7:
+				channel_setup |= ISICOM_CS7;
+				break;
+			case CS8:
+				channel_setup |= ISICOM_CS8;
+				break;
+		}
 		
-	if (C_CSTOPB(tty))
-		channel_setup |= ISICOM_2SB;
-	
-	if (C_PARENB(tty))
-		channel_setup |= ISICOM_EVPAR;
-	if (C_PARODD(tty))
-		channel_setup |= ISICOM_ODPAR;	
-	outw(channel_setup, base);	
-	InterruptTheCard(base);
+		if (C_CSTOPB(tty))
+			channel_setup |= ISICOM_2SB;
 	
+		if (C_PARENB(tty)) {
+			if (C_PARODD(tty))
+				channel_setup |= ISICOM_ODPAR;
+			else
+				channel_setup |= ISICOM_EVPAR;
+		}
+		outw(channel_setup, base);	
+		InterruptTheCard(base);
+
+#ifdef	ISICOM_DEBUG
+		printk(KERN_DEBUG "ISICOM:isicom_config_port:channel setup\n");
+#endif
+		unlock_card(card);	
+	}
+	else 
+		printk(KERN_WARNING "ISICOM:isicom_config_port:"
+				"Card (0x%x) busy for Channel Setup\n",
+				card->base);
+
 	if (C_CLOCAL(tty))
 		port->flags &= ~ASYNC_CHECK_CD;
 	else
@@ -827,23 +945,33 @@
 	if (I_IXOFF(tty))
 		flow_ctrl |= ISICOM_INITIATE_XONXOFF;	
 		
-	wait = 100;	
-	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
-	if (!wait) {
-		printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at flow setup.\n");
-		return;
-	}			 
-	outw(0x8000 | (channel << shift_count) |0x04, base);
-	outw(flow_ctrl << 8 | 0x05, base);
-	outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
-	InterruptTheCard(base);
+	if (lock_card(card)) {
+		outw(0x8000 | (channel << shift_count) |0x04, base);
+		outw(flow_ctrl << 8 | 0x05, base);
+		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
+		InterruptTheCard(base);
+
+#ifdef	ISICOM_DEBUG
+		printk(KERN_DEBUG "ISICOM:isicom_config_port:flow setup\n");
+#endif
+
+		unlock_card(card);
+	}
+	else
+		printk(KERN_WARNING "ISICOM:isicom_config_port:"
+				"Card (0x%x) busy for Flow Ctrl Setup\n",
+				card->base);
 	
+			 
 	/*	rx enabled -> enable port for rx on the card	*/
 	if (C_CREAD(tty)) {
 		card->port_status |= (1 << channel);
 		outw(card->port_status, base + 0x02);
 	}
 		
+#ifdef	ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM:isicom_config_port:stop\n");
+#endif
 }
  
 /* open et all */ 
@@ -854,74 +982,106 @@
 	struct isi_port * port;
 	unsigned long flags;
 	
-	if (bp->status & BOARD_ACTIVE) 
-		return;
-	port = bp->ports;
 #ifdef ISICOM_DEBUG	
-	printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts start, port_count %d...\n", bp->port_count);
+	printk(KERN_DEBUG "ISICOM: setup_board:start\n");
+#endif
+
+	spin_lock_irqsave(&bp->card_lock, flags);
+		if (bp->status & BOARD_ACTIVE) {
+#ifdef ISICOM_DEBUG	
+			printk(KERN_DEBUG "ISICOM: setup_board:"
+					" already active\n");
+#endif
+			spin_unlock_irqrestore(&bp->card_lock, flags);
+			return;
+		}
+		port = bp->ports;
+		bp->status |= BOARD_ACTIVE;
+		MOD_INC_USE_COUNT;
+	spin_unlock_irqrestore(&bp->card_lock, flags);
+
+	for(channel = 0; channel < bp->port_count; channel++, port++)
+		drop_dtr(port);
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: setup_board:stop\n");
 #endif
-	for(channel = 0; channel < bp->port_count; channel++, port++) {
-		save_flags(flags); cli();
-		drop_dtr_rts(port);
-		restore_flags(flags);
-	}
-#ifdef ISICOM_DEBUG		
-	printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts stop...\n");	
-#endif	
-	
-	bp->status |= BOARD_ACTIVE;
-	MOD_INC_USE_COUNT;
 	return;
 }
- 
-static int isicom_setup_port(struct isi_port * port)
+
+#ifndef	ISICOM_DEBUG 
+static 
+#endif
+int isicom_setup_port(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	unsigned long flags;
 	
-	if (port->flags & ASYNC_INITIALIZED)
-		return 0;
-	if (!port->xmit_buf) {
-		unsigned long page;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: setup_port:start\n");
+#endif
+	spin_lock_irqsave(&card->card_lock, flags);
+		if (port->flags & ASYNC_INITIALIZED) {
+			spin_unlock_irqrestore(&card->card_lock, flags);
+			return 0;
+		}
+
+		if (!port->xmit_buf) {
+			unsigned long page;
 		
-		if (!(page = get_free_page(GFP_KERNEL)))
-			return -ENOMEM;
+			if (!(page = get_free_page(GFP_KERNEL))) {
+				spin_unlock_irqrestore(&card->card_lock, flags);
+				return -ENOMEM;
+			}
 		
-		if (port->xmit_buf) {
-			free_page(page);
-			return -ERESTARTSYS;
+			if (port->xmit_buf) {
+				free_page(page);
+				spin_unlock_irqrestore(&card->card_lock, flags);
+				return -ERESTARTSYS;
+			}
+			port->xmit_buf = (unsigned char *) page;	
 		}
-		port->xmit_buf = (unsigned char *) page;	
-	}	
-	save_flags(flags); cli();
-	if (port->tty)
-		clear_bit(TTY_IO_ERROR, &port->tty->flags);
-	if (port->count == 1)
-		card->count++;
+
+		if (port->tty)
+			clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		if (port->count == 1)
+			card->count++;
 		
-	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+		port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+
+		port->flags |= ASYNC_INITIALIZED;
+	spin_unlock_irqrestore(&card->card_lock, flags);
 	
 	/*	discard any residual data	*/
 	kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX);
 	
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: setup_port: isicom_config_port...\n");	
+#endif	
 	isicom_config_port(port);
-	port->flags |= ASYNC_INITIALIZED;
 	
-	restore_flags(flags);
+	set_signal_mask(port, 0xff); /* enable EIA status interrupt reporting */
 	
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: setup_port:stop\n");
+#endif
 	return 0;		
-} 
- 
-static int block_til_ready(struct tty_struct * tty, struct file * filp, struct isi_port * port) 
+}
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+int block_til_ready(struct tty_struct * tty, struct file * filp, 
+				struct isi_port * port)
 {
 	int do_clocal = 0, retval;
 	struct wait_queue wait = { current, NULL };
+	struct isi_board *card = port->card;
 
 	/* block if port is in the process of being closed */
 
 	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
 #ifdef ISICOM_DEBUG	
-		printk(KERN_DEBUG "ISICOM: block_til_ready: close in progress.\n");
+		printk(KERN_DEBUG "ISICOM: block_til_ready:"
+				" close in progress.\n");
 #endif		
 		interruptible_sleep_on(&port->close_wait);
 		if (port->flags & ASYNC_HUP_NOTIFY)
@@ -948,15 +1108,14 @@
 		    (port->pgrp != current->pgrp))
 			return -EBUSY;
 		port->flags |= ASYNC_CALLOUT_ACTIVE;
-		cli();
 		raise_dtr_rts(port);
-		sti();
 		return 0;
 	}
 	
 	/* if non-blocking mode is set ... */
 	
-	if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {
+	if ((filp->f_flags & O_NONBLOCK) || 
+		(tty->flags & (1 << TTY_IO_ERROR))) {
 #ifdef ISICOM_DEBUG	
 		printk(KERN_DEBUG "ISICOM: block_til_ready: non-block mode.\n");
 #endif		
@@ -982,20 +1141,20 @@
 						callout dev is busy */
 	retval = 0;
 	add_wait_queue(&port->open_wait, &wait);
-	cli();
+
+	spin_lock_irqsave(&card->card_lock, card->flags);
 		if (!tty_hung_up_p(filp))
 			port->count--;
-	sti();
+	spin_unlock_irqrestore(&card->card_lock, card->flags);
+
 	port->blocked_open++;
 #ifdef ISICOM_DEBUG	
 	printk(KERN_DEBUG "ISICOM: block_til_ready: waiting for DCD...\n");
 #endif	
 	while (1) {
-		cli();
 		if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) 
 			raise_dtr_rts(port);
 		
-		sti();
 		current->state = TASK_INTERRUPTIBLE;
 		if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { 	
 			if (port->flags & ASYNC_HUP_NOTIFY)
@@ -1003,7 +1162,8 @@
 			else
 				retval = -ERESTARTSYS;
 #ifdef ISICOM_DEBUG				
-			printk(KERN_DEBUG "ISICOM: block_til_ready: tty_hung_up_p || not init.\n"); 
+			printk(KERN_DEBUG "ISICOM: block_til_ready:"
+					" tty_hung_up_p || not init.\n"); 
 #endif			
 			break;
 		}	
@@ -1011,13 +1171,15 @@
 		    !(port->flags & ASYNC_CLOSING) &&
 		    (do_clocal || (port->status & ISI_DCD))) {
 #ifdef ISICOM_DEBUG		    
-		 	printk(KERN_DEBUG "ISICOM: block_til_ready: do_clocal || DCD.\n");   
+		 	printk(KERN_DEBUG "ISICOM: block_til_ready:"
+					" do_clocal || DCD.\n");   
 #endif		 	
 			break;
 		}	
 		if (signal_pending(current)) {
 #ifdef ISICOM_DEBUG		
-			printk(KERN_DEBUG "ISICOM: block_til_ready: sig blocked.\n");
+			printk(KERN_DEBUG "ISICOM: block_til_ready:"
+					" sig blocked.\n");
 #endif			
 			retval = -ERESTARTSYS;
 			break;
@@ -1034,13 +1196,15 @@
 	port->flags |= ASYNC_NORMAL_ACTIVE;
 	return 0;
 }
- 
-static int isicom_open(struct tty_struct * tty, struct file * filp)
+
+#ifndef	ISICOM_DEBUG 
+static 
+#endif
+int isicom_open(struct tty_struct * tty, struct file * filp)
 {
 	struct isi_port * port;
 	struct isi_board * card;
 	unsigned int line, board;
-	unsigned long flags;
 	int error;
 
 #ifdef ISICOM_DEBUG	
@@ -1063,14 +1227,16 @@
 	card = &isi_card[board];
 	if (!(card->status & FIRMWARE_LOADED)) {
 #ifdef ISICOM_DEBUG	
-		printk(KERN_DEBUG"ISICOM: Firmware not loaded to card%d.\n", board);
+		printk(KERN_DEBUG"ISICOM: Firmware not loaded"
+				" to card%d.\n", board);
 #endif		
 		return -ENODEV;
 	}
 	
 	/*  open on a port greater than the port count for the card !!! */
 	if (line > ((board * 16) + card->port_count - 1)) {
-		printk(KERN_ERR "ISICOM: Open on a port which exceeds the port_count of the card!\n");
+		printk(KERN_ERR "ISICOM: Open on a port which exceeds"
+				" the port_count of the card!\n");
 		return -ENODEV;
 	}	
 	port = &isi_ports[line];	
@@ -1080,16 +1246,18 @@
 #ifdef ISICOM_DEBUG		
 	printk(KERN_DEBUG "ISICOM: isicom_setup_board ...\n");		
 #endif	
-	isicom_setup_board(card);		
+	isicom_setup_board(card);
 	
 	port->count++;
 	tty->driver_data = port;
 	port->tty = tty;
+
 #ifdef ISICOM_DEBUG	
 	printk(KERN_DEBUG "ISICOM: isicom_setup_port ...\n");
 #endif	
 	if ((error = isicom_setup_port(port))!=0)
 		return error;
+
 #ifdef ISICOM_DEBUG		
 	printk(KERN_DEBUG "ISICOM: block_til_ready ...\n");	
 #endif	
@@ -1101,9 +1269,7 @@
 			*tty->termios = port->normal_termios;
 		else 
 			*tty->termios = port->callout_termios;
-		save_flags(flags); cli();
 		isicom_config_port(port);
-		restore_flags(flags);		
 	}	
 	
 	port->session = current->session;	
@@ -1118,30 +1284,40 @@
 
 extern inline void isicom_shutdown_board(struct isi_board * bp)
 {
-	int channel;
-	struct isi_port * port;
-	
-	if (!(bp->status & BOARD_ACTIVE))
-		return;
-	bp->status &= ~BOARD_ACTIVE;
-	port = bp->ports;
-	for(channel = 0; channel < bp->port_count; channel++, port++) {
-		drop_dtr_rts(port);
-	}	
-	MOD_DEC_USE_COUNT;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bp->card_lock, flags);
+		if (!(bp->status & BOARD_ACTIVE)) {
+			spin_unlock_irqrestore(&bp->card_lock, flags);
+			return;
+		}
+		bp->status &= ~BOARD_ACTIVE;
+		MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore(&bp->card_lock, flags);
 }
 
-static void isicom_shutdown_port(struct isi_port * port)
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+void isicom_shutdown_port(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
-	struct tty_struct * tty;	
-	
-	if (!(port->flags & ASYNC_INITIALIZED))
-		return;
-	if (port->xmit_buf) {
-		free_page((unsigned long) port->xmit_buf);
-		port->xmit_buf = NULL;
-	}	
+	struct tty_struct * tty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->card_lock, flags);	
+		if (!(port->flags & ASYNC_INITIALIZED)){
+			spin_unlock_irqrestore(&card->card_lock, flags);
+			return;
+		}
+		if (port->xmit_buf) {
+			free_page((unsigned long) port->xmit_buf);
+			port->xmit_buf = NULL;
+		}	
+		port->flags &= ~ASYNC_INITIALIZED;
+	spin_unlock_irqrestore(&card->card_lock, flags);
+
+	tty = port->tty;
 	if (!(tty = port->tty) || C_HUPCL(tty)) 
 		/* drop dtr on this port */
 		drop_dtr(port);
@@ -1150,37 +1326,44 @@
 	
 	if (tty)
 		set_bit(TTY_IO_ERROR, &tty->flags);
-	port->flags &= ~ASYNC_INITIALIZED;
 	
 	if (--card->count < 0) {
-		printk(KERN_DEBUG "ISICOM: isicom_shutdown_port: bad board(0x%x) count %d.\n",
-			card->base, card->count);
+		printk(KERN_DEBUG "ISICOM: isicom_shutdown_port:"
+				" bad board(0x%x) count %d.\n",
+				card->base, card->count);
 		card->count = 0;	
 	}
 	
+	set_signal_mask(port, 0x00); /*disable EIA status interrupt reporting */
+
 	/* last port was closed , shutdown that boad too */
 	if (!card->count)
 		isicom_shutdown_board(card);
 }
 
-static void isicom_close(struct tty_struct * tty, struct file * filp)
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+void isicom_close(struct tty_struct * tty, struct file * filp)
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
-	struct isi_board * card = port->card;
+	struct isi_board * card;
 	unsigned long flags;
 	
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: Close start!!!.\n");
+#endif	
 	if (!port)
 		return;
+
+	card = port->card;
+
 	if (isicom_paranoia_check(port, tty->device, "isicom_close"))
 		return;
 	
-#ifdef ISICOM_DEBUG		
-	printk(KERN_DEBUG "ISICOM: Close start!!!.\n");
-#endif	
-	
-	save_flags(flags); cli();
+	spin_lock_irqsave(&card->card_lock, flags);
 	if (tty_hung_up_p(filp)) {
-		restore_flags(flags);
+		spin_unlock_irqrestore(&card->card_lock, flags);
 		return;
 	}
 	
@@ -1191,14 +1374,16 @@
 		port->count = 1;
 	}
 	if (--port->count < 0) {
-		printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count for"
-			"channel%d = %d", card->base, port->channel, 
+		printk(KERN_WARNING "ISICOM:(0x%x) isicom_close:"
+			" bad port count for channel%d = %d", 
+			card->base, 
+			port->channel, 
 			port->count);
 		port->count = 0;	
 	}
 	
 	if (port->count) {
-		restore_flags(flags);
+		spin_unlock_irqrestore(&card->card_lock, flags);
 		return;
 	} 	
 	port->flags |= ASYNC_CLOSING;
@@ -1212,22 +1397,35 @@
 		port->callout_termios = *tty->termios;
 	
 	tty->closing = 1;
+
+	spin_unlock_irqrestore(&card->card_lock, flags);
 	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
 		tty_wait_until_sent(tty, port->closing_wait);
-	/* indicate to the card that no more data can be received 
-	   on this port */
+	spin_lock_irqsave(&card->card_lock, flags);
+
+	/* 
+	 *	indicate to the card that no more data can be received 
+	 *	on this port
+	 */
 	if (port->flags & ASYNC_INITIALIZED) {   
 		card->port_status &= ~(1 << port->channel);
 		outw(card->port_status, card->base + 0x02);
-	}	
+	}
+
+	spin_unlock_irqrestore(&card->card_lock, flags);
+
 	isicom_shutdown_port(port);
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
 	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
+
+	spin_lock_irqsave(&card->card_lock, flags);
+
 	tty->closing = 0;
 	port->tty = 0;
 	if (port->blocked_open) {
+		spin_unlock_irqrestore(&card->card_lock, flags);
 		if (port->close_delay) {
 			current->state = TASK_INTERRUPTIBLE;
 #ifdef ISICOM_DEBUG			
@@ -1236,101 +1434,130 @@
 			schedule_timeout(port->close_delay);
 		}
 		wake_up_interruptible(&port->open_wait);
+		spin_lock_irqsave(&card->card_lock, flags);
 	}	
 	port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | 
 			ASYNC_CLOSING);
+	port->status &= ~ISI_TXOK;
 	wake_up_interruptible(&port->close_wait);
-	restore_flags(flags);
+
+	spin_unlock_irqrestore(&card->card_lock, flags);
+
 #ifdef ISICOM_DEBUG	
 	printk(KERN_DEBUG "ISICOM: Close end!!!.\n");
 #endif	
 }
 
 /* write et all */
-static int isicom_write(struct tty_struct * tty, int from_user,
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+int isicom_write(struct tty_struct * tty, int from_user,
 			const unsigned char * buf, int count)
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
-	unsigned long flags;
+	struct isi_board * card = port->card;
 	int cnt, total = 0;
 #ifdef ISICOM_DEBUG
-	printk(KERN_DEBUG "ISICOM: isicom_write for port%d: %d bytes.\n",
-			port->channel+1, count);
+/*	printk(KERN_DEBUG "ISICOM: isicom_write for port%d: %d bytes.\n",
+			port->channel+1, count);*/
 #endif	  	
 	if (isicom_paranoia_check(port, tty->device, "isicom_write"))
 		return 0;
 	
+	spin_lock_irqsave(&card->card_lock, card->flags);
+
 	if (!tty || !port->xmit_buf || !tmp_buf)
 		return 0;
 	if (from_user)
 		down(&tmp_buf_sem); /* acquire xclusive access to tmp_buf */
 		
-	save_flags(flags);
+	
 	while(1) {	
-		cli();
+
+/*		spin_lock_irqsave(&card->card_lock, card->flags);*/
 		cnt = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
 			SERIAL_XMIT_SIZE - port->xmit_head));
-		if (cnt <= 0) 
+		if (cnt <= 0)  {
+/*			spin_unlock_irqrestore(&card->card_lock, card->flags);*/
 			break;
+		}
 		
 		if (from_user) {
-			/* the following may block for paging... hence 
-			   enabling interrupts but tx routine may have 
-			   created more space in xmit_buf when the ctrl 
-			   gets back here  */
-			sti(); 
+			/*	
+			 *	the following may block for paging... hence 
+			 *	enabling interrupts but tx routine may have 
+			 *	created more space in xmit_buf when the ctrl 
+			 *	gets back here
+			 */
+
+			spin_unlock_irqrestore(&card->card_lock, card->flags);
+
 			copy_from_user(tmp_buf, buf, cnt);
-			cli();
-			cnt = MIN(cnt, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
-			SERIAL_XMIT_SIZE - port->xmit_head));
+
+			spin_lock_irqsave(&card->card_lock, card->flags);
+
+			cnt = MIN(cnt,
+				MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+				SERIAL_XMIT_SIZE - port->xmit_head));
 			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, cnt);
 		}	
 		else
 			memcpy(port->xmit_buf + port->xmit_head, buf, cnt);
-		port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - 1);
+		port->xmit_head = (port->xmit_head + cnt) &
+				(SERIAL_XMIT_SIZE - 1);
 		port->xmit_cnt += cnt;
-		restore_flags(flags);
+
+/*		spin_unlock_irqrestore(&card->card_lock, card->flags);*/
+		
 		buf += cnt;
 		count -= cnt;
 		total += cnt;
-	}		
+	}
 	if (from_user)
 		up(&tmp_buf_sem);
+
 	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
 		port->status |= ISI_TXOK;
-	restore_flags(flags);
+
+	spin_unlock_irqrestore(&card->card_lock, card->flags);
+
 #ifdef ISICOM_DEBUG
-	printk(KERN_DEBUG "ISICOM: isicom_write %d bytes written.\n", total);
+/*	printk(KERN_DEBUG "ISICOM: isicom_write %d bytes written.\n", total);*/
 #endif		
-	return total;	
+	return total;
 }
 
 /* put_char et all */
-static void isicom_put_char(struct tty_struct * tty, unsigned char ch)
+#ifndef	ISICOM_DEBUG
+static 
+#endif
+void isicom_put_char(struct tty_struct * tty, unsigned char ch)
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
-	unsigned long flags;
+	struct isi_board * card = port->card;
 	
+#ifdef ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM: put_char, port %d, char %c.\n", port->channel+1, ch);
+#endif			
 	if (isicom_paranoia_check(port, tty->device, "isicom_put_char"))
 		return;
 	
 	if (!tty || !port->xmit_buf)
 		return;
-#ifdef ISICOM_DEBUG
-	printk(KERN_DEBUG "ISICOM: put_char, port %d, char %c.\n", port->channel+1, ch);
-#endif			
-		
-	save_flags(flags); cli();
+
+	spin_lock_irqsave(&card->card_lock, card->flags);
 	
-	if (port->xmit_cnt >= (SERIAL_XMIT_SIZE - 1)) {
-		restore_flags(flags);
-		return;
-	}
+		if (port->xmit_cnt >= (SERIAL_XMIT_SIZE - 1)) {
+			spin_unlock_irqrestore(&card->card_lock, card->flags);
+			return;
+		}
 	
-	port->xmit_buf[port->xmit_head++] = ch;
-	port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
-	port->xmit_cnt++;
-	restore_flags(flags);
+		port->xmit_buf[port->xmit_head++] = ch;
+		port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
+		port->xmit_cnt++;
+
+	spin_unlock_irqrestore(&card->card_lock, card->flags);
 }
 
 /* flush_chars et all */
@@ -1377,21 +1604,20 @@
 extern inline void isicom_send_break(struct isi_port * port, unsigned long length)
 {
 	struct isi_board * card = port->card;
-	short wait = 10;
 	unsigned short base = card->base;	
-	unsigned long flags;
 	
-	save_flags(flags); cli();
-	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
-	if (!wait) {
-		printk(KERN_DEBUG "ISICOM: Card found busy in isicom_send_break.\n");
+	if (!lock_card(card)) {
+		printk(KERN_DEBUG "ISICOM: Card found busy in"
+			" isicom_send_break.\n");
 		return;
-	}	
+	}
+
 	outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
 	outw((length & 0xff) << 8 | 0x00, base);
 	outw((length & 0xff00), base);
 	InterruptTheCard(base);
-	restore_flags(flags);
+
+	unlock_card(card);
 }
 
 static int isicom_get_modem_info(struct isi_port * port, unsigned int * value)
@@ -1414,12 +1640,10 @@
 					unsigned int * value)
 {
 	unsigned int arg;
-	unsigned long flags;
 	
 	if(get_user(arg, value))
 		return -EFAULT;
 	
-	save_flags(flags); cli();
 	
 	switch(cmd) {
 		case TIOCMBIS:
@@ -1449,10 +1673,8 @@
 			break;
 		
 		default:
-			restore_flags(flags);
 			return -EINVAL;		 	
 	}
-	restore_flags(flags);
 	return 0;
 }			
 
@@ -1460,7 +1682,6 @@
 					struct serial_struct * info)
 {
 	struct serial_struct newinfo;
-	unsigned long flags;
 	int reconfig_port;
 
 	if(copy_from_user(&newinfo, info, sizeof(newinfo)))
@@ -1485,9 +1706,7 @@
 				(newinfo.flags & ASYNC_FLAGS));
 	}
 	if (reconfig_port) {
-		save_flags(flags); cli();
 		isicom_config_port(port);
-		restore_flags(flags);
 	}
 	return 0;		 
 }		
@@ -1576,7 +1795,6 @@
 static void isicom_set_termios(struct tty_struct * tty, struct termios * old_termios)
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
-	unsigned long flags;
 	
 	if (isicom_paranoia_check(port, tty->device, "isicom_set_termios"))
 		return;
@@ -1585,9 +1803,7 @@
 	    tty->termios->c_iflag == old_termios->c_iflag)
 		return;
 		
-	save_flags(flags); cli();
 	isicom_config_port(port);
-	restore_flags(flags);
 	
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {	
@@ -1601,16 +1817,13 @@
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
 	struct isi_board * card = port->card;
-	unsigned long flags;
 	
 	if (isicom_paranoia_check(port, tty->device, "isicom_throttle"))
 		return;
 	
 	/* tell the card that this port cannot handle any more data for now */
-	save_flags(flags); cli();
 	card->port_status &= ~(1 << port->channel);
 	outw(card->port_status, card->base + 0x02);
-	restore_flags(flags);
 }
 
 /* unthrottle et all */
@@ -1618,16 +1831,13 @@
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
 	struct isi_board * card = port->card;
-	unsigned long flags;
 	
 	if (isicom_paranoia_check(port, tty->device, "isicom_unthrottle"))
 		return;
 	
 	/* tell the card that this port is ready to accept more data */
-	save_flags(flags); cli();
 	card->port_status |= (1 << port->channel);
 	outw(card->port_status, card->base + 0x02);
-	restore_flags(flags);
 }
 
 /* stop et all */
@@ -1688,16 +1898,20 @@
 {
 	struct isi_port * port = (struct isi_port *) tty->driver_data;
 	unsigned long flags;
+#ifdef	__SMP__
+	struct isi_board *card = port->card;
+#endif
 	
 	if (isicom_paranoia_check(port, tty->device, "isicom_flush_buffer"))
 		return;
 	
-	save_flags(flags); cli();
+	spin_lock_irqsave(&card->card_lock, flags);
+
 	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-	restore_flags(flags);
+
+	spin_unlock_irqrestore(&card->card_lock, flags);
 	
 	wake_up_interruptible(&tty->write_wait);
-	wake_up_interruptible(&tty->poll_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
@@ -1710,14 +1924,23 @@
 	for (count=0; count < BOARD_COUNT; count++ ) {
 		if (isi_card[count].base) {
 			if (check_region(isi_card[count].base,16)) {
-				printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x is busy. Card%d will be disabled.\n",
-					isi_card[count].base,isi_card[count].base+15,count+1);
+				printk(KERN_DEBUG "ISICOM: I/O Region"
+					" 0x%x-0x%x is busy."
+					" Card%d will be disabled.\n",
+					isi_card[count].base,
+					isi_card[count].base+15,
+					count+1);
 				isi_card[count].base=0;
 			}
 			else {
-				request_region(isi_card[count].base,16,ISICOM_NAME);
+				request_region(isi_card[count].base,16,
+						ISICOM_NAME);
 #ifdef ISICOM_DEBUG				
-				printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x requested for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+				printk(KERN_DEBUG "ISICOM: I/O Region"
+					" 0x%x-0x%x requested for Card%d.\n",
+					isi_card[count].base,
+					isi_card[count].base+15,
+					count+1);
 #endif				
 				done++;
 			}
@@ -1733,7 +1956,11 @@
 		if (isi_card[count].base) {
 			release_region(isi_card[count].base,16);
 #ifdef ISICOM_DEBUG			
-			printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x released for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+			printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x"
+				" released for Card%d.\n",
+				isi_card[count].base,
+				isi_card[count].base+15,
+				count+1);
 #endif			
 		}
 }
@@ -1744,55 +1971,57 @@
 
 	/* tty driver structure initialization */
 	memset(&isicom_normal, 0, sizeof(struct tty_driver));
-	isicom_normal.magic	= TTY_DRIVER_MAGIC;
-	isicom_normal.name 	= "ttyM";
-	isicom_normal.major	= ISICOM_NMAJOR;
+	isicom_normal.magic		= TTY_DRIVER_MAGIC;
+	isicom_normal.name 		= "ttyM";
+	isicom_normal.major		= ISICOM_NMAJOR;
 	isicom_normal.minor_start	= 0;
-	isicom_normal.num	= PORT_COUNT;
-	isicom_normal.type	= TTY_DRIVER_TYPE_SERIAL;
-	isicom_normal.subtype	= SERIAL_TYPE_NORMAL;
+	isicom_normal.num		= PORT_COUNT;
+	isicom_normal.type		= TTY_DRIVER_TYPE_SERIAL;
+	isicom_normal.subtype		= SERIAL_TYPE_NORMAL;
 	isicom_normal.init_termios	= tty_std_termios;
 	isicom_normal.init_termios.c_cflag	= 
 				B9600 | CS8 | CREAD | HUPCL |CLOCAL;
-	isicom_normal.flags	= TTY_DRIVER_REAL_RAW;
-	isicom_normal.refcount	= &isicom_refcount;
+	isicom_normal.flags		= TTY_DRIVER_REAL_RAW;
+	isicom_normal.refcount		= &isicom_refcount;
 	
-	isicom_normal.table	= isicom_table;
-	isicom_normal.termios	= isicom_termios;
+	isicom_normal.table		= isicom_table;
+	isicom_normal.termios		= isicom_termios;
 	isicom_normal.termios_locked	= isicom_termios_locked;
 	
-	isicom_normal.open	= isicom_open;
-	isicom_normal.close	= isicom_close;
-	isicom_normal.write	= isicom_write;
-	isicom_normal.put_char	= isicom_put_char;
+	isicom_normal.open		= isicom_open;
+	isicom_normal.close		= isicom_close;
+	isicom_normal.write		= isicom_write;
+	isicom_normal.put_char		= isicom_put_char;
 	isicom_normal.flush_chars	= isicom_flush_chars;
 	isicom_normal.write_room	= isicom_write_room;
 	isicom_normal.chars_in_buffer	= isicom_chars_in_buffer;
-	isicom_normal.ioctl	= isicom_ioctl;
+	isicom_normal.ioctl		= isicom_ioctl;
 	isicom_normal.set_termios	= isicom_set_termios;
-	isicom_normal.throttle	= isicom_throttle;
+	isicom_normal.throttle		= isicom_throttle;
 	isicom_normal.unthrottle	= isicom_unthrottle;
-	isicom_normal.stop	= isicom_stop;
-	isicom_normal.start	= isicom_start;
-	isicom_normal.hangup	= isicom_hangup;
+	isicom_normal.stop		= isicom_stop;
+	isicom_normal.start		= isicom_start;
+	isicom_normal.hangup		= isicom_hangup;
 	isicom_normal.flush_buffer	= isicom_flush_buffer;
 	
 	/*	callout device	*/
 	
-	isicom_callout	= isicom_normal;
-	isicom_callout.name	= "cum"; 
-	isicom_callout.major	= ISICOM_CMAJOR;
-	isicom_callout.subtype	= SERIAL_TYPE_CALLOUT;
+	isicom_callout			= isicom_normal;
+	isicom_callout.name		= "cum"; 
+	isicom_callout.major		= ISICOM_CMAJOR;
+	isicom_callout.subtype		= SERIAL_TYPE_CALLOUT;
 	
 	if ((error=tty_register_driver(&isicom_normal))!=0) {
-		printk(KERN_DEBUG "ISICOM: Couldn't register the dialin driver, error=%d\n",
-			error);
+		printk(KERN_DEBUG "ISICOM: Couldn't register the"
+				" dialin driver, error=%d\n",
+				error);
 		return error;
 	}
 	if ((error=tty_register_driver(&isicom_callout))!=0) {
 		tty_unregister_driver(&isicom_normal);
-		printk(KERN_DEBUG "ISICOM: Couldn't register the callout driver, error=%d\n",
-			error);
+		printk(KERN_DEBUG "ISICOM: Couldn't register the"
+				" callout driver, error=%d\n",
+				error);
 		return error;	
 	}
 	return 0;
@@ -1802,15 +2031,17 @@
 {
 	int error;
 	if ((error=tty_unregister_driver(&isicom_callout))!=0)
-		printk(KERN_DEBUG "ISICOM: couldn't unregister callout driver error=%d.\n",error);
+		printk(KERN_DEBUG "ISICOM: couldn't unregister"
+				" callout driver error=%d.\n",error);
 	if (tty_unregister_driver(&isicom_normal))
-		printk(KERN_DEBUG "ISICOM: couldn't unregister normal driver error=%d.\n",error);
+		printk(KERN_DEBUG "ISICOM: couldn't unregister"
+				" normal driver error=%d.\n",error);
 }
 
+#if 0
 static int register_isr(void)
 {
 	int count, done=0, card;
-	int flag;
 	unsigned char request;
 	for (count=0; count < BOARD_COUNT; count++ ) {
 		if (isi_card[count].base) {
@@ -1820,9 +2051,11 @@
 		 */
 			request = YES;
 			for(card = 0; card < count; card++)
-			if ((isi_card[card].base) && (isi_card[card].irq == isi_card[count].irq)) {
+			if ((isi_card[card].base) && 
+				(isi_card[card].irq == isi_card[count].irq)) {
 				request = NO;
-				if ((isi_card[count].isa == NO) && (isi_card[card].isa == NO))
+				if ((isi_card[count].isa == NO) 
+					&& (isi_card[card].isa == NO))
 					break;
 				/*
 				 * ISA cards cannot share interrupts with other
@@ -1832,22 +2065,24 @@
 				isi_card[count].base = 0;
 				break;
 			}
-			flag=0;
-			if(isi_card[count].isa == NO)
-				flag |= SA_SHIRQ;
-				
 			if (request == YES) { 
-				if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT|flag, ISICOM_NAME, NULL)) {
-					printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n",
+				if (request_irq(isi_card[count].irq, 
+						isicom_interrupt, 
+						SA_INTERRUPT | SA_SHIRQ, 
+						ISICOM_NAME, NULL)) {
+
+					printk(KERN_WARNING "ISICOM: Could not"
+						" install handler at Irq %d."
+						" Card%d will be disabled.\n",
 						isi_card[count].irq, count+1);
+
 					release_region(isi_card[count].base,16);
 					isi_card[count].base=0;
 				}
 				else {
-					printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n", 
-					count+1, isi_card[count].base, isi_card[count].irq); 
 					
-					irq_to_board[isi_card[count].irq]=&isi_card[count];
+/*					irq_to_board[isi_card[count].irq]=
+								&isi_card[count];*/
 					done++;
 				}
 			}
@@ -1855,7 +2090,41 @@
 	}
 	return done;
 }
+#endif
+
+static int register_isr(void)
+{
+	int count, done = 0;
+	unsigned long irqflags;
+
+	for (count=0; count < BOARD_COUNT; count++ ) {
+		if (isi_card[count].base) {
+
+			irqflags = (isi_card[count].isa == YES) ? 
+					SA_INTERRUPT : 
+					(SA_INTERRUPT | SA_SHIRQ);
+
+			if (request_irq(isi_card[count].irq, 
+					isicom_interrupt, 
+					irqflags, 
+					ISICOM_NAME, &isi_card[count])) {
+
+				printk(KERN_WARNING "ISICOM: Could not"
+					" install handler at Irq %d."
+					" Card%d will be disabled.\n",
+					isi_card[count].irq, count+1);
 
+				release_region(isi_card[count].base,16);
+				isi_card[count].base=0;
+			}
+			else
+				done++;
+		}
+	}
+	return done;
+}
+
+#if 0
 static void unregister_isr(void)
 {
 	int count, card;
@@ -1864,19 +2133,42 @@
 		if (isi_card[count].base) {
 			freeirq = YES;
 			for(card = 0; card < count; card++)
-				if ((isi_card[card].base) && (isi_card[card].irq == isi_card[count].irq)) {
+				if ((isi_card[card].base) && 
+					(isi_card[card].irq == 
+						isi_card[count].irq)) {
 					freeirq = NO;
 					break;
 				}
 			if (freeirq == YES) {
 				free_irq(isi_card[count].irq, NULL);
 #ifdef ISICOM_DEBUG			
-				printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1);
+				printk(KERN_DEBUG "ISICOM: Irq %d released"
+					" for Card%d.\n",
+					isi_card[count].irq, 
+					count+1);
 #endif	
 			}		
 		}
 	}
 }
+#endif
+
+static void unregister_isr(void)
+{
+	int count;
+
+	for (count=0; count < BOARD_COUNT; count++ ) {
+		if (isi_card[count].base) {
+			free_irq(isi_card[count].irq, &isi_card[count]);
+#ifdef ISICOM_DEBUG			
+			printk(KERN_DEBUG "ISICOM: Irq %d released"
+				" for Card%d.\n",
+				isi_card[count].irq, 
+				count+1);
+#endif	
+		}
+	}
+}
 
 static int isicom_init(void)
 {
@@ -1888,7 +2180,8 @@
 		page = get_free_page(GFP_KERNEL);
 	      	if (!page) {
 #ifdef ISICOM_DEBUG	      	
-	      		printk(KERN_DEBUG "ISICOM: Couldn't allocate page for tmp_buf.\n");
+	      		printk(KERN_DEBUG "ISICOM: Couldn't allocate page"
+				" for tmp_buf.\n");
 #else
 			printk(KERN_ERR "ISICOM: Not enough memory...\n");
 #endif	      
@@ -1909,6 +2202,7 @@
 		free_page((unsigned long)tmp_buf);
 		return 0;
 	}
+
 	if (!register_isr()) 
 	{
 		unregister_drivers();
@@ -1916,7 +2210,7 @@
 		free_page((unsigned long)tmp_buf);
 		return 0;		
 	}
-	
+
 	/* initialize bottom half  */
 	init_bh(ISICOM_BH, do_isicom_bh);
 
@@ -1925,6 +2219,9 @@
 	for (card = 0; card < BOARD_COUNT; card++) {
 		port = &isi_ports[card * 16];
 		isi_card[card].ports = port;
+		/* 11/5/00 -sameer SMPCHG*/
+		isi_card[card].card_lock = SPIN_LOCK_UNLOCKED;
+
 		base = isi_card[card].base;
 		for (channel = 0; channel < 16; channel++, port++) {
 			port->magic = ISICOM_MAGIC;
@@ -1967,54 +2264,45 @@
 	int retval, card, idx, count;
 	unsigned char pciirq;
 	unsigned int ioaddr;
-	                
+
+	for (idx = 0; idx < BOARD_COUNT; idx++) {
+		isi_card[idx].base = 0;
+		isi_card[idx].irq = 0;
+	}
+
 	card = 0;
 	for(idx=0; idx < BOARD_COUNT; idx++) {	
-		if (io[idx]) {
-			isi_card[idx].base=io[idx];
-			isi_card[idx].irq=irq[idx];
-			isi_card[idx].isa=YES;
+		if (io[idx] && reset_card(io[idx], 0) && valid_irq(irq[idx])) {
+			isi_card[card].base=io[idx];
+			isi_card[card].irq=irq[idx];
+			isi_card[card].isa=YES;
 			card++;
 		}
-		else {
-			isi_card[idx].base = 0;
-			isi_card[idx].irq = 0;
-		}
 	}
 	
-	for (idx=0 ;idx < card; idx++) {
-		if (!((isi_card[idx].irq==2)||(isi_card[idx].irq==3)||
-		    (isi_card[idx].irq==4)||(isi_card[idx].irq==5)||
-		    (isi_card[idx].irq==7)||(isi_card[idx].irq==10)||
-		    (isi_card[idx].irq==11)||(isi_card[idx].irq==12)||
-		    (isi_card[idx].irq==15))) {
-			
-			if (isi_card[idx].base) {
-				printk(KERN_ERR "ISICOM: Irq %d unsupported. Disabling Card%d...\n",
-					isi_card[idx].irq, idx+1);
-				isi_card[idx].base=0;
-				card--;
-			}	
-		}
-	}	
-	
 	if (pci_present() && (card < BOARD_COUNT)) {
 		for (idx=0; idx < DEVID_COUNT; idx++) {
 			dev = NULL;
 			for (;;){
-				if (!(dev = pci_find_device(VENDOR_ID, device_id[idx], dev)))
+				if (!(dev = pci_find_device(VENDOR_ID, 
+							device_id[idx], dev)))
 					break;
 				if (card >= BOARD_COUNT)
 					break;
 					
 				/* found a PCI ISI card! */
-				ioaddr = dev->base_address[3]; /* i.e at offset 0x1c in the
-								* PCI configuration register
-								* space.
-								*/
+
+				/*
+				 *	16 bytes address space base at 
+				 *	offset 0x1c in the PCI configuration 
+				 *	register space
+				 */
+				ioaddr = dev->base_address[3]; 
 				ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
 				pciirq = dev->irq;
-				printk(KERN_INFO "ISI PCI Card(Device ID 0x%x)\n", device_id[idx]);
+				printk(KERN_INFO "ISI PCI "
+					"Card(Device ID 0x%x)\n", 
+					device_id[idx]);
 				/*
 				 * allot the first empty slot in the array
 				 */				
@@ -2032,29 +2320,34 @@
 		}
 	}
 	
-	if (!(isi_card[0].base || isi_card[1].base || isi_card[2].base || isi_card[3].base)) {
-		printk(KERN_ERR "ISICOM: No valid card configuration. Driver cannot be initialized...\n"); 
+	if (!(isi_card[0].base || isi_card[1].base ||
+		isi_card[2].base || isi_card[3].base)) {
+
+		printk(KERN_ERR "ISICOM: No valid card configuration."
+				" Driver cannot be initialized...\n"); 
 		return -EIO;
 	}		
 	retval=misc_register(&isiloader_device);
 	if (retval<0) {
-		printk(KERN_ERR "ISICOM: Unable to register firmware loader driver.\n");
+		printk(KERN_ERR "ISICOM: Unable to register"
+				" firmware loader driver.\n");
 		return -EIO;
 	}
 	
 	if (!isicom_init()) {
 		if (misc_deregister(&isiloader_device)) 
-			printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+			printk(KERN_ERR "ISICOM: Unable to unregister"
+					" Firmware Loader driver\n");
 		return -EIO;
 	}
-	
+
 	init_timer(&tx);
 	tx.expires = jiffies + 1;
 	tx.data = 0;
 	tx.function = isicom_tx;
 	re_schedule = 1;
 	add_timer(&tx);
-	
+
 	return 0;
 }
 
@@ -2067,10 +2360,11 @@
 	
 #ifdef ISICOM_DEBUG	
 	printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count);
+	printk("ISICOM: isicom_interrupt intr_count = %ld.\n", intr_count);
 #endif	
 
 #ifdef ISICOM_DEBUG
-	printk("ISICOM: uregistering isr ...\n");
+	printk("ISICOM: unregistering isr ...\n");
 #endif	
 	unregister_isr();
 
@@ -2093,5 +2387,6 @@
 	printk("ISICOM: unregistering firmware loader ...\n");	
 #endif
 	if (misc_deregister(&isiloader_device))
-		printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+		printk(KERN_ERR "ISICOM: Unable to unregister"
+				" Firmware Loader driver\n");
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/keyboard.c linux.18p17/drivers/char/keyboard.c
--- linux.vanilla/drivers/char/keyboard.c	Fri Apr 21 12:46:04 2000
+++ linux.18p17/drivers/char/keyboard.c	Fri Sep  1 13:51:29 2000
@@ -61,7 +61,9 @@
 #define KBD_DEFLOCK 0
 #endif
 
+void (*kbd_ledfunc)(unsigned int led) = NULL;
 EXPORT_SYMBOL(handle_scancode);
+EXPORT_SYMBOL(kbd_ledfunc);
 
 extern void ctrl_alt_del(void);
 
@@ -905,6 +907,7 @@
 	if (leds != ledstate) {
 		ledstate = leds;
 		kbd_leds(leds);
+		if (kbd_ledfunc) kbd_ledfunc(leds);
 	}
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/lp_mfc.c linux.18p17/drivers/char/lp_mfc.c
--- linux.vanilla/drivers/char/lp_mfc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/lp_mfc.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/m68kserial.c linux.18p17/drivers/char/m68kserial.c
--- linux.vanilla/drivers/char/m68kserial.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/m68kserial.c	Sat Oct 14 00:05:37 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/mac_SCC.c linux.18p17/drivers/char/mac_SCC.c
--- linux.vanilla/drivers/char/mac_SCC.c	Fri Apr 21 12:46:04 2000
+++ linux.18p17/drivers/char/mac_SCC.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/mem.c linux.18p17/drivers/char/mem.c
--- linux.vanilla/drivers/char/mem.c	Fri Apr 21 12:46:04 2000
+++ linux.18p17/drivers/char/mem.c	Fri Sep  1 13:51:29 2000
@@ -55,14 +55,8 @@
 #if defined(CONFIG_PPC) || defined(CONFIG_MAC)
 extern void adbdev_init(void);
 #endif
-#ifdef CONFIG_USB_UHCI
-int uhci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI
-int ohci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-int ohci_hcd_init(void);
+#ifdef CONFIG_USB
+int usb_init(void);
 #endif
      
 static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
@@ -621,15 +615,7 @@
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 	rand_initialize();
 #ifdef CONFIG_USB
-#ifdef CONFIG_USB_UHCI
-	uhci_init();
-#endif
-#ifdef CONFIG_USB_OHCI
-	ohci_init();
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-        ohci_hcd_init(); 
-#endif
+	usb_init();
 #endif
 #if defined (CONFIG_FB)
 	fbmem_init();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/misc.c linux.18p17/drivers/char/misc.c
--- linux.vanilla/drivers/char/misc.c	Sat Sep  9 18:42:35 2000
+++ linux.18p17/drivers/char/misc.c	Mon Sep 11 17:33:47 2000
@@ -43,9 +43,6 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/init.h>
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-#endif
 
 #include <linux/tty.h>
 #include <linux/selection.h>
@@ -87,6 +84,7 @@
 extern int pc110pad_init(void);
 extern int pmu_device_init(void);
 extern int tosh_init(void);
+extern int rng_init(void);
 
 static int misc_read_proc(char *buf, char **start, off_t offset,
 			  int len, int *eof, void *private)
@@ -241,9 +239,6 @@
 #ifdef CONFIG_DTLK
 	dtlk_init();
 #endif
-#ifdef CONFIG_APM
-	apm_init();
-#endif
 #ifdef CONFIG_H8
 	h8_init();
 #endif
@@ -279,6 +274,9 @@
 #endif
 #ifdef CONFIG_SGI
 	streamable_init ();
+#endif
+#ifdef CONFIG_INTEL_RNG
+	rng_init ();
 #endif
 #ifdef CONFIG_TOSHIBA
 	tosh_init();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/n_hdlc.c linux.18p17/drivers/char/n_hdlc.c
--- linux.vanilla/drivers/char/n_hdlc.c	Sun Jun 11 21:44:13 2000
+++ linux.18p17/drivers/char/n_hdlc.c	Fri Sep  1 22:03:12 2000
@@ -118,13 +118,6 @@
 #include <linux/kerneld.h>
 #endif
 
-#if LINUX_VERSION_CODE < VERSION(2,3,0) 
-typedef struct wait_queue *wait_queue_head_t;
-#define DECLARE_WAITQUEUE(name,task) struct wait_queue (name) = {(task),NULL}
-#define init_waitqueue_head(head) *(head) = NULL
-#define set_current_state(a) current->state = (a)
-#endif
-
 #if LINUX_VERSION_CODE >= VERSION(2,1,4)
 #include <asm/segment.h>
 #define GET_USER(error,value,addr) error = get_user(value,addr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/pc_keyb.c linux.18p17/drivers/char/pc_keyb.c
--- linux.vanilla/drivers/char/pc_keyb.c	Sat Sep  9 18:42:35 2000
+++ linux.18p17/drivers/char/pc_keyb.c	Fri Sep 29 10:31:24 2000
@@ -58,6 +58,7 @@
 static void kbd_write_output_w(int data);
 #ifdef CONFIG_PSMOUSE
 static void aux_write_ack(int val);
+static int aux_reconnect = 0;
 #endif
 
 spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
@@ -399,18 +400,20 @@
 		}
 		mouse_reply_expected = 0;
 	}
-    else if(scancode == AUX_RECONNECT){
-        queue->head = queue->tail = 0;  /* Flush input queue */
-        /* ping the mouse :) */
-	kb_wait();
-	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
-	kb_wait();
-	kbd_write_output(AUX_ENABLE_DEV);
-	/* we expect an ACK in response. */
-	mouse_reply_expected++;
-	kb_wait();
-        return;
-    }
+	
+	else if(scancode == AUX_RECONNECT && aux_reconnect)
+	{
+	        queue->head = queue->tail = 0;  /* Flush input queue */
+        	/* ping the mouse :) */
+		kb_wait();
+		kbd_write_command(KBD_CCMD_WRITE_MOUSE);
+		kb_wait();
+		kbd_write_output(AUX_ENABLE_DEV);
+		/* we expect an ACK in response. */
+		mouse_reply_expected++;
+		kb_wait();
+	        return;
+	}
 
 	add_mouse_randomness(scancode);
 	if (aux_count) {
@@ -428,6 +431,8 @@
 #endif
 }
 
+static unsigned char kbd_exists = 1;
+
 /*
  * This reads the keyboard status port, and does the
  * appropriate action.
@@ -444,17 +449,21 @@
 		unsigned char scancode;
 
 		scancode = kbd_read_input();
-#  ifdef CHECK_RECONNECT_SCANCODE
-    printk(KERN_INFO "-=db=-: kbd_read_input() : scancode == %d\n",scancode);
-#  endif
-		if (status & KBD_STAT_MOUSE_OBF) {
-			handle_mouse_event(scancode);
-		} else {
-			if (do_acknowledge(scancode))
-				handle_scancode(scancode, !(scancode & 0x80));
-			mark_bh(KEYBOARD_BH);
+		
+		/* Check for errors. Shouldnt ever happen but it does on Compaq
+		   Presario 16[89]. */
+		   
+		if(!(status&(KBD_STAT_GTO|KBD_STAT_PERR)))
+		{
+			if (status & KBD_STAT_MOUSE_OBF) {
+				handle_mouse_event(scancode);
+			} else {
+				kbd_exists = 1;
+				if (do_acknowledge(scancode))
+					handle_scancode(scancode, !(scancode & 0x80));
+				mark_bh(KEYBOARD_BH);
+			}
 		}
-
 		status = kbd_read_status();
 		
 		if(!work--)
@@ -506,7 +515,7 @@
 			mdelay(1);
 			if (!--timeout) {
 #ifdef KBD_REPORT_TIMEOUTS
-				printk(KERN_WARNING "Keyboard timeout[2]\n");
+				printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?\n");
 #endif
 				return 0;
 			}
@@ -520,8 +529,10 @@
 
 void pckbd_leds(unsigned char leds)
 {
-	if (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))
-	    send_data(KBD_CMD_ENABLE);	/* re-enable kbd if any errors */
+	if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) {
+		send_data(KBD_CMD_ENABLE);	/* re-enable kbd if any errors */
+		kbd_exists = 0;
+	}
 }
 
 /*
@@ -545,6 +556,7 @@
 	kbd_startup_reset = 1;
 }
 
+
 #define KBD_NO_DATA	(-1)	/* No data */
 #define KBD_BAD_DATA	(-2)	/* Parity or other error */
 
@@ -742,6 +754,11 @@
 }
 
 #if defined CONFIG_PSMOUSE
+
+void __init aux_reconnect_setup(char *str, int *ints)
+{
+	aux_reconnect=1;
+}
 
 /*
  * Check if this is a dual port controller.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/pcxx.c linux.18p17/drivers/char/pcxx.c
--- linux.vanilla/drivers/char/pcxx.c	Fri Apr 21 12:46:04 2000
+++ linux.18p17/drivers/char/pcxx.c	Sun Oct  1 11:19:19 2000
@@ -1781,10 +1781,9 @@
 /*
  * pcxxdelay - delays a specified number of milliseconds
  */
-static void pcxxdelay(int msec)
+static void pcxxdelay(int mseconds)
 {
-	while(msec-- > 0)
-		__delay(loops_per_sec/1000);
+	mdelay(mseconds);
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/q40_keyb.c linux.18p17/drivers/char/q40_keyb.c
--- linux.vanilla/drivers/char/q40_keyb.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/q40_keyb.c	Fri Oct 13 23:59:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/qpmouse.c linux.18p17/drivers/char/qpmouse.c
--- linux.vanilla/drivers/char/qpmouse.c	Fri Apr 21 12:46:05 2000
+++ linux.18p17/drivers/char/qpmouse.c	Thu Aug 31 15:16:11 2000
@@ -349,9 +349,14 @@
 
 	printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n");
 /*	printk("82C710 address = %x (should be 0x310)\n", qp_data); */
+	queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+	if(queue==NULL)
+	{
+		printk(KERN_ERR "qpmouse: no queue memory.\n");
+		return -ENOMEM;
+	}	
 	qp_present = 1;
 	misc_register(&qp_mouse);
-	queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 	memset(queue, 0, sizeof(*queue));
 	queue->head = queue->tail = 0;
 	queue->proc_list = NULL;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/radio-cadet.c linux.18p17/drivers/char/radio-cadet.c
--- linux.vanilla/drivers/char/radio-cadet.c	Fri Apr 21 12:46:05 2000
+++ linux.18p17/drivers/char/radio-cadet.c	Mon Sep 25 15:57:25 2000
@@ -548,7 +548,7 @@
 {
 #ifndef MODULE        
         if(cadet_probe()<0) {
-	        return EINVAL;
+	        return -EINVAL;
 	}
 #endif
 	if(video_register_device(&cadet_radio,VFL_TYPE_RADIO)==-1)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/radio-maestro.c linux.18p17/drivers/char/radio-maestro.c
--- linux.vanilla/drivers/char/radio-maestro.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/radio-maestro.c	Fri Sep 29 10:08:06 2000
@@ -0,0 +1,378 @@
+/* Maestro PCI sound card radio driver for Linux support
+ * (c) 2000 A. Tlalka, atlka@pg.gda.pl
+ * Notes on the hardware
+ *
+ *  + Frequency control is done digitally 
+ *  + No volume control - only mute/unmute - you have to use Aux line volume
+ *  control on Maestro card to set the volume
+ *  + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
+ *  frequency setting (>100ms) and only when the radio is unmuted.
+ *  version 0.02
+ *  + io port is automatically detected - only the first radio is used
+ *  version 0.03
+ *  + thread access locking additions
+ *  version 0.04
+ * + code improvements
+ * + VIDEO_TUNER_LOW is permanent
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/pci.h>
+#include <linux/videodev.h>
+
+#define DRIVER_VERSION	"0.04"
+
+#define PCI_VENDOR_ESS                  0x125D
+#define PCI_DEVICE_ID_ESS_ESS1968       0x1968          /* Maestro 2    */
+#define PCI_DEVICE_ID_ESS_ESS1978       0x1978          /* Maestro 2E   */
+
+#define GPIO_DATA       0x60   /* port offset from ESS_IO_BASE */
+
+#define IO_MASK		4      /* mask      register offset from GPIO_DATA
+				bits 1=unmask write to given bit */
+#define IO_DIR		8      /* direction register offset from GPIO_DATA
+				bits 0/1=read/write direction */
+
+#define GPIO6           0x0040 /* mask bits for GPIO lines */
+#define GPIO7           0x0080
+#define GPIO8           0x0100
+#define GPIO9           0x0200
+
+#define STR_DATA        GPIO6  /* radio TEA5757 pins and GPIO bits */
+#define STR_CLK         GPIO7
+#define STR_WREN        GPIO8
+#define STR_MOST        GPIO9
+
+#define FREQ_LO		 50*16000
+#define FREQ_HI		150*16000
+
+#define FREQ_IF         171200 /* 10.7*16000   */
+#define FREQ_STEP       200    /* 12.5*16      */
+
+#define FREQ2BITS(x)	((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
+			/(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
+
+#define BITS2FREQ(x)	((x) * FREQ_STEP - FREQ_IF)
+
+
+
+static int radio_open(struct video_device *, int);
+static int radio_ioctl(struct video_device *, unsigned int, void *);
+static void radio_close(struct video_device *);
+
+static struct video_device maestro_radio=
+{
+	"Maestro radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_SF16MI,
+	radio_open,
+	radio_close,
+	NULL,
+	NULL,
+	NULL,
+	radio_ioctl,
+	NULL,
+	NULL
+};
+
+static struct radio_device
+{
+	__u16	io,	/* base of Maestro card radio io (GPIO_DATA)*/
+		muted,	/* VIDEO_AUDIO_MUTE */
+		stereo,	/* VIDEO_TUNER_STEREO_ON */	
+		tuned;	/* signal strength (0 or 0xffff) */
+	struct  semaphore lock;
+} radio_unit = {0, 0, 0, 0, MUTEX};
+
+static int users = 0;
+
+static void sleep_125ms(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+			 schedule_timeout(HZ >> 3);
+}
+
+static void udelay4(void)
+{
+	udelay(4);
+}
+
+static void udelay16(void)
+{
+	udelay(16);
+}
+
+static __u32 radio_bits_get(struct radio_device *dev)
+{
+	register __u16 io=dev->io, l, rdata;
+	register __u32 data=0;
+	__u16 omask;
+	omask = inw(io + IO_MASK);
+	outw(~(STR_CLK | STR_WREN), io + IO_MASK);
+	outw(0, io);
+	udelay16();
+	for (l=24;l--;) {
+		outw(STR_CLK, io);		/* HI state */
+		udelay4();
+		if(!l) 
+			dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
+		outw(0, io);			/* LO state */
+		udelay4();
+		data <<= 1;			/* shift data */
+		rdata = inw(io);
+		if(!l)
+			dev->stereo =  rdata & STR_MOST ? 
+			0 : VIDEO_TUNER_STEREO_ON;
+		else
+			if(rdata & STR_DATA)
+				data++;
+		udelay4();
+	}
+	if(dev->muted)
+		outw(STR_WREN, io);
+	udelay4();
+	outw(omask, io + IO_MASK);
+	return data & 0x3ffe;
+}
+
+static void radio_bits_set(struct radio_device *dev, __u32 data)
+{
+	register __u16 io=dev->io, l, bits;
+	__u16 omask, odir;
+	omask = inw(io + IO_MASK);
+	odir  = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+	outw(odir | STR_DATA, io + IO_DIR);
+	outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+	udelay16();
+	for (l=25;l;l--) {
+		bits = ((data >> 18) & STR_DATA) | STR_WREN ;
+		data <<= 1;			/* shift data */
+		outw(bits, io);			/* start strobe */
+		udelay4();
+		outw(bits | STR_CLK, io);	/* HI level */
+		udelay4();   
+		outw(bits, io);			/* LO level */
+		udelay4();
+	}
+	if(!dev->muted)
+		outw(0, io);
+	udelay4();
+	outw(omask, io + IO_MASK);
+	outw(odir, io + IO_DIR);
+	sleep_125ms();
+}
+
+inline static int radio_function(struct video_device *dev, 
+				 unsigned int cmd, void *arg)
+{
+	struct radio_device *card=dev->priv;
+	switch(cmd) {
+		case VIDIOCGCAP: {
+			struct video_capability v;
+			strcpy(v.name, "Maestro radio");
+			v.type=VID_TYPE_TUNER;
+			v.channels=v.audios=1;
+			v.maxwidth=v.maxheight=v.minwidth=v.minheight=0;
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER: {
+			struct video_tuner v;
+			if(copy_from_user(&v, arg,sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)
+				return -EINVAL;
+			(void)radio_bits_get(card);
+			v.flags = VIDEO_TUNER_LOW | card->stereo;
+			v.signal = card->tuned;
+			strcpy(v.name, "FM");
+			v.rangelow = FREQ_LO;
+			v.rangehigh = FREQ_HI;
+			v.mode = VIDEO_MODE_AUTO;
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+		        return 0;
+		}
+		case VIDIOCSTUNER: {
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGFREQ: {
+			unsigned long tmp=BITS2FREQ(radio_bits_get(card));
+			if(copy_to_user(arg, &tmp, sizeof(tmp)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSFREQ: {
+			unsigned long tmp;
+			if(copy_from_user(&tmp, arg, sizeof(tmp)))
+				return -EFAULT;
+			if ( tmp<FREQ_LO || tmp>FREQ_HI )
+				return -EINVAL;
+			radio_bits_set(card, FREQ2BITS(tmp));
+			return 0;
+		}
+		case VIDIOCGAUDIO: {	
+			struct video_audio v;
+			strcpy(v.name, "Radio");
+			v.audio=v.volume=v.bass=v.treble=v.balance=v.step=0;
+			v.flags=VIDEO_AUDIO_MUTABLE | card->muted;
+			v.mode=VIDEO_SOUND_STEREO;
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;		
+		}
+		case VIDIOCSAUDIO: {
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.audio)
+				return -EINVAL;
+			{
+				register __u16 io=card->io;
+				register __u16 omask = inw(io + IO_MASK);
+				outw(~STR_WREN, io + IO_MASK);
+				outw((card->muted = v.flags & VIDEO_AUDIO_MUTE)
+				     ? STR_WREN : 0, io);
+				udelay4();
+				outw(omask, io + IO_MASK);
+				sleep_125ms();
+				return 0;
+			}
+		}
+		case VIDIOCGUNIT: {
+			struct video_unit v;
+			v.video=VIDEO_NO_UNIT;
+			v.vbi=VIDEO_NO_UNIT;
+			v.radio=dev->minor;
+			v.audio=0;
+			v.teletext=VIDEO_NO_UNIT;
+			if(copy_to_user(arg, &v, sizeof(v)))
+				return -EFAULT;
+			return 0;		
+		}
+		default: return -ENOIOCTLCMD;
+	}
+}
+
+static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct radio_device *card=dev->priv;
+	int ret;
+	down(&card->lock);
+	ret = radio_function(dev, cmd, arg);
+	up(&card->lock);
+	return ret;
+}
+
+static int radio_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void radio_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+
+inline static __u16 radio_install(struct pci_dev *pcidev);
+
+#ifdef MODULE
+MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
+MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
+
+EXPORT_NO_SYMBOLS;
+
+void cleanup_module(void)
+{
+	video_unregister_device(&maestro_radio);
+}
+
+int init_module(void)
+#else
+__initfunc(int maestro_radio_init(struct video_init *v))
+#endif
+{
+	register __u16 found=0;
+	struct pci_dev *pcidev = NULL;
+	if(!pci_present())
+		return -ENODEV;
+	while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS, 
+						  PCI_DEVICE_ID_ESS_ESS1968,
+						  pcidev)))
+		found |= radio_install(pcidev);
+	while(!found && (pcidev = pci_find_device(PCI_VENDOR_ESS,
+						  PCI_DEVICE_ID_ESS_ESS1978, 
+						  pcidev)))
+		found |= radio_install(pcidev);
+	if(!found) {
+		printk(KERN_INFO "radio-maestro: no devices found.\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+inline static __u16 radio_power_on(struct radio_device *dev)
+{
+	register __u16 io=dev->io;
+	register __u32 ofreq;
+	__u16 omask, odir;
+	omask = inw(io + IO_MASK);
+	odir  = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+	outw(odir & ~STR_WREN, io + IO_DIR);
+	dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE;
+	outw(odir, io + IO_DIR);
+	outw(~(STR_WREN | STR_CLK), io + IO_MASK);
+	outw(dev->muted ? 0 : STR_WREN, io);
+	udelay16();
+	outw(omask, io + IO_MASK);
+	ofreq = radio_bits_get(dev);
+	if((ofreq<FREQ2BITS(FREQ_LO)) || (ofreq>FREQ2BITS(FREQ_HI)))
+		ofreq = FREQ2BITS(FREQ_LO);
+	radio_bits_set(dev, ofreq);
+	return (ofreq == radio_bits_get(dev));
+}
+
+inline static __u16 radio_install(struct pci_dev *pcidev)
+{
+	if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+		return 0;
+	
+	radio_unit.io = (pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)
+		+ GPIO_DATA;
+	maestro_radio.priv = &radio_unit;
+	if(radio_power_on(&radio_unit)) {
+		if(video_register_device(&maestro_radio, VFL_TYPE_RADIO)==-1) {
+			printk("radio-maestro: can't register device!");
+			return 0;
+		}
+		printk(KERN_INFO "radio-maestro: version "
+		       DRIVER_VERSION 
+		       " time " 
+		       __TIME__ "  "
+		       __DATE__
+		       "\n");
+		printk(KERN_INFO "radio-maestro: radio chip initialized\n");
+		return 1;
+	} else
+		return 0;   
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rtc.c linux.18p17/drivers/char/rtc.c
--- linux.vanilla/drivers/char/rtc.c	Fri Apr 21 12:46:06 2000
+++ linux.18p17/drivers/char/rtc.c	Tue Sep 12 13:14:58 2000
@@ -113,10 +113,15 @@
 unsigned char days_in_mo[] = 
 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
+extern spinlock_t rtc_lock;
+
 /*
  *	A very tiny interrupt handler. It runs with SA_INTERRUPT set,
- *	so that there is no possibility of conflicting with the
- *	set_rtc_mmss() call that happens during some timer interrupts.
+ *	but there is possibility of conflicting with the set_rtc_mmss()
+ *	call (the rtc irq and the timer irq can easily run at the same
+ *	time in two different CPUs). So we need to serializes
+ *	accesses to the chip with the rtc_lock spinlock that each
+ *	architecture should implement in the timer code.
  *	(See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
  */
 
@@ -131,7 +136,10 @@
 
 	rtc_irq_data += 0x100;
 	rtc_irq_data &= ~0xff;
+	/* runs with irq locally disabled (see SA_INTERRUPT flag). */
+	spin_lock(&rtc_lock);
 	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
+	spin_unlock(&rtc_lock);
 	wake_up_interruptible(&rtc_wait);	
 
 	if (rtc_status & RTC_TIMER_ON)
@@ -276,8 +284,7 @@
 		if (sec >= 60)
 			sec = 0xff;
 
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&rtc_lock, flags);
 		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
 		    RTC_ALWAYS_BCD)
 		{
@@ -288,7 +295,7 @@
 		CMOS_WRITE(hrs, RTC_HOURS_ALARM);
 		CMOS_WRITE(min, RTC_MINUTES_ALARM);
 		CMOS_WRITE(sec, RTC_SECONDS_ALARM);
-		restore_flags(flags);
+		spin_unlock_irqrestore(&rtc_lock, flags);
 
 		return 0;
 	}
@@ -336,12 +343,11 @@
 		if ((yrs -= epoch) > 255)    /* They are unsigned */
 			return -EINVAL;
 
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&rtc_lock, flags);
 		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
 		    || RTC_ALWAYS_BCD) {
 			if (yrs > 169) {
-				restore_flags(flags);
+				spin_unlock_irqrestore(&rtc_lock, flags);
 				return -EINVAL;
 			}
 			if (yrs >= 100)
@@ -370,7 +376,7 @@
 		CMOS_WRITE(save_control, RTC_CONTROL);
 		CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
-		restore_flags(flags);
+		spin_unlock_irqrestore(&rtc_lock, flags);
 		return 0;
 	}
 	case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
@@ -405,12 +411,11 @@
 
 		rtc_freq = arg;
 
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&rtc_lock, flags);
 		val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
 		val |= (16 - tmp);
 		CMOS_WRITE(val, RTC_FREQ_SELECT);
-		restore_flags(flags);
+		spin_unlock_irqrestore(&rtc_lock, flags);
 		return 0;
 	}
 #ifdef __alpha__
@@ -465,15 +470,14 @@
 	unsigned char tmp;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	tmp = CMOS_READ(RTC_CONTROL);
 	tmp &=  ~RTC_PIE;
 	tmp &=  ~RTC_AIE;
 	tmp &=  ~RTC_UIE;
 	CMOS_WRITE(tmp, RTC_CONTROL);
 	CMOS_READ(RTC_INTR_FLAGS);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	if (rtc_status & RTC_TIMER_ON) {
 		rtc_status &= ~RTC_TIMER_ON;
@@ -546,11 +550,10 @@
 		while (jiffies - uip_watchdog < 2*HZ/100)
 			barrier();
 	
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	year = CMOS_READ(RTC_YEAR);
 	ctrl = CMOS_READ(RTC_CONTROL);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 	
 	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 		BCD_TO_BIN(year);       /* This should never happen... */
@@ -568,11 +571,10 @@
 	init_timer(&rtc_irq_timer);
 	rtc_irq_timer.function = rtc_dropped_irq;
 	rtc_wait = NULL;
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
 	CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 	rtc_freq = 1024;
 	return 0;
 }
@@ -596,12 +598,11 @@
 	printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq);
 	mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	rtc_irq_data += ((rtc_freq/HZ)<<8);
 	rtc_irq_data &= ~0xff;
 	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 }
 
 /*
@@ -615,11 +616,10 @@
 	unsigned char batt, ctrl;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	batt = CMOS_READ(RTC_VALID) & RTC_VRT;
 	ctrl = CMOS_READ(RTC_CONTROL);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	p = buf;
 
@@ -690,10 +690,9 @@
 	unsigned long flags;
 	unsigned char uip;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 	return uip;
 }
 
@@ -723,8 +722,7 @@
 	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
 	 * by the RTC when initially set to a non-zero value.
 	 */
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
 	rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
 	rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
@@ -732,7 +730,7 @@
 	rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
 	rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
 	ctrl = CMOS_READ(RTC_CONTROL);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 	{
@@ -763,13 +761,12 @@
 	 * Only the values that we read from the RTC are set. That
 	 * means only tm_hour, tm_min, and tm_sec.
 	 */
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
 	alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
 	alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
 	ctrl = CMOS_READ(RTC_CONTROL);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 	{
@@ -794,13 +791,12 @@
 	unsigned char val;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	val = CMOS_READ(RTC_CONTROL);
 	val &=  ~bit;
 	CMOS_WRITE(val, RTC_CONTROL);
 	CMOS_READ(RTC_INTR_FLAGS);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 	rtc_irq_data = 0;
 }
 
@@ -809,12 +805,11 @@
 	unsigned char val;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&rtc_lock, flags);
 	val = CMOS_READ(RTC_CONTROL);
 	val |= bit;
 	CMOS_WRITE(val, RTC_CONTROL);
 	CMOS_READ(RTC_INTR_FLAGS);
 	rtc_irq_data = 0;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/saa7111.c linux.18p17/drivers/char/saa7111.c
--- linux.vanilla/drivers/char/saa7111.c	Fri Apr 21 12:46:06 2000
+++ linux.18p17/drivers/char/saa7111.c	Mon Sep 11 17:49:08 2000
@@ -85,7 +85,7 @@
 
 static int saa7111_write_block(struct saa7111 *dev, unsigned const char *data, unsigned int len)
 {
-	int ack;
+	int ack = 0;
 	unsigned subaddr;
 	unsigned long flags;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/saa7185.c linux.18p17/drivers/char/saa7185.c
--- linux.vanilla/drivers/char/saa7185.c	Fri Apr 21 12:46:06 2000
+++ linux.18p17/drivers/char/saa7185.c	Mon Sep 11 17:49:08 2000
@@ -85,7 +85,7 @@
 
 static int saa7185_write_block(struct saa7185 *dev, unsigned const char *data, unsigned int len)
 {
-	int ack;
+	int ack=0;
 	unsigned subaddr;
 	unsigned long flags;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_hpdca.c linux.18p17/drivers/char/ser_hpdca.c
--- linux.vanilla/drivers/char/ser_hpdca.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_hpdca.c	Sat Oct 14 00:04:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_hpdca.h linux.18p17/drivers/char/ser_hpdca.h
--- linux.vanilla/drivers/char/ser_hpdca.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_hpdca.h	Sat Oct 14 00:04:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_hypercom1.c linux.18p17/drivers/char/ser_hypercom1.c
--- linux.vanilla/drivers/char/ser_hypercom1.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_hypercom1.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_hypercom1.h linux.18p17/drivers/char/ser_hypercom1.h
--- linux.vanilla/drivers/char/ser_hypercom1.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_hypercom1.h	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_mfc.c linux.18p17/drivers/char/ser_mfc.c
--- linux.vanilla/drivers/char/ser_mfc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_mfc.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_whippet.c linux.18p17/drivers/char/ser_whippet.c
--- linux.vanilla/drivers/char/ser_whippet.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_whippet.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_whippet.h linux.18p17/drivers/char/ser_whippet.h
--- linux.vanilla/drivers/char/ser_whippet.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/ser_whippet.h	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial167.c linux.18p17/drivers/char/serial167.c
--- linux.vanilla/drivers/char/serial167.c	Fri Apr 21 12:46:06 2000
+++ linux.18p17/drivers/char/serial167.c	Sat Oct 14 00:02:24 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sx.c linux.18p17/drivers/char/sx.c
--- linux.vanilla/drivers/char/sx.c	Sat Sep  9 18:42:36 2000
+++ linux.18p17/drivers/char/sx.c	Sat Oct 14 00:42:50 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/synclink.c linux.18p17/drivers/char/synclink.c
--- linux.vanilla/drivers/char/synclink.c	Sat Sep  9 18:42:36 2000
+++ linux.18p17/drivers/char/synclink.c	Fri Sep  1 22:02:52 2000
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/char/synclink.c
  *
- * ==FILEDATE 20000705==
+ * ==FILEDATE 20000821==
  *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
@@ -102,14 +102,6 @@
 #include <linux/termios.h>
 #include <linux/tqueue.h>
 
-#if LINUX_VERSION_CODE < VERSION(2,3,0) 
-typedef struct wait_queue *wait_queue_head_t;
-#define DECLARE_WAITQUEUE(name,task) struct wait_queue (name) = {(task),NULL}
-#define init_waitqueue_head(head) *(head) = NULL
-#define DECLARE_MUTEX(name) struct semaphore (name) = MUTEX
-#define set_current_state(a) current->state = (a)
-#endif
-
 #ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
 #define CONFIG_SYNCLINK_SYNCPPP 1
 #endif
@@ -124,7 +116,6 @@
 #define netif_queue_stopped(a) ((a)->tbusy)
 #else
 #include "../net/wan/syncppp.h"
-#define netif_queue_stopped(a) test_bit(LINK_STATE_XOFF,&(a)->state)
 #endif
 #endif
 
@@ -401,8 +392,9 @@
 /*
  * The size of the serial xmit buffer is 1 page, or 4096 bytes
  */
+#ifndef SERIAL_XMIT_SIZE
 #define SERIAL_XMIT_SIZE 4096
-
+#endif
 
 /*
  * These macros define the offsets used in calculating the
@@ -965,7 +957,7 @@
 #endif
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "1.19";
+static char *driver_version = "1.22";
 
 static struct tty_driver serial_driver, callout_driver;
 static int serial_refcount;
@@ -4528,6 +4520,12 @@
 	if (!serial_table || !serial_termios || !serial_termios_locked){
 		printk("%s(%d):Can't allocate tty/termios arrays.\n",
 			__FILE__,__LINE__);
+		if (serial_table)
+			kfree(serial_table);
+		if (serial_termios)
+			kfree(serial_termios);
+		if (serial_termios_locked)
+			kfree(serial_termios_locked);
 		return -ENOMEM;
 	}
 	
@@ -7396,12 +7394,7 @@
 void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, 
 	unsigned short count )
 {
-	/*******************************************************/
-	/* A load interval of 16 allows for 4 32-bit writes at */
-	/* 60ns each for a maximum latency of 240ns on the		 */
-	/* local bus.														 */
-	/*******************************************************/
-
+	/* 16 32-bit writes @ 60ns each = 960ns max latency on local bus */
 #define PCI_LOAD_INTERVAL 64
 
 	unsigned short Intervalcount = count / PCI_LOAD_INTERVAL;
@@ -7411,7 +7404,7 @@
 	for ( Index = 0 ; Index < Intervalcount ; Index++ )
 	{
 		memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL);
-		Dummy = *((unsigned long *)TargetPtr);
+		Dummy = *((volatile unsigned long *)TargetPtr);
 		TargetPtr += PCI_LOAD_INTERVAL;
 		SourcePtr += PCI_LOAD_INTERVAL;
 	}
@@ -7582,7 +7575,7 @@
 	sppp_attach(&info->pppdev);
 
 	d = info->netdev;
-	d->name = info->netname;
+	strcpy(d->name, info->netname);
 	d->base_addr = info->io_base;
 	d->irq = info->irq_level;
 	d->dma = info->dma_level;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/wd501p.h linux.18p17/drivers/char/wd501p.h
--- linux.vanilla/drivers/char/wd501p.h	Fri Apr 21 23:14:54 2000
+++ linux.18p17/drivers/char/wd501p.h	Mon Sep 11 18:02:33 2000
@@ -25,11 +25,21 @@
 #define WDT_COUNT1		(io+1)
 #define WDT_COUNT2		(io+2)
 #define WDT_CR			(io+3)
-#define WDT_SR			(io+4)
-#define WDT_RT			(io+5)
-#define WDT_UNUSED		(io+6)
+#define WDT_SR			(io+4)		/* Buzzer start PCI */
+#define WDT_RT			(io+5)		/* Buzzer stop PCI */
+#define WDT_BUZZER		(io+6)		/* PCI only */
 #define WDT_DC			(io+7)
 
+/* The following are only on the PCI card, they're outside of I/O space on
+ * the ISA card: */
+#define WDT_CLOCK		(io+12)	/* COUNT2: rd=16.67MHz, wr=2.0833MHz */
+/* inverted opto isolated reset output: */
+#define WDT_OPTONOTRST		(io+13)	/* wr=enable, rd=disable */
+/* opto isolated reset output: */
+#define WDT_OPTORST		(io+14)	/* wr=enable, rd=disable */
+/* programmable outputs: */
+#define WDT_PROGOUT		(io+15)	/* wr=enable, rd=disable */
+
 #define WDC_SR_WCCR		1	/* Active low */
 #define WDC_SR_TGOOD		2
 #define WDC_SR_ISOI0		4
@@ -39,6 +49,8 @@
 #define WDC_SR_PSUUNDR		64	/* Active low */
 #define WDC_SR_IRQ		128	/* Active low */
 
+#ifndef WDT_IS_PCI
+
 /*
  *	Feature Map 1 is the active high inputs not supported on your card.
  *	Feature Map 2 is the active low inputs not supported on your card.
@@ -62,6 +74,13 @@
 #endif
 
 #ifdef CONFIG_WDT_500		/* Minimal board */
+#define FEATUREMAP1		(WDC_SR_TGOOD|WDC_SR_FANGOOD)
+#define FEATUREMAP2		(WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
+#define WDT_OPTION_MASK		(WDIOF_OVERHEAT)
+#endif
+
+#else
+
 #define FEATUREMAP1		(WDC_SR_TGOOD|WDC_SR_FANGOOD)
 #define FEATUREMAP2		(WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
 #define WDT_OPTION_MASK		(WDIOF_OVERHEAT)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/wdt_pci.c linux.18p17/drivers/char/wdt_pci.c
--- linux.vanilla/drivers/char/wdt_pci.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/char/wdt_pci.c	Mon Sep 25 16:04:57 2000
@@ -0,0 +1,562 @@
+/*
+ *	Industrial Computer Source WDT500/501 driver for Linux 2.1.x
+ *
+ *	(c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ *				http://www.redhat.com
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ *	Release 0.08.
+ *
+ *	Fixes
+ *		Dave Gregorich	:	Modularisation and minor bugs
+ *		Alan Cox	:	Added the watchdog ioctl() stuff
+ *		Alan Cox	:	Fixed the reboot problem (as noted by
+ *					Matt Crocker).
+ *		Alan Cox	:	Added wdt= boot option
+ *		Alan Cox	:	Cleaned up copy/user stuff
+ *		Tim Hockin	:	Added insmod parameters, comment cleanup
+ *					Parameterized timeout
+ *		JP Nollmann	:	Added support for PCI wdt501p
+ *		Alan Cox	:	Split ISA and PCI cards into two drivers
+ *		Jeff Garzik	:	PCI cleanups
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#define WDT_IS_PCI
+#include "wd501p.h"
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/kcomp.h>
+#include <linux/pci.h>
+
+#define PFX "wdt_pci: "
+
+/*
+ * Until Access I/O gets their application for a PCI vendor ID approved,
+ * I don't think that it's appropriate to move these constants into the
+ * regular pci_ids.h file. -- JPN 2000/01/18
+ */
+
+#ifndef PCI_VENDOR_ID_ACCESSIO
+#define PCI_VENDOR_ID_ACCESSIO 0x494f
+#endif
+#ifndef PCI_DEVICE_ID_WDG_CSM
+#define PCI_DEVICE_ID_WDG_CSM 0x22c0
+#endif
+
+static int wdt_is_open=0;
+
+/*
+ *	You must set these - there is no sane way to probe for this board.
+ *	You can use wdt=x,y to set these now.
+ */
+ 
+static int io=0x240;
+static int irq=11;
+
+#define WD_TIMO (100*60)		/* 1 minute */
+
+/*
+ *	Programming support
+ */
+ 
+static void wdtpci_ctr_mode(int ctr, int mode)
+{
+	ctr<<=6;
+	ctr|=0x30;
+	ctr|=(mode<<1);
+	outb_p(ctr, WDT_CR);
+}
+
+static void wdtpci_ctr_load(int ctr, int val)
+{
+	outb_p(val&0xFF, WDT_COUNT0+ctr);
+	outb_p(val>>8, WDT_COUNT0+ctr);
+}
+
+/*
+ *	Kernel methods.
+ */
+ 
+ 
+/**
+ *	wdtpci_status:
+ *	
+ *	Extract the status information from a WDT watchdog device. There are
+ *	several board variants so we have to know which bits are valid. Some
+ *	bits default to one and some to zero in order to be maximally painful.
+ *
+ *	we then map the bits onto the status ioctl flags.
+ */
+ 
+static int wdtpci_status(void)
+{
+	/*
+	 *	Status register to bit flags
+	 */
+	 
+	int flag=0;
+	unsigned char status=inb_p(WDT_SR);
+	status|=FEATUREMAP1;
+	status&=~FEATUREMAP2;	
+	
+	if(!(status&WDC_SR_TGOOD))
+		flag|=WDIOF_OVERHEAT;
+	if(!(status&WDC_SR_PSUOVER))
+		flag|=WDIOF_POWEROVER;
+	if(!(status&WDC_SR_PSUUNDR))
+		flag|=WDIOF_POWERUNDER;
+	if(!(status&WDC_SR_FANGOOD))
+		flag|=WDIOF_FANFAULT;
+	if(status&WDC_SR_ISOI0)
+		flag|=WDIOF_EXTERN1;
+	if(status&WDC_SR_ISII1)
+		flag|=WDIOF_EXTERN2;
+	return flag;
+}
+
+/**
+ *	wdtpci_interrupt:
+ *	@irq:		Interrupt number
+ *	@dev_id:	Unused as we don't allow multiple devices.
+ *	@regs:		Unused.
+ *
+ *	Handle an interrupt from the board. These are raised when the status
+ *	map changes in what the board considers an interesting way. That means
+ *	a failure condition occuring.
+ */
+ 
+static void wdtpci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/*
+	 *	Read the status register see what is up and
+	 *	then printk it. 
+	 */
+	 
+	unsigned char status=inb_p(WDT_SR);
+	
+	status|=FEATUREMAP1;
+	status&=~FEATUREMAP2;	
+	
+	printk(KERN_CRIT "WDT status %d\n", status);
+	
+	if(!(status&WDC_SR_TGOOD))
+		printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT));
+	if(!(status&WDC_SR_PSUOVER))
+		printk(KERN_CRIT "PSU over voltage.\n");
+	if(!(status&WDC_SR_PSUUNDR))
+		printk(KERN_CRIT "PSU under voltage.\n");
+	if(!(status&WDC_SR_FANGOOD))
+		printk(KERN_CRIT "Possible fan fault.\n");
+	if(!(status&WDC_SR_WCCR))
+#ifdef SOFTWARE_REBOOT
+#ifdef ONLY_TESTING
+		printk(KERN_CRIT "Would Reboot.\n");
+#else		
+		printk(KERN_CRIT "Initiating system reboot.\n");
+		machine_restart(NULL);
+#endif		
+#else
+		printk(KERN_CRIT "Reset in 5ms.\n");
+#endif		
+}
+
+
+static long long wdtpci_llseek(struct file *file, long long offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/**
+ *	wdtpci_ping:
+ *
+ *	Reload counter one with the watchdog timeout. We don't bother reloading
+ *	the cascade counter. 
+ */
+ 
+static void wdtpci_ping(void)
+{
+	/* Write a watchdog value */
+	inb_p(WDT_DC);
+	wdtpci_ctr_mode(1,2);
+	wdtpci_ctr_load(1,WD_TIMO);		/* Timeout */
+	outb_p(0, WDT_DC);
+}
+
+/**
+ *	wdtpci_write:
+ *	@file: file handle to the watchdog
+ *	@buf: buffer to write (unused as data does not matter here 
+ *	@count: count of bytes
+ *	@ppos: pointer to the position to write. No seeks allowed
+ *
+ *	A write to a watchdog device is defined as a keepalive signal. Any
+ *	write of data will do, as we we don't define content meaning.
+ */
+ 
+static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if(count)
+	{
+		wdtpci_ping();
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ *	wdtpci_read:
+ *	@file: file handle to the watchdog board
+ *	@buf: buffer to write 1 byte into
+ *	@count: length of buffer
+ *	@ptr: offset (no seek allowed)
+ *
+ *	Read reports the temperature in degrees Fahrenheit. The API is in
+ *	farenheit. It was designed by an imperial measurement luddite.
+ */
+ 
+static ssize_t wdtpci_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+	unsigned short c=inb_p(WDT_RT);
+	unsigned char cp;
+	
+	/*  Can't seek (pread) on this device  */
+	if (ptr != &file->f_pos)
+		return -ESPIPE;
+
+	switch(MINOR(file->f_dentry->d_inode->i_rdev))
+	{
+		case TEMP_MINOR:
+			c*=11;
+			c/=15;
+			cp=c+7;
+			if(copy_to_user(buf,&cp,1))
+				return -EFAULT;
+			return 1;
+		default:
+			return -EINVAL;
+	}
+}
+
+/**
+ *	wdtpci_ioctl:
+ *	@inode: inode of the device
+ *	@file: file handle to the device
+ *	@cmd: watchdog command
+ *	@arg: argument pointer
+ *
+ *	The watchdog API defines a common set of functions for all watchdogs
+ *	according to their available features. We only actually usefully support
+ *	querying capabilities and current status. 
+ */
+ 
+static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	static struct watchdog_info ident=
+	{
+		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
+			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
+		1,
+		"WDT500/501PCI"
+	};
+	
+	ident.options&=WDT_OPTION_MASK;	/* Mask down to the card we have */
+	switch(cmd)
+	{
+		default:
+			return -ENOIOCTLCMD;
+		case WDIOC_GETSUPPORT:
+			return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
+
+		case WDIOC_GETSTATUS:
+			return put_user(wdtpci_status(),(int *)arg);
+		case WDIOC_GETBOOTSTATUS:
+			return put_user(0, (int *)arg);
+		case WDIOC_KEEPALIVE:
+			wdtpci_ping();
+			return 0;
+	}
+}
+
+/**
+ *	wdtpci_open:
+ *	@inode: inode of device
+ *	@file: file handle to device
+ *
+ *	One of our two misc devices has been opened. The watchdog device is
+ *	single open and on opening we load the counters. Counter zero is a 
+ *	100Hz cascade, into counter 1 which downcounts to reboot. When the
+ *	counter triggers counter 2 downcounts the length of the reset pulse
+ *	which set set to be as long as possible. 
+ */
+ 
+static int wdtpci_open(struct inode *inode, struct file *file)
+{
+	switch(MINOR(inode->i_rdev))
+	{
+		case WATCHDOG_MINOR:
+			if(wdt_is_open)
+				return -EBUSY;
+#ifdef CONFIG_WATCHDOG_NOWAYOUT	
+			MOD_INC_USE_COUNT;
+#endif
+			/*
+			 *	Activate 
+			 */
+	 
+			wdt_is_open=1;
+
+			inb_p(WDT_DC);		/* Disable */
+
+			/*
+			 * "pet" the watchdog, as Access says.
+			 * This resets the clock outputs.
+			 */
+				
+			wdtpci_ctr_mode(2,0);
+			outb_p(0, WDT_DC);
+
+			inb_p(WDT_DC);
+
+			outb_p(0, WDT_CLOCK);	/* 2.0833MHz clock */
+			inb_p(WDT_BUZZER);	/* disable */
+			inb_p(WDT_OPTONOTRST);	/* disable */
+			inb_p(WDT_OPTORST);	/* disable */
+			inb_p(WDT_PROGOUT);	/* disable */
+			wdtpci_ctr_mode(0,3);
+			wdtpci_ctr_mode(1,2);
+			wdtpci_ctr_mode(2,1);
+			wdtpci_ctr_load(0,20833);	/* count at 100Hz */
+			wdtpci_ctr_load(1,WD_TIMO);/* Timeout 60 seconds */
+			/* DO NOT LOAD CTR2 on PCI card! -- JPN */
+			outb_p(0, WDT_DC);	/* Enable */
+			return 0;
+		case TEMP_MINOR:
+			return 0;
+		default:
+			return -ENODEV;
+	}
+}
+
+/**
+ *	wdtpci_close:
+ *	@inode: inode to board
+ *	@file: file handle to board
+ *
+ *	The watchdog has a configurable API. There is a religious dispute 
+ *	between people who want their watchdog to be able to shut down and 
+ *	those who want to be sure if the watchdog manager dies the machine
+ *	reboots. In the former case we disable the counters, in the latter
+ *	case you have to open it again very soon.
+ */
+ 
+static int wdtpci_release(struct inode *inode, struct file *file)
+{
+	if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
+	{
+		lock_kernel();
+#ifndef CONFIG_WATCHDOG_NOWAYOUT	
+		inb_p(WDT_DC);		/* Disable counters */
+		wdtpci_ctr_load(2,0);	/* 0 length reset pulses now */
+#endif		
+		wdt_is_open=0;
+		unlock_kernel();
+	}
+	return 0;
+}
+
+/**
+ *	notify_sys:
+ *	@this: our notifier block
+ *	@code: the event being reported
+ *	@unused: unused
+ *
+ *	Our notifier is called on system shutdowns. We want to turn the card
+ *	off at reboot otherwise the machine will reboot again during memory
+ *	test or worse yet during the following fsck. This would suck, in fact
+ *	trust me - if it happens it does suck.
+ */
+
+static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
+	void *unused)
+{
+	if(code==SYS_DOWN || code==SYS_HALT)
+	{
+		/* Turn the card off */
+		inb_p(WDT_DC);
+		wdtpci_ctr_load(2,0);
+	}
+	return NOTIFY_DONE;
+}
+ 
+/*
+ *	Kernel Interfaces
+ */
+ 
+ 
+static struct file_operations wdtpci_fops = {
+	llseek:		wdtpci_llseek,
+	read:		wdtpci_read,
+	write:		wdtpci_write,
+	ioctl:		wdtpci_ioctl,
+	open:		wdtpci_open,
+	release:	wdtpci_release,
+};
+
+static struct miscdevice wdtpci_miscdev=
+{
+	WATCHDOG_MINOR,
+	"watchdog",
+	&wdtpci_fops
+};
+
+#ifdef CONFIG_WDT_501
+static struct miscdevice temp_miscdev=
+{
+	TEMP_MINOR,
+	"temperature",
+	&wdtpci_fops
+};
+#endif
+
+/*
+ *	The WDT card needs to learn about soft shutdowns in order to
+ *	turn the timebomb registers off. 
+ */
+ 
+static struct notifier_block wdtpci_notifier=
+{
+	wdtpci_notify_sys,
+	NULL,
+	0
+};
+
+
+static int __init wdtpci_init_one (struct pci_dev *dev)
+{
+	static int dev_count = 0;
+
+	dev_count++;
+	if (dev_count > 1) {
+		printk (KERN_ERR PFX
+			"this driver only supports 1 device\n");
+		return -ENODEV;
+	}
+
+	irq = dev->irq;
+	io = dev->base_address[2]&PCI_BASE_ADDRESS_IO_MASK;
+	
+	printk ("WDT501-P(PCI-WDG-CSM) driver 0.07 at %X "
+		"(Interrupt %d)\n", io, irq);
+
+	request_region (io, 16, "wdt-pci");
+	if (request_irq (irq, wdtpci_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			 "wdt-pci", &wdtpci_miscdev)) {
+		printk (KERN_ERR PFX "IRQ %d is not free.\n", irq);
+		goto err_out_free_res;
+	}
+
+	misc_register (&wdtpci_miscdev);
+
+#ifdef CONFIG_WDT_501
+	misc_register (&temp_miscdev);
+#endif
+
+	register_reboot_notifier (&wdtpci_notifier);
+
+	return 0;
+
+err_out_free_res:
+	release_region (io, 16);
+	return -EIO;
+}
+
+
+static void __exit wdtpci_remove_one (struct pci_dev *pdev)
+{
+	/* here we assume only one device will ever have
+	 * been picked up and registered by probe function */
+	unregister_reboot_notifier(&wdtpci_notifier);
+#ifdef CONFIG_WDT_501_PCI
+	misc_deregister(&temp_miscdev);
+#endif	
+	misc_deregister(&wdtpci_miscdev);
+	free_irq(irq, &wdtpci_miscdev);
+	release_region(io, 16);
+}
+
+/**
+ *	wdtpci_cleanup:
+ *
+ *	Unload the watchdog. You cannot do this with any file handles open.
+ *	If your watchdog is set to continue ticking on close and you unload
+ *	it, well it keeps ticking. We won't get the interrupt but the board
+ *	will not touch PC memory so all is fine. You just have to load a new
+ *	module in 60 seconds or reboot.
+ */
+
+static struct pci_dev *wdt_dev;
+ 
+static void __exit wdtpci_cleanup(void)
+{
+	if(wdt_dev)
+		wdtpci_remove_one(wdt_dev);
+	wdt_dev = NULL;
+}
+
+
+/**
+ * 	wdtpci_init:
+ *
+ *	Set up the WDT watchdog board. All we have to do is grab the
+ *	resources we require and bitch if anyone beat us to them.
+ *	The open() function will actually kick the board off.
+ */
+ 
+static int __init wdtpci_init(void)
+{
+	wdt_dev=pci_find_device(PCI_VENDOR_ID_ACCESSIO, PCI_DEVICE_ID_WDG_CSM, NULL);
+	if (wdt_dev ==NULL)
+		return -ENODEV;
+	if(wdtpci_init_one(wdt_dev)<0)
+	{
+		wdt_dev=NULL;
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+module_init(wdtpci_init);
+module_exit(wdtpci_cleanup);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/act2000/module.c linux.18p17/drivers/isdn/act2000/module.c
--- linux.vanilla/drivers/isdn/act2000/module.c	Fri Apr 21 12:46:07 2000
+++ linux.18p17/drivers/isdn/act2000/module.c	Sun Oct 15 21:51:57 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/avmcard.h linux.18p17/drivers/isdn/avmb1/avmcard.h
--- linux.vanilla/drivers/isdn/avmb1/avmcard.h	Fri Apr 21 12:46:07 2000
+++ linux.18p17/drivers/isdn/avmb1/avmcard.h	Sat Sep 30 18:28:27 2000
@@ -347,13 +347,13 @@
 static inline void b1_reset(unsigned int base)
 {
 	b1outp(base, B1_RESET, 0);
-	udelay(55 * 2 * 1000);	/* 2 TIC's */
+	mdelay(55 * 2);	/* 2 TIC's */
 
 	b1outp(base, B1_RESET, 1);
-	udelay(55 * 2 * 1000);	/* 2 TIC's */
+	mdelay(55 * 2);	/* 2 TIC's */
 
 	b1outp(base, B1_RESET, 0);
-	udelay(55 * 2 * 1000);	/* 2 TIC's */
+	mdelay(55 * 2);	/* 2 TIC's */
 }
 
 static inline unsigned char b1_disable_irq(unsigned int base)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/b1dma.c linux.18p17/drivers/isdn/avmb1/b1dma.c
--- linux.vanilla/drivers/isdn/avmb1/b1dma.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/isdn/avmb1/b1dma.c	Sat Sep 30 18:12:55 2000
@@ -230,14 +230,14 @@
 	restore_flags(flags);
 
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(10 * 1000);
+	mdelay(10);
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
-	udelay(10 * 1000);
+	mdelay(10);
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
 	if (card->cardtype == avm_t1pci)
-		udelay(42 * 1000);
+		mdelay(42);
 	else
-		udelay(10 * 1000);
+		mdelay(10);
 }
 
 /* ------------------------------------------------------------- */
@@ -245,11 +245,11 @@
 int b1dma_detect(avmcard *card)
 {
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(10 * 1000);
+	mdelay(10);
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */
-	udelay(10 * 1000);
+	mdelay(10);
 	b1dmaoutmeml(card->mbase+AMCC_MCSR, 0);
-	udelay(42 * 1000);
+	mdelay(42);
 
 	b1dmaoutmeml(card->mbase+AMCC_RXLEN, 0);
 	b1dmaoutmeml(card->mbase+AMCC_TXLEN, 0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/c4.c linux.18p17/drivers/isdn/avmb1/c4.c
--- linux.vanilla/drivers/isdn/avmb1/c4.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/isdn/avmb1/c4.c	Sat Sep 30 18:13:09 2000
@@ -373,7 +373,7 @@
 		return 8;
 	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9;
 
-        udelay(1000);
+        mdelay(1);
 
 	if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
 	if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
@@ -385,7 +385,7 @@
 	if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16;
 	if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17;
 
-        udelay(1000);
+        mdelay(1);
 
 	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF))
 		return 18;
@@ -863,7 +863,7 @@
 	c4outmeml(card->mbase+MBOX_UP_LEN, 0);
 	c4outmeml(card->mbase+MBOX_DOWN_LEN, 0);
 	c4outmeml(card->mbase+DOORBELL, DBELL_INIT);
-	udelay(1000);
+	mdelay(1);
 	c4outmeml(card->mbase+DOORBELL,
 			DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/avmb1/t1isa.c linux.18p17/drivers/isdn/avmb1/t1isa.c
--- linux.vanilla/drivers/isdn/avmb1/t1isa.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/isdn/avmb1/t1isa.c	Sat Sep 30 18:13:46 2000
@@ -135,7 +135,7 @@
 	cli();
 	/* board reset */
 	t1outp(base, T1_RESETBOARD, 0xf);
-	udelay(100 * 1000);
+	mdelay(100);
 	dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
 
 	/* write config */
@@ -147,18 +147,18 @@
 	t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
 	restore_flags(flags);
 
-	udelay(100 * 1000);
+	mdelay(100);
 	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
 	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
-	udelay(10 * 1000);
+	mdelay(10);
 	t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
 	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
-	udelay(100 * 1000);
+	mdelay(100);
 	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
 	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
-	udelay(10 * 1000);
+	mdelay(10);
 	t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
-	udelay(5 * 1000);
+	mdelay(5);
 	t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
 
 	if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/divert/divert_procfs.c linux.18p17/drivers/isdn/divert/divert_procfs.c
--- linux.vanilla/drivers/isdn/divert/divert_procfs.c	Fri Apr 21 12:46:08 2000
+++ linux.18p17/drivers/isdn/divert/divert_procfs.c	Sat Sep 23 13:15:35 2000
@@ -1,5 +1,5 @@
 /*
- * $Id: divert_procfs.c,v 1.6 2000/02/14 19:23:03 werner Exp $
+ * $Id: divert_procfs.c,v 1.9 2000/08/20 07:40:14 keil Exp $
  *
  * Filesystem handling for the diversion supplementary services.
  *
@@ -20,6 +20,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: divert_procfs.c,v $
+ * Revision 1.9  2000/08/20 07:40:14  keil
+ * changes from 2.4
+ *
+ * Revision 1.8  2000/03/03 16:37:11  kai
+ * incorporated some cosmetic changes from the official kernel tree back
+ * into CVS
+ *
+ * Revision 1.7  2000/03/02 00:11:06  werner
+ *
+ * Changes related to procfs for 2.3.48
+ *
  * Revision 1.6  2000/02/14 19:23:03  werner
  *
  * Changed handling of proc filesystem tables to a more portable version
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/hisax/hfc_pci.c linux.18p17/drivers/isdn/hisax/hfc_pci.c
--- linux.vanilla/drivers/isdn/hisax/hfc_pci.c	Fri Apr 21 12:46:10 2000
+++ linux.18p17/drivers/isdn/hisax/hfc_pci.c	Mon Sep 11 17:06:28 2000
@@ -1761,6 +1761,7 @@
 			printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
 			return (0);
 		}
+#ifdef notdef
 		if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) {
 			printk(KERN_WARNING "HFC-PCI shared mem address will be corrected\n");
 			pcibios_write_config_word(cs->hw.hfcpci.pci_bus,
@@ -1794,6 +1795,7 @@
 			}
 			dev_hfcpci->base_address[1] = (int) cs->hw.hfcpci.pci_io;
 		}
+#endif
 		if (!cs->hw.hfcpci.pci_io) {
 			printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
 			return (0);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/icn/icn.c linux.18p17/drivers/isdn/icn/icn.c
--- linux.vanilla/drivers/isdn/icn/icn.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/icn/icn.c	Sun Oct 15 21:51:57 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_audio.c linux.18p17/drivers/isdn/isdn_audio.c
--- linux.vanilla/drivers/isdn/isdn_audio.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_audio.c	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_audio.c,v 1.17 1999/08/17 11:10:52 paul Exp $
+/* $Id: isdn_audio.c,v 1.21 2000/06/20 18:01:55 keil Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
@@ -20,73 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_audio.c,v $
- * Revision 1.17  1999/08/17 11:10:52  paul
- * don't try to use x86 assembler on non-x86!
- *
- * Revision 1.16  1999/08/06 12:47:35  calle
- * Using __GNUC__ == 2 && __GNUC_MINOR__ < 95 how to define
- *   ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT
- *
- * Revision 1.15  1999/08/06 12:02:52  calle
- * egcs 2.95 complain about invalid asm statement:
- *    "fixed or forbidden register 2 (cx) was spilled for class CREG."
- * Using ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT and not
- * define it at the moment.
- *
- * Revision 1.14  1999/07/11 17:14:06  armin
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- * Moved "Add CPN to RING message" to new register S23,
- * "Display message" is now correct on register S13 bit 7.
- * New audio command AT+VDD implemented (deactivate DTMF decoder and
- * activate possible existing hardware/DSP decoder).
- * Moved some tty defines to .h file.
- * Made whitespace possible in AT command line.
- * Some AT-emulator output bugfixes.
- * First Fax G3 implementations.
- *
- * Revision 1.13  1999/04/12 12:33:09  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.12  1998/07/26 18:48:43  armin
- * Added silence detection in voice receive mode.
- *
- * Revision 1.11  1998/04/10 10:35:10  paul
- * fixed (silly?) warnings from egcs on Alpha.
- *
- * Revision 1.10  1998/02/20 17:09:40  fritz
- * Changes for recent kernels.
- *
- * Revision 1.9  1997/10/01 09:20:25  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.8  1997/03/02 14:29:16  fritz
- * More ttyI related cleanup.
- *
- * Revision 1.7  1997/02/03 22:44:11  fritz
- * Reformatted according CodingStyle
- *
- * Revision 1.6  1996/06/06 14:43:31  fritz
- * Changed to support DTMF decoding on audio playback also.
- *
- * Revision 1.5  1996/06/05 02:24:08  fritz
- * Added DTMF decoder for audio mode.
- *
- * Revision 1.4  1996/05/17 03:48:01  fritz
- * Removed some test statements.
- * Added revision string.
- *
- * Revision 1.3  1996/05/10 08:48:11  fritz
- * Corrected adpcm bugs.
- *
- * Revision 1.2  1996/04/30 09:31:17  fritz
- * General rewrite.
- *
- * Revision 1.1.1.1  1996/04/28 12:25:40  fritz
- * Taken under CVS control
- *
  */
 
 #define __NO_VERSION__
@@ -95,7 +28,7 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision = "$Revision: 1.17 $";
+char *isdn_audio_revision = "$Revision: 1.21 $";
 
 /*
  * Misc. lookup-tables.
@@ -250,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
@@ -292,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_audio.h linux.18p17/drivers/isdn/isdn_audio.h
--- linux.vanilla/drivers/isdn/isdn_audio.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_audio.h	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_audio.h,v 1.8 1999/07/11 17:14:07 armin Exp $
+/* $Id: isdn_audio.h,v 1.9 2000/05/11 22:29:20 kai Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
@@ -17,39 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Log: isdn_audio.h,v $
- * Revision 1.8  1999/07/11 17:14:07  armin
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- * Moved "Add CPN to RING message" to new register S23,
- * "Display message" is now correct on register S13 bit 7.
- * New audio command AT+VDD implemented (deactivate DTMF decoder and
- * activate possible existing hardware/DSP decoder).
- * Moved some tty defines to .h file.
- * Made whitespace possible in AT command line.
- * Some AT-emulator output bugfixes.
- * First Fax G3 implementations.
- *
- * Revision 1.7  1999/04/12 12:33:11  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.6  1998/07/26 18:48:44  armin
- * Added silence detection in voice receive mode.
- *
- * Revision 1.5  1997/02/03 22:45:21  fritz
- * Reformatted according CodingStyle
- *
- * Revision 1.4  1996/06/06 14:43:32  fritz
- * Changed to support DTMF decoding on audio playback also.
- *
- * Revision 1.3  1996/06/05 02:24:09  fritz
- * Added DTMF decoder for audio mode.
- *
- * Revision 1.2  1996/05/10 08:48:32  fritz
- * Corrected adpcm bugs.
- *
- * Revision 1.1  1996/04/30 09:29:06  fritz
- * Taken under CVS control.
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_cards.c linux.18p17/drivers/isdn/isdn_cards.c
--- linux.vanilla/drivers/isdn/isdn_cards.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/isdn/isdn_cards.c	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_cards.c,v 1.10 1999/07/20 06:41:28 calle Exp $
+/* $Id: isdn_cards.c,v 1.11 2000/05/11 22:29:20 kai Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
@@ -17,38 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Log: isdn_cards.c,v $
- * Revision 1.10  1999/07/20 06:41:28  calle
- * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even
- *         compile, if not selected as modules.
- *
- * Revision 1.9  1999/04/12 12:33:11  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.8  1999/03/29 11:13:23  armin
- * Added eicon driver init.
- *
- * Revision 1.7  1998/02/20 17:24:28  fritz
- * Added ACT2000 init.
- *
- * Revision 1.6  1997/04/23 18:56:03  fritz
- * Old Teles driver removed, Changed doc and scripts accordingly.
- *
- * Revision 1.5  1997/03/30 17:10:36  calle
- * added support for AVM-B1-PCI card.
- *
- * Revision 1.4  1997/03/04 21:59:44  calle
- * Added AVM-B1-CAPI2.0 driver
- *
- * Revision 1.3  1997/02/03 23:31:14  fritz
- * Reformatted according CodingStyle
- *
- * Revision 1.2  1996/10/13 19:52:17  keil
- * HiSax support
- *
- * Revision 1.1  1996/04/20 16:04:36  fritz
- * Initial revision
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_cards.h linux.18p17/drivers/isdn/isdn_cards.h
--- linux.vanilla/drivers/isdn/isdn_cards.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_cards.h	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_cards.h,v 1.3 1999/04/12 12:33:13 fritz Exp $
+/* $Id: isdn_cards.h,v 1.4 2000/05/11 22:29:20 kai Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
@@ -17,16 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Log: isdn_cards.h,v $
- * Revision 1.3  1999/04/12 12:33:13  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.2  1997/02/03 23:31:55  fritz
- * Reformatted according CodingStyle
- *
- * Revision 1.1  1996/04/20 16:04:03  fritz
- * Initial revision
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_common.c linux.18p17/drivers/isdn/isdn_common.c
--- linux.vanilla/drivers/isdn/isdn_common.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/isdn/isdn_common.c	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.100 2000/03/03 16:37:11 kai Exp $
+/* $Id: isdn_common.c,v 1.111 2000/08/20 07:40:14 keil Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -20,412 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_common.c,v $
- * Revision 1.100  2000/03/03 16:37:11  kai
- * incorporated some cosmetic changes from the official kernel tree back
- * into CVS
- *
- * Revision 1.99  2000/02/26 01:00:52  keil
- * changes from 2.3.47
- *
- * Revision 1.98  2000/02/16 14:56:27  paul
- * translated ISDN_MODEM_ANZREG to ISDN_MODEM_NUMREG for english speakers
- *
- * Revision 1.97  2000/01/23 18:45:37  keil
- * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
- *
- * Revision 1.96  2000/01/20 19:55:33  keil
- * Add FAX Class 1 support
- *
- * Revision 1.95  2000/01/09 20:43:13  detabc
- * exand logical bind-group's for both call's (in and out).
- * add first part of kernel-config-help for abc-extension.
- *
- * Revision 1.94  1999/11/20 22:14:13  detabc
- * added channel dial-skip in case of external use
- * (isdn phone or another isdn device) on the same NTBA.
- * usefull with two or more card's connected the different NTBA's.
- * global switchable in kernel-config and also per netinterface.
- *
- * add auto disable of netinterface's in case of:
- * 	to many connection's in short time.
- * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
- * 	or remote side.
- * 	wrong password's or something else to a ISP (syncppp).
- *
- * possible encapsulations for this future are:
- * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
- * and ISDN_NET_ENCAP_CISCOHDLCK.
- *
- * Revision 1.93  1999/11/04 13:11:36  keil
- * Reinit of v110 structs
- *
- * Revision 1.92  1999/10/31 15:59:50  he
- * more skb headroom checks
- *
- * Revision 1.91  1999/10/28 22:48:45  armin
- * Bugfix: isdn_free_channel() now frees the channel,
- * even when the usage of the ttyI has changed.
- *
- * Revision 1.90  1999/10/27 21:21:17  detabc
- * Added support for building logically-bind-group's per interface.
- * usefull for outgoing call's with more then one isdn-card.
- *
- * Switchable support to dont reset the hangup-timeout for
- * receive frames. Most part's of the timru-rules for receiving frames
- * are now obsolete. If the input- or forwarding-firewall deny
- * the frame, the line will be not hold open.
- *
- * Revision 1.89  1999/10/16 14:46:47  keil
- * replace kmalloc with vmalloc for the big dev struct
- *
- * Revision 1.88  1999/10/02 00:39:26  he
- * Fixed a 2.3.x wait queue initialization (was causing panics)
- *
- * Revision 1.87  1999/09/12 16:19:39  detabc
- * added abc features
- * low cost routing for net-interfaces (only the HL side).
- * need more implementation in the isdnlog-utility
- * udp info support (first part).
- * different EAZ on outgoing call's.
- * more checks on D-Channel callbacks (double use of channels).
- * tested and running with kernel 2.3.17
- *
- * Revision 1.86  1999/07/31 12:59:42  armin
- * Added tty fax capabilities.
- *
- * Revision 1.85  1999/07/29 16:58:35  armin
- * Bugfix: DLE handling in isdn_readbchan()
- *
- * Revision 1.84  1999/07/25 16:21:10  keil
- * fix number matching
- *
- * Revision 1.83  1999/07/13 21:02:05  werner
- * Added limit possibilty of driver b_channel resources (ISDN_STAT_DISCH)
- *
- * Revision 1.82  1999/07/12 21:06:50  werner
- * Fixed problem when loading more than one driver temporary
- *
- * Revision 1.81  1999/07/11 17:14:09  armin
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- * Moved "Add CPN to RING message" to new register S23,
- * "Display message" is now correct on register S13 bit 7.
- * New audio command AT+VDD implemented (deactivate DTMF decoder and
- * activate possible existing hardware/DSP decoder).
- * Moved some tty defines to .h file.
- * Made whitespace possible in AT command line.
- * Some AT-emulator output bugfixes.
- * First Fax G3 implementations.
- *
- * Revision 1.80  1999/07/07 10:14:00  detabc
- * remove unused messages
- *
- * Revision 1.79  1999/07/05 23:51:30  werner
- * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl
- * hisaxctrl id 10 <nr. of chans 0-2>
- *
- * Revision 1.78  1999/07/05 20:21:15  werner
- * changes to use diversion sources for all kernel versions.
- * removed static device, only proc filesystem used
- *
- * Revision 1.77  1999/07/01 08:29:50  keil
- * compatibility to 2.3 kernel
- *
- * Revision 1.76  1999/06/29 16:16:44  calle
- * Let ISDN_CMD_UNLOAD work with open isdn devices without crash again.
- * Also right unlocking (ISDN_CMD_UNLOCK) is done now.
- * isdnlog should check returncode of read(2) calls.
- *
- * Revision 1.75  1999/04/18 14:06:47  fritz
- * Removed TIMRU stuff.
- *
- * Revision 1.74  1999/04/12 13:16:45  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.73  1999/04/12 12:33:15  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.72  1999/03/02 12:04:44  armin
- * -added ISDN_STAT_ADDCH to increase supported channels after
- *  register_isdn().
- * -ttyI now goes on-hook on ATZ when B-Ch is connected.
- * -added timer-function for register S7 (Wait for Carrier).
- * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
- * -on L2_MODEM a string will be appended to the CONNECT-Message,
- *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
- * -variable "dialing" used for ATA also, for interrupting call
- *  establishment and register S7.
- *
- * Revision 1.71  1999/01/28 09:10:43  armin
- * Fixed bad while-loop in isdn_readbch().
- *
- * Revision 1.70  1999/01/15 19:58:54  he
- * removed compatibiltity macro
- *
- * Revision 1.69  1998/09/07 21:59:58  he
- * flush method for 2.1.118 and above
- * updated IIOCTLNETGPN
- *
- * Revision 1.68  1998/08/31 21:09:45  he
- * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
- *     peer phone number)
- *
- * Revision 1.67  1998/06/26 15:12:21  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 1.66  1998/06/17 19:50:41  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- *
- * Revision 1.62  1998/04/14 16:28:43  he
- * Fixed user space access with interrupts off and remaining
- * copy_{to,from}_user() -> -EFAULT return codes
- *
- * Revision 1.61  1998/03/22 18:50:46  hipp
- * Added BSD Compression for syncPPP .. UNTESTED at the moment
- *
- * Revision 1.60  1998/03/19 13:18:18  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 1.59  1998/03/09 17:46:23  he
- * merged in 2.1.89 changes
- *
- * Revision 1.58  1998/03/07 22:35:24  fritz
- * Starting generic module support (Nothing usable yet).
- *
- * Revision 1.57  1998/03/07 18:21:01  cal
- * Dynamic Timeout-Rule-Handling vs. 971110 included
- *
- * Revision 1.56  1998/02/25 17:49:38  he
- * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
- *
- * Revision 1.55  1998/02/23 23:35:32  fritz
- * Eliminated some compiler warnings.
- *
- * Revision 1.54  1998/02/22 19:44:19  fritz
- * Bugfixes and improvements regarding V.110, V.110 now running.
- *
- * Revision 1.53  1998/02/20 17:18:05  fritz
- * Changes for recent kernels.
- * Added common stub for sending commands to lowlevel.
- * Added V.110.
- *
- * Revision 1.52  1998/01/31 22:05:57  keil
- * Lots of changes for X.25 support:
- * Added generic support for connection-controlling encapsulation protocols
- * Added support of BHUP status message
- * Added support for additional p_encap X25IFACE
- * Added support for kernels >= 2.1.72
- *
- * Revision 1.51  1998/01/31 19:17:29  calle
- * merged changes from and for 2.1.82
- *
- * Revision 1.50  1997/12/12 06:12:11  calle
- * moved EXPORT_SYMBOL(register_isdn) from isdn_syms.c to isdn_common.c
- *
- * Revision 1.49  1997/11/06 17:16:52  keil
- * Sync to 2.1.62 changes
- *
- * Revision 1.48  1997/11/02 23:55:50  keil
- * Andi Kleen's changes for 2.1.60
- * without it the isdninfo and isdnctrl devices won't work
- *
- * Revision 1.47  1997/10/09 21:28:46  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.46  1997/10/01 09:20:27  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.45  1997/08/21 23:11:41  fritz
- * Added changes for kernels >= 2.1.45
- *
- * Revision 1.44  1997/05/27 15:17:23  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * Revision 1.43  1997/03/31 14:09:43  fritz
- * Fixed memory leak in isdn_close().
- *
- * Revision 1.42  1997/03/30 16:51:08  calle
- * changed calls to copy_from_user/copy_to_user and removed verify_area
- * were possible.
- *
- * Revision 1.41  1997/03/24 22:54:41  fritz
- * Some small fixes in debug code.
- *
- * Revision 1.40  1997/03/08 08:13:51  fritz
- * Bugfix: IIOCSETMAP (Set mapping) was broken.
- *
- * Revision 1.39  1997/03/07 01:32:54  fritz
- * Added proper ifdef's for CONFIG_ISDN_AUDIO
- *
- * Revision 1.38  1997/03/05 21:15:02  fritz
- * Fix: reduced stack usage of isdn_ioctl() and isdn_set_allcfg()
- *
- * Revision 1.37  1997/03/02 14:29:18  fritz
- * More ttyI related cleanup.
- *
- * Revision 1.36  1997/02/28 02:32:40  fritz
- * Cleanup: Moved some tty related stuff from isdn_common.c
- *          to isdn_tty.c
- * Bugfix:  Bisync protocol did not behave like documented.
- *
- * Revision 1.35  1997/02/21 13:01:19  fritz
- * Changes CAUSE message output in kernel log.
- *
- * Revision 1.34  1997/02/10 20:12:43  fritz
- * Changed interface for reporting incoming calls.
- *
- * Revision 1.33  1997/02/10 10:05:42  fritz
- * More changes for Kernel 2.1.X
- * Symbol information moved to isdn_syms.c
- *
- * Revision 1.32  1997/02/03 22:55:26  fritz
- * Reformatted according CodingStyle.
- * Changed isdn_writebuf_stub static.
- * Slow down tty-RING counter.
- * skb->free stuff replaced by macro.
- * Bugfix in audio-skb locking.
- * Bugfix in HL-driver locking.
- *
- * Revision 1.31  1997/01/17 01:19:18  fritz
- * Applied chargeint patch.
- *
- * Revision 1.30  1997/01/14 01:27:47  fritz
- * Changed audio receive not to rely on skb->users and skb->lock.
- * Added ATI2 and related variables.
- * Started adding full-duplex audio capability.
- *
- * Revision 1.29  1997/01/12 23:33:03  fritz
- * Made isdn_all_eaz foolproof.
- *
- * Revision 1.28  1996/11/13 02:33:19  fritz
- * Fixed a race condition.
- *
- * Revision 1.27  1996/10/27 22:02:41  keil
- * return codes for ISDN_STAT_ICALL
- *
- * Revision 1.26  1996/10/23 11:59:40  fritz
- * More compatibility changes.
- *
- * Revision 1.25  1996/10/22 23:13:54  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.24  1996/10/11 14:02:03  fritz
- * Bugfix: call to isdn_ppp_timer_timeout() never compiled, because of
- *         typo in #ifdef.
- *
- * Revision 1.23  1996/06/25 18:35:38  fritz
- * Fixed bogus memory access in isdn_set_allcfg().
- *
- * Revision 1.22  1996/06/24 17:37:37  fritz
- * Bugfix: isdn_timer_ctrl() did restart timer, even if it
- *         was already running.
- *         lowlevel driver locking did use wrong parameters.
- *
- * Revision 1.21  1996/06/15 14:58:20  fritz
- * Added version signatures for data structures used
- * by userlevel programs.
- *
- * Revision 1.20  1996/06/12 16:01:49  fritz
- * Bugfix: Remote B-channel hangup sometimes did not result
- *         in a NO CARRIER on tty.
- *
- * Revision 1.19  1996/06/11 14:52:04  hipp
- * minor bugfix in isdn_writebuf_skb_stub()
- *
- * Revision 1.18  1996/06/06 14:51:51  fritz
- * Changed to support DTMF decoding on audio playback also.
- *
- * Revision 1.17  1996/06/05 02:24:10  fritz
- * Added DTMF decoder for audio mode.
- *
- * Revision 1.16  1996/06/03 20:09:05  fritz
- * Bugfix: called wrong function pointer for locking in
- *         isdn_get_free_channel().
- *
- * Revision 1.15  1996/05/31 01:10:54  fritz
- * Bugfixes:
- *   Lowlevel modules did not get locked correctly.
- *   Did show wrong revision when initializing.
- *   Minor fixes in ioctl code.
- *   sk_buff did not get freed, if error in writebuf_stub.
- *
- * Revision 1.14  1996/05/18 01:36:55  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.13  1996/05/17 15:43:30  fritz
- * Bugfix: decrement of rcvcount in readbchan() corrected.
- *
- * Revision 1.12  1996/05/17 03:55:43  fritz
- * Changed DLE handling for audio receive.
- * Some cleanup.
- * Added display of isdn_audio_revision.
- *
- * Revision 1.11  1996/05/11 21:51:32  fritz
- * Changed queue management to use sk_buffs.
- *
- * Revision 1.10  1996/05/10 08:49:16  fritz
- * Checkin before major changes of tty-code.
- *
- * Revision 1.9  1996/05/07 09:19:41  fritz
- * Adapted to changes in isdn_tty.c
- *
- * Revision 1.8  1996/05/06 11:34:51  hipp
- * fixed a few bugs
- *
- * Revision 1.7  1996/05/02 03:55:17  fritz
- * Bugfixes:
- *  - B-channel connect message for modem devices
- *    sometimes did not result in a CONNECT-message.
- *  - register_isdn did not check for driverId-conflicts.
- *
- * Revision 1.6  1996/04/30 20:57:21  fritz
- * Commit test
- *
- * Revision 1.5  1996/04/20 16:19:07  fritz
- * Changed slow timer handlers to increase accuracy.
- * Added statistic information for usage by xisdnload.
- * Fixed behaviour of isdnctrl-device on non-blocked io.
- * Fixed all io to go through generic writebuf-function without
- * bypassing. Same for incoming data.
- * Fixed bug: Last channel had been unusable.
- * Fixed kfree of tty xmit_buf on ppp initialization failure.
- *
- * Revision 1.4  1996/02/11 02:33:26  fritz
- * Fixed bug in main timer-dispatcher.
- * Bugfix: Lot of tty-callbacks got called regardless of the events already
- * been handled by network-devices.
- * Changed ioctl-names.
- *
- * Revision 1.3  1996/01/22 05:16:11  fritz
- * Changed ioctl-names.
- * Fixed bugs in isdn_open and isdn_close regarding PPP_MINOR.
- *
- * Revision 1.2  1996/01/21 16:52:40  fritz
- * Support for sk_buffs added, changed header-stuffing.
- *
- * Revision 1.1  1996/01/09 04:12:52  fritz
- * Initial revision
- *
  */
 
 #include <linux/config.h>
@@ -452,7 +46,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.100 $";
+static char *isdn_revision = "$Revision: 1.111 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -475,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)
@@ -556,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;
@@ -602,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)
 {
@@ -673,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);
 	}
 }
@@ -696,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);
 }
 
@@ -878,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.
@@ -887,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))) 
@@ -1433,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;
 }
@@ -2067,7 +1674,7 @@
 
 static struct file_operations isdn_fops =
 {
-	llseek:		isdn_lseek,
+	llseek:		isdn_llseek,
 	read:		isdn_read,
 	write:		isdn_write,
 	poll:		isdn_poll,
@@ -2252,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 */
@@ -2291,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_common.h linux.18p17/drivers/isdn/isdn_common.h
--- linux.vanilla/drivers/isdn/isdn_common.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_common.h	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.h,v 1.18 2000/01/23 18:45:37 keil Exp $
+/* $Id: isdn_common.h,v 1.20 2000/06/16 13:00:27 keil Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
@@ -20,90 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_common.h,v $
- * Revision 1.18  2000/01/23 18:45:37  keil
- * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
- *
- * Revision 1.17  1999/10/27 21:21:17  detabc
- * Added support for building logically-bind-group's per interface.
- * usefull for outgoing call's with more then one isdn-card.
- *
- * Switchable support to dont reset the hangup-timeout for
- * receive frames. Most part's of the timru-rules for receiving frames
- * are now obsolete. If the input- or forwarding-firewall deny
- * the frame, the line will be not hold open.
- *
- * Revision 1.16  1999/07/01 08:29:54  keil
- * compatibility to 2.3 kernel
- *
- * Revision 1.15  1999/04/18 14:06:50  fritz
- * Removed TIMRU stuff.
- *
- * Revision 1.14  1999/04/12 12:33:18  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.13  1999/03/02 12:04:47  armin
- * -added ISDN_STAT_ADDCH to increase supported channels after
- *  register_isdn().
- * -ttyI now goes on-hook on ATZ when B-Ch is connected.
- * -added timer-function for register S7 (Wait for Carrier).
- * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
- * -on L2_MODEM a string will be appended to the CONNECT-Message,
- *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
- * -variable "dialing" used for ATA also, for interrupting call
- *  establishment and register S7.
- *
- * Revision 1.12  1998/06/26 15:12:27  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 1.11  1998/04/14 16:28:47  he
- * Fixed user space access with interrupts off and remaining
- * copy_{to,from}_user() -> -EFAULT return codes
- *
- * Revision 1.10  1998/03/07 18:21:03  cal
- * Dynamic Timeout-Rule-Handling vs. 971110 included
- *
- * Revision 1.9  1998/02/20 17:19:01  fritz
- * Added common stub for sending commands to lowlevel.
- *
- * Revision 1.8  1997/10/09 21:28:49  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.7  1997/10/01 09:20:30  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.6  1997/02/28 02:32:44  fritz
- * Cleanup: Moved some tty related stuff from isdn_common.c
- *          to isdn_tty.c
- * Bugfix:  Bisync protocol did not behave like documented.
- *
- * Revision 1.5  1997/02/10 10:05:45  fritz
- * More changes for Kernel 2.1.X
- * Symbol information moved to isdn_syms.c
- *
- * Revision 1.4  1997/02/03 22:56:50  fritz
- * Removed isdn_writebuf_stub prototype.
- *
- * Revision 1.3  1996/05/19 00:13:05  fritz
- * Removed debug flag.
- *
- * Revision 1.2  1996/04/20 16:20:40  fritz
- * Misc. typos.
- *
- * Revision 1.1  1996/01/10 21:37:19  fritz
- * Initial revision
- *
  */
 
 #undef  ISDN_DEBUG_MODEM_OPEN
@@ -134,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_concap.c linux.18p17/drivers/isdn/isdn_concap.c
--- linux.vanilla/drivers/isdn/isdn_concap.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_concap.c	Sat Sep 23 13:15:35 2000
@@ -1,32 +1,26 @@
-/* $Id: isdn_concap.c,v 1.6 1999/08/22 20:26:01 calle Exp $
+/* $Id: isdn_concap.c,v 1.8 2000/05/11 22:29:20 kai Exp $
  
- * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
  * stuff goes here. Stuff that depends only on the concap protocol goes to
  * another -- protocol specific -- source file.
- *
- * $Log: isdn_concap.c,v $
- * Revision 1.6  1999/08/22 20:26:01  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.5  1998/10/30 18:44:48  he
- * pass return value from isdn_net_dial_req for dialmode change
- *
- * Revision 1.4  1998/10/30 17:55:24  he
- * dialmode for x25iface and multulink ppp
- *
- * Revision 1.3  1998/05/26 22:39:22  he
- * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
- * concap typo
- * cleared dev.tbusy in isdn_net BCONN status callback
- *
- * Revision 1.2  1998/01/31 22:49:21  keil
- * correct comments
- *
- * Revision 1.1  1998/01/31 22:27:57  keil
- * New files from Henner Eisen for X.25 support
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_concap.h linux.18p17/drivers/isdn/isdn_concap.h
--- linux.vanilla/drivers/isdn/isdn_concap.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_concap.h	Sat Sep 23 13:15:35 2000
@@ -1,5 +1,23 @@
-/* $Id: isdn_concap.h,v 1.2 1998/01/31 22:49:21 keil Exp $
+/* $Id: isdn_concap.h,v 1.3 2000/05/11 22:29:20 kai Exp $
+ *
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
  */
+
 extern struct concap_device_ops isdn_concap_reliable_dl_dops;
 extern struct concap_device_ops isdn_concap_demand_dial_dops;
 extern struct concap_proto * isdn_concap_new( int );
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_net.c linux.18p17/drivers/isdn/isdn_net.c
--- linux.vanilla/drivers/isdn/isdn_net.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/isdn/isdn_net.c	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.114 2000/03/16 16:37:41 kai Exp $
+/* $Id: isdn_net.c,v 1.137 2000/09/12 19:43:47 kai Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -20,443 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_net.c,v $
- * Revision 1.114  2000/03/16 16:37:41  kai
- * Allow phone numbers starting with "*" as outgoing numbers for
- * networking interface. Some PBX's need this to allow dialing internal
- * numbers (mine, for example ;-)
- *
- * Revision 1.113  2000/03/16 15:46:37  kai
- * a little bugfix and cosmetic changes
- *
- * Revision 1.112  2000/03/04 16:20:42  detabc
- * copy frames before rewriting frame's saddr
- *
- * Revision 1.111  2000/02/28 22:28:24  he
- * moved tx_timeout warning messages in old (2.2.x) branch where it really only
- * indicates problems.
- *
- * Revision 1.110  2000/02/26 01:00:53  keil
- * changes from 2.3.47
- *
- * Revision 1.109  2000/02/25 11:29:17  paul
- * changed chargetime to ulong from int (after about 20 days the "chargetime of
- * ipppX is now 1234" message displays a negative number on alpha).
- *
- * Revision 1.108  2000/02/15 12:54:01  kai
- * set TX timeout back to 2 secs for 2.2.x, just to be safe
- *
- * Revision 1.107  2000/02/13 09:52:05  kai
- * increased TX_TIMEOUT to 20sec
- *
- * Revision 1.106  2000/02/12 19:26:55  kai
- * adopted to latest 2.3 softnet changes.
- *
- * tested with PPP and MPPP, it works here.
- * can somebody check raw-ip?
- *
- * also changed std2kern, stddiff for bash-1 compatibility,
- * hope this doesn't break anything.
- *
- * Revision 1.105  2000/02/12 11:43:26  he
- * SOFTNET related changes, first try. Compatible with linux 2.2.x, but
- * not tested for kernels with softnet (>= 2.3.43) yet.
- *
- * Revision 1.104  2000/02/06 21:49:59  detabc
- * add rewriting of socket's and frame's saddr for udp-ipv4 dynip-connections.
- * Include checksum-recompute of ip- and udp-header's.
- *
- * Revision 1.103  2000/01/23 18:45:37  keil
- * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
- *
- * Revision 1.102  2000/01/09 20:43:14  detabc
- * exand logical bind-group's for both call's (in and out).
- * add first part of kernel-config-help for abc-extension.
- *
- * Revision 1.101  1999/12/05 16:06:08  detabc
- * add resethandling for rawip-compression.
- * at now all B2-Protocols are usable with rawip-compression
- *
- * Revision 1.100  1999/12/04 15:05:25  detabc
- * bugfix abc-rawip-bsdcompress with channel-bundeling
- *
- * Revision 1.99  1999/11/30 11:29:06  detabc
- * add a on the fly frame-counter and limit
- *
- * Revision 1.98  1999/11/28 14:49:07  detabc
- * In case of rawip-compress adjust dev[x]->ibytes/obytes to reflect the
- * uncompressed size.
- *
- * Revision 1.97  1999/11/26 15:54:59  detabc
- * added compression (isdn_bsdcompress) for rawip interfaces with x75i B2-protocol.
- *
- * Revision 1.96  1999/11/20 22:14:13  detabc
- * added channel dial-skip in case of external use
- * (isdn phone or another isdn device) on the same NTBA.
- * usefull with two or more card's connected the different NTBA's.
- * global switchable in kernel-config and also per netinterface.
- *
- * add auto disable of netinterface's in case of:
- * 	to many connection's in short time.
- * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
- * 	or remote side.
- * 	wrong password's or something else to a ISP (syncppp).
- *
- * possible encapsulations for this future are:
- * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
- * and ISDN_NET_ENCAP_CISCOHDLCK.
- *
- * Revision 1.95  1999/10/27 21:21:17  detabc
- * Added support for building logically-bind-group's per interface.
- * usefull for outgoing call's with more then one isdn-card.
- *
- * Switchable support to dont reset the hangup-timeout for
- * receive frames. Most part's of the timru-rules for receiving frames
- * are now obsolete. If the input- or forwarding-firewall deny
- * the frame, the line will be not hold open.
- *
- * Revision 1.94  1999/10/02 11:07:02  he
- * Changed tbusy logic in indn_net.c
- *
- * Revision 1.93  1999/09/23 22:22:41  detabc
- * added tcp-keepalive-detect with local response (ipv4 only)
- * added host-only-interface support
- * (source ipaddr == interface ipaddr) (ipv4 only)
- * ok with kernel 2.3.18 and 2.2.12
- *
- * Revision 1.92  1999/09/13 23:25:17  he
- * serialized xmitting frames from isdn_ppp and BSENT statcallb
- *
- * Revision 1.91  1999/09/12 16:19:39  detabc
- * added abc features
- * low cost routing for net-interfaces (only the HL side).
- * need more implementation in the isdnlog-utility
- * udp info support (first part).
- * different EAZ on outgoing call's.
- * more checks on D-Channel callbacks (double use of channels).
- * tested and running with kernel 2.3.17
- *
- * Revision 1.90  1999/09/04 22:21:39  detabc
- *
- * Revision 1.89  1999/08/22 20:26:03  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.88  1999/07/07 10:13:31  detabc
- * remove unused messages
- *
- * Revision 1.87  1999/07/06 07:53:53  calle
- * calls to dev_alloc_skb waste 16 bytes of memory, if we calculate the
- * right header space for the lowlevel driver. using alloc_skb instead.
- *
- * Revision 1.86  1999/06/09 10:12:05  paul
- * thinko in previous patch
- *
- * Revision 1.85  1999/06/07 19:42:39  paul
- * isdn_net_getpeer() fixed to return correct `outgoing' flag
- *
- * Revision 1.84  1999/04/18 14:06:55  fritz
- * Removed TIMRU stuff.
- *
- * Revision 1.83  1999/04/12 12:33:23  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.82  1999/01/17 00:55:58  he
- * added mark_bh in BCONN statcallb and cleaned up some dead code
- *
- * Revision 1.81  1999/01/15 16:36:52  he
- * replaced icmp_send() by dst_link_failure()
- *
- * Revision 1.80  1998/12/01 13:06:22  paul
- * Also huptimeout with dialmode == manual
- *
- * Revision 1.79  1998/10/30 17:55:27  he
- * dialmode for x25iface and multulink ppp
- *
- * Revision 1.78  1998/10/26 18:20:46  he
- * re-inserted p=p->next in isdn_net_find_icall() (fixes kernel lock up
- * on incoming call not matching the first interface)
- *
- * Revision 1.77  1998/10/23 10:18:44  paul
- * Implementation of "dialmode" (successor of "status")
- * You also need current isdnctrl for this!
- *
- * Revision 1.76  1998/09/07 22:00:05  he
- * flush method for 2.1.118 and above
- * updated IIOCTLNETGPN
- *
- * Revision 1.75  1998/08/31 21:09:50  he
- * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
- *     peer phone number)
- *
- * Revision 1.74  1998/07/30 11:28:32  paul
- * printk message only appeared when status is off and interface is rawIP,
- * which is confusing for people who don't know about "isdnctrl status <if> on".
- *
- * Revision 1.73  1998/06/26 22:01:37  keil
- * tx_queue_len = 5 was too small
- *
- * Revision 1.72  1998/06/26 15:12:31  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 1.71  1998/06/18 22:43:08  fritz
- * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at cleanup.
- *
- * Revision 1.70  1998/06/17 19:50:49  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- * Revision 1.69  1998/06/09 12:27:37  cal
- * Changed default of local netdev flags: ISDN_NET_STOPPED is default now,
- * so autodial is suppressed for that device until it is switched on using
- * 'isdnctrl status dev-name on'.
- *
- *
- *
- * Revision 1.66  1998/05/26 22:39:24  he
- * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
- * concap typo
- * cleared dev.tbusy in isdn_net BCONN status callback
- *
- * Revision 1.61  1998/04/16 19:19:42  keil
- * Fix from vger (tx max qlength)
- *
- * Revision 1.60  1998/04/14 16:28:49  he
- * Fixed user space access with interrupts off and remaining
- * copy_{to,from}_user() -> -EFAULT return codes
- *
- * Revision 1.59  1998/03/07 22:37:33  fritz
- * Bugfix: restore_flags missing.
- *
- * Revision 1.58  1998/03/07 18:21:05  cal
- * Dynamic Timeout-Rule-Handling vs. 971110 included
- *
- * Revision 1.57  1998/02/25 18:31:13  fritz
- * Added debugging output in adjust_header.
- *
- * Revision 1.56  1998/02/25 17:49:42  he
- * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
- *
- * Revision 1.55  1998/02/23 19:38:22  fritz
- * Corrected check for modified feature-flags.
- *
- * Revision 1.54  1998/02/20 17:15:07  fritz
- * Changes for recent kernels.
- * Ugly workaround for adjusting Ethernet frames with recent kernels.
- * replaced direct calls to lowlevel-driver command by common hook.
- *
- * Revision 1.53  1998/01/31 22:05:54  keil
- * Lots of changes for X.25 support:
- * Added generic support for connection-controlling encapsulation protocols
- * Added support of BHUP status message
- * Added support for additional p_encap X25IFACE
- * Added support for kernels >= 2.1.72
- *
- * Revision 1.52  1998/01/31 19:29:51  calle
- * Merged changes from and for 2.1.82, not tested only compiled ...
- *
- * Revision 1.51  1997/10/09 21:28:50  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.50  1997/10/01 09:20:32  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.49  1997/08/21 14:38:13  fritz
- * Bugfix: Did not compile without SyncPPP.
- *
- * Revision 1.48  1997/06/22 11:57:15  fritz
- * Added ability to adjust slave triggerlevel.
- *
- * Revision 1.47  1997/06/21 10:52:05  fritz
- * Removed wrong SET_SKB_FREE in isdn_net_send_skb()
- *
- * Revision 1.46  1997/06/17 13:05:24  hipp
- * Applied Eric's underflow-patches (slightly modified)
- *
- * Revision 1.45  1997/06/10 16:24:22  hipp
- * hard_header changes for syncPPP (now behaves like RAWIP)
- *
- * Revision 1.44  1997/05/27 15:17:26  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * Revision 1.43  1997/03/30 16:51:13  calle
- * changed calls to copy_from_user/copy_to_user and removed verify_area
- * were possible.
- *
- * Revision 1.42  1997/03/11 08:43:51  fritz
- * Perform a hangup if number is deleted while dialing.
- *
- * Revision 1.41  1997/03/08 08:16:31  fritz
- * Bugfix: Deleting a phone number during dial gave unpredictable results.
- *
- * Revision 1.40  1997/03/05 21:16:08  fritz
- * Fix: did not compile with 2.1.27
- *
- * Revision 1.39  1997/03/04 21:36:52  fritz
- * Added sending ICMP messages when no connetion is possible.
- *
- * Revision 1.38  1997/02/23 23:41:14  fritz
- * Bugfix: Slave interfaces have to be hung up before master.
- *
- * Revision 1.37  1997/02/11 18:32:51  fritz
- * Bugfix in isdn_ppp_free_mpqueue().
- *
- * Revision 1.36  1997/02/10 21:31:11  fritz
- * Changed setup-interface (incoming and outgoing).
- *
- * Revision 1.35  1997/02/10 20:12:45  fritz
- * Changed interface for reporting incoming calls.
- *
- * Revision 1.34  1997/02/03 23:15:07  fritz
- * Reformatted according CodingStyle.
- * replaced arp_find prototype by proper include.
- * made dev_purge_queues static.
- * Bugfix in bogocps calculation.
- * removed isdn_net_receive_callback - was never used ;-)
- * Misc. fixes for Kernel 2.1.X comaptibility.
- *
- * Revision 1.33  1997/01/17 01:19:25  fritz
- * Applied chargeint patch.
- *
- * Revision 1.32  1997/01/14 01:29:31  fritz
- * Bugfix: isdn_net_hangup() did not reset ISDN_NET_CONNECTED.
- *
- * Revision 1.31  1997/01/11 23:30:42  fritz
- * Speed up dial statemachine.
- *
- * Revision 1.30  1996/11/25 17:20:50  hipp
- * fixed pppbind bug in isdn_net_find_icall()
- *
- * Revision 1.29  1996/11/13 02:31:38  fritz
- * Minor cleanup.
- *
- * Revision 1.28  1996/10/27 20:49:06  keil
- * bugfix to compile without MPP
- *
- * Revision 1.27  1996/10/25 18:46:01  fritz
- * Another bugfix in isdn_net_autohup()
- *
- * Revision 1.26  1996/10/23 23:05:36  fritz
- * Bugfix: Divide by zero in isdn_net_autohup()
- *
- * Revision 1.25  1996/10/22 23:13:58  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.24  1996/10/11 13:57:40  fritz
- * Bugfix: Error in BogoCPS calculation.
- *
- * Revision 1.23  1996/09/23 01:58:08  fritz
- * Fix: With syncPPP encapsulation, discard LCP packets
- *      when calculating hangup timeout.
- *
- * Revision 1.22  1996/09/23 00:03:37  fritz
- * Fix: did not compile without CONFIG_ISDN_PPP
- *
- * Revision 1.21  1996/09/07 12:44:50  hipp
- * (hopefully) fixed callback problem with syncPPP
- * syncPPP network devices now show PPP link encap
- *
- * Revision 1.20  1996/08/29 20:06:03  fritz
- * Bugfix: Transmission timeout had been much to low.
- *
- * Revision 1.19  1996/08/12 16:24:32  hipp
- * removed some (now) obsolete functions for syncPPP in rebuild_header etc.
- *
- * Revision 1.18  1996/07/03 13:48:51  hipp
- * bugfix: Call dev_purge_queues() only for master device
- *
- * Revision 1.17  1996/06/25 18:37:37  fritz
- * Fixed return count for empty return string in isdn_net_getphones().
- *
- * Revision 1.16  1996/06/24 17:48:08  fritz
- * Bugfixes:
- *   - Did not free channel on unbinding.
- *   - ioctl returned wrong callback settings.
- *
- * Revision 1.15  1996/06/16 17:42:54  tsbogend
- * fixed problem with IP addresses on Linux/Alpha (long is 8 byte there)
- *
- * Revision 1.14  1996/06/11 14:54:08  hipp
- * minor bugfix in isdn_net_send_skb
- * changes in BSENT callback handler for syncPPP
- * added lp->sav_skb stuff
- *
- * Revision 1.13  1996/06/06 14:25:44  fritz
- * Changed loglevel of "incoming ... without OAD" message, since
- * with audio support this is quite normal.
- *
- * Revision 1.12  1996/06/05 02:36:45  fritz
- * Minor bugfixes by M. Hipp.
- *
- * Revision 1.11  1996/05/18 01:36:59  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.10  1996/05/17 03:49:01  fritz
- * Some cleanup.
- *
- * Revision 1.9  1996/05/06 11:34:57  hipp
- * fixed a few bugs
- *
- * Revision 1.8  1996/04/30 21:04:40  fritz
- * Test commit
- *
- * Revision 1.7  1996/04/30 11:10:42  fritz
- * Added Michael's ippp-bind patch.
- *
- * Revision 1.6  1996/04/30 09:34:35  fritz
- * Removed compatibility-macros.
- *
- * Revision 1.5  1996/04/20 16:28:38  fritz
- * Made more parameters of the dial statemachine user-configurable and
- * added hangup after dial for more reliability using callback.
- * Changed all io going through generic routines in isdn_common.c
- * Added missing call to dev_free_skb on failed dialing.
- * Added uihdlc encapsulation.
- * Fixed isdn_net_setcfg not to destroy interface-flags anymore.
- * Misc. typos.
- *
- * Revision 1.4  1996/02/19 15:23:38  fritz
- * Bugfix: Sync-PPP packets got compressed twice, when resent due to
- *         send-queue-full reject.
- *
- * Revision 1.3  1996/02/11 02:22:28  fritz
- * Changed status- receive-callbacks to use pointer-arrays for finding
- * a corresponding interface instead of looping over all interfaces.
- * Activate Auto-hangup-timer only when interface is online.
- * Some bugfixes in the dialing-statemachine.
- * Lot of bugfixes in sk_buff'ized encapsulation handling.
- * For speedup connection-setup after dialing, remember sk_buf that triggered
- * dialing.
- * Fixed isdn_net_log_packet according to different encapsulations.
- * Correct ARP-handling for ETHERNET-encapsulation.
- *
- * Revision 1.2  1996/01/22 05:05:12  fritz
- * Changed returncode-logic for isdn_net_start_xmit() and its
- * helper-functions.
- * Changed handling of buildheader for RAWIP and ETHERNET-encapsulation.
- *
- * Revision 1.1  1996/01/09 04:12:34  fritz
- * Initial revision
- *
  */
 
 #include <linux/config.h>
@@ -2220,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;
@@ -2236,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) */
@@ -2279,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)
@@ -2371,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_net.h linux.18p17/drivers/isdn/isdn_net.h
--- linux.vanilla/drivers/isdn/isdn_net.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_net.h	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.h,v 1.10 1999/08/22 20:26:06 calle Exp $
+/* $Id: isdn_net.h,v 1.19 2000/06/21 09:54:29 keil Exp $
 
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
@@ -20,48 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_net.h,v $
- * Revision 1.10  1999/08/22 20:26:06  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.9  1999/04/12 12:33:27  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.8  1998/10/30 17:55:33  he
- * dialmode for x25iface and multulink ppp
- *
- * Revision 1.7  1998/08/31 21:09:55  he
- * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
- *     peer phone number)
- *
- * Revision 1.6  1997/10/09 21:28:54  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.5  1997/02/10 20:12:47  fritz
- * Changed interface for reporting incoming calls.
- *
- * Revision 1.4  1997/02/03 23:16:48  fritz
- * Removed isdn_net_receive_callback prototype.
- *
- * Revision 1.3  1997/01/17 01:19:30  fritz
- * Applied chargeint patch.
- *
- * Revision 1.2  1996/04/20 16:29:43  fritz
- * Misc. typos
- *
- * Revision 1.1  1996/02/11 02:35:13  fritz
- * Initial revision
- *
  */
 
 			      /* Definitions for hupflags:                */
@@ -117,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ppp.c linux.18p17/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/isdn/isdn_ppp.c	Sat Sep 23 13:15:35 2000
@@ -18,255 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_ppp.c,v $
- * Revision 1.63  2000/03/16 15:46:37  kai
- * a little bugfix and cosmetic changes
- *
- * Revision 1.62  2000/02/12 19:26:55  kai
- * adopted to latest 2.3 softnet changes.
- *
- * tested with PPP and MPPP, it works here.
- * can somebody check raw-ip?
- *
- * also changed std2kern, stddiff for bash-1 compatibility,
- * hope this doesn't break anything.
- *
- * Revision 1.61  1999/11/20 22:14:14  detabc
- * added channel dial-skip in case of external use
- * (isdn phone or another isdn device) on the same NTBA.
- * usefull with two or more card's connected the different NTBA's.
- * global switchable in kernel-config and also per netinterface.
- *
- * add auto disable of netinterface's in case of:
- * 	to many connection's in short time.
- * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
- * 	or remote side.
- * 	wrong password's or something else to a ISP (syncppp).
- *
- * possible encapsulations for this future are:
- * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
- * and ISDN_NET_ENCAP_CISCOHDLCK.
- *
- * Revision 1.60  1999/11/04 20:29:55  he
- * applied Andre Beck's reset_free fix
- *
- * Revision 1.59  1999/10/31 15:59:50  he
- * more skb headroom checks
- *
- * Revision 1.58  1999/10/30 13:13:01  keil
- * Henners isdn_ppp_skb_push:under fix
- *
- * Revision 1.57  1999/10/05 22:47:17  he
- * Removed dead ISDN_SYNCPPP_READDRESS code (obsoleted by sysctl_ip_dynaddr
- * and network address translation)
- *
- * Revision 1.56  1999/09/29 16:01:06  he
- * replaced dev_alloc_skb() for downstream skbs by equivalent alloc_skb()
- *
- * Revision 1.55  1999/09/23 22:07:51  detabc
- *
- * make ipc_head common usable (for use compressor with raw-ip)
- * add function before netif_rx(). needed for ipv4-tcp-keepalive-detect.
- * ~
- *
- * Revision 1.54  1999/09/13 23:25:17  he
- * serialized xmitting frames from isdn_ppp and BSENT statcallb
- *
- * Revision 1.53  1999/08/31 11:18:14  paul
- * various spelling corrections (new checksums may be needed, Karsten!)
- *
- * Revision 1.52  1999/08/22 20:26:07  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.51  1999/08/18 16:19:17  hipp
- * applied MPPP-resize-headroom patch
- *
- * Revision 1.50  1999/08/16 07:11:41  hipp
- * Additional VJ decomp-buffer-size increased from 40 to 128
- *
- * Revision 1.49  1999/07/06 07:47:11  calle
- * bugfix: dev_alloc_skb only reserve 16 bytes. We need to look at the
- *  	hdrlen the driver want. So I changed dev_alloc_skb calls
- *         to alloc_skb and skb_reserve.
- *
- * Revision 1.48  1999/07/01 08:29:56  keil
- * compatibility to 2.3 kernel
- *
- * Revision 1.47  1999/04/18 14:06:59  fritz
- * Removed TIMRU stuff.
- *
- * Revision 1.46  1999/04/12 12:33:35  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.45  1998/12/30 17:48:24  paul
- * fixed syncPPP callback out
- *
- * Revision 1.44  1998/10/30 17:55:34  he
- * dialmode for x25iface and multulink ppp
- *
- * Revision 1.43  1998/10/29 17:23:54  hipp
- * Minor MPPP fixes, verboser logging.
- *
- * Revision 1.42  1998/07/20 11:30:07  hipp
- * Readded compression check
- *
- * Revision 1.41  1998/07/08 16:50:57  hipp
- * Compression changes
- *
- * Revision 1.40  1998/04/06 19:07:27  hipp
- * added check, whether compression is enabled.
- *
- * Revision 1.39  1998/03/25 22:46:53  hipp
- * Some additional CCP changes.
- *
- * Revision 1.38  1998/03/24 16:33:06  hipp
- * More CCP changes. BSD compression now "works" on a local loopback link.
- * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
- *
- * Revision 1.37  1998/03/22 18:50:49  hipp
- * Added BSD Compression for syncPPP .. UNTESTED at the moment
- *
- * Revision 1.36  1998/03/09 17:46:30  he
- * merged in 2.1.89 changes
- *
- * Revision 1.35  1998/03/07 18:21:11  cal
- * Dynamic Timeout-Rule-Handling vs. 971110 included
- *
- * Revision 1.34  1998/02/25 17:49:48  he
- * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
- *
- * Revision 1.33  1998/02/20 17:11:54  fritz
- * Changes for recent kernels.
- *
- * Revision 1.32  1998/01/31 19:29:55  calle
- * Merged changes from and for 2.1.82, not tested only compiled ...
- *
- * Revision 1.31  1997/10/09 21:29:01  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.30  1997/10/01 09:20:38  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.29  1997/08/21 23:11:44  fritz
- * Added changes for kernels >= 2.1.45
- *
- * Revision 1.28  1997/06/17 13:05:57  hipp
- * Applied Eric's underflow-patches (slightly modified)
- * more compression changes (but disabled at the moment)
- * changed one copy_to_user() to run with enabled IRQs
- * a few MP changes
- * changed 'proto' handling in the isdn_ppp receive code
- *
- * Revision 1.27  1997/03/30 16:51:17  calle
- * changed calls to copy_from_user/copy_to_user and removed verify_area
- * were possible.
- *
- * Revision 1.26  1997/02/23 16:53:44  hipp
- * minor cleanup
- * some initial changes for future PPP compresion
- * added AC,PC compression for outgoing frames
- *
- * Revision 1.25  1997/02/12 20:37:35  hipp
- * New ioctl() PPPIOCGCALLINFO, minor cleanup
- *
- * Revision 1.24  1997/02/11 18:32:56  fritz
- * Bugfix in isdn_ppp_free_mpqueue().
- *
- * Revision 1.23  1997/02/10 11:12:19  fritz
- * More changes for Kernel 2.1.X compatibility.
- *
- * Revision 1.22  1997/02/06 15:03:51  hipp
- * changed GFP_KERNEL kmalloc to GFP_ATOMIC in isdn_ppp_fill_mpqueue()
- *
- * Revision 1.21  1997/02/03 23:29:38  fritz
- * Reformatted according CodingStyle
- * Bugfix: removed isdn_ppp_skb_destructor, used by upper layers.
- * Misc changes for Kernel 2.1.X compatibility.
- *
- * Revision 1.20  1996/10/30 12:21:58  fritz
- * Cosmetic fix: Compiler warning when compiling without MPP.
- *
- * Revision 1.19  1996/10/25 19:03:21  hipp
- * changed/added some defines to (re)allow compilation without MP/VJ
- *
- * Revision 1.18  1996/10/22 23:14:00  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.17  1996/10/22 09:39:49  hipp
- * a few MP changes and bugfixes
- *
- * Revision 1.16  1996/09/23 01:58:10  fritz
- * Fix: With syncPPP encapsulation, discard LCP packets
- *      when calculating hangup timeout.
- *
- * Revision 1.15  1996/09/07 12:50:12  hipp
- * bugfixes (unknown device after failed dial attempt, minor bugs)
- *
- * Revision 1.14  1996/08/12 16:26:47  hipp
- * code cleanup
- * changed connection management from minors to slots
- *
- * Revision 1.13  1996/07/01 19:47:24  hipp
- * Fixed memory leak in VJ handling and more VJ changes
- *
- * Revision 1.12  1996/06/24 17:42:03  fritz
- * Minor bugfixes.
- *
- * Revision 1.11  1996/06/16 17:46:05  tsbogend
- * changed unsigned long to u32 to make Alpha people happy
- *
- * Revision 1.10  1996/06/11 14:50:29  hipp
- * Lot of changes and bugfixes.
- * New scheme to resend packets to busy LL devices.
- *
- * Revision 1.9  1996/05/18 01:37:01  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.8  1996/05/06 11:34:55  hipp
- * fixed a few bugs
- *
- * Revision 1.7  1996/04/30 11:07:42  fritz
- * Added Michael's ippp-bind patch.
- *
- * Revision 1.6  1996/04/30 09:33:09  fritz
- * Removed compatibility-macros.
- *
- * Revision 1.5  1996/04/20 16:32:32  fritz
- * Changed ippp_table to an array of pointers, allocating each part
- * separately.
- *
- * Revision 1.4  1996/02/19 15:25:50  fritz
- * Bugfix: Sync-PPP packets got compressed twice, when resent due to
- * send-queue-full reject.
- *
- * Revision 1.3  1996/02/11 02:27:12  fritz
- * Lot of Bugfixes my Michael.
- * Moved calls to skb_push() into isdn_net_header()
- * Fixed a possible race-condition in isdn_ppp_timer_timeout().
- *
- * Revision 1.2  1996/01/22 05:08:06  fritz
- * Merged in Michael's patches for MP.
- * Minor changes in isdn_ppp_xmit.
- *
- * Revision 1.1  1996/01/09 04:11:29  fritz
- * Initial revision
- *
  */
-
-/* TODO: right tbusy handling when using MP */
 
 #define CONFIG_ISDN_CCP 1
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ppp.h linux.18p17/drivers/isdn/isdn_ppp.h
--- linux.vanilla/drivers/isdn/isdn_ppp.h	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/isdn/isdn_ppp.h	Sun Oct  1 11:55:52 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.h,v 1.14 1999/08/22 20:26:10 calle Exp $
+/* $Id: isdn_ppp.h,v 1.17 2000/08/10 22:52:46 kai Exp $
 
  * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -18,65 +18,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_ppp.h,v $
- * Revision 1.14  1999/08/22 20:26:10  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.13  1998/03/22 18:50:50  hipp
- * Added BSD Compression for syncPPP .. UNTESTED at the moment
- *
- * Revision 1.12  1998/01/31 22:07:48  keil
- * changes for newer kernels
- *
- * Revision 1.11  1997/10/01 09:20:44  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.10  1997/06/17 13:06:00  hipp
- * Applied Eric's underflow-patches (slightly modified)
- * more compression changes (but disabled at the moment)
- * changed one copy_to_user() to run with enabled IRQs
- * a few MP changes
- * changed 'proto' handling in the isdn_ppp receive code
- *
- * Revision 1.9  1997/02/11 18:32:59  fritz
- * Bugfix in isdn_ppp_free_mpqueue().
- *
- * Revision 1.8  1997/02/10 10:11:33  fritz
- * More changes for Kernel 2.1.X compatibility.
- *
- * Revision 1.7  1997/02/03 23:18:57  fritz
- * Removed isdn_ppp_free_sqqueue prototype
- *         and ippp_table (both static in isdn_ppp.c).
- *
- * Revision 1.6  1996/09/23 01:58:11  fritz
- * Fix: With syncPPP encapsulation, discard LCP packets
- *      when calculating hangup timeout.
- *
- * Revision 1.5  1996/09/07 12:51:34  hipp
- * *** empty log message ***
- *
- * Revision 1.4  1996/05/06 11:34:56  hipp
- * fixed a few bugs
- *
- * Revision 1.3  1996/04/30 09:33:10  fritz
- * Removed compatibility-macros.
- *
- * Revision 1.2  1996/04/20 16:35:11  fritz
- * Changed isdn_ppp_receive to use sk_buff as parameter.
- * Added definition of isdn_ppp_dial_slave and ippp_table.
- *
- * Revision 1.1  1996/01/10 21:39:10  fritz
- * Initial revision
- *
  */
 
 #include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
-#include <linux/isdn_ppp.h>
+#include <linux/isdn_ppp.h>	/* for isdn_ppp info */
 extern int isdn_ppp_read(int, struct file *, char *, int);
 extern int isdn_ppp_write(int, struct file *, const char *, int);
 extern int isdn_ppp_open(int, struct file *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_tty.c linux.18p17/drivers/isdn/isdn_tty.c
--- linux.vanilla/drivers/isdn/isdn_tty.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_tty.c	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.84 2000/02/16 15:10:14 paul Exp $
+/* $Id: isdn_tty.c,v 1.93 2000/08/05 09:58:26 armin Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -19,334 +19,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_tty.c,v $
- * Revision 1.84  2000/02/16 15:10:14  paul
- * If a ttyI has no open FDs, don't connect incoming calls to it.
- * (Hangup on close of last FD is still to be done.)
- *
- * Revision 1.83  2000/02/16 14:59:33  paul
- * translated ISDN_MODEM_ANZREG to ISDN_MODEM_NUMREG for english speakers;
- * used defines for result codes;
- * fixed RING ... RUNG problem (no empty lines in between).
- *
- * Revision 1.82  2000/01/23 18:45:37  keil
- * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN)
- *
- * Revision 1.81  2000/01/20 19:55:33  keil
- * Add FAX Class 1 support
- *
- * Revision 1.80  1999/11/07 13:34:30  armin
- * Fixed AT command line editor
- *
- * Revision 1.79  1999/10/29 18:35:08  armin
- * Check number len in isdn_get_msnstr() to avoid buffer overflow.
- *
- * Revision 1.78  1999/10/28 23:03:51  armin
- * Bugfix: now freeing channel on modem_hup() even when
- * usage on ttyI has changed and error-report for
- * AT-commands on wrong channel-state.
- *
- * Revision 1.77  1999/10/26 21:13:14  armin
- * using define for checking phone number len in isdn_tty_getdial()
- *
- * Revision 1.76  1999/10/11 22:16:26  keil
- * Suspend/Resume is possible without explicit ID too
- *
- * Revision 1.75  1999/10/08 18:59:32  armin
- * Bugfix of too small MSN buffer and checking phone number
- * in isdn_tty_getdial()
- *
- * Revision 1.74  1999/09/04 06:20:04  keil
- * Changes from kernel set_current_state()
- *
- * Revision 1.73  1999/08/28 21:56:27  keil
- * misplaced #endif caused ttyI crash in 2.3.X
- *
- * Revision 1.72  1999/07/31 12:59:45  armin
- * Added tty fax capabilities.
- *
- * Revision 1.71  1999/07/27 10:34:34  armin
- * Fixed last change. Did not compile with AUDIO support off.
- *
- * Revision 1.70  1999/07/25 16:17:58  keil
- * Fix Suspend/Resume
- *
- * Revision 1.69  1999/07/25 12:56:15  armin
- * isdn_tty_at_cout() now queues the message if online and
- * data is in queue or flip buffer is full.
- * needed for fax connections.
- *
- * Revision 1.68  1999/07/11 17:51:51  armin
- * Bugfix, "-" was missing for AT&L settings.
- *
- * Revision 1.67  1999/07/11 17:14:12  armin
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- * Moved "Add CPN to RING message" to new register S23,
- * "Display message" is now correct on register S13 bit 7.
- * New audio command AT+VDD implemented (deactivate DTMF decoder and
- * activate possible existing hardware/DSP decoder).
- * Moved some tty defines to .h file.
- * Made whitespace possible in AT command line.
- * Some AT-emulator output bugfixes.
- * First Fax G3 implementations.
- *
- * Revision 1.66  1999/07/07 10:13:46  detabc
- * remove unused messages
- *
- * Revision 1.65  1999/07/04 21:01:59  werner
- * Added support for keypad and display (ported from 2.0)
- *
- * Revision 1.64  1999/07/01 08:30:00  keil
- * compatibility to 2.3 kernel
- *
- * Revision 1.63  1999/04/12 12:33:39  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.62  1999/03/02 12:04:48  armin
- * -added ISDN_STAT_ADDCH to increase supported channels after
- *  register_isdn().
- * -ttyI now goes on-hook on ATZ when B-Ch is connected.
- * -added timer-function for register S7 (Wait for Carrier).
- * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
- * -on L2_MODEM a string will be appended to the CONNECT-Message,
- *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
- * -variable "dialing" used for ATA also, for interrupting call
- *  establishment and register S7.
- *
- * Revision 1.61  1999/01/27 22:53:11  he
- * minor updates (spellings, jiffies wrap around in isdn_tty)
- *
- * Revision 1.60  1998/11/15 23:57:32  keil
- * changes for 2.1.127
- *
- * Revision 1.59  1998/08/20 13:50:15  keil
- * More support for hybrid modem (not working yet)
- *
- * Revision 1.58  1998/07/26 18:48:45  armin
- * Added silence detection in voice receive mode.
- *
- * Revision 1.57  1998/06/26 15:12:36  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 1.56  1998/06/18 23:31:51  fritz
- * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
- * Removed direct-senddown feature in isdn_tty_write because it will
- * never succeed with locking and is useless anyway.
- *
- * Revision 1.55  1998/06/17 19:50:55  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- *
- *
- * Revision 1.52  1998/03/19 13:18:21  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- *
- * Revision 1.49  1998/03/08 00:01:59  fritz
- * Bugfix: Lowlevel module usage and channel usage were not
- *         reset on NO DCHANNEL.
- *
- * Revision 1.48  1998/03/07 12:28:15  tsbogend
- * fixed kernel unaligned traps on Linux/Alpha
- *
- * Revision 1.47  1998/02/22 19:44:14  fritz
- * Bugfixes and improvements regarding V.110, V.110 now running.
- *
- * Revision 1.46  1998/02/20 17:23:08  fritz
- * Changes for recent kernels.
- * Merged in contributions by Thomas Pfeiffer (V.110 T.70+ Extended FAX stuff)
- * Added symbolic constants for Modem-Registers.
- *
- * Revision 1.45  1998/01/31 22:07:49  keil
- * changes for newer kernels
- *
- * Revision 1.44  1998/01/31 19:30:02  calle
- * Merged changes from and for 2.1.82, not tested only compiled ...
- *
- * Revision 1.43  1997/10/09 21:29:04  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.42  1997/10/01 09:20:49  fritz
- * Removed old compatibility stuff for 2.0.X kernels.
- * From now on, this code is for 2.1.X ONLY!
- * Old stuff is still in the separate branch.
- *
- * Revision 1.41  1997/05/27 15:17:31  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * Revision 1.40  1997/03/24 22:55:27  fritz
- * Added debug code for status callbacks.
- *
- * Revision 1.39  1997/03/21 18:25:56  fritz
- * Corrected CTS handling.
- *
- * Revision 1.38  1997/03/07 12:13:35  fritz
- * Bugfix: Send audio in adpcm format was broken.
- * Bugfix: CTS handling was wrong.
- *
- * Revision 1.37  1997/03/07 01:37:34  fritz
- * Bugfix: Did not compile with CONFIG_ISDN_AUDIO disabled.
- * Bugfix: isdn_tty_tint() did not handle lowlevel errors correctly.
- * Bugfix: conversion was wrong when sending ulaw audio.
- * Added proper ifdef's for CONFIG_ISDN_AUDIO
- *
- * Revision 1.36  1997/03/04 21:41:55  fritz
- * Fix: Excessive stack usage of isdn_tty_senddown()
- *      and isdn_tty_end_vrx() could lead to problems.
- *
- * Revision 1.35  1997/03/02 19:05:52  fritz
- * Bugfix: Avoid recursion.
- *
- * Revision 1.34  1997/03/02 14:29:22  fritz
- * More ttyI related cleanup.
- *
- * Revision 1.33  1997/02/28 02:32:45  fritz
- * Cleanup: Moved some tty related stuff from isdn_common.c
- *          to isdn_tty.c
- * Bugfix:  Bisync protocol did not behave like documented.
- *
- * Revision 1.32  1997/02/23 15:43:03  fritz
- * Small change in handling of incoming calls
- * documented in newest version of ttyI.4
- *
- * Revision 1.31  1997/02/21 13:05:57  fritz
- * Bugfix: Remote hangup did not set location-info on ttyI's
- *
- * Revision 1.30  1997/02/18 09:41:05  fritz
- * Added support for bitwise access to modem registers (ATSx.y=n, ATSx.y?).
- * Beautified output of AT&V.
- *
- * Revision 1.29  1997/02/16 12:11:51  fritz
- * Added S13,Bit4 option.
- *
- * Revision 1.28  1997/02/10 22:07:08  fritz
- * Added 2 modem registers for numbering plan and screening info.
- *
- * Revision 1.27  1997/02/10 21:31:14  fritz
- * Changed setup-interface (incoming and outgoing).
- *
- * Revision 1.26  1997/02/10 20:12:48  fritz
- * Changed interface for reporting incoming calls.
- *
- * Revision 1.25  1997/02/03 23:04:30  fritz
- * Reformatted according CodingStyle.
- * skb->free stuff replaced by macro.
- * Finished full-duplex audio.
- *
- * Revision 1.24  1997/01/14 01:32:42  fritz
- * Changed audio receive not to rely on skb->users and skb->lock.
- * Added ATI2 and related variables.
- * Started adding full-duplex audio capability.
- *
- * Revision 1.23  1996/10/22 23:14:02  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.22  1996/10/19 18:56:43  fritz
- * ATZ did not change the xmitbuf size.
- *
- * Revision 1.21  1996/06/24 17:40:28  fritz
- * Bugfix: Did not compile without CONFIG_ISDN_AUDIO
- *
- * Revision 1.20  1996/06/15 14:59:39  fritz
- * Fixed isdn_tty_tint() to handle partially sent
- * sk_buffs.
- *
- * Revision 1.19  1996/06/12 15:53:56  fritz
- * Bugfix: AT+VTX and AT+VRX could be executed without
- *         having a connection.
- *         Missing check for NULL tty in isdn_tty_flush_buffer().
- *
- * Revision 1.18  1996/06/07 11:17:33  tsbogend
- * added missing #ifdef CONFIG_ISDN_AUDIO to make compiling without
- * audio support possible
- *
- * Revision 1.17  1996/06/06 14:55:47  fritz
- * Changed to support DTMF decoding on audio playback also.
- * Bugfix: Added check for invalid info->isdn_driver in
- *         isdn_tty_senddown().
- * Clear ncarrier flag on last close() of a tty.
- *
- * Revision 1.16  1996/06/05 02:24:12  fritz
- * Added DTMF decoder for audio mode.
- *
- * Revision 1.15  1996/06/03 20:35:01  fritz
- * Fixed typos.
- *
- * Revision 1.14  1996/06/03 20:12:19  fritz
- * Fixed typos.
- * Added call to write_wakeup via isdn_tty_flush_buffer()
- * in isdn_tty_modem_hup().
- *
- * Revision 1.13  1996/05/31 01:33:29  fritz
- * Changed buffering due to bad performance with mgetty.
- * Now sk_buff is delayed allocated in isdn_tty_senddown
- * using xmit_buff like in standard serial driver.
- * Fixed module locking.
- * Added DLE-DC4 handling in voice mode.
- *
- * Revision 1.12  1996/05/19 01:34:40  fritz
- * Bugfix: ATS returned error.
- *         Register 20 made readonly.
- *
- * Revision 1.11  1996/05/18 01:37:03  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.10  1996/05/17 03:51:49  fritz
- * Changed DLE handling for audio receive.
- *
- * Revision 1.9  1996/05/11 21:52:07  fritz
- * Changed queue management to use sk_buffs.
- *
- * Revision 1.8  1996/05/10 08:49:43  fritz
- * Checkin before major changes of tty-code.
- *
- * Revision 1.7  1996/05/07 09:15:09  fritz
- * Reorganized and general cleanup.
- * Bugfixes:
- *  - Audio-transmit working now.
- *  - "NO CARRIER" now reported, when hanging up with DTR low.
- *  - Corrected CTS handling.
- *
- * Revision 1.6  1996/05/02 03:59:25  fritz
- * Bugfixes:
- *  - On dialout, layer-2 setup had been incomplete
- *    when using new auto-layer2 feature.
- *  - On hangup, "NO CARRIER" message sometimes missing.
- *
- * Revision 1.5  1996/04/30 21:05:25  fritz
- * Test commit
- *
- * Revision 1.4  1996/04/20 16:39:54  fritz
- * Changed all io to go through generic routines in isdn_common.c
- * Fixed a real ugly bug in modem-emulator: 'ATA' had been accepted
- * even when a call has been cancelled from the remote machine.
- *
- * Revision 1.3  1996/02/11 02:12:32  fritz
- * Bugfixes according to similar fixes in standard serial.c of kernel.
- *
- * Revision 1.2  1996/01/22 05:12:25  fritz
- * replaced my_atoi by simple_strtoul
- *
- * Revision 1.1  1996/01/09 04:13:18  fritz
- * Initial revision
- *
  */
 #undef ISDN_TTY_STAT_DEBUG
 
@@ -2480,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",
@@ -2499,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);
@@ -2520,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;
@@ -2531,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
@@ -2584,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_tty.h linux.18p17/drivers/isdn/isdn_tty.h
--- linux.vanilla/drivers/isdn/isdn_tty.h	Fri Apr 21 23:22:17 2000
+++ linux.18p17/drivers/isdn/isdn_tty.h	Sun Oct 15 21:51:57 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.h,v 1.19 2000/02/16 14:59:33 paul Exp $
+/* $Id: isdn_tty.h,v 1.22 2000/06/21 09:54:29 keil Exp $
 
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
@@ -19,89 +19,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_tty.h,v $
- * Revision 1.19  2000/02/16 14:59:33  paul
- * translated ISDN_MODEM_ANZREG to ISDN_MODEM_NUMREG for english speakers;
- * used defines for result codes;
- * fixed RING ... RUNG problem (no empty lines in between).
- *
- * Revision 1.18  2000/01/20 19:55:33  keil
- * Add FAX Class 1 support
- *
- * Revision 1.17  1999/09/21 19:00:35  armin
- * Extended FCON message with added CPN
- * can now be activated with Bit 1 of Reg 23.
- *
- * Revision 1.16  1999/08/22 20:26:10  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.15  1999/07/31 12:59:48  armin
- * Added tty fax capabilities.
- *
- * Revision 1.14  1999/07/11 17:14:15  armin
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- * Moved "Add CPN to RING message" to new register S23,
- * "Display message" is now correct on register S13 bit 7.
- * New audio command AT+VDD implemented (deactivate DTMF decoder and
- * activate possible existing hardware/DSP decoder).
- * Moved some tty defines to .h file.
- * Made whitespace possible in AT command line.
- * Some AT-emulator output bugfixes.
- * First Fax G3 implementations.
- *
- * Revision 1.13  1999/04/12 12:33:46  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.12  1999/03/02 12:04:51  armin
- * -added ISDN_STAT_ADDCH to increase supported channels after
- *  register_isdn().
- * -ttyI now goes on-hook on ATZ when B-Ch is connected.
- * -added timer-function for register S7 (Wait for Carrier).
- * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
- * -on L2_MODEM a string will be appended to the CONNECT-Message,
- *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
- * -variable "dialing" used for ATA also, for interrupting call
- *  establishment and register S7.
- *
- * Revision 1.11  1998/03/19 13:18:27  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 1.10  1997/03/02 14:29:26  fritz
- * More ttyI related cleanup.
- *
- * Revision 1.9  1997/02/28 02:32:49  fritz
- * Cleanup: Moved some tty related stuff from isdn_common.c
- *          to isdn_tty.c
- * Bugfix:  Bisync protocol did not behave like documented.
- *
- * Revision 1.8  1997/02/10 20:12:50  fritz
- * Changed interface for reporting incoming calls.
- *
- * Revision 1.7  1997/02/03 23:06:10  fritz
- * Reformatted according CodingStyle
- *
- * Revision 1.6  1997/01/14 01:35:19  fritz
- * Changed prototype of isdn_tty_modem_hup.
- *
- * Revision 1.5  1996/05/17 03:52:31  fritz
- * Changed DLE handling for audio receive.
- *
- * Revision 1.4  1996/05/11 21:52:34  fritz
- * Changed queue management to use sk_buffs.
- *
- * Revision 1.3  1996/05/07 09:16:34  fritz
- * Changed isdn_try_read parameter.
- *
- * Revision 1.2  1996/04/30 21:05:27  fritz
- * Test commit
- *
- * Revision 1.1  1996/01/10 21:39:22  fritz
- * Initial revision
- *
  */
 
 #include <linux/config.h>
@@ -196,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ttyfax.c linux.18p17/drivers/isdn/isdn_ttyfax.c
--- linux.vanilla/drivers/isdn/isdn_ttyfax.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_ttyfax.c	Sat Sep 23 13:15:35 2000
@@ -1,4 +1,5 @@
-/* $Id: isdn_ttyfax.c,v 1.6 2000/01/26 00:41:13 keil Exp $
+/* $Id: isdn_ttyfax.c,v 1.7 2000/05/11 22:29:21 kai Exp $
+
  * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
  *
  * Copyright 1999    by Armin Schindler (mac@melware.de)
@@ -19,30 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_ttyfax.c,v $
- * Revision 1.6  2000/01/26 00:41:13  keil
- * add "00" as dummy msn in isdn_get_free_channel call
- *
- * Revision 1.5  2000/01/20 19:55:33  keil
- * Add FAX Class 1 support
- *
- * Revision 1.4  1999/09/21 19:00:35  armin
- * Extended FCON message with added CPN
- * can now be activated with Bit 1 of Reg 23.
- *
- * Revision 1.3  1999/08/22 20:26:12  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.2  1999/08/05 10:36:10  armin
- * Bugfix: kernel oops on getting revision.
- *
- * Revision 1.1  1999/07/31 12:59:50  armin
- * Added tty fax capabilities.
- *
- *
  */
 
 #undef ISDN_TTY_FAX_STAT_DEBUG
@@ -56,7 +33,7 @@
 #include "isdn_ttyfax.h"
 
 
-static char *isdn_tty_fax_revision = "$Revision: 1.6 $";
+static char *isdn_tty_fax_revision = "$Revision: 1.7 $";
 
 #define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ttyfax.h linux.18p17/drivers/isdn/isdn_ttyfax.h
--- linux.vanilla/drivers/isdn/isdn_ttyfax.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_ttyfax.h	Sat Sep 23 13:15:36 2000
@@ -1,4 +1,5 @@
-/* $Id: isdn_ttyfax.h,v 1.1 1999/07/31 12:59:51 armin Exp $
+/* $Id: isdn_ttyfax.h,v 1.2 2000/05/11 22:29:21 kai Exp $
+
  * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
  *
  * Copyright 1999   by Armin Schindler (mac@melware.de)
@@ -18,11 +19,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Log: isdn_ttyfax.h,v $
- * Revision 1.1  1999/07/31 12:59:51  armin
- * Added tty fax capabilities.
- *
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_v110.c linux.18p17/drivers/isdn/isdn_v110.c
--- linux.vanilla/drivers/isdn/isdn_v110.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_v110.c	Sat Sep 23 13:15:36 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.c,v 1.4 2000/03/16 16:34:12 kai Exp $
+/* $Id: isdn_v110.c,v 1.5 2000/05/11 22:29:21 kai Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -18,22 +18,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Log: isdn_v110.c,v $
- * Revision 1.4  2000/03/16 16:34:12  kai
- * some translation work
- *
- * there shouldn't be any German comments lurking around anymore ;-)
- *
- * Revision 1.3  1999/10/30 09:49:28  keil
- * Reinit of v110 structs
- *
- * Revision 1.2  1998/02/22 19:44:25  fritz
- * Bugfixes and improvements regarding V.110, V.110 now running.
- *
- * Revision 1.1  1998/02/20 17:32:09  fritz
- * First checkin (not yet completely functionable).
- *
  */
+
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
@@ -44,7 +30,7 @@
 
 #undef ISDN_V110_DEBUG
 
-char *isdn_v110_revision = "$Revision: 1.4 $";
+char *isdn_v110_revision = "$Revision: 1.5 $";
 
 #define V110_38400 255
 #define V110_19200  15
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_v110.h linux.18p17/drivers/isdn/isdn_v110.h
--- linux.vanilla/drivers/isdn/isdn_v110.h	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_v110.h	Sat Sep 23 13:15:36 2000
@@ -1,4 +1,4 @@
-/* $Id: isdn_v110.h,v 1.3 2000/03/16 16:34:12 kai Exp $
+/* $Id: isdn_v110.h,v 1.4 2000/05/11 22:29:21 kai Exp $
 
  * Linux ISDN subsystem, V.110 related functions (linklevel).
  *
@@ -17,18 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Log: isdn_v110.h,v $
- * Revision 1.3  2000/03/16 16:34:12  kai
- * some translation work
- *
- * there shouldn't be any German comments lurking around anymore ;-)
- *
- * Revision 1.2  1999/10/30 09:49:28  keil
- * Reinit of v110 structs
- *
- * Revision 1.1  1998/02/20 17:32:11  fritz
- * First checkin (not yet completely functionable).
  *
  */
 #ifndef _isdn_v110_h_
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_x25iface.c linux.18p17/drivers/isdn/isdn_x25iface.c
--- linux.vanilla/drivers/isdn/isdn_x25iface.c	Fri Apr 21 12:46:12 2000
+++ linux.18p17/drivers/isdn/isdn_x25iface.c	Sat Sep 23 13:15:36 2000
@@ -1,4 +1,23 @@
-/* $Id: isdn_x25iface.c,v 1.7 1999/08/22 20:26:13 calle Exp $
+/* $Id: isdn_x25iface.c,v 1.9 2000/05/16 20:52:10 keil Exp $
+
+ *
+ * Linux ISDN subsystem, X.25 related functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
  * stuff needed to support the Linux X.25 PLP code on top of devices that
  * can provide a lab_b service using the concap_proto mechanism.
  * This module supports a network interface wich provides lapb_sematics
@@ -8,33 +27,6 @@
  *
  * Only protocol specific stuff goes here. Device specific stuff
  * goes to another -- device related -- concap_proto support source file.
- *
- * $Log: isdn_x25iface.c,v $
- * Revision 1.7  1999/08/22 20:26:13  calle
- * backported changes from kernel 2.3.14:
- * - several #include "config.h" gone, others come.
- * - "struct device" changed to "struct net_device" in 2.3.14, added a
- *   define in isdn_compat.h for older kernel versions.
- *
- * Revision 1.6  1999/01/27 22:53:19  he
- * minor updates (spellings, jiffies wrap around in isdn_tty)
- *
- * Revision 1.5  1998/10/30 17:55:39  he
- * dialmode for x25iface and multulink ppp
- *
- * Revision 1.4  1998/06/17 19:51:00  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- * Revision 1.3  1998/02/20 17:25:20  fritz
- * Changes for recent kernels.
- *
- * Revision 1.2  1998/01/31 22:49:22  keil
- * correct comments
- *
- * Revision 1.1  1998/01/31 22:27:58  keil
- * New files from Henner Eisen for X.25 support
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_x25iface.h linux.18p17/drivers/isdn/isdn_x25iface.h
--- linux.vanilla/drivers/isdn/isdn_x25iface.h	Wed May  3 22:33:38 2000
+++ linux.18p17/drivers/isdn/isdn_x25iface.h	Thu Oct 19 01:52:31 2000
@@ -1,5 +1,23 @@
-/* $Id: isdn_x25iface.h,v 1.2 1998/01/31 22:49:23 keil Exp $
+/* $Id: isdn_x25iface.h,v 1.3 2000/05/11 22:29:21 kai Exp $
+
+ * header for Linux ISDN subsystem, x.25 related functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
  */
+
 #ifndef _LINUX_ISDN_X25IFACE_H
 #define _LINUX_ISDN_X25IFACE_H
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/misc/parport_pc.c linux.18p17/drivers/misc/parport_pc.c
--- linux.vanilla/drivers/misc/parport_pc.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/misc/parport_pc.c	Mon Oct  2 10:09:14 2000
@@ -617,7 +617,7 @@
 
 	sti();
 	irqs = probe_irq_on();
-		
+
 	parport_pc_write_econtrol(pb, 0x00);	/* Reset FIFO */
 	parport_pc_write_econtrol(pb, 0xd0);	/* TEST FIFO + nErrIntrEn */
 
@@ -902,11 +902,13 @@
 		  { { 0, -1 }, } },
 		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1,
 		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_BOCA_IOPPAR, 1,
+		  { { 0, -1 }, } },
 		{ PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_TK9902, 1,
 		  { { 0, 1 }, } },
-		{ PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, 1,
-		  { { 2, -1 }, } },
-		{ PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_4008A, 1,
+		{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954PP, 1,
+		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_12PCI840, 1,
 		  { { 0, 1 }, } },
 		{ 0, }
 	};
@@ -946,6 +948,7 @@
 		}
 	}
 
+#ifdef CONFIG_PCI
 	/* Look for parallel controllers that we don't know about. */
 	for (pcidev = pci_devices; pcidev; pcidev = pcidev->next) {
 		const int class_noprogif = pcidev->class & ~0xff;
@@ -966,6 +969,7 @@
 			"tim@cyberelk.demon.co.uk\n",
 			pcidev->vendor, pcidev->device);
 	}
+#endif
 
 	return count;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c505.c linux.18p17/drivers/net/3c505.c
--- linux.vanilla/drivers/net/3c505.c	Fri Apr 21 12:46:14 2000
+++ linux.18p17/drivers/net/3c505.c	Mon Sep 11 17:58:02 2000
@@ -129,15 +129,15 @@
 #define INVALID_PCB_MSG(len) \
 	printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
 
-static const char *search_msg = "%s: Looking for 3c505 adapter at address %#x...";
+static char *search_msg __initdata = "%s: Looking for 3c505 adapter at address %#x...";
 
-static const char *stilllooking_msg = "still looking...";
+static char *stilllooking_msg __initdata = "still looking...";
 
-static const char *found_msg = "found.\n";
+static char *found_msg __initdata = "found.\n";
 
-static const char *notfound_msg = "not found (reason = %d)\n";
+static char *notfound_msg __initdata = "not found (reason = %d)\n";
 
-static const char *couldnot_msg = "%s: 3c505 not found\n";
+static char *couldnot_msg __initdata = "%s: 3c505 not found\n";
 
 /*********************************************************
  *
@@ -179,7 +179,7 @@
  * Last element MUST BE 0!
  *****************************************************************/
 
-static const int addr_list[] __initdata = {0x300, 0x280, 0x310, 0};
+static int addr_list[] __initdata = {0x300, 0x280, 0x310, 0};
 
 /* Dma Memory related stuff */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c509.c linux.18p17/drivers/net/3c509.c
--- linux.vanilla/drivers/net/3c509.c	Fri Apr 21 12:46:14 2000
+++ linux.18p17/drivers/net/3c509.c	Mon Sep 11 17:58:02 2000
@@ -64,6 +64,7 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>	/* for udelay() */
+#include <linux/init.h>
 
 #include <asm/spinlock.h>
 #include <asm/bitops.h>
@@ -156,7 +157,7 @@
 	int id;
 };
 
-struct el3_mca_adapters_struct el3_mca_adapters[] = {
+static struct el3_mca_adapters_struct el3_mca_adapters[] __initdata = {
 	{ "3Com 3c529 EtherLink III (10base2)", 0x627c },
 	{ "3Com 3c529 EtherLink III (10baseT)", 0x627d },
 	{ "3Com 3c529 EtherLink III (test mode)", 0x62db },
@@ -166,7 +167,7 @@
 };
 #endif
 
-int el3_probe(struct device *dev)
+int __init el3_probe(struct device *dev)
 {
 	short lrs_state = 0xff, i;
 	int ioaddr, irq, if_port;
@@ -396,7 +397,7 @@
 /* Read a word from the EEPROM using the regular EEPROM access register.
    Assume that we are in register window zero.
  */
-static ushort read_eeprom(int ioaddr, int index)
+static ushort __init read_eeprom(int ioaddr, int index)
 {
 	outw(EEPROM_READ + index, ioaddr + 10);
 	/* Pause for at least 162 us. for the read to take place. */
@@ -405,7 +406,7 @@
 }
 
 /* Read a word from the EEPROM when in the ISA ID probe state. */
-static ushort id_read_eeprom(int index)
+static ushort __init id_read_eeprom(int index)
 {
 	int bit, word = 0;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c515.c linux.18p17/drivers/net/3c515.c
--- linux.vanilla/drivers/net/3c515.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/net/3c515.c	Mon Sep 11 17:58:02 2000
@@ -62,6 +62,7 @@
 
 #define NEW_MULTICAST
 #include <linux/delay.h>
+#include <linux/init.h>
 
 /* Kernel version compatibility functions. */
 #define RUN_AT(x) (jiffies + (x))
@@ -399,7 +400,7 @@
 }
 
 #else
-int tc515_probe(struct device *dev)
+int __init tc515_probe(struct device *dev)
 {
 	int cards_found = 0;
 
@@ -412,7 +413,7 @@
 }
 #endif  /* not MODULE */
 
-static int vortex_scan(struct device *dev)
+static int __init vortex_scan(struct device *dev)
 {
 	int cards_found = 0;
 	static int ioaddr = 0x100;
@@ -452,7 +453,7 @@
 	return cards_found;
 }
 
-static struct device *vortex_found_device(struct device *dev, int ioaddr,
+static struct device * __init vortex_found_device(struct device *dev, int ioaddr,
 										  int irq, int product_index,
 										  int options)
 {
@@ -517,7 +518,7 @@
 	return dev;
 }
 
-static int vortex_probe1(struct device *dev)
+static int __init vortex_probe1(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c523.c linux.18p17/drivers/net/3c523.c
--- linux.vanilla/drivers/net/3c523.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/3c523.c	Sat Sep 30 18:35:22 2000
@@ -161,15 +161,10 @@
 
 /**************************************************************************/
 
-#define DELAY(x) {int i=jiffies; \
-                  if(loops_per_sec == 1) \
-                     while(time_after(i+(x), jiffies)); \
-                  else \
-                     __delay((loops_per_sec>>5)*x); \
-                 }
+#define DELAY(x) { mdelay(32 * x); }
 
 /* a much shorter delay: */
-#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+#define DELAY_16(); { udelay(16) ; }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() { int i; \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c527.c linux.18p17/drivers/net/3c527.c
--- linux.vanilla/drivers/net/3c527.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/3c527.c	Mon Sep 11 17:58:02 2000
@@ -124,7 +124,7 @@
 	char		*name;
 };
 
-const struct mca_adapters_t mc32_adapters[] = {
+static struct mca_adapters_t mc32_adapters[] __initdata = {
 	{ 0x0041, "3COM EtherLink MC/32" },
 	{ 0x8EF5, "IBM High Performance Lan Adapter" },
 	{ 0x0000, NULL }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/8139too.c linux.18p17/drivers/net/8139too.c
--- linux.vanilla/drivers/net/8139too.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/8139too.c	Sun Oct  1 11:24:15 2000
@@ -0,0 +1,2255 @@
+/*
+
+	8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
+
+ 	Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
+ 
+ 	Much code comes from Donald Becker's rtl8139.c driver,
+ 	versions 1.11 and older.  This driver was originally based
+ 	on rtl8139.c version 1.07.  Header of rtl8139.c version 1.11:
+ 
+ 	-----<snip>-----
+ 
+         	Written 1997-2000 by Donald Becker.
+ 		This software may be used and distributed according to the
+ 		terms of the GNU General Public License (GPL), incorporated
+ 		herein by reference.  Drivers based on or derived from this
+ 		code fall under the GPL and must retain the authorship,
+ 		copyright and license notice.  This file is not a complete
+ 		program and may only be used when the entire operating
+ 		system is licensed under the GPL.
+ 
+ 		This driver is for boards based on the RTL8129 and RTL8139
+ 		PCI ethernet chips.
+ 
+ 		The author may be reached as becker@scyld.com, or C/O Scyld
+ 		Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+ 		MD 21403
+ 
+ 		Support and updates available at
+ 		http://www.scyld.com/network/rtl8139.html
+ 
+ 		Twister-tuning table provided by Kinston
+ 		<shangh@realtek.com.tw>.
+ 
+ 	-----<snip>-----
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	Contributors:
+
+		Donald Becker - he wrote the original driver, kudos to him!
+		(but please don't e-mail him for support, this isn't his driver)
+
+		Tigran Aivazian - bug fixes, skbuff free cleanup
+
+		Martin Mares - suggestions for PCI cleanup
+		
+		David S. Miller - PCI DMA and softnet updates
+
+		Ernst Gill - fixes ported from BSD driver
+
+		Daniel Kobras - identified specific locations of
+			posted MMIO write bugginess
+
+		Gerard Sharp - bug fix, testing and feedback
+		
+		David Ford - Rx ring wrap fix
+		
+		Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+		to find and fix a crucial bug on older chipsets.
+		
+		Donald Becker/Chris Butterworth/Marcus Westergren -
+		Noticed various Rx packet size-related buglets.
+
+		Santiago Garcia Mantinan - testing and feedback
+
+		Jens David - 2.2.x kernel backports
+
+		Martin Dennett - incredibly helpful insight on undocumented
+		features of the 8139 chips
+
+	Submitting bug reports:
+
+		"rtl8139-diag -mmmaaavvveefN" output
+		enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
+
+		See 8139too.txt for more details.
+
+-----------------------------------------------------------------------------
+
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the RealTek RTL8139 series, the RealTek
+Fast Ethernet controllers for PCI and CardBus.  This chip is used on many
+low-end boards, sometimes with its markings changed.
+
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS will assign the
+PCI INTA signal to a (preferably otherwise unused) system IRQ line.
+
+III. Driver operation
+
+IIIa. Rx Ring buffers
+
+The receive unit uses a single linear ring buffer rather than the more
+common (and more efficient) descriptor-based architecture.  Incoming frames
+are sequentially stored into the Rx region, and the host copies them into
+skbuffs.
+
+Comment: While it is theoretically possible to process many frames in place,
+any delay in Rx processing would cause us to drop frames.  More importantly,
+the Linux protocol stack is not designed to operate in this manner.
+
+IIIb. Tx operation
+
+The RTL8139 uses a fixed set of four Tx descriptors in register space.
+In a stunningly bad design choice, Tx frames must be 32 bit aligned.  Linux
+aligns the IP header on word boundaries, and 14 byte ethernet header means
+that almost all frames will need to be copied to an alignment buffer.
+
+IVb. References
+
+http://www.realtek.com.tw/cn/cn.html
+http://www.scyld.com/expert/NWay.html
+
+IVc. Errata
+
+1) The RTL-8139 has a serious problem with motherboards which do
+posted MMIO writes to PCI space.  This driver works around the
+problem by having an MMIO  register write be immediately followed by
+an MMIO register read.
+
+2) The RTL-8129 is only supported in Donald Becker's rtl8139 driver.
+
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+
+#define RTL8139_VERSION "0.9.10-2.2"
+#define RTL8139_MODULE_NAME "8139too"
+#define RTL8139_DRIVER_NAME   RTL8139_MODULE_NAME " FastEthernet driver " RTL8139_VERSION
+#define RTL8139_AUTHOR "Jeff Garzik <jgarzik@mandrakesoft.com>"
+#define PFX RTL8139_MODULE_NAME ": "
+
+
+/* define to 1 to enable PIO instead of MMIO */
+#undef USE_IO_OPS
+
+/* define to 1 to enable copious debugging info */
+#undef RTL8139_DEBUG
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
+
+#ifdef RTL8139_DEBUG
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#ifdef RTL8139_NDEBUG
+#define assert(expr) do {} while (0)
+#else
+#define assert(expr) \
+        if(!(expr)) {					\
+        printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
+        #expr,__FILE__,__FUNCTION__,__LINE__);		\
+        }
+#endif
+
+#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)
+
+static inline void netif_start_queue(struct device *dev)
+{
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+}
+
+#define netif_queue_stopped(dev)	dev->tbusy
+#define netif_running(dev)		dev->start
+#define dma_addr_t unsigned long
+
+struct resource {
+	const char *name;
+	unsigned long start, end;
+	unsigned long flags;
+	struct resource *parent, *sibling, *child;
+};
+
+#define IORESOURCE_BITS         0x000000ff      /* Bus-specific bits */
+#define IORESOURCE_IO           0x00000100      /* Resource type */
+#define IORESOURCE_MEM          0x00000200
+#define IORESOURCE_IRQ          0x00000400
+#define IORESOURCE_DMA          0x00000800
+#define IORESOURCE_PREFETCH     0x00001000      /* No side effects */
+#define IORESOURCE_READONLY     0x00002000
+#define IORESOURCE_CACHEABLE    0x00004000
+#define IORESOURCE_RANGELENGTH  0x00008000
+#define IORESOURCE_SHADOWABLE   0x00010000
+#define IORESOURCE_UNSET        0x20000000
+#define IORESOURCE_AUTO         0x40000000
+#define IORESOURCE_BUSY         0x80000000
+
+#define PCI_USES_IO		1
+#define PCI_USES_MEM		2
+#define PCI_USES_MASTER 	4
+
+static inline unsigned int pci_calc_resource_flags(unsigned int flags)
+{
+	if (flags & PCI_BASE_ADDRESS_SPACE_IO)
+		return IORESOURCE_IO;
+
+	if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
+		return IORESOURCE_MEM | IORESOURCE_PREFETCH;
+
+	return IORESOURCE_MEM;
+}
+
+static void rtl8139_read_pci_resources(struct pci_dev *dev, struct resource *res)
+{
+	unsigned int pos, reg, next;
+	u32 l, sz, tmp;
+	u16 cmd;
+
+	/* Disable IO and memory while we fiddle */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	pci_write_config_word(dev, PCI_COMMAND, tmp);
+	
+	for (pos=0; pos<4; pos = next, res++) {
+		next = pos+1;
+		reg = PCI_BASE_ADDRESS_0 + (pos << 2);
+		pci_read_config_dword(dev, reg, &l);
+		pci_write_config_dword(dev, reg, ~0);
+		pci_read_config_dword(dev, reg, &sz);
+		pci_write_config_dword(dev, reg, l);
+		if (!sz || sz == 0xffffffff)
+			continue;
+		if (l == 0xffffffff)
+			l = 0;
+		if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+			res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
+			sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
+		} else {
+			res->start = l & PCI_BASE_ADDRESS_IO_MASK;
+			sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
+		}
+		res->end = res->start + (unsigned long) sz;
+		res->flags |= (l & 0xf) | pci_calc_resource_flags(l);
+	}
+}
+
+#define pci_resource_start(dev_priv,bar)	((dev_priv)->rtl8139_pci_resource[(bar)].start)
+#define pci_resource_end(dev_priv,bar)	((dev_priv)->rtl8139_pci_resource[(bar)].end)
+#define pci_resource_flags(dev_priv,bar)	((dev_priv)->rtl8139_pci_resource[(bar)].flags)
+#define pci_resource_len(dev_priv,bar) \
+	((pci_resource_start((dev_priv),(bar)) == 0 &&	\
+	  pci_resource_end((dev_priv),(bar)) ==		\
+	  pci_resource_start((dev_priv),(bar))) ? 0 :	\
+	  						\
+	 (pci_resource_end((dev_priv),(bar)) -		\
+	  pci_resource_start((dev_priv),(bar)) + 1))
+
+#define arraysize(x)            (sizeof(x)/sizeof(*(x)))
+
+
+/* A few user-configurable values. */
+/* media options */
+static int media[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+   The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
+static int multicast_filter_limit = 32;
+
+/* Size of the in-memory receive ring. */
+#define RX_BUF_LEN_IDX	2	/* 0==8K, 1==16K, 2==32K, 3==64K */
+#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC	4
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE	1536
+#define TX_BUF_TOT_LEN	(TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+   Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256	/* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH	6	/* Rx buffer level before first PCI xfer.  */
+#define RX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */
+
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (HZ/2)
+
+
+enum {
+	HAS_CHIP_XCVR = 0x020000,
+	HAS_LNK_CHNG = 0x040000,
+};
+
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 256
+
+#define RTL8139_CAPS	HAS_CHIP_XCVR|HAS_LNK_CHNG
+
+typedef enum {
+	RTL8139 = 0,
+	RTL8139_CB,
+	SMC1211TX,
+	DELTA8139,
+	ADDTRON8139,
+} board_t;
+
+
+/* indexed by board_t, above */
+static struct {
+	const char *name;
+} board_info[] = {
+	{ "RealTek RTL8139 Fast Ethernet" },
+	{ "RealTek RTL8139B PCI/CardBus" },
+	{ "SMC1211TX EZCard 10/100 (RealTek RTL8139)" },
+	{ "Delta Electronics 8139 10/100BaseTX" },
+	{ "Addtron Technolgy 8139 10/100BaseTX" },
+};
+
+struct pci_device_id {
+	unsigned int vendor, device;
+	board_t board;
+};
+
+static struct pci_device_id rtl8139_pci_tbl[] = {
+	{0x10ec, 0x8139, RTL8139 },
+	{0x10ec, 0x8138, RTL8139_CB },
+	{0x1113, 0x1211, SMC1211TX },
+	{0x1500, 0x1360, DELTA8139 },
+	{0x4033, 0x1360, ADDTRON8139 },
+	{0,}
+};
+
+/* The rest of these values should never change. */
+
+static struct device *rtl8139_device_tab[16];
+static int rtl8139_device_count = 0;
+static int rtl8139_initialized = 0;
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+	MAC0 = 0,		/* Ethernet hardware address. */
+	MAR0 = 8,		/* Multicast filter. */
+	TxStatus0 = 0x10,	/* Transmit status (Four 32bit registers). */
+	TxAddr0 = 0x20,		/* Tx descriptors (also four 32bit). */
+	RxBuf = 0x30,
+	RxEarlyCnt = 0x34,
+	RxEarlyStatus = 0x36,
+	ChipCmd = 0x37,
+	RxBufPtr = 0x38,
+	RxBufAddr = 0x3A,
+	IntrMask = 0x3C,
+	IntrStatus = 0x3E,
+	TxConfig = 0x40,
+	ChipVersion = 0x43,
+	RxConfig = 0x44,
+	Timer = 0x48,		/* A general-purpose counter. */
+	RxMissed = 0x4C,	/* 24 bits valid, write clears. */
+	Cfg9346 = 0x50,
+	Config0 = 0x51,
+	Config1 = 0x52,
+	FlashReg = 0x54,
+	MediaStatus = 0x58,
+	Config3 = 0x59,
+	Config4 = 0x5A,		/* absent on RTL-8139A */
+	HltClk = 0x5B,
+	MultiIntr = 0x5C,
+	TxSummary = 0x60,
+	BasicModeCtrl = 0x62,
+	BasicModeStatus = 0x64,
+	NWayAdvert = 0x66,
+	NWayLPAR = 0x68,
+	NWayExpansion = 0x6A,
+	/* Undocumented registers, but required for proper operation. */
+	FIFOTMS = 0x70,		/* FIFO Control and test. */
+	CSCR = 0x74,		/* Chip Status and Configuration Register. */
+	PARA78 = 0x78,
+	PARA7c = 0x7c,		/* Magic transceiver parameter register. */
+	Config5 = 0xD8,		/* absent on RTL-8139A */
+};
+
+enum ClearBitMasks {
+	MultiIntrClear = 0xF000,
+	ChipCmdClear = 0xE2,
+	Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+	CmdReset = 0x10,
+	CmdRxEnb = 0x08,
+	CmdTxEnb = 0x04,
+	RxBufEmpty = 0x01,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+	PCIErr = 0x8000,
+	PCSTimeout = 0x4000,
+	RxFIFOOver = 0x40,
+	RxUnderrun = 0x20,
+	RxOverflow = 0x10,
+	TxErr = 0x08,
+	TxOK = 0x04,
+	RxErr = 0x02,
+	RxOK = 0x01,
+};
+enum TxStatusBits {
+	TxHostOwns = 0x2000,
+	TxUnderrun = 0x4000,
+	TxStatOK = 0x8000,
+	TxOutOfWindow = 0x20000000,
+	TxAborted = 0x40000000,
+	TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+	RxMulticast = 0x8000,
+	RxPhysical = 0x4000,
+	RxBroadcast = 0x2000,
+	RxBadSymbol = 0x0020,
+	RxRunt = 0x0010,
+	RxTooLong = 0x0008,
+	RxCRCErr = 0x0004,
+	RxBadAlign = 0x0002,
+	RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+	AcceptErr = 0x20,
+	AcceptRunt = 0x10,
+	AcceptBroadcast = 0x08,
+	AcceptMulticast = 0x04,
+	AcceptMyPhys = 0x02,
+	AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+	TxIFG1 = (1 << 25),	/* Interframe Gap Time */
+	TxIFG0 = (1 << 24),	/* Enabling these bits violates IEEE 802.3 */
+	TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+	TxCRC = (1 << 16),	/* DISABLE appending CRC to end of Tx packets */
+	TxClearAbt = (1 << 0),	/* Clear abort (WO) */
+	TxDMAShift = 8,		/* DMA burst value (0-7) is shift this many bits */
+
+	TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+/* Bits in Config1 */
+enum Config1Bits {
+	Cfg1_PM_Enable = 0x01,
+	Cfg1_VPD_Enable = 0x02,
+	Cfg1_PIO = 0x04,
+	Cfg1_MMIO = 0x08,
+	Cfg1_LWAKE = 0x10,
+	Cfg1_Driver_Load = 0x20,
+	Cfg1_LED0 = 0x40,
+	Cfg1_LED1 = 0x80,
+};
+
+enum RxConfigBits {
+	/* Early Rx threshold, none or X/16 */
+	RxCfgEarlyRxNone = 0,
+	RxCfgEarlyRxShift = 24,
+
+	/* rx fifo threshold */
+	RxCfgFIFOShift = 13,
+	RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+	/* Max DMA burst */
+	RxCfgDMAShift = 8,
+	RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+	/* rx ring buffer length */
+	RxCfgRcv8K = 0,
+	RxCfgRcv16K = (1 << 11),
+	RxCfgRcv32K = (1 << 12),
+	RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+	/* Disable packet wrap at end of Rx buffer */
+	RxNoWrap = (1 << 7),
+};
+
+
+/* Twister tuning parameters from RealTek.
+   Completely undocumented, but required to tune bad links. */
+enum CSCRBits {
+	CSCR_LinkOKBit = 0x0400,
+	CSCR_LinkChangeBit = 0x0800,
+	CSCR_LinkStatusBits = 0x0f000,
+	CSCR_LinkDownOffCmd = 0x003c0,
+	CSCR_LinkDownCmd = 0x0f3c0,
+};
+
+
+enum Cfg9346Bits {
+	Cfg9346_Lock = 0x00,
+	Cfg9346_Unlock = 0xC0,
+};
+
+
+#define PARA78_default	0x78fa8388
+#define PARA7c_default	0xcb38de43	/* param[0][3] */
+#define PARA7c_xxx		0xcb38de43
+static const unsigned long param[4][4] = {
+	{0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+	{0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+};
+
+struct ring_info {
+	struct sk_buff *skb;
+};
+
+
+typedef enum {
+	CH_8139 = 0,
+	CH_8139_K,
+	CH_8139A,
+	CH_8139B,
+	CH_8130,
+	CH_8139C,
+} chip_t;
+
+
+/* directly indexed by chip_t, above */
+const static struct {
+	const char *name;
+	u8 version; /* from RTL8139C docs */
+	u32 RxConfigMask; /* should clear the bits supported by this chip */
+} rtl_chip_info[] = {
+	{ "RTL-8139",
+	  0x40,
+	  0xf0fe0040, /* XXX copied from RTL8139A, verify */
+	},
+	
+	{ "RTL-8139 rev K",
+	  0x60,
+	  0xf0fe0040,
+	},
+	
+	{ "RTL-8139A",
+	  0x70,
+	  0xf0fe0040,
+	},
+	
+	{ "RTL-8139B",
+	  0x78,
+	  0xf0fc0040
+	},
+
+	{ "RTL-8130",
+	  0x7C,
+	  0xf0fe0040, /* XXX copied from RTL8139A, verify */
+	},
+
+	{ "RTL-8139C",
+	  0x74,
+	  0xf0fc0040, /* XXX copied from RTL8139B, verify */
+	},
+
+};
+
+
+struct rtl8139_private {
+	board_t board;
+	void *mmio_addr;
+	int drv_flags;
+	struct pci_dev *pci_dev;
+	struct resource rtl8139_pci_resource[4];
+	struct net_device_stats stats;
+	struct timer_list timer;	/* Media selection timer. */
+	unsigned char *rx_ring;
+	unsigned int cur_rx;	/* Index into the Rx buffer of next Rx pkt. */
+	unsigned int tx_flag;
+	atomic_t cur_tx;
+	atomic_t dirty_tx;
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct ring_info tx_info[NUM_TX_DESC];
+	unsigned char *tx_buf[NUM_TX_DESC];	/* Tx bounce buffers */
+	unsigned char *tx_bufs;	/* Tx bounce buffer region. */
+	dma_addr_t rx_ring_dma;
+	dma_addr_t tx_bufs_dma;
+	char phys[4];		/* MII device addresses. */
+	char twistie, twist_row, twist_col;	/* Twister tune state. */
+	unsigned int full_duplex:1;	/* Full-duplex operation requested. */
+	unsigned int duplex_lock:1;
+	unsigned int default_port:4;	/* Last dev->if_port value. */
+	unsigned int media2:4;	/* Secondary monitored media port. */
+	unsigned int medialock:1;	/* Don't sense media type. */
+	unsigned int mediasense:1;	/* Media sensing in progress. */
+	spinlock_t lock;
+	chip_t chipset;
+};
+
+
+
+static int read_eeprom (void *ioaddr, int location, int addr_len);
+static int rtl8139_open (struct device *dev);
+static int mdio_read (struct device *dev, int phy_id, int location);
+static void mdio_write (struct device *dev, int phy_id, int location,
+			int val);
+static void rtl8139_timer (unsigned long data);
+static void rtl8139_tx_timeout (struct device *dev);
+static void rtl8139_init_ring (struct device *dev);
+static int rtl8139_start_xmit (struct sk_buff *skb,
+			       struct device *dev);
+static void rtl8139_interrupt (int irq, void *dev_instance,
+			       struct pt_regs *regs);
+static int rtl8139_close (struct device *dev);
+static int mii_ioctl (struct device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8139_get_stats (struct device *dev);
+static inline u32 ether_crc (int length, unsigned char *data);
+static void rtl8139_set_rx_mode (struct device *dev);
+static void rtl8139_hw_start (struct device *dev);
+
+
+#ifdef USE_IO_OPS
+
+#define RTL_R8(reg)		inb (((unsigned long)ioaddr) + (reg))
+#define RTL_R16(reg)		inw (((unsigned long)ioaddr) + (reg))
+#define RTL_R32(reg)		inl (((unsigned long)ioaddr) + (reg))
+#define RTL_W8(reg, val8)	outb ((val8), ((unsigned long)ioaddr) + (reg))
+#define RTL_W16(reg, val16)	outw ((val16), ((unsigned long)ioaddr) + (reg))
+#define RTL_W32(reg, val32)	outl ((val32), ((unsigned long)ioaddr) + (reg))
+#define RTL_W8_F		RTL_W8
+#define RTL_W16_F		RTL_W16
+#define RTL_W32_F		RTL_W32
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb(addr) inb((unsigned long)(addr))
+#define readw(addr) inw((unsigned long)(addr))
+#define readl(addr) inl((unsigned long)(addr))
+#define writeb(val,addr) outb((val),(unsigned long)(addr))
+#define writew(val,addr) outw((val),(unsigned long)(addr))
+#define writel(val,addr) outl((val),(unsigned long)(addr))
+
+#else
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8)	do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16)	do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32)	do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0)
+
+
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8)	writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16)	writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32)	writel ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8		RTL_W8_F
+#define RTL_W16		RTL_W16_F
+#define RTL_W32		RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg)		readb (ioaddr + (reg))
+#define RTL_R16(reg)		readw (ioaddr + (reg))
+#define RTL_R32(reg)		readl (ioaddr + (reg))
+
+#endif /* USE_IO_OPS */
+
+
+static const u16 rtl8139_intr_mask = 
+	PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+	TxErr | TxOK | RxErr | RxOK;
+
+static const unsigned int rtl8139_rx_config =
+	  RxCfgEarlyRxNone | RxCfgRcv32K | RxNoWrap |
+	  (RX_FIFO_THRESH << RxCfgFIFOShift) |
+	  (RX_DMA_BURST << RxCfgDMAShift);
+
+
+static int rtl8139_init_board (struct pci_dev *pdev, struct device **dev_out,
+					 void **ioaddr_out)
+{
+	void *ioaddr = NULL;
+	struct device *dev;
+	struct rtl8139_private *tp;
+	u8 tmp8;
+	int rc, i;
+	u32 pio_start, pio_end, pio_flags, pio_len;
+	unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+	u32 tmp;
+	u16 pci_command, new_command;
+
+	DPRINTK ("ENTER\n");
+
+	assert (pdev != NULL);
+	assert (ioaddr_out != NULL);
+
+	*ioaddr_out = NULL;
+	*dev_out = NULL;
+
+	/* dev zeroed in init_etherdev */
+	dev = init_etherdev (NULL, sizeof (*tp));
+	if (dev == NULL) {
+		printk (KERN_ERR PFX "unable to alloc new ethernet\n");
+		DPRINTK ("EXIT, returning -ENOMEM\n");
+		return -ENOMEM;
+	}
+	tp = dev->priv;
+
+	rtl8139_read_pci_resources(pdev, &tp->rtl8139_pci_resource[0]);
+
+	pio_start = pci_resource_start (tp, 0);
+	pio_end = pci_resource_end (tp, 0);
+	pio_flags = pci_resource_flags (tp, 0);
+	pio_len = pci_resource_len (tp, 0);
+
+	mmio_start = pci_resource_start (tp, 1);
+	mmio_end = pci_resource_end (tp, 1);
+	mmio_flags = pci_resource_flags (tp, 1);
+	mmio_len = pci_resource_len (tp, 1);
+
+	/* set this immediately, we need to know before
+	 * we talk to the chip directly */
+	DPRINTK("PIO region size == 0x%02X\n", pio_len);
+	DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
+	if (pio_len == RTL8139B_IO_SIZE)
+		tp->chipset = CH_8139B;
+
+	/* make sure PCI base addr 0 is PIO */
+	if (!(pio_flags & IORESOURCE_IO)) {
+		printk (KERN_ERR PFX "region #0 not a PIO resource, aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+	
+	/* make sure PCI base addr 1 is MMIO */
+	if (!(mmio_flags & IORESOURCE_MEM)) {
+		printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+	
+	/* check for weird/broken PCI region reporting */
+	if ((pio_len != mmio_len) ||
+	    (pio_len < RTL_MIN_IO_SIZE) ||
+	    (mmio_len < RTL_MIN_IO_SIZE)) {
+		printk (KERN_ERR PFX "Invalid PCI region size(s), aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	/* make sure our PIO region in PCI space is available */
+	if (check_region(pio_start, pio_len)) {
+		printk (KERN_ERR PFX "no I/O resource available, aborting\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+	request_region(pio_start, pio_len, dev->name);
+	
+	pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+	ioaddr = (void *) pio_start;
+#else
+	/* ioremap MMIO region */
+	ioaddr = ioremap_nocache(mmio_start, mmio_len);
+	
+	if (ioaddr == NULL) {
+		printk (KERN_ERR PFX "cannot remap MMIO, aborting\n");
+		rc = -EIO;
+		goto err_out_free_mmio;
+	}
+#endif /* USE_IO_OPS */
+
+	/* Activate the card: fix for brain-damaged Win98 BIOSes. */
+	pci_read_config_word(pdev,  PCI_COMMAND, &pci_command);
+	new_command = pci_command | PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER;
+	if (pci_command != new_command)
+		pci_write_config_word(pdev, PCI_COMMAND, new_command);
+	
+	/* Soft reset the chip. */
+	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset);
+
+	/* Check that the chip has finished the reset. */
+	for (i = 10000; i > 0; i--)
+		if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+			break;
+		else
+			udelay (10);
+
+	/* Bring the chip out of low-power mode. */
+	if (tp->chipset == CH_8139B) {
+		RTL_W8 (Config1, RTL_R8 (Config1) & ~(1<<4));
+		RTL_W8 (Config4, RTL_R8 (Config4) & ~(1<<2));
+	} else {
+		/* handle RTL8139A and RTL8139 cases */
+		/* XXX from becker driver. is this right?? */
+		RTL_W8 (Config1, 0);
+	}
+
+#ifndef USE_IO_OPS
+	/* sanity checks -- ensure PIO and MMIO registers agree */
+	assert (inb (pio_start+Config0) == readb (ioaddr+Config0));
+	assert (inb (pio_start+Config1) == readb (ioaddr+Config1));
+	assert (inb (pio_start+TxConfig) == readb (ioaddr+TxConfig));
+	assert (inb (pio_start+RxConfig) == readb (ioaddr+RxConfig));
+#endif /* !USE_IO_OPS */
+	
+	/* make sure chip thinks PIO and MMIO are enabled */
+	tmp8 = RTL_R8 (Config1);
+	if ((tmp8 & Cfg1_PIO) == 0) {
+		printk (KERN_ERR PFX "PIO not enabled, Cfg1=%02X, aborting\n", tmp8);
+		rc = -EIO;
+		goto err_out_iounmap;
+	}
+	if ((tmp8 & Cfg1_MMIO) == 0) {
+		printk (KERN_ERR PFX "MMIO not enabled, Cfg1=%02X, aborting\n", tmp8);
+		rc = -EIO;
+		goto err_out_iounmap;
+	}
+	
+	/* identify chip attached to board */
+	tmp = RTL_R8 (ChipVersion);
+	for (i = arraysize (rtl_chip_info) - 1; i >= 0; i--)
+		if (tmp == rtl_chip_info[i].version) {
+			tp->chipset = i;
+			goto match;
+		}
+
+	/* if unknown chip, assume array element #0, original RTL-8139 in this case */
+	printk (KERN_DEBUG PFX "PCI device %08lX: unknown chip version, assuming RTL-8139\n",
+		(unsigned long) pdev);
+	printk (KERN_DEBUG PFX "PCI device %08lX: TxConfig = 0x%x\n", (unsigned long) pdev, RTL_R32 (TxConfig));
+	tp->chipset = 0;
+
+match:
+	DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+		tmp,
+		tp->chipset,
+		rtl_chip_info[tp->chipset].name);
+	
+	DPRINTK ("EXIT, returning 0\n");
+	*ioaddr_out = ioaddr;
+	*dev_out = dev;
+	return 0;	
+
+err_out_iounmap:
+	assert (ioaddr > 0);
+#ifndef USE_IO_OPS
+	iounmap (ioaddr);
+#endif /* !USE_IO_OPS */
+err_out_free_mmio:
+	release_region (pio_start, pio_len);
+err_out:
+	unregister_netdev (dev);
+	kfree (dev);
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+static int rtl8139_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct device *dev = NULL;
+	struct rtl8139_private *tp;
+	int i, addr_len, option;
+	void *ioaddr = NULL;
+	static int board_idx = -1;
+	u8 tmp;
+
+	DPRINTK ("ENTER\n");
+	
+	assert (pdev != NULL);
+	assert (ent != NULL);
+
+	board_idx++;
+
+	i = rtl8139_init_board (pdev, &dev, &ioaddr);
+	if (i < 0) {
+		DPRINTK ("EXIT, returning %d\n", i);
+		return i;
+	}
+	
+	tp = dev->priv;
+	
+	assert (ioaddr != NULL);
+	assert (dev != NULL);
+	assert (tp != NULL);
+
+	addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+	for (i = 0; i < 3; i++)
+		((u16 *) (dev->dev_addr))[i] =
+		    le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
+
+	/* The Rtl8139-specific entries in the device structure. */
+	dev->open = rtl8139_open;
+	dev->hard_start_xmit = rtl8139_start_xmit;
+	dev->stop = rtl8139_close;
+	dev->get_stats = rtl8139_get_stats;
+	dev->set_multicast_list = rtl8139_set_rx_mode;
+	dev->do_ioctl = mii_ioctl;
+
+	dev->irq = pdev->irq;
+	dev->base_addr = (unsigned long) ioaddr;
+
+	/* dev->priv/tp zeroed and aligned in init_etherdev */
+	tp = dev->priv;
+
+	/* note: tp->chipset set in rtl8139_init_board */
+	tp->drv_flags = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+			PCI_COMMAND_MASTER | RTL8139_CAPS;
+	tp->pci_dev = pdev;
+	tp->board = ent->board;
+	tp->mmio_addr = ioaddr;
+	tp->lock = SPIN_LOCK_UNLOCKED;
+
+	tp->phys[0] = 32;
+
+	if (rtl8139_device_count == 0) {
+		printk (KERN_INFO "%s: " RTL8139_DRIVER_NAME " " RTL8139_AUTHOR "\n", dev->name);
+		printk (KERN_INFO "%s: Linux-2.2 bug reports to Jens David <dg1kjd@afthd.tu-darmstadt.de>\n", dev->name);
+	}
+	
+	printk (KERN_INFO "%s: %s board found at 0x%lx, IRQ %d\n",
+		dev->name, board_info[ent->board].name,
+		dev->base_addr, dev->irq);
+
+	printk (KERN_INFO "%s: Chip is '%s' - MAC address '%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x'.\n",
+		dev->name,
+		rtl_chip_info[tp->chipset].name,
+		dev->dev_addr[0], dev->dev_addr[1],
+		dev->dev_addr[2], dev->dev_addr[3],
+		dev->dev_addr[4], dev->dev_addr[5]);
+
+	/* Put the chip into low-power mode. */
+	RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+
+	tmp = RTL_R8 (Config1) & Config1Clear;
+	tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */
+	RTL_W8_F (Config1, tmp);
+
+	RTL_W8_F (HltClk, 'H');	/* 'R' would leave the clock running. */
+
+	/* The lower four bits are the media type. */
+	option = (board_idx > 7) ? 0 : media[board_idx];
+	if (option > 0) {
+		tp->full_duplex = (option & 0x200) ? 1 : 0;
+		tp->default_port = option & 15;
+		if (tp->default_port)
+			tp->medialock = 1;
+	}
+
+	if (tp->full_duplex) {
+		printk (KERN_INFO
+			"%s: Media type forced to Full Duplex.\n",
+			dev->name);
+		mdio_write (dev, tp->phys[0], 4, 0x141);
+		tp->duplex_lock = 1;
+	}
+
+	rtl8139_device_tab[rtl8139_device_count++] = dev;
+	
+	DPRINTK ("EXIT - returning 0\n");
+	return 0;
+}
+
+static void rtl8139_remove_one (struct device *dev)
+{
+	struct pci_dev *pdev;
+	struct rtl8139_private *np;
+
+	DPRINTK ("ENTER\n");
+
+	assert (dev != NULL);
+
+	np = (struct rtl8139_private *) (dev->priv);
+	assert (np != NULL);
+
+	pdev = np->pci_dev;
+	assert (pdev != NULL);
+
+	unregister_netdev (dev);
+
+#ifndef USE_IO_OPS
+	iounmap (np->mmio_addr);
+#endif /* !USE_IO_OPS */
+
+	release_region (pci_resource_start (np, 0),
+			pci_resource_len (np, 0));
+#ifndef RTL8139_NDEBUG
+	/* poison memory before freeing */
+	memset (dev, 0xBC,
+		sizeof (struct device) +
+		sizeof (struct rtl8139_private));
+#endif /* RTL8139_NDEBUG */
+
+	kfree (dev);
+
+	DPRINTK ("EXIT\n");
+}
+
+/* Serial EEPROM section. */
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */
+#define EE_CS			0x08	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x02	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x00
+#define EE_WRITE_1		0x02
+#define EE_DATA_READ	0x01	/* EEPROM chip data out. */
+#define EE_ENB			(0x80 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay()	readl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5)
+#define EE_READ_CMD		(6)
+#define EE_ERASE_CMD	(7)
+
+static int read_eeprom (void *ioaddr, int location, int addr_len)
+{
+	int i;
+	unsigned retval = 0;
+	void *ee_addr = ioaddr + Cfg9346;
+	int read_cmd = location | (EE_READ_CMD << addr_len);
+
+	DPRINTK ("ENTER\n");
+
+	writeb (EE_ENB & ~EE_CS, ee_addr);
+	writeb (EE_ENB, ee_addr);
+	eeprom_delay ();
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		writeb (EE_ENB | dataval, ee_addr);
+		eeprom_delay ();
+		writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay ();
+	}
+	writeb (EE_ENB, ee_addr);
+	eeprom_delay ();
+
+	for (i = 16; i > 0; i--) {
+		writeb (EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay ();
+		retval =
+		    (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 :
+				     0);
+		writeb (EE_ENB, ee_addr);
+		eeprom_delay ();
+	}
+
+	/* Terminate the EEPROM access. */
+	writeb (~EE_CS, ee_addr);
+	eeprom_delay ();
+
+	DPRINTK ("EXIT - returning %d\n", retval);
+	return retval;
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+   serial MDIO protocol.
+   The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
+   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+   "overclocking" issues. */
+#define MDIO_DIR		0x80
+#define MDIO_DATA_OUT	0x04
+#define MDIO_DATA_IN	0x02
+#define MDIO_CLK		0x01
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay()	readb(mdio_addr)
+
+
+static char mii_2_8139_map[8] = {
+	BasicModeCtrl,
+	BasicModeStatus,
+	0,
+	0,
+	NWayAdvert,
+	NWayLPAR,
+	NWayExpansion,
+	0
+};
+
+
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_sync (void *mdio_addr)
+{
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	for (i = 32; i >= 0; i--) {
+		writeb (MDIO_WRITE1, mdio_addr);
+		mdio_delay ();
+		writeb (MDIO_WRITE1 | MDIO_CLK, mdio_addr);
+		mdio_delay ();
+	}
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static int mdio_read (struct device *dev, int phy_id, int location)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *mdio_addr = tp->mmio_addr + Config4;
+	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	int retval = 0;
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	if (phy_id > 31) {	/* Really a 8139.  Use internal registers. */
+		DPRINTK ("EXIT after directly using 8139 internal regs\n");
+		return location < 8 && mii_2_8139_map[location] ?
+		    readw (tp->mmio_addr + mii_2_8139_map[location]) : 0;
+	}
+	mdio_sync (mdio_addr);
+	/* Shift the read command bits out. */
+	for (i = 15; i >= 0; i--) {
+		int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+
+		writeb (MDIO_DIR | dataval, mdio_addr);
+		mdio_delay ();
+		writeb (MDIO_DIR | dataval | MDIO_CLK, mdio_addr);
+		mdio_delay ();
+	}
+
+	/* Read the two transition, 16 data, and wire-idle bits. */
+	for (i = 19; i > 0; i--) {
+		writeb (0, mdio_addr);
+		mdio_delay ();
+		retval =
+		    (retval << 1) | ((readb (mdio_addr) & MDIO_DATA_IN) ? 1
+				     : 0);
+		writeb (MDIO_CLK, mdio_addr);
+		mdio_delay ();
+	}
+
+	DPRINTK ("EXIT, returning %d\n", (retval >> 1) & 0xffff);
+	return (retval >> 1) & 0xffff;
+}
+
+
+static void mdio_write (struct device *dev, int phy_id, int location,
+			int value)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *mdio_addr = tp->mmio_addr + Config4;
+	int mii_cmd =
+	    (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	if (phy_id > 31) {	/* Really a 8139.  Use internal registers. */
+		if (location < 8 && mii_2_8139_map[location]) {
+			writew (value,
+				tp->mmio_addr + mii_2_8139_map[location]);
+			readw (tp->mmio_addr + mii_2_8139_map[location]);
+		}
+		DPRINTK ("EXIT after directly using 8139 internal regs\n");
+		return;
+	}
+	mdio_sync (mdio_addr);
+
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval =
+		    (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+		writeb (dataval, mdio_addr);
+		mdio_delay ();
+		writeb (dataval | MDIO_CLK, mdio_addr);
+		mdio_delay ();
+	}
+
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		writeb (0, mdio_addr);
+		mdio_delay ();
+		writeb (MDIO_CLK, mdio_addr);
+		mdio_delay ();
+	}
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static int rtl8139_open (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	int retval;
+#ifdef RTL8139_DEBUG
+	void *ioaddr = tp->mmio_addr;
+#endif
+
+	DPRINTK ("ENTER\n");
+
+	MOD_INC_USE_COUNT;
+
+	retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev);
+	if (retval) {
+		DPRINTK ("EXIT, returning %d\n", retval);
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+
+	tp->tx_bufs = kmalloc(TX_BUF_TOT_LEN, GFP_KERNEL | GFP_DMA);
+	tp->tx_bufs_dma = virt_to_bus(tp->tx_bufs);
+	tp->rx_ring = kmalloc(RX_BUF_TOT_LEN, GFP_KERNEL | GFP_DMA);
+	tp->rx_ring_dma = virt_to_bus(tp->rx_ring);
+
+	if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
+		free_irq(dev->irq, dev);
+
+		if (tp->tx_bufs) kfree(tp->tx_bufs);
+		if (tp->rx_ring) kfree(tp->rx_ring);
+
+		DPRINTK ("EXIT, returning -ENOMEM\n");
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+		
+	}
+	
+	tp->full_duplex = tp->duplex_lock;
+	tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+	rtl8139_init_ring (dev);
+	rtl8139_hw_start (dev);
+
+	DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d"
+			" GP Pins %2.2x %s-duplex.\n",
+			dev->name, pci_resource_start (tp, 1),
+			dev->irq, RTL_R8 (MediaStatus),
+			tp->full_duplex ? "full" : "half");
+
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer (&tp->timer);
+	tp->timer.expires = jiffies + 3 * HZ;
+	tp->timer.data = (unsigned long) dev;
+	tp->timer.function = &rtl8139_timer;
+	add_timer (&tp->timer);
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	u32 i;
+	u8 tmp;
+
+	DPRINTK ("ENTER\n");
+	
+	/* Soft reset the chip. */
+	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset);
+	udelay (100);
+
+	/* Check that the chip has finished the reset. */
+	for (i = 1000; i > 0; i--)
+		if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+			break;
+
+	/* Restore our idea of the MAC address. */
+	RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+	RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+
+	/* Must enable Tx/Rx before setting transfer thresholds! */
+	RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) |
+			   CmdRxEnb | CmdTxEnb);
+
+	i = rtl8139_rx_config |
+	    (RTL_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);
+	RTL_W32_F (RxConfig, i);
+
+	/* Check this value: the documentation for IFG contradicts ifself. */
+	RTL_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift));
+
+	/* unlock Config[01234] and BMCR register writes */
+	RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+	udelay (10);
+
+	tp->cur_rx = 0;
+
+	if (tp->chipset >= CH_8139A) {
+		tmp = RTL_R8 (Config1) & Config1Clear;
+		tmp |= Cfg1_Driver_Load;
+		tmp |= (tp->chipset == CH_8139B) ? 3 : 1; /* Enable PM/VPD */
+		RTL_W8_F (Config1, tmp);
+	} else {
+		u8 foo = RTL_R8 (Config1) & Config1Clear;
+		RTL_W8 (Config1, tp->full_duplex ? (foo|0x60) : (foo|0x20));
+	}
+
+	if (tp->chipset >= CH_8139B) {
+		tmp = RTL_R8 (Config4) & ~(1<<2);
+		/* chip will clear Rx FIFO overflow automatically */
+		tmp |= (1<<7);  
+		RTL_W8 (Config4, tmp);
+	
+		/* disable magic packet scanning, which is enabled
+		 * when PM is enabled above (Config1) */
+		RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5));
+	}
+
+	/* Lock Config[01234] and BMCR register writes */
+	RTL_W8_F (Cfg9346, Cfg9346_Lock);
+	udelay (10);
+
+	/* init Rx ring buffer DMA address */
+	RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+	/* init Tx buffer DMA addresses */
+	for (i = 0; i < NUM_TX_DESC; i++)
+		RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
+
+	RTL_W32_F (RxMissed, 0);
+
+	rtl8139_set_rx_mode (dev);
+
+	/* no early-rx interrupts */
+	RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+
+	/* make sure RxTx has started */
+	RTL_W8_F (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) |
+			   CmdRxEnb | CmdTxEnb);
+
+	/* Enable all known interrupts by setting the interrupt mask. */
+	RTL_W16_F (IntrMask, rtl8139_intr_mask);
+
+	netif_start_queue (dev);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	tp->cur_rx = 0;
+	atomic_set (&tp->cur_tx, 0);
+	atomic_set (&tp->dirty_tx, 0);
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		tp->tx_info[i].skb = NULL;
+		tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
+	}
+
+	DPRINTK ("EXIT\n");
+}
+
+
+#ifndef RTL_TUNE_TWISTER
+static inline void rtl8139_tune_twister (struct device *dev,
+				  struct rtl8139_private *tp) {}
+#else
+static void rtl8139_tune_twister (struct device *dev,
+				  struct rtl8139_private *tp)
+{
+	int linkcase;
+
+	DPRINTK ("ENTER\n");
+
+	/* This is a complicated state machine to configure the "twister" for
+	   impedance/echos based on the cable length.
+	   All of this is magic and undocumented.
+	 */
+	switch (tp->twistie) {
+	case 1:
+		if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
+			/* We have link beat, let us tune the twister. */
+			RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
+			tp->twistie = 2;	/* Change to state 2. */
+			next_tick = HZ / 10;
+		} else {
+			/* Just put in some reasonable defaults for when beat returns. */
+			RTL_W16 (CSCR, CSCR_LinkDownCmd);
+			RTL_W32 (FIFOTMS, 0x20);	/* Turn on cable test mode. */
+			RTL_W32 (PARA78, PARA78_default);
+			RTL_W32 (PARA7c, PARA7c_default);
+			tp->twistie = 0;	/* Bail from future actions. */
+		}
+		break;
+	case 2:
+		/* Read how long it took to hear the echo. */
+		linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
+		if (linkcase == 0x7000)
+			tp->twist_row = 3;
+		else if (linkcase == 0x3000)
+			tp->twist_row = 2;
+		else if (linkcase == 0x1000)
+			tp->twist_row = 1;
+		else
+			tp->twist_row = 0;
+		tp->twist_col = 0;
+		tp->twistie = 3;	/* Change to state 2. */
+		next_tick = HZ / 10;
+		break;
+	case 3:
+		/* Put out four tuning parameters, one per 100msec. */
+		if (tp->twist_col == 0)
+			RTL_W16 (FIFOTMS, 0);
+		RTL_W32 (PARA7c, param[(int) tp->twist_row]
+			 [(int) tp->twist_col]);
+		next_tick = HZ / 10;
+		if (++tp->twist_col >= 4) {
+			/* For short cables we are done.
+			   For long cables (row == 3) check for mistune. */
+			tp->twistie =
+			    (tp->twist_row == 3) ? 4 : 0;
+		}
+		break;
+	case 4:
+		/* Special case for long cables: check for mistune. */
+		if ((RTL_R16 (CSCR) &
+		     CSCR_LinkStatusBits) == 0x7000) {
+			tp->twistie = 0;
+			break;
+		} else {
+			RTL_W32 (PARA7c, 0xfb38de03);
+			tp->twistie = 5;
+			next_tick = HZ / 10;
+		}
+		break;
+	case 5:
+		/* Retune for shorter cable (column 2). */
+		RTL_W32 (FIFOTMS, 0x20);
+		RTL_W32 (PARA78, PARA78_default);
+		RTL_W32 (PARA7c, PARA7c_default);
+		RTL_W32 (FIFOTMS, 0x00);
+		tp->twist_row = 2;
+		tp->twist_col = 0;
+		tp->twistie = 3;
+		next_tick = HZ / 10;
+		break;
+
+	default:
+		/* do nothing */
+		break;
+	}
+
+	DPRINTK ("EXIT\n");
+}
+#endif /* RTL_TUNE_TWISTER */
+
+
+static void rtl8139_timer (unsigned long data)
+{
+	struct device *dev = (struct device *) data;
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int next_tick = 60 * HZ;
+	int mii_reg5;
+
+	mii_reg5 = mdio_read (dev, tp->phys[0], 5);
+
+	if (!tp->duplex_lock && mii_reg5 != 0xffff) {
+		int duplex = (mii_reg5 & 0x0100)
+		    || (mii_reg5 & 0x01C0) == 0x0040;
+		if (tp->full_duplex != duplex) {
+			tp->full_duplex = duplex;
+			printk (KERN_INFO
+				"%s: Setting %s-duplex based on MII #%d link"
+				" partner ability of %4.4x.\n", dev->name,
+				tp->full_duplex ? "full" : "half",
+				tp->phys[0], mii_reg5);
+			RTL_W8 (Cfg9346, Cfg9346_Unlock);
+			RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
+			RTL_W8 (Cfg9346, Cfg9346_Lock);
+		}
+	}
+
+	rtl8139_tune_twister (dev, tp);
+
+	DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
+		 dev->name, RTL_R16 (NWayLPAR));
+	DPRINTK ("%s:  Other registers are IntMask %4.4x IntStatus %4.4x"
+		 " RxStatus %4.4x.\n", dev->name,
+		 RTL_R16 (IntrMask),
+		 RTL_R16 (IntrStatus),
+		 RTL_R32 (RxEarlyStatus));
+	DPRINTK ("%s:  Chip config %2.2x %2.2x.\n",
+		 dev->name, RTL_R8 (Config0),
+		 RTL_R8 (Config1));
+
+	tp->timer.expires = jiffies + next_tick;
+	add_timer (&tp->timer);
+}
+
+
+static void rtl8139_tx_timeout (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int i;
+	unsigned long flags;
+
+	DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x "
+		 "media %2.2x.\n", dev->name,
+		 RTL_R8 (ChipCmd),
+		 RTL_R16 (IntrStatus),
+		 RTL_R8 (MediaStatus));
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	RTL_W16 (IntrMask, 0x0000);
+
+	/* Emit info to figure out what went wrong. */
+        printk (KERN_DEBUG "%s: Tx queue start entry %d  dirty entry %d.\n",
+		dev->name, atomic_read (&tp->cur_tx),
+		atomic_read (&tp->dirty_tx));
+	for (i = 0; i < NUM_TX_DESC; i++)
+		printk (KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n",
+			dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
+			i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ?
+				" (queue head)" : "");
+
+	spin_lock_irqsave (&tp->lock, flags);
+
+	/* Stop a shared interrupt from scavenging while we are. */
+	atomic_set (&tp->cur_tx, 0);
+	atomic_set (&tp->dirty_tx, 0);
+
+	/* Dump the unsent Tx packets. */
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		struct ring_info *rp = &tp->tx_info[i];
+		if (rp->skb) {
+			dev_kfree_skb (rp->skb);
+			rp->skb = NULL;
+			tp->stats.tx_dropped++;
+		}
+	}
+	
+	spin_unlock_irqrestore (&tp->lock, flags);
+
+	/* ...and finally, reset everything */
+	rtl8139_hw_start (dev);
+}
+
+
+
+static int rtl8139_start_xmit (struct sk_buff *skb, struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int entry;
+
+	if (dev->tbusy) {
+		/* If this happens network layer tells us we're broken. */
+		if (jiffies - dev->trans_start > TX_TIMEOUT)
+			rtl8139_tx_timeout(dev);
+	}
+
+	/* Calculate the next Tx descriptor entry. */
+	entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC;
+
+	assert (tp->tx_info[entry].skb == NULL);
+
+	tp->tx_info[entry].skb = skb;
+	memcpy (tp->tx_buf[entry], skb->data, skb->len);
+
+	/* Note: the chip doesn't have auto-pad! */
+	RTL_W32 (TxStatus0 + (entry * sizeof(u32)),
+		 tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+
+	dev->trans_start = jiffies;
+	atomic_inc (&tp->cur_tx);
+	if ((atomic_read (&tp->cur_tx) - atomic_read (&tp->dirty_tx)) >= NUM_TX_DESC)
+		netif_stop_queue (dev);
+
+	DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n",
+		 dev->name, skb->data, skb->len, entry);
+
+	return 0;
+}
+
+
+static inline void rtl8139_tx_interrupt (struct device *dev,
+					 struct rtl8139_private *tp,
+					 void *ioaddr)
+{
+	unsigned int dirty_tx;
+
+	assert (dev != NULL);
+	assert (tp != NULL);
+	assert (ioaddr != NULL);
+	
+	dirty_tx = atomic_read (&tp->dirty_tx);
+
+	while ((atomic_read (&tp->cur_tx) - dirty_tx) > 0) {
+		int entry = dirty_tx % NUM_TX_DESC;
+		int txstatus;
+
+		txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+		
+		if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+			break;	/* It still hasn't been Txed */
+
+		/* Note: TxCarrierLost is always asserted at 100mbps. */
+		if (txstatus & (TxOutOfWindow | TxAborted)) {
+			/* There was an major error, log it. */
+			DPRINTK ("%s: Transmit error, Tx status %8.8x.\n",
+				 dev->name, txstatus);
+			tp->stats.tx_errors++;
+			if (txstatus & TxAborted) {
+				tp->stats.tx_aborted_errors++;
+				RTL_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift));
+			}
+			if (txstatus & TxCarrierLost)
+				tp->stats.tx_carrier_errors++;
+			if (txstatus & TxOutOfWindow)
+				tp->stats.tx_window_errors++;
+#ifdef ETHER_STATS
+			if ((txstatus & 0x0f000000) == 0x0f000000)
+				tp->stats.collisions16++;
+#endif
+		} else {
+			if (txstatus & TxUnderrun) {
+				/* Add 64 to the Tx FIFO threshold. */
+				if (tp->tx_flag < 0x00300000)
+					tp->tx_flag += 0x00020000;
+				tp->stats.tx_fifo_errors++;
+			}
+			tp->stats.collisions += (txstatus >> 24) & 15;
+			tp->stats.tx_bytes += txstatus & 0x7ff;
+			tp->stats.tx_packets++;
+		}
+
+		dev_kfree_skb_irq (tp->tx_info[entry].skb);
+		tp->tx_info[entry].skb = NULL;
+		dirty_tx++;
+		if (netif_queue_stopped (dev) &&
+		    (atomic_read (&tp->cur_tx) - dirty_tx < NUM_TX_DESC))
+			netif_wake_queue (dev);
+		mark_bh(NET_BH);
+	}
+
+#ifndef RTL8139_NDEBUG
+	if (atomic_read (&tp->cur_tx) - dirty_tx > NUM_TX_DESC) {
+		printk (KERN_ERR
+		  "%s: Out-of-sync dirty pointer, %d vs. %d.\n",
+		     dev->name, dirty_tx, atomic_read (&tp->cur_tx));
+		dirty_tx += NUM_TX_DESC;
+	}
+#endif /* RTL8139_NDEBUG */
+
+	atomic_set (&tp->dirty_tx, dirty_tx);
+}
+
+/* TODO: clean this up!  Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct device *dev,
+			    struct rtl8139_private *tp, void *ioaddr)
+{
+	u8 tmp8;
+	int tmp_work = 1000;
+
+	DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n",
+	         dev->name, rx_status);
+	if (rx_status & RxTooLong) {
+		DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+			 dev->name, rx_status);
+		/* A.C.: The chip hangs here. */
+	}
+	tp->stats.rx_errors++;
+	if (rx_status & (RxBadSymbol | RxBadAlign))
+		tp->stats.rx_frame_errors++;
+	if (rx_status & (RxRunt | RxTooLong))
+		tp->stats.rx_length_errors++;
+	if (rx_status & RxCRCErr)
+		tp->stats.rx_crc_errors++;
+	/* Reset the receiver, based on RealTek recommendation. (Bug?) */
+	tp->cur_rx = 0;
+
+	/* disable receive */
+	tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear;
+	RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb);
+
+	/* A.C.: Reset the multicast list. */
+	rtl8139_set_rx_mode (dev);
+
+	/* XXX potentially temporary hack to
+	 * restart hung receiver */
+	while (--tmp_work > 0) {
+		tmp8 = RTL_R8 (ChipCmd);
+		if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+			break;
+		RTL_W8_F (ChipCmd,
+			  (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb);
+	}
+
+	/* G.S.: Re-enable receiver */
+	/* XXX temporary hack to work around receiver hang */
+	rtl8139_set_rx_mode (dev);
+
+	if (tmp_work <= 0)
+		printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
+}
+
+
+/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
+   field alignments and semantics. */
+static void rtl8139_rx_interrupt (struct device *dev,
+				  struct rtl8139_private *tp, void *ioaddr)
+{
+	unsigned char *rx_ring;
+	u16 cur_rx;
+
+	assert (dev != NULL);
+	assert (tp != NULL);
+	assert (ioaddr != NULL);
+
+	rx_ring = tp->rx_ring;
+	cur_rx = tp->cur_rx;
+
+	DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+		 " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+		 RTL_R16 (RxBufAddr),
+		 RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+	while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+		int ring_offset = cur_rx % RX_BUF_LEN;
+		u32 rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
+		int rx_size = rx_status >> 16;
+		struct sk_buff *skb;
+		int pkt_size = rx_size - 4;
+
+		DPRINTK ("%s:  rtl8139_rx() status %4.4x, size %4.4x,"
+			 " cur %4.4x.\n", dev->name, rx_status,
+			 rx_size, cur_rx);
+#if RTL8139_DEBUG > 2
+		{
+			int i;
+			DPRINTK ("%s: Frame contents ", dev->name);
+			for (i = 0; i < 70; i++)
+				printk (" %2.2x",
+					rx_ring[ring_offset + i]);
+			printk (".\n");
+		}
+#endif
+
+		/* E. Gill */
+		/* Note from BSD driver:
+		 * Here's a totally undocumented fact for you. When the
+		 * RealTek chip is in the process of copying a packet into
+		 * RAM for you, the length will be 0xfff0. If you spot a
+		 * packet header with this value, you need to stop. The
+		 * datasheet makes absolutely no mention of this and
+		 * RealTek should be shot for this.
+		 */
+		if (rx_size == 0xfff0)
+			break;
+
+		/* if Rx err received, Rx process gets reset, so
+		 * we abort any further Rx processing
+		 */
+		if (rx_status &
+		    (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr | RxBadAlign)) {
+			rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+			return;
+		}
+
+		/* Malloc up new buffer, compatible with net-2e. */
+		/* Omit the four octet CRC from the length. */
+
+		/* TODO: consider allocating skb's outside of
+		 * interrupt context, both to speed interrupt processing,
+		 * and also to reduce the chances of having to
+		 * drop packets here under memory pressure.
+		 */
+
+		skb = dev_alloc_skb (pkt_size + 2);
+		if (skb == NULL) {
+			printk (KERN_WARNING
+				"%s: Memory squeeze, dropping packet.\n",
+				dev->name);
+			tp->stats.rx_dropped++;
+			break;
+		}
+		skb->dev = dev;
+		skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
+
+		eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+		skb_put (skb, pkt_size);
+
+		skb->protocol = eth_type_trans (skb, dev);
+		netif_rx (skb);
+		mark_bh(NET_BH);
+		tp->stats.rx_bytes += pkt_size ;
+		tp->stats.rx_packets++;
+
+		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+		RTL_W16_F (RxBufPtr, cur_rx - 16);
+	}
+
+	DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+		" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+		RTL_R16 (RxBufAddr),
+		RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+	tp->cur_rx = cur_rx;
+}
+
+
+static void rtl8139_weird_interrupt (struct device *dev,
+				     struct rtl8139_private *tp,
+				     void *ioaddr,
+				     int status, int link_changed)
+{
+	printk (KERN_DEBUG "%s: Abnormal interrupt, status %8.8x.\n",
+		dev->name, status);
+		 
+	assert (dev != NULL);
+	assert (tp != NULL);
+	assert (ioaddr != NULL);
+	
+	/* Update the error count. */
+	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+	RTL_W32 (RxMissed, 0);
+
+	if ((status & RxUnderrun) && link_changed &&
+	    (tp->drv_flags & HAS_LNK_CHNG)) {
+		/* Really link-change on new chips. */
+		int lpar = RTL_R16 (NWayLPAR);
+		int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040
+				|| tp->duplex_lock;
+		if (tp->full_duplex != duplex) {
+			tp->full_duplex = duplex;
+			RTL_W8 (Cfg9346, Cfg9346_Unlock);
+			RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
+		}
+		status &= ~RxUnderrun;
+	}
+	if (status &
+	    (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))
+		tp->stats.rx_errors++;
+
+	if (status & (PCSTimeout))
+		tp->stats.rx_length_errors++;
+	if (status & (RxUnderrun | RxFIFOOver))
+		tp->stats.rx_fifo_errors++;
+	if (status & RxOverflow) {
+		tp->stats.rx_over_errors++;
+		tp->cur_rx = RTL_R16 (RxBufAddr) % RX_BUF_LEN;
+		RTL_W16_F (RxBufPtr, tp->cur_rx - 16);
+	}
+	if (status & PCIErr) {
+		u16 pci_cmd_status;
+		pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
+
+		printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
+			dev->name, pci_cmd_status);
+	}
+}
+
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void rtl8139_interrupt (int irq, void *dev_instance,
+			       struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_instance;
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	int boguscnt = max_interrupt_work;
+	void *ioaddr = tp->mmio_addr;
+	int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */
+
+	spin_lock (&tp->lock);
+	
+	do {
+		status = RTL_R16 (IntrStatus);
+
+		/* h/w no longer present (hotplug?) or major error, bail */
+		if (status == 0xFFFF)
+			break;
+		
+		/* Acknowledge all of the current interrupt sources ASAP, but
+		   an first get an additional status bit from CSCR. */
+		if (status & RxUnderrun)
+			link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+		/* E. Gill */
+		/* In case of an RxFIFOOver we must also clear the RxOverflow
+		   bit to avoid dropping frames for ever. Believe me, I got a
+		   lot of troubles copying huge data (approximately 2 RxFIFOOver
+		   errors per 1GB data transfer).
+		   The following is written in the 'p-guide.pdf' file (RTL8139(A/B)
+		   Programming guide V0.1, from 1999/1/15) on page 9 from REALTEC.
+		   -----------------------------------------------------------
+		   2. RxFIFOOvw handling:
+		     When RxFIFOOvw occurs, all incoming packets are discarded.
+		     Clear ISR(RxFIFOOvw) doesn't dismiss RxFIFOOvw event. To
+		     dismiss RxFIFOOvw event, the ISR(RxBufOvw) must be written
+		     with a '1'.
+		   -----------------------------------------------------------
+		   Unfortunately I was not able to find any reason for the
+		   RxFIFOOver error (I got the feeling this depends on the
+		   CPU speed, lower CPU speed --> more errors).
+		   After clearing the RxOverflow bit the transfer of the
+		   packet was repeated and all data are error free transfered */
+		RTL_W16_F (IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status);
+
+		DPRINTK ("%s: interrupt  status=%#4.4x new intstat=%#4.4x.\n",
+				dev->name, status,
+				RTL_R16 (IntrStatus));
+
+		if ((status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow
+			| RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)
+			break;
+
+		/* Check uncommon events with one test. */
+		if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
+		  	      RxFIFOOver | TxErr | RxErr))
+			rtl8139_weird_interrupt (dev, tp, ioaddr,
+						 status, link_changed);
+
+		if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver))	/* Rx interrupt */
+			rtl8139_rx_interrupt (dev, tp, ioaddr);
+
+		if (status & (TxOK | TxErr))
+			rtl8139_tx_interrupt (dev, tp, ioaddr);
+
+		boguscnt--;
+	} while (boguscnt > 0);
+
+	if (boguscnt <= 0) {
+		printk (KERN_WARNING
+			"%s: Too much work at interrupt, "
+			"IntrStatus=0x%4.4x.\n", dev->name,
+			status);
+
+		/* Clear all interrupt sources. */
+		RTL_W16 (IntrStatus, 0xffff);
+	}
+
+	spin_unlock (&tp->lock);
+	
+	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+		 dev->name, RTL_R16 (IntrStatus));
+}
+
+
+static int rtl8139_close (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int i;
+	unsigned long flags;
+
+	DPRINTK ("ENTER\n");
+
+	netif_stop_queue (dev);
+	dev->start = 0;
+
+	DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n",
+			dev->name, RTL_R16 (IntrStatus));
+
+	del_timer (&tp->timer);
+	
+	spin_lock_irqsave (&tp->lock, flags);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	RTL_W16 (IntrMask, 0x0000);
+
+	/* Stop the chip's Tx and Rx DMA processes. */
+	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear));
+
+	/* Update the error counts. */
+	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+	RTL_W32 (RxMissed, 0);
+
+	spin_unlock_irqrestore (&tp->lock, flags);
+	
+	/* snooze for a small bit */
+	if (current->need_resched)
+		schedule ();
+
+	free_irq (dev->irq, dev);
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		struct sk_buff *skb = tp->tx_info[i].skb;
+
+		if (skb) {
+			dev_kfree_skb (skb);
+			DPRINTK("PCI unmap?\n");
+		}
+		tp->tx_info[i].skb = NULL;
+	}
+
+	kfree(tp->rx_ring);
+	kfree(tp->tx_bufs);
+	tp->rx_ring = NULL;
+	tp->tx_bufs = NULL;
+
+	/* Green! Put the chip in low-power mode. */
+	RTL_W8 (Cfg9346, Cfg9346_Unlock);
+	RTL_W8 (Config1, 0x03);
+	RTL_W8 (HltClk, 'H');	/* 'R' would leave the clock running. */
+
+	MOD_DEC_USE_COUNT;
+
+	DPRINTK ("EXIT\n");
+	return 0;
+}
+
+
+static int mii_ioctl (struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	u16 *data = (u16 *) & rq->ifr_data;
+	unsigned long flags;
+	int rc = 0;
+
+	DPRINTK ("ENTER\n");
+
+	switch (cmd) {
+	case SIOCDEVPRIVATE:	/* Get the address of the PHY in use. */
+		data[0] = tp->phys[0] & 0x3f;
+		/* Fall Through */
+
+	case SIOCDEVPRIVATE + 1:	/* Read the specified MII register. */
+		spin_lock_irqsave (&tp->lock, flags);
+		data[3] = mdio_read (dev, data[0], data[1] & 0x1f);
+		spin_unlock_irqrestore (&tp->lock, flags);
+		break;
+
+	case SIOCDEVPRIVATE + 2:	/* Write the specified MII register */
+		if (!capable (CAP_NET_ADMIN)) {
+			rc = -EPERM;
+			break;
+		}
+
+		spin_lock_irqsave (&tp->lock, flags);
+		mdio_write (dev, data[0], data[1] & 0x1f, data[2]);
+		spin_unlock_irqrestore (&tp->lock, flags);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static struct net_device_stats *rtl8139_get_stats (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+
+	DPRINTK ("ENTER\n");
+
+	assert (tp != NULL);
+
+	if (netif_running(dev)) {
+		unsigned long flags;
+
+		spin_lock_irqsave (&tp->lock, flags);
+
+		tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+		RTL_W32 (RxMissed, 0);
+
+		spin_unlock_irqrestore (&tp->lock, flags);
+	}
+
+	DPRINTK ("EXIT\n");
+	return &tp->stats;
+}
+
+
+
+/* Set or clear the multicast filter for this adaptor.
+   This routine is not state sensitive and need not be SMP locked. */
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc (int length, unsigned char *data)
+{
+	int crc = -1;
+
+	DPRINTK ("ENTER\n");
+
+	while (--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+			crc = (crc << 1) ^
+			    ((crc < 0) ^ (current_octet & 1) ?
+			     ethernet_polynomial : 0);
+	}
+
+	DPRINTK ("EXIT\n");
+	return crc;
+}
+
+
+static void rtl8139_set_rx_mode (struct device *dev)
+{
+	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	u32 mc_filter[2];	/* Multicast hash filter */
+	int i, rx_mode;
+	u32 tmp;
+
+	DPRINTK ("ENTER\n");
+
+	DPRINTK ("%s:   rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
+			dev->name, dev->flags, RTL_R32 (RxConfig));
+
+	/* Note: do not reorder, GCC is clever about common statements. */
+	if (dev->flags & IFF_PROMISC) {
+		/* Unconditionally log net taps. */
+		printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+			dev->name);
+		rx_mode =
+		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+		    AcceptAllPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+	} else if ((dev->mc_count > multicast_filter_limit)
+		   || (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to filter perfectly -- accept all multicasts. */
+		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+	} else {
+		struct dev_mc_list *mclist;
+		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0;
+		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+		     i++, mclist = mclist->next)
+			set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26,
+				 mc_filter);
+	}
+	
+	spin_lock_irq (&tp->lock);
+
+	/* We can safely update without stopping the chip. */
+	tmp = rtl8139_rx_config | rx_mode |
+		(RTL_R32 (RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);
+	RTL_W32_F (RxConfig, tmp);
+	RTL_W32_F (MAR0 + 0, mc_filter[0]);
+	RTL_W32_F (MAR0 + 4, mc_filter[1]);
+
+	spin_unlock_irq (&tp->lock);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+int rtl8139_init(void)
+{
+	struct pci_dev *pcidev;
+	struct pci_device_id *pdid;
+
+	if (rtl8139_initialized) return -ENODEV;
+	for (pdid=&rtl8139_pci_tbl[0]; pdid->vendor != 0; pdid++) {
+		for (pcidev = pci_find_device(pdid->vendor, pdid->device, NULL); pcidev != NULL; pcidev = pci_find_device(pdid->vendor, pdid->device, pcidev)) {
+			if (rtl8139_init_one(pcidev, pdid) != 0) {
+				printk(KERN_ERR "Error initializing PCI device %08lX.\n", (unsigned long) pcidev);
+			}
+		}
+	}
+	rtl8139_initialized++;
+	return (rtl8139_device_count > 0 ? 0 : -ENODEV);
+}
+
+void rtl8139_cleanup(void)
+{
+	int i;
+	
+	for (i=0; i<rtl8139_device_count; i++) {
+		rtl8139_remove_one(rtl8139_device_tab[i]);
+	}
+	return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+MODULE_AUTHOR (RTL8139_AUTHOR);
+MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver");
+MODULE_PARM (multicast_filter_limit, "i");
+MODULE_PARM (max_interrupt_work, "i");
+MODULE_PARM (debug, "i");
+MODULE_PARM (media, "1-" __MODULE_STRING(8) "i");
+
+int init_module(void)
+{
+	return rtl8139_init();
+}
+
+void cleanup_module(void)
+{
+	rtl8139_cleanup();
+	return;
+}
+
+#else
+
+int rtl8139too_probe(struct device *dev)
+{
+	return rtl8139_init();
+}
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Config.in linux.18p17/drivers/net/Config.in
--- linux.vanilla/drivers/net/Config.in	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/Config.in	Sun Oct 15 22:01:48 2000
@@ -100,6 +100,9 @@
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
   fi
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'RealTek 8139too driver support' CONFIG_RTL8139TOO
+  fi
   bool 'Other ISA cards' CONFIG_NET_ISA
   if [ "$CONFIG_NET_ISA" = "y" ]; then
     tristate 'AT1700/1720 support (EXPERIMENTAL)' CONFIG_AT1700
@@ -184,7 +187,8 @@
 
 bool 'FDDI driver support' CONFIG_FDDI
 if [ "$CONFIG_FDDI" = "y" ]; then
-  bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
+  bool '  Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
+  tristate '  SysKonnect FDDI PCI support' CONFIG_SKFP
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -315,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
@@ -357,6 +362,11 @@
     fi 
   fi
 fi
+
+#
+# Xpeed drivers
+#
+tristate 'Xpeed X200/X300 DSL NIC support' CONFIG_XPEED
 
 endmenu
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Makefile linux.18p17/drivers/net/Makefile
--- linux.vanilla/drivers/net/Makefile	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/Makefile	Sun Oct 15 21:57:14 2000
@@ -5,7 +5,7 @@
 
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc sk98lin lmc
+ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc sk98lin lmc skfp
 
 L_TARGET := net.a
 L_OBJS   :=
@@ -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
@@ -580,6 +585,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_RTL8139TOO),y)
+L_OBJS += 8139too.o
+else
+  ifeq ($(CONFIG_RTL8139TOO),m)
+  M_OBJS += 8139too.o
+  endif
+endif
+
 ifeq ($(CONFIG_SIS900),y)
 L_OBJS += sis900.o
 else
@@ -623,6 +636,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_SKFP),y)
+  SUB_DIRS += skfp
+  MOD_IN_SUB_DIRS += skfp
+  L_OBJS += skfp/skfp.o
+else
+  ifeq ($(CONFIG_SKFP),m)
+    MOD_IN_SUB_DIRS += skfp
+  endif
+endif
+
 ifeq ($(CONFIG_WAVELAN),y)
 L_OBJS += wavelan.o
 else
@@ -958,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.
 
@@ -1083,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
@@ -1215,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
@@ -1252,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Space.c linux.18p17/drivers/net/Space.c
--- linux.vanilla/drivers/net/Space.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/Space.c	Sun Oct  1 11:24:15 2000
@@ -31,6 +31,7 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/netlink.h>
+#include <net/divert.h>
 
 #define	NEXT_DEV	NULL
 
@@ -114,6 +115,7 @@
 extern int am79c961_probe(struct device *dev);
 extern int epic100_probe(struct device *dev);
 extern int rtl8139_probe(struct device *dev);
+extern int rtl8139too_probe(struct device *dev);
 extern int sis900_probe(struct device *dev);
 extern int hplance_probe(struct device *dev);
 extern int bagetlance_probe(struct device *);
@@ -140,6 +142,7 @@
 /* FDDI adapters */
 extern int dfx_probe(struct device *dev);
 extern int apfddi_init(struct device *dev);
+extern int skfp_probe(struct device *dev);
 
 /* HIPPI boards */
 extern int rr_hippi_probe(struct device *);
@@ -171,13 +174,35 @@
 	struct devprobe *p = plist;
 	unsigned long base_addr = dev->base_addr;
 
+#ifdef CONFIG_NET_DIVERT
+	int	ret;
+#endif
+
 	while (p->probe != NULL) {
 		if (base_addr && p->probe(dev) == 0)	/* probe given addr */
+#ifdef CONFIG_NET_DIVERT
+		{
+			ret=alloc_divert_blk(dev);
+			if (ret)
+				return ret;
 			return 0;
+		}
+#else
+			return 0;
+#endif
 		else if (p->status == 0) {		/* has autoprobe failed yet? */
 			p->status = p->probe(dev);	/* no, try autoprobe */
 			if (p->status == 0)
+#ifdef CONFIG_NET_DIVERT
+			{
+				ret=alloc_divert_blk(dev);
+				if (ret)
+					return ret;
 				return 0;
+			}
+#else
+				return 0;
+#endif
 		}
 		p++;
 	}
@@ -226,6 +251,9 @@
 #ifdef CONFIG_RTL8139
 	{rtl8139_probe, 0},
 #endif
+#ifdef CONFIG_RTL8139TOO
+	{rtl8139too_probe, 0},
+#endif
 #ifdef CONFIG_SIS900
 	{sis900_probe, 0},
 #endif
@@ -583,6 +611,9 @@
 #endif
 #ifdef CONFIG_APFDDI
 	&& apfddi_init(dev)
+#endif
+#ifdef CONFIG_SKFP
+	&& skfp_probe(dev)
 #endif
 	&& 1 ) {
 	    return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/acenic.c linux.18p17/drivers/net/acenic.c
--- linux.vanilla/drivers/net/acenic.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/net/acenic.c	Mon Sep 11 17:05:04 2000
@@ -29,7 +29,12 @@
  *                                       infrastructure and Sparc support
  *   Pierrick Pinasseau (CERN): For lending me an Ultra 5 to test the
  *                              driver under Linux/Sparc64
- *   Matt Domsch <Matt_Domsch@dell.com>: Detect 1000baseT cards
+ *   Matt Domsch <Matt_Domsch@dell.com>: Detect Alteon 1000baseT cards
+ *   Chip Salzenberg <chip@valinux.com>: Fix race condition between tx
+ *                                       handler and close() cleanup.
+ *   Ken Aaker <kdaaker@rchland.vnet.ibm.com>: Correct check for whether
+ *                                       memory mapped IO is enabled to
+ *                                       make the driver work on RS/6000.
  */
 
 #include <linux/config.h>
@@ -83,8 +88,13 @@
 #define PCI_VENDOR_ID_NETGEAR		0x1385
 #define PCI_DEVICE_ID_NETGEAR_GA620	0x620a
 #endif
+#ifndef PCI_DEVICE_ID_NETGEAR_GA620T
+#define PCI_DEVICE_ID_NETGEAR_GA620T	0x630a
+#endif
+
 /*
- * They used the DEC vendor ID by mistake
+ * Farallon used the DEC vendor ID by mistake and they seem not
+ * to care - stinky!
  */
 #ifndef PCI_DEVICE_ID_FARALLON_PN9000SX
 #define PCI_DEVICE_ID_FARALLON_PN9000SX	0x1a
@@ -124,7 +134,6 @@
 #endif
 
 #if (LINUX_VERSION_CODE < 0x02032a)
-typedef u32 dma_addr_t;
 
 static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
 					 dma_addr_t *dma_handle)
@@ -389,7 +398,7 @@
 static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
 
 static const char __initdata *version = 
-  "acenic.c: v0.44 05/11/2000  Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
+  "acenic.c: v0.46 2000/09/05  Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
   "                            http://home.cern.ch/~jes/gige/acenic.html\n";
 
 static struct net_device *root_dev = NULL;
@@ -429,7 +438,8 @@
 		    !((pdev->vendor == PCI_VENDOR_ID_3COM) &&
 		      (pdev->device == PCI_DEVICE_ID_3COM_3C985)) &&
 		    !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) &&
-		      (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620)) &&
+		      ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || 
+		       (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) &&
 		/*
 		 * Farallon used the DEC vendor ID on their cards by
 		 * mistake for a while
@@ -480,7 +490,7 @@
 		pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command);
 
 		/* OpenFirmware on Mac's does not set this - DOH.. */ 
-		if (!ap->pci_command & PCI_COMMAND_MEMORY) {
+		if (!(ap->pci_command & PCI_COMMAND_MEMORY)) {
 			printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
 			       "access - was not enabled by BIOS/Firmware\n",
 			       dev->name);
@@ -597,7 +607,6 @@
 }
 
 
-#ifdef MODULE
 MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");
 MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
 MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
@@ -606,7 +615,6 @@
 MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
-#endif
 
 
 void __exit ace_module_cleanup(void)
@@ -712,7 +720,7 @@
 	return status;
 }
 
-
+#ifdef MODULE
 #if (LINUX_VERSION_CODE < 0x02032a)
 int init_module(void)
 {
@@ -728,7 +736,7 @@
 module_init(ace_module_init);
 module_exit(ace_module_cleanup);
 #endif
-
+#endif
 
 static void ace_free_descriptors(struct net_device *dev)
 {
@@ -1010,7 +1018,7 @@
 	pci_read_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, &cache);
 	if ((cache << 2) != SMP_CACHE_BYTES) {
 		printk(KERN_INFO "  PCI cache line size set incorrectly "
-		       "(%i bytes) by BIOS/FW, corring to %i\n",
+		       "(%i bytes) by BIOS/FW, correcting to %i\n",
 		       (cache << 2), SMP_CACHE_BYTES);
 		pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE,
 				      SMP_CACHE_BYTES >> 2);
@@ -1996,6 +2004,19 @@
 			struct sk_buff *skb;
 
 			skb = ap->skb->tx_skbuff[idx].skb;
+			/*
+			 * Race condition between the code cleaning
+			 * the tx queue in the interrupt handler and the
+			 * interface close,
+			 *
+			 * This is a kludge that really should be fixed 
+			 * by preventing the driver from generating a tx
+			 * interrupt when the packet has already been
+			 * removed from the tx queue.
+			 *
+			 * Nailed by Don Dugger and Chip Salzenberg of
+			 * VA Linux.
+			 */
 			if (skb) {
 				dma_addr_t mapping;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/acenic.h linux.18p17/drivers/net/acenic.h
--- linux.vanilla/drivers/net/acenic.h	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/net/acenic.h	Fri Sep  8 16:58:34 2000
@@ -609,7 +609,7 @@
 	struct timer_list	timer;
 
 	unsigned long		std_refill_busy
-				__attribute__ ((aligned (L1_CACHE_BYTES)));
+				__attribute__ ((aligned (SMP_CACHE_BYTES)));
 	unsigned long		mini_refill_busy, jumbo_refill_busy;
 	atomic_t		cur_rx_bufs,
 				cur_mini_bufs,
@@ -642,7 +642,7 @@
 	char			name[48];
 #ifdef INDEX_DEBUG
 	spinlock_t		debug_lock
-				__attribute__ ((aligned (L1_CACHE_BYTES)));;
+				__attribute__ ((aligned (SMP_CACHE_BYTES)));;
 	u32			last_tx, last_std_rx, last_mini_rx;
 #endif
 	struct net_device_stats stats;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/am79c961a.c linux.18p17/drivers/net/am79c961a.c
--- linux.vanilla/drivers/net/am79c961a.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/am79c961a.c	Mon Oct  2 10:08:21 2000
@@ -38,35 +38,29 @@
 static void
 am79c961_setmulticastlist (struct device *dev);
 
-static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.00\n";
-
-#define FUNC_PROLOGUE \
-	struct dev_priv *priv = (struct dev_priv *)dev->priv
+static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.01\n";
 
 /* --------------------------------------------------------------------------- */
 
+#ifdef __arm__
 static void
 write_rreg (unsigned long base, unsigned int reg, unsigned short val)
 {
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		strh	%0, [%2, #-4]	@ NET_RDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		str%?h	%0, [%2, #-4]	@ NET_RDP
 		" : : "r" (val), "r" (reg), "r" (0xf0000464));
 }
 
 static inline void
 write_ireg (unsigned long base, unsigned int reg, unsigned short val)
 {
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		strh	%0, [%2, #8]	@ NET_RDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		str%?h	%0, [%2, #8]	@ NET_RDP
 		" : : "r" (val), "r" (reg), "r" (0xf0000464));
 }
 
 #define am_writeword(dev,off,val)\
-	__asm__("\
-		strh	%0, [%1]\
-		" : : "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
+	__asm__("str%?h	%0, [%1]" : : "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
 
 static inline void
 am_writebuffer(struct device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
@@ -74,30 +68,28 @@
 	offset = 0xe0000000 + (offset << 1);
 	length = (length + 1) & ~1;
 	if ((int)buf & 2) {
-		__asm__ __volatile__("
-			strh	%2, [%0], #4
-		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
 		buf += 2;
 		length -= 2;
 	}
 	while (length > 8) {
 		unsigned int tmp, tmp2;
 		__asm__ __volatile__("
-			ldmia	%1!, {%2, %3}
-			strh	%2, [%0], #4
-			mov	%2, %2, lsr #16
-			strh	%2, [%0], #4
-			strh	%3, [%0], #4
-			mov	%3, %3, lsr #16
-			strh	%3, [%0], #4
-		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
+			ldm%?ia	%1!, {%2, %3}
+			str%?h	%2, [%0], #4
+			mov%?	%2, %2, lsr #16
+			str%?h	%2, [%0], #4
+			str%?h	%3, [%0], #4
+			mov%?	%3, %3, lsr #16
+			str%?h	%3, [%0], #4
+		" : "=&r" (offset), "=&r" (buf), "=&r" (tmp), "=&r" (tmp2)
 		  : "0" (offset), "1" (buf));
 		length -= 8;
 	}
 	while (length > 0) {
-		__asm__ __volatile__("
-			strh	%2, [%0], #4
-		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
 		buf += 2;
 		length -= 2;
 	}
@@ -107,9 +99,8 @@
 read_rreg (unsigned int base_addr, unsigned int reg)
 {
 	unsigned short v;
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		ldrh	%0, [%2, #-4]	@ NET_IDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		ldr%?h	%0, [%2, #-4]	@ NET_IDP
 		" : "=r" (v): "r" (reg), "r" (0xf0000464));
 	return v;
 }
@@ -120,9 +111,7 @@
 	unsigned long address = 0xe0000000 + (off << 1);
 	unsigned short val;
 
-	__asm__("
-		ldrh	%0, [%1]
-		" : "=r" (val): "r" (address));
+	__asm__("ldr%?h	%0, [%1]" : "=r" (val): "r" (address));
 	return val;
 }
 
@@ -134,38 +123,41 @@
 	if ((int)buf & 2) {
 		unsigned int tmp;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			strb	%2, [%1], #1
-			mov	%2, %2, lsr #8
-			strb	%2, [%1], #1
+			ldr%?h	%2, [%0], #4
+			str%?b	%2, [%1], #1
+			mov%?	%2, %2, lsr #8
+			str%?b	%2, [%1], #1
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
 		length -= 2;
 	}
 	while (length > 8) {
 		unsigned int tmp, tmp2, tmp3;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			ldrh	%3, [%0], #4
-			orr	%2, %2, %3, lsl #16
-			ldrh	%3, [%0], #4
-			ldrh	%4, [%0], #4
-			orr	%3, %3, %4, lsl #16
-			stmia	%1!, {%2, %3}
-		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
+			ldr%?h	%2, [%0], #4
+			ldr%?h	%3, [%0], #4
+			orr%?	%2, %2, %3, lsl #16
+			ldr%?h	%3, [%0], #4
+			ldr%?h	%4, [%0], #4
+			orr%?	%3, %3, %4, lsl #16
+			stm%?ia	%1!, {%2, %3}
+		" : "=&r" (offset), "=&r" (buf), "=&r" (tmp), "=&r" (tmp2), "=&r" (tmp3)
 		  : "0" (offset), "1" (buf));
 		length -= 8;
 	}
 	while (length > 0) {
 		unsigned int tmp;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			strb	%2, [%1], #1
-			mov	%2, %2, lsr #8
-			strb	%2, [%1], #1
+			ldr%?h	%2, [%0], #4
+			str%?b	%2, [%1], #1
+			mov%?	%2, %2, lsr #8
+			str%?b	%2, [%1], #1
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
 		length -= 2;
 	}
 }
+#else
+#error Not compatable
+#endif
 
 static int
 am79c961_ramtest(struct device *dev, unsigned int val)
@@ -259,7 +251,7 @@
 	write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
 	write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
 	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
-	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM);
+	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
 	write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
 }
 
@@ -304,7 +296,7 @@
 	/*
 	 * The PNP initialisation should have been done by the ether bootp loader.
 	 */
-	inb ((dev->base_addr + NET_RESET) >> 1);	/* reset the device */
+	inb((dev->base_addr + NET_RESET) >> 1);	/* reset the device */
 
 	udelay (5);
 
@@ -420,26 +412,105 @@
 	return &priv->stats;
 }
 
+static inline u32 update_crc(u32 crc, u8 byte)
+{
+	int i;
+
+	for (i = 8; i != 0; i--) {
+		byte ^= crc & 1;
+		crc >>= 1;
+
+		if (byte & 1)
+			crc ^= 0xedb88320;
+
+		byte >>= 1;
+	}
+
+	return crc;
+}
+
+static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash)
+{
+	if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) {
+	    	int i, idx, bit;
+		u32 crc;
+
+		crc = 0xffffffff;
+
+		for (i = 0; i < ETH_ALEN; i++)
+			crc = update_crc(crc, dmi->dmi_addr[i]);
+
+		idx = crc >> 30;
+		bit = (crc >> 26) & 15;
+
+		hash[idx] |= 1 << bit;
+	}
+}
+
 /*
  * Set or clear promiscuous/multicast mode filter for this adaptor.
- *
- * We don't attempt any packet filtering.  The card may have a SEEQ 8004
- * in which does not have the other ethernet address registers present...
+ * We don't attempt any packet filtering.
  */
 static void am79c961_setmulticastlist (struct device *dev)
 {
 	unsigned long flags;
-	int i;
+	unsigned short multi_hash[4], mode;
+	int i, stopped;
 
-	dev->flags &= ~IFF_ALLMULTI;
+	mode = MODE_PORT0;
 
-	i = MODE_PORT0;
-	if (dev->flags & IFF_PROMISC)
-		i |= MODE_PROMISC;
+	if (dev->flags & IFF_PROMISC) {
+		mode |= MODE_PROMISC;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		memset(multi_hash, 0xff, sizeof(multi_hash));
+	} else {
+		struct dev_mc_list *dmi;
 
-	save_flags_cli (flags);
-	write_rreg (dev->base_addr, MODE, i);
-	restore_flags (flags);
+		memset(multi_hash, 0x00, sizeof(multi_hash));
+
+		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+			am79c961_mc_hash(dmi, multi_hash);
+	}
+
+	save_flags_cli(flags);
+
+	stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
+
+	if (!stopped) {
+		/*
+		 * Put the chip into suspend mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
+
+		/*
+		 * Spin waiting for chip to report suspend mode
+		 */
+		while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
+			restore_flags(flags);
+			nop();
+			save_flags_cli(flags);
+		}
+	}
+
+	/*
+	 * Update the multicast hash table
+	 */
+	for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++)
+		write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
+
+	/*
+	 * Write the mode register
+	 */
+	write_rreg(dev->base_addr, MODE, mode);
+
+	if (!stopped) {
+		/*
+		 * Put the chip back into running mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, 0);
+	}
+
+	restore_flags(flags);
 }
 
 /*
@@ -455,17 +526,20 @@
 		if (!test_and_set_bit(0, (void*)&dev->tbusy)) {
 			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 			unsigned int hdraddr, bufaddr;
+			unsigned int head;
 			unsigned long flags;
 
-			hdraddr = priv->txhdr + (priv->txhead << 3);
-			bufaddr = priv->txbuffer[priv->txhead];
-			priv->txhead ++;
-			if (priv->txhead >= TX_BUFFERS)
-				priv->txhead = 0;
+			head = priv->txhead;
+			hdraddr = priv->txhdr + (head << 3);
+			bufaddr = priv->txbuffer[head];
+			head += 1;
+			if (head >= TX_BUFFERS)
+				head = 0;
 
 			am_writebuffer (dev, bufaddr, skb->data, length);
 			am_writeword (dev, hdraddr + 4, -length);
 			am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+			priv->txhead = head;
 
 			save_flags_cli (flags);
 			write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
@@ -483,10 +557,14 @@
 		int tickssofar = jiffies - dev->trans_start;
 		if (tickssofar < 5)
 			return 1;
-		printk (KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name);
+		printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name);
 		/* Try to restart the adaptor. */
+		disable_irq(dev->irq);
+		am79c961_init(dev);
+		am79c961_init_for_open(dev);
 		dev->tbusy = 0;
 		dev->trans_start = jiffies;
+		enable_irq(dev->irq);
 		goto again;
 	}
 }
@@ -613,7 +691,7 @@
 			am_writeword (dev, hdraddr + 6, 0);
 
 			if (status2 & TST_RTRY)
-				priv->stats.collisions += 1;
+				priv->stats.collisions += 16;
 			if (status2 & TST_LCOL)
 				priv->stats.tx_window_errors ++;
 			if (status2 & TST_LCAR)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/am79c961a.h linux.18p17/drivers/net/am79c961a.h
--- linux.vanilla/drivers/net/am79c961a.h	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/am79c961a.h	Mon Oct  2 10:08:21 2000
@@ -45,6 +45,7 @@
 #define CSR3_EMBA	0x0008
 #define CSR3_DXMT2PD	0x0010
 #define CSR3_LAPPEN	0x0020
+#define CSR3_DXSUFLO	0x0040
 #define CSR3_IDONM	0x0100
 #define CSR3_TINTM	0x0200
 #define CSR3_RINTM	0x0400
@@ -53,6 +54,9 @@
 #define CSR3_BABLM	0x4000
 #define CSR3_MASKALL	0x5F00
 
+#define CTRL1		5
+#define CTRL1_SPND	0x0001
+
 #define LADRL		8
 #define LADRM1		9
 #define LADRM2		10
@@ -97,8 +101,8 @@
 #define TMD_ERR		0x4000
 #define TMD_OWN		0x8000
 
-#define TST_RTRY	0x0200
-#define TST_LCAR	0x0400
+#define TST_RTRY	0x0400
+#define TST_LCAR	0x0800
 #define TST_LCOL	0x1000
 #define TST_UFLO	0x4000
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ariadne.c linux.18p17/drivers/net/ariadne.c
--- linux.vanilla/drivers/net/ariadne.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/ariadne.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ariadne2.c linux.18p17/drivers/net/ariadne2.c
--- linux.vanilla/drivers/net/ariadne2.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/ariadne2.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/arlan.c linux.18p17/drivers/net/arlan.c
--- linux.vanilla/drivers/net/arlan.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/arlan.c	Sat Sep 30 18:06:53 2000
@@ -1346,7 +1346,7 @@
 	priv->timer.function = &arlan_registration_timer;	/* timer handler */
 
 	arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW);
-	udelay(200000);
+	mdelay(200);
 	dev->tbusy = 0;
 	dev->start = 1;
 	add_timer(&priv->timer);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/atari_bionet.c linux.18p17/drivers/net/atari_bionet.c
--- linux.vanilla/drivers/net/atari_bionet.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/atari_bionet.c	Sat Oct 14 00:07:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/atari_pamsnet.c linux.18p17/drivers/net/atari_pamsnet.c
--- linux.vanilla/drivers/net/atari_pamsnet.c	Fri Apr 21 12:46:15 2000
+++ linux.18p17/drivers/net/atari_pamsnet.c	Sat Oct 14 00:07:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/cops.c linux.18p17/drivers/net/cops.c
--- linux.vanilla/drivers/net/cops.c	Fri Apr 21 12:46:16 2000
+++ linux.18p17/drivers/net/cops.c	Sat Sep 30 18:18:15 2000
@@ -363,7 +363,8 @@
         {
                 outb(0, ioaddr+DAYNA_RESET);
                 inb(ioaddr+DAYNA_RESET);
-                udelay(333333);
+                current->state = TASK_UNINTERRUPTIBLE;
+                schedule_timeout(HZ/3);
         }
         if(board==TANGENT)
         {
@@ -499,7 +500,7 @@
                                 schedule();
                 }
                 else
-                        udelay(333333);
+                        mdelay(334);
         }
         dev->tbusy=0;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/cosa.c linux.18p17/drivers/net/cosa.c
--- linux.vanilla/drivers/net/cosa.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/net/cosa.c	Fri Sep  1 22:09:03 2000
@@ -102,13 +102,6 @@
 #include "syncppp.h"
 #include "cosa.h"
 
-/* Linux version stuff */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
-typedef struct wait_queue *wait_queue_head_t;
-#define DECLARE_WAITQUEUE(wait, current) \
-	struct wait_queue wait = { current, NULL }
-#endif
-
 /* Maximum length of the identification string. */
 #define COSA_MAX_ID_STRING	128
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/daynaport.c linux.18p17/drivers/net/daynaport.c
--- linux.vanilla/drivers/net/daynaport.c	Fri Apr 21 12:46:16 2000
+++ linux.18p17/drivers/net/daynaport.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eepro.c linux.18p17/drivers/net/eepro.c
--- linux.vanilla/drivers/net/eepro.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/eepro.c	Tue Sep 26 17:05:33 2000
@@ -23,6 +23,8 @@
 	This is a compatibility hardware problem.
 
 	Versions:
+	0.12c	fixed other multiple cards bug and other cleanups
+		(aris, 08/21/2000)
 	0.12b	added reset when the tx interrupt is called and TX isn't done
 		and other minor fixes. this may fix a problem found after
 		initialization that delays tx until a transmit timeout is 
@@ -212,6 +214,12 @@
 				   version of the 82595 chip. */
 	int stepping;
 	spinlock_t lock; /* Serializing lock  */ 
+	unsigned rcv_ram;
+	unsigned rcv_start;
+	unsigned xmt_bar;
+	unsigned xmt_lower_limit_reg;
+	unsigned xmt_upper_limit_reg;
+	unsigned eeprom_reg;
 };
 
 /* The station (ethernet) address prefix, used for IDing the board. */
@@ -356,24 +364,20 @@
 
 #define	RCV_HEADER	8
 #define RCV_DEFAULT_RAM	0x6000
-#define RCV_RAM 	rcv_ram
-
-static unsigned rcv_ram = RCV_DEFAULT_RAM;
+#define RCV_RAM 	lp->rcv_ram
 
 #define XMT_HEADER	8
 #define XMT_RAM		(RAM_SIZE - RCV_RAM)
 
-#define XMT_START	((rcv_start + RCV_RAM) % RAM_SIZE)
+#define XMT_START	((lp->rcv_start + RCV_RAM) % RAM_SIZE)
 
-#define RCV_LOWER_LIMIT	(rcv_start >> 8)
-#define RCV_UPPER_LIMIT	(((rcv_start + RCV_RAM) - 2) >> 8)
+#define RCV_LOWER_LIMIT	(lp->rcv_start >> 8)
+#define RCV_UPPER_LIMIT	(((lp->rcv_start + RCV_RAM) - 2) >> 8)
 #define XMT_LOWER_LIMIT	(XMT_START >> 8)
 #define XMT_UPPER_LIMIT	(((XMT_START + XMT_RAM) - 2) >> 8)
 
 #define RCV_START_PRO	0x00
 #define RCV_START_10	XMT_RAM
-					/* by default the old driver */
-static unsigned rcv_start = RCV_START_PRO;
 
 #define	RCV_DONE	0x0008
 #define	RX_OK		0x2000
@@ -422,7 +426,6 @@
 
 #define	XMT_BAR_PRO	0x0a
 #define	XMT_BAR_10	0x0b
-static unsigned xmt_bar = XMT_BAR_PRO;
 
 #define	HOST_ADDRESS_REG	0x0c
 #define	IO_PORT		0x0e
@@ -440,8 +443,6 @@
 #define	XMT_UPPER_LIMIT_REG_PRO	0x0b
 #define	XMT_LOWER_LIMIT_REG_10	0x0b
 #define	XMT_UPPER_LIMIT_REG_10	0x0a
-static unsigned xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
-static unsigned xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
 
 /* Bank 2 registers */
 #define	XMT_Chain_Int	0x20	/* Interrupt at the end of the transmit chain */
@@ -466,7 +467,6 @@
 
 #define EEPROM_REG_PRO	0x0a
 #define EEPROM_REG_10	0x0b
-static unsigned eeprom_reg = EEPROM_REG_PRO;
 
 #define EESK 0x01
 #define EECS 0x02
@@ -528,7 +528,8 @@
 #define eepro_ack_tx(ioaddr) outb (TX_INT, ioaddr + STATUS_REG)
 
 /* a complete sel reset */
-#define eepro_complete_selreset(ioaddr) { 	eepro_dis_int(ioaddr);\
+#define eepro_complete_selreset(ioaddr) { 	\
+						/* eepro_dis_int(ioaddr); */ \
 						lp->stats.tx_errors++;\
 						eepro_sel_reset(ioaddr);\
 						lp->tx_end = \
@@ -537,7 +538,7 @@
 						lp->tx_last = 0;\
 						dev->tbusy=0;\
 						dev->trans_start = jiffies;\
-						eepro_en_int(ioaddr);\
+						/*eepro_en_int(ioaddr); */ \
 						eepro_en_rx(ioaddr);\
 					}
 
@@ -670,7 +671,15 @@
 
 			lp = (struct eepro_local *)dev->priv;
 
-			/* Now, get the ethernet hardware address from
+			/* default values */
+			lp->rcv_start = RCV_START_PRO;
+			lp->xmt_bar = XMT_BAR_PRO;
+			lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
+			lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
+			lp->eeprom_reg = EEPROM_REG_PRO;
+			lp->rcv_ram = RCV_DEFAULT_RAM;
+
+		/* Now, get the ethernet hardware address from
 			   the EEPROM */
 
 			station_addr[0] = read_eeprom(ioaddr, 2, dev);
@@ -682,18 +691,18 @@
 			    station_addr[0] == 0xffff) {
 				eepro = 3;
 				lp->eepro = LAN595FX_10ISA;
-				eeprom_reg = EEPROM_REG_10;
-				rcv_start = RCV_START_10;
-				xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
-				xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+				lp->eeprom_reg = EEPROM_REG_10;
+				lp->rcv_start = RCV_START_10;
+				lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
+				lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+				lp->xmt_bar = XMT_BAR_10;
 
 				station_addr[0] = read_eeprom(ioaddr, 2, dev);
 			}
-			
+
 			station_addr[1] = read_eeprom(ioaddr, 3, dev);
 			station_addr[2] = read_eeprom(ioaddr, 4, dev);
 
-
 			if (eepro) {
 				printk("%s: Intel EtherExpress 10 ISA\n at %#x,",
 					dev->name, ioaddr);
@@ -729,7 +738,7 @@
 			else {
 				dev->mem_end = (dev->mem_end * 1024) +
 							(RCV_LOWER_LIMIT << 8);
-				rcv_ram = dev->mem_end - (RCV_LOWER_LIMIT << 8);
+				lp->rcv_ram = dev->mem_end - (RCV_LOWER_LIMIT << 8);
 			}
 
 			/* From now on, dev->mem_end - dev->mem_start contains 
@@ -939,7 +948,7 @@
 	/* Initialize the 82595. */
 
 	eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
-	temp_reg = inb(ioaddr + eeprom_reg);
+	temp_reg = inb(ioaddr + lp->eeprom_reg);
 
 	lp->stepping = temp_reg >> 5;	/* Get the stepping number of the 595 */
 	/* Get the stepping number of the 595 */
@@ -948,7 +957,7 @@
 		printk(KERN_DEBUG "The stepping of the 82595 is %d\n", lp->stepping);
 
 	if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
-		outb(temp_reg & 0xef, ioaddr + eeprom_reg);
+		outb(temp_reg & 0xef, ioaddr + lp->eeprom_reg);
 	for (i=0; i < 6; i++) /* Fill the mac address */
 		outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
 			
@@ -983,8 +992,8 @@
 	/* Initialize the RCV and XMT upper and lower limits */
 	outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); 
 	outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); 
-	outb(XMT_LOWER_LIMIT, ioaddr + xmt_lower_limit_reg); 
-	outb(XMT_UPPER_LIMIT, ioaddr + xmt_upper_limit_reg); 
+	outb(XMT_LOWER_LIMIT, ioaddr + lp->xmt_lower_limit_reg); 
+	outb(XMT_UPPER_LIMIT, ioaddr + lp->xmt_upper_limit_reg); 
 
 	/* Enable the interrupt line. */
 	eepro_en_intline(ioaddr);
@@ -1003,7 +1012,7 @@
 	outw(((RCV_UPPER_LIMIT << 8) | 0xfe), ioaddr + RCV_STOP); 
 
 	/* Initialize XMT */
-	outw((XMT_LOWER_LIMIT << 8), ioaddr + xmt_bar); 
+	outw((XMT_LOWER_LIMIT << 8), ioaddr + lp->xmt_bar); 
 
 	/* Check for the i82595TX and i82595FX */
 	old8 = inb(ioaddr + 8);
@@ -1174,7 +1183,6 @@
 				break;
 			case TX_INT:
 				eepro_ack_tx(ioaddr);
-				break;
 		}
 		if (status & RX_INT) {
 			if (net_debug > 4)
@@ -1330,7 +1338,7 @@
 		outw(eaddrs[0], ioaddr + IO_PORT);
 		outw(eaddrs[1], ioaddr + IO_PORT);
 		outw(eaddrs[2], ioaddr + IO_PORT);
-		outw(lp->tx_end, ioaddr + xmt_bar);
+		outw(lp->tx_end, ioaddr + lp->xmt_bar);
 		outb(MC_SETUP, ioaddr);
 
 		/* Update the transmit queue */
@@ -1392,8 +1400,8 @@
 {
 	int i;
 	unsigned short retval = 0;
-	short ee_addr = ioaddr + eeprom_reg;
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
+	short ee_addr = ioaddr + lp->eeprom_reg;
 	int read_cmd = location | EE_READ_CMD;
 	short ctrl_val = EECS ;
 	
@@ -1512,7 +1520,7 @@
 		status = inw(ioaddr + IO_PORT); 
 
 		if (lp->tx_start == lp->tx_end) {
-			outw(last, ioaddr + xmt_bar);
+			outw(last, ioaddr + lp->xmt_bar);
 			outb(XMT_CMD, ioaddr);			
 			lp->tx_start = last;   /* I don't like to change tx_start here */
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eepro100.c linux.18p17/drivers/net/eepro100.c
--- linux.vanilla/drivers/net/eepro100.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/eepro100.c	Tue Sep  5 22:15:08 2000
@@ -167,6 +167,9 @@
 #ifndef PCI_DEVICE_ID_INTEL_ID1030
 #define PCI_DEVICE_ID_INTEL_ID1030 0x1030
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_ID2449
+#define PCI_DEVICE_ID_INTEL_ID2449 0x2449
+#endif
 
 /* The total I/O port extent of the board.
    The registers beyond 0x18 only exist on the i82558. */
@@ -325,6 +328,10 @@
 	{ "Intel Corporation 82559 InBusiness 10/100",
 	  PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030,
 	  0 
+	},
+	{ "Intel PCI EtherExpress Pro100 82562EM",
+	  PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID2449,
+	  0
 	},
 	{0,}						/* 0 terminated list. */
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/fc/iph5526.c linux.18p17/drivers/net/fc/iph5526.c
--- linux.vanilla/drivers/net/fc/iph5526.c	Fri Apr 21 12:46:17 2000
+++ linux.18p17/drivers/net/fc/iph5526.c	Mon Oct  2 10:13:24 2000
@@ -2242,7 +2242,7 @@
 	/* (i)chip reset */
 	writel(ICHIP_HCR_RESET, fi->i_r.ptr_ichip_hw_control_reg);
 	/*wait for chip to get reset */
-	udelay(10000);
+	mdelay(10);
 	/*de-assert reset */
 	writel(ICHIP_HCR_DERESET, fi->i_r.ptr_ichip_hw_control_reg);
 	
@@ -3884,7 +3884,7 @@
 	/* This is to make sure that the ACC to the PRLI comes in 
 	 * for the last ALPA. 
 	 */
-	udelay(1000000); /* Ugly! Let the Gods forgive me */
+	mdelay(1000); /* Ugly! Let the Gods forgive me */
 
 	DPRINTK1("leaving iph5526_detect\n");
 	return no_of_hosts;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamradio/Makefile linux.18p17/drivers/net/hamradio/Makefile
--- linux.vanilla/drivers/net/hamradio/Makefile	Fri Apr 21 12:46:17 2000
+++ linux.18p17/drivers/net/hamradio/Makefile	Thu Oct 19 01:28:39 2000
@@ -1,10 +1,6 @@
 # File: drivers/hamradio/Makefile
 #
 # Makefile for the Linux AX.25 and HFMODEM device drivers.
-#
-# 19971130 	Moved the amateur radio related network drivers from 
-#		drivers/net/ to drivers/hamradio for easier maintainance.
-#               Joerg Reuter DL1BKE <jreuter@poboxes.com>
 
 
 SUB_DIRS     := 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamradio/bpqether.c linux.18p17/drivers/net/hamradio/bpqether.c
--- linux.vanilla/drivers/net/hamradio/bpqether.c	Fri Apr 21 12:46:17 2000
+++ linux.18p17/drivers/net/hamradio/bpqether.c	Thu Oct 19 01:28:11 2000
@@ -652,7 +652,7 @@
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;
 
-MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@yaina.de>");
 MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
 
 int init_module(void)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamradio/mkiss.c linux.18p17/drivers/net/hamradio/mkiss.c
--- linux.vanilla/drivers/net/hamradio/mkiss.c	Fri Apr 21 12:46:17 2000
+++ linux.18p17/drivers/net/hamradio/mkiss.c	Wed Sep 13 16:18:07 2000
@@ -802,7 +802,7 @@
 static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len)
 {
 	unsigned char *ptr = d;
-	unsigned char c;
+	unsigned char c=0;
 
 	*ptr++ = END;
 	while (len > 0) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamradio/scc.c linux.18p17/drivers/net/hamradio/scc.c
--- linux.vanilla/drivers/net/hamradio/scc.c	Sat Sep  9 18:42:38 2000
+++ linux.18p17/drivers/net/hamradio/scc.c	Thu Oct 19 01:28:39 2000
@@ -19,7 +19,7 @@
 
    ********************************************************************
 
-	Copyright (c) 1993, 1998 Joerg Reuter DL1BKE
+	Copyright (c) 1993, 2000 Joerg Reuter DL1BKE
 
 	portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -127,9 +127,9 @@
 
    vy 73,
    Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@poboxes.com
-		www     : http://poboxes.com/jreuter/
+		AX-25   : DL1BKE @ DB0ABH.#BAY.DEU.EU
+		Internet: jreuter@yaina.de
+		www     : http://yaina.de/jreuter/
 */
 
 /* ----------------------------------------------------------------------- */
@@ -236,7 +236,7 @@
 static int Nchips = 0;
 static io_port Vector_Latch = 0;
 
-MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
+MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
 MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
 MODULE_SUPPORTED_DEVICE("scc");
 
@@ -2242,7 +2242,7 @@
 	result = scc_init();
 
 	if (result == 0)
-		printk(KERN_INFO "Copyright 1993,1998 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n");
+		printk(KERN_INFO "Copyright 1993,2000 Joerg Reuter DL1BKE (jreuter@yaina.de)\n");
 		
 	return result;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ipddp.c linux.18p17/drivers/net/ipddp.c
--- linux.vanilla/drivers/net/ipddp.c	Fri Apr 21 12:46:17 2000
+++ linux.18p17/drivers/net/ipddp.c	Thu Aug 31 15:19:44 2000
@@ -242,7 +242,10 @@
         rt->next = NULL;
         rt->dev = atrtr_get_dev(&rt->at);
         if(rt->dev == NULL)
+        {
+        	kfree(rt);
                 return (-ENETUNREACH);
+        }
 
 	test = ipddp_find_route(rt);
 	if(test != NULL)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/irda/toshoboe.c linux.18p17/drivers/net/irda/toshoboe.c
--- linux.vanilla/drivers/net/irda/toshoboe.c	Sun Jun 11 21:44:14 2000
+++ linux.18p17/drivers/net/irda/toshoboe.c	Sat Sep 30 18:10:07 2000
@@ -902,7 +902,7 @@
 /*FIXME: can't sleep here wait one second */
 
   while ((i--) && (self->txpending))
-    udelay (100000);
+    mdelay (100);
 
   toshoboe_stopchip (self);
   toshoboe_disablebm (self);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ltpc.c linux.18p17/drivers/net/ltpc.c
--- linux.vanilla/drivers/net/ltpc.c	Fri Apr 21 12:46:18 2000
+++ linux.18p17/drivers/net/ltpc.c	Sat Sep 30 15:55:35 2000
@@ -353,13 +353,12 @@
 	/* returns true if it stayed c */
 	/* this uses base+6, but it's ok */
 	int i;
-	int timeout;
 
 	/* twenty second or so total */
 
-	for(i=0;i<20000;i++) {
+	for(i=0;i<200000;i++) {
 		if ( c != inb_p(dev->base_addr+6) ) return 0;
-		for(timeout=loops_per_sec/1000; timeout > 0; timeout--) ;
+		udelay(100);
 	}
 	return 1; /* timed out */
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/mac8390.c linux.18p17/drivers/net/mac8390.c
--- linux.vanilla/drivers/net/mac8390.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/mac8390.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/mac89x0.c linux.18p17/drivers/net/mac89x0.c
--- linux.vanilla/drivers/net/mac89x0.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/mac89x0.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/macmace.c linux.18p17/drivers/net/macmace.c
--- linux.vanilla/drivers/net/macmace.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/macmace.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/macsonic.c linux.18p17/drivers/net/macsonic.c
--- linux.vanilla/drivers/net/macsonic.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/macsonic.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/mvme147.c linux.18p17/drivers/net/mvme147.c
--- linux.vanilla/drivers/net/mvme147.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/mvme147.c	Sat Oct 14 00:02:32 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ne2.c linux.18p17/drivers/net/ne2.c
--- linux.vanilla/drivers/net/ne2.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/ne2.c	Mon Sep 11 17:58:02 2000
@@ -110,16 +110,16 @@
 #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
 
 /* From the .ADF file: */
-static unsigned int addresses[7]=
+static unsigned int addresses[7] __initdata =
 		{0x1000, 0x2020, 0x8020, 0xa0a0, 0xb0b0, 0xc0c0, 0xc3d0};
-static int irqs[4] = {3, 4, 5, 9};
+static int irqs[4] __initdata = {3, 4, 5, 9};
 
 struct ne2_adapters_t {
 	unsigned int	id;
 	char		*name;
 };
 
-const struct ne2_adapters_t ne2_adapters[] = {
+static struct ne2_adapters_t ne2_adapters[] __initdata = {
 	{ 0x6354, "Arco Ethernet Adapter AE/2" },
 	{ 0x70DE, "Compex ENET-16 MC/P" },
 	{ 0x7154, "Novell Ethernet Adapter NE/2" },
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ni52.c linux.18p17/drivers/net/ni52.c
--- linux.vanilla/drivers/net/ni52.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/ni52.c	Sat Sep 30 18:35:22 2000
@@ -160,9 +160,9 @@
 /**************************************************************************/
 
 /* different DELAYs */
-#define DELAY(x) __delay((loops_per_sec>>5)*(x));
-#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
-#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); }
+#define DELAY(x) mdelay(32 * x);
+#define DELAY_16(); { udelay(16); }
+#define DELAY_18(); { udelay(4); }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcnet32.c linux.18p17/drivers/net/pcnet32.c
--- linux.vanilla/drivers/net/pcnet32.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/pcnet32.c	Sat Sep 30 23:21:57 2000
@@ -316,6 +316,12 @@
 	PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000,
 	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
 	pcnet32_probe1},
+    /* Someone apparently got their vendor id wrong ..
+       reported in an IBM box */
+    { "AMD PCnetPCI series (IBM)",
+	0x1023, 0x2000, 0, 0,
+	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
+	pcnet32_probe1},
     { "AMD PCnetHome series",
 	PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0,
 	PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rcif.h linux.18p17/drivers/net/rcif.h
--- linux.vanilla/drivers/net/rcif.h	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/rcif.h	Sat Sep 23 13:59:26 2000
@@ -43,10 +43,10 @@
 /* define a single TCB & buffer */
 typedef struct                  /* a single buffer */
 {
-     U32 context;               /* context */
-     U32 scount;                /* segment count */
-     U32 size;                  /* segment size */
-     U32 addr;                  /* segment physical address */
+     u32 context;               /* context */
+     u32 scount;                /* segment count */
+     u32 size;                  /* segment size */
+     u32 addr;                  /* segment physical address */
 }
 __attribute__((packed))
 singleB, *psingleB ;
@@ -65,7 +65,7 @@
      **  |  <physical address>   |  physical address of buffer  /
      **  +-----------------------+                            _/
      */
-     U32 bcount;                /* buffer count */
+     u32 bcount;                /* buffer count */
      singleB b;                 /* buffer */
 
 }
@@ -82,7 +82,7 @@
       5) Command identifier entry
 
    For Example ("GETSPEED"):
-      1) struct  RCgetspeed_tag { U32 LinkSpeedCode; } RCgetspeed;
+      1) struct  RCgetspeed_tag { u32 LinkSpeedCode; } RCgetspeed;
       2) struct  RCgetspeed_tag *getspeed;
       3) #define RCUS_GETSPEED  data.RCgetspeed;
       4) #define RCUD_GETSPEED  _RC_user_data.getspeed
@@ -121,66 +121,66 @@
      
         /* GETSPEED structure */
         struct  RCgetspeed_tag {
-            U32 LinkSpeedCode;
+            u32 LinkSpeedCode;
         } RCgetspeed;                   /* <---- RCgetspeed */
   
         /* SETSPEED structure */
         struct RCsetspeed_tag {
-            U16 LinkSpeedCode;
+            u16 LinkSpeedCode;
         } RCsetspeed;                   /* <---- RCsetspeed */
 
         /* GETPROM structure */
         struct  RCgetprom_tag {
-            U32 PromMode;
+            u32 PromMode;
         } RCgetprom;                   /* <---- RCgetprom */
   
         /* SETPROM structure */
         struct RCsetprom_tag {
-            U16 PromMode;
+            u16 PromMode;
         } RCsetprom;                   /* <---- RCsetprom */
 
         /* GETBROADCAST structure */
         struct  RCgetbroadcast_tag {
-            U32 BroadcastMode;
+            u32 BroadcastMode;
         } RCgetbroadcast;                   /* <---- RCgetbroadcast */
   
         /* SETBROADCAST structure */
         struct RCsetbroadcast_tag {
-            U16 BroadcastMode;
+            u16 BroadcastMode;
         } RCsetbroadcast;                   /* <---- RCsetbroadcast */
 
         /* GETFIRMWAREVER structure */
         #define FirmStringLen 80
         struct RCgetfwver_tag {
-            U8 FirmString[FirmStringLen];
+            u8 FirmString[FirmStringLen];
         } RCgetfwver;                   /* <---- RCgetfwver */
   
         /* GETIPANDMASK structure */
         struct RCgetipnmask_tag {
-            U32 IpAddr;
-            U32 NetMask;
+            u32 IpAddr;
+            u32 NetMask;
         } RCgetipandmask;               /* <---- RCgetipandmask */
 
         /* SETIPANDMASK structure */
         struct RCsetipnmask_tag {
-            U32 IpAddr;
-            U32 NetMask;
+            u32 IpAddr;
+            u32 NetMask;
         } RCsetipandmask;               /* <---- RCsetipandmask */
 
         /* GETMAC structure */
         #define MAC_SIZE 10
         struct RCgetmac_tag {
-            U8 mac[MAC_SIZE];
+            u8 mac[MAC_SIZE];
         } RCgetmac;                     /* <---- RCgetmac */
 
         /* SETMAC structure */
         struct RCsetmac_tag {
-            U8 mac[MAC_SIZE];
+            u8 mac[MAC_SIZE];
         } RCsetmac;                     /* <---- RCsetmac */
 
         /* GETLINKSTATUS structure */
         struct RCgetlnkstatus_tag {
-            U32 ReturnStatus;
+            u32 ReturnStatus;
         } RCgetlnkstatus;               /* <---- RCgetlnkstatus */
 
         /* GETLINKSTATISTICS structure */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rclanmtl.c linux.18p17/drivers/net/rclanmtl.c
--- linux.vanilla/drivers/net/rclanmtl.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/rclanmtl.c	Fri Sep 29 10:11:45 2000
@@ -231,13 +231,13 @@
 /* I2O message header */
 typedef struct _I2O_MESSAGE_FRAME 
 {
-    U8                          VersionOffset;
-    U8                          MsgFlags;
-    U16                         MessageSize;
+    u8                          VersionOffset;
+    u8                          MsgFlags;
+    u16                         MessageSize;
     BF                          TargetAddress:I2O_TID_SZ;
     BF                          InitiatorAddress:I2O_TID_SZ;
     BF                          Function:I2O_FUNCTION_SZ;
-    U32                         InitiatorContext;
+    u32                         InitiatorContext;
     /* SGL[] */ 
 }
 I2O_MESSAGE_FRAME, *PI2O_MESSAGE_FRAME;
@@ -289,15 +289,15 @@
 typedef struct
 {
     PATU             p_atu;                /* ptr to  ATU register block */
-    PU8              pPci45LinBaseAddr;
-    PU8              pLinOutMsgBlock;
-    U32              outMsgBlockPhyAddr; 
+    u8 *              pPci45LinBaseAddr;
+    u8 *              pLinOutMsgBlock;
+    u32              outMsgBlockPhyAddr; 
     PFNTXCALLBACK    pTransCallbackFunc;
     PFNRXCALLBACK    pRecvCallbackFunc;
     PFNCALLBACK      pRebootCallbackFunc;
     PFNCALLBACK      pCallbackFunc;
-    U16              IOPState;
-    U16              InboundMFrameSize;
+    u16              IOPState;
+    u16              InboundMFrameSize;
 }
 PAB, *PPAB;
 
@@ -357,8 +357,8 @@
 
 
 /* local function prototypes */
-static void ProcessOutboundI2OMsg(PPAB pPab, U32 phyMsgAddr);
-static int FillI2OMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
+static void ProcessOutboundI2OMsg(PPAB pPab, u32 phyMsgAddr);
+static int FillI2OMsgSGLFromTCB(u32 * pMsg, PRCTCB pXmitCntrlBlock);
 static int GetI2OStatus(PPAB pPab);
 static int SendI2OOutboundQInitMsg(PPAB pPab);
 static int SendEnableSysMsg(PPAB pPab);
@@ -386,8 +386,8 @@
 ** =========================================================================
 */
 RC_RETURN
-RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
+RCInitI2OMsgLayer(u16 AdapterID, u32 pciBaseAddr, 
+                  u8 * p_msgbuf,  u8 * p_phymsgbuf,
                   PFNTXCALLBACK  TransmitCallbackFunction,
                   PFNRXCALLBACK  ReceiveCallbackFunction,
                   PFNCALLBACK    RebootCallbackFunction)
@@ -406,7 +406,7 @@
     if (PCIAdapterBlock[AdapterID] != NULL)
     {
         printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
-//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+//        RCResetLANCard(AdapterID, 0, (u32 *)NULL, (PFNCALLBACK)NULL);
         PCIAdapterBlock[AdapterID] = NULL;
     }
 
@@ -417,11 +417,11 @@
     pPab = (PPAB)p_msgbuf;
     
     pPab->p_atu = (PATU)pciBaseAddr;
-    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
+    pPab->pPci45LinBaseAddr =  (u8 *)pciBaseAddr;
     
     /* Set outbound message frame addr - skip over Adapter Block */
-    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
-    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+    pPab->outMsgBlockPhyAddr = (u32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+    pPab->pLinOutMsgBlock    = (u8 *)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
 
     /* store callback function addresses */
     pPab->pTransCallbackFunc = TransmitCallbackFunction;
@@ -440,7 +440,7 @@
     if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL)
     {
         printk("pPab->IOPState == op: resetting adapter\n");
-        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+        RCResetLANCard(AdapterID, 0, (u32 *)NULL, (PFNCALLBACK)NULL);
     }
         
     result = SendI2OOutboundQInitMsg(pPab);
@@ -469,7 +469,7 @@
 */
 #define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
 
-RC_RETURN RCDisableI2OInterrupts(U16 AdapterID)
+RC_RETURN RCDisableI2OInterrupts(u16 AdapterID)
 {
     PPAB pPab;
 
@@ -484,7 +484,7 @@
     return RC_RTN_NO_ERROR;
 }
 
-RC_RETURN RCEnableI2OInterrupts(U16 AdapterID)
+RC_RETURN RCEnableI2OInterrupts(u16 AdapterID)
 {
     PPAB pPab;
 
@@ -506,10 +506,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCI2OSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
+RCI2OSendPacket(u16 AdapterID, u32 InitiatorContext, PRCTCB pTransCtrlBlock)
 {
-    U32 msgOffset;
-    PU32 pMsg;
+    u32 msgOffset;
+    u32 * pMsg;
     int size;
     PPAB pPab;
 
@@ -536,7 +536,7 @@
     }
         
     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
 
@@ -572,10 +572,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
+RCPostRecvBuffers(u16 AdapterID, PRCTCB pTransCtrlBlock)
 {
-    U32 msgOffset;
-    PU32 pMsg;
+    u32 msgOffset;
+    u32 * pMsg;
     int size;
     PPAB pPab;
 
@@ -603,7 +603,7 @@
    
     }
     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
 
@@ -623,7 +623,7 @@
         pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
         pMsg[1] = I2O_LAN_RECEIVE_POST << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
         pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
+        pMsg[3] = *(u32 *)pTransCtrlBlock; /* number of packet buffers */
         /* post to Post Q */   
         pPab->p_atu->InQueue = msgOffset;
         return RC_RTN_NO_ERROR;
@@ -639,12 +639,12 @@
 ** =========================================================================
 */
 void 
-RCProcI2OMsgQ(U16 AdapterID)
+RCProcI2OMsgQ(u16 AdapterID)
 {
-    U32 phyAddrMsg;
-    PU8 p8Msg;
-    PU32 p32;
-    U16 count;
+    u32 phyAddrMsg;
+    u8 * p8Msg;
+    u32 * p32;
+    u16 count;
     PPAB pPab;
     unsigned char debug_msg[20];
     
@@ -658,7 +658,7 @@
     while (phyAddrMsg != 0xFFFFFFFF)
     {
         p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-        p32 = (PU32)p8Msg;
+        p32 = (u32 *)p8Msg;
         
         //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
 
@@ -667,7 +667,7 @@
         */
         if (I2O_LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
         {
-            count = *(PU16)(p8Msg+2);
+            count = *(u16 *)(p8Msg+2);
             count -= p8Msg[0] >> 4;
             /* status, count, context[], adapter */
             (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
@@ -773,14 +773,14 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetLinkStatistics(U16 AdapterID, 
+RCGetLinkStatistics(u16 AdapterID, 
                     P_RCLINKSTATS StatsReturnAddr,
                     PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32, pReturnAddr;
+    u32 msgOffset;
+    volatile u32 timeout;
+    volatile u32 * pMsg;
+    volatile u32 * p32, *pReturnAddr;
     P_NICSTAT pStats;
     int i;
     PPAB pPab;
@@ -803,7 +803,7 @@
     }
 
     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
 /*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
 /*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
@@ -815,7 +815,7 @@
     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS;
     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
 
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
 
     pStats = (P_NICSTAT)p32;
     pStats->dump_status = 0xFFFFFFFF;
@@ -844,7 +844,7 @@
         }
     }
     
-    pReturnAddr = (PU32)StatsReturnAddr;
+    pReturnAddr = (u32 *)StatsReturnAddr;
     
     /* copy Nic stats to user's structure */
     for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
@@ -860,12 +860,12 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
+RCGetLinkStatus(u16 AdapterID, u32 * ReturnAddr, PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset;
+    volatile u32 timeout;
+    volatile u32 * pMsg;
+    volatile u32 * p32;
     PPAB pPab;
 
 /*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
@@ -886,7 +886,7 @@
     }
 
     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 /*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
 /*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
 
@@ -897,7 +897,7 @@
     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
 
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     *p32 = 0xFFFFFFFF;
     
     /* post to Inbound Post Q */
@@ -906,7 +906,7 @@
     timeout = 100000;
     while (1)
     {
-        U32 i;
+        u32 i;
 
         if (WaitCallback)
             (*WaitCallback)();
@@ -941,12 +941,12 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
+RCGetMAC(u16 AdapterID, u8 * mac, PFNWAITCALLBACK WaitCallback)
 {
     unsigned i, timeout;
-    U32      off;
-    PU32     p;
-    U32      temp[2];
+    u32      off;
+    u32 *     p;
+    u32      temp[2];
     PPAB     pPab;
     PATU     p_atu;
     
@@ -965,7 +965,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
 
-    p = (PU32)(pPab->pPci45LinBaseAddr + off);
+    p = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
 #ifdef RCDEBUG
     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
@@ -1025,10 +1025,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCSetMAC(U16 AdapterID, PU8 mac)
+RCSetMAC(u16 AdapterID, u8 * mac)
 {
-    U32  off;
-    PU32 pMsg;
+    u32  off;
+    u32 * pMsg;
     PPAB pPab;
 
 
@@ -1042,7 +1042,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1074,10 +1074,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
+RCSetLinkSpeed(u16 AdapterID, u16 LinkSpeedCode)
 {
-    U32  off;
-    PU32 pMsg;
+    u32  off;
+    u32 * pMsg;
     PPAB pPab;
 
     
@@ -1091,7 +1091,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1116,10 +1116,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+RCSetPromiscuousMode(u16 AdapterID, u16 Mode)
 {
-    U32  off;
-    PU32 pMsg;
+    u32  off;
+    u32 * pMsg;
     PPAB pPab;
     
     pPab =PCIAdapterBlock[AdapterID];
@@ -1132,7 +1132,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1159,11 +1159,11 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+RCGetPromiscuousMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset, timeout;
+    u32 * pMsg;
+    volatile u32 * p32;
     PPAB pPab;
 
     pPab =PCIAdapterBlock[AdapterID];
@@ -1179,10 +1179,10 @@
     }
 
     /* calc virtual address of msg - virtual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     /* virtual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0xff;
 
     /* setup private message */
@@ -1222,7 +1222,7 @@
     }
 
     /* get mode */
-    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+    *pMode = (u8)((volatile u8 *)p32)[0] & 0x0f;
 
     return RC_RTN_NO_ERROR;
 }
@@ -1237,10 +1237,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+RCSetBroadcastMode(u16 AdapterID, u16 Mode)
 {
-    U32  off;
-    PU32 pMsg;
+    u32  off;
+    u32 * pMsg;
     PPAB pPab;
     
     pPab =PCIAdapterBlock[AdapterID];
@@ -1253,7 +1253,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1280,11 +1280,11 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+RCGetBroadcastMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset, timeout;
+    u32 * pMsg;
+    volatile u32 * p32;
     PPAB pPab;
 
     pPab =PCIAdapterBlock[AdapterID];
@@ -1300,10 +1300,10 @@
     }
 
     /* calc virtual address of msg - virtual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     /* virtual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0xff;
 
     /* setup private message */
@@ -1343,7 +1343,7 @@
     }
 
     /* get mode */
-    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+    *pMode = (u8)((volatile u8 *)p32)[0] & 0x0f;
 
     return RC_RTN_NO_ERROR;
 }
@@ -1363,12 +1363,12 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
+RCGetLinkSpeed(u16 AdapterID, u32 * pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
-    U8 IOPLinkSpeed;
+    u32 msgOffset, timeout;
+    u32 * pMsg;
+    volatile u32 * p32;
+    u8 IOPLinkSpeed;
     PPAB pPab;
 
     pPab =PCIAdapterBlock[AdapterID];
@@ -1384,10 +1384,10 @@
     }
 
     /* calc virtual address of msg - virtual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     /* virtual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0xff;
 
     /* setup private message */
@@ -1427,7 +1427,7 @@
     }
 
     /* get Link speed */
-    IOPLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
+    IOPLinkSpeed = (u8)((volatile u8 *)p32)[0] & 0x0f;
 
     *pLinkSpeedCode= IOPLinkSpeed;
 
@@ -1436,7 +1436,7 @@
 
 /*
 ** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
+** RCReportDriverCapability(u16 AdapterID, u32 capability)
 **
 ** Currently defined bits:
 ** WARM_REBOOT_CAPABLE   0x01
@@ -1444,10 +1444,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability)
+RCReportDriverCapability(u16 AdapterID, u32 capability)
 {
-    U32  off;
-    PU32 pMsg;
+    u32  off;
+    u32 * pMsg;
     PPAB pPab;
 
     pPab =PCIAdapterBlock[AdapterID];
@@ -1460,7 +1460,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1484,11 +1484,11 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
+RCGetFirmwareVer(u16 AdapterID, u8 * pFirmString, PFNWAITCALLBACK WaitCallback)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset, timeout;
+    u32 * pMsg;
+    volatile u32 * p32;
     PPAB pPab;
 
     pPab =PCIAdapterBlock[AdapterID];
@@ -1503,10 +1503,10 @@
     }
 
     /* calc virtual address of msg - virtual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     /* virtual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0xff;
 
     /* setup private message */
@@ -1547,7 +1547,7 @@
         }
     }
 
-    strcpy(pFirmString, (PU8)p32);
+    strcpy(pFirmString, (u8 *)p32);
     return RC_RTN_NO_ERROR;
 }
 
@@ -1565,10 +1565,10 @@
 ** =========================================================================
 */
 RC_RETURN 
-RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+RCResetLANCard(u16 AdapterID, u16 ResourceFlags, u32 * ReturnAddr, PFNCALLBACK CallbackFunction)
 {
     unsigned long off;
-    unsigned long *pMsg;
+    u32 *pMsg;
     PPAB pPab;
     int i;
     long timeout = 0;
@@ -1586,7 +1586,7 @@
     
     pPab->pCallbackFunc = CallbackFunction;
 
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup message */
     pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1611,8 +1611,8 @@
                 break;
             }
         }
-        if (ReturnAddr != (PU32)NULL)
-            *ReturnAddr = (U32)pPab->pCallbackFunc;
+        if (ReturnAddr != (u32 *)NULL)
+            *ReturnAddr = (u32)pPab->pCallbackFunc;
     }
 
     return RC_RTN_NO_ERROR ;
@@ -1626,12 +1626,12 @@
 ** =========================================================================
 */
 RC_RETURN 
-RCResetIOP(U16 AdapterID)
+RCResetIOP(u16 AdapterID)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
+    u32 msgOffset, timeout;
+    u32 *pMsg;
     PPAB pPab;
-    volatile PU32 p32;
+    volatile u32 * p32;
     
     pPab = PCIAdapterBlock[AdapterID];
     msgOffset = pPab->p_atu->InQueue;
@@ -1642,7 +1642,7 @@
     }
 
     /* calc virtual address of msg - virtual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
     pMsg[1] = I2O_EXEC_IOP_RESET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
@@ -1656,7 +1656,7 @@
     pMsg[8] = 1;  /*  return 1 byte */
 
     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0;
     p32[1] = 0;
 
@@ -1699,10 +1699,10 @@
 ** =========================================================================
 */
 RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+RCShutdownLANCard(u16 AdapterID, u16 ResourceFlags, u32 * ReturnAddr, PFNCALLBACK CallbackFunction)
 {
-    volatile PU32 pMsg;
-    U32 off;
+    volatile u32 * pMsg;
+    u32 off;
     PPAB pPab;
     int i;
     long timeout = 0;
@@ -1719,7 +1719,7 @@
 
     pPab->pCallbackFunc = CallbackFunction;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup message */
     pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1745,8 +1745,8 @@
                 break;
             }
         }
-        if (ReturnAddr != (PU32)NULL)
-            *ReturnAddr = (U32)pPab->pCallbackFunc;
+        if (ReturnAddr != (u32 *)NULL)
+            *ReturnAddr = (u32)pPab->pCallbackFunc;
     }
     return RC_RTN_NO_ERROR ;
 }
@@ -1765,10 +1765,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
+RCSetRavlinIPandMask(u16 AdapterID, u32 ipAddr, u32 netMask)
 {
-    volatile PU32 pMsg;
-    U32 off;
+    volatile u32 * pMsg;
+    u32 off;
     PPAB pPab;
 
     pPab = PCIAdapterBlock[AdapterID];
@@ -1781,7 +1781,7 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
     
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
     /* setup private message */
     pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1807,12 +1807,12 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+RCGetRavlinIPandMask(u16 AdapterID, u32 * pIpAddr, u32 * pNetMask, 
                      PFNWAITCALLBACK WaitCallback)
 {
     unsigned i, timeout;
-    U32      off;
-    PU32     pMsg, p32;
+    u32      off;
+    u32 *     pMsg, *p32;
     PPAB     pPab;
     PATU     p_atu;
     
@@ -1831,10 +1831,10 @@
     if (0xFFFFFFFF == off)
         return RC_RTN_FREE_Q_EMPTY;
 
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     *p32 = 0xFFFFFFFF;
 
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + off);
 
 #ifdef DEBUG
     kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
@@ -1907,9 +1907,9 @@
 static int 
 SendI2OOutboundQInitMsg(PPAB pPab)
 {
-    U32 msgOffset, timeout, phyOutQFrames, i;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset, timeout, phyOutQFrames, i;
+    volatile u32 * pMsg;
+    volatile u32 * p32;
     
     
     
@@ -1926,7 +1926,7 @@
     
     
     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
 #ifdef DEBUG
     kprintf("SendI2OOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
@@ -1943,7 +1943,7 @@
     pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
 
     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0;
     
     /* post to Inbound Post Q */   
@@ -2009,9 +2009,9 @@
 static int 
 GetI2OStatus(PPAB pPab)
 {
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
+    u32 msgOffset, timeout;
+    u32 * pMsg;
+    volatile u32 * p32;
     
     
     msgOffset = pPab->p_atu->InQueue; 
@@ -2027,7 +2027,7 @@
     }
 
     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
     pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
     pMsg[1] = I2O_EXEC_STATUS_GET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
@@ -2041,7 +2041,7 @@
     pMsg[8] = 88;  /*  return 88 bytes */
 
     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32 = (volatile u32 *)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
     p32[0] = 0;
     p32[1] = 0;
 
@@ -2087,8 +2087,8 @@
     kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
 #endif /* DEBUG */
     /* get IOP state */
-    pPab->IOPState = ((volatile PU8)p32)[10];
-    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
+    pPab->IOPState = ((volatile u8 *)p32)[10];
+    pPab->InboundMFrameSize  = ((volatile u16 *)p32)[6];
     
 #ifdef DEBUG
     kprintf("IOP state 0x%02.2x InFrameSize = 0x%04.4x\n", 
@@ -2108,8 +2108,8 @@
 static int 
 SendEnableSysMsg(PPAB pPab)
 {
-    U32 msgOffset; // timeout;
-    volatile PU32 pMsg;
+    u32 msgOffset; // timeout;
+    volatile u32 * pMsg;
 
     msgOffset = pPab->p_atu->InQueue;
 
@@ -2122,7 +2122,7 @@
     }
 
     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+    pMsg = (u32 *)(pPab->pPci45LinBaseAddr + msgOffset);
 
 #ifdef DEBUG
     kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
@@ -2145,17 +2145,17 @@
 ** =========================================================================
 ** FillI2OMsgFromTCB()
 **
-** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
+** inputs   pMsgu32 - virual pointer (mapped to physical) of message frame
 **          pXmitCntrlBlock - pointer to caller buffer control block.
 **
 ** fills in LAN SGL after Transaction Control Word or Bucket Count.
 ** =========================================================================
 */
 static int 
-FillI2OMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
+FillI2OMsgSGLFromTCB(u32 * pMsgFrame, PRCTCB pTransCtrlBlock)
 {
     unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
-    PU32 pTCB, pMsg;
+    u32 * pTCB, *pMsg;
 
     /* SGL element flags */   
 #define EOB        0x40000000
@@ -2163,7 +2163,7 @@
 #define SIMPLE_SGL 0x10000000
 #define BC_PRESENT 0x01000000
 
-    pTCB = (PU32)pTransCtrlBlock;
+    pTCB = (u32 *)pTransCtrlBlock;
     pMsg = pMsgFrame;
     nmbrDwords = 0;
 
@@ -2248,15 +2248,15 @@
 ** =========================================================================
 */
 static void 
-ProcessOutboundI2OMsg(PPAB pPab, U32 phyAddrMsg)
+ProcessOutboundI2OMsg(PPAB pPab, u32 phyAddrMsg)
 {
-    PU8 p8Msg;
-    PU32 p32;
-    //  U16 count;
+    u8 * p8Msg;
+    u32 * p32;
+    //  u16 count;
     
     
     p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-    p32 = (PU32)p8Msg;
+    p32 = (u32 *)p8Msg;
     
 #ifdef DEBUG
     kprintf("VXD: ProcessOutboundI2OMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rclanmtl.h linux.18p17/drivers/net/rclanmtl.h
--- linux.vanilla/drivers/net/rclanmtl.h	Fri Apr 21 23:18:12 2000
+++ linux.18p17/drivers/net/rclanmtl.h	Sat Sep 23 13:57:41 2000
@@ -39,11 +39,7 @@
 
 /* Linux specific includes */
 #define kprintf printk
-#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
 #include <linux/string.h>
-#else
-#include <string.h>
-#endif
 
 /* PCI/45 Configuration space values */
 #define RC_PCI45_VENDOR_ID  0x4916
@@ -68,12 +64,6 @@
 #define WARM_REBOOT_CAPABLE      0x01
 
  /* scalar data types */
-typedef unsigned char   U8;
-typedef unsigned char*  PU8;
-typedef unsigned short  U16;
-typedef unsigned short* PU16;
-typedef unsigned long   U32;
-typedef unsigned long*  PU32;
 typedef unsigned long   BF;
 typedef int             RC_RETURN;
 
@@ -97,10 +87,10 @@
  ** The User's TransmitCallbackFunction should recover (put back in free queue)
  ** the packet buffers associated with the buffer context values.
  */
-typedef void (*PFNTXCALLBACK)(U32  Status,
-                              U16  PcktCount,
-                              PU32 BufferContext,
-                              U16  AdaterID);
+typedef void (*PFNTXCALLBACK)(u32  Status,
+                              u16  PcktCount,
+                              u32 * BufferContext,
+                              u16  AdaterID);
 
  /* 
  ** type PFNRXCALLBACK 
@@ -111,11 +101,11 @@
  ** The received callback function should process the Packet Descriptor Block
  ** pointed to by PacketDescBlock. See Packet Decription Block below.
  */
-typedef void (*PFNRXCALLBACK)(U32  Status,
-                              U8   PktCount,
-                              U32  BucketsRemain,
-                              PU32 PacketDescBlock,
-                              U16  AdapterID);
+typedef void (*PFNRXCALLBACK)(u32  Status,
+                              u8   PktCount,
+                              u32  BucketsRemain,
+                              u32 * PacketDescBlock,
+                              u16  AdapterID);
 
  /* 
  ** type PFNCALLBACK 
@@ -125,10 +115,10 @@
  ** the reset or shutdown is complete.
  ** Param1 and Param2 are invalid for LANReset and LANShutdown.
  */
-typedef void (*PFNCALLBACK)(U32  Status,
-                              U32  Param1,
-                              U32  Param2,
-                              U16  AdapterID);
+typedef void (*PFNCALLBACK)(u32  Status,
+                              u32  Param1,
+                              u32  Param2,
+                              u16  AdapterID);
 
 /*
 ** Status - Transmit and Receive callback status word 
@@ -275,12 +265,12 @@
  /* Buffer Segment Descriptor */
 typedef struct
 {
-    U32 size;
-    U32 phyAddress;
+    u32 size;
+    u32 phyAddress;
 }
  BSD, *PBSD;
  
-typedef PU32 PRCTCB;
+typedef u32 * PRCTCB;
 /*
 ** -------------------------------------------------------------------------
 ** Exported functions comprising the API to the LAN I2O message transport layer
@@ -310,8 +300,8 @@
  **          ReceiveCallbackFunction  - address of user's RX callback function
  **
  */
-RC_RETURN RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
+RC_RETURN RCInitI2OMsgLayer(u16 AdapterID, u32 pciBaseAddr, 
+                            u8 * p_msgbuf,  u8 * p_phymsgbuf,
                             PFNTXCALLBACK TransmitCallbackFunction,
                             PFNRXCALLBACK ReceiveCallbackFunction,
                             PFNCALLBACK   RebootCallbackFunction);
@@ -326,7 +316,7 @@
  ** 0x04030201 and 0x00FFFFFF on a little endian machine.
  **
  */
-RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
+RC_RETURN RCSetRavlinIPandMask(u16 AdapterID, u32 ipAddr, u32 netMask);
 
 
 /*
@@ -338,7 +328,7 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+RCGetRavlinIPandMask(u16 AdapterID, u32 * pIpAddr, u32 * pNetMask, 
                         PFNWAITCALLBACK WaitCallback);
 
  /* 
@@ -350,7 +340,7 @@
  ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
  ** if a TX or RX transaction has completed.
  */
-void RCProcI2OMsgQ(U16 AdapterID);
+void RCProcI2OMsgQ(u16 AdapterID);
 
 
  /*
@@ -361,8 +351,8 @@
  ** will prevent hardware interrupt to host even though the outbound I2O msg
  ** queue is not emtpy.
  */
-RC_RETURN RCEnableI2OInterrupts(U16 adapterID);
-RC_RETURN RCDisableI2OInterrupts(U16 AdapterID);
+RC_RETURN RCEnableI2OInterrupts(u16 adapterID);
+RC_RETURN RCDisableI2OInterrupts(u16 AdapterID);
 
 
  /* 
@@ -377,7 +367,7 @@
  ** to the RedCreek adapter are considered owned by the adapter until the
  ** context is return to user through the ReceiveCallbackFunction.
  */
-RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
+RC_RETURN RCPostRecvBuffers(u16 AdapterID, PRCTCB pTransactionCtrlBlock);
 #define MAX_NMBR_POST_BUFFERS_PER_MSG 32
 
  /*
@@ -390,30 +380,30 @@
  ** Transmit buffer are considered owned by the adapter until context's
  ** returned to user through the TransmitCallbackFunction.
  */
-RC_RETURN RCI2OSendPacket(U16 AdapterID, 
-                          U32 context, 
+RC_RETURN RCI2OSendPacket(u16 AdapterID, 
+                          u32 context, 
                           PRCTCB pTransactionCtrlBlock);
 
 
  /* Ethernet Link Statistics structure */
 typedef struct tag_RC_link_stats
 {
-    U32 TX_good;      /* good transmit frames */
-    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
-    U32 TX_latecol;   /* frames not TX due to late collisions */
-    U32 TX_urun;      /* frames not TX due to DMA underrun */
-    U32 TX_crs;       /* frames TX with lost carrier sense */
-    U32 TX_def;       /* frames deferred due to activity on link */
-    U32 TX_singlecol; /* frames TX with one and only on collision */
-    U32 TX_multcol;   /* frames TX with more than one collision */
-    U32 TX_totcol;    /* total collisions detected during TX */
-    U32 Rcv_good;     /* good frames received */
-    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
-    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
-    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
-    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
-    U32 Rcv_cdt;      /* RX frames with collision during RX */
-    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
+    u32 TX_good;      /* good transmit frames */
+    u32 TX_maxcol;    /* frames not TX due to MAX collisions */
+    u32 TX_latecol;   /* frames not TX due to late collisions */
+    u32 TX_urun;      /* frames not TX due to DMA underrun */
+    u32 TX_crs;       /* frames TX with lost carrier sense */
+    u32 TX_def;       /* frames deferred due to activity on link */
+    u32 TX_singlecol; /* frames TX with one and only on collision */
+    u32 TX_multcol;   /* frames TX with more than one collision */
+    u32 TX_totcol;    /* total collisions detected during TX */
+    u32 Rcv_good;     /* good frames received */
+    u32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
+    u32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
+    u32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
+    u32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
+    u32 Rcv_cdt;      /* RX frames with collision during RX */
+    u32 Rcv_runt;     /* RX frames shorter than 64 bytes */
 }
  RCLINKSTATS, *P_RCLINKSTATS;
 
@@ -424,7 +414,7 @@
  ** If given, not NULL, the function WaitCallback is called during the wait
  ** loop while waiting for the adapter to respond.
  */
-RC_RETURN RCGetLinkStatistics(U16 AdapterID,
+RC_RETURN RCGetLinkStatistics(u16 AdapterID,
                               P_RCLINKSTATS StatsReturnAddr,
                               PFNWAITCALLBACK WaitCallback);
 
@@ -435,8 +425,8 @@
  ** If given, not NULL, the function WaitCallback is called during the wait
  ** loop while waiting for the adapter to respond.
  */
-RC_RETURN RCGetLinkStatus(U16 AdapterID, 
-                          PU32 pReturnStatus,
+RC_RETURN RCGetLinkStatus(u16 AdapterID, 
+                          u32 * pReturnStatus,
                           PFNWAITCALLBACK WaitCallback);
                                
  /* Link Status defines - value returned in pReturnStatus */
@@ -452,7 +442,7 @@
  ** adapter runs in promiscous mode because of the dual address requirement.
  ** The MAC address is returned to the unsigned char array pointer to by mac.
  */
-RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
+RC_RETURN RCGetMAC(u16 AdapterID, u8 * mac, PFNWAITCALLBACK WaitCallback);
 
  /*
  ** RCSetMAC()
@@ -460,14 +450,14 @@
  ** Set a new user port MAC address.  This address will be returned on
  ** subsequent RCGetMAC() calls.
  */
-RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
+RC_RETURN RCSetMAC(u16 AdapterID, u8 * mac);
 
  /*
  ** RCSetLinkSpeed()
  **
  ** set adapter's link speed based on given input code.
  */
-RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
+RC_RETURN RCSetLinkSpeed(u16 AdapterID, u16 LinkSpeedCode);
  /* Set link speed codes */
 #define LNK_SPD_AUTO_NEG_NWAY   0
 #define LNK_SPD_100MB_FULL      1
@@ -491,10 +481,10 @@
 #define LNK_SPD_10MB_HALF       4
 
 RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
+RCGetLinkSpeed(u16 AdapterID, u32 * pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
 /*
 ** =========================================================================
-** RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+** RCSetPromiscuousMode(u16 AdapterID, u16 Mode)
 **
 ** Defined values for Mode:
 **  0 - turn off promiscuous mode
@@ -505,10 +495,10 @@
 #define PROMISCUOUS_MODE_OFF 0
 #define PROMISCUOUS_MODE_ON  1
 RC_RETURN
-RCSetPromiscuousMode(U16 AdapterID, U16 Mode);
+RCSetPromiscuousMode(u16 AdapterID, u16 Mode);
 /*
 ** =========================================================================
-** RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+** RCGetPromiscuousMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback)
 **
 ** get promiscuous mode setting
 **
@@ -519,11 +509,11 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+RCGetPromiscuousMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback);
 
 /*
 ** =========================================================================
-** RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+** RCSetBroadcastMode(u16 AdapterID, u16 Mode)
 **
 ** Defined values for Mode:
 **  0 - turn off promiscuous mode
@@ -534,10 +524,10 @@
 #define BROADCAST_MODE_OFF 0
 #define BROADCAST_MODE_ON  1
 RC_RETURN
-RCSetBroadcastMode(U16 AdapterID, U16 Mode);
+RCSetBroadcastMode(u16 AdapterID, u16 Mode);
 /*
 ** =========================================================================
-** RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+** RCGetBroadcastMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback)
 **
 ** get broadcast mode setting
 **
@@ -548,10 +538,10 @@
 ** =========================================================================
 */
 RC_RETURN
-RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+RCGetBroadcastMode(u16 AdapterID, u32 * pMode, PFNWAITCALLBACK WaitCallback);
 /*
 ** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
+** RCReportDriverCapability(u16 AdapterID, u32 capability)
 **
 ** Currently defined bits:
 ** WARM_REBOOT_CAPABLE   0x01
@@ -559,7 +549,7 @@
 ** =========================================================================
 */
 RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability);
+RCReportDriverCapability(u16 AdapterID, u32 capability);
 
 /*
 ** RCGetFirmwareVer()
@@ -569,7 +559,7 @@
 ** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
 */
 RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
+RCGetFirmwareVer(u16 AdapterID, u8 * pFirmString, PFNWAITCALLBACK WaitCallback);
 
 /*
 ** ----------------------------------------------
@@ -600,7 +590,7 @@
  ** operation if the receive buffers were returned during LANReset.
  ** Note: The IOP status is not affected by a LAN reset.
  */
-RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+RC_RETURN RCResetLANCard(u16 AdapterID, u16 ResourceFlags, u32 * ReturnAddr, PFNCALLBACK CallbackFunction);
 
 
  /*
@@ -622,7 +612,7 @@
  ** Note: The IOP status is not affected by a LAN shutdown.
  */                                      
 RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+RCShutdownLANCard(u16 AdapterID, u16 ResourceFlags, u32 * ReturnAddr, PFNCALLBACK CallbackFunction);
 
  /*
  ** RCResetIOP();
@@ -632,6 +622,6 @@
  **     Clears outbound message Q. 
  */
 RC_RETURN 
-RCResetIOP(U16 AdapterID);
+RCResetIOP(u16 AdapterID);
 
 #endif /* RCLANMTL_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rcpci45.c linux.18p17/drivers/net/rcpci45.c
--- linux.vanilla/drivers/net/rcpci45.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/rcpci45.c	Fri Sep 29 10:11:45 2000
@@ -114,7 +114,7 @@
 #define DEFAULT_RECV_INIT_CONTEXT  0xA17  
 
 
-static U32 DriverControlWord =  0;
+static u32 DriverControlWord =  0;
 
 static void rc_timer(unsigned long);
 
@@ -131,10 +131,10 @@
     struct device *dev;            
      
     char devname[8];                /* "ethN" string */
-    U8     id;                        /* the AdapterID */
-    U32    pci_addr;               /* the pci address of the adapter */
-    U32    bus;
-    U32    function;
+    u8     id;                        /* the AdapterID */
+    u32    pci_addr;               /* the pci address of the adapter */
+    u32    bus;
+    u32    function;
     struct timer_list timer;        /*  timer */
     struct enet_statistics  stats; /* the statistics structure */
     struct device *next;            /* points to the next RC adapter */
@@ -142,7 +142,7 @@
     unsigned char shutdown;
     unsigned char reboot;
     unsigned char nexus;
-    PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
+    u8 *    PLanApiPA;             /* Pointer to Lan Api Private Area */
 
 }
 DPA, *PDPA;
@@ -169,10 +169,10 @@
 static struct enet_statistics *RCget_stats(struct device *);
 static int RCioctl(struct device *, struct ifreq *, int);
 static int RCconfig(struct device *, struct ifmap *);
-static void RCxmit_callback(U32, U16, PU32, U16);
-static void RCrecv_callback(U32, U8, U32, PU32, U16);
-static void RCreset_callback(U32, U32, U32, U16);
-static void RCreboot_callback(U32, U32, U32, U16);
+static void RCxmit_callback(u32, u16, u32 *, u16);
+static void RCrecv_callback(u32, u8, u32, u32 *, u16);
+static void RCreset_callback(u32, u32, u32, u16);
+static void RCreboot_callback(u32, u32, u32, u16);
 static int RC_allocate_and_post_buffers(struct device *, int);
 
 
@@ -405,14 +405,14 @@
     if ( request_irq(dev->irq, (void *)RCinterrupt,
                      SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
     {
-        printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
+        printk( "RC PCI 45: %s: unable to get IRQ %d\n", (u8 *)dev->name, (uint)dev->irq );
         iounmap(vaddr);
         kfree(dev);
         return 1;
     }
 
     init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
-                  pDpa->PLanApiPA, (PU8)virt_to_bus((void *)pDpa->PLanApiPA),
+                  pDpa->PLanApiPA, (u8 *)virt_to_bus((void *)pDpa->PLanApiPA),
                   (PFNTXCALLBACK)RCxmit_callback,
                   (PFNRXCALLBACK)RCrecv_callback,
                   (PFNCALLBACK)RCreboot_callback);
@@ -560,7 +560,7 @@
      * we'll get the context when the adapter interrupts us to tell us that
      * the transmision is done. At that time, we can free skb.
      */
-    ptcb->b.context = (U32)skb;    
+    ptcb->b.context = (u32)skb;    
     ptcb->b.scount = 1;
     ptcb->b.size = skb->len;
     ptcb->b.addr = virt_to_bus((void *)skb->data);
@@ -569,7 +569,7 @@
     printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
            (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
 #endif
-    if ( (status = RCI2OSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
+    if ( (status = RCI2OSendPacket(pDpa->id, (u32)NULL, (PRCTCB)ptcb))
          != RC_RTN_NO_ERROR)
     {
 #ifdef RCDEBUG
@@ -600,10 +600,10 @@
  * All we need to do is free the buffers.
  */
 static void 
-RCxmit_callback(U32 Status, 
-                U16 PcktCount, 
-                PU32 BufferContext, 
-                U16 AdapterID)
+RCxmit_callback(u32 Status, 
+                u16 PcktCount, 
+                u32 * BufferContext, 
+                u16 AdapterID)
 {
     struct sk_buff *skb;
     PDPA pDpa;
@@ -649,7 +649,7 @@
 }
 
 static void
-RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+RCreset_callback(u32 Status, u32 p1, u32 p2, u16 AdapterID)
 {
     PDPA pDpa;
     struct device *dev;
@@ -693,7 +693,7 @@
 }
 
 static void
-RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+RCreboot_callback(u32 Status, u32 p1, u32 p2, u16 AdapterID)
 {
     PDPA pDpa;
     
@@ -742,14 +742,14 @@
  * filled (one ethernet packet per buffer).
  */
 static void
-RCrecv_callback(U32  Status, 
-                U8   PktCount, 
-                U32  BucketsRemain, 
-                PU32 PacketDescBlock, 
-                U16  AdapterID)
+RCrecv_callback(u32  Status, 
+                u8   PktCount, 
+                u32  BucketsRemain, 
+                u32 * PacketDescBlock, 
+                u16  AdapterID)
 {
 
-    U32 len, count;
+    u32 len, count;
     PDPA pDpa;
     struct sk_buff *skb;
     struct device *dev;
@@ -845,7 +845,7 @@
                      * post one buffer at a time but ... may be that should be 
                      * optimized at some point.
                      */
-                    ptcb->b.context = (U32)skb;    
+                    ptcb->b.context = (u32)skb;    
                     ptcb->b.scount = 1;
                     ptcb->b.size = MAX_ETHER_SIZE;
                     ptcb->b.addr = virt_to_bus((void *)skb->data);
@@ -946,7 +946,7 @@
     {
         init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
                                     pDpa->PLanApiPA, 
-                                    (PU8)virt_to_bus((void *)pDpa->PLanApiPA),
+                                    (u8 *)virt_to_bus((void *)pDpa->PLanApiPA),
                                     (PFNTXCALLBACK)RCxmit_callback,
                                     (PFNRXCALLBACK)RCrecv_callback,
                                     (PFNCALLBACK)RCreboot_callback);
@@ -1180,7 +1180,7 @@
         case RCUC_GETFWVER:
             printk("RC GETFWVER\n");
             RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
-            RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
+            RCGetFirmwareVer(pDpa->id, (u8 *) &RCUD_GETFWVER->FirmString, NULL);
             break;
         case RCUC_GETINFO:
             printk("RC GETINFO\n");
@@ -1193,8 +1193,8 @@
         case RCUC_GETIPANDMASK:
             printk("RC GETIPANDMASK\n");
             RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
-            RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
-                                 (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
+            RCGetRavlinIPandMask(pDpa->id, (u32 *) &RCUD_GETIPANDMASK->IpAddr,
+                                 (u32 *) &RCUD_GETIPANDMASK->NetMask, NULL);
             break;
         case RCUC_GETLINKSTATISTICS:
             printk("RC GETLINKSTATISTICS\n");
@@ -1204,22 +1204,22 @@
         case RCUC_GETLINKSTATUS:
             printk("RC GETLINKSTATUS\n");
             RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
-            RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
+            RCGetLinkStatus(pDpa->id, (u32 *) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
             break;
         case RCUC_GETMAC:
             printk("RC GETMAC\n");
             RCUD_GETMAC = &RCuser.RCUS_GETMAC;
-            RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
+            RCGetMAC(pDpa->id, (u8 *) &RCUD_GETMAC->mac, NULL);
             break;
         case RCUC_GETPROM:
             printk("RC GETPROM\n");
             RCUD_GETPROM = &RCuser.RCUS_GETPROM;
-            RCGetPromiscuousMode(pDpa->id, (PU32) &RCUD_GETPROM->PromMode, NULL);
+            RCGetPromiscuousMode(pDpa->id, (u32 *) &RCUD_GETPROM->PromMode, NULL);
             break;
         case RCUC_GETBROADCAST:
             printk("RC GETBROADCAST\n");
             RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST;
-            RCGetBroadcastMode(pDpa->id, (PU32) &RCUD_GETBROADCAST->BroadcastMode, NULL);
+            RCGetBroadcastMode(pDpa->id, (u32 *) &RCUD_GETBROADCAST->BroadcastMode, NULL);
             break;
         case RCUC_GETSPEED:
             printk("RC GETSPEED\n");
@@ -1229,47 +1229,47 @@
                 return -ENODATA;
             }
             RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
-            RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
-            printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
+            RCGetLinkSpeed(pDpa->id, (u32 *) &RCUD_GETSPEED->LinkSpeedCode, NULL);
+            printk("RC speed = 0x%d\n", RCUD_GETSPEED->LinkSpeedCode);
             break;
         case RCUC_SETIPANDMASK:
             printk("RC SETIPANDMASK\n");
             RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK;
-            printk ("RC New IP Addr = %d.%d.%d.%d, ", (U8) ((RCUD_SETIPANDMASK->IpAddr) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->IpAddr >>  8) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 16) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 24) & 0xff));
-            printk ("RC New Mask = %d.%d.%d.%d\n", (U8) ((RCUD_SETIPANDMASK->NetMask) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->NetMask >>  8) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 16) & 0xff),
-                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 24) & 0xff));
-            RCSetRavlinIPandMask(pDpa->id, (U32) RCUD_SETIPANDMASK->IpAddr,
-                                 (U32) RCUD_SETIPANDMASK->NetMask);
+            printk ("RC New IP Addr = %d.%d.%d.%d, ", (u8) ((RCUD_SETIPANDMASK->IpAddr) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->IpAddr >>  8) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->IpAddr >> 16) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->IpAddr >> 24) & 0xff));
+            printk ("RC New Mask = %d.%d.%d.%d\n", (u8) ((RCUD_SETIPANDMASK->NetMask) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->NetMask >>  8) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->NetMask >> 16) & 0xff),
+                    (u8) ((RCUD_SETIPANDMASK->NetMask >> 24) & 0xff));
+            RCSetRavlinIPandMask(pDpa->id, (u32) RCUD_SETIPANDMASK->IpAddr,
+                                 (u32) RCUD_SETIPANDMASK->NetMask);
             break;
         case RCUC_SETMAC:
             printk("RC SETMAC\n");
             RCUD_SETMAC = &RCuser.RCUS_SETMAC;
             printk ("RC New MAC addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
-                    (U8) (RCUD_SETMAC->mac[0]), (U8) (RCUD_SETMAC->mac[1]), (U8) (RCUD_SETMAC->mac[2]),
-                    (U8) (RCUD_SETMAC->mac[3]), (U8) (RCUD_SETMAC->mac[4]), (U8) (RCUD_SETMAC->mac[5]));
-            RCSetMAC(pDpa->id, (PU8) &RCUD_SETMAC->mac);
+                    (u8) (RCUD_SETMAC->mac[0]), (u8) (RCUD_SETMAC->mac[1]), (u8) (RCUD_SETMAC->mac[2]),
+                    (u8) (RCUD_SETMAC->mac[3]), (u8) (RCUD_SETMAC->mac[4]), (u8) (RCUD_SETMAC->mac[5]));
+            RCSetMAC(pDpa->id, (u8 *) &RCUD_SETMAC->mac);
             break;
         case RCUC_SETSPEED:
             printk("RC SETSPEED\n");
             RCUD_SETSPEED = &RCuser.RCUS_SETSPEED;
-            RCSetLinkSpeed(pDpa->id, (U16) RCUD_SETSPEED->LinkSpeedCode);
+            RCSetLinkSpeed(pDpa->id, (u16) RCUD_SETSPEED->LinkSpeedCode);
             printk("RC New speed = 0x%d\n", RCUD_SETSPEED->LinkSpeedCode);
             break;
         case RCUC_SETPROM:
             printk("RC SETPROM\n");
             RCUD_SETPROM = &RCuser.RCUS_SETPROM;
-            RCSetPromiscuousMode(pDpa->id,(U16)RCUD_SETPROM->PromMode);
+            RCSetPromiscuousMode(pDpa->id,(u16)RCUD_SETPROM->PromMode);
             printk("RC New prom mode = 0x%d\n", RCUD_SETPROM->PromMode);
             break;
         case RCUC_SETBROADCAST:
             printk("RC SETBROADCAST\n");
             RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST;
-            RCSetBroadcastMode(pDpa->id,(U16)RCUD_SETBROADCAST->BroadcastMode);
+            RCSetBroadcastMode(pDpa->id,(u16)RCUD_SETBROADCAST->BroadcastMode);
             printk("RC New broadcast mode = 0x%d\n", RCUD_SETBROADCAST->BroadcastMode);
             break;
         default:
@@ -1349,7 +1349,7 @@
 
     int i;
     PDPA pDpa = (PDPA)dev->priv;
-    PU32 p;
+    u32 * p;
     psingleB pB;
     struct sk_buff *skb;
     RC_RETURN status;
@@ -1365,7 +1365,7 @@
         numBuffers = 32;
     }
     
-    p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+    p = (u32 *) kmalloc(sizeof(u32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
 
 #ifdef RCDEBUG
     printk("rc: TCB = 0x%x\n", (uint)p);
@@ -1378,7 +1378,7 @@
     }
 
     p[0] = 0;                              /* Buffer Count */
-    pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+    pB = (psingleB)((u32)p + sizeof(u32)); /* point to the first buffer */
 
 #ifdef RCDEBUG
     printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
@@ -1408,7 +1408,7 @@
         printk("post 0x%x\n", (uint)skb);
 #endif
         skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
-        pB->context = (U32)skb;
+        pB->context = (u32)skb;
         pB->scount = 1;        /* segment count */
         pB->size = MAX_ETHER_SIZE;
         pB->addr = virt_to_bus((void *)skb->data);
@@ -1419,7 +1419,7 @@
     if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
     {
         printk("rc: Post buffer failed with error code 0x%x!\n", status);
-        pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+        pB = (psingleB)((u32)p + sizeof(u32)); /* point to the first buffer */
         while(p[0])
         {
             skb = (struct sk_buff *)pB->context;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rtl8139.c linux.18p17/drivers/net/rtl8139.c
--- linux.vanilla/drivers/net/rtl8139.c	Sat Sep  9 18:42:40 2000
+++ linux.18p17/drivers/net/rtl8139.c	Tue Sep 12 12:49:40 2000
@@ -1235,8 +1235,9 @@
 			/* Malloc up new buffer, compatible with net-2e. */
 			/* Omit the four octet CRC from the length. */
 			struct sk_buff *skb;
+			int pkt_size = rx_size - 4;
 
-			skb = dev_alloc_skb(rx_size + 2);
+			skb = dev_alloc_skb(pkt_size + 2);
 			if (skb == NULL) {
 				printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n",
 					   dev->name);
@@ -1247,12 +1248,12 @@
 			}
 			skb->dev = dev;
 			skb_reserve(skb, 2);	/* 16 byte align the IP fields. */
-			if (ring_offset+rx_size+4 > RX_BUF_LEN) {
+			if (ring_offset+rx_size > RX_BUF_LEN) {
 				int semi_count = RX_BUF_LEN - ring_offset - 4;
 				memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4],
 					   semi_count);
-				memcpy(skb_put(skb, rx_size-semi_count), rx_ring,
-					   rx_size-semi_count);
+				memcpy(skb_put(skb, pkt_size-semi_count), rx_ring,
+					   pkt_size-semi_count);
 				if (rtl8129_debug > 4) {
 					int i;
 					printk(KERN_DEBUG"%s:  Frame wrap @%d",
@@ -1265,17 +1266,17 @@
 			} else {
 #if 1  /* USE_IP_COPYSUM */
 				eth_copy_and_sum(skb, &rx_ring[ring_offset + 4],
-						 rx_size - 4, 0);
-				skb_put(skb, rx_size - 4);
+						 pkt_size, 0);
+				skb_put(skb, pkt_size);
 #else
-				memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4],
-					   rx_size);
+				memcpy(skb_put(skb, pkt_size), &rx_ring[ring_offset + 4],
+					   pkt_size);
 #endif
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 #if LINUX_VERSION_CODE > 0x20119
-			tp->stats.rx_bytes += rx_size;
+			tp->stats.rx_bytes += pkt_size;
 #endif
 			tp->stats.rx_packets++;
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sbni.c linux.18p17/drivers/net/sbni.c
--- linux.vanilla/drivers/net/sbni.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/sbni.c	Sun Sep  3 13:54:34 2000
@@ -342,7 +342,7 @@
 	if(base_addr > 0x1ff)	/* Check a single specified location. */
 		return sbni_probe1(dev, base_addr);
 	else if(base_addr != 0)	/* Don't probe at all. */
-		return ENXIO;
+		return -ENXIO;
 	for(i = 0; (base_addr = netcard_portlist[i]); i++)
 	{ 
 		if(!check_region(base_addr, SBNI_IO_EXTENT) && base_addr != 1)
@@ -353,7 +353,7 @@
 				return 0;
 		}
 	}
-	return ENODEV;
+	return -ENODEV;
 }
 
 #endif /* have devlist*/
@@ -416,7 +416,7 @@
 	}
 
 	if(bad_card)
-		return ENODEV;
+		return -ENODEV;
 	else
 		outb(0, ioaddr + CSR0); 
 	if(dev->irq < 2)
@@ -429,8 +429,8 @@
 
 		if(autoirq == 0)
 		{
-			printk("sbni probe at %#x failed to detect IRQ line\n", ioaddr);
-			return EAGAIN;
+			printk(KERN_ERR "sbni probe at %#x failed to detect IRQ line\n", ioaddr);
+			return -EAGAIN;
 		}
 	}
 	/* clear FIFO buffer */
@@ -444,7 +444,7 @@
 		if (irqval) 
 		{
 			printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
-			return EAGAIN;
+			return -EAGAIN;
 		}
 	}
      
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sdla.c linux.18p17/drivers/net/sdla.c
--- linux.vanilla/drivers/net/sdla.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/sdla.c	Thu Aug 31 15:20:28 2000
@@ -1218,7 +1218,10 @@
 			return(-ENOMEM);
 		sdla_read(dev, mem.addr, temp, mem.len);
 		if(copy_to_user(mem.data, temp, mem.len))
+		{
+			kfree(temp);
 			return -EFAULT;
+		}
 		kfree(temp);
 	}
 	else
@@ -1227,7 +1230,10 @@
 		if (!temp)
 			return(-ENOMEM);
 		if(copy_from_user(temp, mem.data, mem.len))
+		{
+			kfree(temp);
 			return -EFAULT;
+		}
 		sdla_write(dev, mem.addr, temp, mem.len);
 		kfree(temp);
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sis900.c linux.18p17/drivers/net/sis900.c
--- linux.vanilla/drivers/net/sis900.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/sis900.c	Thu Aug 31 14:30:01 2000
@@ -1,8 +1,8 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:	1.05	Aug 7 1999
-   
-   Modified from the driver which is originally written by Donald Becker. 
+   Revision:	1.06.05	Aug 24 2000
+
+   Modified from the driver which is originally written by Donald Becker.
    
    This software may be used and distributed according to the terms
    of the GNU Public License (GPL), incorporated herein by reference.
@@ -17,11 +17,13 @@
    SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
-   
-   Rev 1.06.02 Nov. 23 1999 Ollie Lho 
+
+   Rev 1.06.05 Aug. 22 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E equalier workaroung rule
+   Rev 1.06.03 Dec. 23 1999 Ollie Lho Third release
+   Rev 1.06.02 Nov. 23 1999 Ollie Lho bug in mac probing fixed
    Rev 1.06.01 Nov. 16 1999 Ollie Lho CRC calculation provide by Joseph Zbiciak (im14u2c@primenet.com)
    Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
-   Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx  
+   Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx
    Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
    Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release
 */
@@ -41,7 +43,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <asm/processor.h>      /* Processor type for cache alignment. */
+#include <asm/processor.h>	/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -49,22 +51,22 @@
 #include "sis900.h"
 
 static const char *version =
-"sis900.c: v1.06.03  12/23/99\n";
+"sis900.c: v1.06.05  08/24/00\n";
 
 static int max_interrupt_work = 20;
+static int multicast_filter_limit = 128;
+
 #define sis900_debug debug
 static int sis900_debug = 0;
 
-static int multicast_filter_limit = 128;
-
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (4*HZ)
 
 struct mac_chip_info {
-        const char *name;
-        u16 	vendor_id, device_id, flags;
-        int 	io_size;
-        struct device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev, 
+	const char *name;
+	u16	vendor_id, device_id, flags;
+	int	io_size;
+	struct device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev,
 				 struct device * net_dev);
 };
 static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
@@ -75,7 +77,7 @@
 	  PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
 	{ "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
 	  PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
-	{0,},                                          /* 0 terminated list. */
+	{0,},					       /* 0 terminated list. */
 };
 
 static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex);
@@ -102,37 +104,38 @@
 };
 
 typedef struct _BufferDesc {
-        u32     link;
-        u32     cmdsts;
-        u32     bufptr;
+	u32	link;
+	u32	cmdsts;
+	u32	bufptr;
 } BufferDesc;
 
 struct sis900_private {
-        struct device *next_module;
-        struct net_device_stats stats;
+	struct device *next_module;
+	struct net_device_stats stats;
 	struct pci_dev * pci_dev;
 
 	struct mac_chip_info * mac;
 	struct mii_phy * mii;
 	unsigned int cur_phy;
 
-        struct timer_list timer;        		/* Link status detection timer. */
-        unsigned int cur_rx, dirty_rx;		
-        unsigned int cur_tx, dirty_tx;
+	struct timer_list timer;		/* Link status detection timer. */
+
+	unsigned int cur_rx, dirty_rx;	/* producer/comsumer pointers for Tx/Rx ring */
+	unsigned int cur_tx, dirty_tx;
 
-        /* The saved address of a sent/receive-in-place packet buffer */
-        struct sk_buff *tx_skbuff[NUM_TX_DESC];
+	/* The saved address of a sent/receive-in-place packet buffer */
+	struct sk_buff *tx_skbuff[NUM_TX_DESC];
 	struct sk_buff *rx_skbuff[NUM_RX_DESC];
-	BufferDesc tx_ring[NUM_TX_DESC];	
-        BufferDesc rx_ring[NUM_RX_DESC];
-        unsigned int tx_full;			/* The Tx queue is full.    */
+	BufferDesc tx_ring[NUM_TX_DESC];
+	BufferDesc rx_ring[NUM_RX_DESC];
 
-        int LinkOn;
+	unsigned int tx_full;		/* The Tx queue is full.    */
+	int LinkOn;
 };
 
 #ifdef MODULE
 #if LINUX_VERSION_CODE > 0x20115
-MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>");
+MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>, Ollie Lho <ollie@sis.com.tw>");
 MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver");
 MODULE_PARM(multicast_filter_limit, "i");
 MODULE_PARM(max_interrupt_work, "i");
@@ -161,6 +164,7 @@
 static u16 sis900_compute_hashtable_index(u8 *addr);
 static void set_rx_mode(struct device *net_dev);
 static void sis900_reset(struct device *net_dev);
+static void sis630e_set_eq(struct device *net_dev);
 
 /* A list of all installed SiS900 devices, for removing the driver module. */
 static struct device *root_sis900_dev = NULL;
@@ -170,10 +174,10 @@
 {
 	int found = 0;
 	struct pci_dev * pci_dev = NULL;
-		
+
 	if (!pci_present())
 		return -ENODEV;
-		
+
 	while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) {
 		/* pci_dev contains all ethernet devices */
 		u32 pci_io_base;
@@ -185,23 +189,23 @@
 			    pci_dev->device == mac->device_id)
 				break;
 		}
-		
+
 		if (mac->vendor_id == 0)
 			/* pci_dev does not match any of our cards */
 			continue;
-		
+
 		/* now, pci_dev should be either 900 or 7016 */
 		pci_io_base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
-		if ((mac->flags & PCI_COMMAND_IO ) && 
+		if ((mac->flags & PCI_COMMAND_IO ) &&
 		    check_region(pci_io_base, mac->io_size))
 			continue;
-		
+
 		/* setup various bits in PCI command register */
 		pci_set_master(pci_dev);
 
 		/* do the real low level jobs */
 		net_dev = mac->probe(mac, pci_dev, net_dev);
-		
+
 		if (net_dev != NULL) {
 			found++;
 		}
@@ -210,15 +214,83 @@
 	return found ? 0 : -ENODEV;
 }
 
-static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, 
+/* older SiS900 and friends, use EEPROM to store MAC address */
+static int sis900_get_mac_addr(struct pci_dev * pci_dev, struct device *net_dev)
+{
+	long ioaddr = pci_dev->base_address[0] & ~3;
+	u16 signature;
+	int i;
+
+	/* check to see if we have sane EEPROM */
+	signature = (u16) read_eeprom(ioaddr, EEPROMSignature);    
+	if (signature == 0xffff || signature == 0x0000) {
+		printk (KERN_INFO "%s: Error EERPOM read %x\n", 
+			net_dev->name, signature);
+		return 0;
+	}
+
+	/* get MAC address from EEPROM */
+	for (i = 0; i < 3; i++)
+	        ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+
+	return 1;
+}
+
+/* SiS630E model, use APC CMOS RAM to store MAC address */
+static int sis630e_get_mac_addr(struct pci_dev * pci_dev, struct device *net_dev)
+{
+	struct pci_dev *isa_bridge = NULL;
+	u8 reg;
+	int i;
+
+	if ((isa_bridge = pci_find_device(0x1039, 0x0008, isa_bridge)) == NULL) {
+		printk("%s: Can not find ISA bridge\n", net_dev->name);
+		return 0;
+	}
+	pci_read_config_byte(isa_bridge, 0x48, &reg);
+	pci_write_config_byte(isa_bridge, 0x48, reg | 0x40);
+
+	for (i = 0; i < 6; i++) {
+		outb(0x09 + i, 0x70);
+		((u8 *)(net_dev->dev_addr))[i] = inb(0x71); 
+	}
+	pci_write_config_byte(isa_bridge, 0x48, reg & ~0x40);
+
+	return 1;
+}
+
+/* SiS630E A1, The Mac address is hardcoded in the RFCR register so it is actually not necessary to
+   probe the MAC address */
+static int sis630ea1_get_mac_addr(struct pci_dev * pci_dev, struct device *net_dev)
+{
+	long ioaddr = pci_dev->base_address[0] & ~3;
+	u32 reg;
+	int i;
+
+	/* reload MAC address */
+	reg = inl(ioaddr + cr);
+	outl(reg | RELOAD, ioaddr + cr);
+
+	reg = inl(ioaddr + cr);
+	outl(reg & ~RELOAD, ioaddr + cr);
+
+	for (i = 0; i < 3; i++) {
+		outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+		((u16 *)(net_dev->dev_addr))[i] = inl(ioaddr + rfdr);
+	}
+
+	return 1;
+}
+
+static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
 					 struct device * net_dev)
 {
 	struct sis900_private *sis_priv;
 	long ioaddr = pci_dev->base_address[0] & ~3;
 	int irq = pci_dev->irq;
 	static int did_version = 0;
-	u16 signature;
-	int i;
+	u8 revision;
+	int i, ret = 0;
 
 	if (did_version++ == 0)
 		printk(KERN_INFO "%s", version);
@@ -226,21 +298,24 @@
 	if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
 		return NULL;
 
-	/* check to see if we have sane EEPROM */
-	signature = (u16) read_eeprom(ioaddr, EEPROMSignature);    
-	if (signature == 0xffff || signature == 0x0000) {
-		printk (KERN_INFO "%s: Error EEPROM read: %x\n",
-			net_dev->name, signature);
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+	if (revision == SIS630E_REV)
+		ret = sis630e_get_mac_addr(pci_dev, net_dev);
+	else if (revision == SIS630EA1_REV) {
+		ret = sis630e_get_mac_addr(pci_dev, net_dev);
+		printk(KERN_INFO "using 630ea1\n");
+	}
+	else
+		ret = sis900_get_mac_addr(pci_dev, net_dev);
+
+	if (ret == 0) {
 		unregister_netdevice(net_dev);
 		return NULL;
 	}
 
+	/* print some information about our NIC */
 	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name,
 	       ioaddr, irq);
-
-	/* get MAC address from EEPROM */
-	for (i = 0; i < 3; i++)
-		((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
 	for (i = 0; i < 5; i++)
 		printk("%2.2x:", (u8)net_dev->dev_addr[i]);
 	printk("%2.2x.\n", net_dev->dev_addr[i]);
@@ -286,247 +361,268 @@
 {
 	struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv;
 	int phy_addr;
-	
+	u8 revision;
+
 	sis_priv->mii = NULL;
-	
+
 	/* search for total of 32 possible mii phy addresses */
 	for (phy_addr = 0; phy_addr < 32; phy_addr++) {
 		u16 mii_status;
 		u16 phy_id0, phy_id1;
 		int i;
-		
+
 		mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
 		if (mii_status == 0xffff || mii_status == 0x0000)
 			/* the mii is not accessable, try next one */
 			continue;
-		
+
 		phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
 		phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);
-		
+
 		/* search our mii table for the current mii */ 
 		for (i = 0; mii_chip_table[i].phy_id1; i++)
 			if (phy_id0 == mii_chip_table[i].phy_id0) {
 				struct mii_phy * mii_phy;
-				
-				printk(KERN_INFO 
+
+				printk(KERN_INFO
 				       "%s: %s transceiver found at address %d.\n",
-				       net_dev->name, mii_chip_table[i].name, 
+				       net_dev->name, mii_chip_table[i].name,
 				       phy_addr);;
 				if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
 					mii_phy->chip_info = mii_chip_table+i;
 					mii_phy->phy_addr = phy_addr;
-					mii_phy->status = mdio_read(net_dev, phy_addr, 
+					mii_phy->status = mdio_read(net_dev, phy_addr,
 								    MII_STATUS);
 					mii_phy->next = sis_priv->mii;
 					sis_priv->mii = mii_phy;
 				}
-				/* the current mii is on our mii_info_table, 
+				/* the current mii is on our mii_info_table,
 				   try next address */
 				break;
 			}
 	}
-	
+
 	if (sis_priv->mii == NULL) {
-		printk(KERN_INFO "%s: No MII transceivers found!\n", 
+		printk(KERN_INFO "%s: No MII transceivers found!\n",
 		       net_dev->name);
 		return 0;
 	}
 
-	/* arbitrary choose that last PHY and current PHY */
+	/* arbitrary choose that last PHY as current PHY */
 	sis_priv->cur_phy = sis_priv->mii->phy_addr;
 	printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
 	       sis_priv->mii->chip_info->name);
 
-	if (sis_priv->mii->status & MII_STAT_LINK) 
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if (revision == SIS630E_REV) {
+		/* SiS 630E has some bugs on default value of PHY registers */
+		mdio_write(net_dev, sis_priv->cur_phy, MII_ANADV, 0x05e1);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG1, 0x22);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG2, 0xff00);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_MASK, 0xffc0);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	
+	}
+
+	if (sis_priv->mii->status & MII_STAT_LINK)
 		sis_priv->LinkOn = TRUE;
 	else
 		sis_priv->LinkOn = FALSE;
-
+	
 	return 1;
 }
 
 /* Delay between EEPROM clock transitions. */
-#define eeprom_delay()  inl(ee_addr)
+#define eeprom_delay()	inl(ee_addr)
 
-/* Read Serial EEPROM through EEPROM Access Register, Note that location is 
+/* Read Serial EEPROM through EEPROM Access Register, Note that location is
    in word (16 bits) unit */
 static u16 read_eeprom(long ioaddr, int location)
 {
 	int i;
-        u16 retval = 0;
-        long ee_addr = ioaddr + mear;
-        u32 read_cmd = location | EEread;
-
-        outl(0, ee_addr);
-        eeprom_delay();
-        outl(EECLK, ee_addr);
-        eeprom_delay();
-
-        /* Shift the read command (9) bits out. */
-        for (i = 8; i >= 0; i--) {
-                u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
-                outl(dataval, ee_addr);
-                eeprom_delay();
-                outl(dataval | EECLK, ee_addr);
-                eeprom_delay();
-        }
-        outb(EECS, ee_addr);
-        eeprom_delay();
+	u16 retval = 0;
+	long ee_addr = ioaddr + mear;
+	u32 read_cmd = location | EEread;
+
+	outl(0, ee_addr);
+	eeprom_delay();
+	outl(EECLK, ee_addr);
+	eeprom_delay();
+
+	/* Shift the read command (9) bits out. */
+	for (i = 8; i >= 0; i--) {
+		u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
+		outl(dataval, ee_addr);
+		eeprom_delay();
+		outl(dataval | EECLK, ee_addr);
+		eeprom_delay();
+	}
+	outb(EECS, ee_addr);
+	eeprom_delay();
 
 	/* read the 16-bits data in */
-        for (i = 16; i > 0; i--) {
-                outl(EECS, ee_addr);
-                eeprom_delay();
-                outl(EECS | EECLK, ee_addr);
-                eeprom_delay();
-                retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
-                eeprom_delay();
-        }
-		
-        /* Terminate the EEPROM access. */
-        outl(0, ee_addr);
-        eeprom_delay();
-        outl(EECLK, ee_addr);
+	for (i = 16; i > 0; i--) {
+		outl(EECS, ee_addr);
+		eeprom_delay();
+		outl(EECS | EECLK, ee_addr);
+		eeprom_delay();
+		retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(0, ee_addr);
+	eeprom_delay();
+	outl(EECLK, ee_addr);
 
-        return (retval);
+	return (retval);
 }
 
 /* Read and write the MII management registers using software-generated
    serial MDIO protocol. Note that the command bits and data bits are
    send out seperately */
-#define mdio_delay()    inl(mdio_addr)
+#define mdio_delay()	inl(mdio_addr)
 
 static void mdio_idle(long mdio_addr)
 {
-        outl(MDIO | MDDIR, mdio_addr);
-        mdio_delay();
-        outl(MDIO | MDDIR | MDC, mdio_addr);
+	outl(MDIO | MDDIR, mdio_addr);
+	mdio_delay();
+	outl(MDIO | MDDIR | MDC, mdio_addr);
 }
 
 /* Syncronize the MII management interface by shifting 32 one bits out. */
 static void mdio_reset(long mdio_addr)
 {
-        int i;
+	int i;
 
-        for (i = 31; i >= 0; i--) {
-                outl(MDDIR | MDIO, mdio_addr);
-                mdio_delay();
-                outl(MDDIR | MDIO | MDC, mdio_addr);
-                mdio_delay();
-        }
-        return;
+	for (i = 31; i >= 0; i--) {
+		outl(MDDIR | MDIO, mdio_addr);
+		mdio_delay();
+		outl(MDDIR | MDIO | MDC, mdio_addr);
+		mdio_delay();
+	}
+	return;
 }
 
 static u16 mdio_read(struct device *net_dev, int phy_id, int location)
 {
-        long mdio_addr = net_dev->base_addr + mear;
-        int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
-        u16 retval = 0;
-        int i;
-
-        mdio_reset(mdio_addr);
-        mdio_idle(mdio_addr);
-
-        for (i = 15; i >= 0; i--) {
-                int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
-                outl(dataval, mdio_addr);
+	long mdio_addr = net_dev->base_addr + mear;
+	int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+	u16 retval = 0;
+	int i;
+
+	mdio_reset(mdio_addr);
+	mdio_idle(mdio_addr);
+
+	for (i = 15; i >= 0; i--) {
+		int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+		outl(dataval, mdio_addr);
 		mdio_delay();
-                outl(dataval | MDC, mdio_addr);
+		outl(dataval | MDC, mdio_addr);
 		mdio_delay();
-        }
+	}
 
-        /* Read the 16 data bits. */
-        for (i = 16; i > 0; i--) {
-                outl(0, mdio_addr);
-                mdio_delay();
-                retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
-                outl(MDC, mdio_addr);
-                mdio_delay();
-        }
-        return retval;
+	/* Read the 16 data bits. */
+	for (i = 16; i > 0; i--) {
+		outl(0, mdio_addr);
+		mdio_delay();
+		retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
+		outl(MDC, mdio_addr);
+		mdio_delay();
+	}
+	outl(0x00, mdio_addr);
+
+	return retval;
 }
 
 static void mdio_write(struct device *net_dev, int phy_id, int location, int value)
 {
-        long mdio_addr = net_dev->base_addr + mear;
-        int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
-        int i;
-
-        mdio_reset(mdio_addr);
-        mdio_idle(mdio_addr);
-
-        /* Shift the command bits out. */
-        for (i = 15; i >= 0; i--) {
-                int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
-                outb(dataval, mdio_addr);
-                mdio_delay();
-                outb(dataval | MDC, mdio_addr);
-                mdio_delay();
-        }
-        mdio_delay();
+	long mdio_addr = net_dev->base_addr + mear;
+	int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+	int i;
+
+	mdio_reset(mdio_addr);
+	mdio_idle(mdio_addr);
+
+	/* Shift the command bits out. */
+	for (i = 15; i >= 0; i--) {
+		int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+		outb(dataval, mdio_addr);
+		mdio_delay();
+		outb(dataval | MDC, mdio_addr);
+		mdio_delay();
+	}
+	mdio_delay();
 
 	/* Shift the value bits out. */
 	for (i = 15; i >= 0; i--) {
 		int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
-	        outl(dataval, mdio_addr);
-	        mdio_delay();
-	        outl(dataval | MDC, mdio_addr);
-	       	mdio_delay();
+		outl(dataval, mdio_addr);
+		mdio_delay();
+		outl(dataval | MDC, mdio_addr);
+		mdio_delay();
 	}
 	mdio_delay();
-	
-        /* Clear out extra bits. */
-        for (i = 2; i > 0; i--) {
-                outb(0, mdio_addr);
-                mdio_delay();
-                outb(MDC, mdio_addr);
-                mdio_delay();
-        }
-        return;
+
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		outb(0, mdio_addr);
+		mdio_delay();
+		outb(MDC, mdio_addr);
+		mdio_delay();
+	}
+	outl(0x00, mdio_addr);
+
+	return;
 }
 
 static int
 sis900_open(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
+	u8 revision;
 
-        /* Soft reset the chip. */
+	/* Soft reset the chip. */
 	sis900_reset(net_dev);
+	
+	/* Equalizer workaroung Rule */
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if (revision == SIS630E_REV || revision == SIS630EA1_REV)
+		sis630e_set_eq(net_dev);
+	
+	if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
+		return -EAGAIN;
+	}
 
-        if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
-                return -EAGAIN;
-        }
-
-        MOD_INC_USE_COUNT;
+	MOD_INC_USE_COUNT;
 
 	sis900_init_rxfilter(net_dev);
 
 	sis900_init_tx_ring(net_dev);
 	sis900_init_rx_ring(net_dev);
 
-        set_rx_mode(net_dev);
+	set_rx_mode(net_dev);
 
-        net_dev->tbusy = 0;
-        net_dev->interrupt = 0;
-        net_dev->start = 1;
+	net_dev->tbusy = 0;
+	net_dev->interrupt = 0;
+	net_dev->start = 1;
 
-        /* Enable all known interrupts by setting the interrupt mask. */
+	/* Enable all known interrupts by setting the interrupt mask. */
 	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
-        outl(RxENA, ioaddr + cr);
-        outl(IE, ioaddr + ier);
+	outl(RxENA, ioaddr + cr);
+	outl(IE, ioaddr + ier);
 
 	sis900_check_mode(net_dev, sis_priv->mii);
 
-        /* Set the timer to switch to check for link beat and perhaps switch
-           to an alternate media type. */
-        init_timer(&sis_priv->timer);
-        sis_priv->timer.expires = jiffies + HZ;
-        sis_priv->timer.data = (unsigned long)net_dev;
-        sis_priv->timer.function = &sis900_timer;
-        add_timer(&sis_priv->timer);
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&sis_priv->timer);
+	sis_priv->timer.expires = jiffies + HZ;
+	sis_priv->timer.data = (unsigned long)net_dev;
+	sis_priv->timer.function = &sis900_timer;
+	add_timer(&sis_priv->timer);
 
-        return 0;
+	return 0;
 }
 
 /* set receive filter address to our MAC address */
@@ -536,7 +632,7 @@
 	long ioaddr = net_dev->base_addr;
 	u32 rfcrSave;
 	u32 i;
-	
+
 	rfcrSave = inl(rfcr + ioaddr);
 
 	/* disable packet filtering before setting filter */
@@ -564,38 +660,38 @@
 static void
 sis900_init_tx_ring(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr; 
-        int i;
-	
-        sis_priv->tx_full = 0;
-        sis_priv->dirty_tx = sis_priv->cur_tx = 0;
-	
-        for (i = 0; i < NUM_TX_DESC; i++) {
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
+	int i;
+
+	sis_priv->tx_full = 0;
+	sis_priv->dirty_tx = sis_priv->cur_tx = 0;
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
 		sis_priv->tx_skbuff[i] = NULL;
 
 		sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]);
-                sis_priv->tx_ring[i].cmdsts = 0;
+		sis_priv->tx_ring[i].cmdsts = 0;
 		sis_priv->tx_ring[i].bufptr = 0;
-        }
+	}
 	sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]);
 
 	/* load Transmit Descriptor Register */
-        outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); 
+	outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp);
 	if (sis900_debug > 2)
-                printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", 
+		printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n",
 		       net_dev->name, inl(ioaddr + txdp));
 }
 
-/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ 
+/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */
 static void 
-sis900_init_rx_ring(struct device *net_dev) 
-{ 
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; 
-        long ioaddr = net_dev->base_addr; 
-        int i;
- 
-        sis_priv->cur_rx = 0; 
+sis900_init_rx_ring(struct device *net_dev)
+{
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
+	int i;
+
+	sis_priv->cur_rx = 0;
 	sis_priv->dirty_rx = 0;
 
 	/* init RX descriptor */
@@ -608,14 +704,14 @@
 	}
 	sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]);
 
-        /* allocate sock buffers */
+	/* allocate sock buffers */
 	for (i = 0; i < NUM_RX_DESC; i++) {
 		struct sk_buff *skb;
 
 		if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
 			/* not enough memory for skbuff, this makes a "hole"
-			   on the buffer ring, it is not clear how the 
-			   hardware will react to this kind of degenerated 
+			   on the buffer ring, it is not clear how the
+			   hardware will react to this kind of degenerated
 			   buffer */
 			break;
 		}
@@ -627,31 +723,97 @@
 	sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
 
 	/* load Receive Descriptor Register */
-        outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
+	outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
 	if (sis900_debug > 2)
-                printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", 
+		printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n",
 		       net_dev->name, inl(ioaddr + rxdp));
 }
+
+/* 630E equalizer workaroung rule(Cyrus Huang 08/15)
+   PHY register 14h(Test)
+   Bit 14: 0 -- Automatically dectect (default)
+           1 -- Manually set Equalizer filter
+   Bit 13: 0 -- (Default)
+           1 -- Speed up convergence of equalizer setting
+   Bit 9 : 0 -- (Default)
+           1 -- Disable Baseline Wander
+   Bit 3~7   -- Equalizer filter setting
+   
+   Link ON: Set Bit 9, 13 to 1, Bit 14 to 0
+            Then calculate equalizer value
+            Then set equalizer value, and set Bit 14 to 1, Bit 9 to 0
+   Link Off:Set Bit 13 to 1, Bit 14 to 0
+   
+   Calculate Equalizer value:
+    When Link is ON and Bit 14 is 0, SIS900PHY will auto-dectect proper equalizer value.
+    When the equalizer is stable, this value is not a fixed value. It will be within
+    a small range(eg. 7~9). Then we get a minimum and a maximum value(eg. min=7, max=9)
+    0 <= max <= 4  --> set equalizer to max
+    5 <= max <= 14 --> set equalizer to max+1 or
+                       set equalizer to max+2 if max == min
+    max >= 15      --> set equalizer to max+5 or
+                       set equalizer to max+6 if max == min
+*/
+static void sis630e_set_eq(struct device *net_dev)
+{
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	u16 reg14h, eq_value, max_value=0, min_value=0;
+	int i, maxcount=10;
+
+	if (sis_priv->LinkOn == TRUE) {
+		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF);
+		for (i=0; i < maxcount; i++) {
+			eq_value=(0x00F8 & mdio_read(net_dev, sis_priv->cur_phy, MII_RESV)) >> 3;
+			max_value=(eq_value > max_value) ? eq_value : max_value;
+			min_value=(eq_value < min_value) ? eq_value : min_value;
+		} 
+		if (max_value < 5)
+			eq_value=max_value;
+		else if (max_value >= 5 && max_value < 15)
+			eq_value=(max_value == min_value) ? max_value+2 : max_value+1;
+		else if (max_value >= 15)
+			eq_value=(max_value == min_value) ? max_value+6 : max_value+5;
+		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);
+		reg14h=(reg14h & 0xFF07) | ((eq_value << 3) & 0x00F8);
+		reg14h=(reg14h | 0x6000) & 0xFDFF;
+		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, reg14h);
+	}
+	else {
+		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);
+		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);
+	}
+	return;
+}
+
 /* on each timer ticks we check two things, Link Status (ON/OFF) and 
    Link Mode (10/100/Full/Half)
  */
 static void sis900_timer(unsigned long data)
 {
-        struct device *net_dev = (struct device *)data;
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	struct device *net_dev = (struct device *)data;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
 	struct mii_phy *mii_phy = sis_priv->mii;
-        static int next_tick = 5*HZ;
-        u16 status;
+	static int next_tick = 5*HZ;
+	u16 status;
+	u8  revision;
 
 	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
 
 	/* current mii phy is failed to link, try another one */
-	while (!(status & MII_STAT_LINK)) {		
-		if (mii_phy->next == NULL) { 
+	while (!(status & MII_STAT_LINK)) {
+		if (mii_phy->next == NULL) {
 			if (sis_priv->LinkOn) {
 				/* link stat change from ON to OFF */
 				next_tick = HZ;
 				sis_priv->LinkOn = FALSE;
+				
+				/* Equalizer workaroung Rule */
+				pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+				if (revision == SIS630E_REV || revision == SIS630EA1_REV)
+					sis630e_set_eq(net_dev);
+
 				printk(KERN_INFO "%s: Media Link Off\n",
 				       net_dev->name);
 			}
@@ -667,19 +829,28 @@
 		/* link stat change forn OFF to ON, read and report link mode */
 		sis_priv->LinkOn = TRUE;
 		next_tick = 5*HZ;
+		
+		/* Equalizer workaroung Rule */
+		pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+		if (revision == SIS630E_REV || revision == SIS630EA1_REV)
+			sis630e_set_eq(net_dev);
+		
 		/* change what cur_phy means */
 		if (mii_phy->phy_addr != sis_priv->cur_phy) {
-			printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name,
-			       mii_phy->chip_info->name);
+			printk(KERN_INFO "%s: Changing transceiver to %s\n",
+			       net_dev->name, mii_phy->chip_info->name);
+			/* disable previous PHY */
 			status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
-			mdio_write(net_dev, sis_priv->cur_phy, 
+			mdio_write(net_dev, sis_priv->cur_phy,
 				   MII_CONTROL, status | MII_CNTL_ISOLATE);
+			/* enable next PHY */
 			status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
-			mdio_write(net_dev, mii_phy->phy_addr, 
+			mdio_write(net_dev, mii_phy->phy_addr,
 				   MII_CONTROL, status & ~MII_CNTL_ISOLATE);
 			sis_priv->cur_phy = mii_phy->phy_addr;
 		}
 		sis900_check_mode(net_dev, mii_phy);
+
 	}
 
 	sis_priv->timer.expires = jiffies + next_tick;
@@ -688,7 +859,7 @@
 static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy)
 {
 	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr;
+	long ioaddr = net_dev->base_addr;
 	int speed, duplex;
 	u32 tx_flags = 0, rx_flags = 0;
 
@@ -718,7 +889,7 @@
 {
 	int i = 0;
 	u32 status;
-	
+
 	/* STSOUT register is Latched on Transition, read operation updates it */
 	while (i++ < 2)
 		status = mdio_read(net_dev, phy_addr, MII_STSOUT);
@@ -736,9 +907,9 @@
 	if (status & MII_STSOUT_LINK_FAIL)
 		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
 	else
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
 		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ? 
+		       *speed == HW_SPEED_100_MBPS ?
 		       "100mbps" : "10mbps",
 		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
 		       "full" : "half");
@@ -747,7 +918,7 @@
 {
 	int i;
 	u16 status;
-	
+
 	for (i = 0; i < 2; i++)
 		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
@@ -765,9 +936,9 @@
 			*duplex = FDX_CAPABLE_HALF_SELECTED;
 
 		if (status & MII_STSSUM_LINK)
-			printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+			printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
 			       net_dev->name,
-			       *speed == HW_SPEED_100_MBPS ? 
+			       *speed == HW_SPEED_100_MBPS ?
 			       "100mbps" : "10mbps",
 			       *duplex == FDX_CAPABLE_FULL_SELECTED ?
 			       "full" : "half");
@@ -779,7 +950,7 @@
 		*speed = HW_SPEED_HOME;
 		*duplex = FDX_CAPABLE_HALF_SELECTED;
 		if (status & MII_STAT_LINK)
-			printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n", 
+			printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n",
 			       net_dev->name);
 		else
 			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
@@ -787,188 +958,189 @@
 }
 static void sis900_tx_timeout(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
 	int i;
 
 	printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
 	       net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
-	
-        /* Disable interrupts by clearing the interrupt mask. */
-        outl(0x0000, ioaddr + imr);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	outl(0x0000, ioaddr + imr);
 
 	/* discard unsent packets, should this code section be protected by
 	   cli(), sti() ?? */
 	sis_priv->dirty_tx = sis_priv->cur_tx = 0;
 	for (i = 0; i < NUM_TX_DESC; i++) {
 		if (sis_priv->tx_skbuff[i] != NULL) {
-		    dev_kfree_skb(sis_priv->tx_skbuff[i]);
-		    sis_priv->tx_skbuff[i] = 0;
+			dev_kfree_skb(sis_priv->tx_skbuff[i]);
+			sis_priv->tx_skbuff[i] = 0;
 			sis_priv->tx_ring[i].cmdsts = 0;
 			sis_priv->tx_ring[i].bufptr = 0;
 			sis_priv->stats.tx_dropped++;
 		}
 	}
-        net_dev->trans_start = jiffies;
+	net_dev->trans_start = jiffies;
 	net_dev->tbusy = sis_priv->tx_full = 0;
 
 	/* FIXME: Should we restart the transmission thread here  ?? */
+	outl(TxENA, ioaddr + cr);
 
-        /* Enable all known interrupts by setting the interrupt mask. */
+	/* Enable all known interrupts by setting the interrupt mask. */
 	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
-        return;
+	return;
 }
 
 static int
 sis900_start_xmit(struct sk_buff *skb, struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr;
-        unsigned int  entry;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
+	unsigned int  entry;
 
 	/* test tbusy to see if we have timeout situation then set it */
-        if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) {
-                if (jiffies - net_dev->trans_start > TX_TIMEOUT)
+	if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) {
+		if (jiffies - net_dev->trans_start > TX_TIMEOUT)
 			sis900_tx_timeout(net_dev);
-                return 1;
-        }
+		return 1;
+	}
 
-        /* Calculate the next Tx descriptor entry. */
-        entry = sis_priv->cur_tx % NUM_TX_DESC;
-        sis_priv->tx_skbuff[entry] = skb;
+	/* Calculate the next Tx descriptor entry. */
+	entry = sis_priv->cur_tx % NUM_TX_DESC;
+	sis_priv->tx_skbuff[entry] = skb;
 
 	/* set the transmit buffer descriptor and enable Transmit State Machine */
 	sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data);
-        sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
-        outl(TxENA, ioaddr + cr);
+	sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
+	outl(TxENA, ioaddr + cr);
 
-        if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) {
+	if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) {
 		/* Typical path, clear tbusy to indicate more 
 		   transmission is possible */
-                clear_bit(0, (void*)&net_dev->tbusy);
-        } else {
+		clear_bit(0, (void*)&net_dev->tbusy);
+	} else {
 		/* no more transmit descriptor avaiable, tbusy remain set */
-                sis_priv->tx_full = 1;
-        }
+		sis_priv->tx_full = 1;
+	}
 
-        net_dev->trans_start = jiffies;
+	net_dev->trans_start = jiffies;
 
-        if (sis900_debug > 3)
-                printk(KERN_INFO "%s: Queued Tx packet at %p size %d "
+	if (sis900_debug > 3)
+		printk(KERN_INFO "%s: Queued Tx packet at %p size %d "
 		       "to slot %d.\n",
 		       net_dev->name, skb->data, (int)skb->len, entry);
 
-        return 0;
+	return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
 static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-        struct device *net_dev = (struct device *)dev_instance;
-        int boguscnt = max_interrupt_work;
+	struct device *net_dev = (struct device *)dev_instance;
+	int boguscnt = max_interrupt_work;
 	long ioaddr = net_dev->base_addr;
 	u32 status;
 
 #if defined(__i386__)
-        /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
-        if (test_and_set_bit(0, (void*)&net_dev->interrupt)) {
-                printk(KERN_INFO "%s: SMP simultaneous entry of "
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&net_dev->interrupt)) {
+		printk(KERN_INFO "%s: SMP simultaneous entry of "
 		       "an interrupt handler.\n", net_dev->name);
-                net_dev->interrupt = 0;     /* Avoid halting machine. */
-                return;
-        }
+		net_dev->interrupt = 0;		/* Avoid halting machine. */
+		return;
+	}
 #else
-        if (net_dev->interrupt) {
-                printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", 
+	if (net_dev->interrupt) {
+		printk(KERN_INFO "%s: Re-entering the interrupt handler.\n",
 		       net_dev->name);
-                return;
-        }
-        net_dev->interrupt = 1;
+		return;
+	}
+	net_dev->interrupt = 1;
 #endif
 
-        do {
-                status = inl(ioaddr + isr);
-		
+	do {
+		status = inl(ioaddr + isr);
+
 		if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0)
 			/* nothing intresting happened */
-                        break;
+			break;
 
 		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */
-                if (status & (RxORN | RxERR | RxOK))
+		if (status & (RxORN | RxERR | RxOK))
 			/* Rx interrupt */
-                        sis900_rx(net_dev);
+			sis900_rx(net_dev);
 
 		if (status & (TxURN | TxERR | TxIDLE))
 			/* Tx interrupt */
 			sis900_finish_xmit(net_dev);
 
-                /* something strange happened !!! */
-                if (status & HIBERR) {
+		/* something strange happened !!! */
+		if (status & HIBERR) {
 			printk(KERN_INFO "%s: Abnormal interrupt,"
 			       "status %#8.8x.\n", net_dev->name, status);
 			break;
 		}
-                if (--boguscnt < 0) {
-                        printk(KERN_INFO "%s: Too much work at interrupt, "
+		if (--boguscnt < 0) {
+			printk(KERN_INFO "%s: Too much work at interrupt, "
 			       "interrupt status = %#8.8x.\n",
 			       net_dev->name, status);
-                        break;
-                }
-        } while (1);
-	
-        if (sis900_debug > 3)
-                printk(KERN_INFO "%s: exiting interrupt, "
+			break;
+		}
+	} while (1);
+
+	if (sis900_debug > 3)
+		printk(KERN_INFO "%s: exiting interrupt, "
 		       "interrupt status = 0x%#8.8x.\n",
 		       net_dev->name, inl(ioaddr + isr));
 	
 #if defined(__i386__)
-        clear_bit(0, (void*)&net_dev->interrupt);
+	clear_bit(0, (void*)&net_dev->interrupt);
 #else
-        net_dev->interrupt = 0;
+	net_dev->interrupt = 0;
 #endif
-        return;
+	return;
 }
 
 /* Process receive interrupt events, put buffer to higher layer and refill buffer pool 
    Note: This fucntion is called by interrupt handler, don't do "too much" work here */
 static int sis900_rx(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        long ioaddr = net_dev->base_addr;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	long ioaddr = net_dev->base_addr;
 	unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
-        u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
-	
-        if (sis900_debug > 3)
-                printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
+	u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
+
+	if (sis900_debug > 3)
+		printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
 		       "status:0x%8.8x\n",
-		       sis_priv->cur_rx, sis_priv->dirty_rx,rx_status);
-	
-        while (rx_status & OWN) {
-                unsigned int rx_size;
-		
+		       sis_priv->cur_rx, sis_priv->dirty_rx, rx_status);
+
+	while (rx_status & OWN) {
+		unsigned int rx_size;
+
 		rx_size = (rx_status & DSIZE) - CRC_SIZE;
-		
+
 		if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
 			/* corrupted packet received */
-                        if (sis900_debug > 3)
-                                printk(KERN_INFO "%s: Corrupted packet "
+			if (sis900_debug > 3)
+				printk(KERN_INFO "%s: Corrupted packet "
 				       "received, buffer status = 0x%8.8x.\n",
 				       net_dev->name, rx_status);
-                        sis_priv->stats.rx_errors++;
+			sis_priv->stats.rx_errors++;
 			if (rx_status & OVERRUN)
 				sis_priv->stats.rx_over_errors++;
 			if (rx_status & (TOOLONG|RUNT))
-                                sis_priv->stats.rx_length_errors++;
-                        if (rx_status & (RXISERR | FAERR))
-                                sis_priv->stats.rx_frame_errors++;
+				sis_priv->stats.rx_length_errors++;
+			if (rx_status & (RXISERR | FAERR))
+				sis_priv->stats.rx_frame_errors++;
 			if (rx_status & CRCERR) 
 				sis_priv->stats.rx_crc_errors++;
 			/* reset buffer descriptor state */
 			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
-                } else {
+		} else {
 			struct sk_buff * skb;
-			
+
 			/* This situation should never happen, but due to
 			   some unknow bugs, it is possible that
 			   we are working on NULL sk_buff :-( */
@@ -976,41 +1148,62 @@
 				printk(KERN_INFO "%s: NULL pointer " 
 				       "encountered in Rx ring, skipping\n",
 				       net_dev->name);
-				break;			
+				break;
 			}
+
+			/* gvie the socket buffer to upper layers */
 			skb = sis_priv->rx_skbuff[entry];
-			sis_priv->rx_skbuff[entry] = NULL;
-			/* reset buffer descriptor state */
-			sis_priv->rx_ring[entry].cmdsts = 0;
-			sis_priv->rx_ring[entry].bufptr = 0;
-			
 			skb_put(skb, rx_size);
-                        skb->protocol = eth_type_trans(skb, net_dev);
-                        netif_rx(skb);
-			
+			skb->protocol = eth_type_trans(skb, net_dev);
+			netif_rx(skb);
+
+			/* some network statistics */
 			if ((rx_status & BCAST) == MCAST)
 				sis_priv->stats.multicast++;
 			net_dev->last_rx = jiffies;
-                        sis_priv->stats.rx_bytes += rx_size;
-                        sis_priv->stats.rx_packets++;
-                }
-                sis_priv->cur_rx++;
+			sis_priv->stats.rx_bytes += rx_size;
+			sis_priv->stats.rx_packets++;
+
+			/* refill the Rx buffer, what if there is not enought memory for
+			   new socket buffer ?? */
+			if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+				/* not enough memory for skbuff, this makes a "hole"
+				   on the buffer ring, it is not clear how the
+				   hardware will react to this kind of degenerated
+				   buffer */
+				printk(KERN_INFO "%s: Memory squeeze,"
+				       "deferring packet.\n",
+				       net_dev->name);
+				sis_priv->rx_skbuff[entry] = NULL;
+				/* reset buffer descriptor state */
+				sis_priv->rx_ring[entry].cmdsts = 0;
+				sis_priv->rx_ring[entry].bufptr = 0;
+				sis_priv->stats.rx_dropped++;
+				break;
+			}
+			skb->dev = net_dev;
+			sis_priv->rx_skbuff[entry] = skb;
+			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+			sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
+			sis_priv->dirty_rx++;
+		}
+		sis_priv->cur_rx++;
 		entry = sis_priv->cur_rx % NUM_RX_DESC;
-                rx_status = sis_priv->rx_ring[entry].cmdsts;
-        } // while
-	
+		rx_status = sis_priv->rx_ring[entry].cmdsts;
+	} // while
+
 	/* refill the Rx buffer, what if the rate of refilling is slower than 
 	   consuming ?? */
 	for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
 		struct sk_buff *skb;
-		
+
 		entry = sis_priv->dirty_rx % NUM_RX_DESC;
-		
+
 		if (sis_priv->rx_skbuff[entry] == NULL) {
 			if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
 				/* not enough memory for skbuff, this makes a "hole"
-				   on the buffer ring, it is not clear how the 
-				   hardware will react to this kind of degenerated 
+				   on the buffer ring, it is not clear how the
+				   hardware will react to this kind of degenerated
 				   buffer */
 				printk(KERN_INFO "%s: Memory squeeze,"
 				       "deferring packet.\n",
@@ -1024,10 +1217,11 @@
 			sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
 		}
 	}
+
 	/* re-enable the potentially idle receive state matchine */
 	outl(RxENA , ioaddr + cr );
-	
-        return 0;
+
+	return 0;
 }
 
 /* finish up transmission of packets, check for error condition and free skbuff etc.
@@ -1035,21 +1229,21 @@
 static void sis900_finish_xmit (struct device *net_dev)
 {
 	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-	
+
 	for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) {
 		unsigned int entry;
 		u32 tx_status;
-		
+
 		entry = sis_priv->dirty_tx % NUM_TX_DESC;
-	        tx_status = sis_priv->tx_ring[entry].cmdsts;
-		
+		tx_status = sis_priv->tx_ring[entry].cmdsts;
+
 		if (tx_status & OWN) {
 			/* The packet is not transmited yet (owned by hardware) !
 			   Note: the interrupt is generated only when Tx Machine
 			   is idle, so this is an almost impossible case */
 			break;
 		}
-		
+
 		if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
 			/* packet unsuccessfully transmited */
 			if (sis900_debug > 3)
@@ -1077,7 +1271,7 @@
 		sis_priv->tx_ring[entry].bufptr = 0;
 		sis_priv->tx_ring[entry].cmdsts = 0;
 	}
-	
+
 	if (sis_priv->tx_full && net_dev->tbusy && 
 	    sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
 		/* The ring is no longer full, clear tbusy, tx_full and 
@@ -1091,23 +1285,23 @@
 static int
 sis900_close(struct device *net_dev)
 {
-        long ioaddr = net_dev->base_addr;
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        int i;
-	
-        net_dev->start = 0;
-        net_dev->tbusy = 1;
+	long ioaddr = net_dev->base_addr;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	int i;
+
+	net_dev->start = 0;
+	net_dev->tbusy = 1;
 
-        /* Disable interrupts by clearing the interrupt mask. */
-        outl(0x0000, ioaddr + imr);
+	/* Disable interrupts by clearing the interrupt mask. */
+	outl(0x0000, ioaddr + imr);
 	outl(0x0000, ioaddr + ier);
 
-        /* Stop the chip's Tx and Rx Status Machine */
-        outl(RxDIS | TxDIS, ioaddr + cr);
+	/* Stop the chip's Tx and Rx Status Machine */
+	outl(RxDIS | TxDIS, ioaddr + cr);
 
-        del_timer(&sis_priv->timer);
+	del_timer(&sis_priv->timer);
 
-        free_irq(net_dev->irq, net_dev);
+	free_irq(net_dev->irq, net_dev);
 
 	/* Free Tx and RX skbuff */
 	for (i = 0; i < NUM_RX_DESC; i++) {
@@ -1115,47 +1309,47 @@
 			dev_kfree_skb(sis_priv->rx_skbuff[i]);
 		sis_priv->rx_skbuff[i] = 0;
 	}
-        for (i = 0; i < NUM_TX_DESC; i++) {
-                if (sis_priv->tx_skbuff[i] != NULL)
-                        dev_kfree_skb(sis_priv->tx_skbuff[i]);
-                sis_priv->tx_skbuff[i] = 0;
-        }
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		if (sis_priv->tx_skbuff[i] != NULL)
+			dev_kfree_skb(sis_priv->tx_skbuff[i]);
+		sis_priv->tx_skbuff[i] = 0;
+	}
 
-        /* Green! Put the chip in low-power mode. */
+	/* Green! Put the chip in low-power mode. */
 
-        MOD_DEC_USE_COUNT;
+	MOD_DEC_USE_COUNT;
 
-        return 0;
+	return 0;
 }
 
 static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
-        u16 *data = (u16 *)&rq->ifr_data;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	u16 *data = (u16 *)&rq->ifr_data;
 
-        switch(cmd) {
-        case SIOCDEVPRIVATE:            	/* Get the address of the PHY in use. */
-                data[0] = sis_priv->mii->phy_addr;
-                /* Fall Through */
-        case SIOCDEVPRIVATE+1:          	/* Read the specified MII register. */
-                data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
-                return 0;
-        case SIOCDEVPRIVATE+2:          	/* Write the specified MII register */
-                if (!suser())
-                        return -EPERM;
-                mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
-                return 0;
-        default:
-                return -EOPNOTSUPP;
-        }
+	switch(cmd) {
+	case SIOCDEVPRIVATE:			/* Get the address of the PHY in use. */
+		data[0] = sis_priv->mii->phy_addr;
+		/* Fall Through */
+	case SIOCDEVPRIVATE+1:			/* Read the specified MII register. */
+		data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
+		return 0;
+	case SIOCDEVPRIVATE+2:			/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
 static struct enet_statistics *
 sis900_get_stats(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
 
-        return &sis_priv->stats;
+	return &sis_priv->stats;
 }
 
 /* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast hash table, which makes
@@ -1183,49 +1377,49 @@
 			byte >>= 1;
 		}
 	}
-	/* leave 7 most siginifant bits */ 
+	/* leave 7 most siginifant bits */
 	return ((int)(crc >> 25));
 }
 
 static void set_rx_mode(struct device *net_dev)
 {
-        long ioaddr = net_dev->base_addr;
-        u16 mc_filter[8];			/* 128 bits multicast hash table */
-        int i;
-        u32 rx_mode;
+	long ioaddr = net_dev->base_addr;
+	u16 mc_filter[8];			/* 128 bits multicast hash table */
+	int i;
+	u32 rx_mode;
 
-        if (net_dev->flags & IFF_PROMISC) {
+	if (net_dev->flags & IFF_PROMISC) {
 		/* Accept any kinds of packets */
 		rx_mode = RFPromiscuous;
-                for (i = 0; i < 8; i++)
-                        mc_filter[i] = 0xffff;
-        } else if ((net_dev->mc_count > multicast_filter_limit) ||
+		for (i = 0; i < 8; i++)
+			mc_filter[i] = 0xffff;
+	} else if ((net_dev->mc_count > multicast_filter_limit) ||
 		   (net_dev->flags & IFF_ALLMULTI)) {
 		/* too many multicast addresses or accept all multicast packets */
-                rx_mode = RFAAB | RFAAM;
-                for (i = 0; i < 8; i++)
-                        mc_filter[i] = 0xffff;
-        } else {
+		rx_mode = RFAAB | RFAAM;
+		for (i = 0; i < 8; i++)
+			mc_filter[i] = 0xffff;
+	} else {
 		/* Accept Broadcast packets, destination addresses match our MAC address,
 		   use Receive Filter to reject unwanted MCAST packets */
-                struct dev_mc_list *mclist;
-                rx_mode = RFAAB;
-                for (i = 0; i < 8; i++)
-                        mc_filter[i]=0;
-                for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
+		struct dev_mc_list *mclist;
+		rx_mode = RFAAB;
+		for (i = 0; i < 8; i++)
+			mc_filter[i]=0;
+		for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
 		     i++, mclist = mclist->next)
-                        set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
 				mc_filter);
-        }
+	}
 
 	/* update Multicast Hash Table in Receive Filter */
-        for (i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++) {
 		/* why plus 0x04 ??, That makes the correct value for hash table. */
-                outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
-                outl(mc_filter[i], ioaddr + rfdr);
-        }
+		outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+		outl(mc_filter[i], ioaddr + rfdr);
+	}
 
-        outl(RFEN | rx_mode, ioaddr + rfcr);
+	outl(RFEN | rx_mode, ioaddr + rfcr);
 
 	/* sis900 is capatable of looping back packet at MAC level for debugging purpose */
 	if (net_dev->flags & IFF_LOOPBACK) {
@@ -1240,33 +1434,33 @@
 		outl(cr_saved, ioaddr + cr);
 	}		
 
-        return;
+	return;
 }
 
 static void sis900_reset(struct device *net_dev)
 {
-        long ioaddr = net_dev->base_addr;
+	long ioaddr = net_dev->base_addr;
 	int i = 0;
 	u32 status = TxRCMP | RxRCMP;
 
-        outl(0, ioaddr + ier);
-        outl(0, ioaddr + imr);
-        outl(0, ioaddr + rfcr);
+	outl(0, ioaddr + ier);
+	outl(0, ioaddr + imr);
+	outl(0, ioaddr + rfcr);
 
-        outl(RxRESET | TxRESET | RESET, ioaddr + cr);
+	outl(RxRESET | TxRESET | RESET, ioaddr + cr);
 	
 	/* Check that the chip has finished the reset. */
 	while (status && (i++ < 1000)) {
 		status ^= (inl(isr + ioaddr) & status);
-        }
+	}
 
-        outl(PESEL, ioaddr + cfg);
+	outl(PESEL, ioaddr + cfg);
 }
 
 #ifdef MODULE
 int init_module(void)
 {
-        return sis900_probe(NULL);
+	return sis900_probe(NULL);
 }
 
 void
@@ -1277,7 +1471,7 @@
 		struct sis900_private *sis_priv =
 			(struct sis900_private *)root_sis900_dev->priv;
 		struct device *next_dev = sis_priv->next_module;
-		
+
 		unregister_netdev(root_sis900_dev);
 		release_region(root_sis900_dev->base_addr,
 			       sis_priv->mac->io_size);
@@ -1285,7 +1479,7 @@
 		kfree(root_sis900_dev);
 
 		root_sis900_dev = next_dev;
-        }
+	}
 }
 
-#endif  /* MODULE */
+#endif	/* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sis900.h linux.18p17/drivers/net/sis900.h
--- linux.vanilla/drivers/net/sis900.h	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/sis900.h	Thu Aug 31 14:30:01 2000
@@ -38,6 +38,7 @@
 
 /* Symbolic names for bits in various registers */
 enum sis900_command_register_bits {
+	RELOAD  = 0x00000400,
 	RESET   = 0x00000100, SWI = 0x00000080, RxRESET = 0x00000020,
 	TxRESET = 0x00000010, RxDIS = 0x00000008, RxENA = 0x00000004,
 	TxDIS   = 0x00000002, TxENA = 0x00000001
@@ -164,7 +165,7 @@
 /* mii registers specific to SiS 900 */
 enum sis_mii_registers {
 	MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012,
-	MII_MASK    = 0x0013
+	MII_MASK    = 0x0013, MII_RESV    = 0x0014
 };
 
 /* mii registers specific to AMD 79C901 */
@@ -214,6 +215,10 @@
 enum mii_stssum_register_bits {
 	MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004,
 	MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD  = 0x0001
+};
+
+enum sis630_revision_id {
+	SIS630E_REV = 0x81, SIS630EA1_REV = 0x83
 };
 
 #define FDX_CAPABLE_DUPLEX_UNKNOWN      0
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sk98lin/skge.c linux.18p17/drivers/net/sk98lin/skge.c
--- linux.vanilla/drivers/net/sk98lin/skge.c	Fri Apr 21 12:46:19 2000
+++ linux.18p17/drivers/net/sk98lin/skge.c	Fri Sep  1 13:48:23 2000
@@ -431,6 +431,7 @@
 		pci_set_master(pdev);
 
 		base_address = pdev->base_address[0];
+		base_address &= PCI_BASE_ADDRESS_MEM_MASK;
 
 #ifdef __sparc_v9__
 		/* SPARC machines do not initialize the chache line size */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/Makefile linux.18p17/drivers/net/skfp/Makefile
--- linux.vanilla/drivers/net/skfp/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/Makefile	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,39 @@
+#
+# Makefile for the SysKonnect FDDI PCI adapter driver
+#
+
+ifeq ($(CONFIG_SKFP),y)
+    O_TARGET := skfp.o
+    O_OBJS =  skfddi.o    hwmtm.o    fplustm.o  smt.o      cfm.o     \
+              ecm.o       pcmplc.o   pmf.o      queue.o    rmt.o     \
+	      smtdef.o    smtinit.o  smttimer.o srf.o      lnkstat.o \
+              smtparse.o  hwt.o      drvfbi.o   ess.o
+else
+  ifeq ($(CONFIG_SKFP),m)
+    MOD_LIST_NAME := SKFP_MODULES
+    M_OBJS := skfp.o
+    O_TARGET := skfp.o
+    O_OBJS =  skfddi.o    hwmtm.o    fplustm.o  smt.o      cfm.o     \
+              ecm.o       pcmplc.o   pmf.o      queue.o    rmt.o     \
+	      smtdef.o    smtinit.o  smttimer.o srf.o      lnkstat.o \
+              smtparse.o  hwt.o      drvfbi.o   ess.o
+  endif
+endif
+
+# NOTE:
+#   Compiling this driver produces some warnings (and some more are 
+#   switched off below), but I did not fix this, because the Hardware
+#   Module source (see skfddi.c for details) is used for different
+#   drivers, and fixing it for Linux might bring problems on other
+#   projects. To keep the source common for all those drivers (and
+#   thus simplify fixes to it), please do not clean it up!
+ 
+EXTRA_CFLAGS += -I. -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes 
+
+include $(TOPDIR)/Rules.make
+
+clean:
+	rm -f core *.o *.a *.s
+
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/can.c linux.18p17/drivers/net/skfp/can.c
--- linux.vanilla/drivers/net/skfp/can.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/can.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	lint
+static const char xID_sccs[] = "@(#)can.c	1.5 97/04/07 (C) SK " ;
+#endif
+
+/*
+ * canonical bit order
+ */
+const u_char canonical[256] = {
+	0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,
+	0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
+	0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,
+	0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
+	0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
+	0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
+	0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,
+	0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
+	0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,
+	0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+	0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,
+	0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
+	0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,
+	0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
+	0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
+	0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
+	0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,
+	0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
+	0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,
+	0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+	0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,
+	0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
+	0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,
+	0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
+	0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
+	0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
+	0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,
+	0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
+	0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,
+	0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+	0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,
+	0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
+} ;
+
+#ifdef	MAKE_TABLE
+int byte_reverse(x)
+int x ;
+{
+	int     y = 0 ;
+
+	if (x & 0x01)
+		y |= 0x80 ;
+	if (x & 0x02)
+		y |= 0x40 ;
+	if (x & 0x04)
+		y |= 0x20 ;
+	if (x & 0x08)
+		y |= 0x10 ;
+	if (x & 0x10)
+		y |= 0x08 ;
+	if (x & 0x20)
+		y |= 0x04 ;
+	if (x & 0x40)
+		y |= 0x02 ;
+	if (x & 0x80)
+		y |= 0x01 ;
+	return(y) ;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/cfm.c linux.18p17/drivers/net/skfp/cfm.c
--- linux.vanilla/drivers/net/skfp/cfm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/cfm.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,642 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT CFM
+	Configuration Management
+	DAS with single MAC
+*/
+
+/*
+ *	Hardware independant state machine implemantation
+ *	The following external SMT functions are referenced :
+ *
+ *		queue_event()
+ *
+ *	The following external HW dependant functions are referenced :
+ *		config_mux()
+ *
+ *	The following HW dependant events are required :
+ *		NONE 
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)cfm.c	2.18 98/10/06 (C) SK " ;
+#endif
+
+/*
+ * FSM Macros
+ */
+#define AFLAG	0x10
+#define GO_STATE(x)	(smc->mib.fddiSMTCF_State = (x)|AFLAG)
+#define ACTIONS_DONE()	(smc->mib.fddiSMTCF_State &= ~AFLAG)
+#define ACTIONS(x)	(x|AFLAG)
+
+#ifdef	DEBUG
+/*
+ * symbolic state names
+ */
+static const char * const cfm_states[] = {
+	"SC0_ISOLATED","CF1","CF2","CF3","CF4",
+	"SC1_WRAP_A","SC2_WRAP_B","SC5_TRHU_B","SC7_WRAP_S",
+	"SC9_C_WRAP_A","SC10_C_WRAP_B","SC11_C_WRAP_S","SC4_THRU_A"
+} ;
+
+/*
+ * symbolic event names
+ */
+static const char * const cfm_events[] = {
+	"NONE","CF_LOOP_A","CF_LOOP_B","CF_JOIN_A","CF_JOIN_B"
+} ;
+#endif
+
+/*
+ * map from state to downstream port type
+ */
+static const u_char cf_to_ptype[] = {
+	TNONE,TNONE,TNONE,TNONE,TNONE,
+	TNONE,TB,TB,TS,
+	TA,TB,TS,TB
+} ;
+
+/*
+ * CEM port states
+ */
+#define	CEM_PST_DOWN	0
+#define	CEM_PST_UP	1
+#define	CEM_PST_HOLD	2
+/* define portstate array only for A and B port */
+/* Do this within the smc structure (use in multiple cards) */
+
+/*
+ * all Globals  are defined in smc.h
+ * struct s_cfm
+ */
+
+/*
+ * function declarations
+ */
+static void cfm_fsm() ;
+
+/*
+	init CFM state machine
+	clear all CFM vars and flags
+*/
+void cfm_init(smc)
+struct s_smc *smc ;
+{
+	smc->mib.fddiSMTCF_State = ACTIONS(SC0_ISOLATED) ;
+	smc->r.rm_join = 0 ;
+	smc->r.rm_loop = 0 ;
+	smc->y[PA].scrub = 0 ;
+	smc->y[PB].scrub = 0 ;
+	smc->y[PA].cem_pst = CEM_PST_DOWN ;
+	smc->y[PB].cem_pst = CEM_PST_DOWN ;
+}
+
+/* Some terms conditions used by the selection criteria */
+#define THRU_ENABLED(smc)	(smc->y[PA].pc_mode != PM_TREE && \
+				 smc->y[PB].pc_mode != PM_TREE)
+/* Selection criteria for the ports */
+static void	selection_criteria (smc,phy)
+struct s_smc	*smc ;
+struct s_phy	*phy ;
+{
+
+	switch (phy->mib->fddiPORTMy_Type) {
+	case TA:
+		if ( !THRU_ENABLED(smc) && smc->y[PB].cf_join ) {
+			phy->wc_flag = TRUE ;
+		} else {
+			phy->wc_flag = FALSE ;
+		}
+
+		break;
+	case TB:
+		/* take precedence over PA */
+		phy->wc_flag = FALSE ;
+		break;
+	case TS:
+		phy->wc_flag = FALSE ;
+		break;
+	case TM:
+		phy->wc_flag = FALSE ;
+		break;
+	}
+
+}
+
+void	all_selection_criteria (smc)
+struct s_smc *smc ;
+{
+	struct s_phy	*phy ;
+	int		p ;
+
+	for ( p = 0,phy = smc->y ; p < NUMPHYS; p++, phy++ ) {
+		/* Do the selection criteria */
+		selection_criteria (smc,phy);
+	}
+}
+
+static void	cem_priv_state (smc, event)
+struct s_smc *smc ;
+int event ;
+/* State machine for private PORT states: used to optimize dual homing */
+{
+	int	np;	/* Number of the port */
+	int	i;
+
+	/* Do this only in a DAS */
+	if (smc->s.sas != SMT_DAS )
+		return ;
+
+	np = event - CF_JOIN;
+
+	if (np != PA && np != PB) {
+		return ;
+	}
+	/* Change the port state according to the event (portnumber) */
+	if (smc->y[np].cf_join) {
+		smc->y[np].cem_pst = CEM_PST_UP ;
+	} else if (!smc->y[np].wc_flag) {
+		/* set the port to done only if it is not withheld */
+		smc->y[np].cem_pst = CEM_PST_DOWN ;
+	}
+
+	/* Don't set an hold port to down */
+
+	/* Check all ports of restart conditions */
+	for (i = 0 ; i < 2 ; i ++ ) {
+		/* Check all port for PORT is on hold and no withhold is done */
+		if ( smc->y[i].cem_pst == CEM_PST_HOLD && !smc->y[i].wc_flag ) {
+			smc->y[i].cem_pst = CEM_PST_DOWN;
+			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
+		}
+		if ( smc->y[i].cem_pst == CEM_PST_UP && smc->y[i].wc_flag ) {
+			smc->y[i].cem_pst = CEM_PST_HOLD;
+			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
+		}
+		if ( smc->y[i].cem_pst == CEM_PST_DOWN && smc->y[i].wc_flag ) {
+			/*
+			 * The port must be restarted when the wc_flag
+			 * will be reset. So set the port on hold.
+			 */
+			smc->y[i].cem_pst = CEM_PST_HOLD;
+		}
+	}
+	return ;
+}
+
+/*
+	CFM state machine
+	called by dispatcher
+
+	do
+		display state change
+		process event
+	until SM is stable
+*/
+void cfm(smc,event)
+struct s_smc *smc ;
+int event ;
+{
+	int	state ;		/* remember last state */
+	int	cond ;
+	int	oldstate ;
+
+	/* We will do the following: */
+	/*  - compute the variable WC_Flag for every port (This is where */
+	/*    we can extend the requested path checking !!) */
+	/*  - do the old (SMT 6.2 like) state machine */
+	/*  - do the resulting station states */
+
+	all_selection_criteria (smc);
+
+	/* We will check now whether a state transition is allowed or not */
+	/*  - change the portstates */
+	cem_priv_state (smc, event);
+
+	oldstate = smc->mib.fddiSMTCF_State ;
+	do {
+		DB_CFM("CFM : state %s%s",
+			(smc->mib.fddiSMTCF_State & AFLAG) ? "ACTIONS " : "",
+			cfm_states[smc->mib.fddiSMTCF_State & ~AFLAG]) ;
+		DB_CFM(" event %s\n",cfm_events[event],0) ;
+		state = smc->mib.fddiSMTCF_State ;
+		cfm_fsm(smc,event) ;
+		event = 0 ;
+	} while (state != smc->mib.fddiSMTCF_State) ;
+
+#ifndef	SLIM_SMT
+	/*
+	 * check peer wrap condition
+	 */
+	cond = FALSE ;
+	if (	(smc->mib.fddiSMTCF_State == SC9_C_WRAP_A &&
+		smc->y[PA].pc_mode == PM_PEER) 	||
+		(smc->mib.fddiSMTCF_State == SC10_C_WRAP_B &&
+		smc->y[PB].pc_mode == PM_PEER) 	||
+		(smc->mib.fddiSMTCF_State == SC11_C_WRAP_S &&
+		smc->y[PS].pc_mode == PM_PEER &&
+		smc->y[PS].mib->fddiPORTNeighborType != TS ) ) {
+			cond = TRUE ;
+	}
+	if (cond != smc->mib.fddiSMTPeerWrapFlag)
+		smt_srf_event(smc,SMT_COND_SMT_PEER_WRAP,0,cond) ;
+
+#if	0
+	/*
+	 * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired
+	 * to the primary path.
+	 */
+	/*
+	 * path change
+	 */
+	if (smc->mib.fddiSMTCF_State != oldstate) {
+		smt_srf_event(smc,SMT_EVENT_MAC_PATH_CHANGE,INDEX_MAC,0) ;
+	}
+#endif
+#endif	/* no SLIM_SMT */
+
+	/*
+	 * set MAC port type
+	 */
+	smc->mib.m[MAC0].fddiMACDownstreamPORTType =
+		cf_to_ptype[smc->mib.fddiSMTCF_State] ;
+	cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ;
+}
+
+/*
+	process CFM event
+*/
+/*ARGSUSED1*/
+static void cfm_fsm(smc,cmd)
+struct s_smc *smc ;
+int cmd ;
+{
+	switch(smc->mib.fddiSMTCF_State) {
+	case ACTIONS(SC0_ISOLATED) :
+		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
+		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
+		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
+		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ;
+		config_mux(smc,MUX_ISOLATE) ;	/* configure PHY Mux */
+		smc->r.rm_loop = FALSE ;
+		smc->r.rm_join = FALSE ;
+		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		/* Don't do the WC-Flag changing here */
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break;
+	case SC0_ISOLATED :
+		/*SC07*/
+		/*SAS port can be PA or PB ! */
+		if (smc->s.sas && (smc->y[PA].cf_join || smc->y[PA].cf_loop ||
+				smc->y[PB].cf_join || smc->y[PB].cf_loop)) {
+			GO_STATE(SC11_C_WRAP_S) ;
+			break ;
+		}
+		/*SC01*/
+		if ((smc->y[PA].cem_pst == CEM_PST_UP && smc->y[PA].cf_join &&
+		     !smc->y[PA].wc_flag) || smc->y[PA].cf_loop) {
+			GO_STATE(SC9_C_WRAP_A) ;
+			break ;
+		}
+		/*SC02*/
+		if ((smc->y[PB].cem_pst == CEM_PST_UP && smc->y[PB].cf_join &&
+		     !smc->y[PB].wc_flag) || smc->y[PB].cf_loop) {
+			GO_STATE(SC10_C_WRAP_B) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(SC9_C_WRAP_A) :
+		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
+		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
+		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
+		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
+		config_mux(smc,MUX_WRAPA) ;		/* configure PHY mux */
+		if (smc->y[PA].cf_loop) {
+			smc->r.rm_join = FALSE ;
+			smc->r.rm_loop = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
+		}
+		if (smc->y[PA].cf_join) {
+			smc->r.rm_loop = FALSE ;
+			smc->r.rm_join = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		}
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break ;
+	case SC9_C_WRAP_A :
+		/*SC10*/
+		if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) &&
+		      !smc->y[PA].cf_loop ) {
+			GO_STATE(SC0_ISOLATED) ;
+			break ;
+		}
+		/*SC12*/
+		else if ( (smc->y[PB].cf_loop && smc->y[PA].cf_join &&
+			   smc->y[PA].cem_pst == CEM_PST_UP) ||
+			  ((smc->y[PB].cf_loop ||
+			   (smc->y[PB].cf_join &&
+			    smc->y[PB].cem_pst == CEM_PST_UP)) &&
+			    (smc->y[PA].pc_mode == PM_TREE ||
+			     smc->y[PB].pc_mode == PM_TREE))) {
+			smc->y[PA].scrub = TRUE ;
+			GO_STATE(SC10_C_WRAP_B) ;
+			break ;
+		}
+		/*SC14*/
+		else if (!smc->s.attach_s &&
+			  smc->y[PA].cf_join &&
+			  smc->y[PA].cem_pst == CEM_PST_UP &&
+			  smc->y[PA].pc_mode == PM_PEER && smc->y[PB].cf_join &&
+			  smc->y[PB].cem_pst == CEM_PST_UP &&
+			  smc->y[PB].pc_mode == PM_PEER) {
+			smc->y[PA].scrub = TRUE ;
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC4_THRU_A) ;
+			break ;
+		}
+		/*SC15*/
+		else if ( smc->s.attach_s &&
+			  smc->y[PA].cf_join &&
+			  smc->y[PA].cem_pst == CEM_PST_UP &&
+			  smc->y[PA].pc_mode == PM_PEER &&
+			  smc->y[PB].cf_join &&
+			  smc->y[PB].cem_pst == CEM_PST_UP &&
+			  smc->y[PB].pc_mode == PM_PEER) {
+			smc->y[PA].scrub = TRUE ;
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC5_THRU_B) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(SC10_C_WRAP_B) :
+		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
+		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
+		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
+		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
+		config_mux(smc,MUX_WRAPB) ;		/* configure PHY mux */
+		if (smc->y[PB].cf_loop) {
+			smc->r.rm_join = FALSE ;
+			smc->r.rm_loop = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
+		}
+		if (smc->y[PB].cf_join) {
+			smc->r.rm_loop = FALSE ;
+			smc->r.rm_join = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		}
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break ;
+	case SC10_C_WRAP_B :
+		/*SC20*/
+		if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) {
+			GO_STATE(SC0_ISOLATED) ;
+			break ;
+		}
+		/*SC21*/
+		else if ( smc->y[PA].cf_loop && smc->y[PA].pc_mode == PM_PEER &&
+			  smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC9_C_WRAP_A) ;
+			break ;
+		}
+		/*SC24*/
+		else if (!smc->s.attach_s &&
+			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
+			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
+			smc->y[PA].scrub = TRUE ;
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC4_THRU_A) ;
+			break ;
+		}
+		/*SC25*/
+		else if ( smc->s.attach_s &&
+			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
+			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
+			smc->y[PA].scrub = TRUE ;
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC5_THRU_B) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(SC4_THRU_A) :
+		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
+		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
+		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
+		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
+		config_mux(smc,MUX_THRUA) ;		/* configure PHY mux */
+		smc->r.rm_loop = FALSE ;
+		smc->r.rm_join = TRUE ;
+		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break ;
+	case SC4_THRU_A :
+		/*SC41*/
+		if (smc->y[PB].wc_flag || !smc->y[PB].cf_join) {
+			smc->y[PA].scrub = TRUE ;
+			GO_STATE(SC9_C_WRAP_A) ;
+			break ;
+		}
+		/*SC42*/
+		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC10_C_WRAP_B) ;
+			break ;
+		}
+		/*SC45*/
+		else if (smc->s.attach_s) {
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC5_THRU_B) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(SC5_THRU_B) :
+		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
+		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
+		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
+		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
+		config_mux(smc,MUX_THRUB) ;		/* configure PHY mux */
+		smc->r.rm_loop = FALSE ;
+		smc->r.rm_join = TRUE ;
+		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break ;
+	case SC5_THRU_B :
+		/*SC51*/
+		if (!smc->y[PB].cf_join || smc->y[PB].wc_flag) {
+			smc->y[PA].scrub = TRUE ;
+			GO_STATE(SC9_C_WRAP_A) ;
+			break ;
+		}
+		/*SC52*/
+		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
+			smc->y[PB].scrub = TRUE ;
+			GO_STATE(SC10_C_WRAP_B) ;
+			break ;
+		}
+		/*SC54*/
+		else if (!smc->s.attach_s) {
+			smc->y[PA].scrub = TRUE ;
+			GO_STATE(SC4_THRU_A) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(SC11_C_WRAP_S) :
+		smc->mib.p[PS].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
+		smc->mib.p[PS].fddiPORTMACPlacement = INDEX_MAC ;
+		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
+		config_mux(smc,MUX_WRAPS) ;		/* configure PHY mux */
+		if (smc->y[PA].cf_loop || smc->y[PB].cf_loop) {
+			smc->r.rm_join = FALSE ;
+			smc->r.rm_loop = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
+		}
+		if (smc->y[PA].cf_join || smc->y[PB].cf_join) {
+			smc->r.rm_loop = FALSE ;
+			smc->r.rm_join = TRUE ;
+			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
+		}
+		ACTIONS_DONE() ;
+		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
+		break ;
+	case SC11_C_WRAP_S :
+		/*SC70*/
+		if ( !smc->y[PA].cf_join && !smc->y[PA].cf_loop &&
+		     !smc->y[PB].cf_join && !smc->y[PB].cf_loop) {
+			GO_STATE(SC0_ISOLATED) ;
+			break ;
+		}
+		break ;
+	default:
+		SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ;
+		break;
+	}
+}
+
+/*
+ * get MAC's input Port
+ *	return :
+ *		PA or PB
+ */
+int cfm_get_mac_input(smc)
+struct s_smc *smc ;
+{
+	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
+		smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA) ;
+}
+
+/*
+ * get MAC's output Port
+ *	return :
+ *		PA or PB
+ */
+int cfm_get_mac_output(smc)
+struct s_smc *smc ;
+{
+	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
+		smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA) ;
+}
+
+static char path_iso[] = {
+	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
+	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
+} ;
+
+static char path_wrap_a[] = {
+	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
+	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
+} ;
+
+static char path_wrap_b[] = {
+	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
+	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO
+} ;
+
+static char path_thru[] = {
+	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
+	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM
+} ;
+
+static char path_wrap_s[] = {
+	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_PRIM,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
+} ;
+
+static char path_iso_s[] = {
+	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_ISO,
+	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
+} ;
+
+int cem_build_path(smc,to,path_index)
+struct s_smc *smc ;
+char *to ;
+int path_index ;
+{
+	char	*path ;
+	int	len ;
+
+	switch (smc->mib.fddiSMTCF_State) {
+	default :
+	case SC0_ISOLATED :
+		path = smc->s.sas ? path_iso_s : path_iso ;
+		len = smc->s.sas ? sizeof(path_iso_s) :  sizeof(path_iso) ;
+		break ;
+	case SC9_C_WRAP_A :
+		path = path_wrap_a ;
+		len = sizeof(path_wrap_a) ;
+		break ;
+	case SC10_C_WRAP_B :
+		path = path_wrap_b ;
+		len = sizeof(path_wrap_b) ;
+		break ;
+	case SC4_THRU_A :
+		path = path_thru ;
+		len = sizeof(path_thru) ;
+		break ;
+	case SC11_C_WRAP_S :
+		path = path_wrap_s ;
+		len = sizeof(path_wrap_s) ;
+		break ;
+	}
+	memcpy(to,path,len) ;
+
+	LINT_USE(path_index);
+
+	return(len) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/drvfbi.c linux.18p17/drivers/net/skfp/drvfbi.c
--- linux.vanilla/drivers/net/skfp/drvfbi.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/drvfbi.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,1612 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * FBI board dependent Driver for SMT and LLC
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/supern_2.h"
+#include "h/skfbiinc.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)drvfbi.c	1.63 99/02/11 (C) SK " ;
+#endif
+
+/*
+ * PCM active state
+ */
+#define PC8_ACTIVE	8
+
+#define	LED_Y_ON	0x11	/* Used for ring up/down indication */
+#define	LED_Y_OFF	0x10
+
+
+#define MS2BCLK(x)	((x)*12500L)
+
+/*
+ * valid configuration values are:
+ */
+#ifdef	ISA
+const int opt_ints[] = {8,	3, 4, 5, 9, 10, 11, 12, 15} ;
+const int opt_iops[] = {8,
+	0x100, 0x120, 0x180, 0x1a0, 0x220, 0x240, 0x320, 0x340};
+const int opt_dmas[] = {4,	3, 5, 6, 7} ;
+const int opt_eproms[] = {15,	0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce,
+			0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc} ;
+#endif
+#ifdef	EISA
+const int opt_ints[] = {5, 9, 10, 11} ;
+const int opt_dmas[] = {0, 5, 6, 7} ;
+const int opt_eproms[] = {0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce,
+				0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc} ;
+#endif
+
+#ifdef	MCA
+int	opt_ints[] = {3, 11, 10, 9} ;			/* FM1 */
+int	opt_eproms[] = {0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc} ;
+#endif	/* MCA */
+
+/*
+ *	xPOS_ID:xxxx
+ *	|	\  /
+ *	|	 \/
+ *	|	  --------------------- the patched POS_ID of the Adapter
+ *	|				xxxx = (Vendor ID low byte,
+ *	|					Vendor ID high byte,
+ *	|					Device ID low byte,
+ *	|					Device ID high byte)
+ *	+------------------------------ the patched oem_id must be
+ *					'S' for SK or 'I' for IBM
+ *					this is a short id for the driver.
+ */
+#ifndef MULT_OEM
+#ifndef	OEM_CONCEPT
+#ifndef MCA
+const u_char oem_id[] = "xPOS_ID:xxxx" ;
+#else
+const u_char oem_id[] = "xPOSID1:xxxx" ;	/* FM1 card id. */
+#endif
+#else	/* OEM_CONCEPT */
+#ifndef MCA
+const u_char oem_id[] = OEM_ID ;
+#else
+const u_char oem_id[] = OEM_ID1 ;	/* FM1 card id. */
+#endif	/* MCA */
+#endif	/* OEM_CONCEPT */
+#define	ID_BYTE0	8
+#define	OEMID(smc,i)	oem_id[ID_BYTE0 + i]
+#else	/* MULT_OEM */
+const struct s_oem_ids oem_ids[] = {
+#include "oemids.h"
+{0}
+};
+#define	OEMID(smc,i)	smc->hw.oem_id->oi_id[i]
+#endif	/* MULT_OEM */
+
+/* Prototypes of external functions */
+extern void hwt_restart() ;
+#ifdef AIX
+extern int AIX_vpdReadByte() ;
+#endif
+
+
+/* Prototypes of local functions. */
+void smt_stop_watchdog() ;
+
+#ifdef MCA
+static int read_card_id() ;
+static void DisableSlotAccess() ;
+static void EnableSlotAccess() ;
+#ifdef AIX
+extern int attach_POS_addr() ;
+extern int detach_POS_addr() ;
+extern u_char read_POS() ;
+extern void write_POS() ;
+extern int AIX_vpdReadByte() ;
+#else
+#define	read_POS(smc,a1,a2)	((u_char) inp(a1))
+#define	write_POS(smc,a1,a2,a3)	outp((a1),(a3))
+#endif
+#endif	/* MCA */
+
+
+/*
+ * FDDI card reset
+ */
+static void card_start(smc)
+struct s_smc *smc ;
+{
+	int i ;
+#ifdef	PCI
+	u_char	rev_id ;
+	u_short word;
+#endif
+
+	smt_stop_watchdog(smc) ;
+
+#ifdef	ISA
+	outpw(CSR_A,0) ;			/* reset for all chips */
+	for (i = 10 ; i ; i--)			/* delay for PLC's */
+		(void)inpw(ISR_A) ;
+	OUT_82c54_TIMER(3,COUNT(2) | RW_OP(3) | TMODE(2)) ;
+					/* counter 2, mode 2 */
+	OUT_82c54_TIMER(2,97) ;		/* LSB */
+	OUT_82c54_TIMER(2,0) ;		/* MSB ( 15.6 us ) */
+	outpw(CSR_A,CS_CRESET) ;
+#endif
+#ifdef	EISA
+	outpw(CSR_A,0) ;			/* reset for all chips */
+	for (i = 10 ; i ; i--)			/* delay for PLC's */
+		(void)inpw(ISR_A) ;
+	outpw(CSR_A,CS_CRESET) ;
+	smc->hw.led = (2<<6) ;
+	outpw(CSR_A,CS_CRESET | smc->hw.led) ;
+#endif
+#ifdef	MCA
+	outp(ADDR(CARD_DIS),0) ;		/* reset for all chips */
+	for (i = 10 ; i ; i--)			/* delay for PLC's */
+		(void)inpw(ISR_A) ;
+	outp(ADDR(CARD_EN),0) ;
+	/* first I/O after reset must not be a access to FORMAC or PLC */
+
+	/*
+	 * bus timeout (MCA)
+	 */
+	OUT_82c54_TIMER(3,COUNT(2) | RW_OP(3) | TMODE(3)) ;
+					/* counter 2, mode 3 */
+	OUT_82c54_TIMER(2,(2*24)) ;	/* 3.9 us * 2 square wave */
+	OUT_82c54_TIMER(2,0) ;		/* MSB */
+
+	/* POS 102 indicated an activ Check Line or Buss Error monitoring */
+	if (inpw(CSA_A) & (POS_EN_CHKINT | POS_EN_BUS_ERR)) {
+		outp(ADDR(IRQ_CHCK_EN),0) ;
+	}
+
+	if (!((i = inpw(CSR_A)) & CS_SAS)) {
+		if (!(i & CS_BYSTAT)) {
+			outp(ADDR(BYPASS(STAT_INS)),0) ;/* insert station */
+		}
+	}
+	outpw(LEDR_A,LED_1) ;	/* yellow */
+#endif	/* MCA */
+#ifdef	PCI
+	/*
+	 * make sure no transfer activity is pending
+	 */
+	outpw(FM_A(FM_MDREG1),FM_MINIT) ;
+	outp(ADDR(B0_CTRL), CTRL_HPI_SET) ;
+	hwt_wait_time(smc,hwt_quick_read(smc),MS2BCLK(10)) ;
+	/*
+	 * now reset everything
+	 */
+	outp(ADDR(B0_CTRL),CTRL_RST_SET) ;	/* reset for all chips */
+	i = (int) inp(ADDR(B0_CTRL)) ;		/* do dummy read */
+	SK_UNUSED(i) ;				/* Make LINT happy. */
+	outp(ADDR(B0_CTRL), CTRL_RST_CLR) ;
+
+	/*
+	 * Reset all bits in the PCI STATUS register
+	 */
+	outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_ON) ;	/* enable for writes */
+	word = inpw(PCI_C(PCI_STATUS)) ;
+	outpw(PCI_C(PCI_STATUS), word | PCI_ERRBITS) ;
+	outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_OFF) ;	/* disable writes */
+
+	/*
+	 * Release the reset of all the State machines
+	 * Release Master_Reset
+	 * Release HPI_SM_Reset
+	 */
+	outp(ADDR(B0_CTRL), CTRL_MRST_CLR|CTRL_HPI_CLR) ;
+
+	/*
+	 * determine the adapter type
+	 * Note: Do it here, because some drivers may call card_start() once
+	 *	 at very first before any other initialization functions is
+	 *	 executed.
+	 */
+	rev_id = inp(PCI_C(PCI_REV_ID)) ;
+	if ((rev_id & 0xf0) == SK_ML_ID_1 || (rev_id & 0xf0) == SK_ML_ID_2) {
+		smc->hw.hw_is_64bit = TRUE ;
+	} else {
+		smc->hw.hw_is_64bit = FALSE ;
+	}
+
+	/*
+	 * Watermark initialization
+	 */
+	if (!smc->hw.hw_is_64bit) {
+		outpd(ADDR(B4_R1_F), RX_WATERMARK) ;
+		outpd(ADDR(B5_XA_F), TX_WATERMARK) ;
+		outpd(ADDR(B5_XS_F), TX_WATERMARK) ;
+	}
+
+	outp(ADDR(B0_CTRL),CTRL_RST_CLR) ;	/* clear the reset chips */
+	outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_ON|LED_GB_OFF) ; /* ye LED on */
+
+	/* init the timer value for the watch dog 2,5 minutes */
+	outpd(ADDR(B2_WDOG_INI),0x6FC23AC0) ;
+
+	/* initialize the ISR mask */
+	smc->hw.is_imask = ISR_MASK ;
+	smc->hw.hw_state = STOPPED ;
+#endif
+	GET_PAGE(0) ;		/* necessary for BOOT */
+}
+
+void card_stop(smc)
+struct s_smc *smc ;
+{
+	smt_stop_watchdog(smc) ;
+	smc->hw.mac_ring_is_up = 0 ;		/* ring down */
+#ifdef	ISA
+	outpw(CSR_A,0) ;			/* reset for all chips */
+#endif
+#ifdef	EISA
+	outpw(CSR_A,0) ;			/* reset for all chips */
+#endif
+#ifdef	MCA
+	outp(ADDR(CARD_DIS),0) ;		/* reset for all chips */
+#endif
+#ifdef	PCI
+	/*
+	 * make sure no transfer activity is pending
+	 */
+	outpw(FM_A(FM_MDREG1),FM_MINIT) ;
+	outp(ADDR(B0_CTRL), CTRL_HPI_SET) ;
+	hwt_wait_time(smc,hwt_quick_read(smc),MS2BCLK(10)) ;
+	/*
+	 * now reset everything
+	 */
+	outp(ADDR(B0_CTRL),CTRL_RST_SET) ;	/* reset for all chips */
+	outp(ADDR(B0_CTRL),CTRL_RST_CLR) ;	/* reset for all chips */
+	outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_OFF|LED_GB_OFF) ; /* all LEDs off */
+	smc->hw.hw_state = STOPPED ;
+#endif
+}
+/*--------------------------- ISR handling ----------------------------------*/
+
+#ifndef PCI
+void mac1_irq(smc,stu, stl)
+struct s_smc *smc ;
+u_short stu;
+u_short stl;
+{
+	int	restart_tx = 0 ;
+again:
+#ifndef ISA
+/*
+ * FORMAC+ bug modified the queue pointer if many read/write accesses happens!?
+ */
+	if (stl & (FM_SPCEPDS  |	/* parit/coding err. syn.q.*/
+		   FM_SPCEPDA0 |	/* parit/coding err. a.q.0 */
+		   FM_SPCEPDA1 |	/* parit/coding err. a.q.1 */
+		   FM_SPCEPDA2)) {	/* parit/coding err. a.q.2 */
+		SMT_PANIC(smc,SMT_E0132, SMT_E0132_MSG) ;
+	}
+	if (stl & (FM_STBURS  |	/* tx buffer underrun syn.q.*/
+		   FM_STBURA0 |	/* tx buffer underrun a.q.0 */
+		   FM_STBURA1 |	/* tx buffer underrun a.q.1 */
+		   FM_STBURA2)) {	/* tx buffer underrun a.q.2 */
+		SMT_PANIC(smc,SMT_E0133, SMT_E0133_MSG) ;
+	}
+#endif
+	if ( (stu & (FM_SXMTABT |		/* transmit abort */
+#ifdef	SYNC
+		     FM_STXABRS |	/* syn. tx abort */
+#endif	/* SYNC */
+		     FM_STXABRA0)) ||	/* asyn. tx abort */
+	     (stl & (FM_SQLCKS |		/* lock for syn. q. */
+		     FM_SQLCKA0)) ) {	/* lock for asyn. q. */
+		formac_tx_restart(smc) ;		/* init tx */
+		restart_tx = 1 ;
+		stu = inpw(FM_A(FM_ST1U)) ;
+		stl = inpw(FM_A(FM_ST1L)) ;
+		stu &= ~ (FM_STECFRMA0 | FM_STEFRMA0 | FM_STEFRMS) ;
+		if (stu || stl)
+			goto again ;
+	}
+
+#ifndef	SYNC
+	if (stu & (FM_STECFRMA0 | /* end of chain asyn tx */
+		   FM_STEFRMA0)) { /* end of frame asyn tx */
+		/* free tx_queue */
+		smc->hw.n_a_send = 0 ;
+		if (++smc->hw.fp.tx_free < smc->hw.fp.tx_max) {
+			start_next_send(smc);
+		}
+		restart_tx = 1 ;
+	}
+#else	/* SYNC */
+	if (stu & (FM_STEFRMA0 |	/* end of asyn tx */
+		    FM_STEFRMS)) {	/* end of sync tx */
+		restart_tx = 1 ;
+	}
+#endif	/* SYNC */
+	if (restart_tx)
+		llc_restart_tx(smc) ;
+}
+#else	/* PCI */
+
+void mac1_irq(smc,stu, stl)
+struct s_smc *smc ;
+u_short stu;
+u_short stl;
+{
+	int	restart_tx = 0 ;
+again:
+
+	/*
+	 * parity error: note encoding error is not possible in tag mode
+	 */
+	if (stl & (FM_SPCEPDS  |	/* parity err. syn.q.*/
+		   FM_SPCEPDA0 |	/* parity err. a.q.0 */
+		   FM_SPCEPDA1)) {	/* parity err. a.q.1 */
+		SMT_PANIC(smc,SMT_E0134, SMT_E0134_MSG) ;
+	}
+	/*
+	 * buffer underrun: can only occur if a tx threshold is specified
+	 */
+	if (stl & (FM_STBURS  |		/* tx buffer underrun syn.q.*/
+		   FM_STBURA0 |		/* tx buffer underrun a.q.0 */
+		   FM_STBURA1)) {	/* tx buffer underrun a.q.2 */
+		SMT_PANIC(smc,SMT_E0133, SMT_E0133_MSG) ;
+	}
+
+	if ( (stu & (FM_SXMTABT |		/* transmit abort */
+		     FM_STXABRS |		/* syn. tx abort */
+		     FM_STXABRA0)) ||		/* asyn. tx abort */
+	     (stl & (FM_SQLCKS |		/* lock for syn. q. */
+		     FM_SQLCKA0)) ) {		/* lock for asyn. q. */
+		formac_tx_restart(smc) ;	/* init tx */
+		restart_tx = 1 ;
+		stu = inpw(FM_A(FM_ST1U)) ;
+		stl = inpw(FM_A(FM_ST1L)) ;
+		stu &= ~ (FM_STECFRMA0 | FM_STEFRMA0 | FM_STEFRMS) ;
+		if (stu || stl)
+			goto again ;
+	}
+
+	if (stu & (FM_STEFRMA0 |	/* end of asyn tx */
+		    FM_STEFRMS)) {	/* end of sync tx */
+		restart_tx = 1 ;
+	}
+
+	if (restart_tx)
+		llc_restart_tx(smc) ;
+}
+#endif	/* PCI */
+/*
+ * interrupt source= plc1
+ * this function is called in nwfbisr.asm
+ */
+void plc1_irq(smc)
+struct s_smc *smc ;
+{
+	u_short	st = inpw(PLC(PB,PL_INTR_EVENT)) ;
+
+#if	(defined(ISA) || defined(EISA))
+	/* reset PLC Int. bits */
+	outpw(PLC1_I,inpw(PLC1_I)) ;
+#endif
+	plc_irq(smc,PB,st) ;
+}
+
+/*
+ * interrupt source= plc2
+ * this function is called in nwfbisr.asm
+ */
+void plc2_irq(smc)
+struct s_smc *smc ;
+{
+	u_short	st = inpw(PLC(PA,PL_INTR_EVENT)) ;
+
+#if	(defined(ISA) || defined(EISA))
+	/* reset PLC Int. bits */
+	outpw(PLC2_I,inpw(PLC2_I)) ;
+#endif
+	plc_irq(smc,PA,st) ;
+}
+
+
+/*
+ * interrupt source= timer
+ */
+void timer_irq(smc)
+struct s_smc *smc ;
+{
+	hwt_restart(smc);
+	smc->hw.t_stop = smc->hw.t_start;
+	smt_timer_done(smc) ;
+}
+
+/*
+ * return S-port (PA or PB)
+ */
+int pcm_get_s_port(smc)
+struct s_smc *smc ;
+{
+	SK_UNUSED(smc) ;
+	return(PS) ;
+}
+
+/*
+ * Station Label = "FDDI-XYZ" where
+ *
+ *	X = connector type
+ *	Y = PMD type
+ *	Z = port type
+ */
+#define STATION_LABEL_CONNECTOR_OFFSET	5
+#define STATION_LABEL_PMD_OFFSET	6
+#define STATION_LABEL_PORT_OFFSET	7
+
+void read_address(smc,mac_addr)
+struct s_smc *smc ;
+u_char *mac_addr ;
+{
+	char ConnectorType ;
+	char PmdType ;
+	int	i ;
+
+	extern const u_char canonical[256] ;
+
+#if	(defined(ISA) || defined(MCA))
+	for (i = 0; i < 4 ;i++) {	/* read mac address from board */
+		smc->hw.fddi_phys_addr.a[i] =
+			canonical[(inpw(PR_A(i+SA_MAC))&0xff)] ;
+	}
+	for (i = 4; i < 6; i++) {
+		smc->hw.fddi_phys_addr.a[i] =
+			canonical[(inpw(PR_A(i+SA_MAC+PRA_OFF))&0xff)] ;
+	}
+#endif
+#ifdef	EISA
+	/*
+	 * Note: We get trouble on an Alpha machine if we make a inpw()
+	 * instead of inp()
+	 */
+	for (i = 0; i < 4 ;i++) {	/* read mac address from board */
+		smc->hw.fddi_phys_addr.a[i] =
+			canonical[inp(PR_A(i+SA_MAC))] ;
+	}
+	for (i = 4; i < 6; i++) {
+		smc->hw.fddi_phys_addr.a[i] =
+			canonical[inp(PR_A(i+SA_MAC+PRA_OFF))] ;
+	}
+#endif
+#ifdef	PCI
+	for (i = 0; i < 6; i++) {	/* read mac address from board */
+		smc->hw.fddi_phys_addr.a[i] =
+			canonical[inp(ADDR(B2_MAC_0+i))] ;
+	}
+#endif
+#ifndef	PCI
+	ConnectorType = inpw(PR_A(SA_PMD_TYPE)) & 0xff ;
+	PmdType = inpw(PR_A(SA_PMD_TYPE+1)) & 0xff ;
+#else
+	ConnectorType = inp(ADDR(B2_CONN_TYP)) ;
+	PmdType = inp(ADDR(B2_PMD_TYP)) ;
+#endif
+
+	smc->y[PA].pmd_type[PMD_SK_CONN] =
+	smc->y[PB].pmd_type[PMD_SK_CONN] = ConnectorType ;
+	smc->y[PA].pmd_type[PMD_SK_PMD ] =
+	smc->y[PB].pmd_type[PMD_SK_PMD ] = PmdType ;
+
+	if (mac_addr) {
+		for (i = 0; i < 6 ;i++) {
+			smc->hw.fddi_canon_addr.a[i] = mac_addr[i] ;
+			smc->hw.fddi_home_addr.a[i] = canonical[mac_addr[i]] ;
+		}
+		return ;
+	}
+	smc->hw.fddi_home_addr = smc->hw.fddi_phys_addr ;
+
+	for (i = 0; i < 6 ;i++) {
+		smc->hw.fddi_canon_addr.a[i] =
+			canonical[smc->hw.fddi_phys_addr.a[i]] ;
+	}
+}
+
+/*
+ * FDDI card soft reset
+ */
+void init_board(smc,mac_addr)
+struct s_smc *smc ;
+u_char *mac_addr ;
+{
+	card_start(smc) ;
+	read_address(smc,mac_addr) ;
+
+#ifndef	PCI
+	if (inpw(CSR_A) & CS_SAS)
+#else
+	if (!(inp(ADDR(B0_DAS)) & DAS_AVAIL))
+#endif
+		smc->s.sas = SMT_SAS ;	/* Single att. station */
+	else
+		smc->s.sas = SMT_DAS ;	/* Dual att. station */
+
+#ifndef	PCI
+	if (inpw(CSR_A) & CS_BYSTAT)
+#else
+	if (!(inp(ADDR(B0_DAS)) & DAS_BYP_ST))
+#endif
+		smc->mib.fddiSMTBypassPresent = 0 ;
+		/* without opt. bypass */
+	else
+		smc->mib.fddiSMTBypassPresent = 1 ;
+		/* with opt. bypass */
+}
+
+/*
+ * insert or deinsert optical bypass (called by ECM)
+ */
+void sm_pm_bypass_req(smc,mode)
+struct s_smc *smc ;
+int mode;
+{
+#if	(defined(ISA) || defined(EISA))
+	int csra_v ;
+#endif
+
+	DB_ECMN(1,"ECM : sm_pm_bypass_req(%s)\n",(mode == BP_INSERT) ?
+					"BP_INSERT" : "BP_DEINSERT",0) ;
+
+	if (smc->s.sas != SMT_DAS)
+		return ;
+
+#if	(defined(ISA) || defined(EISA))
+
+	csra_v = inpw(CSR_A) & ~CS_BYPASS ;
+#ifdef	EISA
+	csra_v |= smc->hw.led ;
+#endif
+
+	switch(mode) {
+	case BP_INSERT :
+		outpw(CSR_A,csra_v | CS_BYPASS) ;
+		break ;
+	case BP_DEINSERT :
+		outpw(CSR_A,csra_v) ;
+		break ;
+	}
+#endif	/* ISA / EISA */
+#ifdef	MCA
+	switch(mode) {
+	case BP_INSERT :
+		outp(ADDR(BYPASS(STAT_INS)),0) ;/* insert station */
+		break ;
+	case BP_DEINSERT :
+		outp(ADDR(BYPASS(STAT_BYP)),0) ;	/* bypass station */
+		break ;
+	}
+#endif
+#ifdef	PCI
+	switch(mode) {
+	case BP_INSERT :
+		outp(ADDR(B0_DAS),DAS_BYP_INS) ;	/* insert station */
+		break ;
+	case BP_DEINSERT :
+		outp(ADDR(B0_DAS),DAS_BYP_RMV) ;	/* bypass station */
+		break ;
+	}
+#endif
+}
+
+/*
+ * check if bypass connected
+ */
+int sm_pm_bypass_present(smc)
+struct s_smc *smc ;
+{
+#ifndef	PCI
+	return(	(inpw(CSR_A) & CS_BYSTAT) ? FALSE : TRUE ) ;
+#else
+	return(	(inp(ADDR(B0_DAS)) & DAS_BYP_ST) ? TRUE: FALSE) ;
+#endif
+}
+
+void plc_clear_irq(smc,p)
+struct s_smc *smc ;
+int p ;
+{
+	SK_UNUSED(p) ;
+
+#if	(defined(ISA) || defined(EISA))
+	switch (p) {
+	case PA :
+		/* reset PLC Int. bits */
+		outpw(PLC2_I,inpw(PLC2_I)) ;
+		break ;
+	case PB :
+		/* reset PLC Int. bits */
+		outpw(PLC1_I,inpw(PLC1_I)) ;
+		break ;
+	}
+#else
+	SK_UNUSED(smc) ;
+#endif
+}
+
+
+/*
+ * led_indication called by rmt_indication() and
+ * pcm_state_change()
+ *
+ * Input:
+ *	smc:	SMT context
+ *	led_event:
+ *	0	Only switch green LEDs according to their respective PCM state
+ *	LED_Y_OFF	just switch yellow LED off
+ *	LED_Y_ON	just switch yello LED on
+ */
+void led_indication(smc,led_event)
+struct s_smc	*smc ;
+int		led_event;
+{
+	/* use smc->hw.mac_ring_is_up == TRUE 
+	 * as indication for Ring Operational
+	 */
+	u_short			led_state ;
+	struct s_phy		*phy ;
+	struct fddi_mib_p	*mib_a ;
+	struct fddi_mib_p	*mib_b ;
+
+	phy = &smc->y[PA] ;
+	mib_a = phy->mib ;
+	phy = &smc->y[PB] ;
+	mib_b = phy->mib ;
+
+#ifdef	EISA
+	/* Ring up = yellow led OFF*/
+	if (led_event == LED_Y_ON) {
+		smc->hw.led |= CS_LED_1 ;
+	}
+	else if (led_event == LED_Y_OFF) {
+		smc->hw.led &= ~CS_LED_1 ;
+	}
+	else {
+		/* Link at Port A or B = green led ON */
+		if (mib_a->fddiPORTPCMState == PC8_ACTIVE ||
+		    mib_b->fddiPORTPCMState == PC8_ACTIVE) {
+			smc->hw.led |= CS_LED_0 ;
+		}
+		else {
+			smc->hw.led &= ~CS_LED_0 ;
+		}
+	}
+#endif
+#ifdef	MCA
+	led_state = inpw(LEDR_A) ;
+	
+	/* Ring up = yellow led OFF*/
+	if (led_event == LED_Y_ON) {
+		led_state |= LED_1 ;
+	}
+	else if (led_event == LED_Y_OFF) {
+		led_state &= ~LED_1 ;
+	}
+	else {
+                led_state &= ~(LED_2|LED_0) ;
+
+		/* Link at Port A = green led A ON */
+		if (mib_a->fddiPORTPCMState == PC8_ACTIVE) {	
+			led_state |= LED_2 ;
+		}
+		
+		/* Link at Port B/S = green led B ON */
+		if (mib_b->fddiPORTPCMState == PC8_ACTIVE) {
+			led_state |= LED_0 ;
+		}
+	}
+
+        outpw(LEDR_A, led_state) ;
+#endif	/* MCA */
+#ifdef	PCI
+        led_state = 0 ;
+	
+	/* Ring up = yellow led OFF*/
+	if (led_event == LED_Y_ON) {
+		led_state |= LED_MY_ON ;
+	}
+	else if (led_event == LED_Y_OFF) {
+		led_state |= LED_MY_OFF ;
+	}
+	else {	/* PCM state changed */
+		/* Link at Port A/S = green led A ON */
+		if (mib_a->fddiPORTPCMState == PC8_ACTIVE) {	
+			led_state |= LED_GA_ON ;
+		}
+		else {
+			led_state |= LED_GA_OFF ;
+		}
+		
+		/* Link at Port B = green led B ON */
+		if (mib_b->fddiPORTPCMState == PC8_ACTIVE) {
+			led_state |= LED_GB_ON ;
+		}
+		else {
+			led_state |= LED_GB_OFF ;
+		}
+	}
+
+        outp(ADDR(B0_LED), led_state) ;
+#endif	/* PCI */
+
+}
+
+
+void pcm_state_change(smc,plc,p_state)
+struct s_smc *smc;
+int plc;
+int p_state;
+{
+	/*
+	 * the current implementation of pcm_state_change() in the driver
+	 * parts must be renamed to drv_pcm_state_change() which will be called
+	 * now after led_indication.
+	 */
+	DRV_PCM_STATE_CHANGE(smc,plc,p_state) ;
+	
+	led_indication(smc,0) ;
+}
+
+
+void rmt_indication(smc,i)
+struct s_smc *smc ;
+int i;
+{
+	/* Call a driver special function if defined */
+	DRV_RMT_INDICATION(smc,i) ;
+
+        led_indication(smc, i ? LED_Y_OFF : LED_Y_ON) ;
+}
+
+
+/*
+ * llc_recover_tx called by init_tx (fplus.c)
+ */
+void llc_recover_tx(smc)
+struct s_smc *smc ;
+{
+#ifdef	LOAD_GEN
+	extern	int load_gen_flag ;
+
+	load_gen_flag = 0 ;
+#endif
+#ifndef	SYNC
+	smc->hw.n_a_send= 0 ;
+#else
+	SK_UNUSED(smc) ;
+#endif
+}
+
+/*--------------------------- DMA init ----------------------------*/
+#ifdef	ISA
+
+/*
+ * init DMA
+ */
+void init_dma(smc,dma)
+struct s_smc *smc;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+
+	/*
+	 * set cascade mode,
+	 * clear mask bit (enable DMA cannal)
+	 */
+	if (dma > 3) {
+		outp(0xd6,(dma & 0x03) | 0xc0) ;
+		outp(0xd4, dma & 0x03) ;
+	}
+	else {
+		outp(0x0b,(dma & 0x03) | 0xc0) ;
+		outp(0x0a,dma & 0x03) ;
+	}
+}
+
+/*
+ * disable DMA
+ */
+void dis_dma(smc,dma)
+struct s_smc *smc ;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+
+	/*
+	 * set mask bit (disable DMA cannal)
+	 */
+	if (dma > 3) {
+		outp(0xd4,(dma & 0x03) | 0x04) ;
+	}
+	else {
+		outp(0x0a,(dma & 0x03) | 0x04) ;
+	}
+}
+
+#endif	/* ISA */
+
+#ifdef	EISA
+
+/*arrays with io adresses of dma controller length and adress registers*/
+static const int cntr[8] = { 0x001,0x003,0x005,0x007,0,0x0c6,0x0ca,0x0ce } ;
+static const int base[8] = { 0x000,0x002,0x004,0x006,0,0x0c4,0x0c8,0x0cc } ;
+static const int page[8] = { 0x087,0x083,0x081,0x082,0,0x08b,0x089,0x08a } ;
+
+void init_dma(smc,dma)
+struct s_smc *smc ;
+int	dma;
+{
+	/*
+	 * extended mode register
+	 * 32 bit IO
+	 * type c
+	 * TC output
+	 * disable stop
+	 */
+
+	/* mode read (write) demand */
+	smc->hw.dma_rmode = (dma & 3) | 0x08 | 0x0 ;
+	smc->hw.dma_wmode = (dma & 3) | 0x04 | 0x0 ;
+
+	/* 32 bit IO's, burst DMA mode (type "C") */
+	smc->hw.dma_emode = (dma & 3) | 0x08 | 0x30 ;
+
+	outp((dma < 4) ? 0x40b : 0x4d6,smc->hw.dma_emode) ;
+
+	/* disable chaining */
+	outp((dma < 4) ? 0x40a : 0x4d4,(dma&3)) ;
+
+	/*load dma controller addresses for fast access during set dma*/
+	smc->hw.dma_base_word_count = cntr[smc->hw.dma];
+	smc->hw.dma_base_address = base[smc->hw.dma];
+	smc->hw.dma_base_address_page = page[smc->hw.dma];
+
+}
+
+void dis_dma(smc,dma)
+struct s_smc *smc ;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+
+	outp((dma < 4) ? 0x0a : 0xd4,(dma&3)|4) ;/* mask bit */
+}
+#endif	/* EISA */
+
+#ifdef	MCA
+void init_dma(smc,dma)
+struct s_smc *smc;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(dma) ;
+}
+void dis_dma(smc,dma)
+struct s_smc *smc;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(dma) ;
+}
+#endif
+
+#ifdef	PCI
+void init_dma(smc,dma)
+struct s_smc *smc;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(dma) ;
+}
+void dis_dma(smc,dma)
+struct s_smc *smc;
+int	dma;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(dma) ;
+}
+#endif
+
+#ifdef MULT_OEM
+static int is_equal_num(comp1,comp2,num)
+char comp1[] ;
+char comp2[] ;
+int num ;
+{
+	int i ;
+
+	for (i = 0 ; i < num ; i++) {
+		if (comp1[i] != comp2[i])
+			return (0) ;
+	}
+		return (1) ;
+}	/* is_equal_num */
+
+
+/*
+ * set the OEM ID defaults, and test the contents of the OEM data base
+ * The default OEM is the first ACTIVE entry in the OEM data base 
+ *
+ * returns:	0	success
+ *		1	error in data base
+ *		2	data base empty
+ *		3	no active entry	
+ */
+int set_oi_id_def(smc)
+struct s_smc *smc ;
+{
+	int sel_id ;
+	int i ;
+	int act_entries ;
+
+	i = 0 ;
+	sel_id = -1 ;
+	act_entries = FALSE ;
+	smc->hw.oem_id = 0 ;
+	smc->hw.oem_min_status = OI_STAT_ACTIVE ;
+	
+	/* check OEM data base */
+	while (oem_ids[i].oi_status) {
+		switch (oem_ids[i].oi_status) {
+		case OI_STAT_ACTIVE:
+			act_entries = TRUE ;	/* we have active IDs */
+			if (sel_id == -1)
+				sel_id = i ;	/* save the first active ID */
+		case OI_STAT_VALID:
+		case OI_STAT_PRESENT:
+			i++ ;
+			break ;			/* entry ok */
+		default:
+			return (1) ;		/* invalid oi_status */
+		}
+	}
+
+	if (i == 0)
+		return (2) ;
+	if (!act_entries)
+		return (3) ;
+
+	/* ok, we have a valid OEM data base with an active entry */
+	smc->hw.oem_id = (struct s_oem_ids *)  &oem_ids[sel_id] ;
+	return (0) ;
+}
+#endif	/* MULT_OEM */
+
+
+#ifdef	MCA
+/************************
+ *
+ * BEGIN_MANUAL_ENTRY()
+ *
+ *	exist_board
+ *
+ *	Check if an MCA board is present in the specified slot.
+ *
+ *	int exist_board(
+ *		struct s_smc *smc,
+ *		int slot) ;
+ * In
+ *	smc - A pointer to the SMT Context struct.
+ *
+ *	slot - The number of the slot to inspect.
+ * Out
+ *	0 = No adapter present.
+ *	1 = Found FM1 adapter.
+ *
+ * Pseudo
+ *      Read MCA ID
+ *	for all valid OEM_IDs
+ *		compare with ID read
+ *		if equal, return 1
+ *	return(0
+ *
+ * Note
+ *	The smc pointer must be valid now.
+ *
+ * END_MANUAL_ENTRY()
+ *
+ ************************/
+#define LONG_CARD_ID(lo, hi)	((((hi) & 0xff) << 8) | ((lo) & 0xff))
+int exist_board(smc,slot)
+struct s_smc *smc ;
+int	slot ;
+{
+#ifdef MULT_OEM
+	SK_LOC_DECL(u_char,id[2]) ;
+	int idi ;
+#endif	/* MULT_OEM */
+
+	/* No longer valid. */
+	if (smc == NULL)
+		return(0) ;
+
+#ifndef MULT_OEM
+	if (read_card_id(smc, slot)
+		== LONG_CARD_ID(OEMID(smc,0), OEMID(smc,1)))
+		return (1) ;	/* Found FM adapter. */
+
+#else	/* MULT_OEM */
+	idi = read_card_id(smc, slot) ;
+	id[0] = idi & 0xff ;
+	id[1] = idi >> 8 ;
+
+        smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ;
+	for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) {
+		if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status)
+			continue ;
+
+		if (is_equal_num(&id[0],&OEMID(smc,0),2))
+			return (1) ;
+	}
+#endif	/* MULT_OEM */
+	return (0) ;	/* No adapter found. */
+}
+
+/************************
+ *
+ *	read_card_id
+ *
+ *	Read the MCA card id from the specified slot.
+ * In
+ *	smc - A pointer to the SMT Context struct.
+ *	CAVEAT: This pointer may be NULL and *must not* be used within this
+ *	function. It's only purpose is for drivers that need some information
+ *	for the inp() and outp() macros.
+ *
+ *	slot - The number of the slot for which the card id is returned.
+ * Out
+ *	Returns the card id read from the specified slot. If an illegal slot
+ *	number is specified, the function returns zero.
+ *
+ ************************/
+static int read_card_id(smc,slot)
+struct s_smc *smc ;	/* Do not use. */
+int slot ;
+{
+	int card_id ;
+
+	SK_UNUSED(smc) ;	/* Make LINT happy. */
+	if ((slot < 1) || (slot > 15))	/* max 16 slots, 0 = motherboard */
+		return (0) ;	/* Illegal slot number specified. */
+
+	EnableSlotAccess(smc, slot) ;
+
+	card_id = ((read_POS(smc,POS_ID_HIGH,slot - 1) & 0xff) << 8) |
+				(read_POS(smc,POS_ID_LOW,slot - 1) & 0xff) ;
+
+	DisableSlotAccess(smc) ;
+
+	return (card_id) ;
+}
+
+/************************
+ *
+ * BEGIN_MANUAL_ENTRY()
+ *
+ *	get_board_para
+ *
+ *	Get adapter configuration information. Fill all board specific
+ *	parameters within the 'smc' structure.
+ *
+ *	int get_board_para(
+ *		struct s_smc *smc,
+ *		int slot) ;
+ * In
+ *	smc - A pointer to the SMT Context struct, to which this function will
+ *	write some adapter configuration data.
+ *
+ *	slot - The number of the slot, in which the adapter is installed.
+ * Out
+ *	0 = No adapter present.
+ *	1 = Ok.
+ *	2 = Adapter present, but card enable bit not set.
+ *
+ * END_MANUAL_ENTRY()
+ *
+ ************************/
+int get_board_para(smc,slot)
+struct s_smc *smc ;
+int slot ;
+{
+	int val ;
+	int i ;
+
+	/* Check if adapter present & get type of adapter. */
+	switch (exist_board(smc, slot)) {
+	case 0:	/* Adapter not present. */
+		return (0) ;
+	case 1:	/* FM Rev. 1 */
+		smc->hw.rev = FM1_REV ;
+		smc->hw.VFullRead = 0x0a ;
+		smc->hw.VFullWrite = 0x05 ;
+		smc->hw.DmaWriteExtraBytes = 8 ;	/* 2 extra words. */
+		break ;
+	}
+	smc->hw.slot = slot ;
+
+	EnableSlotAccess(smc, slot) ;
+
+	if (!(read_POS(smc,POS_102, slot - 1) & POS_CARD_EN)) {
+		DisableSlotAccess(smc) ;
+		return (2) ;	/* Card enable bit not set. */
+	}
+
+	val = read_POS(smc,POS_104, slot - 1) ;	/* I/O, IRQ */
+
+#ifndef MEM_MAPPED_IO	/* is defined by the operating system */
+	i = val & POS_IOSEL ;	/* I/O base addr. (0x0200 .. 0xfe00) */
+	smc->hw.iop = (i + 1) * 0x0400 - 0x200 ;
+#endif
+	i = ((val & POS_IRQSEL) >> 6) & 0x03 ;	/* IRQ <0, 1> */
+	smc->hw.irq = opt_ints[i] ;
+
+	/* FPROM base addr. */
+	i = ((read_POS(smc,POS_103, slot - 1) & POS_MSEL) >> 4) & 0x07 ;
+	smc->hw.eprom = opt_eproms[i] ;
+
+	DisableSlotAccess(smc) ;
+
+	/* before this, the smc->hw.iop must be set !!! */
+	smc->hw.slot_32 = inpw(CSF_A) & SLOT_32 ;
+
+	return (1) ;
+}
+
+/* Enable access to specified MCA slot. */
+static void EnableSlotAccess(smc,slot)
+struct s_smc *smc ;
+int slot ;
+{
+	SK_UNUSED(slot) ;
+
+#ifndef AIX
+	SK_UNUSED(smc) ;
+
+	/* System mode. */
+	outp(POS_SYS_SETUP, POS_SYSTEM) ;
+
+	/* Select slot. */
+	outp(POS_CHANNEL_POS, POS_CHANNEL_BIT | (slot-1)) ;
+#else
+	attach_POS_addr (smc) ;
+#endif
+}
+
+/* Disable access to MCA slot formerly enabled via EnableSlotAccess(). */
+static void DisableSlotAccess(smc)
+struct s_smc *smc ;
+{
+#ifndef AIX
+	SK_UNUSED(smc) ;
+
+	outp(POS_CHANNEL_POS, 0) ;
+#else
+	detach_POS_addr (smc) ;
+#endif
+}
+#endif	/* MCA */
+
+#ifdef	EISA
+#ifndef	MEM_MAPPED_IO
+#define	SADDR(slot)	(((slot)<<12)&0xf000)
+#else	/* MEM_MAPPED_IO */
+#define	SADDR(slot)	(smc->hw.iop)
+#endif	/* MEM_MAPPED_IO */
+
+/************************
+ *
+ * BEGIN_MANUAL_ENTRY()
+ *
+ *	exist_board
+ *
+ *	Check if an EISA board is present in the specified slot.
+ *
+ *	int exist_board(
+ *		struct s_smc *smc,
+ *		int slot) ;
+ * In
+ *	smc - A pointer to the SMT Context struct.
+ *
+ *	slot - The number of the slot to inspect.
+ * Out
+ *	0 = No adapter present.
+ *	1 = Found adapter.
+ *
+ * Pseudo
+ *      Read EISA ID
+ *	for all valid OEM_IDs
+ *		compare with ID read
+ *		if equal, return 1
+ *	return(0
+ *
+ * Note
+ *	The smc pointer must be valid now.
+ *
+ ************************/
+int exist_board(smc,slot)
+struct s_smc *smc ;
+int	slot ;
+{
+	int i ;
+#ifdef MULT_OEM
+	SK_LOC_DECL(u_char,id[4]) ;
+#endif	/* MULT_OEM */
+
+	/* No longer valid. */
+	if (smc == NULL)
+		return(0);
+
+	SK_UNUSED(slot) ;
+
+#ifndef MULT_OEM
+	for (i = 0 ; i < 4 ; i++) {
+		if (inp(SADDR(slot)+PRA(i)) != OEMID(smc,i))
+			return(0) ;
+	}
+	return(1) ;
+#else	/* MULT_OEM */
+	for (i = 0 ; i < 4 ; i++)
+		id[i] = inp(SADDR(slot)+PRA(i)) ;
+
+	smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ;
+
+	for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) {
+		if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status)
+			continue ;
+
+		if (is_equal_num(&id[0],&OEMID(smc,0),4))
+			return (1) ;
+	}
+	return (0) ;	/* No adapter found. */
+#endif	/* MULT_OEM */
+}
+
+
+int get_board_para(smc,slot)
+struct s_smc *smc ;
+int	slot ;
+{
+	int	i ;
+
+	if (!exist_board(smc,slot))
+		return(0) ;
+
+	smc->hw.slot = slot ;
+#ifndef	MEM_MAPPED_IO		/* if defined by the operating system */
+	smc->hw.iop = SADDR(slot) ;
+#endif
+
+	if (!(inp(C0_A(0))&CFG_CARD_EN)) {
+		return(2) ;			/* CFG_CARD_EN bit not set! */
+	}
+
+	smc->hw.irq = opt_ints[(inp(C1_A(0)) & CFG_IRQ_SEL)] ;
+	smc->hw.dma = opt_dmas[((inp(C1_A(0)) & CFG_DRQ_SEL)>>3)] ;
+
+	if ((i = inp(C2_A(0)) & CFG_EPROM_SEL) != 0x0f)
+		smc->hw.eprom = opt_eproms[i] ;
+	else
+		smc->hw.eprom = 0 ;
+
+	smc->hw.DmaWriteExtraBytes = 8 ;
+
+	return(1) ;
+}
+#endif	/* EISA */
+
+#ifdef	ISA
+#ifndef MULT_OEM
+const u_char sklogo[6] = SKLOGO_STR ;
+#define	SIZE_SKLOGO(smc)	sizeof(sklogo)
+#define	SKLOGO(smc,i)		sklogo[i]
+#else	/* MULT_OEM */
+#define	SIZE_SKLOGO(smc)	smc->hw.oem_id->oi_logo_len
+#define	SKLOGO(smc,i)		smc->hw.oem_id->oi_logo[i]
+#endif	/* MULT_OEM */
+
+
+int exist_board(smc,port)
+struct s_smc *smc ;
+HW_PTR	port ;
+{
+	int	i ;
+#ifdef MULT_OEM
+	int	bytes_read ;
+	u_char	board_logo[15] ;
+	SK_LOC_DECL(u_char,id[4]) ;
+#endif	/* MULT_OEM */
+
+	/* No longer valid. */
+	if (smc == NULL)
+		return(0);
+
+	SK_UNUSED(smc) ;
+#ifndef MULT_OEM
+	for (i = SADDRL ; i < (signed) (SADDRL+SIZE_SKLOGO(smc)) ; i++) {
+		if ((u_char)inpw((PRA(i)+port)) != SKLOGO(smc,i-SADDRL)) {
+			return(0) ;
+		}
+	}
+
+	/* check MAC address (S&K or other) */
+	for (i = 0 ; i < 3 ; i++) {
+		if ((u_char)inpw((PRA(i)+port)) != OEMID(smc,i))
+			return(0) ;
+	}
+	return(1) ;
+#else	/* MULT_OEM */
+        smc->hw.oem_id = (struct s_oem_ids *)  &oem_ids[0] ;
+	board_logo[0] = (u_char)inpw((PRA(SADDRL)+port)) ;
+	bytes_read = 1 ;
+
+	for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) {
+		if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status)
+			continue ;
+
+		/* Test all read bytes with current OEM_entry */
+		/* for (i=0; (i<bytes_read) && (i < SIZE_SKLOGO(smc)); i++) { */
+		for (i = 0; i < bytes_read; i++) {
+			if (board_logo[i] != SKLOGO(smc,i))
+				break ;
+		}
+
+		/* If mismatch, switch to next OEM entry */
+		if ((board_logo[i] != SKLOGO(smc,i)) && (i < bytes_read))
+			continue ;
+
+		--i ;
+		while (bytes_read < SIZE_SKLOGO(smc)) {
+			//   inpw next byte SK_Logo
+			i++ ;
+			board_logo[i] = (u_char)inpw((PRA(SADDRL+i)+port)) ;
+			bytes_read++ ;
+			if (board_logo[i] != SKLOGO(smc,i))
+				break ;
+		}
+
+		for (i = 0 ; i < 3 ; i++)
+			id[i] = (u_char)inpw((PRA(i)+port)) ;
+
+		if ((board_logo[i] == SKLOGO(smc,i))
+			&& (bytes_read == SIZE_SKLOGO(smc))) {
+
+			if (is_equal_num(&id[0],&OEMID(smc,0),3))
+				return(1);
+		}
+	}	/* for */
+	return(0) ;
+#endif	/* MULT_OEM */
+}
+
+int get_board_para(smc,slot)
+struct s_smc *smc ;
+int	slot ;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(slot) ;
+	return(0) ;	/* for ISA not supported */
+}
+#endif	/* ISA */
+
+#ifdef PCI
+#ifdef USE_BIOS_FUN
+int exist_board(smc,slot)
+struct s_smc *smc ;
+int	slot ;
+{
+	u_short dev_id ;
+	u_short ven_id ;
+	int found ; 
+	int i ;
+
+	found = FALSE ;		/* make sure we returned with adatper not found*/
+				/* if an empty oemids.h was included */
+
+#ifdef MULT_OEM
+        smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ;
+	for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) {
+		if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status)
+			continue ;
+#endif
+		ven_id = OEMID(smc,0) + (OEMID(smc,1) << 8) ; 
+		dev_id = OEMID(smc,2) + (OEMID(smc,3) << 8) ; 
+		for (i = 0; i < slot; i++) {
+			if (pci_find_device(i,&smc->hw.pci_handle,
+				dev_id,ven_id) != 0) {
+
+				found = FALSE ;
+			} else {
+				found = TRUE ;
+			}
+		}
+		if (found) {
+			return(1) ;	/* adapter was found */
+		}
+#ifdef MULT_OEM
+	}
+#endif
+	return(0) ;	/* adapter was not found */
+}
+#endif	/* PCI */
+#endif	/* USE_BIOS_FUNC */
+
+void driver_get_bia(smc, bia_addr)
+struct s_smc *smc ;
+struct fddi_addr *bia_addr ;
+{
+	int i ;
+
+	extern const u_char canonical[256] ;
+
+	for (i = 0 ; i < 6 ; i++) {
+		bia_addr->a[i] = canonical[smc->hw.fddi_phys_addr.a[i]] ;
+	}
+}
+
+void smt_start_watchdog(smc)
+struct s_smc *smc ;
+{
+	SK_UNUSED(smc) ;	/* Make LINT happy. */
+
+#ifndef	DEBUG
+
+#ifdef	PCI
+	if (smc->hw.wdog_used) {
+		outpw(ADDR(B2_WDOG_CRTL),TIM_START) ;	/* Start timer. */
+	}
+#endif
+
+#endif	/* DEBUG */
+}
+
+void smt_stop_watchdog(smc)
+struct s_smc *smc ;
+{
+	SK_UNUSED(smc) ;	/* Make LINT happy. */
+#ifndef	DEBUG
+
+#ifdef	PCI
+	if (smc->hw.wdog_used) {
+		outpw(ADDR(B2_WDOG_CRTL),TIM_STOP) ;	/* Stop timer. */
+	}
+#endif
+
+#endif	/* DEBUG */
+}
+
+#ifdef	PCI
+static char get_rom_byte(smc,addr)
+struct s_smc *smc ;
+u_short	addr ;
+{
+	GET_PAGE(addr) ;
+	return (READ_PROM(ADDR(B2_FDP))) ;
+}
+
+/*
+ * ROM image defines
+ */
+#define	ROM_SIG_1	0
+#define ROM_SIG_2	1
+#define PCI_DATA_1	0x18
+#define PCI_DATA_2	0x19
+
+/*
+ * PCI data structure defines
+ */
+#define	VPD_DATA_1	0x08
+#define	VPD_DATA_2	0x09
+#define IMAGE_LEN_1	0x10
+#define IMAGE_LEN_2	0x11
+#define	CODE_TYPE	0x14
+#define	INDICATOR	0x15
+
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_vpd_read)
+ *	mac_drv_vpd_read(smc,buf,size,image)
+ *
+ * function	DOWNCALL	(FDDIWARE)
+ *		reads the VPD data of the FPROM and writes it into the
+ *		buffer
+ *
+ * para	buf	points to the buffer for the VPD data
+ *	size	size of the VPD data buffer
+ *	image	boot image; code type of the boot image
+ *		image = 0	Intel x86, PC-AT compatible
+ *			1	OPENBOOT standard for PCI
+ *			2-FF	reserved
+ *
+ * returns	len	number of VPD data bytes read form the FPROM
+ *		<0	number of read bytes
+ *		>0	error: data invalid
+ *
+ *	END_MANUAL_ENTRY
+ */
+int mac_drv_vpd_read(smc,buf,size,image)
+struct s_smc *smc ;
+char *buf ;
+int size ;
+char image ;
+{
+	u_short	ibase ;
+	u_short pci_base ;
+	u_short vpd ;
+	int	len ;
+
+	len = 0 ;
+	ibase = 0 ;
+	/*
+	 * as long images defined
+	 */
+	while (get_rom_byte(smc,ibase+ROM_SIG_1) == 0x55 &&
+		(u_char) get_rom_byte(smc,ibase+ROM_SIG_2) == 0xaa) {
+		/*
+		 * get the pointer to the PCI data structure
+		 */
+		pci_base = ibase + get_rom_byte(smc,ibase+PCI_DATA_1) +
+				(get_rom_byte(smc,ibase+PCI_DATA_2) << 8) ;
+
+		if (image == get_rom_byte(smc,pci_base+CODE_TYPE)) {
+			/*
+			 * we have the right image, read the VPD data
+			 */
+			vpd = ibase + get_rom_byte(smc,pci_base+VPD_DATA_1) +
+				(get_rom_byte(smc,pci_base+VPD_DATA_2) << 8) ;
+			if (vpd == ibase) {
+				break ;		/* no VPD data */
+			}
+			for (len = 0; len < size; len++,buf++,vpd++) {
+				*buf = get_rom_byte(smc,vpd) ;
+			}
+			break ;
+		}
+		else {
+			/*
+			 * try the next image
+			 */
+			if (get_rom_byte(smc,pci_base+INDICATOR) & 0x80) {
+				break ;		/* this was the last image */
+			}
+			ibase = ibase + get_rom_byte(smc,ibase+IMAGE_LEN_1) +
+				(get_rom_byte(smc,ibase+IMAGE_LEN_2) << 8) ;
+		}
+	}
+
+	return(len) ;
+}
+
+void mac_drv_pci_fix(smc,fix_value)
+struct s_smc *smc ;
+u_long fix_value ;
+{
+	smc->hw.pci_fix_value = fix_value ;
+}
+
+void mac_do_pci_fix(smc)
+struct s_smc *smc ;
+{
+	SK_UNUSED(smc) ;
+}
+#endif	/* PCI */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/ecm.c linux.18p17/drivers/net/skfp/ecm.c
--- linux.vanilla/drivers/net/skfp/ecm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/ecm.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,547 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT ECM
+	Entity Coordination Management
+	Hardware independant state machine
+*/
+
+/*
+ * Hardware independant state machine implemantation
+ * The following external SMT functions are referenced :
+ *
+ * 		queue_event()
+ * 		smt_timer_start()
+ * 		smt_timer_stop()
+ *
+ * 	The following external HW dependant functions are referenced :
+ * 		sm_pm_bypass_req()
+ * 		sm_pm_ls_latch()
+ * 		sm_pm_get_ls()
+ * 
+ * 	The following HW dependant events are required :
+ *		NONE
+ *
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)ecm.c	2.7 99/08/05 (C) SK " ;
+#endif
+
+/*
+ * FSM Macros
+ */
+#define AFLAG	0x10
+#define GO_STATE(x)	(smc->mib.fddiSMTECMState = (x)|AFLAG)
+#define ACTIONS_DONE()	(smc->mib.fddiSMTECMState &= ~AFLAG)
+#define ACTIONS(x)	(x|AFLAG)
+
+#define EC0_OUT		0			/* not inserted */
+#define EC1_IN		1			/* inserted */
+#define EC2_TRACE	2			/* tracing */
+#define EC3_LEAVE	3			/* leaving the ring */
+#define EC4_PATH_TEST	4			/* performing path test */
+#define EC5_INSERT	5			/* bypass being turned on */
+#define EC6_CHECK	6			/* checking bypass */
+#define EC7_DEINSERT	7			/* bypass being turnde off */
+
+#ifdef	DEBUG
+/*
+ * symbolic state names
+ */
+static const char * const ecm_states[] = {
+	"EC0_OUT","EC1_IN","EC2_TRACE","EC3_LEAVE","EC4_PATH_TEST",
+	"EC5_INSERT","EC6_CHECK","EC7_DEINSERT"
+} ;
+
+/*
+ * symbolic event names
+ */
+static const char * const ecm_events[] = {
+	"NONE","EC_CONNECT","EC_DISCONNECT","EC_TRACE_PROP","EC_PATH_TEST",
+	"EC_TIMEOUT_TD","EC_TIMEOUT_TMAX",
+	"EC_TIMEOUT_IMAX","EC_TIMEOUT_INMAX","EC_TEST_DONE"
+} ;
+#endif
+
+/*
+ * all Globals  are defined in smc.h
+ * struct s_ecm
+ */
+
+/*
+ * function declarations
+ */
+
+static void ecm_fsm() ;
+static void start_ecm_timer() ;
+static void stop_ecm_timer() ;
+static void prop_actions() ;
+
+/*
+	init ECM state machine
+	clear all ECM vars and flags
+*/
+void ecm_init(smc)
+struct s_smc *smc ;
+{
+	smc->e.path_test = PT_PASSED ;
+	smc->e.trace_prop = 0 ;
+	smc->e.sb_flag = 0 ;
+	smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ;
+	smc->e.ecm_line_state = FALSE ;
+}
+
+/*
+	ECM state machine
+	called by dispatcher
+
+	do
+		display state change
+		process event
+	until SM is stable
+*/
+void ecm(smc,event)
+struct s_smc *smc ;
+int event ;
+{
+	int	state ;
+
+	do {
+		DB_ECM("ECM : state %s%s",
+			(smc->mib.fddiSMTECMState & AFLAG) ? "ACTIONS " : "",
+			ecm_states[smc->mib.fddiSMTECMState & ~AFLAG]) ;
+		DB_ECM(" event %s\n",ecm_events[event],0) ;
+		state = smc->mib.fddiSMTECMState ;
+		ecm_fsm(smc,event) ;
+		event = 0 ;
+	} while (state != smc->mib.fddiSMTECMState) ;
+	ecm_state_change(smc,(int)smc->mib.fddiSMTECMState) ;
+}
+
+/*
+	process ECM event
+*/
+static void ecm_fsm(smc,cmd)
+struct s_smc *smc ;
+int cmd ;
+{
+	int ls_a ;			/* current line state PHY A */
+	int ls_b ;			/* current line state PHY B */
+	int	p ;			/* ports */
+
+
+	smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ;
+	if (cmd == EC_CONNECT)
+		smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
+
+	/* For AIX event notification: */
+	/* Is a disconnect  command remotely issued ? */
+	if (cmd == EC_DISCONNECT &&
+		smc->mib.fddiSMTRemoteDisconnectFlag == TRUE)
+		AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long)
+			FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc),
+			smt_get_error_word(smc) );
+
+	/*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/
+	if (cmd == EC_CONNECT) {
+		smc->e.DisconnectFlag = FALSE ;
+	}
+	else if (cmd == EC_DISCONNECT) {
+		smc->e.DisconnectFlag = TRUE ;
+	}
+	
+	switch(smc->mib.fddiSMTECMState) {
+	case ACTIONS(EC0_OUT) :
+		/*
+		 * We do not perform a path test
+		 */
+		smc->e.path_test = PT_PASSED ;
+		smc->e.ecm_line_state = FALSE ;
+		stop_ecm_timer(smc) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC0_OUT:
+		/*EC01*/
+		if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent
+			&& smc->e.path_test==PT_PASSED) {
+			GO_STATE(EC1_IN) ;
+			break ;
+		}
+		/*EC05*/
+		else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) &&
+			smc->mib.fddiSMTBypassPresent &&
+			(smc->s.sas == SMT_DAS)) {
+			GO_STATE(EC5_INSERT) ;
+			break ;
+		}
+		break;
+	case ACTIONS(EC1_IN) :
+		stop_ecm_timer(smc) ;
+		smc->e.trace_prop = 0 ;
+		sm_ma_control(smc,MA_TREQ) ;
+		for (p = 0 ; p < NUMPHYS ; p++)
+			if (smc->mib.p[p].fddiPORTHardwarePresent)
+				queue_event(smc,EVENT_PCMA+p,PC_START) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC1_IN:
+		/*EC12*/
+		if (cmd == EC_TRACE_PROP) {
+			prop_actions(smc) ;
+			GO_STATE(EC2_TRACE) ;
+			break ;
+		}
+		/*EC13*/
+		else if (cmd == EC_DISCONNECT) {
+			GO_STATE(EC3_LEAVE) ;
+			break ;
+		}
+		break;
+	case ACTIONS(EC2_TRACE) :
+		start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration),
+			EC_TIMEOUT_TMAX) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC2_TRACE :
+		/*EC22*/
+		if (cmd == EC_TRACE_PROP) {
+			prop_actions(smc) ;
+			GO_STATE(EC2_TRACE) ;
+			break ;
+		}
+		/*EC23a*/
+		else if (cmd == EC_DISCONNECT) {
+			smc->e.path_test = PT_EXITING ;
+			GO_STATE(EC3_LEAVE) ;
+			break ;
+		}
+		/*EC23b*/
+		else if (smc->e.path_test == PT_PENDING) {
+			GO_STATE(EC3_LEAVE) ;
+			break ;
+		}
+		/*EC23c*/
+		else if (cmd == EC_TIMEOUT_TMAX) {
+			/* Trace_Max is expired */
+			/* -> send AIX_EVENT */
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS,
+				(u_long) FDDI_SMT_ERROR, (u_long)
+				FDDI_TRACE_MAX, smt_get_error_word(smc));
+			smc->e.path_test = PT_PENDING ;
+			GO_STATE(EC3_LEAVE) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(EC3_LEAVE) :
+		start_ecm_timer(smc,smc->s.ecm_td_min,EC_TIMEOUT_TD) ;
+		for (p = 0 ; p < NUMPHYS ; p++)
+			queue_event(smc,EVENT_PCMA+p,PC_STOP) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC3_LEAVE:
+		/*EC30*/
+		if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent &&
+			(smc->e.path_test != PT_PENDING)) {
+			GO_STATE(EC0_OUT) ;
+			break ;
+		}
+		/*EC34*/
+		else if (cmd == EC_TIMEOUT_TD &&
+			(smc->e.path_test == PT_PENDING)) {
+			GO_STATE(EC4_PATH_TEST) ;
+			break ;
+		}
+		/*EC31*/
+		else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
+			GO_STATE(EC1_IN) ;
+			break ;
+		}
+		/*EC33*/
+		else if (cmd == EC_DISCONNECT &&
+			smc->e.path_test == PT_PENDING) {
+			smc->e.path_test = PT_EXITING ;
+			/*
+			 * stay in state - state will be left via timeout
+			 */
+		}
+		/*EC37*/
+		else if (cmd == EC_TIMEOUT_TD &&
+			smc->mib.fddiSMTBypassPresent &&
+			smc->e.path_test != PT_PENDING) {
+			GO_STATE(EC7_DEINSERT) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(EC4_PATH_TEST) :
+		stop_ecm_timer(smc) ;
+		smc->e.path_test = PT_TESTING ;
+		start_ecm_timer(smc,smc->s.ecm_test_done,EC_TEST_DONE) ;
+		/* now perform path test ... just a simulation */
+		ACTIONS_DONE() ;
+		break ;
+	case EC4_PATH_TEST :
+		/* path test done delay */
+		if (cmd == EC_TEST_DONE)
+			smc->e.path_test = PT_PASSED ;
+
+		if (smc->e.path_test == PT_FAILED)
+			RS_SET(smc,RS_PATHTEST) ;
+
+		/*EC40a*/
+		if (smc->e.path_test == PT_FAILED &&
+			!smc->mib.fddiSMTBypassPresent) {
+			GO_STATE(EC0_OUT) ;
+			break ;
+		}
+		/*EC40b*/
+		else if (cmd == EC_DISCONNECT &&
+			!smc->mib.fddiSMTBypassPresent) {
+			GO_STATE(EC0_OUT) ;
+			break ;
+		}
+		/*EC41*/
+		else if (smc->e.path_test == PT_PASSED) {
+			GO_STATE(EC1_IN) ;
+			break ;
+		}
+		/*EC47a*/
+		else if (smc->e.path_test == PT_FAILED &&
+			smc->mib.fddiSMTBypassPresent) {
+			GO_STATE(EC7_DEINSERT) ;
+			break ;
+		}
+		/*EC47b*/
+		else if (cmd == EC_DISCONNECT &&
+			smc->mib.fddiSMTBypassPresent) {
+			GO_STATE(EC7_DEINSERT) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(EC5_INSERT) :
+		sm_pm_bypass_req(smc,BP_INSERT);
+		start_ecm_timer(smc,smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC5_INSERT :
+		/*EC56*/
+		if (cmd == EC_TIMEOUT_INMAX) {
+			GO_STATE(EC6_CHECK) ;
+			break ;
+		}
+		/*EC57*/
+		else if (cmd == EC_DISCONNECT) {
+			GO_STATE(EC7_DEINSERT) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(EC6_CHECK) :
+		/*
+		 * in EC6_CHECK, we *POLL* the line state !
+		 * check whether both bypass switches have switched.
+		 */
+		start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
+		smc->e.ecm_line_state = TRUE ;	/* flag to pcm: report Q/HLS */
+		(void) sm_pm_ls_latch(smc,PA,1) ; /* enable line state latch */
+		(void) sm_pm_ls_latch(smc,PB,1) ; /* enable line state latch */
+		ACTIONS_DONE() ;
+		break ;
+	case EC6_CHECK :
+		ls_a = sm_pm_get_ls(smc,PA) ;
+		ls_b = sm_pm_get_ls(smc,PB) ;
+
+		/*EC61*/
+		if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) &&
+		    ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) {
+			smc->e.sb_flag = FALSE ;
+			smc->e.ecm_line_state = FALSE ;
+			GO_STATE(EC1_IN) ;
+			break ;
+		}
+		/*EC66*/
+		else if (!smc->e.sb_flag &&
+			 (((ls_a == PC_ILS) && (ls_b == PC_QLS)) ||
+			  ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){
+			smc->e.sb_flag = TRUE ;
+			DB_ECMN(1,"ECM : EC6_CHECK - stuck bypass\n",0,0) ;
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+				FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK,
+				smt_get_error_word(smc));
+		}
+		/*EC67*/
+		else if (cmd == EC_DISCONNECT) {
+			smc->e.ecm_line_state = FALSE ;
+			GO_STATE(EC7_DEINSERT) ;
+			break ;
+		}
+		else {
+			/*
+			 * restart poll
+			 */
+			start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
+		}
+		break ;
+	case ACTIONS(EC7_DEINSERT) :
+		sm_pm_bypass_req(smc,BP_DEINSERT);
+		start_ecm_timer(smc,smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ;
+		ACTIONS_DONE() ;
+		break ;
+	case EC7_DEINSERT:
+		/*EC70*/
+		if (cmd == EC_TIMEOUT_IMAX) {
+			GO_STATE(EC0_OUT) ;
+			break ;
+		}
+		/*EC75*/
+		else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
+			GO_STATE(EC5_INSERT) ;
+			break ;
+		}
+		break;
+	default:
+		SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ;
+		break;
+	}
+}
+
+#ifndef	CONCENTRATOR
+/*
+ * trace propagation actions for SAS & DAS
+ */
+static void prop_actions(smc)
+struct s_smc *smc ;
+{
+	int	port_in = 0 ;
+	int	port_out = 0 ;
+
+	RS_SET(smc,RS_EVENT) ;
+	switch (smc->s.sas) {
+	case SMT_SAS :
+		port_in = port_out = pcm_get_s_port(smc) ;
+		break ;
+	case SMT_DAS :
+		port_in = cfm_get_mac_input(smc) ;	/* PA or PB */
+		port_out = cfm_get_mac_output(smc) ;	/* PA or PB */
+		break ;
+	case SMT_NAC :
+		SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ;
+		return ;
+	}
+
+	DB_ECM("ECM : prop_actions - trace_prop %d\n", smc->e.trace_prop,0) ;
+	DB_ECM("ECM : prop_actions - in %d out %d\n", port_in,port_out) ;
+
+	if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
+		/* trace initiatior */
+		DB_ECM("ECM : initiate TRACE on PHY %c\n",'A'+port_in-PA,0) ;
+		queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ;
+	}
+	else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) &&
+		port_out != PA) {
+		/* trace propagate upstream */
+		DB_ECM("ECM : propagate TRACE on PHY B\n",0,0) ;
+		queue_event(smc,EVENT_PCMB,PC_TRACE) ;
+	}
+	else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) &&
+		port_out != PB) {
+		/* trace propagate upstream */
+		DB_ECM("ECM : propagate TRACE on PHY A\n",0,0) ;
+		queue_event(smc,EVENT_PCMA,PC_TRACE) ;
+	}
+	else {
+		/* signal trace termination */
+		DB_ECM("ECM : TRACE terminated\n",0,0) ;
+		smc->e.path_test = PT_PENDING ;
+	}
+	smc->e.trace_prop = 0 ;
+}
+#else
+/*
+ * trace propagation actions for Concentrator
+ */
+static void prop_actions(smc)
+struct s_smc *smc ;
+{
+	int	initiator ;
+	int	upstream ;
+	int	p ;
+
+	RS_SET(smc,RS_EVENT) ;
+	while (smc->e.trace_prop) {
+		DB_ECM("ECM : prop_actions - trace_prop %d\n",
+			smc->e.trace_prop,0) ;
+
+		if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
+			initiator = ENTITY_MAC ;
+			smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ;
+			DB_ECM("ECM: MAC initiates trace\n",0,0) ;
+		}
+		else {
+			for (p = NUMPHYS-1 ; p >= 0 ; p--) {
+				if (smc->e.trace_prop &
+					ENTITY_BIT(ENTITY_PHY(p)))
+					break ;
+			}
+			initiator = ENTITY_PHY(p) ;
+			smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ;
+		}
+		upstream = cem_get_upstream(smc,initiator) ;
+
+		if (upstream == ENTITY_MAC) {
+			/* signal trace termination */
+			DB_ECM("ECM : TRACE terminated\n",0,0) ;
+			smc->e.path_test = PT_PENDING ;
+		}
+		else {
+			/* trace propagate upstream */
+			DB_ECM("ECM : propagate TRACE on PHY %d\n",upstream,0) ;
+			queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ;
+		}
+	}
+}
+#endif
+
+
+/*
+ * SMT timer interface
+ *	start ECM timer
+ */
+static void start_ecm_timer(smc,value,event)
+struct s_smc *smc ;
+u_long value;
+int event ;
+{
+	smt_timer_start(smc,&smc->e.ecm_timer,value,EV_TOKEN(EVENT_ECM,event));
+}
+
+/*
+ * SMT timer interface
+ *	stop ECM timer
+ */
+static void stop_ecm_timer(smc)
+struct s_smc *smc ;
+{
+	if (smc->e.ecm_timer.tm_active)
+		smt_timer_stop(smc,&smc->e.ecm_timer) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/ess.c linux.18p17/drivers/net/skfp/ess.c
--- linux.vanilla/drivers/net/skfp/ess.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/ess.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,732 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * *******************************************************************
+ * This SBA code implements the Synchronous Bandwidth Allocation
+ * functions described in the "FDDI Synchronous Forum Implementer's
+ * Agreement" dated December 1th, 1993.
+ * *******************************************************************
+ *
+ *	PURPOSE: The purpose of this function is to control
+ *		 synchronous allocations on a single FDDI segment.
+ *		 Allocations are limited to the primary FDDI ring.
+ *		 The SBM provides recovery mechanisms to recover
+ *		 unused bandwidth also resolves T_Neg and
+ *		 reconfiguration changes. Many of the SBM state
+ *		 machine inputs are sourced by the underlying
+ *		 FDDI sub-system supporting the SBA application.
+ *
+ * *******************************************************************
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smt_p.h"
+
+
+#ifndef	SLIM_SMT
+
+#ifdef ESS
+
+#ifndef lint
+static const char ID_sccs[] = "@(#)ess.c	1.10 96/02/23 (C) SK" ;
+#define LINT_USE(x)
+#else
+#define LINT_USE(x)	(x)=(x)
+#endif
+#define MS2BCLK(x)	((x)*12500L)
+
+/*
+	-------------------------------------------------------------
+	LOCAL VARIABLES:
+	-------------------------------------------------------------
+*/
+
+static const u_short plist_raf_alc_res[] = { SMT_P0012, SMT_P320B, SMT_P320F,
+					SMT_P3210, SMT_P0019, SMT_P001A,
+					SMT_P001D, 0 } ;
+
+static const u_short plist_raf_chg_req[] = { SMT_P320B, SMT_P320F, SMT_P3210,
+					SMT_P001A, 0 } ;
+
+static const struct fddi_addr smt_sba_da = {{0x80,0x01,0x43,0x00,0x80,0x0C}} ;
+static const struct fddi_addr null_addr = {{0,0,0,0,0,0}} ;
+
+/*
+	-------------------------------------------------------------
+	GLOBAL VARIABLES:
+	-------------------------------------------------------------
+*/
+
+
+/*
+	-------------------------------------------------------------
+	LOCAL FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+static	void	ess_send_response(),		ess_config_fifo(),
+		ess_send_alc_req(),		ess_send_frame() ;
+
+/*
+	-------------------------------------------------------------
+	EXTERNAL FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+extern	void	*sm_to_para() ;
+
+extern	void	smt_send_frame(),	smt_free_mbuf(),
+		set_formac_tsync(),	formac_reinit_tx() ;
+
+extern	int	smt_check_para() ;
+
+extern	SMbuf	*smt_get_mbuf(),	*smt_build_frame() ;
+
+extern	u_long	smt_get_tid() ;
+
+/*
+	-------------------------------------------------------------
+	PUBLIC FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+	void	ess_timer_poll(),		ess_para_change() ;
+
+	int	ess_raf_received_pack(),	process_bw_alloc() ;
+
+
+/*
+ * --------------------------------------------------------------------------
+ *	End Station Support	(ESS)
+ * --------------------------------------------------------------------------
+ */
+
+/*
+ * evaluate the RAF frame
+ */
+int ess_raf_received_pack(smc,mb,sm,fs)
+struct s_smc *smc ;
+SMbuf *mb ;
+struct smt_header *sm ;
+int fs ;
+{
+	void			*p ;		/* universal pointer */
+	struct smt_p_0016	*cmd ;		/* para: command for the ESS */
+	SMbuf			*db ;
+	u_long			msg_res_type ;	/* recource type */
+	u_long			payload, overhead ;
+	int			local ;
+	int			i ;
+
+	/*
+	 * Message Processing Code
+	 */
+	 local = ((fs & L_INDICATOR) != 0) ;
+
+	/*
+	 * get the resource type
+	 */
+	if (!(p = (void *) sm_to_para(smc,sm,SMT_P0015))) {
+		DB_ESS("ESS: RAF frame error, parameter type not found\n",0,0) ;
+		return(fs) ;
+	}
+	msg_res_type = ((struct smt_p_0015 *)p)->res_type ;
+
+	/*
+	 * get the pointer to the ESS command
+	 */
+	if (!(cmd = (struct smt_p_0016 *) sm_to_para(smc,sm,SMT_P0016))) {
+		/*
+		 * error in frame: para ESS command was not found
+		 */
+		 DB_ESS("ESS: RAF frame error, parameter command not found\n",0,0);
+		 return(fs) ;
+	}
+
+	DB_ESSN(2,"fc %x	ft %x\n",sm->smt_class,sm->smt_type) ;
+	DB_ESSN(2,"ver %x	tran %lx\n",sm->smt_version,sm->smt_tid) ;
+	DB_ESSN(2,"stn_id %s\n",addr_to_string(&sm->smt_source),0) ;
+
+	DB_ESSN(2,"infolen %x	res %x\n",sm->smt_len, msg_res_type) ;
+	DB_ESSN(2,"sbacmd %x\n",cmd->sba_cmd,0) ;
+
+	/*
+	 * evaluate the ESS command
+	 */
+	switch (cmd->sba_cmd) {
+
+	/*
+	 * Process an ESS Allocation Request
+	 */
+	case REQUEST_ALLOCATION :
+		/*
+		 * check for an RAF Request (Allocation Request)
+		 */
+		if (sm->smt_type == SMT_REQUEST) {
+			/*
+			 * process the Allocation request only if the frame is
+			 * local and no static allocation is used
+			 */
+			if (!local || smc->mib.fddiESSPayload)
+				return(fs) ;
+			
+			p = (void *) sm_to_para(smc,sm,SMT_P0019)  ;
+			for (i = 0; i < 5; i++) {
+				if (((struct smt_p_0019 *)p)->alloc_addr.a[i]) {
+					return(fs) ;
+				}
+			}
+
+			/*
+			 * Note: The Application should send a LAN_LOC_FRAME.
+			 *	 The ESS do not send the Frame to the network!
+			 */
+			smc->ess.alloc_trans_id = sm->smt_tid ;
+			DB_ESS("ESS: save Alloc Req Trans ID %lx\n",sm->smt_tid,0);
+			p = (void *) sm_to_para(smc,sm,SMT_P320F) ;
+			((struct smt_p_320f *)p)->mib_payload =
+				smc->mib.a[PATH0].fddiPATHSbaPayload ;
+			p = (void *) sm_to_para(smc,sm,SMT_P3210) ;
+			((struct smt_p_3210 *)p)->mib_overhead =
+				smc->mib.a[PATH0].fddiPATHSbaOverhead ;
+			sm->smt_dest = smt_sba_da ;
+
+			if (smc->ess.local_sba_active)
+				return(fs | I_INDICATOR) ;
+
+			if (!(db = smt_get_mbuf(smc)))
+				return(fs) ;
+
+			db->sm_len = mb->sm_len ;
+			db->sm_off = mb->sm_off ;
+			memcpy(((char *)(db->sm_data+db->sm_off)),(char *)sm,
+				(int)db->sm_len) ;
+			dump_smt(smc,
+				(struct smt_header *)(db->sm_data+db->sm_off),
+				"RAF") ;
+			smt_send_frame(smc,db,FC_SMT_INFO,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * The RAF frame is an Allocation Response !
+		 * check the parameters
+		 */
+		if (smt_check_para(smc,sm,plist_raf_alc_res)) {
+			DB_ESS("ESS: RAF with para problem, ignoring\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * VERIFY THE FRAME IS WELL BUILT:
+		 *
+		 *	1. path index = primary ring only
+		 *	2. resource type = sync bw only
+		 *	3. trans action id = alloc_trans_id
+		 *	4. reason code = success
+		 *
+		 * If any are violated, discard the RAF frame
+		 */
+		if ((((struct smt_p_320b *)sm_to_para(smc,sm,SMT_P320B))->path_index
+			!= PRIMARY_RING) ||
+			(msg_res_type != SYNC_BW) ||
+		(((struct smt_p_reason *)sm_to_para(smc,sm,SMT_P0012))->rdf_reason
+			!= SMT_RDF_SUCCESS) ||
+			(sm->smt_tid != smc->ess.alloc_trans_id)) {
+
+			DB_ESS("ESS: Allocation Responce not accepted\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * Extract message parameters
+		 */
+		p = (void *) sm_to_para(smc,sm,SMT_P320F) ;
+		payload = ((struct smt_p_320f *)p)->mib_payload ;
+		p = (void *) sm_to_para(smc,sm,SMT_P3210) ;
+		overhead = ((struct smt_p_3210 *)p)->mib_overhead ;
+
+		DB_ESSN(2,"payload= %lx	overhead= %lx\n",payload,overhead) ;
+
+		/*
+		 * process the bandwidth allocation
+		 */
+		(void)process_bw_alloc(smc,(long)payload,(long)overhead) ;
+
+		return(fs) ;
+		/* end of Process Allocation Request */
+
+	/*
+	 * Process an ESS Change Request
+	 */
+	case CHANGE_ALLOCATION :
+		/*
+		 * except only replies
+		 */
+		if (sm->smt_type != SMT_REQUEST) {
+			DB_ESS("ESS: Do not process Change Responses\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * check the para for the Change Request
+		 */
+		if (smt_check_para(smc,sm,plist_raf_chg_req)) {
+			DB_ESS("ESS: RAF with para problem, ignoring\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * Verify the path index and resource
+		 * type are correct. If any of
+		 * these are false, don't process this
+		 * change request frame.
+		 */
+		if ((((struct smt_p_320b *)sm_to_para(smc,sm,SMT_P320B))->path_index
+			!= PRIMARY_RING) || (msg_res_type != SYNC_BW)) {
+			DB_ESS("ESS: RAF frame with para problem, ignoring\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * Extract message queue parameters
+		 */
+		p = (void *) sm_to_para(smc,sm,SMT_P320F) ;
+		payload = ((struct smt_p_320f *)p)->mib_payload ;
+		p = (void *) sm_to_para(smc,sm,SMT_P3210) ;
+		overhead = ((struct smt_p_3210 *)p)->mib_overhead ;
+
+		DB_ESSN(2,"ESS: Change Request from %s\n",
+			addr_to_string(&sm->smt_source),0) ;
+		DB_ESSN(2,"payload= %lx	overhead= %lx\n",payload,overhead) ;
+
+		/*
+		 * process the bandwidth allocation
+		 */
+		if(!process_bw_alloc(smc,(long)payload,(long)overhead))
+			return(fs) ;
+
+		/*
+		 * send an RAF Change Reply
+		 */
+		ess_send_response(smc,sm,CHANGE_ALLOCATION) ;
+
+		return(fs) ;
+		/* end of Process Change Request */
+
+	/*
+	 * Process Report Response
+	 */
+	case REPORT_ALLOCATION :
+		/*
+		 * except only requests
+		 */
+		if (sm->smt_type != SMT_REQUEST) {
+			DB_ESS("ESS: Do not process a Report Reply\n",0,0) ;
+			return(fs) ;
+		}
+
+		DB_ESSN(2,"ESS: Report Request from %s\n",
+			addr_to_string(&(sm->smt_source)),0) ;
+
+		/*
+		 * verify that the resource type is sync bw only
+		 */
+		if (msg_res_type != SYNC_BW) {
+			DB_ESS("ESS: ignoring RAF with para problem\n",0,0) ;
+			return(fs) ;
+		}
+
+		/*
+		 * send an RAF Change Reply
+		 */
+		ess_send_response(smc,sm,REPORT_ALLOCATION) ;
+
+		return(fs) ;
+		/* end of Process Report Request */
+
+	default:
+		/*
+		 * error in frame
+		 */
+		DB_ESS("ESS: ignoring RAF with bad sba_cmd\n",0,0) ;
+		break ;
+	}
+
+	return(fs) ;
+}
+
+/*
+ * determines the synchronous bandwidth, set the TSYNC register and the
+ * mib variables SBAPayload, SBAOverhead and fddiMACT-NEG.
+ */
+int process_bw_alloc(smc,payload,overhead)
+struct s_smc *smc ;
+long payload ;
+long overhead ;
+{
+	/*
+	 * determine the synchronous bandwidth (sync_bw) in bytes per T-NEG,
+	 * if the payload is greater than zero.
+	 * For the SBAPayload and the SBAOverhead we have the following
+	 * unite quations
+ 	 *		      _		  _
+	 *		     |	     bytes |
+	 *	SBAPayload = | 8000 ------ |
+	 *		     |		s  |
+	 *		      -		  -
+ 	 *		       _       _
+	 *		      |	 bytes	|
+	 *	SBAOverhead = | ------	|
+	 *		      |	 T-NEG	|
+	 *		       -       -
+ 	 *
+	 * T-NEG is discribed by the equation:
+	 *
+	 *		     (-) fddiMACT-NEG
+	 *	T-NEG =	    -------------------
+	 *			12500000 1/s
+	 *
+	 * The number of bytes we are able to send is the payload
+	 * plus the overhead.
+	 *
+	 *			  bytes    T-NEG SBAPayload 8000 bytes/s
+	 * sync_bw =  SBAOverhead ------ + -----------------------------
+	 *	   		  T-NEG		T-NEG
+	 *
+	 *
+	 *	      		     1
+	 * sync_bw =  SBAOverhead + ---- (-)fddiMACT-NEG * SBAPayload
+	 *	       		    1562
+	 *
+	 */
+
+	/*
+	 * set the mib attributes fddiPATHSbaOverhead, fddiPATHSbaPayload
+	 */
+/*	if (smt_set_obj(smc,SMT_P320F,payload,S_SET)) {
+		DB_ESS("ESS: SMT does not accept the payload value\n",0,0) ;
+		return(FALSE) ;
+	}
+	if (smt_set_obj(smc,SMT_P3210,overhead,S_SET)) {
+		DB_ESS("ESS: SMT does not accept the overhead value\n",0,0) ;
+		return(FALSE) ;
+	} */
+
+	/* premliminary */
+	if (payload > MAX_PAYLOAD || overhead > 5000) {
+		DB_ESS("ESS: payload / overhead not accepted\n",0,0) ;
+		return(FALSE) ;
+	}
+
+	/*
+	 * start the iterative allocation process if the payload or the overhead
+	 * are smaller than the parsed values
+	 */
+	if (smc->mib.fddiESSPayload &&
+		((u_long)payload != smc->mib.fddiESSPayload ||
+		(u_long)overhead != smc->mib.fddiESSOverhead)) {
+		smc->ess.raf_act_timer_poll = TRUE ;
+		smc->ess.timer_count = 0 ;
+	}
+
+	/*
+	 * evulate the Payload
+	 */
+	if (payload) {
+		DB_ESSN(2,"ESS: turn SMT_ST_SYNC_SERVICE bit on\n",0,0) ;
+		smc->ess.sync_bw_available = TRUE ;
+
+		smc->ess.sync_bw = overhead -
+			(long)smc->mib.m[MAC0].fddiMACT_Neg *
+			payload / 1562 ;
+	}
+	else {
+		DB_ESSN(2,"ESS: turn SMT_ST_SYNC_SERVICE bit off\n",0,0) ;
+		smc->ess.sync_bw_available = FALSE ;
+		smc->ess.sync_bw = 0 ;
+		overhead = 0 ;
+	}
+
+	smc->mib.a[PATH0].fddiPATHSbaPayload = payload ;
+	smc->mib.a[PATH0].fddiPATHSbaOverhead = overhead ;
+
+
+	DB_ESSN(2,"tsync = %lx\n",smc->ess.sync_bw,0) ;
+
+	ess_config_fifo(smc) ;
+	set_formac_tsync(smc,smc->ess.sync_bw) ;
+	return(TRUE) ;
+}
+
+static void ess_send_response(smc,sm,sba_cmd)
+struct s_smc *smc ;
+struct smt_header *sm ;
+int sba_cmd ;
+{
+	struct smt_sba_chg	*chg ;
+	SMbuf			*mb ;
+	void			*p ;
+
+	/*
+	 * get and initialize the responce frame
+	 */
+	if (sba_cmd == CHANGE_ALLOCATION) {
+		if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REPLY,
+				sizeof(struct smt_sba_chg))))
+				return ;
+	}
+	else {
+		if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REPLY,
+				sizeof(struct smt_sba_rep_res))))
+				return ;
+	}
+
+	chg = smtod(mb,struct smt_sba_chg *) ;
+	chg->smt.smt_tid = sm->smt_tid ;
+	chg->smt.smt_dest = sm->smt_source ;
+
+	/* set P15 */
+	chg->s_type.para.p_type = SMT_P0015 ;
+	chg->s_type.para.p_len = sizeof(struct smt_p_0015) - PARA_LEN ;
+	chg->s_type.res_type = SYNC_BW ;
+
+	/* set P16 */
+	chg->cmd.para.p_type = SMT_P0016 ;
+	chg->cmd.para.p_len = sizeof(struct smt_p_0016) - PARA_LEN ;
+	chg->cmd.sba_cmd = sba_cmd ;
+
+	/* set P320B */
+	chg->path.para.p_type = SMT_P320B ;
+	chg->path.para.p_len = sizeof(struct smt_p_320b) - PARA_LEN ;
+	chg->path.mib_index = SBAPATHINDEX ;
+	chg->path.path_pad = (u_short)NULL ;
+	chg->path.path_index = PRIMARY_RING ;
+
+	/* set P320F */
+	chg->payload.para.p_type = SMT_P320F ;
+	chg->payload.para.p_len = sizeof(struct smt_p_320f) - PARA_LEN ;
+	chg->payload.mib_index = SBAPATHINDEX ;
+	chg->payload.mib_payload = smc->mib.a[PATH0].fddiPATHSbaPayload ;
+
+	/* set P3210 */
+	chg->overhead.para.p_type = SMT_P3210 ;
+	chg->overhead.para.p_len = sizeof(struct smt_p_3210) - PARA_LEN ;
+	chg->overhead.mib_index = SBAPATHINDEX ;
+	chg->overhead.mib_overhead = smc->mib.a[PATH0].fddiPATHSbaOverhead ;
+
+	if (sba_cmd == CHANGE_ALLOCATION) {
+		/* set P1A */
+		chg->cat.para.p_type = SMT_P001A ;
+		chg->cat.para.p_len = sizeof(struct smt_p_001a) - PARA_LEN ;
+		p = (void *) sm_to_para(smc,sm,SMT_P001A) ;
+		chg->cat.category = ((struct smt_p_001a *)p)->category ;
+	}
+	dump_smt(smc,(struct smt_header *)chg,"RAF") ;
+	ess_send_frame(smc,mb) ;
+}
+
+
+void ess_timer_poll(smc)
+struct s_smc *smc ;
+{
+	if (!smc->ess.raf_act_timer_poll)
+		return ;
+
+	DB_ESSN(2,"ESS: timer_poll\n",0,0) ;
+
+	smc->ess.timer_count++ ;
+	if (smc->ess.timer_count == 10) {
+		smc->ess.timer_count = 0 ;
+		ess_send_alc_req(smc) ;
+	}
+}
+
+static void ess_send_alc_req(smc)
+struct s_smc *smc ;
+{
+	struct smt_sba_alc_req *req ;
+	SMbuf	*mb ;
+
+	/*
+	 * send never allocation request where the requested payload and
+	 * overhead is zero or deallocate bandwidht when no bandwidth is
+	 * parsed
+	 */
+	if (!smc->mib.fddiESSPayload) {
+		smc->mib.fddiESSOverhead = 0 ;
+	}
+	else {
+		if (!smc->mib.fddiESSOverhead)
+			smc->mib.fddiESSOverhead = DEFAULT_OV ;
+	}
+
+	if (smc->mib.fddiESSOverhead ==
+		smc->mib.a[PATH0].fddiPATHSbaOverhead &&
+		smc->mib.fddiESSPayload ==
+		smc->mib.a[PATH0].fddiPATHSbaPayload){
+		smc->ess.raf_act_timer_poll = FALSE ;
+		smc->ess.timer_count = 7 ;	/* next RAF alc req after 3 s */
+		return ;
+	}
+	
+	/*
+	 * get and initialize the responce frame
+	 */
+	if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REQUEST,
+			sizeof(struct smt_sba_alc_req))))
+			return ;
+	req = smtod(mb,struct smt_sba_alc_req *) ;
+	req->smt.smt_tid = smc->ess.alloc_trans_id = smt_get_tid(smc) ;
+	req->smt.smt_dest = smt_sba_da ;
+
+	/* set P15 */
+	req->s_type.para.p_type = SMT_P0015 ;
+	req->s_type.para.p_len = sizeof(struct smt_p_0015) - PARA_LEN ;
+	req->s_type.res_type = SYNC_BW ;
+
+	/* set P16 */
+	req->cmd.para.p_type = SMT_P0016 ;
+	req->cmd.para.p_len = sizeof(struct smt_p_0016) - PARA_LEN ;
+	req->cmd.sba_cmd = REQUEST_ALLOCATION ;
+
+	/*
+	 * set the parameter type and parameter lenght of all used
+	 * parameters
+	 */
+
+	/* set P320B */
+	req->path.para.p_type = SMT_P320B ;
+	req->path.para.p_len = sizeof(struct smt_p_320b) - PARA_LEN ;
+	req->path.mib_index = SBAPATHINDEX ;
+	req->path.path_pad = (u_short)NULL ;
+	req->path.path_index = PRIMARY_RING ;
+
+	/* set P0017 */
+	req->pl_req.para.p_type = SMT_P0017 ;
+	req->pl_req.para.p_len = sizeof(struct smt_p_0017) - PARA_LEN ;
+	req->pl_req.sba_pl_req = smc->mib.fddiESSPayload -
+		smc->mib.a[PATH0].fddiPATHSbaPayload ;
+
+	/* set P0018 */
+	req->ov_req.para.p_type = SMT_P0018 ;
+	req->ov_req.para.p_len = sizeof(struct smt_p_0018) - PARA_LEN ;
+	req->ov_req.sba_ov_req = smc->mib.fddiESSOverhead -
+		smc->mib.a[PATH0].fddiPATHSbaOverhead ;
+
+	/* set P320F */
+	req->payload.para.p_type = SMT_P320F ;
+	req->payload.para.p_len = sizeof(struct smt_p_320f) - PARA_LEN ;
+	req->payload.mib_index = SBAPATHINDEX ;
+	req->payload.mib_payload = smc->mib.a[PATH0].fddiPATHSbaPayload ;
+
+	/* set P3210 */
+	req->overhead.para.p_type = SMT_P3210 ;
+	req->overhead.para.p_len = sizeof(struct smt_p_3210) - PARA_LEN ;
+	req->overhead.mib_index = SBAPATHINDEX ;
+	req->overhead.mib_overhead = smc->mib.a[PATH0].fddiPATHSbaOverhead ;
+
+	/* set P19 */
+	req->a_addr.para.p_type = SMT_P0019 ;
+	req->a_addr.para.p_len = sizeof(struct smt_p_0019) - PARA_LEN ;
+	req->a_addr.sba_pad = (u_short)NULL ;
+	req->a_addr.alloc_addr = null_addr ;
+
+	/* set P1A */
+	req->cat.para.p_type = SMT_P001A ;
+	req->cat.para.p_len = sizeof(struct smt_p_001a) - PARA_LEN ;
+	req->cat.category = smc->mib.fddiESSCategory ;
+
+	/* set P1B */
+	req->tneg.para.p_type = SMT_P001B ;
+	req->tneg.para.p_len = sizeof(struct smt_p_001b) - PARA_LEN ;
+	req->tneg.max_t_neg = smc->mib.fddiESSMaxTNeg ;
+
+	/* set P1C */
+	req->segm.para.p_type = SMT_P001C ;
+	req->segm.para.p_len = sizeof(struct smt_p_001c) - PARA_LEN ;
+	req->segm.min_seg_siz = smc->mib.fddiESSMinSegmentSize ;
+
+	dump_smt(smc,(struct smt_header *)req,"RAF") ;
+	ess_send_frame(smc,mb) ;
+}
+
+static void ess_send_frame(smc,mb)
+struct s_smc *smc ;
+SMbuf *mb ;
+{
+	/*
+	 * check if the frame must be send to the own ESS
+	 */
+	if (smc->ess.local_sba_active) {
+		/*
+		 * Send the Change Reply to the local SBA
+		 */
+		DB_ESS("ESS:Send to the local SBA\n",0,0) ;
+		if (!smc->ess.sba_reply_pend)
+			smc->ess.sba_reply_pend = mb ;
+		else {
+			DB_ESS("Frame is lost - another frame was pending\n",0,0);
+			smt_free_mbuf(smc,mb) ;
+		}
+	}
+	else {
+		/*
+		 * Send the SBA RAF Change Reply to the network
+		 */
+		DB_ESS("ESS:Send to the network\n",0,0) ;
+		smt_send_frame(smc,mb,FC_SMT_INFO,0) ;
+	}
+}
+
+void ess_para_change(smc)
+struct s_smc *smc ;
+{
+	(void)process_bw_alloc(smc,(long)smc->mib.a[PATH0].fddiPATHSbaPayload,
+		(long)smc->mib.a[PATH0].fddiPATHSbaOverhead) ;
+}
+
+static void ess_config_fifo(smc)
+struct s_smc *smc ;
+{
+	/*
+	 * if nothing to do exit 
+	 */
+	if (smc->mib.a[PATH0].fddiPATHSbaPayload) {
+		if (smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON &&
+			(smc->hw.fp.fifo.fifo_config_mode&SEND_ASYNC_AS_SYNC) ==
+			smc->mib.fddiESSSynchTxMode) {
+			return ;
+		}
+	}
+	else {
+		if (!(smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON)) {
+			return ;
+		}
+	}
+
+	/*
+	 * split up the FIFO and reinitialize the queues
+	 */
+	formac_reinit_tx(smc) ;
+}
+
+#endif /* ESS */
+
+#endif	/* no SLIM_SMT */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/fplustm.c linux.18p17/drivers/net/skfp/fplustm.c
--- linux.vanilla/drivers/net/skfp/fplustm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/fplustm.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,1645 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * FORMAC+ Driver for tag mode
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/supern_2.h"
+#include "can.c"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)fplustm.c	1.32 99/02/23 (C) SK " ;
+#endif
+
+#ifndef UNUSED
+#ifdef  lint
+#define UNUSED(x)	(x) = (x)
+#else
+#define UNUSED(x)
+#endif
+#endif
+
+#define FM_ADDRX	 (FM_ADDET|FM_EXGPA0|FM_EXGPA1)
+#define MS2BCLK(x)	((x)*12500L)
+#define US2BCLK(x)	((x)*1250L)
+
+/*
+ * prototypes for static function
+ */
+static void build_claim_beacon() ;
+static int init_mac() ;
+static void rtm_init() ;
+static void smt_split_up_fifo() ;
+
+#if (!defined(NO_SMT_PANIC) || defined(DEBUG))
+static	char write_mdr_warning [] = "E350 write_mdr() FM_SNPPND is set\n";
+static	char cam_warning [] = "E_SMT_004: CAM still busy\n";
+#endif
+
+#define	DUMMY_READ()	smc->hw.mc_dummy = (u_short) inp(ADDR(B0_RAP))
+
+#define	CHECK_NPP() {	unsigned k = 10000 ;\
+			while ((inpw(FM_A(FM_STMCHN)) & FM_SNPPND) && k) k--;\
+			if (!k) { \
+				SMT_PANIC(smc,SMT_E0130, SMT_E0130_MSG) ; \
+			}	\
+		}
+
+#define	CHECK_CAM() {	unsigned k = 10 ;\
+			while (!(inpw(FM_A(FM_AFSTAT)) & FM_DONE) && k) k--;\
+			if (!k) { \
+				SMT_PANIC(smc,SMT_E0131, SMT_E0131_MSG) ; \
+			}	\
+		}
+
+const struct fddi_addr fddi_broadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+static const struct fddi_addr null_addr = {{0,0,0,0,0,0}};
+static const struct fddi_addr dbeacon_multi = {{0x01,0x80,0xc2,0x00,0x01,0x00}};
+
+static const u_short my_said = 0xffff ;	/* short address (n.u.) */
+static const u_short my_sagp = 0xffff ;	/* short group address (n.u.) */
+
+/*
+ * define my address
+ */
+#ifdef	USE_CAN_ADDR
+#define MA	smc->hw.fddi_canon_addr
+#else
+#define MA	smc->hw.fddi_home_addr
+#endif
+
+
+/*
+ * usefull interrupt bits
+ */
+static int mac_imsk1u = FM_STXABRS | FM_STXABRA0 | FM_SXMTABT ;
+static int mac_imsk1l = FM_SQLCKS | FM_SQLCKA0 | FM_SPCEPDS | FM_SPCEPDA0|
+			FM_STBURS | FM_STBURA0 ;
+
+	/* delete FM_SRBFL after tests */
+static int mac_imsk2u = FM_SERRSF | FM_SNFSLD | FM_SRCVOVR | FM_SRBFL |
+			FM_SMYCLM ;
+static int mac_imsk2l = FM_STRTEXR | FM_SDUPCLM | FM_SFRMCTR |
+			FM_SERRCTR | FM_SLSTCTR |
+			FM_STRTEXP | FM_SMULTDA | FM_SRNGOP ;
+
+static int mac_imsk3u = FM_SRCVOVR2 | FM_SRBFL2 ;
+static int mac_imsk3l = FM_SRPERRQ2 | FM_SRPERRQ1 ;
+
+static int mac_beacon_imsk2u = FM_SOTRBEC | FM_SMYBEC | FM_SBEC |
+			FM_SLOCLM | FM_SHICLM | FM_SMYCLM | FM_SCLM ;
+
+
+static u_long mac_get_tneg(smc)
+struct s_smc *smc ;
+{
+	u_long	tneg ;
+
+	tneg = (u_long)((long)inpw(FM_A(FM_TNEG))<<5) ;
+	return((u_long)((tneg + ((inpw(FM_A(FM_TMRS))>>10)&0x1f)) |
+		0xffe00000L)) ;
+}
+
+void mac_update_counter(smc)
+struct s_smc *smc ;
+{
+	smc->mib.m[MAC0].fddiMACFrame_Ct =
+		(smc->mib.m[MAC0].fddiMACFrame_Ct & 0xffff0000L)
+		+ (u_short) inpw(FM_A(FM_FCNTR)) ;
+	smc->mib.m[MAC0].fddiMACLost_Ct =
+		(smc->mib.m[MAC0].fddiMACLost_Ct & 0xffff0000L)
+		+ (u_short) inpw(FM_A(FM_LCNTR)) ;
+	smc->mib.m[MAC0].fddiMACError_Ct =
+		(smc->mib.m[MAC0].fddiMACError_Ct & 0xffff0000L)
+		+ (u_short) inpw(FM_A(FM_ECNTR)) ;
+	smc->mib.m[MAC0].fddiMACT_Neg = mac_get_tneg(smc) ;
+#ifdef SMT_REAL_TOKEN_CT
+	/*
+	 * If the token counter is emulated it is updated in smt_event.
+	 */
+	TBD
+#else
+	smt_emulate_token_ct( smc, MAC0 );
+#endif
+}
+
+/*
+ * write long value into buffer memory over memory data register (MDR),
+ */
+void	write_mdr(smc,val)
+struct s_smc *smc ;
+u_long val;
+{
+	CHECK_NPP() ;
+	MDRW(val) ;
+}
+
+/*
+ * read long value from buffer memory over memory data register (MDR),
+ */
+u_long read_mdr(smc,addr)
+struct s_smc *smc ;
+unsigned int addr;
+{
+	long p ;
+	CHECK_NPP() ;
+	MARR(addr) ;
+	outpw(FM_A(FM_CMDREG1),FM_IRMEMWO) ;
+	CHECK_NPP() ;	/* needed for PCI to prevent from timeing violations */
+/*	p = MDRR() ; */	/* bad read values if the workaround */
+			/* smc->hw.mc_dummy = *((short volatile far *)(addr)))*/
+			/* is used */
+	p = (u_long)inpw(FM_A(FM_MDRU))<<16 ;
+	p += (u_long)inpw(FM_A(FM_MDRL)) ;
+	return(p) ;
+}
+/*
+ * clear buffer memory
+ */
+static void init_ram(smc)
+struct s_smc *smc ;
+{
+	u_short i ;
+
+	smc->hw.fp.fifo.rbc_ram_start = 0 ;
+	smc->hw.fp.fifo.rbc_ram_end =
+		smc->hw.fp.fifo.rbc_ram_start + RBC_MEM_SIZE ;
+	CHECK_NPP() ;
+	MARW(smc->hw.fp.fifo.rbc_ram_start) ;
+	for (i = smc->hw.fp.fifo.rbc_ram_start;
+		i < (u_short) (smc->hw.fp.fifo.rbc_ram_end-1); i++)
+		write_mdr(smc,0L) ;
+	/* Erase the last byte too */
+	write_mdr(smc,0L) ;
+}
+
+/*
+ * set receive FIFO pointer
+ */
+static void set_recvptr(smc)
+struct s_smc *smc ;
+{
+	/*
+	 * initialize the pointer for receive queue 1
+	 */
+	outpw(FM_A(FM_RPR1),smc->hw.fp.fifo.rx1_fifo_start) ;	/* RPR1 */
+	outpw(FM_A(FM_SWPR1),smc->hw.fp.fifo.rx1_fifo_start) ;	/* SWPR1 */
+	outpw(FM_A(FM_WPR1),smc->hw.fp.fifo.rx1_fifo_start) ;	/* WPR1 */
+	outpw(FM_A(FM_EARV1),smc->hw.fp.fifo.tx_s_start-1) ;	/* EARV1 */
+
+	/*
+	 * initialize the pointer for receive queue 2
+	 */
+	if (smc->hw.fp.fifo.rx2_fifo_size) {
+		outpw(FM_A(FM_RPR2),smc->hw.fp.fifo.rx2_fifo_start) ;
+		outpw(FM_A(FM_SWPR2),smc->hw.fp.fifo.rx2_fifo_start) ;
+		outpw(FM_A(FM_WPR2),smc->hw.fp.fifo.rx2_fifo_start) ;
+		outpw(FM_A(FM_EARV2),smc->hw.fp.fifo.rbc_ram_end-1) ;
+	}
+	else {
+		outpw(FM_A(FM_RPR2),smc->hw.fp.fifo.rbc_ram_end-1) ;
+		outpw(FM_A(FM_SWPR2),smc->hw.fp.fifo.rbc_ram_end-1) ;
+		outpw(FM_A(FM_WPR2),smc->hw.fp.fifo.rbc_ram_end-1) ;
+		outpw(FM_A(FM_EARV2),smc->hw.fp.fifo.rbc_ram_end-1) ;
+	}
+}
+
+/*
+ * set transmit FIFO pointer
+ */
+static void set_txptr(smc)
+struct s_smc *smc ;
+{
+	outpw(FM_A(FM_CMDREG2),FM_IRSTQ) ;	/* reset transmit queues */
+
+	/*
+	 * initialize the pointer for asynchronous transmit queue
+	 */
+	outpw(FM_A(FM_RPXA0),smc->hw.fp.fifo.tx_a0_start) ;	/* RPXA0 */
+	outpw(FM_A(FM_SWPXA0),smc->hw.fp.fifo.tx_a0_start) ;	/* SWPXA0 */
+	outpw(FM_A(FM_WPXA0),smc->hw.fp.fifo.tx_a0_start) ;	/* WPXA0 */
+	outpw(FM_A(FM_EAA0),smc->hw.fp.fifo.rx2_fifo_start-1) ;	/* EAA0 */
+
+	/*
+	 * initialize the pointer for synchronous transmit queue
+	 */
+	if (smc->hw.fp.fifo.tx_s_size) {
+		outpw(FM_A(FM_RPXS),smc->hw.fp.fifo.tx_s_start) ;
+		outpw(FM_A(FM_SWPXS),smc->hw.fp.fifo.tx_s_start) ;
+		outpw(FM_A(FM_WPXS),smc->hw.fp.fifo.tx_s_start) ;
+		outpw(FM_A(FM_EAS),smc->hw.fp.fifo.tx_a0_start-1) ;
+	}
+	else {
+		outpw(FM_A(FM_RPXS),smc->hw.fp.fifo.tx_a0_start-1) ;
+		outpw(FM_A(FM_SWPXS),smc->hw.fp.fifo.tx_a0_start-1) ;
+		outpw(FM_A(FM_WPXS),smc->hw.fp.fifo.tx_a0_start-1) ;
+		outpw(FM_A(FM_EAS),smc->hw.fp.fifo.tx_a0_start-1) ;
+	}
+}
+
+/*
+ * init memory buffer management registers
+ */
+static void init_rbc(smc)
+struct s_smc *smc ;
+{
+	u_short	rbc_ram_addr ;
+
+	/*
+	 * set unused pointers or permanent pointers
+	 */
+	rbc_ram_addr = smc->hw.fp.fifo.rx2_fifo_start - 1 ;
+
+	outpw(FM_A(FM_RPXA1),rbc_ram_addr) ;	/* a1-send pointer */
+	outpw(FM_A(FM_WPXA1),rbc_ram_addr) ;
+	outpw(FM_A(FM_SWPXA1),rbc_ram_addr) ;
+	outpw(FM_A(FM_EAA1),rbc_ram_addr) ;
+
+	set_recvptr(smc) ;
+	set_txptr(smc) ;
+}
+
+/*
+ * init rx pointer
+ */
+static void init_rx(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_rx_queue	*queue ;
+
+	/*
+	 * init all tx data structures for receive queue 1
+	 */
+	smc->hw.fp.rx[QUEUE_R1] = queue = &smc->hw.fp.rx_q[QUEUE_R1] ;
+	queue->rx_bmu_ctl = (HW_PTR) ADDR(B0_R1_CSR) ;
+	queue->rx_bmu_dsc = (HW_PTR) ADDR(B4_R1_DA) ;
+
+	/*
+	 * init all tx data structures for receive queue 2
+	 */
+	smc->hw.fp.rx[QUEUE_R2] = queue = &smc->hw.fp.rx_q[QUEUE_R2] ;
+	queue->rx_bmu_ctl = (HW_PTR) ADDR(B0_R2_CSR) ;
+	queue->rx_bmu_dsc = (HW_PTR) ADDR(B4_R2_DA) ;
+}
+
+/*
+ * set the TSYNC register of the FORMAC to regulate synchronous transmission
+ */
+void set_formac_tsync(smc,sync_bw)
+struct s_smc *smc ;
+long sync_bw ;
+{
+	outpw(FM_A(FM_TSYNC),(unsigned int) (((-sync_bw) >> 5) & 0xffff) ) ;
+}
+
+/*
+ * init all tx data structures
+ */
+static void init_tx(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_tx_queue	*queue ;
+
+	/*
+	 * init all tx data structures for the synchronous queue
+	 */
+	smc->hw.fp.tx[QUEUE_S] = queue = &smc->hw.fp.tx_q[QUEUE_S] ;
+	queue->tx_bmu_ctl = (HW_PTR) ADDR(B0_XS_CSR) ;
+	queue->tx_bmu_dsc = (HW_PTR) ADDR(B5_XS_DA) ;
+
+#ifdef ESS
+	set_formac_tsync(smc,smc->ess.sync_bw) ;
+#endif
+
+	/*
+	 * init all tx data structures for the asynchronous queue 0
+	 */
+	smc->hw.fp.tx[QUEUE_A0] = queue = &smc->hw.fp.tx_q[QUEUE_A0] ;
+	queue->tx_bmu_ctl = (HW_PTR) ADDR(B0_XA_CSR) ;
+	queue->tx_bmu_dsc = (HW_PTR) ADDR(B5_XA_DA) ;
+
+
+	llc_recover_tx(smc) ;
+}
+
+static void mac_counter_init(smc)
+struct s_smc *smc ;
+{
+	int i ;
+	u_long *ec ;
+
+	/*
+	 * clear FORMAC+ frame-, lost- and error counter
+	 */
+	outpw(FM_A(FM_FCNTR),0) ;
+	outpw(FM_A(FM_LCNTR),0) ;
+	outpw(FM_A(FM_ECNTR),0) ;
+	/*
+	 * clear internal error counter stucture
+	 */
+	ec = (u_long *)&smc->hw.fp.err_stats ;
+	for (i = (sizeof(struct err_st)/sizeof(long)) ; i ; i--)
+		*ec++ = 0L ;
+	smc->mib.m[MAC0].fddiMACRingOp_Ct = 0 ;
+}
+
+/*
+ * set FORMAC address, and t_request
+ */
+static	void set_formac_addr(smc)
+struct s_smc *smc ;
+{
+	long	t_requ = smc->mib.m[MAC0].fddiMACT_Req ;
+
+	outpw(FM_A(FM_SAID),my_said) ;	/* set short address */
+	outpw(FM_A(FM_LAIL),(unsigned)((smc->hw.fddi_home_addr.a[4]<<8) +
+					smc->hw.fddi_home_addr.a[5])) ;
+	outpw(FM_A(FM_LAIC),(unsigned)((smc->hw.fddi_home_addr.a[2]<<8) +
+					smc->hw.fddi_home_addr.a[3])) ;
+	outpw(FM_A(FM_LAIM),(unsigned)((smc->hw.fddi_home_addr.a[0]<<8) +
+					smc->hw.fddi_home_addr.a[1])) ;
+
+	outpw(FM_A(FM_SAGP),my_sagp) ;	/* set short group address */
+
+	outpw(FM_A(FM_LAGL),(unsigned)((smc->hw.fp.group_addr.a[4]<<8) +
+					smc->hw.fp.group_addr.a[5])) ;
+	outpw(FM_A(FM_LAGC),(unsigned)((smc->hw.fp.group_addr.a[2]<<8) +
+					smc->hw.fp.group_addr.a[3])) ;
+	outpw(FM_A(FM_LAGM),(unsigned)((smc->hw.fp.group_addr.a[0]<<8) +
+					smc->hw.fp.group_addr.a[1])) ;
+
+	/* set r_request regs. (MSW & LSW of TRT ) */
+	outpw(FM_A(FM_TREQ1),(unsigned)(t_requ>>16)) ;
+	outpw(FM_A(FM_TREQ0),(unsigned)t_requ) ;
+}
+
+void set_int(p,l)
+char *p;
+int l;
+{
+	p[0] = (char)(l >> 24) ;
+	p[1] = (char)(l >> 16) ;
+	p[2] = (char)(l >> 8) ;
+	p[3] = (char)(l >> 0) ;
+}
+
+/*
+ * copy TX descriptor to buffer mem
+ * append FC field and MAC frame
+ * if more bit is set in descr
+ *	append pointer to descriptor (endless loop)
+ * else
+ *	append 'end of chain' pointer
+ */
+static void copy_tx_mac(smc,td,mac,off,len)
+struct s_smc *smc ;
+u_long td;		/* transmit descriptor */
+struct fddi_mac *mac;	/* mac frame pointer */
+unsigned off;		/* start address within buffer memory */
+int len ;		/* lenght of the frame including the FC */
+{
+	int	i ;
+	u_int	*p ;
+
+	CHECK_NPP() ;
+	MARW(off) ;		/* set memory address reg for writes */
+
+	p = (u_int *) mac ;
+	for (i = (len + 3)/4 ; i ; i--) {
+		if (i == 1) {
+			/* last word, set the tag bit */
+			outpw(FM_A(FM_CMDREG2),FM_ISTTB) ;
+		}
+		write_mdr(smc,MDR_REVERSE(*p)) ;
+		p++ ;
+	}
+
+	outpw(FM_A(FM_CMDREG2),FM_ISTTB) ;	/* set the tag bit */
+	write_mdr(smc,td) ;	/* write over memory data reg to buffer */
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(module;tests;3)
+	How to test directed beacon frames
+	----------------------------------------------------------------
+
+	o Insert a break point in the function build_claim_beacon()
+	  before calling copy_tx_mac() for building the claim frame.
+	o Modify the RM3_DETECT case so that the RM6_DETECT state
+	  will always entered from the RM3_DETECT state (function rmt_fsm(),
+	  rmt.c)
+	o Compile the driver.
+	o Set the parameter TREQ in the protocol.ini or net.cfg to a
+	  small value to make sure your station will win the claim
+	  process.
+	o Start the driver.
+	o When you reach the break point, modify the SA and DA address
+	  of the claim frame (e.g. SA = DA = 10005affffff).
+	o When you see RM3_DETECT and RM6_DETECT, observe the direct
+	  beacon frames on the UPPSLANA.
+
+	END_MANUAL_ENTRY
+ */
+static void directed_beacon(smc)
+struct s_smc *smc ;
+{
+	SK_LOC_DECL(u_int,a[2]) ;
+
+	/*
+	 * set UNA in frame
+	 * enable FORMAC to send endless queue of directed beacon
+	 * important: the UNA starts at byte 1 (not at byte 0)
+	 */
+	* (char *) a = (char) ((long)DBEACON_INFO<<24L) ;
+	a[1] = 0 ;
+	memcpy((char *)a+1,(char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr,6) ;
+
+	CHECK_NPP() ;
+	 /* set memory address reg for writes */
+	MARW(smc->hw.fp.fifo.rbc_ram_start+DBEACON_FRAME_OFF+4) ;
+	write_mdr(smc,MDR_REVERSE(a[0])) ;
+	outpw(FM_A(FM_CMDREG2),FM_ISTTB) ;	/* set the tag bit */
+	write_mdr(smc,MDR_REVERSE(a[1])) ;
+
+	outpw(FM_A(FM_SABC),smc->hw.fp.fifo.rbc_ram_start + DBEACON_FRAME_OFF) ;
+}
+
+/*
+	setup claim & beacon pointer
+	NOTE :
+		special frame packets end with a pointer to their own
+		descriptor, and the MORE bit is set in the descriptor
+*/
+static void build_claim_beacon(smc,t_request)
+struct s_smc *smc ;
+u_long t_request;
+{
+	u_int	td ;
+	int	len ;
+	struct fddi_mac_sf *mac ;
+
+	/*
+	 * build claim packet
+	 */
+	len = 17 ;
+	td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ;
+	mac = &smc->hw.fp.mac_sfb ;
+	mac->mac_fc = FC_CLAIM ;
+	/* DA == SA in claim frame */
+	mac->mac_source = mac->mac_dest = MA ;
+	/* 2's complement */
+	set_int((char *)mac->mac_info,(int)t_request) ;
+
+	copy_tx_mac(smc,td,(struct fddi_mac *)mac,
+		smc->hw.fp.fifo.rbc_ram_start + CLAIM_FRAME_OFF,len) ;
+	/* set CLAIM start pointer */
+	outpw(FM_A(FM_SACL),smc->hw.fp.fifo.rbc_ram_start + CLAIM_FRAME_OFF) ;
+
+	/*
+	 * build beacon packet
+	 */
+	len = 17 ;
+	td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ;
+	mac->mac_fc = FC_BEACON ;
+	mac->mac_source = MA ;
+	mac->mac_dest = null_addr ;		/* DA == 0 in beacon frame */
+	set_int((char *) mac->mac_info,((int)BEACON_INFO<<24) + 0 ) ;
+
+	copy_tx_mac(smc,td,(struct fddi_mac *)mac,
+		smc->hw.fp.fifo.rbc_ram_start + BEACON_FRAME_OFF,len) ;
+	/* set beacon start pointer */
+	outpw(FM_A(FM_SABC),smc->hw.fp.fifo.rbc_ram_start + BEACON_FRAME_OFF) ;
+
+	/*
+	 * build directed beacon packet
+	 * contains optional UNA
+	 */
+	len = 23 ;
+	td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ;
+	mac->mac_fc = FC_BEACON ;
+	mac->mac_source = MA ;
+	mac->mac_dest = dbeacon_multi ;		/* multicast */
+	set_int((char *) mac->mac_info,((int)DBEACON_INFO<<24) + 0 ) ;
+	set_int((char *) mac->mac_info+4,0) ;
+	set_int((char *) mac->mac_info+8,0) ;
+
+	copy_tx_mac(smc,td,(struct fddi_mac *)mac,
+		smc->hw.fp.fifo.rbc_ram_start + DBEACON_FRAME_OFF,len) ;
+
+	/* end of claim/beacon queue */
+	outpw(FM_A(FM_EACB),smc->hw.fp.fifo.rx1_fifo_start-1) ;
+
+	outpw(FM_A(FM_WPXSF),0) ;
+	outpw(FM_A(FM_RPXSF),0) ;
+}
+
+void formac_rcv_restart(smc)
+struct s_smc *smc ;
+{
+	/* enable receive function */
+	SETMASK(FM_A(FM_MDREG1),smc->hw.fp.rx_mode,FM_ADDRX) ;
+
+	outpw(FM_A(FM_CMDREG1),FM_ICLLR) ;	/* clear receive lock */
+}
+
+void formac_tx_restart(smc)
+struct s_smc *smc ;
+{
+	outpw(FM_A(FM_CMDREG1),FM_ICLLS) ;	/* clear s-frame lock */
+	outpw(FM_A(FM_CMDREG1),FM_ICLLA0) ;	/* clear a-frame lock */
+}
+
+static void enable_formac(smc)
+struct s_smc *smc ;
+{
+	/* set formac IMSK : 0 enables irq */
+	outpw(FM_A(FM_IMSK1U),~mac_imsk1u) ;
+	outpw(FM_A(FM_IMSK1L),~mac_imsk1l) ;
+	outpw(FM_A(FM_IMSK2U),~mac_imsk2u) ;
+	outpw(FM_A(FM_IMSK2L),~mac_imsk2l) ;
+	outpw(FM_A(FM_IMSK3U),~mac_imsk3u) ;
+	outpw(FM_A(FM_IMSK3L),~mac_imsk3l) ;
+}
+
+#if 0	/* Removed because the driver should use the ASICs TX complete IRQ. */
+	/* The FORMACs tx complete IRQ should be used any longer */
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;4)
+
+	void enable_tx_irq(smc, queue)
+	struct s_smc *smc ;
+	u_short	queue ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		enable_tx_irq() enables the FORMACs transmit complete
+		interrupt of the queue.
+
+Para	queue	= QUEUE_S:	synchronous queue
+		= QUEUE_A0:	asynchronous queue
+
+Note	After any ring operational change the transmit complete
+	interrupts are disabled.
+	The operating system dependent module must enable
+	the transmit complete interrupt of a queue,
+		- when it queues the first frame,
+		  because of no transmit resources are beeing
+		  available and
+		- when it escapes from the function llc_restart_tx
+		  while some frames are still queued.
+
+	END_MANUAL_ENTRY
+ */
+void enable_tx_irq(smc, queue)
+struct s_smc *smc ;
+u_short	queue ;		/* 0 = synchronous queue, 1 = asynchronous queue 0 */
+{
+	u_short	imask ;
+
+	imask = ~(inpw(FM_A(FM_IMSK1U))) ;
+
+	if (queue == 0) {
+		outpw(FM_A(FM_IMSK1U),~(imask|FM_STEFRMS)) ;
+	}
+	if (queue == 1) {
+		outpw(FM_A(FM_IMSK1U),~(imask|FM_STEFRMA0)) ;
+	}
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;4)
+
+	void disable_tx_irq(smc, queue)
+	struct s_smc *smc ;
+	u_short	queue ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		disable_tx_irq disables the FORMACs transmit complete
+		interrupt of the queue
+
+Para	queue	= QUEUE_S:	synchronous queue
+		= QUEUE_A0:	asynchronous queue
+
+Note	The operating system dependent module should disable
+	the transmit complete interrupts if it escapes from the
+	function llc_restart_tx and no frames are queued.
+
+	END_MANUAL_ENTRY
+ */
+void disable_tx_irq(smc, queue)
+struct s_smc *smc ;
+u_short	queue ;		/* 0 = synchronous queue, 1 = asynchronous queue 0 */
+{
+	u_short	imask ;
+
+	imask = ~(inpw(FM_A(FM_IMSK1U))) ;
+
+	if (queue == 0) {
+		outpw(FM_A(FM_IMSK1U),~(imask&~FM_STEFRMS)) ;
+	}
+	if (queue == 1) {
+		outpw(FM_A(FM_IMSK1U),~(imask&~FM_STEFRMA0)) ;
+	}
+}
+#endif
+
+static void disable_formac(smc)
+struct s_smc *smc ;
+{
+	/* clear formac IMSK : 1 disables irq */
+	outpw(FM_A(FM_IMSK1U),MW) ;
+	outpw(FM_A(FM_IMSK1L),MW) ;
+	outpw(FM_A(FM_IMSK2U),MW) ;
+	outpw(FM_A(FM_IMSK2L),MW) ;
+	outpw(FM_A(FM_IMSK3U),MW) ;
+	outpw(FM_A(FM_IMSK3L),MW) ;
+}
+
+
+static void mac_ring_up(smc,up)
+struct s_smc *smc ;
+int up;
+{
+	if (up) {
+		formac_rcv_restart(smc) ;	/* enable receive function */
+		smc->hw.mac_ring_is_up = TRUE ;
+		llc_restart_tx(smc) ;		/* TX queue */
+	}
+	else {
+		/* disable receive function */
+		SETMASK(FM_A(FM_MDREG1),FM_MDISRCV,FM_ADDET) ;
+
+		/* abort current transmit activity */
+		outpw(FM_A(FM_CMDREG2),FM_IACTR) ;
+
+		smc->hw.mac_ring_is_up = FALSE ;
+	}
+}
+
+/*--------------------------- ISR handling ----------------------------------*/
+/*
+ * mac1_irq is in drvfbi.c
+ */
+
+/*
+ * mac2_irq:	status bits for the receive queue 1, and ring status
+ * 		ring status indication bits
+ */
+void mac2_irq(smc,code_s2u,code_s2l)
+struct s_smc *smc ;
+u_short code_s2u ;
+u_short code_s2l ;
+{
+	u_short	change_s2l ;
+	u_short	change_s2u ;
+
+	/* (jd) 22-Feb-1999
+	 * Restart 2_DMax Timer after end of claiming or beaconing
+	 */
+	if (code_s2u & (FM_SCLM|FM_SHICLM|FM_SBEC|FM_SOTRBEC)) {
+		queue_event(smc,EVENT_RMT,RM_TX_STATE_CHANGE) ;
+	}
+	else if (code_s2l & (FM_STKISS)) {
+		queue_event(smc,EVENT_RMT,RM_TX_STATE_CHANGE) ;
+	}
+
+	/*
+	 * XOR current st bits with the last to avoid useless RMT event queuing
+	 */
+	change_s2l = smc->hw.fp.s2l ^ code_s2l ;
+	change_s2u = smc->hw.fp.s2u ^ code_s2u ;
+
+	if ((change_s2l & FM_SRNGOP) ||
+		(!smc->hw.mac_ring_is_up && ((code_s2l & FM_SRNGOP)))) {
+		if (code_s2l & FM_SRNGOP) {
+			mac_ring_up(smc,1) ;
+			queue_event(smc,EVENT_RMT,RM_RING_OP) ;
+			smc->mib.m[MAC0].fddiMACRingOp_Ct++ ;
+		}
+		else {
+			mac_ring_up(smc,0) ;
+			queue_event(smc,EVENT_RMT,RM_RING_NON_OP) ;
+		}
+		goto mac2_end ;
+	}
+	if (code_s2l & FM_SMISFRM) {	/* missed frame */
+		smc->mib.m[MAC0].fddiMACNotCopied_Ct++ ;
+	}
+	if (code_s2u & (FM_SRCVOVR |	/* recv. FIFO overflow */
+			FM_SRBFL)) {	/* recv. buffer full */
+		smc->hw.mac_ct.mac_r_restart_counter++ ;
+/*		formac_rcv_restart(smc) ;	*/
+		smt_stat_counter(smc,1) ;
+/*		goto mac2_end ;			*/
+	}
+	if (code_s2u & FM_SOTRBEC)
+		queue_event(smc,EVENT_RMT,RM_OTHER_BEACON) ;
+	if (code_s2u & FM_SMYBEC)
+		queue_event(smc,EVENT_RMT,RM_MY_BEACON) ;
+	if (change_s2u & code_s2u & FM_SLOCLM) {
+		DB_RMTN(2,"RMT : lower claim received\n",0,0) ;
+	}
+	if ((code_s2u & FM_SMYCLM) && !(code_s2l & FM_SDUPCLM)) {
+		/*
+		 * This is my claim and that claim is not detected as a
+		 * duplicate one.
+		 */
+		queue_event(smc,EVENT_RMT,RM_MY_CLAIM) ;
+	}
+	if (code_s2l & FM_SDUPCLM) {
+		/*
+		 * If a duplicate claim frame (same SA but T_Bid != T_Req)
+		 * this flag will be set.
+		 * In the RMT state machine we need a RM_VALID_CLAIM event
+		 * to do the appropriate state change.
+		 * RM(34c)
+		 */
+		queue_event(smc,EVENT_RMT,RM_VALID_CLAIM) ;
+	}
+	if (change_s2u & code_s2u & FM_SHICLM) {
+		DB_RMTN(2,"RMT : higher claim received\n",0,0) ;
+	}
+	if ( (code_s2l & FM_STRTEXP) ||
+	     (code_s2l & FM_STRTEXR) )
+		queue_event(smc,EVENT_RMT,RM_TRT_EXP) ;
+	if (code_s2l & FM_SMULTDA) {
+		/*
+		 * The MAC has found a 2. MAC with the same address.
+		 * Signal dup_addr_test = failed to RMT state machine.
+		 * RM(25)
+		 */
+		smc->r.dup_addr_test = DA_FAILED ;
+		queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ;
+	}
+	if (code_s2u & FM_SBEC)
+		smc->hw.fp.err_stats.err_bec_stat++ ;
+	if (code_s2u & FM_SCLM)
+		smc->hw.fp.err_stats.err_clm_stat++ ;
+	if (code_s2l & FM_STVXEXP)
+		smc->mib.m[MAC0].fddiMACTvxExpired_Ct++ ;
+	if ((code_s2u & (FM_SBEC|FM_SCLM))) {
+		if (!(change_s2l & FM_SRNGOP) && (smc->hw.fp.s2l & FM_SRNGOP)) {
+			mac_ring_up(smc,0) ;
+			queue_event(smc,EVENT_RMT,RM_RING_NON_OP) ;
+
+			mac_ring_up(smc,1) ;
+			queue_event(smc,EVENT_RMT,RM_RING_OP) ;
+			smc->mib.m[MAC0].fddiMACRingOp_Ct++ ;
+		}
+	}
+	if (code_s2l & FM_SPHINV)
+		smc->hw.fp.err_stats.err_phinv++ ;
+	if (code_s2l & FM_SSIFG)
+		smc->hw.fp.err_stats.err_sifg_det++ ;
+	if (code_s2l & FM_STKISS)
+		smc->hw.fp.err_stats.err_tkiss++ ;
+	if (code_s2l & FM_STKERR)
+		smc->hw.fp.err_stats.err_tkerr++ ;
+	if (code_s2l & FM_SFRMCTR)
+		smc->mib.m[MAC0].fddiMACFrame_Ct += 0x10000L ;
+	if (code_s2l & FM_SERRCTR)
+		smc->mib.m[MAC0].fddiMACError_Ct += 0x10000L ;
+	if (code_s2l & FM_SLSTCTR)
+		smc->mib.m[MAC0].fddiMACLost_Ct  += 0x10000L ;
+	if (code_s2u & FM_SERRSF) {
+		SMT_PANIC(smc,SMT_E0114, SMT_E0114_MSG) ;
+	}
+mac2_end:
+	/* notice old status */
+	smc->hw.fp.s2l = code_s2l ;
+	smc->hw.fp.s2u = code_s2u ;
+	outpw(FM_A(FM_IMSK2U),~mac_imsk2u) ;
+}
+
+/*
+ * mac3_irq:	receive queue 2 bits and address detection bits
+ */
+void mac3_irq(smc,code_s3u,code_s3l)
+struct s_smc *smc ;
+u_short code_s3u ;
+u_short code_s3l ;
+{
+	UNUSED(code_s3l) ;
+
+	if (code_s3u & (FM_SRCVOVR2 |	/* recv. FIFO overflow */
+			FM_SRBFL2)) {	/* recv. buffer full */
+		smc->hw.mac_ct.mac_r_restart_counter++ ;
+		smt_stat_counter(smc,1);
+	}
+
+
+	if (code_s3u & FM_SRPERRQ2) {	/* parity error receive queue 2 */
+		SMT_PANIC(smc,SMT_E0115, SMT_E0115_MSG) ;
+	}
+	if (code_s3u & FM_SRPERRQ1) {	/* parity error receive queue 2 */
+		SMT_PANIC(smc,SMT_E0116, SMT_E0116_MSG) ;
+	}
+}
+
+
+/*
+ * take formac offline
+ */
+static void formac_offline(smc)
+struct s_smc *smc ;
+{
+	outpw(FM_A(FM_CMDREG2),FM_IACTR) ;/* abort current transmit activity */
+
+	/* disable receive function */
+	SETMASK(FM_A(FM_MDREG1),FM_MDISRCV,FM_ADDET) ;
+
+	/* FORMAC+ 'Initialize Mode' */
+	SETMASK(FM_A(FM_MDREG1),FM_MINIT,FM_MMODE) ;
+
+	disable_formac(smc) ;
+	smc->hw.mac_ring_is_up = FALSE ;
+	smc->hw.hw_state = STOPPED ;
+}
+
+/*
+ * bring formac online
+ */
+static void formac_online(smc)
+struct s_smc *smc ;
+{
+	enable_formac(smc) ;
+	SETMASK(FM_A(FM_MDREG1),FM_MONLINE | FM_SELRA | MDR1INIT |
+		smc->hw.fp.rx_mode, FM_MMODE | FM_SELRA | FM_ADDRX) ;
+}
+
+/*
+ * FORMAC+ full init. (tx, rx, timer, counter, claim & beacon)
+ */
+int init_fplus(smc)
+struct s_smc *smc ;
+{
+	smc->hw.fp.nsa_mode = FM_MRNNSAFNMA ;
+	smc->hw.fp.rx_mode = FM_MDAMA ;
+	smc->hw.fp.group_addr = fddi_broadcast ;
+	smc->hw.fp.func_addr = 0 ;
+	smc->hw.fp.frselreg_init = 0 ;
+
+	init_driver_fplus(smc) ;
+	if (smc->s.sas == SMT_DAS)
+		smc->hw.fp.mdr3init |= FM_MENDAS ;
+
+	smc->hw.mac_ct.mac_nobuf_counter = 0 ;
+	smc->hw.mac_ct.mac_r_restart_counter = 0 ;
+
+	smc->hw.fp.fm_st1u = (HW_PTR) ADDR(B0_ST1U) ;
+	smc->hw.fp.fm_st1l = (HW_PTR) ADDR(B0_ST1L) ;
+	smc->hw.fp.fm_st2u = (HW_PTR) ADDR(B0_ST2U) ;
+	smc->hw.fp.fm_st2l = (HW_PTR) ADDR(B0_ST2L) ;
+	smc->hw.fp.fm_st3u = (HW_PTR) ADDR(B0_ST3U) ;
+	smc->hw.fp.fm_st3l = (HW_PTR) ADDR(B0_ST3L) ;
+
+	smc->hw.fp.s2l = smc->hw.fp.s2u = 0 ;
+	smc->hw.mac_ring_is_up = 0 ;
+
+	mac_counter_init(smc) ;
+
+	/* convert BCKL units to symbol time */
+	smc->hw.mac_pa.t_neg = (u_long)0 ;
+	smc->hw.mac_pa.t_pri = (u_long)0 ;
+
+	/* make sure all PCI settings are correct */
+	mac_do_pci_fix(smc) ;
+
+	return(init_mac(smc,1)) ;
+	/* enable_formac(smc) ; */
+}
+
+static int init_mac(smc,all)
+struct s_smc *smc ;
+int all ;
+{
+	u_short	t_max,x ;
+	u_long	time=0 ;
+
+	/*
+	 * clear memory
+	 */
+	outpw(FM_A(FM_MDREG1),FM_MINIT) ;	/* FORMAC+ init mode */
+	set_formac_addr(smc) ;
+	outpw(FM_A(FM_MDREG1),FM_MMEMACT) ;	/* FORMAC+ memory activ mode */
+	/* Note: Mode register 2 is set here, incase parity is enabled. */
+	outpw(FM_A(FM_MDREG2),smc->hw.fp.mdr2init) ;
+
+	if (all) {
+		init_ram(smc) ;
+	}
+	else {
+		/*
+		 * reset the HPI, the Master and the BMUs
+		 */
+		outp(ADDR(B0_CTRL), CTRL_HPI_SET) ;
+		time = hwt_quick_read(smc) ;
+	}
+
+	/*
+	 * set all pointers, frames etc
+	 */
+	smt_split_up_fifo(smc) ;
+
+	init_tx(smc) ;
+	init_rx(smc) ;
+	init_rbc(smc) ;
+
+	build_claim_beacon(smc,smc->mib.m[MAC0].fddiMACT_Req) ;
+
+	/* set RX threshold */
+	/* see Errata #SN2 Phantom receive overflow */
+	outpw(FM_A(FM_FRMTHR),14<<12) ;		/* switch on */
+
+	/* set formac work mode */
+	outpw(FM_A(FM_MDREG1),MDR1INIT | FM_SELRA | smc->hw.fp.rx_mode) ;
+	outpw(FM_A(FM_MDREG2),smc->hw.fp.mdr2init) ;
+	outpw(FM_A(FM_MDREG3),smc->hw.fp.mdr3init) ;
+	outpw(FM_A(FM_FRSELREG),smc->hw.fp.frselreg_init) ;
+
+	/* set timer */
+	/*
+	 * errata #22 fplus:
+	 * T_MAX must not be FFFE
+	 * or one of FFDF, FFB8, FF91 (-0x27 etc..)
+	 */
+	t_max = (u_short)(smc->mib.m[MAC0].fddiMACT_Max/32) ;
+	x = t_max/0x27 ;
+	x *= 0x27 ;
+	if ((t_max == 0xfffe) || (t_max - x == 0x16))
+		t_max-- ;
+	outpw(FM_A(FM_TMAX),(u_short)t_max) ;
+
+	/* BugFix for report #10204 */
+	if (smc->mib.m[MAC0].fddiMACTvxValue < (u_long) (- US2BCLK(52))) {
+		outpw(FM_A(FM_TVX), (u_short) (- US2BCLK(52))/255 & MB) ;
+	} else {
+		outpw(FM_A(FM_TVX),
+			(u_short)((smc->mib.m[MAC0].fddiMACTvxValue/255) & MB)) ;
+	}
+
+	outpw(FM_A(FM_CMDREG1),FM_ICLLS) ;	/* clear s-frame lock */
+	outpw(FM_A(FM_CMDREG1),FM_ICLLA0) ;	/* clear a-frame lock */
+	outpw(FM_A(FM_CMDREG1),FM_ICLLR);	/* clear receive lock */
+
+	/* Auto unlock receice threshold for receive queue 1 and 2 */
+	outpw(FM_A(FM_UNLCKDLY),(0xff|(0xff<<8))) ;
+
+	rtm_init(smc) ;				/* RT-Monitor */
+
+	if (!all) {
+		/*
+		 * after 10ms, reset the BMUs and repair the rings
+		 */
+		hwt_wait_time(smc,time,MS2BCLK(10)) ;
+		outpd(ADDR(B0_R1_CSR),CSR_SET_RESET) ;
+		outpd(ADDR(B0_XA_CSR),CSR_SET_RESET) ;
+		outpd(ADDR(B0_XS_CSR),CSR_SET_RESET) ;
+		outp(ADDR(B0_CTRL), CTRL_HPI_CLR) ;
+		outpd(ADDR(B0_R1_CSR),CSR_CLR_RESET) ;
+		outpd(ADDR(B0_XA_CSR),CSR_CLR_RESET) ;
+		outpd(ADDR(B0_XS_CSR),CSR_CLR_RESET) ;
+		if (!smc->hw.hw_is_64bit) {
+			outpd(ADDR(B4_R1_F), RX_WATERMARK) ;
+			outpd(ADDR(B5_XA_F), TX_WATERMARK) ;
+			outpd(ADDR(B5_XS_F), TX_WATERMARK) ;
+		}
+		smc->hw.hw_state = STOPPED ;
+		mac_drv_repair_descr(smc) ;
+	}
+	smc->hw.hw_state = STARTED ;
+
+	return(0) ;
+}
+
+
+/*
+ * called by CFM
+ */
+void config_mux(smc,mux)
+struct s_smc *smc ;
+int mux;
+{
+	plc_config_mux(smc,mux) ;
+
+	SETMASK(FM_A(FM_MDREG1),FM_SELRA,FM_SELRA) ;
+}
+
+/*
+ * called by RMT
+ * enable CLAIM/BEACON interrupts
+ * (only called if these events are of interest, e.g. in DETECT state
+ * the interrupt must not be permanently enabled
+ * RMT calls this function periodically (timer driven polling)
+ */
+void sm_mac_check_beacon_claim(smc)
+struct s_smc *smc ;
+{
+	/* set formac IMSK : 0 enables irq */
+	outpw(FM_A(FM_IMSK2U),~(mac_imsk2u | mac_beacon_imsk2u)) ;
+	/* the driver must receive the directed beacons */
+	formac_rcv_restart(smc) ;
+	process_receive(smc) ;
+}
+
+/*-------------------------- interface functions ----------------------------*/
+/*
+ * control ODL output
+ */
+void sm_pm_control(smc,mode)
+struct s_smc *smc ;
+int mode;
+{
+	SK_UNUSED(smc) ;
+
+	/*
+	 * if PCM logic has set LS_REQUEST = Transmit QUIET Line State
+	 *	/FOTOFF signal turn activ -> ODL disable
+	 */
+	switch(mode) {
+	case PM_TRANSMIT_DISABLE :
+		break ;
+	case PM_TRANSMIT_ENABLE :
+		break ;
+	}
+}
+
+/*
+ * control MAC layer	(called by RMT)
+ */
+void sm_ma_control(smc,mode)
+struct s_smc *smc ;
+int mode;
+{
+	switch(mode) {
+	case MA_OFFLINE :
+		/* Add to make the MAC offline in RM0_ISOLATED state */
+		formac_offline(smc) ;
+		break ;
+	case MA_RESET :
+		(void)init_mac(smc,0) ;
+		break ;
+	case MA_BEACON :
+		formac_online(smc) ;
+		break ;
+	case MA_DIRECTED :
+		directed_beacon(smc) ;
+		break ;
+	case MA_TREQ :
+		/*
+		 * no actions necessary, TREQ is already set
+		 */
+		break ;
+	}
+}
+
+int sm_mac_get_tx_state(smc)
+struct s_smc *smc ;
+{
+	return((inpw(FM_A(FM_STMCHN))>>4)&7) ;
+}
+
+/*
+ * multicast functions
+ */
+
+static struct s_fpmc	*mac_get_mc_table(smc,user,own,del,can)
+struct s_smc *smc ;
+struct fddi_addr *user ;
+struct fddi_addr *own ;
+int del ;
+int can ;
+{
+	struct s_fpmc	*tb ;
+	struct s_fpmc	*slot ;
+	u_char	*p ;
+	int i ;
+
+	/*
+	 * set own = can(user)
+	 */
+	*own = *user ;
+	if (can) {
+		p = own->a ;
+		for (i = 0 ; i < 6 ; i++, p++)
+			*p = canonical[*p] ;
+	}
+	slot = 0 ;
+	for (i = 0, tb = smc->hw.fp.mc.table ; i < FPMAX_MULTICAST ; i++, tb++){
+		if (!tb->n) {		/* not used */
+			if (!del && !slot)	/* if !del save first free */
+				slot = tb ;
+			continue ;
+		}
+		if (memcmp((char *)&tb->a,(char *)own,6))
+			continue ;
+		return(tb) ;
+	}
+	return(slot) ;			/* return first free or NULL */
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;2)
+
+	void mac_clear_multicast(smc)
+	struct s_smc *smc ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		Clear all multicast entries
+
+	END_MANUAL_ENTRY()
+ */
+void mac_clear_multicast(smc)
+struct s_smc *smc ;
+{
+	struct s_fpmc	*tb ;
+	int i ;
+
+	smc->hw.fp.os_slots_used = 0 ;	/* note the SMT addresses */
+					/* will not be deleted */
+	for (i = 0, tb = smc->hw.fp.mc.table ; i < FPMAX_MULTICAST ; i++, tb++){
+		if (!tb->perm) {
+			tb->n = 0 ;
+		}
+	}
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;2)
+
+	int mac_set_func_addr(smc,f_addr)
+	struct s_smc *smc ;
+	u_long f_addr ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		Set a Token-Ring functional address, the address will
+		be activated after calling mac_update_multicast()
+
+Para	f_addr	functional bits in non-canonical format
+
+Returns	0: always success
+
+	END_MANUAL_ENTRY()
+ */
+int mac_set_func_addr(smc,f_addr)
+struct s_smc *smc ;
+u_long f_addr ;
+{
+	smc->hw.fp.func_addr = f_addr ;
+	return(0) ;
+}
+
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;2)
+
+	int mac_add_multicast(smc,addr,can)
+	struct s_smc *smc ;
+	struct fddi_addr *addr ;
+	int can ;
+
+Function	DOWNCALL	(SMC, fplustm.c)
+		Add an entry to the multicast table
+
+Para	addr	pointer to a multicast address
+	can	= 0:	the multicast address has the physical format
+		= 1:	the multicast address has the canonical format
+		| 0x80	permanent
+
+Returns	0: success
+	1: address table full
+
+Note	After a 'driver reset' or a 'station set address' all
+	entries of the multicast table are cleared.
+	In this case the driver has to fill the multicast table again.
+	After the operating system dependent module filled
+	the multicast table it must call mac_update_multicast
+	to activate the new multicast addresses!
+
+	END_MANUAL_ENTRY()
+ */
+int mac_add_multicast(smc,addr,can)
+struct s_smc *smc ;
+struct fddi_addr *addr ;
+int can ;
+{
+	SK_LOC_DECL(struct fddi_addr,own) ;
+	struct s_fpmc	*tb ;
+
+	/*
+	 * check if there are free table entries
+	 */
+	if (can & 0x80) {
+		if (smc->hw.fp.smt_slots_used >= SMT_MAX_MULTI) {
+			return(1) ;
+		}
+	}
+	else {
+		if (smc->hw.fp.os_slots_used >= FPMAX_MULTICAST-SMT_MAX_MULTI) {
+			return(1) ;
+		}
+	}
+
+	/*
+	 * find empty slot
+	 */
+	if (!(tb = mac_get_mc_table(smc,addr,&own,0,can & ~0x80)))
+		return(1) ;
+	tb->n++ ;
+	tb->a = own ;
+	tb->perm = (can & 0x80) ? 1 : 0 ;
+
+	if (can & 0x80)
+		smc->hw.fp.smt_slots_used++ ;
+	else
+		smc->hw.fp.os_slots_used++ ;
+
+	return(0) ;
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;2)
+
+	void mac_del_multicast(smc,addr,can)
+	struct s_smc *smc ;
+	struct fddi_addr *addr ;
+	int can ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		Delete an entry from the multicast table
+
+Para	addr	pointer to a multicast address
+	can	= 0:	the multicast address has the physical format
+		= 1:	the multicast address has the canonical format
+		| 0x80	permanent
+
+	END_MANUAL_ENTRY()
+ */
+void mac_del_multicast(smc,addr,can)
+struct s_smc *smc ;
+struct fddi_addr *addr ;
+int can ;
+{
+	SK_LOC_DECL(struct fddi_addr,own) ;
+	struct s_fpmc	*tb ;
+
+	if (!(tb = mac_get_mc_table(smc,addr,&own,1,can & ~0x80)))
+		return ;
+	/*
+	 * permanent addresses must be deleted with perm bit
+	 * and vice versa
+	 */
+	if (( tb->perm &&  (can & 0x80)) ||
+	    (!tb->perm && !(can & 0x80))) {
+		/*
+		 * delete it
+		 */
+		if (tb->n) {
+			tb->n-- ;
+			if (tb->perm) {
+				smc->hw.fp.smt_slots_used-- ;
+			}
+			else {
+				smc->hw.fp.os_slots_used-- ;
+			}
+		}
+	}
+}
+
+/*
+ * mode
+ */
+
+#define RX_MODE_PROM		0x1
+#define RX_MODE_ALL_MULTI	0x2
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;2)
+
+	void mac_update_multicast(smc)
+	struct s_smc *smc ;
+
+Function	DOWNCALL	(SMT, fplustm.c)
+		Update FORMAC multicast registers
+
+	END_MANUAL_ENTRY()
+ */
+void mac_update_multicast(smc)
+struct s_smc *smc ;
+{
+	struct s_fpmc	*tb ;
+	u_char	*fu ;
+	int	i ;
+
+	/*
+	 * invalidate the CAM
+	 */
+	outpw(FM_A(FM_AFCMD),FM_IINV_CAM) ;
+
+	/*
+	 * set the functional address
+	 */
+	if (smc->hw.fp.func_addr) {
+		fu = (u_char *) &smc->hw.fp.func_addr ;
+		outpw(FM_A(FM_AFMASK2),0xffff) ;
+		outpw(FM_A(FM_AFMASK1),(u_short) ~((fu[0] << 8) + fu[1])) ;
+		outpw(FM_A(FM_AFMASK0),(u_short) ~((fu[2] << 8) + fu[3])) ;
+		outpw(FM_A(FM_AFPERS),FM_VALID|FM_DA) ;
+		outpw(FM_A(FM_AFCOMP2), 0xc000) ;
+		outpw(FM_A(FM_AFCOMP1), 0x0000) ;
+		outpw(FM_A(FM_AFCOMP0), 0x0000) ;
+		outpw(FM_A(FM_AFCMD),FM_IWRITE_CAM) ;
+	}
+
+	/*
+	 * set the mask and the personality register(s)
+	 */
+	outpw(FM_A(FM_AFMASK0),0xffff) ;
+	outpw(FM_A(FM_AFMASK1),0xffff) ;
+	outpw(FM_A(FM_AFMASK2),0xffff) ;
+	outpw(FM_A(FM_AFPERS),FM_VALID|FM_DA) ;
+
+	for (i = 0, tb = smc->hw.fp.mc.table; i < FPMAX_MULTICAST; i++, tb++) {
+		if (tb->n) {
+			CHECK_CAM() ;
+
+			/*
+			 * wirte the multicast addres into the CAM
+			 */
+			outpw(FM_A(FM_AFCOMP2),
+				(u_short)((tb->a.a[0]<<8)+tb->a.a[1])) ;
+			outpw(FM_A(FM_AFCOMP1),
+				(u_short)((tb->a.a[2]<<8)+tb->a.a[3])) ;
+			outpw(FM_A(FM_AFCOMP0),
+				(u_short)((tb->a.a[4]<<8)+tb->a.a[5])) ;
+			outpw(FM_A(FM_AFCMD),FM_IWRITE_CAM) ;
+		}
+	}
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;3)
+
+	void mac_set_rx_mode(smc,mode)
+	struct s_smc *smc ;
+	int mode ;
+
+Function	DOWNCALL/INTERN	(SMT, fplustm.c)
+		This function enables / disables the selected receive.
+		Don't call this function if the hardware module is
+		used -- use mac_drv_rx_mode() instead of.
+
+Para	mode =	1	RX_ENABLE_ALLMULTI	enable all multicasts
+		2	RX_DISABLE_ALLMULTI	disable "enable all multicasts"
+		3	RX_ENABLE_PROMISC	enable promiscous
+		4	RX_DISABLE_PROMISC	disable promiscous
+		5	RX_ENABLE_NSA		enable reception of NSA frames
+		6	RX_DISABLE_NSA		disable reception of NSA frames
+
+Note	The selected receive modes will be lost after 'driver reset'
+	or 'set station address'
+
+	END_MANUAL_ENTRY
+ */
+void mac_set_rx_mode(smc,mode)
+struct s_smc *smc ;
+int mode ;
+{
+	switch (mode) {
+	case RX_ENABLE_ALLMULTI :
+		smc->hw.fp.rx_prom |= RX_MODE_ALL_MULTI ;
+		break ;
+	case RX_DISABLE_ALLMULTI :
+		smc->hw.fp.rx_prom &= ~RX_MODE_ALL_MULTI ;
+		break ;
+	case RX_ENABLE_PROMISC :
+		smc->hw.fp.rx_prom |= RX_MODE_PROM ;
+		break ;
+	case RX_DISABLE_PROMISC :
+		smc->hw.fp.rx_prom &= ~RX_MODE_PROM ;
+		break ;
+	case RX_ENABLE_NSA :
+		smc->hw.fp.nsa_mode = FM_MDAMA ;
+		smc->hw.fp.rx_mode = (smc->hw.fp.rx_mode & ~FM_ADDET) |
+			smc->hw.fp.nsa_mode ;
+		break ;
+	case RX_DISABLE_NSA :
+		smc->hw.fp.nsa_mode = FM_MRNNSAFNMA ;
+		smc->hw.fp.rx_mode = (smc->hw.fp.rx_mode & ~FM_ADDET) |
+			smc->hw.fp.nsa_mode ;
+		break ;
+	}
+	if (smc->hw.fp.rx_prom & RX_MODE_PROM) {
+		smc->hw.fp.rx_mode = FM_MLIMPROM ;
+	}
+	else if (smc->hw.fp.rx_prom & RX_MODE_ALL_MULTI) {
+		smc->hw.fp.rx_mode = smc->hw.fp.nsa_mode | FM_EXGPA0 ;
+	}
+	else
+		smc->hw.fp.rx_mode = smc->hw.fp.nsa_mode ;
+	SETMASK(FM_A(FM_MDREG1),smc->hw.fp.rx_mode,FM_ADDRX) ;
+	mac_update_multicast(smc) ;
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(module;tests;3)
+	How to test the Restricted Token Monitor
+	----------------------------------------------------------------
+
+	o Insert a break point in the function rtm_irq()
+	o Remove all stations with a restricted token monitor from the
+	  network.
+	o Connect a UPPS ISA or EISA station to the network.
+	o Give the FORMAC of UPPS station the command to send
+	  restricted tokens until the ring becomes instable.
+	o Now connect your test test client.
+	o The restricted token monitor should detect the restricted token,
+	  and your break point will be reached.
+	o You can ovserve how the station will clean the ring.
+
+	END_MANUAL_ENTRY
+ */
+void rtm_irq(smc)
+struct s_smc *smc ;
+{
+	outpw(ADDR(B2_RTM_CRTL),TIM_CL_IRQ) ;		/* clear IRQ */
+	if (inpw(ADDR(B2_RTM_CRTL)) & TIM_RES_TOK) {
+		outpw(FM_A(FM_CMDREG1),FM_ICL) ;	/* force claim */
+		DB_RMT("RMT: fddiPATHT_Rmode expired\n",0,0) ;
+		AIX_EVENT(smc, (u_long) FDDI_RING_STATUS,
+				(u_long) FDDI_SMT_EVENT,
+				(u_long) FDDI_RTT, smt_get_event_word(smc));
+	}
+	outpw(ADDR(B2_RTM_CRTL),TIM_START) ;	/* enable RTM monitoring */
+}
+
+static void rtm_init(smc)
+struct s_smc *smc ;
+{
+	outpd(ADDR(B2_RTM_INI),0) ;		/* timer = 0 */
+	outpw(ADDR(B2_RTM_CRTL),TIM_START) ;	/* enable IRQ */
+}
+
+void rtm_set_timer(smc)
+struct s_smc *smc ;
+{
+	/*
+	 * MIB timer and hardware timer have the same resolution of 80nS
+	 */
+	DB_RMT("RMT: setting new fddiPATHT_Rmode, t = %d ns \n",
+		(int) smc->mib.a[PATH0].fddiPATHT_Rmode,0) ;
+	outpd(ADDR(B2_RTM_INI),smc->mib.a[PATH0].fddiPATHT_Rmode) ;
+}
+
+static void smt_split_up_fifo(smc)
+struct s_smc *smc ;
+{
+
+/*
+	BEGIN_MANUAL_ENTRY(module;mem;1)
+	-------------------------------------------------------------
+	RECEIVE BUFFER MEMORY DIVERSION
+	-------------------------------------------------------------
+
+	R1_RxD == SMT_R1_RXD_COUNT
+	R2_RxD == SMT_R2_RXD_COUNT
+
+	SMT_R1_RXD_COUNT must be unequal zero
+
+		   | R1_RxD R2_RxD |R1_RxD R2_RxD | R1_RxD R2_RxD
+		   |   x      0	   |  x	    1-3	  |   x     < 3
+	----------------------------------------------------------------------
+		   |   63,75 kB	   |    54,75	  |	R1_RxD
+	rx queue 1 | RX_FIFO_SPACE | RX_LARGE_FIFO| ------------- * 63,75 kB
+		   |		   |		  | R1_RxD+R2_RxD
+	----------------------------------------------------------------------
+		   |		   |    9 kB	  |     R2_RxD
+	rx queue 2 |	0 kB	   | RX_SMALL_FIFO| ------------- * 63,75 kB
+		   |  (not used)   |		  | R1_RxD+R2_RxD
+
+	END_MANUAL_ENTRY
+*/
+
+	if (SMT_R1_RXD_COUNT == 0) {
+		SMT_PANIC(smc,SMT_E0117, SMT_E0117_MSG) ;
+	}
+
+	switch(SMT_R2_RXD_COUNT) {
+	case 0:
+		smc->hw.fp.fifo.rx1_fifo_size = RX_FIFO_SPACE ;
+		smc->hw.fp.fifo.rx2_fifo_size = 0 ;
+		break ;
+	case 1:
+	case 2:
+	case 3:
+		smc->hw.fp.fifo.rx1_fifo_size = RX_LARGE_FIFO ;
+		smc->hw.fp.fifo.rx2_fifo_size = RX_SMALL_FIFO ;
+		break ;
+	default:	/* this is not the real defaule */
+		smc->hw.fp.fifo.rx1_fifo_size = RX_FIFO_SPACE *
+		SMT_R1_RXD_COUNT/(SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT) ;
+		smc->hw.fp.fifo.rx2_fifo_size = RX_FIFO_SPACE *
+		SMT_R2_RXD_COUNT/(SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT) ;
+		break ;
+	}
+
+/*
+	BEGIN_MANUAL_ENTRY(module;mem;1)
+	-------------------------------------------------------------
+	TRANSMIT BUFFER MEMORY DIVERSION
+	-------------------------------------------------------------
+
+
+		 | no sync bw	| sync bw available and | sync bw available and
+		 | available	| SynchTxMode = SPLIT	| SynchTxMode = ALL
+	-----------------------------------------------------------------------
+	sync tx	 |     0 kB	|	32 kB		|	55 kB
+	queue	 |		|   TX_MEDIUM_FIFO	|   TX_LARGE_FIFO
+	-----------------------------------------------------------------------
+	async tx |    64 kB	|	32 kB		|	 9 k
+	queue	 | TX_FIFO_SPACE|   TX_MEDIUM_FIFO	|   TX_SMALL_FIFO
+
+	END_MANUAL_ENTRY
+*/
+
+	/*
+	 * set the tx mode bits
+	 */
+	if (smc->mib.a[PATH0].fddiPATHSbaPayload) {
+#ifdef ESS
+		smc->hw.fp.fifo.fifo_config_mode |=
+			smc->mib.fddiESSSynchTxMode | SYNC_TRAFFIC_ON ;
+#endif
+	}
+	else {
+		smc->hw.fp.fifo.fifo_config_mode &=
+			~(SEND_ASYNC_AS_SYNC|SYNC_TRAFFIC_ON) ;
+	}
+
+	/*
+	 * split up the FIFO
+	 */
+	if (smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON) {
+		if (smc->hw.fp.fifo.fifo_config_mode & SEND_ASYNC_AS_SYNC) {
+			smc->hw.fp.fifo.tx_s_size = TX_LARGE_FIFO ;
+			smc->hw.fp.fifo.tx_a0_size = TX_SMALL_FIFO ;
+		}
+		else {
+			smc->hw.fp.fifo.tx_s_size = TX_MEDIUM_FIFO ;
+			smc->hw.fp.fifo.tx_a0_size = TX_MEDIUM_FIFO ;
+		}
+	}
+	else {
+			smc->hw.fp.fifo.tx_s_size = 0 ;
+			smc->hw.fp.fifo.tx_a0_size = TX_FIFO_SPACE ;
+	}
+
+	smc->hw.fp.fifo.rx1_fifo_start = smc->hw.fp.fifo.rbc_ram_start +
+		RX_FIFO_OFF ;
+	smc->hw.fp.fifo.tx_s_start = smc->hw.fp.fifo.rx1_fifo_start +
+		smc->hw.fp.fifo.rx1_fifo_size ;
+	smc->hw.fp.fifo.tx_a0_start = smc->hw.fp.fifo.tx_s_start +
+		smc->hw.fp.fifo.tx_s_size ;
+	smc->hw.fp.fifo.rx2_fifo_start = smc->hw.fp.fifo.tx_a0_start +
+		smc->hw.fp.fifo.tx_a0_size ;
+
+	DB_SMT("FIFO split: mode = %x\n",smc->hw.fp.fifo.fifo_config_mode,0) ;
+	DB_SMT("rbc_ram_start =	%x	 rbc_ram_end = 	%x\n",
+		smc->hw.fp.fifo.rbc_ram_start, smc->hw.fp.fifo.rbc_ram_end) ;
+	DB_SMT("rx1_fifo_start = %x	 tx_s_start = 	%x\n",
+		smc->hw.fp.fifo.rx1_fifo_start, smc->hw.fp.fifo.tx_s_start) ;
+	DB_SMT("tx_a0_start =	%x	 rx2_fifo_start = 	%x\n",
+		smc->hw.fp.fifo.tx_a0_start, smc->hw.fp.fifo.rx2_fifo_start) ;
+}
+
+void formac_reinit_tx(smc)
+struct s_smc *smc ;
+{
+	/*
+	 * Split up the FIFO and reinitialize the MAC if synchronous
+	 * bandwidth becomes available but no synchronous queue is
+	 * configured.
+	 */
+	if (!smc->hw.fp.fifo.tx_s_size && smc->mib.a[PATH0].fddiPATHSbaPayload){
+		(void)init_mac(smc,0) ;
+	}
+}
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/cmtdef.h linux.18p17/drivers/net/skfp/h/cmtdef.h
--- linux.vanilla/drivers/net/skfp/h/cmtdef.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/cmtdef.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,801 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_CMTDEF_
+#define _CMTDEF_
+
+/* **************************************************************** */
+
+/*
+ * implementation specific constants
+ * MODIIFY THE FOLLWOING THREE DEFINES
+ */
+#define AMDPLC			/* if Amd PLC chip used */
+#ifdef	CONC
+#define NUMPHYS		12	/* 2 for SAS or DAS, more for Concentrator */
+#else
+#ifdef	CONC_II
+#define NUMPHYS		24	/* 2 for SAS or DAS, more for Concentrator */
+#else
+#define NUMPHYS		2	/* 2 for SAS or DAS, more for Concentrator */
+#endif
+#endif
+#define NUMMACS		1	/* only 1 supported at the moment */
+#define NUMPATHS	2	/* primary and secondary path supported */
+
+/*
+ * DO NOT MODIFY BEYOND THIS POINT
+ */
+
+/* **************************************************************** */
+
+#if	NUMPHYS > 2
+#define CONCENTRATOR
+#endif
+
+/*
+ * Definitions for comfortable LINT usage
+ */
+#ifdef	lint
+#define LINT_USE(x)	(x)=(x)
+#else
+#define LINT_USE(x)
+#endif
+
+#ifdef	DEBUG
+#define	DB_PR(flag,a,b,c)	{ if (flag) printf(a,b,c) ; }
+#else
+#define	DB_PR(flag,a,b,c)
+#endif
+
+#ifdef DEBUG_BRD
+#define DB_ECM(a,b,c)		DB_PR((smc->debug.d_smt&1),a,b,c)
+#define DB_ECMN(n,a,b,c)	DB_PR((smc->debug.d_ecm >=(n)),a,b,c)
+#define DB_RMT(a,b,c)		DB_PR((smc->debug.d_smt&2),a,b,c)
+#define DB_RMTN(n,a,b,c)	DB_PR((smc->debug.d_rmt >=(n)),a,b,c)
+#define DB_CFM(a,b,c)		DB_PR((smc->debug.d_smt&4),a,b,c)
+#define DB_CFMN(n,a,b,c)	DB_PR((smc->debug.d_cfm >=(n)),a,b,c)
+#define DB_PCM(a,b,c)		DB_PR((smc->debug.d_smt&8),a,b,c)
+#define DB_PCMN(n,a,b,c)	DB_PR((smc->debug.d_pcm >=(n)),a,b,c)
+#define DB_SMT(a,b,c)		DB_PR((smc->debug.d_smtf),a,b,c)
+#define DB_SMTN(n,a,b,c)	DB_PR((smc->debug.d_smtf >=(n)),a,b,c)
+#define DB_SBA(a,b,c)		DB_PR((smc->debug.d_sba),a,b,c)
+#define DB_SBAN(n,a,b,c)	DB_PR((smc->debug.d_sba >=(n)),a,b,c)
+#define DB_ESS(a,b,c)		DB_PR((smc->debug.d_ess),a,b,c)
+#define DB_ESSN(n,a,b,c)	DB_PR((smc->debug.d_ess >=(n)),a,b,c)
+#else
+#define DB_ECM(a,b,c)		DB_PR((debug.d_smt&1),a,b,c)
+#define DB_ECMN(n,a,b,c)	DB_PR((debug.d_ecm >=(n)),a,b,c)
+#define DB_RMT(a,b,c)		DB_PR((debug.d_smt&2),a,b,c)
+#define DB_RMTN(n,a,b,c)	DB_PR((debug.d_rmt >=(n)),a,b,c)
+#define DB_CFM(a,b,c)		DB_PR((debug.d_smt&4),a,b,c)
+#define DB_CFMN(n,a,b,c)	DB_PR((debug.d_cfm >=(n)),a,b,c)
+#define DB_PCM(a,b,c)		DB_PR((debug.d_smt&8),a,b,c)
+#define DB_PCMN(n,a,b,c)	DB_PR((debug.d_pcm >=(n)),a,b,c)
+#define DB_SMT(a,b,c)		DB_PR((debug.d_smtf),a,b,c)
+#define DB_SMTN(n,a,b,c)	DB_PR((debug.d_smtf >=(n)),a,b,c)
+#define DB_SBA(a,b,c)		DB_PR((debug.d_sba),a,b,c)
+#define DB_SBAN(n,a,b,c)	DB_PR((debug.d_sba >=(n)),a,b,c)
+#define DB_ESS(a,b,c)		DB_PR((debug.d_ess),a,b,c)
+#define DB_ESSN(n,a,b,c)	DB_PR((debug.d_ess >=(n)),a,b,c)
+#endif
+
+#ifndef	SS_NOT_DS
+#define	SK_LOC_DECL(type,var)	type var
+#else
+#define	SK_LOC_DECL(type,var)	static type var
+#endif
+/*
+ * PHYs and PORTS
+ * Note: Don't touch the definition of PA and PB. Those might be used
+ *	by some "for" loops.
+ */
+#define PA		0
+#define PB		1
+#if	defined(SUPERNET_3) || defined(CONC_II)
+/*
+ * The port indices have to be different,
+ * because the MAC output goes through the 2. PLC
+ * Conc II: It has to be the first port in the row.
+ */
+#define PS		0	/* Internal PLC which is the same as PA */
+#else
+#define PS		1
+#endif
+#define PM		2		/* PM .. PA+NUM_PHYS-1 */
+
+/*
+ * PHY types - as in path descriptor 'fddiPHYType'
+ */
+#define TA			0	/* A port */
+#define TB			1	/* B port */
+#define TS			2	/* S port */
+#define TM			3	/* M port */
+#define TNONE			4
+
+
+/*
+ * indexes in MIB
+ */
+#define INDEX_MAC	1
+#define INDEX_PATH	1
+#define INDEX_PORT	1
+
+
+/*
+ * policies
+ */
+#define POLICY_AA	(1<<0)		/* reject AA */
+#define POLICY_AB	(1<<1)		/* reject AB */
+#define POLICY_AS	(1<<2)		/* reject AS */
+#define POLICY_AM	(1<<3)		/* reject AM */
+#define POLICY_BA	(1<<4)		/* reject BA */
+#define POLICY_BB	(1<<5)		/* reject BB */
+#define POLICY_BS	(1<<6)		/* reject BS */
+#define POLICY_BM	(1<<7)		/* reject BM */
+#define POLICY_SA	(1<<8)		/* reject SA */
+#define POLICY_SB	(1<<9)		/* reject SB */
+#define POLICY_SS	(1<<10)		/* reject SS */
+#define POLICY_SM	(1<<11)		/* reject SM */
+#define POLICY_MA	(1<<12)		/* reject MA */
+#define POLICY_MB	(1<<13)		/* reject MB */
+#define POLICY_MS	(1<<14)		/* reject MS */
+#define POLICY_MM	(1<<15)		/* reject MM */
+
+/*
+ * commands
+ */
+
+/*
+ * EVENTS
+ * event classes
+ */
+#define EVENT_ECM	1		/* event class ECM */
+#define EVENT_CFM	2		/* event class CFM */
+#define EVENT_RMT	3		/* event class RMT */
+#define EVENT_SMT	4		/* event class SMT */
+#define EVENT_PCM	5		/* event class PCM */
+#define EVENT_PCMA	5		/* event class PCMA */
+#define EVENT_PCMB	6		/* event class PCMB */
+
+/* WARNING :
+ * EVENT_PCM* must be last in the above list
+ * if more then two ports are used, EVENT_PCM .. EVENT_PCMA+NUM_PHYS-1
+ * are used !
+ */
+
+#define EV_TOKEN(class,event)	(((u_long)(class)<<16L)|((u_long)(event)))
+#define EV_T_CLASS(token)	((int)((token)>>16)&0xffff)
+#define EV_T_EVENT(token)	((int)(token)&0xffff)
+
+/*
+ * ECM events
+ */
+#define EC_CONNECT	1		/* connect request */
+#define EC_DISCONNECT	2		/* disconnect request */
+#define EC_TRACE_PROP	3		/* trace propagation */
+#define EC_PATH_TEST	4		/* path test */
+#define EC_TIMEOUT_TD	5		/* timer TD_min */
+#define EC_TIMEOUT_TMAX	6		/* timer trace_max */
+#define EC_TIMEOUT_IMAX	7		/* timer I_max */
+#define EC_TIMEOUT_INMAX 8		/* timer IN_max */
+#define EC_TEST_DONE	9		/* path test done */
+
+/*
+ * CFM events
+ */
+#define CF_LOOP		1		/* cf_loop flag from PCM */
+#define CF_LOOP_A	1		/* cf_loop flag from PCM */
+#define CF_LOOP_B	2		/* cf_loop flag from PCM */
+#define CF_JOIN		3		/* cf_join flag from PCM */
+#define CF_JOIN_A	3		/* cf_join flag from PCM */
+#define CF_JOIN_B	4		/* cf_join flag from PCM */
+
+/*
+ * PCM events
+ */
+#define PC_START		1
+#define PC_STOP			2
+#define PC_LOOP			3
+#define PC_JOIN			4
+#define PC_SIGNAL		5
+#define PC_REJECT		6
+#define PC_MAINT    		7
+#define PC_TRACE		8
+#define PC_PDR			9
+#define PC_ENABLE		10
+#define PC_DISABLE		11
+
+/*
+ * must be ordered as in LineStateType
+ */
+#define PC_QLS			12
+#define PC_ILS			13
+#define PC_MLS			14
+#define PC_HLS			15
+#define PC_LS_PDR		16
+#define PC_LS_NONE		17
+#define LS2MIB(x)	((x)-PC_QLS)
+#define MIB2LS(x)	((x)+PC_QLS)
+
+#define PC_TIMEOUT_TB_MAX	18	/* timer TB_max */
+#define PC_TIMEOUT_TB_MIN	19	/* timer TB_min */
+#define PC_TIMEOUT_C_MIN	20	/* timer C_Min */
+#define PC_TIMEOUT_T_OUT	21	/* timer T_Out */
+#define PC_TIMEOUT_TL_MIN	22	/* timer TL_Min */
+#define PC_TIMEOUT_T_NEXT	23	/* timer t_next[] */
+#define PC_TIMEOUT_LCT		24
+#define PC_NSE			25	/* NOISE hardware timer */
+#define PC_LEM			26	/* LEM done */
+
+/*
+ * RMT events				  meaning		from
+ */
+#define RM_RING_OP	1		/* ring operational	MAC	*/
+#define RM_RING_NON_OP	2		/* ring not operational	MAC	*/
+#define RM_MY_BEACON	3		/* recvd my beacon	MAC	*/
+#define RM_OTHER_BEACON	4		/* recvd other beacon	MAC	*/
+#define RM_MY_CLAIM	5		/* recvd my claim	MAC	*/
+#define RM_TRT_EXP	6		/* TRT exp		MAC	*/
+#define RM_VALID_CLAIM	7		/* claim from dup addr	MAC	*/
+#define RM_JOIN		8		/* signal rm_join	CFM	*/
+#define RM_LOOP		9		/* signal rm_loop	CFM	*/
+#define RM_DUP_ADDR	10		/* dup_addr_test hange	SMT-NIF	*/
+#define RM_ENABLE_FLAG	11		/* enable flag */
+
+#define RM_TIMEOUT_NON_OP	12	/* timeout T_Non_OP	*/
+#define RM_TIMEOUT_T_STUCK	13	/* timeout T_Stuck	*/
+#define RM_TIMEOUT_ANNOUNCE	14	/* timeout T_Announce	*/
+#define RM_TIMEOUT_T_DIRECT	15	/* timeout T_Direct	*/
+#define RM_TIMEOUT_D_MAX	16	/* timeout D_Max	*/
+#define RM_TIMEOUT_POLL		17	/* claim/beacon poller	*/
+#define RM_TX_STATE_CHANGE	18	/* To restart timer for D_Max */
+
+/*
+ * SMT events
+ */
+#define SM_TIMER	1		/* timer */
+#define SM_FAST		2		/* smt_force_irq */
+
+/* PC modes */
+#define PM_NONE		0
+#define PM_PEER		1
+#define PM_TREE		2
+
+/*
+ * PCM withhold codes
+ * MIB PC-WithholdType ENUM
+ */
+#define PC_WH_NONE	0		/* ok */
+#define PC_WH_M_M	1		/* M to M */
+#define PC_WH_OTHER	2		/* other incompatible phys */
+#define PC_WH_PATH	3		/* path not available */
+/*
+ * LCT duration
+ */
+#define LC_SHORT	1		/* short LCT */
+#define LC_MEDIUM	2		/* medium LCT */
+#define LC_LONG		3		/* long LCT */
+#define LC_EXTENDED	4		/* extended LCT */
+
+/*
+ * path_test values
+ */
+#define PT_NONE		0
+#define PT_TESTING	1		/* test is running */
+#define PT_PASSED	2		/* test passed */
+#define PT_FAILED	3		/* test failed */
+#define PT_PENDING	4		/* path test follows */
+#define PT_EXITING	5		/* disconnected while in trace/leave */
+
+/*
+ * duplicate address test
+ * MIB DupAddressTest ENUM
+ */
+#define DA_NONE		0		/* 		*/
+#define DA_PASSED	1		/* test passed */
+#define DA_FAILED	2		/* test failed */
+
+
+/*
+ * optical bypass
+ */
+#define BP_DEINSERT	0		/* disable bypass */
+#define BP_INSERT	1		/* enable bypass */
+
+/*
+ * ODL enable/disable
+ */
+#define PM_TRANSMIT_DISABLE	0	/* disable xmit */
+#define PM_TRANSMIT_ENABLE	1	/* enable xmit */
+
+/*
+ * parameter for config_mux
+ * note : number is index in config_endec table !
+ */
+#define MUX_THRUA	0		/* through A */
+#define MUX_THRUB	1		/* through B */
+#define MUX_WRAPA	2		/* wrap A */
+#define MUX_WRAPB	3		/* wrap B */
+#define MUX_ISOLATE	4		/* isolated */
+#define MUX_WRAPS	5		/* SAS */
+
+/*
+ * MAC control
+ */
+#define MA_RESET	0
+#define MA_BEACON	1
+#define MA_CLAIM	2
+#define MA_DIRECTED	3		/* directed beacon */
+#define MA_TREQ		4		/* change T_Req */
+#define MA_OFFLINE	5		/* switch MAC to offline */
+
+
+/*
+ * trace prop
+ * bit map for trace propagation
+ */
+#define ENTITY_MAC	(NUMPHYS)
+#define ENTITY_PHY(p)	(p)
+#define ENTITY_BIT(m)	(1<<(m))
+
+/*
+ * Resource Tag Types
+ */
+#define PATH_ISO	0	/* isolated */
+#define PATH_PRIM	3	/* primary path */
+#define PATH_THRU	5	/* through path */
+
+#define RES_MAC		2	/* resource type MAC */
+#define RES_PORT	4	/* resource type PORT */
+
+
+/*
+ * CFM state
+ * oops: MUST MATCH CF-StateType in SMT7.2 !
+ */
+#define SC0_ISOLATED	0		/* isolated */
+#define SC1_WRAP_A	5		/* wrap A (not used) */
+#define SC2_WRAP_B	6		/* wrap B (not used) */
+#define SC4_THRU_A	12		/* through A */
+#define SC5_THRU_B	7		/* through B (used in SMT 6.2) */
+#define SC7_WRAP_S	8		/* SAS (not used) */
+#define SC9_C_WRAP_A	9		/* c wrap A */
+#define SC10_C_WRAP_B	10		/* c wrap B */
+#define SC11_C_WRAP_S	11		/* c wrap S */
+
+/*
+ * convert MIB time in units of 80nS to uS
+ */
+#define MIB2US(t)		((t)/12)
+#define SEC2MIB(s)	((s)*12500000L)
+/*
+ * SMT timer
+ */
+struct smt_timer {
+	struct smt_timer	*tm_next ;	/* linked list */
+	struct s_smc		*tm_smc ;	/* pointer to context */
+	u_long			tm_delta ;	/* delta time */
+	u_long			tm_token ;	/* token value */
+	u_short			tm_active ;	/* flag : active/inactive */
+	u_short			tm_pad ;	/* pad field */
+} ;
+
+/*
+ * communication structures
+ */
+struct mac_parameter {
+	u_long	t_neg ;		/* T_Neg parameter */
+	u_long	t_pri ;		/* T_Pri register in MAC */
+} ;
+
+/*
+ * MAC counters
+ */
+struct mac_counter {
+	u_long	mac_nobuf_counter ;	/* MAC SW counter: no buffer */
+	u_long	mac_r_restart_counter ;	/* MAC SW counter: rx restarted */
+} ;
+
+/*
+ * para struct context for SMT parameters
+ */
+struct s_pcon {
+	int	pc_len ;
+	int	pc_err ;
+	int	pc_badset ;
+	void	*pc_p ;
+} ;
+
+
+/*
+ * link error monitor
+ */
+#define LEM_AVG	5
+struct lem_counter {
+#ifdef	AM29K
+	int	lem_on	;
+	u_long	lem_errors ;
+	u_long	lem_symbols ;
+	u_long	lem_tsymbols ;
+	int	lem_s_count ;
+	int	lem_n_s ;
+	int	lem_values ;
+	int	lem_index ;
+	int	lem_avg_ber[LEM_AVG] ;
+	int	lem_sum ;
+#else
+	u_short	lem_float_ber ;		/* 10E-nn bit error rate */
+	u_long	lem_errors ;		/* accumulated error count */
+	u_short	lem_on	;
+#endif
+} ;
+
+#define NUMBITS	10
+
+
+#ifdef	AMDPLC
+
+/*
+ * PLC state table
+ */
+struct s_plc {
+	u_short	p_state ;		/* current state */
+	u_short	p_bits ;		/* number of bits to send */
+	u_short	p_start ;		/* first bit pos */
+	u_short	p_pad ;			/* padding for alignment */
+	u_long soft_err ;		/* error counter */
+	u_long parity_err ;		/* error counter */
+	u_long ebuf_err ;		/* error counter */
+	u_long ebuf_cont ;		/* continous error counter */
+	u_long phyinv ;			/* error counter */
+	u_long vsym_ctr ;		/* error counter */
+	u_long mini_ctr ;		/* error counter */
+	u_long tpc_exp ;		/* error counter */
+	u_long np_err ;			/* error counter */
+	u_long b_pcs ;			/* error counter */
+	u_long b_tpc ;			/* error counter */
+	u_long b_tne ;			/* error counter */
+	u_long b_qls ;			/* error counter */
+	u_long b_ils ;			/* error counter */
+	u_long b_hls ;			/* error counter */
+} ;
+#endif
+
+#ifdef	PROTOTYP_INC
+#include "fddi/driver.pro"
+#else	/* PROTOTYP_INC */
+/*
+ * function prototypes
+ */
+#include "h/mbuf.h"	/* Type definitions for MBUFs */
+void hwt_restart(                       /* hwt.c */
+#ifdef ANSIC
+        struct s_smc *smc
+#endif
+	) ;
+
+SMbuf *smt_build_frame(                 /* smt.c */
+#ifdef ANSIC
+        struct s_smc *smc,
+        int class,
+        int type,
+        int length
+#endif
+	) ;
+
+SMbuf *smt_get_mbuf(                     /* drvsr.c */
+#ifdef ANSIC
+        struct s_smc *smc
+#endif
+	) ;
+
+void *sm_to_para(                       /* smt.c */
+#ifdef ANSIC
+	struct s_smc *smc,
+        struct smt_header *sm,
+        int para
+#endif
+	) ;
+
+#ifndef SK_UNUSED
+#define SK_UNUSED(var)		(void)(var)
+#endif
+
+void queue_event() ;
+void ecm() ;
+void ecm_init() ;
+void rmt() ;
+void rmt_init() ;
+void pcm() ;
+void pcm_init() ;
+void cfm() ;
+void cfm_init() ;
+void smt_timer_start() ;
+void smt_timer_stop() ;
+void pcm_status_state() ;
+void plc_config_mux() ;
+void sm_lem_evaluate() ;
+void smt_clear_una_dna() ;
+void mac_status_para() ;
+void mac_update_counter() ;
+void sm_pm_ls_latch() ;
+void sm_ma_control() ;
+void sm_mac_check_beacon_claim() ;
+void config_mux() ;
+void smt_agent_init() ;
+void smt_timer_init() ;
+void smt_received_pack() ;
+void smt_add_para() ;
+void smt_swap_para() ;
+void ev_init() ;
+void hwt_init() ;
+u_long hwt_read() ;
+void hwt_stop() ;
+void hwt_start() ;
+void smt_send_mbuf() ;
+void smt_free_mbuf() ;
+void sm_pm_bypass_req() ;
+void rmt_indication() ;
+void cfm_state_change() ;
+void rx_indication() ;
+void tx_indication() ;
+#ifndef NO_SMT_PANIC
+void smt_panic() ;
+#else
+#ifdef	DEBUG
+void smt_panic() ;
+#else
+#define	smt_panic(smc,text)
+#endif	/* DEBUG */
+#endif	/* NO_SMT_PANIC */
+void smt_stat_counter() ;
+void smt_timer_poll() ;
+u_long smt_get_time() ;
+u_long smt_get_tid() ;
+void smt_timer_done() ;
+void smt_set_defaults() ;
+void smt_fixup_mib() ;
+void smt_reset_defaults() ;
+void smt_agent_task() ;
+void smt_please_reconnect() ;
+int smt_check_para() ;
+void driver_get_bia() ;
+#ifdef	SUPERNET_3
+void drv_reset_indication() ;
+#endif	/* SUPERNET_3 */
+void smt_start_watchdog() ;
+
+void smt_event() ;
+void pcm_event() ;
+void rmt_event() ;
+void cfm_event() ;
+void timer_event() ;
+void ev_dispatcher() ;
+
+void smt_get_state() ;
+void ecm_get_state() ;
+void pcm_get_state() ;
+void rmt_get_state() ;
+
+void ecm_state_change() ;
+int sm_pm_bypass_present() ;
+void pcm_state_change() ;
+void rmt_state_change() ;
+int sm_pm_get_ls() ;
+int pcm_get_s_port() ;
+int pcm_rooted_station() ;
+int cfm_get_mac_input() ;
+int cfm_get_mac_output() ;
+int port_to_mib() ;
+int cem_build_path() ;
+int sm_mac_get_tx_state() ;
+int is_individual() ;
+int is_my_addr() ;
+int is_broadcast() ;
+int is_equal() ;
+char *get_pcmstate() ;
+
+int smt_action() ;
+u_short smt_online() ;
+void smt_force_irq() ;
+void smt_pmf_received_pack() ;
+void smt_send_frame() ;
+void smt_set_timestamp() ;
+void mac_set_rx_mode() ;
+int mac_add_multicast() ;
+int mac_set_func_addr() ;
+void mac_del_multicast() ;
+void mac_update_multicast() ;
+void mac_clear_multicast() ;
+void mac_rx_directed_beacon() ;
+void set_formac_tsync() ;
+void formac_reinit_tx() ;
+void formac_tx_restart() ;
+void process_receive() ;
+void init_driver_fplus() ;
+
+void rtm_irq() ;
+void rtm_set_timer() ;
+void ring_status_indication() ;
+void llc_recover_tx() ;
+void llc_restart_tx() ;
+void plc_clear_irq() ;
+void plc_irq() ;
+int smt_set_mac_opvalues() ;
+#ifdef	TAG_MODE
+void mac_drv_pci_fix() ;
+void mac_do_pci_fix() ;
+void mac_drv_clear_tx_queue() ;
+void mac_drv_repair_descr() ;
+u_long hwt_quick_read() ;
+void hwt_wait_time() ;
+#endif
+
+#ifdef SMT_PNMI
+#ifdef ANSIC
+int pnmi_init (struct s_smc* smc);
+int pnmi_process_ndis_id (struct s_smc* smc, u_long ndis_oid, void* buf,
+		int len, int* BytesAccessed, int* BytesNeeded, u_char action);
+#else
+int pnmi_init ();
+int pnmi_process_ndis_id ();
+#endif
+#endif
+
+#ifdef	SBA
+#ifndef _H2INC
+void sba() ;
+#endif
+void sba_raf_received_pack() ;
+void sba_timer_poll() ;
+void smt_init_sba() ;
+#endif
+#ifdef	ESS
+int ess_raf_received_pack() ;
+void ess_timer_poll() ;
+void ess_para_change() ;
+#endif
+
+#ifdef	BOOT
+#define smt_srf_event(a,b,c,d)
+#define smt_init_evc(a)
+#else
+void smt_init_evc() ;
+void smt_srf_event() ;
+#endif
+
+#ifndef SMT_REAL_TOKEN_CT
+void smt_emulate_token_ct();
+#endif
+
+#if defined(DEBUG) && !defined(BOOT)
+void dump_smt() ;
+#else
+#define	dump_smt(smc,sm,text)
+#endif
+
+#ifdef	DEBUG
+char	*addr_to_string() ;
+void	dump_hex() ;
+#endif
+#endif	/* PROTOTYP_INC */
+
+/* PNMI default defines */
+#ifndef PNMI_INIT
+#define	PNMI_INIT(smc)	/* Nothing */
+#endif
+#ifndef PNMI_GET_ID
+#define PNMI_GET_ID( smc, ndis_oid, buf, len, BytesWritten, BytesNeeded ) \
+		( 1 ? (-1) : (-1) )
+#endif
+#ifndef PNMI_SET_ID
+#define PNMI_SET_ID( smc, ndis_oid, buf, len, BytesRead, BytesNeeded, \
+		set_type) ( 1 ? (-1) : (-1) )
+#endif
+
+/*
+ * SMT_PANIC defines
+ */
+#ifndef	SMT_PANIC
+#define	SMT_PANIC(smc,nr,msg)	smt_panic (smc, msg)
+#endif
+
+#ifndef	SMT_ERR_LOG
+#define	SMT_ERR_LOG(smc,nr,msg)	SMT_PANIC (smc, nr, msg)
+#endif
+
+#ifndef	SMT_EBASE
+#define	SMT_EBASE	100
+#endif
+
+#define	SMT_E0100	SMT_EBASE + 0
+#define	SMT_E0100_MSG	"cfm FSM: illegal ce_type"
+#define	SMT_E0101	SMT_EBASE + 1
+#define	SMT_E0101_MSG	"CEM: case ???"
+#define	SMT_E0102	SMT_EBASE + 2
+#define	SMT_E0102_MSG	"CEM A: illegal state"
+#define	SMT_E0103	SMT_EBASE + 3
+#define	SMT_E0103_MSG	"CEM B: illegal state"
+#define	SMT_E0104	SMT_EBASE + 4
+#define	SMT_E0104_MSG	"CEM M: illegal state"
+#define	SMT_E0105	SMT_EBASE + 5
+#define	SMT_E0105_MSG	"CEM S: illegal state"
+#define	SMT_E0106	SMT_EBASE + 6
+#define	SMT_E0106_MSG	"CFM : illegal state"
+#define	SMT_E0107	SMT_EBASE + 7
+#define	SMT_E0107_MSG	"ECM : illegal state"
+#define	SMT_E0108	SMT_EBASE + 8
+#define	SMT_E0108_MSG	"prop_actions : NAC in DAS CFM"
+#define	SMT_E0109	SMT_EBASE + 9
+#define	SMT_E0109_MSG	"ST2U.FM_SERRSF error in special frame"
+#define	SMT_E0110	SMT_EBASE + 10
+#define	SMT_E0110_MSG	"ST2U.FM_SRFRCTOV recv. count. overflow"
+#define	SMT_E0111	SMT_EBASE + 11
+#define	SMT_E0111_MSG	"ST2U.FM_SNFSLD NP & FORMAC simult. load"
+#define	SMT_E0112	SMT_EBASE + 12
+#define	SMT_E0112_MSG	"ST2U.FM_SRCVFRM single-frame recv.-mode"
+#define	SMT_E0113	SMT_EBASE + 13
+#define	SMT_E0113_MSG	"FPLUS: Buffer Memory Error"
+#define	SMT_E0114	SMT_EBASE + 14
+#define	SMT_E0114_MSG	"ST2U.FM_SERRSF error in special frame"
+#define	SMT_E0115	SMT_EBASE + 15
+#define	SMT_E0115_MSG	"ST3L: parity error in receive queue 2"
+#define	SMT_E0116	SMT_EBASE + 16
+#define	SMT_E0116_MSG	"ST3L: parity error in receive queue 1"
+#define	SMT_E0117	SMT_EBASE + 17
+#define	SMT_E0117_MSG	"E_SMT_001: RxD count for receive queue 1 = 0"
+#define	SMT_E0118	SMT_EBASE + 18
+#define	SMT_E0118_MSG	"PCM : illegal state"
+#define	SMT_E0119	SMT_EBASE + 19
+#define	SMT_E0119_MSG	"smt_add_para"
+#define	SMT_E0120	SMT_EBASE + 20
+#define	SMT_E0120_MSG	"smt_set_para"
+#define	SMT_E0121	SMT_EBASE + 21
+#define	SMT_E0121_MSG	"illegal event in dispatcher"
+#define	SMT_E0122	SMT_EBASE + 22
+#define	SMT_E0122_MSG	"RMT : illegal state"
+#define	SMT_E0123	SMT_EBASE + 23
+#define	SMT_E0123_MSG	"SBA: state machine has illegal state"
+#define	SMT_E0124	SMT_EBASE + 24
+#define	SMT_E0124_MSG	"sba_free_session() called with NULL pointer"
+#define	SMT_E0125	SMT_EBASE + 25
+#define	SMT_E0125_MSG	"SBA : illegal session pointer"
+#define	SMT_E0126	SMT_EBASE + 26
+#define	SMT_E0126_MSG	"smt_free_mbuf() called with NULL pointer\n"
+#define	SMT_E0127	SMT_EBASE + 27
+#define	SMT_E0127_MSG	"sizeof evcs"
+#define	SMT_E0128	SMT_EBASE + 28
+#define	SMT_E0128_MSG	"evc->evc_cond_state = 0"
+#define	SMT_E0129	SMT_EBASE + 29
+#define	SMT_E0129_MSG	"evc->evc_multiple = 0"
+#define	SMT_E0130	SMT_EBASE + 30
+#define	SMT_E0130_MSG	write_mdr_warning
+#define	SMT_E0131	SMT_EBASE + 31
+#define	SMT_E0131_MSG	cam_warning
+#define SMT_E0132	SMT_EBASE + 32
+#define SMT_E0132_MSG	"ST1L.FM_SPCEPDx parity/coding error"
+#define SMT_E0133	SMT_EBASE + 33
+#define SMT_E0133_MSG	"ST1L.FM_STBURx tx buffer underrun"
+#define SMT_E0134	SMT_EBASE + 34
+#define SMT_E0134_MSG	"ST1L.FM_SPCEPDx parity error"
+#define SMT_E0135	SMT_EBASE + 35
+#define SMT_E0135_MSG	"RMT: duplicate MAC address detected. Ring left!"
+#define SMT_E0136	SMT_EBASE + 36
+#define SMT_E0136_MSG	"Elasticity Buffer hang-up"
+#define SMT_E0137	SMT_EBASE + 37
+#define SMT_E0137_MSG	"SMT: queue overrun"
+#define SMT_E0138	SMT_EBASE + 38
+#define SMT_E0138_MSG	"RMT: duplicate MAC address detected. Ring NOT left!"
+#endif	/* _CMTDEF_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/fddi.h linux.18p17/drivers/net/skfp/h/fddi.h
--- linux.vanilla/drivers/net/skfp/h/fddi.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/fddi.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_FDDI_
+#define _FDDI_
+
+struct fddi_addr {
+	u_char	a[6] ;
+} ;
+
+#define GROUP_ADDR	0x80		/* MSB in a[0] */
+
+struct fddi_mac {
+	struct fddi_addr	mac_dest ;
+	struct fddi_addr	mac_source ;
+	u_char			mac_info[4478] ;
+} ;
+
+#define FDDI_MAC_SIZE	(12)
+#define FDDI_RAW_MTU	(4500-5)	/* exl. Pr,SD, ED/FS */
+#define FDDI_RAW	(4500)
+
+/*
+ * FC values
+ */
+#define FC_VOID		0x40		/* void frame */
+#define FC_TOKEN	0x80		/* token */
+#define FC_RES_TOKEN	0xc0		/* restricted token */
+#define FC_SMT_INFO	0x41		/* SMT Info frame */
+/*
+ * FC_SMT_LAN_LOC && FC_SMT_LOC are SK specific !
+ */
+#define FC_SMT_LAN_LOC	0x42		/* local SMT Info frame */
+#define FC_SMT_LOC	0x43		/* local SMT Info frame */
+#define FC_SMT_NSA	0x4f		/* SMT NSA frame */
+#define FC_MAC		0xc0		/* MAC frame */
+#define FC_BEACON	0xc2		/* MAC beacon frame */
+#define FC_CLAIM	0xc3		/* MAC claim frame */
+#define FC_SYNC_LLC	0xd0		/* sync. LLC frame */
+#define FC_ASYNC_LLC	0x50		/* async. LLC frame */
+#define FC_SYNC_BIT	0x80		/* sync. bit in FC */
+
+#define FC_LLC_PRIOR	0x07		/* priority bits */
+
+#define BEACON_INFO	0		/* beacon type */
+#define DBEACON_INFO	1		/* beacon type DIRECTED */
+
+
+/*
+ * indicator bits
+ */
+#define C_INDICATOR	(1<<0)
+#define A_INDICATOR	(1<<1)
+#define E_INDICATOR	(1<<2)
+#define I_INDICATOR	(1<<6)		/* SK specific */ 
+#define L_INDICATOR	(1<<7)		/* SK specific */
+
+#endif	/* _FDDI_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/fddimib.h linux.18p17/drivers/net/skfp/h/fddimib.h
--- linux.vanilla/drivers/net/skfp/h/fddimib.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/fddimib.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,349 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * FDDI MIB
+ */
+
+/*
+ * typedefs
+ */
+
+typedef	u_long	Counter ;
+typedef u_char	TimeStamp[8] ;
+typedef struct fddi_addr LongAddr ;
+typedef	u_long	Timer_2 ;
+typedef	u_long	Timer ;
+typedef	u_short	ResId ;
+typedef u_short	SMTEnum ;
+typedef	u_char	SMTFlag ;
+
+typedef struct {
+	Counter		count ;
+	TimeStamp	timestamp ;
+} SetCountType ;
+
+/*
+ * bits for bit string "available_path"
+ */
+#define MIB_PATH_P	(1<<0)
+#define MIB_PATH_S	(1<<1)
+#define MIB_PATH_L	(1<<2)
+
+/*
+ * bits for bit string PermittedPaths & RequestedPaths (SIZE(8))
+ */
+#define MIB_P_PATH_LOCAL	(1<<0)
+#define MIB_P_PATH_SEC_ALTER	(1<<1)
+#define MIB_P_PATH_PRIM_ALTER	(1<<2)
+#define MIB_P_PATH_CON_ALTER	(1<<3)
+#define MIB_P_PATH_SEC_PREFER	(1<<4)
+#define MIB_P_PATH_PRIM_PREFER	(1<<5)
+#define MIB_P_PATH_CON_PREFER	(1<<6)
+#define MIB_P_PATH_THRU		(1<<7)
+
+/*
+ * enum current path
+ */
+#define MIB_PATH_ISOLATED	0
+#define MIB_PATH_LOCAL		1
+#define MIB_PATH_SECONDARY	2
+#define MIB_PATH_PRIMARY	3
+#define MIB_PATH_CONCATENATED	4
+#define MIB_PATH_THRU		5
+
+/*
+ * enum PMDClass
+ */
+#define MIB_PMDCLASS_MULTI	0
+#define MIB_PMDCLASS_SINGLE1	1
+#define MIB_PMDCLASS_SINGLE2	2
+#define MIB_PMDCLASS_SONET	3
+#define MIB_PMDCLASS_LCF	4
+#define MIB_PMDCLASS_TP		5
+#define MIB_PMDCLASS_UNKNOWN	6
+#define MIB_PMDCLASS_UNSPEC	7
+
+/*
+ * enum SMTStationStatus
+ */
+#define MIB_SMT_STASTA_CON	0
+#define MIB_SMT_STASTA_SEPA	1
+#define MIB_SMT_STASTA_THRU	2
+
+
+struct fddi_mib {
+	/*
+	 * private
+	 */
+	u_char			fddiPRPMFPasswd[8] ;
+	struct smt_sid		fddiPRPMFStation ;
+
+#ifdef	ESS
+	/*
+	 * private variables for static allocation of the
+	 * End Station Support
+	 */
+	u_long	fddiESSPayload ;	/* payload for static alloc */
+	u_long	fddiESSOverhead ;	/* frame ov for static alloc */
+	u_long	fddiESSMaxTNeg ;	/* maximum of T-NEG */
+	u_long	fddiESSMinSegmentSize ;	/* min size of the sync frames */
+	u_long	fddiESSCategory ;	/* category for the Alloc req */
+	short	fddiESSSynchTxMode ;	/* send all LLC frames as sync */
+#endif	/* ESS */
+#ifdef	SBA
+	/*
+	 * private variables for the Synchronous Bandwidth Allocator
+	 */
+	char	fddiSBACommand ;	/* holds the parsed SBA cmd */
+	u_char	fddiSBAAvailable ;	/* SBA allocatable value */
+#endif	/* SBA */
+
+	/*
+	 * SMT standard mib
+	 */
+	struct smt_sid		fddiSMTStationId ;
+	u_short			fddiSMTOpVersionId ;
+	u_short			fddiSMTHiVersionId ;
+	u_short			fddiSMTLoVersionId ;
+	u_char			fddiSMTManufacturerData[32] ;
+	u_char			fddiSMTUserData[32] ;
+	u_short			fddiSMTMIBVersionId ;
+
+	/*
+	 * ConfigGrp
+	 */
+	u_char			fddiSMTMac_Ct ;
+	u_char			fddiSMTNonMaster_Ct ;
+	u_char			fddiSMTMaster_Ct ;
+	u_char			fddiSMTAvailablePaths ;
+	u_short			fddiSMTConfigCapabilities ;
+	u_short			fddiSMTConfigPolicy ;
+	u_short			fddiSMTConnectionPolicy ;
+	u_short			fddiSMTTT_Notify ;
+	u_char			fddiSMTStatRptPolicy ;
+	u_long			fddiSMTTrace_MaxExpiration ;
+	u_short			fddiSMTPORTIndexes[NUMPHYS] ;
+	u_short			fddiSMTMACIndexes ;
+	u_char			fddiSMTBypassPresent ;
+
+	/*
+	 * StatusGrp
+	 */
+	SMTEnum			fddiSMTECMState ;
+	SMTEnum			fddiSMTCF_State ;
+	SMTEnum			fddiSMTStationStatus ;
+	u_char			fddiSMTRemoteDisconnectFlag ;
+	u_char			fddiSMTPeerWrapFlag ;
+
+	/*
+	 * MIBOperationGrp
+	 */
+	TimeStamp		fddiSMTTimeStamp ;
+	TimeStamp		fddiSMTTransitionTimeStamp ;
+	SetCountType		fddiSMTSetCount ;
+	struct smt_sid		fddiSMTLastSetStationId ;
+
+	struct fddi_mib_m {
+		u_short		fddiMACFrameStatusFunctions ;
+		Timer_2		fddiMACT_MaxCapabilitiy ;
+		Timer_2		fddiMACTVXCapabilitiy ;
+
+		/* ConfigGrp */
+		u_char		fddiMACMultiple_N ;	/* private */
+		u_char		fddiMACMultiple_P ;	/* private */
+		u_char		fddiMACDuplicateAddressCond ;/* private */
+		u_char		fddiMACAvailablePaths ;
+		u_short		fddiMACCurrentPath ;
+		LongAddr	fddiMACUpstreamNbr ;
+		LongAddr	fddiMACDownstreamNbr ;
+		LongAddr	fddiMACOldUpstreamNbr ;
+		LongAddr	fddiMACOldDownstreamNbr ;
+		SMTEnum		fddiMACDupAddressTest ;
+		u_short		fddiMACRequestedPaths ;
+		SMTEnum		fddiMACDownstreamPORTType ;
+		ResId		fddiMACIndex ;
+
+		/* AddressGrp */
+		LongAddr	fddiMACSMTAddress ;
+
+		/* OperationGrp */
+		Timer_2		fddiMACT_Min ;	/* private */
+		Timer_2		fddiMACT_ReqMIB ;
+		Timer_2		fddiMACT_Req ;	/* private */
+		Timer_2		fddiMACT_Neg ;
+		Timer_2		fddiMACT_MaxMIB ;
+		Timer_2		fddiMACT_Max ;	/* private */
+		Timer_2		fddiMACTvxValueMIB ;
+		Timer_2		fddiMACTvxValue ; /* private */
+		Timer_2		fddiMACT_Pri0 ;
+		Timer_2		fddiMACT_Pri1 ;
+		Timer_2		fddiMACT_Pri2 ;
+		Timer_2		fddiMACT_Pri3 ;
+		Timer_2		fddiMACT_Pri4 ;
+		Timer_2		fddiMACT_Pri5 ;
+		Timer_2		fddiMACT_Pri6 ;
+
+		/* CountersGrp */
+		Counter		fddiMACFrame_Ct ;
+		Counter		fddiMACCopied_Ct ;
+		Counter		fddiMACTransmit_Ct ;
+		Counter		fddiMACToken_Ct ;
+		Counter		fddiMACError_Ct ;
+		Counter		fddiMACLost_Ct ;
+		Counter		fddiMACTvxExpired_Ct ;
+		Counter		fddiMACNotCopied_Ct ;
+		Counter		fddiMACRingOp_Ct ;
+
+		Counter		fddiMACSMTCopied_Ct ;		/* private */
+		Counter		fddiMACSMTTransmit_Ct ;		/* private */
+
+		/* private for delta ratio */
+		Counter		fddiMACOld_Frame_Ct ;
+		Counter		fddiMACOld_Copied_Ct ;
+		Counter		fddiMACOld_Error_Ct ;
+		Counter		fddiMACOld_Lost_Ct ;
+		Counter		fddiMACOld_NotCopied_Ct ;
+
+		/* FrameErrorConditionGrp */
+		u_short		fddiMACFrameErrorThreshold ;
+		u_short		fddiMACFrameErrorRatio ;
+
+		/* NotCopiedConditionGrp */
+		u_short		fddiMACNotCopiedThreshold ;
+		u_short		fddiMACNotCopiedRatio ;
+
+		/* StatusGrp */
+		SMTEnum		fddiMACRMTState ;
+		SMTFlag		fddiMACDA_Flag ;
+		SMTFlag		fddiMACUNDA_Flag ;
+		SMTFlag		fddiMACFrameErrorFlag ;
+		SMTFlag		fddiMACNotCopiedFlag ;
+		SMTFlag		fddiMACMA_UnitdataAvailable ;
+		SMTFlag		fddiMACHardwarePresent ;
+		SMTFlag		fddiMACMA_UnitdataEnable ;
+
+	} m[NUMMACS] ;
+#define MAC0	0
+
+	struct fddi_mib_a {
+		ResId		fddiPATHIndex ;
+		u_long		fddiPATHSbaPayload ;
+		u_long		fddiPATHSbaOverhead ;
+		/* fddiPATHConfiguration is built on demand */
+		/* u_long		fddiPATHConfiguration ; */
+		Timer		fddiPATHT_Rmode ;
+		u_long		fddiPATHSbaAvailable ;
+		Timer_2		fddiPATHTVXLowerBound ;
+		Timer_2		fddiPATHT_MaxLowerBound ;
+		Timer_2		fddiPATHMaxT_Req ;
+	} a[NUMPATHS] ;
+#define PATH0	0
+
+	struct fddi_mib_p {
+		/* ConfigGrp */
+		SMTEnum		fddiPORTMy_Type ;
+		SMTEnum		fddiPORTNeighborType ;
+		u_char		fddiPORTConnectionPolicies ;
+		struct {
+			u_char	T_val ;
+			u_char	R_val ;
+		} fddiPORTMacIndicated ;
+		SMTEnum		fddiPORTCurrentPath ;
+		/* must be 4: is 32 bit in SMT format
+		 * indices :
+		 *	1	none
+		 *	2	tree
+		 *	3	peer
+		 */
+		u_char		fddiPORTRequestedPaths[4] ;
+		u_short		fddiPORTMACPlacement ;
+		u_char		fddiPORTAvailablePaths ;
+		u_char		fddiPORTConnectionCapabilities ;
+		SMTEnum		fddiPORTPMDClass ;
+		ResId		fddiPORTIndex ;
+
+		/* OperationGrp */
+		SMTEnum		fddiPORTMaint_LS ;
+		SMTEnum		fddiPORTPC_LS ;
+		u_char		fddiPORTBS_Flag ;
+
+		/* ErrorCtrsGrp */
+		Counter		fddiPORTLCTFail_Ct ;
+		Counter		fddiPORTEBError_Ct ;
+		Counter		fddiPORTOldEBError_Ct ;
+
+		/* LerGrp */
+		Counter		fddiPORTLem_Reject_Ct ;
+		Counter		fddiPORTLem_Ct ;
+		u_char		fddiPORTLer_Estimate ;
+		u_char		fddiPORTLer_Cutoff ;
+		u_char		fddiPORTLer_Alarm ;
+
+		/* StatusGrp */
+		SMTEnum		fddiPORTConnectState ;
+		SMTEnum		fddiPORTPCMState ;	/* real value */
+		SMTEnum		fddiPORTPCMStateX ;	/* value for MIB */
+		SMTEnum		fddiPORTPC_Withhold ;
+		SMTFlag		fddiPORTHardwarePresent ;
+		u_char		fddiPORTLerFlag ;
+
+		u_char		fddiPORTMultiple_U ;	/* private */
+		u_char		fddiPORTMultiple_P ;	/* private */
+		u_char		fddiPORTEB_Condition ;	/* private */
+	} p[NUMPHYS] ;
+	struct {
+		Counter		fddiPRIVECF_Req_Rx ;	/* ECF req received */
+		Counter		fddiPRIVECF_Reply_Rx ;	/* ECF repl received */
+		Counter		fddiPRIVECF_Req_Tx ;	/* ECF req transm */
+		Counter		fddiPRIVECF_Reply_Tx ;	/* ECF repl transm */
+		Counter		fddiPRIVPMF_Get_Rx ;	/* PMF Get rec */
+		Counter		fddiPRIVPMF_Set_Rx ;	/* PMF Set rec */
+		Counter		fddiPRIVRDF_Rx ;	/* RDF received */
+		Counter		fddiPRIVRDF_Tx ;	/* RDF transmitted */
+	} priv ;
+} ;
+
+/*
+ * OIDs for statistics
+ */
+#define	SMT_OID_CF_STATE	1	/* fddiSMTCF_State */
+#define	SMT_OID_PCM_STATE_A	2	/* fddiPORTPCMState port A */
+#define	SMT_OID_PCM_STATE_B	17	/* fddiPORTPCMState port B */
+#define	SMT_OID_RMT_STATE	3	/* fddiMACRMTState */
+#define	SMT_OID_UNA		4	/* fddiMACUpstreamNbr */
+#define	SMT_OID_DNA		5	/* fddiMACOldDownstreamNbr */
+#define	SMT_OID_ERROR_CT	6	/* fddiMACError_Ct */
+#define	SMT_OID_LOST_CT		7	/* fddiMACLost_Ct */
+#define	SMT_OID_LEM_CT		8	/* fddiPORTLem_Ct */
+#define	SMT_OID_LEM_CT_A	11	/* fddiPORTLem_Ct port A */
+#define	SMT_OID_LEM_CT_B	12	/* fddiPORTLem_Ct port B */
+#define	SMT_OID_LCT_FAIL_CT	9	/* fddiPORTLCTFail_Ct */
+#define	SMT_OID_LCT_FAIL_CT_A	13	/* fddiPORTLCTFail_Ct port A */
+#define	SMT_OID_LCT_FAIL_CT_B	14	/* fddiPORTLCTFail_Ct port B */
+#define	SMT_OID_LEM_REJECT_CT	10	/* fddiPORTLem_Reject_Ct */
+#define	SMT_OID_LEM_REJECT_CT_A	15	/* fddiPORTLem_Reject_Ct port A */
+#define	SMT_OID_LEM_REJECT_CT_B	16	/* fddiPORTLem_Reject_Ct port B */
+
+/*
+ * SK MIB
+ */
+#define SMT_OID_ECF_REQ_RX	20	/* ECF requests received */
+#define SMT_OID_ECF_REPLY_RX	21	/* ECF replies received */
+#define SMT_OID_ECF_REQ_TX	22	/* ECF requests transmitted */
+#define SMT_OID_ECF_REPLY_TX	23	/* ECF replies transmitted */
+#define SMT_OID_PMF_GET_RX	24	/* PMF get requests received */
+#define SMT_OID_PMF_SET_RX	25	/* PMF set requests received */
+#define SMT_OID_RDF_RX		26	/* RDF received */
+#define SMT_OID_RDF_TX		27	/* RDF transmitted */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/fplustm.h linux.18p17/drivers/net/skfp/h/fplustm.h
--- linux.vanilla/drivers/net/skfp/h/fplustm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/fplustm.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,278 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ *	AMD Fplus in tag mode data structs
+ *	defs for fplustm.c
+ */
+
+#ifndef	_FPLUS_
+#define _FPLUS_
+
+#ifndef	HW_PTR
+#ifdef	MEM_MAPPED_IO
+#define	HW_PTR	u_long
+#else
+#define	HW_PTR	u_short
+#endif
+#endif
+
+/*
+ * fplus error statistic structure
+ */
+struct err_st {
+	u_long err_valid ;		/* memory status valid */
+	u_long err_abort ;		/* memory status receive abort */
+	u_long err_e_indicator ;	/* error indicator */
+	u_long err_crc ;		/* error detected (CRC or length) */
+	u_long err_llc_frame ;		/* LLC frame */
+	u_long err_mac_frame ;		/* MAC frame */
+	u_long err_smt_frame ;		/* SMT frame */
+	u_long err_imp_frame ;		/* implementer frame */
+	u_long err_no_buf ;		/* no buffer available */
+	u_long err_too_long ;		/* longer than max. buffer */
+	u_long err_bec_stat ;		/* beacon state entered */
+	u_long err_clm_stat ;		/* claim state entered */
+	u_long err_sifg_det ;		/* short interframe gap detect */
+	u_long err_phinv ;		/* PHY invalid */
+	u_long err_tkiss ;		/* token issued */
+	u_long err_tkerr ;		/* token error */
+} ;
+
+/*
+ *	Transmit Descriptor struct
+ */
+struct s_smt_fp_txd {
+	u_int txd_tbctrl ;		/* transmit buffer control */
+	u_int txd_txdscr ;		/* transmit frame status word */
+	u_int txd_tbadr ;		/* physical tx buffer address */
+	u_int txd_ntdadr ;		/* physical pointer to the next TxD */
+#ifdef	ENA_64BIT_SUP
+	u_int txd_tbadr_hi ;		/* physical tx buffer addr (high dword)*/
+#endif
+	char far *txd_virt ;		/* virtual pointer to the data frag */
+					/* virt pointer to the next TxD */
+	struct s_smt_fp_txd volatile far *txd_next ;
+	struct s_txd_os txd_os ;	/* OS - specific struct */
+} ;
+
+/*
+ *	Receive Descriptor struct
+ */
+struct s_smt_fp_rxd {
+	u_int rxd_rbctrl ;		/* receive buffer control */
+	u_int rxd_rfsw ;		/* receive frame status word */
+	u_int rxd_rbadr ;		/* physical rx buffer address */
+	u_int rxd_nrdadr ;		/* physical pointer to the next RxD */
+#ifdef	ENA_64BIT_SUP
+	u_int rxd_rbadr_hi ;		/* physical tx buffer addr (high dword)*/
+#endif
+	char far *rxd_virt ;		/* virtual pointer to the data frag */
+					/* virt pointer to the next RxD */
+	struct s_smt_fp_rxd volatile far *rxd_next ;
+	struct s_rxd_os rxd_os ;	/* OS - specific struct */
+} ;
+
+/*
+ *	Descriptor Union Definition
+ */
+union s_fp_descr {
+	struct	s_smt_fp_txd t ;		/* pointer to the TxD */
+	struct	s_smt_fp_rxd r ;		/* pointer to the RxD */
+} ;
+
+/*
+ *	TxD Ring Control struct
+ */
+struct s_smt_tx_queue {
+	struct s_smt_fp_txd volatile *tx_curr_put ; /* next free TxD */
+	struct s_smt_fp_txd volatile *tx_prev_put ; /* shadow put pointer */
+	struct s_smt_fp_txd volatile *tx_curr_get ; /* next TxD to release*/
+	u_short tx_free ;			/* count of free TxD's */
+	u_short tx_used ;			/* count of used TxD's */
+	HW_PTR tx_bmu_ctl ;			/* BMU addr for tx start */
+	HW_PTR tx_bmu_dsc ;			/* BMU addr for curr dsc. */
+} ;
+
+/*
+ *	RxD Ring Control struct
+ */
+struct s_smt_rx_queue {
+	struct s_smt_fp_rxd volatile *rx_curr_put ; /* next RxD to queue into */
+	struct s_smt_fp_rxd volatile *rx_prev_put ; /* shadow put pointer */
+	struct s_smt_fp_rxd volatile *rx_curr_get ; /* next RxD to fill */
+	u_short rx_free ;			/* count of free RxD's */
+	u_short rx_used ;			/* count of used RxD's */
+	HW_PTR rx_bmu_ctl ;			/* BMU addr for rx start */
+	HW_PTR rx_bmu_dsc ;			/* BMU addr for curr dsc. */
+} ;
+
+#define VOID_FRAME_OFF		0x00
+#define CLAIM_FRAME_OFF		0x08
+#define BEACON_FRAME_OFF	0x10
+#define DBEACON_FRAME_OFF	0x18
+#define RX_FIFO_OFF		0x21		/* to get a prime number for */
+						/* the RX_FIFO_SPACE */
+
+#define RBC_MEM_SIZE		0x8000
+#define SEND_ASYNC_AS_SYNC	0x1
+#define	SYNC_TRAFFIC_ON		0x2
+
+/* big FIFO memory */
+#define	RX_FIFO_SPACE		0x4000 - RX_FIFO_OFF
+#define	TX_FIFO_SPACE		0x4000
+
+#define	TX_SMALL_FIFO		0x0900
+#define	TX_MEDIUM_FIFO		TX_FIFO_SPACE / 2	
+#define	TX_LARGE_FIFO		TX_FIFO_SPACE - TX_SMALL_FIFO	
+
+#define	RX_SMALL_FIFO		0x0900
+#define	RX_LARGE_FIFO		RX_FIFO_SPACE - RX_SMALL_FIFO	
+
+struct s_smt_fifo_conf {
+	u_short	rbc_ram_start ;		/* FIFO start address */
+	u_short	rbc_ram_end ;		/* FIFO size */
+	u_short	rx1_fifo_start ;	/* rx queue start address */
+	u_short	rx1_fifo_size ;		/* rx queue size */
+	u_short	rx2_fifo_start ;	/* rx queue start address */
+	u_short	rx2_fifo_size ;		/* rx queue size */
+	u_short	tx_s_start ;		/* sync queue start address */
+	u_short	tx_s_size ;		/* sync queue size */
+	u_short	tx_a0_start ;		/* async queue A0 start address */
+	u_short	tx_a0_size ;		/* async queue A0 size */
+	u_short	fifo_config_mode ;	/* FIFO configuration mode */
+} ;
+
+#define FM_ADDRX	(FM_ADDET|FM_EXGPA0|FM_EXGPA1)
+
+struct s_smt_fp {
+	u_short	mdr2init ;		/* mode register 2 init value */
+	u_short	mdr3init ;		/* mode register 3 init value */
+	u_short frselreg_init ;		/* frame selection register init val */
+	u_short	rx_mode ;		/* address mode broad/multi/promisc */
+	u_short	nsa_mode ;
+	u_short rx_prom ;
+	u_short	exgpa ;
+
+	struct err_st err_stats ;	/* error statistics */
+
+	/*
+	 * MAC buffers
+	 */
+	struct fddi_mac_sf {		/* special frame build buffer */
+		u_char			mac_fc ;
+		struct fddi_addr	mac_dest ;
+		struct fddi_addr	mac_source ;
+		u_char			mac_info[0x20] ;
+	} mac_sfb ;
+
+
+	/*
+	 * queues
+	 */
+#define QUEUE_S			0
+#define QUEUE_A0		1
+#define QUEUE_R1		0
+#define QUEUE_R2		1
+#define USED_QUEUES		2
+
+	/*
+	 * queue pointers; points to the queue dependent variables
+	 */
+	struct s_smt_tx_queue *tx[USED_QUEUES] ;
+	struct s_smt_rx_queue *rx[USED_QUEUES] ;
+
+	/*
+	 * queue dependent variables
+	 */
+	struct s_smt_tx_queue tx_q[USED_QUEUES] ;
+	struct s_smt_rx_queue rx_q[USED_QUEUES] ;
+
+	/*
+	 * FIFO configuration struct
+	 */
+	struct	s_smt_fifo_conf	fifo ;
+
+	/* last formac status */
+	u_short	 s2u ;
+	u_short	 s2l ;
+
+	/* calculated FORMAC+ reg.addr. */
+	HW_PTR	fm_st1u ;
+	HW_PTR	fm_st1l ;
+	HW_PTR	fm_st2u ;
+	HW_PTR	fm_st2l ;
+	HW_PTR	fm_st3u ;
+	HW_PTR	fm_st3l ;
+
+
+	/*
+	 * multicast table
+	 */
+#define FPMAX_MULTICAST 32 
+#define	SMT_MAX_MULTI	4
+	struct {
+		struct s_fpmc {
+			struct fddi_addr	a ;	/* mc address */
+			u_char			n ;	/* usage counter */
+			u_char			perm ;	/* flag: permanent */
+		} table[FPMAX_MULTICAST] ;
+	} mc ;
+	struct fddi_addr	group_addr ;
+	u_long	func_addr ;		/* functional address */
+	int	smt_slots_used ;	/* count of table entries for the SMT */
+	int	os_slots_used ;		/* count of table entries */ 
+					/* used by the os-specific module */
+} ;
+
+/*
+ * modes for mac_set_rx_mode()
+ */
+#define RX_ENABLE_ALLMULTI	1	/* enable all multicasts */
+#define RX_DISABLE_ALLMULTI	2	/* disable "enable all multicasts" */
+#define RX_ENABLE_PROMISC	3	/* enable promiscous */
+#define RX_DISABLE_PROMISC	4	/* disable promiscous */
+#define RX_ENABLE_NSA		5	/* enable reception of NSA frames */
+#define RX_DISABLE_NSA		6	/* disable reception of NSA frames */
+
+
+/*
+ * support for byte reversal in AIX
+ * (descriptors and pointers must be byte reversed in memory
+ *  CPU is big endian; M-Channel is little endian)
+ */
+#ifdef	AIX
+#define MDR_REV
+#define	AIX_REVERSE(x)		((((x)<<24L)&0xff000000L)	+	\
+				 (((x)<< 8L)&0x00ff0000L)	+	\
+				 (((x)>> 8L)&0x0000ff00L)	+	\
+				 (((x)>>24L)&0x000000ffL))
+#else
+#ifndef AIX_REVERSE
+#define	AIX_REVERSE(x)	(x)
+#endif
+#endif
+
+#ifdef	MDR_REV	
+#define	MDR_REVERSE(x)		((((x)<<24L)&0xff000000L)	+	\
+				 (((x)<< 8L)&0x00ff0000L)	+	\
+				 (((x)>> 8L)&0x0000ff00L)	+	\
+				 (((x)>>24L)&0x000000ffL))
+#else
+#ifndef MDR_REVERSE
+#define	MDR_REVERSE(x)	(x)
+#endif
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/hwmtm.h linux.18p17/drivers/net/skfp/h/hwmtm.h
--- linux.vanilla/drivers/net/skfp/h/hwmtm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/hwmtm.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,424 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_HWM_
+#define	_HWM_
+
+#include "h/mbuf.h"
+
+/*
+ * MACRO for DMA synchronization:
+ *	The descriptor 'desc' is flushed for the device 'flag'.
+ *	Devices are the CPU (DDI_DMA_SYNC_FORCPU) and the
+ *	adapter (DDI_DMA_SYNC_FORDEV).
+ *
+ *	'desc'	Pointer to a Rx or Tx descriptor.
+ *	'flag'	Flag for direction (view for CPU or DEVICE) that
+ *		should be synchronized.
+ *
+ *	Empty macros and defines are specified here. The real macro
+ *	is os-specific and should be defined in osdef1st.h.
+ */
+#ifndef DRV_BUF_FLUSH
+#define DRV_BUF_FLUSH(desc,flag)
+#define DDI_DMA_SYNC_FORCPU
+#define DDI_DMA_SYNC_FORDEV
+#endif
+
+	/*
+	 * hardware modul dependent receive modes
+	 */
+#define	RX_ENABLE_PASS_SMT	21
+#define	RX_DISABLE_PASS_SMT	22
+#define	RX_ENABLE_PASS_NSA	23
+#define	RX_DISABLE_PASS_NSA	24
+#define	RX_ENABLE_PASS_DB	25
+#define	RX_DISABLE_PASS_DB	26
+#define	RX_DISABLE_PASS_ALL	27
+#define	RX_DISABLE_LLC_PROMISC	28
+#define	RX_ENABLE_LLC_PROMISC	29
+
+
+#ifndef	DMA_RD
+#define DMA_RD		1	/* memory -> hw */
+#endif
+#ifndef DMA_WR
+#define DMA_WR		2	/* hw -> memory */
+#endif
+#define SMT_BUF		0x80
+
+	/*
+	 * bits of the frame status byte
+	 */
+#define EN_IRQ_EOF	0x02	/* get IRQ after end of frame transmission */
+#define	LOC_TX		0x04	/* send frame to the local SMT */
+#define LAST_FRAG	0x08	/* last TxD of the frame */
+#define	FIRST_FRAG	0x10	/* first TxD of the frame */
+#define	LAN_TX		0x20	/* send frame to network if set */
+#define RING_DOWN	0x40	/* error: unable to send, ring down */
+#define OUT_OF_TXD	0x80	/* error: not enough TxDs available */
+
+
+#ifndef NULL
+#define NULL 		0
+#endif
+
+#ifdef	LITTLE_ENDIAN
+#define HWM_REVERSE(x)	(x)
+#else
+#define	HWM_REVERSE(x)		((((x)<<24L)&0xff000000L)	+	\
+				 (((x)<< 8L)&0x00ff0000L)	+	\
+				 (((x)>> 8L)&0x0000ff00L)	+	\
+				 (((x)>>24L)&0x000000ffL))
+#endif
+
+#define C_INDIC		(1L<<25)
+#define A_INDIC		(1L<<26)
+#define	RD_FS_LOCAL	0x80
+
+	/*
+	 * DEBUG FLAGS
+	 */
+#define	DEBUG_SMTF	1
+#define	DEBUG_SMT	2
+#define	DEBUG_ECM	3
+#define	DEBUG_RMT	4
+#define	DEBUG_CFM	5
+#define	DEBUG_PCM	6
+#define	DEBUG_SBA	7
+#define	DEBUG_ESS	8
+
+#define	DB_HWM_RX	10
+#define	DB_HWM_TX	11
+#define DB_HWM_GEN	12
+
+struct s_mbuf_pool {
+#ifndef	MB_OUTSIDE_SMC
+	SMbuf		mb[MAX_MBUF] ;		/* mbuf pool */
+#endif
+	SMbuf		*mb_start ;		/* points to the first mb */
+	SMbuf		*mb_free ;		/* free queue */
+} ;
+
+struct hwm_r {
+	/*
+	 * hardware modul specific receive variables
+	 */
+	u_int			len ;		/* length of the whole frame */
+	char			*mb_pos ;	/* SMbuf receive position */
+} ;
+
+struct hw_modul {
+	/*
+	 * All hardware modul specific variables
+	 */
+	struct	s_mbuf_pool	mbuf_pool ;
+	struct	hwm_r	r ;
+
+	union s_fp_descr volatile *descr_p ; /* points to the desriptor area */
+
+	u_short pass_SMT ;		/* pass SMT frames */
+	u_short pass_NSA ;		/* pass all NSA frames */
+	u_short pass_DB ;		/* pass Direct Beacon Frames */
+	u_short pass_llc_promisc ;	/* pass all llc frames (default ON) */
+
+	SMbuf	*llc_rx_pipe ;		/* points to the first queued llc fr */
+	SMbuf	*llc_rx_tail ;		/* points to the last queued llc fr */
+	int	queued_rx_frames ;	/* number of queued frames */
+
+	SMbuf	*txd_tx_pipe ;		/* points to first mb in the txd ring */
+	SMbuf	*txd_tx_tail ;		/* points to last mb in the txd ring */
+	int	queued_txd_mb ;		/* number of SMT MBufs in txd ring */
+
+	int	rx_break ;		/* rev. was breaked because ind. off */
+	int	leave_isr ;		/* leave fddi_isr immedeately if set */
+	int	isr_flag ;		/* set, when HWM is entered from isr */
+	/*
+	 * varaibles for the current transmit frame
+	 */
+	struct s_smt_tx_queue *tx_p ;	/* pointer to the transmit queue */
+	u_long	tx_descr ;		/* tx descriptor for FORMAC+ */
+	int	tx_len ;		/* tx frame length */
+	SMbuf	*tx_mb ;		/* SMT tx MBuf pointer */
+	char	*tx_data ;		/* data pointer to the SMT tx Mbuf */
+
+	int	detec_count ;		/* counter for out of RxD condition */
+	u_long	rx_len_error ;		/* rx len FORMAC != sum of fragments */
+} ;
+
+
+/*
+ * DEBUG structs and macros
+ */
+
+#ifdef	DEBUG
+struct os_debug {
+	int	hwm_rx ;
+	int	hwm_tx ;
+	int	hwm_gen ;
+} ;
+#endif
+
+#ifdef	DEBUG
+#ifdef	DEBUG_BRD
+#define	DB_P	smc->debug
+#else
+#define DB_P	debug
+#endif
+
+#define DB_RX(a,b,c,lev) if (DB_P.d_os.hwm_rx >= (lev))	printf(a,b,c)
+#define DB_TX(a,b,c,lev) if (DB_P.d_os.hwm_tx >= (lev))	printf(a,b,c)
+#define DB_GEN(a,b,c,lev) if (DB_P.d_os.hwm_gen >= (lev)) printf(a,b,c)
+#else	/* DEBUG */
+#define DB_RX(a,b,c,lev)
+#define DB_TX(a,b,c,lev)
+#define DB_GEN(a,b,c,lev)
+#endif	/* DEBUG */
+
+#ifndef	SK_BREAK
+#define	SK_BREAK()
+#endif
+
+
+/*
+ * HWM Macros
+ */
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_TX_PHYS)
+ *	u_long HWM_GET_TX_PHYS(txd)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to read
+ *		the physical address of the specified TxD.
+ *
+ * para	txd	pointer to the TxD
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_TX_PHYS(txd)		(u_long)AIX_REVERSE((txd)->txd_tbadr)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_TX_LEN)
+ *	int HWM_GET_TX_LEN(txd)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to read
+ *		the fragment length of the specified TxD
+ *
+ * para	rxd	pointer to the TxD
+ *
+ * return	the length of the fragment in bytes
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_TX_LEN(txd)	((int)AIX_REVERSE((txd)->txd_tbctrl)& RD_LENGTH)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_TX_USED)
+ *	txd *HWM_GET_TX_USED(smc,queue)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to get the
+ *		number of used TxDs for the queue, specified by the index.
+ *
+ * para	queue	the number of the send queue: Can be specified by
+ *		QUEUE_A0, QUEUE_S or (frame_status & QUEUE_A0)
+ *
+ * return	number of used TxDs for this send queue
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_TX_USED(smc,queue)	(int) (smc)->hw.fp.tx_q[queue].tx_used
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_CURR_TXD)
+ *	txd *HWM_GET_CURR_TXD(smc,queue)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to get the
+ *		pointer to the TxD which points to the current queue put
+ *		position.
+ *
+ * para	queue	the number of the send queue: Can be specified by
+ *		QUEUE_A0, QUEUE_S or (frame_status & QUEUE_A0)
+ *
+ * return	pointer to the current TxD
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_CURR_TXD(smc,queue)	(struct s_smt_fp_txd volatile *)\
+					(smc)->hw.fp.tx_q[queue].tx_curr_put
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_TX_CHECK)
+ *	void HWM_TX_CHECK(smc,frame_status,low_water)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro is invoked by the OS-specific before it left it's
+ *		driver_send function. This macro calls mac_drv_clear_txd
+ *		if the free TxDs of the current transmit queue is equal or
+ *		lower than the given low water mark.
+ *
+ * para	frame_status	status of the frame, see design description
+ *	low_water	low water mark of free TxD's
+ *
+ *	END_MANUAL_ENTRY
+ */
+#ifndef HWM_NO_FLOW_CTL
+#define	HWM_TX_CHECK(smc,frame_status,low_water) {\
+	if ((low_water)>=(smc)->hw.fp.tx_q[(frame_status)&QUEUE_A0].tx_free) {\
+		mac_drv_clear_txd(smc) ;\
+	}\
+}
+#else
+#define	HWM_TX_CHECK(smc,frame_status,low_water)	mac_drv_clear_txd(smc)
+#endif
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_RX_FRAG_LEN)
+ *	int HWM_GET_RX_FRAG_LEN(rxd)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to read
+ *		the fragment length of the specified RxD
+ *
+ * para	rxd	pointer to the RxD
+ *
+ * return	the length of the fragment in bytes
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_RX_FRAG_LEN(rxd)	((int)AIX_REVERSE((rxd)->rxd_rbctrl)& \
+				RD_LENGTH)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_RX_PHYS)
+ *	u_long HWM_GET_RX_PHYS(rxd)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to read
+ *		the physical address of the specified RxD.
+ *
+ * para	rxd	pointer to the RxD
+ *
+ * return	the RxD's physical pointer to the data fragment
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_RX_PHYS(rxd)	(u_long)AIX_REVERSE((rxd)->rxd_rbadr)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_RX_USED)
+ *	int HWM_GET_RX_USED(smc)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to get
+ *		the count of used RXDs in receive queue 1.
+ *
+ * return	the used RXD count of receive queue 1
+ *
+ * NOTE: Remember, because of an ASIC bug at least one RXD should be unused
+ *	 in the descriptor ring !
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_RX_USED(smc)	((int)(smc)->hw.fp.rx_q[QUEUE_R1].rx_used)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_RX_FREE)
+ *	int HWM_GET_RX_FREE(smc)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to get
+ *		the rxd_free count of receive queue 1.
+ *
+ * return	the rxd_free count of receive queue 1
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_RX_FREE(smc)	((int)(smc)->hw.fp.rx_q[QUEUE_R1].rx_free-1)
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_GET_CURR_RXD)
+ *	rxd *HWM_GET_CURR_RXD(smc)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro may be invoked by the OS-specific module to get the
+ *		pointer to the RxD which points to the current queue put
+ *		position.
+ *
+ * return	pointer to the current RxD
+ *
+ *	END_MANUAL_ENTRY
+ */
+#define	HWM_GET_CURR_RXD(smc)	(struct s_smt_fp_rxd volatile *)\
+				(smc)->hw.fp.rx_q[QUEUE_R1].rx_curr_put
+
+/*
+ *	BEGIN_MANUAL_ENTRY(HWM_RX_CHECK)
+ *	void HWM_RX_CHECK(smc,low_water)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This macro is invoked by the OS-specific before it left the
+ *		function mac_drv_rx_complete. This macro calls mac_drv_fill_rxd
+ *		if the number of used RxDs is equal or lower than the
+ *		the given low water mark.
+ *
+ * para	low_water	low water mark of used RxD's
+ *
+ *	END_MANUAL_ENTRY
+ */
+#ifndef HWM_NO_FLOW_CTL
+#define	HWM_RX_CHECK(smc,low_water) {\
+	if ((low_water) >= (smc)->hw.fp.rx_q[QUEUE_R1].rx_used) {\
+		mac_drv_fill_rxd(smc) ;\
+	}\
+}
+#else
+#define	HWM_RX_CHECK(smc,low_water)		mac_drv_fill_rxd(smc)
+#endif
+
+#ifndef	HWM_EBASE
+#define	HWM_EBASE	500
+#endif
+
+#define	HWM_E0001	HWM_EBASE + 1
+#define	HWM_E0001_MSG	"HWM: Wrong size of s_rxd_os struct"
+#define	HWM_E0002	HWM_EBASE + 2
+#define	HWM_E0002_MSG	"HWM: Wrong size of s_txd_os struct"
+#define	HWM_E0003	HWM_EBASE + 3
+#define	HWM_E0003_MSG	"HWM: smt_free_mbuf() called with NULL pointer"
+#define	HWM_E0004	HWM_EBASE + 4
+#define	HWM_E0004_MSG	"HWM: Parity error rx queue 1"
+#define	HWM_E0005	HWM_EBASE + 5
+#define	HWM_E0005_MSG	"HWM: Encoding error rx queue 1"
+#define	HWM_E0006	HWM_EBASE + 6
+#define	HWM_E0006_MSG	"HWM: Encoding error async tx queue"
+#define	HWM_E0007	HWM_EBASE + 7
+#define	HWM_E0007_MSG	"HWM: Encoding error sync tx queue"
+#define	HWM_E0008	HWM_EBASE + 8
+#define	HWM_E0008_MSG	""
+#define	HWM_E0009	HWM_EBASE + 9
+#define	HWM_E0009_MSG	"HWM: Out of RxD condition detected"
+#define	HWM_E0010	HWM_EBASE + 10
+#define	HWM_E0010_MSG	"HWM: A protocol layer has tried to send a frame with an invalid frame control"
+#define HWM_E0011	HWM_EBASE + 11
+#define HWM_E0011_MSG	"HWM: mac_drv_clear_tx_queue was called although the hardware wasn't stopped"
+#define HWM_E0012	HWM_EBASE + 12
+#define HWM_E0012_MSG	"HWM: mac_drv_clear_rx_queue was called although the hardware wasn't stopped"
+#define HWM_E0013	HWM_EBASE + 13
+#define HWM_E0013_MSG	"HWM: mac_drv_repair_descr was called although the hardware wasn't stopped"
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/lnkstat.h linux.18p17/drivers/net/skfp/h/lnkstat.h
--- linux.vanilla/drivers/net/skfp/h/lnkstat.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/lnkstat.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * Definition of the Error Log Structure
+ * This structure will be copied into the Error Log buffer
+ * during the NDIS General Request ReadErrorLog by the MAC Driver
+ */
+
+struct	s_error_log {
+
+	/*
+	 * place holder for token ring adapter error log (zeros)
+	 */
+	u_char	reserved_0 ;			/* byte 0 inside Error Log */
+	u_char	reserved_1 ;			/* byte 1 */
+	u_char	reserved_2 ;			/* byte 2 */	
+	u_char	reserved_3 ;			/* byte 3 */
+	u_char	reserved_4 ;			/* byte 4 */
+	u_char	reserved_5 ;			/* byte 5 */
+	u_char	reserved_6 ;			/* byte 6 */
+	u_char	reserved_7 ;			/* byte 7 */
+	u_char	reserved_8 ;			/* byte 8 */
+	u_char	reserved_9 ;			/* byte 9 */
+	u_char	reserved_10 ;			/* byte 10 */
+	u_char	reserved_11 ;			/* byte 11 */
+	u_char	reserved_12 ;			/* byte 12 */
+	u_char	reserved_13 ;			/* byte 13 */
+
+	/*
+	 * FDDI link statistics 
+	 */
+/*
+ * smt error low
+ */
+#define SMT_ERL_AEB	(1<<15)			/* A elast. buffer */
+#define SMT_ERL_BLC	(1<<14)			/* B link error condition */
+#define SMT_ERL_ALC	(1<<13)			/* A link error condition */
+#define SMT_ERL_NCC	(1<<12)			/* not copied condition */
+#define SMT_ERL_FEC	(1<<11)			/* frame error condition */
+
+/*
+ * smt event low
+ */
+#define SMT_EVL_NCE	(1<<5)
+
+	u_short	smt_error_low ;			/* byte 14/15 */
+	u_short	smt_error_high ;		/* byte 16/17 */
+	u_short	smt_event_low ;			/* byte 18/19 */
+	u_short	smt_event_high ;		/* byte 20/21 */
+	u_short	connection_policy_violation ;	/* byte 22/23 */
+	u_short port_event ;			/* byte 24/25 */
+	u_short set_count_low ;			/* byte 26/27 */
+	u_short set_count_high ;		/* byte 28/29 */
+	u_short	aci_id_code ;			/* byte 30/31 */
+	u_short	purge_frame_counter ;		/* byte 32/33 */
+
+	/*
+	 * CMT and RMT state machines
+	 */
+	u_short	ecm_state ;			/* byte 34/35 */
+	u_short	pcm_a_state ;			/* byte 36/37 */
+	u_short pcm_b_state ;			/* byte 38/39 */
+	u_short	cfm_state ;			/* byte 40/41 */
+	u_short	rmt_state ;			/* byte 42/43 */
+
+	u_short	not_used[30] ;			/* byte 44-103 */
+
+	u_short	ucode_version_level ;		/* byte 104/105 */
+
+	u_short	not_used_1 ;			/* byte 106/107 */
+	u_short not_used_2 ;			/* byte 108/109 */
+} ;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/mbuf.h linux.18p17/drivers/net/skfp/h/mbuf.h
--- linux.vanilla/drivers/net/skfp/h/mbuf.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/mbuf.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_MBUF_
+#define _MBUF_
+
+#ifndef PCI
+#define M_SIZE	4550
+#else
+#define M_SIZE	4504
+#endif
+
+#ifndef MAX_MBUF
+#define MAX_MBUF	4
+#endif
+
+#ifndef NO_STD_MBUF
+#define sm_next         m_next
+#define sm_off          m_off
+#define sm_len          m_len
+#define sm_data         m_data
+#define SMbuf           Mbuf
+#define mtod		smtod
+#define mtodoff		smtodoff
+#endif
+
+struct s_mbuf {
+	struct s_mbuf	*sm_next ;		/* low level linked list */
+	short		sm_off ;			/* offset in m_data */
+	u_int		sm_len ;			/* len of data */
+#ifdef	PCI
+	int		sm_use_count ;
+#endif
+	char		sm_data[M_SIZE] ;
+} ;
+
+typedef struct s_mbuf SMbuf ;
+
+/* mbuf head, to typed data */
+#define	smtod(x,t)	((t)((x)->sm_data + (x)->sm_off))
+#define	smtodoff(x,t,o)	((t)((x)->sm_data + (o)))
+
+#endif	/* _MBUF_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/osdef1st.h linux.18p17/drivers/net/skfp/h/osdef1st.h
--- linux.vanilla/drivers/net/skfp/h/osdef1st.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/osdef1st.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,121 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/* 
+ * Operating system-dependant definitions that have to be defined
+ * before any other header files are included.
+ */
+
+// HWM (HardWare Module) Definitions
+// -----------------------
+
+#ifdef __LITTLE_ENDIAN
+#define LITTLE_ENDIAN
+#else
+#define BIG_ENDIAN
+#endif
+
+// this is set in the makefile
+// #define PCI			/* only PCI adapters supported by this driver */
+// #define MEM_MAPPED_IO	/* use memory mapped I/O */
+
+
+#define USE_CAN_ADDR		/* DA and SA in MAC header are canonical. */
+
+#define MB_OUTSIDE_SMC		/* SMT Mbufs outside of smc struct. */
+
+// -----------------------
+
+
+// SMT Definitions 
+// -----------------------
+#define SYNC	       		/* allow synchronous frames */
+
+// #define SBA			/* Synchronous Bandwidth Allocator support */
+				/* not available as free source */
+
+#define ESS			/* SBA End Station Support */
+
+#define	SMT_PANIC(smc, nr, msg)	printk(KERN_INFO "SMT PANIC: code: %d, msg: %s\n",nr,msg)
+
+
+#ifdef DEBUG
+#define printf(s,args...) printk(KERN_INFO s, ## args)
+#endif
+
+// #define HW_PTR	u_long
+// -----------------------
+
+
+
+// HWM and OS-specific buffer definitions
+// -----------------------
+
+// default number of receive buffers.
+#define NUM_RECEIVE_BUFFERS		10
+
+// default number of transmit buffers.
+#define NUM_TRANSMIT_BUFFERS		10
+
+// Number of SMT buffers (Mbufs).
+#define NUM_SMT_BUF	4
+
+// Number of TXDs for asynchronous transmit queue.
+#define HWM_ASYNC_TXD_COUNT	(NUM_TRANSMIT_BUFFERS + NUM_SMT_BUF)
+
+// Number of TXDs for synchronous transmit queue.
+#define HWM_SYNC_TXD_COUNT	HWM_ASYNC_TXD_COUNT
+
+
+// Number of RXDs for receive queue #1.
+// Note: Workaround for ASIC Errata #7: One extra RXD is required.
+#if (NUM_RECEIVE_BUFFERS > 100)
+#define SMT_R1_RXD_COUNT	(1 + 100)
+#else
+#define SMT_R1_RXD_COUNT	(1 + NUM_RECEIVE_BUFFERS)
+#endif
+
+// Number of RXDs for receive queue #2.
+#define SMT_R2_RXD_COUNT	0	// Not used.
+// -----------------------
+
+
+
+/*
+ * OS-specific part of the transmit/receive descriptor structure (TXD/RXD).
+ *
+ * Note: The size of these structures must follow this rule:
+ *
+ *	sizeof(struct) + 2*sizeof(void*) == n * 16, n >= 1
+ *
+ */
+
+struct s_txd_os {	// os-specific part of transmit descriptor
+	struct sk_buff *skb;
+	long		padding;
+} ;
+
+struct s_rxd_os {	// os-specific part of receive descriptor
+	struct sk_buff *skb;
+	long		padding;
+} ;
+
+
+/*
+ * So we do not need to make too many modifications to the generic driver
+ * parts, we take advantage of the AIX byte swapping macro interface.
+ */
+
+#define AIX_REVERSE(x)		((u32)le32_to_cpu((u32)(x)))
+#define MDR_REVERSE(x)		((u32)le32_to_cpu((u32)(x)))
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/sba.h linux.18p17/drivers/net/skfp/h/sba.h
--- linux.vanilla/drivers/net/skfp/h/sba.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/sba.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,142 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * Synchronous Bandwith Allocation (SBA) structs
+ */
+ 
+#ifndef _SBA_
+#define _SBA_
+
+#include "h/mbuf.h"
+#include "h/sba_def.h"
+
+#ifdef	SBA
+
+/* Timer Cell Template */
+struct timer_cell {
+	struct timer_cell	*next_ptr ;
+	struct timer_cell	*prev_ptr ;
+	u_long			start_time ;
+	struct s_sba_node_vars	*node_var ;
+} ;
+
+/*
+ * Node variables
+ */
+struct s_sba_node_vars {
+	u_char			change_resp_flag ;
+	u_char			report_resp_flag ;
+	u_char			change_req_flag ;
+	u_char			report_req_flag ;
+	long			change_amount ;
+	long			node_overhead ;
+	long			node_payload ;
+	u_long			node_status ;
+	u_char			deallocate_status ;
+	u_char			timer_state ;
+	u_short			report_cnt ;
+	long			lastrep_req_tranid ;
+	struct fddi_addr	mac_address ;
+	struct s_sba_sessions 	*node_sessions ;
+	struct timer_cell	timer ;
+} ;
+
+/*
+ * Session variables
+ */
+struct s_sba_sessions {
+	u_long			deallocate_status ;
+	long			session_overhead ;
+	u_long			min_segment_size ;
+	long			session_payload ;
+	u_long			session_status ;
+	u_long			sba_category ;
+	long			lastchg_req_tranid ;
+	u_short			session_id ;
+	u_char			class ;
+	u_char			fddi2 ;
+	u_long			max_t_neg ;
+	struct s_sba_sessions	*next_session ;
+} ;
+
+struct s_sba {
+
+	struct s_sba_node_vars	node[MAX_NODES] ;
+	struct s_sba_sessions	session[MAX_SESSIONS] ;
+
+	struct s_sba_sessions	*free_session ;	/* points to the first */
+						/* free session */
+
+	struct timer_cell	*tail_timer ;	/* points to the last timer cell */
+
+	/*
+	 * variables for allocation actions
+	 */
+	long	total_payload ;		/* Total Payload */
+	long	total_overhead ;	/* Total Overhead */
+	long	sba_allocatable ;	/* allocatable sync bandwidth */
+
+	/*
+	 * RAF message receive parameters
+	 */
+	long		msg_path_index ;	/* Path Type */
+	long		msg_sba_pl_req ;	/* Payload Request */
+	long		msg_sba_ov_req ;	/* Overhead Request */
+	long		msg_mib_pl ;		/* Current Payload for this Path */
+	long		msg_mib_ov ;		/* Current Overhead for this Path*/
+	long		msg_category ;		/* Category of the Allocation */
+	u_long		msg_max_t_neg ;		/* longest T_Neg acceptable */
+	u_long		msg_min_seg_siz ;	/* minimum segement size */
+	struct smt_header	*sm ;		/* points to the rec message */
+	struct fddi_addr	*msg_alloc_addr ;	/* Allocation Address */
+
+	/*
+	 * SBA variables
+	 */
+	u_long	sba_t_neg ;		/* holds the last T_NEG */
+	long	sba_max_alloc ;		/* the parsed value of SBAAvailable */	
+
+	/*
+	 * SBA state machine variables
+	 */
+	short	sba_next_state ;	/* the next state of the SBA */
+	char	sba_command ;		/* holds the execuded SBA cmd */
+	u_char	sba_available ;		/* parsed value after possible check */
+} ;
+
+#endif	/* SBA */
+
+	/*
+	 * variables for the End Station Support
+	 */
+struct s_ess {
+
+	/*
+	 * flags and counters
+	 */
+	u_char	sync_bw_available ;	/* is set if sync bw is allocated */
+	u_char	local_sba_active ;	/* set when a local sba is available */
+	char	raf_act_timer_poll ;	/* activate the timer to send allc req */
+	char	timer_count ;		/* counts every timer function call */
+
+	SMbuf	*sba_reply_pend ;	/* local reply for the sba is pending */
+	
+	/*
+	 * variables for the ess bandwidth control
+	 */
+	long	sync_bw ;		/* holds the allocaed sync bw */
+	u_long	alloc_trans_id ;	/* trans id of the last alloc req */
+} ;
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/sba_def.h linux.18p17/drivers/net/skfp/h/sba_def.h
--- linux.vanilla/drivers/net/skfp/h/sba_def.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/sba_def.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,76 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#define PHYS			0		/* physical addr */
+#define PERM_ADDR		0x80		/* permanet address */
+#define SB_STATIC		0x00000001
+#define MAX_PAYLOAD		1562
+#define PRIMARY_RING		0x00000001
+#ifndef NULL
+#define NULL			0x00
+#endif
+
+/*********************** SB_Input Variable Values ***********************/
+/*	 may be needed when ever the SBA state machine is called	*/
+
+#define UNKNOWN_SYNC_SOURCE	0x0001
+#define REQ_ALLOCATION		0x0002
+#define REPORT_RESP		0x0003
+#define CHANGE_RESP		0x0004
+#define TNEG			0x0005
+#define NIF			0x0006
+#define SB_STOP			0x0007
+#define SB_START		0x0008
+#define REPORT_TIMER		0x0009
+#define CHANGE_REQUIRED		0x000A
+
+#define DEFAULT_OV		50
+
+#ifdef SBA
+/**************************** SBA STATES *****************************/
+
+#define SBA_STANDBY		0x00000000
+#define SBA_ACTIVE		0x00000001
+#define SBA_RECOVERY		0x00000002
+#define SBA_REPORT		0x00000003
+#define SBA_CHANGE		0x00000004
+
+/**************************** OTHERS *********************************/
+
+#define FIFTY_PERCENT		50		/* bytes per second */
+#define MAX_SESSIONS		150	
+#define TWO_MINUTES		13079		/* 9.175 ms/tick */
+#define FIFTY_BYTES		50
+#define SBA_DENIED		0x0000000D
+#define I_NEED_ONE		0x00000000
+#define MAX_NODES		50
+/*#define T_REPORT		0x59682F00L*/	/* 120s/80ns in Hex */
+#define	TWO_MIN			120		/* seconds */
+#define SBA_ST_UNKNOWN		0x00000002
+#define SBA_ST_ACTIVE		0x00000001
+#define S_CLEAR			0x00000000L
+#define ZERO			0x00000000
+#define FULL			0x00000000	/* old: 0xFFFFFFFFF */
+#define S_SET			0x00000001L
+#define LOW_PRIO		0x02		/* ??????? */
+#define OK			0x01		/* ??????? */
+#define NOT_OK			0x00		/* ??????? */
+
+/****************************************/
+/* deallocate_status[ni][si] values	*/
+/****************************************/
+#define TX_CHANGE		0X00000001L
+#define PENDING			0x00000002L
+#define NONE			0X00000000L
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/skfbi.h linux.18p17/drivers/net/skfp/h/skfbi.h
--- linux.vanilla/drivers/net/skfp/h/skfbi.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/skfbi.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,1920 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_SKFBI_H_
+#define	_SKFBI_H_
+
+#ifdef SYNC
+#define exist_board_far			exist_board
+#define get_board_para_far		get_board_para
+#endif
+
+/*
+ * physical address offset + IO-Port base address
+ */
+#ifndef	PCI
+#define	ADDR(a)	((a)+smc->hw.iop)
+#define	ADDRS(smc,a) ((a)+(smc)->hw.iop)
+#endif
+
+/*
+ * FDDI-Fx (x := {I(SA), E(ISA), M(CA), P(CI)})
+ *	address calculation & function defines
+ */
+
+#ifdef	EISA
+
+/*
+ * Configuration PROM:	 !! all 8-Bit IO's !!
+ *					    |<-	  MAC-Address	 ->|
+ *	/-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-/
+ * val:	  |PROD_ID0..3|	   | free      |    |00|00|5A|40|    |nn|mm|00|00|
+ *	/-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-/
+ * IO-	  ^	      ^	   ^		    ^		     ^
+ * port	0C80	    0C83  0C88		   0C90		    0C98
+ *	  |	       \
+ *	  |		\
+ *	  |		 \______________________________________________
+ * EISA Expansion Board Product ID:					\
+ * BIT:	  |7 6 5 4 3 2 1 0|						 \
+ *	  | PROD_ID0	  | PROD_ID1	  | PROD_ID2	  | PROD_ID3	  |
+ *	  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *	  |0| MAN_C0  | MAN_C1	| MAN_C2  | PROD1 | PROD0 | REV1  | REV0  |
+ *	  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *	   ^=reserved			  | product numb. | revision numb |
+ * MAN_Cx = compressed manufacterer code (x:=0..2)
+ *	ASCII : 'A'..'Z' : 0x41..0x5A -> compr.(c-0x40) : 0x01..0x1A (5Bits!)
+ */
+
+#ifndef	MULT_OEM
+#ifndef	OEM_CONCEPT
+#define	MAN_C0		('S'-0x40)
+#define	MAN_C1		('K'-0x40)
+#define	MAN_C2		('D'-0x40)
+#define	PROD_ID0	(u_char)((MAN_C0<<2) | (MAN_C1>>3))
+#define	PROD_ID1	(u_char)(((MAN_C1<<5) & 0xff) | MAN_C2)
+#define	PROD_ID2	(u_char)(1)	/* prod. nr. */
+#define	PROD_ID3	(u_char)(0)	/* rev. nr. */
+
+#ifndef	OEM_USER_DATA
+#define	OEM_USER_DATA	"SK-NET FDDI V2.0 Userdata"
+#endif
+#else	/*  OEM_CONCEPT */
+
+/* MAN_C(0|1|2) no longer present (ra). */
+#define	PROD_ID0	(u_char)OEM_PROD_ID0
+#define	PROD_ID1	(u_char)OEM_PROD_ID1
+#define	PROD_ID2	(u_char)OEM_PROD_ID2
+#define	PROD_ID3	(u_char)OEM_PROD_ID3
+#endif	/* OEM_CONCEPT */
+
+#define	SKLOGO		PROD_ID0, PROD_ID1, PROD_ID2, PROD_ID3
+#endif	/* MULT_OEM */
+
+#define	SADDRL	(0)		/* start address SKLOGO */
+#define	SA_MAC	(0x10)		/* start addr. MAC_AD within the PROM */
+#define	PRA_OFF	(4)
+#define SA_PMD_TYPE	(8)	/* start addr. PMD-Type */
+
+#define	SKFDDI_PSZ	32		/* address PROM size */
+
+/*
+ * address transmision from logical to physical offset address on board
+ */
+#define FMA(a)	(0x0400|((a)<<1))	/* FORMAC+ (r/w) */
+#define P1A(a)	(0x0800|((a)<<1))	/* PLC1 (r/w) */
+#define P2A(a)	(0x0840|((a)<<1))	/* PLC2 (r/w) */
+#define TIA(a)	(0x0880|((a)<<1))	/* Timer (r/w) */
+#define PRA(a)	(0x0c80| (a))		/* configuration PROM */
+#define	C0A(a)	(0x0c84| (a))		/* config. RAM */
+#define	C1A(a)	(0x0ca0| (a))		/* IRQ-, DMA-nr., EPROM type */
+#define	C2A(a)	(0x0ca4| (a))		/* EPROM and PAGE selector */
+
+#define	CONF	C0A(0)			/* config RAM (card enable bit port) */
+#define PGRA	C2A(0)			/* Flash page register */
+#define	CDID	PRA(0)			/* Card ID I/O port addr. offset */
+
+
+/*
+ * physical address offset + slot specific IO-Port base address
+ */
+#define FM_A(a)	(FMA(a)+smc->hw.iop)	/* FORMAC Plus physical addr */
+#define P1_A(a)	(P1A(a)+smc->hw.iop)	/* PLC1 (r/w) */
+#define P2_A(a)	(P2A(a)+smc->hw.iop)	/* PLC2 (r/w) */
+#define TI_A(a)	(TIA(a)+smc->hw.iop)	/* Timer (r/w) */
+#define PR_A(a)	(PRA(a)+smc->hw.iop)	/* config. PROM */
+#define C0_A(a)	(C0A(a)+smc->hw.iop)	/* config. RAM */
+#define C1_A(a)	(C1A(a)+smc->hw.iop)	/* config. RAM */
+#define C2_A(a)	(C2A(a)+smc->hw.iop)	/* config. RAM */
+
+
+#define	CSRA	0x0008		/* control/status register address (r/w) */
+#define	ISRA	0x0008		/* int. source register address (upper 8Bits) */
+#define PLC1I	0x001a		/* clear PLC1 interrupt (write only) */
+#define PLC2I	0x0020		/* clear PLC2 interrupt (write only) */
+#define CSFA	0x001c		/* control/status FIFO BUSY flags (read only) */
+#define RQAA	0x001c		/* Request reg. (write only) */
+#define WCTA	0x001e		/* word counter (r/w) */
+#define	FFLAG	0x005e		/* FLAG/V_FULL (FIFO almost full, write only)*/
+
+#define	CSR_A	(CSRA+smc->hw.iop)	/* control/status register address (r/w) */
+#ifdef UNIX
+#define	CSR_AS(smc)	(CSRA+(smc)->hw.iop)	/* control/status register address (r/w) */
+#endif
+#define	ISR_A	(ISRA+smc->hw.iop)	/* int. source register address (upper 8Bits) */
+#define PLC1_I	(PLC1I+smc->hw.iop)	/* clear PLC1 internupt (write only) */
+#define PLC2_I	(PLC2I+smc->hw.iop)	/* clear PLC2 interrupt (write only) */
+#define CSF_A	(CSFA+smc->hw.iop)	/* control/status FIFO BUSY flags (r/w) */
+#define RQA_A	(RQAA+smc->hw.iop)	/* Request reg. (write only) */
+#define WCT_A	(WCTA+smc->hw.iop)	/* word counter (r/w) */
+#define	FFLAG_A	(FFLAG+smc->hw.iop)	/* FLAG/V_FULL (FIFO almost full, write only)*/
+
+/*
+ * control/status register CSRA	bits
+ */
+/* write */
+#define CS_CRESET	0x01		/* Card reset (0=reset) */
+#define	CS_RESET_FIFO	0x02		/* FIFO reset (0=reset) */
+#define	CS_IMSK		0x04		/* enable IRQ (1=enable, 0=disable) */
+#define	CS_EN_IRQ_TC	0x08		/* enable IRQ from transfer counter */
+#define CS_BYPASS	0x20		/* bypass switch (0=remove, 1=insert)*/
+#define CS_LED_0	0x40		/* switch LED 0 */
+#define	CS_LED_1	0x80		/* switch LED 1 */
+/* read */
+#define	CS_BYSTAT	0x40		/* 0=Bypass exist, 1= ..not */
+#define	CS_SAS		0x80		/* single attachement station (=1) */
+
+/*
+ * control/status register CSFA bits (FIFO)
+ */
+#define	CSF_MUX0	0x01
+#define	CSF_MUX1	0x02
+#define	CSF_HSREQ0	0x04
+#define	CSF_HSREQ1	0x08
+#define	CSF_HSREQ2	0x10
+#define	CSF_BUSY_DMA	0x40
+#define	CSF_BUSY_FIFO	0x80
+
+/*
+ * Interrupt source register ISRA (upper 8 data bits) read only & low activ.
+ */
+#define IS_MINTR1	0x0100		/* FORMAC ST1U/L & ~IMSK1U/L*/
+#define IS_MINTR2	0x0200		/* FORMAC ST2U/L & ~IMSK2U/L*/
+#define IS_PLINT1	0x0400		/* PLC1 */
+#define IS_PLINT2	0x0800		/* PLC2 */
+#define IS_TIMINT	0x1000		/* Timer 82C54-2 */
+#define	IS_TC		0x2000		/* transf. counter */
+
+#define	ALL_IRSR (IS_MINTR1|IS_MINTR2|IS_PLINT1|IS_PLINT2|IS_TIMINT|IS_TC)
+
+/*
+ * CONFIG<0> RAM (C0_A())
+ */
+#define	CFG_CARD_EN	0x01		/* card enable */
+
+/*
+ * CONFIG<1> RAM (C1_A())
+ */
+#define	CFG_IRQ_SEL	0x03		/* IRQ select (4 nr.) */
+#define	CFG_IRQ_TT	0x04		/* IRQ trigger type (LEVEL/EDGE) */
+#define	CFG_DRQ_SEL	0x18		/* DMA requ. (4 nr.) */
+#define	CFG_BOOT_EN	0x20		/* 0=BOOT-, 1=Application Software */
+#define	CFG_PROG_EN	0x40		/* V_Prog for FLASH_PROM (1=on) */
+
+/*
+ * CONFIG<2> RAM (C2_A())
+ */
+#define	CFG_EPROM_SEL	0x0f		/* FPROM start address selection */
+#define	CFG_PAGE	0xf0		/* FPROM page selection */
+
+
+#define	READ_PROM(a)	((u_char)inp(a))
+#define	GET_PAGE(i)	outp(C2_A(0),((int)(i)<<4) | (inp(C2_A(0)) & ~CFG_PAGE))
+#define	FPROM_SW()	(inp(C1_A(0)) & CFG_BOOT_EN)
+
+#define	MAX_PAGES	16		/* 16 pages */
+#define	MAX_FADDR	0x2000		/* 8K per page */
+#define	VPP_ON()	outp(C1_A(0),inp(C1_A(0)) |  CFG_PROG_EN)
+#define	VPP_OFF()	outp(C1_A(0),inp(C1_A(0)) & ~CFG_PROG_EN)
+
+#define	DMA_BUSY()	(inpw(CSF_A) & CSF_BUSY_DMA)
+#define FIFO_BUSY()	(inpw(CSF_A) & CSF_BUSY_FIFO)
+#define	DMA_FIFO_BUSY()	(inpw(CSF_A) & (CSF_BUSY_DMA | CSF_BUSY_FIFO))
+#define	BUS_CHECK()
+
+#ifdef UNISYS
+/* For UNISYS use another macro with drv_usecewait function */
+#define CHECK_DMA() {u_long k = 1000000; \
+		while (k && (DMA_BUSY())) { k--; drv_usecwait(20); } \
+		if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; }
+#else
+#define CHECK_DMA() {u_long k = 1000000 ;\
+		while (k && (DMA_BUSY())) k-- ;\
+		if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; }
+#endif
+
+#define CHECK_FIFO() {u_long k = 1000000 ;\
+		while (k && (FIFO_BUSY())) k-- ;\
+		if (!k) SMT_PANIC(smc,HWM_E0019,HWM_E0019_MSG) ; }
+
+#define CHECK_DMA_FIFO() {u_long k = 1000000 ;\
+		while (k && (DMA_FIFO_BUSY())) k-- ;\
+		if (!k) SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ; }
+
+#define	GET_ISR()	~inpw(ISR_A)
+#define CHECK_ISR()	~inpw(ISR_A)
+
+#ifndef UNIX
+#ifndef	WINNT
+#define	CLI_FBI()	outpw(CSR_A,(inpw(CSR_A)&\
+			(CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|smc->hw.led)
+#else	/* WINNT */
+#define CLI_FBI()	outpw(CSR_A,(l_inpw(CSR_A)&\
+			(CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|smc->hw.led)
+#endif	/* WINNT */
+#else	/* UNIX */
+#define	CLI_FBI(smc)	outpw(CSR_AS(smc),(inpw(CSR_AS(smc))&\
+			(CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|(smc)->hw.led)
+#endif
+
+#ifndef UNIX
+#define	STI_FBI()	outpw(CSR_A,(inpw(CSR_A)&\
+		(CS_CRESET|CS_BYPASS|CS_RESET_FIFO))|CS_IMSK|smc->hw.led)
+#else
+#define	STI_FBI(smc)	outpw(CSR_AS(smc),(inpw(CSR_AS(smc))&\
+		(CS_CRESET|CS_BYPASS|CS_RESET_FIFO))|CS_IMSK|(smc)->hw.led)
+#endif
+
+/* EISA DMA Controller */
+#define DMA_WRITE_SINGLE_MASK_BIT_M	0x0a	/* Master DMA Controller */
+#define DMA_WRITE_SINGLE_MASK_BIT_S	0xd4	/* Slave DMA Controller */
+#define DMA_CLEAR_BYTE_POINTER_M	0x0c
+#define DMA_CLEAR_BYTE_POINTER_S	0xd8
+
+#endif	/* EISA */
+
+#ifdef	MCA
+
+/*
+ * POS Register:	 !! all I/O's are 8-Bit !!
+ */
+#define	POS_SYS_SETUP	0x94	/* system setup register */
+#define	POS_SYSTEM	0xff	/* system mode */
+
+#define	POS_CHANNEL_POS	0x96	/* register slot ID */
+#define	POS_CHANNEL_BIT	0x08	/* mask for -"- */
+
+#define	POS_BASE	0x100	/* POS base address */
+#define	POS_ID_LOW	POS_BASE	/* card ID low */
+#define	POS_ID_HIGH	(POS_BASE+1)	/* card ID high */
+#define	POS_102		(POS_BASE+2)	/* card en., arbitration level .. */
+#define	POS_103		(POS_BASE+3)	/* FPROM addr, page */
+#define	POS_104		(POS_BASE+4)	/* I/O, IRQ */
+#define	POS_105		(POS_BASE+5)	/* POS_CHCK */
+#define	POS_106		(POS_BASE+6)	/* to read VPD */
+#define	POS_107		(POS_BASE+7)	/* added without function */
+
+/* FM1 card IDs */
+#define	FM1_CARD_ID0	0x83
+#define	FM1_CARD_ID1	0
+
+#define	FM1_IBM_ID0	0x9c
+#define	FM1_IBM_ID1	0x8f
+
+
+/* FM2 card IDs */
+#define	FM2_CARD_ID0	0xab
+#define	FM2_CARD_ID1	0
+
+#define	FM2_IBM_ID0	0x7e
+#define	FM2_IBM_ID1	0x8f
+
+/* Board revision. */
+#define FM1_REV		0
+#define FM2_REV		1
+
+#define	MAX_SLOT	8
+
+/*
+ * POS_102
+ */
+#define	POS_CARD_EN	0x01	/* card enable =1 */
+#define	POS_SDAT_EN	0x02	/* enable 32-bit streaming data mode */
+#define	POS_EN_CHKINT	0x04	/* enable int. from check line asserted */
+#define	POS_EN_BUS_ERR	0x08	/* enable int. on invalid busmaster transf. */
+#define	POS_FAIRNESS	0x10	/* fairnes on =1 */
+/* attention: arbitration level used with bit 0 POS 105 */
+#define	POS_LARBIT	0xe0	/* arbitration level	(0,0,0)->level = 0x8
+							(1,1,1)->level = 0xf */
+/*
+ * POS_103
+ */
+#define	POS_PAGE	0x07	/* FPROM page selection */
+#define	POS_BOOT_EN	0x08	/* boot PROM enable =1 */
+#define	POS_MSEL	0x70	/* memory start address for FPROM mapping */
+#define	PROG_EN		0x80	/* FM1: Vpp prog on/off */
+#define	POS_SDR		0x80	/* FM2: Streaming data bit */
+
+/*
+ * POS_104
+ */
+#define	POS_IOSEL	0x3f	/* selected I/O base address */
+#define	POS_IRQSEL	0xc0	/* selected interrupt */
+
+/*
+ * POS_105
+ */
+#define	POS_CHCK	0x80
+#define POS_SYNC_ERR	0x20	/* FM2: synchronous error reporting	*/
+#define POS_PAR_DATA	0x10	/* FM2: data parity enable bit	*/
+#define POS_PAR_ADDR	0x08	/* FM2: address parity enable bit	*/
+#define	POS_IRQHSEL	0x02	/* FM2: Highest bit for IRQ_selection	*/
+#define POS_HARBIT	0x01	/* Highest bit in Bus arbitration selection */
+
+#define	SA_MAC	(0)		/* start addr. MAC_AD within the PROM	*/
+#define	PRA_OFF	(0)
+#define SA_PMD_TYPE	(8)	/* start addr. PMD-Type	*/
+
+/*
+ * address transmision from logical to physical offset address on board
+ */
+#define	FMA(a)	(0x0100|((a)<<1))	/* FORMAC+ (r/w) */
+#define	P2(a)	(0x00c0|((a)<<1))	/* PLC2 (r/w) (DAS) */
+#define	P1(a)	(0x0080|((a)<<1))	/* PLC1 (r/w) */
+#define	TI(a)	(0x0060|((a)<<1))	/* Timer (r/w) */
+#define	PR(a)	(0x0040|((a)<<1))	/* configuration PROM */
+#define	CS(a)	(0x0020| (a))		/* control/status */
+#define	FF(a)	(0x0010|((a)<<1))	/* FIFO ASIC */
+#define	CT(a)	(0x0000|((a)<<1))	/* counter */
+
+/*
+ * counter
+ */
+#define	ACLA	CT(0)		/* address counter low */
+#define	ACHA	CT(1)		/* address counter high */
+#define	BCN	CT(2)		/* byte counter */
+#define	MUX	CT(3)		/* MUX-register */
+#define	WCN	CT(0x08)	/* word counter */
+#define	FFLG	CT(0x09)	/* FIFO Flags */
+
+/*
+ * test/control register (FM2 only)
+ */
+#define CNT_TST	0x018		/* Counter test control register */
+#define CNT_STP 0x01a		/* Counter test step reg. (8 Bit) */
+
+/*
+ * CS register (read only)
+ */
+#define	CSRA	CS(0)		/* control/status register address */
+#define	CSFA	CS(2)		/* control/status FIFO BUSY ...	 */
+#define	ISRA	CS(4)		/* first int. source register address */
+#define	ISR2	CS(6)		/* second int. source register address */
+#define	LEDR	CS(0x0c)	/* LED register r/w */
+#define	CSIL	CS(0x10)	/* I/O mapped POS_ID_low (100) */
+#define	CSIH	CS(0x12)	/*	- " - POS_ID_HIGH (101) */
+#define	CSA	CS(0x14)	/*	- " - POS_102 */
+#define	CSM	CS(0x0e)	/*	- " - POS_103 */
+#define	CSM_FM1	CS(0x16)	/*	- " - POS_103 (copy in FM1) */
+#define	CSI	CS(0x18)	/*	- " - POS_104 */
+#define	CSS	CS(0x1a)	/*	- " - POS_105 */
+#define	CSP_06	CS(0x1c)	/*	- " - POS_106 */
+#define	WDOG_ST		0x1c	/* Watchdog status (FM2 only)	*/
+#define	WDOG_EN		0x1c	/* Watchdog enabling (FM2 only, 8Bit)	*/
+#define	WDOG_DIS	0x1e	/* Watchdog disabling (FM2 only, 8Bit)	*/
+
+#define PGRA	CSM		/* Flash page register */
+
+
+#define	WCTA	FF(0)		/* word counter */
+#define	FFLAG	FF(1)		/* FLAG/V_FULL (FIFO almost full, write only)*/
+
+/*
+ * Timer register (FM2 only)
+ */
+#define RTM_CNT		0x28		/* RTM Counter */
+#define TI_DIV		0x60		/* Timer Prescaler */
+#define TI_CH1		0x62		/* Timer channel 1 counter */
+#define TI_STOP		0x64		/* Stop timer on channel 1 */
+#define TI_STRT		0x66		/* Start timer on channel 1 */
+#define TI_INI2		0x68		/* Timer: Bus master preemption */
+#define TI_CNT2		0x6a		/* Timer */
+#define TI_INI3		0x6c		/* Timer: Streaming data */
+#define TI_CNT3		0x6e		/* Timer */
+#define WDOG_LO		0x70		/* Watchdog counter low */
+#define WDOG_HI		0x72		/* Watchdog counter high */
+#define RTM_PRE		0x74		/* restr. token prescaler */
+#define RTM_TIM		0x76		/* restr. token timer */
+
+/*
+ * Recommended Timeout values (for FM2 timer only)
+ */
+#define TOUT_BM_PRE	188		/* 3.76 usec	*/
+#define TOUT_S_DAT	374		/* 7.48 usec	*/
+
+/*
+ * CS register (write only)
+ */
+#define	HSR(p)	CS(0x18|(p))	/* Host request register */
+
+#define	RTM_PUT		0x36		/* restr. token counter write */
+#define	RTM_GET		0x28		/*	- " -	clear */
+#define	RTM_CLEAR	0x34		/*	- " -	read */
+
+/*
+ * BCN	Bit definitions
+ */
+#define BCN_BUSY	0x8000		/* DMA Busy flag */
+#define BCN_AZERO	0x4000		/* Almost zero flag (BCN < 4) */
+#define BCN_STREAM	0x2000		/* Allow streaming data (BCN >= 8) */
+
+/*
+ * WCN	Bit definitions
+ */
+#define WCN_ZERO	0x2000		/* Zero flag (counted to zero) */
+#define WCN_AZERO	0x1000		/* Almost zero flag (BCN < 4) */
+
+/*
+ * CNT_TST	Bit definitions
+ */
+#define CNT_MODE	0x01		/* Go into test mode */
+#define	CNT_D32		0x02		/* 16/32 BIT test mode */
+
+/*
+ * FIFO Flag		FIFO Flags/Vfull register
+ */
+#define FF_VFULL	0x003f		/* V_full value mask */
+#define FFLG_FULL	0x2000		/* FULL flag */
+#define FFLG_A_FULL	0x1000		/* Almost full flag */
+#define FFLG_VFULL	0x0800		/* V_full Flag */
+#define FFLG_A_EMP	0x0400		/* almost empty flag */
+#define FFLG_EMP	0x0200		/* empty flag */
+#define FFLG_T_EMP	0x0100		/* totally empty flag */
+
+/*
+ * WDOG		Watchdog status register
+ */
+#define WDOG_ALM	0x01		/* Watchdog alarm Bit */
+#define WDOG_ACT	0x02		/* Watchdog active Bit */
+
+/*
+ * CS(0)	CONTROLS
+ */
+#define	CS_CRESET	0x0001
+#define	FIFO_RST	0x0002
+#define	CS_IMSK		0x0004
+#define	EN_IRQ_CHCK	0x0008
+#define	EN_IRQ_TOKEN	0x0010
+#define	EN_IRQ_TC	0x0020
+#define	TOKEN_STATUS	0x0040
+#define	RTM_CHANGE	0x0080
+
+#define	CS_SAS		0x0100
+#define	CS_BYSTAT	0x0200	/* bypass connected (0=conn.) */
+#define	CS_BYPASS	0x0400	/* bypass on/off indication */
+
+/*
+ * CS(2)	FIFOSTAT
+ */
+#define	HSREQ		0x0007
+#define	BIGDIR		0x0008
+#define	CSF_BUSY_FIFO	0x0010
+#define	CSF_BUSY_DMA	0x0020
+#define	SLOT_32		0x0040
+
+#define	LED_0		0x0001
+#define	LED_1		0x0002
+#define	LED_2		0x0100
+
+#define	MAX_PAGES	8		/* pages */
+#define	MAX_FADDR	0x4000		/* 16K per page */
+
+/*
+ *	IRQ = ISRA || ISR2 ;
+ *
+ *	ISRA = IRQ_OTH_EN && (IS_LAN | IS_BUS) ;
+ *	ISR2 = IRQ_TC_EN && IS_TC ;
+ *
+ *	IS_LAN = (IS_MINTR1 | IS_MINTR2 | IS_PLINT1 | IS_PLINT2 | IS_TIMINT) ||
+ *		 (IRQ_EN_TOKEN && IS_TOKEN) ;
+ *	IS_BUS = IRQ_CHCK_EN && (IS_BUSERR | IS_CHCK_L) ;
+ */
+/*
+ *	ISRA	!!! activ high !!!
+ */
+#define	IS_MINTR1	0x0001		/* FORMAC ST1U/L & ~IMSK1U/L*/
+#define	IS_MINTR2	0x0002		/* FORMAC ST2U/L & ~IMSK2U/L*/
+#define	IS_PLINT1	0x0004		/* PLC1 */
+#define	IS_PLINT2	0x0008		/* PLC2 */
+#define	IS_TIMINT	0x0010		/* Timer 82C54-2 */
+#define	IS_TOKEN	0x0020		/* restrictet token monitoring */
+#define	IS_CHCK_L	0x0040		/* check line asserted */
+#define	IS_BUSERR	0x0080		/* bus error */
+/*
+ *	ISR2
+ */
+#define	IS_TC		0x0001		/* terminal count irq */
+#define IS_SFDBKRTN	0x0002		/* selected feedback return */
+#define IS_D16		0x0004		/* DS16 */
+#define	IS_D32		0x0008		/* DS32 */
+#define IS_DPEI		0x0010		/* Data Parity Indication */
+
+#define	ALL_IRSR	0x00ff
+
+#define	FM_A(a)	ADDR(FMA(a))	/* FORMAC Plus physical addr */
+#define	P1_A(a)	ADDR(P1(a))	/* PLC1 (r/w) */
+#define	P2_A(a)	ADDR(P2(a))	/* PLC2 (r/w) (DAS) */
+#define	TI_A(a)	ADDR(TI(a))	/* Timer (r/w) FM1 only! */
+#define	PR_A(a)	ADDR(PR(a))	/* config. PROM */
+#define	CS_A(a)	ADDR(CS(a))	/* control/status */
+
+#define	ISR1_A	ADDR(ISRA)	/* first int. source register address */
+#define	ISR2_A	ADDR(ISR2)	/* second	-"-	 */
+#define	CSR_A	ADDR(CSRA)	/* control/status register address */
+#define	CSF_A	ADDR(CSFA)	/* control/status FIFO BUSY flags (r/w) */
+
+#define	CSIL_A	ADDR(CSIL)	/* I/O mapped POS_ID_low (102) */
+#define	CSIH_A	ADDR(CSIH)	/*	- " - POS_ID_HIGH (101) */
+#define	CSA_A	ADDR(CSA)	/*	- " - POS_102 */
+#define	CSI_A	ADDR(CSI)	/*	- " - POS_104 */
+#define	CSM_A	ADDR(CSM)	/*	- " - POS_103 */
+#define	CSM_FM1_A	ADDR(CSM_FM1)	/*	- " - POS_103 (2nd copy, FM1) */
+#define	CSP_06_A	ADDR(CSP_06)	/*	- " - POS_106 */
+
+#define	WCT_A	ADDR(WCTA)	/* word counter (r/w) */
+#define	FFLAG_A	ADDR(FFLAG)	/* FLAG/V_FULL (FIFO almost full, write only)*/
+
+#define	ACL_A	ADDR(ACLA)	/* address counter low */
+#define	ACH_A	ADDR(ACHA)	/* address counter high */
+#define	BCN_A	ADDR(BCN)	/* byte counter */
+#define	MUX_A	ADDR(MUX)	/* MUX-register */
+
+#define	ISR_A	ADDR(ISRA)	/* Interrupt Source Register */
+#define	FIFO_RESET_A	ADDR(FIFO_RESET)	/* reset the FIFO */
+#define	FIFO_EN_A	ADDR(FIFO_EN)		/* enable the FIFO */
+
+#define WDOG_EN_A	ADDR(WDOG_EN)		/* reset and start the WDOG */
+#define WDOG_DIS_A	ADDR(WDOG_DIS)		/* disable the WDOG */
+/*
+ * all control reg. (read!) are 8 bit (except PAGE_RG_A and LEDR_A)
+ */
+#define	HSR_A(p)	ADDR(HSR(p))	/* Host request register */
+
+#define	STAT_BYP	0		/* bypass station */
+#define	STAT_INS	2		/* insert station */
+#define	BYPASS(o)	CS(0x10|(o))	/* o=STAT_BYP || STAT_INS */
+
+#define	IRQ_TC_EN	CS(0x0b)	/* enable/disable IRQ on TC */
+#define	IRQ_TC_DIS	CS(0x0a)
+#define	IRQ_TOKEN_EN	CS(9)		/* enable/disable IRQ on restr. Token */
+#define	IRQ_TOKEN_DIS	CS(8)
+#define	IRQ_CHCK_EN	CS(7)		/*	-"-	IRQ after CHCK line */
+#define	IRQ_CHCK_DIS	CS(6)
+#define	IRQ_OTH_EN	CS(5)		/*	-"-	other IRQ's */
+#define	IRQ_OTH_DIS	CS(4)
+#define	FIFO_EN		CS(3)		/* disable (reset), enable FIFO */
+#define	FIFO_RESET	CS(2)
+#define	CARD_EN		CS(1)		/* disable (reset), enable card */
+#define	CARD_DIS	CS(0)
+
+#define	LEDR_A		ADDR(LEDR)	/* D0=green, D1=yellow, D8=L2 */
+#define	PAGE_RG_A	ADDR(CSM)	/* D<2..0> */
+#define	IRQ_CHCK_EN_A	ADDR(IRQ_CHCK_EN)
+#define IRQ_CHCK_DIS_A	ADDR(IRQ_CHCK_DIS)
+
+#define	GET_PAGE(bank)	outpw(PAGE_RG_A,(inpw(PAGE_RG_A) &\
+				(~POS_PAGE)) |(int) (bank))
+#define	VPP_ON()	if (smc->hw.rev == FM1_REV) {	\
+				outpw(PAGE_RG_A,	\
+				(inpw(PAGE_RG_A) & POS_PAGE) | PROG_EN); \
+			}
+#define	VPP_OFF()	if (smc->hw.rev == FM1_REV) {	\
+				outpw(PAGE_RG_A,(inpw(PAGE_RG_A) & POS_PAGE)); \
+			}
+
+#define	SKFDDI_PSZ	16		/* address PROM size */
+
+#define	READ_PROM(a)	((u_char)inp(a))
+
+#define	GET_ISR()	~inpw(ISR1_A)
+#ifndef	TCI
+#define	CHECK_ISR()	~inpw(ISR1_A)
+#define	CHECK_ISR_SMP(iop)	~inpw((iop)+ISRA)
+#else
+#define	CHECK_ISR()		(~inpw(ISR1_A) | ~inpw(ISR2_A))
+#define	CHECK_ISR_SMP(iop)	(~inpw((iop)+ISRA) | ~inpw((iop)+ISR2))
+#endif
+
+#define	DMA_BUSY()	(inpw(CSF_A) & CSF_BUSY_DMA)
+#define	FIFO_BUSY()	(inpw(CSF_A) & CSF_BUSY_FIFO)
+#define	DMA_FIFO_BUSY()	(inpw(CSF_A) & (CSF_BUSY_DMA | CSF_BUSY_FIFO))
+#define	BUS_CHECK() {	int i ; \
+			if ((i = GET_ISR()) & IS_BUSERR) \
+				SMT_PANIC(smc,HWM_E0020,HWM_E0020_MSG) ; \
+			if (i & IS_CHCK_L) \
+				SMT_PANIC(smc,HWM_E0014,HWM_E0014_MSG) ; \
+		}
+
+#define	CHECK_DMA() {	u_long k = 10000 ; \
+		 while (k && (DMA_BUSY())) { \
+			k-- ; \
+			BUS_CHECK() ; \
+		 } \
+		 if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; }
+
+#define	CHECK_FIFO() {u_long k = 1000000 ;\
+		 while (k && (FIFO_BUSY())) k-- ;\
+		 if (!k) SMT_PANIC(smc,HWM_E0019,HWM_E0019_MSG) ; }
+
+#define	CHECK_DMA_FIFO() {u_long k = 1000000 ;\
+		 while (k && (DMA_FIFO_BUSY())) { \
+			k-- ;\
+			BUS_CHECK() ; \
+		 } \
+		 if (!k) SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ; }
+
+#ifndef UNIX
+#define	CLI_FBI()	outp(ADDR(IRQ_OTH_DIS),0)
+#else
+#define	CLI_FBI(smc)	outp(ADDRS((smc),IRQ_OTH_DIS),0)
+#endif
+
+#ifndef	TCI
+#define	CLI_FBI_SMP(iop)	outp((iop)+IRQ_OTH_DIS,0)
+#else
+#define	CLI_FBI_SMP(iop)	outp((iop)+IRQ_OTH_DIS,0) ;\
+				outp((iop)+IRQ_TC_DIS,0)
+#endif
+
+#ifndef UNIX
+#define	STI_FBI()	outp(ADDR(IRQ_OTH_EN),0)
+#else
+#define	STI_FBI(smc)	outp(ADDRS((smc),IRQ_OTH_EN),0)
+#endif
+
+/*
+ * Terminal count primitives
+ */
+#define CLI_TCI(smc)	outp(ADDRS((smc),IRQ_TC_DIS),0)
+#define STI_TCI(smc)	outp(ADDRS((smc),IRQ_TC_EN),0)
+#define CHECK_TC(smc,k)	{(k) = 10000 ;\
+	while ((k) && (~inpw(ISR2_A) & IS_TC)) (k)-- ;\
+	if (!k) SMT_PANIC(smc,HWM_E0018,HWM_E0018_MSG) ; }
+
+#endif	/* MCA */
+
+#ifdef	ISA
+
+/*
+ * address transmision from logic NPADDR6-0 to physical offset address on board
+ */
+#define FMA(a)	(0x8000|(((a)&0x07)<<1)|(((a)&0x78)<<7))  /* FORMAC+ (r/w) */
+#define PRA(a)	(0x1000|(((a)&0x07)<<1)|(((a)&0x18)<<7))  /* PROM (read only)*/
+#define P1A(a)	(0x4000|(((a)&0x07)<<1)|(((a)&0x18)<<7))  /* PLC1 (r/w) */
+#define P2A(a)	(0x5000|(((a)&0x07)<<1)|(((a)&0x18)<<7))  /* PLC2 (r/w) */
+#define TIA(a)	(0x6000|(((a)&0x03)<<1))		  /* Timer (r/w) */
+
+#define	ISRA	0x0000		/* int. source register address (read only) */
+#define	ACLA	0x0000		/* address counter low address (write only) */
+#define	ACHA	0x0002		/* address counter high address (write only) */
+#define	TRCA	0x0004		/* transfer counter address (write only) */
+#define	PGRA	0x0006		/* page register address (write only) */
+#define RQAA	0x2000		/* Request reg. (write only) */
+#define	CSRA	0x3000		/* control/status register address (r/w) */
+
+/*
+ * physical address offset + IO-Port base address
+ */
+#define FM_A(a)	(FMA(a)+smc->hw.iop)	/* FORMAC Plus physical addr */
+#define PR_A(a)	(PRA(a)+smc->hw.iop)	/* PROM (read only)*/
+#define P1_A(a)	(P1A(a)+smc->hw.iop)	/* PLC1 (r/w) */
+#define P2_A(a)	(P2A(a)+smc->hw.iop)	/* PLC2 (r/w) */
+#define TI_A(a)	(TIA(a)+smc->hw.iop)	/* Timer (r/w) */
+
+#define	ISR_A	(0x0000+smc->hw.iop) /* int. source register address (read only) */
+#define	ACL_A	(0x0000+smc->hw.iop) /* address counter low address (write only) */
+#define	ACH_A	(0x0002+smc->hw.iop) /* address counter high address (write only)*/
+#define	TRC_A	(0x0004+smc->hw.iop) /* transfer counter address (write only) */
+#define	PGR_A	(0x0006+smc->hw.iop) /* page register address (write only) */
+#define RQA_A	(0x2000+smc->hw.iop) /* Request reg. (write only) */
+#define	CSR_A	(0x3000+smc->hw.iop) /* control/status register address (r/w) */
+#ifdef UNIX
+#define	CSR_AS(smc) (0x3000+(smc)->hw.iop) /* control/status register address */
+#endif
+#define	PLC1_I	(0x3400+smc->hw.iop) /* clear PLC1 interrupt bit */
+#define	PLC2_I	(0x3800+smc->hw.iop) /* clear PLC2 interrupt bit */
+
+#ifndef	MULT_OEM
+#ifndef	OEM_CONCEPT
+#define	SKLOGO_STR	"SKFDDI"
+#else	/* OEM_CONCEPT */
+#define	SKLOGO_STR	OEM_FDDI_LOGO
+#endif	/* OEM_CONCEPT */
+#endif  /* MULT_OEM */
+#define	SADDRL	(24)		/* start address SKLOGO */
+#define	SA_MAC	(0)		/* start addr. MAC_AD within the PROM */
+#define	PRA_OFF	(0)
+#define SA_PMD_TYPE	(8)	/* start addr. PMD-Type */
+
+#define	CDID	(PRA(SADDRL))	/* Card ID int/O port addr. offset */
+#define	NEXT_CDID	((PRA(SADDRL+1)) - CDID)
+
+#define	SKFDDI_PSZ	32		/* address PROM size */
+
+#define	READ_PROM(a)	((u_char)inpw(a))
+#define	GET_PAGE(i)	outpw(PGR_A,(int)(i))
+
+#define	MAX_PAGES	16		/* 16 pages */
+#define	MAX_FADDR	0x2000		/* 8K per page */
+#define	VPP_OFF()	outpw(CSR_A,(inpw(CSR_A) & (CS_CRESET|CS_BYPASS)))
+#define	VPP_ON()	outpw(CSR_A,(inpw(CSR_A) & (CS_CRESET|CS_BYPASS)) | \
+				CS_VPPSW)
+
+/*
+ * control/status register CSRA	bits (log. addr: 0x3000)
+ */
+/* write */
+#define CS_CRESET	0x01		/* Card reset (0=reset) */
+#define	CS_IMSK		0x02		/* enable IRQ (1=enable, 0=disable) */
+#define CS_RESINT1	0x04		/* PLINT1 reset */
+#define	CS_VPPSW	0x10		/* 12V power switch (0=off, 1=on) */
+#define CS_BYPASS	0x20		/* bypass switch (0=remove, 1=insert)*/
+#define CS_RESINT2	0x40		/* PLINT2 reset */
+/* read */
+#define	CS_BUSY		0x04		/* master transfer activ (=1) */
+#define	CS_SW_EPROM	0x08		/* 0=Application Soft. 1=BOOT-EPROM */
+#define	CS_BYSTAT	0x40		/* 0=Bypass exist, 1= ..not */
+#define	CS_SAS		0x80		/* single attachement station (=1) */
+
+/*
+ * Interrupt source register ISRA (log. addr: 0x0000) read only & low activ.
+ */
+#define IS_MINTR1	0x01		/* FORMAC ST1U/L && ~IMSK1U/L*/
+#define IS_MINTR2	0x02		/* FORMAC ST2U/L && ~IMSK2U/L*/
+#define IS_PLINT1	0x04		/* PLC1 */
+#define IS_PLINT2	0x08		/* PLC2 */
+#define IS_TIMINT	0x10		/* Timer 82C54-2 */
+
+#define	ALL_IRSR	(IS_MINTR1|IS_MINTR2|IS_PLINT1|IS_PLINT2|IS_TIMINT)
+
+#define	FPROM_SW()	(inpw(CSR_A)&CS_SW_EPROM)
+#define	DMA_BUSY()	(inpw(CSR_A)&CS_BUSY)
+#define CHECK_FIFO()
+#define	BUS_CHECK()
+
+/*
+ * set Host Request register (wr.)
+ */
+#define SET_HRQ(qup)	outpw(RQA_A+((qup)<<1),0)
+
+#ifndef UNIX
+#ifndef WINNT
+#define	CLI_FBI()	outpw(CSR_A,(inpw(CSR_A)&(CS_CRESET|CS_BYPASS|CS_VPPSW)))
+#else
+#define	CLI_FBI()	outpw(CSR_A,(l_inpw(CSR_A) & \
+				(CS_CRESET|CS_BYPASS|CS_VPPSW)))
+#endif
+#else
+#define	CLI_FBI(smc)	outpw(CSR_AS(smc),(inpw(CSR_AS(smc))& \
+						(CS_CRESET|CS_BYPASS|CS_VPPSW)))
+#endif
+
+#ifndef UNIX
+#define	STI_FBI()	outpw(CSR_A,(inpw(CSR_A) & \
+				(CS_CRESET|CS_BYPASS|CS_VPPSW)) | CS_IMSK)
+#else
+#define	STI_FBI(smc)	outpw(CSR_AS(smc),(inpw(CSR_AS(smc)) & \
+				(CS_CRESET|CS_BYPASS|CS_VPPSW)) | CS_IMSK)
+#endif
+
+#define CHECK_DMA()	{unsigned k = 10000 ;\
+			while (k && (DMA_BUSY())) k-- ;\
+			if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; }
+
+#define	GET_ISR()	~inpw(ISR_A)
+
+#endif	/* ISA */
+
+/*--------------------------------------------------------------------------*/
+#ifdef	PCI
+
+/*
+ *	(DV)	= only defined for Da Vinci
+ *	(ML)	= only defined for Monalisa
+ */
+
+/*
+ * Configuration Space header
+ */
+#define	PCI_VENDOR_ID	0x00	/* 16 bit	Vendor ID */
+#define	PCI_DEVICE_ID	0x02	/* 16 bit	Device ID */
+#define	PCI_COMMAND	0x04	/* 16 bit	Command */
+#define	PCI_STATUS	0x06	/* 16 bit	Status */
+#define	PCI_REV_ID	0x08	/*  8 bit	Revision ID */
+#define	PCI_CLASS_CODE	0x09	/* 24 bit	Class Code */
+#define	PCI_CACHE_LSZ	0x0c	/*  8 bit	Cache Line Size */
+#define	PCI_LAT_TIM	0x0d	/*  8 bit	Latency Timer */
+#define	PCI_HEADER_T	0x0e	/*  8 bit	Header Type */
+#define	PCI_BIST	0x0f	/*  8 bit	Built-in selftest */
+#define	PCI_BASE_1ST	0x10	/* 32 bit	1st Base address */
+#define	PCI_BASE_2ND	0x14	/* 32 bit	2nd Base address */
+/* Byte 18..2b:	Reserved */
+#define	PCI_SUB_VID	0x2c	/* 16 bit	Subsystem Vendor ID */
+#define	PCI_SUB_ID	0x2e	/* 16 bit	Subsystem ID */
+#define	PCI_BASE_ROM	0x30	/* 32 bit	Expansion ROM Base Address */
+/* Byte 34..33:	Reserved */
+#define PCI_CAP_PTR	0x34	/*  8 bit (ML)	Capabilities Ptr */
+/* Byte 35..3b:	Reserved */
+#define	PCI_IRQ_LINE	0x3c	/*  8 bit	Interrupt Line */
+#define	PCI_IRQ_PIN	0x3d	/*  8 bit	Interrupt Pin */
+#define	PCI_MIN_GNT	0x3e	/*  8 bit	Min_Gnt */
+#define	PCI_MAX_LAT	0x3f	/*  8 bit	Max_Lat */
+/* Device Dependent Region */
+#define	PCI_OUR_REG	0x40	/* 32 bit (DV)	Our Register */
+#define	PCI_OUR_REG_1	0x40	/* 32 bit (ML)	Our Register 1 */
+#define	PCI_OUR_REG_2	0x44	/* 32 bit (ML)	Our Register 2 */
+/* Power Management Region */
+#define PCI_PM_CAP_ID	0x48	/*  8 bit (ML)	Power Management Cap. ID */
+#define PCI_PM_NITEM	0x49	/*  8 bit (ML)	Next Item Ptr */
+#define PCI_PM_CAP_REG	0x4a	/* 16 bit (ML)	Power Management Capabilities */
+#define PCI_PM_CTL_STS	0x4c	/* 16 bit (ML)	Power Manag. Control/Status */
+/* Byte 0x4e:	Reserved */
+#define PCI_PM_DAT_REG	0x4f	/*  8 bit (ML)	Power Manag. Data Register */
+/* VPD Region */
+#define	PCI_VPD_CAP_ID	0x50	/*  8 bit (ML)	VPD Cap. ID */
+#define PCI_VPD_NITEM	0x51	/*  8 bit (ML)	Next Item Ptr */
+#define PCI_VPD_ADR_REG	0x52	/* 16 bit (ML)	VPD Address Register */
+#define PCI_VPD_DAT_REG	0x54	/* 32 bit (ML)	VPD Data Register */
+/* Byte 58..ff:	Reserved */
+
+/*
+ * I2C Address (PCI Config)
+ *
+ * Note: The temperature and voltage sensors are relocated on a different
+ *	 I2C bus.
+ */
+#define I2C_ADDR_VPD	0xA0	/* I2C address for the VPD EEPROM */ 
+
+/*
+ * Define Bits and Values of the registers
+ */
+/*	PCI_VENDOR_ID	16 bit	Vendor ID */
+/*	PCI_DEVICE_ID	16 bit	Device ID */
+/* Values for Vendor ID and Device ID shall be patched into the code */
+/*	PCI_COMMAND	16 bit	Command */
+#define	PCI_FBTEN	0x0200	/* Bit 9:	Fast Back-To-Back enable */
+#define	PCI_SERREN	0x0100	/* Bit 8:	SERR enable */
+#define	PCI_ADSTEP	0x0080	/* Bit 7:	Address Stepping */
+#define	PCI_PERREN	0x0040	/* Bit 6:	Parity Report Response enable */
+#define	PCI_VGA_SNOOP	0x0020	/* Bit 5:	VGA palette snoop */
+#define	PCI_MWIEN	0x0010	/* Bit 4:	Memory write an inv cycl ena */
+#define	PCI_SCYCEN	0x0008	/* Bit 3:	Special Cycle enable */
+#define	PCI_BMEN	0x0004	/* Bit 2:	Bus Master enable */
+#define	PCI_MEMEN	0x0002	/* Bit 1:	Memory Space Access enable */
+#define	PCI_IOEN	0x0001	/* Bit 0:	IO Space Access enable */
+
+/*	PCI_STATUS	16 bit	Status */
+#define	PCI_PERR	0x8000	/* Bit 15:	Parity Error */
+#define	PCI_SERR	0x4000	/* Bit 14:	Signaled SERR */
+#define	PCI_RMABORT	0x2000	/* Bit 13:	Received Master Abort */
+#define	PCI_RTABORT	0x1000	/* Bit 12:	Received Target Abort */
+#define	PCI_STABORT	0x0800	/* Bit 11:	Sent Target Abort */
+#define	PCI_DEVSEL	0x0600	/* Bit 10..9:	DEVSEL Timing */
+#define	PCI_DEV_FAST	(0<<9)	/*		fast */
+#define	PCI_DEV_MEDIUM	(1<<9)	/*		medium */
+#define	PCI_DEV_SLOW	(2<<9)	/*		slow */
+#define	PCI_DATAPERR	0x0100	/* Bit 8:	DATA Parity error detected */
+#define	PCI_FB2BCAP	0x0080	/* Bit 7:	Fast Back-to-Back Capability */
+#define	PCI_UDF		0x0040	/* Bit 6:	User Defined Features */
+#define PCI_66MHZCAP	0x0020	/* Bit 5:	66 MHz PCI bus clock capable */
+#define PCI_NEWCAP	0x0010	/* Bit 4:	New cap. list implemented */
+
+#define PCI_ERRBITS	(PCI_PERR|PCI_SERR|PCI_RMABORT|PCI_STABORT|PCI_DATAPERR)
+
+/*	PCI_REV_ID	8 bit	Revision ID */
+/*	PCI_CLASS_CODE	24 bit	Class Code */
+/*	Byte 2:		Base Class		(02) */
+/*	Byte 1:		SubClass		(02) */
+/*	Byte 0:		Programming Interface	(00) */
+
+/*	PCI_CACHE_LSZ	8 bit	Cache Line Size */
+/*	Possible values: 0,2,4,8,16	*/
+
+/*	PCI_LAT_TIM	8 bit	Latency Timer */
+
+/*	PCI_HEADER_T	8 bit	Header Type */
+#define	PCI_HD_MF_DEV	0x80	/* Bit 7:	0= single, 1= multi-func dev */
+#define	PCI_HD_TYPE	0x7f	/* Bit 6..0:	Header Layout 0= normal */
+
+/*	PCI_BIST	8 bit	Built-in selftest */
+#define	PCI_BIST_CAP	0x80	/* Bit 7:	BIST Capable */
+#define	PCI_BIST_ST	0x40	/* Bit 6:	Start BIST */
+#define	PCI_BIST_RET	0x0f	/* Bit 3..0:	Completion Code */
+
+/*	PCI_BASE_1ST	32 bit	1st Base address */
+#define	PCI_MEMSIZE	0x800L       /* use 2 kB Memory Base */
+#define	PCI_MEMBASE_BITS 0xfffff800L /* Bit 31..11:	Memory Base Address */
+#define	PCI_MEMSIZE_BIIS 0x000007f0L /* Bit 10..4:	Memory Size Req. */
+#define	PCI_PREFEN	0x00000008L  /* Bit 3:		Prefetchable */
+#define	PCI_MEM_TYP	0x00000006L  /* Bit 2..1:	Memory Type */
+#define	PCI_MEM32BIT	(0<<1)	     /* Base addr anywhere in 32 Bit range */
+#define	PCI_MEM1M	(1<<1)	     /* Base addr below 1 MegaByte */
+#define	PCI_MEM64BIT	(2<<1)	     /* Base addr anywhere in 64 Bit range */
+#define	PCI_MEMSPACE	0x00000001L  /* Bit 0:	Memory Space Indic. */
+
+/*	PCI_BASE_2ND	32 bit	2nd Base address */
+#define	PCI_IOBASE	0xffffff00L  /* Bit 31..8:  I/O Base address */
+#define	PCI_IOSIZE	0x000000fcL  /* Bit 7..2:   I/O Size Requirements */
+#define	PCI_IOSPACE	0x00000001L  /* Bit 0:	    I/O Space Indicator */
+
+/*	PCI_SUB_VID	16 bit	Subsystem Vendor ID */
+/*	PCI_SUB_ID	16 bit	Subsystem ID */
+
+/*	PCI_BASE_ROM	32 bit	Expansion ROM Base Address */
+#define	PCI_ROMBASE	0xfffe0000L  /* Bit 31..17: ROM BASE addres (1st) */
+#define	PCI_ROMBASZ	0x0001c000L  /* Bit 16..14: Treat as BASE or SIZE */
+#define	PCI_ROMSIZE	0x00003800L  /* Bit 13..11: ROM Size Requirements */
+#define	PCI_ROMEN	0x00000001L  /* Bit 0:	    Address Decode enable */
+
+/*	PCI_CAP_PTR	8 bit	New Capabilities Pointers */
+/*	PCI_IRQ_LINE	8 bit	Interrupt Line */
+/*	PCI_IRQ_PIN	8 bit	Interrupt Pin */
+/*	PCI_MIN_GNT	8 bit	Min_Gnt */
+/*	PCI_MAX_LAT	8 bit	Max_Lat */
+/* Device Dependent Region */
+/*	PCI_OUR_REG	(DV)	32 bit	Our Register */
+/*	PCI_OUR_REG_1	(ML)	32 bit	Our Register 1 */
+				  /*	 Bit 31..29:	reserved */
+#define	PCI_PATCH_DIR	(3L<<27)  /*(DV) Bit 28..27:	Ext Patchs direction */
+#define PCI_PATCH_DIR_0	(1L<<27)  /*(DV) Type of the pins EXT_PATCHS<1..0>   */
+#define PCI_PATCH_DIR_1 (1L<<28)  /*	   0 = input			     */
+				  /*	   1 = output			     */
+#define	PCI_EXT_PATCHS	(3L<<25)  /*(DV) Bit 26..25:	Extended Patches     */
+#define PCI_EXT_PATCH_0 (1L<<25)  /*(DV)				     */
+#define PCI_EXT_PATCH_1 (1L<<26)  /*	 CLK for MicroWire (ML)		     */
+#define PCI_VIO		(1L<<25)  /*(ML)				     */
+#define	PCI_EN_BOOT	(1L<<24)  /*	 Bit 24:	Enable BOOT via ROM  */
+				  /*	   1 = Don't boot with ROM	     */
+				  /*	   0 = Boot with ROM		     */
+#define	PCI_EN_IO	(1L<<23)  /*	 Bit 23:	Mapping to IO space  */
+#define	PCI_EN_FPROM	(1L<<22)  /*	 Bit 22:	FLASH mapped to mem? */
+				  /*	   1 = Map Flash to Memory	     */
+			  	  /*	   0 = Disable all addr. decoding    */
+#define	PCI_PAGESIZE	(3L<<20)  /*	 Bit 21..20:	FLASH Page Size	     */
+#define	PCI_PAGE_16	(0L<<20)  /*		16 k pages		     */
+#define	PCI_PAGE_32K	(1L<<20)  /*		32 k pages		     */
+#define	PCI_PAGE_64K	(2L<<20)  /*		64 k pages		     */
+#define	PCI_PAGE_128K	(3L<<20)  /*		128 k pages		     */
+				  /*	 Bit 19: reserved (ML) and (DV)	     */
+#define	PCI_PAGEREG	(7L<<16)  /*	 Bit 18..16:	Page Register	     */
+				  /*	 Bit 15:	reserved	     */
+#define	PCI_FORCE_BE	(1L<<14)  /*	 Bit 14:	Assert all BEs on MR */
+#define	PCI_DIS_MRL	(1L<<13)  /*	 Bit 13:	Disable Mem R Line   */
+#define	PCI_DIS_MRM	(1L<<12)  /*	 Bit 12:	Disable Mem R multip */
+#define	PCI_DIS_MWI	(1L<<11)  /*	 Bit 11:	Disable Mem W & inv  */
+#define	PCI_DISC_CLS	(1L<<10)  /*	 Bit 10:	Disc: cacheLsz bound */
+#define	PCI_BURST_DIS	(1L<<9)	  /*	 Bit  9:	Burst Disable	     */
+#define	PCI_BYTE_SWAP	(1L<<8)	  /*(DV) Bit  8:	Byte Swap in DATA    */
+#define	PCI_SKEW_DAS	(0xfL<<4) /*	 Bit 7..4:	Skew Ctrl, DAS Ext   */
+#define	PCI_SKEW_BASE	(0xfL<<0) /*	 Bit 3..0:	Skew Ctrl, Base	     */
+
+/*	PCI_OUR_REG_2	(ML)	32 bit	Our Register 2 (Monalisa only) */
+#define PCI_VPD_WR_TH	(0xffL<<24)	/* Bit 24..31	VPD Write Threshold  */
+#define	PCI_DEV_SEL	(0x7fL<<17)	/* Bit 17..23	EEPROM Device Select */
+#define	PCI_VPD_ROM_SZ	(7L<<14)	/* Bit 14..16	VPD ROM Size	     */
+					/* Bit 12..13	reserved	     */
+#define	PCI_PATCH_DIR2	(0xfL<<8)	/* Bit  8..11	Ext Patchs dir 2..5  */
+#define	PCI_PATCH_DIR_2	(1L<<8)		/* Bit  8	CS for MicroWire     */
+#define	PCI_PATCH_DIR_3	(1L<<9)
+#define	PCI_PATCH_DIR_4	(1L<<10)
+#define	PCI_PATCH_DIR_5	(1L<<11)
+#define PCI_EXT_PATCHS2 (0xfL<<4)	/* Bit  4..7	Extended Patches     */
+#define	PCI_EXT_PATCH_2	(1L<<4)		/* Bit  4	CS for MicroWire     */
+#define	PCI_EXT_PATCH_3	(1L<<5)
+#define	PCI_EXT_PATCH_4	(1L<<6)
+#define	PCI_EXT_PATCH_5	(1L<<7)
+#define	PCI_EN_DUMMY_RD	(1L<<3)		/* Bit  3	Enable Dummy Read    */
+#define PCI_REV_DESC	(1L<<2)		/* Bit  2	Reverse Desc. Bytes  */
+#define PCI_USEADDR64	(1L<<1)		/* Bit  1	Use 64 Bit Addresse  */
+#define PCI_USEDATA64	(1L<<0)		/* Bit  0	Use 64 Bit Data bus ext*/
+
+/* Power Management Region */
+/*	PCI_PM_CAP_ID		 8 bit (ML)	Power Management Cap. ID */
+/*	PCI_PM_NITEM		 8 bit (ML)	Next Item Ptr */
+/*	PCI_PM_CAP_REG		16 bit (ML)	Power Management Capabilities*/
+#define	PCI_PME_SUP	(0x1f<<11)	/* Bit 11..15	PM Manag. Event Support*/
+#define PCI_PM_D2_SUB	(1<<10)		/* Bit 10	D2 Support Bit	     */
+#define PCI_PM_D1_SUB	(1<<9)		/* Bit 9	D1 Support Bit       */
+					/* Bit 6..8 reserved		     */
+#define PCI_PM_DSI	(1<<5)		/* Bit 5	Device Specific Init.*/
+#define PCI_PM_APS	(1<<4)		/* Bit 4	Auxialiary Power Src */
+#define PCI_PME_CLOCK	(1<<3)		/* Bit 3	PM Event Clock       */
+#define PCI_PM_VER	(7<<0)		/* Bit 0..2	PM PCI Spec. version */
+
+/*	PCI_PM_CTL_STS		16 bit (ML)	Power Manag. Control/Status  */
+#define	PCI_PME_STATUS	(1<<15)		/* Bit 15 	PFA doesn't sup. PME#*/
+#define PCI_PM_DAT_SCL	(3<<13)		/* Bit 13..14	dat reg Scaling factor */
+#define PCI_PM_DAT_SEL	(0xf<<9)	/* Bit  9..12	PM data selector field */
+					/* Bit  7.. 2	reserved	     */
+#define PCI_PM_STATE	(3<<0)		/* Bit  0.. 1	Power Management State */
+#define PCI_PM_STATE_D0	(0<<0)		/* D0:	Operational (default) */
+#define	PCI_PM_STATE_D1	(1<<0)		/* D1:	not supported */
+#define PCI_PM_STATE_D2	(2<<0)		/* D2:	not supported */
+#define PCI_PM_STATE_D3 (3<<0)		/* D3:	HOT, Power Down and Reset */
+
+/*	PCI_PM_DAT_REG		 8 bit (ML)	Power Manag. Data Register */
+/* VPD Region */
+/*	PCI_VPD_CAP_ID		 8 bit (ML)	VPD Cap. ID */
+/*	PCI_VPD_NITEM		 8 bit (ML)	Next Item Ptr */
+/*	PCI_VPD_ADR_REG		16 bit (ML)	VPD Address Register */
+#define	PCI_VPD_FLAG	(1<<15)		/* Bit 15	starts VPD rd/wd cycle*/
+#define PCI_VPD_ADDR	(0x3fff<<0)	/* Bit  0..14	VPD address */
+
+/*	PCI_VPD_DAT_REG		32 bit (ML)	VPD Data Register */
+
+/*
+ *	Control Register File:
+ *	Bank 0
+ */
+#define	B0_RAP		0x0000	/*  8 bit register address port */
+	/* 0x0001 - 0x0003:	reserved */
+#define	B0_CTRL		0x0004	/*  8 bit control register */
+#define	B0_DAS		0x0005	/*  8 Bit control register (DAS) */
+#define	B0_LED		0x0006	/*  8 Bit LED register */
+#define	B0_TST_CTRL	0x0007	/*  8 bit test control register */
+#define	B0_ISRC		0x0008	/* 32 bit Interrupt source register */
+#define	B0_IMSK		0x000c	/* 32 bit Interrupt mask register */
+
+/* 0x0010 - 0x006b:	formac+ (supernet_3) fequently used registers */
+#define B0_CMDREG1	0x0010	/* write command reg 1 instruction */
+#define B0_CMDREG2	0x0014	/* write command reg 2 instruction */
+#define B0_ST1U		0x0010	/* read upper 16-bit of status reg 1 */
+#define B0_ST1L		0x0014	/* read lower 16-bit of status reg 1 */
+#define B0_ST2U		0x0018	/* read upper 16-bit of status reg 2 */
+#define B0_ST2L		0x001c	/* read lower 16-bit of status reg 2 */
+
+#define B0_MARR		0x0020	/* r/w the memory read addr register */
+#define B0_MARW		0x0024	/* r/w the memory write addr register*/
+#define B0_MDRU		0x0028	/* r/w upper 16-bit of mem. data reg */
+#define B0_MDRL		0x002c	/* r/w lower 16-bit of mem. data reg */
+
+#define	B0_MDREG3	0x0030	/* r/w Mode Register 3 */
+#define	B0_ST3U		0x0034	/* read upper 16-bit of status reg 3 */
+#define	B0_ST3L		0x0038	/* read lower 16-bit of status reg 3 */
+#define	B0_IMSK3U	0x003c	/* r/w upper 16-bit of IMSK reg 3 */
+#define	B0_IMSK3L	0x0040	/* r/w lower 16-bit of IMSK reg 3 */
+#define	B0_IVR		0x0044	/* read Interrupt Vector register */
+#define	B0_IMR		0x0048	/* r/w Interrupt mask register */
+/* 0x4c	Hidden */
+
+#define B0_CNTRL_A	0x0050	/* control register A (r/w) */
+#define B0_CNTRL_B	0x0054	/* control register B (r/w) */
+#define B0_INTR_MASK	0x0058	/* interrupt mask (r/w) */
+#define B0_XMIT_VECTOR	0x005c	/* transmit vector register (r/w) */
+
+#define B0_STATUS_A	0x0060	/* status register A (read only) */
+#define B0_STATUS_B	0x0064	/* status register B (read only) */
+#define B0_CNTRL_C	0x0068	/* control register C (r/w) */
+#define	B0_MDREG1	0x006c	/* r/w Mode Register 1 */
+
+#define	B0_R1_CSR	0x0070	/* 32 bit BMU control/status reg (rec q 1) */
+#define	B0_R2_CSR	0x0074	/* 32 bit BMU control/status reg (rec q 2)(DV)*/
+#define	B0_XA_CSR	0x0078	/* 32 bit BMU control/status reg (a xmit q) */
+#define	B0_XS_CSR	0x007c	/* 32 bit BMU control/status reg (s xmit q) */
+
+/*
+ *	Bank 1
+ *	- completely empty (this is the RAP Block window)
+ *	Note: if RAP = 1 this page is reserved
+ */
+
+/*
+ *	Bank 2
+ */
+#define	B2_MAC_0	0x0100	/*  8 bit MAC address Byte 0 */
+#define	B2_MAC_1	0x0101	/*  8 bit MAC address Byte 1 */
+#define	B2_MAC_2	0x0102	/*  8 bit MAC address Byte 2 */
+#define	B2_MAC_3	0x0103	/*  8 bit MAC address Byte 3 */
+#define	B2_MAC_4	0x0104	/*  8 bit MAC address Byte 4 */
+#define	B2_MAC_5	0x0105	/*  8 bit MAC address Byte 5 */
+#define	B2_MAC_6	0x0106	/*  8 bit MAC address Byte 6 (== 0) (DV) */
+#define	B2_MAC_7	0x0107	/*  8 bit MAC address Byte 7 (== 0) (DV) */
+
+#define B2_CONN_TYP	0x0108	/*  8 bit Connector type */
+#define B2_PMD_TYP	0x0109	/*  8 bit PMD type */
+				/* 0x010a - 0x010b:	reserved */
+	/* Eprom registers are currently of no use */
+#define B2_E_0		0x010c	/*  8 bit EPROM Byte 0 */
+#define B2_E_1		0x010d	/*  8 bit EPROM Byte 1 */
+#define B2_E_2		0x010e	/*  8 bit EPROM Byte 2 */
+#define B2_E_3		0x010f	/*  8 bit EPROM Byte 3 */
+#define B2_FAR		0x0110	/* 32 bit Flash-Prom Address Register/Counter */
+#define B2_FDP		0x0114	/*  8 bit Flash-Prom Data Port */
+				/* 0x0115 - 0x0117:	reserved */
+#define B2_LD_CRTL	0x0118	/*  8 bit loader control */
+#define B2_LD_TEST	0x0119	/*  8 bit loader test */
+				/* 0x011a - 0x011f:	reserved */
+#define B2_TI_INI	0x0120	/* 32 bit Timer init value */
+#define B2_TI_VAL	0x0124	/* 32 bit Timer value */
+#define B2_TI_CRTL	0x0128	/*  8 bit Timer control */
+#define B2_TI_TEST	0x0129	/*  8 Bit Timer Test */
+				/* 0x012a - 0x012f:	reserved */
+#define B2_WDOG_INI	0x0130	/* 32 bit Watchdog init value */
+#define B2_WDOG_VAL	0x0134	/* 32 bit Watchdog value */
+#define B2_WDOG_CRTL	0x0138	/*  8 bit Watchdog control */
+#define B2_WDOG_TEST	0x0139	/*  8 Bit Watchdog Test */
+				/* 0x013a - 0x013f:	reserved */
+#define B2_RTM_INI	0x0140	/* 32 bit RTM init value */
+#define B2_RTM_VAL	0x0144	/* 32 bit RTM value */
+#define B2_RTM_CRTL	0x0148	/*  8 bit RTM control */
+#define B2_RTM_TEST	0x0149	/*  8 Bit RTM Test */
+
+#define B2_TOK_COUNT	0x014c	/* (ML)	32 bit	Token Counter */
+#define B2_DESC_ADDR_H	0x0150	/* (ML) 32 bit	Desciptor Base Addr Reg High */
+#define B2_CTRL_2	0x0154	/* (ML)	 8 bit	Control Register 2 */
+#define B2_IFACE_REG	0x0155	/* (ML)	 8 bit	Interface Register */
+				/* 0x0156:		reserved */
+#define B2_TST_CTRL_2	0x0157	/* (ML)  8 bit	Test Control Register 2 */
+#define B2_I2C_CTRL	0x0158	/* (ML)	32 bit	I2C Control Register */
+#define B2_I2C_DATA	0x015c	/* (ML) 32 bit	I2C Data Register */
+
+#define B2_IRQ_MOD_INI	0x0160	/* (ML) 32 bit	IRQ Moderation Timer Init Reg. */
+#define B2_IRQ_MOD_VAL	0x0164	/* (ML)	32 bit	IRQ Moderation Timer Value */
+#define B2_IRQ_MOD_CTRL	0x0168	/* (ML)  8 bit	IRQ Moderation Timer Control */
+#define B2_IRQ_MOD_TEST	0x0169	/* (ML)	 8 bit	IRQ Moderation Timer Test */
+				/* 0x016a - 0x017f:	reserved */
+
+/*
+ *	Bank 3
+ */
+/*
+ * This is a copy of the Configuration register file (lower half)
+ */
+#define B3_CFG_SPC	0x180
+
+/*
+ *	Bank 4
+ */
+#define B4_R1_D		0x0200	/* 	4*32 bit current receive Descriptor  */
+#define B4_R1_DA	0x0210	/* 	32 bit current rec desc address	     */
+#define B4_R1_AC	0x0214	/* 	32 bit current receive Address Count */
+#define B4_R1_BC	0x0218	/*	32 bit current receive Byte Counter  */
+#define B4_R1_CSR	0x021c	/* 	32 bit BMU Control/Status Register   */
+#define B4_R1_F		0x0220	/* 	32 bit flag register		     */
+#define B4_R1_T1	0x0224	/* 	32 bit Test Register 1		     */
+#define B4_R1_T1_TR	0x0224	/* 	8 bit Test Register 1 TR	     */
+#define B4_R1_T1_WR	0x0225	/* 	8 bit Test Register 1 WR	     */
+#define B4_R1_T1_RD	0x0226	/* 	8 bit Test Register 1 RD	     */
+#define B4_R1_T1_SV	0x0227	/* 	8 bit Test Register 1 SV	     */
+#define B4_R1_T2	0x0228	/* 	32 bit Test Register 2		     */
+#define B4_R1_T3	0x022c	/* 	32 bit Test Register 3		     */
+#define B4_R1_DA_H	0x0230	/* (ML)	32 bit Curr Rx Desc Address High     */
+#define B4_R1_AC_H	0x0234	/* (ML)	32 bit Curr Addr Counter High dword  */
+				/* 0x0238 - 0x023f:	reserved	  */
+				/* Receive queue 2 is removed on Monalisa */
+#define B4_R2_D		0x0240	/* 4*32 bit current receive Descriptor	(q2) */
+#define B4_R2_DA	0x0250	/* 32 bit current rec desc address	(q2) */
+#define B4_R2_AC	0x0254	/* 32 bit current receive Address Count	(q2) */
+#define B4_R2_BC	0x0258	/* 32 bit current receive Byte Counter	(q2) */
+#define B4_R2_CSR	0x025c	/* 32 bit BMU Control/Status Register	(q2) */
+#define B4_R2_F		0x0260	/* 32 bit flag register			(q2) */
+#define B4_R2_T1	0x0264	/* 32 bit Test Register 1		(q2) */
+#define B4_R2_T1_TR	0x0264	/* 8 bit Test Register 1 TR		(q2) */
+#define B4_R2_T1_WR	0x0265	/* 8 bit Test Register 1 WR		(q2) */
+#define B4_R2_T1_RD	0x0266	/* 8 bit Test Register 1 RD		(q2) */
+#define B4_R2_T1_SV	0x0267	/* 8 bit Test Register 1 SV		(q2) */
+#define B4_R2_T2	0x0268	/* 32 bit Test Register 2		(q2) */
+#define B4_R2_T3	0x026c	/* 32 bit Test Register 3		(q2) */
+				/* 0x0270 - 0x027c:	reserved */
+
+/*
+ *	Bank 5
+ */
+#define B5_XA_D		0x0280	/* 4*32 bit current transmit Descriptor	(xa) */
+#define B5_XA_DA	0x0290	/* 32 bit current tx desc address	(xa) */
+#define B5_XA_AC	0x0294	/* 32 bit current tx Address Count	(xa) */
+#define B5_XA_BC	0x0298	/* 32 bit current tx Byte Counter	(xa) */
+#define B5_XA_CSR	0x029c	/* 32 bit BMU Control/Status Register	(xa) */
+#define B5_XA_F		0x02a0	/* 32 bit flag register			(xa) */
+#define B5_XA_T1	0x02a4	/* 32 bit Test Register 1		(xa) */
+#define B5_XA_T1_TR	0x02a4	/* 8 bit Test Register 1 TR		(xa) */
+#define B5_XA_T1_WR	0x02a5	/* 8 bit Test Register 1 WR		(xa) */
+#define B5_XA_T1_RD	0x02a6	/* 8 bit Test Register 1 RD		(xa) */
+#define B5_XA_T1_SV	0x02a7	/* 8 bit Test Register 1 SV		(xa) */
+#define B5_XA_T2	0x02a8	/* 32 bit Test Register 2		(xa) */
+#define B5_XA_T3	0x02ac	/* 32 bit Test Register 3		(xa) */
+#define B5_XA_DA_H	0x02b0	/* (ML)	32 bit Curr Tx Desc Address High     */
+#define B5_XA_AC_H	0x02b4	/* (ML)	32 bit Curr Addr Counter High dword  */
+				/* 0x02b8 - 0x02bc:	reserved */
+#define B5_XS_D		0x02c0	/* 4*32 bit current transmit Descriptor	(xs) */
+#define B5_XS_DA	0x02d0	/* 32 bit current tx desc address	(xs) */
+#define B5_XS_AC	0x02d4	/* 32 bit current transmit Address Count(xs) */
+#define B5_XS_BC	0x02d8	/* 32 bit current transmit Byte Counter	(xs) */
+#define B5_XS_CSR	0x02dc	/* 32 bit BMU Control/Status Register	(xs) */
+#define B5_XS_F		0x02e0	/* 32 bit flag register			(xs) */
+#define B5_XS_T1	0x02e4	/* 32 bit Test Register 1		(xs) */
+#define B5_XS_T1_TR	0x02e4	/* 8 bit Test Register 1 TR		(xs) */
+#define B5_XS_T1_WR	0x02e5	/* 8 bit Test Register 1 WR		(xs) */
+#define B5_XS_T1_RD	0x02e6	/* 8 bit Test Register 1 RD		(xs) */
+#define B5_XS_T1_SV	0x02e7	/* 8 bit Test Register 1 SV		(xs) */
+#define B5_XS_T2	0x02e8	/* 32 bit Test Register 2		(xs) */
+#define B5_XS_T3	0x02ec	/* 32 bit Test Register 3		(xs) */
+#define B5_XS_DA_H	0x02f0	/* (ML)	32 bit Curr Tx Desc Address High     */
+#define B5_XS_AC_H	0x02f4	/* (ML)	32 bit Curr Addr Counter High dword  */
+				/* 0x02f8 - 0x02fc:	reserved */
+
+/*
+ *	Bank 6
+ */
+/* External PLC-S registers (SN2 compatibility for DV) */
+/* External registers (ML) */
+#define B6_EXT_REG	0x300
+
+/*
+ *	Bank 7
+ */
+/* DAS PLC-S Registers */
+
+/*
+ *	Bank 8 - 15
+ */
+/* IFCP registers */
+
+/*---------------------------------------------------------------------------*/
+/* Definitions of the Bits in the registers */
+
+/*	B0_RAP		16 bit register address port */
+#define	RAP_RAP		0x0f	/* Bit 3..0:	0 = block0, .., f = block15 */
+
+/*	B0_CTRL		8 bit control register */
+#define CTRL_FDDI_CLR	(1<<7)	/* Bit 7: (ML)	Clear FDDI Reset */
+#define CTRL_FDDI_SET	(1<<6)	/* Bit 6: (ML)	Set FDDI Reset */
+#define	CTRL_HPI_CLR	(1<<5)	/* Bit 5:	Clear HPI SM reset */
+#define	CTRL_HPI_SET	(1<<4)	/* Bit 4:	Set HPI SM reset */
+#define	CTRL_MRST_CLR	(1<<3)	/* Bit 3:	Clear Master reset */
+#define	CTRL_MRST_SET	(1<<2)	/* Bit 2:	Set Master reset */
+#define	CTRL_RST_CLR	(1<<1)	/* Bit 1:	Clear Software reset */
+#define	CTRL_RST_SET	(1<<0)	/* Bit 0:	Set Software reset */
+
+/*	B0_DAS		8 Bit control register (DAS) */
+#define BUS_CLOCK	(1<<7)	/* Bit 7: (ML)	Bus Clock 0/1 = 33/66MHz */
+#define BUS_SLOT_SZ	(1<<6)	/* Bit 6: (ML)	Slot Size 0/1 = 32/64 bit slot*/
+				/* Bit 5..4:	reserved */
+#define	DAS_AVAIL	(1<<3)	/* Bit 3:	1 = DAS, 0 = SAS */
+#define DAS_BYP_ST	(1<<2)	/* Bit 2:	1 = avail,SAS, 0 = not avail */
+#define DAS_BYP_INS	(1<<1)	/* Bit 1:	1 = insert Bypass */
+#define DAS_BYP_RMV	(1<<0)	/* Bit 0:	1 = remove Bypass */
+
+/*	B0_LED		8 Bit LED register */
+				/* Bit 7..6:	reserved */
+#define LED_2_ON	(1<<5)	/* Bit 5:	1 = switch LED_2 on (left,gn)*/
+#define LED_2_OFF	(1<<4)	/* Bit 4:	1 = switch LED_2 off */
+#define LED_1_ON	(1<<3)	/* Bit 3:	1 = switch LED_1 on (mid,yel)*/
+#define LED_1_OFF	(1<<2)	/* Bit 2:	1 = switch LED_1 off */
+#define LED_0_ON	(1<<1)	/* Bit 1:	1 = switch LED_0 on (rght,gn)*/
+#define LED_0_OFF	(1<<0)	/* Bit 0:	1 = switch LED_0 off */
+/* This hardware defines are very ugly therefore we define some others */
+
+#define LED_GA_ON	LED_2_ON	/* S port = A port */
+#define LED_GA_OFF	LED_2_OFF	/* S port = A port */
+#define LED_MY_ON	LED_1_ON
+#define LED_MY_OFF	LED_1_OFF
+#define LED_GB_ON	LED_0_ON
+#define LED_GB_OFF	LED_0_OFF
+
+/*	B0_TST_CTRL	8 bit test control register */
+#define	TST_FRC_DPERR_MR	(1<<7)	/* Bit 7:  force DATAPERR on MST RE. */
+#define	TST_FRC_DPERR_MW	(1<<6)	/* Bit 6:  force DATAPERR on MST WR. */
+#define	TST_FRC_DPERR_TR	(1<<5)	/* Bit 5:  force DATAPERR on TRG RE. */
+#define	TST_FRC_DPERR_TW	(1<<4)	/* Bit 4:  force DATAPERR on TRG WR. */
+#define	TST_FRC_APERR_M		(1<<3)	/* Bit 3:  force ADDRPERR on MST     */
+#define	TST_FRC_APERR_T		(1<<2)	/* Bit 2:  force ADDRPERR on TRG     */
+#define	TST_CFG_WRITE_ON	(1<<1)	/* Bit 1:  ena configuration reg. WR */
+#define	TST_CFG_WRITE_OFF	(1<<0)	/* Bit 0:  dis configuration reg. WR */
+
+/*	B0_ISRC		32 bit Interrupt source register */
+					/* Bit 31..28:	reserved	     */
+#define IS_I2C_READY	(1L<<27)	/* Bit 27: (ML)	IRQ on end of I2C tx */
+#define IS_IRQ_SW	(1L<<26)	/* Bit 26: (ML)	SW forced IRQ	     */
+#define IS_EXT_REG	(1L<<25)	/* Bit 25: (ML) IRQ from external reg*/
+#define	IS_IRQ_STAT	(1L<<24)	/* Bit 24:	IRQ status execption */
+					/*   PERR, RMABORT, RTABORT DATAPERR */
+#define	IS_IRQ_MST_ERR	(1L<<23)	/* Bit 23:	IRQ master error     */
+					/*   RMABORT, RTABORT, DATAPERR	     */
+#define	IS_TIMINT	(1L<<22)	/* Bit 22:	IRQ_TIMER	*/
+#define	IS_TOKEN	(1L<<21)	/* Bit 21:	IRQ_RTM		*/
+/*
+ * Note: The DAS is our First Port (!=PA)
+ */
+#define	IS_PLINT1	(1L<<20)	/* Bit 20:	IRQ_PHY_DAS	*/
+#define	IS_PLINT2	(1L<<19)	/* Bit 19:	IRQ_IFCP_4	*/
+#define	IS_MINTR3	(1L<<18)	/* Bit 18:	IRQ_IFCP_3/IRQ_PHY */
+#define	IS_MINTR2	(1L<<17)	/* Bit 17:	IRQ_IFCP_2/IRQ_MAC_2 */
+#define	IS_MINTR1	(1L<<16)	/* Bit 16:	IRQ_IFCP_1/IRQ_MAC_1 */
+/* Receive Queue 1 */
+#define	IS_R1_P		(1L<<15)	/* Bit 15:	Parity Error (q1) */
+#define	IS_R1_B		(1L<<14)	/* Bit 14:	End of Buffer (q1) */
+#define	IS_R1_F		(1L<<13)	/* Bit 13:	End of Frame (q1) */
+#define	IS_R1_C		(1L<<12)	/* Bit 12:	Encoding Error (q1) */
+/* Receive Queue 2 */
+#define	IS_R2_P		(1L<<11)	/* Bit 11: (DV)	Parity Error (q2) */
+#define	IS_R2_B		(1L<<10)	/* Bit 10: (DV)	End of Buffer (q2) */
+#define	IS_R2_F		(1L<<9)		/* Bit	9: (DV)	End of Frame (q2) */
+#define	IS_R2_C		(1L<<8)		/* Bit	8: (DV)	Encoding Error (q2) */
+/* Asynchronous Transmit queue */
+					/* Bit  7:	reserved */
+#define	IS_XA_B		(1L<<6)		/* Bit	6:	End of Buffer (xa) */
+#define	IS_XA_F		(1L<<5)		/* Bit	5:	End of Frame (xa) */
+#define	IS_XA_C		(1L<<4)		/* Bit	4:	Encoding Error (xa) */
+/* Synchronous Transmit queue */
+					/* Bit  3:	reserved */
+#define	IS_XS_B		(1L<<2)		/* Bit	2:	End of Buffer (xs) */
+#define	IS_XS_F		(1L<<1)		/* Bit	1:	End of Frame (xs) */
+#define	IS_XS_C		(1L<<0)		/* Bit	0:	Encoding Error (xs) */
+
+/*
+ * Define all valid interrupt source Bits from GET_ISR ()
+ */
+#define	ALL_IRSR	0x01ffff77L	/* (DV) */
+#define	ALL_IRSR_ML	0x0ffff077L	/* (ML) */
+
+
+/*	B0_IMSK		32 bit Interrupt mask register */
+/*
+ * The Bit definnition of this register are the same as of the interrupt
+ * source register. These definition are directly derived from the Hardware
+ * spec.
+ */
+					/* Bit 31..28:	reserved	     */
+#define IRQ_I2C_READY	(1L<<27)	/* Bit 27: (ML)	IRQ on end of I2C tx */
+#define IRQ_SW		(1L<<26)	/* Bit 26: (ML)	SW forced IRQ	     */
+#define IRQ_EXT_REG	(1L<<25)	/* Bit 25: (ML) IRQ from external reg*/
+#define	IRQ_STAT	(1L<<24)	/* Bit 24:	IRQ status execption */
+					/*   PERR, RMABORT, RTABORT DATAPERR */
+#define	IRQ_MST_ERR	(1L<<23)	/* Bit 23:	IRQ master error     */
+					/*   RMABORT, RTABORT, DATAPERR	     */
+#define	IRQ_TIMER	(1L<<22)	/* Bit 22:	IRQ_TIMER	*/
+#define	IRQ_RTM		(1L<<21)	/* Bit 21:	IRQ_RTM		*/
+#define	IRQ_DAS		(1L<<20)	/* Bit 20:	IRQ_PHY_DAS	*/
+#define	IRQ_IFCP_4	(1L<<19)	/* Bit 19:	IRQ_IFCP_4	*/
+#define	IRQ_IFCP_3	(1L<<18)	/* Bit 18:	IRQ_IFCP_3/IRQ_PHY */
+#define	IRQ_IFCP_2	(1L<<17)	/* Bit 17:	IRQ_IFCP_2/IRQ_MAC_2 */
+#define	IRQ_IFCP_1	(1L<<16)	/* Bit 16:	IRQ_IFCP_1/IRQ_MAC_1 */
+/* Receive Queue 1 */
+#define	IRQ_R1_P	(1L<<15)	/* Bit 15:	Parity Error (q1) */
+#define	IRQ_R1_B	(1L<<14)	/* Bit 14:	End of Buffer (q1) */
+#define	IRQ_R1_F	(1L<<13)	/* Bit 13:	End of Frame (q1) */
+#define	IRQ_R1_C	(1L<<12)	/* Bit 12:	Encoding Error (q1) */
+/* Receive Queue 2 */
+#define	IRQ_R2_P	(1L<<11)	/* Bit 11: (DV)	Parity Error (q2) */
+#define	IRQ_R2_B	(1L<<10)	/* Bit 10: (DV)	End of Buffer (q2) */
+#define	IRQ_R2_F	(1L<<9)		/* Bit	9: (DV)	End of Frame (q2) */
+#define	IRQ_R2_C	(1L<<8)		/* Bit	8: (DV)	Encoding Error (q2) */
+/* Asynchronous Transmit queue */
+					/* Bit  7:	reserved */
+#define	IRQ_XA_B	(1L<<6)		/* Bit	6:	End of Buffer (xa) */
+#define	IRQ_XA_F	(1L<<5)		/* Bit	5:	End of Frame (xa) */
+#define	IRQ_XA_C	(1L<<4)		/* Bit	4:	Encoding Error (xa) */
+/* Synchronous Transmit queue */
+					/* Bit  3:	reserved */
+#define	IRQ_XS_B	(1L<<2)		/* Bit	2:	End of Buffer (xs) */
+#define	IRQ_XS_F	(1L<<1)		/* Bit	1:	End of Frame (xs) */
+#define	IRQ_XS_C	(1L<<0)		/* Bit	0:	Encoding Error (xs) */
+
+/* 0x0010 - 0x006b:	formac+ (supernet_3) fequently used registers */
+/*	B0_R1_CSR	32 bit BMU control/status reg (rec q 1 ) */
+/*	B0_R2_CSR	32 bit BMU control/status reg (rec q 2 ) */
+/*	B0_XA_CSR	32 bit BMU control/status reg (a xmit q ) */
+/*	B0_XS_CSR	32 bit BMU control/status reg (s xmit q ) */
+/* The registers are the same as B4_R1_CSR, B4_R2_CSR, B5_Xa_CSR, B5_XS_CSR */
+
+/*	B2_MAC_0	8 bit MAC address Byte 0 */
+/*	B2_MAC_1	8 bit MAC address Byte 1 */
+/*	B2_MAC_2	8 bit MAC address Byte 2 */
+/*	B2_MAC_3	8 bit MAC address Byte 3 */
+/*	B2_MAC_4	8 bit MAC address Byte 4 */
+/*	B2_MAC_5	8 bit MAC address Byte 5 */
+/*	B2_MAC_6	8 bit MAC address Byte 6 (== 0) (DV) */
+/*	B2_MAC_7	8 bit MAC address Byte 7 (== 0) (DV) */
+
+/*	B2_CONN_TYP	8 bit Connector type */
+/*	B2_PMD_TYP	8 bit PMD type */
+/*	Values of connector and PMD type comply to SysKonnect internal std */
+
+/*	The EPROM register are currently of no use */
+/*	B2_E_0		8 bit EPROM Byte 0 */
+/*	B2_E_1		8 bit EPROM Byte 1 */
+/*	B2_E_2		8 bit EPROM Byte 2 */
+/*	B2_E_3		8 bit EPROM Byte 3 */
+
+/*	B2_FAR		32 bit Flash-Prom Address Register/Counter */
+#define	FAR_ADDR	0x1ffffL	/* Bit 16..0:	FPROM Address mask */
+
+/*	B2_FDP		8 bit Flash-Prom Data Port */
+
+/*	B2_LD_CRTL	8 bit loader control */
+/*	Bits are currently reserved */
+
+/*	B2_LD_TEST	8 bit loader test */
+#define	LD_T_ON		(1<<3)	/* Bit 3:    Loader Testmode on */
+#define	LD_T_OFF	(1<<2)	/* Bit 2:    Loader Testmode off */
+#define	LD_T_STEP	(1<<1)	/* Bit 1:    Decrement FPROM addr. Counter */
+#define	LD_START	(1<<0)	/* Bit 0:    Start loading FPROM */
+
+/*	B2_TI_INI	32 bit Timer init value */
+/*	B2_TI_VAL	32 bit Timer value */
+/*	B2_TI_CRTL	8 bit Timer control */
+/*	B2_TI_TEST	8 Bit Timer Test */
+/*	B2_WDOG_INI	32 bit Watchdog init value */
+/*	B2_WDOG_VAL	32 bit Watchdog value */
+/*	B2_WDOG_CRTL	8 bit Watchdog control */
+/*	B2_WDOG_TEST	8 Bit Watchdog Test */
+/*	B2_RTM_INI	32 bit RTM init value */
+/*	B2_RTM_VAL	32 bit RTM value */
+/*	B2_RTM_CRTL	8 bit RTM control */
+/*	B2_RTM_TEST	8 Bit RTM Test */
+/*	B2_<TIM>_CRTL	8 bit <TIM> control */
+/*	B2_IRQ_MOD_INI	32 bit IRQ Moderation Timer Init Reg.	(ML) */
+/*	B2_IRQ_MOD_VAL	32 bit IRQ Moderation Timer Value	(ML) */
+/*	B2_IRQ_MOD_CTRL	8 bit IRQ Moderation Timer Control	(ML) */
+/*	B2_IRQ_MOD_TEST	8 bit IRQ Moderation Timer Test		(ML) */
+#define GET_TOK_CT	(1<<4)	/* Bit 4: Get the Token Counter (RTM) */
+#define TIM_RES_TOK	(1<<3)	/* Bit 3: RTM Status: 1 == restricted */
+#define TIM_ALARM	(1<<3)	/* Bit 3: Timer Alarm (WDOG) */
+#define TIM_START	(1<<2)	/* Bit 2: Start Timer (TI,WDOG,RTM,IRQ_MOD)*/
+#define TIM_STOP	(1<<1)	/* Bit 1: Stop Timer (TI,WDOG,RTM,IRQ_MOD) */
+#define TIM_CL_IRQ	(1<<0)	/* Bit 0: Clear Timer IRQ (TI,WDOG,RTM) */
+/*	B2_<TIM>_TEST	8 Bit <TIM> Test */
+#define	TIM_T_ON	(1<<2)	/* Bit 2: Test mode on (TI,WDOG,RTM,IRQ_MOD) */
+#define	TIM_T_OFF	(1<<1)	/* Bit 1: Test mode off (TI,WDOG,RTM,IRQ_MOD) */
+#define	TIM_T_STEP	(1<<0)	/* Bit 0: Test step (TI,WDOG,RTM,IRQ_MOD) */
+
+/*	B2_TOK_COUNT	0x014c	(ML)	32 bit	Token Counter */
+/*	B2_DESC_ADDR_H	0x0150	(ML)	32 bit	Desciptor Base Addr Reg High */
+/*	B2_CTRL_2	0x0154	(ML)	 8 bit	Control Register 2 */
+				/* Bit 7..5:	reserved		*/
+#define CTRL_CL_I2C_IRQ (1<<4)	/* Bit 4:	Clear I2C IRQ		*/
+#define CTRL_ST_SW_IRQ	(1<<3)	/* Bit 3:	Set IRQ SW Request	*/
+#define CTRL_CL_SW_IRQ	(1<<2)	/* Bit 2:	Clear IRQ SW Request	*/
+#define CTRL_STOP_DONE	(1<<1)	/* Bit 1:	Stop Master is finished */
+#define	CTRL_STOP_MAST	(1<<0)	/* Bit 0:	Command Bit to stop the master*/
+
+/*	B2_IFACE_REG	0x0155	(ML)	 8 bit	Interface Register */
+				/* Bit 7..3:	reserved		*/
+#define	IF_I2C_DATA_DIR	(1<<2)	/* Bit 2:	direction of IF_I2C_DATA*/
+#define IF_I2C_DATA	(1<<1)	/* Bit 1:	I2C Data Port		*/
+#define	IF_I2C_CLK	(1<<0)	/* Bit 0:	I2C Clock Port		*/
+
+				/* 0x0156:		reserved */
+/*	B2_TST_CTRL_2	0x0157	(ML)	 8 bit	Test Control Register 2 */
+					/* Bit 7..4:	reserved */
+					/* force the following error on */
+					/* the next master read/write	*/
+#define TST_FRC_DPERR_MR64	(1<<3)	/* Bit 3:	DataPERR RD 64	*/
+#define TST_FRC_DPERR_MW64	(1<<2)	/* Bit 2:	DataPERR WR 64	*/
+#define TST_FRC_APERR_1M64	(1<<1)	/* Bit 1:	AddrPERR on 1. phase */
+#define TST_FRC_APERR_2M64	(1<<0)	/* Bit 0:	AddrPERR on 2. phase */
+
+/*	B2_I2C_CTRL	0x0158	(ML)	32 bit	I2C Control Register	       */
+#define	I2C_FLAG	(1L<<31)	/* Bit 31:	Start read/write if WR */
+#define I2C_ADDR	(0x7fffL<<16)	/* Bit 30..16:	Addr to be read/written*/
+#define	I2C_DEV_SEL	(0x7fL<<9)	/* Bit  9..15:	I2C Device Select      */
+					/* Bit  5.. 8:	reserved	       */
+#define I2C_BURST_LEN	(1L<<4)		/* Bit  4	Burst Len, 1/4 bytes   */
+#define I2C_DEV_SIZE	(7L<<1)		/* Bit	1.. 3:	I2C Device Size	       */
+#define I2C_025K_DEV	(0L<<1)		/*		0: 256 Bytes or smaller*/
+#define I2C_05K_DEV	(1L<<1)		/* 		1: 512	Bytes	       */
+#define	I2C_1K_DEV	(2L<<1)		/*		2: 1024 Bytes	       */
+#define I2C_2K_DEV	(3L<<1)		/*		3: 2048	Bytes	       */
+#define	I2C_4K_DEV	(4L<<1)		/*		4: 4096 Bytes	       */
+#define	I2C_8K_DEV	(5L<<1)		/*		5: 8192 Bytes	       */
+#define	I2C_16K_DEV	(6L<<1)		/*		6: 16384 Bytes	       */
+#define	I2C_32K_DEV	(7L<<1)		/*		7: 32768 Bytes	       */
+#define I2C_STOP_BIT	(1<<0)		/* Bit  0:	Interrupt I2C transfer */
+
+/*
+ * I2C Addresses
+ *
+ * The temperature sensor and the voltage sensor are on the same I2C bus.
+ * Note: The voltage sensor (Micorwire) will be selected by PCI_EXT_PATCH_1
+ *	 in PCI_OUR_REG 1.
+ */
+#define	I2C_ADDR_TEMP	0x90	/* I2C Address Temperature Sensor */
+
+/*	B2_I2C_DATA	0x015c	(ML)	32 bit	I2C Data Register */
+
+/*	B4_R1_D		4*32 bit current receive Descriptor	(q1) */
+/*	B4_R1_DA	32 bit current rec desc address		(q1) */
+/*	B4_R1_AC	32 bit current receive Address Count	(q1) */
+/*	B4_R1_BC	32 bit current receive Byte Counter	(q1) */
+/*	B4_R1_CSR	32 bit BMU Control/Status Register	(q1) */
+/*	B4_R1_F		32 bit flag register			(q1) */
+/*	B4_R1_T1	32 bit Test Register 1		 	(q1) */
+/*	B4_R1_T2	32 bit Test Register 2		 	(q1) */
+/*	B4_R1_T3	32 bit Test Register 3		 	(q1) */
+/*	B4_R2_D		4*32 bit current receive Descriptor	(q2) */
+/*	B4_R2_DA	32 bit current rec desc address		(q2) */
+/*	B4_R2_AC	32 bit current receive Address Count	(q2) */
+/*	B4_R2_BC	32 bit current receive Byte Counter	(q2) */
+/*	B4_R2_CSR	32 bit BMU Control/Status Register	(q2) */
+/*	B4_R2_F		32 bit flag register			(q2) */
+/*	B4_R2_T1	32 bit Test Register 1			(q2) */
+/*	B4_R2_T2	32 bit Test Register 2			(q2) */
+/*	B4_R2_T3	32 bit Test Register 3			(q2) */
+/*	B5_XA_D		4*32 bit current receive Descriptor	(xa) */
+/*	B5_XA_DA	32 bit current rec desc address		(xa) */
+/*	B5_XA_AC	32 bit current receive Address Count	(xa) */
+/*	B5_XA_BC	32 bit current receive Byte Counter	(xa) */
+/*	B5_XA_CSR	32 bit BMU Control/Status Register	(xa) */
+/*	B5_XA_F		32 bit flag register			(xa) */
+/*	B5_XA_T1	32 bit Test Register 1			(xa) */
+/*	B5_XA_T2	32 bit Test Register 2			(xa) */
+/*	B5_XA_T3	32 bit Test Register 3			(xa) */
+/*	B5_XS_D		4*32 bit current receive Descriptor	(xs) */
+/*	B5_XS_DA	32 bit current rec desc address		(xs) */
+/*	B5_XS_AC	32 bit current receive Address Count	(xs) */
+/*	B5_XS_BC	32 bit current receive Byte Counter	(xs) */
+/*	B5_XS_CSR	32 bit BMU Control/Status Register	(xs) */
+/*	B5_XS_F		32 bit flag register			(xs) */
+/*	B5_XS_T1	32 bit Test Register 1			(xs) */
+/*	B5_XS_T2	32 bit Test Register 2			(xs) */
+/*	B5_XS_T3	32 bit Test Register 3			(xs) */
+/*	B5_<xx>_CSR	32 bit BMU Control/Status Register	(xx) */
+#define	CSR_DESC_CLEAR	(1L<<21)    /* Bit 21:	Clear Reset for Descr */
+#define	CSR_DESC_SET	(1L<<20)    /* Bit 20:	Set Reset for Descr */
+#define	CSR_FIFO_CLEAR	(1L<<19)    /* Bit 19:	Clear Reset for FIFO */
+#define	CSR_FIFO_SET	(1L<<18)    /* Bit 18:	Set Reset for FIFO */
+#define	CSR_HPI_RUN	(1L<<17)    /* Bit 17:	Release HPI SM */
+#define	CSR_HPI_RST	(1L<<16)    /* Bit 16:	Reset HPI SM to Idle */
+#define	CSR_SV_RUN	(1L<<15)    /* Bit 15:	Release Supervisor SM */
+#define	CSR_SV_RST	(1L<<14)    /* Bit 14:	Reset Supervisor SM */
+#define	CSR_DREAD_RUN	(1L<<13)    /* Bit 13:	Release Descr Read SM */
+#define	CSR_DREAD_RST	(1L<<12)    /* Bit 12:	Reset Descr Read SM */
+#define	CSR_DWRITE_RUN	(1L<<11)    /* Bit 11:	Rel. Descr Write SM */
+#define	CSR_DWRITE_RST	(1L<<10)    /* Bit 10:	Reset Descr Write SM */
+#define	CSR_TRANS_RUN	(1L<<9)     /* Bit 9:	Release Transfer SM */
+#define	CSR_TRANS_RST	(1L<<8)     /* Bit 8:	Reset Transfer SM */
+				    /* Bit 7..5: reserved */
+#define	CSR_START	(1L<<4)     /* Bit 4:	Start Rec/Xmit Queue */
+#define	CSR_IRQ_CL_P	(1L<<3)     /* Bit 3:	Clear Parity IRQ, Rcv */
+#define	CSR_IRQ_CL_B	(1L<<2)     /* Bit 2:	Clear EOB IRQ */
+#define	CSR_IRQ_CL_F	(1L<<1)     /* Bit 1:	Clear EOF IRQ */
+#define	CSR_IRQ_CL_C	(1L<<0)     /* Bit 0:	Clear ERR IRQ */
+
+#define CSR_SET_RESET	(CSR_DESC_SET|CSR_FIFO_SET|CSR_HPI_RST|CSR_SV_RST|\
+			CSR_DREAD_RST|CSR_DWRITE_RST|CSR_TRANS_RST)
+#define CSR_CLR_RESET	(CSR_DESC_CLEAR|CSR_FIFO_CLEAR|CSR_HPI_RUN|CSR_SV_RUN|\
+			CSR_DREAD_RUN|CSR_DWRITE_RUN|CSR_TRANS_RUN)
+
+
+/*	B5_<xx>_F	32 bit flag register		 (xx) */
+					/* Bit 28..31:	reserved	      */
+#define F_ALM_FULL	(1L<<27)	/* Bit 27: (ML)	FIFO almost full      */
+#define F_FIFO_EOF	(1L<<26)	/* Bit 26: (ML)	Fag bit in FIFO       */
+#define F_WM_REACHED	(1L<<25)	/* Bit 25: (ML)	Watermark reached     */
+#define F_UP_DW_USED	(1L<<24)	/* Bit 24: (ML) Upper Dword used (bug)*/
+					/* Bit 23: 	reserved	      */
+#define F_FIFO_LEVEL	(0x1fL<<16)	/* Bit 16..22:(ML) # of Qwords in FIFO*/
+					/* Bit  8..15: 	reserved	      */
+#define F_ML_WATER_M	0x0000ffL	/* Bit  0.. 7:(ML) Watermark	      */
+#define	FLAG_WATER	0x00001fL	/* Bit 4..0:(DV) Level of req data tr.*/
+
+/*	B5_<xx>_T1	32 bit Test Register 1		 (xx) */
+/*		Holds four State Machine control Bytes */
+#define	SM_CRTL_SV	(0xffL<<24) /* Bit 31..24:  Control Supervisor SM */
+#define	SM_CRTL_RD	(0xffL<<16) /* Bit 23..16:  Control Read Desc SM */
+#define	SM_CRTL_WR	(0xffL<<8)  /* Bit 15..8:   Control Write Desc SM */
+#define	SM_CRTL_TR	(0xffL<<0)  /* Bit 7..0:    Control Transfer SM */
+
+/*	B4_<xx>_T1_TR	8 bit Test Register 1 TR		(xx) */
+/*	B4_<xx>_T1_WR	8 bit Test Register 1 WR		(xx) */
+/*	B4_<xx>_T1_RD	8 bit Test Register 1 RD		(xx) */
+/*	B4_<xx>_T1_SV	8 bit Test Register 1 SV		(xx) */
+/* The control status byte of each machine looks like ... */
+#define	SM_STATE	0xf0	/* Bit 7..4:	State which shall be loaded */
+#define	SM_LOAD		0x08	/* Bit 3:	Load the SM with SM_STATE */
+#define	SM_TEST_ON	0x04	/* Bit 2:	Switch on SM Test Mode */
+#define	SM_TEST_OFF	0x02	/* Bit 1:	Go off the Test Mode */
+#define	SM_STEP		0x01	/* Bit 0:	Step the State Machine */
+
+/* The coding of the states */
+#define	SM_SV_IDLE	0x0	/* Supervisor	Idle		Tr/Re	     */
+#define	SM_SV_RES_START	0x1	/* Supervisor	Res_Start	Tr/Re	     */
+#define	SM_SV_GET_DESC	0x3	/* Supervisor	Get_Desc	Tr/Re	     */
+#define	SM_SV_CHECK	0x2	/* Supervisor	Check		Tr/Re	     */
+#define	SM_SV_MOV_DATA	0x6	/* Supervisor	Move_Data	Tr/Re	     */
+#define	SM_SV_PUT_DESC	0x7	/* Supervisor	Put_Desc	Tr/Re	     */
+#define	SM_SV_SET_IRQ	0x5	/* Supervisor	Set_Irq		Tr/Re	     */
+
+#define	SM_RD_IDLE	0x0	/* Read Desc.	Idle		Tr/Re	     */
+#define	SM_RD_LOAD	0x1	/* Read Desc.	Load		Tr/Re	     */
+#define	SM_RD_WAIT_TC	0x3	/* Read Desc.	Wait_TC		Tr/Re	     */
+#define	SM_RD_RST_EOF	0x6	/* Read Desc.	Reset_EOF	   Re	     */
+#define	SM_RD_WDONE_R	0x2	/* Read Desc.	Wait_Done	   Re	     */
+#define	SM_RD_WDONE_T	0x4	/* Read Desc.	Wait_Done	Tr   	     */
+
+#define	SM_TR_IDLE	0x0	/* Trans. Data	Idle		Tr/Re	     */
+#define	SM_TR_LOAD	0x3	/* Trans. Data	Load		Tr/Re	     */
+#define	SM_TR_LOAD_R_ML	0x1	/* Trans. Data	Load		  /Re	(ML) */
+#define	SM_TR_WAIT_TC	0x2	/* Trans. Data	Wait_TC		Tr/Re	     */
+#define	SM_TR_WDONE	0x4	/* Trans. Data	Wait_Done	Tr/Re	     */
+
+#define	SM_WR_IDLE	0x0	/* Write Desc.	Idle		Tr/Re	     */
+#define	SM_WR_ABLEN	0x1	/* Write Desc.	Act_Buf_Length	Tr/Re	     */
+#define	SM_WR_LD_A4	0x2	/* Write Desc.	Load_A4		   Re	     */
+#define	SM_WR_RES_OWN	0x2	/* Write Desc.	Res_OWN		Tr   	     */
+#define	SM_WR_WAIT_EOF	0x3	/* Write Desc.	Wait_EOF	   Re	     */
+#define	SM_WR_LD_N2C_R	0x4	/* Write Desc.	Load_N2C	   Re	     */
+#define	SM_WR_WAIT_TC_R	0x5	/* Write Desc.	Wait_TC		   Re	     */
+#define	SM_WR_WAIT_TC4	0x6	/* Write Desc.	Wait_TC4	   Re	     */
+#define	SM_WR_LD_A_T	0x6	/* Write Desc.	Load_A		Tr   	     */
+#define	SM_WR_LD_A_R	0x7	/* Write Desc.	Load_A		   Re	     */
+#define	SM_WR_WAIT_TC_T	0x7	/* Write Desc.	Wait_TC		Tr   	     */
+#define	SM_WR_LD_N2C_T	0xc	/* Write Desc.	Load_N2C	Tr   	     */
+#define	SM_WR_WDONE_T	0x9	/* Write Desc.	Wait_Done	Tr   	     */
+#define	SM_WR_WDONE_R	0xc	/* Write Desc.	Wait_Done	   Re	     */
+#define SM_WR_LD_D_AD	0xe	/* Write Desc.  Load_Dumr_A	   Re	(ML) */
+#define SM_WR_WAIT_D_TC	0xf	/* Write Desc.	Wait_Dumr_TC	   Re	(ML) */
+
+/*	B5_<xx>_T2	32 bit Test Register 2		 (xx) */
+/* Note: This register is only defined for the transmit queues */
+				/* Bit 31..8:	reserved */
+#define	AC_TEST_ON	(1<<7)	/* Bit 7:	Address Counter Test Mode on */
+#define	AC_TEST_OFF	(1<<6)	/* Bit 6:	Address Counter Test Mode off*/
+#define	BC_TEST_ON	(1<<5)	/* Bit 5:	Byte Counter Test Mode on */
+#define	BC_TEST_OFF	(1<<4)	/* Bit 4:	Byte Counter Test Mode off */
+#define	TEST_STEP04	(1<<3)	/* Bit 3:	Inc AC/Dec BC by 4 */
+#define	TEST_STEP03	(1<<2)	/* Bit 2:	Inc AC/Dec BC by 3 */
+#define	TEST_STEP02	(1<<1)	/* Bit 1:	Inc AC/Dec BC by 2 */
+#define	TEST_STEP01	(1<<0)	/* Bit 0:	Inc AC/Dec BC by 1 */
+
+/*	B5_<xx>_T3	32 bit Test Register 3		 (xx) */
+/* Note: This register is only defined for the transmit queues */
+				/* Bit 31..8:	reserved */
+#define T3_MUX_2	(1<<7)	/* Bit 7: (ML)	Mux position MSB */
+#define T3_VRAM_2	(1<<6)	/* Bit 6: (ML)	Virtual RAM buffer addr MSB */
+#define	T3_LOOP		(1<<5)	/* Bit 5: 	Set Loopback (Xmit) */
+#define	T3_UNLOOP	(1<<4)	/* Bit 4: 	Unset Loopback (Xmit) */
+#define	T3_MUX		(3<<2)	/* Bit 3..2:	Mux position */
+#define	T3_VRAM		(3<<0)	/* Bit 1..0:	Virtual RAM buffer Address */
+
+/* PCI card IDs */
+/*
+ * Note: The following 4 byte definitions shall not be used! Use OEM Concept!
+ */
+#define	PCI_VEND_ID0	0x48		/* PCI vendor ID (SysKonnect) */
+#define	PCI_VEND_ID1	0x11		/* PCI vendor ID (SysKonnect) */
+					/*		 (High byte) */
+#define	PCI_DEV_ID0	0x00		/* PCI device ID */
+#define	PCI_DEV_ID1	0x40		/* PCI device ID (High byte) */
+
+/*#define PCI_CLASS	0x02*/		/* PCI class code: network device */
+#define PCI_NW_CLASS	0x02		/* PCI class code: network device */
+#define PCI_SUB_CLASS	0x02		/* PCI subclass ID: FDDI device */
+#define PCI_PROG_INTFC	0x00		/* PCI programming Interface (=0) */
+
+/*
+ * address transmision from logical to physical offset address on board
+ */
+#define	FMA(a)	(0x0400|((a)<<2))	/* FORMAC+ (r/w) (SN3) */
+#define	P1(a)	(0x0380|((a)<<2))	/* PLC1 (r/w) (DAS) */
+#define	P2(a)	(0x0600|((a)<<2))	/* PLC2 (r/w) (covered by the SN3) */
+#define PRA(a)	(B2_MAC_0 + (a))	/* configuration PROM (MAC address) */
+
+/*
+ * FlashProm specification
+ */
+#define	MAX_PAGES	0x20000L	/* Every byte has a single page */
+#define	MAX_FADDR	1		/* 1 byte per page */
+
+/*
+ * Receive / Transmit Buffer Control word
+ */
+#define	BMU_OWN		(1UL<<31)	/* OWN bit: 0 == host, 1 == adapter */
+#define	BMU_STF		(1L<<30)	/* Start of Frame ?		*/
+#define	BMU_EOF		(1L<<29)	/* End of Frame ?		*/
+#define	BMU_EN_IRQ_EOB	(1L<<28)	/* Enable "End of Buffer" IRQ	*/
+#define	BMU_EN_IRQ_EOF	(1L<<27)	/* Enable "End of Frame" IRQ	*/
+#define	BMU_DEV_0	(1L<<26)	/* RX: don't transfer to system mem */
+#define BMU_SMT_TX	(1L<<25)	/* TX: if set, buffer type SMT_MBuf */
+#define BMU_ST_BUF	(1L<<25)	/* RX: copy of start of frame */
+#define BMU_UNUSED	(1L<<24)	/* Set if the Descr is curr unused */
+#define BMU_SW		(3L<<24)	/* 2 Bits reserved for SW usage */
+#define	BMU_CHECK	0x00550000L	/* To identify the control word */
+#define	BMU_BBC		0x0000FFFFL	/* R/T Buffer Byte Count        */
+
+/*
+ * physical address offset + IO-Port base address
+ */
+#ifdef MEM_MAPPED_IO
+#define	ADDR(a)		(char far *) smc->hw.iop+(a)
+#define	ADDRS(smc,a)	(char far *) (smc)->hw.iop+(a)
+#else
+#define	ADDR(a)	(((a)>>7) ? (outp(smc->hw.iop+B0_RAP,(a)>>7), \
+	(smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) : \
+	(smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0))))
+#define	ADDRS(smc,a) (((a)>>7) ? (outp((smc)->hw.iop+B0_RAP,(a)>>7), \
+	((smc)->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) : \
+	((smc)->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0))))
+#endif
+
+/*
+ * Define a macro to access the configuration space
+ */
+#define PCI_C(a)	ADDR(B3_CFG_SPC + (a))	/* PCI Config Space */
+
+#define EXT_R(a)	ADDR(B6_EXT_REG + (a))	/* External Registers */
+
+/*
+ * Define some values needed for the MAC address (PROM)
+ */
+#define	SA_MAC		(0)	/* start addr. MAC_AD within the PROM */
+#define	PRA_OFF		(0)	/* offset correction when 4th byte reading */
+
+#define	SKFDDI_PSZ	8	/* address PROM size */
+
+#define	FM_A(a)	ADDR(FMA(a))	/* FORMAC Plus physical addr */
+#define	P1_A(a)	ADDR(P1(a))	/* PLC1 (r/w) */
+#define	P2_A(a)	ADDR(P2(a))	/* PLC2 (r/w) (DAS) */
+#define PR_A(a)	ADDR(PRA(a))	/* config. PROM (MAC address) */
+
+/*
+ * Macro to read the PROM
+ */
+#define	READ_PROM(a)	((u_char)inp(a))
+
+#define	GET_PAGE(bank)	outpd(ADDR(B2_FAR),bank)
+#define	VPP_ON()
+#define	VPP_OFF()
+
+/*
+ * Note: Values of the Interrupt Source Register are defined above
+ */
+#define ISR_A		ADDR(B0_ISRC)
+#define	GET_ISR()		inpd(ISR_A)
+#define GET_ISR_SMP(iop)	inpd((iop)+B0_ISRC)
+#define	CHECK_ISR()		(inpd(ISR_A) & inpd(ADDR(B0_IMSK)))
+#define CHECK_ISR_SMP(iop)	(inpd((iop)+B0_ISRC) & inpd((iop)+B0_IMSK))
+
+#define	BUS_CHECK()
+
+/*
+ * CLI_FBI:	Disable Board Interrupts
+ * STI_FBI:	Enable Board Interrupts
+ */
+#ifndef UNIX
+#define	CLI_FBI()	outpd(ADDR(B0_IMSK),0)
+#else
+#define	CLI_FBI(smc)	outpd(ADDRS((smc),B0_IMSK),0)
+#endif
+
+#ifndef UNIX
+#define	STI_FBI()	outpd(ADDR(B0_IMSK),smc->hw.is_imask)
+#else
+#define	STI_FBI(smc)	outpd(ADDRS((smc),B0_IMSK),(smc)->hw.is_imask)
+#endif
+
+#define CLI_FBI_SMP(iop)	outpd((iop)+B0_IMSK,0)
+#define	STI_FBI_SMP(smc,iop)	outpd((iop)+B0_IMSK,(smc)->hw.is_imask)
+
+#endif	/* PCI */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * 12 bit transfer (dword) counter:
+ *	(ISA:	2*trc = number of byte)
+ *	(EISA:	4*trc = number of byte)
+ *	(MCA:	4*trc = number of byte)
+ */
+#define	MAX_TRANS	(0x0fff)
+
+/*
+ * PC PIC
+ */
+#define	MST_8259 (0x20)
+#define	SLV_8259 (0xA0)
+
+#define TPS		(18)		/* ticks per second */
+
+/*
+ * error timer defs
+ */
+#define	TN		(4)	/* number of supported timer = TN+1 */
+#define	SNPPND_TIME	(5)	/* buffer memory access over mem. data reg. */
+
+#define	MAC_AD	0x405a0000
+
+#define MODR1	FM_A(FM_MDREG1)	/* mode register 1 */
+#define MODR2	FM_A(FM_MDREG2)	/* mode register 2 */
+
+#define CMDR1	FM_A(FM_CMDREG1)	/* command register 1 */
+#define CMDR2	FM_A(FM_CMDREG2)	/* command register 2 */
+
+
+/*
+ * function defines
+ */
+#define	CLEAR(io,mask)		outpw((io),inpw(io)&(~(mask)))
+#define	SET(io,mask)		outpw((io),inpw(io)|(mask))
+#define	GET(io,mask)		(inpw(io)&(mask))
+#define	SETMASK(io,val,mask)	outpw((io),(inpw(io) & ~(mask)) | (val))
+
+/*
+ * PHY Port A (PA) = PLC 1
+ * With SuperNet 3 PHY-A and PHY S are identical.
+ */
+#define	PLC(np,reg)	(((np) == PA) ? P2_A(reg) : P1_A(reg))
+
+/*
+ * set memory address register for write and read
+ */
+#define	MARW(ma)	outpw(FM_A(FM_MARW),(unsigned int)(ma))
+#define	MARR(ma)	outpw(FM_A(FM_MARR),(unsigned int)(ma))
+
+/*
+ * read/write from/to memory data register
+ */
+/* write double word */
+#define	MDRW(dd)	outpw(FM_A(FM_MDRU),(unsigned int)((dd)>>16)) ;\
+			outpw(FM_A(FM_MDRL),(unsigned int)(dd))
+
+#ifndef WINNT
+/* read double word */
+#define	MDRR()		(((long)inpw(FM_A(FM_MDRU))<<16) + inpw(FM_A(FM_MDRL)))
+
+/* read FORMAC+ 32-bit status register */
+#define	GET_ST1()	(((long)inpw(FM_A(FM_ST1U))<<16) + inpw(FM_A(FM_ST1L)))
+#define	GET_ST2()	(((long)inpw(FM_A(FM_ST2U))<<16) + inpw(FM_A(FM_ST2L)))
+#ifdef	SUPERNET_3
+#define	GET_ST3()	(((long)inpw(FM_A(FM_ST3U))<<16) + inpw(FM_A(FM_ST3L)))
+#endif
+#else
+/* read double word */
+#define MDRR()		inp2w((FM_A(FM_MDRU)),(FM_A(FM_MDRL)))
+
+/* read FORMAC+ 32-bit status register */
+#define GET_ST1()	inp2w((FM_A(FM_ST1U)),(FM_A(FM_ST1L)))
+#define GET_ST2()	inp2w((FM_A(FM_ST2U)),(FM_A(FM_ST2L)))
+#ifdef	SUPERNET_3
+#define GET_ST3()	inp2w((FM_A(FM_ST3U)),(FM_A(FM_ST3L)))
+#endif
+#endif
+
+/* Special timer macro for 82c54 */
+				/* timer access over data bus bit 8..15 */
+#define	OUT_82c54_TIMER(port,val)	outpw(TI_A(port),(val)<<8)
+#define	IN_82c54_TIMER(port)		((inpw(TI_A(port))>>8) & 0xff)
+
+
+#ifdef	DEBUG
+#define	DB_MAC(mac,st) {if (debug_mac & 0x1)\
+				printf("M") ;\
+			if (debug_mac & 0x2)\
+				printf("\tMAC %d status 0x%08lx\n",mac,st) ;\
+			if (debug_mac & 0x4)\
+				dp_mac(mac,st) ;\
+}
+
+#define	DB_PLC(p,iev) {	if (debug_plc & 0x1)\
+				printf("P") ;\
+			if (debug_plc & 0x2)\
+				printf("\tPLC %s Int 0x%04x\n", \
+					(p == PA) ? "A" : "B", iev) ;\
+			if (debug_plc & 0x4)\
+				dp_plc(p,iev) ;\
+}
+
+#define	DB_TIMER() {	if (debug_timer & 0x1)\
+				printf("T") ;\
+			if (debug_timer & 0x2)\
+				printf("\tTimer ISR\n") ;\
+}
+
+#else	/* no DEBUG */
+
+#define	DB_MAC(mac,st)
+#define	DB_PLC(p,iev)
+#define	DB_TIMER()
+
+#endif	/* no DEBUG */
+
+#define	INC_PTR(sp,cp,ep)	if (++cp == ep) cp = sp
+/*
+ * timer defs
+ */
+#define	COUNT(t)	((t)<<6)	/* counter */
+#define	RW_OP(o)	((o)<<4)	/* read/write operation */
+#define	TMODE(m)	((m)<<1)	/* timer mode */
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/skfbiinc.h linux.18p17/drivers/net/skfp/h/skfbiinc.h
--- linux.vanilla/drivers/net/skfp/h/skfbiinc.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/skfbiinc.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_SKFBIINC_
+#define _SKFBIINC_
+
+#include "h/supern_2.h"
+
+/*
+ * special defines for use into .asm files
+ */
+#define ERR_FLAGS (FS_MSRABT | FS_SEAC2 | FS_SFRMERR | FS_SFRMTY1)
+
+#ifdef	ISA
+#define DMA_BUSY_CHECK	CSRA
+#define	IMASK_FAST	(IS_PLINT1 | IS_PLINT2 | IS_TIMINT)
+#define	HRQR		(RQAA+(RQ_RRQ<<1))
+#define	HRQW		(RQAA+(RQ_WA2<<1))
+#define	HRQA0		(RQAA+(RQ_WA0<<1))
+#define HRQSQ		(RQAA+(RQ_WSQ<<1))
+#endif
+
+#ifdef	EISA
+#define	DMA_BUSY_CHECK	CSRA
+#define DMA_HIGH_WORD	0x0400
+#define DMA_MASK_M	0x0a
+#define DMA_MODE_M	0x0b
+#define DMA_BYTE_PTR_M	0x0c
+#define DMA_MASK_S	0x0d4
+#define DMA_MODE_S	0x0d6
+#define DMA_BYTE_PTR_S	0x0d8
+#define	IMASK_FAST	(IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TC)
+#endif	/* EISA */
+
+#ifdef	MCA
+#define	IMASK_FAST	(IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \
+			 IS_CHCK_L | IS_BUSERR)
+#endif
+
+#ifdef PCI
+#define	IMASK_FAST	(IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \
+			 IS_MINTR2 | IS_MINTR3 | IS_R1_P | \
+			 IS_R1_C | IS_XA_C | IS_XS_C)
+#endif
+
+#ifdef	PCI
+#define	ISR_MASK	(IS_MINTR1 | IS_R1_F | IS_XS_F| IS_XA_F | IMASK_FAST)
+#else
+#define	ISR_MASK	(IS_MINTR1 | IS_MINTR2 | IMASK_FAST)
+#endif
+
+#define	FMA_FM_CMDREG1	FMA(FM_CMDREG1)
+#define	FMA_FM_CMDREG2	FMA(FM_CMDREG2)
+#define	FMA_FM_STMCHN	FMA(FM_STMCHN)
+#define	FMA_FM_RPR	FMA(FM_RPR)
+#define	FMA_FM_WPXA0	FMA(FM_WPXA0)
+#define	FMA_FM_WPXA2	FMA(FM_WPXA2)
+#define	FMA_FM_MARR	FMA(FM_MARR)
+#define	FMA_FM_MARW	FMA(FM_MARW)
+#define	FMA_FM_MDRU	FMA(FM_MDRU)
+#define	FMA_FM_MDRL	FMA(FM_MDRL)
+#define	FMA_ST1L	FMA(FM_ST1L)
+#define	FMA_ST1U	FMA(FM_ST1U)
+#define	FMA_ST2L	FMA(FM_ST2L)
+#define	FMA_ST2U	FMA(FM_ST2U)
+#ifdef	SUPERNET_3
+#define FMA_ST3L	FMA(FM_ST3L)
+#define FMA_ST3U	FMA(FM_ST3U)
+#endif
+
+#define TMODE_RRQ	RQ_RRQ
+#define TMODE_WAQ2	RQ_WA2
+#define	HSRA		HSR(0)
+
+
+#define FMA_FM_ST1L	FMA_ST1L
+#define FMA_FM_ST1U	FMA_ST1U
+#define FMA_FM_ST2L	FMA_ST2L
+#define FMA_FM_ST2U	FMA_ST2U
+#ifdef	SUPERNET_3
+#define FMA_FM_ST3L	FMA_ST3L
+#define FMA_FM_ST3U	FMA_ST3U
+#endif
+
+#define FMA_FM_SWPR	FMA(FM_SWPR)
+
+#define FMA_FM_RPXA0	FMA(FM_RPXA0)
+
+#define	FMA_FM_RPXS	FMA(FM_RPXS)
+#define	FMA_FM_WPXS	FMA(FM_WPXS)
+
+#define	FMA_FM_IMSK1U	FMA(FM_IMSK1U)
+#define	FMA_FM_IMSK1L	FMA(FM_IMSK1L)
+
+#define	FMA_FM_EAS	FMA(FM_EAS)
+#define	FMA_FM_EAA0	FMA(FM_EAA0)
+
+#define	TMODE_WAQ0	RQ_WA0
+#define TMODE_WSQ	RQ_WSQ
+
+/* Define default for DRV_PCM_STATE_CHANGE */
+#ifndef	DRV_PCM_STATE_CHANGE
+#define	DRV_PCM_STATE_CHANGE(smc,plc,p_state)	/* nothing */
+#endif
+
+/* Define default for DRV_RMT_INDICATION */
+#ifndef	DRV_RMT_INDICATION
+#define	DRV_RMT_INDICATION(smc,i)	/* nothing */
+#endif
+
+#endif	/* n_SKFBIINC_ */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/smc.h linux.18p17/drivers/net/skfp/h/smc.h
--- linux.vanilla/drivers/net/skfp/h/smc.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/smc.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,471 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_SCMECM_
+#define _SCMECM_
+
+#if	defined(PCI) && !defined(OSDEF)
+/*
+ * In the case of the PCI bus the file osdef1st.h must be present
+ */
+#define	OSDEF
+#endif
+
+#ifdef	PCI
+#ifndef	SUPERNET_3
+#define	SUPERNET_3
+#endif
+#ifndef	TAG_MODE
+#define	TAG_MODE
+#endif
+#endif
+
+/*
+ * include all other files in required order
+ * the following files must have been included before:
+ *	types.h
+ *	fddi.h
+ */
+#ifdef	OSDEF
+#include "h/osdef1st.h"
+#endif	/* OSDEF */
+#ifdef	OEM_CONCEPT
+#include "oemdef.h"
+#endif	/* OEM_CONCEPT */
+#include "h/smt.h"
+#include "h/cmtdef.h"
+#include "h/fddimib.h"
+#include "h/targethw.h"		/* all target hw dependencies */
+#include "h/targetos.h"		/* all target os dependencies */
+#ifdef	ESS
+#include "h/sba.h"
+#endif
+
+/*
+ * Event Queue
+ *	queue.c
+ * events are class/value pairs
+ *	class	is addressee, e.g. RMT, PCM etc.
+ *	value	is command, e.g. line state change, ring op change etc.
+ */
+struct event_queue {
+	u_short	class ;			/* event class */
+	u_short	event ;			/* event value */
+} ;
+
+/*
+ * define event queue as circular buffer
+ */
+#ifdef	CONCENTRATOR
+#define MAX_EVENT	128
+#else	/* nCONCENTRATOR */
+#define MAX_EVENT	64
+#endif	/* nCONCENTRATOR */
+
+struct s_queue {
+
+	struct event_queue ev_queue[MAX_EVENT];
+	struct event_queue *ev_put ;
+	struct event_queue *ev_get ;
+} ;
+
+/*
+ * ECM - Entity Coordination Management
+ * ecm.c
+ */
+struct s_ecm {
+	u_char path_test ;		/* ECM path test variable */
+	u_char sb_flag ;		/* ECM stuck bypass */
+	u_char DisconnectFlag ;		/* jd 05-Aug-1999 Bug #10419 
+					 * ECM disconnected */
+	u_char ecm_line_state ;		/* flag to dispatcher : line states */
+	u_long trace_prop ;		/* ECM Trace_Prop flag >= 16 bits !! */
+	/* NUMPHYS note:
+	 * this variable must have enough bits to hold all entiies in
+	 * the station. So NUMPHYS may not be greater than 31.
+	 */
+	char	ec_pad[2] ;
+	struct smt_timer ecm_timer ;	/* timer */
+} ;
+
+
+/*
+ * RMT - Ring Management
+ * rmt.c
+ */
+struct s_rmt {
+	u_char dup_addr_test ;		/* state of dupl. addr. test */
+	u_char da_flag ;		/* flag : duplicate address det. */
+	u_char loop_avail ;		/* flag : MAC available for loopback */
+	u_char sm_ma_avail ;		/* flag : MAC available for SMT */
+	u_char no_flag ;		/* flag : ring not operational */
+	u_char bn_flag ;		/* flag : MAC reached beacon state */
+	u_char jm_flag ;		/* flag : jamming in NON_OP_DUP */
+	u_char rm_join ;		/* CFM flag RM_Join */
+	u_char rm_loop ;		/* CFM flag RM_Loop */
+
+	long fast_rm_join ;		/* bit mask of active ports */
+	/*
+	 * timer and flags
+	 */
+	struct smt_timer rmt_timer0 ;	/* timer 0 */
+	struct smt_timer rmt_timer1 ;	/* timer 1 */
+	struct smt_timer rmt_timer2 ;	/* timer 2 */
+	u_char timer0_exp ;		/* flag : timer 0 expired */
+	u_char timer1_exp ;		/* flag : timer 1 expired */
+	u_char timer2_exp ;		/* flag : timer 2 expired */
+
+	u_char rm_pad1[1] ;
+} ;
+
+/*
+ * CFM - Configuration Management
+ * cfm.c
+ * used for SAS and DAS
+ */
+struct s_cfm {
+	u_char cf_state;		/* CFM state machine current state */
+	u_char cf_pad[3] ;
+} ;
+
+/*
+ * CEM - Configuration Element Management
+ * cem.c
+ * used for Concentrator
+ */
+#ifdef	CONCENTRATOR
+struct s_cem {
+	int	ce_state ;	/* CEM state */
+	int	ce_port ;	/* PA PB PM PM+1 .. */
+	int	ce_type ;	/* TA TB TS TM */
+} ;
+
+/*
+ * linked list of CCEs in current token path
+ */
+struct s_c_ring {
+	struct s_c_ring	*c_next ;
+	char		c_entity ;
+} ;
+
+struct mib_path_config {
+	u_long	fddimibPATHConfigSMTIndex;
+	u_long	fddimibPATHConfigPATHIndex;
+	u_long	fddimibPATHConfigTokenOrder;
+	u_long	fddimibPATHConfigResourceType;
+#define SNMP_RES_TYPE_MAC	2	/* Resource is a MAC */
+#define SNMP_RES_TYPE_PORT	4	/* Resource is a PORT */
+	u_long	fddimibPATHConfigResourceIndex;
+	u_long	fddimibPATHConfigCurrentPath;
+#define SNMP_PATH_ISOLATED	1	/* Current path is isolated */
+#define SNMP_PATH_LOCAL		2	/* Current path is local */
+#define SNMP_PATH_SECONDARY	3	/* Current path is secondary */
+#define SNMP_PATH_PRIMARY	4	/* Current path is primary */
+#define SNMP_PATH_CONCATENATED	5	/* Current path is concatenated */
+#define SNMP_PATH_THRU		6	/* Current path is thru */
+};
+
+
+#endif
+
+/*
+ * PCM connect states
+ */
+#define PCM_DISABLED	0
+#define PCM_CONNECTING	1
+#define PCM_STANDBY	2
+#define PCM_ACTIVE	3
+
+struct s_pcm {
+	u_char	pcm_pad[3] ;
+} ;
+
+/*
+ * PHY struct
+ * one per physical port
+ */
+struct s_phy {
+	/* Inter Module Globals */
+	struct fddi_mib_p	*mib ;
+
+	u_char np ;		/* index 0 .. NUMPHYS */
+	u_char cf_join ;
+	u_char cf_loop ;
+	u_char wc_flag ;	/* withhold connection flag */
+	u_char pc_mode ;	/* Holds the negotiated mode of the PCM */
+	u_char pc_lem_fail ;	/* flag : LCT failed */
+	u_char lc_test ;
+	u_char scrub ;		/* CFM flag Scrub -> PCM */
+	char phy_name ;
+	u_char pmd_type[2] ;	/* SK connector/transceiver type codes */
+#define PMD_SK_CONN	0	/* pmd_type[PMD_SK_CONN] = Connector */
+#define PMD_SK_PMD	1	/* pmd_type[PMD_SK_PMD] = Xver */
+	u_char pmd_scramble ;	/* scrambler on/off */
+
+	/* inner Module Globals */
+	u_char curr_ls ;	/* current line state */
+	u_char ls_flag ;
+	u_char rc_flag ;
+	u_char tc_flag ;
+	u_char td_flag ;
+	u_char bitn ;
+	u_char tr_flag ;	/* trace recvd while in active */
+	u_char twisted ;	/* flag to indicate an A-A or B-B connection */
+	u_char t_val[NUMBITS] ;	/* transmit bits for signaling */
+	u_char r_val[NUMBITS] ;	/* receive bits for signaling */
+	u_long t_next[NUMBITS] ;
+	struct smt_timer pcm_timer0 ;
+	struct smt_timer pcm_timer1 ;
+	struct smt_timer pcm_timer2 ;
+	u_char timer0_exp ;
+	u_char timer1_exp ;
+	u_char timer2_exp ;
+	u_char pcm_pad1[1] ;
+	int	cem_pst ;	/* CEM privae state; used for dual homing */
+	struct lem_counter lem ;
+#ifdef	AMDPLC
+	struct s_plc	plc ;
+#endif
+} ;
+
+/*
+ * timer package
+ * smttimer.c
+ */
+struct s_timer {
+	struct smt_timer	*st_queue ;
+	struct smt_timer	st_fast ;
+} ;
+
+/*
+ * SRF types and data
+ */
+#define SMT_EVENT_BASE			1
+#define SMT_EVENT_MAC_PATH_CHANGE	(SMT_EVENT_BASE+0)
+#define SMT_EVENT_MAC_NEIGHBOR_CHANGE	(SMT_EVENT_BASE+1)
+#define SMT_EVENT_PORT_PATH_CHANGE	(SMT_EVENT_BASE+2)
+#define SMT_EVENT_PORT_CONNECTION	(SMT_EVENT_BASE+3)
+
+#define SMT_IS_CONDITION(x)			((x)>=SMT_COND_BASE)
+
+#define SMT_COND_BASE		(SMT_EVENT_PORT_CONNECTION+1)
+#define SMT_COND_SMT_PEER_WRAP		(SMT_COND_BASE+0)
+#define SMT_COND_SMT_HOLD		(SMT_COND_BASE+1)
+#define SMT_COND_MAC_FRAME_ERROR	(SMT_COND_BASE+2)
+#define SMT_COND_MAC_DUP_ADDR		(SMT_COND_BASE+3)
+#define SMT_COND_MAC_NOT_COPIED		(SMT_COND_BASE+4)
+#define SMT_COND_PORT_EB_ERROR		(SMT_COND_BASE+5)
+#define SMT_COND_PORT_LER		(SMT_COND_BASE+6)
+
+#define SR0_WAIT	0
+#define SR1_HOLDOFF	1
+#define SR2_DISABLED	2
+
+struct s_srf {
+	u_long	SRThreshold ;			/* threshold value */
+	u_char	RT_Flag ;			/* report transmitted flag */
+	u_char	sr_state ;			/* state-machine */
+	u_char	any_report ;			/* any report required */
+	u_long	TSR ;				/* timer */
+	u_short	ring_status ;			/* IBM ring status */
+} ;
+
+/*
+ * IBM token ring status
+ */
+#define RS_RES15	(1<<15)			/* reserved */
+#define RS_HARDERROR	(1<<14)			/* ring down */
+#define RS_SOFTERROR	(1<<13)			/* sent SRF */
+#define RS_BEACON	(1<<12)			/* transmitted beacon */
+#define RS_PATHTEST	(1<<11)			/* path test failed */
+#define RS_SELFTEST	(1<<10)			/* selftest required */
+#define RS_RES9		(1<< 9)			/* reserved */
+#define RS_DISCONNECT	(1<< 8)			/* remote disconnect */
+#define RS_RES7		(1<< 7)			/* reserved */
+#define RS_DUPADDR	(1<< 6)			/* duplicate address */
+#define RS_NORINGOP	(1<< 5)			/* no ring op */
+#define RS_VERSION	(1<< 4)			/* SMT version mismatch */
+#define RS_STUCKBYPASSS	(1<< 3)			/* stuck bypass */
+#define RS_EVENT	(1<< 2)			/* FDDI event occured */
+#define RS_RINGOPCHANGE	(1<< 1)			/* ring op changed */
+#define RS_RES0		(1<< 0)			/* reserved */
+
+#define RS_SET(smc,bit) \
+	ring_status_indication(smc,smc->srf.ring_status |= bit)
+#define RS_CLEAR(smc,bit)	\
+	ring_status_indication(smc,smc->srf.ring_status &= ~bit)
+
+#define RS_CLEAR_EVENT	(0xffff & ~(RS_NORINGOP))
+
+/* Define the AIX-event-Notification as null function if it isn't defined */
+/* in the targetos.h file */
+#ifndef AIX_EVENT
+#define AIX_EVENT(smc,opt0,opt1,opt2,opt3)	/* nothing */
+#endif
+
+struct s_srf_evc {
+	u_char	evc_code ;			/* event code type */
+	u_char	evc_index ;			/* index for mult. instances */
+	u_char	evc_rep_required ;		/* report required */
+	u_short	evc_para ;			/* SMT Para Number */
+	u_char	*evc_cond_state ;		/* condition state */
+	u_char	*evc_multiple ;			/* multiple occurence */
+} ;
+
+/*
+ * Values used by frame based services
+ * smt.c
+ */
+#define SMT_MAX_TEST		5
+#define SMT_TID_NIF		0		/* pending NIF request */
+#define SMT_TID_NIF_TEST	1		/* pending NIF test */
+#define SMT_TID_ECF_UNA		2		/* pending ECF UNA test */
+#define SMT_TID_ECF_DNA		3		/* pending ECF DNA test */
+#define SMT_TID_ECF		4		/* pending ECF test */
+
+struct smt_values {
+	u_long		smt_tvu ;		/* timer valid una */
+	u_long		smt_tvd ;		/* timer valid dna */
+	u_long		smt_tid ;		/* transaction id */
+	u_long		pend[SMT_MAX_TEST] ;	/* TID of requests */
+	u_long		uniq_time ;		/* unique time stamp */
+	u_short		uniq_ticks  ;		/* unique time stamp */
+	u_short		please_reconnect ;	/* flag : reconnect */
+	u_long		smt_last_lem ;
+	u_long		smt_last_notify ;
+	struct smt_timer	smt_timer ;	/* SMT NIF timer */
+	u_long		last_tok_time[NUMMACS];	/* token cnt emulation */
+} ;
+
+/*
+ * SMT/CMT configurable parameters
+ */
+#define SMT_DAS	0			/* dual attach */
+#define SMT_SAS	1			/* single attach */
+#define SMT_NAC	2			/* null attach concentrator */
+
+struct smt_config {
+	u_char	attach_s ;		/* CFM attach to secondary path */
+	u_char	sas ;			/* SMT_DAS/SAS/NAC */
+	u_char	build_ring_map ;	/* build ringmap if TRUE */
+	u_char	numphys ;		/* number of active phys */
+	u_char	sc_pad[1] ;
+
+	u_long	pcm_tb_min ;		/* PCM : TB_Min timer value */
+	u_long	pcm_tb_max ;		/* PCM : TB_Max timer value */
+	u_long	pcm_c_min ;		/* PCM : C_Min timer value */
+	u_long	pcm_t_out ;		/* PCM : T_Out timer value */
+	u_long	pcm_tl_min ;		/* PCM : TL_min timer value */
+	u_long	pcm_lc_short ;		/* PCM : LC_Short timer value */
+	u_long	pcm_lc_medium ;		/* PCM : LC_Medium timer value */
+	u_long	pcm_lc_long ;		/* PCM : LC_Long timer value */
+	u_long	pcm_lc_extended ;	/* PCM : LC_Extended timer value */
+	u_long	pcm_t_next_9 ;		/* PCM : T_Next[9] timer value */
+	u_long	pcm_ns_max ;		/* PCM : NS_Max timer value */
+
+	u_long	ecm_i_max ;		/* ECM : I_Max timer value */
+	u_long	ecm_in_max ;		/* ECM : IN_Max timer value */
+	u_long	ecm_td_min ;		/* ECM : TD_Min timer */
+	u_long	ecm_test_done ;		/* ECM : path test done timer */
+	u_long	ecm_check_poll ;	/* ECM : check bypass poller */
+
+	u_long	rmt_t_non_op ;		/* RMT : T_Non_OP timer value */
+	u_long	rmt_t_stuck ;		/* RMT : T_Stuck timer value */
+	u_long	rmt_t_direct ;		/* RMT : T_Direct timer value */
+	u_long	rmt_t_jam ;		/* RMT : T_Jam timer value */
+	u_long	rmt_t_announce ;	/* RMT : T_Announce timer value */
+	u_long	rmt_t_poll ;		/* RMT : claim/beacon poller */
+	u_long  rmt_dup_mac_behavior ;  /* Flag for the beavior of SMT if
+					 * a Duplicate MAC Address was detected.
+					 * FALSE: SMT will leave finaly the ring
+					 * TRUE:  SMT will reinstert into the ring
+					 */
+	u_long	mac_d_max ;		/* MAC : D_Max timer value */
+
+	u_long lct_short ;		/* LCT : error threshhold */
+	u_long lct_medium ;		/* LCT : error threshhold */
+	u_long lct_long ;		/* LCT : error threshhold */
+	u_long lct_extended ;		/* LCT : error threshhold */
+} ;
+
+#ifdef	DEBUG
+/*
+ * Debugging struct sometimes used in smc
+ */
+struct	smt_debug {
+	int	d_smtf ;
+	int	d_smt ;
+	int	d_ecm ;
+	int	d_rmt ;
+	int	d_cfm ;
+	int	d_pcm ;
+	int	d_plc ;
+#ifdef	ESS
+	int	d_ess ;
+#endif
+#ifdef	SBA
+	int	d_sba ;
+#endif
+	struct	os_debug	d_os;	/* Include specific OS DEBUG struct */
+} ;
+
+#ifndef	DEBUG_BRD
+/* all boards shall be debugged with one debug struct */
+extern	struct	smt_debug	debug;	/* Declaration of debug struct */
+#endif	/* DEBUG_BRD */
+
+#endif	/* DEBUG */
+
+/*
+ * the SMT Context Struct SMC
+ * this struct contains ALL global variables of SMT
+ */
+struct s_smc {
+	struct s_smt_os	os ;		/* os specific */
+	struct s_smt_hw	hw ;		/* hardware */
+
+/*
+ * NOTE: os and hw MUST BE the first two structs
+ * anything beyond hw WILL BE SET TO ZERO in smt_set_defaults()
+ */
+	struct smt_config s ;		/* smt constants */
+	struct smt_values sm ;		/* smt variables */
+	struct s_ecm	e ;		/* ecm */
+	struct s_rmt	r ;		/* rmt */
+	struct s_cfm	cf ;		/* cfm/cem */
+#ifdef	CONCENTRATOR
+	struct s_cem	ce[NUMPHYS] ;	/* cem */
+	struct s_c_ring	cr[NUMPHYS+NUMMACS] ;
+#endif
+	struct s_pcm	p ;		/* pcm */
+	struct s_phy	y[NUMPHYS] ;	/* phy */
+	struct s_queue	q ;		/* queue */
+	struct s_timer	t ;		/* timer */
+	struct s_srf srf ;		/* SRF */
+	struct s_srf_evc evcs[6+NUMPHYS*4] ;
+	struct fddi_mib	mib ;		/* __THE_MIB__ */
+#ifdef	SBA
+	struct s_sba	sba ;		/* SBA variables */
+#endif
+#ifdef	ESS
+	struct s_ess	ess ;		/* Ess variables */
+#endif
+#if	defined(DEBUG) && defined(DEBUG_BRD)
+	/* If you want all single board to be debugged separately */
+	struct smt_debug	debug;	/* Declaration of debug struct */
+#endif	/* DEBUG_BRD && DEBUG */
+} ;
+
+#endif	/* _SCMECM_ */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/smt.h linux.18p17/drivers/net/skfp/h/smt.h
--- linux.vanilla/drivers/net/skfp/h/smt.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/smt.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,882 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ *	SMT 7.2 frame definitions
+ */
+
+#ifndef	_SMT_
+#define _SMT_
+
+/* #define SMT5_10 */
+#define SMT6_10
+#define SMT7_20
+
+#define	OPT_PMF		/* if parameter management is supported */
+#define	OPT_SRF		/* if status report is supported */
+
+/*
+ * SMT frame version 5.1
+ */
+
+#define SMT_VID	0x0001			/* V 5.1 .. 6.1 */
+#define SMT_VID_2 0x0002		/* V 7.2 */
+
+struct smt_sid {
+	u_char	sid_oem[2] ;			/* implementation spec. */
+	struct fddi_addr sid_node ;		/* node address */
+} ;
+
+typedef u_char	t_station_id[8] ;
+
+/*
+ * note on alignment :
+ * sizeof(struct smt_header) = 32
+ * all parameters are long aligned
+ * if struct smt_header starts at offset 0, all longs are aligned correctly
+ * (FC starts at offset 3)
+ */
+_packed struct smt_header {
+	struct fddi_addr    	smt_dest ;	/* destination address */
+	struct fddi_addr	smt_source ;	/* source address */
+	u_char			smt_class ;	/* NIF, SIF ... */
+	u_char			smt_type ;	/* req., response .. */
+	u_short			smt_version ;	/* version id */
+	u_int			smt_tid ;	/* transaction ID */
+	struct smt_sid		smt_sid ;	/* station ID */
+	u_short			smt_pad ;	/* pad with 0 */
+	u_short			smt_len ;	/* length of info field */
+} ;
+#define SWAP_SMTHEADER	"662sl8ss"
+
+#if	0
+/*
+ * MAC FC values
+ */
+#define FC_SMT_INFO	0x41		/* SMT info */
+#define FC_SMT_NSA	0x4f		/* SMT Next Station Addressing */
+#endif
+
+
+/*
+ * type codes
+ */
+#define SMT_ANNOUNCE	0x01		/* announcement */
+#define SMT_REQUEST	0x02		/* request */
+#define SMT_REPLY	0x03		/* reply */
+
+/*
+ * class codes
+ */
+#define SMT_NIF		0x01		/* neighbor information frames */
+#define SMT_SIF_CONFIG	0x02		/* station information configuration */
+#define SMT_SIF_OPER	0x03		/* station information operation */
+#define SMT_ECF		0x04		/* echo frames */
+#define SMT_RAF		0x05		/* resource allocation */
+#define SMT_RDF		0x06		/* request denied */
+#define SMT_SRF		0x07		/* status report */
+#define SMT_PMF_GET	0x08		/* parameter management get */
+#define SMT_PMF_SET	0x09		/* parameter management set */
+#define SMT_ESF		0xff		/* extended service */
+
+#define SMT_MAX_ECHO_LEN	4458	/* max length of SMT Echo */
+#if	defined(CONC) || defined(CONC_II)
+#define SMT_TEST_ECHO_LEN	50	/* test length of SMT Echo */
+#else
+#define SMT_TEST_ECHO_LEN	SMT_MAX_ECHO_LEN	/* test length */
+#endif
+
+#define SMT_MAX_INFO_LEN	(4352-20)	/* max length for SMT info */
+
+
+/*
+ * parameter types
+ */
+
+struct smt_para {
+	u_short	p_type ;		/* type */
+	u_short	p_len ;			/* length of parameter */
+} ;
+
+#define PARA_LEN	(sizeof(struct smt_para))
+
+#define SMTSETPARA(p,t)		(p)->para.p_type = (t),\
+				(p)->para.p_len = sizeof(*(p)) - PARA_LEN
+
+/*
+ * P01 : Upstream Neighbor Address, UNA
+ */
+#define SMT_P_UNA	0x0001		/* upstream neighbor address */
+#define SWAP_SMT_P_UNA	"s6"
+
+struct smt_p_una {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	una_pad ;
+	struct fddi_addr una_node ;	/* node address, zero if unknown */
+} ;
+
+/*
+ * P02 : Station Descriptor
+ */
+#define SMT_P_SDE	0x0002		/* station descriptor */
+#define SWAP_SMT_P_SDE	"1111"
+
+#define SMT_SDE_STATION		0	/* end node */
+#define SMT_SDE_CONCENTRATOR	1	/* concentrator */
+
+struct smt_p_sde {
+	struct smt_para	para ;		/* generic parameter header */
+	u_char	sde_type ;		/* station type */
+	u_char	sde_mac_count ;		/* number of MACs */
+	u_char	sde_non_master ;	/* number of A,B or S ports */
+	u_char	sde_master ;		/* number of S ports on conc. */
+} ;
+
+/*
+ * P03 : Station State
+ */
+#define SMT_P_STATE	0x0003		/* station state */
+#define SWAP_SMT_P_STATE	"scc"
+
+struct smt_p_state {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	st_pad ;
+	u_char	st_topology ;		/* topology */
+	u_char	st_dupl_addr ;		/* duplicate address detected */
+} ;
+#define SMT_ST_WRAPPED		(1<<0)	/* station wrapped */
+#define SMT_ST_UNATTACHED	(1<<1)	/* unattached concentrator */
+#define SMT_ST_TWISTED_A	(1<<2)	/* A-A connection, twisted ring */
+#define SMT_ST_TWISTED_B	(1<<3)	/* B-B connection, twisted ring */
+#define SMT_ST_ROOTED_S		(1<<4)	/* rooted station */
+#define SMT_ST_SRF		(1<<5)	/* SRF protocol supported */
+#define SMT_ST_SYNC_SERVICE	(1<<6)	/* use synchronous bandwidth */
+
+#define SMT_ST_MY_DUPA		(1<<0)	/* my station detected dupl. */
+#define SMT_ST_UNA_DUPA		(1<<1)	/* my UNA detected duplicate */
+
+/*
+ * P04 : timestamp
+ */
+#define SMT_P_TIMESTAMP	0x0004		/* time stamp */
+#define SWAP_SMT_P_TIMESTAMP	"8"
+struct smt_p_timestamp {
+	struct smt_para	para ;		/* generic parameter header */
+	u_char	ts_time[8] ;		/* time, resolution 80nS, unique */
+} ;
+
+/*
+ * P05 : station policies
+ */
+#define SMT_P_POLICY	0x0005		/* station policies */
+#define SWAP_SMT_P_POLICY	"ss"
+
+struct smt_p_policy {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	pl_config ;
+	u_short pl_connect ;		/* bit string POLICY_AA ... */
+} ;
+#define SMT_PL_HOLD		1	/* hold policy supported (Dual MAC) */
+
+/*
+ * P06 : latency equivalent
+ */
+#define SMT_P_LATENCY	0x0006		/* latency */
+#define SWAP_SMT_P_LATENCY	"ssss"
+
+/*
+ * note: latency has two phy entries by definition
+ * for a SAS, the 2nd one is null
+ */
+struct smt_p_latency {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	lt_phyout_idx1 ;	/* index */
+	u_short	lt_latency1 ;		/* latency , unit : byte clock */
+	u_short	lt_phyout_idx2 ;	/* 0 if SAS */
+	u_short	lt_latency2 ;		/* 0 if SAS */
+} ;
+
+/*
+ * P07 : MAC neighbors
+ */
+#define SMT_P_NEIGHBORS	0x0007		/* MAC neighbor description */
+#define SWAP_SMT_P_NEIGHBORS	"ss66"
+
+struct smt_p_neighbor {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	nb_mib_index ;		/* MIB index */
+	u_short	nb_mac_index ;		/* n+1 .. n+m, m = #MACs, n = #PHYs */
+	struct fddi_addr nb_una ;	/* UNA , 0 for unknown */
+	struct fddi_addr nb_dna ;	/* DNA , 0 for unknown */
+} ;
+
+/*
+ * PHY record
+ */
+#define SMT_PHY_A	0		/* A port */
+#define SMT_PHY_B	1		/* B port */
+#define SMT_PHY_S	2		/* slave port */
+#define SMT_PHY_M	3		/* master port */
+
+#define SMT_CS_DISABLED	0		/* connect state : disabled */
+#define SMT_CS_CONNECTING	1	/* connect state : connecting */
+#define SMT_CS_STANDBY	2		/* connect state : stand by */
+#define SMT_CS_ACTIVE	3		/* connect state : active */
+
+#define SMT_RM_NONE	0
+#define SMT_RM_MAC	1
+
+struct smt_phy_rec {
+	u_short	phy_mib_index ;		/* MIB index */
+	u_char	phy_type ;		/* A/B/S/M */
+	u_char	phy_connect_state ;	/* disabled/connecting/active */
+	u_char	phy_remote_type ;	/* A/B/S/M */
+	u_char	phy_remote_mac ;	/* none/remote */
+	u_short	phy_resource_idx ;	/* 1 .. n */
+} ;
+
+/*
+ * MAC record
+ */
+struct smt_mac_rec {
+	struct fddi_addr mac_addr ;		/* MAC address */
+	u_short		mac_resource_idx ;	/* n+1 .. n+m */
+} ;
+
+/*
+ * P08 : path descriptors
+ * should be really an array ; however our environment has a fixed number of
+ * PHYs and MACs
+ */
+#define SMT_P_PATH	0x0008			/* path descriptor */
+#define SWAP_SMT_P_PATH	"[6s]"
+
+struct smt_p_path {
+	struct smt_para	para ;		/* generic parameter header */
+	struct smt_phy_rec	pd_phy[2] ;	/* PHY A */
+	struct smt_mac_rec	pd_mac ;	/* MAC record */
+} ;
+
+/*
+ * P09 : MAC status
+ */
+#define SMT_P_MAC_STATUS	0x0009		/* MAC status */
+#define SWAP_SMT_P_MAC_STATUS	"sslllllllll"
+
+struct smt_p_mac_status {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short st_mib_index ;		/* MIB index */
+	u_short	st_mac_index ;		/* n+1 .. n+m */
+	u_int	st_t_req ;		/* T_Req */
+	u_int	st_t_neg ;		/* T_Neg */
+	u_int	st_t_max ;		/* T_Max */
+	u_int	st_tvx_value ;		/* TVX_Value */
+	u_int	st_t_min ;		/* T_Min */
+	u_int	st_sba ;		/* synchr. bandwidth alloc */
+	u_int	st_frame_ct ;		/* frame counter */
+	u_int	st_error_ct ;		/* error counter */
+	u_int	st_lost_ct ;		/* lost frames counter */
+} ;
+
+/*
+ * P0A : PHY link error rate monitoring
+ */
+#define SMT_P_LEM	0x000a		/* link error monitor */
+#define SWAP_SMT_P_LEM	"ssccccll"
+/*
+ * units of lem_cutoff,lem_alarm,lem_estimate : 10**-x
+ */
+struct smt_p_lem {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	lem_mib_index ;		/* MIB index */
+	u_short	lem_phy_index ;		/* 1 .. n */
+	u_char	lem_pad2 ;		/* be nice and make it even . */
+	u_char	lem_cutoff ;		/* 0x4 .. 0xf, default 0x7 */
+	u_char	lem_alarm ;		/* 0x4 .. 0xf, default 0x8 */
+	u_char	lem_estimate ;		/* 0x0 .. 0xff */
+	u_int	lem_reject_ct ;		/* 0x00000000 .. 0xffffffff */
+	u_int	lem_ct ;		/* 0x00000000 .. 0xffffffff */
+} ;
+
+/*
+ * P0B : MAC frame counters
+ */
+#define SMT_P_MAC_COUNTER 0x000b	/* MAC frame counters */
+#define SWAP_SMT_P_MAC_COUNTER	"ssll"
+
+struct smt_p_mac_counter {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	mc_mib_index ;		/* MIB index */
+	u_short	mc_index ;		/* mac index */
+	u_int	mc_receive_ct ;		/* receive counter */
+	u_int	mc_transmit_ct ;	/* transmit counter */
+} ;
+
+/*
+ * P0C : MAC frame not copied counter
+ */
+#define SMT_P_MAC_FNC	0x000c		/* MAC frame not copied counter */
+#define SWAP_SMT_P_MAC_FNC	"ssl"
+
+struct smt_p_mac_fnc {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	nc_mib_index ;		/* MIB index */
+	u_short	nc_index ;		/* mac index */
+	u_int	nc_counter ;		/* not copied counter */
+} ;
+
+
+/*
+ * P0D : MAC priority values
+ */
+#define SMT_P_PRIORITY	0x000d		/* MAC priority values */
+#define SWAP_SMT_P_PRIORITY	"ssl"
+
+struct smt_p_priority {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	pr_mib_index ;		/* MIB index */
+	u_short	pr_index ;		/* mac index */
+	u_int	pr_priority[7] ;	/* priority values */
+} ;
+
+/*
+ * P0E : PHY elasticity buffer status
+ */
+#define SMT_P_EB	0x000e		/* PHY EB status */
+#define SWAP_SMT_P_EB	"ssl"
+
+struct smt_p_eb {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	eb_mib_index ;		/* MIB index */
+	u_short	eb_index ;		/* phy index */
+	u_int	eb_error_ct ;		/* # of eb overflows */
+} ;
+
+/*
+ * P0F : manufacturer field
+ */
+#define SMT_P_MANUFACTURER	0x000f	/* manufacturer field */
+#define SWAP_SMT_P_MANUFACTURER	""
+
+struct smp_p_manufacturer {
+	struct smt_para	para ;		/* generic parameter header */
+	u_char mf_data[32] ;		/* OUI + arbitrary data */
+} ;
+
+/*
+ * P10 : user field
+ */
+#define SMT_P_USER		0x0010	/* manufacturer field */
+#define SWAP_SMT_P_USER	""
+
+struct smp_p_user {
+	struct smt_para	para ;		/* generic parameter header */
+	u_char us_data[32] ;		/* arbitrary data */
+} ;
+
+
+
+/*
+ * P11 : echo data
+ */
+#define SMT_P_ECHODATA	0x0011		/* echo data */
+#define SWAP_SMT_P_ECHODATA	""
+
+struct smt_p_echo {
+	struct smt_para	para ;		/* generic parameter header */
+	u_char	ec_data[SMT_MAX_ECHO_LEN-4] ;	/* echo data */
+} ;
+
+/*
+ * P12 : reason code
+ */
+#define SMT_P_REASON	0x0012		/* reason code */
+#define SWAP_SMT_P_REASON	"l"
+
+struct smt_p_reason {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int	rdf_reason ;		/* CLASS/VERSION */
+} ;
+#define SMT_RDF_CLASS	0x00000001	/* class not supported */
+#define SMT_RDF_VERSION	0x00000002	/* version not supported */
+#define SMT_RDF_SUCCESS	0x00000003	/* success (PMF) */
+#define SMT_RDF_BADSET	0x00000004	/* bad set count (PMF) */
+#define SMT_RDF_ILLEGAL 0x00000005	/* read only (PMF) */
+#define SMT_RDF_NOPARAM	0x6		/* paramter not supported (PMF) */
+#define SMT_RDF_RANGE	0x8		/* out of range */
+#define SMT_RDF_AUTHOR	0x9		/* not autohorized */
+#define SMT_RDF_LENGTH	0x0a		/* length error */
+#define SMT_RDF_TOOLONG	0x0b		/* length error */
+#define SMT_RDF_SBA	0x0d		/* SBA denied */
+
+/*
+ * P13 : refused frame beginning
+ */
+#define SMT_P_REFUSED	0x0013		/* refused frame beginning */
+#define SWAP_SMT_P_REFUSED	"l"
+
+struct smt_p_refused {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int	ref_fc ;		/* 3 bytes 0 + FC */
+	struct smt_header	ref_header ;	/* refused header */
+} ;
+
+/*
+ * P14 : supported SMT versions
+ */
+#define SMT_P_VERSION	0x0014		/* SMT supported versions */
+#define SWAP_SMT_P_VERSION	"sccss"
+
+struct smt_p_version {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	v_pad ;
+	u_char	v_n ;			/* 1 .. 0xff, #versions */
+	u_char	v_index ;		/* 1 .. 0xff, index of op. v. */
+	u_short	v_version[1] ;		/* list of min. 1 version */
+	u_short	v_pad2 ;		/* pad if necessary */
+} ;
+
+/*
+ * P15 : Resource Type
+ */
+#define	SWAP_SMT_P0015		"l"
+
+struct smt_p_0015 {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		res_type ;	/* recsource type */
+} ;
+
+#define	SYNC_BW		0x00000001L	/* Synchronous Bandwidth */
+
+/*
+ * P16 : SBA Command
+ */
+#define	SWAP_SMT_P0016		"l"
+
+struct smt_p_0016 {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		sba_cmd ;	/* command for the SBA */
+} ;
+
+#define	REQUEST_ALLOCATION	0x1	/* req allocation of sync bandwidth */
+#define	REPORT_ALLOCATION	0x2	/* rep of sync bandwidth allocation */
+#define	CHANGE_ALLOCATION	0x3	/* forces a station using sync band-*/
+					/* width to change its current allo-*/
+					/* cation */
+
+/*
+ * P17 : SBA Payload Request
+ */
+#define	SWAP_SMT_P0017		"l"
+
+struct smt_p_0017 {
+	struct smt_para	para ;		/* generic parameter header */
+	int		sba_pl_req ;	/* total sync bandwidth measured in */
+} ;					/* bytes per 125 us */
+
+/*
+ * P18 : SBA Overhead Request
+ */
+#define	SWAP_SMT_P0018		"l"
+
+struct smt_p_0018 {
+	struct smt_para	para ;		/* generic parameter header */
+	int		sba_ov_req ;	/* total sync bandwidth req for overhead*/
+} ;					/* measuered in bytes per T_Neg */
+
+/*
+ * P19 : SBA Allocation Address
+ */
+#define	SWAP_SMT_P0019		"s6"
+
+struct smt_p_0019 {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short		sba_pad ;
+	struct fddi_addr alloc_addr ;	/* Allocation Address */
+} ;
+
+/*
+ * P1A : SBA Category
+ */
+#define	SWAP_SMT_P001A		"l"
+
+struct smt_p_001a {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		category ;	/* Allocator defined classification */
+} ;
+
+/*
+ * P1B : Maximum T_Neg
+ */
+#define	SWAP_SMT_P001B		"l"
+
+struct smt_p_001b {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		max_t_neg ;	/* longest T_NEG for the sync service*/
+} ;
+
+/*
+ * P1C : Minimum SBA Segment Size
+ */
+#define	SWAP_SMT_P001C		"l"
+
+struct smt_p_001c {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		min_seg_siz ;	/* smallest number of bytes per frame*/
+} ;
+
+/*
+ * P1D : SBA Allocatable
+ */
+#define	SWAP_SMT_P001D		"l"
+
+struct smt_p_001d {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		allocatable ;	/* total sync bw availabel for alloc */
+} ;
+
+/*
+ * P20 0B : frame status capabilities
+ * NOTE: not in swap table, is used by smt.c AND PMF table
+ */
+#define SMT_P_FSC	0x200b
+/* #define SWAP_SMT_P_FSC	"ssss" */
+
+struct smt_p_fsc {
+	struct smt_para	para ;		/* generic parameter header */
+	u_short	fsc_pad0 ;
+	u_short	fsc_mac_index ;		/* mac index 1 .. ff */
+	u_short	fsc_pad1 ;
+	u_short	fsc_value ;		/* FSC_TYPE[0-2] */
+} ;
+
+#define FSC_TYPE0	0		/* "normal" node (A/C handling) */
+#define FSC_TYPE1	1		/* Special A/C indicator forwarding */
+#define FSC_TYPE2	2		/* Special A/C indicator forwarding */
+
+/*
+ * P00 21 : user defined authoriziation (see pmf.c)
+ */
+#define SMT_P_AUTHOR	0x0021
+
+/*
+ * notification parameters
+ */
+#define SWAP_SMT_P1048	"ll"
+struct smt_p_1048 {
+	u_int p1048_flag ;
+	u_int p1048_cf_state ;
+} ;
+
+/*
+ * NOTE: all 2xxx 3xxx and 4xxx must include the INDEX in the swap string,
+ *	even so the INDEX is NOT part of the struct.
+ *	INDEX is already swapped in pmf.c, format in string is '4'
+ */
+#define SWAP_SMT_P208C	"4lss66"
+struct smt_p_208c {
+	u_int			p208c_flag ;
+	u_short			p208c_pad ;
+	u_short			p208c_dupcondition ;
+	struct	fddi_addr	p208c_fddilong ;
+	struct	fddi_addr	p208c_fddiunalong ;
+} ;
+
+#define SWAP_SMT_P208D	"4lllll"
+struct smt_p_208d {
+	u_int			p208d_flag ;
+	u_int			p208d_frame_ct ;
+	u_int			p208d_error_ct ;
+	u_int			p208d_lost_ct ;
+	u_int			p208d_ratio ;
+} ;
+
+#define SWAP_SMT_P208E	"4llll"
+struct smt_p_208e {
+	u_int			p208e_flag ;
+	u_int			p208e_not_copied ;
+	u_int			p208e_copied ;
+	u_int			p208e_not_copied_ratio ;
+} ;
+
+#define SWAP_SMT_P208F	"4ll6666s6"
+
+struct smt_p_208f {
+	u_int			p208f_multiple ;
+	u_int			p208f_nacondition ;
+	struct fddi_addr	p208f_old_una ;
+	struct fddi_addr	p208f_new_una ;
+	struct fddi_addr	p208f_old_dna ;
+	struct fddi_addr	p208f_new_dna ;
+	u_short			p208f_curren_path ;
+	struct fddi_addr	p208f_smt_address ;
+} ;
+
+#define SWAP_SMT_P2090	"4lssl"
+
+struct smt_p_2090 {
+	u_int			p2090_multiple ;
+	u_short			p2090_availablepaths ;
+	u_short			p2090_currentpath ;
+	u_int			p2090_requestedpaths ;
+} ;
+
+/*
+ * NOTE:
+ * special kludge for parameters 320b,320f,3210
+ * these parameters are part of RAF frames
+ * RAF frames are parsed in SBA.C and must be swapped
+ * PMF.C has special code to avoid double swapping
+ */
+#ifdef	LITTLE_ENDIAN
+#define SBAPATHINDEX	(0x01000000L)
+#else
+#define SBAPATHINDEX	(0x01L)
+#endif
+
+#define	SWAP_SMT_P320B	"42s"
+
+struct	smt_p_320b {
+	struct smt_para para ;	/* generic parameter header */
+	u_int	mib_index ;
+	u_short path_pad ;
+	u_short	path_index ;
+} ;
+
+#define	SWAP_SMT_P320F	"4l"
+
+struct	smt_p_320f {
+	struct smt_para para ;	/* generic parameter header */
+	u_int	mib_index ;
+	u_int	mib_payload ;
+} ;
+
+#define	SWAP_SMT_P3210	"4l"
+
+struct	smt_p_3210 {
+	struct smt_para para ;	/* generic parameter header */
+	u_int	mib_index ;
+	u_int	mib_overhead ;
+} ;
+
+#define SWAP_SMT_P4050	"4l1111ll"
+
+struct smt_p_4050 {
+	u_int			p4050_flag ;
+	u_char			p4050_pad ;
+	u_char			p4050_cutoff ;
+	u_char			p4050_alarm ;
+	u_char			p4050_estimate ;
+	u_int			p4050_reject_ct ;
+	u_int			p4050_ct ;
+} ;
+
+#define SWAP_SMT_P4051	"4lssss"
+struct smt_p_4051 {
+	u_int			p4051_multiple ;
+	u_short			p4051_porttype ;
+	u_short			p4051_connectstate ;
+	u_short			p4051_pc_neighbor ;
+	u_short			p4051_pc_withhold ;
+} ;
+
+#define SWAP_SMT_P4052	"4ll"
+struct smt_p_4052 {
+	u_int			p4052_flag ;
+	u_int			p4052_eberrorcount ;
+} ;
+
+#define SWAP_SMT_P4053	"4lsslss"
+
+struct smt_p_4053 {
+	u_int			p4053_multiple ;
+	u_short			p4053_availablepaths ;
+	u_short			p4053_currentpath ;
+	u_int			p4053_requestedpaths ;
+	u_short			p4053_mytype ;
+	u_short			p4053_neighbortype ;
+} ;
+
+
+#define SMT_P_SETCOUNT	0x1035
+#define SWAP_SMT_P_SETCOUNT	"l8"
+
+struct smt_p_setcount {
+	struct smt_para	para ;		/* generic parameter header */
+	u_int		count ;
+	u_char		timestamp[8] ;
+} ;
+
+/*
+ * SMT FRAMES
+ */
+
+/*
+ * NIF : neighbor information frames
+ */
+struct smt_nif {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_una	una ;		/* UNA */
+	struct smt_p_sde	sde ;		/* station descriptor */
+	struct smt_p_state	state ;		/* station state */
+#ifdef	SMT6_10
+	struct smt_p_fsc	fsc ;		/* frame status cap. */
+#endif
+} ;
+
+/*
+ * SIF : station information frames
+ */
+struct smt_sif_config {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_timestamp	ts ;		/* time stamp */
+	struct smt_p_sde	sde ;		/* station descriptor */
+	struct smt_p_version	version ;	/* supported versions */
+	struct smt_p_state	state ;		/* station state */
+	struct smt_p_policy	policy ;	/* station policy */
+	struct smt_p_latency	latency ;	/* path latency */
+	struct smt_p_neighbor	neighbor ;	/* neighbors, we have only one*/
+#ifdef	OPT_PMF
+	struct smt_p_setcount	setcount ;	 /* Set Count mandatory */
+#endif
+	/* WARNING : path MUST BE LAST FIELD !!! (see smt.c:smt_fill_path) */
+	struct smt_p_path	path ;		/* path descriptor */
+} ;
+#define SIZEOF_SMT_SIF_CONFIG	(sizeof(struct smt_sif_config)- \
+				 sizeof(struct smt_p_path))
+
+struct smt_sif_operation {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_timestamp	ts ;		/* time stamp */
+	struct smt_p_mac_status	status ;	/* mac status */
+	struct smt_p_mac_counter mc ;		/* MAC counter */
+	struct smt_p_mac_fnc 	fnc ;		/* MAC frame not copied */
+	struct smp_p_manufacturer man ;		/* manufacturer field */
+	struct smp_p_user	user ;		/* user field */
+#ifdef	OPT_PMF
+	struct smt_p_setcount	setcount ;	 /* Set Count mandatory */
+#endif
+	/* must be last */
+	struct smt_p_lem	lem[1] ;	/* phy lem status */
+} ;
+#define SIZEOF_SMT_SIF_OPERATION	(sizeof(struct smt_sif_operation)- \
+					 sizeof(struct smt_p_lem))
+
+/*
+ * ECF : echo frame
+ */
+struct smt_ecf {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_echo	ec_echo ;	/* echo parameter */
+} ;
+#define SMT_ECF_LEN	(sizeof(struct smt_header)+sizeof(struct smt_para))
+
+/*
+ * RDF : request denied frame
+ */
+struct smt_rdf {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_reason	reason ;	/* reason code */
+	struct smt_p_version	version ;	/* supported versions */
+	struct smt_p_refused	refused ;	/* refused frame fragment */
+} ;
+
+/*
+ * SBA Request Allocation Responce Frame
+ */
+struct smt_sba_alc_res {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_0015	s_type ;	/* resource type */
+	struct smt_p_0016	cmd ;		/* SBA command */
+	struct smt_p_reason	reason ;	/* reason code */
+	struct smt_p_320b	path ;		/* path type */
+	struct smt_p_320f	payload ;	/* current SBA payload */
+	struct smt_p_3210	overhead ;	/* current SBA overhead */
+	struct smt_p_0019	a_addr ;	/* Allocation Address */
+	struct smt_p_001a	cat ;		/* Category - from the request */
+	struct smt_p_001d	alloc ;		/* SBA Allocatable */
+} ;
+
+/*
+ * SBA Request Allocation Request Frame
+ */
+struct smt_sba_alc_req {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_0015	s_type ;	/* resource type */
+	struct smt_p_0016	cmd ;		/* SBA command */
+	struct smt_p_320b	path ;		/* path type */
+	struct smt_p_0017	pl_req ;	/* requested payload */
+	struct smt_p_0018	ov_req ;	/* requested SBA overhead */
+	struct smt_p_320f	payload ;	/* current SBA payload */
+	struct smt_p_3210	overhead ;	/* current SBA overhead */
+	struct smt_p_0019	a_addr ;	/* Allocation Address */
+	struct smt_p_001a	cat ;		/* Category - from the request */
+	struct smt_p_001b	tneg ;		/* max T-NEG */
+	struct smt_p_001c	segm ;		/* minimum segment size */
+} ;
+
+/*
+ * SBA Change Allocation Request Frame
+ */
+struct smt_sba_chg {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_0015	s_type ;	/* resource type */
+	struct smt_p_0016	cmd ;		/* SBA command */
+	struct smt_p_320b	path ;		/* path type */
+	struct smt_p_320f	payload ;	/* current SBA payload */
+	struct smt_p_3210	overhead ;	/* current SBA overhead */
+	struct smt_p_001a	cat ;		/* Category - from the request */
+} ;
+
+/*
+ * SBA Report Allocation Request Frame
+ */
+struct smt_sba_rep_req {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_0015	s_type ;	/* resource type */
+	struct smt_p_0016	cmd ;		/* SBA command */
+} ;
+
+/*
+ * SBA Report Allocation Response Frame
+ */
+struct smt_sba_rep_res {
+	struct smt_header	smt ;		/* generic header */
+	struct smt_p_0015	s_type ;	/* resource type */
+	struct smt_p_0016	cmd ;		/* SBA command */
+	struct smt_p_320b	path ;		/* path type */
+	struct smt_p_320f	payload ;	/* current SBA payload */
+	struct smt_p_3210	overhead ;	/* current SBA overhead */
+} ;
+
+/*
+ * actions
+ */
+#define SMT_STATION_ACTION	1
+#define SMT_STATION_ACTION_CONNECT	0
+#define SMT_STATION_ACTION_DISCONNECT	1
+#define SMT_STATION_ACTION_PATHTEST	2
+#define SMT_STATION_ACTION_SELFTEST	3
+#define SMT_STATION_ACTION_DISABLE_A	4
+#define SMT_STATION_ACTION_DISABLE_B	5
+#define SMT_STATION_ACTION_DISABLE_M	6
+
+#define SMT_PORT_ACTION		2
+#define SMT_PORT_ACTION_MAINT	0
+#define SMT_PORT_ACTION_ENABLE	1
+#define SMT_PORT_ACTION_DISABLE	2
+#define SMT_PORT_ACTION_START	3
+#define SMT_PORT_ACTION_STOP	4
+
+#endif	/* _SMT_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/smt_p.h linux.18p17/drivers/net/skfp/h/smt_p.h
--- linux.vanilla/drivers/net/skfp/h/smt_p.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/smt_p.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,326 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * defines for all SMT attributes
+ */
+
+/*
+ * this boring file was produced by perl
+ * thanks Larry !
+ */
+#define	SMT_P0012	0x0012
+
+#define	SMT_P0015	0x0015
+#define	SMT_P0016	0x0016
+#define	SMT_P0017	0x0017
+#define	SMT_P0018	0x0018
+#define	SMT_P0019	0x0019
+
+#define	SMT_P001A	0x001a
+#define	SMT_P001B	0x001b
+#define	SMT_P001C	0x001c
+#define	SMT_P001D	0x001d
+
+#define	SMT_P100A	0x100a
+#define	SMT_P100B	0x100b
+#define	SMT_P100C	0x100c
+#define	SMT_P100D	0x100d
+#define	SMT_P100E	0x100e
+#define	SMT_P100F	0x100f
+#define	SMT_P1010	0x1010
+#define	SMT_P1011	0x1011
+#define	SMT_P1012	0x1012
+#define	SMT_P1013	0x1013
+#define	SMT_P1014	0x1014
+#define	SMT_P1015	0x1015
+#define	SMT_P1016	0x1016
+#define	SMT_P1017	0x1017
+#define	SMT_P1018	0x1018
+#define	SMT_P1019	0x1019
+#define	SMT_P101A	0x101a
+#define	SMT_P101B	0x101b
+#define	SMT_P101C	0x101c
+#define	SMT_P101D	0x101d
+#define	SMT_P101E	0x101e
+#define	SMT_P101F	0x101f
+#define	SMT_P1020	0x1020
+#define	SMT_P1021	0x1021
+#define	SMT_P1022	0x1022
+#define	SMT_P1023	0x1023
+#define	SMT_P1024	0x1024
+#define	SMT_P1025	0x1025
+#define	SMT_P1026	0x1026
+#define	SMT_P1027	0x1027
+#define	SMT_P1028	0x1028
+#define	SMT_P1029	0x1029
+#define	SMT_P102A	0x102a
+#define	SMT_P102B	0x102b
+#define	SMT_P102C	0x102c
+#define	SMT_P102D	0x102d
+#define	SMT_P102E	0x102e
+#define	SMT_P102F	0x102f
+#define	SMT_P1030	0x1030
+#define	SMT_P1031	0x1031
+#define	SMT_P1032	0x1032
+#define	SMT_P1033	0x1033
+#define	SMT_P1034	0x1034
+#define	SMT_P1035	0x1035
+#define	SMT_P1036	0x1036
+#define	SMT_P1037	0x1037
+#define	SMT_P1038	0x1038
+#define	SMT_P1039	0x1039
+#define	SMT_P103A	0x103a
+#define	SMT_P103B	0x103b
+#define	SMT_P103C	0x103c
+#define	SMT_P103D	0x103d
+#define	SMT_P103E	0x103e
+#define	SMT_P103F	0x103f
+#define	SMT_P1040	0x1040
+#define	SMT_P1041	0x1041
+#define	SMT_P1042	0x1042
+#define	SMT_P1043	0x1043
+#define	SMT_P1044	0x1044
+#define	SMT_P1045	0x1045
+#define	SMT_P1046	0x1046
+#define	SMT_P1047	0x1047
+#define	SMT_P1048	0x1048
+#define	SMT_P1049	0x1049
+#define	SMT_P104A	0x104a
+#define	SMT_P104B	0x104b
+#define	SMT_P104C	0x104c
+#define	SMT_P104D	0x104d
+#define	SMT_P104E	0x104e
+#define	SMT_P104F	0x104f
+#define	SMT_P1050	0x1050
+#define	SMT_P1051	0x1051
+#define	SMT_P1052	0x1052
+#define	SMT_P1053	0x1053
+#define	SMT_P1054	0x1054
+
+#define	SMT_P10F0	0x10f0
+#define	SMT_P10F1	0x10f1
+#ifdef	ESS
+#define	SMT_P10F2	0x10f2
+#define	SMT_P10F3	0x10f3
+#define	SMT_P10F4	0x10f4
+#define	SMT_P10F5	0x10f5
+#define	SMT_P10F6	0x10f6
+#define	SMT_P10F7	0x10f7
+#endif
+#ifdef	SBA
+#define	SMT_P10F8	0x10f8
+#define	SMT_P10F9	0x10f9
+#endif
+
+#define	SMT_P200A	0x200a
+#define	SMT_P200B	0x200b
+#define	SMT_P200C	0x200c
+#define	SMT_P200D	0x200d
+#define	SMT_P200E	0x200e
+#define	SMT_P200F	0x200f
+#define	SMT_P2010	0x2010
+#define	SMT_P2011	0x2011
+#define	SMT_P2012	0x2012
+#define	SMT_P2013	0x2013
+#define	SMT_P2014	0x2014
+#define	SMT_P2015	0x2015
+#define	SMT_P2016	0x2016
+#define	SMT_P2017	0x2017
+#define	SMT_P2018	0x2018
+#define	SMT_P2019	0x2019
+#define	SMT_P201A	0x201a
+#define	SMT_P201B	0x201b
+#define	SMT_P201C	0x201c
+#define	SMT_P201D	0x201d
+#define	SMT_P201E	0x201e
+#define	SMT_P201F	0x201f
+#define	SMT_P2020	0x2020
+#define	SMT_P2021	0x2021
+#define	SMT_P2022	0x2022
+#define	SMT_P2023	0x2023
+#define	SMT_P2024	0x2024
+#define	SMT_P2025	0x2025
+#define	SMT_P2026	0x2026
+#define	SMT_P2027	0x2027
+#define	SMT_P2028	0x2028
+#define	SMT_P2029	0x2029
+#define	SMT_P202A	0x202a
+#define	SMT_P202B	0x202b
+#define	SMT_P202C	0x202c
+#define	SMT_P202D	0x202d
+#define	SMT_P202E	0x202e
+#define	SMT_P202F	0x202f
+#define	SMT_P2030	0x2030
+#define	SMT_P2031	0x2031
+#define	SMT_P2032	0x2032
+#define	SMT_P2033	0x2033
+#define	SMT_P2034	0x2034
+#define	SMT_P2035	0x2035
+#define	SMT_P2036	0x2036
+#define	SMT_P2037	0x2037
+#define	SMT_P2038	0x2038
+#define	SMT_P2039	0x2039
+#define	SMT_P203A	0x203a
+#define	SMT_P203B	0x203b
+#define	SMT_P203C	0x203c
+#define	SMT_P203D	0x203d
+#define	SMT_P203E	0x203e
+#define	SMT_P203F	0x203f
+#define	SMT_P2040	0x2040
+#define	SMT_P2041	0x2041
+#define	SMT_P2042	0x2042
+#define	SMT_P2043	0x2043
+#define	SMT_P2044	0x2044
+#define	SMT_P2045	0x2045
+#define	SMT_P2046	0x2046
+#define	SMT_P2047	0x2047
+#define	SMT_P2048	0x2048
+#define	SMT_P2049	0x2049
+#define	SMT_P204A	0x204a
+#define	SMT_P204B	0x204b
+#define	SMT_P204C	0x204c
+#define	SMT_P204D	0x204d
+#define	SMT_P204E	0x204e
+#define	SMT_P204F	0x204f
+#define	SMT_P2050	0x2050
+#define	SMT_P2051	0x2051
+#define	SMT_P2052	0x2052
+#define	SMT_P2053	0x2053
+#define	SMT_P2054	0x2054
+#define	SMT_P2055	0x2055
+#define	SMT_P2056	0x2056
+#define	SMT_P2057	0x2057
+#define	SMT_P2058	0x2058
+#define	SMT_P2059	0x2059
+#define	SMT_P205A	0x205a
+#define	SMT_P205B	0x205b
+#define	SMT_P205C	0x205c
+#define	SMT_P205D	0x205d
+#define	SMT_P205E	0x205e
+#define	SMT_P205F	0x205f
+#define	SMT_P2060	0x2060
+#define	SMT_P2061	0x2061
+#define	SMT_P2062	0x2062
+#define	SMT_P2063	0x2063
+#define	SMT_P2064	0x2064
+#define	SMT_P2065	0x2065
+#define	SMT_P2066	0x2066
+#define	SMT_P2067	0x2067
+#define	SMT_P2068	0x2068
+#define	SMT_P2069	0x2069
+#define	SMT_P206A	0x206a
+#define	SMT_P206B	0x206b
+#define	SMT_P206C	0x206c
+#define	SMT_P206D	0x206d
+#define	SMT_P206E	0x206e
+#define	SMT_P206F	0x206f
+#define	SMT_P2070	0x2070
+#define	SMT_P2071	0x2071
+#define	SMT_P2072	0x2072
+#define	SMT_P2073	0x2073
+#define	SMT_P2074	0x2074
+#define	SMT_P2075	0x2075
+#define	SMT_P2076	0x2076
+
+#define	SMT_P208C	0x208c
+#define	SMT_P208D	0x208d
+#define	SMT_P208E	0x208e
+#define	SMT_P208F	0x208f
+#define	SMT_P2090	0x2090
+
+#define	SMT_P20F0	0x20F0
+#define	SMT_P20F1	0x20F1
+
+#define	SMT_P320A	0x320a
+#define	SMT_P320B	0x320b
+#define	SMT_P320C	0x320c
+#define	SMT_P320D	0x320d
+#define	SMT_P320E	0x320e
+#define	SMT_P320F	0x320f
+#define	SMT_P3210	0x3210
+#define	SMT_P3211	0x3211
+#define	SMT_P3212	0x3212
+#define	SMT_P3213	0x3213
+#define	SMT_P3214	0x3214
+#define	SMT_P3215	0x3215
+#define	SMT_P3216	0x3216
+#define	SMT_P3217	0x3217
+
+#define	SMT_P400A	0x400a
+#define	SMT_P400B	0x400b
+#define	SMT_P400C	0x400c
+#define	SMT_P400D	0x400d
+#define	SMT_P400E	0x400e
+#define	SMT_P400F	0x400f
+#define	SMT_P4010	0x4010
+#define	SMT_P4011	0x4011
+#define	SMT_P4012	0x4012
+#define	SMT_P4013	0x4013
+#define	SMT_P4014	0x4014
+#define	SMT_P4015	0x4015
+#define	SMT_P4016	0x4016
+#define	SMT_P4017	0x4017
+#define	SMT_P4018	0x4018
+#define	SMT_P4019	0x4019
+#define	SMT_P401A	0x401a
+#define	SMT_P401B	0x401b
+#define	SMT_P401C	0x401c
+#define	SMT_P401D	0x401d
+#define	SMT_P401E	0x401e
+#define	SMT_P401F	0x401f
+#define	SMT_P4020	0x4020
+#define	SMT_P4021	0x4021
+#define	SMT_P4022	0x4022
+#define	SMT_P4023	0x4023
+#define	SMT_P4024	0x4024
+#define	SMT_P4025	0x4025
+#define	SMT_P4026	0x4026
+#define	SMT_P4027	0x4027
+#define	SMT_P4028	0x4028
+#define	SMT_P4029	0x4029
+#define	SMT_P402A	0x402a
+#define	SMT_P402B	0x402b
+#define	SMT_P402C	0x402c
+#define	SMT_P402D	0x402d
+#define	SMT_P402E	0x402e
+#define	SMT_P402F	0x402f
+#define	SMT_P4030	0x4030
+#define	SMT_P4031	0x4031
+#define	SMT_P4032	0x4032
+#define	SMT_P4033	0x4033
+#define	SMT_P4034	0x4034
+#define	SMT_P4035	0x4035
+#define	SMT_P4036	0x4036
+#define	SMT_P4037	0x4037
+#define	SMT_P4038	0x4038
+#define	SMT_P4039	0x4039
+#define	SMT_P403A	0x403a
+#define	SMT_P403B	0x403b
+#define	SMT_P403C	0x403c
+#define	SMT_P403D	0x403d
+#define	SMT_P403E	0x403e
+#define	SMT_P403F	0x403f
+#define	SMT_P4040	0x4040
+#define	SMT_P4041	0x4041
+#define	SMT_P4042	0x4042
+#define	SMT_P4043	0x4043
+#define	SMT_P4044	0x4044
+#define	SMT_P4045	0x4045
+#define	SMT_P4046	0x4046
+
+#define	SMT_P4050	0x4050
+#define	SMT_P4051	0x4051
+#define	SMT_P4052	0x4052
+#define	SMT_P4053	0x4053
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/smtstate.h linux.18p17/drivers/net/skfp/h/smtstate.h
--- linux.vanilla/drivers/net/skfp/h/smtstate.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/smtstate.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,100 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ *	SMT state definitions
+ */
+
+#ifndef	KERNEL
+/*
+ * PCM states
+ */
+#define PC0_OFF			0
+#define PC1_BREAK		1
+#define PC2_TRACE		2
+#define PC3_CONNECT		3
+#define PC4_NEXT		4
+#define PC5_SIGNAL		5
+#define PC6_JOIN		6
+#define PC7_VERIFY		7
+#define PC8_ACTIVE		8
+#define PC9_MAINT		9
+
+/*
+ * PCM modes
+ */
+#define PM_NONE			0
+#define PM_PEER			1
+#define PM_TREE			2
+
+/*
+ * PCM type
+ */
+#define TA			0
+#define TB			1
+#define TS			2
+#define TM			3
+#define TNONE			4
+
+/*
+ * CFM states
+ */
+#define SC0_ISOLATED	0		/* isolated */
+#define SC1_WRAP_A	5		/* wrap A */
+#define SC2_WRAP_B	6		/* wrap B */
+#define SC4_THRU_A	12		/* through A */
+#define SC5_THRU_B	7		/* through B (SMt 6.2) */
+#define SC7_WRAP_S	8		/* SAS */
+
+/*
+ * ECM states
+ */
+#define EC0_OUT		0
+#define EC1_IN		1
+#define EC2_TRACE	2
+#define EC3_LEAVE	3
+#define EC4_PATH_TEST	4
+#define EC5_INSERT	5
+#define EC6_CHECK	6
+#define EC7_DEINSERT	7
+
+/*
+ * RMT states
+ */
+#define RM0_ISOLATED	0
+#define RM1_NON_OP	1		/* not operational */
+#define RM2_RING_OP	2		/* ring operational */
+#define RM3_DETECT	3		/* detect dupl addresses */
+#define RM4_NON_OP_DUP	4		/* dupl. addr detected */
+#define RM5_RING_OP_DUP	5		/* ring oper. with dupl. addr */
+#define RM6_DIRECTED	6		/* sending directed beacons */
+#define RM7_TRACE	7		/* trace initiated */
+#endif
+
+struct pcm_state {
+	unsigned char	pcm_type ;		/* TA TB TS TM */
+	unsigned char	pcm_state ;		/* state PC[0-9]_* */
+	unsigned char	pcm_mode ;		/* PM_{NONE,PEER,TREE} */
+	unsigned char	pcm_neighbor ;		/* TA TB TS TM */
+	unsigned char	pcm_bsf ;		/* flag bs : TRUE/FALSE */
+	unsigned char	pcm_lsf ;		/* flag ls : TRUE/FALSE */
+	unsigned char	pcm_lct_fail ;		/* counter lct_fail */
+	unsigned char	pcm_ls_rx ;		/* rx line state */
+	short		pcm_r_val ;		/* signaling bits */
+	short		pcm_t_val ;		/* signaling bits */
+} ;
+
+struct smt_state {
+	struct pcm_state pcm_state[NUMPHYS] ;	/* port A & port B */
+} ;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/supern_2.h linux.18p17/drivers/net/skfp/h/supern_2.h
--- linux.vanilla/drivers/net/skfp/h/supern_2.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/supern_2.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,1059 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	defines for AMD Supernet II chip set
+	the chips are refered to as
+		FPLUS	Formac Plus
+		PLC	Physical Layer
+
+	added defines for AMD Supernet III chip set
+	added comments on differences between Supernet II and Supernet III
+	added defines for the Motorola ELM (MOT_ELM)
+*/
+
+#ifndef	_SUPERNET_
+#define _SUPERNET_
+
+/*
+ * Define Supernet 3 when used
+ */
+#ifdef	PCI
+#ifndef	SUPERNET_3
+#define	SUPERNET_3
+#endif
+#define TAG
+#endif
+
+#define	MB	0xff
+#define	MW	0xffff
+#define	MD	0xffffffff
+
+/*
+ * FORMAC frame status (rx_msext)
+ */
+#define	FS_EI		(1<<2)
+#define	FS_AI		(1<<1)
+#define	FS_CI		(1<<0)
+
+#define FS_MSVALID	(1<<15)		/* end of queue */
+#define FS_MSRABT	(1<<14)		/* frame was aborted during reception*/
+#define FS_SSRCRTG	(1<<12)		/* if SA has set MSB (source-routing)*/
+#define FS_SEAC2	(FS_EI<<9)	/* error indicator */
+#define FS_SEAC1	(FS_AI<<9)	/* address indicator */
+#define FS_SEAC0	(FS_CI<<9)	/* copy indicator */
+#define FS_SFRMERR	(1<<8)		/* error detected (CRC or length) */
+#define FS_SADRRG	(1<<7)		/* address recognized */
+#define FS_SFRMTY2	(1<<6)		/* frame-class bit */
+#define FS_SFRMTY1	(1<<5)		/* frame-type bit (impementor) */
+#define FS_SFRMTY0	(1<<4)		/* frame-type bit (LLC) */
+#define FS_ERFBB1	(1<<1)		/* byte offset (depends on LSB bit) */
+#define FS_ERFBB0	(1<<0)		/*  - " - */
+
+/*
+ * status frame type
+ */
+#define	FRM_SMT		(0)	/* asynchr. frames */
+#define	FRM_LLCA	(1)
+#define	FRM_IMPA	(2)	
+#define	FRM_MAC		(4)	/* synchr. frames */
+#define	FRM_LLCS	(5)
+#define	FRM_IMPS	(6)
+
+/*
+ * bits in rx_descr.i	(receive frame status word)
+ */
+#define RX_MSVALID	((long)1<<31)	/* memory status valid */
+#define RX_MSRABT	((long)1<<30)	/* memory status receive abort */
+#define RX_FS_E		((long)FS_SEAC2<<16)	/* error indicator */
+#define RX_FS_A		((long)FS_SEAC1<<16)	/* address indicator */
+#define RX_FS_C		((long)FS_SEAC0<<16)	/* copy indicator */
+#define RX_FS_CRC	((long)FS_SFRMERR<<16)/* error detected */
+#define RX_FS_ADDRESS	((long)FS_SADRRG<<16)	/* address recognized */
+#define RX_FS_MAC	((long)FS_SFRMTY2<<16)/* MAC frame */
+#define RX_FS_SMT	((long)0<<16)		/* SMT frame */
+#define RX_FS_IMPL	((long)FS_SFRMTY1<<16)/* implementer frame */
+#define RX_FS_LLC	((long)FS_SFRMTY0<<16)/* LLC frame */
+
+/*
+ * receive frame descriptor
+ */
+union rx_descr {
+	struct {
+#ifdef	LITTLE_ENDIAN
+	unsigned	rx_length :16 ;	/* frame length lower/upper byte */
+	unsigned	rx_erfbb  :2 ;	/* received frame byte boundary */
+	unsigned	rx_reserv2:2 ;	/* reserved */	
+	unsigned	rx_sfrmty :3 ;	/* frame type bits */
+	unsigned	rx_sadrrg :1 ;	/* DA == MA or broad-/multicast */
+	unsigned	rx_sfrmerr:1 ;	/* received frame not valid */
+	unsigned	rx_seac0  :1 ;	/* frame-copied  C-indicator */
+	unsigned	rx_seac1  :1 ;	/* address-match A-indicator */
+	unsigned	rx_seac2  :1 ;	/* frame-error   E-indicator */
+	unsigned	rx_ssrcrtg:1 ;	/* == 1 SA has MSB set */
+	unsigned	rx_reserv1:1 ;	/* reserved */	
+	unsigned	rx_msrabt :1 ;	/* memory status receive abort */
+	unsigned	rx_msvalid:1 ;	/* memory status valid */
+#else
+	unsigned	rx_msvalid:1 ;	/* memory status valid */
+	unsigned	rx_msrabt :1 ;	/* memory status receive abort */
+	unsigned	rx_reserv1:1 ;	/* reserved */	
+	unsigned	rx_ssrcrtg:1 ;	/* == 1 SA has MSB set */
+	unsigned	rx_seac2  :1 ;	/* frame-error   E-indicator */
+	unsigned	rx_seac1  :1 ;	/* address-match A-indicator */
+	unsigned	rx_seac0  :1 ;	/* frame-copied  C-indicator */
+	unsigned	rx_sfrmerr:1 ;	/* received frame not valid */
+	unsigned	rx_sadrrg :1 ;	/* DA == MA or broad-/multicast */
+	unsigned	rx_sfrmty :3 ;	/* frame type bits */
+	unsigned	rx_erfbb  :2 ;	/* received frame byte boundary */
+	unsigned	rx_reserv2:2 ;	/* reserved */	
+	unsigned	rx_length :16 ;	/* frame length lower/upper byte */
+#endif
+	} r ;
+	long	i ;
+} ;
+
+/* defines for Receive Frame Descriptor access */
+#define RD_S_ERFBB	0x00030000L	/* received frame byte boundary */
+#define RD_S_RES2	0x000c0000L	/* reserved */
+#define RD_S_SFRMTY	0x00700000L	/* frame type bits */
+#define RD_S_SADRRG	0x00800000L	/* DA == MA or broad-/multicast */
+#define RD_S_SFRMERR	0x01000000L	/* received frame not valid */
+#define	RD_S_SEAC	0x0e000000L	/* frame status indicators */
+#define RD_S_SEAC0	0x02000000L	/* frame-copied  case-indicator */
+#define RD_S_SEAC1	0x04000000L	/* address-match A-indicator */
+#define RD_S_SEAC2	0x08000000L	/* frame-error   E-indicator */
+#define RD_S_SSRCRTG	0x10000000L	/* == 1 SA has MSB set */
+#define RD_S_RES1	0x20000000L	/* reserved */
+#define RD_S_MSRABT	0x40000000L	/* memory status receive abort */
+#define RD_S_MSVALID	0x80000000L	/* memory status valid */
+
+#define	RD_STATUS	0xffff0000L
+#define	RD_LENGTH	0x0000ffffL
+
+/* defines for Receive Frames Status Word values */
+/*RD_S_SFRMTY*/
+#define RD_FRM_SMT	(unsigned long)(0<<20)     /* asynchr. frames */
+#define RD_FRM_LLCA	(unsigned long)(1<<20)
+#define RD_FRM_IMPA	(unsigned long)(2<<20)
+#define RD_FRM_MAC	(unsigned long)(4<<20)     /* synchr. frames */
+#define RD_FRM_LLCS	(unsigned long)(5<<20)
+#define RD_FRM_IMPS	(unsigned long)(6<<20)
+
+#define TX_DESCRIPTOR	0x40000000L
+#define TX_OFFSET_3	0x18000000L
+
+#define TXP1	2
+
+/*
+ * transmit frame descriptor
+ */
+union tx_descr {
+	struct {
+#ifdef	LITTLE_ENDIAN
+	unsigned	tx_length:16 ;	/* frame length lower/upper byte */
+	unsigned	tx_res	 :8 ;	/* reserved 	 (bit 16..23) */
+	unsigned	tx_xmtabt:1 ;	/* transmit abort */
+	unsigned	tx_nfcs  :1 ;	/* no frame check sequence */
+	unsigned	tx_xdone :1 ;	/* give up token */
+	unsigned	tx_rpxm  :2 ;	/* byte offset */
+	unsigned	tx_pat1  :2 ;	/* must be TXP1 */
+	unsigned	tx_more	 :1 ;	/* more frame in chain */
+#else
+	unsigned	tx_more	 :1 ;	/* more frame in chain */
+	unsigned	tx_pat1  :2 ;	/* must be TXP1 */
+	unsigned	tx_rpxm  :2 ;	/* byte offset */
+	unsigned	tx_xdone :1 ;	/* give up token */
+	unsigned	tx_nfcs  :1 ;	/* no frame check sequence */
+	unsigned	tx_xmtabt:1 ;	/* transmit abort */
+	unsigned	tx_res	 :8 ;	/* reserved 	 (bit 16..23) */
+	unsigned	tx_length:16 ;	/* frame length lower/upper byte */
+#endif
+	} t ;
+	long	i ;
+} ;
+
+/* defines for Transmit Descriptor access */
+#define	TD_C_MORE	0x80000000L	/* more frame in chain */
+#define	TD_C_DESCR	0x60000000L	/* must be TXP1 */
+#define	TD_C_TXFBB	0x18000000L	/* byte offset */
+#define	TD_C_XDONE	0x04000000L	/* give up token */
+#define TD_C_NFCS	0x02000000L	/* no frame check sequence */
+#define TD_C_XMTABT	0x01000000L	/* transmit abort */
+
+#define	TD_C_LNCNU	0x0000ff00L	
+#define TD_C_LNCNL	0x000000ffL
+#define TD_C_LNCN	0x0000ffffL	/* frame length lower/upper byte */
+ 
+/*
+ * transmit pointer
+ */
+union tx_pointer {
+	struct t {
+#ifdef	LITTLE_ENDIAN
+	unsigned	tp_pointer:16 ;	/* pointer to tx_descr (low/high) */
+	unsigned	tp_res	  :8 ;	/* reserved 	 (bit 16..23) */
+	unsigned	tp_pattern:8 ;	/* fixed pattern (bit 24..31) */
+#else
+	unsigned	tp_pattern:8 ;	/* fixed pattern (bit 24..31) */
+	unsigned	tp_res	  :8 ;	/* reserved 	 (bit 16..23) */
+	unsigned	tp_pointer:16 ;	/* pointer to tx_descr (low/high) */
+#endif
+	} t ;
+	long	i ;
+} ;
+
+/* defines for Nontag Mode Pointer access */
+#define	TD_P_CNTRL	0xff000000L
+#define TD_P_RPXU	0x0000ff00L
+#define TD_P_RPXL	0x000000ffL
+#define TD_P_RPX	0x0000ffffL
+
+
+#define TX_PATTERN	0xa0
+#define TX_POINTER_END	0xa0000000L
+#define TX_INT_PATTERN	0xa0000000L
+
+struct tx_queue {
+	struct tx_queue *tq_next ;
+	u_short tq_pack_offset ;	/* offset buffer memory */
+	u_char  tq_pad[2] ;
+} ;
+
+/*
+	defines for FORMAC Plus (Am79C830)
+*/
+
+/*
+ *  FORMAC+ read/write (r/w) registers
+ */
+#define FM_CMDREG1	0x00		/* write command reg 1 instruction */
+#define FM_CMDREG2	0x01		/* write command reg 2 instruction */
+#define FM_ST1U		0x00		/* read upper 16-bit of status reg 1 */
+#define FM_ST1L		0x01		/* read lower 16-bit of status reg 1 */
+#define FM_ST2U		0x02		/* read upper 16-bit of status reg 2 */
+#define FM_ST2L		0x03		/* read lower 16-bit of status reg 2 */
+#define FM_IMSK1U	0x04		/* r/w upper 16-bit of IMSK 1 */
+#define FM_IMSK1L	0x05		/* r/w lower 16-bit of IMSK 1 */
+#define FM_IMSK2U	0x06		/* r/w upper 16-bit of IMSK 2 */
+#define FM_IMSK2L	0x07		/* r/w lower 16-bit of IMSK 2 */
+#define FM_SAID		0x08		/* r/w short addr.-individual */
+#define FM_LAIM		0x09		/* r/w long addr.-ind. (MSW of LAID) */
+#define FM_LAIC		0x0a		/* r/w long addr.-ind. (middle)*/
+#define FM_LAIL		0x0b		/* r/w long addr.-ind. (LSW) */
+#define FM_SAGP		0x0c		/* r/w short address-group */
+#define FM_LAGM		0x0d		/* r/w long addr.-gr. (MSW of LAGP) */
+#define FM_LAGC		0x0e		/* r/w long addr.-gr. (middle) */
+#define FM_LAGL		0x0f		/* r/w long addr.-gr. (LSW) */
+#define FM_MDREG1	0x10		/* r/w 16-bit mode reg 1 */
+#define FM_STMCHN	0x11		/* read state-machine reg */
+#define FM_MIR1		0x12		/* read upper 16-bit of MAC Info Reg */
+#define FM_MIR0		0x13		/* read lower 16-bit of MAC Info Reg */
+#define FM_TMAX		0x14		/* r/w 16-bit TMAX reg */
+#define FM_TVX		0x15		/* write 8-bit TVX reg with NP7-0
+					   read TVX on NP7-0, timer on NP15-8*/
+#define FM_TRT		0x16		/* r/w upper 16-bit of TRT timer */
+#define FM_THT		0x17		/* r/w upper 16-bit of THT timer */
+#define FM_TNEG		0x18		/* read upper 16-bit of TNEG (TTRT) */
+#define FM_TMRS		0x19		/* read lower 5-bit of TNEG,TRT,THT */
+			/* F E D C  B A 9 8  7 6 5 4  3 2 1 0
+			   x |-TNEG4-0| |-TRT4-0-| |-THT4-0-| (x-late count) */
+#define FM_TREQ0	0x1a		/* r/w 16-bit TREQ0 reg (LSW of TRT) */
+#define FM_TREQ1	0x1b		/* r/w 16-bit TREQ1 reg (MSW of TRT) */
+#define FM_PRI0		0x1c		/* r/w priority r. for asyn.-queue 0 */
+#define FM_PRI1		0x1d		/* r/w priority r. for asyn.-queue 1 */
+#define FM_PRI2		0x1e		/* r/w priority r. for asyn.-queue 2 */
+#define FM_TSYNC	0x1f		/* r/w 16-bit of the TSYNC register */
+#define FM_MDREG2	0x20		/* r/w 16-bit mode reg 2 */
+#define FM_FRMTHR	0x21		/* r/w the frame threshold register */
+#define FM_EACB		0x22		/* r/w end addr of claim/beacon area */
+#define FM_EARV		0x23		/* r/w end addr of receive queue */
+/* Supernet 3 */
+#define	FM_EARV1	FM_EARV
+
+#define FM_EAS		0x24		/* r/w end addr of synchr. queue */
+#define FM_EAA0		0x25		/* r/w end addr of asyn. queue 0 */
+#define FM_EAA1		0x26		/* r/w end addr of asyn. queue 1 */
+#define FM_EAA2		0x27		/* r/w end addr of asyn. queue 2 */
+#define FM_SACL		0x28		/* r/w start addr of claim frame */
+#define FM_SABC		0x29		/* r/w start addr of beacon frame */
+#define FM_WPXSF	0x2a		/* r/w the write ptr. for special fr.*/
+#define FM_RPXSF	0x2b		/* r/w the read ptr. for special fr. */
+#define FM_RPR		0x2d		/* r/w the read ptr. for receive qu. */
+#define FM_WPR		0x2e		/* r/w the write ptr. for receive qu.*/
+#define FM_SWPR		0x2f		/* r/w the shadow wr.-ptr. for rec.q.*/
+/* Supernet 3 */ 
+#define FM_RPR1         FM_RPR   
+#define FM_WPR1         FM_WPR 
+#define FM_SWPR1        FM_SWPR
+
+#define FM_WPXS		0x30		/* r/w the write ptr. for synchr. qu.*/
+#define FM_WPXA0	0x31		/* r/w the write ptr. for asyn. qu.0 */
+#define FM_WPXA1	0x32		/* r/w the write ptr. for asyn. qu.1 */
+#define FM_WPXA2	0x33		/* r/w the write ptr. for asyn. qu.2 */
+#define FM_SWPXS	0x34		/* r/w the shadow wr.-ptr. for syn.q.*/
+#define FM_SWPXA0	0x35		/* r/w the shad. wr.-ptr. for asyn.q0*/
+#define FM_SWPXA1	0x36		/* r/w the shad. wr.-ptr. for asyn.q1*/
+#define FM_SWPXA2	0x37		/* r/w the shad. wr.-ptr. for asyn.q2*/
+#define FM_RPXS		0x38		/* r/w the read ptr. for synchr. qu. */
+#define FM_RPXA0	0x39		/* r/w the read ptr. for asyn. qu. 0 */
+#define FM_RPXA1	0x3a		/* r/w the read ptr. for asyn. qu. 1 */
+#define FM_RPXA2	0x3b		/* r/w the read ptr. for asyn. qu. 2 */
+#define FM_MARR		0x3c		/* r/w the memory read addr register */
+#define FM_MARW		0x3d		/* r/w the memory write addr register*/
+#define FM_MDRU		0x3e		/* r/w upper 16-bit of mem. data reg */
+#define FM_MDRL		0x3f		/* r/w lower 16-bit of mem. data reg */
+
+/* following instructions relate to MAC counters and timer */
+#define FM_TMSYNC	0x40		/* r/w upper 16 bits of TMSYNC timer */
+#define FM_FCNTR	0x41		/* r/w the 16-bit frame counter */
+#define FM_LCNTR	0x42		/* r/w the 16-bit lost counter */
+#define FM_ECNTR	0x43		/* r/w the 16-bit error counter */
+
+/* Supernet 3:	extensions to old register block */
+#define	FM_FSCNTR	0x44		/* r/? Frame Strip Counter */
+#define	FM_FRSELREG	0x45		/* r/w Frame Selection Register */
+
+/* Supernet 3:	extensions for 2. receive queue etc. */
+#define	FM_MDREG3	0x60		/* r/w Mode Register 3 */
+#define	FM_ST3U		0x61		/* read upper 16-bit of status reg 3 */
+#define	FM_ST3L		0x62		/* read lower 16-bit of status reg 3 */
+#define	FM_IMSK3U	0x63		/* r/w upper 16-bit of IMSK reg 3 */
+#define	FM_IMSK3L	0x64		/* r/w lower 16-bit of IMSK reg 3 */
+#define	FM_IVR		0x65		/* read Interrupt Vector register */
+#define	FM_IMR		0x66		/* r/w Interrupt mask register */
+/* 0x67	Hidden */
+#define	FM_RPR2		0x68		/* r/w the read ptr. for rec. qu. 2 */
+#define	FM_WPR2		0x69		/* r/w the write ptr. for rec. qu. 2 */
+#define	FM_SWPR2	0x6a		/* r/w the shadow wptr. for rec. q. 2 */
+#define	FM_EARV2	0x6b		/* r/w end addr of rec. qu. 2 */
+#define	FM_UNLCKDLY	0x6c		/* r/w Auto Unlock Delay register */
+					/* Bit 15-8: RECV2 unlock threshold */
+					/* Bit  7-0: RECV1 unlock threshold */
+/* 0x6f-0x73	Hidden */
+#define	FM_LTDPA1	0x79		/* r/w Last Trans desc ptr for A1 qu. */
+/* 0x80-0x9a	PLCS registers of built-in PLCS  (Supernet 3 only) */
+
+/* Supernet 3: Adderss Filter Registers */
+#define	FM_AFCMD	0xb0		/* r/w Address Filter Command Reg */
+#define	FM_AFSTAT	0xb2		/* r/w Address Filter Status Reg */
+#define	FM_AFBIST	0xb4		/* r/w Address Filter BIST signature */
+#define	FM_AFCOMP2	0xb6		/* r/w Address Filter Comparand 2 */
+#define	FM_AFCOMP1	0xb8		/* r/w Address Filter Comparand 1 */
+#define	FM_AFCOMP0	0xba		/* r/w Address Filter Comparand 0 */
+#define	FM_AFMASK2	0xbc		/* r/w Address Filter Mask 2 */
+#define	FM_AFMASK1	0xbe		/* r/w Address Filter Mask 1 */
+#define	FM_AFMASK0	0xc0		/* r/w Address Filter Mask 0 */
+#define	FM_AFPERS	0xc2		/* r/w Address Filter Personality Reg */
+
+/* Supernet 3: Orion (PDX?) Registers */
+#define	FM_ORBIST	0xd0		/* r/w Orion BIST signature */
+#define	FM_ORSTAT	0xd2		/* r/w Orion Status Register */
+
+
+/*
+ * Mode Register 1 (MDREG1)
+ */
+#define FM_RES0		0x0001		/* reserved */
+					/* SN3: other definition */
+#define	FM_XMTINH_HOLD	0x0002		/* transmit-inhibit/hold bit */
+					/* SN3: other definition */
+#define	FM_HOFLXI	0x0003		/* SN3: Hold / Flush / Inhibit */
+#define	FM_FULL_HALF	0x0004		/* full-duplex/half-duplex bit */
+#define	FM_LOCKTX	0x0008		/* lock-transmit-asynchr.-queues bit */
+#define FM_EXGPA0	0x0010		/* extended-group-addressing bit 0 */
+#define FM_EXGPA1	0x0020		/* extended-group-addressing bit 1 */
+#define FM_DISCRY	0x0040		/* disable-carry bit */
+					/* SN3: reserved */
+#define FM_SELRA	0x0080		/* select input from PHY (1=RA,0=RB) */
+
+#define FM_ADDET	0x0700		/* address detection */
+#define FM_MDAMA	(0<<8)		/* address detection : DA = MA */
+#define FM_MDASAMA	(1<<8)		/* address detection : DA=MA||SA=MA */
+#define	FM_MRNNSAFNMA	(2<<8)		/* rec. non-NSA frames DA=MA&&SA!=MA */
+#define	FM_MRNNSAF	(3<<8)		/* rec. non-NSA frames DA = MA */
+#define	FM_MDISRCV	(4<<8)		/* disable receive function */
+#define	FM_MRES0	(5<<8)		/* reserve */
+#define	FM_MLIMPROM	(6<<8)		/* limited-promiscuous mode */
+#define FM_MPROMISCOUS	(7<<8)		/* address detection : promiscous */
+
+#define FM_SELSA	0x0800		/* select-short-address bit */
+
+#define FM_MMODE	0x7000		/* mode select */
+#define FM_MINIT	(0<<12)		/* initialize */
+#define FM_MMEMACT	(1<<12)		/* memory activate */
+#define FM_MONLINESP	(2<<12)		/* on-line special */
+#define FM_MONLINE	(3<<12)		/* on-line (FDDI operational mode) */
+#define FM_MILOOP	(4<<12)		/* internal loopback */
+#define FM_MRES1	(5<<12)		/* reserved */
+#define FM_MRES2	(6<<12)		/* reserved */
+#define FM_MELOOP	(7<<12)		/* external loopback */
+
+#define	FM_SNGLFRM	0x8000		/* single-frame-receive mode */
+					/* SN3: reserved */
+
+#define	MDR1INIT	(FM_MINIT | FM_MDAMA)
+
+/*
+ * Mode Register 2 (MDREG2)
+ */
+#define	FM_AFULL	0x000f		/* 4-bit value (empty loc.in txqueue)*/
+#define	FM_RCVERR	0x0010		/* rec.-errored-frames bit */
+#define	FM_SYMCTL	0x0020		/* sysmbol-control bit */
+					/* SN3: reserved */
+#define	FM_SYNPRQ	0x0040		/* synchron.-NP-DMA-request bit */
+#define	FM_ENNPRQ	0x0080		/* enable-NP-DMA-request bit */
+#define	FM_ENHSRQ	0x0100		/* enable-host-request bit */
+#define	FM_RXFBB01	0x0600		/* rec. frame byte boundary bit0 & 1 */
+#define	FM_LSB		0x0800		/* determ. ordering of bytes in buffer*/
+#define	FM_PARITY	0x1000		/* 1 = even, 0 = odd */
+#define	FM_CHKPAR	0x2000		/* 1 = parity of 32-bit buffer BD-bus*/
+#define	FM_STRPFCS	0x4000		/* 1 = strips FCS field of rec.frame */
+#define	FM_BMMODE	0x8000		/* Buffer-Memory-Mode (1 = tag mode) */
+					/* SN3: 1 = tag, 0 = modified tag */
+
+/*
+ * Status Register 1, Upper 16 Bits (ST1U)
+ */
+#define FM_STEFRMS	0x0001		/* transmit end of frame: synchr. qu.*/
+#define FM_STEFRMA0	0x0002		/* transmit end of frame: asyn. qu.0 */
+#define FM_STEFRMA1	0x0004		/* transmit end of frame: asyn. qu.1 */
+#define FM_STEFRMA2	0x0008		/* transmit end of frame: asyn. qu.2 */
+					/* SN3: reserved */
+#define FM_STECFRMS	0x0010		/* transmit end of chain of syn. qu. */
+					/* SN3: reserved */
+#define FM_STECFRMA0	0x0020		/* transmit end of chain of asyn. q0 */
+					/* SN3: reserved */
+#define FM_STECFRMA1	0x0040		/* transmit end of chain of asyn. q1 */
+					/* SN3: STECMDA1 */
+#define FM_STECMDA1	0x0040		/* SN3: 'no description' */
+#define FM_STECFRMA2	0x0080		/* transmit end of chain of asyn. q2 */
+					/* SN3: reserved */
+#define	FM_STEXDONS	0x0100		/* transmit until XDONE in syn. qu. */
+#define	FM_STBFLA	0x0200		/* asynchr.-queue trans. buffer full */
+#define	FM_STBFLS	0x0400		/* synchr.-queue transm. buffer full */
+#define	FM_STXABRS	0x0800		/* synchr. queue transmit-abort */
+#define	FM_STXABRA0	0x1000		/* asynchr. queue 0 transmit-abort */
+#define	FM_STXABRA1	0x2000		/* asynchr. queue 1 transmit-abort */
+#define	FM_STXABRA2	0x4000		/* asynchr. queue 2 transmit-abort */
+					/* SN3: reserved */
+#define	FM_SXMTABT	0x8000		/* transmit abort */
+
+/*
+ * Status Register 1, Lower 16 Bits (ST1L)
+ */
+#define FM_SQLCKS	0x0001		/* queue lock for synchr. queue */
+#define FM_SQLCKA0	0x0002		/* queue lock for asynchr. queue 0 */
+#define FM_SQLCKA1	0x0004		/* queue lock for asynchr. queue 1 */
+#define FM_SQLCKA2	0x0008		/* queue lock for asynchr. queue 2 */
+					/* SN3: reserved */
+#define FM_STXINFLS	0x0010		/* transmit instruction full: syn. */
+					/* SN3: reserved */
+#define FM_STXINFLA0	0x0020		/* transmit instruction full: asyn.0 */
+					/* SN3: reserved */
+#define FM_STXINFLA1	0x0040		/* transmit instruction full: asyn.1 */
+					/* SN3: reserved */
+#define FM_STXINFLA2	0x0080		/* transmit instruction full: asyn.2 */
+					/* SN3: reserved */
+#define FM_SPCEPDS	0x0100		/* parity/coding error: syn. queue */
+#define FM_SPCEPDA0	0x0200		/* parity/coding error: asyn. queue0 */
+#define FM_SPCEPDA1	0x0400		/* parity/coding error: asyn. queue1 */
+#define FM_SPCEPDA2	0x0800		/* parity/coding error: asyn. queue2 */
+					/* SN3: reserved */
+#define FM_STBURS	0x1000		/* transmit buffer underrun: syn. q. */
+#define FM_STBURA0	0x2000		/* transmit buffer underrun: asyn.0 */
+#define FM_STBURA1	0x4000		/* transmit buffer underrun: asyn.1 */
+#define FM_STBURA2	0x8000		/* transmit buffer underrun: asyn.2 */
+					/* SN3: reserved */
+
+/*
+ * Status Register 2, Upper 16 Bits (ST2U)
+ */
+#define FM_SOTRBEC	0x0001		/* other beacon received */
+#define FM_SMYBEC	0x0002		/* my beacon received */
+#define FM_SBEC		0x0004		/* beacon state entered */
+#define FM_SLOCLM	0x0008		/* low claim received */
+#define FM_SHICLM	0x0010		/* high claim received */
+#define FM_SMYCLM	0x0020		/* my claim received */
+#define FM_SCLM		0x0040		/* claim state entered */
+#define FM_SERRSF	0x0080		/* error in special frame */
+#define FM_SNFSLD	0x0100		/* NP and FORMAC+ simultaneous load */
+#define FM_SRFRCTOV	0x0200		/* receive frame counter overflow */
+					/* SN3: reserved */
+#define FM_SRCVFRM	0x0400		/* receive frame */
+					/* SN3: reserved */
+#define FM_SRCVOVR	0x0800		/* receive FIFO overflow */
+#define FM_SRBFL	0x1000		/* receive buffer full */
+#define FM_SRABT	0x2000		/* receive abort */
+#define FM_SRBMT	0x4000		/* receive buffer empty */
+#define FM_SRCOMP	0x8000		/* receive complete. Nontag mode */
+
+/*
+ * Status Register 2, Lower 16 Bits (ST2L)
+ * Attention: SN3 docu shows these bits the other way around
+ */
+#define FM_SRES0	0x0001		/* reserved */
+#define FM_SESTRIPTK	0x0001		/* SN3: 'no description' */
+#define FM_STRTEXR	0x0002		/* TRT expired in claim | beacon st. */
+#define FM_SDUPCLM	0x0004		/* duplicate claim received */
+#define FM_SSIFG	0x0008		/* short interframe gap */
+#define FM_SFRMCTR	0x0010		/* frame counter overflow */
+#define FM_SERRCTR	0x0020		/* error counter overflow */
+#define FM_SLSTCTR	0x0040		/* lost counter overflow */
+#define FM_SPHINV	0x0080		/* PHY invalid */
+#define FM_SADET	0x0100		/* address detect */
+#define FM_SMISFRM	0x0200		/* missed frame */
+#define FM_STRTEXP	0x0400		/* TRT expired and late count > 0 */
+#define FM_STVXEXP	0x0800		/* TVX expired */
+#define FM_STKISS	0x1000		/* token issued */
+#define FM_STKERR	0x2000		/* token error */
+#define FM_SMULTDA	0x4000		/* multiple destination address */
+#define FM_SRNGOP	0x8000		/* ring operational */
+
+/*
+ * Supernet 3:
+ * Status Register 3, Upper 16 Bits (ST3U)
+ */
+#define	FM_SRQUNLCK1	0x0001		/* receive queue unlocked queue 1 */
+#define	FM_SRQUNLCK2	0x0002		/* receive queue unlocked queue 2 */
+#define	FM_SRPERRQ1	0x0004		/* receive parity error rx queue 1 */
+#define	FM_SRPERRQ2	0x0008		/* receive parity error rx queue 2 */
+					/* Bit 4-10: reserved */
+#define	FM_SRCVOVR2	0x0800		/* receive FIFO overfull rx queue 2 */
+#define	FM_SRBFL2	0x1000		/* receive buffer full rx queue 2 */
+#define	FM_SRABT2	0x2000		/* receive abort rx queue 2 */
+#define	FM_SRBMT2	0x4000		/* receive buf empty rx queue 2 */
+#define	FM_SRCOMP2	0x8000		/* receive comp rx queue 2 */
+
+/*
+ * Supernet 3:
+ * Status Register 3, Lower 16 Bits (ST3L)
+ */
+#define	FM_AF_BIST_DONE		0x0001	/* Address Filter BIST is done */
+#define	FM_PLC_BIST_DONE	0x0002	/* internal PLC Bist is done */
+#define	FM_PDX_BIST_DONE	0x0004	/* PDX BIST is done */
+					/* Bit  3: reserved */
+#define	FM_SICAMDAMAT		0x0010	/* Status internal CAM DA match */
+#define	FM_SICAMDAXACT		0x0020	/* Status internal CAM DA exact match */
+#define	FM_SICAMSAMAT		0x0040	/* Status internal CAM SA match */
+#define	FM_SICAMSAXACT		0x0080	/* Status internal CAM SA exact match */
+
+/*
+ * MAC State-Machine Register FM_STMCHN
+ */
+#define	FM_MDRTAG	0x0004		/* tag bit of long word read */
+#define	FM_SNPPND	0x0008		/* r/w from buffer mem. is pending */
+#define	FM_TXSTAT	0x0070		/* transmitter state machine state */
+#define	FM_RCSTAT	0x0380		/* receiver state machine state */
+#define	FM_TM01		0x0c00		/* indicate token mode */
+#define	FM_SIM		0x1000		/* indicate send immediate-mode */
+#define	FM_REV		0xe000		/* FORMAC Plus revision number */
+
+/*
+ * Supernet 3
+ * Mode Register 3
+ */
+#define	FM_MENRS	0x0001		/* Ena enhanced rec status encoding */
+#define	FM_MENXS	0x0002		/* Ena enhanced xmit status encoding */
+#define	FM_MENXCT	0x0004		/* Ena EXACT/INEXACT matching */
+#define	FM_MENAFULL	0x0008		/* Ena enh QCTRL encoding for AFULL */
+#define	FM_MEIND	0x0030		/* Ena enh A,C indicator settings */
+#define	FM_MENQCTRL	0x0040		/* Ena enh QCTRL encoding */
+#define	FM_MENRQAUNLCK	0x0080		/* Ena rec q auto unlock */
+#define	FM_MENDAS	0x0100		/* Ena DAS connections by cntr MUX */
+#define	FM_MENPLCCST	0x0200		/* Ena Counter Segm test in PLC blck */
+#define	FM_MENSGLINT	0x0400		/* Ena Vectored Interrupt reading */
+#define	FM_MENDRCV	0x0800		/* Ena dual receive queue operation */
+#define	FM_MENFCLOC	0x3000		/* Ena FC location within frm data */
+#define	FM_MENTRCMD	0x4000		/* Ena ASYNC1 xmit only after command */
+#define	FM_MENTDLPBK	0x8000		/* Ena TDAT to RDAT lkoopback */
+
+/*
+ * Supernet 3
+ * Frame Selection Register
+ */
+#define	FM_RECV1	0x000f		/* options for receive queue 1 */
+#define	FM_RCV1_ALL	(0<<0)		/* receive all frames */
+#define	FM_RCV1_LLC	(1<<0)		/* rec all LLC frames */
+#define	FM_RCV1_SMT	(2<<0)		/* rec all SMT frames */
+#define	FM_RCV1_NSMT	(3<<0)		/* rec non-SMT frames */
+#define	FM_RCV1_IMP	(4<<0)		/* rec Implementor frames */
+#define	FM_RCV1_MAC	(5<<0)		/* rec all MAC frames */
+#define	FM_RCV1_SLLC	(6<<0)		/* rec all sync LLC frames */
+#define	FM_RCV1_ALLC	(7<<0)		/* rec all async LLC frames */
+#define	FM_RCV1_VOID	(8<<0)		/* rec all void frames */
+#define	FM_RCV1_ALSMT	(9<<0)		/* rec all async LLC & SMT frames */
+#define	FM_RECV2	0x00f0		/* options for receive queue 2 */
+#define	FM_RCV2_ALL	(0<<4)		/* receive all other frames */
+#define	FM_RCV2_LLC	(1<<4)		/* rec all LLC frames */
+#define	FM_RCV2_SMT	(2<<4)		/* rec all SMT frames */
+#define	FM_RCV2_NSMT	(3<<4)		/* rec non-SMT frames */
+#define	FM_RCV2_IMP	(4<<4)		/* rec Implementor frames */
+#define	FM_RCV2_MAC	(5<<4)		/* rec all MAC frames */
+#define	FM_RCV2_SLLC	(6<<4)		/* rec all sync LLC frames */
+#define	FM_RCV2_ALLC	(7<<4)		/* rec all async LLC frames */
+#define	FM_RCV2_VOID	(8<<4)		/* rec all void frames */
+#define	FM_RCV2_ALSMT	(9<<4)		/* rec all async LLC & SMT frames */
+#define	FM_ENXMTADSWAP	0x4000		/* enh rec addr swap (phys -> can) */
+#define	FM_ENRCVADSWAP	0x8000		/* enh tx addr swap (can -> phys) */
+
+/*
+ * Supernet 3:
+ * Address Filter Command Register (AFCMD)
+ */
+#define	FM_INST		0x0007		/* Address Filter Operation */
+#define FM_IINV_CAM	(0<<0)		/* Invalidate CAM */
+#define FM_IWRITE_CAM	(1<<0)		/* Write CAM */
+#define FM_IREAD_CAM	(2<<0)		/* Read CAM */
+#define FM_IRUN_BIST	(3<<0)		/* Run BIST */
+#define FM_IFIND	(4<<0)		/* Find */
+#define FM_IINV		(5<<0)		/* Invalidate */
+#define FM_ISKIP	(6<<0)		/* Skip */
+#define FM_ICL_SKIP	(7<<0)		/* Clear all SKIP bits */
+
+/*
+ * Supernet 3:
+ * Address Filter Status Register (AFSTAT)
+ */
+					/* Bit  0-4: reserved */
+#define	FM_REV_NO	0x00e0		/* Revision Number of Address Filter */
+#define	FM_BIST_DONE	0x0100		/* BIST complete */
+#define	FM_EMPTY	0x0200		/* CAM empty */
+#define	FM_ERROR	0x0400		/* Error (improper operation) */
+#define	FM_MULT		0x0800		/* Multiple Match */
+#define	FM_EXACT	0x1000		/* Exact Match */
+#define	FM_FOUND	0x2000		/* Comparand found in CAM */
+#define	FM_FULL		0x4000		/* CAM full */
+#define	FM_DONE		0x8000		/* DONE indicator */
+
+/*
+ * Supernet 3:
+ * BIST Signature Register (AFBIST)
+ */
+#define	AF_BIST_SIGNAT	0x0553		/* Address Filter BIST Signature */
+
+/*
+ * Supernet 3:
+ * Personality Register (AFPERS)
+ */
+#define	FM_VALID	0x0001		/* CAM Entry Valid */
+#define	FM_DA		0x0002		/* Destination Address */
+#define	FM_DAX		0x0004		/* Destination Address Exact */
+#define	FM_SA		0x0008		/* Source Address */
+#define	FM_SAX		0x0010		/* Source Address Exact */
+#define	FM_SKIP		0x0020		/* Skip this entry */
+
+/*
+ * instruction set for command register 1 (NPADDR6-0 = 0x00)
+ */
+#define FM_IRESET	0x01		/* software reset */
+#define FM_IRMEMWI	0x02		/* load Memory Data Reg., inc MARR */
+#define FM_IRMEMWO	0x03		/* load MDR from buffer memory, n.i. */
+#define FM_IIL		0x04		/* idle/listen */
+#define FM_ICL		0x05		/* claim/listen */
+#define FM_IBL		0x06		/* beacon/listen */
+#define FM_ILTVX	0x07		/* load TVX timer from TVX reg */
+#define FM_INRTM	0x08		/* nonrestricted token mode */
+#define FM_IENTM	0x09		/* enter nonrestricted token mode */
+#define FM_IERTM	0x0a		/* enter restricted token mode */
+#define FM_IRTM		0x0b		/* restricted token mode */
+#define FM_ISURT	0x0c		/* send unrestricted token */
+#define FM_ISRT		0x0d		/* send restricted token */
+#define FM_ISIM		0x0e		/* enter send-immediate mode */
+#define FM_IESIM	0x0f		/* exit send-immediate mode */
+#define FM_ICLLS	0x11		/* clear synchronous queue lock */
+#define FM_ICLLA0	0x12		/* clear asynchronous queue 0 lock */
+#define FM_ICLLA1	0x14		/* clear asynchronous queue 1 lock */
+#define FM_ICLLA2	0x18		/* clear asynchronous queue 2 lock */
+					/* SN3: reserved */
+#define FM_ICLLR	0x20		/* clear receive queue (SN3:1) lock */
+#define FM_ICLLR2	0x21		/* SN3: clear receive queue 2 lock */
+#define FM_ITRXBUS	0x22		/* SN3: Tristate X-Bus (SAS only) */
+#define FM_IDRXBUS	0x23		/* SN3: drive X-Bus */
+#define FM_ICLLAL	0x3f		/* clear all queue locks */
+
+/*
+ * instruction set for command register 2 (NPADDR6-0 = 0x01)
+ */
+#define FM_ITRS		0x01		/* transmit synchronous queue */
+					/* SN3: reserved */
+#define FM_ITRA0	0x02		/* transmit asynchronous queue 0 */
+					/* SN3: reserved */
+#define FM_ITRA1	0x04		/* transmit asynchronous queue 1 */
+					/* SN3: reserved */
+#define FM_ITRA2	0x08		/* transmit asynchronous queue 2 */
+					/* SN3: reserved */
+#define FM_IACTR	0x10		/* abort current transmit activity */
+#define FM_IRSTQ	0x20		/* reset transmit queues */
+#define FM_ISTTB	0x30		/* set tag bit */
+#define FM_IERSF	0x40		/* enable receive single frame */
+					/* SN3: reserved */
+#define	FM_ITR		0x50		/* SN3: Transmit Command */
+
+
+/*
+ *	defines for PLC (Am79C864)
+ */
+
+/*
+ *  PLC read/write (r/w) registers
+ */
+#define PL_CNTRL_A	0x00		/* control register A (r/w) */
+#define PL_CNTRL_B	0x01		/* control register B (r/w) */
+#define PL_INTR_MASK	0x02		/* interrupt mask (r/w) */
+#define PL_XMIT_VECTOR	0x03		/* transmit vector register (r/w) */
+#define PL_VECTOR_LEN	0x04		/* transmit vector length (r/w) */
+#define PL_LE_THRESHOLD	0x05		/* link error event threshold (r/w) */
+#define PL_C_MIN	0x06		/* minimum connect state time (r/w) */
+#define PL_TL_MIN	0x07		/* min. line state transmit t. (r/w) */
+#define PL_TB_MIN	0x08		/* minimum break time (r/w) */
+#define PL_T_OUT	0x09		/* signal timeout (r/w) */
+#define PL_CNTRL_C	0x0a		/* control register C (r/w) */
+#define PL_LC_LENGTH	0x0b		/* link confidence test time (r/w) */
+#define PL_T_SCRUB	0x0c		/* scrub time = MAC TVX (r/w) */
+#define PL_NS_MAX	0x0d		/* max. noise time before break (r/w)*/
+#define PL_TPC_LOAD_V	0x0e		/* TPC timer load value (write only) */
+#define PL_TNE_LOAD_V	0x0f		/* TNE timer load value (write only) */
+#define PL_STATUS_A	0x10		/* status register A (read only) */
+#define PL_STATUS_B	0x11		/* status register B (read only) */
+#define PL_TPC		0x12		/* timer for PCM (ro) [20.48 us] */
+#define PL_TNE		0x13		/* time of noise event [0.32 us] */
+#define PL_CLK_DIV	0x14		/* TNE clock divider (read only) */
+#define PL_BIST_SIGNAT	0x15		/* built in self test signature (ro)*/
+#define PL_RCV_VECTOR	0x16		/* receive vector reg. (read only) */
+#define PL_INTR_EVENT	0x17		/* interrupt event reg. (read only) */
+#define PL_VIOL_SYM_CTR	0x18		/* violation symbol count. (read o) */
+#define PL_MIN_IDLE_CTR	0x19		/* minimum idle counter (read only) */
+#define PL_LINK_ERR_CTR	0x1a		/* link error event ctr.(read only) */
+#ifdef	MOT_ELM
+#define	PL_T_FOT_ASS	0x1e		/* FOTOFF Assert Timer */
+#define	PL_T_FOT_DEASS	0x1f		/* FOTOFF Deassert Timer */
+#endif	/* MOT_ELM */
+
+#ifdef	MOT_ELM
+/*
+ * Special Quad-Elm Registers.
+ * A Quad-ELM consists of for ELMs and these additional registers.
+ */
+#define	QELM_XBAR_W	0x80		/* Crossbar Control ELM W */
+#define	QELM_XBAR_X	0x81		/* Crossbar Control ELM X */
+#define	QELM_XBAR_Y	0x82		/* Crossbar Control ELM Y */
+#define	QELM_XBAR_Z	0x83		/* Crossbar Control ELM Z */
+#define	QELM_XBAR_P	0x84		/* Crossbar Control Bus P */
+#define	QELM_XBAR_S	0x85		/* Crossbar Control Bus S */
+#define	QELM_XBAR_R	0x86		/* Crossbar Control Bus R */
+#define	QELM_WR_XBAR	0x87		/* Write the Crossbar now (write) */
+#define	QELM_CTR_W	0x88		/* Counter W */
+#define	QELM_CTR_X	0x89		/* Counter X */
+#define	QELM_CTR_Y	0x8a		/* Counter Y */
+#define	QELM_CTR_Z	0x8b		/* Counter Z */
+#define	QELM_INT_MASK	0x8c		/* Interrupt mask register */
+#define	QELM_INT_DATA	0x8d		/* Interrupt data (event) register */
+#define	QELM_ELMB	0x00		/* Elm base */
+#define	QELM_ELM_SIZE	0x20		/* ELM size */
+#endif	/* MOT_ELM */
+/*
+ * PLC control register A (PL_CNTRL_A: log. addr. 0x00)
+ * It is used for timer configuration, specification of PCM MAINT state option,
+ * counter interrupt frequency, PLC data path config. and Built In Self Test.
+ */
+#define	PL_RUN_BIST	0x0001		/* begin running its Built In Self T.*/
+#define	PL_RF_DISABLE	0x0002		/* disable the Repeat Filter state m.*/
+#define	PL_SC_REM_LOOP	0x0004		/* remote loopback path */
+#define	PL_SC_BYPASS	0x0008		/* by providing a physical bypass */
+#define	PL_LM_LOC_LOOP	0x0010		/* loop path just after elastic buff.*/
+#define	PL_EB_LOC_LOOP	0x0020		/* loop path just prior to PDT/PDR IF*/
+#define	PL_FOT_OFF	0x0040		/* assertion of /FOTOFF pin of PLC */
+#define	PL_LOOPBACK	0x0080		/* it cause the /LPBCK pin ass. low */
+#define	PL_MINI_CTR_INT 0x0100		/* partially contr. when bit is ass. */
+#define	PL_VSYM_CTR_INT	0x0200		/* controls when int bit is asserted */
+#define	PL_ENA_PAR_CHK	0x0400		/* enable parity check */
+#define	PL_REQ_SCRUB	0x0800		/* limited access to scrub capability*/
+#define	PL_TPC_16BIT	0x1000		/* causes the TPC as a 16 bit timer */
+#define	PL_TNE_16BIT	0x2000		/* causes the TNE as a 16 bit timer */
+#define	PL_NOISE_TIMER	0x4000		/* allows the noise timing function */
+
+/*
+ * PLC control register B (PL_CNTRL_B: log. addr. 0x01)
+ * It contains signals and requeste to direct the process of PCM and it is also
+ * used to control the Line State Match interrupt.
+ */
+#define	PL_PCM_CNTRL	0x0003		/* control PCM state machine */
+#define	PL_PCM_NAF	(0)		/* state is not affected */
+#define	PL_PCM_START	(1)		/* goes to the BREAK state */
+#define	PL_PCM_TRACE	(2)		/* goes to the TRACE state */
+#define	PL_PCM_STOP	(3)		/* goes to the OFF state */
+
+#define	PL_MAINT	0x0004		/* if OFF state --> MAINT state */
+#define	PL_LONG		0x0008		/* perf. a long Link Confid.Test(LCT)*/
+#define	PL_PC_JOIN	0x0010		/* if NEXT state --> JOIN state */
+
+#define	PL_PC_LOOP	0x0060		/* loopback used in the LCT */
+#define	PL_NOLCT	(0<<5)		/* no LCT is performed */
+#define	PL_TPDR		(1<<5)		/* PCM asserts transmit PDR */
+#define	PL_TIDLE	(2<<5)		/* PCM asserts transmit idle */
+#define	PL_RLBP		(3<<5)		/* trans. PDR & remote loopb. path */
+
+#define	PL_CLASS_S	0x0080		/* signif. that single att. station */
+
+#define	PL_MAINT_LS	0x0700		/* line state while in the MAINT st. */
+#define	PL_M_QUI0	(0<<8)		/* transmit QUIET line state */
+#define	PL_M_IDLE	(1<<8)		/* transmit IDLE line state */
+#define	PL_M_HALT	(2<<8)		/* transmit HALT line state */
+#define	PL_M_MASTR	(3<<8)		/* transmit MASTER line state */
+#define	PL_M_QUI1	(4<<8)		/* transmit QUIET line state */
+#define	PL_M_QUI2	(5<<8)		/* transmit QUIET line state */
+#define	PL_M_TPDR	(6<<8)		/* tr. PHY_DATA requ.-symbol is tr.ed*/
+#define	PL_M_QUI3	(7<<8)		/* transmit QUIET line state */
+
+#define	PL_MATCH_LS	0x7800		/* line state to be comp. with curr.*/
+#define	PL_I_ANY	(0<<11)		/* Int. on any change in *_LINE_ST */
+#define	PL_I_IDLE	(1<<11)		/* Interrupt on IDLE line state */
+#define	PL_I_HALT	(2<<11)		/* Interrupt on HALT line state */
+#define	PL_I_MASTR	(4<<11)		/* Interrupt on MASTER line state */
+#define	PL_I_QUIET	(8<<11)		/* Interrupt on QUIET line state */
+
+#define	PL_CONFIG_CNTRL	0x8000		/* control over scrub, byp. & loopb.*/
+
+/*
+ * PLC control register C (PL_CNTRL_C: log. addr. 0x0a)
+ * It contains the scrambling control registers (PLC-S only)
+ */
+#define PL_C_CIPHER_ENABLE	(1<<0)	/* enable scrambler */
+#define PL_C_CIPHER_LPBCK	(1<<1)	/* loopback scrambler */
+#define PL_C_SDOFF_ENABLE	(1<<6)	/* enable SDOFF timer */
+#define PL_C_SDON_ENABLE	(1<<7)	/* enable SDON timer */
+#ifdef	MOT_ELM
+#define PL_C_FOTOFF_CTRL	(3<<2)	/* FOTOFF timer control */
+#define PL_C_FOTOFF_TIM		(0<<2)	/* FOTOFF use timer for (de)-assert */
+#define PL_C_FOTOFF_INA		(2<<2)	/* FOTOFF forced inactive */
+#define PL_C_FOTOFF_ACT		(3<<2)	/* FOTOFF forced active */
+#define PL_C_FOTOFF_SRCE	(1<<4)	/* FOTOFF source is PCM state != OFF */
+#define	PL_C_RXDATA_EN		(1<<5)	/* Rec scr data forced to 0 */
+#define	PL_C_SDNRZEN		(1<<8)	/* Monitor rec descr. data for act */
+#else	/* nMOT_ELM */
+#define PL_C_FOTOFF_CTRL	(3<<8)	/* FOTOFF timer control */
+#define PL_C_FOTOFF_0		(0<<8)	/* timer off */
+#define PL_C_FOTOFF_30		(1<<8)	/* 30uS */
+#define PL_C_FOTOFF_50		(2<<8)	/* 50uS */
+#define PL_C_FOTOFF_NEVER	(3<<8)	/* never */
+#define PL_C_SDON_TIMER		(3<<10)	/* SDON timer control */
+#define PL_C_SDON_084		(0<<10)	/* 0.84 uS */
+#define PL_C_SDON_132		(1<<10)	/* 1.32 uS */
+#define PL_C_SDON_252		(2<<10)	/* 2.52 uS */
+#define PL_C_SDON_512		(3<<10)	/* 5.12 uS */
+#define PL_C_SOFF_TIMER		(3<<12)	/* SDOFF timer control */
+#define PL_C_SOFF_076		(0<<12)	/* 0.76 uS */
+#define PL_C_SOFF_132		(1<<12)	/* 1.32 uS */
+#define PL_C_SOFF_252		(2<<12)	/* 2.52 uS */
+#define PL_C_SOFF_512		(3<<12)	/* 5.12 uS */
+#define PL_C_TSEL		(3<<14)	/* scrambler path select */
+#endif	/* nMOT_ELM */
+
+/*
+ * PLC status register A (PL_STATUS_A: log. addr. 0x10)
+ * It is used to report status information to the Node Processor about the 
+ * Line State Machine (LSM).
+ */
+#ifdef	MOT_ELM
+#define PLC_INT_MASK	0xc000		/* ELM integration bits in status A */
+#define PLC_INT_C	0x0000		/* ELM Revision Band C */
+#define PLC_INT_CAMEL	0x4000		/* ELM integrated into CAMEL */
+#define PLC_INT_QE	0x8000		/* ELM integrated into Quad ELM */
+#define PLC_REV_MASK	0x3800		/* revision bits in status A */
+#define PLC_REVISION_B	0x0000		/* rev bits for ELM Rev B */
+#define PLC_REVISION_QA	0x0800		/* rev bits for ELM core in QELM-A */
+#else	/* nMOT_ELM */
+#define PLC_REV_MASK	0xf800		/* revision bits in status A */
+#define PLC_REVISION_A	0x0000		/* revision bits for PLC */
+#define PLC_REVISION_S	0xf800		/* revision bits for PLC-S */
+#define PLC_REV_SN3	0x7800		/* revision bits for PLC-S in IFCP */
+#endif	/* nMOT_ELM */
+#define	PL_SYM_PR_CTR	0x0007		/* contains the LSM symbol pair Ctr. */
+#define	PL_UNKN_LINE_ST	0x0008		/* unknown line state bit from LSM */
+#define	PL_LSM_STATE	0x0010		/* state bit of LSM */
+
+#define	PL_LINE_ST	0x00e0		/* contains recogn. line state of LSM*/
+#define	PL_L_NLS	(0<<5)		/* noise line state */
+#define	PL_L_ALS	(1<<5)		/* activ line state */
+#define	PL_L_UND	(2<<5)		/* undefined */
+#define	PL_L_ILS4	(3<<5)		/* idle l. s. (after 4 idle symbols) */
+#define	PL_L_QLS	(4<<5)		/* quiet line state */
+#define	PL_L_MLS	(5<<5)		/* master line state */
+#define	PL_L_HLS	(6<<5)		/* halt line state */
+#define	PL_L_ILS16	(7<<5)		/* idle line state (after 16 idle s.)*/
+
+#define	PL_PREV_LINE_ST	0x0300		/* value of previous line state */
+#define	PL_P_QLS	(0<<8)		/* quiet line state */
+#define	PL_P_MLS	(1<<8)		/* master line state */
+#define	PL_P_HLS	(2<<8)		/* halt line state */
+#define	PL_P_ILS16	(3<<8)		/* idle line state (after 16 idle s.)*/
+
+#define	PL_SIGNAL_DET	0x0400		/* 1=that signal detect is deasserted*/
+
+
+/*
+ * PLC status register B (PL_STATUS_B: log. addr. 0x11)
+ * It contains signals and status from the repeat filter and PCM state machine.
+ */
+#define	PL_BREAK_REASON	0x0007		/* reason for PCM state mach.s to br.*/
+#define	PL_B_NOT	(0)		/* PCM SM has not gone to BREAK state*/
+#define	PL_B_PCS	(1)		/* PC_Start issued */
+#define	PL_B_TPC	(2)		/* TPC timer expired after T_OUT */
+#define	PL_B_TNE	(3)		/* TNE timer expired after NS_MAX */
+#define	PL_B_QLS	(4)		/* quit line state detected */
+#define	PL_B_ILS	(5)		/* idle line state detected */
+#define	PL_B_HLS	(6)		/* halt line state detected */
+
+#define	PL_TCF		0x0008		/* transmit code flag (start exec.) */
+#define	PL_RCF		0x0010		/* receive code flag (start exec.) */
+#define	PL_LSF		0x0020		/* line state flag (l.s. has been r.)*/
+#define	PL_PCM_SIGNAL	0x0040		/* indic. that XMIT_VECTOR hb.written*/
+
+#define	PL_PCM_STATE	0x0780		/* state bits of PCM state machine */
+#define	PL_PC0		(0<<7)		/* OFF	   - when /RST or PCM_CNTRL */
+#define	PL_PC1		(1<<7)		/* BREAK   - entry point in start PCM*/
+#define	PL_PC2		(2<<7)		/* TRACE   - to localize stuck Beacon*/
+#define	PL_PC3		(3<<7)		/* CONNECT - synchronize ends of conn*/
+#define	PL_PC4		(4<<7)		/* NEXT	   - to seperate the signalng*/
+#define	PL_PC5		(5<<7)		/* SIGNAL  - PCM trans/rec. bit infos*/
+#define	PL_PC6		(6<<7)		/* JOIN	   - 1. state to activ conn. */
+#define	PL_PC7		(7<<7)		/* VERIFY  - 2. - " - (3. ACTIVE) */
+#define	PL_PC8		(8<<7)		/* ACTIVE  - PHY has been incorporated*/
+#define	PL_PC9		(9<<7)		/* MAINT   - for test purposes or so 
+					   that PCM op. completely in softw. */
+
+#define	PL_PCI_SCRUB	0x0800		/* scrubbing function is being exec. */
+
+#define	PL_PCI_STATE	0x3000		/* Physical Connect. Insertion SM */
+#define	PL_CI_REMV	(0<<12)		/* REMOVED */
+#define	PL_CI_ISCR	(1<<12)		/* INSERT_SCRUB */
+#define	PL_CI_RSCR	(2<<12)		/* REMOVE_SCRUB */
+#define	PL_CI_INS	(3<<12)		/* INSERTED */
+
+#define	PL_RF_STATE	0xc000		/* state bit of repeate filter SM */
+#define	PL_RF_REPT	(0<<14)		/* REPEAT */
+#define	PL_RF_IDLE	(1<<14)		/* IDLE */
+#define	PL_RF_HALT1	(2<<14)		/* HALT1 */
+#define	PL_RF_HALT2	(3<<14)		/* HALT2 */
+
+
+/*
+ * PLC interrupt event register (PL_INTR_EVENT: log. addr. 0x17)
+ * It is read only and is clearde whenever it is read!
+ * It is used by the PLC to report events to the node processor.
+ */
+#define	PL_PARITY_ERR	0x0001		/* p. error h.b.detected on TX9-0 inp*/
+#define	PL_LS_MATCH	0x0002		/* l.s.== l.s. PLC_CNTRL_B's MATCH_LS*/
+#define	PL_PCM_CODE	0x0004		/* transmit&receive | LCT complete */
+#define	PL_TRACE_PROP	0x0008		/* master l.s. while PCM ACTIV|TRACE */
+#define	PL_SELF_TEST	0x0010		/* QUIET|HALT while PCM in TRACE st. */
+#define	PL_PCM_BREAK	0x0020		/* PCM has entered the BREAK state */
+#define	PL_PCM_ENABLED	0x0040		/* asserted SC_JOIN, scrub. & ACTIV */
+#define	PL_TPC_EXPIRED	0x0080		/* TPC timer reached zero */
+#define	PL_TNE_EXPIRED	0x0100		/* TNE timer reached zero */
+#define	PL_EBUF_ERR	0x0200		/* elastic buff. det. over-|underflow*/
+#define	PL_PHYINV	0x0400		/* physical layer invalid signal */
+#define	PL_VSYM_CTR	0x0800		/* violation symbol counter has incr.*/
+#define	PL_MINI_CTR	0x1000		/* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
+#define	PL_LE_CTR	0x2000		/* link error event counter */
+#define	PL_LSDO		0x4000		/* SDO input pin changed to a 1 */
+#define	PL_NP_ERR	0x8000		/* NP has requested to r/w an inv. r.*/
+
+/*
+ * The PLC interrupt mask register (PL_INTR_MASK: log. addr. 0x02) constr. is
+ * equal PL_INTR_EVENT register.
+ * For each set bit, the setting of corresponding bit generate an int to NP. 
+ */
+
+#ifdef	MOT_ELM
+/*
+ * Quad ELM Crosbar Control register values (QELM_XBAR_?)
+ */
+#define	QELM_XOUT_IDLE	0x0000		/* Idles/Passthrough */
+#define	QELM_XOUT_P	0x0001		/* Output to: Bus P */
+#define	QELM_XOUT_S	0x0002		/* Output to: Bus S */
+#define	QELM_XOUT_R	0x0003		/* Output to: Bus R */
+#define	QELM_XOUT_W	0x0004		/* Output to: ELM W */
+#define	QELM_XOUT_X	0x0005		/* Output to: ELM X */
+#define	QELM_XOUT_Y	0x0006		/* Output to: ELM Y */
+#define	QELM_XOUT_Z	0x0007		/* Output to: ELM Z */
+
+/*
+ * Quad ELM Interrupt data and event registers.
+ */
+#define	QELM_NP_ERR	(1<<15)		/* Node Processor Error */
+#define	QELM_COUNT_Z	(1<<7)		/* Counter Z Interrupt */
+#define	QELM_COUNT_Y	(1<<6)		/* Counter Y Interrupt */
+#define	QELM_COUNT_X	(1<<5)		/* Counter X Interrupt */
+#define	QELM_COUNT_W	(1<<4)		/* Counter W Interrupt */
+#define	QELM_ELM_Z	(1<<3)		/* ELM Z Interrupt */
+#define	QELM_ELM_Y	(1<<2)		/* ELM Y Interrupt */
+#define	QELM_ELM_X	(1<<1)		/* ELM X Interrupt */
+#define	QELM_ELM_W	(1<<0)		/* ELM W Interrupt */
+#endif	/* MOT_ELM */
+/*
+ * PLC Timing Parameters
+ */
+#define	TP_C_MIN	0xff9c	/*   2    ms */
+#define	TP_TL_MIN	0xfff0	/*   0.3  ms */
+#define	TP_TB_MIN	0xff10	/*   5    ms */
+#define	TP_T_OUT	0xd9db	/* 200    ms */
+#define	TP_LC_LENGTH	0xf676	/*  50    ms */
+#define	TP_LC_LONGLN	0xa0a2	/* 500    ms */
+#define	TP_T_SCRUB	0xff6d	/*   3.5  ms */
+#define	TP_NS_MAX	0xf021	/*   1.3   ms */
+
+/*
+ * BIST values
+ */
+#define PLC_BIST	0x6ecd		/* BIST signature for PLC */
+#define PLCS_BIST	0x5b6b 		/* BIST signature for PLC-S */
+#define	PLC_ELM_B_BIST	0x6ecd		/* BIST signature of ELM Rev. B */
+#define	PLC_ELM_D_BIST	0x5b6b		/* BIST signature of ELM Rev. D */
+#define	PLC_CAM_A_BIST	0x9e75		/* BIST signature of CAMEL Rev. A */
+#define	PLC_CAM_B_BIST	0x5b6b		/* BIST signature of CAMEL Rev. B */
+#define	PLC_IFD_A_BIST	0x9e75		/* BIST signature of IFDDI Rev. A */
+#define	PLC_IFD_B_BIST	0x5b6b		/* BIST signature of IFDDI Rev. B */
+#define	PLC_QELM_A_BIST	0x5b6b		/* BIST signature of QELM Rev. A */
+
+/*
+ 	FDDI board recources	
+ */
+
+/*
+ * request register array (log. addr: RQA_A + a<<1 {a=0..7}) write only.
+ * It specifies to FORMAC+ the type of buffer memory access the host requires.
+ */
+#define	RQ_NOT		0		/* not request */
+#define	RQ_RES		1		/* reserved */
+#define	RQ_SFW		2		/* special frame write */
+#define	RQ_RRQ		3		/* read request: receive queue */
+#define	RQ_WSQ		4		/* write request: synchronous queue */
+#define	RQ_WA0		5		/* write requ.: asynchronous queue 0 */
+#define	RQ_WA1		6		/* write requ.: asynchronous queue 1 */
+#define	RQ_WA2		7		/* write requ.: asynchronous queue 2 */
+
+#define	SZ_LONG		(sizeof(long))
+
+/*
+ * FDDI defaults
+ * NOTE : In the ANSI docs, times are specified in units of "symbol time".
+ * 	  AMD chips use BCLK as unit. 1 BCKL == 2 symbols
+ */
+#define	COMPLREF	((u_long)32*256*256)	/* two's complement 21 bit */
+#define MSTOBCLK(x)	((u_long)(x)*12500L)
+#define MSTOTVX(x)	(((u_long)(x)*1000L)/80/255)
+
+#endif	/* _SUPERNET_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/targethw.h linux.18p17/drivers/net/skfp/h/targethw.h
--- linux.vanilla/drivers/net/skfp/h/targethw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/targethw.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,173 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	_TARGETHW_
+#define _TARGETHW_
+
+	/*
+	 *  PCI Watermark definition
+	 */
+#ifdef	PCI
+#define	RX_WATERMARK	24
+#define TX_WATERMARK	24
+#define SK_ML_ID_1	0x20
+#define SK_ML_ID_2	0x30
+#endif
+
+#include	"h/skfbi.h"
+#ifndef TAG_MODE	
+#include	"h/fplus.h"
+#else
+#include	"h/fplustm.h"
+#endif
+
+#ifndef	HW_PTR
+#ifdef  MEM_MAPPED_IO
+#define HW_PTR  u_long
+#else
+#define HW_PTR  u_short
+#endif
+#endif
+
+#ifdef MULT_OEM
+#define	OI_STAT_LAST		0	/* end of OEM data base */
+#define	OI_STAT_PRESENT		1	/* entry present but not empty */
+#define	OI_STAT_VALID		2	/* holds valid ID, but is not active */ 
+#define	OI_STAT_ACTIVE		3	/* holds valid ID, entry is active */
+					/* active = adapter is supported */
+
+/* Memory representation of IDs must match representation in adapter. */
+struct	s_oem_ids {
+	u_char	oi_status ;		/* Stat: last, present, valid, active */
+	u_char	oi_mark[5] ;		/* "PID00" .. "PID07" ..	*/
+	u_char 	oi_id[4] ;		/* id bytes, representation as	*/
+					/* defined by hardware,		*/	
+#ifdef PCI
+	u_char 	oi_sub_id[4] ;		/* sub id bytes, representation as */
+					/* defined by hardware,		*/
+#endif
+#ifdef ISA
+	u_char	oi_logo_len ;		/* the length of the adapter logo */	
+	u_char	oi_logo[6] ;		/* the adapter logo		*/
+	u_char	oi_reserved1 ;
+#endif	/* ISA */
+} ;
+#endif	/* MULT_OEM */
+
+
+struct s_smt_hw {
+	/*
+	 * global
+	 */
+	HW_PTR	iop ;			/* IO base address */
+	short	dma ;			/* DMA channel */
+	short	irq ;			/* IRQ level */
+	short	eprom ;			/* FLASH prom */
+#ifndef	PCI
+	short	DmaWriteExtraBytes ;	/* add bytes for DMA write */
+#endif
+
+#ifndef SYNC
+	u_short	n_a_send ;		/* pending send requests */
+#endif
+
+#if	(defined(EISA) || defined(MCA) || defined(PCI))
+	short	slot ;			/* slot number */
+	short   max_slots ;		/* maximum number of slots */
+#endif
+
+#if	(defined(PCI) || defined(MCA))
+	short	wdog_used ;		/* TRUE if the watch dog is used */
+#endif
+
+#ifdef	MCA
+	short	slot_32 ;		/* 32bit slot (1) or 16bit slot (0) */
+	short	rev ;			/* Board revision (FMx_REV). */
+	short	VFullRead ;		/* V_full value for DMA read */
+	short	VFullWrite ;		/* V_full value for DMA write */
+#endif
+
+#ifdef	EISA
+	short	led ;			/* LED for FE card */
+
+	short	dma_rmode ;		/* read mode */
+	short	dma_wmode ;		/* write mode */
+	short	dma_emode ;		/* extend mode */
+
+	/* DMA controller channel dependent io addresses */
+	u_short dma_base_word_count ;
+	u_short dma_base_address ;
+	u_short dma_base_address_page ;
+#endif
+
+#ifdef	PCI
+	u_short	pci_handle ;		/* handle to access the BIOS func */
+	u_long	is_imask ;		/* int maske for the int source reg */
+	u_long	phys_mem_addr ;		/* physical memory address */
+	u_short	mc_dummy ;		/* work around for MC compiler bug */	
+	/*
+	 * state of the hardware
+	 */
+	u_short hw_state ;		/* started or stopped */
+
+#define	STARTED		1
+#define	STOPPED		0
+
+	int	hw_is_64bit ;		/* does we have a 64 bit adapter */
+#endif
+
+#ifdef	TAG_MODE
+	u_long	pci_fix_value ;		/* value parsed by PCIFIX */
+#endif
+
+	/*
+	 * hwt.c
+	 */
+	u_long	t_start ;		/* HWT start */
+	u_long	t_stop ;		/* HWT stop */
+	u_short	timer_activ ;		/* HWT timer active */
+
+	/*
+	 * PIC
+	 */
+	u_char	pic_a1 ;
+	u_char	pic_21 ;
+
+	/*
+	 * GENERIC ; do not modify beyond this line
+	 */
+
+	/*
+	 * physical and canonical address
+	 */
+	struct fddi_addr fddi_home_addr ;
+	struct fddi_addr fddi_canon_addr ;
+	struct fddi_addr fddi_phys_addr ;
+
+	/*
+	 * mac variables
+	 */
+	struct mac_parameter mac_pa ;	/* tmin, tmax, tvx, treq .. */
+	struct mac_counter mac_ct ;	/* recv., lost, error  */
+	u_short	mac_ring_is_up ;	/* ring is up flag */
+
+	struct s_smt_fp	fp ;		/* formac+ */
+
+#ifdef MULT_OEM
+	struct s_oem_ids *oem_id ;	/* pointer to selected id */
+	int oem_min_status ;		/* IDs to take care of */
+#endif	/* MULT_OEM */
+
+} ;
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/targetos.h linux.18p17/drivers/net/skfp/h/targetos.h
--- linux.vanilla/drivers/net/skfp/h/targetos.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/targetos.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,164 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ *	Operating system specific definitions for driver and
+ *	hardware module.
+ */
+
+#ifndef	TARGETOS_H
+#define TARGETOS_H
+
+
+//-------- those should go into include/linux/pci.h
+#define PCI_VENDOR_ID_SK		0x1148
+#define PCI_DEVICE_ID_SK_FP		0x4000
+//--------
+
+
+
+//-------- those should go into include/linux/if_fddi.h
+#define FDDI_MAC_HDR_LEN 13
+
+#define FDDI_RII	0x01 /* routing information bit */
+#define FDDI_RCF_DIR_BIT 0x80
+#define FDDI_RCF_LEN_MASK 0x1f
+#define FDDI_RCF_BROADCAST 0x8000
+#define FDDI_RCF_LIMITED_BROADCAST 0xA000
+#define FDDI_RCF_FRAME2K 0x20
+#define FDDI_RCF_FRAME4K 0x30
+//--------
+
+
+#undef ADDR
+
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/netdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+// is redefined by linux, but we need our definition
+#undef ADDR
+#ifdef MEM_MAPPED_IO
+#define	ADDR(a) (char far *) smc->hw.iop+(a)
+#else
+#define	ADDR(a) (((a)>>7) ? (outp(smc->hw.iop+B0_RAP,(a)>>7), (smc->hw.iop+( ((a)&0x7F) | ((a)>>7 ? 0x80:0)) )) : (smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0))))
+#endif
+
+#include "h/hwmtm.h"
+
+#define TRUE  1
+#define FALSE 0
+
+// HWM Definitions
+// -----------------------
+#define FDDI_TRACE(string, arg1, arg2, arg3)	// Performance analysis.
+#ifdef PCI
+#define NDD_TRACE(string, arg1, arg2, arg3)	// Performance analysis.
+#endif	// PCI
+#define SMT_PAGESIZE	PAGE_SIZE	// Size of a memory page (power of 2).
+// -----------------------
+
+
+// SMT Definitions
+// -----------------------
+#define	TICKS_PER_SECOND	HZ
+#define SMC_VERSION    		1
+// -----------------------
+
+
+// OS-Driver Definitions
+// -----------------------
+#define NO_ADDRESS 0xffe0	/* No Device (I/O) Address */
+#define SKFP_MAX_NUM_BOARDS 8	/* maximum number of PCI boards */
+
+#define SK_BUS_TYPE_PCI		0
+#define SK_BUS_TYPE_EISA	1
+
+#define FP_IO_LEN		256	/* length of IO area used */
+
+#define u8	unsigned char
+#define u16	unsigned short
+#define u32	unsigned int
+
+#define MAX_TX_QUEUE_LEN	20 // number of packets queued by driver
+#define MAX_FRAME_SIZE		4550
+
+#define	RX_LOW_WATERMARK	NUM_RECEIVE_BUFFERS  / 2
+#define TX_LOW_WATERMARK	NUM_TRANSMIT_BUFFERS - 2
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define	SKFPIOCTL	SIOCDEVPRIVATE
+
+struct s_skfp_ioctl {
+	unsigned short cmd;                /* Command to run */
+	unsigned short len;                /* Length of the data buffer */
+	unsigned char  *data;              /* Pointer to the data buffer */
+};
+
+/* 
+** Recognised ioctl commands for the driver 
+*/
+#define SKFP_GET_STATS		0x05 /* Get the driver statistics */
+#define SKFP_CLR_STATS		0x06 /* Zero out the driver statistics */
+
+// The per-adapter driver structure
+struct s_smt_os {
+	struct device	*dev;
+	struct device	*next_module;
+	u32		bus_type;/* bus type (0 == PCI, 1 == EISA) */
+	struct pci_dev	pdev;	/* PCI device structure */
+	
+	unsigned long base_addr;
+	unsigned char factory_mac_addr[8];
+	ulong	SharedMemSize;
+	ulong	SharedMemHeap;
+	void*	SharedMemAddr;
+
+	ulong	QueueSkb;
+	struct	sk_buff_head SendSkbQueue;
+
+	ulong	MaxFrameSize;
+	u8	ResetRequested;
+
+	// MAC statistics structure
+	struct fddi_statistics MacStat;
+
+	// receive into this local buffer if no skb available
+	// data will be not valid, because multiple RxDs can
+	// point here at the same time, it must be at least
+	// MAX_FRAME_SIZE bytes in size
+	unsigned char *LocalRxBuffer;
+	
+	// Version (required by SMT module).
+	u_long smc_version ;
+
+	// Required by Hardware Module (HWM).
+	struct hw_modul hwm ;
+	
+	// For SMP-savety
+	spinlock_t DriverLock;
+	
+};
+
+typedef struct s_smt_os skfddi_priv;
+
+#endif	 // _TARGETOS_
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/h/types.h linux.18p17/drivers/net/skfp/h/types.h
--- linux.vanilla/drivers/net/skfp/h/types.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/h/types.h	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#include	<linux/types.h>
+/*
+	----------------------
+	Basic SMT system types
+	----------------------
+*/
+#ifndef _TYPES_
+#define	_TYPES_
+
+#define _packed
+#ifndef far
+#define far
+#endif
+#ifndef _far
+#define _far
+#endif
+
+#ifndef MEM_MAPPED_IO // "normal" IO
+#define inp(p)  inb(p)
+#define inpw(p)	inw(p)
+#define inpd(p) inl(p)
+#define outp(p,c)  outb(c,p)
+#define outpw(p,s) outw(s,p)
+#define outpd(p,l) outl(l,p)
+#else // memory mapped io
+#define inp(a)		readb(a)
+#define inpw(a)		readw(a)
+#define inpd(a)		readl(a)
+#define outp(a,v)	writeb(v, a)	
+#define outpw(a,v)	writew(v, a)	
+#define outpd(a,v)	writel(v, a)	
+#endif
+
+#endif	/* _TYPES_ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/hwmtm.c linux.18p17/drivers/net/skfp/hwmtm.c
--- linux.vanilla/drivers/net/skfp/hwmtm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/hwmtm.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,2241 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#ifndef	lint
+static char const ID_sccs[] = "@(#)hwmtm.c	1.40 99/05/31 (C) SK" ;
+#endif
+
+#define	HWMTM
+
+#ifndef FDDI
+#define	FDDI
+#endif
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/supern_2.h"
+#include "h/skfbiinc.h"
+
+/*
+	-------------------------------------------------------------
+	DOCUMENTATION
+	-------------------------------------------------------------
+	BEGIN_MANUAL_ENTRY(DOCUMENTATION)
+
+			T B D
+
+	END_MANUAL_ENTRY
+*/
+/*
+	-------------------------------------------------------------
+	LOCAL VARIABLES:
+	-------------------------------------------------------------
+*/
+#ifdef COMMON_MB_POOL
+static	SMbuf *mb_start = 0 ;
+static	SMbuf *mb_free = 0 ;
+static	int mb_init = FALSE ;
+static	int call_count = 0 ;
+#endif
+
+/*
+	-------------------------------------------------------------
+	EXTERNE VARIABLES:
+	-------------------------------------------------------------
+*/
+
+#ifdef	DEBUG
+#ifndef	DEBUG_BRD
+extern	struct smt_debug	debug ;
+#endif
+#endif
+
+#ifdef	NDIS_OS2
+extern	u_char	offDepth ;
+extern	u_char	force_irq_pending ;
+#endif
+
+/*
+	-------------------------------------------------------------
+	LOCAL FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+static	void	queue_llc_rx(),		smt_to_llc(),
+		init_txd_ring(),	init_rxd_ring(),
+		queue_txd_mb() ;
+
+static	u_long	init_descr_ring(),	repair_txd_ring(),
+		repair_rxd_ring() ;
+
+static	SMbuf	*get_llc_rx(),		*get_txd_mb() ;
+
+
+/*
+	-------------------------------------------------------------
+	EXTERNAL FUNCTIONS:
+	-------------------------------------------------------------
+*/
+/*	The external SMT functions are listed in cmtdef.h */
+
+extern	void	*mac_drv_get_space(),	*mac_drv_get_desc_mem(),
+		init_board(),		mac_drv_fill_rxd(),
+		plc1_irq(),		mac_drv_tx_complete(),
+		plc2_irq(),		mac1_irq(),
+		mac2_irq(),		mac3_irq(),
+		timer_irq(),		mac_drv_rx_complete(),
+		mac_drv_requeue_rxd(),	init_plc(),
+		mac_drv_clear_rxd(),	llc_restart_tx(),
+		ev_dispatcher(),	smt_force_irq() ;
+
+#ifdef	USE_OS_CPY
+extern	void	hwm_cpy_rxd2mb(),	hwm_cpy_txd2mb() ;
+#endif
+#ifdef	ALL_RX_COMPLETE
+extern	void	mac_drv_all_receives_complete() ;
+#endif
+
+extern	u_long	mac_drv_virt2phys(),	dma_master() ;
+
+#ifdef	NDIS_OS2
+extern	void	post_proc() ;
+#else
+extern	void	dma_complete() ;
+#endif
+
+extern	int	init_fplus(),		mac_drv_rx_init() ;
+
+/*
+	-------------------------------------------------------------
+	PUBLIC FUNCTIONS:
+	-------------------------------------------------------------
+*/
+	void	process_receive(),	smt_send_mbuf(),
+		fddi_isr(),		mac_drv_clear_txd(),
+		smt_free_mbuf(),	init_driver_fplus(),
+		mac_drv_rx_mode(),	init_fddi_driver(),
+		mac_drv_clear_tx_queue(),
+		mac_drv_clear_rx_queue(),
+		hwm_tx_frag(),		hwm_rx_frag() ;
+
+	int	mac_drv_rx_frag(),	mac_drv_init(),
+		hwm_tx_init() ;
+
+	u_int	mac_drv_check_space() ;
+
+	SMbuf 	*smt_get_mbuf() ;
+
+#ifdef DEBUG
+	void	mac_drv_debug_lev() ;
+#endif
+
+/*
+	-------------------------------------------------------------
+	MACROS:
+	-------------------------------------------------------------
+*/
+#ifndef	UNUSED
+#ifdef	lint
+#define UNUSED(x)	(x) = (x)
+#else
+#define UNUSED(x)
+#endif
+#endif
+
+#ifdef	USE_CAN_ADDR
+#define MA		smc->hw.fddi_canon_addr.a
+#define	GROUP_ADDR_BIT	0x01
+#else
+#define	MA		smc->hw.fddi_home_addr.a
+#define	GROUP_ADDR_BIT	0x80
+#endif
+
+#define RXD_TXD_COUNT	(HWM_ASYNC_TXD_COUNT+HWM_SYNC_TXD_COUNT+\
+			SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT)
+
+#ifdef	MB_OUTSIDE_SMC
+#define	EXT_VIRT_MEM	((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd) +\
+			MAX_MBUF*sizeof(SMbuf))
+#define	EXT_VIRT_MEM_2	((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd))
+#else
+#define	EXT_VIRT_MEM	((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd))
+#endif
+
+	/*
+	 * define critical read for 16 Bit drivers
+	 */
+#if	defined(NDIS_OS2) || defined(ODI2)
+#define CR_READ(var)	((var) & 0xffff0000 | ((var) & 0xffff))
+#else
+#define CR_READ(var)	(u_long)(var)
+#endif
+
+#define IMASK_SLOW	(IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \
+			 IS_MINTR1 | IS_MINTR2 | IS_MINTR3 | IS_R1_P | \
+			 IS_R1_C | IS_XA_C | IS_XS_C)
+
+/*
+	-------------------------------------------------------------
+	INIT- AND SMT FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_check_space)
+ *	u_int mac_drv_check_space()
+ *
+ *	function	DOWNCALL	(drvsr.c)
+ *			This function calculates the needed non virtual
+ *			memory for MBufs, RxD and TxD descriptors etc.
+ *			needed by the driver.
+ *
+ *	return		u_int	memory in bytes
+ *
+ *	END_MANUAL_ENTRY
+ */
+u_int mac_drv_check_space()
+{
+#ifdef	MB_OUTSIDE_SMC
+#ifdef	COMMON_MB_POOL
+	call_count++ ;
+	if (call_count == 1) {
+		return(EXT_VIRT_MEM) ;
+	}
+	else {
+		return(EXT_VIRT_MEM_2) ;
+	}
+#else
+	return (EXT_VIRT_MEM) ;
+#endif
+#else
+	return (0) ;
+#endif
+}
+
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_init)
+ *	void mac_drv_init(smc)
+ *
+ *	function	DOWNCALL	(drvsr.c)
+ *			In this function the hardware module allocates it's
+ *			memory.
+ *			The operating system dependent module should call
+ *			mac_drv_init once, after the adatper is detected.
+ *	END_MANUAL_ENTRY
+ */
+int mac_drv_init(smc)
+struct s_smc *smc ;
+{
+	if (sizeof(struct s_smt_fp_rxd) % 16) {
+		SMT_PANIC(smc,HWM_E0001,HWM_E0001_MSG) ;
+	}
+	if (sizeof(struct s_smt_fp_txd) % 16) {
+		SMT_PANIC(smc,HWM_E0002,HWM_E0002_MSG) ;
+	}
+
+	/*
+	 * get the required memory for the RxDs and TxDs
+	 */
+	if (!(smc->os.hwm.descr_p = (union s_fp_descr volatile *)
+		mac_drv_get_desc_mem(smc,(u_int)
+		(RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd)))) {
+		return(1) ;	/* no space the hwm modul can't work */
+	}
+
+	/*
+	 * get the memory for the SMT MBufs
+	 */
+#ifndef	MB_OUTSIDE_SMC
+	smc->os.hwm.mbuf_pool.mb_start=(SMbuf *)(&smc->os.hwm.mbuf_pool.mb[0]) ;
+#else
+#ifndef	COMMON_MB_POOL
+	if (!(smc->os.hwm.mbuf_pool.mb_start = (SMbuf *) mac_drv_get_space(smc,
+		MAX_MBUF*sizeof(SMbuf)))) {
+		return(1) ;	/* no space the hwm modul can't work */
+	}
+#else
+	if (!mb_start) {
+		if (!(mb_start = (SMbuf *) mac_drv_get_space(smc,
+			MAX_MBUF*sizeof(SMbuf)))) {
+			return(1) ;	/* no space the hwm modul can't work */
+		}
+	}
+#endif
+#endif
+	return (0) ;
+}
+
+/*
+ *	BEGIN_MANUAL_ENTRY(init_driver_fplus)
+ *	init_driver_fplus(smc)
+ *
+ * Sets hardware modul specific values for the mode register 2
+ * (e.g. the byte alignment for the received frames, the position of the
+ *	 least significant byte etc.)
+ *	END_MANUAL_ENTRY
+ */
+void init_driver_fplus(smc)
+struct s_smc *smc ;
+{
+	smc->hw.fp.mdr2init = FM_LSB | FM_BMMODE | FM_ENNPRQ | FM_ENHSRQ | 3 ;
+
+#ifdef	PCI
+	smc->hw.fp.mdr2init |= FM_CHKPAR | FM_PARITY ;
+#endif
+	smc->hw.fp.mdr3init = FM_MENRQAUNLCK | FM_MENRS ;
+
+#ifdef	USE_CAN_ADDR
+	/* enable address bit swapping */
+	smc->hw.fp.frselreg_init = FM_ENXMTADSWAP | FM_ENRCVADSWAP ;
+#endif
+}
+
+static u_long init_descr_ring(smc,start,count)
+struct s_smc *smc ;
+union s_fp_descr volatile *start;
+int count ;
+{
+	int i ;
+	union s_fp_descr volatile *d1 ;
+	union s_fp_descr volatile *d2 ;
+	u_long	phys ;
+
+	DB_GEN("descr ring starts at = %x ",(void *)start,0,3) ;
+	for (i=count-1, d1=start; i ; i--) {
+		d2 = d1 ;
+		d1++ ;		/* descr is owned by the host */
+		d2->r.rxd_rbctrl = AIX_REVERSE(BMU_CHECK) ;
+		d2->r.rxd_next = &d1->r ;
+		phys = mac_drv_virt2phys(smc,(void *)d1) ;
+		d2->r.rxd_nrdadr = AIX_REVERSE(phys) ;
+	}
+	DB_GEN("descr ring ends at = %x ",(void *)d1,0,3) ;
+	d1->r.rxd_rbctrl = AIX_REVERSE(BMU_CHECK) ;
+	d1->r.rxd_next = &start->r ;
+	phys = mac_drv_virt2phys(smc,(void *)start) ;
+	d1->r.rxd_nrdadr = AIX_REVERSE(phys) ;
+
+	for (i=count, d1=start; i ; i--) {
+		DRV_BUF_FLUSH(&d1->r,DDI_DMA_SYNC_FORDEV) ;
+		d1++;
+	}
+	return(phys) ;
+}
+
+static void init_txd_ring(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_fp_txd volatile *ds ;
+	struct s_smt_tx_queue *queue ;
+	u_long	phys ;
+
+	/*
+	 * initialize the transmit descriptors
+	 */
+	ds = (struct s_smt_fp_txd volatile *) ((char *)smc->os.hwm.descr_p +
+		SMT_R1_RXD_COUNT*sizeof(struct s_smt_fp_rxd)) ;
+	queue = smc->hw.fp.tx[QUEUE_A0] ;
+	DB_GEN("Init async TxD ring, %d TxDs ",HWM_ASYNC_TXD_COUNT,0,3) ;
+	(void)init_descr_ring(smc,(union s_fp_descr volatile *)ds,
+		HWM_ASYNC_TXD_COUNT) ;
+	phys = AIX_REVERSE(ds->txd_ntdadr) ;
+	ds++ ;
+	queue->tx_curr_put = queue->tx_curr_get = ds ;
+	ds-- ;
+	queue->tx_free = HWM_ASYNC_TXD_COUNT ;
+	queue->tx_used = 0 ;
+	outpd(ADDR(B5_XA_DA),phys) ;
+
+	ds = (struct s_smt_fp_txd volatile *) ((char *)ds +
+		HWM_ASYNC_TXD_COUNT*sizeof(struct s_smt_fp_txd)) ;
+	queue = smc->hw.fp.tx[QUEUE_S] ;
+	DB_GEN("Init sync TxD ring, %d TxDs ",HWM_SYNC_TXD_COUNT,0,3) ;
+	(void)init_descr_ring(smc,(union s_fp_descr volatile *)ds,
+		HWM_SYNC_TXD_COUNT) ;
+	phys = AIX_REVERSE(ds->txd_ntdadr) ;
+	ds++ ;
+	queue->tx_curr_put = queue->tx_curr_get = ds ;
+	queue->tx_free = HWM_SYNC_TXD_COUNT ;
+	queue->tx_used = 0 ;
+	outpd(ADDR(B5_XS_DA),phys) ;
+}
+
+static void init_rxd_ring(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_fp_rxd volatile *ds ;
+	struct s_smt_rx_queue *queue ;
+	u_long	phys ;
+
+	/*
+	 * initialize the receive descriptors
+	 */
+	ds = (struct s_smt_fp_rxd volatile *) smc->os.hwm.descr_p ;
+	queue = smc->hw.fp.rx[QUEUE_R1] ;
+	DB_GEN("Init RxD ring, %d RxDs ",SMT_R1_RXD_COUNT,0,3) ;
+	(void)init_descr_ring(smc,(union s_fp_descr volatile *)ds,
+		SMT_R1_RXD_COUNT) ;
+	phys = AIX_REVERSE(ds->rxd_nrdadr) ;
+	ds++ ;
+	queue->rx_curr_put = queue->rx_curr_get = ds ;
+	queue->rx_free = SMT_R1_RXD_COUNT ;
+	queue->rx_used = 0 ;
+	outpd(ADDR(B4_R1_DA),phys) ;
+}
+
+/*
+ *	BEGIN_MANUAL_ENTRY(init_fddi_driver)
+ *	void init_fddi_driver(smc,mac_addr)
+ *
+ * initializes the driver and it's variables
+ *
+ *	END_MANUAL_ENTRY
+ */
+void init_fddi_driver(smc,mac_addr)
+struct	s_smc	*smc ;
+u_char		*mac_addr ;	/* canonical address */
+{
+	SMbuf	*mb ;
+	int	i ;
+
+	init_board(smc,mac_addr) ;
+	(void)init_fplus(smc) ;
+
+	/*
+	 * initialize the SMbufs for the SMT
+	 */
+#ifndef	COMMON_MB_POOL
+	mb = smc->os.hwm.mbuf_pool.mb_start ;
+	smc->os.hwm.mbuf_pool.mb_free = (SMbuf *)NULL ;
+	for (i = 0; i < MAX_MBUF; i++) {
+		mb->sm_use_count = 1 ;
+		smt_free_mbuf(smc,mb)	;
+		mb++ ;
+	}
+#else
+	mb = mb_start ;
+	if (!mb_init) {
+		mb_free = 0 ;
+		for (i = 0; i < MAX_MBUF; i++) {
+			mb->sm_use_count = 1 ;
+			smt_free_mbuf(smc,mb)	;
+			mb++ ;
+		}
+		mb_init = TRUE ;
+	}
+#endif
+
+	/*
+	 * initialize the other variables
+	 */
+	smc->os.hwm.llc_rx_pipe = smc->os.hwm.llc_rx_tail = (SMbuf *)NULL ;
+	smc->os.hwm.txd_tx_pipe = smc->os.hwm.txd_tx_tail = NULL ;
+	smc->os.hwm.pass_SMT = smc->os.hwm.pass_NSA = smc->os.hwm.pass_DB = 0 ;
+	smc->os.hwm.pass_llc_promisc = TRUE ;
+	smc->os.hwm.queued_rx_frames = smc->os.hwm.queued_txd_mb = 0 ;
+	smc->os.hwm.detec_count = 0 ;
+	smc->os.hwm.rx_break = 0 ;
+	smc->os.hwm.rx_len_error = 0 ;
+	smc->os.hwm.isr_flag = FALSE ;
+
+	/*
+	 * make sure that the start pointer is 16 byte aligned
+	 */
+	i = 16 - ((long)smc->os.hwm.descr_p & 0xf) ;
+	if (i != 16) {
+		DB_GEN("i = %d",i,0,3) ;
+		smc->os.hwm.descr_p = (union s_fp_descr volatile *)
+			((char *)smc->os.hwm.descr_p+i) ;
+	}
+	DB_GEN("pt to descr area = %x",(void *)smc->os.hwm.descr_p,0,3) ;
+
+	init_txd_ring(smc) ;
+	init_rxd_ring(smc) ;
+	mac_drv_fill_rxd(smc) ;
+
+	init_plc(smc) ;
+}
+
+
+SMbuf *smt_get_mbuf(smc)
+struct s_smc *smc ;
+{
+	register SMbuf	*mb ;
+
+#ifndef	COMMON_MB_POOL
+	mb = smc->os.hwm.mbuf_pool.mb_free ;
+#else
+	mb = mb_free ;
+#endif
+	if (mb) {
+#ifndef	COMMON_MB_POOL
+		smc->os.hwm.mbuf_pool.mb_free = mb->sm_next ;
+#else
+		mb_free = mb->sm_next ;
+#endif
+		mb->sm_off = 8 ;
+		mb->sm_use_count = 1 ;
+	}
+	DB_GEN("get SMbuf: mb = %x",(void *)mb,0,3) ;
+	return (mb) ;	/* May be NULL */
+}
+
+void smt_free_mbuf(smc, mb)
+struct s_smc	*smc ;
+SMbuf		*mb;
+{
+
+	if (mb) {
+		mb->sm_use_count-- ;
+		DB_GEN("free_mbuf: sm_use_count = %d",mb->sm_use_count,0,3) ;
+		/*
+		 * If the use_count is != zero the MBuf is queued
+		 * more than once and must not queued into the
+		 * free MBuf queue
+		 */
+		if (!mb->sm_use_count) {
+			DB_GEN("free SMbuf: mb = %x",(void *)mb,0,3) ;
+#ifndef	COMMON_MB_POOL
+			mb->sm_next = smc->os.hwm.mbuf_pool.mb_free ;
+			smc->os.hwm.mbuf_pool.mb_free = mb ;
+#else
+			mb->sm_next = mb_free ;
+			mb_free = mb ;
+#endif
+		}
+	}
+	else
+		SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ;
+}
+
+
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_repair_descr)
+ *	void mac_drv_repair_descr(smc)
+ *
+ * function	called from SMT	(HWM / hwmtm.c)
+ *		The BMU is idle when this function is called.
+ *		Mac_drv_repair_descr sets up the physical address
+ *		for all receive and transmit queues where the BMU
+ *		should continue.
+ *		It may be that the BMU was reseted during a fragmented
+ *		transfer. In this case there are some fragments which will
+ *		never completed by the BMU. The OWN bit of this fragments
+ *		must be switched to be owned by the host.
+ *
+ *		Give a start command to the receive BMU.
+ *		Start the transmit BMUs if transmit frames pending.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_repair_descr(smc)
+struct s_smc *smc ;
+{
+	u_long	phys ;
+
+	if (smc->hw.hw_state != STOPPED) {
+		SK_BREAK() ;
+		SMT_PANIC(smc,HWM_E0013,HWM_E0013_MSG) ;
+		return ;
+	}
+
+	/*
+	 * repair tx queues: don't start
+	 */
+	phys = repair_txd_ring(smc,smc->hw.fp.tx[QUEUE_A0]) ;
+	outpd(ADDR(B5_XA_DA),phys) ;
+	if (smc->hw.fp.tx_q[QUEUE_A0].tx_used) {
+		outpd(ADDR(B0_XA_CSR),CSR_START) ;
+	}
+	phys = repair_txd_ring(smc,smc->hw.fp.tx[QUEUE_S]) ;
+	outpd(ADDR(B5_XS_DA),phys) ;
+	if (smc->hw.fp.tx_q[QUEUE_S].tx_used) {
+		outpd(ADDR(B0_XS_CSR),CSR_START) ;
+	}
+
+	/*
+	 * repair rx queues
+	 */
+	phys = repair_rxd_ring(smc,smc->hw.fp.rx[QUEUE_R1]) ;
+	outpd(ADDR(B4_R1_DA),phys) ;
+	outpd(ADDR(B0_R1_CSR),CSR_START) ;
+}
+
+static u_long repair_txd_ring(smc,queue)
+struct s_smc *smc ;
+struct s_smt_tx_queue *queue ;
+{
+	int i ;
+	int tx_used ;
+	u_long phys ;
+	u_long tbctrl ;
+	struct s_smt_fp_txd volatile *t ;
+
+	SK_UNUSED(smc) ;
+
+	t = queue->tx_curr_get ;
+	tx_used = queue->tx_used ;
+	for (i = tx_used+queue->tx_free-1 ; i ; i-- ) {
+		t = t->txd_next ;
+	}
+	phys = AIX_REVERSE(t->txd_ntdadr) ;
+
+	t = queue->tx_curr_get ;
+	while (tx_used) {
+		DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORCPU) ;
+		tbctrl = AIX_REVERSE(t->txd_tbctrl) ;
+
+		if (tbctrl & BMU_OWN) {
+			if (tbctrl & BMU_STF) {
+				break ;		/* exit the loop */
+			}
+			else {
+				/*
+				 * repair the descriptor
+				 */
+				t->txd_tbctrl &= AIX_REVERSE(~BMU_OWN) ;
+			}
+		}
+		phys = AIX_REVERSE(t->txd_ntdadr) ;
+		DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+		t = t->txd_next ;
+		tx_used-- ;
+	}
+	return(phys) ;
+}
+
+/*
+ * Repairs the receive descriptor ring and returns the physical address
+ * where the BMU should continue working.
+ *
+ *	o The physical address where the BMU was stopped has to be
+ *	  determined. This is the next RxD after rx_curr_get with an OWN
+ *	  bit set.
+ *	o The BMU should start working at beginning of the next frame.
+ *	  RxDs with an OWN bit set but with a reset STF bit should be
+ *	  skipped and owned by the driver (OWN = 0). 
+ */
+static u_long repair_rxd_ring(smc,queue)
+struct s_smc *smc ;
+struct s_smt_rx_queue *queue ;
+{
+	int i ;
+	int rx_used ;
+	u_long phys ;
+	u_long rbctrl ;
+	struct s_smt_fp_rxd volatile *r ;
+
+	SK_UNUSED(smc) ;
+
+	r = queue->rx_curr_get ;
+	rx_used = queue->rx_used ;
+	for (i = SMT_R1_RXD_COUNT-1 ; i ; i-- ) {
+		r = r->rxd_next ;
+	}
+	phys = AIX_REVERSE(r->rxd_nrdadr) ;
+
+	r = queue->rx_curr_get ;
+	while (rx_used) {
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+		rbctrl = AIX_REVERSE(r->rxd_rbctrl) ;
+
+		if (rbctrl & BMU_OWN) {
+			if (rbctrl & BMU_STF) {
+				break ;		/* exit the loop */
+			}
+			else {
+				/*
+				 * repair the descriptor
+				 */
+				r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ;
+			}
+		}
+		phys = AIX_REVERSE(r->rxd_nrdadr) ;
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ;
+		r = r->rxd_next ;
+		rx_used-- ;
+	}
+	return(phys) ;
+}
+
+
+/*
+	-------------------------------------------------------------
+	INTERRUPT SERVICE ROUTINE:
+	-------------------------------------------------------------
+*/
+
+/*
+ *	BEGIN_MANUAL_ENTRY(fddi_isr)
+ *	void fddi_isr(smc)
+ *
+ * function	DOWNCALL	(drvsr.c)
+ *		interrupt service routine, handles the interrupt requests
+ *		generated by the FDDI adapter.
+ *
+ * NOTE:	The operating system dependent module must garantee that the
+ *		interrupts of the adapter are disabled when it calls fddi_isr.
+ *
+ *	About the USE_BREAK_ISR mechanismn:
+ *
+ *	The main requirement of this mechanismn is to force an timer IRQ when
+ *	leaving process_receive() with leave_isr set. process_receive() may
+ *	be called at any time from anywhere!
+ *	To be sure we don't miss such event we set 'force_irq' per default.
+ *	We have to force and Timer IRQ if 'smc->os.hwm.leave_isr' AND
+ *	'force_irq' are set. 'force_irq' may be reset if a receive complete
+ *	IRQ is pending.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void fddi_isr(smc)
+struct s_smc *smc ;
+{
+	u_long		is ;		/* ISR source */
+	u_short		stu, stl ;
+	SMbuf		*mb ;
+
+#ifdef	USE_BREAK_ISR
+	int	force_irq ;
+#endif
+
+#ifdef	ODI2
+	if (smc->os.hwm.rx_break) {
+		mac_drv_fill_rxd(smc) ;
+		if (smc->hw.fp.rx_q[QUEUE_R1].rx_used > 0) {
+			smc->os.hwm.rx_break = 0 ;
+			process_receive(smc) ;
+		}
+		else {
+			smc->os.hwm.detec_count = 0 ;
+			smt_force_irq(smc) ;
+		}
+	}
+#endif
+	smc->os.hwm.isr_flag = TRUE ;
+
+#ifdef	USE_BREAK_ISR
+	force_irq = TRUE ;
+	if (smc->os.hwm.leave_isr) {
+		smc->os.hwm.leave_isr = FALSE ;
+		process_receive(smc) ;
+	}
+#endif
+
+	while ((is = GET_ISR() & ISR_MASK)) {
+		NDD_TRACE("CH0B",is,0,0) ;
+		DB_GEN("ISA = 0x%x",is,0,7) ;
+
+		if (is & IMASK_SLOW) {
+			NDD_TRACE("CH1b",is,0,0) ;
+			if (is & IS_PLINT1) {	/* PLC1 */
+				plc1_irq(smc) ;
+			}
+			if (is & IS_PLINT2) {	/* PLC2 */
+				plc2_irq(smc) ;
+			}
+			if (is & IS_MINTR1) {	/* FORMAC+ STU1(U/L) */
+				stu = inpw(FM_A(FM_ST1U)) ;
+				stl = inpw(FM_A(FM_ST1L)) ;
+				DB_GEN("Slow transmit complete",0,0,6) ;
+				mac1_irq(smc,stu,stl) ;
+			}
+			if (is & IS_MINTR2) {	/* FORMAC+ STU2(U/L) */
+				stu= inpw(FM_A(FM_ST2U)) ;
+				stl= inpw(FM_A(FM_ST2L)) ;
+				DB_GEN("Slow receive complete",0,0,6) ;
+				DB_GEN("stl = %x : stu = %x",stl,stu,7) ;
+				mac2_irq(smc,stu,stl) ;
+			}
+			if (is & IS_MINTR3) {	/* FORMAC+ STU3(U/L) */
+				stu= inpw(FM_A(FM_ST3U)) ;
+				stl= inpw(FM_A(FM_ST3L)) ;
+				DB_GEN("FORMAC Mode Register 3",0,0,6) ;
+				mac3_irq(smc,stu,stl) ;
+			}
+			if (is & IS_TIMINT) {	/* Timer 82C54-2 */
+				timer_irq(smc) ;
+#ifdef	NDIS_OS2
+				force_irq_pending = 0 ;
+#endif
+				/*
+				 * out of RxD detection
+				 */
+				if (++smc->os.hwm.detec_count > 4) {
+					/*
+					 * check out of RxD condition
+					 */
+					 process_receive(smc) ;
+				}
+			}
+			if (is & IS_TOKEN) {	/* Restricted Token Monitor */
+				rtm_irq(smc) ;
+			}
+			if (is & IS_R1_P) {	/* Parity error rx queue 1 */
+				/* clear IRQ */
+				outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_P) ;
+				SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ;
+			}
+			if (is & IS_R1_C) {	/* Encoding error rx queue 1 */
+				/* clear IRQ */
+				outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_C) ;
+				SMT_PANIC(smc,HWM_E0005,HWM_E0005_MSG) ;
+			}
+			if (is & IS_XA_C) {	/* Encoding error async tx q */
+				/* clear IRQ */
+				outpd(ADDR(B5_XA_CSR),CSR_IRQ_CL_C) ;
+				SMT_PANIC(smc,HWM_E0006,HWM_E0006_MSG) ;
+			}
+			if (is & IS_XS_C) {	/* Encoding error sync tx q */
+				/* clear IRQ */
+				outpd(ADDR(B5_XS_CSR),CSR_IRQ_CL_C) ;
+				SMT_PANIC(smc,HWM_E0007,HWM_E0007_MSG) ;
+			}
+		}
+
+		/*
+		 *	Fast Tx complete Async/Sync Queue (BMU service)
+		 */
+		if (is & (IS_XS_F|IS_XA_F)) {
+			DB_GEN("Fast tx complete queue",0,0,6) ;
+			/*
+			 * clear IRQ, Note: no IRQ is lost, because
+			 * 	we always service both queues
+			 */
+			outpd(ADDR(B5_XS_CSR),CSR_IRQ_CL_F) ;
+			outpd(ADDR(B5_XA_CSR),CSR_IRQ_CL_F) ;
+			mac_drv_clear_txd(smc) ;
+			llc_restart_tx(smc) ;
+		}
+
+		/*
+		 *	Fast Rx Complete (BMU service)
+		 */
+		if (is & IS_R1_F) {
+			DB_GEN("Fast receive complete",0,0,6) ;
+			/* clear IRQ */
+#ifndef USE_BREAK_ISR
+			outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_F) ;
+			process_receive(smc) ;
+#else
+			process_receive(smc) ;
+			if (smc->os.hwm.leave_isr) {
+				force_irq = FALSE ;
+			} else {
+				outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_F) ;
+				process_receive(smc) ;
+			}
+#endif
+		}
+
+#ifndef	NDIS_OS2
+		while ((mb = get_llc_rx(smc))) {
+			smt_to_llc(smc,mb) ;
+		}
+#else
+		if (offDepth)
+			post_proc() ;
+
+		while (!offDepth && (mb = get_llc_rx(smc))) {
+			smt_to_llc(smc,mb) ;
+		}
+
+		if (!offDepth && smc->os.hwm.rx_break) {
+			process_receive(smc) ;
+		}
+#endif
+		if (smc->q.ev_get != smc->q.ev_put) {
+			NDD_TRACE("CH2a",0,0,0) ;
+			ev_dispatcher(smc) ;
+		}
+#ifdef	NDIS_OS2
+		post_proc() ;
+		if (offDepth) {		/* leave fddi_isr because */
+			break ;		/* indications not allowed */
+		}
+#endif
+#ifdef	USE_BREAK_ISR
+		if (smc->os.hwm.leave_isr) {
+			break ;		/* leave fddi_isr */
+		}
+#endif
+
+		/* NOTE: when the isr is left, no rx is pending */
+	}	/* end of interrupt source polling loop */
+
+#ifdef	USE_BREAK_ISR
+	if (smc->os.hwm.leave_isr && force_irq) {
+		smt_force_irq(smc) ;
+	}
+#endif
+	smc->os.hwm.isr_flag = FALSE ;
+	NDD_TRACE("CH0E",0,0,0) ;
+}
+
+
+/*
+	-------------------------------------------------------------
+	RECEIVE FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+#ifndef	NDIS_OS2
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_rx_mode)
+ *	void mac_drv_rx_mode(smc,mode)
+ *
+ * function	DOWNCALL	(fplus.c)
+ *		Corresponding to the parameter mode, the operating system
+ *		dependent module can activate several receive modes.
+ *
+ * para	mode	= 1:	RX_ENABLE_ALLMULTI	enable all multicasts
+ *		= 2:	RX_DISABLE_ALLMULTI	disable "enable all multicasts"
+ *		= 3:	RX_ENABLE_PROMISC	enable promiscuous
+ *		= 4:	RX_DISABLE_PROMISC	disable promiscuous
+ *		= 5:	RX_ENABLE_NSA		enable rec. of all NSA frames
+ *			(disabled after 'driver reset' & 'set station address')
+ *		= 6:	RX_DISABLE_NSA		disable rec. of all NSA frames
+ *
+ *		= 21:	RX_ENABLE_PASS_SMT	( see description )
+ *		= 22:	RX_DISABLE_PASS_SMT	(  "	   "	  )
+ *		= 23:	RX_ENABLE_PASS_NSA	(  "	   "	  )
+ *		= 24:	RX_DISABLE_PASS_NSA	(  "	   "	  )
+ *		= 25:	RX_ENABLE_PASS_DB	(  "	   "	  )
+ *		= 26:	RX_DISABLE_PASS_DB	(  "	   "	  )
+ *		= 27:	RX_DISABLE_PASS_ALL	(  "	   "	  )
+ *		= 28:	RX_DISABLE_LLC_PROMISC	(  "	   "	  )
+ *		= 29:	RX_ENABLE_LLC_PROMISC	(  "	   "	  )
+ *
+ *
+ *		RX_ENABLE_PASS_SMT / RX_DISABLE_PASS_SMT
+ *
+ *		If the operating system dependent module activates the
+ *		mode RX_ENABLE_PASS_SMT, the hardware module
+ *		duplicates all SMT frames with the frame control
+ *		FC_SMT_INFO and passes them to the LLC receive channel
+ *		by calling mac_drv_rx_init.
+ *		The SMT Frames which are sent by the local SMT and the NSA
+ *		frames whose A- and C-Indicator is not set are also duplicated
+ *		and passed.
+ *		The receive mode RX_DISABLE_PASS_SMT disables the passing
+ *		of SMT frames.
+ *
+ *		RX_ENABLE_PASS_NSA / RX_DISABLE_PASS_NSA
+ *
+ *		If the operating system dependent module activates the
+ *		mode RX_ENABLE_PASS_NSA, the hardware module
+ *		duplicates all NSA frames with frame control FC_SMT_NSA
+ *		and a set A-Indicator and passed them to the LLC
+ *		receive channel by calling mac_drv_rx_init.
+ *		All NSA Frames which are sent by the local SMT
+ *		are also duplicated and passed.
+ *		The receive mode RX_DISABLE_PASS_NSA disables the passing
+ *		of NSA frames with the A- or C-Indicator set.
+ *
+ * NOTE:	For fear that the hardware module receives NSA frames with
+ *		a reset A-Indicator, the operating system dependent module
+ *		has to call mac_drv_rx_mode with the mode RX_ENABLE_NSA
+ *		before activate the RX_ENABLE_PASS_NSA mode and after every
+ *		'driver reset' and 'set station address'.
+ *
+ *		RX_ENABLE_PASS_DB / RX_DISABLE_PASS_DB
+ *
+ *		If the operating system dependent module activates the
+ *		mode RX_ENABLE_PASS_DB, direct BEACON frames
+ *		(FC_BEACON frame control) are passed to the LLC receive
+ *		channel by mac_drv_rx_init.
+ *		The receive mode RX_DISABLE_PASS_DB disables the passing
+ *		of direct BEACON frames.
+ *
+ *		RX_DISABLE_PASS_ALL
+ *
+ *		Disables all special receives modes. It is equal to
+ *		call mac_drv_set_rx_mode successively with the
+ *		parameters RX_DISABLE_NSA, RX_DISABLE_PASS_SMT,
+ *		RX_DISABLE_PASS_NSA and RX_DISABLE_PASS_DB.
+ *
+ *		RX_ENABLE_LLC_PROMISC
+ *
+ *		(default) all received LLC frames and all SMT/NSA/DBEACON
+ *		frames depending on the attitude of the flags
+ *		PASS_SMT/PASS_NSA/PASS_DBEACON will be delivered to the
+ *		LLC layer
+ *
+ *		RX_DISABLE_LLC_PROMISC
+ *
+ *		all received SMT/NSA/DBEACON frames depending on the
+ *		attitude of the flags PASS_SMT/PASS_NSA/PASS_DBEACON
+ *		will be delivered to the LLC layer.
+ *		all received LLC frames with a directed address, Multicast
+ *		or Broadcast address will be delivered to the LLC
+ *		layer too.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_rx_mode(smc,mode)
+struct s_smc *smc ;
+int mode ;
+{
+	switch(mode) {
+	case RX_ENABLE_PASS_SMT:
+		smc->os.hwm.pass_SMT = TRUE ;
+		break ;
+	case RX_DISABLE_PASS_SMT:
+		smc->os.hwm.pass_SMT = FALSE ;
+		break ;
+	case RX_ENABLE_PASS_NSA:
+		smc->os.hwm.pass_NSA = TRUE ;
+		break ;
+	case RX_DISABLE_PASS_NSA:
+		smc->os.hwm.pass_NSA = FALSE ;
+		break ;
+	case RX_ENABLE_PASS_DB:
+		smc->os.hwm.pass_DB = TRUE ;
+		break ;
+	case RX_DISABLE_PASS_DB:
+		smc->os.hwm.pass_DB = FALSE ;
+		break ;
+	case RX_DISABLE_PASS_ALL:
+		smc->os.hwm.pass_SMT = smc->os.hwm.pass_NSA = FALSE ;
+		smc->os.hwm.pass_DB = FALSE ;
+		smc->os.hwm.pass_llc_promisc = TRUE ;
+		mac_set_rx_mode(smc,RX_DISABLE_NSA) ;
+		break ;
+	case RX_DISABLE_LLC_PROMISC:
+		smc->os.hwm.pass_llc_promisc = FALSE ;
+		break ;
+	case RX_ENABLE_LLC_PROMISC:
+		smc->os.hwm.pass_llc_promisc = TRUE ;
+		break ;
+	case RX_ENABLE_ALLMULTI:
+	case RX_DISABLE_ALLMULTI:
+	case RX_ENABLE_PROMISC:
+	case RX_DISABLE_PROMISC:
+	case RX_ENABLE_NSA:
+	case RX_DISABLE_NSA:
+	default:
+		mac_set_rx_mode(smc,mode) ;
+		break ;
+	}
+}
+#endif	/* ifndef NDIS_OS2 */
+
+/*
+ * process receive queue
+ */
+void process_receive(smc)
+struct s_smc *smc ;
+{
+	int i ;
+	int n ;
+	int frag_count ;		/* number of RxDs of the curr rx buf */
+	int used_frags ;		/* number of RxDs of the curr frame */
+	struct s_smt_rx_queue *queue ;	/* points to the queue ctl struct */
+	struct s_smt_fp_rxd volatile *r ;	/* rxd pointer */
+	struct s_smt_fp_rxd volatile *rxd ;	/* first rxd of rx frame */
+	u_long rbctrl ;			/* receive buffer control word */
+	u_long rfsw ;			/* receive frame status word */
+	u_short rx_used ;
+	u_char far *virt ;
+	char far *data ;
+	SMbuf *mb ;
+	u_char fc ;			/* Frame control */
+	int len ;			/* Frame length */
+
+	smc->os.hwm.detec_count = 0 ;
+	queue = smc->hw.fp.rx[QUEUE_R1] ;
+	NDD_TRACE("RHxB",0,0,0) ;
+	for ( ; ; ) {
+		r = queue->rx_curr_get ;
+		rx_used = queue->rx_used ;
+		frag_count = 0 ;
+
+#ifdef	USE_BREAK_ISR
+		if (smc->os.hwm.leave_isr) {
+			goto rx_end ;
+		}
+#endif
+#ifdef	NDIS_OS2
+		if (offDepth) {
+			smc->os.hwm.rx_break = 1 ;
+			goto rx_end ;
+		}
+		smc->os.hwm.rx_break = 0 ;
+#endif
+#ifdef	ODI2
+		if (smc->os.hwm.rx_break) {
+			goto rx_end ;
+		}
+#endif
+		n = 0 ;
+		do {
+			DB_RX("Check RxD %x for OWN and EOF",(void *)r,0,5) ;
+			DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+			rbctrl = CR_READ(r->rxd_rbctrl) ;
+			rbctrl = AIX_REVERSE(rbctrl) ;
+
+			if (rbctrl & BMU_OWN) {
+				NDD_TRACE("RHxE",r,rfsw,rbctrl) ;
+				DB_RX("End of RxDs",0,0,4) ;
+				goto rx_end ;
+			}
+			/*
+			 * out of RxD detection
+			 */
+			if (!rx_used) {
+				SK_BREAK() ;
+				SMT_PANIC(smc,HWM_E0009,HWM_E0009_MSG) ;
+				/* Either we don't have an RxD or all
+				 * RxDs are filled. Therefore it's allowed
+				 * for to set the STOPPED flag */
+				smc->hw.hw_state = STOPPED ;
+				mac_drv_clear_rx_queue(smc) ;
+				smc->hw.hw_state = STARTED ;
+				mac_drv_fill_rxd(smc) ;
+				smc->os.hwm.detec_count = 0 ;
+				goto rx_end ;
+			}
+			rfsw = AIX_REVERSE(r->rxd_rfsw) ;
+			if ((rbctrl & BMU_STF) != ((rbctrl & BMU_ST_BUF) <<5)) {
+				/*
+				 * The BMU_STF bit is deleted, 1 frame is
+				 * placed into more than 1 rx buffer
+				 *
+				 * skip frame by setting the rx len to 0
+				 *
+				 * if fragment count == 0
+				 *	The missing STF bit belongs to the
+				 *	current frame, search for the
+				 *	EOF bit to complete the frame
+				 * else
+				 *	the fragment belongs to the next frame,
+				 *	exit the loop and process the frame
+				 */
+				SK_BREAK() ;
+				rfsw = 0 ;
+				if (frag_count) {
+					break ;
+				}
+			}
+			n += rbctrl & 0xffff ;
+			r = r->rxd_next ;
+			frag_count++ ;
+			rx_used-- ;
+		} while (!(rbctrl & BMU_EOF)) ;
+		used_frags = frag_count ;
+		DB_RX("EOF set in RxD, used_frags = %d ",used_frags,0,5) ;
+
+		/* may be next 2 DRV_BUF_FLUSH() can be skipped, because */
+		/* BMU_ST_BUF will not be changed by the ASIC */
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+		while (rx_used && !(r->rxd_rbctrl & AIX_REVERSE(BMU_ST_BUF))) {
+			DB_RX("Check STF bit in %x",(void *)r,0,5) ;
+			r = r->rxd_next ;
+			DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+			frag_count++ ;
+			rx_used-- ;
+		}
+		DB_RX("STF bit found",0,0,5) ;
+
+		/*
+		 * The received frame is finished for the process receive
+		 */
+		rxd = queue->rx_curr_get ;
+		queue->rx_curr_get = r ;
+		queue->rx_free += frag_count ;
+		queue->rx_used = rx_used ;
+
+		/*
+		 * ASIC Errata no. 7 (STF - Bit Bug)
+		 */
+		rxd->rxd_rbctrl &= AIX_REVERSE(~BMU_STF) ;
+
+		for (r=rxd, i=frag_count ; i ; r=r->rxd_next, i--){
+			DB_RX("dma_complete for RxD %x",(void *)r,0,5) ;
+			dma_complete(smc,(union s_fp_descr volatile *)r,DMA_WR);
+		}
+		smc->hw.fp.err_stats.err_valid++ ;
+		smc->mib.m[MAC0].fddiMACCopied_Ct++ ;
+
+		/* the length of the data including the FC */
+		len = (rfsw & RD_LENGTH) - 4 ;
+
+		DB_RX("frame length = %d",len,0,4) ;
+		/*
+		 * check the frame_lenght and all error flags
+		 */
+		if (rfsw & (RX_MSRABT|RX_FS_E|RX_FS_CRC|RX_FS_IMPL)){
+			if (rfsw & RD_S_MSRABT) {
+				DB_RX("Frame aborted by the FORMAC",0,0,2) ;
+				smc->hw.fp.err_stats.err_abort++ ;
+			}
+			/*
+			 * check frame status
+			 */
+			if (rfsw & RD_S_SEAC2) {
+				DB_RX("E-Indicator set",0,0,2) ;
+				smc->hw.fp.err_stats.err_e_indicator++ ;
+			}
+			if (rfsw & RD_S_SFRMERR) {
+				DB_RX("CRC error",0,0,2) ;
+				smc->hw.fp.err_stats.err_crc++ ;
+			}
+			if (rfsw & RX_FS_IMPL) {
+				DB_RX("Implementer frame",0,0,2) ;
+				smc->hw.fp.err_stats.err_imp_frame++ ;
+			}
+			goto abort_frame ;
+		}
+		if (len > FDDI_RAW_MTU-4) {
+			DB_RX("Frame to long error",0,0,2) ;
+			smc->hw.fp.err_stats.err_too_long++ ;
+			goto abort_frame ;
+		}
+		/*
+		 * SUPERNET 3 Bug: FORMAC delivers status words
+		 * of aborded frames to the BMU
+		 */
+		if (len <= 4) {
+			DB_RX("Frame length = 0",0,0,2) ;
+			goto abort_frame ;
+		}
+
+		if (len != (n-4)) {
+			DB_RX("BMU: rx len differs: [%d:%d]",len,n,4);
+			smc->os.hwm.rx_len_error++ ;
+			goto abort_frame ;
+		}
+
+		/*
+		 * Check SA == MA
+		 */
+		virt = (u_char far *) rxd->rxd_virt ;
+		DB_RX("FC = %x",*virt,0,2) ;
+		if (virt[12] == MA[5] &&
+		    virt[11] == MA[4] &&
+		    virt[10] == MA[3] &&
+		    virt[9] == MA[2] &&
+		    virt[8] == MA[1] &&
+		    (virt[7] & ~GROUP_ADDR_BIT) == MA[0]) {
+			goto abort_frame ;
+		}
+
+		/*
+		 * test if LLC frame
+		 */
+		if (rfsw & RX_FS_LLC) {
+			/*
+			 * if pass_llc_promisc is disable
+			 *	if DA != Multicast or Broadcast or DA!=MA
+			 *		abort the frame
+			 */
+			if (!smc->os.hwm.pass_llc_promisc) {
+				if(!(virt[1] & GROUP_ADDR_BIT)) {
+					if (virt[6] != MA[5] ||
+					    virt[5] != MA[4] ||
+					    virt[4] != MA[3] ||
+					    virt[3] != MA[2] ||
+					    virt[2] != MA[1] ||
+					    virt[1] != MA[0]) {
+						DB_RX("DA != MA and not multi- or broadcast",0,0,2) ;
+						goto abort_frame ;
+					}
+				}
+			}
+
+			/*
+			 * LLC frame received
+			 */
+			DB_RX("LLC - receive",0,0,4) ;
+			mac_drv_rx_complete(smc,rxd,frag_count,len) ;
+		}
+		else {
+			if (!(mb = smt_get_mbuf(smc))) {
+				smc->hw.fp.err_stats.err_no_buf++ ;
+				DB_RX("No SMbuf; receive terminated",0,0,4) ;
+				goto abort_frame ;
+			}
+			data = smtod(mb,char *) - 1 ;
+
+			/*
+			 * copy the frame into a SMT_MBuf
+			 */
+#ifdef USE_OS_CPY
+			hwm_cpy_rxd2mb(rxd,data,len) ;
+#else
+			for (r=rxd, i=used_frags ; i ; r=r->rxd_next, i--){
+				n = AIX_REVERSE(r->rxd_rbctrl) & RD_LENGTH ;
+				DB_RX("cp SMT frame to mb: len = %d",n,0,6) ;
+				memcpy(data,r->rxd_virt,n) ;
+				data += n ;
+			}
+			data = smtod(mb,char *) - 1 ;
+#endif
+			fc = *(char *)mb->sm_data = *data ;
+			mb->sm_len = len - 1 ;		/* len - fc */
+			data++ ;
+
+			/*
+			 * SMT frame received
+			 */
+			switch(fc) {
+			case FC_SMT_INFO :
+				smc->hw.fp.err_stats.err_smt_frame++ ;
+				DB_RX("SMT frame received ",0,0,5) ;
+
+				if (smc->os.hwm.pass_SMT) {
+					DB_RX("pass SMT frame ",0,0,5) ;
+					mac_drv_rx_complete(smc, rxd,
+						frag_count,len) ;
+				}
+				else {
+					DB_RX("requeue RxD",0,0,5) ;
+					mac_drv_requeue_rxd(smc,rxd,frag_count);
+				}
+
+				smt_received_pack(smc,mb,(int)(rfsw>>25)) ;
+				break ;
+			case FC_SMT_NSA :
+				smc->hw.fp.err_stats.err_smt_frame++ ;
+				DB_RX("SMT frame received ",0,0,5) ;
+
+				/* if pass_NSA set pass the NSA frame or */
+				/* pass_SMT set and the A-Indicator */
+				/* is not set, pass the NSA frame */
+				if (smc->os.hwm.pass_NSA ||
+					(smc->os.hwm.pass_SMT &&
+					!(rfsw & A_INDIC))) {
+					DB_RX("pass SMT frame ",0,0,5) ;
+					mac_drv_rx_complete(smc, rxd,
+						frag_count,len) ;
+				}
+				else {
+					DB_RX("requeue RxD",0,0,5) ;
+					mac_drv_requeue_rxd(smc,rxd,frag_count);
+				}
+
+				smt_received_pack(smc,mb,(int)(rfsw>>25)) ;
+				break ;
+			case FC_BEACON :
+				if (smc->os.hwm.pass_DB) {
+					DB_RX("pass DB frame ",0,0,5) ;
+					mac_drv_rx_complete(smc, rxd,
+						frag_count,len) ;
+				}
+				else {
+					DB_RX("requeue RxD",0,0,5) ;
+					mac_drv_requeue_rxd(smc,rxd,frag_count);
+				}
+				smt_free_mbuf(smc,mb) ;
+				break ;
+			default :
+				/*
+				 * unknown FC abord the frame
+				 */
+				DB_RX("unknown FC error",0,0,2) ;
+				smt_free_mbuf(smc,mb) ;
+				DB_RX("requeue RxD",0,0,5) ;
+				mac_drv_requeue_rxd(smc,rxd,frag_count) ;
+				if ((fc & 0xf0) == FC_MAC)
+					smc->hw.fp.err_stats.err_mac_frame++ ;
+				else
+					smc->hw.fp.err_stats.err_imp_frame++ ;
+
+				break ;
+			}
+		}
+
+		DB_RX("next RxD is %x ",queue->rx_curr_get,0,3) ;
+		NDD_TRACE("RHx1",queue->rx_curr_get,0,0) ;
+
+		continue ;
+	/*--------------------------------------------------------------------*/
+abort_frame:
+		DB_RX("requeue RxD",0,0,5) ;
+		mac_drv_requeue_rxd(smc,rxd,frag_count) ;
+
+		DB_RX("next RxD is %x ",queue->rx_curr_get,0,3) ;
+		NDD_TRACE("RHx2",queue->rx_curr_get,0,0) ;
+	}
+rx_end:
+#ifdef	ALL_RX_COMPLETE
+	mac_drv_all_receives_complete(smc) ;
+#endif
+	return ;	/* lint bug: needs return detect end of function */
+}
+
+static void smt_to_llc(smc,mb)
+struct s_smc *smc ;
+SMbuf *mb ;
+{
+	u_char	fc ;
+
+	DB_RX("send a queued frame to the llc layer",0,0,4) ;
+	smc->os.hwm.r.len = mb->sm_len ;
+	smc->os.hwm.r.mb_pos = smtod(mb,char *) ;
+	fc = *smc->os.hwm.r.mb_pos ;
+	(void)mac_drv_rx_init(smc,(int)mb->sm_len,(int)fc,
+		smc->os.hwm.r.mb_pos,(int)mb->sm_len) ;
+	smt_free_mbuf(smc,mb) ;
+}
+
+/*
+ *	BEGIN_MANUAL_ENTRY(hwm_rx_frag)
+ *	void hwm_rx_frag(smc,virt,phys,len,frame_status)
+ *
+ * function	MACRO		(hardware module, hwmtm.h)
+ *		This function calls dma_master for preparing the
+ *		system hardware for the DMA transfer and initializes
+ *		the current RxD with the length and the physical and
+ *		virtual address of the fragment. Furthermore, it sets the
+ *		STF and EOF bits depending on the frame status byte,
+ *		switches the OWN flag of the RxD, so that it is owned by the
+ *		adapter and issues an rx_start.
+ *
+ * para	virt	virtual pointer to the fragment
+ *	len	the length of the fragment
+ *	frame_status	status of the frame, see design description
+ *
+ * NOTE:	It is possible to call this function with a fragment length
+ *		of zero.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void hwm_rx_frag(smc,virt,phys,len,frame_status)
+struct s_smc *smc ;
+char far *virt ;
+u_long phys ;
+int len ;
+int frame_status ;
+{
+	struct s_smt_fp_rxd volatile *r ;
+	u_int	rbctrl ;
+
+	NDD_TRACE("RHfB",virt,len,frame_status) ;
+	DB_RX("hwm_rx_frag: len = %d, frame_status = %x\n",len,frame_status,2) ;
+	r = smc->hw.fp.rx_q[QUEUE_R1].rx_curr_put ;
+	r->rxd_virt = virt ;
+	r->rxd_rbadr = AIX_REVERSE(phys) ;
+	rbctrl = AIX_REVERSE( (((u_long)frame_status &
+		(FIRST_FRAG|LAST_FRAG))<<26) |
+		(((u_long) frame_status & FIRST_FRAG) << 21) |
+		BMU_OWN | BMU_CHECK | BMU_EN_IRQ_EOF | len) ;
+	r->rxd_rbctrl = rbctrl ;
+
+	DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ;
+	outpd(ADDR(B0_R1_CSR),CSR_START) ;
+	smc->hw.fp.rx_q[QUEUE_R1].rx_free-- ;
+	smc->hw.fp.rx_q[QUEUE_R1].rx_used++ ;
+	smc->hw.fp.rx_q[QUEUE_R1].rx_curr_put = r->rxd_next ;
+	NDD_TRACE("RHfE",r,AIX_REVERSE(r->rxd_rbadr),0) ;
+}
+
+#ifndef	NDIS_OS2
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_rx_frag)
+ *	int mac_drv_rx_frag(smc,virt,len)
+ *
+ * function	DOWNCALL	(hwmtm.c)
+ *		mac_drv_rx_frag fills the fragment with a part of the frame.
+ *
+ * para	virt	the virtual address of the fragment
+ *	len	the length in bytes of the fragment
+ *
+ * return 0:	success code, no errors possible
+ *
+ *	END_MANUAL_ENTRY
+ */
+int mac_drv_rx_frag(smc,virt,len)
+struct s_smc *smc ;
+void far *virt ;
+int len ;
+{
+	NDD_TRACE("RHSB",virt,len,smc->os.hwm.r.mb_pos) ;
+
+	DB_RX("receive from queue: len/virt: = %d/%x",len,virt,4) ;
+	memcpy((char far *)virt,smc->os.hwm.r.mb_pos,len) ;
+	smc->os.hwm.r.mb_pos += len ;
+
+	NDD_TRACE("RHSE",smc->os.hwm.r.mb_pos,0,0) ;
+	return(0) ;
+}
+#endif
+
+
+/*
+ *	BEGINN_MANUAL_ENTRY(mac_drv_clear_rx_queue)
+ *
+ * void mac_drv_clear_rx_queue(smc)
+ * struct s_smc *smc ;
+ *
+ * function	DOWNCALL	(hardware module, hwmtm.c)
+ *		mac_drv_clear_rx_queue is called by the OS-specific module
+ *		after it has issued a card_stop.
+ *		In this case, the frames in the receive queue are obsolete and
+ *		should be removed. For removing mac_drv_clear_rx_queue
+ *		calls dma_master for each RxD and mac_drv_clear_rxd for each
+ *		receive buffer.
+ *
+ * NOTE:	calling sequence card_stop:
+ *		CLI_FBI(), card_stop(),
+ *		mac_drv_clear_tx_queue(), mac_drv_clear_rx_queue(),
+ *
+ * NOTE:	The caller is responsible that the BMUs are idle
+ *		when this function is called.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_clear_rx_queue(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_fp_rxd volatile *r ;
+	struct s_smt_fp_rxd volatile *next_rxd ;
+	struct s_smt_rx_queue *queue ;
+	int frag_count ;
+	int i ;
+
+	if (smc->hw.hw_state != STOPPED) {
+		SK_BREAK() ;
+		SMT_PANIC(smc,HWM_E0012,HWM_E0012_MSG) ;
+		return ;
+	}
+
+	queue = smc->hw.fp.rx[QUEUE_R1] ;
+	DB_RX("clear_rx_queue",0,0,5) ;
+
+	/*
+	 * dma_complete and mac_drv_clear_rxd for all RxDs / receive buffers
+	 */
+	r = queue->rx_curr_get ;
+	while (queue->rx_used) {
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+		DB_RX("switch OWN bit of RxD 0x%x ",r,0,5) ;
+		r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ;
+		frag_count = 1 ;
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ;
+		r = r->rxd_next ;
+		DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+		while (r != queue->rx_curr_put &&
+			!(r->rxd_rbctrl & AIX_REVERSE(BMU_ST_BUF))) {
+			DB_RX("Check STF bit in %x",(void *)r,0,5) ;
+			r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ;
+			DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ;
+			r = r->rxd_next ;
+			DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ;
+			frag_count++ ;
+		}
+		DB_RX("STF bit found",0,0,5) ;
+		next_rxd = r ;
+
+		for (r=queue->rx_curr_get,i=frag_count; i ; r=r->rxd_next,i--){
+			DB_RX("dma_complete for RxD %x",(void *)r,0,5) ;
+			dma_complete(smc,(union s_fp_descr volatile *)r,DMA_WR);
+		}
+
+		DB_RX("mac_drv_clear_rxd: RxD %x frag_count %d ",
+			(void *)queue->rx_curr_get,frag_count,5) ;
+		mac_drv_clear_rxd(smc,queue->rx_curr_get,frag_count) ;
+
+		queue->rx_curr_get = next_rxd ;
+		queue->rx_used -= frag_count ;
+		queue->rx_free += frag_count ;
+	}
+}
+
+
+/*
+	-------------------------------------------------------------
+	SEND FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+/*
+ *	BEGIN_MANUAL_ENTRY(hwm_tx_init)
+ *	int hwm_tx_init(smc,fc,frag_count,frame_len,frame_status)
+ *
+ * function	DOWN_CALL	(hardware module, hwmtm.c)
+ *		hwm_tx_init checks if the frame can be sent through the
+ *		corresponding send queue.
+ *
+ * para	fc	the frame control. To determine through which
+ *		send queue the frame should be transmitted.
+ *		0x50 - 0x57:	asynchronous LLC frame
+ *		0xD0 - 0xD7:	synchronous LLC frame
+ *		0x41, 0x4F:	SMT frame to the network
+ *		0x42:		SMT frame to the network and to the local SMT
+ *		0x43:		SMT frame to the local SMT
+ *	frag_count	count of the fragments for this frame
+ *	frame_len	length of the frame
+ *	frame_status	status of the frame, the send queue bit is already
+ *			specified
+ *
+ * return		frame_status
+ *
+ *	END_MANUAL_ENTRY
+ */
+int hwm_tx_init(smc,fc,frag_count,frame_len,frame_status)
+struct	s_smc *smc ;
+u_char fc ;
+int frag_count ;
+int frame_len ;
+int frame_status ;
+{
+	NDD_TRACE("THiB",fc,frag_count,frame_len) ;
+	smc->os.hwm.tx_p = smc->hw.fp.tx[frame_status & QUEUE_A0] ;
+	smc->os.hwm.tx_descr = TX_DESCRIPTOR | (((u_long)(frame_len-1)&3)<<27) ;
+	smc->os.hwm.tx_len = frame_len ;
+	DB_TX("hwm_tx_init: fc = %x, len = %d",fc,frame_len,3) ;
+	if ((fc & ~(FC_SYNC_BIT|FC_LLC_PRIOR)) == FC_ASYNC_LLC) {
+		frame_status |= LAN_TX ;
+	}
+	else {
+		switch (fc) {
+		case FC_SMT_INFO :
+		case FC_SMT_NSA :
+			frame_status |= LAN_TX ;
+			break ;
+		case FC_SMT_LOC :
+			frame_status |= LOC_TX ;
+			break ;
+		case FC_SMT_LAN_LOC :
+			frame_status |= LAN_TX | LOC_TX ;
+			break ;
+		default :
+			SMT_PANIC(smc,HWM_E0010,HWM_E0010_MSG) ;
+		}
+	}
+	if (!smc->hw.mac_ring_is_up) {
+		frame_status &= ~LAN_TX ;
+		frame_status |= RING_DOWN ;
+		DB_TX("Ring is down: terminate LAN_TX",0,0,2) ;
+	}
+	if (frag_count > smc->os.hwm.tx_p->tx_free) {
+#ifndef	NDIS_OS2
+		mac_drv_clear_txd(smc) ;
+		if (frag_count > smc->os.hwm.tx_p->tx_free) {
+			DB_TX("Out of TxDs, terminate LAN_TX",0,0,2) ;
+			frame_status &= ~LAN_TX ;
+			frame_status |= OUT_OF_TXD ;
+		}
+#else
+		DB_TX("Out of TxDs, terminate LAN_TX",0,0,2) ;
+		frame_status &= ~LAN_TX ;
+		frame_status |= OUT_OF_TXD ;
+#endif
+	}
+	DB_TX("frame_status = %x",frame_status,0,3) ;
+	NDD_TRACE("THiE",frame_status,smc->os.hwm.tx_p->tx_free,0) ;
+	return(frame_status) ;
+}
+
+/*
+ *	BEGIN_MANUAL_ENTRY(hwm_tx_frag)
+ *	void hwm_tx_frag(smc,virt,phys,len,frame_status)
+ *
+ * function	DOWNCALL	(hardware module, hwmtm.c)
+ *		If the frame should be sent to the LAN, this function calls
+ *		dma_master, fills the current TxD with the virtual and the
+ *		physical address, sets the STF and EOF bits dependent on
+ *		the frame status, and requests the BMU to start the
+ *		transmit.
+ *		If the frame should be sent to the local SMT, an SMT_MBuf
+ *		is allocated if the FIRST_FRAG bit is set in the frame_status.
+ *		The fragment of the frame is copied into the SMT MBuf.
+ *		The function smt_received_pack is called if the LAST_FRAG
+ *		bit is set in the frame_status word.
+ *
+ * para	virt	virtual pointer to the fragment
+ *	len	the length of the fragment
+ *	frame_status	status of the frame, see design description
+ *
+ * return	nothing returned, no parameter is modified
+ *
+ * NOTE:	It is possible to invoke this macro with a fragment length
+ *		of zero.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void hwm_tx_frag(smc,virt,phys,len,frame_status)
+struct	s_smc *smc ;
+char far *virt ;
+u_long phys ;
+int len ;
+int frame_status ;
+{
+	struct s_smt_fp_txd volatile *t ;
+	struct s_smt_tx_queue *queue ;
+	u_int	tbctrl ;
+
+	queue = smc->os.hwm.tx_p ;
+
+	NDD_TRACE("THfB",virt,len,frame_status) ;
+	/* Bug fix: AF / May 31 1999 (#missing)
+	 * snmpinfo problem reported by IBM is caused by invalid
+	 * t-pointer (txd) if LAN_TX is not set but LOC_TX only.
+	 * Set: t = queue->tx_curr_put  here !
+	 */
+	t = queue->tx_curr_put ;
+
+	DB_TX("hwm_tx_frag: len = %d, frame_status = %x ",len,frame_status,2) ;
+	if (frame_status & LAN_TX) {
+		/* '*t' is already defined */
+		DB_TX("LAN_TX: TxD = %x, virt = %x ",t,virt,3) ;
+		t->txd_virt = virt ;
+		t->txd_txdscr = AIX_REVERSE(smc->os.hwm.tx_descr) ;
+		t->txd_tbadr = AIX_REVERSE(phys) ;
+		tbctrl = AIX_REVERSE((((u_long)frame_status &
+			(FIRST_FRAG|LAST_FRAG|EN_IRQ_EOF))<< 26) |
+			BMU_OWN|BMU_CHECK |len) ;
+		t->txd_tbctrl = tbctrl ;
+
+#ifndef	AIX
+		DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+		outpd(queue->tx_bmu_ctl,CSR_START) ;
+#else	/* ifndef AIX */
+		DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+		if (frame_status & QUEUE_A0) {
+			outpd(ADDR(B0_XA_CSR),CSR_START) ;
+		}
+		else {
+			outpd(ADDR(B0_XS_CSR),CSR_START) ;
+		}
+#endif
+		queue->tx_free-- ;
+		queue->tx_used++ ;
+		queue->tx_curr_put = t->txd_next ;
+		if (frame_status & LAST_FRAG) {
+			smc->mib.m[MAC0].fddiMACTransmit_Ct++ ;
+		}
+	}
+	if (frame_status & LOC_TX) {
+		DB_TX("LOC_TX: ",0,0,3) ;
+		if (frame_status & FIRST_FRAG) {
+			if(!(smc->os.hwm.tx_mb = smt_get_mbuf(smc))) {
+				smc->hw.fp.err_stats.err_no_buf++ ;
+				DB_TX("No SMbuf; transmit terminated",0,0,4) ;
+			}
+			else {
+				smc->os.hwm.tx_data =
+					smtod(smc->os.hwm.tx_mb,char *) - 1 ;
+#ifdef USE_OS_CPY
+#ifdef PASS_1ST_TXD_2_TX_COMP
+				hwm_cpy_txd2mb(t,smc->os.hwm.tx_data,
+					smc->os.hwm.tx_len) ;
+#endif
+#endif
+			}
+		}
+		if (smc->os.hwm.tx_mb) {
+#ifndef	USE_OS_CPY
+			DB_TX("copy fragment into MBuf ",0,0,3) ;
+			memcpy(smc->os.hwm.tx_data,virt,len) ;
+			smc->os.hwm.tx_data += len ;
+#endif
+			if (frame_status & LAST_FRAG) {
+#ifdef	USE_OS_CPY
+#ifndef PASS_1ST_TXD_2_TX_COMP
+				/*
+				 * hwm_cpy_txd2mb(txd,data,len) copies 'len' 
+				 * bytes from the virtual pointer in 'rxd'
+				 * to 'data'. The virtual pointer of the 
+				 * os-specific tx-buffer should be written
+				 * in the LAST txd.
+				 */ 
+				hwm_cpy_txd2mb(t,smc->os.hwm.tx_data,
+					smc->os.hwm.tx_len) ;
+#endif	/* nPASS_1ST_TXD_2_TX_COMP */
+#endif	/* USE_OS_CPY */
+				smc->os.hwm.tx_data =
+					smtod(smc->os.hwm.tx_mb,char *) - 1 ;
+				*(char *)smc->os.hwm.tx_mb->sm_data =
+					*smc->os.hwm.tx_data ;
+				smc->os.hwm.tx_data++ ;
+				smc->os.hwm.tx_mb->sm_len =
+					smc->os.hwm.tx_len - 1 ;
+				DB_TX("pass LLC frame to SMT ",0,0,3) ;
+				smt_received_pack(smc,smc->os.hwm.tx_mb,
+						RD_FS_LOCAL) ;
+			}
+		}
+	}
+	NDD_TRACE("THfE",t,queue->tx_free,0) ;
+}
+
+
+/*
+ * queues a receive for later send
+ */
+static void queue_llc_rx(smc,mb)
+struct	s_smc *smc ;
+SMbuf	*mb ;
+{
+	DB_GEN("queue_llc_rx: mb = %x",(void *)mb,0,4) ;
+	smc->os.hwm.queued_rx_frames++ ;
+	mb->sm_next = (SMbuf *)NULL ;
+	if (smc->os.hwm.llc_rx_pipe == 0) {
+		smc->os.hwm.llc_rx_pipe = mb ;
+	}
+	else {
+		smc->os.hwm.llc_rx_tail->sm_next = mb ;
+	}
+	smc->os.hwm.llc_rx_tail = mb ;
+
+	/*
+	 * force an timer IRQ to receive the data
+	 */
+	if (!smc->os.hwm.isr_flag) {
+		smt_force_irq(smc) ;
+	}
+}
+
+/*
+ * get a SMbuf from the llc_rx_queue
+ */
+static SMbuf *get_llc_rx(smc)
+struct	s_smc *smc ;
+{
+	SMbuf	*mb ;
+
+	if ((mb = smc->os.hwm.llc_rx_pipe)) {
+		smc->os.hwm.queued_rx_frames-- ;
+		smc->os.hwm.llc_rx_pipe = mb->sm_next ;
+	}
+	DB_GEN("get_llc_rx: mb = 0x%x",(void *)mb,0,4) ;
+	return(mb) ;
+}
+
+/*
+ * queues a transmit SMT MBuf during the time were the MBuf is
+ * queued the TxD ring
+ */
+static void queue_txd_mb(smc,mb)
+struct	s_smc *smc ;
+SMbuf	*mb ;
+{
+	DB_GEN("_rx: queue_txd_mb = %x",(void *)mb,0,4) ;
+	smc->os.hwm.queued_txd_mb++ ;
+	mb->sm_next = (SMbuf *)NULL ;
+	if (smc->os.hwm.txd_tx_pipe == 0) {
+		smc->os.hwm.txd_tx_pipe = mb ;
+	}
+	else {
+		smc->os.hwm.txd_tx_tail->sm_next = mb ;
+	}
+	smc->os.hwm.txd_tx_tail = mb ;
+}
+
+/*
+ * get a SMbuf from the txd_tx_queue
+ */
+static SMbuf *get_txd_mb(smc)
+struct	s_smc *smc ;
+{
+	SMbuf *mb ;
+
+	if ((mb = smc->os.hwm.txd_tx_pipe)) {
+		smc->os.hwm.queued_txd_mb-- ;
+		smc->os.hwm.txd_tx_pipe = mb->sm_next ;
+	}
+	DB_GEN("get_txd_mb: mb = 0x%x",(void *)mb,0,4) ;
+	return(mb) ;
+}
+
+/*
+ *	SMT Send function
+ */
+void smt_send_mbuf(smc,mb,fc)
+struct s_smc	*smc;
+SMbuf		*mb;
+int		fc;
+{
+	char far *data ;
+	int	len ;
+	int	n ;
+	int	i ;
+	int	frag_count ;
+	int	frame_status ;
+	SK_LOC_DECL(char far,*virt[3]) ;
+	int	frag_len[3] ;
+	struct s_smt_tx_queue *queue ;
+	struct s_smt_fp_txd volatile *t ;
+	u_long	phys ;
+	u_int	tbctrl ;
+
+	NDD_TRACE("THSB",mb,fc,0) ;
+	DB_TX("smt_send_mbuf: mb = 0x%x, fc = 0x%x",mb,fc,4) ;
+
+	mb->sm_off-- ;	/* set to fc */
+	mb->sm_len++ ;	/* + fc */
+	data = smtod(mb,char *) ;
+	*data = fc ;
+	if (fc == FC_SMT_LOC)
+		*data = FC_SMT_INFO ;
+
+	/*
+	 * determine the frag count and the virt addresses of the frags
+	 */
+	frag_count = 0 ;
+	len = mb->sm_len ;
+	while (len) {
+		n = SMT_PAGESIZE - ((long)data & (SMT_PAGESIZE-1)) ;
+		if (n >= len) {
+			n = len ;
+		}
+		DB_TX("frag: virt/len = 0x%x/%d ",(void *)data,n,5) ;
+		virt[frag_count] = data ;
+		frag_len[frag_count] = n ;
+		frag_count++ ;
+		len -= n ;
+		data += n ;
+	}
+
+	/*
+	 * determine the frame status
+	 */
+	queue = smc->hw.fp.tx[QUEUE_A0] ;
+	if (fc == FC_BEACON || fc == FC_SMT_LOC) {
+		frame_status = LOC_TX ;
+	}
+	else {
+		frame_status = LAN_TX ;
+		if ((smc->os.hwm.pass_NSA &&(fc == FC_SMT_NSA)) ||
+		   (smc->os.hwm.pass_SMT &&(fc == FC_SMT_INFO)))
+			frame_status |= LOC_TX ;
+	}
+
+	if (!smc->hw.mac_ring_is_up || frag_count > queue->tx_free) {
+		if (frame_status &= ~LAN_TX) {
+			DB_TX("Ring is down: terminate LAN_TX",0,0,2) ;
+		}
+		else {
+			DB_TX("Ring is down: terminate transmission",0,0,2) ;
+			smt_free_mbuf(smc,mb) ;
+			return ;
+		}
+	}
+	DB_TX("frame_status = 0x%x ",frame_status,0,5) ;
+
+	if ((frame_status & LAN_TX) && (frame_status & LOC_TX)) {
+		mb->sm_use_count = 2 ;
+	}
+
+	if (frame_status & LAN_TX) {
+		t = queue->tx_curr_put ;
+		frame_status |= FIRST_FRAG ;
+		for (i = 0; i < frag_count; i++) {
+			DB_TX("init TxD = 0x%x",(void *)t,0,5) ;
+			if (i == frag_count-1) {
+				frame_status |= LAST_FRAG ;
+				t->txd_txdscr = AIX_REVERSE(TX_DESCRIPTOR |
+					(((u_long)(mb->sm_len-1)&3) << 27)) ;
+			}
+			t->txd_virt = virt[i] ;
+			phys = dma_master(smc, (void far *)virt[i],
+				frag_len[i], DMA_RD|SMT_BUF) ;
+			t->txd_tbadr = AIX_REVERSE(phys) ;
+			tbctrl = AIX_REVERSE((((u_long) frame_status &
+				(FIRST_FRAG|LAST_FRAG)) << 26) |
+				BMU_OWN | BMU_CHECK | BMU_SMT_TX |frag_len[i]) ;
+			t->txd_tbctrl = tbctrl ;
+#ifndef	AIX
+			DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+			outpd(queue->tx_bmu_ctl,CSR_START) ;
+#else
+			DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+			outpd(ADDR(B0_XA_CSR),CSR_START) ;
+#endif
+			frame_status &= ~FIRST_FRAG ;
+			queue->tx_curr_put = t = t->txd_next ;
+			queue->tx_free-- ;
+			queue->tx_used++ ;
+		}
+		smc->mib.m[MAC0].fddiMACTransmit_Ct++ ;
+		queue_txd_mb(smc,mb) ;
+	}
+
+	if (frame_status & LOC_TX) {
+		DB_TX("pass Mbuf to LLC queue",0,0,5) ;
+		queue_llc_rx(smc,mb) ;
+	}
+
+	/*
+	 * We need to unqueue the free SMT_MBUFs here, because it may
+	 * be that the SMT want's to send more than 1 frame for one down call
+	 */
+	mac_drv_clear_txd(smc) ;
+	NDD_TRACE("THSE",t,queue->tx_free,frag_count) ;
+}
+
+/*	BEGIN_MANUAL_ENTRY(mac_drv_clear_txd)
+ *	void mac_drv_clear_txd(smc)
+ *
+ * function	DOWNCALL	(hardware module, hwmtm.c)
+ *		mac_drv_clear_txd searches in both send queues for TxD's
+ *		which were finished by the adapter. It calls dma_complete
+ *		for each TxD. If the last fragment of an LLC frame is
+ *		reached, it calls mac_drv_tx_complete to release the
+ *		send buffer.
+ *
+ * return	nothing
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_clear_txd(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_tx_queue *queue ;
+	struct s_smt_fp_txd volatile *t1 ;
+	struct s_smt_fp_txd volatile *t2=0 ;
+	SMbuf *mb ;
+	u_long	tbctrl ;
+	int i ;
+	int frag_count ;
+	int n ;
+
+	NDD_TRACE("THcB",0,0,0) ;
+	for (i = QUEUE_S; i <= QUEUE_A0; i++) {
+		queue = smc->hw.fp.tx[i] ;
+		t1 = queue->tx_curr_get ;
+		DB_TX("clear_txd: QUEUE = %d (0=sync/1=async)",i,0,5) ;
+
+		for ( ; ; ) {
+			frag_count = 0 ;
+
+			do {
+				DRV_BUF_FLUSH(t1,DDI_DMA_SYNC_FORCPU) ;
+				DB_TX("check OWN/EOF bit of TxD 0x%x",t1,0,5) ;
+				tbctrl = CR_READ(t1->txd_tbctrl) ;
+				tbctrl = AIX_REVERSE(tbctrl) ;
+
+				if (tbctrl & BMU_OWN || !queue->tx_used){
+					DB_TX("End of TxDs queue %d",i,0,4) ;
+					goto free_next_queue ;	/* next queue */
+				}
+				t1 = t1->txd_next ;
+				frag_count++ ;
+			} while (!(tbctrl & BMU_EOF)) ;
+
+			t1 = queue->tx_curr_get ;
+			for (n = frag_count; n; n--) {
+				tbctrl = AIX_REVERSE(t1->txd_tbctrl) ;
+				dma_complete(smc,
+					(union s_fp_descr volatile *) t1,
+					(int) (DMA_RD |
+					((tbctrl & BMU_SMT_TX) >> 18))) ;
+				t2 = t1 ;
+				t1 = t1->txd_next ;
+			}
+
+			if (tbctrl & BMU_SMT_TX) {
+				mb = get_txd_mb(smc) ;
+				smt_free_mbuf(smc,mb) ;
+			}
+			else {
+#ifndef PASS_1ST_TXD_2_TX_COMP
+				DB_TX("mac_drv_tx_comp for TxD 0x%x",t2,0,4) ;
+				mac_drv_tx_complete(smc,t2) ;
+#else
+				DB_TX("mac_drv_tx_comp for TxD 0x%x",
+					queue->tx_curr_get,0,4) ;
+				mac_drv_tx_complete(smc,queue->tx_curr_get) ;
+#endif
+			}
+			queue->tx_curr_get = t1 ;
+			queue->tx_free += frag_count ;
+			queue->tx_used -= frag_count ;
+		}
+free_next_queue: ;
+	}
+	NDD_TRACE("THcE",0,0,0) ;
+}
+
+/*
+ *	BEGINN_MANUAL_ENTRY(mac_drv_clear_tx_queue)
+ *
+ * void mac_drv_clear_tx_queue(smc)
+ * struct s_smc *smc ;
+ *
+ * function	DOWNCALL	(hardware module, hwmtm.c)
+ *		mac_drv_clear_tx_queue is called from the SMT when
+ *		the RMT state machine has entered the ISOLATE state.
+ *		This function is also called by the os-specific module
+ *		after it has called the function card_stop().
+ *		In this case, the frames in the send queues are obsolete and
+ *		should be removed.
+ *
+ * note		calling sequence:
+ *		CLI_FBI(), card_stop(),
+ *		mac_drv_clear_tx_queue(), mac_drv_clear_rx_queue(),
+ *
+ * NOTE:	The caller is responsible that the BMUs are idle
+ *		when this function is called.
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_clear_tx_queue(smc)
+struct s_smc *smc ;
+{
+	struct s_smt_fp_txd volatile *t ;
+	struct s_smt_tx_queue *queue ;
+	int tx_used ;
+	int i ;
+
+	if (smc->hw.hw_state != STOPPED) {
+		SK_BREAK() ;
+		SMT_PANIC(smc,HWM_E0011,HWM_E0011_MSG) ;
+		return ;
+	}
+
+	for (i = QUEUE_S; i <= QUEUE_A0; i++) {
+		queue = smc->hw.fp.tx[i] ;
+		DB_TX("clear_tx_queue: QUEUE = %d (0=sync/1=async)",i,0,5) ;
+
+		/*
+		 * switch the OWN bit of all pending frames to the host
+		 */
+		t = queue->tx_curr_get ;
+		tx_used = queue->tx_used ;
+		while (tx_used) {
+			DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORCPU) ;
+			DB_TX("switch OWN bit of TxD 0x%x ",t,0,5) ;
+			t->txd_tbctrl &= AIX_REVERSE(~BMU_OWN) ;
+			DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ;
+			t = t->txd_next ;
+			tx_used-- ;
+		}
+	}
+
+	/*
+	 * release all TxD's for both send queues
+	 */
+	mac_drv_clear_txd(smc) ;
+
+	for (i = QUEUE_S; i <= QUEUE_A0; i++) {
+		queue = smc->hw.fp.tx[i] ;
+		t = queue->tx_curr_get ;
+
+		/*
+		 * write the phys pointer of the NEXT descriptor into the
+		 * BMU's current address descriptor pointer and set
+		 * tx_curr_get and tx_curr_put to this position
+		 */
+		if (i == QUEUE_S) {
+			outpd(ADDR(B5_XS_DA),AIX_REVERSE(t->txd_ntdadr)) ;
+		}
+		else {
+			outpd(ADDR(B5_XA_DA),AIX_REVERSE(t->txd_ntdadr)) ;
+		}
+
+		queue->tx_curr_put = queue->tx_curr_get->txd_next ;
+		queue->tx_curr_get = queue->tx_curr_put ;
+	}
+}
+
+
+/*
+	-------------------------------------------------------------
+	TEST FUNCTIONS:
+	-------------------------------------------------------------
+*/
+
+#ifdef	DEBUG
+/*
+ *	BEGIN_MANUAL_ENTRY(mac_drv_debug_lev)
+ *	void mac_drv_debug_lev(smc,flag,lev)
+ *
+ * function	DOWNCALL	(drvsr.c)
+ *		To get a special debug info the user can assign a debug level
+ *		to any debug flag.
+ *
+ * para	flag	debug flag, possible values are:
+ *			= 0:	reset all debug flags (the defined level is
+ *				ignored)
+ *			= 1:	debug.d_smtf
+ *			= 2:	debug.d_smt
+ *			= 3:	debug.d_ecm
+ *			= 4:	debug.d_rmt
+ *			= 5:	debug.d_cfm
+ *			= 6:	debug.d_pcm
+ *
+ *			= 10:	debug.d_os.hwm_rx (hardware module receive path)
+ *			= 11:	debug.d_os.hwm_tx(hardware module transmit path)
+ *			= 12:	debug.d_os.hwm_gen(hardware module general flag)
+ *
+ *	lev	debug level
+ *
+ *	END_MANUAL_ENTRY
+ */
+void mac_drv_debug_lev(smc,flag,lev)
+struct s_smc *smc ;
+int flag ;
+int lev ;
+{
+	switch(flag) {
+	case (int)NULL:
+		DB_P.d_smtf = DB_P.d_smt = DB_P.d_ecm = DB_P.d_rmt = 0 ;
+		DB_P.d_cfm = 0 ;
+		DB_P.d_os.hwm_rx = DB_P.d_os.hwm_tx = DB_P.d_os.hwm_gen = 0 ;
+#ifdef	SBA
+		DB_P.d_sba = 0 ;
+#endif
+#ifdef	ESS
+		DB_P.d_ess = 0 ;
+#endif
+		break ;
+	case DEBUG_SMTF:
+		DB_P.d_smtf = lev ;
+		break ;
+	case DEBUG_SMT:
+		DB_P.d_smt = lev ;
+		break ;
+	case DEBUG_ECM:
+		DB_P.d_ecm = lev ;
+		break ;
+	case DEBUG_RMT:
+		DB_P.d_rmt = lev ;
+		break ;
+	case DEBUG_CFM:
+		DB_P.d_cfm = lev ;
+		break ;
+	case DEBUG_PCM:
+		DB_P.d_pcm = lev ;
+		break ;
+	case DEBUG_SBA:
+#ifdef	SBA
+		DB_P.d_sba = lev ;
+#endif
+		break ;
+	case DEBUG_ESS:
+#ifdef	ESS
+		DB_P.d_ess = lev ;
+#endif
+		break ;
+	case DB_HWM_RX:
+		DB_P.d_os.hwm_rx = lev ;
+		break ;
+	case DB_HWM_TX:
+		DB_P.d_os.hwm_tx = lev ;
+		break ;
+	case DB_HWM_GEN:
+		DB_P.d_os.hwm_gen = lev ;
+		break ;
+	default:
+		break ;
+	}
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/hwt.c linux.18p17/drivers/net/skfp/hwt.c
--- linux.vanilla/drivers/net/skfp/hwt.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/hwt.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,314 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+ * Timer Driver for FBI board (timer chip 82C54)
+ */
+
+/*
+ * Modifications:
+ *
+ *	28-Jun-1994 sw	Edit v1.6.
+ *			MCA: Added support for the SK-NET FDDI-FM2 adapter. The
+ *			 following functions have been added(+) or modified(*):
+ *			 hwt_start(*), hwt_stop(*), hwt_restart(*), hwt_read(*)
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)hwt.c	1.13 97/04/23 (C) SK " ;
+#endif
+
+/*
+ * Prototypes of local functions.
+ */
+/* 28-Jun-1994 sw - Note: hwt_restart() is also used in module 'drvfbi.c'. */
+/*static*/ void hwt_restart() ;
+
+/************************
+ *
+ *	hwt_start
+ *
+ *	Start hardware timer (clock ticks are 16us).
+ *
+ *	void hwt_start(
+ *		struct s_smc *smc,
+ *		u_long time) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ *
+ *	time - The time in units of 16us to load the timer with.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+#define	HWT_MAX	(65000)
+
+void hwt_start(smc, time)
+struct s_smc *smc ;
+u_long time ;
+{
+	u_short	cnt ;
+
+	if (time > HWT_MAX)
+		time = HWT_MAX ;
+
+	smc->hw.t_start = time ;
+	smc->hw.t_stop = 0L ;
+
+	cnt = (u_short)time ;
+	/*
+	 * if time < 16 us
+	 *	time = 16 us
+	 */
+	if (!cnt)
+		cnt++ ;
+#ifndef	PCI
+	/*
+	 * 6.25MHz -> CLK0 : T0 (cnt0 = 16us)	-> OUT0
+	 *    OUT0 -> CLK1 : T1 (cnt1)	OUT1	-> ISRA(IS_TIMINT)
+	 */
+	OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ;	/* counter 1, mode 0 */
+	OUT_82c54_TIMER(1,cnt & 0xff) ;		/* LSB */
+	OUT_82c54_TIMER(1,(cnt>>8) & 0xff) ;	/* MSB */
+	/*
+	 * start timer by switching counter 0 to mode 3
+	 *	T0 resolution 16 us (CLK0=0.16us)
+	 */
+	OUT_82c54_TIMER(3,0<<6 | 3<<4 | 3<<1) ;	/* counter 0, mode 3 */
+	OUT_82c54_TIMER(0,100) ;		/* LSB */
+	OUT_82c54_TIMER(0,0) ;			/* MSB */
+#else	/* PCI */
+	outpd(ADDR(B2_TI_INI), (u_long) cnt * 200) ;	/* Load timer value. */
+	outpw(ADDR(B2_TI_CRTL), TIM_START) ;		/* Start timer. */
+#endif	/* PCI */
+	smc->hw.timer_activ = TRUE ;
+}
+
+/************************
+ *
+ *	hwt_stop
+ *
+ *	Stop hardware timer.
+ *
+ *	void hwt_stop(
+ *		struct s_smc *smc) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void hwt_stop(smc)
+struct s_smc *smc ;
+{
+#ifndef PCI
+	/* stop counter 0 by switching to mode 0 */
+	OUT_82c54_TIMER(3,0<<6 | 3<<4 | 0<<1) ;	/* counter 0, mode 0 */
+	OUT_82c54_TIMER(0,0) ;			/* LSB */
+	OUT_82c54_TIMER(0,0) ;			/* MSB */
+#else	/* PCI */
+	outpw(ADDR(B2_TI_CRTL), TIM_STOP) ;
+	outpw(ADDR(B2_TI_CRTL), TIM_CL_IRQ) ;
+#endif	/* PCI */
+
+	smc->hw.timer_activ = FALSE ;
+}
+
+/************************
+ *
+ *	hwt_init
+ *
+ *	Initialize hardware timer.
+ *
+ *	void hwt_init(
+ *		struct s_smc *smc) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void hwt_init(smc)
+struct s_smc *smc ;
+{
+	smc->hw.t_start = 0 ;
+	smc->hw.t_stop	= 0 ;
+	smc->hw.timer_activ = FALSE ;
+
+	hwt_restart(smc) ;
+}
+
+/************************
+ *
+ *	hwt_restart
+ *
+ *	Clear timer interrupt.
+ *
+ *	void hwt_restart(
+ *		struct s_smc *smc) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void hwt_restart(smc)
+struct s_smc *smc ;
+{
+	hwt_stop(smc) ;
+#ifndef	PCI
+	OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ;	/* counter 1, mode 0 */
+	OUT_82c54_TIMER(1,1 ) ;			/* LSB */
+	OUT_82c54_TIMER(1,0 ) ;			/* MSB */
+#endif
+}
+
+/************************
+ *
+ *	hwt_read
+ *
+ *	Stop hardware timer and read time elapsed since last start.
+ *
+ *	u_long hwt_read(smc) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ * Out
+ *	The elapsed time since last start in units of 16us.
+ *
+ ************************/
+u_long hwt_read(smc)
+struct s_smc *smc ;
+{
+	u_short	tr ;
+#ifndef	PCI
+	u_short	is ;
+#else
+	u_long	is ;
+#endif
+
+	if (smc->hw.timer_activ) {
+		hwt_stop(smc) ;
+#ifndef	PCI
+		OUT_82c54_TIMER(3,1<<6) ;	/* latch command */
+		tr = IN_82c54_TIMER(1) & 0xff ;
+		tr += (IN_82c54_TIMER(1) & 0xff)<<8 ;
+#else	/* PCI */
+		tr = (u_short)((inpd(ADDR(B2_TI_VAL))/200) & 0xffff) ;
+#endif	/* PCI */
+		is = GET_ISR() ;
+		/* Check if timer expired (or wraparound). */
+		if ((tr > smc->hw.t_start) || (is & IS_TIMINT)) {
+			hwt_restart(smc) ;
+			smc->hw.t_stop = smc->hw.t_start ;
+		}
+		else
+			smc->hw.t_stop = smc->hw.t_start - tr ;
+	}
+	return (smc->hw.t_stop) ;
+}
+
+#ifdef	PCI
+/************************
+ *
+ *	hwt_quick_read
+ *
+ *	Stop hardware timer and read timer value and start the timer again.
+ *
+ *	u_long hwt_read(smc) ;
+ * In
+ *	smc - A pointer to the SMT Context structure.
+ * Out
+ *	current timer value in units of 80ns.
+ *
+ ************************/
+u_long hwt_quick_read(smc)
+struct s_smc *smc ;
+{
+	u_long interval ;
+	u_long time ;
+
+	interval = inpd(ADDR(B2_TI_INI)) ;
+	outpw(ADDR(B2_TI_CRTL), TIM_STOP) ;
+	time = inpd(ADDR(B2_TI_VAL)) ;
+	outpd(ADDR(B2_TI_INI),time) ;
+	outpw(ADDR(B2_TI_CRTL), TIM_START) ;
+	outpd(ADDR(B2_TI_INI),interval) ;
+
+	return(time) ;
+}
+
+/************************
+ *
+ *	hwt_wait_time(smc,start,duration)
+ *
+ *	This function returnes after the amount of time is elapsed
+ *	since the start time.
+ * 
+ * para	start		start time
+ *	duration	time to wait
+ *
+ * NOTE: The fuction will return immediatly, if the timer is not 
+ *	 started
+ ************************/
+void hwt_wait_time(smc,start,duration)
+struct s_smc *smc ;
+u_long	start ;
+long	duration ;
+{
+	long	diff ;
+	long	interval ;
+	int	wrapped ;
+
+	/*
+	 * check if timer is running
+	 */
+	if (smc->hw.timer_activ == FALSE ||
+		hwt_quick_read(smc) == hwt_quick_read(smc)) {
+		return ;
+	}
+
+	interval = inpd(ADDR(B2_TI_INI)) ;
+	if (interval > duration) {
+		do {
+			diff = (long)(start - hwt_quick_read(smc)) ;
+			if (diff < 0) {
+				diff += interval ;
+			}
+		} while (diff <= duration) ;
+	}
+	else {
+		diff = interval ;
+		wrapped = 0 ;
+		do {
+			if (!wrapped) {
+				if (hwt_quick_read(smc) >= start) {
+					diff += interval ;
+					wrapped = 1 ;
+				}
+			}
+			else {
+				if (hwt_quick_read(smc) < start) {
+					wrapped = 0 ;
+				}
+			}
+		} while (diff <= duration) ;
+	}
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/lnkstat.c linux.18p17/drivers/net/skfp/lnkstat.c
--- linux.vanilla/drivers/net/skfp/lnkstat.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/lnkstat.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,209 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	IBM FDDI read error log function
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/lnkstat.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)lnkstat.c	1.8 97/04/11 (C) SK " ;
+#endif
+
+#ifdef	sun
+#define _far
+#endif
+
+#define EL_IS_OK(x,l)	((((int)&(((struct s_error_log *)0)->x)) + \
+			sizeof(er->x)) <= l)
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;11)
+
+	u_long smt_get_error_word(smc)
+	struct s_smc *smc ;
+
+Function	DOWNCALL	(SMT, lnkstat.c)
+		This functions returns the SMT error work for AIX events.
+
+Return	smt_error_word	These bits are supported:
+
+		SMT_ERL_ALC	== 	[PS/PA].fddiPORTLerFlag
+		SMT_ERL_BLC	==	[PB].fddiPORTLerFlag
+		SMT_ERL_NCC	==	fddiMACNotCopiedFlag
+		SMT_ERL_FEC	==	fddiMACFrameErrorFlag
+
+	END_MANUAL_ENTRY()
+ */
+u_long smt_get_error_word(smc)
+struct s_smc *smc ;
+{
+	u_long	st;
+
+	/*
+	 * smt error word low
+	 */
+	st = 0 ;
+	if (smc->s.sas == SMT_SAS) {
+		if (smc->mib.p[PS].fddiPORTLerFlag)
+			st |= SMT_ERL_ALC ;
+	}
+	else {
+		if (smc->mib.p[PA].fddiPORTLerFlag)
+			st |= SMT_ERL_ALC ;
+		if (smc->mib.p[PB].fddiPORTLerFlag)
+			st |= SMT_ERL_BLC ;
+	}
+	if (smc->mib.m[MAC0].fddiMACNotCopiedFlag)
+		st |= SMT_ERL_NCC ;		/* not copied condition */
+	if (smc->mib.m[MAC0].fddiMACFrameErrorFlag)
+		st |= SMT_ERL_FEC ;		/* frame error condition */
+
+	return st;
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;11)
+
+	u_long smt_get_event_word(smc)
+	struct s_smc *smc ;
+
+Function	DOWNCALL	(SMT, lnkstat.c)
+		This functions returns the SMT event work for AIX events.
+
+Return	smt_event_word	always 0
+
+	END_MANUAL_ENTRY()
+ */
+u_long smt_get_event_word(smc)
+struct s_smc *smc ;
+{
+	return (u_long) 0;
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;11)
+
+	u_long smt_get_port_event_word(smc)
+	struct s_smc *smc ;
+
+Function	DOWNCALL	(SMT, lnkstat.c)
+		This functions returns the SMT port event work for AIX events.
+
+Return	smt_port_event_word	always 0
+
+	END_MANUAL_ENTRY()
+ */
+u_long smt_get_port_event_word(smc)
+struct s_smc *smc ;
+{
+	return (u_long) 0;
+}
+
+/*
+	BEGIN_MANUAL_ENTRY(if,func;others;11)
+
+	u_long smt_read_errorlog(smc,p,len)
+	struct s_smc *smc ;
+	char _far *p ;
+	int len ;
+
+Function	DOWNCALL	(SMT, lnkstat.c)
+		This functions returns the SMT error log field for AIX events.
+
+Para	p	pointer to the error log field
+	len	len of the error log field
+
+Return	len	used len of the error log field
+
+	END_MANUAL_ENTRY()
+ */
+int smt_read_errorlog(smc,p,len)
+struct s_smc *smc ;
+char _far *p ;
+int len ;
+{
+	int			i ;
+	int			st ;
+	struct s_error_log _far	*er ;
+
+	er = (struct s_error_log _far *) p ;
+	if (len > sizeof(struct s_error_log))
+		len = sizeof(struct s_error_log) ;
+	for (i = 0 ; i < len ; i++)
+		*p++ = 0 ;
+	/*
+	 * set count
+	 */
+	if (EL_IS_OK(set_count_high,len)) {
+		er->set_count_low = (u_short)smc->mib.fddiSMTSetCount.count ;
+		er->set_count_high =
+			(u_short)(smc->mib.fddiSMTSetCount.count >> 16L) ;
+	}
+	/*
+	 * aci
+	 */
+	if (EL_IS_OK(aci_id_code,len)) {
+		er->aci_id_code = 0 ;
+	}
+	/*
+	 * purge counter is missed frames; 16 bits only
+	 */
+	if (EL_IS_OK(purge_frame_counter,len)) {
+		if (smc->mib.m[MAC0].fddiMACCopied_Ct > 0xffff)
+			er->purge_frame_counter = 0xffff ;
+		else
+			er->purge_frame_counter =
+				(u_short)smc->mib.m[MAC0].fddiMACCopied_Ct ;
+	}
+	/*
+	 * CMT and RMT state machines
+	 */
+	if (EL_IS_OK(ecm_state,len))
+		er->ecm_state = smc->mib.fddiSMTECMState ;
+
+	if (EL_IS_OK(pcm_b_state,len)) {
+		if (smc->s.sas == SMT_SAS) {
+			er->pcm_a_state = smc->y[PS].mib->fddiPORTPCMState ;
+			er->pcm_b_state = 0 ;
+		}
+		else {
+			er->pcm_a_state = smc->y[PA].mib->fddiPORTPCMState ;
+			er->pcm_b_state = smc->y[PB].mib->fddiPORTPCMState ;
+		}
+	}
+	if (EL_IS_OK(cfm_state,len))
+		er->cfm_state = smc->mib.fddiSMTCF_State ;
+	if (EL_IS_OK(rmt_state,len))
+		er->rmt_state = smc->mib.m[MAC0].fddiMACRMTState ;
+
+	/*
+	 * smt error word low (we only need the low order 16 bits.)
+	 */
+
+	st = smt_get_error_word(smc) & 0xffff ;
+
+	if (EL_IS_OK(smt_error_low,len))
+		er->smt_error_low = st ;
+
+	if (EL_IS_OK(ucode_version_level,len))
+		er->ucode_version_level = 0x0101 ;
+	return(len) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/pcmplc.c linux.18p17/drivers/net/skfp/pcmplc.c
--- linux.vanilla/drivers/net/skfp/pcmplc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/pcmplc.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,2094 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	PCM
+	Physical Connection Management
+*/
+
+/*
+ * Hardware independant state machine implemantation
+ * The following external SMT functions are referenced :
+ *
+ * 		queue_event()
+ * 		smt_timer_start()
+ * 		smt_timer_stop()
+ *
+ * 	The following external HW dependant functions are referenced :
+ * 		sm_pm_control()
+ *		sm_ph_linestate()
+ *		sm_pm_ls_latch()
+ *
+ * 	The following HW dependant events are required :
+ *		PC_QLS
+ *		PC_ILS
+ *		PC_HLS
+ *		PC_MLS
+ *		PC_NSE
+ *		PC_LEM
+ *
+ */
+
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/supern_2.h"
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)pcmplc.c	2.55 99/08/05 (C) SK " ;
+#endif
+
+#ifdef	FDDI_MIB
+extern int snmp_fddi_trap(
+#ifdef	ANSIC
+struct s_smc	* smc, int  type, int  index
+#endif
+);
+#endif
+#ifdef	CONCENTRATOR
+extern int plc_is_installed(
+#ifdef	ANSIC
+struct s_smc *smc ,
+int p
+#endif
+) ;
+#endif
+/*
+ * FSM Macros
+ */
+#define AFLAG		(0x20)
+#define GO_STATE(x)	(mib->fddiPORTPCMState = (x)|AFLAG)
+#define ACTIONS_DONE()	(mib->fddiPORTPCMState &= ~AFLAG)
+#define ACTIONS(x)	(x|AFLAG)
+
+/*
+ * PCM states
+ */
+#define PC0_OFF			0
+#define PC1_BREAK		1
+#define PC2_TRACE		2
+#define PC3_CONNECT		3
+#define PC4_NEXT		4
+#define PC5_SIGNAL		5
+#define PC6_JOIN		6
+#define PC7_VERIFY		7
+#define PC8_ACTIVE		8
+#define PC9_MAINT		9
+
+#ifdef	DEBUG
+/*
+ * symbolic state names
+ */
+static const char * const pcm_states[] =  {
+	"PC0_OFF","PC1_BREAK","PC2_TRACE","PC3_CONNECT","PC4_NEXT",
+	"PC5_SIGNAL","PC6_JOIN","PC7_VERIFY","PC8_ACTIVE","PC9_MAINT"
+} ;
+
+/*
+ * symbolic event names
+ */
+static const char * const pcm_events[] = {
+	"NONE","PC_START","PC_STOP","PC_LOOP","PC_JOIN","PC_SIGNAL",
+	"PC_REJECT","PC_MAINT","PC_TRACE","PC_PDR",
+	"PC_ENABLE","PC_DISABLE",
+	"PC_QLS","PC_ILS","PC_MLS","PC_HLS","PC_LS_PDR","PC_LS_NONE",
+	"PC_TIMEOUT_TB_MAX","PC_TIMEOUT_TB_MIN",
+	"PC_TIMEOUT_C_MIN","PC_TIMEOUT_T_OUT",
+	"PC_TIMEOUT_TL_MIN","PC_TIMEOUT_T_NEXT","PC_TIMEOUT_LCT",
+	"PC_NSE","PC_LEM"
+} ;
+#endif
+
+#ifdef	MOT_ELM
+/*
+ * PCL-S control register
+ * this register in the PLC-S controls the scrambling parameters
+ */
+#define PLCS_CONTROL_C_U	0
+#define PLCS_CONTROL_C_S	(PL_C_SDOFF_ENABLE | PL_C_SDON_ENABLE | \
+				 PL_C_CIPHER_ENABLE)
+#define	PLCS_FASSERT_U		0
+#define	PLCS_FASSERT_S		0xFd76	/* 52.0 us */
+#define	PLCS_FDEASSERT_U	0
+#define	PLCS_FDEASSERT_S	0
+#else	/* nMOT_ELM */
+/*
+ * PCL-S control register
+ * this register in the PLC-S controls the scrambling parameters
+ * can be patched for ANSI compliance if standard changes
+ */
+static const u_char plcs_control_c_u[17] = "PLC_CNTRL_C_U=\0\0" ;
+static const u_char plcs_control_c_s[17] = "PLC_CNTRL_C_S=\01\02" ;
+
+#define PLCS_CONTROL_C_U (plcs_control_c_u[14] | (plcs_control_c_u[15]<<8))
+#define PLCS_CONTROL_C_S (plcs_control_c_s[14] | (plcs_control_c_s[15]<<8))
+#endif	/* nMOT_ELM */
+
+/*
+ * external vars
+ */
+/* struct definition see 'cmtdef.h' (also used by CFM) */
+
+#define PS_OFF		0
+#define PS_BIT3		1
+#define PS_BIT4		2
+#define PS_BIT7		3
+#define PS_LCT		4
+#define PS_BIT8		5
+#define PS_JOIN		6
+#define PS_ACTIVE	7
+
+#define LCT_LEM_MAX	255
+
+/*
+ * PLC timing parameter
+ */
+
+#define PLC_MS(m)	((int)((0x10000L-(m*100000L/2048))))
+#define SLOW_TL_MIN	PLC_MS(6)
+#define SLOW_C_MIN	PLC_MS(10)
+
+static	const struct plt {
+	int	timer ;			/* relative plc timer address */
+	int	para ;			/* default timing parameters */
+} pltm[] = {
+	{ PL_C_MIN, SLOW_C_MIN },	/* min t. to remain Connect State */
+	{ PL_TL_MIN, SLOW_TL_MIN },	/* min t. to transmit a Line State */
+	{ PL_TB_MIN, TP_TB_MIN },	/* min break time */
+	{ PL_T_OUT, TP_T_OUT },		/* Signaling timeout */
+	{ PL_LC_LENGTH, TP_LC_LENGTH },	/* Link Confidence Test Time */
+	{ PL_T_SCRUB, TP_T_SCRUB },	/* Scrub Time == MAC TVX time ! */
+	{ PL_NS_MAX, TP_NS_MAX },	/* max t. that noise is tolerated */
+	{ 0,0 }
+} ;
+
+/*
+ * interrupt mask
+ */
+#ifdef	SUPERNET_3
+/*
+ * Do we need the EBUF error during signaling, too, to detect SUPERNET_3
+ * PLL bug?
+ */
+static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
+			PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
+#else	/* SUPERNET_3 */
+/*
+ * We do NOT need the elasticity buffer error during signaling.
+ */
+static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
+			PL_PCM_ENABLED | PL_SELF_TEST ;
+#endif	/* SUPERNET_3 */
+static int plc_imsk_act = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
+			PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
+
+/* external functions */
+void	all_selection_criteria ();
+
+/* internal functions */
+static void pcm_fsm() ;
+static void pc_rcode_actions() ;
+static void pc_tcode_actions() ;
+static void reset_lem_struct() ;
+static void plc_init() ;
+static void sm_ph_lem_start() ;
+static void sm_ph_lem_stop() ;
+static void sm_ph_linestate() ;
+static void real_init_plc() ;
+
+/*
+ * SMT timer interface
+ *      start PCM timer 0
+ */
+static void start_pcm_timer0(smc,value,event,phy)
+struct s_smc *smc ;
+u_long value;
+int event;
+struct s_phy *phy;
+{
+	phy->timer0_exp = FALSE ;       /* clear timer event flag */
+	smt_timer_start(smc,&phy->pcm_timer0,value,
+		EV_TOKEN(EVENT_PCM+phy->np,event)) ;
+}
+/*
+ * SMT timer interface
+ *      stop PCM timer 0
+ */
+static void stop_pcm_timer0(smc,phy)
+struct s_smc *smc ;
+struct s_phy *phy;
+{
+	if (phy->pcm_timer0.tm_active)
+		smt_timer_stop(smc,&phy->pcm_timer0) ;
+}
+
+/*
+	init PCM state machine (called by driver)
+	clear all PCM vars and flags
+*/
+void pcm_init(smc)
+struct s_smc *smc ;
+{
+	int		i ;
+	int		np ;
+	struct s_phy	*phy ;
+	struct fddi_mib_p	*mib ;
+
+	for (np = 0,phy = smc->y ; np < NUMPHYS ; np++,phy++) {
+		/* Indicates the type of PHY being used */
+		mib = phy->mib ;
+		mib->fddiPORTPCMState = ACTIONS(PC0_OFF) ;
+		phy->np = np ;
+		switch (smc->s.sas) {
+#ifdef	CONCENTRATOR
+		case SMT_SAS :
+			mib->fddiPORTMy_Type = (np == PS) ? TS : TM ;
+			break ;
+		case SMT_DAS :
+			mib->fddiPORTMy_Type = (np == PA) ? TA :
+					(np == PB) ? TB : TM ;
+			break ;
+		case SMT_NAC :
+			mib->fddiPORTMy_Type = TM ;
+			break;
+#else
+		case SMT_SAS :
+			mib->fddiPORTMy_Type = (np == PS) ? TS : TNONE ;
+			mib->fddiPORTHardwarePresent = (np == PS) ? TRUE :
+					FALSE ;
+#ifndef	SUPERNET_3
+			smc->y[PA].mib->fddiPORTPCMState = PC0_OFF ;
+#else
+			smc->y[PB].mib->fddiPORTPCMState = PC0_OFF ;
+#endif
+			break ;
+		case SMT_DAS :
+			mib->fddiPORTMy_Type = (np == PB) ? TB : TA ;
+			break ;
+#endif
+		}
+		/*
+		 * set PMD-type
+		 */
+		phy->pmd_scramble = 0 ;
+		switch (phy->pmd_type[PMD_SK_PMD]) {
+		case 'P' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ;
+			break ;
+		case 'L' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_LCF ;
+			break ;
+		case 'D' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
+			break ;
+		case 'S' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
+			phy->pmd_scramble = TRUE ;
+			break ;
+		case 'U' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
+			phy->pmd_scramble = TRUE ;
+			break ;
+		case '1' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
+			break ;
+		case '2' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
+			break ;
+		case '3' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
+			break ;
+		case '4' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
+			break ;
+		case 'H' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
+			break ;
+		case 'I' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
+			break ;
+		case 'G' :
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
+			break ;
+		default:
+			mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
+			break ;
+		}
+		/*
+		 * A and B port can be on primary and secondary path
+		 */
+		switch (mib->fddiPORTMy_Type) {
+		case TA :
+			mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
+			mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
+			mib->fddiPORTRequestedPaths[2] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_CON_ALTER |
+				MIB_P_PATH_SEC_PREFER ;
+			mib->fddiPORTRequestedPaths[3] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_CON_ALTER |
+				MIB_P_PATH_SEC_PREFER |
+				MIB_P_PATH_THRU ;
+			break ;
+		case TB :
+			mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
+			mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
+			mib->fddiPORTRequestedPaths[2] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_PRIM_PREFER ;
+			mib->fddiPORTRequestedPaths[3] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_PRIM_PREFER |
+				MIB_P_PATH_CON_PREFER |
+				MIB_P_PATH_THRU ;
+			break ;
+		case TS :
+			mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
+			mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
+			mib->fddiPORTRequestedPaths[2] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_CON_ALTER |
+				MIB_P_PATH_PRIM_PREFER ;
+			mib->fddiPORTRequestedPaths[3] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_CON_ALTER |
+				MIB_P_PATH_PRIM_PREFER ;
+			break ;
+		case TM :
+			mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
+			mib->fddiPORTRequestedPaths[2] =
+				MIB_P_PATH_LOCAL |
+				MIB_P_PATH_SEC_ALTER |
+				MIB_P_PATH_PRIM_ALTER ;
+			mib->fddiPORTRequestedPaths[3] = 0 ;
+			break ;
+		}
+
+		phy->pc_lem_fail = FALSE ;
+		mib->fddiPORTPCMStateX = mib->fddiPORTPCMState ;
+		mib->fddiPORTLCTFail_Ct = 0 ;
+		mib->fddiPORTBS_Flag = 0 ;
+		mib->fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
+		mib->fddiPORTNeighborType = TNONE ;
+		phy->ls_flag = 0 ;
+		phy->rc_flag = 0 ;
+		phy->tc_flag = 0 ;
+		phy->td_flag = 0 ;
+		if (np >= PM)
+			phy->phy_name = '0' + np - PM ;
+		else
+			phy->phy_name = 'A' + np ;
+		phy->wc_flag = FALSE ;		/* set by SMT */
+		memset((char *)&phy->lem,0,sizeof(struct lem_counter)) ;
+		reset_lem_struct(phy) ;
+		memset((char *)&phy->plc,0,sizeof(struct s_plc)) ;
+		phy->plc.p_state = PS_OFF ;
+		for (i = 0 ; i < NUMBITS ; i++) {
+			phy->t_next[i] = 0 ;
+		}
+	}
+	real_init_plc(smc) ;
+}
+
+void init_plc(smc)
+struct s_smc *smc ;
+{
+	SK_UNUSED(smc) ;
+
+	/*
+	 * dummy
+	 * this is an obsolete public entry point that has to remain
+	 * for compat. It is used by various drivers.
+	 * the work is now done in real_init_plc()
+	 * which is called from pcm_init() ;
+	 */
+}
+
+static void real_init_plc(smc)
+struct s_smc *smc ;
+{
+	int	p ;
+
+	for (p = 0 ; p < NUMPHYS ; p++)
+		plc_init(smc,p) ;
+}
+
+static void plc_init(smc,p)
+struct s_smc *smc ;
+int p;
+{
+	int	i ;
+#ifndef	MOT_ELM
+	int	rev ;	/* Revision of PLC-x */
+#endif	/* MOT_ELM */
+
+	/* transit PCM state machine to MAINT state */
+	outpw(PLC(p,PL_CNTRL_B),0) ;
+	outpw(PLC(p,PL_CNTRL_B),PL_PCM_STOP) ;
+	outpw(PLC(p,PL_CNTRL_A),0) ;
+
+	/*
+	 * if PLC-S then set control register C
+	 */
+#ifndef	MOT_ELM
+	rev = inpw(PLC(p,PL_STATUS_A)) & PLC_REV_MASK ;
+	if (rev != PLC_REVISION_A)
+#endif	/* MOT_ELM */
+	{
+		if (smc->y[p].pmd_scramble) {
+			outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_S) ;
+#ifdef	MOT_ELM
+			outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_S) ;
+			outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_S) ;
+#endif	/* MOT_ELM */
+		}
+		else {
+			outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_U) ;
+#ifdef	MOT_ELM
+			outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_U) ;
+			outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_U) ;
+#endif	/* MOT_ELM */
+		}
+	}
+
+	/*
+	 * set timer register
+	 */
+	for ( i = 0 ; pltm[i].timer; i++)	/* set timer parameter reg */
+		outpw(PLC(p,pltm[i].timer),pltm[i].para) ;
+
+	(void)inpw(PLC(p,PL_INTR_EVENT)) ;	/* clear interrupt event reg */
+	plc_clear_irq(smc,p) ;
+	outpw(PLC(p,PL_INTR_MASK),plc_imsk_na); /* enable non active irq's */
+
+	/*
+	 * if PCM is configured for class s, it will NOT go to the
+	 * REMOVE state if offline (page 3-36;)
+	 * in the concentrator, all inactive PHYS always must be in
+	 * the remove state
+	 * there's no real need to use this feature at all ..
+	 */
+#ifndef	CONCENTRATOR
+	if ((smc->s.sas == SMT_SAS) && (p == PS)) {
+		outpw(PLC(p,PL_CNTRL_B),PL_CLASS_S) ;
+	}
+#endif
+}
+
+/*
+ * control PCM state machine
+ */
+static void plc_go_state(smc,p,state)
+struct s_smc *smc ;
+int p;
+int state;
+{
+	HW_PTR port ;
+	int val ;
+
+	SK_UNUSED(smc) ;
+
+	port = (HW_PTR) (PLC(p,PL_CNTRL_B)) ;
+	val = inpw(port) & ~(PL_PCM_CNTRL | PL_MAINT) ;
+	outpw(port,val) ;
+	outpw(port,val | state) ;
+}
+
+/*
+ * read current line state (called by ECM & PCM)
+ */
+int sm_pm_get_ls(smc,phy)
+struct s_smc *smc ;
+int phy;
+{
+	int	state ;
+
+#ifdef	CONCENTRATOR
+	if (!plc_is_installed(smc,phy))
+		return(PC_QLS) ;
+#endif
+
+	state = inpw(PLC(phy,PL_STATUS_A)) & PL_LINE_ST ;
+	switch(state) {
+	case PL_L_QLS:
+		state = PC_QLS ;
+		break ;
+	case PL_L_MLS:
+		state = PC_MLS ;
+		break ;
+	case PL_L_HLS:
+		state = PC_HLS ;
+		break ;
+	case PL_L_ILS4:
+	case PL_L_ILS16:
+		state = PC_ILS ;
+		break ;
+	case PL_L_ALS:
+		state = PC_LS_PDR ;
+		break ;
+	default :
+		state = PC_LS_NONE ;
+	}
+	return(state) ;
+}
+
+static int plc_send_bits(smc,phy,len)
+struct s_smc *smc ;
+struct s_phy *phy;
+int len;
+{
+	int np = phy->np ;		/* PHY index */
+	int	n ;
+	int	i ;
+
+	SK_UNUSED(smc) ;
+
+	/* create bit vector */
+	for (i = len-1,n = 0 ; i >= 0 ; i--) {
+		n = (n<<1) | phy->t_val[phy->bitn+i] ;
+	}
+	if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
+#if	0
+		printf("PL_PCM_SIGNAL is set\n") ;
+#endif
+		return(1) ;
+	}
+	/* write bit[n] & length = 1 to regs */
+	outpw(PLC(np,PL_VECTOR_LEN),len-1) ;	/* len=nr-1 */
+	outpw(PLC(np,PL_XMIT_VECTOR),n) ;
+#ifdef	DEBUG
+#if 1
+#ifdef	DEBUG_BRD
+	if (smc->debug.d_plc & 0x80)
+#else
+	if (debug.d_plc & 0x80)
+#endif
+		printf("SIGNALING bit %d .. %d\n",phy->bitn,phy->bitn+len-1) ;
+#endif
+#endif
+	return(0) ;
+}
+
+/*
+ * config plc muxes
+ */
+void plc_config_mux(smc,mux)
+struct s_smc *smc ;
+int mux ;
+{
+	if (smc->s.sas != SMT_DAS)
+		return ;
+	if (mux == MUX_WRAPB) {
+		SETMASK(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
+		SETMASK(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
+	}
+	else {
+		CLEAR(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
+		CLEAR(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP) ;
+	}
+	CLEAR(PLC(PB,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
+	CLEAR(PLC(PB,PL_CNTRL_A),PL_SC_REM_LOOP) ;
+}
+
+/*
+	PCM state machine
+	called by dispatcher  & fddi_init() (driver)
+	do
+		display state change
+		process event
+	until SM is stable
+*/
+void pcm(smc,np,event)
+struct s_smc *smc ;
+const int np;
+int event;
+{
+	int	state ;
+	int	oldstate ;
+	struct s_phy	*phy ;
+	struct fddi_mib_p	*mib ;
+
+#ifndef	CONCENTRATOR
+	/*
+	 * ignore 2nd PHY if SAS
+	 */
+	if ((np != PS) && (smc->s.sas == SMT_SAS))
+		return ;
+#endif
+	phy = &smc->y[np] ;
+	mib = phy->mib ;
+	oldstate = mib->fddiPORTPCMState ;
+	do {
+		DB_PCM("PCM %c: state %s",
+			phy->phy_name,
+			(mib->fddiPORTPCMState & AFLAG) ? "ACTIONS " : "") ;
+		DB_PCM("%s, event %s\n",
+			pcm_states[mib->fddiPORTPCMState & ~AFLAG],
+			pcm_events[event]) ;
+		state = mib->fddiPORTPCMState ;
+		pcm_fsm(smc,phy,event) ;
+		event = 0 ;
+	} while (state != mib->fddiPORTPCMState) ;
+	/*
+	 * because the PLC does the bit signaling for us,
+	 * we're always in SIGNAL state
+	 * the MIB want's to see CONNECT
+	 * we therefore fake an entry in the MIB
+	 */
+	if (state == PC5_SIGNAL)
+		mib->fddiPORTPCMStateX = PC3_CONNECT ;
+	else
+		mib->fddiPORTPCMStateX = state ;
+
+#ifndef	SLIM_SMT
+	/*
+	 * path change
+	 */
+	if (	mib->fddiPORTPCMState != oldstate &&
+		((oldstate == PC8_ACTIVE) || (mib->fddiPORTPCMState == PC8_ACTIVE))) {
+		smt_srf_event(smc,SMT_EVENT_PORT_PATH_CHANGE,
+			(int) (INDEX_PORT+ phy->np),0) ;
+	}
+#endif
+
+#ifdef FDDI_MIB
+	/* check whether a snmp-trap has to be sent */
+
+	if ( mib->fddiPORTPCMState != oldstate ) {
+		/* a real state change took place */
+		DB_SNMP ("PCM from %d to %d\n", oldstate, mib->fddiPORTPCMState);
+		if ( mib->fddiPORTPCMState == PC0_OFF ) {
+			/* send first trap */
+			snmp_fddi_trap (smc, 1, (int) mib->fddiPORTIndex );
+		} else if ( oldstate == PC0_OFF ) {
+			/* send second trap */
+			snmp_fddi_trap (smc, 2, (int) mib->fddiPORTIndex );
+		} else if ( mib->fddiPORTPCMState != PC2_TRACE &&
+			oldstate == PC8_ACTIVE ) {
+			/* send third trap */
+			snmp_fddi_trap (smc, 3, (int) mib->fddiPORTIndex );
+		} else if ( mib->fddiPORTPCMState == PC8_ACTIVE ) {
+			/* send fourth trap */
+			snmp_fddi_trap (smc, 4, (int) mib->fddiPORTIndex );
+		}
+	}
+#endif
+
+	pcm_state_change(smc,np,state) ;
+}
+
+/*
+ * PCM state machine
+ */
+static void pcm_fsm(smc,phy,cmd)
+struct s_smc *smc ;
+struct s_phy *phy;
+int cmd;
+{
+	int	i ;
+	int	np = phy->np ;		/* PHY index */
+	struct s_plc	*plc ;
+	struct fddi_mib_p	*mib ;
+#ifndef	MOT_ELM
+	u_short	plc_rev ;		/* Revision of the plc */
+#endif	/* nMOT_ELM */
+
+	plc = &phy->plc ;
+	mib = phy->mib ;
+
+	/*
+	 * general transitions independant of state
+	 */
+	switch (cmd) {
+	case PC_STOP :
+		/*PC00-PC80*/
+		if (mib->fddiPORTPCMState != PC9_MAINT) {
+			GO_STATE(PC0_OFF) ;
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+				FDDI_PORT_EVENT, (u_long) FDDI_PORT_STOP,
+				smt_get_port_event_word(smc));
+		}
+		return ;
+	case PC_START :
+		/*PC01-PC81*/
+		if (mib->fddiPORTPCMState != PC9_MAINT)
+			GO_STATE(PC1_BREAK) ;
+		return ;
+	case PC_DISABLE :
+		/* PC09-PC99 */
+		GO_STATE(PC9_MAINT) ;
+		AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+			FDDI_PORT_EVENT, (u_long) FDDI_PORT_DISABLED,
+			smt_get_port_event_word(smc));
+		return ;
+	case PC_TIMEOUT_LCT :
+		/* if long or extended LCT */
+		stop_pcm_timer0(smc,phy) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
+		/* end of LCT is indicate by PCM_CODE (initiate PCM event) */
+		return ;
+	}
+
+	switch(mib->fddiPORTPCMState) {
+	case ACTIONS(PC0_OFF) :
+		stop_pcm_timer0(smc,phy) ;
+		outpw(PLC(np,PL_CNTRL_A),0) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
+		sm_ph_lem_stop(smc,np) ;		/* disable LEM */
+		phy->cf_loop = FALSE ;
+		phy->cf_join = FALSE ;
+		queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
+		plc_go_state(smc,np,PL_PCM_STOP) ;
+		mib->fddiPORTConnectState = PCM_DISABLED ;
+		ACTIONS_DONE() ;
+		break ;
+	case PC0_OFF:
+		/*PC09*/
+		if (cmd == PC_MAINT) {
+			GO_STATE(PC9_MAINT) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(PC1_BREAK) :
+		/* Stop the LCT timer if we came from Signal state */
+		stop_pcm_timer0(smc,phy) ;
+		ACTIONS_DONE() ;
+		plc_go_state(smc,np,0) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
+		sm_ph_lem_stop(smc,np) ;		/* disable LEM */
+		/*
+		 * if vector is already loaded, go to OFF to clear PCM_SIGNAL
+		 */
+#if	0
+		if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
+			plc_go_state(smc,np,PL_PCM_STOP) ;
+			/* TB_MIN ? */
+		}
+#endif
+		/*
+		 * Go to OFF state in any case.
+		 */
+		plc_go_state(smc,np,PL_PCM_STOP) ;
+
+		if (mib->fddiPORTPC_Withhold == PC_WH_NONE)
+			mib->fddiPORTConnectState = PCM_CONNECTING ;
+		phy->cf_loop = FALSE ;
+		phy->cf_join = FALSE ;
+		queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
+		phy->ls_flag = FALSE ;
+		phy->pc_mode = PM_NONE ;	/* needed by CFM */
+		phy->bitn = 0 ;			/* bit signaling start bit */
+		for (i = 0 ; i < 3 ; i++)
+			pc_tcode_actions(smc,i,phy) ;
+
+		/* Set the non-active interrupt mask register */
+		outpw(PLC(np,PL_INTR_MASK),plc_imsk_na) ;
+
+		/*
+		 * If the LCT was stopped. There might be a
+		 * PCM_CODE interrupt event present.
+		 * This must be cleared.
+		 */
+		(void)inpw(PLC(np,PL_INTR_EVENT)) ;
+#ifndef	MOT_ELM
+		/* Get the plc revision for revision dependent code */
+		plc_rev = inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK ;
+
+		if (plc_rev != PLC_REV_SN3)
+#endif	/* MOT_ELM */
+		{
+			/*
+			 * No supernet III PLC, so set Xmit verctor and
+			 * length BEFORE starting the state machine.
+			 */
+			if (plc_send_bits(smc,phy,3)) {
+				return ;
+			}
+		}
+
+		/*
+		 * Now give the Start command.
+		 * - The start command shall be done before setting the bits
+		 *   to be signaled. (In PLC-S description and PLCS in SN3.
+		 * - The start command shall be issued AFTER setting the
+		 *   XMIT vector and the XMIT length register.
+		 *
+		 * We do it exactly according this specs for the old PLC and
+		 * the new PLCS inside the SN3.
+		 * For the usual PLCS we try it the way it is done for the
+		 * old PLC and set the XMIT registers again, if the PLC is
+		 * not in SIGNAL state. This is done according to an PLCS
+		 * errata workaround.
+		 */
+
+		plc_go_state(smc,np,PL_PCM_START) ;
+
+		/*
+		 * workaround for PLC-S eng. sample errata
+		 */
+#ifdef	MOT_ELM
+		if (!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
+#else	/* nMOT_ELM */
+		if (((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) !=
+			PLC_REVISION_A) &&
+			!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
+#endif	/* nMOT_ELM */
+		{
+			/*
+			 * Set register again (PLCS errata) or the first time
+			 * (new SN3 PLCS).
+			 */
+			(void) plc_send_bits(smc,phy,3) ;
+		}
+		/*
+		 * end of workaround
+		 */
+
+		GO_STATE(PC5_SIGNAL) ;
+		plc->p_state = PS_BIT3 ;
+		plc->p_bits = 3 ;
+		plc->p_start = 0 ;
+
+		break ;
+	case PC1_BREAK :
+		break ;
+	case ACTIONS(PC2_TRACE) :
+		plc_go_state(smc,np,PL_PCM_TRACE) ;
+		ACTIONS_DONE() ;
+		break ;
+	case PC2_TRACE :
+		break ;
+
+	case PC3_CONNECT :	/* these states are done by hardware */
+	case PC4_NEXT :
+		break ;
+
+	case ACTIONS(PC5_SIGNAL) :
+		ACTIONS_DONE() ;
+	case PC5_SIGNAL :
+		if ((cmd != PC_SIGNAL) && (cmd != PC_TIMEOUT_LCT))
+			break ;
+		switch (plc->p_state) {
+		case PS_BIT3 :
+			for (i = 0 ; i <= 2 ; i++)
+				pc_rcode_actions(smc,i,phy) ;
+			pc_tcode_actions(smc,3,phy) ;
+			plc->p_state = PS_BIT4 ;
+			plc->p_bits = 1 ;
+			plc->p_start = 3 ;
+			phy->bitn = 3 ;
+			if (plc_send_bits(smc,phy,1)) {
+				return ;
+			}
+			break ;
+		case PS_BIT4 :
+			pc_rcode_actions(smc,3,phy) ;
+			for (i = 4 ; i <= 6 ; i++)
+				pc_tcode_actions(smc,i,phy) ;
+			plc->p_state = PS_BIT7 ;
+			plc->p_bits = 3 ;
+			plc->p_start = 4 ;
+			phy->bitn = 4 ;
+			if (plc_send_bits(smc,phy,3)) {
+				return ;
+			}
+			break ;
+		case PS_BIT7 :
+			for (i = 3 ; i <= 6 ; i++)
+				pc_rcode_actions(smc,i,phy) ;
+			plc->p_state = PS_LCT ;
+			plc->p_bits = 0 ;
+			plc->p_start = 7 ;
+			phy->bitn = 7 ;
+		sm_ph_lem_start(smc,np,(int)smc->s.lct_short) ; /* enable LEM */
+			/* start LCT */
+			i = inpw(PLC(np,PL_CNTRL_B)) & ~PL_PC_LOOP ;
+			outpw(PLC(np,PL_CNTRL_B),i) ;	/* must be cleared */
+			outpw(PLC(np,PL_CNTRL_B),i | PL_RLBP) ;
+			break ;
+		case PS_LCT :
+			/* check for local LCT failure */
+			pc_tcode_actions(smc,7,phy) ;
+			/*
+			 * set tval[7]
+			 */
+			plc->p_state = PS_BIT8 ;
+			plc->p_bits = 1 ;
+			plc->p_start = 7 ;
+			phy->bitn = 7 ;
+			if (plc_send_bits(smc,phy,1)) {
+				return ;
+			}
+			break ;
+		case PS_BIT8 :
+			/* check for remote LCT failure */
+			pc_rcode_actions(smc,7,phy) ;
+			if (phy->t_val[7] || phy->r_val[7]) {
+				plc_go_state(smc,np,PL_PCM_STOP) ;
+				GO_STATE(PC1_BREAK) ;
+				break ;
+			}
+			for (i = 8 ; i <= 9 ; i++)
+				pc_tcode_actions(smc,i,phy) ;
+			plc->p_state = PS_JOIN ;
+			plc->p_bits = 2 ;
+			plc->p_start = 8 ;
+			phy->bitn = 8 ;
+			if (plc_send_bits(smc,phy,2)) {
+				return ;
+			}
+			break ;
+		case PS_JOIN :
+			for (i = 8 ; i <= 9 ; i++)
+				pc_rcode_actions(smc,i,phy) ;
+			plc->p_state = PS_ACTIVE ;
+			GO_STATE(PC6_JOIN) ;
+			break ;
+		}
+		break ;
+
+	case ACTIONS(PC6_JOIN) :
+		/*
+		 * prevent mux error when going from WRAP_A to WRAP_B
+		 */
+		if (smc->s.sas == SMT_DAS && np == PB &&
+			(smc->y[PA].pc_mode == PM_TREE ||
+			 smc->y[PB].pc_mode == PM_TREE)) {
+			SETMASK(PLC(np,PL_CNTRL_A),
+				PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
+			SETMASK(PLC(np,PL_CNTRL_B),
+				PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
+		}
+		SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
+		SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
+		ACTIONS_DONE() ;
+		cmd = 0 ;
+		/* fall thru */
+	case PC6_JOIN :
+		switch (plc->p_state) {
+		case PS_ACTIVE:
+			/*PC88b*/
+			if (!phy->cf_join) {
+				phy->cf_join = TRUE ;
+				queue_event(smc,EVENT_CFM,CF_JOIN+np) ; ;
+			}
+			if (cmd == PC_JOIN)
+				GO_STATE(PC8_ACTIVE) ;
+			/*PC82*/
+			if (cmd == PC_TRACE) {
+				GO_STATE(PC2_TRACE) ;
+				break ;
+			}
+			break ;
+		}
+		break ;
+
+	case PC7_VERIFY :
+		break ;
+
+	case ACTIONS(PC8_ACTIVE) :
+		/*
+		 * start LEM for SMT
+		 */
+		sm_ph_lem_start(smc,(int)phy->np,LCT_LEM_MAX) ;
+
+		phy->tr_flag = FALSE ;
+		mib->fddiPORTConnectState = PCM_ACTIVE ;
+
+		/* Set the active interrupt mask register */
+		outpw(PLC(np,PL_INTR_MASK),plc_imsk_act) ;
+
+		ACTIONS_DONE() ;
+		break ;
+	case PC8_ACTIVE :
+		/*PC81 is done by PL_TNE_EXPIRED irq */
+		/*PC82*/
+		if (cmd == PC_TRACE) {
+			GO_STATE(PC2_TRACE) ;
+			break ;
+		}
+		/*PC88c: is done by TRACE_PROP irq */
+
+		break ;
+	case ACTIONS(PC9_MAINT) :
+		stop_pcm_timer0(smc,phy) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
+		CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
+		CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ;	/* disable LEM int. */
+		sm_ph_lem_stop(smc,np) ;		/* disable LEM */
+		phy->cf_loop = FALSE ;
+		phy->cf_join = FALSE ;
+		queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
+		plc_go_state(smc,np,PL_PCM_STOP) ;
+		mib->fddiPORTConnectState = PCM_DISABLED ;
+		SETMASK(PLC(np,PL_CNTRL_B),PL_MAINT,PL_MAINT) ;
+		sm_ph_linestate(smc,np,(int) MIB2LS(mib->fddiPORTMaint_LS)) ;
+		outpw(PLC(np,PL_CNTRL_A),PL_SC_BYPASS) ;
+		ACTIONS_DONE() ;
+		break ;
+	case PC9_MAINT :
+		DB_PCMN(1,"PCM %c : MAINT\n",phy->phy_name,0) ;
+		/*PC90*/
+		if (cmd == PC_ENABLE) {
+			GO_STATE(PC0_OFF) ;
+			break ;
+		}
+		break ;
+
+	default:
+		SMT_PANIC(smc,SMT_E0118, SMT_E0118_MSG) ;
+		break ;
+	}
+}
+
+/*
+ * force line state on a PHY output	(only in MAINT state)
+ */
+static void sm_ph_linestate(smc,phy,ls)
+struct s_smc *smc ;
+int phy;
+int ls;
+{
+	int	cntrl ;
+
+	SK_UNUSED(smc) ;
+
+	cntrl = (inpw(PLC(phy,PL_CNTRL_B)) & ~PL_MAINT_LS) |
+						PL_PCM_STOP | PL_MAINT ;
+	switch(ls) {
+	case PC_QLS: 		/* Force Quiet */
+		cntrl |= PL_M_QUI0 ;
+		break ;
+	case PC_MLS: 		/* Force Master */
+		cntrl |= PL_M_MASTR ;
+		break ;
+	case PC_HLS: 		/* Force Halt */
+		cntrl |= PL_M_HALT ;
+		break ;
+	default :
+	case PC_ILS: 		/* Force Idle */
+		cntrl |= PL_M_IDLE ;
+		break ;
+	case PC_LS_PDR: 	/* Enable repeat filter */
+		cntrl |= PL_M_TPDR ;
+		break ;
+	}
+	outpw(PLC(phy,PL_CNTRL_B),cntrl) ;
+}
+
+
+static void reset_lem_struct(phy)
+struct s_phy *phy;
+{
+	struct lem_counter *lem = &phy->lem ;
+
+	phy->mib->fddiPORTLer_Estimate = 15 ;
+	lem->lem_float_ber = 15 * 100 ;
+}
+
+/*
+ * link error monitor
+ */
+static void lem_evaluate(smc,phy)
+struct s_smc *smc ;
+struct s_phy *phy;
+{
+	int ber ;
+	u_long errors ;
+	struct lem_counter *lem = &phy->lem ;
+	struct fddi_mib_p	*mib ;
+	int			cond ;
+
+	mib = phy->mib ;
+
+	if (!lem->lem_on)
+		return ;
+
+	errors = inpw(PLC(((int) phy->np),PL_LINK_ERR_CTR)) ;
+	lem->lem_errors += errors ;
+	mib->fddiPORTLem_Ct += errors ;
+
+	errors = lem->lem_errors ;
+	/*
+	 * calculation is called on a intervall of 8 seconds
+	 *	-> this means, that one error in 8 sec. is one of 8*125*10E6
+	 *	the same as BER = 10E-9
+	 * Please note:
+	 *	-> 9 errors in 8 seconds mean:
+	 *	   BER = 9 * 10E-9  and this is
+	 *	    < 10E-8, so the limit of 10E-8 is not reached!
+	 */
+
+		if (!errors)		ber = 15 ;
+	else	if (errors <= 9)	ber = 9 ;
+	else	if (errors <= 99)	ber = 8 ;
+	else	if (errors <= 999)	ber = 7 ;
+	else	if (errors <= 9999)	ber = 6 ;
+	else	if (errors <= 99999)	ber = 5 ;
+	else	if (errors <= 999999)	ber = 4 ;
+	else	if (errors <= 9999999)	ber = 3 ;
+	else	if (errors <= 99999999)	ber = 2 ;
+	else	if (errors <= 999999999) ber = 1 ;
+	else				ber = 0 ;
+
+	/*
+	 * weighted average
+	 */
+	ber *= 100 ;
+	lem->lem_float_ber = lem->lem_float_ber * 7 + ber * 3 ;
+	lem->lem_float_ber /= 10 ;
+	mib->fddiPORTLer_Estimate = lem->lem_float_ber / 100 ;
+	if (mib->fddiPORTLer_Estimate < 4) {
+		mib->fddiPORTLer_Estimate = 4 ;
+	}
+
+	if (lem->lem_errors) {
+		DB_PCMN(1,"LEM %c :\n",phy->np == PB? 'B' : 'A',0) ;
+		DB_PCMN(1,"errors      : %ld\n",lem->lem_errors,0) ;
+		DB_PCMN(1,"sum_errors  : %ld\n",mib->fddiPORTLem_Ct,0) ;
+		DB_PCMN(1,"current BER : 10E-%d\n",ber/100,0) ;
+		DB_PCMN(1,"float BER   : 10E-(%d/100)\n",lem->lem_float_ber,0) ;
+		DB_PCMN(1,"avg. BER    : 10E-%d\n",
+			mib->fddiPORTLer_Estimate,0) ;
+	}
+
+	lem->lem_errors = 0L ;
+
+#ifndef	SLIM_SMT
+	cond = (mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Alarm) ?
+		TRUE : FALSE ;
+#ifdef	SMT_EXT_CUTOFF
+	smt_ler_alarm_check(smc,phy,cond) ;
+#endif	/* nSMT_EXT_CUTOFF */
+	if (cond != mib->fddiPORTLerFlag) {
+		smt_srf_event(smc,SMT_COND_PORT_LER,
+			(int) (INDEX_PORT+ phy->np) ,cond) ;
+	}
+#endif
+
+	if (	mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Cutoff) {
+		phy->pc_lem_fail = TRUE ;		/* flag */
+		mib->fddiPORTLem_Reject_Ct++ ;
+		/*
+		 * "forgive 10e-2" if we cutoff so we can come
+		 * up again ..
+		 */
+		lem->lem_float_ber += 2*100 ;
+
+		/*PC81b*/
+#ifdef	CONCENTRATOR
+		DB_PCMN(1,"PCM: LER cutoff on port %d cutoff %d\n",
+			phy->np, mib->fddiPORTLer_Cutoff) ;
+#endif
+#ifdef	SMT_EXT_CUTOFF
+		smt_port_off_event(smc,phy->np);
+#else	/* nSMT_EXT_CUTOFF */
+		queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
+#endif	/* nSMT_EXT_CUTOFF */
+	}
+}
+
+/*
+ * called by SMT to calculate LEM bit error rate
+ */
+void sm_lem_evaluate(smc)
+struct s_smc *smc ;
+{
+	int np ;
+
+	for (np = 0 ; np < NUMPHYS ; np++)
+		lem_evaluate(smc,&smc->y[np]) ;
+}
+
+static void lem_check_lct(smc,phy)
+struct s_smc *smc ;
+struct s_phy	*phy ;
+{
+	struct lem_counter	*lem = &phy->lem ;
+	struct fddi_mib_p	*mib ;
+	int errors ;
+
+	mib = phy->mib ;
+
+	phy->pc_lem_fail = FALSE ;		/* flag */
+	errors = inpw(PLC(((int)phy->np),PL_LINK_ERR_CTR)) ;
+	lem->lem_errors += errors ;
+	mib->fddiPORTLem_Ct += errors ;
+	if (lem->lem_errors) {
+		switch(phy->lc_test) {
+		case LC_SHORT:
+			if (lem->lem_errors >= smc->s.lct_short)
+				phy->pc_lem_fail = TRUE ;
+			break ;
+		case LC_MEDIUM:
+			if (lem->lem_errors >= smc->s.lct_medium)
+				phy->pc_lem_fail = TRUE ;
+			break ;
+		case LC_LONG:
+			if (lem->lem_errors >= smc->s.lct_long)
+				phy->pc_lem_fail = TRUE ;
+			break ;
+		case LC_EXTENDED:
+			if (lem->lem_errors >= smc->s.lct_extended)
+				phy->pc_lem_fail = TRUE ;
+			break ;
+		}
+		DB_PCMN(1," >>errors : %d\n",lem->lem_errors,0) ;
+	}
+	if (phy->pc_lem_fail) {
+		mib->fddiPORTLCTFail_Ct++ ;
+		mib->fddiPORTLem_Reject_Ct++ ;
+	}
+	else
+		mib->fddiPORTLCTFail_Ct = 0 ;
+}
+
+/*
+ * LEM functions
+ */
+static void sm_ph_lem_start(smc,np,threshold)
+struct s_smc *smc ;
+int np;
+int threshold;
+{
+	struct lem_counter *lem = &smc->y[np].lem ;
+
+	lem->lem_on = 1 ;
+	lem->lem_errors = 0L ;
+
+	/* Do NOT reset mib->fddiPORTLer_Estimate here. It is called too
+	 * often.
+	 */
+
+	outpw(PLC(np,PL_LE_THRESHOLD),threshold) ;
+	(void)inpw(PLC(np,PL_LINK_ERR_CTR)) ;	/* clear error counter */
+
+	/* enable LE INT */
+	SETMASK(PLC(np,PL_INTR_MASK),PL_LE_CTR,PL_LE_CTR) ;
+}
+
+static void sm_ph_lem_stop(smc,np)
+struct s_smc *smc ;
+int np;
+{
+	struct lem_counter *lem = &smc->y[np].lem ;
+
+	lem->lem_on = 0 ;
+	CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ;
+}
+
+/* ARGSUSED */
+void sm_pm_ls_latch(smc,phy,on_off)
+struct s_smc *smc ;
+int phy;
+int on_off;		/* en- or disable ident. ls */
+{
+	SK_UNUSED(smc) ;
+
+	phy = phy ; on_off = on_off ;
+}
+
+
+/*
+ * PCM pseudo code
+ * receive actions are called AFTER the bit n is received,
+ * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received
+ */
+
+/*
+ * PCM pseudo code 5.1 .. 6.1
+ */
+static void pc_rcode_actions(smc,bit,phy)
+struct s_smc *smc ;
+int bit;
+struct s_phy *phy;
+{
+	struct fddi_mib_p	*mib ;
+
+	mib = phy->mib ;
+
+	DB_PCMN(1,"SIG rec %x %x: \n", bit,phy->r_val[bit] ) ;
+	bit++ ;
+
+	switch(bit) {
+	case 0:
+	case 1:
+	case 2:
+		break ;
+	case 3 :
+		if (phy->r_val[1] == 0 && phy->r_val[2] == 0)
+			mib->fddiPORTNeighborType = TA ;
+		else if (phy->r_val[1] == 0 && phy->r_val[2] == 1)
+			mib->fddiPORTNeighborType = TB ;
+		else if (phy->r_val[1] == 1 && phy->r_val[2] == 0)
+			mib->fddiPORTNeighborType = TS ;
+		else if (phy->r_val[1] == 1 && phy->r_val[2] == 1)
+			mib->fddiPORTNeighborType = TM ;
+		break ;
+	case 4:
+		if (mib->fddiPORTMy_Type == TM &&
+			mib->fddiPORTNeighborType == TM) {
+			DB_PCMN(1,"PCM %c : E100 withhold M-M\n",
+				phy->phy_name,0) ;
+			mib->fddiPORTPC_Withhold = PC_WH_M_M ;
+			RS_SET(smc,RS_EVENT) ;
+		}
+		else if (phy->t_val[3] || phy->r_val[3]) {
+			mib->fddiPORTPC_Withhold = PC_WH_NONE ;
+			if (mib->fddiPORTMy_Type == TM ||
+			    mib->fddiPORTNeighborType == TM)
+				phy->pc_mode = PM_TREE ;
+			else
+				phy->pc_mode = PM_PEER ;
+
+			/* reevaluate the selection criteria (wc_flag) */
+			all_selection_criteria (smc);
+
+			if (phy->wc_flag) {
+				mib->fddiPORTPC_Withhold = PC_WH_PATH ;
+			}
+		}
+		else {
+			mib->fddiPORTPC_Withhold = PC_WH_OTHER ;
+			RS_SET(smc,RS_EVENT) ;
+			DB_PCMN(1,"PCM %c : E101 withhold other\n",
+				phy->phy_name,0) ;
+		}
+		phy->twisted = ((mib->fddiPORTMy_Type != TS) &&
+				(mib->fddiPORTMy_Type != TM) &&
+				(mib->fddiPORTNeighborType ==
+				mib->fddiPORTMy_Type)) ;
+		if (phy->twisted) {
+			DB_PCMN(1,"PCM %c : E102 !!! TWISTED !!!\n",
+				phy->phy_name,0) ;
+		}
+		break ;
+	case 5 :
+		break ;
+	case 6:
+		if (phy->t_val[4] || phy->r_val[4]) {
+			if ((phy->t_val[4] && phy->t_val[5]) ||
+			    (phy->r_val[4] && phy->r_val[5]) )
+				phy->lc_test = LC_EXTENDED ;
+			else
+				phy->lc_test = LC_LONG ;
+		}
+		else if (phy->t_val[5] || phy->r_val[5])
+			phy->lc_test = LC_MEDIUM ;
+		else
+			phy->lc_test = LC_SHORT ;
+		switch (phy->lc_test) {
+		case LC_SHORT :				/* 50ms */
+			outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LENGTH ) ;
+			phy->t_next[7] = smc->s.pcm_lc_short ;
+			break ;
+		case LC_MEDIUM :			/* 500ms */
+			outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LONGLN ) ;
+			phy->t_next[7] = smc->s.pcm_lc_medium ;
+			break ;
+		case LC_LONG :
+			SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
+			phy->t_next[7] = smc->s.pcm_lc_long ;
+			break ;
+		case LC_EXTENDED :
+			SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
+			phy->t_next[7] = smc->s.pcm_lc_extended ;
+			break ;
+		}
+		if (phy->t_next[7] > smc->s.pcm_lc_medium) {
+			start_pcm_timer0(smc,phy->t_next[7],PC_TIMEOUT_LCT,phy);
+		}
+		DB_PCMN(1,"LCT timer = %ld us\n", phy->t_next[7], 0) ;
+		phy->t_next[9] = smc->s.pcm_t_next_9 ;
+		break ;
+	case 7:
+		if (phy->t_val[6]) {
+			phy->cf_loop = TRUE ;
+		}
+		phy->td_flag = TRUE ;
+		break ;
+	case 8:
+		if (phy->t_val[7] || phy->r_val[7]) {
+			DB_PCMN(1,"PCM %c : E103 LCT fail %s\n",
+				phy->phy_name,phy->t_val[7]? "local":"remote") ;
+			queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
+		}
+		break ;
+	case 9:
+		if (phy->t_val[8] || phy->r_val[8]) {
+			if (phy->t_val[8])
+				phy->cf_loop = TRUE ;
+			phy->td_flag = TRUE ;
+		}
+		break ;
+	case 10:
+		if (phy->r_val[9]) {
+			/* neighbor intends to have MAC on output */ ;
+			mib->fddiPORTMacIndicated.R_val = TRUE ;
+		}
+		else {
+			/* neighbor does not intend to have MAC on output */ ;
+			mib->fddiPORTMacIndicated.R_val = FALSE ;
+		}
+		break ;
+	}
+}
+
+/*
+ * PCM pseudo code 5.1 .. 6.1
+ */
+static void pc_tcode_actions(smc,bit,phy)
+struct s_smc *smc ;
+const int bit;
+struct s_phy *phy;
+{
+	int	np = phy->np ;
+	struct fddi_mib_p	*mib ;
+
+	mib = phy->mib ;
+
+	switch(bit) {
+	case 0:
+		phy->t_val[0] = 0 ;		/* no escape used */
+		break ;
+	case 1:
+		if (mib->fddiPORTMy_Type == TS || mib->fddiPORTMy_Type == TM)
+			phy->t_val[1] = 1 ;
+		else
+			phy->t_val[1] = 0 ;
+		break ;
+	case 2 :
+		if (mib->fddiPORTMy_Type == TB || mib->fddiPORTMy_Type == TM)
+			phy->t_val[2] = 1 ;
+		else
+			phy->t_val[2] = 0 ;
+		break ;
+	case 3:
+		{
+		int	type,ne ;
+		int	policy ;
+
+		type = mib->fddiPORTMy_Type ;
+		ne = mib->fddiPORTNeighborType ;
+		policy = smc->mib.fddiSMTConnectionPolicy ;
+
+		phy->t_val[3] = 1 ;	/* Accept connection */
+		switch (type) {
+		case TA :
+			if (
+				((policy & POLICY_AA) && ne == TA) ||
+				((policy & POLICY_AB) && ne == TB) ||
+				((policy & POLICY_AS) && ne == TS) ||
+				((policy & POLICY_AM) && ne == TM) )
+				phy->t_val[3] = 0 ;	/* Reject */
+			break ;
+		case TB :
+			if (
+				((policy & POLICY_BA) && ne == TA) ||
+				((policy & POLICY_BB) && ne == TB) ||
+				((policy & POLICY_BS) && ne == TS) ||
+				((policy & POLICY_BM) && ne == TM) )
+				phy->t_val[3] = 0 ;	/* Reject */
+			break ;
+		case TS :
+			if (
+				((policy & POLICY_SA) && ne == TA) ||
+				((policy & POLICY_SB) && ne == TB) ||
+				((policy & POLICY_SS) && ne == TS) ||
+				((policy & POLICY_SM) && ne == TM) )
+				phy->t_val[3] = 0 ;	/* Reject */
+			break ;
+		case TM :
+			if (	ne == TM ||
+				((policy & POLICY_MA) && ne == TA) ||
+				((policy & POLICY_MB) && ne == TB) ||
+				((policy & POLICY_MS) && ne == TS) ||
+				((policy & POLICY_MM) && ne == TM) )
+				phy->t_val[3] = 0 ;	/* Reject */
+			break ;
+		}
+#ifndef	SLIM_SMT
+		/*
+		 * detect undesirable connection attempt event
+		 */
+		if (	(type == TA && ne == TA ) ||
+			(type == TA && ne == TS ) ||
+			(type == TB && ne == TB ) ||
+			(type == TB && ne == TS ) ||
+			(type == TS && ne == TA ) ||
+			(type == TS && ne == TB ) ) {
+			smt_srf_event(smc,SMT_EVENT_PORT_CONNECTION,
+				(int) (INDEX_PORT+ phy->np) ,0) ;
+		}
+#endif
+		}
+		break ;
+	case 4:
+		if (mib->fddiPORTPC_Withhold == PC_WH_NONE) {
+			if (phy->pc_lem_fail) {
+				phy->t_val[4] = 1 ;	/* long */
+				phy->t_val[5] = 0 ;
+			}
+			else {
+				phy->t_val[4] = 0 ;
+				if (mib->fddiPORTLCTFail_Ct > 0)
+					phy->t_val[5] = 1 ;	/* medium */
+				else
+					phy->t_val[5] = 0 ;	/* short */
+
+				/*
+				 * Implementers choice: use medium
+				 * instead of short when undesired
+				 * connection attempt is made.
+				 */
+				if (phy->wc_flag)
+					phy->t_val[5] = 1 ;	/* medium */
+			}
+			mib->fddiPORTConnectState = PCM_CONNECTING ;
+		}
+		else {
+			mib->fddiPORTConnectState = PCM_STANDBY ;
+			phy->t_val[4] = 1 ;	/* extended */
+			phy->t_val[5] = 1 ;
+		}
+		break ;
+	case 5:
+		break ;
+	case 6:
+		/* we do NOT have a MAC for LCT */
+		phy->t_val[6] = 0 ;
+		break ;
+	case 7:
+		phy->cf_loop = FALSE ;
+		lem_check_lct(smc,phy) ;
+		if (phy->pc_lem_fail) {
+			DB_PCMN(1,"PCM %c : E104 LCT failed\n",
+				phy->phy_name,0) ;
+			phy->t_val[7] = 1 ;
+		}
+		else
+			phy->t_val[7] = 0 ;
+		break ;
+	case 8:
+		phy->t_val[8] = 0 ;	/* Don't request MAC loopback */
+		break ;
+	case 9:
+		phy->cf_loop = 0 ;
+		if ((mib->fddiPORTPC_Withhold != PC_WH_NONE) ||
+		     ((smc->s.sas == SMT_DAS) && (phy->wc_flag))) {
+			queue_event(smc,EVENT_PCM+np,PC_START) ;
+			break ;
+		}
+		phy->t_val[9] = FALSE ;
+		switch (smc->s.sas) {
+		case SMT_DAS :
+			/*
+			 * MAC intended on output
+			 */
+			if (phy->pc_mode == PM_TREE) {
+				if ((np == PB) || ((np == PA) &&
+				(smc->y[PB].mib->fddiPORTConnectState !=
+					PCM_ACTIVE)))
+					phy->t_val[9] = TRUE ;
+			}
+			else {
+				if (np == PB)
+					phy->t_val[9] = TRUE ;
+			}
+			break ;
+		case SMT_SAS :
+			if (np == PS)
+				phy->t_val[9] = TRUE ;
+			break ;
+#ifdef	CONCENTRATOR
+		case SMT_NAC :
+			/*
+			 * MAC intended on output
+			 */
+			if (np == PB)
+				phy->t_val[9] = TRUE ;
+			break ;
+#endif
+		}
+		mib->fddiPORTMacIndicated.T_val = phy->t_val[9] ;
+		break ;
+	}
+	DB_PCMN(1,"SIG snd %x %x: \n", bit,phy->t_val[bit] ) ;
+}
+
+/*
+ * return status twisted (called by SMT)
+ */
+int pcm_status_twisted(smc)
+struct s_smc *smc ;
+{
+	int	twist = 0 ;
+	if (smc->s.sas != SMT_DAS)
+		return(0) ;
+	if (smc->y[PA].twisted && (smc->y[PA].mib->fddiPORTPCMState == PC8_ACTIVE))
+		twist |= 1 ;
+	if (smc->y[PB].twisted && (smc->y[PB].mib->fddiPORTPCMState == PC8_ACTIVE))
+		twist |= 2 ;
+	return(twist) ;
+}
+
+/*
+ * return status	(called by SMT)
+ *	type
+ *	state
+ *	remote phy type
+ *	remote mac yes/no
+ */
+void pcm_status_state(smc,np,type,state,remote,mac)
+struct s_smc *smc ;
+int np;
+int *type;
+int *state;
+int *remote;
+int *mac;
+{
+	struct s_phy	*phy = &smc->y[np] ;
+	struct fddi_mib_p	*mib ;
+
+	mib = phy->mib ;
+
+	/* remote PHY type and MAC - set only if active */
+	*mac = 0 ;
+	*type = mib->fddiPORTMy_Type ;		/* our PHY type */
+	*state = mib->fddiPORTConnectState ;
+	*remote = mib->fddiPORTNeighborType ;
+
+	switch(mib->fddiPORTPCMState) {
+	case PC8_ACTIVE :
+		*mac = mib->fddiPORTMacIndicated.R_val ;
+		break ;
+	}
+}
+
+/*
+ * return rooted station status (called by SMT)
+ */
+int pcm_rooted_station(smc)
+struct s_smc *smc ;
+{
+	int	n ;
+
+	for (n = 0 ; n < NUMPHYS ; n++) {
+		if (smc->y[n].mib->fddiPORTPCMState == PC8_ACTIVE &&
+		    smc->y[n].mib->fddiPORTNeighborType == TM)
+			return(0) ;
+	}
+	return(1) ;
+}
+
+/*
+ * Interrupt actions for PLC & PCM events
+ */
+void plc_irq(smc,np,cmd)
+struct s_smc *smc ;
+int np;			/* PHY index */
+unsigned int cmd;
+{
+	struct s_phy *phy = &smc->y[np] ;
+	struct s_plc *plc = &phy->plc ;
+	int		n ;
+#ifdef	SUPERNET_3
+	int		corr_mask ;
+#endif	/* SUPERNET_3 */
+	int		i ;
+
+	if (np >= smc->s.numphys) {
+		plc->soft_err++ ;
+		return ;
+	}
+	if (cmd & PL_EBUF_ERR) {	/* elastic buff. det. over-|underflow*/
+		/*
+		 * Check whether the SRF Condition occured.
+		 */
+		if (!plc->ebuf_cont && phy->mib->fddiPORTPCMState == PC8_ACTIVE){
+			/*
+			 * This is the real Elasticity Error.
+			 * More than one in a row are treated as a
+			 * single one.
+			 * Only count this in the active state.
+			 */
+			phy->mib->fddiPORTEBError_Ct ++ ;
+
+		}
+
+		plc->ebuf_err++ ;
+		if (plc->ebuf_cont <= 1000) {
+			/*
+			 * Prevent counter from being wrapped after
+			 * hanging years in that interrupt.
+			 */
+			plc->ebuf_cont++ ;	/* Ebuf continous error */
+		}
+
+#ifdef	SUPERNET_3
+		if (plc->ebuf_cont == 1000 &&
+			((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) ==
+			PLC_REV_SN3)) {
+			/*
+			 * This interrupt remeained high for at least
+			 * 1000 consecutive interrupt calls.
+			 *
+			 * This is caused by a hardware error of the
+			 * ORION part of the Supernet III chipset.
+			 *
+			 * Disable this bit from the mask.
+			 */
+			corr_mask = (plc_imsk_na & ~PL_EBUF_ERR) ;
+			outpw(PLC(np,PL_INTR_MASK),corr_mask);
+
+			/*
+			 * Disconnect from the ring.
+			 * Call the driver with the reset indication.
+			 */
+			queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+
+			/*
+			 * Make an error log entry.
+			 */
+			SMT_ERR_LOG(smc,SMT_E0136, SMT_E0136_MSG) ;
+
+			/*
+			 * Indicate the Reset.
+			 */
+			drv_reset_indication(smc) ;
+		}
+#endif	/* SUPERNET_3 */
+	} else {
+		/* Reset the continous error variable */
+		plc->ebuf_cont = 0 ;	/* reset Ebuf continous error */
+	}
+	if (cmd & PL_PHYINV) {		/* physical layer invalid signal */
+		plc->phyinv++ ;
+	}
+	if (cmd & PL_VSYM_CTR) {	/* violation symbol counter has incr.*/
+		plc->vsym_ctr++ ;
+	}
+	if (cmd & PL_MINI_CTR) {	/* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
+		plc->mini_ctr++ ;
+	}
+	if (cmd & PL_LE_CTR) {		/* link error event counter */
+		int	j ;
+
+		/*
+		 * note: PL_LINK_ERR_CTR MUST be read to clear it
+		 */
+		j = inpw(PLC(np,PL_LE_THRESHOLD)) ;
+		i = inpw(PLC(np,PL_LINK_ERR_CTR)) ;
+
+		if (i < j) {
+			/* wrapped around */
+			i += 256 ;
+		}
+
+		if (phy->lem.lem_on) {
+			/* Note: Lem errors shall only be counted when
+			 * link is ACTIVE or LCT is active.
+			 */
+			phy->lem.lem_errors += i ;
+			phy->mib->fddiPORTLem_Ct += i ;
+		}
+	}
+	if (cmd & PL_TPC_EXPIRED) {	/* TPC timer reached zero */
+		if (plc->p_state == PS_LCT) {
+			/*
+			 * end of LCT
+			 */
+			;
+		}
+		plc->tpc_exp++ ;
+	}
+	if (cmd & PL_LS_MATCH) {	/* LS == LS in PLC_CNTRL_B's MATCH_LS*/
+		switch (inpw(PLC(np,PL_CNTRL_B)) & PL_MATCH_LS) {
+		case PL_I_IDLE :	phy->curr_ls = PC_ILS ;		break ;
+		case PL_I_HALT :	phy->curr_ls = PC_HLS ;		break ;
+		case PL_I_MASTR :	phy->curr_ls = PC_MLS ;		break ;
+		case PL_I_QUIET :	phy->curr_ls = PC_QLS ;		break ;
+		}
+	}
+	if (cmd & PL_PCM_BREAK) {	/* PCM has entered the BREAK state */
+		int	reason;
+
+		reason = inpw(PLC(np,PL_STATUS_B)) & PL_BREAK_REASON ;
+
+		switch (reason) {
+		case PL_B_PCS :		plc->b_pcs++ ;	break ;
+		case PL_B_TPC :		plc->b_tpc++ ;	break ;
+		case PL_B_TNE :		plc->b_tne++ ;	break ;
+		case PL_B_QLS :		plc->b_qls++ ;	break ;
+		case PL_B_ILS :		plc->b_ils++ ;	break ;
+		case PL_B_HLS :		plc->b_hls++ ;	break ;
+		}
+
+		/*jd 05-Aug-1999 changed: Bug #10419 */
+		DB_PCMN(1,"PLC %d: MDcF = %x\n", np, smc->e.DisconnectFlag);
+		if (smc->e.DisconnectFlag == FALSE) {
+			DB_PCMN(1,"PLC %d: restart (reason %x)\n", np, reason);
+			queue_event(smc,EVENT_PCM+np,PC_START) ;
+		}
+		else {
+			DB_PCMN(1,"PLC %d: NO!! restart (reason %x)\n", np, reason);
+		}
+		return ;
+	}
+	/*
+	 * If both CODE & ENABLE are set ignore enable
+	 */
+	if (cmd & PL_PCM_CODE) { /* receive last sign.-bit | LCT complete */
+		queue_event(smc,EVENT_PCM+np,PC_SIGNAL) ;
+		n = inpw(PLC(np,PL_RCV_VECTOR)) ;
+		for (i = 0 ; i < plc->p_bits ; i++) {
+			phy->r_val[plc->p_start+i] = n & 1 ;
+			n >>= 1 ;
+		}
+	}
+	else if (cmd & PL_PCM_ENABLED) { /* asserted SC_JOIN, scrub.completed*/
+		queue_event(smc,EVENT_PCM+np,PC_JOIN) ;
+	}
+	if (cmd & PL_TRACE_PROP) {	/* MLS while PC8_ACTIV || PC2_TRACE */
+		/*PC22b*/
+		if (!phy->tr_flag) {
+			DB_PCMN(1,"PCM : irq TRACE_PROP %d %d\n",
+				np,smc->mib.fddiSMTECMState) ;
+			phy->tr_flag = TRUE ;
+			smc->e.trace_prop |= ENTITY_BIT(ENTITY_PHY(np)) ;
+			queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ;
+		}
+	}
+	/*
+	 * filter PLC glitch ???
+	 * QLS || HLS only while in PC2_TRACE state
+	 */
+	if ((cmd & PL_SELF_TEST) && (phy->mib->fddiPORTPCMState == PC2_TRACE)) {
+		/*PC22a*/
+		if (smc->e.path_test == PT_PASSED) {
+			DB_PCMN(1,"PCM : state = %s %d\n", get_pcmstate(smc,np),
+				phy->mib->fddiPORTPCMState) ;
+
+			smc->e.path_test = PT_PENDING ;
+			queue_event(smc,EVENT_ECM,EC_PATH_TEST) ;
+		}
+	}
+	if (cmd & PL_TNE_EXPIRED) {	/* TNE: length of noise events */
+		/* break_required (TNE > NS_Max) */
+		if (phy->mib->fddiPORTPCMState == PC8_ACTIVE) {
+			if (!phy->tr_flag) {
+			   DB_PCMN(1,"PCM %c : PC81 %s\n",phy->phy_name,"NSE");
+			   queue_event(smc,EVENT_PCM+np,PC_START) ;
+			   return ;
+			}
+		}
+	}
+#if	0
+	if (cmd & PL_NP_ERR) {		/* NP has requested to r/w an inv reg*/
+		/*
+		 * It's a bug by AMD
+		 */
+		plc->np_err++ ;
+	}
+	/* pin inactiv (GND) */
+	if (cmd & PL_PARITY_ERR) {	/* p. error dedected on TX9-0 inp */
+		plc->parity_err++ ;
+	}
+	if (cmd & PL_LSDO) {		/* carrier detected */
+		;
+	}
+#endif
+}
+
+void pcm_set_lct_short(smc,n)
+struct s_smc *smc ;
+int n ;
+{
+	if (n <= 0 || n > 1000)
+		return ;
+	smc->s.lct_short = n ;
+}
+
+#ifdef	DEBUG
+/*
+ * fill state struct
+ */
+void pcm_get_state(smc,state)
+struct s_smc *smc ;
+struct smt_state *state ;
+{
+	struct s_phy	*phy ;
+	struct pcm_state *pcs ;
+	int	i ;
+	int	ii ;
+	short	rbits ;
+	short	tbits ;
+	struct fddi_mib_p	*mib ;
+
+	for (i = 0, phy = smc->y, pcs = state->pcm_state ; i < NUMPHYS ;
+		i++ , phy++, pcs++ ) {
+		mib = phy->mib ;
+		pcs->pcm_type = (u_char) mib->fddiPORTMy_Type ;
+		pcs->pcm_state = (u_char) mib->fddiPORTPCMState ;
+		pcs->pcm_mode = phy->pc_mode ;
+		pcs->pcm_neighbor = (u_char) mib->fddiPORTNeighborType ;
+		pcs->pcm_bsf = mib->fddiPORTBS_Flag ;
+		pcs->pcm_lsf = phy->ls_flag ;
+		pcs->pcm_lct_fail = (u_char) mib->fddiPORTLCTFail_Ct ;
+		pcs->pcm_ls_rx = LS2MIB(sm_pm_get_ls(smc,i)) ;
+		for (ii = 0, rbits = tbits = 0 ; ii < NUMBITS ; ii++) {
+			rbits <<= 1 ;
+			tbits <<= 1 ;
+			if (phy->r_val[NUMBITS-1-ii])
+				rbits |= 1 ;
+			if (phy->t_val[NUMBITS-1-ii])
+				tbits |= 1 ;
+		}
+		pcs->pcm_r_val = rbits ;
+		pcs->pcm_t_val = tbits ;
+	}
+}
+
+int get_pcm_state(smc,np)
+struct s_smc *smc ;
+int np;
+{
+	int pcs ;
+
+	SK_UNUSED(smc) ;
+
+	switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
+		case PL_PC0 :	pcs = PC_STOP ;		break ;
+		case PL_PC1 :	pcs = PC_START ;	break ;
+		case PL_PC2 :	pcs = PC_TRACE ;	break ;
+		case PL_PC3 :	pcs = PC_SIGNAL ;	break ;
+		case PL_PC4 :	pcs = PC_SIGNAL ;	break ;
+		case PL_PC5 :	pcs = PC_SIGNAL ;	break ;
+		case PL_PC6 :	pcs = PC_JOIN ;		break ;
+		case PL_PC7 :	pcs = PC_JOIN ;		break ;
+		case PL_PC8 :	pcs = PC_ENABLE ;	break ;
+		case PL_PC9 :	pcs = PC_MAINT ;	break ;
+		default :	pcs = PC_DISABLE ; 	break ;
+	}
+	return(pcs) ;
+}
+
+char *get_linestate(smc,np)
+struct s_smc *smc ;
+int np;
+{
+	char *ls = "" ;
+
+	SK_UNUSED(smc) ;
+
+	switch (inpw(PLC(np,PL_STATUS_A)) & PL_LINE_ST) {
+		case PL_L_NLS :	ls = "NOISE" ;	break ;
+		case PL_L_ALS :	ls = "ACTIV" ;	break ;
+		case PL_L_UND :	ls = "UNDEF" ;	break ;
+		case PL_L_ILS4:	ls = "ILS 4" ;	break ;
+		case PL_L_QLS :	ls = "QLS" ;	break ;
+		case PL_L_MLS :	ls = "MLS" ;	break ;
+		case PL_L_HLS :	ls = "HLS" ;	break ;
+		case PL_L_ILS16:ls = "ILS16" ;	break ;
+#ifdef	lint
+		default:	ls = "unknown" ; break ;
+#endif
+	}
+	return(ls) ;
+}
+
+char *get_pcmstate(smc,np)
+struct s_smc *smc ;
+int np;
+{
+	char *pcs ;
+	
+	SK_UNUSED(smc) ;
+
+	switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
+		case PL_PC0 :	pcs = "OFF" ;		break ;
+		case PL_PC1 :	pcs = "BREAK" ;		break ;
+		case PL_PC2 :	pcs = "TRACE" ;		break ;
+		case PL_PC3 :	pcs = "CONNECT";	break ;
+		case PL_PC4 :	pcs = "NEXT" ;		break ;
+		case PL_PC5 :	pcs = "SIGNAL" ;	break ;
+		case PL_PC6 :	pcs = "JOIN" ;		break ;
+		case PL_PC7 :	pcs = "VERIFY" ;	break ;
+		case PL_PC8 :	pcs = "ACTIV" ;		break ;
+		case PL_PC9 :	pcs = "MAINT" ;		break ;
+		default :	pcs = "UNKNOWN" ; 	break ;
+	}
+	return(pcs) ;
+}
+
+void list_phy(smc)
+struct s_smc *smc ;
+{
+	struct s_plc *plc ;
+	int np ;
+
+	for (np = 0 ; np < NUMPHYS ; np++) {
+		plc  = &smc->y[np].plc ;
+		printf("PHY %d:\tERRORS\t\t\tBREAK_REASONS\t\tSTATES:\n",np) ;
+		printf("\tsoft_error: %ld \t\tPC_Start : %ld\n",
+						plc->soft_err,plc->b_pcs);
+		printf("\tparity_err: %ld \t\tTPC exp. : %ld\t\tLine: %s\n",
+			plc->parity_err,plc->b_tpc,get_linestate(smc,np)) ;
+		printf("\tebuf_error: %ld \t\tTNE exp. : %ld\n",
+						plc->ebuf_err,plc->b_tne) ;
+		printf("\tphyinvalid: %ld \t\tQLS det. : %ld\t\tPCM : %s\n",
+			plc->phyinv,plc->b_qls,get_pcmstate(smc,np)) ;
+		printf("\tviosym_ctr: %ld \t\tILS det. : %ld\n",
+						plc->vsym_ctr,plc->b_ils)  ;
+		printf("\tmingap_ctr: %ld \t\tHLS det. : %ld\n",
+						plc->mini_ctr,plc->b_hls) ;
+		printf("\tnodepr_err: %ld\n",plc->np_err) ;
+		printf("\tTPC_exp : %ld\n",plc->tpc_exp) ;
+		printf("\tLEM_err : %ld\n",smc->y[np].lem.lem_errors) ;
+	}
+}
+
+
+#ifdef	CONCENTRATOR
+void pcm_lem_dump(smc)
+struct s_smc *smc ;
+{
+	int		i ;
+	struct s_phy	*phy ;
+	struct fddi_mib_p	*mib ;
+
+	char		*entostring() ;
+
+	printf("PHY	errors	BER\n") ;
+	printf("----------------------\n") ;
+	for (i = 0,phy = smc->y ; i < NUMPHYS ; i++,phy++) {
+		if (!plc_is_installed(smc,i))
+			continue ;
+		mib = phy->mib ;
+		printf("%s\t%ld\t10E-%d\n",
+			entostring(smc,ENTITY_PHY(i)),
+			mib->fddiPORTLem_Ct,
+			mib->fddiPORTLer_Estimate) ;
+	}
+}
+#endif
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/pmf.c linux.18p17/drivers/net/skfp/pmf.c
--- linux.vanilla/drivers/net/skfp/pmf.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/pmf.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,1701 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	Parameter Management Frame processing for SMT 7.2
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smt_p.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	SLIM_SMT
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)pmf.c	1.37 97/08/04 (C) SK " ;
+#endif
+
+static int smt_authorize() ;
+static int smt_check_set_count() ;
+static const struct s_p_tab *smt_get_ptab() ;
+static int smt_mib_phys() ;
+int smt_set_para() ;
+void smt_add_para() ;
+
+#define MOFFSS(e)	((int)&(((struct fddi_mib *)0)->e))
+#define MOFFSA(e)	((int) (((struct fddi_mib *)0)->e))
+
+#define MOFFMS(e)	((int)&(((struct fddi_mib_m *)0)->e))
+#define MOFFMA(e)	((int) (((struct fddi_mib_m *)0)->e))
+
+#define MOFFAS(e)	((int)&(((struct fddi_mib_a *)0)->e))
+#define MOFFAA(e)	((int) (((struct fddi_mib_a *)0)->e))
+
+#define MOFFPS(e)	((int)&(((struct fddi_mib_p *)0)->e))
+#define MOFFPA(e)	((int) (((struct fddi_mib_p *)0)->e))
+
+
+#define AC_G	0x01		/* Get */
+#define AC_GR	0x02		/* Get/Set */
+#define AC_S	0x04		/* Set */
+#define AC_NA	0x08
+#define AC_GROUP	0x10		/* Group */
+#define MS2BCLK(x)	((x)*12500L)
+/*
+	F	LFag (byte)
+	B	byte
+	S	u_short	16 bit
+	C	Counter 32 bit
+	L	Long 32 bit
+	T	Timer_2	32 bit
+	P	TimeStamp ;
+	A	LongAddress (6 byte)
+	E	Enum 16 bit
+	R	ResId 16 Bit
+*/
+static const struct s_p_tab {
+	u_short	p_num ;		/* parameter code */
+	u_char	p_access ;	/* access rights */
+	u_short	p_offset ;	/* offset in mib */
+	char	p_swap[3] ;	/* format string */
+} p_tab[] = {
+	/* StationIdGrp */
+	{ SMT_P100A,AC_GROUP	} ,
+	{ SMT_P100B,AC_G,	MOFFSS(fddiSMTStationId),	"8"	} ,
+	{ SMT_P100D,AC_G,	MOFFSS(fddiSMTOpVersionId),	"S"	} ,
+	{ SMT_P100E,AC_G,	MOFFSS(fddiSMTHiVersionId),	"S"	} ,
+	{ SMT_P100F,AC_G,	MOFFSS(fddiSMTLoVersionId),	"S"	} ,
+	{ SMT_P1010,AC_G,	MOFFSA(fddiSMTManufacturerData), "D" } ,
+	{ SMT_P1011,AC_GR,	MOFFSA(fddiSMTUserData),	"D"	} ,
+	{ SMT_P1012,AC_G,	MOFFSS(fddiSMTMIBVersionId),	"S"	} ,
+
+	/* StationConfigGrp */
+	{ SMT_P1014,AC_GROUP	} ,
+	{ SMT_P1015,AC_G,	MOFFSS(fddiSMTMac_Ct),		"B"	} ,
+	{ SMT_P1016,AC_G,	MOFFSS(fddiSMTNonMaster_Ct),	"B"	} ,
+	{ SMT_P1017,AC_G,	MOFFSS(fddiSMTMaster_Ct),	"B"	} ,
+	{ SMT_P1018,AC_G,	MOFFSS(fddiSMTAvailablePaths),	"B"	} ,
+	{ SMT_P1019,AC_G,	MOFFSS(fddiSMTConfigCapabilities),"S"	} ,
+	{ SMT_P101A,AC_GR,	MOFFSS(fddiSMTConfigPolicy),	"wS"	} ,
+	{ SMT_P101B,AC_GR,	MOFFSS(fddiSMTConnectionPolicy),"wS"	} ,
+	{ SMT_P101D,AC_GR,	MOFFSS(fddiSMTTT_Notify),	"wS"	} ,
+	{ SMT_P101E,AC_GR,	MOFFSS(fddiSMTStatRptPolicy),	"bB"	} ,
+	{ SMT_P101F,AC_GR,	MOFFSS(fddiSMTTrace_MaxExpiration),"lL"	} ,
+	{ SMT_P1020,AC_G,	MOFFSA(fddiSMTPORTIndexes),	"II"	} ,
+	{ SMT_P1021,AC_G,	MOFFSS(fddiSMTMACIndexes),	"I"	} ,
+	{ SMT_P1022,AC_G,	MOFFSS(fddiSMTBypassPresent),	"F"	} ,
+
+	/* StatusGrp */
+	{ SMT_P1028,AC_GROUP	} ,
+	{ SMT_P1029,AC_G,	MOFFSS(fddiSMTECMState),	"E"	} ,
+	{ SMT_P102A,AC_G,	MOFFSS(fddiSMTCF_State),	"E"	} ,
+	{ SMT_P102C,AC_G,	MOFFSS(fddiSMTRemoteDisconnectFlag),"F"	} ,
+	{ SMT_P102D,AC_G,	MOFFSS(fddiSMTStationStatus),	"E"	} ,
+	{ SMT_P102E,AC_G,	MOFFSS(fddiSMTPeerWrapFlag),	"F"	} ,
+
+	/* MIBOperationGrp */
+	{ SMT_P1032,AC_GROUP	} ,
+	{ SMT_P1033,AC_G,	MOFFSA(fddiSMTTimeStamp),"P"		} ,
+	{ SMT_P1034,AC_G,	MOFFSA(fddiSMTTransitionTimeStamp),"P"	} ,
+	/* NOTE : SMT_P1035 is already swapped ! SMT_P_SETCOUNT */
+	{ SMT_P1035,AC_G,	MOFFSS(fddiSMTSetCount),"4P"		} ,
+	{ SMT_P1036,AC_G,	MOFFSS(fddiSMTLastSetStationId),"8"	} ,
+
+	{ SMT_P103C,AC_S,	0,				"wS"	} ,
+
+	/*
+	 * PRIVATE EXTENSIONS
+	 * only accessable locally to get/set passwd
+	 */
+	{ SMT_P10F0,AC_GR,	MOFFSA(fddiPRPMFPasswd),	"8"	} ,
+	{ SMT_P10F1,AC_GR,	MOFFSS(fddiPRPMFStation),	"8"	} ,
+#ifdef	ESS
+	{ SMT_P10F2,AC_GR,	MOFFSS(fddiESSPayload),		"lL"	} ,
+	{ SMT_P10F3,AC_GR,	MOFFSS(fddiESSOverhead),	"lL"	} ,
+	{ SMT_P10F4,AC_GR,	MOFFSS(fddiESSMaxTNeg),		"lL"	} ,
+	{ SMT_P10F5,AC_GR,	MOFFSS(fddiESSMinSegmentSize),	"lL"	} ,
+	{ SMT_P10F6,AC_GR,	MOFFSS(fddiESSCategory),	"lL"	} ,
+	{ SMT_P10F7,AC_GR,	MOFFSS(fddiESSSynchTxMode),	"wS"	} ,
+#endif
+#ifdef	SBA
+	{ SMT_P10F8,AC_GR,	MOFFSS(fddiSBACommand),		"bF"	} ,
+	{ SMT_P10F9,AC_GR,	MOFFSS(fddiSBAAvailable),	"bF"	} ,
+#endif
+	/* MAC Attributes */
+	{ SMT_P200A,AC_GROUP	} ,
+	{ SMT_P200B,AC_G,	MOFFMS(fddiMACFrameStatusFunctions),"S"	} ,
+	{ SMT_P200D,AC_G,	MOFFMS(fddiMACT_MaxCapabilitiy),"T"	} ,
+	{ SMT_P200E,AC_G,	MOFFMS(fddiMACTVXCapabilitiy),"T"	} ,
+
+	/* ConfigGrp */
+	{ SMT_P2014,AC_GROUP	} ,
+	{ SMT_P2016,AC_G,	MOFFMS(fddiMACAvailablePaths),	"B"	} ,
+	{ SMT_P2017,AC_G,	MOFFMS(fddiMACCurrentPath),	"S"	} ,
+	{ SMT_P2018,AC_G,	MOFFMS(fddiMACUpstreamNbr),	"A"	} ,
+	{ SMT_P2019,AC_G,	MOFFMS(fddiMACDownstreamNbr),	"A"	} ,
+	{ SMT_P201A,AC_G,	MOFFMS(fddiMACOldUpstreamNbr),	"A"	} ,
+	{ SMT_P201B,AC_G,	MOFFMS(fddiMACOldDownstreamNbr),"A"	} ,
+	{ SMT_P201D,AC_G,	MOFFMS(fddiMACDupAddressTest),	"E"	} ,
+	{ SMT_P2020,AC_GR,	MOFFMS(fddiMACRequestedPaths),	"wS"	} ,
+	{ SMT_P2021,AC_G,	MOFFMS(fddiMACDownstreamPORTType),"E"	} ,
+	{ SMT_P2022,AC_G,	MOFFMS(fddiMACIndex),		"S"	} ,
+
+	/* AddressGrp */
+	{ SMT_P2028,AC_GROUP	} ,
+	{ SMT_P2029,AC_G,	MOFFMS(fddiMACSMTAddress),	"A"	} ,
+
+	/* OperationGrp */
+	{ SMT_P2032,AC_GROUP	} ,
+	{ SMT_P2033,AC_G,	MOFFMS(fddiMACT_Req),		"T"	} ,
+	{ SMT_P2034,AC_G,	MOFFMS(fddiMACT_Neg),		"T"	} ,
+	{ SMT_P2035,AC_G,	MOFFMS(fddiMACT_Max),		"T"	} ,
+	{ SMT_P2036,AC_G,	MOFFMS(fddiMACTvxValue),	"T"	} ,
+	{ SMT_P2038,AC_G,	MOFFMS(fddiMACT_Pri0),		"T"	} ,
+	{ SMT_P2039,AC_G,	MOFFMS(fddiMACT_Pri1),		"T"	} ,
+	{ SMT_P203A,AC_G,	MOFFMS(fddiMACT_Pri2),		"T"	} ,
+	{ SMT_P203B,AC_G,	MOFFMS(fddiMACT_Pri3),		"T"	} ,
+	{ SMT_P203C,AC_G,	MOFFMS(fddiMACT_Pri4),		"T"	} ,
+	{ SMT_P203D,AC_G,	MOFFMS(fddiMACT_Pri5),		"T"	} ,
+	{ SMT_P203E,AC_G,	MOFFMS(fddiMACT_Pri6),		"T"	} ,
+
+
+	/* CountersGrp */
+	{ SMT_P2046,AC_GROUP	} ,
+	{ SMT_P2047,AC_G,	MOFFMS(fddiMACFrame_Ct),	"C"	} ,
+	{ SMT_P2048,AC_G,	MOFFMS(fddiMACCopied_Ct),	"C"	} ,
+	{ SMT_P2049,AC_G,	MOFFMS(fddiMACTransmit_Ct),	"C"	} ,
+	{ SMT_P204A,AC_G,	MOFFMS(fddiMACToken_Ct),	"C"	} ,
+	{ SMT_P2051,AC_G,	MOFFMS(fddiMACError_Ct),	"C"	} ,
+	{ SMT_P2052,AC_G,	MOFFMS(fddiMACLost_Ct),		"C"	} ,
+	{ SMT_P2053,AC_G,	MOFFMS(fddiMACTvxExpired_Ct),	"C"	} ,
+	{ SMT_P2054,AC_G,	MOFFMS(fddiMACNotCopied_Ct),	"C"	} ,
+	{ SMT_P2056,AC_G,	MOFFMS(fddiMACRingOp_Ct),	"C"	} ,
+
+	/* FrameErrorConditionGrp */
+	{ SMT_P205A,AC_GROUP	} ,
+	{ SMT_P205F,AC_GR,	MOFFMS(fddiMACFrameErrorThreshold),"wS"	} ,
+	{ SMT_P2060,AC_G,	MOFFMS(fddiMACFrameErrorRatio),	"S"	} ,
+
+	/* NotCopiedConditionGrp */
+	{ SMT_P2064,AC_GROUP	} ,
+	{ SMT_P2067,AC_GR,	MOFFMS(fddiMACNotCopiedThreshold),"wS"	} ,
+	{ SMT_P2069,AC_G,	MOFFMS(fddiMACNotCopiedRatio),	"S"	} ,
+
+	/* StatusGrp */
+	{ SMT_P206E,AC_GROUP	} ,
+	{ SMT_P206F,AC_G,	MOFFMS(fddiMACRMTState),	"S"	} ,
+	{ SMT_P2070,AC_G,	MOFFMS(fddiMACDA_Flag),	"F"	} ,
+	{ SMT_P2071,AC_G,	MOFFMS(fddiMACUNDA_Flag),	"F"	} ,
+	{ SMT_P2072,AC_G,	MOFFMS(fddiMACFrameErrorFlag),	"F"	} ,
+	{ SMT_P2073,AC_G,	MOFFMS(fddiMACNotCopiedFlag),	"F"	} ,
+	{ SMT_P2074,AC_G,	MOFFMS(fddiMACMA_UnitdataAvailable),"F"	} ,
+	{ SMT_P2075,AC_G,	MOFFMS(fddiMACHardwarePresent),	"F"	} ,
+	{ SMT_P2076,AC_GR,	MOFFMS(fddiMACMA_UnitdataEnable),"bF"	} ,
+
+	/*
+	 * PRIVATE EXTENSIONS
+	 * only accessable locally to get/set TMIN
+	 */
+	{ SMT_P20F0,AC_NA						} ,
+	{ SMT_P20F1,AC_GR,	MOFFMS(fddiMACT_Min),		"lT"	} ,
+
+	/* Path Attributes */
+	/*
+	 * DON't swap 320B,320F,3210: they are already swapped in swap_para()
+	 */
+	{ SMT_P320A,AC_GROUP	} ,
+	{ SMT_P320B,AC_G,	MOFFAS(fddiPATHIndex),		"r"	} ,
+	{ SMT_P320F,AC_GR,	MOFFAS(fddiPATHSbaPayload),	"l4"	} ,
+	{ SMT_P3210,AC_GR,	MOFFAS(fddiPATHSbaOverhead),	"l4"	} ,
+	/* fddiPATHConfiguration */
+	{ SMT_P3212,AC_G,	0,				""	} ,
+	{ SMT_P3213,AC_GR,	MOFFAS(fddiPATHT_Rmode),	"lT"	} ,
+	{ SMT_P3214,AC_GR,	MOFFAS(fddiPATHSbaAvailable),	"lL"	} ,
+	{ SMT_P3215,AC_GR,	MOFFAS(fddiPATHTVXLowerBound),	"lT"	} ,
+	{ SMT_P3216,AC_GR,	MOFFAS(fddiPATHT_MaxLowerBound),"lT"	} ,
+	{ SMT_P3217,AC_GR,	MOFFAS(fddiPATHMaxT_Req),	"lT"	} ,
+
+	/* Port Attributes */
+	/* ConfigGrp */
+	{ SMT_P400A,AC_GROUP	} ,
+	{ SMT_P400C,AC_G,	MOFFPS(fddiPORTMy_Type),	"E"	} ,
+	{ SMT_P400D,AC_G,	MOFFPS(fddiPORTNeighborType),	"E"	} ,
+	{ SMT_P400E,AC_GR,	MOFFPS(fddiPORTConnectionPolicies),"bB"	} ,
+	{ SMT_P400F,AC_G,	MOFFPS(fddiPORTMacIndicated),	"2"	} ,
+	{ SMT_P4010,AC_G,	MOFFPS(fddiPORTCurrentPath),	"E"	} ,
+	{ SMT_P4011,AC_GR,	MOFFPA(fddiPORTRequestedPaths),	"l4"	} ,
+	{ SMT_P4012,AC_G,	MOFFPS(fddiPORTMACPlacement),	"S"	} ,
+	{ SMT_P4013,AC_G,	MOFFPS(fddiPORTAvailablePaths),	"B"	} ,
+	{ SMT_P4016,AC_G,	MOFFPS(fddiPORTPMDClass),	"E"	} ,
+	{ SMT_P4017,AC_G,	MOFFPS(fddiPORTConnectionCapabilities),	"B"} ,
+	{ SMT_P401D,AC_G,	MOFFPS(fddiPORTIndex),		"R"	} ,
+
+	/* OperationGrp */
+	{ SMT_P401E,AC_GROUP	} ,
+	{ SMT_P401F,AC_GR,	MOFFPS(fddiPORTMaint_LS),	"wE"	} ,
+	{ SMT_P4021,AC_G,	MOFFPS(fddiPORTBS_Flag),	"F"	} ,
+	{ SMT_P4022,AC_G,	MOFFPS(fddiPORTPC_LS),		"E"	} ,
+
+	/* ErrorCtrsGrp */
+	{ SMT_P4028,AC_GROUP	} ,
+	{ SMT_P4029,AC_G,	MOFFPS(fddiPORTEBError_Ct),	"C"	} ,
+	{ SMT_P402A,AC_G,	MOFFPS(fddiPORTLCTFail_Ct),	"C"	} ,
+
+	/* LerGrp */
+	{ SMT_P4032,AC_GROUP	} ,
+	{ SMT_P4033,AC_G,	MOFFPS(fddiPORTLer_Estimate),	"F"	} ,
+	{ SMT_P4034,AC_G,	MOFFPS(fddiPORTLem_Reject_Ct),	"C"	} ,
+	{ SMT_P4035,AC_G,	MOFFPS(fddiPORTLem_Ct),		"C"	} ,
+	{ SMT_P403A,AC_GR,	MOFFPS(fddiPORTLer_Cutoff),	"bB"	} ,
+	{ SMT_P403B,AC_GR,	MOFFPS(fddiPORTLer_Alarm),	"bB"	} ,
+
+	/* StatusGrp */
+	{ SMT_P403C,AC_GROUP	} ,
+	{ SMT_P403D,AC_G,	MOFFPS(fddiPORTConnectState),	"E"	} ,
+	{ SMT_P403E,AC_G,	MOFFPS(fddiPORTPCMStateX),	"E"	} ,
+	{ SMT_P403F,AC_G,	MOFFPS(fddiPORTPC_Withhold),	"E"	} ,
+	{ SMT_P4040,AC_G,	MOFFPS(fddiPORTLerFlag),	"F"	} ,
+	{ SMT_P4041,AC_G,	MOFFPS(fddiPORTHardwarePresent),"F"	} ,
+
+	{ SMT_P4046,AC_S,	0,				"wS"	} ,
+
+	{ 0,	AC_GROUP	} ,
+	{ 0 }
+} ;
+
+
+static SMbuf *smt_build_pmf_response() ;
+
+void smt_pmf_received_pack(smc,mb,local)
+struct s_smc *smc ;
+SMbuf *mb ;
+int local ;
+{
+	struct smt_header	*sm ;
+	SMbuf		*reply ;
+
+	sm = smtod(mb,struct smt_header *) ;
+	DB_SMT("SMT: processing PMF frame at %x len %d\n",sm,mb->sm_len) ;
+#ifdef	DEBUG
+	dump_smt(smc,sm,"PMF Received") ;
+#endif
+	/*
+	 * Start the watchdog: It may be a long, long packet and
+	 * maybe the watchdog occurs ...
+	 */
+	smt_start_watchdog(smc) ;
+
+	if (sm->smt_class == SMT_PMF_GET ||
+	    sm->smt_class == SMT_PMF_SET) {
+		reply = smt_build_pmf_response(smc,sm,
+			sm->smt_class == SMT_PMF_SET,local) ;
+		if (reply) {
+			sm = smtod(reply,struct smt_header *) ;
+#ifdef	DEBUG
+			dump_smt(smc,sm,"PMF Reply") ;
+#endif
+			smt_send_frame(smc,reply,FC_SMT_INFO,local) ;
+		}
+	}
+}
+
+extern	SMbuf	*smt_get_mbuf() ;
+
+static SMbuf *smt_build_pmf_response(smc,req,set,local)
+struct s_smc *smc ;
+struct smt_header *req ;
+int	set ;
+int	local ;
+{
+	SMbuf			*mb ;
+	struct smt_header	*smt ;
+	struct smt_para		*pa ;
+	struct smt_p_reason	*res ;
+	const struct s_p_tab	*pt ;
+	int			len ;
+	int			index ;
+	int			idx_end ;
+	int			error ;
+	int			range ;
+	SK_LOC_DECL(struct s_pcon,pcon) ;
+	SK_LOC_DECL(struct s_pcon,set_pcon) ;
+
+	/*
+	 * build SMT header
+	 */
+	if (!(mb = smt_get_mbuf(smc)))
+		return(mb) ;
+
+	smt = smtod(mb, struct smt_header *) ;
+	smt->smt_dest = req->smt_source ;	/* DA == source of request */
+	smt->smt_class = req->smt_class ;	/* same class (GET/SET) */
+	smt->smt_type = SMT_REPLY ;
+	smt->smt_version = SMT_VID_2 ;
+	smt->smt_tid = req->smt_tid ;		/* same TID */
+	smt->smt_pad = 0 ;
+	smt->smt_len = 0 ;
+
+	/*
+	 * setup parameter status
+	 */
+	pcon.pc_len = SMT_MAX_INFO_LEN ;	/* max para length */
+	pcon.pc_err = 0 ;			/* no error */
+	pcon.pc_badset = 0 ;			/* no bad set count */
+	pcon.pc_p = (void *) (smt + 1) ;	/* paras start here */
+
+	/*
+	 * check authoriziation and set count
+	 */
+	error = 0 ;
+	if (set) {
+		if (!local && smt_authorize(smc,req))
+			error = SMT_RDF_AUTHOR ;
+		else if (smt_check_set_count(smc,req))
+			pcon.pc_badset = SMT_RDF_BADSET ;
+	}
+	/*
+	 * add reason code and all mandatory parameters
+	 */
+	res = (struct smt_p_reason *) pcon.pc_p ;
+	smt_add_para(smc,&pcon,(u_short) SMT_P_REASON,0,0) ;
+	smt_add_para(smc,&pcon,(u_short) SMT_P1033,0,0) ;
+	/* update 1035 and 1036 later if set */
+	set_pcon = pcon ;
+	smt_add_para(smc,&pcon,(u_short) SMT_P1035,0,0) ;
+	smt_add_para(smc,&pcon,(u_short) SMT_P1036,0,0) ;
+
+	pcon.pc_err = error ;
+	len = req->smt_len ;
+	pa = (struct smt_para *) (req + 1) ;
+	/*
+	 * process list of paras
+	 */
+	while (!pcon.pc_err && len > 0 ) {
+		if (((u_short)len < pa->p_len + PARA_LEN) || (pa->p_len & 3)) {
+			pcon.pc_err = SMT_RDF_LENGTH ;
+			break ;
+		}
+
+		if (((range = (pa->p_type & 0xf000)) == 0x2000) ||
+			range == 0x3000 || range == 0x4000) {
+			/*
+			 * get index for PART,MAC ad PATH group
+			 */
+			index = *((u_char *)pa + PARA_LEN + 3) ;/* index */
+			idx_end = index ;
+			if (!set && (pa->p_len != 4)) {
+				pcon.pc_err = SMT_RDF_LENGTH ;
+				break ;
+			}
+			if (!index && !set) {
+				switch (range) {
+				case 0x2000 :
+					index = INDEX_MAC ;
+					idx_end = index - 1 + NUMMACS ;
+					break ;
+				case 0x3000 :
+					index = INDEX_PATH ;
+					idx_end = index - 1 + NUMPATHS ;
+					break ;
+				case 0x4000 :
+					index = INDEX_PORT ;
+					idx_end = index - 1 + NUMPHYS ;
+#ifndef	CONCENTRATOR
+					if (smc->s.sas == SMT_SAS)
+						idx_end = INDEX_PORT ;
+#endif
+					break ;
+				}
+			}
+		}
+		else {
+			/*
+			 * smt group has no index
+			 */
+			if (!set && (pa->p_len != 0)) {
+				pcon.pc_err = SMT_RDF_LENGTH ;
+				break ;
+			}
+			index = 0 ;
+			idx_end = 0 ;
+		}
+		while (index <= idx_end) {
+			/*
+			 * if group
+			 *	add all paras of group
+			 */
+			pt = smt_get_ptab(pa->p_type) ;
+			if (pt && pt->p_access == AC_GROUP && !set) {
+				pt++ ;
+				while (pt->p_access == AC_G ||
+					pt->p_access == AC_GR) {
+					smt_add_para(smc,&pcon,pt->p_num,
+						index,local);
+					pt++ ;
+				}
+			}
+			/*
+			 * ignore
+			 *	AUTHORIZATION in get/set
+			 *	SET COUNT in set
+			 */
+			else if (pa->p_type != SMT_P_AUTHOR &&
+				 (!set || (pa->p_type != SMT_P1035))) {
+				int	st ;
+				if (pcon.pc_badset) {
+					smt_add_para(smc,&pcon,pa->p_type,
+						index,local) ;
+				}
+				else if (set) {
+					st = smt_set_para(smc,pa,index,local,1);
+					/*
+					 * return para even if error
+					 */
+					smt_add_para(smc,&pcon,pa->p_type,
+						index,local) ;
+					pcon.pc_err = st ;
+				}
+				else {
+					if (pt && pt->p_access == AC_S) {
+						pcon.pc_err =
+							SMT_RDF_ILLEGAL ;
+					}
+					smt_add_para(smc,&pcon,pa->p_type,
+						index,local) ;
+				}
+			}
+			if (pcon.pc_err)
+				break ;
+			index++ ;
+		}
+		len -= pa->p_len + PARA_LEN ;
+		pa = (struct smt_para *) ((char *)pa + pa->p_len + PARA_LEN) ;
+	}
+	smt->smt_len = SMT_MAX_INFO_LEN - pcon.pc_len ;
+	mb->sm_len = smt->smt_len + sizeof(struct smt_header) ;
+
+	/* update reason code */
+	res->rdf_reason = pcon.pc_badset ? pcon.pc_badset :
+			pcon.pc_err ? pcon.pc_err : SMT_RDF_SUCCESS ;
+	if (set && (res->rdf_reason == SMT_RDF_SUCCESS)) {
+		/*
+		 * increment set count
+		 * set time stamp
+		 * store station id of last set
+		 */
+		smc->mib.fddiSMTSetCount.count++ ;
+		smt_set_timestamp(smc,smc->mib.fddiSMTSetCount.timestamp) ;
+		smc->mib.fddiSMTLastSetStationId = req->smt_sid ;
+		smt_add_para(smc,&set_pcon,(u_short) SMT_P1035,0,0) ;
+		smt_add_para(smc,&set_pcon,(u_short) SMT_P1036,0,0) ;
+	}
+	return(mb) ;
+}
+
+extern void *sm_to_para() ;
+
+static int smt_authorize(smc,sm)
+struct s_smc *smc ;
+struct smt_header *sm ;
+{
+	struct smt_para	*pa ;
+	int		i ;
+	char		*p ;
+
+	/*
+	 * check source station id if not zero
+	 */
+	p = (char *) &smc->mib.fddiPRPMFStation ;
+	for (i = 0 ; i < 8 && !p[i] ; i++)
+		;
+	if (i != 8) {
+		if (memcmp((char *) &sm->smt_sid,
+			(char *) &smc->mib.fddiPRPMFStation,8))
+			return(1) ;
+	}
+	/*
+	 * check authoriziation parameter if passwd not zero
+	 */
+	p = (char *) smc->mib.fddiPRPMFPasswd ;
+	for (i = 0 ; i < 8 && !p[i] ; i++)
+		;
+	if (i != 8) {
+		pa = (struct smt_para *) sm_to_para(smc,sm,SMT_P_AUTHOR) ;
+		if (!pa)
+			return(1) ;
+		if (pa->p_len != 8)
+			return(1) ;
+		if (memcmp((char *)(pa+1),(char *)smc->mib.fddiPRPMFPasswd,8))
+			return(1) ;
+	}
+	return(0) ;
+}
+
+static int smt_check_set_count(smc,sm)
+struct s_smc *smc ;
+struct smt_header *sm ;
+{
+	struct smt_para	*pa ;
+	struct smt_p_setcount	*sc ;
+
+	pa = (struct smt_para *) sm_to_para(smc,sm,SMT_P1035) ;
+	if (pa) {
+		sc = (struct smt_p_setcount *) pa ;
+		if ((smc->mib.fddiSMTSetCount.count != sc->count) ||
+			memcmp((char *) smc->mib.fddiSMTSetCount.timestamp,
+			(char *)sc->timestamp,8))
+			return(1) ;
+	}
+	return(0) ;
+}
+
+void smt_add_para(smc,pcon,para,index,local)
+struct s_smc *smc ;
+struct s_pcon *pcon ;
+u_short para ;
+int index ;
+int local ;
+{
+	struct smt_para	*pa ;
+	const struct s_p_tab	*pt ;
+	struct fddi_mib_m *mib_m = 0 ;
+	struct fddi_mib_p *mib_p = 0 ;
+	int		len ;
+	int		plen ;
+	char		*from ;
+	char		*to ;
+	const char	*swap ;
+	char		c ;
+	int		range ;
+	char		*mib_addr ;
+	int		mac ;
+	int		path ;
+	int		port ;
+	int		sp_len ;
+
+	/*
+	 * skip if errror
+	 */
+	if (pcon->pc_err)
+		return ;
+
+	/*
+	 * actions don't have a value
+	 */
+	pt = smt_get_ptab(para) ;
+	if (pt && pt->p_access == AC_S)
+		return ;
+
+	to = (char *) (pcon->pc_p) ;	/* destination pointer */
+	len = pcon->pc_len ;		/* free space */
+	plen = len ;			/* remember start length */
+	pa = (struct smt_para *) to ;	/* type/length pointer */
+	to += PARA_LEN ;		/* skip smt_para */
+	len -= PARA_LEN ;
+	/*
+	 * set index if required
+	 */
+	if (((range = (para & 0xf000)) == 0x2000) ||
+		range == 0x3000 || range == 0x4000) {
+		if (len < 4)
+			goto wrong_error ;
+		to[0] = 0 ;
+		to[1] = 0 ;
+		to[2] = 0 ;
+		to[3] = index ;
+		len -= 4 ;
+		to += 4 ;
+	}
+	mac = index - INDEX_MAC ;
+	path = index - INDEX_PATH ;
+	port = index - INDEX_PORT ;
+	/*
+	 * get pointer to mib
+	 */
+	switch (range) {
+	case 0x1000 :
+	default :
+		mib_addr = (char *) (&smc->mib) ;
+		break ;
+	case 0x2000 :
+		if (mac < 0 || mac >= NUMMACS) {
+			pcon->pc_err = SMT_RDF_NOPARAM ;
+			return ;
+		}
+		mib_addr = (char *) (&smc->mib.m[mac]) ;
+		mib_m = (struct fddi_mib_m *) mib_addr ;
+		break ;
+	case 0x3000 :
+		if (path < 0 || path >= NUMPATHS) {
+			pcon->pc_err = SMT_RDF_NOPARAM ;
+			return ;
+		}
+		mib_addr = (char *) (&smc->mib.a[path]) ;
+		break ;
+	case 0x4000 :
+		if (port < 0 || port >= smt_mib_phys(smc)) {
+			pcon->pc_err = SMT_RDF_NOPARAM ;
+			return ;
+		}
+		mib_addr = (char *) (&smc->mib.p[port_to_mib(smc,port)]) ;
+		mib_p = (struct fddi_mib_p *) mib_addr ;
+		break ;
+	}
+	/*
+	 * check special paras
+	 */
+	swap = 0 ;
+	switch (para) {
+	case SMT_P10F0 :
+	case SMT_P10F1 :
+#ifdef	ESS
+	case SMT_P10F2 :
+	case SMT_P10F3 :
+	case SMT_P10F4 :
+	case SMT_P10F5 :
+	case SMT_P10F6 :
+	case SMT_P10F7 :
+#endif
+#ifdef	SBA
+	case SMT_P10F8 :
+	case SMT_P10F9 :
+#endif
+	case SMT_P20F1 :
+		if (!local) {
+			pcon->pc_err = SMT_RDF_NOPARAM ;
+			return ;
+		}
+		break ;
+	case SMT_P2034 :
+	case SMT_P2046 :
+	case SMT_P2047 :
+	case SMT_P204A :
+	case SMT_P2051 :
+	case SMT_P2052 :
+		mac_update_counter(smc) ;
+		break ;
+	case SMT_P4022:
+		mib_p->fddiPORTPC_LS = LS2MIB(
+			sm_pm_get_ls(smc,port_to_mib(smc,port))) ;
+		break ;
+	case SMT_P_REASON :
+		* (u_long *) to = 0 ;
+		sp_len = 4 ;
+		goto sp_done ;
+	case SMT_P1033 :			/* time stamp */
+		smt_set_timestamp(smc,smc->mib.fddiSMTTimeStamp) ;
+		break ;
+
+	case SMT_P1020:				/* port indexes */
+#if	NUMPHYS == 12
+		swap = "IIIIIIIIIIII" ;
+#else
+#if	NUMPHYS == 2
+		if (smc->s.sas == SMT_SAS)
+			swap = "I" ;
+		else
+			swap = "II" ;
+#else
+#if	NUMPHYS == 24
+		swap = "IIIIIIIIIIIIIIIIIIIIIIII" ;
+#else
+	????
+#endif
+#endif
+#endif
+		break ;
+	case SMT_P3212 :
+		{
+			sp_len = cem_build_path(smc,to,path) ;
+			goto sp_done ;
+		}
+	case SMT_P1048 :		/* peer wrap condition */
+		{
+			struct smt_p_1048	*sp ;
+			sp = (struct smt_p_1048 *) to ;
+			sp->p1048_flag = smc->mib.fddiSMTPeerWrapFlag ;
+			sp->p1048_cf_state = smc->mib.fddiSMTCF_State ;
+			sp_len = sizeof(struct smt_p_1048) ;
+			goto sp_done ;
+		}
+	case SMT_P208C :
+		{
+			struct smt_p_208c	*sp ;
+			sp = (struct smt_p_208c *) to ;
+			sp->p208c_flag =
+				smc->mib.m[MAC0].fddiMACDuplicateAddressCond ;
+			sp->p208c_dupcondition =
+				(mib_m->fddiMACDA_Flag ? SMT_ST_MY_DUPA : 0) |
+				(mib_m->fddiMACUNDA_Flag ? SMT_ST_UNA_DUPA : 0);
+			sp->p208c_fddilong =
+				mib_m->fddiMACSMTAddress ;
+			sp->p208c_fddiunalong =
+				mib_m->fddiMACUpstreamNbr ;
+			sp->p208c_pad = 0 ;
+			sp_len = sizeof(struct smt_p_208c) ;
+			goto sp_done ;
+		}
+	case SMT_P208D :		/* frame error condition */
+		{
+			struct smt_p_208d	*sp ;
+			sp = (struct smt_p_208d *) to ;
+			sp->p208d_flag =
+				mib_m->fddiMACFrameErrorFlag ;
+			sp->p208d_frame_ct =
+				mib_m->fddiMACFrame_Ct ;
+			sp->p208d_error_ct =
+				mib_m->fddiMACError_Ct ;
+			sp->p208d_lost_ct =
+				mib_m->fddiMACLost_Ct ;
+			sp->p208d_ratio =
+				mib_m->fddiMACFrameErrorRatio ;
+			sp_len = sizeof(struct smt_p_208d) ;
+			goto sp_done ;
+		}
+	case SMT_P208E :		/* not copied condition */
+		{
+			struct smt_p_208e	*sp ;
+			sp = (struct smt_p_208e *) to ;
+			sp->p208e_flag =
+				mib_m->fddiMACNotCopiedFlag ;
+			sp->p208e_not_copied =
+				mib_m->fddiMACNotCopied_Ct ;
+			sp->p208e_copied =
+				mib_m->fddiMACCopied_Ct ;
+			sp->p208e_not_copied_ratio =
+				mib_m->fddiMACNotCopiedRatio ;
+			sp_len = sizeof(struct smt_p_208e) ;
+			goto sp_done ;
+		}
+	case SMT_P208F :	/* neighbor change event */
+		{
+			struct smt_p_208f	*sp ;
+			sp = (struct smt_p_208f *) to ;
+			sp->p208f_multiple =
+				mib_m->fddiMACMultiple_N ;
+			sp->p208f_nacondition =
+				mib_m->fddiMACDuplicateAddressCond ;
+			sp->p208f_old_una =
+				mib_m->fddiMACOldUpstreamNbr ;
+			sp->p208f_new_una =
+				mib_m->fddiMACUpstreamNbr ;
+			sp->p208f_old_dna =
+				mib_m->fddiMACOldDownstreamNbr ;
+			sp->p208f_new_dna =
+				mib_m->fddiMACDownstreamNbr ;
+			sp->p208f_curren_path =
+				mib_m->fddiMACCurrentPath ;
+			sp->p208f_smt_address =
+				mib_m->fddiMACSMTAddress ;
+			sp_len = sizeof(struct smt_p_208f) ;
+			goto sp_done ;
+		}
+	case SMT_P2090 :
+		{
+			struct smt_p_2090	*sp ;
+			sp = (struct smt_p_2090 *) to ;
+			sp->p2090_multiple =
+				mib_m->fddiMACMultiple_P ;
+			sp->p2090_availablepaths =
+				mib_m->fddiMACAvailablePaths ;
+			sp->p2090_currentpath =
+				mib_m->fddiMACCurrentPath ;
+			sp->p2090_requestedpaths =
+				mib_m->fddiMACRequestedPaths ;
+			sp_len = sizeof(struct smt_p_2090) ;
+			goto sp_done ;
+		}
+	case SMT_P4050 :
+		{
+			struct smt_p_4050	*sp ;
+			sp = (struct smt_p_4050 *) to ;
+			sp->p4050_flag =
+				mib_p->fddiPORTLerFlag ;
+			sp->p4050_pad = 0 ;
+			sp->p4050_cutoff =
+				mib_p->fddiPORTLer_Cutoff ; ;
+			sp->p4050_alarm =
+				mib_p->fddiPORTLer_Alarm ; ;
+			sp->p4050_estimate =
+				mib_p->fddiPORTLer_Estimate ;
+			sp->p4050_reject_ct =
+				mib_p->fddiPORTLem_Reject_Ct ;
+			sp->p4050_ct =
+				mib_p->fddiPORTLem_Ct ;
+			sp_len = sizeof(struct smt_p_4050) ;
+			goto sp_done ;
+		}
+
+	case SMT_P4051 :
+		{
+			struct smt_p_4051	*sp ;
+			sp = (struct smt_p_4051 *) to ;
+			sp->p4051_multiple =
+				mib_p->fddiPORTMultiple_U ;
+			sp->p4051_porttype =
+				mib_p->fddiPORTMy_Type ;
+			sp->p4051_connectstate =
+				mib_p->fddiPORTConnectState ; ;
+			sp->p4051_pc_neighbor =
+				mib_p->fddiPORTNeighborType ;
+			sp->p4051_pc_withhold =
+				mib_p->fddiPORTPC_Withhold ;
+			sp_len = sizeof(struct smt_p_4051) ;
+			goto sp_done ;
+		}
+	case SMT_P4052 :
+		{
+			struct smt_p_4052	*sp ;
+			sp = (struct smt_p_4052 *) to ;
+			sp->p4052_flag =
+				mib_p->fddiPORTEB_Condition ;
+			sp->p4052_eberrorcount =
+				mib_p->fddiPORTEBError_Ct ;
+			sp_len = sizeof(struct smt_p_4052) ;
+			goto sp_done ;
+		}
+	case SMT_P4053 :
+		{
+			struct smt_p_4053	*sp ;
+			sp = (struct smt_p_4053 *) to ;
+			sp->p4053_multiple =
+				mib_p->fddiPORTMultiple_P ; ;
+			sp->p4053_availablepaths =
+				mib_p->fddiPORTAvailablePaths ;
+			sp->p4053_currentpath =
+				mib_p->fddiPORTCurrentPath ;
+			memcpy(	(char *) &sp->p4053_requestedpaths,
+				(char *) mib_p->fddiPORTRequestedPaths,4) ;
+			sp->p4053_mytype =
+				mib_p->fddiPORTMy_Type ;
+			sp->p4053_neighbortype =
+				mib_p->fddiPORTNeighborType ;
+			sp_len = sizeof(struct smt_p_4053) ;
+			goto sp_done ;
+		}
+	default :
+		break ;
+	}
+	/*
+	 * in table ?
+	 */
+	if (!pt) {
+		pcon->pc_err = (para & 0xff00) ? SMT_RDF_NOPARAM :
+						SMT_RDF_ILLEGAL ;
+		return ;
+	}
+	/*
+	 * check access rights
+	 */
+	switch (pt->p_access) {
+	case AC_G :
+	case AC_GR :
+		break ;
+	default :
+		pcon->pc_err = SMT_RDF_ILLEGAL ;
+		return ;
+	}
+	from = mib_addr + pt->p_offset ;
+	if (!swap)
+		swap = pt->p_swap ;		/* pointer to swap string */
+
+	/*
+	 * copy values
+	 */
+	while ((c = *swap++)) {
+		switch(c) {
+		case 'b' :
+		case 'w' :
+		case 'l' :
+			break ;
+		case 'S' :
+		case 'E' :
+		case 'R' :
+		case 'r' :
+			if (len < 4)
+				goto len_error ;
+			to[0] = 0 ;
+			to[1] = 0 ;
+#ifdef	LITTLE_ENDIAN
+			if (c == 'r') {
+				to[2] = *from++ ;
+				to[3] = *from++ ;
+			}
+			else {
+				to[3] = *from++ ;
+				to[2] = *from++ ;
+			}
+#else
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+#endif
+			to += 4 ;
+			len -= 4 ;
+			break ;
+		case 'I' :		/* for SET of port indexes */
+			if (len < 2)
+				goto len_error ;
+#ifdef	LITTLE_ENDIAN
+			to[1] = *from++ ;
+			to[0] = *from++ ;
+#else
+			to[0] = *from++ ;
+			to[1] = *from++ ;
+#endif
+			to += 2 ;
+			len -= 2 ;
+			break ;
+		case 'F' :
+		case 'B' :
+			if (len < 4)
+				goto len_error ;
+			len -= 4 ;
+			to[0] = 0 ;
+			to[1] = 0 ;
+			to[2] = 0 ;
+			to[3] = *from++ ;
+			to += 4 ;
+			break ;
+		case 'C' :
+		case 'T' :
+		case 'L' :
+			if (len < 4)
+				goto len_error ;
+#ifdef	LITTLE_ENDIAN
+			to[3] = *from++ ;
+			to[2] = *from++ ;
+			to[1] = *from++ ;
+			to[0] = *from++ ;
+#else
+			to[0] = *from++ ;
+			to[1] = *from++ ;
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+#endif
+			len -= 4 ;
+			to += 4 ;
+			break ;
+		case '2' :		/* PortMacIndicated */
+			if (len < 4)
+				goto len_error ;
+			to[0] = 0 ;
+			to[1] = 0 ;
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+			len -= 4 ;
+			to += 4 ;
+			break ;
+		case '4' :
+			if (len < 4)
+				goto len_error ;
+			to[0] = *from++ ;
+			to[1] = *from++ ;
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+			len -= 4 ;
+			to += 4 ;
+			break ;
+		case 'A' :
+			if (len < 8)
+				goto len_error ;
+			to[0] = 0 ;
+			to[1] = 0 ;
+			memcpy((char *) to+2,(char *) from,6) ;
+			to += 8 ;
+			from += 8 ;
+			len -= 8 ;
+			break ;
+		case '8' :
+			if (len < 8)
+				goto len_error ;
+			memcpy((char *) to,(char *) from,8) ;
+			to += 8 ;
+			from += 8 ;
+			len -= 8 ;
+			break ;
+		case 'D' :
+			if (len < 32)
+				goto len_error ;
+			memcpy((char *) to,(char *) from,32) ;
+			to += 32 ;
+			from += 32 ;
+			len -= 32 ;
+			break ;
+		case 'P' :		/* timestamp is NOT swapped */
+			if (len < 8)
+				goto len_error ;
+			to[0] = *from++ ;
+			to[1] = *from++ ;
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+			to[4] = *from++ ;
+			to[5] = *from++ ;
+			to[6] = *from++ ;
+			to[7] = *from++ ;
+			to += 8 ;
+			len -= 8 ;
+			break ;
+		default :
+			SMT_PANIC(smc,SMT_E0119, SMT_E0119_MSG) ;
+			break ;
+		}
+	}
+
+done:
+	/*
+	 * make it even (in case of 'I' encoding)
+	 * note: len is DECREMENTED
+	 */
+	if (len & 3) {
+		to[0] = 0 ;
+		to[1] = 0 ;
+		to += 4 - (len & 3 ) ;
+		len = len & ~ 3 ;
+	}
+
+	/* set type and length */
+	pa->p_type = para ;
+	pa->p_len = plen - len - PARA_LEN ;
+	/* return values */
+	pcon->pc_p = (void *) to ;
+	pcon->pc_len = len ;
+	return ;
+
+sp_done:
+	len -= sp_len ;
+	to += sp_len ;
+	goto done ;
+
+len_error:
+	/* parameter does not fit in frame */
+	pcon->pc_err = SMT_RDF_TOOLONG ;
+	return ;
+
+wrong_error:
+	pcon->pc_err = SMT_RDF_LENGTH ;
+}
+
+/*
+ * set parameter
+ */
+int smt_set_para(smc,pa,index,local,set)
+struct s_smc *smc ;
+struct smt_para	*pa ;
+int index ;
+int local ;
+int set ;
+{
+#define IFSET(x)	if (set) (x)
+
+	const struct s_p_tab	*pt ;
+	int		len ;
+	char		*from ;
+	char		*to ;
+	const char	*swap ;
+	char		c ;
+	char		*mib_addr ;
+	struct fddi_mib	*mib ;
+	struct fddi_mib_m	*mib_m = 0 ;
+	struct fddi_mib_a	*mib_a = 0 ;
+	struct fddi_mib_p	*mib_p = 0 ;
+	int		mac ;
+	int		path ;
+	int		port ;
+	SK_LOC_DECL(u_char,byte_val) ;
+	SK_LOC_DECL(u_short,word_val) ;
+	SK_LOC_DECL(u_long,long_val) ;
+
+	mac = index - INDEX_MAC ;
+	path = index - INDEX_PATH ;
+	port = index - INDEX_PORT ;
+	len = pa->p_len ;
+	from = (char *) (pa + 1 ) ;
+
+	mib = &smc->mib ;
+	switch (pa->p_type & 0xf000) {
+	case 0x1000 :
+	default :
+		mib_addr = (char *) mib ;
+		break ;
+	case 0x2000 :
+		if (mac < 0 || mac >= NUMMACS) {
+			return(SMT_RDF_NOPARAM) ;
+		}
+		mib_m = &smc->mib.m[mac] ;
+		mib_addr = (char *) mib_m ;
+		from += 4 ;		/* skip index */
+		len -= 4 ;
+		break ;
+	case 0x3000 :
+		if (path < 0 || path >= NUMPATHS) {
+			return(SMT_RDF_NOPARAM) ;
+		}
+		mib_a = &smc->mib.a[path] ;
+		mib_addr = (char *) mib_a ;
+		from += 4 ;		/* skip index */
+		len -= 4 ;
+		break ;
+	case 0x4000 :
+		if (port < 0 || port >= smt_mib_phys(smc)) {
+			return(SMT_RDF_NOPARAM) ;
+		}
+		mib_p = &smc->mib.p[port_to_mib(smc,port)] ;
+		mib_addr = (char *) mib_p ;
+		from += 4 ;		/* skip index */
+		len -= 4 ;
+		break ;
+	}
+	switch (pa->p_type) {
+	case SMT_P10F0 :
+	case SMT_P10F1 :
+#ifdef	ESS
+	case SMT_P10F2 :
+	case SMT_P10F3 :
+	case SMT_P10F4 :
+	case SMT_P10F5 :
+	case SMT_P10F6 :
+	case SMT_P10F7 :
+#endif
+#ifdef	SBA
+	case SMT_P10F8 :
+	case SMT_P10F9 :
+#endif
+	case SMT_P20F1 :
+		if (!local) {
+			return(SMT_RDF_NOPARAM) ;
+		}
+		break ;
+	}
+	pt = smt_get_ptab(pa->p_type) ;
+	if (!pt) {
+		return( (pa->p_type & 0xff00) ? SMT_RDF_NOPARAM :
+						SMT_RDF_ILLEGAL ) ;
+	}
+	switch (pt->p_access) {
+	case AC_GR :
+	case AC_S :
+		break ;
+	default :
+		return(SMT_RDF_ILLEGAL) ;
+	}
+	to = mib_addr + pt->p_offset ;
+	swap = pt->p_swap ;		/* pointer to swap string */
+
+	while (swap && (c = *swap++)) {
+		switch(c) {
+		case 'b' :
+			to = (char *) &byte_val ;
+			break ;
+		case 'w' :
+			to = (char *) &word_val ;
+			break ;
+		case 'l' :
+			to = (char *) &long_val ;
+			break ;
+		case 'S' :
+		case 'E' :
+		case 'R' :
+		case 'r' :
+			if (len < 4) {
+				goto len_error ;
+			}
+			if (from[0] | from[1])
+				goto val_error ;
+#ifdef	LITTLE_ENDIAN
+			if (c == 'r') {
+				to[0] = from[2] ;
+				to[1] = from[3] ;
+			}
+			else {
+				to[1] = from[2] ;
+				to[0] = from[3] ;
+			}
+#else
+			to[0] = from[2] ;
+			to[1] = from[3] ;
+#endif
+			from += 4 ;
+			to += 2 ;
+			len -= 4 ;
+			break ;
+		case 'F' :
+		case 'B' :
+			if (len < 4) {
+				goto len_error ;
+			}
+			if (from[0] | from[1] | from[2])
+				goto val_error ;
+			to[0] = from[3] ;
+			len -= 4 ;
+			from += 4 ;
+			to += 4 ;
+			break ;
+		case 'C' :
+		case 'T' :
+		case 'L' :
+			if (len < 4) {
+				goto len_error ;
+			}
+#ifdef	LITTLE_ENDIAN
+			to[3] = *from++ ;
+			to[2] = *from++ ;
+			to[1] = *from++ ;
+			to[0] = *from++ ;
+#else
+			to[0] = *from++ ;
+			to[1] = *from++ ;
+			to[2] = *from++ ;
+			to[3] = *from++ ;
+#endif
+			len -= 4 ;
+			to += 4 ;
+			break ;
+		case 'A' :
+			if (len < 8)
+				goto len_error ;
+			if (set)
+				memcpy((char *) to,(char *) from+2,6) ;
+			to += 8 ;
+			from += 8 ;
+			len -= 8 ;
+			break ;
+		case '4' :
+			if (len < 4)
+				goto len_error ;
+			if (set)
+				memcpy((char *) to,(char *) from,4) ;
+			to += 4 ;
+			from += 4 ;
+			len -= 4 ;
+			break ;
+		case '8' :
+			if (len < 8)
+				goto len_error ;
+			if (set)
+				memcpy((char *) to,(char *) from,8) ;
+			to += 8 ;
+			from += 8 ;
+			len -= 8 ;
+			break ;
+		case 'D' :
+			if (len < 32)
+				goto len_error ;
+			if (set)
+				memcpy((char *) to,(char *) from,32) ;
+			to += 32 ;
+			from += 32 ;
+			len -= 32 ;
+			break ;
+		case 'P' :		/* timestamp is NOT swapped */
+			if (set) {
+				to[0] = *from++ ;
+				to[1] = *from++ ;
+				to[2] = *from++ ;
+				to[3] = *from++ ;
+				to[4] = *from++ ;
+				to[5] = *from++ ;
+				to[6] = *from++ ;
+				to[7] = *from++ ;
+			}
+			to += 8 ;
+			len -= 8 ;
+			break ;
+		default :
+			SMT_PANIC(smc,SMT_E0120, SMT_E0120_MSG) ;
+			return(SMT_RDF_ILLEGAL) ;
+		}
+	}
+	/*
+	 * actions and internal updates
+	 */
+	switch (pa->p_type) {
+	case SMT_P101A:			/* fddiSMTConfigPolicy */
+		if (word_val & ~1)
+			goto val_error ;
+		IFSET(mib->fddiSMTConfigPolicy = word_val) ;
+		break ;
+	case SMT_P101B :		/* fddiSMTConnectionPolicy */
+		if (!(word_val & POLICY_MM))
+			goto val_error ;
+		IFSET(mib->fddiSMTConnectionPolicy = word_val) ;
+		break ;
+	case SMT_P101D : 		/* fddiSMTTT_Notify */
+		if (word_val < 2 || word_val > 30)
+			goto val_error ;
+		IFSET(mib->fddiSMTTT_Notify = word_val) ;
+		break ;
+	case SMT_P101E :		/* fddiSMTStatRptPolicy */
+		if (byte_val & ~1)
+			goto val_error ;
+		IFSET(mib->fddiSMTStatRptPolicy = byte_val) ;
+		break ;
+	case SMT_P101F :		/* fddiSMTTrace_MaxExpiration */
+		/*
+		 * note: lower limit trace_max = 6.001773... s
+		 * NO upper limit
+		 */
+		if (long_val < (long)0x478bf51L)
+			goto val_error ;
+		IFSET(mib->fddiSMTTrace_MaxExpiration = long_val) ;
+		break ;
+#ifdef	ESS
+	case SMT_P10F2 :		/* fddiESSPayload */
+		if (long_val > 1562)
+			goto val_error ;
+		if (set && smc->mib.fddiESSPayload != long_val) {
+			smc->ess.raf_act_timer_poll = TRUE ;
+			smc->mib.fddiESSPayload = long_val ;
+		}
+		break ;
+	case SMT_P10F3 :		/* fddiESSOverhead */
+		if (long_val < 50 || long_val > 5000)
+			goto val_error ;
+		if (set && smc->mib.fddiESSPayload &&
+			smc->mib.fddiESSOverhead != long_val) {
+			smc->ess.raf_act_timer_poll = TRUE ;
+			smc->mib.fddiESSOverhead = long_val ;
+		}
+		break ;
+	case SMT_P10F4 :		/* fddiESSMaxTNeg */
+		if (long_val > -MS2BCLK(5) || long_val < -MS2BCLK(165))
+			goto val_error ;
+		IFSET(mib->fddiESSMaxTNeg = long_val) ;
+		break ;
+	case SMT_P10F5 :		/* fddiESSMinSegmentSize */
+		if (long_val < 1 || long_val > 4478)
+			goto val_error ;
+		IFSET(mib->fddiESSMinSegmentSize = long_val) ;
+		break ;
+	case SMT_P10F6 :		/* fddiESSCategory */
+		if ((long_val & 0xffff) != 1)
+			goto val_error ;
+		IFSET(mib->fddiESSCategory = long_val) ;
+		break ;
+	case SMT_P10F7 :		/* fddiESSSyncTxMode */
+		if (word_val > 1)
+			goto val_error ;
+		IFSET(mib->fddiESSSynchTxMode = word_val) ;
+		break ;
+#endif
+#ifdef	SBA
+	case SMT_P10F8 :		/* fddiSBACommand */
+		if (byte_val != SB_STOP && byte_val != SB_START)
+			goto val_error ;
+		IFSET(mib->fddiSBACommand = byte_val) ;
+		break ;
+	case SMT_P10F9 :		/* fddiSBAAvailable */
+		if (byte_val > 100)
+			goto val_error ;
+		IFSET(mib->fddiSBAAvailable = byte_val) ;
+		break ;
+#endif
+	case SMT_P2020 :		/* fddiMACRequestedPaths */
+		if ((word_val & (MIB_P_PATH_PRIM_PREFER |
+			MIB_P_PATH_PRIM_ALTER)) == 0 )
+			goto val_error ;
+		IFSET(mib_m->fddiMACRequestedPaths = word_val) ;
+		break ;
+	case SMT_P205F :		/* fddiMACFrameErrorThreshold */
+		/* 0 .. ffff acceptable */
+		IFSET(mib_m->fddiMACFrameErrorThreshold = word_val) ;
+		break ;
+	case SMT_P2067 :		/* fddiMACNotCopiedThreshold */
+		/* 0 .. ffff acceptable */
+		IFSET(mib_m->fddiMACNotCopiedThreshold = word_val) ;
+		break ;
+	case SMT_P2076:			/* fddiMACMA_UnitdataEnable */
+		if (byte_val & ~1)
+			goto val_error ;
+		if (set) {
+			mib_m->fddiMACMA_UnitdataEnable = byte_val ;
+			queue_event(smc,EVENT_RMT,RM_ENABLE_FLAG) ;
+		}
+		break ;
+	case SMT_P20F1 :		/* fddiMACT_Min */
+		IFSET(mib_m->fddiMACT_Min = long_val) ;
+		break ;
+	case SMT_P320F :
+		if (long_val > 1562)
+			goto val_error ;
+		IFSET(mib_a->fddiPATHSbaPayload = long_val) ;
+#ifdef	ESS
+		if (set)
+			ess_para_change(smc) ;
+#endif
+		break ;
+	case SMT_P3210 :
+		if (long_val > 5000)
+			goto val_error ;
+		
+		if (long_val != 0 && mib_a->fddiPATHSbaPayload == 0)
+			goto val_error ;
+
+		IFSET(mib_a->fddiPATHSbaOverhead = long_val) ;
+#ifdef	ESS
+		if (set)
+			ess_para_change(smc) ;
+#endif
+		break ;
+	case SMT_P3213:			/* fddiPATHT_Rmode */
+		/* no limit :
+		 * 0 .. 343.597 => 0 .. 2e32 * 80nS
+		 */
+		if (set) {
+			mib_a->fddiPATHT_Rmode = long_val ;
+			rtm_set_timer(smc) ;
+		}
+		break ;
+	case SMT_P3214 :		/* fddiPATHSbaAvailable */
+		if (long_val > 0x00BEBC20L)
+			goto val_error ;
+#ifdef SBA 
+		if (set && mib->fddiSBACommand == SB_STOP)
+			goto val_error ;
+#endif
+		IFSET(mib_a->fddiPATHSbaAvailable = long_val) ;
+		break ;
+	case SMT_P3215 :		/* fddiPATHTVXLowerBound */
+		IFSET(mib_a->fddiPATHTVXLowerBound = long_val) ;
+		goto change_mac_para ;
+	case SMT_P3216 :		/* fddiPATHT_MaxLowerBound */
+		IFSET(mib_a->fddiPATHT_MaxLowerBound = long_val) ;
+		goto change_mac_para ;
+	case SMT_P3217 :		/* fddiPATHMaxT_Req */
+		IFSET(mib_a->fddiPATHMaxT_Req = long_val) ;
+
+change_mac_para:
+		if (set && smt_set_mac_opvalues(smc)) {
+			RS_SET(smc,RS_EVENT) ;
+			smc->sm.please_reconnect = 1 ;
+			queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+		}
+		break ;
+	case SMT_P400E :		/* fddiPORTConnectionPolicies */
+		if (byte_val > 1)
+			goto val_error ;
+		IFSET(mib_p->fddiPORTConnectionPolicies = byte_val) ;
+		break ;
+	case SMT_P4011 :		/* fddiPORTRequestedPaths */
+		/* all 3*8 bits allowed */
+		IFSET(memcpy((char *)mib_p->fddiPORTRequestedPaths,
+			(char *)&long_val,4)) ;
+		break ;
+	case SMT_P401F:			/* fddiPORTMaint_LS */
+		if (word_val > 4)
+			goto val_error ;
+		IFSET(mib_p->fddiPORTMaint_LS = word_val) ;
+		break ;
+	case SMT_P403A :		/* fddiPORTLer_Cutoff */
+		if (byte_val < 4 || byte_val > 15)
+			goto val_error ;
+		IFSET(mib_p->fddiPORTLer_Cutoff = byte_val) ;
+		break ;
+	case SMT_P403B :		/* fddiPORTLer_Alarm */
+		if (byte_val < 4 || byte_val > 15)
+			goto val_error ;
+		IFSET(mib_p->fddiPORTLer_Alarm = byte_val) ;
+		break ;
+
+	/*
+	 * Actions
+	 */
+	case SMT_P103C :		/* fddiSMTStationAction */
+		if (smt_action(smc,SMT_STATION_ACTION, (int) word_val, 0))
+			goto val_error ;
+		break ;
+	case SMT_P4046:			/* fddiPORTAction */
+		if (smt_action(smc,SMT_PORT_ACTION, (int) word_val,
+			port_to_mib(smc,port)))
+			goto val_error ;
+		break ;
+	default :
+		break ;
+	}
+	return(0) ;
+
+val_error:
+	/* parameter value in frame is out of range */
+	return(SMT_RDF_RANGE) ;
+
+len_error:
+	/* parameter value in frame is too short */
+	return(SMT_RDF_LENGTH) ;
+
+#if	0
+no_author_error:
+	/* parameter not setable, because the SBA is not active
+	 * Please note: we give the return code 'not authorizeed
+	 *  because SBA denied is not a valid return code in the
+	 * PMF protocol.
+	 */
+	return(SMT_RDF_AUTHOR) ;
+#endif
+}
+
+static const struct s_p_tab *smt_get_ptab(para)
+u_short para ;
+{
+	const struct s_p_tab	*pt ;
+	for (pt = p_tab ; pt->p_num && pt->p_num != para ; pt++)
+		;
+	return(pt->p_num ? pt : 0) ;
+}
+
+static int smt_mib_phys(smc)
+struct s_smc *smc ;
+{
+#ifdef	CONCENTRATOR
+	SK_UNUSED(smc) ;
+
+	return(NUMPHYS) ;
+#else
+	if (smc->s.sas == SMT_SAS)
+		return(1) ;
+	return(NUMPHYS) ;
+#endif
+}
+
+int port_to_mib(smc,p)
+struct s_smc *smc ;
+int p ;
+{
+#ifdef	CONCENTRATOR
+	SK_UNUSED(smc) ;
+
+	return(p) ;
+#else
+	if (smc->s.sas == SMT_SAS)
+		return(PS) ;
+	return(p) ;
+#endif
+}
+
+
+#ifdef	DEBUG
+#ifndef	BOOT
+void dump_smt(smc,sm,text)
+struct s_smc *smc ;
+struct smt_header *sm ;
+char *text ;
+{
+	int	len ;
+	struct smt_para	*pa ;
+	char	*c ;
+	int	n ;
+	int	nn ;
+#ifdef	LITTLE_ENDIAN
+	int	smtlen ;
+#endif
+
+	SK_UNUSED(smc) ;
+
+#ifdef	DEBUG_BRD
+	if (smc->debug.d_smtf < 2)
+#else
+	if (debug.d_smtf < 2)
+#endif
+		return ;
+#ifdef	LITTLE_ENDIAN
+	smtlen = sm->smt_len + sizeof(struct smt_header) ;
+#endif
+	printf("SMT Frame [%s]:\nDA  ",text) ;
+	dump_hex((char *) &sm->smt_dest,6) ;
+	printf("\tSA ") ;
+	dump_hex((char *) &sm->smt_source,6) ;
+	printf(" Class %x Type %x Version %x\n",
+		sm->smt_class,sm->smt_type,sm->smt_version)  ;
+	printf("TID %lx\t\tSID ",sm->smt_tid) ;
+	dump_hex((char *) &sm->smt_sid,8) ;
+	printf(" LEN %x\n",sm->smt_len) ;
+
+	len = sm->smt_len ;
+	pa = (struct smt_para *) (sm + 1) ;
+	while (len > 0 ) {
+		int	plen ;
+#ifdef UNIX
+		printf("TYPE %x LEN %x VALUE\t",pa->p_type,pa->p_len) ;
+#else
+		printf("TYPE %04x LEN %2x VALUE\t",pa->p_type,pa->p_len) ;
+#endif
+		n = pa->p_len ;
+		if ( (n < 0 ) || (n > (int)(len - PARA_LEN))) {
+			n = len - PARA_LEN ;
+			printf(" BAD LENGTH\n") ;
+			break ;
+		}
+#ifdef	LITTLE_ENDIAN
+		smt_swap_para(sm,smtlen,0) ;
+#endif
+		if (n < 24) {
+			dump_hex((char *)(pa+1),(int) n) ;
+			printf("\n") ;
+		}
+		else {
+			int	first = 0 ;
+			c = (char *)(pa+1) ;
+			dump_hex(c,16) ;
+			printf("\n") ;
+			n -= 16 ;
+			c += 16 ;
+			while (n > 0) {
+				nn = (n > 16) ? 16 : n ;
+				if (n > 64) {
+					if (first == 0)
+						printf("\t\t\t...\n") ;
+					first = 1 ;
+				}
+				else {
+					printf("\t\t\t") ;
+					dump_hex(c,nn) ;
+					printf("\n") ;
+				}
+				n -= nn ;
+				c += 16 ;
+			}
+		}
+#ifdef	LITTLE_ENDIAN
+		smt_swap_para(sm,smtlen,1) ;
+#endif
+		plen = (pa->p_len + PARA_LEN + 3) & ~3 ;
+		len -= plen ;
+		pa = (struct smt_para *)((char *)pa + plen) ;
+	}
+	printf("-------------------------------------------------\n\n") ;
+}
+
+void dump_hex(p,len)
+char *p ;
+int len ;
+{
+	int	n = 0 ;
+	while (len--) {
+		n++ ;
+#ifdef UNIX
+		printf("%x%s",*p++ & 0xff,len ? ( (n & 7) ? " " : "-") : "") ;
+#else
+		printf("%02x%s",*p++ & 0xff,len ? ( (n & 7) ? " " : "-") : "") ;
+#endif
+	}
+}
+#endif	/* no BOOT */
+#endif	/* DEBUG */
+
+
+#endif	/* no SLIM_SMT */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/queue.c linux.18p17/drivers/net/skfp/queue.c
--- linux.vanilla/drivers/net/skfp/queue.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/queue.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,185 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT Event Queue Management
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)queue.c	2.9 97/08/04 (C) SK " ;
+#endif
+
+#define PRINTF(a,b,c)
+
+/*
+ * init event queue management
+ */
+void ev_init(smc)
+struct s_smc *smc ;
+{
+	smc->q.ev_put = smc->q.ev_get = smc->q.ev_queue ;
+}
+
+/*
+ * add event to queue
+ */
+void queue_event(smc,class,event)
+struct s_smc *smc ;
+int class ;
+int event ;
+{
+	PRINTF("queue class %d event %d\n",class,event) ;
+	smc->q.ev_put->class = class ;
+	smc->q.ev_put->event = event ;
+	if (++smc->q.ev_put == &smc->q.ev_queue[MAX_EVENT])
+		smc->q.ev_put = smc->q.ev_queue ;
+
+	if (smc->q.ev_put == smc->q.ev_get) {
+		SMT_ERR_LOG(smc,SMT_E0137, SMT_E0137_MSG) ;
+	}
+}
+
+/*
+ * timer_event is called from HW timer package.
+ */
+void timer_event(smc,token)
+struct s_smc *smc ;
+u_long token ;
+{
+	PRINTF("timer event class %d token %d\n",
+		EV_T_CLASS(token),
+		EV_T_EVENT(token)) ;
+	queue_event(smc,EV_T_CLASS(token),EV_T_EVENT(token));
+}
+
+/*
+ * event dispatcher
+ *	while event queue is not empty
+ *		get event from queue
+ *		send command to state machine
+ *	end
+ */
+void ev_dispatcher(smc)
+struct s_smc *smc ;
+{
+	struct event_queue *ev ;	/* pointer into queue */
+	int		class ;
+
+	ev = smc->q.ev_get ;
+	PRINTF("dispatch get %x put %x\n",ev,smc->q.ev_put) ;
+	while (ev != smc->q.ev_put) {
+		PRINTF("dispatch class %d event %d\n",ev->class,ev->event) ;
+		switch(class = ev->class) {
+		case EVENT_ECM :		/* Entity Corordination  Man. */
+			ecm(smc,(int)ev->event) ;
+			break ;
+		case EVENT_CFM :		/* Configuration Man. */
+			cfm(smc,(int)ev->event) ;
+			break ;
+		case EVENT_RMT :		/* Ring Man. */
+			rmt(smc,(int)ev->event) ;
+			break ;
+		case EVENT_SMT :
+			smt_event(smc,(int)ev->event) ;
+			break ;
+#ifdef	CONCENTRATOR
+		case 99 :
+			timer_test_event(smc,(int)ev->event) ;
+			break ;
+#endif
+		case EVENT_PCMA :		/* PHY A */
+		case EVENT_PCMB :		/* PHY B */
+		default :
+			if (class >= EVENT_PCMA &&
+			    class < EVENT_PCMA + NUMPHYS) {
+				pcm(smc,class - EVENT_PCMA,(int)ev->event) ;
+				break ;
+			}
+			SMT_PANIC(smc,SMT_E0121, SMT_E0121_MSG) ;
+			return ;
+		}
+
+		if (++ev == &smc->q.ev_queue[MAX_EVENT])
+			ev = smc->q.ev_queue ;
+
+		/* Renew get: it is used in queue_events to detect overruns */
+		smc->q.ev_get = ev;
+	}
+}
+
+/*
+ * smt_online connects to or disconnects from the ring
+ * MUST be called to initiate connection establishment
+ *
+ *	on	0	disconnect
+ *	on	1	connect
+ */
+u_short smt_online(smc,on)
+struct s_smc *smc ;
+int on ;
+{
+	queue_event(smc,EVENT_ECM,on ? EC_CONNECT : EC_DISCONNECT) ;
+	ev_dispatcher(smc) ;
+	return(smc->mib.fddiSMTCF_State) ;
+}
+
+/*
+ * set SMT flag to value
+ *	flag		flag name
+ *	value		flag value
+ * dump current flag setting
+ */
+#ifdef	CONCENTRATOR
+void do_smt_flag(smc,flag,value)
+struct s_smc *smc ;
+char *flag ;
+int value ;
+{
+#ifdef	DEBUG
+	struct smt_debug	*deb;
+
+	SK_UNUSED(smc) ;
+
+#ifdef	DEBUG_BRD
+	deb = &smc->debug;
+#else
+	deb = &debug;
+#endif
+	if (!strcmp(flag,"smt"))
+		deb->d_smt = value ;
+	else if (!strcmp(flag,"smtf"))
+		deb->d_smtf = value ;
+	else if (!strcmp(flag,"pcm"))
+		deb->d_pcm = value ;
+	else if (!strcmp(flag,"rmt"))
+		deb->d_rmt = value ;
+	else if (!strcmp(flag,"cfm"))
+		deb->d_cfm = value ;
+	else if (!strcmp(flag,"ecm"))
+		deb->d_ecm = value ;
+	printf("smt	%d\n",deb->d_smt) ;
+	printf("smtf	%d\n",deb->d_smtf) ;
+	printf("pcm	%d\n",deb->d_pcm) ;
+	printf("rmt	%d\n",deb->d_rmt) ;
+	printf("cfm	%d\n",deb->d_cfm) ;
+	printf("ecm	%d\n",deb->d_ecm) ;
+#endif	/* DEBUG */
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/rmt.c linux.18p17/drivers/net/skfp/rmt.c
--- linux.vanilla/drivers/net/skfp/rmt.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/rmt.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,674 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT RMT
+	Ring Management
+*/
+
+/*
+ * Hardware independant state machine implemantation
+ * The following external SMT functions are referenced :
+ *
+ * 		queue_event()
+ * 		smt_timer_start()
+ * 		smt_timer_stop()
+ *
+ * 	The following external HW dependant functions are referenced :
+ *		sm_ma_control()
+ *		sm_mac_check_beacon_claim()
+ *
+ * 	The following HW dependant events are required :
+ *		RM_RING_OP
+ *		RM_RING_NON_OP
+ *		RM_MY_BEACON
+ *		RM_OTHER_BEACON
+ *		RM_MY_CLAIM
+ *		RM_TRT_EXP
+ *		RM_VALID_CLAIM
+ *
+ */
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)rmt.c	2.13 99/07/02 (C) SK " ;
+#endif
+
+/*
+ * FSM Macros
+ */
+#define AFLAG	0x10
+#define GO_STATE(x)	(smc->mib.m[MAC0].fddiMACRMTState = (x)|AFLAG)
+#define ACTIONS_DONE()	(smc->mib.m[MAC0].fddiMACRMTState &= ~AFLAG)
+#define ACTIONS(x)	(x|AFLAG)
+
+#define RM0_ISOLATED	0
+#define RM1_NON_OP	1		/* not operational */
+#define RM2_RING_OP	2		/* ring operational */
+#define RM3_DETECT	3		/* detect dupl addresses */
+#define RM4_NON_OP_DUP	4		/* dupl. addr detected */
+#define RM5_RING_OP_DUP	5		/* ring oper. with dupl. addr */
+#define RM6_DIRECTED	6		/* sending directed beacons */
+#define RM7_TRACE	7		/* trace initiated */
+
+#ifdef	DEBUG
+/*
+ * symbolic state names
+ */
+static const char * const rmt_states[] = {
+	"RM0_ISOLATED","RM1_NON_OP","RM2_RING_OP","RM3_DETECT",
+	"RM4_NON_OP_DUP","RM5_RING_OP_DUP","RM6_DIRECTED",
+	"RM7_TRACE"
+} ;
+
+/*
+ * symbolic event names
+ */
+static const char * const rmt_events[] = {
+	"NONE","RM_RING_OP","RM_RING_NON_OP","RM_MY_BEACON",
+	"RM_OTHER_BEACON","RM_MY_CLAIM","RM_TRT_EXP","RM_VALID_CLAIM",
+	"RM_JOIN","RM_LOOP","RM_DUP_ADDR","RM_ENABLE_FLAG",
+	"RM_TIMEOUT_NON_OP","RM_TIMEOUT_T_STUCK",
+	"RM_TIMEOUT_ANNOUNCE","RM_TIMEOUT_T_DIRECT",
+	"RM_TIMEOUT_D_MAX","RM_TIMEOUT_POLL","RM_TX_STATE_CHANGE"
+} ;
+#endif
+
+/*
+ * Globals
+ * in struct s_rmt
+ */
+
+
+/*
+ * function declarations
+ */
+static void rmt_fsm() ;
+static void start_rmt_timer0() ;
+static void start_rmt_timer1() ;
+static void start_rmt_timer2() ;
+static void stop_rmt_timer0() ;
+static void stop_rmt_timer1() ;
+static void stop_rmt_timer2() ;
+static void rmt_dup_actions() ;
+static void rmt_reinsert_actions() ;
+static void rmt_leave_actions() ;
+static void rmt_new_dup_actions() ;
+
+#ifndef SUPERNET_3
+extern void restart_trt_for_dbcn() ;
+#endif /*SUPERNET_3*/
+
+/*
+	init RMT state machine
+	clear all RMT vars and flags
+*/
+void rmt_init(smc)
+struct s_smc *smc ;
+{
+	smc->mib.m[MAC0].fddiMACRMTState = ACTIONS(RM0_ISOLATED) ;
+	smc->r.dup_addr_test = DA_NONE ;
+	smc->r.da_flag = 0 ;
+	smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+	smc->r.sm_ma_avail = FALSE ;
+	smc->r.loop_avail = 0 ;
+	smc->r.bn_flag = 0 ;
+	smc->r.jm_flag = 0 ;
+	smc->r.no_flag = TRUE ;
+}
+
+/*
+	RMT state machine
+	called by dispatcher
+
+	do
+		display state change
+		process event
+	until SM is stable
+*/
+void rmt(smc,event)
+struct s_smc *smc ;
+int event ;
+{
+	int	state ;
+
+	do {
+		DB_RMT("RMT : state %s%s",
+			(smc->mib.m[MAC0].fddiMACRMTState & AFLAG) ? "ACTIONS " : "",
+			rmt_states[smc->mib.m[MAC0].fddiMACRMTState & ~AFLAG]) ;
+		DB_RMT(" event %s\n",rmt_events[event],0) ;
+		state = smc->mib.m[MAC0].fddiMACRMTState ;
+		rmt_fsm(smc,event) ;
+		event = 0 ;
+	} while (state != smc->mib.m[MAC0].fddiMACRMTState) ;
+	rmt_state_change(smc,(int)smc->mib.m[MAC0].fddiMACRMTState) ;
+}
+
+/*
+	process RMT event
+*/
+static void rmt_fsm(smc,cmd)
+struct s_smc *smc ;
+int cmd ;
+{
+	/*
+	 * RM00-RM70 : from all states
+	 */
+	if (!smc->r.rm_join && !smc->r.rm_loop &&
+		smc->mib.m[MAC0].fddiMACRMTState != ACTIONS(RM0_ISOLATED) &&
+		smc->mib.m[MAC0].fddiMACRMTState != RM0_ISOLATED) {
+		RS_SET(smc,RS_NORINGOP) ;
+		rmt_indication(smc,0) ;
+		GO_STATE(RM0_ISOLATED) ;
+		return ;
+	}
+
+	switch(smc->mib.m[MAC0].fddiMACRMTState) {
+	case ACTIONS(RM0_ISOLATED) :
+		stop_rmt_timer0(smc) ;
+		stop_rmt_timer1(smc) ;
+		stop_rmt_timer2(smc) ;
+
+		/*
+		 * Disable MAC.
+		 */
+		sm_ma_control(smc,MA_OFFLINE) ;
+		smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+		smc->r.loop_avail = FALSE ;
+		smc->r.sm_ma_avail = FALSE ;
+		smc->r.no_flag = TRUE ;
+		DB_RMTN(1,"RMT : ISOLATED\n",0,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM0_ISOLATED :
+		/*RM01*/
+		if (smc->r.rm_join || smc->r.rm_loop) {
+			/*
+			 * According to the standard the MAC must be reset
+			 * here. The FORMAC will be initialized and Claim
+			 * and Beacon Frames will be uploaded to the MAC.
+			 * So any change of Treq will take effect NOW.
+			 */
+			sm_ma_control(smc,MA_RESET) ;
+			GO_STATE(RM1_NON_OP) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM1_NON_OP) :
+		start_rmt_timer0(smc,smc->s.rmt_t_non_op,RM_TIMEOUT_NON_OP) ;
+		stop_rmt_timer1(smc) ;
+		stop_rmt_timer2(smc) ;
+		sm_ma_control(smc,MA_BEACON) ;
+		DB_RMTN(1,"RMT : RING DOWN\n",0,0) ;
+		RS_SET(smc,RS_NORINGOP) ;
+		smc->r.sm_ma_avail = FALSE ;
+		rmt_indication(smc,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM1_NON_OP :
+		/*RM12*/
+		if (cmd == RM_RING_OP) {
+			RS_SET(smc,RS_RINGOPCHANGE) ;
+			GO_STATE(RM2_RING_OP) ;
+			break ;
+		}
+		/*RM13*/
+		else if (cmd == RM_TIMEOUT_NON_OP) {
+			smc->r.bn_flag = FALSE ;
+			smc->r.no_flag = TRUE ;
+			GO_STATE(RM3_DETECT) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM2_RING_OP) :
+		stop_rmt_timer0(smc) ;
+		stop_rmt_timer1(smc) ;
+		stop_rmt_timer2(smc) ;
+		smc->r.no_flag = FALSE ;
+		if (smc->r.rm_loop)
+			smc->r.loop_avail = TRUE ;
+		if (smc->r.rm_join) {
+			smc->r.sm_ma_avail = TRUE ;
+			if (smc->mib.m[MAC0].fddiMACMA_UnitdataEnable)
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = TRUE ;
+				else
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+		}
+		DB_RMTN(1,"RMT : RING UP\n",0,0) ;
+		RS_CLEAR(smc,RS_NORINGOP) ;
+		RS_SET(smc,RS_RINGOPCHANGE) ;
+		rmt_indication(smc,1) ;
+		smt_stat_counter(smc,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM2_RING_OP :
+		/*RM21*/
+		if (cmd == RM_RING_NON_OP) {
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+			smc->r.loop_avail = FALSE ;
+			RS_SET(smc,RS_RINGOPCHANGE) ;
+			GO_STATE(RM1_NON_OP) ;
+			break ;
+		}
+		/*RM22a*/
+		else if (cmd == RM_ENABLE_FLAG) {
+			if (smc->mib.m[MAC0].fddiMACMA_UnitdataEnable)
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = TRUE ;
+				else
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+		}
+		/*RM25*/
+		else if (smc->r.dup_addr_test == DA_FAILED) {
+			smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ;
+			smc->r.loop_avail = FALSE ;
+			smc->r.da_flag = TRUE ;
+			GO_STATE(RM5_RING_OP_DUP) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM3_DETECT) :
+		start_rmt_timer0(smc,smc->s.mac_d_max*2,RM_TIMEOUT_D_MAX) ;
+		start_rmt_timer1(smc,smc->s.rmt_t_stuck,RM_TIMEOUT_T_STUCK) ;
+		start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ;
+		sm_mac_check_beacon_claim(smc) ;
+		DB_RMTN(1,"RMT : RM3_DETECT\n",0,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM3_DETECT :
+		if (cmd == RM_TIMEOUT_POLL) {
+			start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL);
+			sm_mac_check_beacon_claim(smc) ;
+			break ;
+		}
+		if (cmd == RM_TIMEOUT_D_MAX) {
+			smc->r.timer0_exp = TRUE ;
+		}
+		/*
+		 *jd(22-Feb-1999)
+		 * We need a time ">= 2*mac_d_max" since we had finished
+		 * Claim or Beacon state. So we will restart timer0 at
+		 * every state change.
+		 */
+		if (cmd == RM_TX_STATE_CHANGE) {
+			start_rmt_timer0(smc,
+					 smc->s.mac_d_max*2,
+					 RM_TIMEOUT_D_MAX) ;
+		}
+		/*RM32*/
+		if (cmd == RM_RING_OP) {
+			GO_STATE(RM2_RING_OP) ;
+			break ;
+		}
+		/*RM33a*/
+		else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON)
+			&& smc->r.bn_flag) {
+			smc->r.bn_flag = FALSE ;
+		}
+		/*RM33b*/
+		else if (cmd == RM_TRT_EXP && !smc->r.bn_flag) {
+			int	tx ;
+			/*
+			 * set bn_flag only if in state T4 or T5:
+			 * only if we're the beaconer should we start the
+			 * trace !
+			 */
+			if ((tx =  sm_mac_get_tx_state(smc)) == 4 || tx == 5) {
+			DB_RMTN(2,"RMT : DETECT && TRT_EXPIRED && T4/T5\n",0,0);
+				smc->r.bn_flag = TRUE ;
+				/*
+				 * If one of the upstream stations beaconed
+				 * and the link to the upstream neighbor is
+				 * lost we need to restart the stuck timer to
+				 * check the "stuck beacon" condition.
+				 */
+				start_rmt_timer1(smc,smc->s.rmt_t_stuck,
+					RM_TIMEOUT_T_STUCK) ;
+			}
+			/*
+			 * We do NOT need to clear smc->r.bn_flag in case of
+			 * not being in state T4 or T5, because the flag
+			 * must be cleared in order to get in this condition.
+			 */
+
+			DB_RMTN(2,
+			"RMT : sm_mac_get_tx_state() = %d (bn_flag = %d)\n",
+			tx,smc->r.bn_flag) ;
+		}
+		/*RM34a*/
+		else if (cmd == RM_MY_CLAIM && smc->r.timer0_exp) {
+			rmt_new_dup_actions(smc) ;
+			GO_STATE(RM4_NON_OP_DUP) ;
+			break ;
+		}
+		/*RM34b*/
+		else if (cmd == RM_MY_BEACON && smc->r.timer0_exp) {
+			rmt_new_dup_actions(smc) ;
+			GO_STATE(RM4_NON_OP_DUP) ;
+			break ;
+		}
+		/*RM34c*/
+		else if (cmd == RM_VALID_CLAIM) {
+			rmt_new_dup_actions(smc) ;
+			GO_STATE(RM4_NON_OP_DUP) ;
+			break ;
+		}
+		/*RM36*/
+		else if (cmd == RM_TIMEOUT_T_STUCK &&
+			smc->r.rm_join && smc->r.bn_flag) {
+			GO_STATE(RM6_DIRECTED) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM4_NON_OP_DUP) :
+		start_rmt_timer0(smc,smc->s.rmt_t_announce,RM_TIMEOUT_ANNOUNCE);
+		start_rmt_timer1(smc,smc->s.rmt_t_stuck,RM_TIMEOUT_T_STUCK) ;
+		start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ;
+		sm_mac_check_beacon_claim(smc) ;
+		DB_RMTN(1,"RMT : RM4_NON_OP_DUP\n",0,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM4_NON_OP_DUP :
+		if (cmd == RM_TIMEOUT_POLL) {
+			start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL);
+			sm_mac_check_beacon_claim(smc) ;
+			break ;
+		}
+		/*RM41*/
+		if (!smc->r.da_flag) {
+			GO_STATE(RM1_NON_OP) ;
+			break ;
+		}
+		/*RM44a*/
+		else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) &&
+			smc->r.bn_flag) {
+			smc->r.bn_flag = FALSE ;
+		}
+		/*RM44b*/
+		else if (cmd == RM_TRT_EXP && !smc->r.bn_flag) {
+			int	tx ;
+			/*
+			 * set bn_flag only if in state T4 or T5:
+			 * only if we're the beaconer should we start the
+			 * trace !
+			 */
+			if ((tx =  sm_mac_get_tx_state(smc)) == 4 || tx == 5) {
+			DB_RMTN(2,"RMT : NOPDUP && TRT_EXPIRED && T4/T5\n",0,0);
+				smc->r.bn_flag = TRUE ;
+				/*
+				 * If one of the upstream stations beaconed
+				 * and the link to the upstream neighbor is
+				 * lost we need to restart the stuck timer to
+				 * check the "stuck beacon" condition.
+				 */
+				start_rmt_timer1(smc,smc->s.rmt_t_stuck,
+					RM_TIMEOUT_T_STUCK) ;
+			}
+			/*
+			 * We do NOT need to clear smc->r.bn_flag in case of
+			 * not being in state T4 or T5, because the flag
+			 * must be cleared in order to get in this condition.
+			 */
+
+			DB_RMTN(2,
+			"RMT : sm_mac_get_tx_state() = %d (bn_flag = %d)\n",
+			tx,smc->r.bn_flag) ;
+		}
+		/*RM44c*/
+		else if (cmd == RM_TIMEOUT_ANNOUNCE && !smc->r.bn_flag) {
+			rmt_dup_actions(smc) ;
+		}
+		/*RM45*/
+		else if (cmd == RM_RING_OP) {
+			smc->r.no_flag = FALSE ;
+			GO_STATE(RM5_RING_OP_DUP) ;
+			break ;
+		}
+		/*RM46*/
+		else if (cmd == RM_TIMEOUT_T_STUCK &&
+			smc->r.rm_join && smc->r.bn_flag) {
+			GO_STATE(RM6_DIRECTED) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM5_RING_OP_DUP) :
+		stop_rmt_timer0(smc) ;
+		stop_rmt_timer1(smc) ;
+		stop_rmt_timer2(smc) ;
+		DB_RMTN(1,"RMT : RM5_RING_OP_DUP\n",0,0) ;
+		ACTIONS_DONE() ;
+		break;
+	case RM5_RING_OP_DUP :
+		/*RM52*/
+		if (smc->r.dup_addr_test == DA_PASSED) {
+			smc->r.da_flag = FALSE ;
+			GO_STATE(RM2_RING_OP) ;
+			break ;
+		}
+		/*RM54*/
+		else if (cmd == RM_RING_NON_OP) {
+			smc->r.jm_flag = FALSE ;
+			smc->r.bn_flag = FALSE ;
+			GO_STATE(RM4_NON_OP_DUP) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM6_DIRECTED) :
+		start_rmt_timer0(smc,smc->s.rmt_t_direct,RM_TIMEOUT_T_DIRECT) ;
+		stop_rmt_timer1(smc) ;
+		start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ;
+		sm_ma_control(smc,MA_DIRECTED) ;
+		RS_SET(smc,RS_BEACON) ;
+		DB_RMTN(1,"RMT : RM6_DIRECTED\n",0,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM6_DIRECTED :
+		/*RM63*/
+		if (cmd == RM_TIMEOUT_POLL) {
+			start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL);
+			sm_mac_check_beacon_claim(smc) ;
+#ifndef SUPERNET_3
+			/* Because of problems with the Supernet II chip set
+			 * sending of Directed Beacon will stop after 165ms
+			 * therefore restart_trt_for_dbcn(smc) will be called
+			 * to prevent this.
+			 */
+			restart_trt_for_dbcn(smc) ;
+#endif /*SUPERNET_3*/
+			break ;
+		}
+		if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) &&
+			!smc->r.da_flag) {
+			smc->r.bn_flag = FALSE ;
+			GO_STATE(RM3_DETECT) ;
+			break ;
+		}
+		/*RM64*/
+		else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) &&
+			smc->r.da_flag) {
+			smc->r.bn_flag = FALSE ;
+			GO_STATE(RM4_NON_OP_DUP) ;
+			break ;
+		}
+		/*RM67*/
+		else if (cmd == RM_TIMEOUT_T_DIRECT) {
+			GO_STATE(RM7_TRACE) ;
+			break ;
+		}
+		break ;
+	case ACTIONS(RM7_TRACE) :
+		stop_rmt_timer0(smc) ;
+		stop_rmt_timer1(smc) ;
+		stop_rmt_timer2(smc) ;
+		smc->e.trace_prop |= ENTITY_BIT(ENTITY_MAC) ;
+		queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ;
+		DB_RMTN(1,"RMT : RM7_TRACE\n",0,0) ;
+		ACTIONS_DONE() ;
+		break ;
+	case RM7_TRACE :
+		break ;
+	default:
+		SMT_PANIC(smc,SMT_E0122, SMT_E0122_MSG) ;
+		break;
+	}
+}
+
+/*
+ * (jd) RMT duplicate address actions
+ * leave the ring or reinsert just as configured
+ */
+static void rmt_dup_actions(smc)
+struct s_smc *smc ;
+{
+	if (smc->r.jm_flag) {
+	}
+	else {
+		if (smc->s.rmt_dup_mac_behavior) {
+			SMT_ERR_LOG(smc,SMT_E0138, SMT_E0138_MSG) ;
+                        rmt_reinsert_actions(smc) ;
+		}
+		else {
+			SMT_ERR_LOG(smc,SMT_E0135, SMT_E0135_MSG) ;
+			rmt_leave_actions(smc) ;
+		}
+	}
+}
+
+/*
+ * Reconnect to the Ring
+ */
+static void rmt_reinsert_actions(smc)
+struct s_smc *smc ;
+{
+	queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+	queue_event(smc,EVENT_ECM,EC_CONNECT) ;
+}
+
+/*
+ * duplicate address detected
+ */
+static void rmt_new_dup_actions(smc)
+struct s_smc *smc ;
+{
+	smc->r.da_flag = TRUE ;
+	smc->r.bn_flag = FALSE ;
+	smc->r.jm_flag = FALSE ;
+	/*
+	 * we have three options : change address, jam or leave
+	 * we leave the ring as default 
+	 * Optionally it's possible to reinsert after leaving the Ring
+	 * but this will not conform with SMT Spec.
+	 */
+	if (smc->s.rmt_dup_mac_behavior) {
+		SMT_ERR_LOG(smc,SMT_E0138, SMT_E0138_MSG) ;
+		rmt_reinsert_actions(smc) ;
+	}
+	else {
+		SMT_ERR_LOG(smc,SMT_E0135, SMT_E0135_MSG) ;
+		rmt_leave_actions(smc) ;
+	}
+}
+
+
+/*
+ * leave the ring
+ */
+static void rmt_leave_actions(smc)
+struct s_smc *smc ;
+{
+	queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+	/*
+	 * Note: Do NOT try again later. (with please reconnect)
+	 * The station must be left from the ring!
+	 */
+}
+
+/*
+ * SMT timer interface
+ *	start RMT timer 0
+ */
+static void start_rmt_timer0(smc,value,event)
+struct s_smc *smc ;
+u_long value ;
+int event ;
+{
+	smc->r.timer0_exp = FALSE ;		/* clear timer event flag */
+	smt_timer_start(smc,&smc->r.rmt_timer0,value,EV_TOKEN(EVENT_RMT,event));
+}
+
+/*
+ * SMT timer interface
+ *	start RMT timer 1
+ */
+static void start_rmt_timer1(smc,value,event)
+struct s_smc *smc ;
+u_long value ;
+int event ;
+{
+	smc->r.timer1_exp = FALSE ;	/* clear timer event flag */
+	smt_timer_start(smc,&smc->r.rmt_timer1,value,EV_TOKEN(EVENT_RMT,event));
+}
+
+/*
+ * SMT timer interface
+ *	start RMT timer 2
+ */
+static void start_rmt_timer2(smc,value,event)
+struct s_smc *smc ;
+u_long value ;
+int event ;
+{
+	smc->r.timer2_exp = FALSE ;		/* clear timer event flag */
+	smt_timer_start(smc,&smc->r.rmt_timer2,value,EV_TOKEN(EVENT_RMT,event));
+}
+
+/*
+ * SMT timer interface
+ *	stop RMT timer 0
+ */
+static void stop_rmt_timer0(smc)
+struct s_smc *smc ;
+{
+	if (smc->r.rmt_timer0.tm_active)
+		smt_timer_stop(smc,&smc->r.rmt_timer0) ;
+}
+
+/*
+ * SMT timer interface
+ *	stop RMT timer 1
+ */
+static void stop_rmt_timer1(smc)
+struct s_smc *smc ;
+{
+	if (smc->r.rmt_timer1.tm_active)
+		smt_timer_stop(smc,&smc->r.rmt_timer1) ;
+}
+
+/*
+ * SMT timer interface
+ *	stop RMT timer 2
+ */
+static void stop_rmt_timer2(smc)
+struct s_smc *smc ;
+{
+	if (smc->r.rmt_timer2.tm_active)
+		smt_timer_stop(smc,&smc->r.rmt_timer2) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/skfddi.c linux.18p17/drivers/net/skfp/skfddi.c
--- linux.vanilla/drivers/net/skfp/skfddi.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/skfddi.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,2614 @@
+/*
+ * File Name:
+ *   skfddi.c
+ *
+ * Copyright Information:
+ *   Copyright SysKonnect 1998,1999.
+ *
+ * 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.
+ *
+ * The information in this file is provided "AS IS" without warranty.
+ *
+ * Abstract:
+ *   A Linux device driver supporting the SysKonnect FDDI PCI controller
+ *   familie.
+ *
+ * Maintainers:
+ *   CG    Christoph Goos (cgoos@syskonnect.de)
+ *
+ * Contributors:
+ *   DM    David S. Miller
+ *   OT    Oliver Teuber
+ *
+ * Address all questions to:
+ *   linux@syskonnect.de
+ *
+ * The technical manual for the adapters is available from SysKonnect's
+ * web pages: www.syskonnect.com
+ * Goto "Support" and search Knowledge Base for "manual".
+ *
+ * Driver Architecture:
+ *   The driver architecture is based on the DEC FDDI driver by
+ *   Lawrence V. Stefani and several ethernet drivers.
+ *   I also used an existing Windows NT miniport driver.
+ *   All hardware dependant fuctions are handled by the SysKonnect
+ *   Hardware Module.
+ *   The only headerfiles that are directly related to this source
+ *   are skfddi.c, h/types.h, h/osdef1st.h, h/targetos.h.
+ *   The others belong to the SysKonnect FDDI Hardware Module and
+ *   should better not be changed.
+ * NOTE:
+ *   Compiling this driver produces some warnings, but I did not fix
+ *   this, because the Hardware Module source is used for different
+ *   drivers, and fixing it for Linux might bring problems on other
+ *   projects. To keep the source common for all those drivers (and
+ *   thus simplify fixes to it), please do not clean it up!
+ *
+ * Modification History:
+ *              Date            Name    Description
+ *              02-Mar-98       CG	Created.
+ *
+ *		10-Mar-99	CG	Support for 2.2.x added.
+ *		25-Mar-99	CG	Corrected IRQ routing for SMP (APIC)
+ *		26-Oct-99	CG	Fixed compilation error on 2.2.13
+ *		12-Nov-99	CG	Source code release
+ *		15-Dec-99	CG	Removed some printk statements
+ *		11-Jan-2000	CG	Fixed printk statements
+ *		21-Jan-2000	OT	Added byte counter
+ *		07-May-2000	DM	64 bit fixes, new dma interface
+ *		18-Jul-2000	CG	Submitted to 2.2.17 kernel source tree.
+ *
+ * Compilation options (-Dxxx):
+ *              DRIVERDEBUG     print lots of messages to log file
+ *              DUMPPACKETS     print received/transmitted packets to logfile
+ * 
+ * Tested cpu architectures:
+ *	- i386
+ *	- sparc64
+ */
+
+/* Version information string - should be updated prior to */
+/* each new release!!! */
+#define VERSION		"2.07"
+
+static const char *boot_msg = 
+	"SysKonnect FDDI PCI Adapter driver v" VERSION " for\n"
+	"  SK-55xx/SK-58xx adapters (SK-NET FDDI-FP/UP/LP)";
+
+/* Include files */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>	// isdigit
+
+#include <linux/netdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+
+#include	"h/types.h"
+#undef ADDR			// undo Linux definition
+#include	"h/skfbi.h"
+#include	"h/fddi.h"
+#include	"h/smc.h"
+#include	"h/smtstate.h"
+
+
+// Define global routines
+int skfp_probe(struct device *dev);
+
+
+// Define module-wide (static) routines
+static struct device *alloc_device(struct device *dev, u_long iobase);
+static struct device *insert_device(struct device *dev,
+				    int (*init) (struct device *));
+static int fddi_dev_index(unsigned char *s);
+static void init_dev(struct device *dev, u_long iobase);
+static void link_modules(struct device *dev, struct device *tmp);
+static int skfp_driver_init(struct device *dev);
+static int skfp_open(struct device *dev);
+static int skfp_close(struct device *dev);
+static void skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct enet_statistics *skfp_ctl_get_stats(struct device *dev);
+static void skfp_ctl_set_multicast_list(struct device *dev);
+static void skfp_ctl_set_multicast_list_wo_lock(struct device *dev);
+static int skfp_ctl_set_mac_address(struct device *dev, void *addr);
+static int skfp_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int skfp_send_pkt(struct sk_buff *skb, struct device *dev);
+static void send_queued_packets(struct s_smc *smc);
+static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr);
+static void ResetAdapter(struct s_smc *smc);
+
+
+// Functions needed by the hardware module
+void *mac_drv_get_space(struct s_smc *smc, u_int size);
+void *mac_drv_get_desc_mem(struct s_smc *smc, u_int size);
+unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt);
+unsigned long dma_master(struct s_smc *smc, void *virt, int len, int flag);
+void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr,
+		  int flag);
+void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd);
+void llc_restart_tx(struct s_smc *smc);
+void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+			 int frag_count, int len);
+void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+			 int frag_count);
+void mac_drv_fill_rxd(struct s_smc *smc);
+void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+		       int frag_count);
+int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead,
+		    int la_len);
+void smt_timer_poll(struct s_smc *smc);
+void ring_status_indication(struct s_smc *smc, u_long status);
+unsigned long smt_get_time(void);
+void smt_stat_counter(struct s_smc *smc, int stat);
+void cfm_state_change(struct s_smc *smc, int c_state);
+void ecm_state_change(struct s_smc *smc, int e_state);
+void pcm_state_change(struct s_smc *smc, int plc, int p_state);
+void rmt_state_change(struct s_smc *smc, int r_state);
+void drv_reset_indication(struct s_smc *smc);
+void dump_data(unsigned char *Data, int length);
+
+
+// External functions from the hardware module
+extern u_int mac_drv_check_space();
+extern void read_address(struct s_smc *smc, u_char * mac_addr);
+extern void card_stop(struct s_smc *smc);
+extern int mac_drv_init(struct s_smc *smc);
+extern void hwm_tx_frag(struct s_smc *smc, char far * virt, u_long phys,
+			int len, int frame_status);
+extern int hwm_tx_init(struct s_smc *smc, u_char fc, int frag_count,
+		       int frame_len, int frame_status);
+extern int init_smt(struct s_smc *smc, u_char * mac_addr);
+extern void fddi_isr(struct s_smc *smc);
+extern void hwm_rx_frag(struct s_smc *smc, char far * virt, u_long phys,
+			int len, int frame_status);
+extern void mac_drv_rx_mode(struct s_smc *smc, int mode);
+extern void mac_drv_clear_tx_queue(struct s_smc *smc);
+extern void mac_drv_clear_rx_queue(struct s_smc *smc);
+extern void mac_clear_multicast(struct s_smc *smc);
+extern void enable_tx_irq(struct s_smc *smc, u_short queue);
+extern void mac_drv_clear_txd(struct s_smc *smc);
+
+
+// Define module-wide (static) variables
+
+static int num_boards = 0;	/* total number of adapters configured */
+static int num_fddi = 0;
+static int autoprobed = 0;
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static struct device *unlink_modules(struct device *p);
+static int loading_module = 1;
+#else
+static int loading_module = 0;
+#endif				// MODULE
+
+#ifdef DRIVERDEBUG
+#define PRINTK(s, args...) printk(s, ## args)
+#else
+#define PRINTK(s, args...)
+#endif				// DRIVERDEBUG
+
+#define PRIV(dev) (&(((struct s_smc *)dev->priv)->os))
+
+/*
+ * ==============
+ * = skfp_probe =
+ * ==============
+ *   
+ * Overview:
+ *   Probes for supported FDDI PCI controllers
+ *  
+ * Returns:
+ *   Condition code
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   This routine is called by the OS for each FDDI device name (fddi0,
+ *   fddi1,...,fddi6, fddi7) specified in drivers/net/Space.c.
+ *   If loaded as a module, it will detect and initialize all 
+ *   adapters the first time it is called.
+ *
+ *   Let's say that skfp_probe() is getting called to initialize fddi0.
+ *   Furthermore, let's say there are three supported controllers in the
+ *   system.  Before skfp_probe() leaves, devices fddi0, fddi1, and fddi2
+ *   will be initialized and a global flag will be set to indicate that
+ *   skfp_probe() has already been called.
+ *
+ *   However...the OS doesn't know that we've already initialized
+ *   devices fddi1 and fddi2 so skfp_probe() gets called again and again
+ *   until it reaches the end of the device list for FDDI (presently,
+ *   fddi7).  It's important that the driver "pretend" to probe for
+ *   devices fddi1 and fddi2 and return success.  Devices fddi3
+ *   through fddi7 will return failure since they weren't initialized.
+ *
+ *   This algorithm seems to work for the time being.  As other FDDI
+ *   drivers are written for Linux, a more generic approach (perhaps
+ *   similar to the Ethernet card approach) may need to be implemented.
+ *   
+ * Return Codes:
+ *   0           - This device (fddi0, fddi1, etc) configured successfully
+ *   -ENODEV - No devices present, or no SysKonnect FDDI PCI device
+ *                         present for this device name
+ *
+ *
+ * Side Effects:
+ *   Device structures for FDDI adapters (fddi0, fddi1, etc) are
+ *   initialized and the board resources are read and stored in
+ *   the device structure.
+ */
+int skfp_probe(struct device *dev)
+{
+	int i;			/* used in for loops */
+	struct pci_dev *pdev = NULL;	/* PCI device structure */
+#ifndef MEM_MAPPED_IO
+	u16 port;		/* temporary I/O (port) address */
+	int port_len;		/* length of port address range (in bytes) */
+#else
+	unsigned long port;
+#endif
+	u16 command;	/* PCI Configuration space Command register val */
+	struct s_smc *smc;	/* board pointer */
+	struct device *tmp = dev;
+	u8 first_dev_used = 0;
+	u16 SubSysId;
+#ifdef __sparc_v9__
+	unsigned short	pci_tmp;
+#endif
+
+	PRINTK(KERN_INFO "entering skfp_probe\n");
+
+	/*
+	 * Verify whether we're going through skfp_probe() again
+	 *
+	 * If so, see if we're going through for a subsequent fddi device that
+	 * we've already initialized.  If we are, return success (0).  If not,
+	 * return failure (-ENODEV).
+	 */
+
+	if (autoprobed) {
+		PRINTK(KERN_INFO "Already entered skfp_probe\n");
+		if (dev != NULL) {
+			if ((strncmp(dev->name, "fddi", 4) == 0) &&
+			    (dev->base_addr != 0)) {
+				return (0);
+			}
+			return (-ENODEV);
+		}
+	}
+	autoprobed = 1;		/* set global flag */
+
+	printk("%s\n", boot_msg);
+
+	/* Scan for Syskonnect FDDI PCI controllers */
+	if (!pci_present()) {	/* is PCI BIOS even present? */
+		printk("no PCI BIOS present\n");
+		return (-ENODEV);
+	}
+	for (i = 0; i < SKFP_MAX_NUM_BOARDS; i++) {	// scan for PCI cards
+		PRINTK(KERN_INFO "Check device %d\n", i);
+		if ((pdev=pci_find_device(PCI_VENDOR_ID_SK, PCI_DEVICE_ID_SK_FP,
+			pdev)) == 0) {
+			break;
+		}
+
+#ifndef MEM_MAPPED_IO
+		/* Verify that I/O enable bit is set (PCI slot is enabled) */
+		pci_read_config_word(pdev, PCI_COMMAND, &command);
+		if ((command & PCI_COMMAND_IO) == 0) {
+			PRINTK("I/O enable bit not set!");
+			PRINTK(" Verify that slot is enabled\n");
+			continue;
+		}
+
+		/* Turn off memory mapped space and enable mastering */
+
+		PRINTK(KERN_INFO "Command Reg: %04x\n", command);
+		command |= PCI_COMMAND_MASTER;
+		command &= ~PCI_COMMAND_MEMORY;
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+
+		/* Read I/O base address from PCI Configuration Space */
+
+		pci_read_config_word(pdev, PCI_BASE_ADDRESS_1, &port);
+		port &= PCI_BASE_ADDRESS_IO_MASK; // clear I/O bit (bit 0)
+
+		/* Verify port address range is not already being used */
+
+		port_len = FP_IO_LEN;
+		if (check_region(port, port_len) != 0) {
+			printk("I/O range allocated to adapter");
+			printk(" (0x%X-0x%X) is already being used!\n", port,
+			       (port + port_len - 1));
+			continue;
+		}
+#else
+		/* Verify that MEM enable bit is set (PCI slot is enabled) */
+		pci_read_config_word(pdev, PCI_COMMAND, &command);
+		if ((command & PCI_COMMAND_MEMORY) == 0) {
+			PRINTK("MEMORY-I/O enable bit not set!");
+			PRINTK(" Verify that slot is enabled\n");
+			continue;
+		}
+
+		pci_set_master(pdev);
+
+#ifdef __sparc_v9__
+		/* SPARC machines do not initialize the cache line size */
+                pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
+		/* SPARC machines do not set "Memory write and invalidate" */
+                pci_read_config_word(pdev, PCI_COMMAND, &pci_tmp);
+		pci_tmp |= PCI_COMMAND_INVALIDATE;
+                pci_write_config_word(pdev, PCI_COMMAND, pci_tmp);
+#endif 
+		
+		port = pdev->base_address[0];
+		port &= PCI_BASE_ADDRESS_MEM_MASK;
+
+#ifndef __sparc_v9__
+		port = (unsigned long)ioremap(port, 0x4000);
+#else
+		/* workaround for bug in 2.2 kernel for sparcv9 */
+		/* use port as is */
+#endif
+		if (!port){
+			printk("skfp:  Unable to map MEMORY register, "
+			"FDDI adapter will be disabled.\n");
+			break;
+		}
+#endif
+		
+		if ((!loading_module) || first_dev_used) {
+			/* Allocate a device structure for this adapter */
+			tmp = alloc_device(dev, port);
+		}
+		first_dev_used = 1;	// only significant first time
+
+		pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &SubSysId);
+		
+		if (tmp != NULL) {
+			if (loading_module)
+				link_modules(dev, tmp);
+			dev = tmp;
+			init_dev(dev, port);
+			dev->irq = pdev->irq;
+
+			/* Initialize board structure with bus-specific info */
+
+			smc = (struct s_smc *) dev->priv;
+			smc->os.dev = dev;
+			smc->os.bus_type = SK_BUS_TYPE_PCI;
+			smc->os.pdev = *pdev;
+			smc->os.QueueSkb = MAX_TX_QUEUE_LEN;
+			smc->os.MaxFrameSize = MAX_FRAME_SIZE;
+			smc->os.dev = dev;
+			smc->hw.slot = -1;
+			smc->os.ResetRequested = FALSE;
+			skb_queue_head_init(&smc->os.SendSkbQueue);
+
+			if (skfp_driver_init(dev) == 0) {
+				// only increment global board 
+				// count on success
+				num_boards++;
+				request_region(dev->base_addr,
+					       FP_IO_LEN, dev->name);
+				if ((SubSysId & 0xff00) == 0x5500 ||
+					(SubSysId & 0xff00) == 0x5800) {
+				printk("%s: SysKonnect FDDI PCI adapter"
+				       " found (SK-%04X)\n", dev->name,
+					SubSysId);
+				} else {
+				printk("%s: FDDI PCI adapter found\n",
+					dev->name);
+				}
+			} else {
+				kfree(dev);
+				i = SKFP_MAX_NUM_BOARDS;	// stop search
+
+			}
+
+		}		// if (dev != NULL)
+
+	}			// for SKFP_MAX_NUM_BOARDS
+
+	/*
+	 * If we're at this point we're going through skfp_probe() for the
+	 * first time. Return success (0) if we've initialized 1 or more
+	 * boards. Otherwise, return failure (-ENODEV).
+	 */
+
+	if (num_boards > 0)
+		return (0);
+	else {
+		printk("no SysKonnect FDDI adapter found\n");
+		return (-ENODEV);
+	}
+}				// skfp_probe
+
+
+/************************
+ *
+ * Search the entire 'fddi' device list for a fixed probe. If a match isn't
+ * found then check for an autoprobe or unused device location. If they
+ * are not available then insert a new device structure at the end of
+ * the current list.
+ *
+ ************************/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+	struct device *adev = NULL;
+	int fixed = 0, new_dev = 0;
+
+	PRINTK(KERN_INFO "entering alloc_device\n");
+	if (!dev)
+		return dev;
+
+	num_fddi = fddi_dev_index(dev->name);
+	if (loading_module) {
+		num_fddi++;
+		dev = insert_device(dev, skfp_probe);
+		return dev;
+	}
+	while (1) {
+		if (((dev->base_addr == NO_ADDRESS) ||
+		     (dev->base_addr == 0)) && !adev) {
+			adev = dev;
+		} else if ((dev->priv == NULL) && (dev->base_addr == iobase)) {
+			fixed = 1;
+		} else {
+			if (dev->next == NULL) {
+				new_dev = 1;
+			} else if (strncmp(dev->next->name, "fddi", 4) != 0) {
+				new_dev = 1;
+			}
+		}
+		if ((dev->next == NULL) || new_dev || fixed)
+			break;
+		dev = dev->next;
+		num_fddi++;
+	}			// while (1)
+
+	if (adev && !fixed) {
+		dev = adev;
+		num_fddi = fddi_dev_index(dev->name);
+		new_dev = 0;
+	}
+	if (((dev->next == NULL) && ((dev->base_addr != NO_ADDRESS) &&
+				     (dev->base_addr != 0)) && !fixed) ||
+	    new_dev) {
+		num_fddi++;	/* New device */
+		dev = insert_device(dev, skfp_probe);
+	}
+	if (dev) {
+		if (!dev->priv) {
+			/* Allocate space for private board structure */
+			dev->priv = (void *) kmalloc(sizeof(struct s_smc),
+						     GFP_KERNEL);
+			if (dev->priv == NULL) {
+				printk("%s: Could not allocate memory for",
+					dev->name);
+				printk(" private board structure!\n");
+				return (NULL);
+			}
+			/* clear structure */
+			memset(dev->priv, 0, sizeof(struct s_smc));
+		}
+	}
+	return dev;
+}				// alloc_device
+
+
+
+/************************
+ *
+ * Initialize device structure
+ *
+ ************************/
+static void init_dev(struct device *dev, u_long iobase)
+{
+	/* Initialize new device structure */
+
+	dev->rmem_end = 0;	/* shared memory isn't used */
+	dev->rmem_start = 0;	/* shared memory isn't used */
+	dev->mem_end = 0;	/* shared memory isn't used */
+	dev->mem_start = 0;	/* shared memory isn't used */
+	dev->base_addr = iobase;	/* save port (I/O) base address */
+	dev->irq = 0;		/* set in skfp_driver_init() */
+	dev->if_port = 0;	/* not applicable to FDDI adapters */
+	dev->dma = 0;		/* Bus Master DMA doesn't require channel */
+	dev->interrupt = 0;
+
+	dev->get_stats = &skfp_ctl_get_stats;
+	dev->open = &skfp_open;
+	dev->stop = &skfp_close;
+	dev->hard_start_xmit = &skfp_send_pkt;
+	dev->hard_header = NULL;	/* set in fddi_setup() */
+	dev->rebuild_header = NULL;	/* set in fddi_setup() */
+	dev->set_multicast_list = &skfp_ctl_set_multicast_list;
+	dev->set_mac_address = &skfp_ctl_set_mac_address;
+	dev->do_ioctl = &skfp_ioctl;
+	dev->set_config = NULL;	/* not supported for now &&& */
+	dev->header_cache_update = NULL;	/* not supported */
+	dev->change_mtu = NULL;	/* set in fddi_setup() */
+
+	/* Initialize remaining device structure information */
+	fddi_setup(dev);
+}				// init_device
+
+
+/************************
+ *
+ * If at end of fddi device list and can't use current entry, malloc
+ * one up. If memory could not be allocated, print an error message.
+ *
+************************/
+static struct device *insert_device(struct device *dev,
+				    int (*init) (struct device *))
+{
+	struct device *new;
+	int len;
+
+	PRINTK(KERN_INFO "entering insert_device\n");
+	len = sizeof(struct device) + sizeof(struct s_smc);
+	new = (struct device *) kmalloc(len, GFP_KERNEL);
+	if (new == NULL) {
+		printk("fddi%d: Device not initialised, insufficient memory\n",
+		       num_fddi);
+		return NULL;
+	} else {
+		memset((char *) new, 0, len);
+		new->name = (char *) (new + 1);
+		new->priv = (struct s_smc *) (new->name + 8);
+		new->init = init;	/* initialisation routine */
+		if (!loading_module) {
+			new->next = dev->next;
+			dev->next = new;
+		}
+		/* create new device name */
+		if (num_fddi > 999) {
+			sprintf(new->name, "fddi????");
+		} else {
+			sprintf(new->name, "fddi%d", num_fddi);
+		}
+	}
+	return new;
+}				// insert_device
+
+
+/************************
+ *
+ * Get the number of a "fddiX" string
+ *
+ ************************/
+static int fddi_dev_index(unsigned char *s)
+{
+	int i = 0, j = 0;
+
+	for (; *s; s++) {
+		if (isdigit(*s)) {
+			j = 1;
+			i = (i * 10) + (*s - '0');
+		} else if (j)
+			break;
+	}
+	return i;
+}				// fddi_dev_index
+
+
+/************************
+ *
+ * Used if loaded as module only. Link the device structures
+ * together. Needed to release them all at unload.
+ *
+************************/
+static void link_modules(struct device *dev, struct device *tmp)
+{
+	struct device *p = dev;
+
+	if (p) {
+		while (((struct s_smc *) (p->priv))->os.next_module) {
+			p = ((struct s_smc *) (p->priv))->os.next_module;
+		}
+
+		if (dev != tmp) {
+			((struct s_smc *) (p->priv))->os.next_module = tmp;
+		} else {
+			((struct s_smc *) (p->priv))->os.next_module = NULL;
+		}
+	}
+	return;
+}				// link_modules
+
+
+
+/*
+ * ====================
+ * = skfp_driver_init =
+ * ====================
+ *   
+ * Overview:
+ *   Initializes remaining adapter board structure information
+ *   and makes sure adapter is in a safe state prior to skfp_open().
+ *  
+ * Returns:
+ *   Condition code
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   This function allocates additional resources such as the host memory
+ *   blocks needed by the adapter.
+ *   The adapter is also reset. The OS must call skfp_open() to open 
+ *   the adapter and bring it on-line.
+ *
+ * Return Codes:
+ *    0 - initialization succeeded
+ *   -1 - initialization failed
+ */
+static int skfp_driver_init(struct device *dev)
+{
+	struct s_smc *smc = (struct s_smc *) dev->priv;
+	skfddi_priv *bp = PRIV(dev);
+	u8 val;			/* used for I/O read/writes */
+
+	PRINTK(KERN_INFO "entering skfp_driver_init\n");
+
+	// set the io address in private structures
+	bp->base_addr = dev->base_addr;
+	smc->hw.iop = dev->base_addr;
+
+	// Get the interrupt level from the PCI Configuration Table
+	val = dev->irq;
+
+	smc->hw.irq = val;
+
+	spin_lock_init(&bp->DriverLock);
+	
+	// Determine the required size of the 'shared' memory area.
+	bp->SharedMemSize = mac_drv_check_space();
+	PRINTK(KERN_INFO "Memory for HWM: %ld\n", bp->SharedMemSize);
+	if (bp->SharedMemSize > 0) {
+		bp->SharedMemSize += 16;	// for descriptor alignment
+
+		bp->SharedMemAddr = kmalloc(bp->SharedMemSize, GFP_KERNEL);
+		if (!bp->SharedMemSize) {
+			printk("could not allocate mem for ");
+			printk("hardware module: %ld byte\n",
+			       bp->SharedMemSize);
+			return (-1);
+		}
+		bp->SharedMemHeap = 0;	// Nothing used yet.
+
+	} else {
+		bp->SharedMemAddr = NULL;
+		bp->SharedMemHeap = 0;
+	}			// SharedMemSize > 0
+
+	memset(bp->SharedMemAddr, 0, bp->SharedMemSize);
+
+	card_stop(smc);		// Reset adapter.
+
+	PRINTK(KERN_INFO "mac_drv_init()..\n");
+	if (mac_drv_init(smc) != 0) {
+		PRINTK(KERN_INFO "mac_drv_init() failed.\n");
+		return (-1);
+	}
+	read_address(smc, NULL);
+	PRINTK(KERN_INFO "HW-Addr: %02x %02x %02x %02x %02x %02x\n",
+	       smc->hw.fddi_canon_addr.a[0],
+	       smc->hw.fddi_canon_addr.a[1],
+	       smc->hw.fddi_canon_addr.a[2],
+	       smc->hw.fddi_canon_addr.a[3],
+	       smc->hw.fddi_canon_addr.a[4],
+	       smc->hw.fddi_canon_addr.a[5]);
+	memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+
+	smt_reset_defaults(smc, 0);
+
+	return (0);
+}				// skfp_driver_init
+
+
+/*
+ * =============
+ * = skfp_open =
+ * =============
+ *   
+ * Overview:
+ *   Opens the adapter
+ *  
+ * Returns:
+ *   Condition code
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   This function brings the adapter to an operational state.
+ *
+ * Return Codes:
+ *   0           - Adapter was successfully opened
+ *   -EAGAIN - Could not register IRQ
+ */
+static int skfp_open(struct device *dev)
+{
+	struct s_smc *smc = (struct s_smc *) dev->priv;
+
+	PRINTK(KERN_INFO "entering skfp_open\n");
+	/* Register IRQ - support shared interrupts by passing device ptr */
+	if (request_irq(dev->irq, (void *) skfp_interrupt, SA_SHIRQ,
+			dev->name, dev)) {
+		printk("%s: Requested IRQ %d is busy\n", dev->name, dev->irq);
+		return (-EAGAIN);
+	}
+	/*
+	 * Set current address to factory MAC address
+	 *
+	 * Note: We've already done this step in skfp_driver_init.
+	 *       However, it's possible that a user has set a node
+	 *               address override, then closed and reopened the
+	 *               adapter.  Unless we reset the device address field
+	 *               now, we'll continue to use the existing modified
+	 *               address.
+	 */
+	read_address(smc, NULL);
+	memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+
+	init_smt(smc, NULL);
+	smt_online(smc, 1);
+	STI_FBI();
+
+	MOD_INC_USE_COUNT;
+
+	/* Set device structure info */
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	/* Clear local multicast address tables */
+	mac_clear_multicast(smc);
+
+	/* Disable promiscuous filter settings */
+	mac_drv_rx_mode(smc, RX_DISABLE_PROMISC);
+
+	return (0);
+}				// skfp_open
+
+
+/*
+ * ==============
+ * = skfp_close =
+ * ==============
+ *   
+ * Overview:
+ *   Closes the device/module.
+ *  
+ * Returns:
+ *   Condition code
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   This routine closes the adapter and brings it to a safe state.
+ *   The interrupt service routine is deregistered with the OS.
+ *   The adapter can be opened again with another call to skfp_open().
+ *
+ * Return Codes:
+ *   Always return 0.
+ *
+ * Assumptions:
+ *   No further requests for this adapter are made after this routine is
+ *   called.  skfp_open() can be called to reset and reinitialize the
+ *   adapter.
+ */
+static int skfp_close(struct device *dev)
+{
+	struct s_smc *smc = (struct s_smc *) dev->priv;
+	struct sk_buff *skb;
+	skfddi_priv *bp = PRIV(dev);
+
+	CLI_FBI();
+	smt_reset_defaults(smc, 1);
+	card_stop(smc);
+	mac_drv_clear_tx_queue(smc);
+	mac_drv_clear_rx_queue(smc);
+
+	/* Clear device structure flags */
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	/* Deregister (free) IRQ */
+	free_irq(dev->irq, dev);
+
+	for (;;) {
+		skb = skb_dequeue(&bp->SendSkbQueue);
+		if (skb == NULL)
+			break;
+		bp->QueueSkb++;
+		dev_kfree_skb(skb);
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return (0);
+}				// skfp_close
+
+
+/*
+ * ==================
+ * = skfp_interrupt =
+ * ==================
+ *   
+ * Overview:
+ *   Interrupt processing routine
+ *  
+ * Returns:
+ *   None
+ *       
+ * Arguments:
+ *   irq        - interrupt vector
+ *   dev_id     - pointer to device information
+ *       regs   - pointer to registers structure
+ *
+ * Functional Description:
+ *   This routine calls the interrupt processing routine for this adapter.  It
+ *   disables and reenables adapter interrupts, as appropriate.  We can support
+ *   shared interrupts since the incoming dev_id pointer provides our device
+ *   structure context. All the real work is done in the hardware module.
+ *
+ * Return Codes:
+ *   None
+ *
+ * Assumptions:
+ *   The interrupt acknowledgement at the hardware level (eg. ACKing the PIC
+ *   on Intel-based systems) is done by the operating system outside this
+ *   routine.
+ *
+ *       System interrupts are enabled through this call.
+ *
+ * Side Effects:
+ *   Interrupts are disabled, then reenabled at the adapter.
+ */
+
+void skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_id;
+	struct s_smc *smc;	/* private board structure pointer */
+	skfddi_priv *bp = PRIV(dev);
+
+
+	if (dev == NULL) {
+		printk("%s: ERROR: irq %d for unknown device\n", dev->name, irq);
+		return;
+	}
+	if (dev->interrupt) {
+		printk("%s: ERROR: reentering interrupt handler\n", dev->name);
+	}
+	dev->interrupt = 1;
+
+	smc = (struct s_smc *) dev->priv;
+
+	// IRQs enabled or disabled ?
+	if (inpd(ADDR(B0_IMSK)) == 0) {
+		// IRQs are disabled.
+		// printk(KERN_INFO "Foreign IRQ (B).\n");
+		dev->interrupt = 0;
+		return;
+	}
+	// Note: At this point, IRQs are enabled.
+	if ((inpd(ISR_A) & smc->hw.is_imask) == 0) {	// IRQ?
+		// Adapter did not issue an IRQ.
+		// printk(KERN_INFO "Foreign IRQ (C).\n");
+		dev->interrupt = 0;
+		return;
+	}
+	CLI_FBI();		// Disable IRQs from our adapter.
+	spin_lock(&bp->DriverLock);
+
+	// Call interrupt handler in hardware module (HWM).
+	fddi_isr(smc);
+
+	if (smc->os.ResetRequested) {
+		ResetAdapter(smc);
+		smc->os.ResetRequested = FALSE;
+	}
+	dev->interrupt = 0;
+	spin_unlock(&bp->DriverLock);
+	STI_FBI();		// Enable IRQs from our adapter.
+
+	return;
+}				// skfp_interrupt
+
+
+/*
+ * ======================
+ * = skfp_ctl_get_stats =
+ * ======================
+ *   
+ * Overview:
+ *   Get statistics for FDDI adapter
+ *  
+ * Returns:
+ *   Pointer to FDDI statistics structure
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   Gets current MIB objects from adapter, then
+ *   returns FDDI statistics structure as defined
+ *   in if_fddi.h.
+ *
+ *   Note: Since the FDDI statistics structure is
+ *   still new and the device structure doesn't
+ *   have an FDDI-specific get statistics handler,
+ *   we'll return the FDDI statistics structure as
+ *   a pointer to an Ethernet statistics structure.
+ *   That way, at least the first part of the statistics
+ *   structure can be decoded properly.
+ *   We'll have to pay attention to this routine as the
+ *   device structure becomes more mature and LAN media
+ *   independent.
+ *
+ */
+struct enet_statistics *skfp_ctl_get_stats(struct device *dev)
+{
+	struct s_smc *bp = (struct s_smc *) dev->priv;
+
+	/* Fill the bp->stats structure with driver-maintained counters */
+
+	bp->os.MacStat.port_bs_flag[0] = 0x1234;
+	bp->os.MacStat.port_bs_flag[1] = 0x5678;
+// goos: need to fill out fddi statistic
+#if 0
+	/* Get FDDI SMT MIB objects */
+
+/* Fill the bp->stats structure with the SMT MIB object values */
+
+	memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id));
+	bp->stats.smt_op_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id;
+	bp->stats.smt_hi_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id;
+	bp->stats.smt_lo_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id;
+	memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data));
+	bp->stats.smt_mib_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id;
+	bp->stats.smt_mac_cts = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct;
+	bp->stats.smt_non_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct;
+	bp->stats.smt_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct;
+	bp->stats.smt_available_paths = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths;
+	bp->stats.smt_config_capabilities = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities;
+	bp->stats.smt_config_policy = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy;
+	bp->stats.smt_connection_policy = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy;
+	bp->stats.smt_t_notify = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify;
+	bp->stats.smt_stat_rpt_policy = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy;
+	bp->stats.smt_trace_max_expiration = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration;
+	bp->stats.smt_bypass_present = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present;
+	bp->stats.smt_ecm_state = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state;
+	bp->stats.smt_cf_state = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state;
+	bp->stats.smt_remote_disconnect_flag = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag;
+	bp->stats.smt_station_status = bp->cmd_rsp_virt->smt_mib_get.smt_station_status;
+	bp->stats.smt_peer_wrap_flag = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag;
+	bp->stats.smt_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls;
+	bp->stats.smt_transition_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls;
+	bp->stats.mac_frame_status_functions = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions;
+	bp->stats.mac_t_max_capability = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability;
+	bp->stats.mac_tvx_capability = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability;
+	bp->stats.mac_available_paths = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths;
+	bp->stats.mac_current_path = bp->cmd_rsp_virt->smt_mib_get.mac_current_path;
+	memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN);
+	memcpy(bp->stats.mac_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_downstream_nbr, FDDI_K_ALEN);
+	memcpy(bp->stats.mac_old_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_upstream_nbr, FDDI_K_ALEN);
+	memcpy(bp->stats.mac_old_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_downstream_nbr, FDDI_K_ALEN);
+	bp->stats.mac_dup_address_test = bp->cmd_rsp_virt->smt_mib_get.mac_dup_address_test;
+	bp->stats.mac_requested_paths = bp->cmd_rsp_virt->smt_mib_get.mac_requested_paths;
+	bp->stats.mac_downstream_port_type = bp->cmd_rsp_virt->smt_mib_get.mac_downstream_port_type;
+	memcpy(bp->stats.mac_smt_address, &bp->cmd_rsp_virt->smt_mib_get.mac_smt_address, FDDI_K_ALEN);
+	bp->stats.mac_t_req = bp->cmd_rsp_virt->smt_mib_get.mac_t_req;
+	bp->stats.mac_t_neg = bp->cmd_rsp_virt->smt_mib_get.mac_t_neg;
+	bp->stats.mac_t_max = bp->cmd_rsp_virt->smt_mib_get.mac_t_max;
+	bp->stats.mac_tvx_value = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_value;
+	bp->stats.mac_frame_error_threshold = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_threshold;
+	bp->stats.mac_frame_error_ratio = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_ratio;
+	bp->stats.mac_rmt_state = bp->cmd_rsp_virt->smt_mib_get.mac_rmt_state;
+	bp->stats.mac_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_da_flag;
+	bp->stats.mac_una_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_unda_flag;
+	bp->stats.mac_frame_error_flag = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_flag;
+	bp->stats.mac_ma_unitdata_available = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_available;
+	bp->stats.mac_hardware_present = bp->cmd_rsp_virt->smt_mib_get.mac_hardware_present;
+	bp->stats.mac_ma_unitdata_enable = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_enable;
+	bp->stats.path_tvx_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_tvx_lower_bound;
+	bp->stats.path_t_max_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_t_max_lower_bound;
+	bp->stats.path_max_t_req = bp->cmd_rsp_virt->smt_mib_get.path_max_t_req;
+	memcpy(bp->stats.path_configuration, &bp->cmd_rsp_virt->smt_mib_get.path_configuration, sizeof(bp->cmd_rsp_virt->smt_mib_get.path_configuration));
+	bp->stats.port_my_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[0];
+	bp->stats.port_my_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[1];
+	bp->stats.port_neighbor_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[0];
+	bp->stats.port_neighbor_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[1];
+	bp->stats.port_connection_policies[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[0];
+	bp->stats.port_connection_policies[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[1];
+	bp->stats.port_mac_indicated[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[0];
+	bp->stats.port_mac_indicated[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[1];
+	bp->stats.port_current_path[0] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[0];
+	bp->stats.port_current_path[1] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[1];
+	memcpy(&bp->stats.port_requested_paths[0 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[0], 3);
+	memcpy(&bp->stats.port_requested_paths[1 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[1], 3);
+	bp->stats.port_mac_placement[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[0];
+	bp->stats.port_mac_placement[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[1];
+	bp->stats.port_available_paths[0] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[0];
+	bp->stats.port_available_paths[1] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[1];
+	bp->stats.port_pmd_class[0] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[0];
+	bp->stats.port_pmd_class[1] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[1];
+	bp->stats.port_connection_capabilities[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[0];
+	bp->stats.port_connection_capabilities[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[1];
+	bp->stats.port_bs_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[0];
+	bp->stats.port_bs_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[1];
+	bp->stats.port_ler_estimate[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[0];
+	bp->stats.port_ler_estimate[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[1];
+	bp->stats.port_ler_cutoff[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[0];
+	bp->stats.port_ler_cutoff[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[1];
+	bp->stats.port_ler_alarm[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[0];
+	bp->stats.port_ler_alarm[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[1];
+	bp->stats.port_connect_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[0];
+	bp->stats.port_connect_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[1];
+	bp->stats.port_pcm_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[0];
+	bp->stats.port_pcm_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[1];
+	bp->stats.port_pc_withhold[0] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[0];
+	bp->stats.port_pc_withhold[1] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[1];
+	bp->stats.port_ler_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[0];
+	bp->stats.port_ler_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[1];
+	bp->stats.port_hardware_present[0] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[0];
+	bp->stats.port_hardware_present[1] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[1];
+
+
+	/* Fill the bp->stats structure with the FDDI counter values */
+
+	bp->stats.mac_frame_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.frame_cnt.ls;
+	bp->stats.mac_copied_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.copied_cnt.ls;
+	bp->stats.mac_transmit_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.transmit_cnt.ls;
+	bp->stats.mac_error_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.error_cnt.ls;
+	bp->stats.mac_lost_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.lost_cnt.ls;
+	bp->stats.port_lct_fail_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[0].ls;
+	bp->stats.port_lct_fail_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[1].ls;
+	bp->stats.port_lem_reject_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[0].ls;
+	bp->stats.port_lem_reject_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[1].ls;
+	bp->stats.port_lem_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[0].ls;
+	bp->stats.port_lem_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[1].ls;
+
+#endif
+	return ((struct enet_statistics *) &bp->os.MacStat);
+}				// ctl_get_stat
+
+
+/*
+ * ==============================
+ * = skfp_ctl_set_multicast_list =
+ * ==============================
+ *   
+ * Overview:
+ *   Enable/Disable LLC frame promiscuous mode reception
+ *   on the adapter and/or update multicast address table.
+ *  
+ * Returns:
+ *   None
+ *       
+ * Arguments:
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   This function aquires the driver lock and only calls
+ *   skfp_ctl_set_multicast_list_wo_lock then.
+ *   This routine follows a fairly simple algorithm for setting the
+ *   adapter filters and CAM:
+ *
+ *      if IFF_PROMISC flag is set
+ *              enable promiscuous mode
+ *      else
+ *              disable promiscuous mode
+ *              if number of multicast addresses <= max. multicast number
+ *                      add mc addresses to adapter table
+ *              else
+ *                      enable promiscuous mode
+ *              update adapter filters
+ *
+ * Assumptions:
+ *   Multicast addresses are presented in canonical (LSB) format.
+ *
+ * Side Effects:
+ *   On-board adapter filters are updated.
+ */
+static void skfp_ctl_set_multicast_list(struct device *dev)
+{
+#ifdef __SMP__
+	skfddi_priv *bp = PRIV(dev);
+#endif
+	unsigned long Flags;
+
+	spin_lock_irqsave(&bp->DriverLock, Flags);
+	skfp_ctl_set_multicast_list_wo_lock(dev);
+	spin_unlock_irqrestore(&bp->DriverLock, Flags);
+	return;
+}				// skfp_ctl_set_multicast_list
+
+
+
+static void skfp_ctl_set_multicast_list_wo_lock(struct device *dev)
+{
+	struct s_smc *smc = (struct s_smc *) dev->priv;
+	struct dev_mc_list *dmi;	/* ptr to multicast addr entry */
+	int i;
+
+	/* Enable promiscuous mode, if necessary */
+	if (dev->flags & IFF_PROMISC) {
+		mac_drv_rx_mode(smc, RX_ENABLE_PROMISC);
+		PRINTK(KERN_INFO "PROMISCUOUS MODE ENABLED\n");
+	}
+	/* Else, update multicast address table */
+	else {
+		mac_drv_rx_mode(smc, RX_DISABLE_PROMISC);
+		PRINTK(KERN_INFO "PROMISCUOUS MODE DISABLED\n");
+
+		// Reset all MC addresses
+		mac_clear_multicast(smc);
+		mac_drv_rx_mode(smc, RX_DISABLE_ALLMULTI);
+
+		if (dev->flags & IFF_ALLMULTI) {
+			mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI);
+			PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n");
+		} else if (dev->mc_count > 0) {
+			if (dev->mc_count <= FPMAX_MULTICAST) {
+				/* use exact filtering */
+
+				// point to first multicast addr
+				dmi = dev->mc_list;
+
+				for (i = 0; i < dev->mc_count; i++) {
+					mac_add_multicast(smc,
+							  dmi->dmi_addr, 1);
+					PRINTK(KERN_INFO "ENABLE MC ADDRESS:");
+					PRINTK(" %02x %02x %02x ",
+					       dmi->dmi_addr[0],
+					       dmi->dmi_addr[1],
+					       dmi->dmi_addr[2]);
+					PRINTK("%02x %02x %02x\n",
+					       dmi->dmi_addr[3],
+					       dmi->dmi_addr[4],
+					       dmi->dmi_addr[5]);
+					dmi = dmi->next;
+				}	// for
+
+			} else {	// more MC addresses than HW supports
+
+				mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI);
+				PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n");
+			}
+		} else {	// no MC addresses
+
+			PRINTK(KERN_INFO "DISABLE ALL MC ADDRESSES\n");
+		}
+
+		/* Update adapter filters */
+		mac_update_multicast(smc);
+	}
+	return;
+}				// skfp_ctl_set_multicast_list_wo_lock
+
+
+/*
+ * ===========================
+ * = skfp_ctl_set_mac_address =
+ * ===========================
+ *   
+ * Overview:
+ *   set new mac address on adapter and update dev_addr field in device table.
+ *  
+ * Returns:
+ *   None
+ *       
+ * Arguments:
+ *   dev  - pointer to device information
+ *   addr - pointer to sockaddr structure containing unicast address to set
+ *
+ * Assumptions:
+ *   The address pointed to by addr->sa_data is a valid unicast
+ *   address and is presented in canonical (LSB) format.
+ */
+static int skfp_ctl_set_mac_address(struct device *dev, void *addr)
+{
+	struct s_smc *smc = (struct s_smc *) dev->priv;
+	struct sockaddr *p_sockaddr = (struct sockaddr *) addr;
+#ifdef __SMP__
+	skfddi_priv *bp = (skfddi_priv *) & smc->os;
+#endif
+	unsigned long Flags;
+
+
+	memcpy(dev->dev_addr, p_sockaddr->sa_data, FDDI_K_ALEN);
+	spin_lock_irqsave(&bp->DriverLock, Flags);
+	ResetAdapter(smc);
+	spin_unlock_irqrestore(&bp->DriverLock, Flags);
+
+	return (0);		/* always return zero */
+}				// skfp_ctl_set_mac_address
+
+
+/*
+ * ==============
+ * = skfp_ioctl =
+ * ==============
+ *   
+ * Overview:
+ *
+ * Perform IOCTL call functions here. Some are privileged operations and the
+ * effective uid is checked in those cases.
+ *  
+ * Returns:
+ *   status value
+ *   0 - success
+ *   other - failure
+ *       
+ * Arguments:
+ *   dev  - pointer to device information
+ *   rq - pointer to ioctl request structure
+ *   cmd - ?
+ *
+ */
+
+
+static int skfp_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	skfddi_priv *lp = PRIV(dev);
+	struct s_skfp_ioctl ioc;
+	int status = 0;
+
+	copy_from_user(&ioc, rq->ifr_data, sizeof(struct s_skfp_ioctl));
+	switch (ioc.cmd) {
+	case SKFP_GET_STATS:	/* Get the driver statistics */
+		ioc.len = sizeof(lp->MacStat);
+		copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len);
+		break;
+	case SKFP_CLR_STATS:	/* Zero out the driver statistics */
+		if (suser()) {
+			memset(&lp->MacStat, 0, sizeof(lp->MacStat));
+		} else {
+			status = -EPERM;
+		}
+		break;
+	default:
+		printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd);
+	}			// switch
+
+	return status;
+}				// skfp_ioctl
+
+
+/*
+ * =====================
+ * = skfp_send_pkt     =
+ * =====================
+ *   
+ * Overview:
+ *   Queues a packet for transmission and try to transmit it.
+ *  
+ * Returns:
+ *   Condition code
+ *       
+ * Arguments:
+ *   skb - pointer to sk_buff to queue for transmission
+ *   dev - pointer to device information
+ *
+ * Functional Description:
+ *   Here we assume that an incoming skb transmit request
+ *   is contained in a single physically contiguous buffer
+ *   in which the virtual address of the start of packet
+ *   (skb->data) can be converted to a physical address
+ *   by using virt_to_bus().
+ *
+ *   We have an internal queue for packets we can not send 
+ *   immediately. Packets in this queue can be given to the 
+ *   adapter if transmit buffers are freed.
+ *
+ *   We can't free the skb until after it's been DMA'd
+ *   out by the adapter, so we'll keep it in the driver and
+ *   return it in mac_drv_tx_complete.
+ *
+ * Return Codes:
+ *   0 - driver has queued and/or sent packet
+ *       1 - caller should requeue the sk_buff for later transmission
+ *
+ * Assumptions:
+ *   The entire packet is stored in one physically
+ *   contiguous buffer which is not cached and whose
+ *   32-bit physical address can be determined.
+ *
+ *   It's vital that this routine is NOT reentered for the
+ *   same board and that the OS is not in another section of
+ *   code (eg. skfp_interrupt) for the same board on a
+ *   different thread.
+ *
+ * Side Effects:
+ *   None
+ */
+static int skfp_send_pkt(struct sk_buff *skb, struct device *dev)
+{
+	skfddi_priv *bp = PRIV(dev);
+	unsigned long Flags;
+
+	PRINTK(KERN_INFO "skfp_send_pkt\n");
+	if (dev->interrupt)
+		return 1;
+
+	if (skb == NULL) {
+		mark_bh(NET_BH);
+		return 0;
+	}
+	if (dev->tbusy) {
+		// printk(KERN_INFO "%s transmit timeout\n", dev->name);
+		spin_lock_irqsave(&bp->DriverLock, Flags);
+		ResetAdapter((struct s_smc *) dev->priv);
+		spin_unlock_irqrestore(&bp->DriverLock, Flags);
+		return 1;
+	}
+	/*
+	 * Verify that incoming transmit request is OK
+	 *
+	 * Note: The packet size check is consistent with other
+	 *               Linux device drivers, although the correct packet
+	 *               size should be verified before calling the
+	 *               transmit routine.
+	 */
+
+	if (!(skb->len >= FDDI_K_LLC_ZLEN && skb->len <= FDDI_K_LLC_LEN)) {
+		bp->MacStat.tx_errors++;	/* bump error counter */
+		// dequeue packets from xmt queue and send them
+		mark_bh(NET_BH);
+		dev_kfree_skb(skb);
+		return (0);	/* return "success" */
+	}
+	if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+		printk(KERN_INFO "%s: ERROR: Transmitter access conflict.\n",
+		       dev->name);
+		return 1;
+	}
+	if (bp->QueueSkb == 0) {	// return with tbusy set: queue full
+
+		// printk(KERN_INFO "%s: queue full\n", dev->name);
+		return 1;
+	}
+	bp->QueueSkb--;
+	skb_queue_tail(&bp->SendSkbQueue, skb);
+	send_queued_packets((struct s_smc *) dev->priv);
+	if (bp->QueueSkb > 0) {
+		dev->tbusy = 0;
+	}
+	dev->trans_start = jiffies;
+	return 0;
+
+}				// skfp_send_pkt
+
+
+/*
+ * =======================
+ * = send_queued_packets =
+ * =======================
+ *   
+ * Overview:
+ *   Send packets from the driver queue as long as there are some and
+ *   transmit resources are available.
+ *  
+ * Returns:
+ *   None
+ *       
+ * Arguments:
+ *   smc - pointer to smc (adapter) structure
+ *
+ * Functional Description:
+ *   Take a packet from queue if there is any. If not, then we are done.
+ *   Check if there are resources to send the packet. If not, requeue it
+ *   and exit. 
+ *   Set packet descriptor flags and give packet to adapter.
+ *   Check if any send resources can be freed (we do not use the
+ *   transmit complete interrupt).
+ */
+static void send_queued_packets(struct s_smc *smc)
+{
+	skfddi_priv *bp = (skfddi_priv *) & smc->os;
+	struct sk_buff *skb;
+	unsigned char fc;
+	int queue;
+	struct s_smt_fp_txd *txd;	// Current TxD.
+	static int log_counter=0;	// to avoid to much logging
+	unsigned long Flags;
+
+	int frame_status;	// HWM tx frame status.
+
+	PRINTK(KERN_INFO "send queued packets\n");
+	for (;;) {
+		// send first buffer from queue
+		skb = skb_dequeue(&bp->SendSkbQueue);
+
+		if (!skb) {
+			PRINTK(KERN_INFO "queue empty\n");
+			return;
+		}		// queue empty !
+
+		spin_lock_irqsave(&bp->DriverLock, Flags);
+		fc = skb->data[0];
+		queue = (fc & FC_SYNC_BIT) ? QUEUE_S : QUEUE_A0;
+#ifdef ESS
+		// Check if the frame may/must be sent as a synchronous frame.
+
+		if ((fc & ~(FC_SYNC_BIT | FC_LLC_PRIOR)) == FC_ASYNC_LLC) {
+			// It's an LLC frame.
+			if (!smc->ess.sync_bw_available)
+				fc &= ~FC_SYNC_BIT; // No bandwidth available.
+
+			else {	// Bandwidth is available.
+
+				if (smc->mib.fddiESSSynchTxMode) {
+					// Send as sync. frame.
+					fc |= FC_SYNC_BIT;
+				}
+			}
+		}
+#endif				// ESS
+		frame_status = hwm_tx_init(smc, fc, 1, skb->len, queue);
+
+		if ((frame_status & (LOC_TX | LAN_TX)) == 0) {
+			// Unable to send the frame.
+
+			if ((frame_status & RING_DOWN) != 0) {
+				// Ring is down.
+				PRINTK("Tx attempt while ring down.\n");
+			} else if ((frame_status & OUT_OF_TXD) != 0) {
+				if(log_counter == 0) {
+					// printk(KERN_INFO "%s: out of TXDs.\n",
+					//	bp->dev->name);
+				}
+				log_counter++;
+				if (log_counter >= 100000) 
+					log_counter = 0;
+			} else {
+				if(log_counter == 0) {
+					// printk(KERN_INFO "%s: out of transmit"
+					//	"resources\n",
+					//	bp->dev->name);
+				}
+				log_counter++;
+				if (log_counter >= 100000) 
+					log_counter = 0;
+			}
+
+			// Note: We will retry the operation as soon as
+			// transmit resources become available.
+			skb_queue_head(&bp->SendSkbQueue, skb);
+			spin_unlock_irqrestore(&bp->DriverLock, Flags);
+			return;	// Packet has been queued.
+
+		}		// if (unable to send frame)
+
+		bp->QueueSkb++;	// one packet less in local queue
+
+		// source address in packet ?
+		CheckSourceAddress(skb->data, smc->hw.fddi_canon_addr.a);
+
+		txd = (struct s_smt_fp_txd *) HWM_GET_CURR_TXD(smc, queue);
+
+		if (frame_status & LAN_TX) {
+			txd->txd_os.skb = skb;	// save skb
+
+		}
+		hwm_tx_frag(smc, skb->data, virt_to_bus(skb->data), skb->len,
+                      frame_status | FIRST_FRAG | LAST_FRAG | EN_IRQ_EOF);
+
+		if (!(frame_status & LAN_TX)) {		// local only frame
+			dev_kfree_skb(skb);
+		}
+		spin_unlock_irqrestore(&bp->DriverLock, Flags);
+	}			// for
+
+	return;			// never reached
+
+}				// send_queued_packets
+
+
+/************************
+ * 
+ * CheckSourceAddress
+ *
+ * Verify if the source address is set. Insert it if necessary.
+ *
+ ************************/
+void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr)
+{
+	unsigned char SRBit;
+
+	if ((((unsigned long) frame[1 + 6]) & ~0x01) != 0) // source routing bit
+
+		return;
+	if ((unsigned short) frame[1 + 10] != 0)
+		return;
+	SRBit = frame[1 + 6] & 0x01;
+	memcpy(&frame[1 + 6], hw_addr, 6);
+	frame[8] |= SRBit;
+}				// CheckSourceAddress
+
+
+/************************
+ *
+ *	ResetAdapter
+ *
+ *	Reset the adapter and bring it back to operational mode.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+static void ResetAdapter(struct s_smc *smc)
+{
+
+	PRINTK(KERN_INFO "[fddi: ResetAdapter]\n");
+
+	// Stop the adapter.
+
+	card_stop(smc);		// Stop all activity.
+
+	// Clear the transmit and receive descriptor queues.
+	mac_drv_clear_tx_queue(smc);
+	mac_drv_clear_rx_queue(smc);
+
+	// Restart the adapter.
+
+	smt_reset_defaults(smc, 1);	// Initialize the SMT module.
+
+	init_smt(smc, (smc->os.dev)->dev_addr);	// Initialize the hardware.
+
+	smt_online(smc, 1);	// Insert into the ring again.
+	STI_FBI();
+
+	// Restore original receive mode (multicasts, promiscuous, etc.).
+	skfp_ctl_set_multicast_list_wo_lock(smc->os.dev);
+}				// ResetAdapter
+
+
+//--------------- functions called by hardware module ----------------
+
+/************************
+ *
+ *	llc_restart_tx
+ *
+ *	The hardware driver calls this routine when the transmit complete
+ *	interrupt bits (end of frame) for the synchronous or asynchronous
+ *	queue is set.
+ *
+ * NOTE The hardware driver calls this function also if no packets are queued.
+ *	The routine must be able to handle this case.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void llc_restart_tx(struct s_smc *smc)
+{
+	skfddi_priv *bp = (skfddi_priv *) & smc->os;
+
+	PRINTK(KERN_INFO "[llc_restart_tx]\n");
+
+	// Try to send queued packets
+	spin_unlock(&bp->DriverLock);
+	send_queued_packets(smc);
+	spin_lock(&bp->DriverLock);
+	bp->dev->tbusy = 0;	// system may send again if it was blocked
+
+}				// llc_restart_tx
+
+
+/************************
+ *
+ *	mac_drv_get_space
+ *
+ *	The hardware module calls this function to allocate the memory
+ *	for the SMT MBufs if the define MB_OUTSIDE_SMC is specified.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	size - Size of memory in bytes to allocate.
+ * Out
+ *	!= 0	A pointer to the virtual address of the allocated memory.
+ *	== 0	Allocation error.
+ *
+ ************************/
+void *mac_drv_get_space(struct s_smc *smc, unsigned int size)
+{
+	void *virt;
+
+	PRINTK(KERN_INFO "mac_drv_get_space\n");
+	virt = (void *) (smc->os.SharedMemAddr + smc->os.SharedMemHeap);
+
+	if ((smc->os.SharedMemHeap + size) > smc->os.SharedMemSize) {
+		printk("Unexpected SMT memory size requested: %d\n", size);
+		return (NULL);
+	}
+	smc->os.SharedMemHeap += size;	// Move heap pointer.
+
+	PRINTK(KERN_INFO "mac_drv_get_space end\n");
+	PRINTK(KERN_INFO "virt addr: %lx\n", (ulong) virt);
+	PRINTK(KERN_INFO "bus  addr: %lx\n", (ulong) virt_to_bus(virt));
+	return (virt);
+}				// mac_drv_get_space
+
+
+/************************
+ *
+ *	mac_drv_get_desc_mem
+ *
+ *	This function is called by the hardware dependent module.
+ *	It allocates the memory for the RxD and TxD descriptors.
+ *
+ *	This memory must be non-cached, non-movable and non-swapable.
+ *	This memory should start at a physical page boundary.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	size - Size of memory in bytes to allocate.
+ * Out
+ *	!= 0	A pointer to the virtual address of the allocated memory.
+ *	== 0	Allocation error.
+ *
+ ************************/
+void *mac_drv_get_desc_mem(struct s_smc *smc, unsigned int size)
+{
+
+	char *virt;
+
+	PRINTK(KERN_INFO "mac_drv_get_desc_mem\n");
+
+	// Descriptor memory must be aligned on 16-byte boundary.
+
+	virt = mac_drv_get_space(smc, size);
+
+	size = (u_int) ((0 - (unsigned long) virt) & 15);
+
+	PRINTK("Allocate %u bytes alignment gap ", size);
+	PRINTK("for descriptor memory.\n");
+
+	if (!mac_drv_get_space(smc, size)) {
+		printk("fddi: Unable to align descriptor memory.\n");
+		return (NULL);
+	}
+	return (virt + size);
+}				// mac_drv_get_desc_mem
+
+
+/************************
+ *
+ *	mac_drv_virt2phys
+ *
+ *	Get the physical address of a given virtual address.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	virt - A (virtual) pointer into our 'shared' memory area.
+ * Out
+ *	Physical address of the given virtual address.
+ *
+ ************************/
+unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt)
+{
+	return virt_to_bus(virt);
+}				// mac_drv_virt2phys
+
+
+/************************
+ *
+ *	dma_master
+ *
+ *	The HWM calls this function, when the driver leads through a DMA
+ *	transfer. If the OS-specific module must prepare the system hardware
+ *	for the DMA transfer, it should do it in this function.
+ *
+ *	The hardware module calls this dma_master if it wants to send an SMT
+ *	frame.  This means that the virt address passed in here is part of
+ *      the 'shared' memory area.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	virt - The virtual address of the data.
+ *
+ *	len - The length in bytes of the data.
+ *
+ *	flag - Indicates the transmit direction and the buffer type:
+ *		DMA_RD	(0x01)	system RAM ==> adapter buffer memory
+ *		DMA_WR	(0x02)	adapter buffer memory ==> system RAM
+ *		SMT_BUF (0x80)	SMT buffer
+ *
+ *	>> NOTE: SMT_BUF and DMA_RD are always set for PCI. <<
+ * Out
+ *	Returns the pyhsical address for the DMA transfer.
+ *
+ ************************/
+u_long dma_master(struct s_smc * smc, void *virt, int len, int flag)
+{
+	return (virt_to_bus(virt));
+}				// dma_master
+
+
+/************************
+ *
+ *	dma_complete
+ *
+ *	The hardware module calls this routine when it has completed a DMA
+ *	transfer. If the operating system dependant module has set up the DMA
+ *	channel via dma_master() (e.g. Windows NT or AIX) it should clean up
+ *	the DMA channel.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	descr - A pointer to a TxD or RxD, respectively.
+ *
+ *	flag - Indicates the DMA transfer direction / SMT buffer:
+ *		DMA_RD	(0x01)	system RAM ==> adapter buffer memory
+ *		DMA_WR	(0x02)	adapter buffer memory ==> system RAM
+ *		SMT_BUF (0x80)	SMT buffer (managed by HWM)
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, int flag)
+{
+	return;
+}				// dma_complete
+
+
+/************************
+ *
+ *	mac_drv_tx_complete
+ *
+ *	Transmit of a packet is complete. Release the tx staging buffer.
+ *
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	txd - A pointer to the last TxD which is used by the frame.
+ * Out
+ *	Returns nothing.
+ *
+ ************************/
+void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd)
+{
+	struct sk_buff *skb;
+
+	PRINTK(KERN_INFO "entering mac_drv_tx_complete\n");
+	// Check if this TxD points to a skb
+
+	if (!(skb = txd->txd_os.skb)) {
+		PRINTK("TXD with no skb assigned.\n");
+		return;
+	}
+	txd->txd_os.skb = NULL;
+
+	smc->os.MacStat.tx_packets++;		// Count transmitted packets.
+	smc->os.MacStat.tx_bytes+=skb->len;	// Count bytes
+
+	// free the skb
+	dev_kfree_skb(skb);
+
+	PRINTK(KERN_INFO "leaving mac_drv_tx_complete\n");
+}				// mac_drv_tx_complete
+
+
+/************************
+ *
+ * dump packets to logfile
+ *
+ ************************/
+#ifdef DUMPPACKETS
+void dump_data(unsigned char *Data, int length)
+{
+	int i, j;
+	unsigned char s[255], sh[10];
+	if (length > 64) {
+		length = 64;
+	}
+	printk(KERN_INFO "---Packet start---\n");
+	for (i = 0, j = 0; i < length / 8; i++, j += 8)
+		printk(KERN_INFO "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+		       Data[j + 0], Data[j + 1], Data[j + 2], Data[j + 3],
+		       Data[j + 4], Data[j + 5], Data[j + 6], Data[j + 7]);
+	strcpy(s, "");
+	for (i = 0; i < length % 8; i++) {
+		sprintf(sh, "%02x ", Data[j + i]);
+		strcat(s, sh);
+	}
+	printk(KERN_INFO "%s\n", s);
+	printk(KERN_INFO "------------------\n");
+}				// dump_data
+#else
+#define dump_data(data,len)
+#endif				// DUMPPACKETS
+
+/************************
+ *
+ *	mac_drv_rx_complete
+ *
+ *	The hardware module calls this function if an LLC frame is received
+ *	in a receive buffer. Also the SMT, NSA, and directed beacon frames
+ *	from the network will be passed to the LLC layer by this function
+ *	if passing is enabled.
+ *
+ *	mac_drv_rx_complete forwards the frame to the LLC layer if it should
+ *	be received. It also fills the RxD ring with new receive buffers if
+ *	some can be queued.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	rxd - A pointer to the first RxD which is used by the receive frame.
+ *
+ *	frag_count - Count of RxDs used by the received frame.
+ *
+ *	len - Frame length.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+			 int frag_count, int len)
+{
+	skfddi_priv *bp = (skfddi_priv *) & smc->os;
+	struct sk_buff *skb;
+	unsigned char *virt, *cp;
+	unsigned short ri;
+	u_int RifLength;
+
+	PRINTK(KERN_INFO "entering mac_drv_rx_complete (len=%d)\n", len);
+	if (frag_count != 1) {	// This is not allowed to happen.
+
+		printk("fddi: ERROR: Multi-fragment receive!\n");
+		goto RequeueRxd;	// Re-use the given RXD(s).
+
+	}
+	skb = rxd->rxd_os.skb;
+	if (!skb) {
+		PRINTK(KERN_INFO "No skb in rxd\n");
+		smc->os.MacStat.rx_errors++;
+		goto RequeueRxd;
+	}
+	virt = skb->data;
+
+	dump_data(skb->data, len);
+
+	/*
+	 * FDDI Frame format:
+	 * +-------+-------+-------+------------+--------+------------+
+	 * | FC[1] | DA[6] | SA[6] | RIF[0..18] | LLC[3] | Data[0..n] |
+	 * +-------+-------+-------+------------+--------+------------+
+	 *
+	 * FC = Frame Control
+	 * DA = Destination Address
+	 * SA = Source Address
+	 * RIF = Routing Information Field
+	 * LLC = Logical Link Control
+	 */
+
+	// Remove Routing Information Field (RIF), if present.
+
+	if ((virt[1 + 6] & FDDI_RII) == 0)
+		RifLength = 0;
+	else {
+		int n;
+// goos: RIF removal has still to be tested
+		PRINTK(KERN_INFO "RIF found\n");
+		// Get RIF length from Routing Control (RC) field.
+		cp = virt + FDDI_MAC_HDR_LEN;	// Point behind MAC header.
+
+		ri = ntohs(*((unsigned short *) cp));
+		RifLength = ri & FDDI_RCF_LEN_MASK;
+		if (len < (int) (FDDI_MAC_HDR_LEN + RifLength)) {
+			printk("fddi: Invalid RIF.\n");
+			goto RequeueRxd;	// Discard the frame.
+
+		}
+		virt[1 + 6] &= ~FDDI_RII;	// Clear RII bit.
+		// regions overlap
+
+		virt = cp + RifLength;
+		for (n = FDDI_MAC_HDR_LEN; n; n--)
+			*--virt = *--cp;
+		// adjust sbd->data pointer
+		skb_pull(skb, RifLength);
+		len -= RifLength;
+		RifLength = 0;
+	}
+
+	// Count statistics.
+	smc->os.MacStat.rx_packets++;	// Count indicated receive packets.
+	smc->os.MacStat.rx_bytes+=len;	// Count bytes
+
+	// virt points to header again
+	if (virt[1] & 0x01) {	// Check group (multicast) bit.
+
+		smc->os.MacStat.multicast++;
+	}
+
+	// deliver frame to system
+	rxd->rxd_os.skb = NULL;
+	skb_trim(skb, len);
+	skb->protocol = fddi_type_trans(skb, bp->dev);
+	skb->dev = bp->dev;	/* pass up device pointer */
+
+	netif_rx(skb);
+
+	HWM_RX_CHECK(smc, RX_LOW_WATERMARK);
+	return;
+
+      RequeueRxd:
+	PRINTK(KERN_INFO "Rx: re-queue RXD.\n");
+	mac_drv_requeue_rxd(smc, rxd, frag_count);
+	smc->os.MacStat.rx_errors++;	// Count receive packets not indicated.
+
+}				// mac_drv_rx_complete
+
+
+/************************
+ *
+ *	mac_drv_requeue_rxd
+ *
+ *	The hardware module calls this function to request the OS-specific
+ *	module to queue the receive buffer(s) represented by the pointer
+ *	to the RxD and the frag_count into the receive queue again. This
+ *	buffer was filled with an invalid frame or an SMT frame.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	rxd - A pointer to the first RxD which is used by the receive frame.
+ *
+ *	frag_count - Count of RxDs used by the received frame.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+			 int frag_count)
+{
+	volatile struct s_smt_fp_rxd *next_rxd;
+	volatile struct s_smt_fp_rxd *src_rxd;
+	struct sk_buff *skb;
+	int MaxFrameSize;
+	unsigned char *v_addr;
+	unsigned long b_addr;
+
+	if (frag_count != 1)	// This is not allowed to happen.
+
+		printk("fddi: Multi-fragment requeue!\n");
+
+	MaxFrameSize = ((skfddi_priv *) & smc->os)->MaxFrameSize;
+	src_rxd = rxd;
+	for (; frag_count > 0; frag_count--) {
+		next_rxd = src_rxd->rxd_next;
+		rxd = HWM_GET_CURR_RXD(smc);
+
+		skb = src_rxd->rxd_os.skb;
+		if (skb == NULL) {	// this should not happen
+
+			PRINTK("Requeue with no skb in rxd!\n");
+			skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC);
+			if (skb) {
+				// we got a skb
+				rxd->rxd_os.skb = skb;
+				skb_reserve(skb, 3);
+				skb_put(skb, MaxFrameSize);
+				v_addr = skb->data;
+				b_addr = virt_to_bus(v_addr);
+			} else {
+				// no skb available, use local buffer
+				PRINTK("Queueing invalid buffer!\n");
+				rxd->rxd_os.skb = NULL;
+				v_addr = smc->os.LocalRxBuffer;
+				b_addr = virt_to_bus(v_addr);
+			}
+		} else {
+			// we use skb from old rxd
+			rxd->rxd_os.skb = skb;
+			v_addr = skb->data;
+			b_addr = virt_to_bus(v_addr);
+		}
+		hwm_rx_frag(smc, v_addr, b_addr, MaxFrameSize,
+			    FIRST_FRAG | LAST_FRAG);
+
+		src_rxd = next_rxd;
+	}
+}				// mac_drv_requeue_rxd
+
+
+/************************
+ *
+ *	mac_drv_fill_rxd
+ *
+ *	The hardware module calls this function at initialization time
+ *	to fill the RxD ring with receive buffers. It is also called by
+ *	mac_drv_rx_complete if rx_free is large enough to queue some new
+ *	receive buffers into the RxD ring. mac_drv_fill_rxd queues new
+ *	receive buffers as long as enough RxDs and receive buffers are
+ *	available.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void mac_drv_fill_rxd(struct s_smc *smc)
+{
+	int MaxFrameSize;
+	unsigned char *v_addr;
+	unsigned long b_addr;
+	struct sk_buff *skb;
+	volatile struct s_smt_fp_rxd *rxd;
+
+	PRINTK(KERN_INFO "entering mac_drv_fill_rxd\n");
+
+	// Walk through the list of free receive buffers, passing receive
+	// buffers to the HWM as long as RXDs are available.
+
+	MaxFrameSize = ((skfddi_priv *) & smc->os)->MaxFrameSize;
+	// Check if there is any RXD left.
+	while (HWM_GET_RX_FREE(smc) > 0) {
+		PRINTK(KERN_INFO ".\n");
+
+		rxd = HWM_GET_CURR_RXD(smc);
+		skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC);
+		if (skb) {
+			// we got a skb
+			skb_reserve(skb, 3);
+			skb_put(skb, MaxFrameSize);
+			v_addr = skb->data;
+			b_addr = virt_to_bus(v_addr);
+		} else {
+			// no skb available, use local buffer
+			// System has run out of buffer memory, but we want to
+			// keep the receiver running in hope of better times.
+			// Multiple descriptors may point to this local buffer,
+			// so data in it must be considered invalid.
+			PRINTK("Queueing invalid buffer!\n");
+			v_addr = smc->os.LocalRxBuffer;
+			b_addr = virt_to_bus(v_addr);
+		}
+
+		rxd->rxd_os.skb = skb;
+
+		// Pass receive buffer to HWM.
+		hwm_rx_frag(smc, v_addr, b_addr, MaxFrameSize,
+			    FIRST_FRAG | LAST_FRAG);
+	}
+	PRINTK(KERN_INFO "leaving mac_drv_fill_rxd\n");
+}				// mac_drv_fill_rxd
+
+
+/************************
+ *
+ *	mac_drv_clear_rxd
+ *
+ *	The hardware module calls this function to release unused
+ *	receive buffers.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	rxd - A pointer to the first RxD which is used by the receive buffer.
+ *
+ *	frag_count - Count of RxDs used by the receive buffer.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
+		       int frag_count)
+{
+
+	struct sk_buff *skb;
+
+	PRINTK("entering mac_drv_clear_rxd\n");
+
+	if (frag_count != 1)	// This is not allowed to happen.
+
+		printk("fddi: Multi-fragment clear!\n");
+
+	for (; frag_count > 0; frag_count--) {
+		skb = rxd->rxd_os.skb;
+		if (skb != NULL) {
+			dev_kfree_skb(skb);
+			rxd->rxd_os.skb = NULL;
+		}
+		rxd = rxd->rxd_next;	// Next RXD.
+
+	}
+}				// mac_drv_clear_rxd
+
+
+/************************
+ *
+ *	mac_drv_rx_init
+ *
+ *	The hardware module calls this routine when an SMT or NSA frame of the
+ *	local SMT should be delivered to the LLC layer.
+ *
+ *	It is necessary to have this function, because there is no other way to
+ *	copy the contents of SMT MBufs into receive buffers.
+ *
+ *	mac_drv_rx_init allocates the required target memory for this frame,
+ *	and receives the frame fragment by fragment by calling mac_drv_rx_frag.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	len - The length (in bytes) of the received frame (FC, DA, SA, Data).
+ *
+ *	fc - The Frame Control field of the received frame.
+ *
+ *	look_ahead - A pointer to the lookahead data buffer (may be NULL).
+ *
+ *	la_len - The length of the lookahead data stored in the lookahead
+ *	buffer (may be zero).
+ * Out
+ *	Always returns zero (0).
+ *
+ ************************/
+int mac_drv_rx_init(struct s_smc *smc, int len, int fc,
+		    char *look_ahead, int la_len)
+{
+	struct sk_buff *skb;
+
+	PRINTK("entering mac_drv_rx_init(len=%d)\n", len);
+
+	// "Received" a SMT or NSA frame of the local SMT.
+
+	if (len != la_len || len < FDDI_MAC_HDR_LEN || !look_ahead) {
+		PRINTK("fddi: Discard invalid local SMT frame\n");
+		PRINTK("  len=%d, la_len=%d, (ULONG) look_ahead=%08lXh.\n",
+		       len, la_len, (unsigned long) look_ahead);
+		return (0);
+	}
+	skb = alloc_skb(len + 3, GFP_ATOMIC);
+	if (!skb) {
+		PRINTK("fddi: Local SMT: skb memory exhausted.\n");
+		return (0);
+	}
+	skb_reserve(skb, 3);
+	skb_put(skb, len);
+	memcpy(skb->data, look_ahead, len);
+
+	// deliver frame to system
+	skb->protocol = fddi_type_trans(skb, ((skfddi_priv *) & smc->os)->dev);
+	netif_rx(skb);
+
+	return (0);
+}				// mac_drv_rx_init
+
+
+/************************
+ *
+ *	smt_timer_poll
+ *
+ *	This routine is called periodically by the SMT module to clean up the
+ *	driver.
+ *
+ *	Return any queued frames back to the upper protocol layers if the ring
+ *	is down.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void smt_timer_poll(struct s_smc *smc)
+{
+}				// smt_timer_poll
+
+
+/************************
+ *
+ *	ring_status_indication
+ *
+ *	This function indicates a change of the ring state.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	status - The current ring status.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void ring_status_indication(struct s_smc *smc, u_long status)
+{
+	PRINTK("ring_status_indication( ");
+	if (status & RS_RES15)
+		PRINTK("RS_RES15 ");
+	if (status & RS_HARDERROR)
+		PRINTK("RS_HARDERROR ");
+	if (status & RS_SOFTERROR)
+		PRINTK("RS_SOFTERROR ");
+	if (status & RS_BEACON)
+		PRINTK("RS_BEACON ");
+	if (status & RS_PATHTEST)
+		PRINTK("RS_PATHTEST ");
+	if (status & RS_SELFTEST)
+		PRINTK("RS_SELFTEST ");
+	if (status & RS_RES9)
+		PRINTK("RS_RES9 ");
+	if (status & RS_DISCONNECT)
+		PRINTK("RS_DISCONNECT ");
+	if (status & RS_RES7)
+		PRINTK("RS_RES7 ");
+	if (status & RS_DUPADDR)
+		PRINTK("RS_DUPADDR ");
+	if (status & RS_NORINGOP)
+		PRINTK("RS_NORINGOP ");
+	if (status & RS_VERSION)
+		PRINTK("RS_VERSION ");
+	if (status & RS_STUCKBYPASSS)
+		PRINTK("RS_STUCKBYPASSS ");
+	if (status & RS_EVENT)
+		PRINTK("RS_EVENT ");
+	if (status & RS_RINGOPCHANGE)
+		PRINTK("RS_RINGOPCHANGE ");
+	if (status & RS_RES0)
+		PRINTK("RS_RES0 ");
+	PRINTK("]\n");
+}				// ring_status_indication
+
+
+/************************
+ *
+ *	smt_get_time
+ *
+ *	Gets the current time from the system.
+ * Args
+ *	None.
+ * Out
+ *	The current time in TICKS_PER_SECOND.
+ *
+ *	TICKS_PER_SECOND has the unit 'count of timer ticks per second'. It is
+ *	defined in "targetos.h". The definition of TICKS_PER_SECOND must comply
+ *	to the time returned by smt_get_time().
+ *
+ ************************/
+unsigned long smt_get_time(void)
+{
+	return jiffies;
+}				// smt_get_time
+
+
+/************************
+ *
+ *	smt_stat_counter
+ *
+ *	Status counter update (ring_op, fifo full).
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	stat -	= 0: A ring operational change occurred.
+ *		= 1: The FORMAC FIFO buffer is full / FIFO overflow.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void smt_stat_counter(struct s_smc *smc, int stat)
+{
+//      BOOLEAN RingIsUp ;
+
+	PRINTK(KERN_INFO "smt_stat_counter\n");
+	switch (stat) {
+	case 0:
+		PRINTK(KERN_INFO "Ring operational change.\n");
+		break;
+	case 1:
+		PRINTK(KERN_INFO "Receive fifo overflow.\n");
+		smc->os.MacStat.rx_errors++;
+		break;
+	default:
+		PRINTK(KERN_INFO "Unknown status (%d).\n", stat);
+		break;
+	}
+}				// smt_stat_counter
+
+
+/************************
+ *
+ *	cfm_state_change
+ *
+ *	Sets CFM state in custom statistics.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	c_state - Possible values are:
+ *
+ *		EC0_OUT, EC1_IN, EC2_TRACE, EC3_LEAVE, EC4_PATH_TEST,
+ *		EC5_INSERT, EC6_CHECK, EC7_DEINSERT
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void cfm_state_change(struct s_smc *smc, int c_state)
+{
+#ifdef DRIVERDEBUG
+	char *s;
+
+	switch (c_state) {
+	case SC0_ISOLATED:
+		s = "SC0_ISOLATED";
+		break;
+	case SC1_WRAP_A:
+		s = "SC1_WRAP_A";
+		break;
+	case SC2_WRAP_B:
+		s = "SC2_WRAP_B";
+		break;
+	case SC4_THRU_A:
+		s = "SC4_THRU_A";
+		break;
+	case SC5_THRU_B:
+		s = "SC5_THRU_B";
+		break;
+	case SC7_WRAP_S:
+		s = "SC7_WRAP_S";
+		break;
+	case SC9_C_WRAP_A:
+		s = "SC9_C_WRAP_A";
+		break;
+	case SC10_C_WRAP_B:
+		s = "SC10_C_WRAP_B";
+		break;
+	case SC11_C_WRAP_S:
+		s = "SC11_C_WRAP_S";
+		break;
+	default:
+		PRINTK(KERN_INFO "cfm_state_change: unknown %d\n", c_state);
+		return;
+	}
+	PRINTK(KERN_INFO "cfm_state_change: %s\n", s);
+#endif				// DRIVERDEBUG
+}				// cfm_state_change
+
+
+/************************
+ *
+ *	ecm_state_change
+ *
+ *	Sets ECM state in custom statistics.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	e_state - Possible values are:
+ *
+ *		SC0_ISOLATED, SC1_WRAP_A (5), SC2_WRAP_B (6), SC4_THRU_A (12),
+ *		SC5_THRU_B (7), SC7_WRAP_S (8)
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void ecm_state_change(struct s_smc *smc, int e_state)
+{
+#ifdef DRIVERDEBUG
+	char *s;
+
+	switch (e_state) {
+	case EC0_OUT:
+		s = "EC0_OUT";
+		break;
+	case EC1_IN:
+		s = "EC1_IN";
+		break;
+	case EC2_TRACE:
+		s = "EC2_TRACE";
+		break;
+	case EC3_LEAVE:
+		s = "EC3_LEAVE";
+		break;
+	case EC4_PATH_TEST:
+		s = "EC4_PATH_TEST";
+		break;
+	case EC5_INSERT:
+		s = "EC5_INSERT";
+		break;
+	case EC6_CHECK:
+		s = "EC6_CHECK";
+		break;
+	case EC7_DEINSERT:
+		s = "EC7_DEINSERT";
+		break;
+	default:
+		s = "unknown";
+		break;
+	}
+	PRINTK(KERN_INFO "ecm_state_change: %s\n", s);
+#endif				//DRIVERDEBUG
+}				// ecm_state_change
+
+
+/************************
+ *
+ *	rmt_state_change
+ *
+ *	Sets RMT state in custom statistics.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ *
+ *	r_state - Possible values are:
+ *
+ *		RM0_ISOLATED, RM1_NON_OP, RM2_RING_OP, RM3_DETECT,
+ *		RM4_NON_OP_DUP, RM5_RING_OP_DUP, RM6_DIRECTED, RM7_TRACE
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void rmt_state_change(struct s_smc *smc, int r_state)
+{
+#ifdef DRIVERDEBUG
+	char *s;
+
+	switch (r_state) {
+	case RM0_ISOLATED:
+		s = "RM0_ISOLATED";
+		break;
+	case RM1_NON_OP:
+		s = "RM1_NON_OP - not operational";
+		break;
+	case RM2_RING_OP:
+		s = "RM2_RING_OP - ring operational";
+		break;
+	case RM3_DETECT:
+		s = "RM3_DETECT - detect dupl addresses";
+		break;
+	case RM4_NON_OP_DUP:
+		s = "RM4_NON_OP_DUP - dupl. addr detected";
+		break;
+	case RM5_RING_OP_DUP:
+		s = "RM5_RING_OP_DUP - ring oper. with dupl. addr";
+		break;
+	case RM6_DIRECTED:
+		s = "RM6_DIRECTED - sending directed beacons";
+		break;
+	case RM7_TRACE:
+		s = "RM7_TRACE - trace initiated";
+		break;
+	default:
+		s = "unknown";
+		break;
+	}
+	PRINTK(KERN_INFO "[rmt_state_change: %s]\n", s);
+#endif				// DRIVERDEBUG
+}				// rmt_state_change
+
+
+/************************
+ *
+ *	drv_reset_indication
+ *
+ *	This function is called by the SMT when it has detected a severe
+ *	hardware problem. The driver should perform a reset on the adapter
+ *	as soon as possible, but not from within this function.
+ * Args
+ *	smc - A pointer to the SMT context struct.
+ * Out
+ *	Nothing.
+ *
+ ************************/
+void drv_reset_indication(struct s_smc *smc)
+{
+	PRINTK(KERN_INFO "entering drv_reset_indication\n");
+
+	smc->os.ResetRequested = TRUE;	// Set flag.
+
+}				// drv_reset_indication
+
+
+
+//--------------- functions for use as a module ----------------
+
+#ifdef MODULE
+/************************
+ *
+ * Note now that module autoprobing is allowed under PCI. The
+ * IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
+ * to "do the right thing".
+ *
+ ************************/
+#define LP(a) ((struct s_smc*)(a))
+static struct device *mdev = NULL;
+
+/************************
+ *
+ * init_module
+ *
+ *  If compiled as a module, find
+ *  adapters and initialize them.
+ *
+ ************************/
+int init_module(void)
+{
+	struct device *p;
+
+	PRINTK(KERN_INFO "FDDI init module\n");
+	if ((mdev = insert_device(NULL, skfp_probe)) == NULL)
+		return -ENOMEM;
+
+	for (p = mdev; p != NULL; p = LP(p->priv)->os.next_module) {
+		PRINTK(KERN_INFO "device to register: %s\n", p->name);
+		if (register_netdev(p) != 0) {
+			printk("skfddi init_module failed\n");
+			return -EIO;
+		}
+	}
+
+	PRINTK(KERN_INFO "+++++ exit with success +++++\n");
+	return 0;
+}				// init_module
+
+/************************
+ *
+ * cleanup_module
+ *
+ *  Release all resources claimed by this module.
+ *
+ ************************/
+void cleanup_module(void)
+{
+	PRINTK(KERN_INFO "cleanup_module\n");
+	while (mdev != NULL) {
+		mdev = unlink_modules(mdev);
+	}
+	return;
+}				// cleanup_module
+
+
+/************************
+ *
+ * unlink_modules
+ *
+ *  Unregister devices and release their memory.
+ *
+ ************************/
+static struct device *unlink_modules(struct device *p)
+{
+	struct device *next = NULL;
+
+	if (p->priv) {		/* Private areas allocated? */
+		struct s_smc *lp = (struct s_smc *) p->priv;
+
+		next = lp->os.next_module;
+
+		if (lp->os.SharedMemAddr) {
+			kfree(lp->os.SharedMemAddr);
+		}
+		release_region(p->base_addr, 
+			(lp->os.bus_type == SK_BUS_TYPE_PCI ? FP_IO_LEN : 0));
+	}
+	unregister_netdev(p);
+	printk("%s: unloaded\n", p->name);
+	kfree(p);		/* Free the device structure */
+
+	return next;
+}				// unlink_modules
+
+
+#endif				/* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/smt.c linux.18p17/drivers/net/skfp/smt.c
--- linux.vanilla/drivers/net/skfp/smt.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/smt.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,2225 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smt_p.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)smt.c	2.43 98/11/23 (C) SK " ;
+#endif
+
+extern const u_char canonical[256] ;
+
+/*
+ * FC in SMbuf
+ */
+#define m_fc(mb)	((mb)->sm_data[0])
+
+#define SMT_TID_MAGIC	0x1f0a7b3c
+
+#ifdef	DEBUG
+static const char *const smt_type_name[] = {
+	"SMT_00??", "SMT_INFO", "SMT_02??", "SMT_03??",
+	"SMT_04??", "SMT_05??", "SMT_06??", "SMT_07??",
+	"SMT_08??", "SMT_09??", "SMT_0A??", "SMT_0B??",
+	"SMT_0C??", "SMT_0D??", "SMT_0E??", "SMT_NSA"
+} ;
+
+static const char *const smt_class_name[] = {
+	"UNKNOWN","NIF","SIF_CONFIG","SIF_OPER","ECF","RAF","RDF",
+	"SRF","PMF_GET","PMF_SET","ESF"
+} ;
+#endif
+#define LAST_CLASS	(SMT_PMF_SET)
+
+static const struct fddi_addr SMT_Unknown = {
+	{ 0,0,0x1f,0,0,0 }
+} ;
+
+/*
+ * external variables
+ */
+extern const struct fddi_addr fddi_broadcast ;
+
+/*
+ * external functions
+ */
+int pcm_status_twisted() ;
+void pcm_status_state() ;
+int pcm_status_type() ;
+
+extern SMbuf *smt_get_mbuf() ;
+
+#define EXPORT_PMF
+/*
+ * function prototypes
+ */
+u_long smt_get_tid() ;
+EXPORT_PMF SMbuf *smt_build_frame() ;
+EXPORT_PMF void *sm_to_para() ;
+#ifdef	LITTLE_ENDIAN
+static int smt_swap_short() ;
+#endif
+static int mac_index() ;
+static int phy_index() ;
+static int mac_con_resource_index() ;
+static int phy_con_resource_index() ;
+EXPORT_PMF void smt_send_frame() ;
+EXPORT_PMF void smt_set_timestamp() ;
+static void smt_send_rdf() ;
+static void smt_send_nif() ;
+static void smt_send_ecf() ;
+static void smt_echo_test() ;
+static void smt_send_sif_config() ;
+static void smt_send_sif_operation() ;
+EXPORT_PMF void smt_swap_para() ;
+#ifdef LITTLE_ENDIAN
+static void smt_string_swap() ;
+#endif
+static void smt_add_frame_len() ;
+static void smt_fill_una() ;
+static void smt_fill_sde() ;
+static void smt_fill_state() ;
+static void smt_fill_timestamp() ;
+static void smt_fill_policy() ;
+static void smt_fill_latency() ;
+static void smt_fill_neighbor() ;
+static int  smt_fill_path() ;
+static void smt_fill_mac_status() ;
+static void smt_fill_lem() ;
+static void smt_fill_version() ;
+static void smt_fill_fsc() ;
+static void smt_fill_mac_counter() ;
+static void smt_fill_mac_fnc() ;
+static void smt_fill_manufacturer() ;
+static void smt_fill_user() ;
+static void smt_fill_setcount() ;
+static void smt_fill_echo() ;
+int smt_check_para() ;
+
+void smt_clear_una_dna() ;
+static void smt_clear_old_una_dna() ;
+#ifdef	CONCENTRATOR
+static int entity_to_index() ;
+#endif
+static void update_dac() ;
+static int div_ratio() ;
+#ifdef  USE_CAN_ADDR
+void	hwm_conv_can() ;
+#else
+#define		hwm_conv_can(smc,data,len)
+#endif
+
+/*
+ * list of mandatory paras in frames
+ */
+static const u_short plist_nif[] = { SMT_P_UNA,SMT_P_SDE,SMT_P_STATE,0 } ;
+
+/*
+ * init SMT agent
+ */
+void smt_agent_init(smc)
+struct s_smc *smc ;
+{
+	int		i ;
+
+	/*
+	 * get MAC address
+	 */
+	smc->mib.m[MAC0].fddiMACSMTAddress = smc->hw.fddi_home_addr ;
+
+	/*
+	 * get OUI address from driver (bia == built-in-address)
+	 */
+	smc->mib.fddiSMTStationId.sid_oem[0] = 0 ;
+	smc->mib.fddiSMTStationId.sid_oem[1] = 0 ;
+	driver_get_bia(smc,&smc->mib.fddiSMTStationId.sid_node) ;
+	for (i = 0 ; i < 6 ; i ++) {
+		smc->mib.fddiSMTStationId.sid_node.a[i] =
+			canonical[smc->mib.fddiSMTStationId.sid_node.a[i]] ;
+	}
+	smc->mib.fddiSMTManufacturerData[0] =
+		smc->mib.fddiSMTStationId.sid_node.a[0] ;
+	smc->mib.fddiSMTManufacturerData[1] =
+		smc->mib.fddiSMTStationId.sid_node.a[1] ;
+	smc->mib.fddiSMTManufacturerData[2] =
+		smc->mib.fddiSMTStationId.sid_node.a[2] ;
+	smc->sm.smt_tid = 0 ;
+	smc->mib.m[MAC0].fddiMACDupAddressTest = DA_NONE ;
+	smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ;
+#ifndef	SLIM_SMT
+	smt_clear_una_dna(smc) ;
+	smt_clear_old_una_dna(smc) ;
+#endif
+	for (i = 0 ; i < SMT_MAX_TEST ; i++)
+		smc->sm.pend[i] = 0 ;
+	smc->sm.please_reconnect = 0 ;
+	smc->sm.uniq_ticks = 0 ;
+}
+
+/*
+ * SMT task
+ * forever
+ *	delay 30 seconds
+ *	send NIF
+ *	check tvu & tvd
+ * end
+ */
+void smt_agent_task(smc)
+struct s_smc *smc ;
+{
+	smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L,
+		EV_TOKEN(EVENT_SMT,SM_TIMER)) ;
+	DB_SMT("SMT agent task\n",0,0) ;
+}
+
+void smt_please_reconnect(smc,reconn_time)
+struct s_smc	*smc ;		/* Pointer to SMT context */
+int		reconn_time ;	/* Wait for reconnect time in seconds */
+{
+	/*
+	 * The please reconnect variable is used as a timer.
+	 * It is decremented each time smt_event is called.
+	 * This happens every second or when smt_force_irq is called.
+	 * Note: smt_force_irq () is called on some packet receives and
+	 *       when a multicast address is changed. Since nothing
+	 *       is received during the disconnect and the multicast
+	 *       address changes can be viewed as not very often and
+	 *       the timer runs out close to its given value
+	 *       (reconn_time).
+	 */
+	smc->sm.please_reconnect = reconn_time ;
+}
+
+#ifndef SMT_REAL_TOKEN_CT
+void smt_emulate_token_ct(smc, mac_index)
+struct s_smc	*smc;
+int		mac_index;
+{
+	u_long	count;
+	u_long	time;
+
+
+	time = smt_get_time();
+	count =	((time - smc->sm.last_tok_time[mac_index]) *
+					100)/TICKS_PER_SECOND;
+
+	/*
+	 * Only when ring is up we will have a token count. The
+	 * flag is unfortunatly a single instance value. This
+	 * doesn't matter now, because we currently have only
+	 * one MAC instance.
+	 */
+	if (smc->hw.mac_ring_is_up){
+		smc->mib.m[mac_index].fddiMACToken_Ct += count;
+	}
+
+	/* Remember current time */
+	smc->sm.last_tok_time[mac_index] = time;
+
+}
+#endif
+
+/*ARGSUSED1*/
+void smt_event(smc,event)
+struct s_smc *smc ;
+int event ;
+{
+	u_long		time ;
+#ifndef SMT_REAL_TOKEN_CT
+	int		i ;
+#endif
+
+
+	if (smc->sm.please_reconnect) {
+		smc->sm.please_reconnect -- ;
+		if (smc->sm.please_reconnect == 0) {
+			/* Counted down */
+			queue_event(smc,EVENT_ECM,EC_CONNECT) ;
+		}
+	}
+
+	if (event == SM_FAST)
+		return ;
+
+	/*
+	 * timer for periodic cleanup in driver
+	 * reset and start the watchdog (FM2)
+	 * ESS timer
+	 * SBA timer
+	 */
+	smt_timer_poll(smc) ;
+	smt_start_watchdog(smc) ;
+#ifndef	SLIM_SMT
+#ifndef BOOT
+#ifdef	ESS
+	ess_timer_poll(smc) ;
+#endif
+#endif
+#ifdef	SBA
+	sba_timer_poll(smc) ;
+#endif
+
+	smt_srf_event(smc,0,0,0) ;
+
+#endif	/* no SLIM_SMT */
+
+	time = smt_get_time() ;
+
+	if (time - smc->sm.smt_last_lem >= TICKS_PER_SECOND*8) {
+		/*
+		 * Use 8 sec. for the time intervall, it simplifies the
+		 * LER estimation.
+		 */
+		struct fddi_mib_m	*mib ;
+		u_long			upper ;
+		u_long			lower ;
+		int			cond ;
+		int			port;
+		struct s_phy		*phy ;
+		/*
+		 * calculate LEM bit error rate
+		 */
+		sm_lem_evaluate(smc) ;
+		smc->sm.smt_last_lem = time ;
+
+		/*
+		 * check conditions
+		 */
+#ifndef	SLIM_SMT
+		mac_update_counter(smc) ;
+		mib = smc->mib.m ;
+		upper =
+		(mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) +
+		(mib->fddiMACError_Ct - mib->fddiMACOld_Error_Ct) ;
+		lower =
+		(mib->fddiMACFrame_Ct - mib->fddiMACOld_Frame_Ct) +
+		(mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) ;
+		mib->fddiMACFrameErrorRatio = div_ratio(upper,lower) ;
+
+		cond =
+			((!mib->fddiMACFrameErrorThreshold &&
+			mib->fddiMACError_Ct != mib->fddiMACOld_Error_Ct) ||
+			(mib->fddiMACFrameErrorRatio >
+			mib->fddiMACFrameErrorThreshold)) ;
+
+		if (cond != mib->fddiMACFrameErrorFlag)
+			smt_srf_event(smc,SMT_COND_MAC_FRAME_ERROR,
+				INDEX_MAC,cond) ;
+
+		upper =
+		(mib->fddiMACNotCopied_Ct - mib->fddiMACOld_NotCopied_Ct) ;
+		lower =
+		upper +
+		(mib->fddiMACCopied_Ct - mib->fddiMACOld_Copied_Ct) ;
+		mib->fddiMACNotCopiedRatio = div_ratio(upper,lower) ;
+
+		cond =
+			((!mib->fddiMACNotCopiedThreshold &&
+			mib->fddiMACNotCopied_Ct !=
+				mib->fddiMACOld_NotCopied_Ct)||
+			(mib->fddiMACNotCopiedRatio >
+			mib->fddiMACNotCopiedThreshold)) ;
+
+		if (cond != mib->fddiMACNotCopiedFlag)
+			smt_srf_event(smc,SMT_COND_MAC_NOT_COPIED,
+				INDEX_MAC,cond) ;
+
+		/*
+		 * set old values
+		 */
+		mib->fddiMACOld_Frame_Ct = mib->fddiMACFrame_Ct ;
+		mib->fddiMACOld_Copied_Ct = mib->fddiMACCopied_Ct ;
+		mib->fddiMACOld_Error_Ct = mib->fddiMACError_Ct ;
+		mib->fddiMACOld_Lost_Ct = mib->fddiMACLost_Ct ;
+		mib->fddiMACOld_NotCopied_Ct = mib->fddiMACNotCopied_Ct ;
+
+		/*
+		 * Check port EBError Condition
+		 */
+		for (port = 0; port < NUMPHYS; port ++) {
+			phy = &smc->y[port] ;
+
+			if (!phy->mib->fddiPORTHardwarePresent) {
+				continue;
+			}
+
+			cond = (phy->mib->fddiPORTEBError_Ct -
+				phy->mib->fddiPORTOldEBError_Ct > 5) ;
+
+			/* If ratio is more than 5 in 8 seconds
+			 * Set the condition.
+			 */
+			smt_srf_event(smc,SMT_COND_PORT_EB_ERROR,
+				(int) (INDEX_PORT+ phy->np) ,cond) ;
+
+			/*
+			 * set old values
+			 */
+			phy->mib->fddiPORTOldEBError_Ct =
+				phy->mib->fddiPORTEBError_Ct ;
+		}
+
+#endif	/* no SLIM_SMT */
+	}
+
+#ifndef	SLIM_SMT
+
+	if (time - smc->sm.smt_last_notify >= (u_long)
+		(smc->mib.fddiSMTTT_Notify * TICKS_PER_SECOND) ) {
+		/*
+		 * we can either send an announcement or a request
+		 * a request will trigger a reply so that we can update
+		 * our dna
+		 * note: same tid must be used until reply is received
+		 */
+		if (!smc->sm.pend[SMT_TID_NIF])
+			smc->sm.pend[SMT_TID_NIF] = smt_get_tid(smc) ;
+		smt_send_nif(smc,&fddi_broadcast,FC_SMT_NSA,
+			smc->sm.pend[SMT_TID_NIF], SMT_REQUEST,0) ;
+		smc->sm.smt_last_notify = time ;
+	}
+
+	/*
+	 * check timer
+	 */
+	if (smc->sm.smt_tvu &&
+	    time - smc->sm.smt_tvu > 228*TICKS_PER_SECOND) {
+		DB_SMT("SMT : UNA expired\n",0,0) ;
+		smc->sm.smt_tvu = 0 ;
+
+		if (!is_equal(&smc->mib.m[MAC0].fddiMACUpstreamNbr,
+			&SMT_Unknown)){
+			/* Do not update unknown address */
+			smc->mib.m[MAC0].fddiMACOldUpstreamNbr=
+				smc->mib.m[MAC0].fddiMACUpstreamNbr ;
+		}
+		smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ;
+		smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ;
+		/*
+		 * Make sure the fddiMACUNDA_Flag = FALSE is
+		 * included in the SRF so we don't generate
+		 * a seperate SRF for the deassertion of this
+		 * condition
+		 */
+		update_dac(smc,0) ;
+		smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE,
+			INDEX_MAC,0) ;
+	}
+	if (smc->sm.smt_tvd &&
+	    time - smc->sm.smt_tvd > 228*TICKS_PER_SECOND) {
+		DB_SMT("SMT : DNA expired\n",0,0) ;
+		smc->sm.smt_tvd = 0 ;
+		if (!is_equal(&smc->mib.m[MAC0].fddiMACDownstreamNbr,
+			&SMT_Unknown)){
+			/* Do not update unknown address */
+			smc->mib.m[MAC0].fddiMACOldDownstreamNbr=
+				smc->mib.m[MAC0].fddiMACDownstreamNbr ;
+		}
+		smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ;
+		smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE,
+			INDEX_MAC,0) ;
+	}
+
+#endif	/* no SLIM_SMT */
+
+#ifndef SMT_REAL_TOKEN_CT
+	/*
+	 * Token counter emulation section. If hardware supports the token
+	 * count, the token counter will be updated in mac_update_counter.
+	 */
+	for (i = MAC0; i < NUMMACS; i++ ){
+		if (time - smc->sm.last_tok_time[i] > 2*TICKS_PER_SECOND ){
+			smt_emulate_token_ct( smc, i );
+		}
+	}
+#endif
+
+	smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L,
+		EV_TOKEN(EVENT_SMT,SM_TIMER)) ;
+}
+
+static int div_ratio(upper,lower)
+u_long upper ;
+u_long lower ;
+{
+	if ((upper<<16L) < upper)
+		upper = 0xffff0000L ;
+	else
+		upper <<= 16L ;
+	if (!lower)
+		return(0) ;
+	return((int)(upper/lower)) ;
+}
+
+#ifndef	SLIM_SMT
+
+/*
+ * receive packet handler
+ */
+void smt_received_pack(smc,mb,fs)
+struct s_smc *smc ;
+SMbuf *mb ;
+int fs ;			/* frame status */
+{
+	struct smt_header	*sm ;
+	int			local ;
+
+	int			illegal = 0 ;
+
+	switch (m_fc(mb)) {
+	case FC_SMT_INFO :
+	case FC_SMT_LAN_LOC :
+	case FC_SMT_LOC :
+	case FC_SMT_NSA :
+		break ;
+	default :
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+
+	smc->mib.m[MAC0].fddiMACSMTCopied_Ct++ ;
+	sm = smtod(mb,struct smt_header *) ;
+	local = ((fs & L_INDICATOR) != 0) ;
+	hwm_conv_can(smc,(char *)sm,12) ;
+
+	/* check destination address */
+	if (is_individual(&sm->smt_dest) && !is_my_addr(smc,&sm->smt_dest)) {
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+#if	0		/* for DUP recognition, do NOT filter them */
+	/* ignore loop back packets */
+	if (is_my_addr(smc,&sm->smt_source) && !local) {
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+#endif
+
+	smt_swap_para(sm,(int) mb->sm_len,1) ;
+	DB_SMT("SMT : received packet [%s] at 0x%x\n",
+		smt_type_name[m_fc(mb) & 0xf],sm) ;
+	DB_SMT("SMT : version %d, class %s\n",sm->smt_version,
+		smt_class_name[(sm->smt_class>LAST_CLASS)?0 : sm->smt_class]) ;
+
+#ifdef	SBA
+	/*
+	 * check if NSA frame
+	 */
+	if (m_fc(mb) == FC_SMT_NSA && sm->smt_class == SMT_NIF &&
+		(sm->smt_type == SMT_ANNOUNCE || sm->smt_type == SMT_REQUEST)) {
+			smc->sba.sm = sm ;
+			sba(smc,NIF) ;
+	}
+#endif
+
+	/*
+	 * ignore any packet with NSA and A-indicator set
+	 */
+	if ( (fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) {
+		DB_SMT("SMT : ignoring NSA with A-indicator set from %s\n",
+			addr_to_string(&sm->smt_source),0) ;
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+
+	/*
+	 * ignore frames with illegal length
+	 */
+	if (((sm->smt_class == SMT_ECF) && (sm->smt_len > SMT_MAX_ECHO_LEN)) ||
+	    ((sm->smt_class != SMT_ECF) && (sm->smt_len > SMT_MAX_INFO_LEN))) {
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+
+	/*
+	 * check SMT version
+	 */
+	switch (sm->smt_class) {
+	case SMT_NIF :
+	case SMT_SIF_CONFIG :
+	case SMT_SIF_OPER :
+	case SMT_ECF :
+		if (sm->smt_version != SMT_VID)
+			illegal = 1;
+		break ;
+	default :
+		if (sm->smt_version != SMT_VID_2)
+			illegal = 1;
+		break ;
+	}
+	if (illegal) {
+		DB_SMT("SMT : version = %d, dest = %s\n",
+			sm->smt_version,addr_to_string(&sm->smt_source)) ;
+		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_VERSION,local) ;
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+	if ((sm->smt_len > mb->sm_len - sizeof(struct smt_header)) ||
+	    ((sm->smt_len & 3) && (sm->smt_class != SMT_ECF))) {
+		DB_SMT("SMT: info length error, len = %d\n",sm->smt_len,0) ;
+		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH,local) ;
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+	switch (sm->smt_class) {
+	case SMT_NIF :
+		if (smt_check_para(smc,sm,plist_nif)) {
+			DB_SMT("SMT: NIF with para problem, ignoring\n",0,0) ;
+			break ;
+		} ;
+		switch (sm->smt_type) {
+		case SMT_ANNOUNCE :
+		case SMT_REQUEST :
+			if (!(fs & C_INDICATOR) && m_fc(mb) == FC_SMT_NSA
+				&& is_broadcast(&sm->smt_dest)) {
+				struct smt_p_state	*st ;
+
+				/* set my UNA */
+				if (!is_equal(
+					&smc->mib.m[MAC0].fddiMACUpstreamNbr,
+					&sm->smt_source)) {
+					DB_SMT("SMT : updated my UNA = %s\n",
+					addr_to_string(&sm->smt_source),0) ;
+					if (!is_equal(&smc->mib.m[MAC0].
+					    fddiMACUpstreamNbr,&SMT_Unknown)){
+					 /* Do not update unknown address */
+					 smc->mib.m[MAC0].fddiMACOldUpstreamNbr=
+					 smc->mib.m[MAC0].fddiMACUpstreamNbr ;
+					}
+
+					smc->mib.m[MAC0].fddiMACUpstreamNbr =
+						sm->smt_source ;
+					smt_srf_event(smc,
+						SMT_EVENT_MAC_NEIGHBOR_CHANGE,
+						INDEX_MAC,0) ;
+					smt_echo_test(smc,0) ;
+				}
+				smc->sm.smt_tvu = smt_get_time() ;
+				st = (struct smt_p_state *)
+					sm_to_para(smc,sm,SMT_P_STATE) ;
+				if (st) {
+					smc->mib.m[MAC0].fddiMACUNDA_Flag =
+					(st->st_dupl_addr & SMT_ST_MY_DUPA) ?
+					TRUE : FALSE ;
+					update_dac(smc,1) ;
+				}
+			}
+			if ((sm->smt_type == SMT_REQUEST) &&
+			    is_individual(&sm->smt_source) &&
+			    ((!(fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) ||
+			     (m_fc(mb) != FC_SMT_NSA))) {
+				DB_SMT("SMT : replying to NIF request %s\n",
+					addr_to_string(&sm->smt_source),0) ;
+				smt_send_nif(smc,&sm->smt_source,
+					FC_SMT_INFO,
+					sm->smt_tid,
+					SMT_REPLY,local) ;
+			}
+			break ;
+		case SMT_REPLY :
+			DB_SMT("SMT : received NIF response from %s\n",
+				addr_to_string(&sm->smt_source),0) ;
+			if (fs & A_INDICATOR) {
+				smc->sm.pend[SMT_TID_NIF] = 0 ;
+				DB_SMT("SMT : duplicate address\n",0,0) ;
+				smc->mib.m[MAC0].fddiMACDupAddressTest =
+					DA_FAILED ;
+				smc->r.dup_addr_test = DA_FAILED ;
+				queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ;
+				smc->mib.m[MAC0].fddiMACDA_Flag = TRUE ;
+				update_dac(smc,1) ;
+				break ;
+			}
+			if (sm->smt_tid == smc->sm.pend[SMT_TID_NIF]) {
+				smc->sm.pend[SMT_TID_NIF] = 0 ;
+				/* set my DNA */
+				if (!is_equal(
+					&smc->mib.m[MAC0].fddiMACDownstreamNbr,
+					&sm->smt_source)) {
+					DB_SMT("SMT : updated my DNA\n",0,0) ;
+					if (!is_equal(&smc->mib.m[MAC0].
+					 fddiMACDownstreamNbr, &SMT_Unknown)){
+					 /* Do not update unknown address */
+				smc->mib.m[MAC0].fddiMACOldDownstreamNbr =
+					 smc->mib.m[MAC0].fddiMACDownstreamNbr ;
+					}
+
+					smc->mib.m[MAC0].fddiMACDownstreamNbr =
+						sm->smt_source ;
+					smt_srf_event(smc,
+						SMT_EVENT_MAC_NEIGHBOR_CHANGE,
+						INDEX_MAC,0) ;
+					smt_echo_test(smc,1) ;
+				}
+				smc->mib.m[MAC0].fddiMACDA_Flag = FALSE ;
+				update_dac(smc,1) ;
+				smc->sm.smt_tvd = smt_get_time() ;
+				smc->mib.m[MAC0].fddiMACDupAddressTest =
+					DA_PASSED ;
+				if (smc->r.dup_addr_test != DA_PASSED) {
+					smc->r.dup_addr_test = DA_PASSED ;
+					queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ;
+				}
+			}
+			else if (sm->smt_tid ==
+				smc->sm.pend[SMT_TID_NIF_TEST]) {
+				DB_SMT("SMT : NIF test TID ok\n",0,0) ;
+			}
+			else {
+				DB_SMT("SMT : expected TID %lx, got %lx\n",
+				smc->sm.pend[SMT_TID_NIF],sm->smt_tid) ;
+			}
+			break ;
+		default :
+			illegal = 2 ;
+			break ;
+		}
+		break ;
+	case SMT_SIF_CONFIG :	/* station information */
+		if (sm->smt_type != SMT_REQUEST)
+			break ;
+		DB_SMT("SMT : replying to SIF Config request from %s\n",
+			addr_to_string(&sm->smt_source),0) ;
+		smt_send_sif_config(smc,&sm->smt_source,sm->smt_tid,local) ;
+		break ;
+	case SMT_SIF_OPER :	/* station information */
+		if (sm->smt_type != SMT_REQUEST)
+			break ;
+		DB_SMT("SMT : replying to SIF Operation request from %s\n",
+			addr_to_string(&sm->smt_source),0) ;
+		smt_send_sif_operation(smc,&sm->smt_source,sm->smt_tid,local) ;
+		break ;
+	case SMT_ECF :		/* echo frame */
+		switch (sm->smt_type) {
+		case SMT_REPLY :
+			smc->mib.priv.fddiPRIVECF_Reply_Rx++ ;
+			DB_SMT("SMT: received ECF reply from %s\n",
+				addr_to_string(&sm->smt_source),0) ;
+			if (sm_to_para(smc,sm,SMT_P_ECHODATA) == 0) {
+				DB_SMT("SMT: ECHODATA missing\n",0,0) ;
+				break ;
+			}
+			if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF]) {
+				DB_SMT("SMT : ECF test TID ok\n",0,0) ;
+			}
+			else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_UNA]) {
+				DB_SMT("SMT : ECF test UNA ok\n",0,0) ;
+			}
+			else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_DNA]) {
+				DB_SMT("SMT : ECF test DNA ok\n",0,0) ;
+			}
+			else {
+				DB_SMT("SMT : expected TID %lx, got %lx\n",
+					smc->sm.pend[SMT_TID_ECF],
+					sm->smt_tid) ;
+			}
+			break ;
+		case SMT_REQUEST :
+			smc->mib.priv.fddiPRIVECF_Req_Rx++ ;
+			{
+			if (sm->smt_len && !sm_to_para(smc,sm,SMT_P_ECHODATA)) {
+			DB_SMT("SMT: ECF with para problem,sending RDF\n",0,0) ;
+				smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH,
+					local) ;
+				break ;
+			}
+			DB_SMT("SMT - sending ECF reply to %s\n",
+				addr_to_string(&sm->smt_source),0) ;
+
+			/* set destination addr.  & reply */
+			sm->smt_dest = sm->smt_source ;
+			sm->smt_type = SMT_REPLY ;
+			dump_smt(smc,sm,"ECF REPLY") ;
+			smc->mib.priv.fddiPRIVECF_Reply_Tx++ ;
+			smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
+			return ;		/* DON'T free mbuf */
+			}
+		default :
+			illegal = 1 ;
+			break ;
+		}
+		break ;
+#ifndef	BOOT
+	case SMT_RAF :		/* resource allocation */
+#ifdef	ESS
+		DB_ESSN(2,"ESS: RAF frame received\n",0,0) ;
+		fs = ess_raf_received_pack(smc,mb,sm,fs) ;
+#endif
+
+#ifdef	SBA
+		DB_SBAN(2,"SBA: RAF frame received\n",0,0) ;
+		sba_raf_received_pack(smc,sm,fs) ;
+#endif
+		break ;
+	case SMT_RDF :		/* request denied */
+		smc->mib.priv.fddiPRIVRDF_Rx++ ;
+		break ;
+	case SMT_ESF :		/* extended service - not supported */
+		if (sm->smt_type == SMT_REQUEST) {
+			DB_SMT("SMT - received ESF, sending RDF\n",0,0) ;
+			smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ;
+		}
+		break ;
+	case SMT_PMF_GET :
+	case SMT_PMF_SET :
+		if (sm->smt_type != SMT_REQUEST)
+			break ;
+		/* update statistics */
+		if (sm->smt_class == SMT_PMF_GET)
+			smc->mib.priv.fddiPRIVPMF_Get_Rx++ ;
+		else
+			smc->mib.priv.fddiPRIVPMF_Set_Rx++ ;
+		/*
+		 * ignore PMF SET with I/G set
+		 */
+		if ((sm->smt_class == SMT_PMF_SET) &&
+			!is_individual(&sm->smt_dest)) {
+			DB_SMT("SMT: ignoring PMF-SET with I/G set\n",0,0) ;
+			break ;
+		}
+		smt_pmf_received_pack(smc,mb, local) ;
+		break ;
+	case SMT_SRF :
+		dump_smt(smc,sm,"SRF received") ;
+		break ;
+	default :
+		if (sm->smt_type != SMT_REQUEST)
+			break ;
+		/*
+		 * For frames with unknown class:
+		 * we need to send a RDF frame according to 8.1.3.1.1,
+		 * only if it is a REQUEST.
+		 */
+		DB_SMT("SMT : class = %d, send RDF to %s\n",
+			sm->smt_class, addr_to_string(&sm->smt_source)) ;
+
+		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ;
+		break ;
+#endif
+	}
+	if (illegal) {
+		DB_SMT("SMT: discarding illegal frame, reason = %d\n",
+			illegal,0) ;
+	}
+	smt_free_mbuf(smc,mb) ;
+}
+
+static void update_dac(smc,report)
+struct s_smc *smc ;
+int report ;
+{
+	int	cond ;
+
+	cond = ( smc->mib.m[MAC0].fddiMACUNDA_Flag |
+		smc->mib.m[MAC0].fddiMACDA_Flag) != 0 ;
+	if (report && (cond != smc->mib.m[MAC0].fddiMACDuplicateAddressCond))
+		smt_srf_event(smc, SMT_COND_MAC_DUP_ADDR,INDEX_MAC,cond) ;
+	else
+		smc->mib.m[MAC0].fddiMACDuplicateAddressCond = cond ;
+}
+
+/*
+ * send SMT frame
+ *	set source address
+ *	set station ID
+ *	send frame
+ */
+EXPORT_PMF void smt_send_frame(smc,mb,fc,local)
+struct s_smc *smc ;
+SMbuf *mb ;			/* buffer to send */
+int fc ;			/* FC value */
+int local ;
+{
+	struct smt_header	*sm ;
+
+	if (!smc->r.sm_ma_avail && !local) {
+		smt_free_mbuf(smc,mb) ;
+		return ;
+	}
+	sm = smtod(mb,struct smt_header *) ;
+	sm->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ;
+	sm->smt_sid = smc->mib.fddiSMTStationId ;
+
+	smt_swap_para(sm,(int) mb->sm_len,0) ;		/* swap para & header */
+	hwm_conv_can(smc,(char *)sm,12) ;		/* convert SA and DA */
+	smc->mib.m[MAC0].fddiMACSMTTransmit_Ct++ ;
+	smt_send_mbuf(smc,mb,local ? FC_SMT_LOC : fc) ;
+}
+
+/*
+ * generate and send RDF
+ */
+static void smt_send_rdf(smc,rej,fc,reason,local)
+struct s_smc *smc ;
+SMbuf	*rej ;			/* mbuf of offending frame */
+int	fc ;			/* FC of denied frame */
+int reason ;			/* reason code */
+int local ;
+{
+	SMbuf	*mb ;
+	struct smt_header	*sm ;	/* header of offending frame */
+	struct smt_rdf	*rdf ;
+	int		len ;
+	int		frame_len ;
+
+	sm = smtod(rej,struct smt_header *) ;
+	if (sm->smt_type != SMT_REQUEST)
+		return ;
+
+	DB_SMT("SMT: sending RDF to %s,reason = 0x%x\n",
+		addr_to_string(&sm->smt_source),reason) ;
+
+
+	/*
+	 * note: get framelength from MAC length, NOT from SMT header
+	 * smt header length is included in sm_len
+	 */
+	frame_len = rej->sm_len ;
+
+	if (!(mb=smt_build_frame(smc,SMT_RDF,SMT_REPLY,sizeof(struct smt_rdf))))
+		return ;
+	rdf = smtod(mb,struct smt_rdf *) ;
+	rdf->smt.smt_tid = sm->smt_tid ;		/* use TID from sm */
+	rdf->smt.smt_dest = sm->smt_source ;		/* set dest = source */
+
+	/* set P12 */
+	rdf->reason.para.p_type = SMT_P_REASON ;
+	rdf->reason.para.p_len = sizeof(struct smt_p_reason) - PARA_LEN ;
+	rdf->reason.rdf_reason = reason ;
+
+	/* set P14 */
+	rdf->version.para.p_type = SMT_P_VERSION ;
+	rdf->version.para.p_len = sizeof(struct smt_p_version) - PARA_LEN ;
+	rdf->version.v_pad = 0 ;
+	rdf->version.v_n = 1 ;
+	rdf->version.v_index = 1 ;
+	rdf->version.v_version[0] = SMT_VID_2 ;
+	rdf->version.v_pad2 = 0 ;
+
+	/* set P13 */
+	if ((unsigned) frame_len <= SMT_MAX_INFO_LEN - sizeof(*rdf) +
+		2*sizeof(struct smt_header))
+		len = frame_len ;
+	else
+		len = SMT_MAX_INFO_LEN - sizeof(*rdf) +
+			2*sizeof(struct smt_header) ;
+	/* make length multiple of 4 */
+	len &= ~3 ;
+	rdf->refused.para.p_type = SMT_P_REFUSED ;
+	/* length of para is smt_frame + ref_fc */
+	rdf->refused.para.p_len = len + 4 ;
+	rdf->refused.ref_fc = fc ;
+
+	/* swap it back */
+	smt_swap_para(sm,frame_len,0) ;
+
+	memcpy((char *) &rdf->refused.ref_header,(char *) sm,len) ;
+
+	len -= sizeof(struct smt_header) ;
+	mb->sm_len += len ;
+	rdf->smt.smt_len += len ;
+
+	dump_smt(smc,(struct smt_header *)rdf,"RDF") ;
+	smc->mib.priv.fddiPRIVRDF_Tx++ ;
+	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
+}
+
+/*
+ * generate and send NIF
+ */
+static void smt_send_nif(smc,dest,fc,tid,type,local)
+struct s_smc *smc ;
+struct fddi_addr *dest ;		/* dest address */
+int fc ;				/* frame control */
+u_long tid ;				/* transaction id */
+int type ;				/* frame type */
+int local ;
+{
+	struct smt_nif	*nif ;
+	SMbuf		*mb ;
+
+	if (!(mb = smt_build_frame(smc,SMT_NIF,type,sizeof(struct smt_nif))))
+		return ;
+	nif = smtod(mb, struct smt_nif *) ;
+	smt_fill_una(smc,&nif->una) ;	/* set UNA */
+	smt_fill_sde(smc,&nif->sde) ;	/* set station descriptor */
+	smt_fill_state(smc,&nif->state) ;	/* set state information */
+#ifdef	SMT6_10
+	smt_fill_fsc(smc,&nif->fsc) ;	/* set frame status cap. */
+#endif
+	nif->smt.smt_dest = *dest ;	/* destination address */
+	nif->smt.smt_tid = tid ;	/* transaction ID */
+	dump_smt(smc,(struct smt_header *)nif,"NIF") ;
+	smt_send_frame(smc,mb,fc,local) ;
+}
+
+#ifdef	DEBUG
+/*
+ * send NIF request (test purpose)
+ */
+static void smt_send_nif_request(smc,dest)
+struct s_smc *smc ;
+struct fddi_addr *dest ;
+{
+	smc->sm.pend[SMT_TID_NIF_TEST] = smt_get_tid(smc) ;
+	smt_send_nif(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_NIF_TEST],
+		SMT_REQUEST,0) ;
+}
+
+/*
+ * send ECF request (test purpose)
+ */
+static void smt_send_ecf_request(smc,dest,len)
+struct s_smc *smc ;
+struct fddi_addr *dest ;
+int len ;
+{
+	smc->sm.pend[SMT_TID_ECF] = smt_get_tid(smc) ;
+	smt_send_ecf(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_ECF],
+		SMT_REQUEST,len) ;
+}
+#endif
+
+/*
+ * echo test
+ */
+static void smt_echo_test(smc,dna)
+struct s_smc *smc ;
+int dna ;
+{
+	u_long	tid ;
+
+	smc->sm.pend[dna ? SMT_TID_ECF_DNA : SMT_TID_ECF_UNA] =
+		tid = smt_get_tid(smc) ;
+	smt_send_ecf(smc, dna ?
+		&smc->mib.m[MAC0].fddiMACDownstreamNbr :
+		&smc->mib.m[MAC0].fddiMACUpstreamNbr,
+		FC_SMT_INFO,tid, SMT_REQUEST, (SMT_TEST_ECHO_LEN & ~3)-8) ;
+}
+
+/*
+ * generate and send ECF
+ */
+static void smt_send_ecf(smc,dest,fc,tid,type,len)
+struct s_smc *smc ;
+struct fddi_addr *dest ;		/* dest address */
+int fc ;				/* frame control */
+u_long tid ;				/* transaction id */
+int type ;				/* frame type */
+int len ;				/* frame length */
+{
+	struct smt_ecf	*ecf ;
+	SMbuf		*mb ;
+
+	if (!(mb = smt_build_frame(smc,SMT_ECF,type,SMT_ECF_LEN + len)))
+		return ;
+	ecf = smtod(mb, struct smt_ecf *) ;
+
+	smt_fill_echo(smc,&ecf->ec_echo,tid,len) ;	/* set ECHO */
+	ecf->smt.smt_dest = *dest ;	/* destination address */
+	ecf->smt.smt_tid = tid ;	/* transaction ID */
+	smc->mib.priv.fddiPRIVECF_Req_Tx++ ;
+	smt_send_frame(smc,mb,fc,0) ;
+}
+
+/*
+ * generate and send SIF config response
+ */
+
+static void smt_send_sif_config(smc,dest,tid,local)
+struct s_smc *smc ;
+struct fddi_addr *dest ;		/* dest address */
+u_long tid ;				/* transaction id */
+int local ;
+{
+	struct smt_sif_config	*sif ;
+	SMbuf			*mb ;
+	int			len ;
+	if (!(mb = smt_build_frame(smc,SMT_SIF_CONFIG,SMT_REPLY,
+		SIZEOF_SMT_SIF_CONFIG)))
+		return ;
+
+	sif = smtod(mb, struct smt_sif_config *) ;
+	smt_fill_timestamp(smc,&sif->ts) ;	/* set time stamp */
+	smt_fill_sde(smc,&sif->sde) ;		/* set station descriptor */
+	smt_fill_version(smc,&sif->version) ;	/* set version information */
+	smt_fill_state(smc,&sif->state) ;	/* set state information */
+	smt_fill_policy(smc,&sif->policy) ;	/* set station policy */
+	smt_fill_latency(smc,&sif->latency);	/* set station latency */
+	smt_fill_neighbor(smc,&sif->neighbor);	/* set station neighbor */
+	smt_fill_setcount(smc,&sif->setcount) ;	/* set count */
+	len = smt_fill_path(smc,&sif->path);	/* set station path descriptor*/
+	sif->smt.smt_dest = *dest ;		/* destination address */
+	sif->smt.smt_tid = tid ;		/* transaction ID */
+	smt_add_frame_len(mb,len) ;		/* adjust length fields */
+	dump_smt(smc,(struct smt_header *)sif,"SIF Configuration Reply") ;
+	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
+}
+
+/*
+ * generate and send SIF operation response
+ */
+
+static void smt_send_sif_operation(smc,dest,tid,local)
+struct s_smc *smc ;
+struct fddi_addr *dest ;		/* dest address */
+u_long tid ;				/* transaction id */
+int local ;
+{
+	struct smt_sif_operation *sif ;
+	SMbuf			*mb ;
+	int			ports ;
+	int			i ;
+
+	ports = NUMPHYS ;
+#ifndef	CONCENTRATOR
+	if (smc->s.sas == SMT_SAS)
+		ports = 1 ;
+#endif
+
+	if (!(mb = smt_build_frame(smc,SMT_SIF_OPER,SMT_REPLY,
+		SIZEOF_SMT_SIF_OPERATION+ports*sizeof(struct smt_p_lem))))
+		return ;
+	sif = smtod(mb, struct smt_sif_operation *) ;
+	smt_fill_timestamp(smc,&sif->ts) ;	/* set time stamp */
+	smt_fill_mac_status(smc,&sif->status) ; /* set mac status */
+	smt_fill_mac_counter(smc,&sif->mc) ; /* set mac counter field */
+	smt_fill_mac_fnc(smc,&sif->fnc) ; /* set frame not copied counter */
+	smt_fill_manufacturer(smc,&sif->man) ; /* set manufacturer field */
+	smt_fill_user(smc,&sif->user) ;		/* set user field */
+	smt_fill_setcount(smc,&sif->setcount) ;	/* set count */
+	/*
+	 * set link error mon information
+	 */
+	if (ports == 1) {
+		smt_fill_lem(smc,sif->lem,PS) ;
+	}
+	else {
+		for (i = 0 ; i < ports ; i++) {
+			smt_fill_lem(smc,&sif->lem[i],i) ;
+		}
+	}
+
+	sif->smt.smt_dest = *dest ;	/* destination address */
+	sif->smt.smt_tid = tid ;	/* transaction ID */
+	dump_smt(smc,(struct smt_header *)sif,"SIF Operation Reply") ;
+	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
+}
+
+/*
+ * get and initialize SMT frame
+ */
+EXPORT_PMF SMbuf *smt_build_frame(smc,class,type,length)
+struct s_smc *smc ;
+int class ;
+int type ;
+int length ;
+{
+	SMbuf			*mb ;
+	struct smt_header	*smt ;
+
+#if	0
+	if (!smc->r.sm_ma_avail) {
+		return(0) ;
+	}
+#endif
+	if (!(mb = smt_get_mbuf(smc)))
+		return(mb) ;
+
+	mb->sm_len = length ;
+	smt = smtod(mb, struct smt_header *) ;
+	smt->smt_dest = fddi_broadcast ; /* set dest = broadcast */
+	smt->smt_class = class ;
+	smt->smt_type = type ;
+	switch (class) {
+	case SMT_NIF :
+	case SMT_SIF_CONFIG :
+	case SMT_SIF_OPER :
+	case SMT_ECF :
+		smt->smt_version = SMT_VID ;
+		break ;
+	default :
+		smt->smt_version = SMT_VID_2 ;
+		break ;
+	}
+	smt->smt_tid = smt_get_tid(smc) ;	/* set transaction ID */
+	smt->smt_pad = 0 ;
+	smt->smt_len = length - sizeof(struct smt_header) ;
+	return(mb) ;
+}
+
+static void smt_add_frame_len(mb,len)
+SMbuf *mb ;
+int len ;
+{
+	struct smt_header	*smt ;
+
+	smt = smtod(mb, struct smt_header *) ;
+	smt->smt_len += len ;
+	mb->sm_len += len ;
+}
+
+
+
+/*
+ * fill values in UNA parameter
+ */
+static void smt_fill_una(smc,una)
+struct s_smc *smc ;
+struct smt_p_una *una ;
+{
+	SMTSETPARA(una,SMT_P_UNA) ;
+	una->una_pad = 0 ;
+	una->una_node = smc->mib.m[MAC0].fddiMACUpstreamNbr ;
+}
+
+/*
+ * fill values in SDE parameter
+ */
+static void smt_fill_sde(smc,sde)
+struct s_smc *smc ;
+struct smt_p_sde *sde ;
+{
+	SMTSETPARA(sde,SMT_P_SDE) ;
+	sde->sde_non_master = smc->mib.fddiSMTNonMaster_Ct ;
+	sde->sde_master = smc->mib.fddiSMTMaster_Ct ;
+	sde->sde_mac_count = NUMMACS ;		/* only 1 MAC */
+#ifdef	CONCENTRATOR
+	sde->sde_type = SMT_SDE_CONCENTRATOR ;
+#else
+	sde->sde_type = SMT_SDE_STATION ;
+#endif
+}
+
+/*
+ * fill in values in station state parameter
+ */
+static void smt_fill_state(smc,state)
+struct s_smc *smc ;
+struct smt_p_state *state ;
+{
+	int	top ;
+	int	twist ;
+
+	SMTSETPARA(state,SMT_P_STATE) ;
+	state->st_pad = 0 ;
+
+	/* determine topology */
+	top = 0 ;
+	if (smc->mib.fddiSMTPeerWrapFlag) {
+		top |= SMT_ST_WRAPPED ;		/* state wrapped */
+	}
+#ifdef	CONCENTRATOR
+	if (cfm_status_unattached(smc)) {
+		top |= SMT_ST_UNATTACHED ;	/* unattached concentrator */
+	}
+#endif
+	if ((twist = pcm_status_twisted(smc)) & 1) {
+		top |= SMT_ST_TWISTED_A ;	/* twisted cable */
+	}
+	if (twist & 2) {
+		top |= SMT_ST_TWISTED_B ;	/* twisted cable */
+	}
+#ifdef	OPT_SRF
+	top |= SMT_ST_SRF ;
+#endif
+	if (pcm_rooted_station(smc))
+		top |= SMT_ST_ROOTED_S ;
+	if (smc->mib.a[0].fddiPATHSbaPayload != 0)
+		top |= SMT_ST_SYNC_SERVICE ;
+	state->st_topology = top ;
+	state->st_dupl_addr =
+		((smc->mib.m[MAC0].fddiMACDA_Flag ? SMT_ST_MY_DUPA : 0 ) |
+		 (smc->mib.m[MAC0].fddiMACUNDA_Flag ? SMT_ST_UNA_DUPA : 0)) ;
+}
+
+/*
+ * fill values in timestamp parameter
+ */
+static void smt_fill_timestamp(smc,ts)
+struct s_smc *smc ;
+struct smt_p_timestamp *ts ;
+{
+
+	SMTSETPARA(ts,SMT_P_TIMESTAMP) ;
+	smt_set_timestamp(smc,ts->ts_time) ;
+}
+
+EXPORT_PMF void smt_set_timestamp(smc,p)
+struct s_smc *smc ;
+u_char *p ;
+{
+	u_long	time ;
+	u_long	utime ;
+
+	/*
+	 * timestamp is 64 bits long ; resolution is 80 nS
+	 * our clock resolution is 10mS
+	 * 10mS/80ns = 125000 ~ 2^17 = 131072
+	 */
+	utime = smt_get_time() ;
+	time = utime * 100 ;
+	time /= TICKS_PER_SECOND ;
+	p[0] = 0 ;
+	p[1] = (u_char)((time>>(8+8+8+8-1)) & 1) ;
+	p[2] = (u_char)(time>>(8+8+8-1)) ;
+	p[3] = (u_char)(time>>(8+8-1)) ;
+	p[4] = (u_char)(time>>(8-1)) ;
+	p[5] = (u_char)(time<<1) ;
+	p[6] = (u_char)(smc->sm.uniq_ticks>>8) ;
+	p[7] = (u_char)smc->sm.uniq_ticks ;
+	/*
+	 * make sure we don't wrap: restart whenever the upper digits change
+	 */
+	if (utime != smc->sm.uniq_time) {
+		smc->sm.uniq_ticks = 0 ;
+	}
+	smc->sm.uniq_ticks++ ;
+	smc->sm.uniq_time = utime ;
+}
+
+/*
+ * fill values in station policy parameter
+ */
+static void smt_fill_policy(smc,policy)
+struct s_smc *smc ;
+struct smt_p_policy *policy ;
+{
+	int	i ;
+	u_char	*map ;
+	u_short	in ;
+	u_short	out ;
+
+	/*
+	 * MIB para 101b (fddiSMTConnectionPolicy) coding
+	 * is different from 0005 coding
+	 */
+	static u_char	ansi_weirdness[16] = {
+		0,7,5,3,8,1,6,4,9,10,2,11,12,13,14,15
+	} ;
+	SMTSETPARA(policy,SMT_P_POLICY) ;
+
+	out = 0 ;
+	in = smc->mib.fddiSMTConnectionPolicy ;
+	for (i = 0, map = ansi_weirdness ; i < 16 ; i++) {
+		if (in & 1)
+			out |= (1<<*map) ;
+		in >>= 1 ;
+		map++ ;
+	}
+	policy->pl_config = smc->mib.fddiSMTConfigPolicy ;
+	policy->pl_connect = out ;
+}
+
+/*
+ * fill values in latency equivalent parameter
+ */
+static void smt_fill_latency(smc,latency)
+struct s_smc *smc ;
+struct smt_p_latency *latency ;
+{
+	SMTSETPARA(latency,SMT_P_LATENCY) ;
+
+	latency->lt_phyout_idx1 = phy_index(smc,0) ;
+	latency->lt_latency1 = 10 ;	/* in octets (byte clock) */
+	/*
+	 * note: latency has two phy entries by definition
+	 * for a SAS, the 2nd one is null
+	 */
+	if (smc->s.sas == SMT_DAS) {
+		latency->lt_phyout_idx2 = phy_index(smc,1) ;
+		latency->lt_latency2 = 10 ;	/* in octets (byte clock) */
+	}
+	else {
+		latency->lt_phyout_idx2 = 0 ;
+		latency->lt_latency2 = 0 ;
+	}
+}
+
+/*
+ * fill values in MAC neighbors parameter
+ */
+static void smt_fill_neighbor(smc,neighbor)
+struct s_smc *smc ;
+struct smt_p_neighbor *neighbor ;
+{
+	SMTSETPARA(neighbor,SMT_P_NEIGHBORS) ;
+
+	neighbor->nb_mib_index = INDEX_MAC ;
+	neighbor->nb_mac_index = mac_index(smc,1) ;
+	neighbor->nb_una = smc->mib.m[MAC0].fddiMACUpstreamNbr ;
+	neighbor->nb_dna = smc->mib.m[MAC0].fddiMACDownstreamNbr ;
+}
+
+/*
+ * fill values in path descriptor
+ */
+#ifdef	CONCENTRATOR
+#define ALLPHYS	NUMPHYS
+#else
+#define ALLPHYS	((smc->s.sas == SMT_SAS) ? 1 : 2)
+#endif
+
+static int smt_fill_path(smc,path)
+struct s_smc *smc ;
+struct smt_p_path *path ;
+{
+	SK_LOC_DECL(int,type) ;
+	SK_LOC_DECL(int,state) ;
+	SK_LOC_DECL(int,remote) ;
+	SK_LOC_DECL(int,mac) ;
+	int	len ;
+	int	p ;
+	int	physp ;
+	struct smt_phy_rec	*phy ;
+	struct smt_mac_rec	*pd_mac ;
+
+	len =	PARA_LEN +
+		sizeof(struct smt_mac_rec) * NUMMACS +
+		sizeof(struct smt_phy_rec) * ALLPHYS ;
+	path->para.p_type = SMT_P_PATH ;
+	path->para.p_len = len - PARA_LEN ;
+
+	/* PHYs */
+	for (p = 0,phy = path->pd_phy ; p < ALLPHYS ; p++, phy++) {
+		physp = p ;
+#ifndef	CONCENTRATOR
+		if (smc->s.sas == SMT_SAS)
+			physp = PS ;
+#endif
+		pcm_status_state(smc,physp,&type,&state,&remote,&mac) ;
+#ifdef	LITTLE_ENDIAN
+		phy->phy_mib_index = smt_swap_short((u_short)p+INDEX_PORT) ;
+#else
+		phy->phy_mib_index = p+INDEX_PORT ;
+#endif
+		phy->phy_type = type ;
+		phy->phy_connect_state = state ;
+		phy->phy_remote_type = remote ;
+		phy->phy_remote_mac = mac ;
+		phy->phy_resource_idx = phy_con_resource_index(smc,p) ;
+	}
+
+	/* MAC */
+	pd_mac = (struct smt_mac_rec *) phy ;
+	pd_mac->mac_addr = smc->mib.m[MAC0].fddiMACSMTAddress ;
+	pd_mac->mac_resource_idx = mac_con_resource_index(smc,1) ;
+	return(len) ;
+}
+
+/*
+ * fill values in mac status
+ */
+static void smt_fill_mac_status(smc,st)
+struct s_smc *smc ;
+struct smt_p_mac_status *st ;
+{
+	SMTSETPARA(st,SMT_P_MAC_STATUS) ;
+
+	st->st_mib_index = INDEX_MAC ;
+	st->st_mac_index = mac_index(smc,1) ;
+
+	mac_update_counter(smc) ;
+	/*
+	 * timer values are represented in SMT as 2's complement numbers
+	 * units :	internal :  2's complement BCLK
+	 */
+	st->st_t_req = smc->mib.m[MAC0].fddiMACT_Req ;
+	st->st_t_neg = smc->mib.m[MAC0].fddiMACT_Neg ;
+	st->st_t_max = smc->mib.m[MAC0].fddiMACT_Max ;
+	st->st_tvx_value = smc->mib.m[MAC0].fddiMACTvxValue ;
+	st->st_t_min = smc->mib.m[MAC0].fddiMACT_Min ;
+
+	st->st_sba = smc->mib.a[PATH0].fddiPATHSbaPayload ;
+	st->st_frame_ct = smc->mib.m[MAC0].fddiMACFrame_Ct ;
+	st->st_error_ct = smc->mib.m[MAC0].fddiMACError_Ct ;
+	st->st_lost_ct = smc->mib.m[MAC0].fddiMACLost_Ct ;
+}
+
+/*
+ * fill values in LEM status
+ */
+
+static void smt_fill_lem(smc,lem,phy)
+struct s_smc *smc ;
+struct smt_p_lem *lem ;
+int phy ;
+{
+	struct fddi_mib_p	*mib ;
+
+	mib = smc->y[phy].mib ;
+
+	SMTSETPARA(lem,SMT_P_LEM) ;
+	lem->lem_mib_index = phy+INDEX_PORT ;
+	lem->lem_phy_index = phy_index(smc,phy) ;
+	lem->lem_pad2 = 0 ;
+	lem->lem_cutoff = mib->fddiPORTLer_Cutoff ;
+	lem->lem_alarm = mib->fddiPORTLer_Alarm ;
+	/* long term bit error rate */
+	lem->lem_estimate = mib->fddiPORTLer_Estimate ;
+	/* # of rejected connections */
+	lem->lem_reject_ct = mib->fddiPORTLem_Reject_Ct ;
+	lem->lem_ct = mib->fddiPORTLem_Ct ;	/* total number of errors */
+}
+
+/*
+ * fill version parameter
+ */
+static void smt_fill_version(smc,vers)
+struct s_smc *smc ;
+struct smt_p_version *vers ;
+{
+	SK_UNUSED(smc) ;
+	SMTSETPARA(vers,SMT_P_VERSION) ;
+	vers->v_pad = 0 ;
+	vers->v_n = 1 ;				/* one version is enough .. */
+	vers->v_index = 1 ;
+	vers->v_version[0] = SMT_VID_2 ;
+	vers->v_pad2 = 0 ;
+}
+
+#ifdef	SMT6_10
+/*
+ * fill frame status capabilities
+ */
+/*
+ * note: this para 200B is NOT in swap table, because it's also set in
+ * PMF add_para
+ */
+static void smt_fill_fsc(smc,fsc)
+struct s_smc *smc ;
+struct smt_p_fsc *fsc ;
+{
+	SK_UNUSED(smc) ;
+	SMTSETPARA(fsc,SMT_P_FSC) ;
+	fsc->fsc_pad0 = 0 ;
+	fsc->fsc_mac_index = INDEX_MAC ;	/* this is MIB ; MIB is NOT
+						 * mac_index ()i !
+						 */
+	fsc->fsc_pad1 = 0 ;
+	fsc->fsc_value = FSC_TYPE0 ;		/* "normal" node */
+#ifdef	LITTLE_ENDIAN
+	fsc->fsc_mac_index = smt_swap_short(INDEX_MAC) ;
+	fsc->fsc_value = smt_swap_short(FSC_TYPE0) ;
+#endif
+}
+#endif
+
+/*
+ * fill mac counter field
+ */
+static void smt_fill_mac_counter(smc,mc)
+struct s_smc *smc ;
+struct smt_p_mac_counter *mc ;
+{
+	SMTSETPARA(mc,SMT_P_MAC_COUNTER) ;
+	mc->mc_mib_index = INDEX_MAC ;
+	mc->mc_index = mac_index(smc,1) ;
+	mc->mc_receive_ct = smc->mib.m[MAC0].fddiMACCopied_Ct ;
+	mc->mc_transmit_ct =  smc->mib.m[MAC0].fddiMACTransmit_Ct ;
+}
+
+/*
+ * fill mac frame not copied counter
+ */
+static void smt_fill_mac_fnc(smc,fnc)
+struct s_smc *smc ;
+struct smt_p_mac_fnc *fnc ;
+{
+	SMTSETPARA(fnc,SMT_P_MAC_FNC) ;
+	fnc->nc_mib_index = INDEX_MAC ;
+	fnc->nc_index = mac_index(smc,1) ;
+	fnc->nc_counter = smc->mib.m[MAC0].fddiMACNotCopied_Ct ;
+}
+
+
+/*
+ * fill manufacturer field
+ */
+static void smt_fill_manufacturer(smc,man)
+struct s_smc *smc ;
+struct smp_p_manufacturer *man ;
+{
+	SMTSETPARA(man,SMT_P_MANUFACTURER) ;
+	memcpy((char *) man->mf_data,
+		(char *) smc->mib.fddiSMTManufacturerData,
+		sizeof(man->mf_data)) ;
+}
+
+/*
+ * fill user field
+ */
+static void smt_fill_user(smc,user)
+struct s_smc *smc ;
+struct smp_p_user *user ;
+{
+	SMTSETPARA(user,SMT_P_USER) ;
+	memcpy((char *) user->us_data,
+		(char *) smc->mib.fddiSMTUserData,
+		sizeof(user->us_data)) ;
+}
+
+
+
+/*
+ * fill set count
+ */
+static void smt_fill_setcount(smc,setcount)
+struct s_smc *smc ;
+struct smt_p_setcount *setcount ;
+{
+	SK_UNUSED(smc) ;
+	SMTSETPARA(setcount,SMT_P_SETCOUNT) ;
+	setcount->count = smc->mib.fddiSMTSetCount.count ;
+	memcpy((char *)setcount->timestamp,
+		(char *)smc->mib.fddiSMTSetCount.timestamp,8) ;
+}
+
+/*
+ * fill echo data
+ */
+static void smt_fill_echo(smc,echo,seed,len)
+struct s_smc *smc ;
+struct smt_p_echo *echo ;
+u_long seed ;
+int len ;
+{
+
+	u_char	*p ;
+
+	SK_UNUSED(smc) ;
+	SMTSETPARA(echo,SMT_P_ECHODATA) ;
+	echo->para.p_len = len ;
+	for (p = echo->ec_data ; len ; len--) {
+		*p++ = (u_char) seed ;
+		seed += 13 ;
+	}
+}
+
+/*
+ * clear DNA and UNA
+ * called from CFM if configuration changes
+ */
+void smt_clear_una_dna(smc)
+struct s_smc *smc ;
+{
+	smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ;
+	smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ;
+}
+
+static void smt_clear_old_una_dna(smc)
+struct s_smc *smc ;
+{
+	smc->mib.m[MAC0].fddiMACOldUpstreamNbr = SMT_Unknown ;
+	smc->mib.m[MAC0].fddiMACOldDownstreamNbr = SMT_Unknown ;
+}
+
+u_long smt_get_tid(smc)
+struct s_smc *smc ;
+{
+	u_long	tid ;
+	while ((tid = ++(smc->sm.smt_tid) ^ SMT_TID_MAGIC) == 0)
+		;
+	return(tid & 0x3fffffffL) ;
+}
+
+
+/*
+ * table of parameter lengths
+ */
+static const struct smt_pdef {
+	int	ptype ;
+	int	plen ;
+	const char	*pswap ;
+} smt_pdef[] = {
+	{ SMT_P_UNA,	sizeof(struct smt_p_una) ,
+		SWAP_SMT_P_UNA					} ,
+	{ SMT_P_SDE,	sizeof(struct smt_p_sde) ,
+		SWAP_SMT_P_SDE					} ,
+	{ SMT_P_STATE,	sizeof(struct smt_p_state) ,
+		SWAP_SMT_P_STATE				} ,
+	{ SMT_P_TIMESTAMP,sizeof(struct smt_p_timestamp) ,
+		SWAP_SMT_P_TIMESTAMP				} ,
+	{ SMT_P_POLICY,	sizeof(struct smt_p_policy) ,
+		SWAP_SMT_P_POLICY				} ,
+	{ SMT_P_LATENCY,	sizeof(struct smt_p_latency) ,
+		SWAP_SMT_P_LATENCY				} ,
+	{ SMT_P_NEIGHBORS,sizeof(struct smt_p_neighbor) ,
+		SWAP_SMT_P_NEIGHBORS				} ,
+	{ SMT_P_PATH,	sizeof(struct smt_p_path) ,
+		SWAP_SMT_P_PATH					} ,
+	{ SMT_P_MAC_STATUS,sizeof(struct smt_p_mac_status) ,
+		SWAP_SMT_P_MAC_STATUS				} ,
+	{ SMT_P_LEM,	sizeof(struct smt_p_lem) ,
+		SWAP_SMT_P_LEM					} ,
+	{ SMT_P_MAC_COUNTER,sizeof(struct smt_p_mac_counter) ,
+		SWAP_SMT_P_MAC_COUNTER				} ,
+	{ SMT_P_MAC_FNC,sizeof(struct smt_p_mac_fnc) ,
+		SWAP_SMT_P_MAC_FNC				} ,
+	{ SMT_P_PRIORITY,sizeof(struct smt_p_priority) ,
+		SWAP_SMT_P_PRIORITY				} ,
+	{ SMT_P_EB,sizeof(struct smt_p_eb) ,
+		SWAP_SMT_P_EB					} ,
+	{ SMT_P_MANUFACTURER,sizeof(struct smp_p_manufacturer) ,
+		SWAP_SMT_P_MANUFACTURER				} ,
+	{ SMT_P_REASON,	sizeof(struct smt_p_reason) ,
+		SWAP_SMT_P_REASON				} ,
+	{ SMT_P_REFUSED, sizeof(struct smt_p_refused) ,
+		SWAP_SMT_P_REFUSED				} ,
+	{ SMT_P_VERSION, sizeof(struct smt_p_version) ,
+		SWAP_SMT_P_VERSION				} ,
+#ifdef ESS
+	{ SMT_P0015, sizeof(struct smt_p_0015) , SWAP_SMT_P0015 } ,
+	{ SMT_P0016, sizeof(struct smt_p_0016) , SWAP_SMT_P0016 } ,
+	{ SMT_P0017, sizeof(struct smt_p_0017) , SWAP_SMT_P0017 } ,
+	{ SMT_P0018, sizeof(struct smt_p_0018) , SWAP_SMT_P0018 } ,
+	{ SMT_P0019, sizeof(struct smt_p_0019) , SWAP_SMT_P0019 } ,
+	{ SMT_P001A, sizeof(struct smt_p_001a) , SWAP_SMT_P001A } ,
+	{ SMT_P001B, sizeof(struct smt_p_001b) , SWAP_SMT_P001B } ,
+	{ SMT_P001C, sizeof(struct smt_p_001c) , SWAP_SMT_P001C } ,
+	{ SMT_P001D, sizeof(struct smt_p_001d) , SWAP_SMT_P001D } ,
+#endif
+#if	0
+	{ SMT_P_FSC,	sizeof(struct smt_p_fsc) ,
+		SWAP_SMT_P_FSC					} ,
+#endif
+
+	{ SMT_P_SETCOUNT,0,	SWAP_SMT_P_SETCOUNT		} ,
+	{ SMT_P1048,	0,	SWAP_SMT_P1048			} ,
+	{ SMT_P208C,	0,	SWAP_SMT_P208C			} ,
+	{ SMT_P208D,	0,	SWAP_SMT_P208D			} ,
+	{ SMT_P208E,	0,	SWAP_SMT_P208E			} ,
+	{ SMT_P208F,	0,	SWAP_SMT_P208F			} ,
+	{ SMT_P2090,	0,	SWAP_SMT_P2090			} ,
+#ifdef	ESS
+	{ SMT_P320B, sizeof(struct smt_p_320b) , SWAP_SMT_P320B } ,
+	{ SMT_P320F, sizeof(struct smt_p_320f) , SWAP_SMT_P320F } ,
+	{ SMT_P3210, sizeof(struct smt_p_3210) , SWAP_SMT_P3210 } ,
+#endif
+	{ SMT_P4050,	0,	SWAP_SMT_P4050			} ,
+	{ SMT_P4051,	0,	SWAP_SMT_P4051			} ,
+	{ SMT_P4052,	0,	SWAP_SMT_P4052			} ,
+	{ SMT_P4053,	0,	SWAP_SMT_P4053			} ,
+} ;
+
+#define N_SMT_PLEN	(sizeof(smt_pdef)/sizeof(smt_pdef[0]))
+
+int smt_check_para(smc,sm,list)
+struct s_smc *smc ;
+struct smt_header	*sm ;
+const u_short		list[] ;
+{
+	const u_short		*p = list ;
+	while (*p) {
+		if (!sm_to_para(smc,sm,(int) *p)) {
+			DB_SMT("SMT: smt_check_para - missing para %x\n",*p,0);
+			return(-1) ;
+		}
+		p++ ;
+	}
+	return(0) ;
+}
+
+EXPORT_PMF void *sm_to_para(smc,sm,para)
+struct s_smc *smc ;
+struct smt_header	*sm ;
+int para ;
+{
+	char	*p ;
+	int	len ;
+	int	plen ;
+	void	*found = 0 ;
+
+	SK_UNUSED(smc) ;
+
+	len = sm->smt_len ;
+	p = (char *)(sm+1) ;		/* pointer to info */
+	while (len > 0 ) {
+		if (((struct smt_para *)p)->p_type == para)
+			found = (void *) p ;
+		plen = ((struct smt_para *)p)->p_len + PARA_LEN ;
+		p += plen ;
+		len -= plen ;
+		if (len < 0) {
+			DB_SMT("SMT : sm_to_para - length error %d\n",plen,0) ;
+			return(0) ;
+		}
+		if ((plen & 3) && (para != SMT_P_ECHODATA)) {
+			DB_SMT("SMT : sm_to_para - odd length %d\n",plen,0) ;
+			return(0) ;
+		}
+		if (found)
+			return(found) ;
+	}
+	return(0) ;
+}
+
+int is_my_addr(smc,addr)
+struct s_smc *smc ;
+struct fddi_addr *addr ;
+{
+	return(*(short *)(&addr->a[0]) ==
+		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[0])
+	  && *(short *)(&addr->a[2]) ==
+		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[2])
+	  && *(short *)(&addr->a[4]) ==
+		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[4])) ;
+}
+
+int is_zero(addr)
+struct fddi_addr *addr ;
+{
+	return(*(short *)(&addr->a[0]) == 0 &&
+	       *(short *)(&addr->a[2]) == 0 &&
+	       *(short *)(&addr->a[4]) == 0 ) ;
+}
+
+int is_broadcast(addr)
+struct fddi_addr *addr ;
+{
+	return(*(u_short *)(&addr->a[0]) == 0xffff &&
+	       *(u_short *)(&addr->a[2]) == 0xffff &&
+	       *(u_short *)(&addr->a[4]) == 0xffff ) ;
+}
+
+int is_individual(addr)
+struct fddi_addr *addr ;
+{
+	return(!(addr->a[0] & GROUP_ADDR)) ;
+}
+
+int is_equal(addr1,addr2)
+struct fddi_addr *addr1 ;
+struct fddi_addr *addr2 ;
+{
+	return(*(u_short *)(&addr1->a[0]) == *(u_short *)(&addr2->a[0]) &&
+	       *(u_short *)(&addr1->a[2]) == *(u_short *)(&addr2->a[2]) &&
+	       *(u_short *)(&addr1->a[4]) == *(u_short *)(&addr2->a[4]) ) ;
+}
+
+
+#if	0
+/*
+ * send ANTC data test frame
+ */
+void fddi_send_antc(smc,dest)
+struct s_smc *smc ;
+struct fddi_addr *dest ;
+{
+	SK_UNUSED(smc) ;
+	SK_UNUSED(dest) ;
+#if	0
+	SMbuf			*mb ;
+	struct smt_header	*smt ;
+	int			i ;
+	char			*p ;
+
+	mb = smt_get_mbuf() ;
+	mb->sm_len = 3000+12 ;
+	p = smtod(mb, char *) + 12 ;
+	for (i = 0 ; i < 3000 ; i++)
+		*p++ = 1 << (i&7) ;
+
+	smt = smtod(mb, struct smt_header *) ;
+	smt->smt_dest = *dest ;
+	smt->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ;
+	smt_send_mbuf(smc,mb,FC_ASYNC_LLC) ;
+#endif
+}
+#endif
+
+#ifdef	DEBUG
+#define hextoasc(x)	"0123456789abcdef"[x]
+
+char *addr_to_string(addr)
+struct fddi_addr *addr ;
+{
+	int	i ;
+	static char	string[6*3] = "****" ;
+
+	for (i = 0 ; i < 6 ; i++) {
+		string[i*3] = hextoasc((addr->a[i]>>4)&0xf) ;
+		string[i*3+1] = hextoasc((addr->a[i])&0xf) ;
+		string[i*3+2] = ':' ;
+	}
+	string[5*3+2] = 0 ;
+	return(string) ;
+}
+#endif
+
+#ifdef	AM29K
+smt_ifconfig(argc,argv)
+int argc ;
+char *argv[] ;
+{
+	if (argc >= 2 && !strcmp(argv[0],"opt_bypass") &&
+	    !strcmp(argv[1],"yes")) {
+		smc->mib.fddiSMTBypassPresent = 1 ;
+		return(0) ;
+	}
+	return(amdfddi_config(0,argc,argv)) ;
+}
+#endif
+
+/*
+ * return static mac index
+ */
+static int mac_index(smc,mac)
+struct s_smc *smc ;
+int mac ;
+{
+	SK_UNUSED(mac) ;
+#ifdef	CONCENTRATOR
+	SK_UNUSED(smc) ;
+	return(NUMPHYS+1) ;
+#else
+	return((smc->s.sas == SMT_SAS) ? 2 : 3) ;
+#endif
+}
+
+/*
+ * return static phy index
+ */
+static int phy_index(smc,phy)
+struct s_smc *smc ;
+int phy ;
+{
+	SK_UNUSED(smc) ;
+	return(phy+1);
+}
+
+/*
+ * return dynamic mac connection resource index
+ */
+static int mac_con_resource_index(smc,mac)
+struct s_smc *smc ;
+int mac ;
+{
+#ifdef	CONCENTRATOR
+	SK_UNUSED(smc) ;
+	SK_UNUSED(mac) ;
+	return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_MAC))) ;
+#else
+	SK_UNUSED(mac) ;
+	switch (smc->mib.fddiSMTCF_State) {
+	case SC9_C_WRAP_A :
+	case SC5_THRU_B :
+	case SC11_C_WRAP_S :
+		return(1) ;
+	case SC10_C_WRAP_B :
+	case SC4_THRU_A :
+		return(2) ;
+	}
+	return(smc->s.sas == SMT_SAS ? 2 : 3) ;
+#endif
+}
+
+/*
+ * return dynamic phy connection resource index
+ */
+static int phy_con_resource_index(smc,phy)
+struct s_smc *smc ;
+int phy ;
+{
+#ifdef	CONCENTRATOR
+	return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_PHY(phy)))) ;
+#else
+	switch (smc->mib.fddiSMTCF_State) {
+	case SC9_C_WRAP_A :
+		return(phy == PA ? 3 : 2) ;
+	case SC10_C_WRAP_B :
+		return(phy == PA ? 1 : 3) ;
+	case SC4_THRU_A :
+		return(phy == PA ? 3 : 1) ;
+	case SC5_THRU_B :
+		return(phy == PA ? 2 : 3) ;
+	case SC11_C_WRAP_S :
+		return(2) ;
+	}
+	return(phy) ;
+#endif
+}
+
+#ifdef	CONCENTRATOR
+static int entity_to_index(smc,e)
+struct s_smc *smc ;
+int e ;
+{
+	if (e == ENTITY_MAC)
+		return(mac_index(smc,1)) ;
+	else
+		return(phy_index(smc,e - ENTITY_PHY(0))) ;
+}
+#endif
+
+#ifdef	LITTLE_ENDIAN
+static int smt_swap_short(s)
+u_short s ;
+{
+	return(((s>>8)&0xff)|((s&0xff)<<8)) ;
+}
+
+void smt_swap_para(sm,len,direction)
+struct smt_header *sm ;
+int len ;
+int direction ;			/* 0 encode 1 decode */
+{
+	struct smt_para	*pa ;
+	const  struct smt_pdef	*pd ;
+	char	*p ;
+	int	plen ;
+	int	type ;
+	int	i ;
+
+/*	printf("smt_swap_para sm %x len %d dir %d\n",
+		sm,len,direction) ;
+ */
+	smt_string_swap((char *)sm,SWAP_SMTHEADER,len) ;
+
+	/* swap args */
+	len -= sizeof(struct smt_header) ;
+
+	p = (char *) (sm + 1) ;
+	while (len > 0) {
+		pa = (struct smt_para *) p ;
+		plen = pa->p_len ;
+		type = pa->p_type ;
+		pa->p_type = smt_swap_short(pa->p_type) ;
+		pa->p_len = smt_swap_short(pa->p_len) ;
+		if (direction) {
+			plen = pa->p_len ;
+			type = pa->p_type ;
+		}
+		/*
+		 * note: paras can have 0 length !
+		 */
+		if (plen < 0)
+			break ;
+		plen += PARA_LEN ;
+		for (i = N_SMT_PLEN, pd = smt_pdef; i ; i--,pd++) {
+			if (pd->ptype == type)
+				break ;
+		}
+		if (i && pd->pswap) {
+			smt_string_swap(p+PARA_LEN,pd->pswap,len) ;
+		}
+		len -= plen ;
+		p += plen ;
+	}
+}
+
+static void smt_string_swap(data,format,len)
+char *data ;
+const char *format ;
+int len ;
+{
+	const char	*open_paren = 0 ;
+	int	x ;
+
+	while (len > 0  && *format) {
+		switch (*format) {
+		case '[' :
+			open_paren = format ;
+			break ;
+		case ']' :
+			format = open_paren ;
+			break ;
+		case '1' :
+		case '2' :
+		case '3' :
+		case '4' :
+		case '5' :
+		case '6' :
+		case '7' :
+		case '8' :
+		case '9' :
+			data  += *format - '0' ;
+			len   -= *format - '0' ;
+			break ;
+		case 'c':
+			data++ ;
+			len-- ;
+			break ;
+		case 's' :
+			x = data[0] ;
+			data[0] = data[1] ;
+			data[1] = x ;
+			data += 2 ;
+			len -= 2 ;
+			break ;
+		case 'l' :
+			x = data[0] ;
+			data[0] = data[3] ;
+			data[3] = x ;
+			x = data[1] ;
+			data[1] = data[2] ;
+			data[2] = x ;
+			data += 4 ;
+			len -= 4 ;
+			break ;
+		}
+		format++ ;
+	}
+}
+#else
+void smt_swap_para(sm,len,direction)
+struct smt_header *sm ;
+int len ;
+int direction ;			/* 0 encode 1 decode */
+{
+	SK_UNUSED(sm) ;
+	SK_UNUSED(len) ;
+	SK_UNUSED(direction) ;
+}
+#endif
+
+/*
+ * PMF actions
+ */
+int smt_action(smc,class,code,index)
+struct s_smc *smc ;
+int class ;
+int code ;
+int index ;
+{
+	int	event ;
+	int	port ;
+	DB_SMT("SMT: action %d code %d\n",class,code) ;
+	switch(class) {
+	case SMT_STATION_ACTION :
+		switch(code) {
+		case SMT_STATION_ACTION_CONNECT :
+			smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
+			queue_event(smc,EVENT_ECM,EC_CONNECT) ;
+			break ;
+		case SMT_STATION_ACTION_DISCONNECT :
+			queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+			smc->mib.fddiSMTRemoteDisconnectFlag = TRUE ;
+			RS_SET(smc,RS_DISCONNECT) ;
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+				FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_DISCONNECT,
+				smt_get_event_word(smc));
+			break ;
+		case SMT_STATION_ACTION_PATHTEST :
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+				FDDI_SMT_EVENT, (u_long) FDDI_PATH_TEST,
+				smt_get_event_word(smc));
+			break ;
+		case SMT_STATION_ACTION_SELFTEST :
+			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+				FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_SELF_TEST,
+				smt_get_event_word(smc));
+			break ;
+		case SMT_STATION_ACTION_DISABLE_A :
+			if (smc->y[PA].pc_mode == PM_PEER) {
+				RS_SET(smc,RS_EVENT) ;
+				queue_event(smc,EVENT_PCM+PA,PC_DISABLE) ;
+			}
+			break ;
+		case SMT_STATION_ACTION_DISABLE_B :
+			if (smc->y[PB].pc_mode == PM_PEER) {
+				RS_SET(smc,RS_EVENT) ;
+				queue_event(smc,EVENT_PCM+PB,PC_DISABLE) ;
+			}
+			break ;
+		case SMT_STATION_ACTION_DISABLE_M :
+			for (port = 0 ; port <  NUMPHYS ; port++) {
+				if (smc->mib.p[port].fddiPORTMy_Type != TM)
+					continue ;
+				RS_SET(smc,RS_EVENT) ;
+				queue_event(smc,EVENT_PCM+port,PC_DISABLE) ;
+			}
+			break ;
+		default :
+			return(1) ;
+		}
+		break ;
+	case SMT_PORT_ACTION :
+		switch(code) {
+		case SMT_PORT_ACTION_ENABLE :
+			event = PC_ENABLE ;
+			break ;
+		case SMT_PORT_ACTION_DISABLE :
+			event = PC_DISABLE ;
+			break ;
+		case SMT_PORT_ACTION_MAINT :
+			event = PC_MAINT ;
+			break ;
+		case SMT_PORT_ACTION_START :
+			event = PC_START ;
+			break ;
+		case SMT_PORT_ACTION_STOP :
+			event = PC_STOP ;
+			break ;
+		default :
+			return(1) ;
+		}
+		queue_event(smc,EVENT_PCM+index,event) ;
+		break ;
+	default :
+		return(1) ;
+	}
+	return(0) ;
+}
+
+/*
+ * change tneg
+ *	set T_Req in MIB (Path Attribute)
+ *	calculate new values for MAC
+ *	if change required
+ *		disconnect
+ *		set reconnect
+ *	end
+ */
+void smt_change_t_neg(smc,tneg)
+struct s_smc *smc ;
+u_long tneg ;
+{
+	smc->mib.a[PATH0].fddiPATHMaxT_Req = tneg ;
+
+	if (smt_set_mac_opvalues(smc)) {
+		RS_SET(smc,RS_EVENT) ;
+		smc->sm.please_reconnect = 1 ;
+		queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
+	}
+}
+
+/*
+ * canonical conversion of <len> bytes beginning form *data
+ */
+#ifdef  USE_CAN_ADDR
+void hwm_conv_can(smc,data,len)
+struct s_smc *smc ;
+char *data ;
+int len ;
+{
+	int i ;
+
+	SK_UNUSED(smc) ;
+
+	for (i = len; i ; i--, data++) {
+		*data = canonical[*(u_char *)data] ;
+	}
+}
+#endif
+
+#endif	/* no SLIM_SMT */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/smtdef.c linux.18p17/drivers/net/skfp/smtdef.c
--- linux.vanilla/drivers/net/skfp/smtdef.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/smtdef.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,371 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT/CMT defaults
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#ifndef OEM_USER_DATA
+#define OEM_USER_DATA	"SK-NET FDDI V2.0 Userdata"
+#endif
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)smtdef.c	2.53 99/08/11 (C) SK " ;
+#endif
+
+/*
+ * defaults
+ */
+#define TTMS(x)	((u_long)(x)*1000L)
+#define TTS(x)	((u_long)(x)*1000000L)
+#define TTUS(x)	((u_long)(x))
+
+#define DEFAULT_TB_MIN		TTMS(5)
+#define DEFAULT_TB_MAX		TTMS(50)
+#define DEFAULT_C_MIN		TTUS(1600)
+#define DEFAULT_T_OUT		TTMS(100+5)
+#define DEFAULT_TL_MIN		TTUS(30)
+#define DEFAULT_LC_SHORT	TTMS(50+5)
+#define DEFAULT_LC_MEDIUM	TTMS(500+20)
+#define DEFAULT_LC_LONG		TTS(5)+TTMS(50)
+#define DEFAULT_LC_EXTENDED	TTS(50)+TTMS(50)
+#define DEFAULT_T_NEXT_9	TTMS(200+10)
+#define DEFAULT_NS_MAX		TTUS(1310)
+#define DEFAULT_I_MAX		TTMS(25)
+#define DEFAULT_IN_MAX		TTMS(40)
+#define DEFAULT_TD_MIN		TTMS(5)
+#define DEFAULT_T_NON_OP	TTS(1)
+#define DEFAULT_T_STUCK		TTS(8)
+#define DEFAULT_T_DIRECT	TTMS(370)
+#define DEFAULT_T_JAM		TTMS(370)
+#define DEFAULT_T_ANNOUNCE	TTMS(2500)
+#define DEFAULT_D_MAX		TTUS(1617)
+#define DEFAULT_LEM_ALARM	(8)
+#define DEFAULT_LEM_CUTOFF	(7)
+#define DEFAULT_TEST_DONE	TTS(1)
+#define DEFAULT_CHECK_POLL	TTS(1)
+#define DEFAULT_POLL		TTMS(50)
+
+/*
+ * LCT errors threshold
+ */
+#define DEFAULT_LCT_SHORT	1
+#define DEFAULT_LCT_MEDIUM	3
+#define DEFAULT_LCT_LONG	5
+#define DEFAULT_LCT_EXTEND	50
+
+/* Forward declarations */
+extern  void	smt_reset_defaults ();
+static	void	smt_init_mib ();
+
+static int set_min_max() ;
+
+void smt_set_defaults(smc)
+struct s_smc *smc ;
+{
+	smt_reset_defaults(smc,0) ;
+}
+
+#define MS2BCLK(x)	((x)*12500L)
+#define US2BCLK(x)	((x)*1250L)
+
+void smt_reset_defaults(smc,level)
+struct s_smc *smc ;
+int level ;
+{
+	struct smt_config	*smt ;
+	int			i ;
+	u_long			smt_boot_time;
+
+
+	smt_init_mib(smc,level) ;
+
+	smc->os.smc_version = SMC_VERSION ;
+	smt_boot_time = smt_get_time();
+	for( i = 0; i < NUMMACS; i++ )
+		smc->sm.last_tok_time[i] = smt_boot_time ;
+	smt = &smc->s ;
+	smt->attach_s = 0 ;
+	smt->build_ring_map = 1 ;
+	smt->sas = SMT_DAS ;
+	smt->numphys = NUMPHYS ;
+	smt->pcm_tb_min = DEFAULT_TB_MIN ;
+	smt->pcm_tb_max = DEFAULT_TB_MAX ;
+	smt->pcm_c_min = DEFAULT_C_MIN ;
+	smt->pcm_t_out = DEFAULT_T_OUT ;
+	smt->pcm_tl_min = DEFAULT_TL_MIN ;
+	smt->pcm_lc_short = DEFAULT_LC_SHORT ;
+	smt->pcm_lc_medium = DEFAULT_LC_MEDIUM ;
+	smt->pcm_lc_long = DEFAULT_LC_LONG ;
+	smt->pcm_lc_extended = DEFAULT_LC_EXTENDED ;
+	smt->pcm_t_next_9 = DEFAULT_T_NEXT_9 ;
+	smt->pcm_ns_max = DEFAULT_NS_MAX ;
+	smt->ecm_i_max = DEFAULT_I_MAX ;
+	smt->ecm_in_max = DEFAULT_IN_MAX ;
+	smt->ecm_td_min = DEFAULT_TD_MIN ;
+	smt->ecm_test_done = DEFAULT_TEST_DONE ;
+	smt->ecm_check_poll = DEFAULT_CHECK_POLL ;
+	smt->rmt_t_non_op = DEFAULT_T_NON_OP ;
+	smt->rmt_t_stuck = DEFAULT_T_STUCK ;
+	smt->rmt_t_direct = DEFAULT_T_DIRECT ;
+	smt->rmt_t_jam = DEFAULT_T_JAM ;
+	smt->rmt_t_announce = DEFAULT_T_ANNOUNCE ;
+	smt->rmt_t_poll = DEFAULT_POLL ;
+        smt->rmt_dup_mac_behavior = FALSE ;  /* See Struct smt_config */
+	smt->mac_d_max = DEFAULT_D_MAX ;
+
+	smt->lct_short = DEFAULT_LCT_SHORT ;
+	smt->lct_medium = DEFAULT_LCT_MEDIUM ;
+	smt->lct_long = DEFAULT_LCT_LONG ;
+	smt->lct_extended = DEFAULT_LCT_EXTEND ;
+
+#ifndef	SLIM_SMT
+#ifdef	ESS
+	if (level == 0) {
+		smc->ess.sync_bw_available = FALSE ;
+		smc->mib.fddiESSPayload = 0 ;
+		smc->mib.fddiESSOverhead = 0 ;
+		smc->mib.fddiESSMaxTNeg = (u_long)(- MS2BCLK(25)) ;
+		smc->mib.fddiESSMinSegmentSize = 1 ;
+		smc->mib.fddiESSCategory = SB_STATIC ;
+		smc->mib.fddiESSSynchTxMode = FALSE ;
+		smc->ess.raf_act_timer_poll = FALSE ;
+		smc->ess.timer_count = 7 ; 	/* first RAF alc req after 3s */
+	}
+	smc->ess.local_sba_active = FALSE ;
+	smc->ess.sba_reply_pend = NULL ;
+#endif
+#ifdef	SBA
+	smt_init_sba(smc,level) ;
+#endif
+#endif	/* no SLIM_SMT */
+#ifdef	TAG_MODE
+	if (level == 0) {
+		smc->hw.pci_fix_value = 0 ;
+	}
+#endif
+}
+
+/*
+ * manufacturer data
+ */
+static const char man_data[32] =
+/*	 01234567890123456789012345678901	*/
+	"xxxSK-NET FDDI SMT 7.3 - V2.8.8" ;
+
+static void smt_init_mib(smc,level)
+struct s_smc *smc ;
+int level ;
+{
+	struct fddi_mib		*mib ;
+	struct fddi_mib_p	*pm ;
+	int			port ;
+	int			path ;
+
+	mib = &smc->mib ;
+	if (level == 0) {
+		/*
+		 * set EVERYTHING to ZERO
+		 * EXCEPT hw and os
+		 */
+		memset(((char *)smc)+
+			sizeof(struct s_smt_os)+sizeof(struct s_smt_hw), 0,
+			sizeof(struct s_smc) -
+			sizeof(struct s_smt_os) - sizeof(struct s_smt_hw)) ;
+	}
+	else {
+		mib->fddiSMTRemoteDisconnectFlag = 0 ;
+		mib->fddiSMTPeerWrapFlag = 0 ;
+	}
+
+	mib->fddiSMTOpVersionId = 2 ;
+	mib->fddiSMTHiVersionId = 2 ;
+	mib->fddiSMTLoVersionId = 2 ;
+	memcpy((char *) mib->fddiSMTManufacturerData,man_data,32) ;
+	if (level == 0) {
+		strcpy(mib->fddiSMTUserData,OEM_USER_DATA) ;
+	}
+	mib->fddiSMTMIBVersionId = 1 ;
+	mib->fddiSMTMac_Ct = NUMMACS ;
+	mib->fddiSMTConnectionPolicy = POLICY_MM | POLICY_AA | POLICY_BB ;
+
+	/*
+	 * fddiSMTNonMaster_Ct and fddiSMTMaster_Ct are set in smt_fixup_mib
+	 * s.sas is not set yet (is set in init driver)
+	 */
+	mib->fddiSMTAvailablePaths = MIB_PATH_P | MIB_PATH_S ;
+
+	mib->fddiSMTConfigCapabilities = 0 ;	/* no hold,no wrap_ab*/
+	mib->fddiSMTTT_Notify = 10 ;
+	mib->fddiSMTStatRptPolicy = TRUE ;
+	mib->fddiSMTTrace_MaxExpiration = SEC2MIB(7) ;
+	mib->fddiSMTMACIndexes = INDEX_MAC ;
+	mib->fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ;	/* seperated */
+
+	mib->m[MAC0].fddiMACIndex = INDEX_MAC ;
+	mib->m[MAC0].fddiMACFrameStatusFunctions = FSC_TYPE0 ;
+	mib->m[MAC0].fddiMACRequestedPaths =
+		MIB_P_PATH_LOCAL |
+		MIB_P_PATH_SEC_ALTER |
+		MIB_P_PATH_PRIM_ALTER ;
+	mib->m[MAC0].fddiMACAvailablePaths = MIB_PATH_P ;
+	mib->m[MAC0].fddiMACCurrentPath = MIB_PATH_PRIMARY ;
+	mib->m[MAC0].fddiMACT_MaxCapabilitiy = (u_long)(- MS2BCLK(165)) ;
+	mib->m[MAC0].fddiMACTVXCapabilitiy = (u_long)(- US2BCLK(52)) ;
+	if (level == 0) {
+		mib->m[MAC0].fddiMACTvxValue = (u_long)(- US2BCLK(27)) ;
+		mib->m[MAC0].fddiMACTvxValueMIB = (u_long)(- US2BCLK(27)) ;
+		mib->m[MAC0].fddiMACT_Req = (u_long)(- MS2BCLK(165)) ;
+		mib->m[MAC0].fddiMACT_ReqMIB = (u_long)(- MS2BCLK(165)) ;
+		mib->m[MAC0].fddiMACT_Max = (u_long)(- MS2BCLK(165)) ;
+		mib->m[MAC0].fddiMACT_MaxMIB = (u_long)(- MS2BCLK(165)) ;
+		mib->m[MAC0].fddiMACT_Min = (u_long)(- MS2BCLK(4)) ;
+	}
+	mib->m[MAC0].fddiMACHardwarePresent = TRUE ;
+	mib->m[MAC0].fddiMACMA_UnitdataEnable = TRUE ;
+	mib->m[MAC0].fddiMACFrameErrorThreshold = 1 ;
+	mib->m[MAC0].fddiMACNotCopiedThreshold = 1 ;
+	/*
+	 * Path attributes
+	 */
+	for (path = 0 ; path < NUMPATHS ; path++) {
+		mib->a[path].fddiPATHIndex = INDEX_PATH + path ;
+		if (level == 0) {
+			mib->a[path].fddiPATHTVXLowerBound =
+				(u_long)(- US2BCLK(27)) ;
+			mib->a[path].fddiPATHT_MaxLowerBound =
+				(u_long)(- MS2BCLK(165)) ;
+			mib->a[path].fddiPATHMaxT_Req =
+				(u_long)(- MS2BCLK(165)) ;
+		}
+	}
+
+
+	/*
+	 * Port attributes
+	 */
+	pm = mib->p ;
+	for (port = 0 ; port <  NUMPHYS ; port++) {
+		/*
+		 * set MIB pointer in phy
+		 */
+		/* Attention: don't initialize mib pointer here! */
+		/*  It must be initialized during phase 2 */
+		smc->y[port].mib = 0 ;
+		mib->fddiSMTPORTIndexes[port] = port+INDEX_PORT ;
+
+		pm->fddiPORTIndex = port+INDEX_PORT ;
+		pm->fddiPORTHardwarePresent = TRUE ;
+		if (level == 0) {
+			pm->fddiPORTLer_Alarm = DEFAULT_LEM_ALARM ;
+			pm->fddiPORTLer_Cutoff = DEFAULT_LEM_CUTOFF ;
+		}
+		/*
+		 * fddiPORTRequestedPaths are set in pcmplc.c
+		 * we don't know the port type yet !
+		 */
+		pm->fddiPORTRequestedPaths[1] = 0 ;
+		pm->fddiPORTRequestedPaths[2] = 0 ;
+		pm->fddiPORTRequestedPaths[3] = 0 ;
+		pm->fddiPORTAvailablePaths = MIB_PATH_P ;
+		pm->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ;
+		pm++ ;
+	}
+
+	(void) smt_set_mac_opvalues(smc) ;
+}
+
+int smt_set_mac_opvalues(smc)
+struct s_smc *smc ;
+{
+	int	st ;
+	int	st2 ;
+
+	st = set_min_max(1,smc->mib.m[MAC0].fddiMACTvxValueMIB,
+		smc->mib.a[PATH0].fddiPATHTVXLowerBound,
+		&smc->mib.m[MAC0].fddiMACTvxValue) ;
+	st |= set_min_max(0,smc->mib.m[MAC0].fddiMACT_MaxMIB,
+		smc->mib.a[PATH0].fddiPATHT_MaxLowerBound,
+		&smc->mib.m[MAC0].fddiMACT_Max) ;
+	st |= (st2 = set_min_max(0,smc->mib.m[MAC0].fddiMACT_ReqMIB,
+		smc->mib.a[PATH0].fddiPATHMaxT_Req,
+		&smc->mib.m[MAC0].fddiMACT_Req)) ;
+	if (st2) {
+		/* Treq attribute changed remotely. So send an AIX_EVENT to the
+		 * user
+		 */
+		AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
+			FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_T_REQ,
+			smt_get_event_word(smc));
+	}
+	return(st) ;
+}
+
+void smt_fixup_mib(smc)
+struct s_smc *smc ;
+{
+#ifdef	CONCENTRATOR
+	switch (smc->s.sas) {
+	case SMT_SAS :
+		smc->mib.fddiSMTNonMaster_Ct = 1 ;
+		break ;
+	case SMT_DAS :
+		smc->mib.fddiSMTNonMaster_Ct = 2 ;
+		break ;
+	case SMT_NAC :
+		smc->mib.fddiSMTNonMaster_Ct = 0 ;
+		break ;
+	}
+	smc->mib.fddiSMTMaster_Ct = NUMPHYS - smc->mib.fddiSMTNonMaster_Ct ;
+#else
+	switch (smc->s.sas) {
+	case SMT_SAS :
+		smc->mib.fddiSMTNonMaster_Ct = 1 ;
+		break ;
+	case SMT_DAS :
+		smc->mib.fddiSMTNonMaster_Ct = 2 ;
+		break ;
+	}
+	smc->mib.fddiSMTMaster_Ct = 0 ;
+#endif
+}
+
+/*
+ * determine new setting for operational value
+ * if limit is lower than mib
+ *	use limit
+ * else
+ *	use mib
+ * NOTE : numbers are negative, negate comparison !
+ */
+static int set_min_max(maxflag,mib,limit,oper)
+int maxflag ;
+u_long mib ;
+u_long limit ;
+u_long *oper ;
+{
+	u_long	old ;
+	old = *oper ;
+	if ((limit > mib) ^ maxflag)
+		*oper = limit ;
+	else
+		*oper = mib ;
+	return(old != *oper) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/smtinit.c linux.18p17/drivers/net/skfp/smtinit.c
--- linux.vanilla/drivers/net/skfp/smtinit.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/smtinit.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	Init SMT
+	call all module level initialization routines
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)smtinit.c	1.15 97/05/06 (C) SK " ;
+#endif
+
+extern void init_fddi_driver() ;
+
+/* define global debug variable */
+#if defined(DEBUG) && !defined(DEBUG_BRD)
+struct smt_debug debug;
+#endif
+
+#ifndef MULT_OEM
+#define OEMID(smc,i)	oem_id[i]
+	extern u_char	oem_id[] ;
+#else	/* MULT_OEM */
+#define OEMID(smc,i)	smc->hw.oem_id->oi_mark[i]
+	extern struct s_oem_ids	oem_ids[] ;
+#endif	/* MULT_OEM */
+
+/*
+ * Set OEM specific values
+ *
+ * Can not be called in smt_reset_defaults, because it is not sure that
+ * the OEM ID is already defined.
+ */
+static void set_oem_spec_val(smc)
+struct s_smc *smc ;
+{
+	struct fddi_mib *mib ;
+
+	mib = &smc->mib ;
+
+	/*
+	 * set IBM specific values
+	 */
+	if (OEMID(smc,0) == 'I') {
+		mib->fddiSMTConnectionPolicy = POLICY_MM ;
+	}
+}
+
+/*
+ * Init SMT
+ */
+int init_smt(smc,mac_addr)
+struct s_smc *smc ;
+u_char *mac_addr ;		/* canonical address or NULL */
+{
+	int	p ;
+
+#if defined(DEBUG) && !defined(DEBUG_BRD)
+	debug.d_smt = 0 ;
+	debug.d_smtf = 0 ;
+	debug.d_rmt = 0 ;
+	debug.d_ecm = 0 ;
+	debug.d_pcm = 0 ;
+	debug.d_cfm = 0 ;
+
+	debug.d_plc = 0 ;
+#ifdef	ESS
+	debug.d_ess = 0 ;
+#endif
+#ifdef	SBA
+	debug.d_sba = 0 ;
+#endif
+#endif	/* DEBUG && !DEBUG_BRD */
+
+	/* First initialize the ports mib->pointers */
+	for ( p = 0; p < NUMPHYS; p ++ ) {
+		smc->y[p].mib = & smc->mib.p[p] ;
+	}
+
+	set_oem_spec_val(smc) ;	
+	(void) smt_set_mac_opvalues(smc) ;
+	init_fddi_driver(smc,mac_addr) ;	/* HW driver */
+	smt_fixup_mib(smc) ;		/* update values that depend on s.sas */
+
+	ev_init(smc) ;			/* event queue */
+#ifndef	SLIM_SMT
+	smt_init_evc(smc) ;		/* evcs in MIB */
+#endif	/* no SLIM_SMT */
+	smt_timer_init(smc) ;		/* timer package */
+	smt_agent_init(smc) ;		/* SMT frame manager */
+
+	pcm_init(smc) ;			/* PCM state machine */
+	ecm_init(smc) ;			/* ECM state machine */
+	cfm_init(smc) ;			/* CFM state machine */
+	rmt_init(smc) ;			/* RMT state machine */
+
+	for (p = 0 ; p < NUMPHYS ; p++) {
+		pcm(smc,p,0) ;		/* PCM A state machine */
+	}
+	ecm(smc,0) ;			/* ECM state machine */
+	cfm(smc,0) ;			/* CFM state machine */
+	rmt(smc,0) ;			/* RMT state machine */
+
+	smt_agent_task(smc) ;		/* NIF FSM etc */
+
+        PNMI_INIT(smc) ;                /* PNMI initialization */
+
+	return(0) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/smtparse.c linux.18p17/drivers/net/skfp/smtparse.c
--- linux.vanilla/drivers/net/skfp/smtparse.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/smtparse.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,475 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+
+/*
+	parser for SMT parameters
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smt_p.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)smtparse.c	1.12 98/10/06 (C) SK " ;
+#endif
+
+#ifdef	sun
+#define _far
+#endif
+
+/*
+ * convert to BCLK units
+ */
+#define MS2BCLK(x)      ((x)*12500L)
+#define US2BCLK(x)      ((x/10)*125L)
+
+/*
+ * parameter table
+ */
+static struct s_ptab {
+	char	*pt_name ;
+	u_short	pt_num ;
+	u_short	pt_type ;
+	u_long	pt_min ;
+	u_long	pt_max ;
+} ptab[] = {
+	{ "PMFPASSWD",0,	0 } ,
+	{ "USERDATA",1,		0 } ,
+	{ "LERCUTOFFA",2,	1,	4,	15	} ,
+	{ "LERCUTOFFB",3,	1,	4,	15	} ,
+	{ "LERALARMA",4,	1,	4,	15	} ,
+	{ "LERALARMB",5,	1,	4,	15	} ,
+	{ "TMAX",6,		1,	5,	165	} ,
+	{ "TMIN",7,		1,	5,	165	} ,
+	{ "TREQ",8,		1,	5,	165	} ,
+	{ "TVX",9,		1,	2500,	10000	} ,
+#ifdef ESS
+	{ "SBAPAYLOAD",10,	1,	0,	1562	} ,
+	{ "SBAOVERHEAD",11,	1,	50,	5000	} ,
+	{ "MAXTNEG",12,		1,	5,	165	} ,
+	{ "MINSEGMENTSIZE",13,	1,	0,	4478	} ,
+	{ "SBACATEGORY",14,	1,	0,	0xffff	} ,
+	{ "SYNCHTXMODE",15,	0 } ,
+#endif
+#ifdef SBA
+	{ "SBACOMMAND",16,	0 } ,
+	{ "SBAAVAILABLE",17,	1,	0,	100	} ,
+#endif
+	{ 0 }
+} ;
+
+/* Define maximum string size for values and keybuffer */
+#define MAX_VAL	40
+
+/*
+ * local function declarations
+ */
+static u_long parse_num() ;
+static int parse_word() ;
+
+#ifdef SIM
+#define DB_MAIN(a,b,c)	printf(a,b,c)
+#else
+#define DB_MAIN(a,b,c)
+#endif
+
+/*
+ * BEGIN_MANUAL_ENTRY()
+ *
+ *	int smt_parse_arg(struct s_smc *,char _far *keyword,int type,
+		char _far *value)
+ *
+ *	parse SMT parameter
+ *	*keyword
+ *		pointer to keyword, must be \0, \n or \r terminated
+ *	*value	pointer to value, either char * or u_long *
+ *		if char *
+ *			pointer to value, must be \0, \n or \r terminated
+ *		if u_long *
+ *			contains binary value
+ *
+ *	type	0: integer
+ *		1: string
+ *	return
+ *		0	parameter parsed ok
+ *		!= 0	error
+ *	NOTE:
+ *		function can be called with DS != SS
+ *
+ *
+ * END_MANUAL_ENTRY()
+ */
+int smt_parse_arg(smc,keyword,type,value)
+struct s_smc *smc ;
+char _far *keyword ;
+int type ;
+char _far *value ;
+{
+	char		keybuf[MAX_VAL+1];
+	char		valbuf[MAX_VAL+1];
+	char		c ;
+	char 		*p ;
+	char		*v ;
+	char		*d ;
+	u_long		val = 0 ;
+	struct s_ptab	*pt ;
+	int		st ;
+	int		i ;
+
+	/*
+	 * parse keyword
+	 */
+	if ((st = parse_word(keybuf,keyword)))
+		return(st) ;
+	/*
+	 * parse value if given as string
+	 */
+	if (type == 1) {
+		if ((st = parse_word(valbuf,value)))
+			return(st) ;
+	}
+	/*
+	 * search in table
+	 */
+	st = 0 ;
+	for (pt = ptab ; (v = pt->pt_name) ; pt++) {
+		for (p = keybuf ; (c = *p) ; p++,v++) {
+			if (c != *v)
+				break ;
+		}
+		if (!c && !*v)
+			break ;
+	}
+	if (!v)
+		return(-1) ;
+#if	0
+	printf("=>%s<==>%s<=\n",pt->pt_name,valbuf) ;
+#endif
+	/*
+	 * set value in MIB
+	 */
+	if (pt->pt_type)
+		val = parse_num(type,value,valbuf,pt->pt_min,pt->pt_max,1) ;
+	switch (pt->pt_num) {
+	case 0 :
+		v = valbuf ;
+		d = (char *) smc->mib.fddiPRPMFPasswd ;
+		for (i = 0 ; i < (signed)sizeof(smc->mib.fddiPRPMFPasswd) ; i++)
+			*d++ = *v++ ;
+		DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiPRPMFPasswd) ;
+		break ;
+	case 1 :
+		v = valbuf ;
+		d = (char *) smc->mib.fddiSMTUserData ;
+		for (i = 0 ; i < (signed)sizeof(smc->mib.fddiSMTUserData) ; i++)
+			*d++ = *v++ ;
+		DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiSMTUserData) ;
+		break ;
+	case 2 :
+		smc->mib.p[PA].fddiPORTLer_Cutoff = (u_char) val ;
+		DB_MAIN("SET %s = %d\n",
+			pt->pt_name,smc->mib.p[PA].fddiPORTLer_Cutoff) ;
+		break ;
+	case 3 :
+		smc->mib.p[PB].fddiPORTLer_Cutoff = (u_char) val ;
+		DB_MAIN("SET %s = %d\n",
+			pt->pt_name,smc->mib.p[PB].fddiPORTLer_Cutoff) ;
+		break ;
+	case 4 :
+		smc->mib.p[PA].fddiPORTLer_Alarm = (u_char) val ;
+		DB_MAIN("SET %s = %d\n",
+			pt->pt_name,smc->mib.p[PA].fddiPORTLer_Alarm) ;
+		break ;
+	case 5 :
+		smc->mib.p[PB].fddiPORTLer_Alarm = (u_char) val ;
+		DB_MAIN("SET %s = %d\n",
+			pt->pt_name,smc->mib.p[PB].fddiPORTLer_Alarm) ;
+		break ;
+	case 6 :			/* TMAX */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.a[PATH0].fddiPATHT_MaxLowerBound =
+			(u_long) -MS2BCLK((long)val) ;
+		break ;
+	case 7 :			/* TMIN */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.m[MAC0].fddiMACT_Min =
+			(u_long) -MS2BCLK((long)val) ;
+		break ;
+	case 8 :			/* TREQ */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.a[PATH0].fddiPATHMaxT_Req =
+			(u_long) -MS2BCLK((long)val) ;
+		break ;
+	case 9 :			/* TVX */
+		DB_MAIN("SET %s = %d \n",pt->pt_name,val) ;
+		smc->mib.a[PATH0].fddiPATHTVXLowerBound =
+			(u_long) -US2BCLK((long)val) ;
+		break ;
+#ifdef	ESS
+	case 10 :			/* SBAPAYLOAD */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		if (smc->mib.fddiESSPayload != val) {
+			smc->ess.raf_act_timer_poll = TRUE ;
+			smc->mib.fddiESSPayload = val ;
+		}
+		break ;
+	case 11 :			/* SBAOVERHEAD */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.fddiESSOverhead = val ;
+		break ;
+	case 12 :			/* MAXTNEG */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.fddiESSMaxTNeg = (u_long) -MS2BCLK((long)val) ;
+		break ;
+	case 13 :			/* MINSEGMENTSIZE */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.fddiESSMinSegmentSize = val ;
+		break ;
+	case 14 :			/* SBACATEGORY */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.fddiESSCategory =
+			(smc->mib.fddiESSCategory & 0xffff) |
+			((u_long)(val << 16)) ;
+		break ;
+	case 15 :			/* SYNCHTXMODE */
+		/* do not use memcmp(valbuf,"ALL",3) because DS != SS */
+		if (valbuf[0] == 'A' && valbuf[1] == 'L' && valbuf[2] == 'L') {
+			smc->mib.fddiESSSynchTxMode = TRUE ;
+			DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ;
+		}
+		/* if (!memcmp(valbuf,"SPLIT",5)) { */
+		if (valbuf[0] == 'S' && valbuf[1] == 'P' && valbuf[2] == 'L' &&
+			valbuf[3] == 'I' && valbuf[4] == 'T') {
+			DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ;
+			smc->mib.fddiESSSynchTxMode = FALSE ;
+		}
+		break ;
+#endif
+#ifdef	SBA
+	case 16 :			/* SBACOMMAND */
+		/* if (!memcmp(valbuf,"START",5)) { */
+		if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'A' &&
+			valbuf[3] == 'R' && valbuf[4] == 'T') {
+			DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ;
+			smc->mib.fddiSBACommand = SB_START ;
+		}
+		/* if (!memcmp(valbuf,"STOP",4)) { */
+		if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'O' &&
+			valbuf[3] == 'P') {
+			DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ;
+			smc->mib.fddiSBACommand = SB_STOP ;
+		}
+		break ;
+	case 17 :			/* SBAAVAILABLE */
+		DB_MAIN("SET %s = %d\n",pt->pt_name,val) ;
+		smc->mib.fddiSBAAvailable = (u_char) val ;
+		break ;
+#endif
+	}
+	return(0) ;
+}
+
+static int parse_word(buf,text)
+char *buf ;
+char _far *text ;
+{
+	char		c ;
+	char 		*p ;
+	int		p_len ;
+	int		quote ;
+	int		i ;
+	int		ok ;
+
+	/*
+	 * skip leading white space
+	 */
+	p = buf ;
+	for (i = 0 ; i < MAX_VAL ; i++)
+		*p++ = 0 ;
+	p = buf ;
+	p_len = 0 ;
+	ok = 0 ;
+	while ( (c = *text++) && (c != '\n') && (c != '\r')) {
+		if ((c != ' ') && (c != '\t')) {
+			ok = 1 ;
+			break ;
+		}
+	}
+	if (!ok)
+		return(-1) ;
+	if (c == '"') {
+		quote = 1 ;
+	}
+	else {
+		quote = 0 ;
+		text-- ;
+	}
+	/*
+	 * parse valbuf
+	 */
+	ok = 0 ;
+	while (!ok && p_len < MAX_VAL-1 && (c = *text++) && (c != '\n')
+		&& (c != '\r')) {
+		switch (quote) {
+		case 0 :
+			if ((c == ' ') || (c == '\t') || (c == '=')) {
+				ok = 1 ;
+				break ;
+			}
+			*p++ = c ;
+			p_len++ ;
+			break ;
+		case 2 :
+			*p++ = c ;
+			p_len++ ;
+			quote = 1 ;
+			break ;
+		case 1 :
+			switch (c) {
+			case '"' :
+				ok = 1 ;
+				break ;
+			case '\\' :
+				quote = 2 ;
+				break ;
+			default :
+				*p++ = c ;
+				p_len++ ;
+			}
+		}
+	}
+	*p++ = 0 ;
+	for (p = buf ; (c = *p) ; p++) {
+		if (c >= 'a' && c <= 'z')
+			*p = c + 'A' - 'a' ;
+	}
+	return(0) ;
+}
+
+static u_long parse_num(type,value,v,mn,mx,scale)
+int type ;
+char _far *value ;
+char *v ;
+u_long mn ;
+u_long mx ;
+int scale ;
+{
+	u_long	x = 0 ;
+	char	c ;
+
+	if (type == 0) {		/* integer */
+		u_long _far	*l ;
+		u_long		u1 ;
+
+		l = (u_long _far *) value ;
+		u1 = *l ;
+		/*
+		 * if the value is negative take the lower limit
+		 */
+		if ((long)u1 < 0) {
+			if (- ((long)u1) > (long) mx) {
+				u1 = 0 ;
+			}
+			else {
+				u1 = (u_long) - ((long)u1) ;
+			}
+		}
+		x = u1 ;
+	}
+	else {				/* string */
+		int	sign = 0 ;
+
+		if (*v == '-') {
+			sign = 1 ;
+		}
+		while ((c = *v++) && (c >= '0') && (c <= '9')) {
+			x = x * 10 + c - '0' ;
+		}
+		if (scale == 10) {
+			x *= 10 ;
+			if (c == '.') {
+				if ((c = *v++) && (c >= '0') && (c <= '9')) {
+					x += c - '0' ;
+				}
+			}
+		}
+		if (sign)
+			x = (u_long) - ((long)x) ;
+	}
+	/*
+	 * if the value is negative
+	 *	and the absolute value is outside the limits
+	 *		take the lower limit
+	 *	else
+	 *		take the absoute value
+	 */
+	if ((long)x < 0) {
+		if (- ((long)x) > (long) mx) {
+			x = 0 ;
+		}
+		else {
+			x = (u_long) - ((long)x) ;
+		}
+	}
+	if (x < mn)
+		return(mn) ;
+	else if (x > mx)
+		return(mx) ;
+	return(x) ;
+}
+
+#if 0
+struct	s_smc	SMC ;
+main()
+{
+	char	*p ;
+	char	*v ;
+	char	buf[100] ;
+	int	toggle = 0 ;
+
+	while (gets(buf)) {
+		p = buf ;
+		while (*p && ((*p == ' ') || (*p == '\t')))
+			p++ ;
+
+		while (*p && ((*p != ' ') && (*p != '\t')))
+			p++ ;
+
+		v = p ;
+		while (*v && ((*v == ' ') || (*v == '\t')))
+			v++ ;
+		if ((*v >= '0') && (*v <= '9')) {
+			toggle = !toggle ;
+			if (toggle) {
+				u_long	l ;
+				l = atol(v) ;
+				smt_parse_arg(&SMC,buf,0,(char _far *)&l) ;
+			}
+			else
+				smt_parse_arg(&SMC,buf,1,(char _far *)p) ;
+		}
+		else {
+			smt_parse_arg(&SMC,buf,1,(char _far *)p) ;
+		}
+	}
+	exit(0) ;
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/smttimer.c linux.18p17/drivers/net/skfp/smttimer.c
--- linux.vanilla/drivers/net/skfp/smttimer.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/smttimer.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,173 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT timer
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)smttimer.c	2.4 97/08/04 (C) SK " ;
+#endif
+
+/*
+ * external function declarations
+ */
+extern u_long hwt_read() ;
+extern void hwt_stop() ;
+extern void hwt_start() ;
+
+static void timer_done() ;
+
+
+void smt_timer_init(smc)
+struct s_smc *smc ;
+{
+	smc->t.st_queue = 0 ;
+	smc->t.st_fast.tm_active = FALSE ;
+	smc->t.st_fast.tm_next = 0 ;
+	hwt_init(smc) ;
+}
+
+void smt_timer_stop(smc,timer)
+struct s_smc *smc ;
+struct smt_timer *timer ;
+{
+	struct smt_timer	**prev ;
+	struct smt_timer	*tm ;
+
+	/*
+	 * remove timer from queue
+	 */
+	timer->tm_active = FALSE ;
+	if (smc->t.st_queue == timer && !timer->tm_next) {
+		hwt_stop(smc) ;
+	}
+	for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
+		if (tm == timer) {
+			*prev = tm->tm_next ;
+			if (tm->tm_next) {
+				tm->tm_next->tm_delta += tm->tm_delta ;
+			}
+			return ;
+		}
+	}
+}
+
+void smt_timer_start(smc,timer,time,token)
+struct s_smc *smc ;
+struct smt_timer *timer ;
+u_long		time ;
+u_long		token ;
+{
+	struct smt_timer	**prev ;
+	struct smt_timer	*tm ;
+	u_long			delta = 0 ;
+
+	time /= 16 ;		/* input is uS, clock ticks are 16uS */
+	if (!time)
+		time = 1 ;
+	smt_timer_stop(smc,timer) ;
+	timer->tm_smc = smc ;
+	timer->tm_token = token ;
+	timer->tm_active = TRUE ;
+	if (!smc->t.st_queue) {
+		smc->t.st_queue = timer ;
+		timer->tm_next = 0 ;
+		timer->tm_delta = time ;
+		hwt_start(smc,time) ;
+		return ;
+	}
+	/*
+	 * timer correction
+	 */
+	timer_done(smc,0) ;
+
+	/*
+	 * find position in queue
+	 */
+	delta = 0 ;
+	for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
+		if (delta + tm->tm_delta > time) {
+			break ;
+		}
+		delta += tm->tm_delta ;
+	}
+	/* insert in queue */
+	*prev = timer ;
+	timer->tm_next = tm ;
+	timer->tm_delta = time - delta ;
+	if (tm)
+		tm->tm_delta -= timer->tm_delta ;
+	/*
+	 * start new with first
+	 */
+	hwt_start(smc,smc->t.st_queue->tm_delta) ;
+}
+
+void smt_force_irq(smc)
+struct s_smc *smc ;
+{
+	smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST)); 
+}
+
+void smt_timer_done(smc)
+struct s_smc *smc ;
+{
+	timer_done(smc,1) ;
+}
+
+static void timer_done(smc,restart)
+struct s_smc *smc ;
+int restart ;
+{
+	u_long			delta ;
+	struct smt_timer	*tm ;
+	struct smt_timer	*next ;
+	struct smt_timer	**last ;
+	int			done = 0 ;
+
+	delta = hwt_read(smc) ;
+	last = &smc->t.st_queue ;
+	tm = smc->t.st_queue ;
+	while (tm && !done) {
+		if (delta >= tm->tm_delta) {
+			tm->tm_active = FALSE ;
+			delta -= tm->tm_delta ;
+			last = &tm->tm_next ;
+			tm = tm->tm_next ;
+		}
+		else {
+			tm->tm_delta -= delta ;
+			delta = 0 ;
+			done = 1 ;
+		}
+	}
+	*last = 0 ;
+	next = smc->t.st_queue ;
+	smc->t.st_queue = tm ;
+
+	for ( tm = next ; tm ; tm = next) {
+		next = tm->tm_next ;
+		timer_event(smc,tm->tm_token) ;
+	}
+
+	if (restart && smc->t.st_queue)
+		hwt_start(smc,smc->t.st_queue->tm_delta) ;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/skfp/srf.c linux.18p17/drivers/net/skfp/srf.c
--- linux.vanilla/drivers/net/skfp/srf.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/skfp/srf.c	Fri Sep  1 13:48:23 2000
@@ -0,0 +1,441 @@
+/******************************************************************************
+ *
+ *	(C)Copyright 1998,1999 SysKonnect,
+ *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
+ *
+ *	See the file "skfddi.c" for further information.
+ *
+ *	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.
+ *
+ *	The information in this file is provided "AS IS" without warranty.
+ *
+ ******************************************************************************/
+
+/*
+	SMT 7.2 Status Response Frame Implementation
+	SRF state machine and frame generation
+*/
+
+#include "h/types.h"
+#include "h/fddi.h"
+#include "h/smc.h"
+#include "h/smt_p.h"
+
+#define KERNEL
+#include "h/smtstate.h"
+
+#ifndef	SLIM_SMT
+#ifndef	BOOT
+
+#ifndef	lint
+static const char ID_sccs[] = "@(#)srf.c	1.18 97/08/04 (C) SK " ;
+#endif
+
+
+/*
+ * function declarations
+ */
+static void clear_all_rep() ;
+static void clear_reported() ;
+static void smt_send_srf() ;
+static struct s_srf_evc *smt_get_evc() ;
+
+#define MAX_EVCS	(sizeof(smc->evcs)/sizeof(smc->evcs[0]))
+
+struct evc_init {
+	u_char code ;
+	u_char index ;
+	u_char n ;
+	u_short	para ;
+}  ;
+
+static const struct evc_init evc_inits[] = {
+	{ SMT_COND_SMT_PEER_WRAP,		0,1,SMT_P1048	} ,
+
+	{ SMT_COND_MAC_DUP_ADDR,		INDEX_MAC, NUMMACS,SMT_P208C } ,
+	{ SMT_COND_MAC_FRAME_ERROR,		INDEX_MAC, NUMMACS,SMT_P208D } ,
+	{ SMT_COND_MAC_NOT_COPIED,		INDEX_MAC, NUMMACS,SMT_P208E } ,
+	{ SMT_EVENT_MAC_NEIGHBOR_CHANGE,	INDEX_MAC, NUMMACS,SMT_P208F } ,
+	{ SMT_EVENT_MAC_PATH_CHANGE,		INDEX_MAC, NUMMACS,SMT_P2090 } ,
+
+	{ SMT_COND_PORT_LER,			INDEX_PORT,NUMPHYS,SMT_P4050 } ,
+	{ SMT_COND_PORT_EB_ERROR,		INDEX_PORT,NUMPHYS,SMT_P4052 } ,
+	{ SMT_EVENT_PORT_CONNECTION,		INDEX_PORT,NUMPHYS,SMT_P4051 } ,
+	{ SMT_EVENT_PORT_PATH_CHANGE,		INDEX_PORT,NUMPHYS,SMT_P4053 } ,
+} ;
+
+#define MAX_INIT_EVC	(sizeof(evc_inits)/sizeof(evc_inits[0]))
+
+void smt_init_evc(smc)
+struct s_smc *smc ;
+{
+	struct s_srf_evc	*evc ;
+	const struct evc_init 	*init ;
+	int			i ;
+	int			index ;
+	int			offset ;
+
+	static u_char		fail_safe = FALSE ;
+
+	memset((char *)smc->evcs,0,sizeof(smc->evcs)) ;
+
+	evc = smc->evcs ;
+	init = evc_inits ;
+
+	for (i = 0 ; (unsigned) i < MAX_INIT_EVC ; i++) {
+		for (index = 0 ; index < init->n ; index++) {
+			evc->evc_code = init->code ;
+			evc->evc_para = init->para ;
+			evc->evc_index = init->index + index ;
+#ifndef	DEBUG
+			evc->evc_multiple = &fail_safe ;
+			evc->evc_cond_state = &fail_safe ;
+#endif
+			evc++ ;
+		}
+		init++ ;
+	}
+
+	if ((unsigned) (evc - smc->evcs) > MAX_EVCS) {
+		SMT_PANIC(smc,SMT_E0127, SMT_E0127_MSG) ;
+	}
+
+	/*
+	 * conditions
+	 */
+	smc->evcs[0].evc_cond_state = &smc->mib.fddiSMTPeerWrapFlag ;
+	smc->evcs[1].evc_cond_state =
+		&smc->mib.m[MAC0].fddiMACDuplicateAddressCond ;
+	smc->evcs[2].evc_cond_state =
+		&smc->mib.m[MAC0].fddiMACFrameErrorFlag ;
+	smc->evcs[3].evc_cond_state =
+		&smc->mib.m[MAC0].fddiMACNotCopiedFlag ;
+
+	/*
+	 * events
+	 */
+	smc->evcs[4].evc_multiple = &smc->mib.m[MAC0].fddiMACMultiple_N ;
+	smc->evcs[5].evc_multiple = &smc->mib.m[MAC0].fddiMACMultiple_P ;
+
+	offset = 6 ;
+	for (i = 0 ; i < NUMPHYS ; i++) {
+		/*
+		 * conditions
+		 */
+		smc->evcs[offset + 0*NUMPHYS].evc_cond_state =
+			&smc->mib.p[i].fddiPORTLerFlag ;
+		smc->evcs[offset + 1*NUMPHYS].evc_cond_state =
+			&smc->mib.p[i].fddiPORTEB_Condition ;
+
+		/*
+		 * events
+		 */
+		smc->evcs[offset + 2*NUMPHYS].evc_multiple =
+			&smc->mib.p[i].fddiPORTMultiple_U ;
+		smc->evcs[offset + 3*NUMPHYS].evc_multiple =
+			&smc->mib.p[i].fddiPORTMultiple_P ;
+		offset++ ;
+	}
+#ifdef	DEBUG
+	for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) {
+		if (SMT_IS_CONDITION(evc->evc_code)) {
+			if (!evc->evc_cond_state) {
+				SMT_PANIC(smc,SMT_E0128, SMT_E0128_MSG) ;
+			}
+			evc->evc_multiple = &fail_safe ;
+		}
+		else {
+			if (!evc->evc_multiple) {
+				SMT_PANIC(smc,SMT_E0129, SMT_E0129_MSG) ;
+			}
+			evc->evc_cond_state = &fail_safe ;
+		}
+	}
+#endif
+	smc->srf.TSR = smt_get_time() ;
+	smc->srf.sr_state = SR0_WAIT ;
+}
+
+static struct s_srf_evc *smt_get_evc(smc,code,index)
+struct s_smc *smc ;
+int code ;
+int index ;
+{
+	int			i ;
+	struct s_srf_evc	*evc ;
+
+	for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) {
+		if (evc->evc_code == code && evc->evc_index == index)
+			return(evc) ;
+	}
+	return(0) ;
+}
+
+#define THRESHOLD_2	(2*TICKS_PER_SECOND)
+#define THRESHOLD_32	(32*TICKS_PER_SECOND)
+
+#ifdef	DEBUG
+static const char * const srf_names[] = {
+	"None","MACPathChangeEvent",	"MACNeighborChangeEvent",
+	"PORTPathChangeEvent",		"PORTUndesiredConnectionAttemptEvent",
+	"SMTPeerWrapCondition",		"SMTHoldCondition",
+	"MACFrameErrorCondition",	"MACDuplicateAddressCondition",
+	"MACNotCopiedCondition",	"PORTEBErrorCondition",
+	"PORTLerCondition"
+} ;
+#endif
+
+void smt_srf_event(smc,code,index,cond)
+struct s_smc *smc ;
+int code ;
+int index ;
+int cond ;
+{
+	struct s_srf_evc	*evc ;
+	int			cond_asserted = 0 ;
+	int			cond_deasserted = 0 ;
+	int			event_occured = 0 ;
+	int			tsr ;
+	int			T_Limit = 2*TICKS_PER_SECOND ;
+
+	if (code == SMT_COND_MAC_DUP_ADDR && cond) {
+		RS_SET(smc,RS_DUPADDR) ;
+	}
+
+	if (code) {
+		DB_SMT("SRF: %s index %d\n",srf_names[code],index) ;
+
+		if (!(evc = smt_get_evc(smc,code,index))) {
+			DB_SMT("SRF : smt_get_evc() failed\n",0,0) ;
+			return ;
+		}
+		/*
+		 * ignore condition if no change
+		 */
+		if (SMT_IS_CONDITION(code)) {
+			if (*evc->evc_cond_state == cond)
+				return ;
+		}
+
+		/*
+		 * set transition time stamp
+		 */
+		smt_set_timestamp(smc,smc->mib.fddiSMTTransitionTimeStamp) ;
+		if (SMT_IS_CONDITION(code)) {
+			DB_SMT("SRF: condition is %s\n",cond ? "ON":"OFF",0) ;
+			if (cond) {
+				*evc->evc_cond_state = TRUE ;
+				evc->evc_rep_required = TRUE ;
+				smc->srf.any_report = TRUE ;
+				cond_asserted = TRUE ;
+			}
+			else {
+				*evc->evc_cond_state = FALSE ;
+				cond_deasserted = TRUE ;
+			}
+		}
+		else {
+			if (evc->evc_rep_required) {
+				*evc->evc_multiple  = TRUE ;
+			}
+			else {
+				evc->evc_rep_required = TRUE ;
+				*evc->evc_multiple  = FALSE ;
+			}
+			smc->srf.any_report = TRUE ;
+			event_occured = TRUE ;
+		}
+#ifdef	FDDI_MIB
+		snmp_srf_event(smc,evc) ;
+#endif	/* FDDI_MIB */
+	}
+	tsr = smt_get_time() - smc->srf.TSR ;
+
+	switch (smc->srf.sr_state) {
+	case SR0_WAIT :
+		/* SR01a */
+		if (cond_asserted && tsr < T_Limit) {
+			smc->srf.SRThreshold = THRESHOLD_2 ;
+			smc->srf.sr_state = SR1_HOLDOFF ;
+			break ;
+		}
+		/* SR01b */
+		if (cond_deasserted && tsr < T_Limit) {
+			smc->srf.sr_state = SR1_HOLDOFF ;
+			break ;
+		}
+		/* SR01c */
+		if (event_occured && tsr < T_Limit) {
+			smc->srf.sr_state = SR1_HOLDOFF ;
+			break ;
+		}
+		/* SR00b */
+		if (cond_asserted && tsr >= T_Limit) {
+			smc->srf.SRThreshold = THRESHOLD_2 ;
+			smc->srf.TSR = smt_get_time() ;
+			smt_send_srf(smc) ;
+			break ;
+		}
+		/* SR00c */
+		if (cond_deasserted && tsr >= T_Limit) {
+			smc->srf.TSR = smt_get_time() ;
+			smt_send_srf(smc) ;
+			break ;
+		}
+		/* SR00d */
+		if (event_occured && tsr >= T_Limit) {
+			smc->srf.TSR = smt_get_time() ;
+			smt_send_srf(smc) ;
+			break ;
+		}
+		/* SR00e */
+		if (smc->srf.any_report && (u_long) tsr >=
+			smc->srf.SRThreshold) {
+			smc->srf.SRThreshold *= 2 ;
+			if (smc->srf.SRThreshold > THRESHOLD_32)
+				smc->srf.SRThreshold = THRESHOLD_32 ;
+			smc->srf.TSR = smt_get_time() ;
+			smt_send_srf(smc) ;
+			break ;
+		}
+		/* SR02 */
+		if (!smc->mib.fddiSMTStatRptPolicy) {
+			smc->srf.sr_state = SR2_DISABLED ;
+			break ;
+		}
+		break ;
+	case SR1_HOLDOFF :
+		/* SR10b */
+		if (tsr >= T_Limit) {
+			smc->srf.sr_state = SR0_WAIT ;
+			smc->srf.TSR = smt_get_time() ;
+			smt_send_srf(smc) ;
+			break ;
+		}
+		/* SR11a */
+		if (cond_asserted) {
+			smc->srf.SRThreshold = THRESHOLD_2 ;
+		}
+		/* SR11b */
+		/* SR11c */
+		/* handled above */
+		/* SR12 */
+		if (!smc->mib.fddiSMTStatRptPolicy) {
+			smc->srf.sr_state = SR2_DISABLED ;
+			break ;
+		}
+		break ;
+	case SR2_DISABLED :
+		if (smc->mib.fddiSMTStatRptPolicy) {
+			smc->srf.sr_state = SR0_WAIT ;
+			smc->srf.TSR = smt_get_time() ;
+			smc->srf.SRThreshold = THRESHOLD_2 ;
+			clear_all_rep(smc) ;
+			break ;
+		}
+		break ;
+	}
+}
+
+static void clear_all_rep(smc)
+struct s_smc *smc ;
+{
+	struct s_srf_evc	*evc ;
+	int			i ;
+
+	for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) {
+		evc->evc_rep_required = FALSE ;
+		if (SMT_IS_CONDITION(evc->evc_code))
+			*evc->evc_cond_state = FALSE ;
+	}
+	smc->srf.any_report = FALSE ;
+}
+
+static void clear_reported(smc)
+struct s_smc *smc ;
+{
+	struct s_srf_evc	*evc ;
+	int			i ;
+
+	smc->srf.any_report = FALSE ;
+	for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) {
+		if (SMT_IS_CONDITION(evc->evc_code)) {
+			if (*evc->evc_cond_state == FALSE)
+				evc->evc_rep_required = FALSE ;
+			else
+				smc->srf.any_report = TRUE ;
+		}
+		else {
+			evc->evc_rep_required = FALSE ;
+			*evc->evc_multiple = FALSE ;
+		}
+	}
+}
+
+extern SMbuf *smt_build_frame() ;
+
+/*
+ * build and send SMT SRF frame
+ */
+static void smt_send_srf(smc)
+struct s_smc *smc ;
+{
+
+	struct smt_header	*smt ;
+	struct s_srf_evc	*evc ;
+	SK_LOC_DECL(struct s_pcon,pcon) ;
+	SMbuf			*mb ;
+	int			i ;
+
+	static const struct fddi_addr SMT_SRF_DA = {
+		{ 0x80, 0x01, 0x43, 0x00, 0x80, 0x08 }
+	} ;
+
+	/*
+	 * build SMT header
+	 */
+	if (!smc->r.sm_ma_avail)
+		return ;
+	if (!(mb = smt_build_frame(smc,SMT_SRF,SMT_ANNOUNCE,0)))
+		return ;
+
+	RS_SET(smc,RS_SOFTERROR) ;
+
+	smt = smtod(mb, struct smt_header *) ;
+	smt->smt_dest = SMT_SRF_DA ;		/* DA == SRF multicast */
+
+	/*
+	 * setup parameter status
+	 */
+	pcon.pc_len = SMT_MAX_INFO_LEN ;	/* max para length */
+	pcon.pc_err = 0 ;			/* no error */
+	pcon.pc_badset = 0 ;			/* no bad set count */
+	pcon.pc_p = (void *) (smt + 1) ;	/* paras start here */
+
+	smt_add_para(smc,&pcon,(u_short) SMT_P1033,0,0) ;
+	smt_add_para(smc,&pcon,(u_short) SMT_P1034,0,0) ;
+
+	for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) {
+		if (evc->evc_rep_required) {
+			smt_add_para(smc,&pcon,evc->evc_para,
+				(int)evc->evc_index,0) ;
+		}
+	}
+	smt->smt_len = SMT_MAX_INFO_LEN - pcon.pc_len ;
+	mb->sm_len = smt->smt_len + sizeof(struct smt_header) ;
+
+	DB_SMT("SRF: sending SRF at %x, len %d \n",smt,mb->sm_len) ;
+	DB_SMT("SRF: state SR%d Threshold %d\n",
+		smc->srf.sr_state,smc->srf.SRThreshold/TICKS_PER_SECOND) ;
+#ifdef	DEBUG
+	dump_smt(smc,smt,"SRF Send") ;
+#endif
+	smt_send_frame(smc,mb,FC_SMT_INFO,0) ;
+	clear_reported(smc) ;
+}
+
+#endif	/* no BOOT */
+#endif	/* no SLIM_SMT */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/smc9194.c linux.18p17/drivers/net/smc9194.c
--- linux.vanilla/drivers/net/smc9194.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/net/smc9194.c	Thu Aug 31 14:28:50 2000
@@ -18,6 +18,8 @@
  .
  . author:
  . 	Erik Stahlman				( erik@vt.edu )
+ . contributors:
+ .      Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  .
  . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
  .
@@ -47,6 +49,7 @@
  .	03/06/96  Erik Stahlman  Added hardware multicast from Peter Cammaert
  .	04/14/00  Heiko Pruessing (SMA Regelsysteme)  Fixed bug in chip memory
  .				 allocation
+ .      08/20/00  Arnaldo Melo   fix kfree(skb) in smc_hardware_send_packet
  ----------------------------------------------------------------------------*/
 
 static const char *version =
@@ -667,7 +670,7 @@
 	if ( packet_no & 0x80 ) {
 		/* or isn't there?  BAD CHIP! */
 		printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n");
-		kfree(skb);
+		kfree_skb(skb);
 		lp->saved_skb = NULL;
 		dev->tbusy = 0;
 		return;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/syncppp.c linux.18p17/drivers/net/syncppp.c
--- linux.vanilla/drivers/net/syncppp.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/net/syncppp.c	Wed Sep 13 16:10:27 2000
@@ -517,7 +517,6 @@
 			sppp_ipcp_open (sp);
 			break;
 		case LCP_STATE_OPENED:
-#if 0		
 			/* Remote magic changed -- close session. */
 			sp->lcp.state = LCP_STATE_CLOSED;
 			sp->ipcp.state = IPCP_STATE_CLOSED;
@@ -525,7 +524,6 @@
 			sppp_lcp_open (sp);
 			/* An ACK has already been sent. */
 			sp->lcp.state = LCP_STATE_ACK_SENT;
-#endif			
 			break;
 		}
 		break;
@@ -566,7 +564,7 @@
 				sp->lcp.magic += newmagic;
 			} else
 				sp->lcp.magic = rmagic;
-			}
+		}
 		if (sp->lcp.state != LCP_STATE_ACK_SENT) {
 			/* Go to closed state. */
 			sp->lcp.state = LCP_STATE_CLOSED;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/tlan.c linux.18p17/drivers/net/tlan.c
--- linux.vanilla/drivers/net/tlan.c	Sat Sep  9 18:42:40 2000
+++ linux.18p17/drivers/net/tlan.c	Mon Sep 25 18:23:46 2000
@@ -57,6 +57,8 @@
  *
  * 	v1.3b June 28, 2000	- Clean ups.
  *
+ * 	v1.3c Sep 7, 2000	- Minor typo fix.
+ *
  ********************************************************************************/
 
 
@@ -81,7 +83,7 @@
 static	int		aui = 0;
 static	int		duplex = 0;
 static	int		speed = 0;
-static const char *tlan_banner = "ThunderLAN driver v1.3b\n";
+static const char *tlan_banner = "ThunderLAN driver v1.3c\n";
 
 MODULE_AUTHOR("Maintainer: Torben Mathiasen <torben.mathiasen@compaq.com>");
 MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet adapters");
@@ -510,7 +512,7 @@
 	}
 	priv->debug =      dev->mem_end;
 
-	printk(KERN_INFO "TLAN 1.3b: %s irq=%2d io=%04x, %s, Rev. %d\n",
+	printk(KERN_INFO "TLAN 1.3c: %s irq=%2d io=%04x, %s, Rev. %d\n",
 		dev->name, 
 		(int) irq, 
 		io_base,
@@ -1121,7 +1123,7 @@
 u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
 {
 	host_int = 0;
-	printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); 
+	/* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */
 	return 0;
 
 } /* TLan_HandleInvalid */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/tulip.c linux.18p17/drivers/net/tulip.c
--- linux.vanilla/drivers/net/tulip.c	Fri Apr 21 12:46:21 2000
+++ linux.18p17/drivers/net/tulip.c	Sun Oct  1 21:52:05 2000
@@ -126,6 +126,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/init.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -320,7 +321,7 @@
 							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
 };
 #ifndef CARDBUS
-static struct pci_id_info pci_tbl[] = {
+static struct pci_id_info pci_tbl[] __initdata = {
   { "Digital DC21040 Tulip",
 	0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
   { "Digital DC21041 Tulip",
@@ -343,6 +344,8 @@
 	0x11AD, 0xc115, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
   { "ADMtek AN981 Comet",
 	0x1317, 0x0981, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "ADMtek AN985 Comet",
+	0x1317, 0x0981, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
   { "Compex RL100-TX",
 	0x11F6, 0x9881, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
   { "Intel 21145 Tulip",
@@ -571,7 +574,7 @@
 static struct device *root_tulip_dev = NULL;
 
 #ifndef CARDBUS
-int tulip_probe(struct device *dev)
+int __init tulip_probe(struct device *dev)
 {
 	int cards_found = 0;
 	int pci_index = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/via-rhine.c linux.18p17/drivers/net/via-rhine.c
--- linux.vanilla/drivers/net/via-rhine.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/net/via-rhine.c	Sun Oct  1 11:20:09 2000
@@ -77,6 +77,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/init.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -110,7 +111,7 @@
 #define RUN_AT(x) (jiffies + (x))
 
 #if (LINUX_VERSION_CODE >= 0x20100)
-char kernel_version[] = UTS_RELEASE;
+static char kernel_version[] = UTS_RELEASE;
 #else
 #ifndef __alpha__
 #define ioremap vremap
@@ -261,7 +262,7 @@
 								 struct device *dev, long ioaddr, int irq,
 								 int chp_idx, int fnd_cnt);
 
-static struct pci_id_info pci_tbl[] = {
+static struct pci_id_info pci_tbl[] __initdata = {
 	{ "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff,
 	  PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
 	{ "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff,
@@ -275,7 +276,7 @@
 struct chip_info {
 	int io_size;
 	int flags;
-} static cap_tbl[] = {
+} static cap_tbl[] __initdata = {
 	{128, CanHaveMII, },
 	{128, CanHaveMII, },
 };
@@ -398,7 +399,7 @@
    well when dynamically adding drivers.  So instead we detect just the
    cards we know about in slot order. */
 
-static int pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[])
+static int __init pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[])
 {
 	int cards_found = 0;
 	int pci_index = 0;
@@ -506,7 +507,7 @@
 }
 
 #ifndef MODULE
-int via_rhine_probe(struct device *dev)
+int __init via_rhine_probe(struct device *dev)
 {
 	static int did_version = 0;
 	if (!did_version++)
@@ -515,7 +516,7 @@
 }
 #endif
 
-static struct device *via_probe1(int pci_bus, int pci_devfn,
+static struct device * __init via_probe1(int pci_bus, int pci_devfn,
 								 struct device *dev, long ioaddr, int irq,
 								 int chip_id, int card_idx)
 {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/Makefile linux.18p17/drivers/net/xpds/Makefile
--- linux.vanilla/drivers/net/xpds/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/Makefile	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-encap-fr.c linux.18p17/drivers/net/xpds/xpds-encap-fr.c
--- linux.vanilla/drivers/net/xpds/xpds-encap-fr.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-encap-fr.c	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-encap-fr.h linux.18p17/drivers/net/xpds/xpds-encap-fr.h
--- linux.vanilla/drivers/net/xpds/xpds-encap-fr.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-encap-fr.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-fsm.c linux.18p17/drivers/net/xpds/xpds-fsm.c
--- linux.vanilla/drivers/net/xpds/xpds-fsm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-fsm.c	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-fsm.h linux.18p17/drivers/net/xpds/xpds-fsm.h
--- linux.vanilla/drivers/net/xpds/xpds-fsm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-fsm.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-reg.h linux.18p17/drivers/net/xpds/xpds-reg.h
--- linux.vanilla/drivers/net/xpds/xpds-reg.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-reg.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-sdsl.c linux.18p17/drivers/net/xpds/xpds-sdsl.c
--- linux.vanilla/drivers/net/xpds/xpds-sdsl.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-sdsl.c	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-sdsl.h linux.18p17/drivers/net/xpds/xpds-sdsl.h
--- linux.vanilla/drivers/net/xpds/xpds-sdsl.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-sdsl.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds-softnet.h linux.18p17/drivers/net/xpds/xpds-softnet.h
--- linux.vanilla/drivers/net/xpds/xpds-softnet.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds-softnet.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds.c linux.18p17/drivers/net/xpds/xpds.c
--- linux.vanilla/drivers/net/xpds/xpds.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds.c	Sun Oct 15 21:59:26 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/xpds/xpds.h linux.18p17/drivers/net/xpds/xpds.h
--- linux.vanilla/drivers/net/xpds/xpds.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/net/xpds/xpds.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/nubus/Makefile linux.18p17/drivers/nubus/Makefile
--- linux.vanilla/drivers/nubus/Makefile	Fri Apr 21 12:46:24 2000
+++ linux.18p17/drivers/nubus/Makefile	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/nubus/nubus.c linux.18p17/drivers/nubus/nubus.c
--- linux.vanilla/drivers/nubus/nubus.c	Fri Apr 21 12:46:24 2000
+++ linux.18p17/drivers/nubus/nubus.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/nubus/nubus_syms.c linux.18p17/drivers/nubus/nubus_syms.c
--- linux.vanilla/drivers/nubus/nubus_syms.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/nubus/nubus_syms.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/nubus/proc.c linux.18p17/drivers/nubus/proc.c
--- linux.vanilla/drivers/nubus/proc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/nubus/proc.c	Fri Oct 13 23:40:43 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/compat.c linux.18p17/drivers/pci/compat.c
--- linux.vanilla/drivers/pci/compat.c	Fri Apr 21 12:46:24 2000
+++ linux.18p17/drivers/pci/compat.c	Fri Sep  8 17:05:10 2000
@@ -41,3 +41,31 @@
 		}
 	return PCIBIOS_DEVICE_NOT_FOUND;
 }
+
+/* 2.4 compatibility */
+
+unsigned long pci_resource_len (struct pci_dev *dev, int n_base)
+{
+        u32 l, sz;
+        int reg = PCI_BASE_ADDRESS_0 + (n_base << 2);
+
+        /* XXX temporarily disable I/O and memory decoding for this device? */
+
+        pci_read_config_dword (dev, reg, &l);
+        if (l == 0xffffffff)
+                return 0;
+
+        pci_write_config_dword (dev, reg, ~0);
+        pci_read_config_dword (dev, reg, &sz);
+        pci_write_config_dword (dev, reg, l);
+
+        if (!sz || sz == 0xffffffff)
+                return 0;
+        if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+                sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
+        } else {
+                sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
+        }
+
+        return sz + 1;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/pcisyms.c linux.18p17/drivers/pci/pcisyms.c
--- linux.vanilla/drivers/pci/pcisyms.c	Fri Apr 21 12:46:24 2000
+++ linux.18p17/drivers/pci/pcisyms.c	Fri Sep  8 17:05:10 2000
@@ -40,6 +40,10 @@
 EXPORT_SYMBOL(pcibios_find_class);
 EXPORT_SYMBOL(pcibios_find_device);
 
+/* Forward compatibility */
+
+EXPORT_SYMBOL(pci_resource_len);
+
 /* Quirk info */
 
 #ifdef CONFIG_PCI_QUIRKS
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sbus/char/rtc.c linux.18p17/drivers/sbus/char/rtc.c
--- linux.vanilla/drivers/sbus/char/rtc.c	Sat Sep  9 18:42:40 2000
+++ linux.18p17/drivers/sbus/char/rtc.c	Sun Sep 10 14:49:10 2000
@@ -1,4 +1,4 @@
-/* $Id: rtc.c,v 1.13 1998/08/26 10:29:44 davem Exp $
+/* $Id: rtc.c,v 1.13.2.1 2000/08/18 23:40:04 davem Exp $
  *
  * Linux/SPARC Real Time Clock Driver
  * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ChangeLog.ips linux.18p17/drivers/scsi/ChangeLog.ips
--- linux.vanilla/drivers/scsi/ChangeLog.ips	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/scsi/ChangeLog.ips	Tue Sep  5 21:54:35 2000
@@ -1,7 +1,22 @@
 IBM ServeRAID driver Change Log
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        4.20.14  - Update patch files for kernel 2.4.0-test5
+
+        4.20.13  - Fix some failure cases / reset code
+                 - Hook into the reboot_notifier to flush the controller
+                   cache
+
+        4.20.03 - Rename version to coincide with new release schedules
+                - Performance fixes
+                - Fix truncation of /proc files with cat
+                - Merge in changes through kernel 2.4.0test1ac21
+
+        4.10.13 - Fix for dynamic unload and proc file system
+
+        4.10.00 - Add support for ServeRAID 4M/4L
+
         4.00.06 - Fix timeout with initial FFDC command
-	
+
         4.00.05 - Remove wish_block from init routine
                 - Use linux/spinlock.h instead of asm/spinlock.h for kernels
                   2.3.18 and later
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx linux.18p17/drivers/scsi/ChangeLog.ncr53c8xx
--- linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/ChangeLog.ncr53c8xx	Sat Sep  2 19:38:56 2000
@@ -1,12 +1,146 @@
-Wed Jul 21 1999 23:00 Gerard Roudier (groudier@club-internet.fr)
-	* revision 3.2a-2
-	- merge of driver 3.2a-1 with linux-2.3.11-pre3
-
-Sun May 9  15:00 1999 Gerard Roudier (groudier@club-internet.fr)
-	* revision 3.2a-1
-	- Fix the misdetection of SYM53C875E (was detected as a 876).
-	- Set the actual host ID used for each host in the scsi host data 
-	  structure. The mid-layer SCSI driver needs this information.
+Wed Jul 26 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version ncr53c8xx-3.4.1
+	- Provide OpenFirmare path through the proc FS on PPC.
+	- Remove trailing argument #2 from a couple of #undefs.
+
+Sun Jul 09 16:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version ncr53c8xx-3.4.0
+	- Remove the PROFILE C and SCRIPTS code.
+	  This facility was not this useful and thus was not longer 
+	  desirable given the increasing complexity of the driver code.
+	- Merges from FreeBSD sym-1.6.2 driver:
+	  * Clarify memory barriers needed by the driver for architectures 
+	    that implement a weak memory ordering.
+	- General cleanup:
+	  Move definitions for barriers and IO/MMIO operations to the 
+	  sym53c8xx_defs.h header files. They are now shared by the 
+	  both drivers.
+	  Use SCSI_NCR_IOMAPPED instead of NCR_IOMAPPED.
+
+Thu May 11   12:30 2000 Pam Delaney (pam.delaney@lsil.com)
+	* revision 3.3b
+ 
+Mon Apr 24 12:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2i
+	- Return value 1 (instead of 0) from the driver setup routine.
+	- Let the driver also attach controllers that have been set to 
+	  OFF in the NVRAM as it did prior to revision 3.2g.
+
+Sat Apr 1  12:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2h
+	- Fix a compilation problem on Alpha introduced in version 3.2g.
+          (`port' changed to `base_io').
+	- Move from `sym' to this driver a tiny change for __sparc__ that 
+	  applies to cache line size (? Probably from David S Miller).
+	- Make sure no data transfer will happen for Scsi_Cmnd requests 
+	  that supply SCSI_DATA_NONE direction (this avoids some BUG() 
+	  statement in the PCI code when a data buffer is also supplied).
+
+Thu Mar 16   9:30 2000 Pam Delaney (pam.delaney@lsil.com)
+	* revision 3.3b-3
+ 	- Added exclusion for the 53C1010 and 53C1010_66 chips
+	  to the driver (change to sym53c8xx_comm.h).
+
+Mon March 6  23:15 2000 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2g
+	- Add the file sym53c8xx_comm.h that collects code that should 
+	  be shared by sym53c8xx and ncr53c8xx drivers. For now, it is  
+	  a header file that is only included by the ncr53c8xx driver, 
+	  but things will be cleaned up later. This code addresses 
+	  notably:
+	  * Chip detection and PCI related initialisations
+	  * NVRAM detection and reading
+	  * DMA mapping
+	  * Boot setup command
+	  * And some other ...
+	- Add support for the new dynamic dma mapping kernel interface.
+	  Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+	- Get data transfer direction from the scsi command structure 
+	  (Scsi_Cmnd) when this information is available.
+
+Mon March 6  23:15 2000 Gerard Roudier (groudier@club-internet.fr)
+        * revision 3.2g
+        - Add the file sym53c8xx_comm.h that collects code that should
+          be shared by sym53c8xx and ncr53c8xx drivers. For now, it is
+          a header file that is only included by the ncr53c8xx driver,
+          but things will be cleaned up later. This code addresses
+          notably:
+          * Chip detection and PCI related initialisations
+          * NVRAM detection and reading
+          * DMA mapping
+          * Boot setup command
+          * And some other ...
+        - Add support for the new dynamic dma mapping kernel interface.
+          Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+        - Get data transfer direction from the scsi command structure
+          (Scsi_Cmnd) when this information is available.
+
+Fri Jan 14 14:00 2000 Pam Delaney (pam.delaney@lsil.com)
+	* revision pre-3.3b-1
+	- Merge parallel driver series 3.31 and 3.2e 
+
+Tue Jan 11 14:00 2000 Pam Delaney (pam.delaney@lsil.com)
+	* revision 3.31
+	- Added support for mounting disks on wide-narrow-wide
+	  scsi configurations.  
+	- Built off of version 3.30
+
+Mon Jan 10 13:30 2000 Pam Delaney (pam.delaney@lsil.com)
+	* revision 3.30
+	- Added capability to use the integrity checking code
+	  in the kernel (optional).
+	- Disabled support for the 53C1010.
+	- Built off of version 3.2c
+
+Sat Jan 8  22:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2e
+	- Add year 2000 copyright.
+	- Display correctly bus signals when bus is detected wrong.
+	- Remove the dead code that broke driver 3.2d.
+
+Mon Dec 6  22:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2d
+	- Change messages written by the driver at initialisation and 
+	  through the /proc FS (rather cosmetic changes that consist in
+	  printing out the PCI bus number and device/function).
+	- Get rid of the old PCI bios interface, but preserve kernel 2.0 
+	  compatibility from a simple wrapper.
+	- Remove the compilation condition about having to acquire the 
+	  io_request_lock since it seems to be a definite feature now.:)
+	- proc_dir structure no longer needed for kernel >= 2.3.27.
+	- Change the driver detection code by the sym53c8xx one, modulo 
+	  some minor changes. The driver can now attach any number of 
+	  controllers (>40) and does no longer hoger stack space at 
+	  initialisation.
+	- Definitely disable overlapped PCI arbitration for all dual 
+	  function chips, since I cannot make sure for what chip revisions 
+	  it is actually safe.
+	- Add support for the SYM53C1510D.
+	- Update the poor Tekram sync factor table.
+	- Remove the compilation condition about having to acquire the 
+	  io_request_lock since it seems to be a definite feature now.:)
+	- proc_dir structure no longer needed for kernel >= 2.3.27.
+
+Sat Sep 11  18:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.2c
+	- Handle correctly (hopefully) jiffies wrap-around.
+	- Restore the entry used to detect 875 until revision 0xff.
+	  (I removed it inadvertently, it seems :) )
+	- Replace __initfunc() which is deprecated stuff by __init which 
+	  is not yet so. ;-)
+	- Add support of some 'resource handling' for linux-2.3.13.
+	  Basically the BARs have been changed to something more complex 
+	  in the pci_dev structure.
+	- Remove some deprecated code.
+
+Sat May 10  11:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* revision pre-3.2b-1
+	- Support for the 53C895A by Pamela Delaney <pam.delaney@lsil.com>
+	  The 53C895A contains all of the features of the 896 but has only 
+	  one channel and has a 32 bit PCI bus. It does 64 bit PCI addressing 
+	  using dual cycle PCI data transfers.
+	- Miscellaneous minor fixes.
+	- Some additions to the README.ncr53c8xx file.
 
 Sun Apr 11  10:00 1999 Gerard Roudier (groudier@club-internet.fr)
 	* revision 3.2a
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ChangeLog.sym53c8xx linux.18p17/drivers/scsi/ChangeLog.sym53c8xx
--- linux.vanilla/drivers/scsi/ChangeLog.sym53c8xx	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/ChangeLog.sym53c8xx	Sat Sep  2 19:38:56 2000
@@ -1,23 +1,279 @@
-Sat Jul 24  12:00 1999 Gerard Roudier (groudier@club-internet.fr)
-	* version sym53c8xx-1.3g
-	- merge of driver 1.3f with linux-2.2.11-pre3
-	- remove the broken testing of the chip being connected to SCSI 
-	  from the SCSI interrupt handling code.   
+Wed Jul 26 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.7.1
+	- Provide OpenFirmare path through the proc FS on PPC.
+	- Download of on-chip SRAM using memcpy_toio() doesn't work 
+	  on PPC. Restore previous method (MEMORY MOVE from SCRIPTS).
+	- Remove trailing argument #2 from a couple of #undefs.
 
-Sun May 9  15:00 1999 Gerard Roudier (groudier@club-internet.fr)
-	* version sym53c8xx-1.3f
+Sun Jul 09 16:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.7.0
+	- Remove the PROFILE C and SCRIPTS code.
+	  This facility was not this useful and thus was not longer 
+	  desirable given the increasing complexity of the driver code.
+	- Merges from FreeBSD sym-1.6.2 driver:
+	  * Clarify memory barriers needed by the driver for architectures 
+	    that implement a weak memory ordering.
+	  * Simpler handling of illegal phases and data overrun from 
+	    SCRIPTS. These errors are now immediately reported to 
+	    the C code by an interrupt.
+	  * Sync the residual handling code with sym-1.6.2 and now 
+	    report `resid' to user for linux version >= 2.3.99 
+	- General cleanup:
+	  Move definitions for barriers and IO/MMIO operations to the 
+	  sym53c8xx_defs.h header files. They are now shared by the 
+	  both drivers.
+	  Remove unused options that claimed to optimize for the 896.
+	  If fact, they were not this clever. :)
+	  Use SCSI_NCR_IOMAPPED instead of NCR_IOMAPPED.
+	  Remove a couple of unused fields from data structures.
+
+Thu May 11 12:40 2000 Pam Delaney (pam.delaney@lsil.com)
+	* version sym53c8xx-1.6b
+	- Merged version.
+
+Mon Apr 24 12:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5m
+	- Return value 1 (instead of 0) from the driver setup routine.
+	- Donnot enable PCI DAC cycles. This just broke support for 
+	  SYM534C896 on sparc64. Problem fixed by David S. Miller.
+
+Fri Apr 14 9:00 2000 Pam Delaney (pam.delaney@lsil.com)
+	* version sym53c8xx-1.6b-9
+	- Added 53C1010_66 support.
+	- Small fix to integrity checking code.
+	- Removed requirement for integrity checking if want to run
+	  at ultra 3.
+ 
+Sat Apr 1  12:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5l
+	- Tiny change for __sparc__ appeared in 2.3.99-pre4.1 that 
+	  applies to cache line size (? Probably from David S Miller).
+	- Make sure no data transfer will happen for Scsi_Cmnd requests 
+	  that supply SCSI_DATA_NONE direction (this avoids some BUG() 
+	  statement in the PCI code when a data buffer is also supplied).
+
+Sat Mar 11 12:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.6b-5
+	- Test against expected data transfer direction from SCRIPTS.
+	- Add support for the new dynamic dma mapping kernel interface.
+	  Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+	  Many thanks to David S. Miller for his preliminary changes 
+	  that have been useful guidelines.
+	- Get data transfer direction from the scsi command structure 
+	  (Scsi_Cmnd) with kernels that provide this information.
+
+Mon Mar  6 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5k
+	- Test against expected data transfer direction from SCRIPTS.
+	- Revert the change in 'ncr_flush_done_cmds()' but unmap the 
+	  scsi dma buffer prior to queueing the command to our done 
+	  list.
+	- Miscellaneous (minor) fixes in the code added in driver 
+	  version 1.5j.
+
+Mon Feb 14 4:00 2000 Pam Delaney (pam.delaney@lsil.com)
+ 	* version sym53c8xx-pre-1.6b-2.
+	- Updated the SCRIPTS error handling of the SWIDE
+	  condition - to remove any reads of the sbdl
+	  register. Changes needed because the 896 and 1010
+	  chips will check parity in some special circumstances.
+	  This will cause a parity error interrupt if not in
+	  data phase.  Changes based on those made in the
+	  FreeBSD driver version 1.3.2.
+
+Sun Feb 20 11:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5j
+	- Add support for the new dynamic dma mapping kernel interface.
+	  Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+	  Many thanks to David S. Miller for his preliminary changes 
+	  that have been useful guidelines, for having reviewed the 
+	  code and having tested this driver version on Ultra-Sparc.
+	- 2 tiny bugs fixed in the PCI wrapper that provides support 
+	  for early kernels without pci device structure.
+	- Get data transfer direction from the scsi command structure 
+	  (Scsi_Cmnd) with kernels that provide this information.
+	- Fix an old bug that only affected 896 rev. 1 when driver 
+	  profile support option was set in kernel configuration.
+
+Fri Jan 14 14:00 2000 Pam Delaney (pam.delaney@lsil.com)
+ 	* version sym53c8xx-pre-1.6b-1.
+	- Merge parallel driver series 1.61 and 1.5e 
+
+Tue Jan 11 14:00 2000 Pam Delaney (pam.delaney@lsil.com)
+	* version sym53c8xx-1.61
+	- Added support for mounting disks on wide-narrow-wide
+	  scsi configurations. 
+	- Modified offset to be a maximum of 31 in ST mode, 
+	  62 in DT mode.
+	- Based off of 1.60
+
+Mon Jan 10 10:00 2000 Pam Delaney (pam.delaney@lsil.com)
+	* version sym53c8xx-1.60
+	- Added capability to use the integrity checking code
+	  in the kernel (optional).
+	-  Added PPR negotiation.
+	- Added support for 53C1010 Ultra 3 part.
+	- Based off of 1.5f
+
+Sat Jan 8  22:00 2000 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5h
+	- Add year 2000 copyright.
+	- Display correctly bus signals when bus is detected wrong.
+	- Some fix for Sparc from DSM that went directly to kernel tree.
+
+Mon Dec 6  22:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5g
+	- Change messages written by the driver at initialisation and 
+	  through the /proc FS (rather cosmetic changes that consist in
+	  printing out the PCI bus number and PCI device/function).
+	- Ensure the SCRIPTS processor is stopped while calibrating the 
+	  SCSI clock (the initialisation code has been a bit reworked).
+	  Change moved to the FreeBSD sym_hipd driver).
+	- Some fixes in the MODIFY_DP/IGN_RESIDUE code and residual 
+	  calculation (moved from FreeBSD sym_hipd driver).
+	- Add NVRAM support for Tekram boards that use 24C16 EEPROM.
+	  Code moved from the FreeBSD sym_hipd driver, since it has 
+	  been that one that got this feature first.
+	- Definitely disable overlapped PCI arbitration for all dual 
+	  function chips, since I cannot make sure for what chip revisions 
+	  it is actually safe.
+	- Add support for the SYM53C1510D (also for ncr53c8xx).
+	- Fix up properly the PCI latency timer when needed or asked for.
+	- Get rid of the old PCI bios interface, but preserve kernel 2.0 
+	  compatibility from a simple wrapper.
+	- Update the poor Tekram sync factor table.
+	- Fix in a tiny 'printk' bug that may oops in case of extended 
+	  errors (unrecovered parity error, data overrun, etc ...)
+	  (Sent by Pamela Delaney from LSILOGIC)
+	- Remove the compilation condition about having to acquire the 
+	  io_request_lock since it seems to be a definite feature now.:)
+	- Change get_pages by GetPages since Linux >= 2.3.27 now wants 
+	  get_pages to ever be used as a kernel symbol (from 2.3.27).
+	- proc_dir structure no longer needed for kernel >= 2.3.27.
+
+Sun Oct  3  19:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5f
+	- Change the way the driver checks the PCI clock frequency, so 
+	  that overclocked PCI BUS up to 48 MHz will not be refused.
+	  The more the BUS is overclocked, the less the driver will 
+	  guarantee that its measure of the SCSI clock is correct.
+	- Backport some minor improvements of SCRIPTS from the sym_hipd 
+	  driver.
+	- Backport the code rewrite of the START QUEUE dequeuing (on 
+	  bad scsi status received) from the sym_hipd driver.
+
+Sat Sep 11  11:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5e
+	- New linux-2.3.13 __setup scheme support added.
+	- Cleanup of the extended error status handling:
+	  Use 1 bit per error type.
+	- Also save the extended error status prior to auto-sense.
+	- Add the FE_DIFF chip feature bit to indicate support of 
+	  diff probing from GPIO3 (825/825A/876/875).
+	- Remove the quirk handling that has been useless since day one.
+	- Work-around PCI chips being reported twice on some platforms.
+	- Add some redundant PCI reads in order to deal with common 
+	  bridge misbehaviour regarding posted write flushing.
+	- Add some other conditionnal code for people who have to deal 
+	  with really broken bridges (they will have to edit a source 
+	  file to try these options).
+	- Handle correctly (hopefully) jiffies wrap-around.
+	- Restore the entry used to detect 875 until revision 0xff.
+	  (I removed it inadvertently, it seems :) )
+	- Replace __initfunc() which is deprecated stuff by __init which 
+	  is not yet so. ;-)
+	- Rewrite the MESSAGE IN scripts more generic by using a MOVE 
+	  table indirect. Extended messages of any size are accepted now.
+	  (Size is limited to 8 for now, but a constant is just to be 
+	  increased if necessary)
+	- Fix some bug in the fully untested MDP handling:) and share 
+	  some code between MDP handling and residual calculation.
+	- Calculate the data transfer residual as the 2's complement 
+	  integer (A positive value in returned on data overrun, and 
+	  a negative one on underrun).
+	- Add support of some 'resource handling' for linux-2.3.13.
+	  Basically the BARs have been changed to something more complex 
+	  in the pci_dev structure.
+	- Remove some deprecated code.
+
+Sat Jun  5  11:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5c
+	- Donnot negotiate on auto-sense if we are currently using 8 bit 
+	  async transfer for the target.
+	- Only check for SISL/RAID on i386 platforms.
+	  (A problem has been reported on PPC with that code).
+	- On MSG REJECT for a negotiation, the driver attempted to restart 
+	  the SCRIPT processor when this one was already running.
+
+Sat May 29  12:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5b
+	- Force negotiation prior auto-sense.
+	  This ensures that the driver will be able to grab the sense data 
+	  from a device that has received a BUS DEVICE RESET message from 
+	  another initiator.
+	- Complete all disconnected CCBs for a logical UNIT if we are told 
+	  about a UNIT ATTENTION for a RESET condition by this target.
+	- Add the control command 'cleardev' that allows to send a ABORT  
+	  message to a logical UNIT (for test purpose).
+
+Tue May 25  23:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5a
+	- Add support for task abort and bus device reset SCSI message 
+	  and implement proper synchonisation with SCRIPTS to handle 
+          correctly task abortion without races.
+	- Send an ABORT message (if untagged) or ABORT TAG message (if tagged)
+	  when the driver is told to abort a command that is disconnected and 
+	  complete the command with appropriate error.
+	  If the aborted command is not yet started, remove it from the start 
+	  queue and complete it with error.
+	- Add the control command 'resetdev' that allows to send a BUS 
+	  DEVICE RESET message to a target (for test purpose).
+	- Clean-up some unused or useless code.
+
+Fri May 21  23:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.5
+	- Add support for CHMOV with Wide controllers.
+	- Handling of the SWIDE (low byte residue at the end of a CHMOV 
+	  in DATA IN phase with WIDE transfer when the byte count gets odd).
+	- Handling of the IGNORE WIDE RESIDUE message.
+	  Handled from SCRIPTS as possible with some optimizations when both 
+	  a wide device and the controller are odd at the same time (SWIDE 
+	  present and IGNORE WIDE RESIDUE message on the BUS at the same time).
+	- Check against data OVERRUN/UNDERRUN condition at the end of a data 
+	  transfer, whatever a SWIDE is present (OVERRUN in DATA IN phase) 
+	  or the SODL is full (UNDERRUN in DATA out phase).
+	- Handling of the MODIFY DATA POINTER message.
+	  This one cannot be handled from SCRIPTS, but hopefully it will not
+	  happen very often. :)
+	- Large rewrite of the SCSI MESSAGE handling.
+
+Sun May 9  11:00 1999 Gerard Roudier (groudier@club-internet.fr)
+	* version sym53c8xx-1.4
+	- Support for IMMEDIATE ARBITRATION.
+	  See the README file for detailed information about this feature.
+	  Requires both a compile option and a boot option.
+	- Minor SCRIPTS optimization in reselection pattern for LUN 0.
+	- Simpler algorithm to deal with SCSI command starvation.
+	  Just use 2 tag counters in flip/flop and switch to the other 
+	  one every 3 seconds.
+	- Do some work in SCRIPTS after the SELECT instruction and prior 
+	  to testing for a PHASE. SYMBIOS say this feature is working fine. 
+	  (Btw, only problems with Toshiba 3401B had been reported).
+	- Measure the PCI clock speed and donnot attach controllers if 
+	  result is greater than 37 MHz. Since the precision of the 
+	  algorithm (from Stefan Esser) is better than 2%, this should 
+	  be fine.
 	- Fix the misdetection of SYM53C875E (was detected as a 876).
 	- Fix the misdetection of SYM53C810 not A (was detected as a 810A).
+	- Support for up to 256 TAGS per LUN (CMD_PER_LUN).
+	  Currently limited to 255 due to Linux limitation. :)
+	- Support for up to 508 active commands (CAN_QUEUE).
 	- Support for the 53C895A by Pamela Delaney <pam.delaney@lsil.com>
 	  The 53C895A contains all of the features of the 896 but has only 
 	  one channel and has a 32 bit PCI bus. It does 64 bit PCI addressing 
 	  using dual cycle PCI data transfers.
-	- Call request_region() event if MMIO is used and not normal IO.
-	  This allows sym and the ncr driver to be loaded in any order 
-	  without any risk of attaching the same device.
-	- Set the actual host ID used for each host in the scsi host data 
-	  structure. The mid-layer SCSI driver needs this information.
 	- Miscellaneous minor fixes.
+	- Some additions to the README.ncr53c8xx file.
 
 Tue Apr 15  10:00 1999 Gerard Roudier (groudier@club-internet.fr)
 	* version sym53c8xx-1.3e
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Config.in linux.18p17/drivers/scsi/Config.in
--- linux.vanilla/drivers/scsi/Config.in	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/Config.in	Fri Sep 15 22:44:39 2000
@@ -43,6 +43,9 @@
 if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
     bool '  Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
 fi
+if [ "$CONFIG_PCI" = "y" ]; then
+  dep_tristate 'Compaq Fibre Channel 64-bit/66Mhz HBA support' CONFIG_SCSI_CPQFCTS $CONFIG_SCSI
+fi
 dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
   if [ "$CONFIG_SCSI_EATA" != "n" ]; then
@@ -94,7 +97,6 @@
     int  '  default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8
     int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32
     int  '  synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 20
-    bool '  enable profiling' CONFIG_SCSI_NCR53C8XX_PROFILE
     bool '  use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
     if [ "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
       bool '  include support for the NCR PQS/PDS SCSI card' CONFIG_SCSI_NCR53C8XX_PQS_PDS
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Makefile linux.18p17/drivers/scsi/Makefile
--- linux.vanilla/drivers/scsi/Makefile	Fri Apr 21 12:46:30 2000
+++ linux.18p17/drivers/scsi/Makefile	Fri Sep 15 22:44:39 2000
@@ -5,6 +5,10 @@
 # unless it's something special (ie not a .c file).
 #
 
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
 L_TARGET := scsi.a
 L_OBJS   := 
 M_OBJS   := 
@@ -273,6 +277,7 @@
   endif
 endif
 
+
 ifeq ($(CONFIG_SCSI_QLOGIC_FAS),y)
 L_OBJS += qlogicfas.o
 else
@@ -322,6 +327,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_CPQFCTS),y)
+L_OBJS += cpqfc.o 
+else
+  ifeq ($(CONFIG_SCSI_CPQFCTS),m)   
+  M_OBJS += cpqfc.o 
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_AHA152X),y)
 L_OBJS += aha152x.o
 else
@@ -682,6 +695,28 @@
 
 sim710.o : sim710_d.h
 
+# if we have a triggerable HBA (extra circuit addition for
+# TTL level output on GPIO line), build in the trigger file
+
+#CPQTRIGGER_FILE = cpqfcTStrigger.c
+
+CPQTRIG = cpqfcTStrigger.o
+
+cpqfcTStrigger.o: cpqfcTStrigger.c
+
+cpqfcTSinit.o: cpqfcTSinit.c cpqfcTSstructs.h cpqfcTSchip.h cpqfcTSioctl.h
+
+cpqfcTScontrol.o: cpqfcTScontrol.c cpqfcTSstructs.h cpqfcTSchip.h 
+
+cpqfcTSi2c.o: cpqfcTSi2c.c cpqfcTSchip.h 
+
+cpqfcTSworker.o: cpqfcTSworker.c cpqfcTSchip.h cpqfcTSstructs.h cpqfcTSioctl.h
+
+cpqfc.o: cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o cpqfcTSworker.o $(CPQTRIG)
+	$(LD) -r -o cpqfc.o cpqfcTSinit.o cpqfcTScontrol.o \
+	cpqfcTSi2c.o cpqfcTSworker.o $(CPQTRIG)
+
+
 initio.o: ini9100u.c i91uscsi.c
 	$(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o
 	$(CC) $(CFLAGS) -c i91uscsi.c -o i91uscsi.o
@@ -705,3 +740,4 @@
 
 sd_mod.o: sd.o sd_ioctl.o
 	$(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.ibmmca linux.18p17/drivers/scsi/README.ibmmca
--- linux.vanilla/drivers/scsi/README.ibmmca	Fri Apr 21 12:46:31 2000
+++ linux.18p17/drivers/scsi/README.ibmmca	Sat Jul 29 18:07:34 2000
@@ -10,10 +10,10 @@
   General Public License. Originally written by Martin Kolinek, December 1995.
          Officially maintained by Michael Lang since January 1999.
 	   
- 	                       Version 3.1e
+ 	                       Version 3.2
 	
 	
-   Last update: 20 February 1999
+   Last update: 29 July 2000
 
 
    Authors of this Driver
@@ -23,7 +23,8 @@
     - Klaus Kudielka (multiple SCSI-host management/detection, adaption to
                       Linux Kernel 2.1.x, module support)
     - Michael Lang (assigning original pun,lun mapping, dynamical ldn 
-                    assignment, this file, patch, official driver maintenance)
+                    assignment, this file, patch, official driver maintenance
+		    and subsequent pains related with the driver :-))
 
    Table of Contents
    -----------------
@@ -51,7 +52,11 @@
      5.3 Bugreports
      5.4 Support WWW-page
    6 References
-   7 Trademarks
+   7 Credits to
+     7.1 People
+     7.2 Sponsors & Supporters
+   8 Trademarks
+   9 Disclaimer
 
                               * * *
 
@@ -64,7 +69,7 @@
    quite outdated. The history of the driver development is also kept inside 
    here. Multiple historical developments have been summarized to shorten the 
    textsize a bit. At the end of this file you can find a small manual for 
-   this driver and hints to get it running even on your machine (hopefully).
+   this driver and hints to get it running on your machine.
 
    2 Driver Description
    --------------------
@@ -74,27 +79,35 @@
    Microchannel-bus support is enabled, as the IBM SCSI-subsystem needs the
    Microchannel. In a next step, a free interrupt is chosen and the main
    interrupt handler is connected to it to handle answers of the SCSI-
-   subsystem(s). In a further step, it is checked, wether there was a forced
-   detection of the adapter via the kernel commandline, where the I/O port
-   and the SCSI-subsystem id can be specified. The next step checks if there
-   is an integrated SCSI-subsystem installed. This register area is fixed 
-   through all IBM PS/2 MCA-machines and appears as something like a virtual 
-   slot 10 of the MCA-bus. If POS-register 2 is not 0xff, there must be a SCSI-
+   subsystem(s). If the F/W SCSI-adapter is forced by the BIOS to use IRQ11
+   instead of IRQ14, IRQ11 is used for the IBM SCSI-2 F/W adapter. In a 
+   further step it is checked, if the adapter gets detected by force from
+   the kernel commandline, where the I/O port and the SCSI-subsystem id can 
+   be specified. The next step checks if there is an integrated SCSI-subsystem
+   installed. This register area is fixed through all IBM PS/2 MCA-machines 
+   and appears as something like a virtual slot 10 of the MCA-bus. On most
+   PS/2 machines, the POS registers of slot 10 are set to 0xff or 0x00 if not
+   integrated SCSI-controller is available. But on certain PS/2s, like model 
+   9595, this slot 10 is used to store other information which at earlier
+   stage confused the driver and resulted in the detection of some ghost-SCSI. 
+   If POS-register 2 and 3 are not 0x00 and not 0xff, but all other POS
+   registers are either 0xff or 0x00, there must be an integrated SCSI-
    subsystem present and it will be registered as IBM Integrated SCSI-
    Subsystem. The next step checks, if there is a slot-adapter installed on 
    the MCA-bus. To get this, the first two POS-registers, that represent the 
    adapter ID are checked. If they fit to one of the ids, stored in the 
-   adapter list, a SCSI-subsystem is assumed to be found and will be 
+   adapter list, a SCSI-subsystem is assumed to be found in a slot and will be 
    registered. This check is done through all possible MCA-bus slots to allow 
    more than one SCSI-adapter to be present in the PS/2-system and this is 
    already the first point of problems. Looking into the technical reference 
    manual for the IBM PS/2 common interfaces, the POS2 register must have 
-   different interpretation of its single bits. While one can assume, that the
-   integrated subsystem has a fix I/O-address at 0x3540 - 0x3547, further
-   installed IBM SCSI-adapters must use a different I/O-address. This is
-   expressed by bit 1 to 3 of POS2 (multiplied by 8 + 0x3540). Bits 2 and 3 
-   are reserved for the integrated subsystem, but not for the adapters! The
-   following list shows, how the bits of POS2 and POS3 should be interpreted.
+   different interpretation of its single bits to avoid overlapping I/O
+   regions. While one can assume, that the integrated subsystem has a fix 
+   I/O-address at 0x3540 - 0x3547, further installed IBM SCSI-adapters must 
+   use a different I/O-address. This is expressed by bit 1 to 3 of POS2 
+   (multiplied by 8 + 0x3540). Bits 2 and 3 are reserved for the integrated 
+   subsystem, but not for the adapters! The following list shows, how the 
+   bits of POS2 and POS3 should be interpreted.
    
    The POS2-register of all PS/2 models' integrated SCSI-subsystems has the 
    following interpretation of bits:
@@ -102,20 +115,25 @@
                            Bit 3 - 2 : Reserved
                            Bit 1     : 8k NVRAM Disabled
                            Bit 0     : Chip Enable (EN-Signal)
-   The POS3-register is interpreted as follows (for ALL IBM SCSI-subsys.):
+   The POS3-register is interpreted as follows (for most IBM SCSI-subsys.):
                            Bit 7 - 5 : SCSI ID
                            Bit 4 - 0 : Reserved = 0
-   (taken from "IBM, PS/2 Hardware Interface Technical Reference, Common
-   Interfaces (1991)"). 
-   In short words, this means, that IBM PS/2 machines only support 1 single 
-   subsystem by default. But (additional) slot-adapters must have another 
-   configuration on pos2 in order to be enabled to use more than one IBM SCSI-
-   subsystem, e.g. for a network server. From tests with the IBM SCSI Adapter 
-   w/cache, the POS2-register for slot adapters should be interpreted in the 
-   following way:
-                           Bit 7 - 4 : Chip Revision ID (Release)
-                           Bit 3 - 1 : port offset factor ( * 8 + 0x3540 )
-                           Bit 0     : Chip Enable (EN-Signal)
+   The slot-adapters have different interpretation of these bits. The IBM SCSI
+   adapter (w/Cache) and the IBM SCSI-2 F/W adapter use the following
+   interpretation of the POS2 register:
+                           Bit 7 - 4 : ROM Segment Address Select
+			   Bit 3 - 1 : Adapter I/O Address Select (*8+0x3540)
+			   Bit 0     : Adapter Enable (EN-Signal)
+   and for the POS3 register:
+                           Bit 7 - 5 : SCSI ID 
+			   Bit 4     : Fairness Enable (SCSI ID3 f. F/W)
+			   Bit 3 - 0 : Arbitration Level
+   The most modern product of the series is the IBM SCSI-2 F/W adapter, it 
+   allows dual-bus SCSI and SCSI-wide addressing, which means, PUNs may be
+   between 0 and 15. Here, Bit 4 is the high-order bit of the 4-bit wide
+   adapter PUN expression. In short words, this means, that IBM PS/2 machines 
+   can only support 1 single integrated subsystem by default. Additional
+   slot-adapters get ports assigned by the automatic configuration tool.
 
    One day I found a patch in ibmmca_detect(), forcing the I/O-address to be 
    0x3540 for integrated SCSI-subsystems, there was a remark placed, that on 
@@ -156,12 +174,18 @@
    number or pun, also called the scsi id, this is the number you select
    with hardware jumpers), and each physical unit can have up to 8 
    "logical units" (each identified by logical unit number, or lun, 
-   between 0 and 7). 
+   between 0 and 7). The IBM SCSI-2 F/W adapter offers this on up to two
+   busses and provides support for 30 logical devices at the same time, where
+   in wide-addressing mode you can have 16 puns with 32 luns on each device.
+   This section dexribes you the handling of devices on non-F/W adapters.
+   Just imagine, that you can have 16 * 32 = 512 devices on a F/W adapter
+   which means a lot of possible devices for such a small machine.
 
    Typically the adapter has pun=7, so puns of other physical units
-   are between 0 and 6. Almost all physical units have only one   
-   logical unit, with lun=0. A CD-ROM jukebox would be an example of 
-   a physical unit with more than one logical unit.
+   are between 0 and 6(15). On a wide-adapter a pun higher than 7 is
+   possible, but is normally not used. Almost all physical units have only 
+   one logical unit, with lun=0. A CD-ROM jukebox would be an example of a 
+   physical unit with more than one logical unit.
 
    The embedded microprocessor of the IBM SCSI-subsystem hides the complex
    two-dimensional (pun,lun) organization from the operating system.
@@ -169,7 +193,8 @@
    checks, on its own, all 56 possible (pun,lun) combinations, and the first 
    15 devices found are assigned into a one-dimensional array of so-called 
    "logical devices", identified by "logical device numbers" or ldn. The last 
-   ldn=15 is reserved for the subsystem itself.
+   ldn=15 is reserved for the subsystem itself. Wide adapters may have 
+   to check up to 15 * 8 = 120 pun/lun combinations.
    
    2.3 SCSI-Device Recognition and dynamical ldn Assignment
    --------------------------------------------------------
@@ -177,8 +202,12 @@
    numbers are also hidden. The two possibilities to get around this problem
    is to offer fake pun/lun combinations to the operating system or to 
    delete the whole mapping of the adapter and to reassign the ldns, using
-   the immediate assign command of the SCSI-subsystem. At the beginning of the
-   development of this driver, the following approach was used:
+   the immediate assign command of the SCSI-subsystem for probing through
+   all possible pun/lun combinations. a ldn is a "logical device number"
+   which is used by IBM SCSI-subsystems to access some valid SCSI-device.
+   At the beginning of the development of this driver, the following approach 
+   was used:
+   
    First, the driver checked the ldn's (0 to 6) to find out which ldn's
    have devices assigned. This was done by the functions check_devices() and
    device_exists(). The interrupt handler has a special paragraph of code
@@ -199,7 +228,7 @@
    and later, realizes the device recognition in the following way:
    The physical SCSI-devices on the SCSI-bus are probed via immediate_assign- 
    and device_inquiry-commands, that is all implemented in a completely new
-   made check_devices() subroutine. This delivers a exact map of the physical
+   made check_devices() subroutine. This delivers an exact map of the physical
    SCSI-world that is now stored in the get_scsi[][]-array. This means,
    that the once hidden pun,lun assignment is now known to this driver.
    It no longer believes in default-settings of the subsystem and maps all
@@ -230,7 +259,7 @@
    can be different from the old, faked puns. Therefore, Linux will eventually
    change /dev/sdXXX assignments and prompt you for corrupted superblock
    repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!!
-   You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file
+   You have to reboot (CTRL-D) with an old kernel and set the /etc/fstab-file
    entries right. After that, the system should come up as errorfree as before.
    If your boot-partition is not coming up, also edit the /etc/lilo.conf-file
    in a Linux session booted on old kernel and run lilo before reboot. Check
@@ -332,17 +361,19 @@
    ----------------------------------
    The following IBM SCSI-subsystems are supported by this driver:
    
-     - IBM Fast SCSI-2 Adapter
+     - IBM Fast/Wide SCSI-2 Adapter
      - IBM 7568 Industrial Computer SCSI Adapter w/cache
      - IBM Expansion Unit SCSI Controller
      - IBM SCSI Adapter w/Cache
      - IBM SCSI Adapter
      - IBM Integrated SCSI Controller
+     - All clones, 100% compatible with the chipset and subsystem command
+       system of IBM SCSI-adapters (forced detection)
      
    2.14 Linux Kernel Versions
    --------------------------
    The IBM SCSI-subsystem low level driver is prepared to be used with
-   all versions of Linux between 2.0.x and 2.2.x. The compatibility checks
+   all versions of Linux between 2.0.x and 2.4.x. The compatibility checks
    are fully implemented up from version 3.1e of the driver. This means, that
    you just need the latest ibmmca.h and ibmmca.c file and copy it in the
    linux/drivers/scsi directory. The code is automatically adapted during 
@@ -717,18 +748,149 @@
       addition more flexibility.
    - Michael Lang
 
+   Apr 23, 2000 (v3.2pre1)
+   1) During a very long time, I collected a huge amount of bugreports from
+      various people, trying really quite different things on their SCSI-
+      PS/2s. Today, all these bugreports are taken into account and should be
+      mostly solved. The major topics were:
+      - Driver crashes during boottime by no obvious reason.
+      - Driver panics while the midlevel-SCSI-driver is trying to inquire
+        the SCSI-device properties, even though hardware is in perfect state.
+      - Displayed info for the various slot-cards is interpreted wrong.
+      The main reasons for the crashes were two:
+      1) The commands to check for device information like INQUIRY, 
+         TEST_UNIT_READY, REQUEST_SENSE and MODE_SENSE cause the devices
+	 to deliver information of up to 255 bytes. Midlevel drivers offer
+	 1024 bytes of space for the answer, but the IBM-SCSI-adapters do
+	 not accept this, as they stick quite near to ANSI-SCSI and report
+	 a COMMAND_ERROR message which causes the driver to panic. The main
+	 problem was located around the INQUIRY command. Now, for all the
+	 mentioned commands, the buffersize, sent to the adapter is at 
+	 maximum 255 which seems to be a quite reasonable solution. 
+	 TEST_UNIT_READY gets a buffersize of 0 to make sure, that no 
+	 data is transferred in order to avoid any possible command failure.
+      2) On unsuccessful TEST_UNIT_READY, the midlevel-driver has to send
+         a REQUEST_SENSE in order to see, where the problem is located. This
+	 REQUEST_SENSE may have various length in its answer-buffer. IBM
+	 SCSI-subsystems report a command failure, if the returned buffersize
+	 is different from the sent buffersize, but this can be supressed by
+	 a special bit, which is now done and problems seem to be solved.
+   2) Code adaption to all kernel-releases. Now, the 3.2 code compiles on 
+      2.0.x, 2.1.x, 2.2.x and 2.3.x kernel releases without any code-changes.
+   3) Commandline-parameters are recognized again, even under Kernel 2.3.x or
+      higher.
+   - Michael Lang   
+
+   April 27, 2000 (v3.2pre2)
+   1) Bypassed commands get read by the adapter by one cycle instead of two.
+      This increases SCSI-performance.
+   2) Synchronous datatransfer is provided for sure to be 5 MHz on older
+      SCSI and 10 MHz on internal F/W SCSI-adapter.
+   3) New commandline parameters allow to force the adapter to slow down while
+      in synchronous transfer. Could be helpful for very old devices.
+   - Michael Lang
+   
+   June 2, 2000 (v3.2pre5)
+   1) Added Jim Shorney's contribution to make the activity indicator
+      flashing in addition to the LED-alphanumeric display-panel on
+      models 95A. To be enabled to choose this feature freely, a new
+      commandline parameter is added, called 'activity'.
+   2) Added the READ_CONTROL bit for test_unit_ready SCSI-command.
+   3) Added some suppress_exception bits to read_device_capacity and
+      all device_inquiry occurences in the driver code.
+   4) Complaints about the various KERNEL_VERSION implementations are
+      taken into account. Every local_LinuxKernelVersion occurence is
+      now replaced by KERNEL_VERSION, defined in linux/version.h. 
+      Corresponding changes were applied to ibmmca.h, too. This was a
+      contribution to all kernel-parts by Philipp Hahn.
+   - Michael Lang
+   
+   July 17, 2000 (v3.2pre8)
+   A long period of collecting bugreports from all corners of the world
+   now lead to the following corrections to the code:
+   1) SCSI-2 F/W support crashed with a COMMAND ERROR. The reason for this 
+      was, that it is possible to disbale Fast-SCSI for the external bus.
+      The feature-control command, where this crash appeared regularly tried
+      to set the maximum speed of 10MHz synchronous transfer speed and that
+      reports a COMMAND ERROR, if external bus Fast-SCSI is disabled. Now,
+      the feature-command probes down from maximum speed until the adapter 
+      stops to complain, which is at the same time the maximum possible
+      speed selected in the reference program. So, F/W external can run at
+      5 MHz (slow-) or 10 MHz (fast-SCSI). During feature probing, the 
+      COMMAND ERROR message is used to detect if the adapter does not complain.
+   2) Up to now, only combined busmode is supported, if you use external
+      SCSI-devices, attached to the F/W-controller. If dual bus is selected,
+      only the internal SCSI-devices get accessed by Linux. For most 
+      applications, this should do fine. 
+   3) Wide-SCSI-addressing (16-Bit) is now possible for the internal F/W
+      bus on the F/W adapter. If F/W adapter is detected, the driver
+      automatically uses the extended PUN/LUN <-> LDN mapping tables, which
+      are now new from 3.2pre8. This allows PUNs between 0 and 15 and should
+      provide more fun with the F/W adapter.
+   4) Several machines use the SCSI: POS registers for internal/undocumented
+      storage of system relevant info. This confused the driver, mainly on
+      models 9595, as it expected no onboard SCSI only, if all POS in
+      the integrated SCSI-area are set to 0x00 or 0xff. Now, the mechanism
+      to check for integrated SCSI is much more restrictive and these problems
+      should be history.
+   - Michael Lang          
+
+   July 18, 2000 (v3.2pre9)
+   This develop rather quickly at the moment. Two major things were still
+   missing in 3.2pre8:
+   1) The adapter PUN for F/W adapters has 4-bits, while all other adapters
+      have 3-bits. This is now taken into account for F/W.
+   2) When you select CONFIG_IBMMCA_SCSI_ORDER_STANDARD, you should 
+      normally get the inverse probing order of your devices on the SCSI-bus.
+      The ANSI device order gets scrambled in version 3.2pre8!! Now, a new
+      and tested algorithm inverts the device-order on the SCSI-bus and
+      automatically avoids accidental access to whatever SCSI PUN the adapter 
+      is set and works with SCSI- and Wide-SCSI-addressing.
+   - Michael Lang
+
+   July 23, 2000 (v3.2pre10 unpublished) 
+   1) LED panel display supports wide-addressing in ibmmca=display mode.
+   2) Adapter-information and autoadaption to address-space is done.
+   3) Auto-probing for maximum synchronous SCSI transfer rate is working.
+   4) Optimization to some embedded function calls is applied.
+   5) Added some comment for the user to wait for SCSI-devices beeing probed.
+   6) Finished version 3.2 for Kernel 2.4.0. It least, I thought it is but...
+   - Michael Lang
+   
+   July 26, 2000 (v3.2pre11)
+   1) I passed a horrible weekend getting mad with NMIs on kernel 2.2.14 and
+      a model 9595. Asking around in the community, nobody except of me has
+      seen such errors. Weired, but I am trying to recompile everything on
+      the model 9595. Maybe, as I use a specially modified gcc, that could
+      cause problems. But, it was not the reason. The true background was,
+      that the kernel was compiled for i386 and the 9595 has a 486DX-2. 
+      Normally, no troubles should appear, but for this special machine,
+      only the right processor support is working fine!
+   2) Previous problems with synchronous speed, slowing down from one adapter 
+      to the next during probing are corrected. Now, local variables store
+      the synchronous bitmask for every single adapter found on the MCA bus.
+   3) LED alphanumeric panel support for XX95 systems is now showing some
+      alive rotator during boottime. This makes sense, when no monitor is 
+      connected to the system. You can get rid of all display activity, if
+      you do not use any parameter or just ibmmcascsi=activity, for the 
+      harddrive activity LED, existant on all PS/2, except models 8595-XXX.
+      If no monitor is available, please use ibmmcascsi=display, which works
+      fine together with the linuxinfo utility for the LED-panel.
+   - Michael Lang
+   
+   July 29, 2000 (v3.2)
+   1) Submission of this driver for kernel 2.4test-XX and 2.2.17.
+   - Michael Lang
+   
    4 To do
    -------
+        - IBM SCSI-2 F/W external SCSI bus support in seperate mode.
 	- It seems that the handling of bad disks is really bad -
 	  non-existent, in fact.
         - More testing of the full driver-controlled dynamical ldn 
-          (re)mapping for up to 56 SCSI-devices.
-        - Support more of the SCSI-command set.
-	- Support some of the caching abilities, particularly Read Prefetch.
-	  This fetches data into the cache, which later gets hit by the
-	  regular Read Data. (<--- This is coming soon!!!!)
-        - Abort and Reset functions still slightly buggy or better say,
-	  it is the new episode, called SCREAM III.
+          (re)mapping for up to 56 SCSI-devices. I guess, it won't work
+	  at the moment, but nobody ever really tried it.
+        - Abort and Reset functions still slightly buggy.
 
    5 Users' Manual
    ---------------
@@ -749,7 +911,13 @@
 				
 		    where '-' stays dark, 'D' shows the SCSI-device id
 		    and 'A' shows the SCSI hostindex, beeing currently 
-		    accessed.
+		    accessed. During boottime, this will give the message
+		    
+		                SCSIini*
+				
+                    on the LED-panel, where the * represents a rotator, 
+		    showing the activity during the probing phase of the
+		    driver which can take up to two minutes per SCSI-adapter.
 	 adisplay   This works like display, but gives more optical overview 
 	            of the activities on the SCSI-bus. The display will have
 		    the following output:
@@ -761,7 +929,22 @@
 		    hostindex. If display nor adisplay is set, the internal
 		    PS/2 harddisk LED is used for media-activities. So, if
 		    you really do not have a system with a LED-display, you
-		    should not set display or adisplay.
+		    should not set display or adisplay. Keep in mind, that
+		    display and adisplay can only be used alternatively. It
+		    is not recommended to use this option, if you have some
+		    wide-addressed devices e.g. at the SCSI-2 F/W adapter in
+		    your system. In addition, the usage of the display for
+		    other tasks in parallel, like the linuxinfo-utility makes 
+		    no sense with this option.
+	 activity   This enables the PS/2 harddisk LED activity indicator.
+	            Most PS/2 have no alphanumeric LED display, but some
+		    indicator. So you should use this parameter to activate it.
+		    If you own model 9595 (Server95), you can have both, the 
+		    LED panel and the activity indicator in parallel. However,
+		    some PS/2s, like the 8595 do not have any harddisk LED 
+		    activity indicator, which means, that you must use the
+		    alphanumeric LED display if you want to monitor SCSI-
+		    activity.
 	 bypass     This commandline parameter forces the driver never to use
 	            SCSI-subsystems' integrated SCSI-command set. Except of
 		    the immediate assign, which is of vital importance for
@@ -775,7 +958,10 @@
 		    this flag will slow-down SCSI-accesses slightly, as the 
 		    software generated commands are always slower than the 
 		    hardware. Non-harddisk devices always get read/write-
-		    commands in bypass mode.
+		    commands in bypass mode. On the most recent releases of 
+		    the Linux IBM-SCSI-driver, the bypass command should be
+		    no longer a necessary thing, if you are sure about your
+		    SCSI-hardware!
 	 normal     This is the parameter, introduced on the 2.0.x development
 	            rail by ZP Gu. This parameter defines the SCSI-device
 		    scan order in the new industry standard. This means, that
@@ -789,6 +975,19 @@
 		    pun=6 gets sda and a harddisk at pun=0 gets sdb. If you
 		    like to have the same SCSI-device order, as in DOS, OS-9
 		    or OS/2, just use this parameter.
+         fast       SCSI-I/O in synchronous mode is done at 5 MHz for IBM-
+                    SCSI-devices. SCSI-2 Fast/Wide Adapter/A external bus
+                    should then run at 10 MHz if Fast-SCSI is enabled,
+                    and at 5 MHz if Fast-SCSI is disabled on the external
+                    bus. This is the default setting when nothing is 
+                    specified here.
+         medium     Synchronous rate is at 50% approximately, which means
+                    2.5 MHz for IBM SCSI-adapters and 5.0 MHz for F/W ext.
+                    SCSI-bus (when Fast-SCSI speed enabled on external bus).
+         slow       The slowest possible synchronous transfer rate is set. 
+                    This means 1.82 MHz for IBM SCSI-adapters and 2.0 MHz
+                    for F/W external bus at Fast-SCSI speed on the external
+		    bus.
 		    
    A further option is that you can force the SCSI-driver to accept a SCSI-
    subsystem at a certain I/O-address with a predefined adapter PUN. This
@@ -805,7 +1004,7 @@
         ibmmcascsi=adisplay,bypass
 	
    This will use the advanced display mode for the model 95 LED display and
-   every SCSI-command passed to a attached device will get bypassed in order
+   every SCSI-command passed to an attached device will get bypassed in order
    not to use any of the subsystem built-in commands.
    
         ibmmcascsi=display,0x3558,7
@@ -837,9 +1036,9 @@
 	with OS/2 and DOS, you have to activate this flag in the kernel
 	configuration or you should set 'ansi' as parameter for the kernel.
 	The parameter 'normal' sets the new industry standard, starting
-	from pun 0, scaning up to pun 6. This allows you to change your 
+	from pun 0, scanning up to pun 6. This allows you to change your 
 	opinion still after having already compiled the kernel.
-     Q: Why can I not find the IBM MCA SCSI support in the config menue?
+     Q: Why I cannot find the IBM MCA SCSI support in the config menue?
      A: You have to activate MCA bus support, first.
      Q: Where can I find the latest info about this driver?
      A: See the file MAINTAINERS for the current WWW-address, which offers
@@ -849,13 +1048,9 @@
      A: Just force it to be recognized by kernel parameters. See section 5.1.
      Q: The driver screws up, if it starts to probe SCSI-devices, is there
         some way out of it?
-     A: This is based on some problems with the driver. In such cases, send
-        e-mail to the maintainer. If you are owner of a model with the serial
-	number 95XX, just send as subject NOTIFY 95XX PROBLEM and the 
-	maintainer immediately knows about your problem. But please:
-	Check your hardware and only if it works fine with other operating
-	systems, send E-Mail to me to notify the troubles. See the homepage
-	for how to send bug-reports or please read the next Q/A, here:
+     A: Yes, that was some recognition problem of the correct SCSI-adapter
+        and its I/O base addresses. Upgrade your driver to the latest release
+	and it should be fine again.
      Q: I get a message: panic IBM MCA SCSI: command error .... , what can
         I do against this?
      A: Previously, I followed the way by ignoring command errors by using
@@ -867,8 +1062,8 @@
         ibmmcascsi=forgiveall. Are there other possibilities to prevent
 	such panics?
      A: No, get just the latest release of the driver and it should work 
-        better and better with increasing version number. Forget this
-	ibmmcascsi=forgiveall, as also ignorecmd are obsolete.
+        better and better with increasing version number. Forget about this
+	ibmmcascsi=forgiveall, as also ignorecmd are obsolete.!
      Q: Linux panics or stops without any comment, but it is probable, that my
         harddisk(s) have bad blocks.
      A: Sorry, the bad-block handling is still a feeble point of this driver,
@@ -893,10 +1088,45 @@
 	Astonishingly, reset works in most cases quite ok, but the harddisks
 	won't run in synchonous mode anymore after a reset, until you reboot.
      Q: Why does my XXX w/Cache adapter not use read-prefetch?
-     A: w/Cache technical manuals are incoming here, so if I understood the
-        command of read-prefetch, it should be an easy thing to get harddisks
-	read in read-prefetch with w/Cache controllers. Some weeks or months,
-	still ahead and a lot of work still to do, sigh ...
+     A: Ok, that is not completely possible. If a cache is present, the 
+        adapter tries to use it internally. Explicitly, one can use the cache
+	with a read prefetch command, maybe in future, but this requires
+	some major overhead of SCSI-commands that risks the performance to
+	go down more than it gets improved. Tests with that are running.
+     Q: I have a IBM SCSI-2 Fast/Wide adapter, it boots in some way and hangs.
+     A: Yes, that is understood, as for sure, your SCSI-2 Fast/Wide adapter
+        was in such a case recognized as integrated SCSI-adapter or something 
+	else, but not as the correct adapter. As the I/O-ports get assigned 
+	wrongly by that reason, the system should crash in most cases. You 
+	should upgrade to the latest release of the SCSI-driver. The 
+	recommended version is 3.2 or later. Here, the F/W support is in
+	a stable and reliable condition. Wide-addressing is in addition 
+	supported.
+     Q: I get a Ooops message and something like "killing interrupt".
+     A: The reason for this is that the IBM SCSI-subsystem only sends a 
+        termination status back, if some error appeared. In former releases
+	of the driver, it was not checked, if the termination status block
+	is NULL. From version 3.2, it is taken care of this.
+     Q: I have a F/W adapter and the driver sees my internal SCSI-devices,
+        but ignores the external ones.
+     A: Select combined busmode in the config-program and check for that
+        no SCSI-id on the external devices appears on internal devices.
+        Reboot afterwards. Dual busmode is supported, but works only for the
+	internal bus, yet. External bus is still ignored. Take care for your
+	SCSI-ids. If combined bus-mode is activated, on some adapters, 
+	the wide-addressing is not possible, so devices with ids between 8 
+	and 15 get ignored by the driver & adapter!
+     Q: I have a 9595 and I get a NMI during heavy SCSI I/O e.g. during fsck.
+        A COMMAND ERROR is reported and characters on the screen are missing.
+	Warm reboot is not possible. Things look like quite weired.
+     A: Check the processor type of your 9595. If you have an 80486 or 486DX-2
+        processor complex on your mainboard and you compiled a kernel that
+	supports 80386 processors, it is possible, that the kernel cannot
+	keep track of the PS/2 interrupt handling and stops on an NMI. Just
+	compile a kernel for the correct processor type of your PS/2 and
+	everything should be fine. This is necessary even if one assumes,
+	that some 80486 system should be downward compatible to 80386
+	software.
 	
    5.3 Bugreports
    --------------
@@ -909,7 +1139,9 @@
    Zubkoff, as Linus is burried in E-Mail and Leonard is supervising all
    SCSI-drivers and won't have the time left to look inside every single
    driver to fix a bug and especially DO NOT send modified code to Linus
-   Torvalds, which has not been checked here!!! Recently, I got a lot of 
+   Torvalds or Alan J. Cox which has not been checked here!!! They are both
+   quite burried in E-mail (as me, sometimes, too) and one should first check
+   for problems on my local teststand. Recently, I got a lot of 
    bugreports for errors in the ibmmca.c code, which I could not imagine, but
    a look inside some Linux-distribution showed me quite often some modified
    code, which did no longer work on most other machines than the one of the
@@ -934,23 +1166,36 @@
         http://www.uni-mainz.de/~langm000/linux.html
 	
    Here you can find info about the background of this driver, patches,
-   news and a bugreport form.
+   troubleshooting support, news and a bugreport form. Please check that
+   WWW-page regularly for latest hints.
+   
+   For the bugreport, please fill out the formular on the corresponding
+   WWW-page. Read the dedicated instructions and write as much as you
+   know about your problem. If you do not like such formulars, please send
+   some e-mail directly, but at least with the same information as required by
+   the formular.
+   
+   If you have extensive bugreports, including Ooops messages and 
+   screen-shots, please feel free to send it directly to the address
+   of the maintainer, too. The current address of the maintainer is:
+   
+            Michael Lang <langa2@kph.uni-mainz.de>
    
    6 References
    ------------
-   The source of information is "Update for the PS/2 Hardware 
-   Interface Technical Reference, Common Interfaces", September 1991, 
-   part number 04G3281, available in the U.S. for $21.75 at 
-   1-800-IBM-PCTB, elsewhere call your local friendly IBM 
-   representative. E.g. in Germany, "Hallo IBM" works really great.
-   In addition to SCSI subsystem, this update contains fairly detailed 
-   (at hardware register level) sections on diskette  controller,
-   keyboard controller, serial port controller, VGA, and XGA.
+   IBM Corp., "Update for the PS/2 Hardware Interface Technical Reference, 
+   Common Interfaces", Armonk, September 1991, PN 04G3281, 
+   (available in the U.S. for $21.75 at 1-800-IBM-PCTB or in Germany for
+   around 40,-DM at "Hallo IBM").
   
-   Additional information from "Personal System/2 Micro Channel SCSI
-   Adapter with Cache Technical Reference", March 1990, PN 68X2365,
-   probably available from the same source (or possibly found buried
-   in officemates desk).
+   IBM Corp., "Personal System/2 Micro Channel SCSI
+   Adapter with Cache Technical Reference", Armonk, March 1990, PN 68X2365.
+
+   IBM Corp., "Personal System/2 Micro Channel SCSI
+   Adapter Technical Reference", Armonk, March 1990, PN 68X2397.
+
+   IBM Corp., "SCSI-2 Fast/Wide Adapter/A Technical Reference - Dual Bus",
+   Armonk, March 1994, PN 83G7545.
  
    Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie-
    Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl.
@@ -965,14 +1210,101 @@
    Helmut Rompel, "IBM Computerwelt GUIDE", What is what bei IBM., Systeme *
    Programme * Begriffe, IWT-Verlag GmbH - Muenchen, 1988
    
-   7 Trademarks
+   7 Credits to
+   ------------
+   7.1 People
+   ----------
+   Klaus Grimm
+                who already a long time ago gave me the old code from the
+		SCSI-driver in order to get it running for some old machine
+		in our institute.
+   Martin Kolinek
+                who wrote the first release of the IBM SCSI-subsystem driver.
+   Chris Beauregard
+                who for a long time maintained MCA-Linux and the SCSI-driver
+		in the beginning. Chris, wherever you are: Cheers to you!
+   Klaus Kudielka
+                with whom in the 2.1.x times, I had a quite fruitful
+                cooperation to get the driver running as a module and to get
+		it running with multiple SCSI-adapters.
+   David Weinehall
+                for his excellent maintenance of the MCA-stuff and the quite 
+		detailed bug reports and ideas for this driver (and his 
+		patience ;-)).
+   Alan J. Cox  
+                for his bugreports and his bold activities in cross-checking
+		the driver-code with his teststand.
+		
+   7.2 Sponsors & Supporters
+   -------------------------
+   "Hallo IBM",
+   IBM-Deutschland GmbH
+                the service of IBM-Deutschland for customers. Their E-Mail
+		service is unbeatable. Whatever old stuff I asked for, I 
+		always got some helpful answers.
+   Karl-Otto Reimers,
+   IBM Klub - Sparte IBM Geschichte, Sindelfingen
+                for sending me a copy of the w/Cache manual from the 
+		IBM-Deutschland archives.
+   Harald Staiger
+                for his extensive hardware donations which allows me today
+		still to test the driver in various constellations.
+   Erich Fritscher
+                for his very kind sponsoring.
+   Louis Ohland,
+   Charles Lasitter
+                for support by shipping me an IBM SCSI-2 Fast/Wide manual.
+                In addition, the contribution of various hardware is quite 
+                decessive and will make it possible to add FWSR (RAID)
+                adapter support to the driver in the near future! So,
+                complaints about no RAID support won't remain forever.
+                Yes, folks, that is no joke, RAID support is going to rise!
+   Erik Weber
+                for the great deal we made about a model 9595 and the nice
+                surrounding equipment and the cool trip to Mannheim
+                second-hand computer market.
+   Anthony Hogbin
+                for his direct shipment of a SCSI F/W adapter, which allowed
+                me immediately on the first stage to try it on model 8557
+                together with onboard SCSI adapter and some SCSI w/Cache.
+   Andreas Hotz
+                for his support by memory and an IBM SCSI-adapter. Collecting
+                all this together now allows me to try really things with
+                the driver at maximum load and variety on various models in
+                a very quick and efficient way.
+   Peter Jennewein
+                for his model 30, which serves me as part of my teststand
+		and his cool remark about how you make an ordinary diskette
+		drive working and how to connect it to an IBM-diskette port.
+   Johannes Gutenberg-University, Mainz &
+   Institut fuer Kernphysik, MAMI
+                for the offered space, the link, placed on the central
+                homepage and the space to store and offer the driver and 
+		related material and the free working times, which allow
+                me to answer all your e-mail.
+                   
+   8 Trademarks
    ------------
    IBM, PS/2, OS/2, Microchannel are registered trademarks of International 
-   Business Machines Corp.
+   Business Machines Corporation
    
    MS-DOS is a registered trademark of Microsoft Corporation
    
-   OS-9 is a registered trademark of Microware Systems
+   Microware, OS-9 are registered trademarks of Microware Systems
+   
+   9 Disclaimer
+   ------------
+   Beside the GNU public license and the dependant disclaimers and disclaimers
+   concerning the Linux-kernel in special, this SCSI-driver comes without any
+   warranty. Its functionality is tested as good as possible on certain 
+   machines and combinations of computer hardware, which does not exclude,
+   that dataloss or severe damage of hardware is possible while using this
+   part of software on some arbitrary computer hardware or in combination 
+   with other software packages. It is highly recommended to make backup
+   copies of your data before using this software.
+   
+   This driver supports hardware, produced by International Business Machines
+   Corporation (IBM).
    
 ------
 Michael Lang 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.ncr53c8xx linux.18p17/drivers/scsi/README.ncr53c8xx
--- linux.vanilla/drivers/scsi/README.ncr53c8xx	Fri Apr 21 12:46:31 2000
+++ linux.18p17/drivers/scsi/README.ncr53c8xx	Thu Aug 31 14:50:52 2000
@@ -1,10 +1,10 @@
-The Linux NCR53C8XX driver README file
+The Linux NCR53C8XX/SYM53C8XX drivers README file
 
 Written by Gerard Roudier <groudier@club-internet.fr>
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-9 May 1999
+29 May 1999
 ===============================================================================
 
 1.  Introduction
@@ -25,14 +25,39 @@
       8.6  Clear profile counters
       8.7  Set flag (no_disc)
       8.8  Set verbose level
+      8.9  Reset all logical units of a target
+      8.10 Abort all tasks of all logical units of a target
 9.  Configuration parameters
 10. Boot setup commands
       10.1 Syntax
       10.2 Available arguments
+             10.2.1  Master parity checking
+             10.2.2  Scsi parity checking
+             10.2.3  Scsi disconnections
+             10.2.4  Special features
+             10.2.5  Ultra SCSI support
+             10.2.6  Default number of tagged commands
+             10.2.7  Default synchronous period factor
+             10.2.8  Negotiate synchronous with all devices
+             10.2.9  Verbosity level
+             10.2.10 Debug mode
+             10.2.11 Burst max
+             10.2.12 LED support
+             10.2.13 Max wide
+             10.2.14 Differential mode
+             10.2.15 IRQ mode
+             10.2.16 Reverse probe
+             10.2.17 Fix up PCI configuration space
+             10.2.18 Serial NVRAM
+             10.2.19 Check SCSI BUS 
+             10.2.20 Exclude a host from being attached
+             10.2.21 Suggest a default SCSI id for hosts
+             10.2.22 Enable use of IMMEDIATE ARBITRATION
       10.3 Advised boot setup commands
       10.4 PCI configuration fix-up boot option
       10.5 Serial NVRAM support boot option
       10.6 SCSI BUS checking boot option
+      10.7 IMMEDIATE ARBITRATION boot option
 11. Some constants and flags of the ncr53c8xx.h header file
 12. Installation
 13. Architecture dependent features
@@ -43,6 +68,8 @@
       14.4 Possible data corruption during a Memory Write and Invalidate
       14.5 IRQ sharing problems
 15. SCSI problem troubleshooting
+      15.1 Problem tracking
+      15.2 Understanding hardware error reports
 16. Synchonous transfer negotiation tables
       16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
       16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
@@ -69,11 +96,12 @@
 It is now available as a bundle of 2 drivers:
 
 - ncr53c8xx generic driver that supports all the SYM53C8XX family including 
-  the ealiest 810 rev. 1 and the latest 896 2 channels LVD SCSI controller.
+  the ealiest 810 rev. 1, the latest 896 (2 channel LVD SCSI controller) and
+  the new 895A (1 channel LVD SCSI controller).
 - sym53c8xx enhanced driver (a.k.a. 896 drivers) that drops support of oldest 
   chips in order to gain advantage of new features, as LOAD/STORE intructions 
   available since the 810A and hardware phase mismatch available with the 
-  latest 896.
+  896 and the 895A.
 
 You can find technical information about the NCR 8xx family in the
 PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by
@@ -95,15 +123,17 @@
 These tools are not ALPHA but quite clean and work quite well.
 It is essential you have the 'scsiinfo' package.
 
-This short documentation only describes the features of the NCR53C8XX
-driver, configuration parameters and control commands available
-through the proc SCSI file system read / write operations.
+This short documentation describes the features of the generic and enhanced
+drivers, configuration parameters and control commands available through 
+the proc SCSI file system read / write operations.
 
 This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC.
 
 Latest driver version and patches are available at:
 
           ftp://ftp.tux.org/pub/people/gerard-roudier
+or
+          ftp://ftp.symbios.com/mirror/ftp.tux.org/pub/tux/roudier/drivers
 
 I am not a native speaker of English and there are probably lots of
 mistakes in this README file. Any help will be welcome.
@@ -136,7 +166,14 @@
 875        Y         Y      FAST20    40 MB/s        Y             Y
 876        Y         Y      FAST20    40 MB/s        Y             Y
 895        Y         Y      FAST40    80 MB/s        Y             Y
+895A       Y         Y      FAST40    80 MB/s        Y             Y
 896        Y         Y      FAST40    80 MB/s        Y             Y
+897        Y         Y      FAST40    80 MB/s        Y             Y
+1510D      Y         Y      FAST40    80 MB/s        Y             Y
+1010       Y         Y      FAST80   160 MB/s        N             Y
+1010_66*   Y         Y      FAST80   160 MB/s        N             Y
+
+* Chip supports 33MHz and 66MHz PCI buses.
 
 
 Summary of other supported features:
@@ -156,21 +193,25 @@
 
 3.1 Optimized SCSI SCRIPTS.
 
-The 810A, 825A, 875, 895 and newest 896 support new SCSI SCRIPTS instructions 
-named LOAD and STORE that allow to move 1 DWORD from/to an IO register to/from 
-memory much faster that the MOVE MEMORY instruction that is supported by the 
-53c7xx and 53c8xx family. The LOAD/STORE instructions support absolute and 
-DSA relative addressing modes. The SCSI SCRIPTS had been entirely rewritten 
-using LOAD/STORE instead of MOVE MEMORY instructions.
+The 810A, 825A, 875, 895, 896 and 895A support new SCSI SCRIPTS instructions 
+named LOAD and STORE that allow to move up to 1 DWORD from/to an IO register 
+to/from memory much faster that the MOVE MEMORY instruction that is supported 
+by the 53c7xx and 53c8xx family.
+The LOAD/STORE instructions support absolute and DSA relative addressing 
+modes.  The SCSI SCRIPTS had been entirely rewritten using LOAD/STORE instead 
+of MOVE MEMORY instructions.
 
 3.2 New features of the SYM53C896 (64 bit PCI dual LVD SCSI controller)
 
-The 896 allows to handle the phase mismatch context saving from SCRIPTS 
-(avoids the phase mismatch interrupt that stops the SCSI processor 
+The 896 and the 895A allows handling of the phase mismatch context from 
+SCRIPTS (avoids the phase mismatch interrupt that stops the SCSI processor 
 until the C code has saved the context of the transfer).
 Implementing this without using LOAD/STORE instructions would be painfull 
-and I did'nt even try it. This chip also supports 64 bit PCI transactions 
-and addressing. The SCRIPTS processor is not true 64 bit, but uses segment 
+and I did'nt even want to try it.
+
+The 896 chip supports 64 bit PCI transactions and addressing, while the 
+895A supports 32 bit PCI transactions and 64 bit addressing.
+The SCRIPTS processor of these chips is not true 64 bit, but uses segment 
 registers for bit 32-63. Another interesting feature is that LOAD/STORE 
 instructions that address the on-chip RAM (8k) remain internal to the chip.
 
@@ -219,9 +260,13 @@
 is currently set to 8 by default.  This value is suitable for most SCSI
 disks.  With large SCSI disks (>= 2GB, cache >= 512KB, average seek time
 <= 10 ms), using a larger value may give better performances.
-The driver supports up to 64 commands per device, but using more than 
-32 is generally not worth it, unless you are using a very large disk 
-or disk array.
+
+The sym53c8xx driver supports up to 255 commands per device, and the 
+generic ncr53c8xx driver supports up to 64, but using more than 32 is 
+generally not worth-while, unless you are using a very large disk or disk 
+array. It is noticeable that most of recent hard disks seem not to accept 
+more than 64 simultaneous commands. So, using more than 64 queued commands 
+is probably just resource wasting.
 
 If your controller does not have NVRAM or if it is managed by the SDMS 
 BIOS/SETUP, you can configure tagged queueing feature and device queue 
@@ -491,6 +536,24 @@
     The driver default verbose level is 1. This command allows to change 
     th driver verbose level after boot-up.
 
+8.9 Reset all logical units of a target
+
+    resetdev <target>
+
+    target:    target number
+    The driver will try to send a BUS DEVICE RESET message to the target.
+    (Only supported by the SYM53C8XX driver and provided for test purpose)
+
+8.10 Abort all tasks of all logical units of a target
+
+    cleardev <target>
+
+    target:    target number
+    The driver will try to send a ABORT message to all the logical units 
+    of the target.
+    (Only supported by the SYM53C8XX driver and provided for test purpose)
+
+
 9. Configuration parameters
 
 If the firmware of all your devices is perfect enough, all the
@@ -566,10 +629,11 @@
 Setup commands can be passed to the driver either at boot time or as a 
 string variable using 'insmod'.
 
-A boot setup command for the ncr53c8xx driver begins with the driver name 
-"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
-integers separated with comma followed by an optionnal list of  comma- 
-separated strings. Example of boot setup command under lilo prompt:
+A boot setup command for the ncr53c8xx (sym53c8xx) driver begins with the 
+driver name "ncr53c8xx="(sym53c8xx). The kernel syntax parser then expects 
+an optionnal list of integers separated with comma followed by an optional 
+list of  comma-separated strings. Example of boot setup command under lilo 
+prompt:
 
 lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200
 
@@ -582,7 +646,7 @@
 The following command will install driver module with the same options as 
 above.
 
-insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200"
+    insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200"
 
 For the moment, the integer list of arguments is discarded by the driver. 
 It will be used in the future in order to allow a per controller setup.
@@ -590,40 +654,54 @@
 Each string argument must be specified as "keyword:value". Only lower-case 
 characters and digits are allowed.
 
+In a system that contains multiple 53C8xx adapters insmod will install the 
+specified driver on each adapter. To exclude a chip use the 'excl' keyword.
+
+The sequence of commands, 
+
+    insmod sym53c8xx sym53c8xx=excl:0x1400
+    insmod ncr53c8xx
+
+installs the sym53c8xx driver on all adapters except the one at IO port 
+address 0x1400 and then installs the ncr53c8xx driver to the adapter at IO 
+port address 0x1400.
+
+
 10.2 Available arguments
 
-Master parity checking
-      mpar:y     enabled
-      mpar:n     disabled
-
-Scsi parity checking
-      spar:y     enabled
-      spar:n     disabled
-
-Scsi disconnections
-      disc:y     enabled
-      disc:n     disabled
+10.2.1  Master parity checking
+        mpar:y     enabled
+        mpar:n     disabled
+
+10.2.2  Scsi parity checking
+        spar:y     enabled
+        spar:n     disabled
+
+10.2.3  Scsi disconnections
+        disc:y     enabled
+        disc:n     disabled
  
-Special features
+10.2.4  Special features
    Only apply to 810A, 825A, 860, 875 and 895 controllers.
    Have no effect with other ones.
-      specf:y    (or 1) enabled
-      specf:n    (or 0) disabled
-      specf:3           enabled except Memory Write And Invalidate
+        specf:y    (or 1) enabled
+        specf:n    (or 0) disabled
+        specf:3           enabled except Memory Write And Invalidate
    The default driver setup is 'specf:3'. As a consequence, option 'specf:y' 
    must be specified in the boot setup command to enable Memory Write And 
    Invalidate.
 
-Ultra SCSI support
-   Only apply to 860, 875 and 895 controllers.
+10.2.5  Ultra SCSI support
+   Only apply to 860, 875, 895, 895a, 896, 1010 and 1010_66 controllers.
    Have no effect with other ones.
-      ultra:2    Ultra2 enabled
-      ultra:1    Ultra enabled
-      ultra:n    disabled
-
-Default number of tagged commands
-      tags:0     (or tags:1 ) tagged command queuing disabled
-      tags:#tags (#tags  > 1) tagged command queuing enabled
+        ultra:n    All ultra speeds enabled
+        ultra:2    Ultra2 enabled
+        ultra:1    Ultra enabled
+        ultra:0    Ultra speeds disabled
+
+10.2.6  Default number of tagged commands
+        tags:0     (or tags:1 ) tagged command queuing disabled
+        tags:#tags (#tags  > 1) tagged command queuing enabled
   #tags will be truncated to the max queued commands configuration parameter.
   This option also allows to specify a command queue depth for each device 
   that support tagged command queueing.
@@ -635,9 +713,9 @@
       - controller #1 target #1 logical unit #2                -> 32 commands,
       - all other logical units (all targets, all controllers) -> 10 commands.
 
-Default synchronous period factor
-      sync:255     disabled (asynchronous transfer mode)
-      sync:#factor
+10.2.7  Default synchronous period factor
+        sync:255     disabled (asynchronous transfer mode)
+        sync:#factor
   #factor = 10     Ultra-2 SCSI 40 Mega-transfers / second
   #factor = 11     Ultra-2 SCSI 33 Mega-transfers / second
   #factor < 25     Ultra   SCSI 20 Mega-transfers / second
@@ -646,19 +724,19 @@
   In all cases, the driver will use the minimum transfer period supported by 
   controllers according to NCR53C8XX chip type.
 
-Negotiate synchronous with all devices
-  (force sync nego)
-      fsn:y      enabled
-      fsn:n      disabled
-
-Verbosity level
-      verb:0     minimal
-      verb:1     normal
-      verb:2     too much
-
-Debug mode
-      debug:0	 clear debug flags
-      debug:#x   set debug flags
+10.2.8  Negotiate synchronous with all devices
+        (force sync nego)
+        fsn:y      enabled
+        fsn:n      disabled
+
+10.2.9  Verbosity level
+        verb:0     minimal
+        verb:1     normal
+        verb:2     too much
+
+10.2.10 Debug mode
+        debug:0	 clear debug flags
+        debug:#x   set debug flags
   #x is an integer value combining the following power-of-2 values:
   DEBUG_ALLOC       0x1
   DEBUG_PHASE       0x2
@@ -677,10 +755,10 @@
   You can play safely with DEBUG_NEGO. However, some of these flags may 
   generate bunches of syslog messages. 
 
-Burst max
-      burst:0    burst disabled
-      burst:255  get burst length from initial IO register settings.
-      burst:#x   burst enabled (1<<#x burst transfers max)
+10.2.11 Burst max
+        burst:0    burst disabled
+        burst:255  get burst length from initial IO register settings.
+        burst:#x   burst enabled (1<<#x burst transfers max)
   #x is an integer value which is log base 2 of the burst transfers max.
   The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7).
   Other chips only support up to 16 (#x = 4).
@@ -688,42 +766,42 @@
   and revision ids. By default the driver uses the maximum value supported 
   by the chip.
 
-LED support
-     led:1      enable  LED support
-     led:0      disable LED support
+10.2.12 LED support
+        led:1      enable  LED support
+        led:0      disable LED support
   Donnot enable LED support if your scsi board does not use SDMS BIOS.
   (See 'Configuration parameters')
 
-Max wide
-    wide:1      wide scsi enabled
-    wide:0      wide scsi disabled
+10.2.13 Max wide
+        wide:1      wide scsi enabled
+        wide:0      wide scsi disabled
   Some scsi boards use a 875 (ultra wide) and only supply narrow connectors.
   If you have connected a wide device with a 50 pins to 68 pins cable 
   converter, any accepted wide negotiation will break further data transfers.
   In such a case, using "wide:0" in the bootup command will be helpfull. 
 
-Differential mode
-    diff:0	never set up diff mode
-    diff:1	set up diff mode if BIOS set it
-    diff:2	always set up diff mode
-    diff:3	set diff mode if GPIO3 is not set
-
-IRQ mode
-    irqm:0	always open drain
-    irqm:1	same as initial settings (assumed BIOS settings)
-    irqm:2	always totem pole
-    irqm:0x10	driver will not use SA_SHIRQ flag when requesting irq
-    irqm:0x20	driver will not use SA_INTERRUPT flag when requesting irq
+10.2.14 Differential mode
+        diff:0	never set up diff mode
+        diff:1	set up diff mode if BIOS set it
+        diff:2	always set up diff mode
+        diff:3	set diff mode if GPIO3 is not set
+
+10.2.15 IRQ mode
+        irqm:0     always open drain
+        irqm:1     same as initial settings (assumed BIOS settings)
+        irqm:2     always totem pole
+        irqm:0x10  driver will not use SA_SHIRQ flag when requesting irq
+        irqm:0x20  driver will not use SA_INTERRUPT flag when requesting irq
 
     (Bits 0x10 and 0x20 can be combined with hardware irq mode option)
 
-Reverse probe
-    revprob:n   probe chip ids from the PCI configuration in this order:
-                810, 815, 820, 860, 875, 885, 895, 896
-    revprob:y   probe chip ids in the reverse order.
+10.2.16 Reverse probe
+        revprob:n   probe chip ids from the PCI configuration in this order:
+                    810, 815, 820, 860, 875, 885, 895, 896
+        revprob:y   probe chip ids in the reverse order.
 
-Fix up PCI configuration space
-    pcifix:<option bits>
+10.2.17 Fix up PCI configuration space
+        pcifix:<option bits>
 
     Available option bits:
         0x0:   No attempt to fix PCI configuration space registers values.
@@ -733,35 +811,36 @@
 
     Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
 
-Serial NVRAM
-    nvram:n     do not look for serial NVRAM
-    nvram:y     test controllers for onboard serial NVRAM
-    (alternate binary form)
-    mvram=<bits options>
-      0x01   look for NVRAM  (equivalent to nvram=y)
-      0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
-      0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
-      0x08   ignore NVRAM "Scan at boot time" parameter for all devices
-      0x80   also attach controllers set to OFF in the NVRAM (sym53c8xx only)
+10.2.18 Serial NVRAM
+        nvram:n     do not look for serial NVRAM
+        nvram:y     test controllers for onboard serial NVRAM
+        (alternate binary form)
+        mvram=<bits options>
+        0x01   look for NVRAM  (equivalent to nvram=y)
+        0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+        0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+        0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+        0x80   also attach controllers set to OFF in the NVRAM (sym53c8xx only)
 
-Check SCSI BUS 
-    buschk:<option bits>
+10.2.19 Check SCSI BUS 
+        buschk:<option bits>
 
     Available option bits:
         0x0:   No check.
         0x1:   Check and donnot attach the controller on error.  
         0x2:   Check and just warn on error.
+        0x4:   Disable SCSI bus integrity checking.
 
-Exclude a host from being attached
-    excl=<io_address>
+10.2.20 Exclude a host from being attached
+        excl=<io_address>
 
     Prevent host at a given io address from being attached.
-    For example 'ncr53c8xx=excl:0xb400,excl:0xc000' indicates to the 
+    For example 'ncr53c8xx=excl:0xb400,excl:0xc000' indicate to the 
     ncr53c8xx driver not to attach hosts at address 0xb400 and 0xc000.
 
-Suggest a default SCSI id for hosts
-    hostid:255	no id suggested.
-    hostid:#x   (0 < x < 7) x suggested for hosts SCSI id.
+10.2.21 Suggest a default SCSI id for hosts
+        hostid:255	no id suggested.
+        hostid:#x   (0 < x < 7) x suggested for hosts SCSI id.
 
     If a host SCSI id is available from the NVRAM, the driver will ignore 
     any value suggested as boot option. Otherwise, if a suggested value 
@@ -769,6 +848,16 @@
     try to deduce the value previously set in the hardware and use value 
     7 if the hardware value is zero.
 
+10.2.22 Enable use of IMMEDIATE ARBITRATION
+        (only supported by the sym53c8xx driver. See 10.7 for more details)
+        iarb:0    donnot use this feature.
+        iarb:#x   use this feature according to bit fields as follow:
+
+    bit 0 (1) : enable IARB each time the initiator has been reselected 
+                when it arbitrated for the SCSI BUS.
+    (#x >> 4) : maximum number of successive settings of IARB if the initiator 
+                win arbitration and it has other commands to send to a device.
+
 Boot fail safe
     safe:y	load the following assumed fail safe initial setup
 
@@ -792,6 +881,7 @@
   differential support		from BIOS settings	diff:1
   irq mode			from BIOS settings	irqm:1
   SCSI BUS check		donnot attach on error	buschk:1
+  immediate arbitration		disabled		iarb:0
 
 10.3 Advised boot setup commands
 
@@ -920,6 +1010,45 @@
 On the other hand, either bad cabling, broken devices, not conformant 
 devices, ... may cause a SCSI signal to be wrong when te driver reads it.
 
+10.7 IMMEDIATE ARBITRATION boot option
+
+This option is only supported by the SYM53C8XX driver (not by the NCR53C8XX).
+
+SYMBIOS 53C8XX chips are able to arbitrate for the SCSI BUS as soon as they 
+have detected an expected disconnection (BUS FREE PHASE). For this process 
+to be started, bit 1 of SCNTL1 IO register must be set when the chip is 
+connected to the SCSI BUS.
+
+When this feature has been enabled for the current connection, the chip has 
+every chance to win arbitration if only devices with lower priority are 
+competing for the SCSI BUS. By the way, when the chip is using SCSI id 7, 
+then it will for sure win the next SCSI BUS arbitration.
+
+Since, there is no way to know what devices are trying to arbitrate for the 
+BUS, using this feature can be extremally unfair. So, you are not advised 
+to enable it, or at most enable this feature for the case the chip lost 
+the previous arbitration (boot option 'iarb:1').
+
+This feature has the following advantages:
+
+a) Allow the initiator with ID 7 to win arbitration when it wants so.
+b) Overlap at least 4 micro-seconds of arbitration time with the execution 
+   of SCRIPTS that deal with the end of the current connection and that 
+   starts the next job.
+
+Hmmm... But (a) may just prevent other devices from reselecting the initiator, 
+and delay data transfers or status/completions, and (b) may just waste 
+SCSI BUS bandwidth if the SCRIPTS execution lasts more than 4 micro-seconds.
+
+The use of IARB needs the SCSI_NCR_IARB_SUPPORT option to have been defined 
+at compile time and the 'iarb' boot option to have been set to a non zero 
+value at boot time. It is not that usefull for real work, but can be used 
+to stress SCSI devices or for some applications that can gain advantage of 
+it. By the way, if you experience badnesses like 'unexpected disconnections', 
+'bad reselections', etc... when using IARB on heavy IO load, you should not 
+be surprised, because force-feeding anything and blocking its arse at the 
+same time cannot work for a long time. :-))
+
 
 11. Some constants and flags of the ncr53c8xx.h header file
 
@@ -1133,6 +1262,8 @@
 
 15. SCSI problem troubleshooting
 
+15.1 Problem tracking
+
 Most SCSI problems are due to a non conformant SCSI bus or to buggy
 devices.  If infortunately you have SCSI problems, you can check the
 following things:
@@ -1179,6 +1310,77 @@
 Once you have found the device and the feature that cause problems, just 
 disable that feature for that device.
 
+15.2 Understanding hardware error reports
+
+When the driver detects an unexpected error condition, it may display a 
+message of the following pattern.
+
+sym53c876-0:1: ERROR (0:48) (1-21-65) (f/95) @ (script 7c0:19000000).
+sym53c876-0: script cmd = 19000000
+sym53c876-0: regdump: da 10 80 95 47 0f 01 07 75 01 81 21 80 01 09 00.
+
+Some fields in such a message may help you understand the cause of the 
+problem, as follows:
+
+sym53c876-0:1: ERROR (0:48) (1-21-65) (f/95) @ (script 7c0:19000000).
+............A.........B.C....D.E..F....G.H.......I.....J...K.......
+
+Field A : target number.
+  SCSI ID of the device the controller was talking with at the moment the 
+  error occurs.
+
+Field B : DSTAT io register (DMA STATUS)
+  Bit 0x40 : MDPE Master Data Parity Error
+             Data parity error detected on the PCI BUS.
+  Bit 0x20 : BF   Bus Fault
+             PCI bus fault condition detected
+  Bit 0x01 : IID  Illegal Instruction Detected
+             Set by the chip when it detects an Illegal Instruction format 
+             on some condition that makes an instruction illegal.
+  Bit 0x80 : DFE Dma Fifo Empty
+             Pure status bit that does not indicate an error.
+  If the reported DSTAT value contains a combination of MDPE (0x40), 
+  BF (0x20), then the cause may be likely due to a PCI BUS problem.
+
+Field C : SIST io register (SCSI Interrupt Status)
+  Bit 0x08 : SGE  SCSI GROSS ERROR
+             Indicates that the chip detected a severe error condition 
+             on the SCSI BUS that prevents the SCSI protocol from functionning
+             properly.
+  Bit 0x04 : UDC  Undexpected Disconnection
+             Indicates that the device released the SCSI BUS when the chip 
+             was not expecting this to happen. A device may behave so to 
+             indicate the SCSI initiator that an error condition not reportable              using the SCSI protocol has occured.
+  Bit 0x02 : RST  SCSI BUS Reset
+             Generally SCSI targets donnot reset the SCSI BUS, although any 
+             device on the BUS can reset it at any time.
+  Bit 0x01 : PAR  Parity
+             SCSI parity error detected.
+  On a faulty SCSI BUS, any error condition among SGE (0x08), UDC (0x04) and 
+  PAR (0x01) may be detected by the chip. If your SCSI system sometimes 
+  encounters such error conditions, especially SCSI GROSS ERROR, then a SCSI 
+  BUS problem is likely the cause of these errors.
+
+For fields D,E,F,G and H, you may look into the sym53c8xx_defs.h file 
+that contains some minimal comments on IO register bits.
+Field D : SOCL  Scsi Output Control Latch
+          This register reflects the state of the SCSI control lines the 
+          chip want to drive or compare against.
+Field E : SBCL  Scsi Bus Control Lines
+          Actual value of control lines on the SCSI BUS.
+Field F : SBDL  Scsi Bus Data Lines
+          Actual value of data lines on the SCSI BUS.
+Field G : SXFER  SCSI Transfer
+          Contains the setting of the Synchronous Period for output and 
+          the current Synchronous offset (offset 0 means asynchronous).
+Field H : SCNTL3 Scsi Control Register 3
+          Contains the setting of timing values for both asynchronous and 
+          synchronous data transfers. 
+
+Understanding Fields I, J, K and dumps requires to have good knowledge of 
+SCSI standards, chip cores functionnals and internal driver data structures.
+You are not required to decode and understand them, unless you want to help 
+maintain the driver code.
 
 16. Synchonous transfer negotiation tables
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.st linux.18p17/drivers/scsi/README.st
--- linux.vanilla/drivers/scsi/README.st	Fri Apr 21 12:46:31 2000
+++ linux.18p17/drivers/scsi/README.st	Mon Sep 11 21:20:33 2000
@@ -2,7 +2,7 @@
 The driver is currently maintained by Kai M{kisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Sun Apr 18 13:24:43 1999 by makisara@home
+Last modified: Tue Aug 15 15:54:46 2000 by makisara@kai.makisara.local
 
 
 BASICS
@@ -67,7 +67,7 @@
 In variable block mode, the byte count in write() determines the size
 of the physical block on tape. When reading, the drive reads the next
 tape block and returns to the user the data if the read() byte count
-is at least the block size. Otherwise the data is truncated.
+is at least the block size. Otherwise an error is returned.
 
 In fixed block mode, the data transfer between the drive and the
 driver is in multiples of the block size. The write() byte count must
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/a2091.c linux.18p17/drivers/scsi/a2091.c
--- linux.vanilla/drivers/scsi/a2091.c	Fri Apr 21 12:46:27 2000
+++ linux.18p17/drivers/scsi/a2091.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/a3000.c linux.18p17/drivers/scsi/a3000.c
--- linux.vanilla/drivers/scsi/a3000.c	Fri Apr 21 12:46:27 2000
+++ linux.18p17/drivers/scsi/a3000.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aha152x.c linux.18p17/drivers/scsi/aha152x.c
--- linux.vanilla/drivers/scsi/aha152x.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/scsi/aha152x.c	Mon Oct  2 10:14:41 2000
@@ -406,8 +406,6 @@
 
 /* END OF DEFINES */
 
-extern long loops_per_sec;
-
 #define DELAY_DEFAULT 100
 
 /* some additional "phases" for getphase() */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/amiga7xx.c linux.18p17/drivers/scsi/amiga7xx.c
--- linux.vanilla/drivers/scsi/amiga7xx.c	Fri Apr 21 12:46:27 2000
+++ linux.18p17/drivers/scsi/amiga7xx.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/atari_NCR5380.c linux.18p17/drivers/scsi/atari_NCR5380.c
--- linux.vanilla/drivers/scsi/atari_NCR5380.c	Fri Apr 21 12:46:27 2000
+++ linux.18p17/drivers/scsi/atari_NCR5380.c	Sat Oct 14 00:07:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/atari_scsi.c linux.18p17/drivers/scsi/atari_scsi.c
--- linux.vanilla/drivers/scsi/atari_scsi.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/atari_scsi.c	Sat Oct 14 00:07:14 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/atp870u.c linux.18p17/drivers/scsi/atp870u.c
--- linux.vanilla/drivers/scsi/atp870u.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/atp870u.c	Thu Aug 31 15:07:52 2000
@@ -1,11 +1,15 @@
 /* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $
  *  linux/kernel/atp870u.c
  *
- *  Copyright (C) 1997  Wu Ching Chen
+ *  Copyright (C) 1997	Wu Ching Chen
  *  2.1.x update (C) 1998  Krzysztof G. Baranowski
- *   
- * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes 
- * 
+ *
+ * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
+ *
+ * Wu Ching Chen : NULL pointer fixes  2000/06/02
+ *		   support atp876 chip
+ *		   enable 32 bit fifo transfer
+ *		   support cdrom & remove device run ultra speed
  */
 
 #include <linux/module.h>
@@ -106,19 +110,19 @@
 	dev->in_int = 1;
 	workportu = dev->ioport;
 	tmport = workportu;
-	
-	if (dev->working != 0) 
+
+	if (dev->working != 0)
 	{
 		tmport += 0x1f;
 		j = inb(tmport);
-		if((j&0x80)==0)
+		if ((j & 0x80) == 0)
 		{
-			dev->in_int=0;
+			dev->in_int = 0;
 			return;
 		}
 
 		tmpcip = dev->pciport;
-		if ((inb(tmpcip) & 0x08) != 0) 
+		if ((inb(tmpcip) & 0x08) != 0)
 		{
 			tmpcip += 0x2;
 			for (k=0; k < 1000; k++)
@@ -130,7 +134,7 @@
 				if ((inb(tmpcip) & 0x01) == 0)
 				{
 					goto stop_dma;
-				}    
+				}
 			}
 		}
 stop_dma:
@@ -139,9 +143,9 @@
 		tmport -= 0x08;
 
 		i = inb(tmport);
-		if ((j & 0x40) == 0) 
+		if ((j & 0x40) == 0)
 		{
-			if ((dev->last_cmd & 0x40) == 0) 
+			if ((dev->last_cmd & 0x40) == 0)
 			{
 				dev->last_cmd = 0xff;
 			}
@@ -155,30 +159,32 @@
 		/*
 		 *	Remap wide devices onto id numbers
 		 */
-		 
+
 		if ((target_id & 0x40) != 0) {
 			target_id = (target_id & 0x07) | 0x08;
 		} else {
 			target_id &= 0x07;
 		}
-		
-		if (i == 0x85) 
+
+		if (i == 0x85)
 		{
 			/*
 			 *	Flip wide
 			 */
-			if (dev->wide_idu != 0) 
+			if (dev->wide_idu != 0)
 			{
 				tmport = workportu + 0x1b;
-				j = inb(tmport) & 0x0e;
-				j |= 0x01;
-				outb(j, tmport);
+				outb(0x01,tmport);
+				while ((inb(tmport) & 0x01) != 0x01)
+				{
+				   outb(0x01,tmport);
+				}
 			}
 			/*
 			 *	Issue more commands
 			 */
 			if (((dev->quhdu != dev->quendu) || (dev->last_cmd != 0xff)) &&
-			    (dev->in_snd == 0)) 
+			    (dev->in_snd == 0))
 			{
 				send_s870(h);
 			}
@@ -188,7 +194,7 @@
 			dev->in_int = 0;
 			return;
 		}
-		if (i == 0x21) 
+		if (i == 0x21)
 		{
 			tmport -= 0x05;
 			adrcntu = 0;
@@ -206,7 +212,7 @@
 			dev->in_int = 0;
 			return;
 		}
-		if ((i == 0x80) || (i == 0x8f)) 
+		if ((i == 0x80) || (i == 0x8f))
 		{
 			lun = 0;
 			tmport -= 0x07;
@@ -215,7 +221,7 @@
 				tmport += 0x0d;
 				lun = inb(tmport) & 0x07;
 			} else {
-				if (j == 0x41) 
+				if (j == 0x41)
 				{
 					tmport += 0x02;
 					adrcntu = 0;
@@ -231,7 +237,7 @@
 					dev->in_int = 0;
 					return;
 				}
-				else 
+				else
 				{
 					outb(0x46, tmport);
 					dev->id[target_id].dirctu = 0x00;
@@ -252,7 +258,7 @@
 			/*
 			 *	Remap wide identifiers
 			 */
-			if ((target_id & 0x10) != 0) 
+			if ((target_id & 0x10) != 0)
 			{
 				target_id = (target_id & 0x07) | 0x08;
 			} else {
@@ -277,8 +283,21 @@
 			j |= dev->id[target_id].dirctu;
 			outb(j, tmport++);
 			outb(0x80, tmport);
+
+			/* enable 32 bit fifo transfer */
+			tmport = workportu + 0x3a;
+			if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) ||
+			    (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a))
+			{
+			   outb((unsigned char)((inb(tmport) & 0xf3) | 0x08),tmport);
+			}
+			else
+			{
+			   outb((unsigned char)(inb(tmport) & 0xf3),tmport);
+			}
+
 			tmport = workportu + 0x1b;
-			j = inb(tmport) & 0x0e;
+			j = 0;
 			id = 1;
 			id = id << target_id;
 			/*
@@ -288,7 +307,11 @@
 				j |= 0x01;
 			}
 			outb(j, tmport);
-			
+			while ((inb(tmport) & 0x01) != j)
+			{
+			   outb(j,tmport);
+			}
+
 			if (dev->id[target_id].last_lenu == 0) {
 				tmport = workportu + 0x18;
 				outb(0x08, tmport);
@@ -296,7 +319,7 @@
 				return;
 			}
 			prd = dev->id[target_id].prd_posu;
-			while (adrcntu != 0) 
+			while (adrcntu != 0)
 			{
 				id = ((unsigned short int *) (prd))[2];
 				if (id == 0) {
@@ -340,19 +363,19 @@
 			dev->in_int = 0;
 			return;
 		}
-		
+
 		/*
 		 *	Current scsi request on this target
 		 */
-		 
+
 		workrequ = dev->id[target_id].curr_req;
-		
+
 		if (i == 0x42) {
 			errstus = 0x02;
 			workrequ->result = errstus;
 			goto go_42;
 		}
-		if (i == 0x16) 
+		if (i == 0x16)
 		{
 			errstus = 0;
 			tmport -= 0x08;
@@ -376,15 +399,17 @@
 			 */
 			if (dev->wide_idu != 0) {
 				tmport = workportu + 0x1b;
-				j = inb(tmport) & 0x0e;
-				j |= 0x01;
-				outb(j, tmport);
+				outb(0x01,tmport);
+				while ((inb(tmport) & 0x01) != 0x01)
+				{
+				   outb(0x01,tmport);
+				}
 			}
 			/*
 			 *	If there is stuff to send and nothing going then send it
 			 */
 			if (((dev->last_cmd != 0xff) || (dev->quhdu != dev->quendu)) &&
-			    (dev->in_snd == 0)) 
+			    (dev->in_snd == 0))
 			{
 				send_s870(h);
 			}
@@ -445,9 +470,9 @@
 		dev->in_int = 0;
 		return;
 	} else {
-		tmport = workportu + 0x17;
-		inb(tmport);
-		dev->working = 0;
+//		tmport = workportu + 0x17;
+//		inb(tmport);
+//		dev->working = 0;
 		dev->in_int = 0;
 		return;
 	}
@@ -480,7 +505,7 @@
 	/*
 	 *	Fake a timeout for missing targets
 	 */
-	 
+
 	if ((m & dev->active_idu) == 0) {
 		req_p->result = 0x00040000;
 		done(req_p);
@@ -556,7 +581,17 @@
 	if ((dev->last_cmd != 0xff) && ((dev->last_cmd & 0x40) != 0)) {
 		dev->last_cmd &= 0x0f;
 		workrequ = dev->id[dev->last_cmd].curr_req;
-		goto cmd_subp;
+		if (workrequ != NULL)	     /* check NULL pointer */
+		{
+		   goto cmd_subp;
+		}
+		dev->last_cmd = 0xff;
+		if (dev->quhdu == dev->quendu)
+		{
+		   dev->in_snd = 0;
+		   restore_flags(flags);
+		   return ;
+		}
 	}
 	dev->working++;
 	j = dev->quhdu;
@@ -597,6 +632,9 @@
 			workrequ->request_bufflen = 0x08;
 		}
 	}
+	if (dev->ata_cdbu[0] == 0x00) {
+		workrequ->request_bufflen = 0;
+	}
 	/*
 	 *	Why limit this ????
 	 */
@@ -606,11 +644,11 @@
 			dev->ata_cdbu[4] = 0x24;
 		}
 	}
-	
+
 	tmport = workportu + 0x1b;
-	j = inb(tmport) & 0x0e;
+	j = 0;
 	target_id = workrequ->target;
-	
+
 	/*
 	 *	Wide ?
 	 */
@@ -618,13 +656,17 @@
 	w = w << target_id;
 	if ((w & dev->wide_idu) != 0) {
 		j |= 0x01;
-	}	
+	}
 	outb(j, tmport);
-	
+	while ((inb(tmport) & 0x01) != j)
+	{
+	   outb(j,tmport);
+	}
+
 	/*
 	 *	Write the command
 	 */
-	 
+
 	tmport = workportu;
 	outb(workrequ->cmd_len, tmport++);
 	outb(0x2c, tmport++);
@@ -639,17 +681,17 @@
 	 *	Write the target
 	 */
 	outb(dev->id[target_id].devspu, tmport++);
-	
+
 	/*
 	 *	Figure out the transfer size
 	 */
-	if (workrequ->use_sg) 
+	if (workrequ->use_sg)
 	{
 		l = 0;
 		sgpnt = (struct scatterlist *) workrequ->request_buffer;
-		for (i = 0; i < workrequ->use_sg; i++) 
+		for (i = 0; i < workrequ->use_sg; i++)
 		{
-			if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) 
+			if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER)
 			{
 				panic("Foooooooood fight!");
 			}
@@ -700,13 +742,13 @@
 	tmpcip = dev->pciport;
 	prd = dev->id[target_id].prd_tableu;
 	dev->id[target_id].prd_posu = prd;
-	
+
 	/*
 	 *	Now write the request list. Either as scatter/gather or as
 	 *	a linear chain.
 	 */
-	 
-	if (workrequ->use_sg) 
+
+	if (workrequ->use_sg)
 	{
 		sgpnt = (struct scatterlist *) workrequ->request_buffer;
 		i = 0;
@@ -743,8 +785,21 @@
 	outb(0x06, tmpcip);
 	outb(0x00, tmpcip);
 	tmpcip = tmpcip - 2;
+
+	tmport = workportu + 0x3a;
+	if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) ||
+	    (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a))
+	{
+	   outb((unsigned char)((inb(tmport) & 0xf3) | 0x08),tmport);
+	}
+	else
+	{
+	   outb((unsigned char)(inb(tmport) & 0xf3),tmport);
+	}
+	tmport = workportu + 0x1c;
+
 	if ((dev->ata_cdbu[0] == WRITE_6) || (dev->ata_cdbu[0] == WRITE_10) ||
-	    (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT)) 
+	    (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT))
 	{
 		dev->id[target_id].dirctu = 0x20;
 		if (inb(tmport) == 0) {
@@ -758,7 +813,7 @@
 		restore_flags(flags);
 		return;
 	}
-	if (inb(tmport) == 0) 
+	if (inb(tmport) == 0)
 	{
 		tmport = workportu + 0x18;
 		outb(0x08, tmport);
@@ -804,20 +859,20 @@
 			goto FUN_D7;
 		}
 	}
-	*val |= 0x4000;		/* assert DB6           */
+	*val |= 0x4000; 	/* assert DB6		*/
 	outw(*val, tmport);
-	*val &= 0xdfff;		/* assert DB5           */
+	*val &= 0xdfff; 	/* assert DB5		*/
 	outw(*val, tmport);
 FUN_D5:
 	for (i = 0; i < 10; i++) {	/* stable >= bus settle delay(400 ns) */
-		if ((inw(tmport) & 0x2000) != 0) {	/* DB5 all release?       */
+		if ((inw(tmport) & 0x2000) != 0) {	/* DB5 all release?	  */
 			goto FUN_D5;
 		}
 	}
-	*val |= 0x8000;		/* no DB4-0, assert DB7    */
+	*val |= 0x8000; 	/* no DB4-0, assert DB7    */
 	*val &= 0xe0ff;
 	outw(*val, tmport);
-	*val &= 0xbfff;		/* release DB6             */
+	*val &= 0xbfff; 	/* release DB6		   */
 	outw(*val, tmport);
       FUN_D6:
 	for (i = 0; i < 10; i++) {	/* stable >= bus settle delay(400 ns)  */
@@ -893,9 +948,9 @@
 		outb(k, tmport++);
 		tmport = dev->ioport + 0x1b;
 		if (dev->chip_veru == 4) {
-			outb((unsigned char) ((inb(tmport) & 0x0e) | 0x01), tmport);
+			outb(0x01, tmport);
 		} else {
-			outb((unsigned char) (inb(tmport) & 0x0e), tmport);
+			outb(0x00, tmport);
 		}
 wait_rdyok:
 		tmport = dev->ioport + 0x18;
@@ -923,17 +978,17 @@
 
 	outb(0, 0x80);
 
-	val = 0x0080;		/* bsy  */
+	val = 0x0080;		/* bsy	*/
 	tmport = dev->ioport + 0x1c;
 	outw(val, tmport);
-	val |= 0x0040;		/* sel  */
+	val |= 0x0040;		/* sel	*/
 	outw(val, tmport);
-	val |= 0x0004;		/* msg  */
+	val |= 0x0004;		/* msg	*/
 	outw(val, tmport);
 	inb(0x80);		/* 2 deskew delay(45ns*2=90ns) */
 	val &= 0x007f;		/* no bsy  */
 	outw(val, tmport);
-	mydlyu(0xffff);		/* recommanded SCAM selection response time */
+	mydlyu(0xffff); 	/* recommanded SCAM selection response time */
 	mydlyu(0xffff);
 	val &= 0x00fb;		/* after 1ms no msg */
 	outw(val, tmport);
@@ -983,7 +1038,7 @@
 	val |= 0x3f00;
 	fun_scam(dev, &val);
 	outb(3, 0x80);
-	val &= 0x00ff;		/* isolation        */
+	val &= 0x00ff;		/* isolation	    */
 	val |= 0x2000;
 	fun_scam(dev, &val);
 	outb(4, 0x80);
@@ -1018,10 +1073,10 @@
 	printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
 	i = 15;
 	j = mbuf[0];
-	if ((j & 0x20) != 0) {	/* bit5=1:ID upto 7      */
+	if ((j & 0x20) != 0) {	/* bit5=1:ID upto 7	 */
 		i = 7;
 	}
-	if ((j & 0x06) == 0) {	/* IDvalid?             */
+	if ((j & 0x06) == 0) {	/* IDvalid?		*/
 		goto G2Q5;
 	}
 	k = mbuf[1];
@@ -1036,7 +1091,7 @@
 		goto small_id;
 	}
 G2Q5:				/* srch from max acceptable ID#  */
-	k = i;			/* max acceptable ID#            */
+	k = i;			/* max acceptable ID#		 */
 G2Q_LP:
 	m = 1;
 	m <<= k;
@@ -1047,12 +1102,12 @@
 		k--;
 		goto G2Q_LP;
 	}
-G2Q_QUIN:		/* k=binID#,       */
+G2Q_QUIN:		/* k=binID#,	   */
 	assignid_map |= m;
 	if (k < 8) {
 		quintet[0] = 0x38;	/* 1st dft ID<8    */
 	} else {
-		quintet[0] = 0x31;	/* 1st  ID>=8      */
+		quintet[0] = 0x31;	/* 1st	ID>=8	   */
 	}
 	k &= 0x07;
 	quintet[1] = g2q_tab[k];
@@ -1073,15 +1128,15 @@
 void is870(unsigned long host, unsigned int wkport)
 {
 	unsigned int tmport;
-	unsigned char i, j, k, rmb;
+	unsigned char i, j, k, rmb, n;
 	unsigned short int m;
 	static unsigned char mbuf[512];
-	static unsigned char satn[9] = 	{0, 0, 0, 0, 0, 0, 0, 6, 6};
+	static unsigned char satn[9] =	{0, 0, 0, 0, 0, 0, 0, 6, 6};
 	static unsigned char inqd[9] =	{0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6};
-	static unsigned char synn[6] =  {0x80, 1, 3, 1, 0x19, 0x0e};
+	static unsigned char synn[6] =	{0x80, 1, 3, 1, 0x19, 0x0e};
 	static unsigned char synu[6] =	{0x80, 1, 3, 1, 0x0c, 0x0e};
 	static unsigned char synw[6] =	{0x80, 1, 3, 1, 0x0c, 0x07};
-	static unsigned char wide[6] =  {0x80, 1, 2, 3, 1, 0};
+	static unsigned char wide[6] =	{0x80, 1, 2, 3, 1, 0};
 	struct atp_unit *dev = &atp_unit[host];
 
 	sync_idu = 0;
@@ -1101,10 +1156,13 @@
 			printk(KERN_INFO "         ID: %2d  Host Adapter\n", dev->host_idu);
 			continue;
 		}
+		tmport = wkport + 0x1b;
 		if (dev->chip_veru == 4) {
-			tmport = wkport + 0x1b;
-			j = (inb(tmport) & 0x0e) | 0x01;
-			outb(j, tmport);
+		   outb(0x01, tmport);
+		}
+		else
+		{
+		   outb(0x00, tmport);
 		}
 		tmport = wkport + 1;
 		outb(0x08, tmport++);
@@ -1180,10 +1238,9 @@
 			continue;
 		}
 		while (inb(tmport) != 0x8e);
+		tmport = wkport + 0x1b;
 		if (dev->chip_veru == 4) {
-			tmport = wkport + 0x1b;
-			j = inb(tmport) & 0x0e;
-			outb(j, tmport);
+			outb(0x00, tmport);
 		}
 		tmport = wkport + 0x18;
 		outb(0x08, tmport);
@@ -1224,6 +1281,7 @@
 		printk(KERN_INFO "         ID: %2d  %s\n", i, &mbuf[8]);
 		dev->id[i].devtypeu = mbuf[0];
 		rmb = mbuf[1];
+		n = mbuf[7];
 		if (dev->chip_veru != 4) {
 			goto not_wide;
 		}
@@ -1234,8 +1292,7 @@
 			goto not_wide;
 		}
 		tmport = wkport + 0x1b;
-		j = (inb(tmport) & 0x0e) | 0x01;
-		outb(j, tmport);
+		outb(0x01, tmport);
 		tmport = wkport + 3;
 		outb(satn[0], tmport++);
 		outb(satn[1], tmport++);
@@ -1374,13 +1431,15 @@
 		m = m << i;
 		dev->wide_idu |= m;
 not_wide:
-		if ((dev->id[i].devtypeu == 0x00) || (dev->id[i].devtypeu == 0x07)) {
+		if ((dev->id[i].devtypeu == 0x00) || (dev->id[i].devtypeu == 0x07) ||
+		    ((dev->id[i].devtypeu == 0x05) && ((n & 0x10) != 0)))
+		{
 			goto set_sync;
 		}
 		continue;
 set_sync:
 		tmport = wkport + 0x1b;
-		j = inb(tmport) & 0x0e;
+		j = 0;
 		if ((m & dev->wide_idu) != 0) {
 			j |= 0x01;
 		}
@@ -1420,17 +1479,13 @@
 		while ((inb(tmport) & 0x80) == 0) {
 			if ((inb(tmport) & 0x01) != 0) {
 				tmport -= 0x06;
-				if (rmb != 0) {
-					outb(synn[j++], tmport);
+				if ((m & dev->wide_idu) != 0) {
+					outb(synw[j++], tmport);
 				} else {
-					if ((m & dev->wide_idu) != 0) {
-						outb(synw[j++], tmport);
+					if ((m & dev->ultra_map) != 0) {
+						outb(synu[j++], tmport);
 					} else {
-						if ((m & dev->ultra_map) != 0) {
-							outb(synu[j++], tmport);
-						} else {
-							outb(synn[j++], tmport);
-						}
+						outb(synn[j++], tmport);
 					}
 				}
 				tmport += 0x06;
@@ -1579,9 +1634,9 @@
 	int tmpcnt = 0;
 	int count = 0;
 	int result;
-	
-	static unsigned short devid[7] = {
-		0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0
+
+	static unsigned short devid[8] = {
+		0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0x8060, 0
 	};
 
 	printk(KERN_INFO "aec671x_detect: \n");
@@ -1662,7 +1717,7 @@
 		base_io &= 0xfffffff8;
 
 		if (check_region(base_io,0x40) != 0)
-		{  
+		{
 			return 0;
 		}
 		printk(KERN_INFO "   ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d    IO:%x, IRQ:%d.\n"
@@ -1692,6 +1747,13 @@
 			printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n");
 			goto unregister;
 		}
+
+		if (chip_ver[h] > 0x07) 	  /* check if atp876 chip   */
+		{				  /* then enable terminator */
+		   tmport = base_io + 0x3e;
+		   outb(0x30, tmport);
+		}
+
 		tmport = base_io + 0x3a;
 		k = (inb(tmport) & 0xf3) | 0x10;
 		outb(k, tmport);
@@ -1730,7 +1792,7 @@
 		shpnt->n_io_port = 0x40;	/* Number of bytes of I/O space used */
 		shpnt->irq = irq;
 		restore_flags(flags);
-		request_region(base_io, 0x40, "atp870u");	/* Register the IO ports that we use */
+		request_region(base_io, 0x40, "atp870u");       /* Register the IO ports that we use */
 		count++;
 		index++;
 		continue;
@@ -1793,7 +1855,7 @@
 	}
 	panic("Reset bus host not found !");
 find_host:
-/*      SCpnt->result = 0x00080000;
+/*	SCpnt->result = 0x00080000;
 	SCpnt->scsi_done(SCpnt);
 	dev->working=0;
 	dev->quhdu=0;
@@ -1806,14 +1868,14 @@
 {
 	static char buffer[128];
 
-	strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.0+ac ");
+	strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.1+ac ");
 
 	return buffer;
 }
 
 int atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
 {
-	return -ENOSYS;		/* Currently this is a no-op */
+	return -ENOSYS; 	/* Currently this is a no-op */
 }
 
 #define BLS buffer + len + size
@@ -1851,7 +1913,7 @@
 	if (offset == 0) {
 		memset(buff, 0, sizeof(buff));
 	}
-	size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.0+ac\n");
+	size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.1+ac\n");
 	len += size;
 	pos = begin + len;
 	size = 0;
@@ -1899,7 +1961,7 @@
 int atp870u_release (struct Scsi_Host *pshost)
 {
 	int h;
-	for (h = 0; h <= admaxu; h++) 
+	for (h = 0; h <= admaxu; h++)
 	{
 		if (pshost == atp_host[h]) {
 			int k;
@@ -1912,7 +1974,7 @@
 		}
 	}
 	panic("atp870u: bad scsi host passed.\n");
-		
+
 }
 
 #ifdef MODULE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/atp870u.h linux.18p17/drivers/scsi/atp870u.h
--- linux.vanilla/drivers/scsi/atp870u.h	Fri Apr 21 23:20:06 2000
+++ linux.18p17/drivers/scsi/atp870u.h	Thu Oct 19 01:37:49 2000
@@ -1,7 +1,7 @@
 #ifndef _ATP870U_H
 
 /* $Id: atp870u.h,v 1.0 1997/05/07 15:09:00 root Exp root $
-
+ *
  * Header file for the ACARD 870U/W driver for Linux
  *
  * $Log: atp870u.h,v $
@@ -13,6 +13,14 @@
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
 /* I/O Port */
 
 #define MAX_CDB 12
@@ -20,10 +28,10 @@
 
 int atp870u_detect(Scsi_Host_Template *);
 int atp870u_command(Scsi_Cmnd *);
-int atp870u_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int atp870u_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int atp870u_abort(Scsi_Cmnd *);
 int atp870u_reset(Scsi_Cmnd *, unsigned int);
-int atp870u_biosparam(Disk *, kdev_t, int *);
+int atp870u_biosparam(Disk *, kdev_t, int*);
 int atp870u_release(struct Scsi_Host *);
 void send_s870(unsigned char);
 
@@ -32,18 +40,20 @@
 #define ATP870U_CMDLUN 1
 
 #ifndef NULL
-#define NULL 0
+	#define NULL 0
 #endif
 
+extern struct proc_dir_entry proc_scsi_atp870u;
+
 extern const char *atp870u_info(struct Scsi_Host *);
 
 extern int atp870u_proc_info(char *, char **, off_t, int, int, int);
-extern struct proc_dir_entry proc_scsi_atp870u;
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,65)
 #define ATP870U {						\
 	next: NULL,						\
 	module: NULL,						\
-	proc_dir:			&proc_scsi_atp870u,	\
+	proc_dir: &proc_scsi_atp870u,/* proc_dir_entry */	\
 	proc_info: atp870u_proc_info,				\
 	name: NULL,						\
 	detect: atp870u_detect, 				\
@@ -69,5 +79,26 @@
 	use_clustering: ENABLE_CLUSTERING,			\
 	use_new_eh_code: 0					\
 }
-
+#else
+#define ATP870U {  NULL, NULL,				\
+		     &proc_scsi_atp870u,/* proc_dir_entry */ \
+		     atp870u_proc_info, 		\
+		     NULL,				\
+		     atp870u_detect,			\
+		     NULL,				\
+		     atp870u_info,			\
+		     atp870u_command,			\
+		     atp870u_queuecommand,		\
+		     atp870u_abort,			\
+		     atp870u_reset,			\
+		     NULL,				\
+		     atp870u_biosparam, 		\
+		     qcnt,				\
+		     7, 				\
+		     ATP870U_SCATTER,			\
+		     ATP870U_CMDLUN,			\
+		     0, 				\
+		     0, 				\
+		     ENABLE_CLUSTERING}
+#endif
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfc.Readme linux.18p17/drivers/scsi/cpqfc.Readme
--- linux.vanilla/drivers/scsi/cpqfc.Readme	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfc.Readme	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,216 @@
+Notes for CPQFCTS driver for Compaq Tachyon TS
+Fibre Channel Host Bus Adapter, PCI 64-bit, 66MHz
+for Linux (RH 6.1, 6.2 kernel 2.2.12-32, 2.2.14-5)
+SMP tested
+Tested in single and dual HBA configuration, 32 and 64bit busses,
+33 and 66MHz.  Only supports FC-AL.
+SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc() 
+	max of 128k bytes contiguous.
+Ver 1.3.4  Sep   7, 2000
+  Added Modinfo information
+  Fixed problem with statically linking the driver
+
+Ver 1.3.3, Aug  23, 2000
+  Fixed device/function number in ioctl
+
+Ver 1.3.2, July 27, 2000
+  Add include for Alpha compile on 2.2.14 kernel (cpq*i2c.c)
+  Change logic for different FCP-RSP sense_buffer location for HSG80 target
+  And search for Agilent Tachyon XL2 HBAs (not finished! - in test)
+
+Tested with 
+(storage):
+           Compaq RA-4x000, RAID firmware ver 2.40 - 2.54
+           Seagate FC drives model ST39102FC, rev 0006
+           Hitachi DK31CJ-72FC rev J8A8
+           IBM DDYF-T18350R rev F60K
+           Compaq FC-SCSI bridge w/ DLT 35/70 Gb DLT (tape)
+(servers):
+           Compaq PL-1850R
+           Compaq PL-6500 Xeon (400MHz)
+	   Compaq PL-8500 (500MHz, 66MHz, 64bit PCI)
+           Compaq Alpha DS20 (RH 6.1)
+(hubs):
+           Vixel Rapport 1000 (7-port "dumb")
+	   Gadzoox Gibralter (12-port "dumb")
+	   Gadzoox Capellix 2000, 3000 
+(switches):
+           Brocade 2010, 2400, 2800, rev 2.0.3a (& later)
+           Gadzoox 3210 (Fabric blade beta)
+           Vixel 7100 (Fabric beta firmare - known hot plug issues)
+using "qa_test" (esp. io_test script) suite modified from Unix tests.
+	
+Installation:
+copy file cpqfcTS.patch to /usr/src/linux
+patch -p1 < cpqfcTS.patch
+make menuconfig
+  (select SCSI low-level, Compaq FC HBA)
+make dep
+make modules
+make modules_install
+
+e.g. insmod -f cpqfc
+
+Due to Fabric/switch delays, driver requires 4 seconds 
+to initialize.  If adapters are found, there will be a entries at
+/proc/scsi/cpqfcTS/*
+
+sample contents of startup messages
+
+*************************
+ scsi_register allocating 3596 bytes for CPQFCHBA
+ ioremap'd Membase: c887e600
+  HBA Tachyon RevId 1.2
+Allocating 119808 for 576 Exchanges @ c0dc0000
+Allocating 112904 for LinkQ @ c0c20000 (576 elements)
+Allocating 110600 for TachSEST for 512 Exchanges
+  cpqfcTS: writing IMQ BASE 7C0000h    PI 7C4000h
+  cpqfcTS: SEST c0e40000(virt): Wrote base E40000h @ c887e740
+cpqfcTS: New FC port 0000E8h WWN: 500507650642499D SCSI Chan/Trgt 0/0
+cpqfcTS: New FC port 0000EFh WWN: 50000E100000D5A6 SCSI Chan/Trgt 0/1
+cpqfcTS: New FC port 0000E4h WWN: 21000020370097BB SCSI Chan/Trgt 0/2
+cpqfcTS: New FC port 0000E2h WWN: 2100002037009946 SCSI Chan/Trgt 0/3
+cpqfcTS: New FC port 0000E1h WWN: 21000020370098FE SCSI Chan/Trgt 0/4
+cpqfcTS: New FC port 0000E0h WWN: 21000020370097B2 SCSI Chan/Trgt 0/5
+cpqfcTS: New FC port 0000DCh WWN: 2100002037006CC1 SCSI Chan/Trgt 0/6
+cpqfcTS: New FC port 0000DAh WWN: 21000020370059F6 SCSI Chan/Trgt 0/7
+cpqfcTS: New FC port 00000Fh WWN: 500805F1FADB0E20 SCSI Chan/Trgt 0/8
+cpqfcTS: New FC port 000008h WWN: 500805F1FADB0EBA SCSI Chan/Trgt 0/9
+cpqfcTS: New FC port 000004h WWN: 500805F1FADB1EB9 SCSI Chan/Trgt 0/10
+cpqfcTS: New FC port 000002h WWN: 500805F1FADB1ADE SCSI Chan/Trgt 0/11
+cpqfcTS: New FC port 000001h WWN: 500805F1FADBA2CA SCSI Chan/Trgt 0/12
+scsi4 : Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2: WWN 500508B200193F50
+ on PCI bus 0 device 0xa0fc irq 5 IObaseL 0x3400, MEMBASE 0xc6ef8600
+PCI bus width 32 bits, bus speed 33 MHz
+FCP-SCSI Driver v1.3.0
+GBIC detected: Short-wave.  LPSM 0h Monitor
+scsi : 5 hosts.
+  Vendor: IBM       Model: DDYF-T18350R      Rev: F60K
+  Type:   Direct-Access                      ANSI SCSI revision: 03
+Detected scsi disk sdb at scsi4, channel 0, id 0, lun 0
+  Vendor: HITACHI   Model: DK31CJ-72FC       Rev: J8A8
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdc at scsi4, channel 0, id 1, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdd at scsi4, channel 0, id 2, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sde at scsi4, channel 0, id 3, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdf at scsi4, channel 0, id 4, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdg at scsi4, channel 0, id 5, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdh at scsi4, channel 0, id 6, lun 0
+  Vendor: SEAGATE   Model: ST39102FC         Rev: 0006
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdi at scsi4, channel 0, id 7, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.48
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdj at scsi4, channel 0, id 8, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.48
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdk at scsi4, channel 0, id 8, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.40
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdl at scsi4, channel 0, id 9, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.40
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdm at scsi4, channel 0, id 9, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdn at scsi4, channel 0, id 10, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdo at scsi4, channel 0, id 11, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdp at scsi4, channel 0, id 11, lun 1
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdq at scsi4, channel 0, id 12, lun 0
+  Vendor: COMPAQ    Model: LOGICAL VOLUME    Rev: 2.54
+  Type:   Direct-Access                      ANSI SCSI revision: 02
+Detected scsi disk sdr at scsi4, channel 0, id 12, lun 1
+resize_dma_pool: unknown device type 12
+resize_dma_pool: unknown device type 12
+SCSI device sdb: hdwr sector= 512 bytes. Sectors= 35843670 [17501 MB] [17.5 GB]
+ sdb: sdb1
+SCSI device sdc: hdwr sector= 512 bytes. Sectors= 144410880 [70513 MB] [70.5 GB]
+ sdc: sdc1
+SCSI device sdd: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdd: sdd1
+SCSI device sde: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sde: sde1
+SCSI device sdf: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdf: sdf1
+SCSI device sdg: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdg: sdg1
+SCSI device sdh: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdh: sdh1
+SCSI device sdi: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdi: sdi1
+SCSI device sdj: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdj: sdj1
+SCSI device sdk: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdk: sdk1
+SCSI device sdl: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdl: sdl1
+SCSI device sdm: hdwr sector= 512 bytes. Sectors= 8380320 [4091 MB] [4.1 GB]
+ sdm: sdm1
+SCSI device sdn: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdn: sdn1
+SCSI device sdo: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdo: sdo1
+SCSI device sdp: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdp: sdp1
+SCSI device sdq: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdq: sdq1
+SCSI device sdr: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdr: sdr1
+
+*************************
+
+If a GBIC of type Short-wave, Long-wave, or Copper is detected, it will
+print out; otherwise, "none" is displayed.  If the cabling is correct
+and a loop circuit is completed, you should see "Monitor"; otherwise, 
+"LoopFail" (on open circuit) or some LPSM number/state with bit 3 set.
+
+
+ERRATA:
+1. Normally, Linux Scsi queries FC devices with INQUIRY strings.  All LUNs
+found according to INQUIRY should get READ commands at sector 0 to find
+partition table, etc.  Older kernels only query the first 4 devices.  Some
+Linux kernels only look for one LUN per target (i.e. FC device).
+
+2. Physically removing a device, or a malfunctioning system which hides a
+device, leads to a 30-second timeout and subsequent _abort call.  
+In some process contexts, this will hang the kernel (crashing the system).
+Single bit errors in frames and virtually all hot plugging events are 
+gracefully handled with internal driver timer and Abort processing.
+
+3. Some SCSI drives with error conditions will not handle the 7 second timeout
+in this software driver, leading to infinite retries on timed out SCSI commands.
+The 7 secs balances the need to quickly recover from lost frames (esp. on sequence
+initiatives) and time needed by older/slower/error-state drives in responding.
+This can be easily changed in "Exchanges[].timeOut".
+
+4. Due to the nature of FC soft addressing, there is no assurance that the 
+same LUNs (drives) will have the same path (e.g. /dev/sdb1) from one boot to
+next.  Dynamic soft address changes (i.e. 24-bit FC port_id) are
+supported during run time (e.g. due to hot plug event) by the use of WWN to
+SCSI Nexus (channel/target/LUN) mapping.
+
+5. Compaq RA4x00 firmware version 2.54 and later supports SSP (Selective 
+Storage Presentation), which maps LUNs to a WWN.  If RA4x00 firmware prior
+2.54 (e.g. older controller) is used, or the FC HBA is replaced (another WWN
+is used), logical volumes on the RA4x00 will no longer be visible.
+
+
+Send questions/comments to:
+donald.zimmerman@compaq.com
+dszimmerman@yahoo.com
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTS.h linux.18p17/drivers/scsi/cpqfcTS.h
--- linux.vanilla/drivers/scsi/cpqfcTS.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTS.h	Mon Oct  2 11:11:45 2000
@@ -0,0 +1,39 @@
+#ifndef CPQFCTS_H
+#define CPQFCTS_H
+#include "cpqfcTSstructs.h"
+
+// These functions are required by the Linux SCSI layers
+extern int cpqfcTS_detect(Scsi_Host_Template *);
+extern int cpqfcTS_release(struct Scsi_Host *);
+const char * cpqfcTS_info(struct Scsi_Host *);
+extern int cpqfcTS_proc_info(char *, char **, off_t, int, int, int);
+extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+extern int cpqfcTS_abort(Scsi_Cmnd *);
+extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int);
+extern int cpqfcTS_biosparam(Disk *, kdev_t, int[]);
+extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg);
+
+// note: since Tachyon TS supports an extended scatter/gather
+// linked list of infinite length (with linked Ext S/G pages,
+// limited only by available physical memory) we use SG_ALL.
+
+#define CPQFCTS {                                \
+ detect:                 cpqfcTS_detect,         \
+ release:                cpqfcTS_release,        \
+ info:                   cpqfcTS_info,           \
+ proc_info:              cpqfcTS_proc_info,      \
+ ioctl:                  cpqfcTS_ioctl,          \
+ queuecommand:           cpqfcTS_queuecommand,   \
+ eh_abort_handler:       cpqfcTS_abort,          \
+ reset:                  cpqfcTS_reset,          \
+ bios_param:             cpqfcTS_biosparam,      \
+ can_queue:              CPQFCTS_REQ_QUEUE_LEN,  \
+ this_id:                -1,                     \
+ sg_tablesize:           SG_ALL,                 \
+ cmd_per_lun:            CPQFCTS_CMD_PER_LUN,    \
+ present:                0,                      \
+ unchecked_isa_dma:      0,                      \
+ use_clustering:         ENABLE_CLUSTERING       \
+}
+
+#endif /* CPQFCTS_H */ 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSchip.h linux.18p17/drivers/scsi/cpqfcTSchip.h
--- linux.vanilla/drivers/scsi/cpqfcTSchip.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSchip.h	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSCHIP_H
+#define CPQFCTSCHIP_H
+#ifndef TACHYON_CHIP_INC
+
+// FC-PH (Physical) specification levels for Login payloads
+// NOTE: These are NOT strictly complied with by any FC vendors
+
+#define FC_PH42			0x08
+#define FC_PH43			0x09
+#define FC_PH3			0x20
+
+#define TACHLITE_TS_RX_SIZE     1024  // max inbound frame size
+// "I" prefix is for Include
+
+#define IVENDID    0x00  // word
+#define IDEVID     0x02
+#define ITLCFGCMD 0x04
+#define IMEMBASE   0x18    // Tachyon
+#define ITLMEMBASE   0x1C  // Tachlite
+#define IIOBASEL   0x10    // Tachyon I/O base address, lower 256 bytes
+#define IIOBASEU   0x14    // Tachyon I/O base address, upper 256 bytes
+#define ITLIOBASEL   0x14  // TachLite I/O base address, lower 256 bytes
+#define ITLIOBASEU   0x18  // TachLite I/O base address, upper 256 bytes
+#define ITLRAMBASE   0x20  // TL on-board RAM start
+#define ISROMBASE  0x24
+#define IROMBASE   0x30
+
+#define ICFGCMD    0x04    // PCI config - PCI config access (word)
+#define ICFGSTAT   0x06    // PCI status (R - word)
+#define IRCTR_WCTR 0x1F2   // ROM control / pre-fetch wait counter
+#define IPCIMCTR   0x1F3   // PCI master control register
+#define IINTPEND   0x1FD   // Interrupt pending (I/O Upper - Tachyon & TL)
+#define IINTEN     0x1FE   // Interrupt enable  (I/O Upper - Tachyon & TL)
+#define IINTSTAT   0x1FF   // Interrupt status  (I/O Upper - Tachyon & TL)
+
+#define IMQ_BASE            0x80
+#define IMQ_LENGTH          0x84
+#define IMQ_CONSUMER_INDEX  0x88
+#define IMQ_PRODUCER_INDEX  0x8C   // Tach copies its INDX to bits 0-7 of value
+
+/*
+// IOBASE UPPER
+#define SFSBQ_BASE            0x00   // single-frame sequences
+#define SFSBQ_LENGTH          0x04
+#define SFSBQ_PRODUCER_INDEX  0x08
+#define SFSBQ_CONSUMER_INDEX  0x0C   // (R)
+#define SFS_BUFFER_LENGTH     0X10
+                              // SCSI-FCP hardware assists
+#define SEST_BASE             0x40   // SSCI Exchange State Table
+#define SEST_LENGTH           0x44
+#define SCSI_BUFFER_LENGTH    0x48
+#define SEST_LINKED_LIST      0x4C
+
+#define TACHYON_My_ID         0x6C
+#define TACHYON_CONFIGURATION 0x84   // (R/W) reset val 2
+#define TACHYON_CONTROL       0x88
+#define TACHYON_STATUS        0x8C   // (R)
+#define TACHYON_FLUSH_SEST    0x90   // (R/W)
+#define TACHYON_EE_CREDIT_TMR 0x94   // (R)
+#define TACHYON_BB_CREDIT_TMR 0x98   // (R)
+#define TACHYON_RCV_FRAME_ERR 0x9C   // (R)
+#define FRAME_MANAGER_CONFIG  0xC0   // (R/W)
+#define FRAME_MANAGER_CONTROL 0xC4
+#define FRAME_MANAGER_STATUS  0xC8   // (R)
+#define FRAME_MANAGER_ED_TOV  0xCC
+#define FRAME_MANAGER_LINK_ERR1 0xD0   // (R)
+#define FRAME_MANAGER_LINK_ERR2 0xD4   // (R)
+#define FRAME_MANAGER_TIMEOUT2  0xD8   // (W)
+#define FRAME_MANAGER_BB_CREDIT 0xDC   // (R)
+#define FRAME_MANAGER_WWN_HI    0xE0   // (R/W)
+#define FRAME_MANAGER_WWN_LO    0xE4   // (R/W)
+#define FRAME_MANAGER_RCV_AL_PA 0xE8   // (R)
+#define FRAME_MANAGER_PRIMITIVE 0xEC   // {K28.5} byte1 byte2 byte3
+*/
+		    
+#define TL_MEM_ERQ_BASE                    0x0 //ERQ Base
+#define TL_IO_ERQ_BASE                     0x0 //ERQ base
+
+#define TL_MEM_ERQ_LENGTH                  0x4 //ERQ Length
+#define TL_IO_ERQ_LENGTH                   0x4 //ERQ Length
+
+#define TL_MEM_ERQ_PRODUCER_INDEX          0x8 //ERQ Producer Index  register
+#define TL_IO_ERQ_PRODUCER_INDEX           0x8 //ERQ Producer Index  register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+#define TL_IO_ERQ_CONSUMER_INDEX_ADR  0xC //ERQ Consumer Index address register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX     0xC //ERQ Consumer Index 
+#define TL_IO_ERQ_CONSUMER_INDEX      0xC //ERQ Consumer Index 
+
+#define TL_MEM_SFQ_BASE               0x50 //SFQ Base
+#define TL_IO_SFQ_BASE                0x50 //SFQ base
+
+#define TL_MEM_SFQ_LENGTH             0x54 //SFQ Length
+#define TL_IO_SFQ_LENGTH              0x54 //SFQ Length
+
+#define TL_MEM_SFQ_CONSUMER_INDEX     0x58 //SFQ Consumer Index
+#define TL_IO_SFQ_CONSUMER_INDEX      0x58 //SFQ Consumer Index
+
+#define TL_MEM_IMQ_BASE               0x80 //IMQ Base
+#define TL_IO_IMQ_BASE                0x80 //IMQ base
+
+#define TL_MEM_IMQ_LENGTH             0x84 //IMQ Length
+#define TL_IO_IMQ_LENGTH              0x84 //IMQ Length
+
+#define TL_MEM_IMQ_CONSUMER_INDEX     0x88 //IMQ Consumer Index
+#define TL_IO_IMQ_CONSUMER_INDEX      0x88 //IMQ Consumer Index
+
+#define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+#define TL_IO_IMQ_PRODUCER_INDEX_ADR  0x8C //IMQ Producer Index address register
+
+#define TL_MEM_SEST_BASE              0x140 //SFQ Base
+#define TL_IO_SEST_BASE               0x40 //SFQ base
+
+#define TL_MEM_SEST_LENGTH            0x144 //SFQ Length
+#define TL_IO_SEST_LENGTH             0x44 //SFQ Length
+
+#define TL_MEM_SEST_LINKED_LIST       0x14C
+
+#define TL_MEM_SEST_SG_PAGE           0x168  // Extended Scatter/Gather page size
+
+#define TL_MEM_TACH_My_ID             0x16C
+#define TL_IO_TACH_My_ID              0x6C //My AL_PA ID
+
+#define TL_MEM_TACH_CONFIG            0x184 //Tachlite Configuration register
+#define TL_IO_CONFIG                  0x84 //Tachlite Configuration register
+
+#define TL_MEM_TACH_CONTROL           0x188 //Tachlite Control register
+#define TL_IO_CTR                     0x88 //Tachlite Control register
+
+#define TL_MEM_TACH_STATUS            0x18C //Tachlite Status register
+#define TL_IO_STAT                    0x8C //Tachlite Status register
+
+#define TL_MEM_FM_CONFIG        0x1C0 //Frame Manager Configuration register
+#define TL_IO_FM_CONFIG         0xC0 //Frame Manager Configuration register
+
+#define TL_MEM_FM_CONTROL       0x1C4 //Frame Manager Control
+#define TL_IO_FM_CTL            0xC4 //Frame Manager Control
+
+#define TL_MEM_FM_STATUS        0x1C8 //Frame Manager Status
+#define TL_IO_FM_STAT           0xC8 //Frame Manager Status
+
+#define TL_MEM_FM_LINK_STAT1    0x1D0 //Frame Manager Link Status 1
+#define TL_IO_FM_LINK_STAT1     0xD0 //Frame Manager Link Status 1
+
+#define TL_MEM_FM_LINK_STAT2    0x1D4 //Frame Manager Link Status 2
+#define TL_IO_FM_LINK_STAT2     0xD4 //Frame Manager Link Status 2
+
+#define TL_MEM_FM_TIMEOUT2      0x1D8   // (W)
+
+#define TL_MEM_FM_BB_CREDIT0    0x1DC
+
+#define TL_MEM_FM_WWN_HI        0x1E0 //Frame Manager World Wide Name High
+#define TL_IO_FM_WWN_HI         0xE0 //Frame Manager World Wide Name High
+
+#define TL_MEM_FM_WWN_LO        0x1E4 //Frame Manager World Wide Name LOW
+#define TL_IO_FM_WWN_LO         0xE4 //Frame Manager World Wide Name Low
+
+#define TL_MEM_FM_RCV_AL_PA     0x1E8 //Frame Manager AL_PA Received register
+#define TL_IO_FM_ALPA           0xE8 //Frame Manager AL_PA Received register
+
+#define TL_MEM_FM_ED_TOV           0x1CC
+
+#define TL_IO_ROMCTR            0xFA //TL PCI ROM Control Register
+#define TL_IO_PCIMCTR           0xFB //TL PCI Master Control Register
+#define TL_IO_SOFTRST           0xFC //Tachlite Configuration register
+#define TL_MEM_SOFTRST          0x1FC //Tachlite Configuration register
+
+// completion message types (bit 8 set means Interrupt generated)
+// CM_Type
+#define OUTBOUND_COMPLETION        0
+#define ERROR_IDLE_COMPLETION   0x01
+#define OUT_HI_PRI_COMPLETION   0x01
+#define INBOUND_MFS_COMPLETION  0x02
+#define INBOUND_000_COMPLETION  0x03
+#define INBOUND_SFS_COMPLETION  0x04  // Tachyon & TachLite
+#define ERQ_FROZEN_COMPLETION   0x06  // TachLite
+#define INBOUND_C1_TIMEOUT      0x05
+#define INBOUND_BUSIED_FRAME    0x06
+#define SFS_BUF_WARN            0x07
+#define FCP_FROZEN_COMPLETION   0x07  // TachLite
+#define MFS_BUF_WARN            0x08
+#define IMQ_BUF_WARN            0x09
+#define FRAME_MGR_INTERRUPT     0x0A
+#define READ_STATUS             0x0B
+#define INBOUND_SCSI_DATA_COMPLETION  0x0C
+#define INBOUND_FCP_XCHG_COMPLETION   0x0C  // TachLite
+#define INBOUND_SCSI_DATA_COMMAND     0x0D
+#define BAD_SCSI_FRAME                0x0E
+#define INB_SCSI_STATUS_COMPLETION    0x0F
+#define BUFFER_PROCESSED_COMPLETION   0x11
+
+// FC-AL (Tachyon) Loop Port State Machine defs
+// (loop "Up" states)
+#define MONITORING 0x0
+#define ARBITRATING 0x1
+#define ARBITRAT_WON 0x2
+#define OPEN 0x3
+#define OPENED 0x4
+#define XMITTD_CLOSE 0x5
+#define RCVD_CLOSE 0x6
+#define TRANSFER 0x7
+
+// (loop "Down" states)
+#define INITIALIZING 0x8
+#define O_I_INIT 0x9
+#define O_I_PROTOCOL 0xa
+#define O_I_LIP_RCVD 0xb
+#define HOST_CONTROL 0xc
+#define LOOP_FAIL 0xd
+// (no 0xe)
+#define OLD_PORT 0xf
+
+
+
+#define TACHYON_CHIP_INC
+#endif
+#endif /* CPQFCTSCHIP_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTScontrol.c linux.18p17/drivers/scsi/cpqfcTScontrol.c
--- linux.vanilla/drivers/scsi/cpqfcTScontrol.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTScontrol.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,2200 @@
+/* Copyright 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+/* These functions control the host bus adapter (HBA) hardware.  The main chip
+   control takes place in the interrupt handler where we process the IMQ 
+   (Inbound Message Queue).  The IMQ is Tachyon's way of communicating FC link
+   events and state information to the driver.  The Single Frame Queue (SFQ)
+   buffers incoming FC frames for processing by the driver.  References to 
+   "TL/TS UG" are for:
+   "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed.
+   Hewlitt Packard Manual Part Number 5968-1083E.
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>  // request_region() prototype
+#include <linux/sched.h>
+#include <linux/malloc.h>  // need "kfree" for ext. S/G pages
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <asm/io.h>  // struct pt_regs for IRQ handler & Port I/O
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+
+#include "sd.h"
+#include "hosts.h"   // Scsi_Host definition for INT handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define IMQ_DEBUG 1
+
+static void fcParseLinkStatusCounters(TACHYON * fcChip);
+static void CpqTsGetSFQEntry(TACHYON * fcChip, 
+	      USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); 
+
+
+// Note special requirements for Q alignment!  (TL/TS UG pg. 190)
+// We place critical index pointers at end of QUE elements to assist
+// in non-symbolic (i.e. memory dump) debugging
+// opcode defines placement of Queues (e.g. local/external RAM)
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+  int iStatus=0;
+  unsigned long ulAddr;
+
+
+  // NOTE! fcMemManager() will return system virtual addresses.
+  // System (kernel) virtual addresses, though non-paged, still
+  // aren't physical addresses.  Convert to PHYSICAL_ADDRESS for Tachyon's
+  // DMA use.
+  ENTER("CreateTachLiteQues");
+
+
+  // Allocate primary EXCHANGES array...
+  
+  printk("Allocating %u for %u Exchanges ", 
+	  (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID);
+  fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL );
+  printk("@ %p\n", fcChip->Exchanges);
+
+  if( fcChip->Exchanges == NULL ) // fatal error!!
+  {
+    printk("kmalloc failure on Exchanges: fatal error\n");
+    return -1;
+  }
+  // zero out the entire EXCHANGE space
+  memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES));  
+
+
+  printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE));
+  cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL );
+  printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH);
+  memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));
+
+  if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!!
+  {
+    printk("kmalloc failure on fc Link Que: fatal error\n");
+    return -1;
+  }
+  // zero out the entire EXCHANGE space
+  memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));  
+
+
+
+  
+  // Verify that basic Tach I/O registers are not NULL  
+  
+  if( !fcChip->Registers.ReMapMemBase )
+  {
+    printk("HBA base address NULL: fatal error\n");
+    return -1;
+  }
+
+
+  // Initialize the fcMemManager memory pairs (stores allocated/aligned
+  // pairs for future freeing)
+  memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem));
+  
+
+  // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes)
+  
+  fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L );
+  if( !fcChip->ERQ )
+  {
+    printk("kmalloc/alignment failure on ERQ: fatal error\n");
+    return -1;
+  }
+  fcChip->ERQ->length = ERQ_LEN-1;
+  ulAddr = virt_to_bus( fcChip->ERQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->ERQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate Tach's Inbound Message Queue (32 bytes per entry)
+  
+  fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L );
+  if( !fcChip->IMQ )
+  {
+    printk("kmalloc/alignment failure on IMQ: fatal error\n");
+    return -1;
+  }
+  fcChip->IMQ->length = IMQ_LEN-1;
+
+  ulAddr = virt_to_bus( fcChip->IMQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->IMQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate Tach's  Single Frame Queue (64 bytes per entry)
+  fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L );
+  if( !fcChip->SFQ )
+  {
+    printk("kmalloc/alignment failure on SFQ: fatal error\n");
+    return -1;
+  }
+  fcChip->SFQ->length = SFQ_LEN-1;      // i.e. Que length [# entries -
+                                       // min. 32; max.  4096 (0xffff)]
+  
+  ulAddr = virt_to_bus( fcChip->SFQ);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->SFQ->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+  // Allocate SCSI Exchange State Table; aligned nearest @sizeof
+  // power-of-2 boundary
+  // LIVE DANGEROUSLY!  Assume the boundary for SEST mem will
+  // be on physical page (e.g. 4k) boundary.
+  printk("Allocating %u for TachSEST for %u Exchanges\n", 
+		 (ULONG)sizeof(TachSEST), TACH_SEST_LEN);
+  fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		  sizeof(TachSEST),  4, 0L );
+//		  sizeof(TachSEST),  64*TACH_SEST_LEN, 0L );
+  if( !fcChip->SEST )
+  {
+    printk("kmalloc/alignment failure on SEST: fatal error\n");
+    return -1;
+  }
+
+  fcChip->SEST->length = TACH_SEST_LEN;  // e.g. DON'T subtract one 
+                                       // (TL/TS UG, pg 153)
+
+  ulAddr = virt_to_bus( fcChip->SEST);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+  fcChip->SEST->base = (ULONG)ulAddr;  // copy for quick reference
+
+
+			      // Now that structures are defined,
+			      // fill in Tachyon chip registers...
+
+			      // EEEEEEEE  EXCHANGE REQUEST QUEUE
+
+  writel( fcChip->ERQ->base, 
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+      
+  writel( fcChip->ERQ->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH));
+     
+
+  fcChip->ERQ->producerIndex = 0L;
+  writel( fcChip->ERQ->producerIndex,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX));
+      
+
+		// NOTE! write consumer index last, since the write
+		// causes Tachyon to process the other registers
+
+  ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex);
+
+  // NOTE! Tachyon DMAs to the ERQ consumer Index host
+		// address; must be correctly aligned
+  writel( (ULONG)ulAddr,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR));
+
+
+
+				 // IIIIIIIIIIIII  INBOUND MESSAGE QUEUE
+				 // Tell Tachyon where the Que starts
+
+  // set the Host's pointer for Tachyon to access
+
+  printk("  cpqfcTS: writing IMQ BASE %Xh  ", fcChip->IMQ->base );
+  writel( fcChip->IMQ->base, 
+    (fcChip->Registers.ReMapMemBase + IMQ_BASE));
+
+  writel( fcChip->IMQ->length,
+    (fcChip->Registers.ReMapMemBase + IMQ_LENGTH));
+
+  writel( fcChip->IMQ->consumerIndex,
+    (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+
+		// NOTE: TachLite DMAs to the producerIndex host address
+		// must be correctly aligned with address bits 1-0 cleared
+    // Writing the BASE register clears the PI register, so write it last
+  ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex);
+#if BITS_PER_LONG > 32
+  if( (ulAddr >> 32) )
+  {
+    printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+		    (void*)ulAddr);
+    return -1;  // failed
+  }
+#endif
+//#if DBG
+  printk("  PI %Xh\n", (ULONG)ulAddr );
+//#endif
+  writel( (ULONG)ulAddr, 
+    (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX));
+
+
+
+				 // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE
+				 // Tell TachLite where the Que starts
+
+  writel( fcChip->SFQ->base, 
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE));
+
+  writel( fcChip->SFQ->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH));
+
+
+         // tell TachLite where SEST table is & how long
+  writel( fcChip->SEST->base,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE));
+
+  printk("  cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n",
+    fcChip->SEST, fcChip->SEST->base, 
+    fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE);
+
+  writel( fcChip->SEST->length,
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH));
+      
+  writel( (TL_EXT_SG_PAGE_COUNT-1),
+    (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE));
+
+
+  LEAVE("CreateTachLiteQues");
+
+  return iStatus;
+}
+
+
+
+// function to return TachLite to Power On state
+// 1st - reset tachyon ('SOFT' reset)
+// others - future
+
+int CpqTsResetTachLite(void *pHBA, int type)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  ULONG ulBuff, i;
+  int ret_status=0; // def. success
+
+  ENTER("ResetTach");
+  
+  switch(type)
+  {
+
+    case CLEAR_FCPORTS:
+
+      // in case he was running previously, mask Tach's interrupt
+      writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+      
+     // de-allocate mem for any Logged in ports
+      // (e.g., our module is unloading)
+      // search the forward linked list, de-allocating
+      // the memory we allocated when the port was initially logged in
+      {
+        PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort;
+        PFC_LOGGEDIN_PORT ptr;
+//        printk("checking for allocated LoggedInPorts...\n");
+			
+        while( pLoggedInPort )
+        {
+          ptr = pLoggedInPort;
+          pLoggedInPort = ptr->pNextPort;
+//	  printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n",
+//			  ptr, ptr->port_id);
+          kfree( ptr );
+        }
+      }
+      // (continue resetting hardware...)
+
+    case 1:                   // RESTART Tachyon (power-up state)
+
+      // in case he was running previously, mask Tach's interrupt
+      writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+			      // turn OFF laser (NOTE: laser is turned
+                              // off during reset, because GPIO4 is cleared
+                              // to 0 by reset action - see TLUM, sec 7.22)
+                              // However, CPQ 64-bit HBAs have a "health
+                              // circuit" which keeps laser ON for a brief
+                              // period after it is turned off ( < 1s)
+      
+      fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0);
+  
+
+
+            // soft reset timing constraints require:
+            //   1. set RST to 1
+            //   2. read SOFTRST register 
+            //      (128 times per R. Callison code)
+            //   3. clear PCI ints
+            //   4. clear RST to 0
+      writel( 0xff000001L,
+        (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+        
+      for( i=0; i<128; i++)
+        ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST);
+
+        // clear the soft reset
+      for( i=0; i<8; i++)
+  	writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+               
+
+			       // clear out our copy of Tach regs,
+			       // because they must be invalid now,
+			       // since TachLite reset all his regs.
+      CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs
+      cpqfcTSClearLinkStatusCounters(fcChip);  // clear our s/w accumulators
+                               // lower bits give GBIC info
+      fcChip->Registers.TYstatus.value = 
+	              readl( fcChip->Registers.TYstatus.address );
+      break;
+
+/*
+    case 2:                   // freeze SCSI
+    case 3:                   // reset Outbound command que (ERQ)
+    case 4:                   // unfreeze OSM (Outbound Seq. Man.) 'er'
+    case 5:                   // report status
+
+    break;
+*/
+    default:
+      ret_status = -1;  // invalid option passed to RESET function
+      break;
+  }
+  LEAVE("ResetTach");
+  return ret_status;
+}
+
+
+
+
+
+
+// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon
+int CpqTsLaserControl( void* addrBase, int opcode )
+{
+  ULONG dwBuff;
+
+  dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg
+                                                    // (change only bit 4)
+  if( opcode == 1)
+    dwBuff |= ~0xffffffefL; // set - ON
+  else
+    dwBuff &= 0xffffffefL;  // clear - OFF
+  writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg
+  return 0;
+}
+
+
+
+
+
+// Use controller's "Options" field to determine loopback mode (if any)
+//   internal loopback (silicon - no GBIC)
+//   external loopback (GBIC - no FC loop)
+//   no loopback: L_PORT, external cable from GBIC required
+
+int CpqTsInitializeFrameManager( void *pChip, int opcode)
+{
+  PTACHYON fcChip;
+  int iStatus;
+  ULONG wwnLo, wwnHi; // for readback verification
+
+  ENTER("InitializeFrameManager");
+  fcChip = (PTACHYON)pChip;
+  if( !fcChip->Registers.ReMapMemBase )   // undefined controller?
+    return -1;
+
+  // TL/TS UG, pg. 184
+  // 0x0065 = 100ms for RT_TOV
+  // 0x01f5 = 500ms for ED_TOV
+  // 0x07D1 = 2000ms 
+  fcChip->Registers.ed_tov.value = 0x006507D1; 
+  writel( fcChip->Registers.ed_tov.value,
+    (fcChip->Registers.ed_tov.address));
+      
+
+  // Set LP_TOV to the FC-AL2 specified 2 secs.
+  // TL/TS UG, pg. 185
+  writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+
+
+  // Now try to read the WWN from the adapter's NVRAM
+  iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ
+
+  if( iStatus )   // NVRAM read failed?
+  {
+    printk(" WARNING! HBA NVRAM WWN read failed - make alias\n");
+    // make up a WWN.  If NULL or duplicated on loop, FC loop may hang!
+
+
+    fcChip->Registers.wwn_hi = (__u32)jiffies;
+    fcChip->Registers.wwn_hi |= 0x50000000L;
+    fcChip->Registers.wwn_lo = 0x44556677L;
+  }
+
+  
+  writel( fcChip->Registers.wwn_hi, 
+	  fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI);
+  
+  writel( fcChip->Registers.wwn_lo, 
+	  fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+	  
+
+  // readback for verification:
+  wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); 
+          
+  wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+  // test for correct chip register WRITE/READ
+  DEBUG_PCI( printk("  WWN %08X%08X\n",
+    fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) );
+    
+  if( wwnHi != fcChip->Registers.wwn_hi ||
+      wwnLo != fcChip->Registers.wwn_lo )
+  {
+    printk( "cpqfcTS: WorldWideName register load failed\n");
+    return -1; // FAILED!
+  }
+
+
+
+			// set Frame Manager Initialize command
+  fcChip->Registers.FMcontrol.value = 0x06;
+
+  // Note: for test/debug purposes, we may use "Hard" address,
+  // but we completely support "soft" addressing, including
+  // dynamically changing our address.
+  if( fcChip->Options.intLoopback == 1 )            // internal loopback
+    fcChip->Registers.FMconfig.value = 0x0f002080L;
+  else if( fcChip->Options.extLoopback == 1 )            // internal loopback
+    fcChip->Registers.FMconfig.value = 0x0f004080L;
+  else                  // L_Port
+    fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+//    fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick)
+//    fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+		
+  // write config to FM
+
+  if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback )
+                               // (also need LASER for real LOOP)
+    fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER
+
+  writel( fcChip->Registers.FMconfig.value,
+    fcChip->Registers.FMconfig.address);
+    
+
+			       // issue INITIALIZE command to FM - ACTION!
+  writel( fcChip->Registers.FMcontrol.value,
+    fcChip->Registers.FMcontrol.address);
+    
+  LEAVE("InitializeFrameManager");
+  
+  return 0;
+}
+
+
+
+
+
+// This "look ahead" function examines the IMQ for occurence of
+// "type".  Returns 1 if found, 0 if not.
+static int PeekIMQEntry( PTACHYON fcChip, ULONG type)
+{
+  ULONG CI = fcChip->IMQ->consumerIndex;
+  ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes
+  
+  while( CI != PI )
+  {                             // proceed with search
+    if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check
+    
+    switch( type )
+    {
+      case ELS_LILP_FRAME:
+      {
+      // first, we need to find an Inbound Completion message,
+      // If we find it, check the incoming frame payload (1st word)
+      // for LILP frame
+        if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 )
+        { 
+          TachFCHDR_GCMND* fchs;
+          ULONG ulFibreFrame[2048/4];  // max DWORDS in incoming FC Frame
+	  USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL);
+
+	  CpqTsGetSFQEntry( fcChip,
+            SFQpi,        // SFQ producer ndx         
+	    ulFibreFrame, // contiguous dest. buffer
+	    FALSE);       // DON'T update chip--this is a "lookahead"
+          
+	  fchs = (TachFCHDR_GCMND*)&ulFibreFrame;
+          if( fchs->pl[0] == ELS_LILP_FRAME)
+	  {
+            return 1; // found the LILP frame!
+	  }
+	  else
+	  {
+	    // keep looking...
+	  }
+	}  
+      }
+      break;
+
+      case OUTBOUND_COMPLETION:
+        if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 )
+	{
+
+          // any OCM errors?
+          if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L )
+            return 1;   	    // found OCM error
+	}
+      break;
+
+
+      
+      default:
+      break;
+    }
+  }
+  return 0; // failed to find "type"
+}
+
+			
+static void SetTachTOV( CPQFCHBA* cpqfcHBAdata)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip; 
+  
+  // TL/TS UG, pg. 184
+  // 0x0065 = 100ms for RT_TOV
+  // 0x01f5 = 500ms for ED_TOV
+  // 0x07d1 = 2000ms for ED_TOV
+
+  // SANMark Level 1 requires an "initialization backoff"
+  // (See "SANMark Test Suite Level 1":
+  // initialization_timeout.fcal.SANMark-1.fc)
+  // We have to use 2sec, 24sec, then 128sec when login/
+  // port discovery processes fail to complete.
+  
+  // when port discovery completes (logins done), we set
+  // ED_TOV to 500ms -- this is the normal operational case
+  // On the first Link Down, we'll move to 2 secs (7D1 ms)
+  if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5)
+    fcChip->Registers.ed_tov.value = 0x006507D1; 
+  
+  // If we get another LST after we moved TOV to 2 sec,
+  // increase to 24 seconds (5DC1 ms) per SANMark!
+  else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1)
+    fcChip->Registers.ed_tov.value = 0x00655DC1; 
+
+  // If we get still another LST, set the max TOV (Tachyon
+  // has only 16 bits for ms timer, so the max is 65.5 sec)
+  else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1)
+    fcChip->Registers.ed_tov.value = 0x0065FFFF; 
+
+  writel( fcChip->Registers.ed_tov.value,
+    (fcChip->Registers.ed_tov.address));
+  // keep the same 2sec LP_TOV 
+  writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+}	
+
+
+// The IMQ is an array with IMQ_LEN length, each element (QEntry)
+// with eight 32-bit words.  Tachyon PRODUCES a QEntry with each
+// message it wants to send to the host.  The host CONSUMES IMQ entries
+
+// This function copies the current
+// (or oldest not-yet-processed) QEntry to
+// the caller, clears/ re-enables the interrupt, and updates the
+// (Host) Consumer Index.
+// Return value:
+//  0   message processed, none remain (producer and consumer
+//        indexes match)
+//  1   message processed, more messages remain
+// -1   no message processed - none were available to process
+// Remarks:
+//   TL/TS UG specifices that the following actions for
+//   INTA_L handling:
+//   1. read PCI Interrupt Status register (0xff)
+//   2. all IMQ messages should be processed before writing the
+//      IMQ consumer index.
+
+
+int CpqTsProcessIMQEntry(void *host)
+{
+  struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip; 
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  int iStatus;
+  USHORT i, RPCset, DPCset;
+  ULONG x_ID;
+  ULONG ulBuff, dwStatus;
+  TachFCHDR_GCMND* fchs;
+  ULONG ulFibreFrame[2048/4];  // max number of DWORDS in incoming Fibre Frame
+  UCHAR ucInboundMessageType;  // Inbound CM, dword 3 "type" field
+
+  ENTER("ProcessIMQEntry");
+   
+
+				// check TachLite's IMQ producer index -
+				// is a new message waiting for us?
+				// equal indexes means empty que
+
+  if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex )
+  {                             // need to process message
+
+
+#ifdef IMQ_DEBUG
+    printk("PI %X, CI %X  type: %X\n", 
+      fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex,
+      fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type);
+#endif                                
+    // Examine Completion Messages in IMQ
+    // what CM_Type?
+    switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type
+                    & 0xffL) )
+    {
+    case OUTBOUND_COMPLETION:
+
+      // Remarks:
+      // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries
+      // (starting at 0), and SFS entries (starting at
+      // SEST_LEN -- outside the SEST space).
+      // Psuedo code:
+      // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index
+      // range check - x_ID
+      //   if x_ID outside 'Transactions' length, error - exit
+      // if any OCM error, copy error status to Exchange slot
+      // if FCP ASSIST transaction (x_ID within SEST),
+      //   call fcComplete (to App)
+      // ...
+
+
+      ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1];
+      x_ID = ulBuff & 0x7fffL;     // lower 14 bits SEST_Index/Trans_ID
+                                     // Range check CM OX/RX_ID value...
+      if( x_ID < TACH_MAX_XID )   // don't go beyond array space
+      {
+
+
+	if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete?
+          RPCset = 1;              // (SEST transactions only)
+        else
+          RPCset = 0;
+
+        if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete?
+          DPCset = 1;              // (SEST transactions only)
+        else
+          DPCset = 0;
+                // set the status for this Outbound transaction's ID
+        dwStatus = 0L;
+        if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error)
+            dwStatus |= SESTPROG_ERR;
+
+        ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2];
+        if( ulBuff & 0x7a000000L ) // any other errs?
+        {
+          if( ulBuff & 0x40000000L )
+            dwStatus |= INV_ENTRY;
+          if( ulBuff & 0x20000000L )
+            dwStatus |= FRAME_TO;        // FTO
+          if( ulBuff & 0x10000000L )
+            dwStatus |= HOSTPROG_ERR;
+          if( ulBuff & 0x08000000L )
+            dwStatus |= LINKFAIL_TX;
+          if( ulBuff & 0x02000000L )
+            dwStatus |= ABORTSEQ_NOTIFY;  // ASN
+        }
+
+	  
+	if( dwStatus )          // any errors?
+        {
+                  // set the Outbound Completion status
+          Exchanges->fcExchange[ x_ID ].status |= dwStatus;
+
+          // if this Outbound frame was for a SEST entry, automatically
+          // reque it in the case of LINKFAIL (it will restart on PDISC)
+          if( x_ID < TACH_SEST_LEN )
+          {
+
+            printk(" #OCM error %Xh x_ID %X# ", 
+		    dwStatus, x_ID);
+
+	    Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default
+                                                 
+
+	    // We Q ABTS for each exchange.
+	    // NOTE: We can get FRAME_TO on bad alpa (device gone).  Since
+	    // bad alpa is reported before FRAME_TO, examine the status
+	    // flags to see if the device is removed.  If so, DON'T
+	    // post an ABTS, since it will be terminated by the bad alpa
+	    // message.
+	    if( dwStatus & FRAME_TO ) // check for device removed...
+	    {
+	      if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) )
+	      { 
+		// presumes device is still there: send ABTS.
+  
+                cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	      }
+	    }
+	    else  // Abort all other errors
+	    {
+              cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	    }
+
+            // if the HPE bit is set, we have to CLose the LOOP
+            // (see TL/TS UG, pg. 239)
+
+            if( dwStatus &= HOSTPROG_ERR )
+            // set CL bit (see TL/TS UG, pg. 172)
+              writel( 4, fcChip->Registers.FMcontrol.address);
+          }
+        }
+          // NOTE: we don't necessarily care about ALL completion messages...
+                                      // SCSI resp. complete OR
+        if( ((x_ID < TACH_SEST_LEN) && RPCset)|| 
+             (x_ID >= TACH_SEST_LEN) )  // non-SCSI command
+        {
+              // exchange done; complete to upper levels with status
+              // (if necessary) and free the exchange slot
+            
+
+          if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+                                    // A Request or Reply has been sent
+          {                         // signal waiting WorkerThread
+
+            up( cpqfcHBAdata->TYOBcomplete);   // frame is OUT of Tach
+
+                                    // WorkerThread will complete Xchng
+          }
+          else  // X_ID is for FCP assist (SEST)
+          {
+              // TBD (target mode)
+//            fcCompleteExchange( fcChip, x_ID); // TRE completed
+          }
+        }
+      }
+      else  // ERROR CONDITION!  bogus x_ID in completion message
+      {
+
+        printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID);
+
+      }
+
+
+
+          // Load the Frame Manager's error counters.  We check them here
+          // because presumably the link is up and healthy enough for the
+          // counters to be meaningful (i.e., don't check them while loop
+          // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+                  
+      fcChip->Registers.FMLinkStatus2.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+            
+
+      fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+    break;
+
+
+
+    case ERROR_IDLE_COMPLETION:  // TachLite Error Idle...
+    
+    // We usually get this when the link goes down during heavy traffic.
+    // For now, presume that if SEST Exchanges are open, we will
+    // get this as our cue to INVALIDATE all SEST entries
+    // (and we OWN all the SEST entries).
+    // See TL/TS UG, pg. 53
+    
+      for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+      {
+
+        // Does this VALid SEST entry need to be invalidated for Abort?
+        fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; 
+      }
+      
+      CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK
+
+    break;
+
+
+    case INBOUND_SFS_COMPLETION:  //0x04
+          // NOTE! we must process this SFQ message to avoid SFQ filling
+          // up and stopping TachLite.  Incoming commands are placed here,
+          // as well as 'unknown' frames (e.g. LIP loop position data)
+          // write this CM's producer index to global...
+          // TL/TS UG, pg 234:
+          // Type: 0 - reserved
+          //       1 - Unassisted FCP
+          //       2 - BAD FCP
+          //       3 - Unkown Frame
+          //       4-F reserved
+
+
+      fcChip->SFQ->producerIndex = (USHORT)
+        (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL);
+
+
+      ucInboundMessageType = 0;  // default to useless frame
+
+        // we can only process two Types: 1, Unassisted FCP, and 3, Unknown
+        // Also, we aren't interested in processing frame fragments
+        // so don't Que anything with 'LKF' bit set
+      if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] 
+        & 0x40000000) )  // 'LKF' link failure bit clear?
+      {
+        ucInboundMessageType = (UCHAR)  // ICM DWord3, "Type"
+        (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL);
+      }
+      else
+      {
+	fcChip->fcStats.linkFailRX++;
+//        printk("LKF (link failure) bit set on inbound message\n");
+      }
+
+          // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff
+      CpqTsGetSFQEntry(
+        fcChip,                  // i.e. this Device Object
+        (USHORT)fcChip->SFQ->producerIndex,  // SFQ producer ndx         
+        ulFibreFrame, TRUE);    // contiguous destination buffer, update chip
+                     
+        // analyze the incoming frame outside the INT handler...
+        // (i.e., Worker)
+
+      if( ucInboundMessageType == 1 )
+      {
+        fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame
+        // don't fill up our Q with garbage - only accept FCP-CMND  
+        // or XRDY frames
+        if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND
+        {
+	  // someone sent us a SCSI command
+	  
+//          fcPutScsiQue( cpqfcHBAdata, 
+//                        SFQ_UNASSISTED_FCP, ulFibreFrame); 
+	}
+	else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status)
+            (fchs->d_id & 0xFF000000) == 0x05000000 )  // XRDY  
+	{
+	  ULONG x_ID;
+	  // Unfortunately, ABTS requires a Freeze on the chip so
+	  // we can modify the shared memory SEST.  When frozen,
+	  // any received Exchange frames cannot be processed by
+	  // Tachyon, so they will be dumped in here.  It is too
+	  // complex to attempt the reconstruct these frames in
+	  // the correct Exchange context, so we simply seek to
+	  // find status or transfer ready frames, and cause the
+	  // exchange to complete with errors before the timeout
+	  // expires.  We use a Linux Scsi Cmnd result code that
+	  // causes immediate retry.
+	  
+
+	  // Do we have an open exchange that matches this s_id
+	  // and ox_id?
+	  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+	  {
+            if( (fchs->s_id & 0xFFFFFF) == 
+                 (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) 
+		       &&
+                (fchs->ox_rx_id & 0xFFFF0000) == 
+                 (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) )
+	    {
+    //          printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id );
+              // simulate the anticipated error - since the
+	      // SEST was frozen, frames were lost...
+              Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME;
+              
+	      // presumes device is still there: send ABTS.
+              cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+	      break;  // done
+	    }
+	  }
+	}
+	  
+      }
+          
+      else if( ucInboundMessageType == 3)
+      {
+        // FC Link Service frames (e.g. PLOGI, ACC) come in here.  
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); 
+                          
+      }
+
+      else if( ucInboundMessageType == 2 ) // "bad FCP"?
+      {
+#ifdef IMQ_DEBUG
+        printk("Bad FCP incoming frame discarded\n");
+#endif
+      }
+
+      else // don't know this type
+      {
+#ifdef IMQ_DEBUG 
+        printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType);
+#endif
+      }
+        
+        // Check the Frame Manager's error counters.  We check them here
+        // because presumably the link is up and healthy enough for the
+        // counters to be meaningful (i.e., don't check them while loop
+        // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+                  
+
+      fcChip->Registers.FMLinkStatus2.value =    // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+                
+
+      break;
+
+
+
+
+                    // We get this CM because we issued a freeze
+                    // command to stop outbound frames.  We issue the
+                    // freeze command at Link Up time; when this message
+                    // is received, the ERQ base can be switched and PDISC
+                    // frames can be sent.
+
+      
+    case ERQ_FROZEN_COMPLETION:  // note: expect ERQ followed immediately
+                                 // by FCP when freezing TL
+      fcChip->Registers.TYstatus.value =         // read what's frozen
+        readl(fcChip->Registers.TYstatus.address);
+      // (do nothing; wait for FCP frozen message)
+      break;
+    case FCP_FROZEN_COMPLETION:
+      
+      fcChip->Registers.TYstatus.value =         // read what's frozen
+        readl(fcChip->Registers.TYstatus.address);
+      
+      // Signal the kernel thread to proceed with SEST modification
+      up( cpqfcHBAdata->TachFrozen);
+
+      break;
+
+
+
+    case INBOUND_C1_TIMEOUT:
+    case MFS_BUF_WARN:
+    case IMQ_BUF_WARN:
+    break;
+
+
+
+
+
+        // In older Tachyons, we 'clear' the internal 'core' interrupt state
+        // by reading the FMstatus register.  In newer TachLite (Tachyon),
+        // we must WRITE the register
+        // to clear the condition (TL/TS UG, pg 179)
+    case FRAME_MGR_INTERRUPT:
+    {
+      PFC_LOGGEDIN_PORT pLoggedInPort; 
+
+      fcChip->Registers.FMstatus.value = 
+        readl( fcChip->Registers.FMstatus.address );
+                
+      // PROBLEM: It is possible, especially with "dumb" hubs that
+      // don't automatically LIP on by-pass of ports that are going
+      // away, for the hub by-pass process to destroy critical 
+      // ordered sets of a frame.  The result of this is a hung LPSM
+      // (Loop Port State Machine), which on Tachyon results in a
+      // (default 2 sec) Loop State Timeout (LST) FM message.  We 
+      // want to avoid this relatively huge timeout by detecting
+      // likely scenarios which will result in LST.
+      // To do this, we could examine FMstatus for Loss of Synchronization
+      // and/or Elastic Store (ES) errors.  Of these, Elastic Store is better
+      // because we get this indication more quickly than the LOS.
+      // Not all ES errors are harmfull, so we don't want to LIP on every
+      // ES.  Instead, on every ES, detect whether our LPSM in in one
+      // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE,
+      // or RECEIVED CLOSE.  (See TL/TS UG, pg. 181)
+      // If any of these LPSM states are detected
+      // in combination with the LIP while LDn is not set, 
+      // send an FM init (LIP F7,F7 for loops)!
+      // It is critical to the physical link stability NOT to reset (LIP)
+      // more than absolutely necessary; this is a basic premise of the
+      // SANMark level 1 spec.
+      {
+	ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4;
+	
+	if( (fcChip->Registers.FMstatus.value & 0x400)  // ElasticStore?
+                      &&
+            !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn
+	              &&
+            !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF
+	{
+	  if( (Lpsm != 0) || // not MONITORING? or
+	      !(Lpsm & 0x8) )// not already offline?
+	  {
+	  // now check the particular LST states...
+            if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) ||
+	      (Lpsm == OPENED)      || (Lpsm == XMITTD_CLOSE) ||
+	      (Lpsm == RCVD_CLOSE) )
+	    {
+	      // re-init the loop before it hangs itself!
+              printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm);
+
+
+	      fcChip->fcStats.FMinits++;
+              writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+	    }
+	  }
+	}
+	else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST?
+	{
+          printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm);
+	 
+          fcChip->fcStats.FMinits++;
+          writel( 6, fcChip->Registers.FMcontrol.address);  // LIP
+	}  
+      }
+
+
+      // clear only the 'interrupting' type bits for this REG read
+      writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L),
+        fcChip->Registers.FMstatus.address);
+                          
+
+               // copy frame manager status to unused ULONG slot
+      fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] =
+          fcChip->Registers.FMstatus.value; // (for debugging)
+
+
+          // Load the Frame Manager's error counters.  We check them here
+          // because presumably the link is up and healthy enough for the
+          // counters to be meaningful (i.e., don't check them while loop
+          // is initializing).
+      fcChip->Registers.FMLinkStatus1.value =   // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus1.address);
+            
+      fcChip->Registers.FMLinkStatus2.value =   // get TL's counter
+        readl(fcChip->Registers.FMLinkStatus2.address);
+          
+          // Get FM BB_Credit Zero Reg - does not clear on READ
+      fcChip->Registers.FMBB_CreditZero.value =   // get TL's counter
+        readl(fcChip->Registers.FMBB_CreditZero.address);
+            
+
+
+      fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+
+
+               // LINK DOWN
+
+      if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit
+      {                                 
+	
+#ifdef IMQ_DEBUG
+        printk("LinkDn\n");
+#endif
+        printk(" #LDn# ");
+        
+        fcChip->fcStats.linkDown++;
+        
+	SetTachTOV( cpqfcHBAdata);  // must set according to SANMark
+
+	// Check the ERQ - force it to be "empty" to prevent Tach
+	// from sending out frames before we do logins.
+
+
+  	if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex)
+	{
+//	  printk("#ERQ PI != CI#");
+          CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only	  
+	  fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0;
+ 	  writel( fcChip->ERQ->base, 
+	    (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+          // re-writing base forces ERQ PI to equal CI
+  
+	}
+		
+	// link down transition occurred -- port_ids can change
+        // on next LinkUp, so we must invalidate current logins
+        // (and any I/O in progress) until PDISC or PLOGI/PRLI
+        // completes
+        {
+          pLoggedInPort = &fcChip->fcPorts; 
+          while( pLoggedInPort ) // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+          {
+
+	    if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+            {
+              pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds
+                                              // but Timer granularity
+                                              // is 1 second
+            }
+                                // suspend any I/O in progress until
+                                // PDISC received...
+            pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands
+	    
+            pLoggedInPort = pLoggedInPort->pNextPort;
+          }  // ... all Previously known ports checked
+        }
+        
+	// since any hot plugging device may NOT support LILP frames
+	// (such as early Tachyon chips), clear this flag indicating
+	// we shouldn't use (our copy of) a LILP map.
+	// If we receive an LILP frame, we'll set it again.
+	fcChip->Options.LILPin = 0; // our LILPmap is invalid
+        cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports!
+
+          // also, we want to invalidate (i.e. INITIATOR_ABORT) any
+          // open Login exchanges, in case the LinkDown happened in the
+          // middle of logins.  It's possible that some ports already
+          // ACCepted login commands which we have not processed before
+          // another LinkDown occured.  Any accepted Login exhanges are
+          // invalidated by LinkDown, even before they are acknowledged.
+          // It's also possible for a port to have a Queued Reply or Request
+          // for login which was interrupted by LinkDown; it may come later,
+          // but it will be unacceptable to us.
+
+          // we must scan the entire exchange space, find every Login type
+          // originated by us, and abort it. This is NOT an abort due to
+          // timeout, so we don't actually send abort to the other port -
+          // we just complete it to free up the fcExchange slot.
+
+        for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++)
+        {                     // looking for Extended Link Serv.Exchanges
+          if( Exchanges->fcExchange[i].type == ELS_PDISC ||
+              Exchanges->fcExchange[i].type == ELS_PLOGI ||
+              Exchanges->fcExchange[i].type == ELS_PRLI ) 
+          {
+              // ABORT the exchange!
+#ifdef IMQ_DEBUG
+            printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n",
+              i, Exchanges->fcExchange[i].type,
+            Exchanges->fcExchange[i].fchs.d_id);
+#endif
+
+            Exchanges->fcExchange[i].status |= INITIATOR_ABORT;
+            cpqfcTSCompleteExchange( fcChip, i); // abort on LDn
+          }
+        }
+
+      }
+
+             // ################   LINK UP   ##################
+      if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit
+      {                                 // AL_PA could have changed
+
+          // We need the following code, duplicated from LinkDn condition,
+          // because it's possible for the Tachyon to re-initialize (hard
+          // reset) without ever getting a LinkDn indication.
+        pLoggedInPort = &fcChip->fcPorts; 
+        while( pLoggedInPort )   // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+        {
+          if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+          {
+            pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds
+                                              // but Timer granularity
+                                              // is 1 second
+             
+                                  // suspend any I/O in progress until
+                                  // PDISC received...
+
+          }
+          pLoggedInPort = pLoggedInPort->pNextPort;
+        }  // ... all Previously known ports checked
+ 
+          // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA)
+        fcChip->Registers.rcv_al_pa.value = 
+          readl(fcChip->Registers.rcv_al_pa.address);
+ 
+	// Now, if our acquired address is DIFFERENT from our
+        // previous one, we are not allow to do PDISC - we
+        // must go back to PLOGI, which will terminate I/O in
+        // progress for ALL logged in FC devices...
+	// (This is highly unlikely).
+
+	if( (fcChip->Registers.my_al_pa & 0xFF) != 
+	    ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) )
+	{
+
+//	  printk(" #our HBA port_id changed!# "); // FC port_id changed!!	
+
+	  pLoggedInPort = &fcChip->fcPorts; 
+          while( pLoggedInPort ) // for all ports which are expecting
+                                 // PDISC after the next LIP, set the
+                                 // logoutTimer
+          {
+	    pLoggedInPort->pdisc  = FALSE;
+            pLoggedInPort->prli = FALSE;
+            pLoggedInPort = pLoggedInPort->pNextPort;
+          }  // ... all Previously known ports checked
+
+	  // when the port_id changes, we must terminate
+	  // all open exchanges.
+          cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED);
+
+	}
+	               
+	// Replace the entire 24-bit port_id.  We only know the
+	// lower 8 bits (alpa) from Tachyon; if a FLOGI is done,
+	// we'll get the upper 16-bits from the FLOGI ACC frame.
+	// If someone plugs into Fabric switch, we'll do FLOGI and
+	// get full 24-bit port_id; someone could then remove and
+	// hot-plug us into a dumb hub.  If we send a 24-bit PLOGI
+	// to a "private" loop device, it might blow up.
+	// Consequently, we force the upper 16-bits of port_id to
+	// be re-set on every LinkUp transition
+        fcChip->Registers.my_al_pa =
+          (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF;
+
+              
+              // copy frame manager status to unused ULONG slot
+        fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+          fcChip->Registers.my_al_pa; // (for debugging)
+
+              // for TachLite, we need to write the acquired al_pa
+              // back into the FMconfig register, because after
+              // first initialization, the AQ (prev. acq.) bit gets
+              // set, causing TL FM to use the AL_PA field in FMconfig.
+              // (In Tachyon, FM writes the acquired AL_PA for us.)
+        ulBuff = readl( fcChip->Registers.FMconfig.address);
+        ulBuff &= 0x00ffffffL;  // mask out current al_pa
+        ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa
+        fcChip->Registers.FMconfig.value = ulBuff; // copy it back
+        writel( fcChip->Registers.FMconfig.value,  // put in TachLite
+          fcChip->Registers.FMconfig.address);
+            
+
+#ifdef IMQ_DEBUG
+        printk("#LUp %Xh, FMstat 0x%08X#", 
+		fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value);
+#endif
+
+              // also set the WRITE-ONLY My_ID Register (for Fabric
+              // initialization)
+        writel( fcChip->Registers.my_al_pa,
+          fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID);
+          
+
+        fcChip->fcStats.linkUp++;
+
+                                     // reset TL statistics counters
+                                     // (we ignore these error counters
+                                     // while link is down)
+        ulBuff =                     // just reset TL's counter
+                 readl( fcChip->Registers.FMLinkStatus1.address);
+          
+        ulBuff =                     // just reset TL's counter
+                 readl( fcChip->Registers.FMLinkStatus2.address);
+
+          // for initiator, need to start verifying ports (e.g. PDISC)
+
+
+
+         
+      
+      
+	CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK
+	
+	// Tachyon creates an interesting problem for us on LILP frames.
+	// Instead of writing the incoming LILP frame into the SFQ before
+	// indicating LINK UP (the actual order of events), Tachyon tells
+	// us LINK UP, and later us the LILP.  So we delay, then examine the
+	// IMQ for an Inbound CM (x04); if found, we can set
+	// LINKACTIVE after processing the LILP.  Otherwise, just proceed.
+	// Since Tachyon imposes this time delay (and doesn't tell us
+	// what it is), we have to impose a delay before "Peeking" the IMQ
+	// for Tach hardware (DMA) delivery.
+	// Processing LILP is required by SANMark
+	udelay( 1000);  // microsec delay waiting for LILP (if it comes)
+        if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) )
+	{  // found SFQ LILP, which will post LINKACTIVE	  
+//	  printk("skipping LINKACTIVE post\n");
+
+	}
+	else
+          cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame);  
+      }
+
+
+
+      // ******* Set Fabric Login indication ********
+      if( fcChip->Registers.FMstatus.value & 0x2000 )
+      {
+	printk(" #Fabric# ");
+        fcChip->Options.fabric = 1;
+      }
+      else
+        fcChip->Options.fabric = 0;
+
+      
+      
+                             // ******* LIP(F8,x) or BAD AL_PA? ********
+      if( fcChip->Registers.FMstatus.value & 0x30000L )
+      {
+                        // copy the error AL_PAs
+        fcChip->Registers.rcv_al_pa.value = 
+          readl(fcChip->Registers.rcv_al_pa.address);
+            
+                        // Bad AL_PA?
+        if( fcChip->Registers.FMstatus.value & 0x10000L )
+        {
+          PFC_LOGGEDIN_PORT pLoggedInPort;
+        
+                       // copy "BAD" al_pa field
+          fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+              (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8;
+
+	  pLoggedInPort = fcFindLoggedInPort( fcChip,
+            NULL,     // DON'T search Scsi Nexus
+            fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id
+            NULL,     // DON'T search linked list for FC WWN
+            NULL);    // DON'T care about end of list
+ 
+	  if( pLoggedInPort )
+	  {
+            // Just in case we got this BAD_ALPA because a device
+	    // quietly disappeared (can happen on non-managed hubs such 
+	    // as the Vixel Rapport 1000),
+	    // do an Implicit Logout.  We never expect this on a Logged
+	    // in port (but do expect it on port discovery).
+	    // (As a reasonable alternative, this could be changed to 
+	    // simply start the implicit logout timer, giving the device
+	    // several seconds to "come back".)
+	    // 
+	    printk(" #BAD alpa %Xh# ",
+		   fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]);
+            cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+	  }
+        }
+                        // LIP(f8,x)?
+        if( fcChip->Registers.FMstatus.value & 0x20000L )
+        {
+                        // for debugging, copy al_pa field
+          fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] =
+              (fcChip->Registers.rcv_al_pa.value & 0xffL);
+                        // get the other port's al_pa
+                        // (one that sent LIP(F8,?) )
+        }
+      }
+
+                             // Elastic store err
+      if( fcChip->Registers.FMstatus.value & 0x400L )
+      {
+            // don't count e-s if loop is down!
+        if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) )
+          fcChip->fcStats.e_stores++;
+          
+      }
+    }
+    break;
+
+
+    case INBOUND_FCP_XCHG_COMPLETION:  // 0x0C
+
+    // Remarks:
+    // On Tachlite TL/TS, we get this message when the data phase
+    // of a SEST inbound transfer is complete.  For example, if a WRITE command
+    // was received with OX_ID 0, we might respond with XFER_RDY with
+    // RX_ID 8001.  This would start the SEST controlled data phases.  When
+    // all data frames are received, we get this inbound completion. This means
+    // we should send a status frame to complete the status phase of the 
+    // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data
+    // frames.
+    // See Outbound CM discussion of x_IDs
+    // Psuedo Code
+    //   Get SEST index (x_ID)
+    //     x_ID out of range, return (err condition)
+    //   set status bits from 2nd dword
+    //   free transactionID & SEST entry
+    //   call fcComplete with transactionID & status
+
+      ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0];
+      x_ID = ulBuff & 0x7fffL;  // lower 14 bits SEST_Index/Trans_ID
+                                // (mask out MSB "direction" bit)
+                                // Range check CM OX/RX_ID value...
+      if( x_ID < TACH_SEST_LEN )  // don't go beyond SEST array space
+      {
+
+//#define FCP_COMPLETION_DBG 1
+#ifdef FCP_COMPLETION_DBG
+        printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", 
+          x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd);
+#endif
+        if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or -
+                                   // time to send response frame?
+          RPCset = 1;             // (SEST transaction)
+        else
+          RPCset = 0;
+                // set the status for this Inbound SCSI transaction's ID
+        dwStatus = 0L;
+        if( ulBuff & 0x70000000L ) // any errs?
+        {
+          
+          if( ulBuff & 0x40000000L )
+            dwStatus |= LINKFAIL_RX;
+          
+	  if( ulBuff & 0x20000000L )
+            dwStatus |= COUNT_ERROR;
+          
+          if( ulBuff & 0x10000000L )
+            dwStatus |= OVERFLOW;
+        }
+      
+	
+	  // FCP transaction done - copy status
+        Exchanges->fcExchange[ x_ID ].status = dwStatus;
+
+
+        // Did the exchange get an FCP-RSP response frame?
+        // (Note the little endian/big endian FC payload difference)
+
+        if( RPCset )             // SEST transaction Response frame rec'd
+        {
+    	  // complete the command in our driver...
+          cpqfcTSCompleteExchange( fcChip, x_ID);
+
+        }  // end "RPCset"
+	
+        else  // ("target" logic)
+        {
+            // Tachlite says all data frames have been received - now it's time
+            // to analyze data transfer (successful?), then send a response 
+            // frame for this exchange
+
+          ulFibreFrame[0] = x_ID; // copy for later reference
+
+          // if this was a TWE, we have to send satus response
+          if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE )
+	  {
+//            fcPutScsiQue( cpqfcHBAdata, 
+//                NEED_FCP_RSP, ulFibreFrame);  // (ulFibreFrame not used here)
+	  }
+        }
+      }
+      else  // ERROR CONDITION!  bogus x_ID in completion message
+      {
+        printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID);
+      }
+
+    break;
+
+
+
+
+    case INBOUND_SCSI_DATA_COMMAND:
+    case BAD_SCSI_FRAME:
+    case INB_SCSI_STATUS_COMPLETION:
+    case BUFFER_PROCESSED_COMPLETION:
+    break;
+    }
+
+					   // Tachyon is producing;
+					   // we are consuming
+    fcChip->IMQ->consumerIndex++;             // increment OUR consumerIndex
+    if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover
+      fcChip->IMQ->consumerIndex = 0L;        // reset it
+
+
+    if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex )
+    {                           // all Messages are processed -
+      iStatus = 0;              // no more messages to process
+
+    }
+    else
+      iStatus = 1;              // more messages to process
+
+    // update TachLite's ConsumerIndex... (clears INTA_L)
+    // NOTE: according to TL/TS UG, the 
+    // "host must return completion messages in sequential order".
+    // Does this mean one at a time, in the order received?  We
+    // presume so.
+
+    writel( fcChip->IMQ->consumerIndex,
+      (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+		    
+#if IMQ_DEBUG
+    printk("Process IMQ: writing consumer ndx %d\n ", 
+      fcChip->IMQ->consumerIndex);
+    printk("PI %X, CI %X\n", 
+    fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex );
+#endif
+  
+
+
+  }
+  else
+  {
+   // hmmm... why did we get interrupted/called with no message?
+    iStatus = -1;               // nothing to process
+#if IMQ_DEBUG
+    printk("Process IMQ: no message PI %Xh  CI %Xh", 
+      fcChip->IMQ->producerIndex,
+      fcChip->IMQ->consumerIndex);
+#endif
+  }
+
+  LEAVE("ProcessIMQEntry");
+  
+  return iStatus;
+}
+
+
+
+
+
+// This routine initializes Tachyon according to the following
+// options (opcode1):
+// 1 - RESTART Tachyon, simulate power on condition by shutting
+//     down laser, resetting the hardware, de-allocating all buffers;
+//     continue
+// 2 - Config Tachyon / PCI registers;
+//     continue
+// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs);
+//     continue
+// 4 - Config frame manager registers, initialize, turn on laser
+//
+// Returns:
+//  -1 on fatal error
+//   0 on success
+
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  ULONG ulBuff;
+  UCHAR bBuff;
+  int iStatus=-1;  // assume failure
+
+  ENTER("InitializeTachLite");
+
+  // verify board's base address (sanity check)
+
+  if( !fcChip->Registers.ReMapMemBase)                // NULL address for card?
+    return -1;                         // FATAL error!
+
+
+
+  switch( opcode1 )
+  {
+    case 1:       // restore hardware to power-on (hard) restart
+
+
+      iStatus = fcChip->ResetTachyon( 
+		  cpqfcHBAdata, opcode2); // laser off, reset hardware
+				      // de-allocate aligned buffers
+
+
+/* TBD      // reset FC link Q (producer and consumer = 0)
+      fcLinkQReset(cpqfcHBAdata); 
+
+*/
+
+      if( iStatus )
+        break;
+
+    case 2:       // Config PCI/Tachyon registers
+      // NOTE: For Tach TL/TS, bit 31 must be set to 1.  For TS chips, a read
+      // of bit 31 indicates state of M66EN signal; if 1, chip may run at 
+      // 33-66MHz  (see TL/TS UG, pg 159)
+
+      ulBuff = 0x80000000;  // TachLite Configuration Register
+
+      writel( ulBuff, fcChip->Registers.TYconfig.address);
+//      ulBuff = 0x0147L;  // CpqTs PCI CFGCMD register
+//      WritePCIConfiguration( fcChip->Backplane.bus,
+//                           fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4);
+//      ulBuff = 0x0L;  // test!
+//      ReadPCIConfiguration( fcChip->Backplane.bus,
+//                           fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4);
+
+      // read back for reference...
+      fcChip->Registers.TYconfig.value = 
+         readl( fcChip->Registers.TYconfig.address );
+
+      // what is the PCI bus width?
+      pci_read_config_byte( cpqfcHBAdata->PciDev,
+                                0x43, // PCIMCTR offset
+                                &bBuff);
+      
+      fcChip->Registers.PCIMCTR = bBuff;
+
+      // set string identifying the chip on the circuit board
+
+      fcChip->Registers.TYstatus.value =
+        readl( fcChip->Registers.TYstatus.address);
+      
+      {
+// Now that we are supporting multiple boards, we need to change
+// this logic to check for PCI vendor/device IDs...
+// for now, quick & dirty is simply checking Chip rev
+	
+	ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5;
+	UCHAR Minor = (UCHAR)(RevId & 0x3);
+	UCHAR Major = (UCHAR)((RevId & 0x1C) >>2);
+  
+        printk("  HBA Tachyon RevId %d.%d\n", Major, Minor);
+  	if( (Major == 1) && (Minor == 2) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12);
+
+	}
+	else if( (Major == 1) && (Minor == 3) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13);
+	}
+	else if( (Major == 2) && (Minor == 1) )
+        {
+	  sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21);
+	}
+	else
+	  sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN);
+      }
+
+
+
+    case 3:       // allocate mem, set Tachyon Que registers
+      iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2);
+
+      // now that the Queues exist, Tach can DMA to them, so
+      // we can begin processing INTs
+      // INTEN register - enable INT (TachLite interrupt)
+      writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN);
+
+
+      if( iStatus )
+        break;
+
+
+    case 4:       // Config Fame Manager, Init Loop Command, laser on
+
+                 // L_PORT or loopback
+                 // depending on Options
+      iStatus = CpqTsInitializeFrameManager( fcChip,0 );
+      if( iStatus )
+      {
+           // failed to initialize Frame Manager
+	      break;
+      }
+
+    default:
+      break;
+  }
+  LEAVE("InitializeTachLite");
+  
+  return iStatus;
+}
+
+
+
+
+// Depending on the type of platform memory allocation (e.g. dynamic),
+// it's probably best to free memory in opposite order as it was allocated.
+// Order of allocation: see other function
+
+
+int CpqTsDestroyTachLiteQues( void *pHBA, int opcode)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  USHORT i, j, iStatus=0;
+  void* vPtr;  // mem Align manager sets this to the freed address on success
+  unsigned long ulPtr;  // for 64-bit pointer cast (e.g. Alpa machine)
+
+  ENTER("DestroyTachLiteQues");
+
+  if( fcChip->SEST )
+  {
+                // search out and free Pool for Extended S/G list pages
+
+    for( i=0, j=0; i < TACH_SEST_LEN; i++, j=0)  // for each exchange
+    {
+      // It's possible that extended S/G pages were allocated and
+      // not cleared due to error conditions or O/S driver termination.
+      // Make sure they're all gone.
+      while( fcChip->SEST->sgPages[i].PoolPage[j] &&
+        (j < TL_MAX_SGPAGES))
+        kfree( fcChip->SEST->sgPages[i].PoolPage[j++]);
+
+    }
+    ulPtr = (unsigned long)fcChip->SEST;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->SEST = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("SEST mem not freed\n");
+      iStatus = -1;
+    }
+  }
+
+  if( fcChip->SFQ )
+  {
+
+    ulPtr = (unsigned long)fcChip->SFQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->SFQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("SFQ mem not freed\n");
+      iStatus = -2;
+    }
+  }
+
+
+  if( fcChip->IMQ )
+  {
+      // clear Indexes to show empty Queue
+    fcChip->IMQ->producerIndex = 0;
+    fcChip->IMQ->consumerIndex = 0;
+
+    ulPtr = (unsigned long)fcChip->IMQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->IMQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("IMQ mem not freed\n");
+      iStatus = -3;
+    }
+  }
+
+  if( fcChip->ERQ )         // release memory blocks used by the queues
+  {
+    ulPtr = (unsigned long)fcChip->ERQ;
+    vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+		    0,0, (ULONG)ulPtr ); // 'free' mem
+    fcChip->ERQ = 0L;  // null invalid ptr
+    if( !vPtr )
+    {
+      printk("ERQ mem not freed\n");
+      iStatus = -4;
+    }
+  }
+    
+  // free up the primary EXCHANGES struct
+  if( fcChip->Exchanges != NULL)
+  {
+//    printk("kfree() on Exchanges @%p\n", fcChip->Exchanges);
+    kfree( fcChip->Exchanges);
+  }
+
+  // free up Link Q
+  if( cpqfcHBAdata->fcLQ != NULL )
+  {
+//    printk("kfree() on LinkQ @%p\n", fcChip->fcLQ);
+    kfree( cpqfcHBAdata->fcLQ);
+  }
+  
+  LEAVE("DestroyTachLiteQues");
+  
+  return iStatus;     // non-zero (failed) if any memory not freed
+}
+
+
+
+
+
+// The SFQ is an array with SFQ_LEN length, each element (QEntry)
+// with eight 32-bit words.  TachLite places incoming FC frames (i.e.
+// a valid FC frame with our AL_PA ) in contiguous SFQ entries
+// and sends a completion message telling the host where the frame is
+// in the que.
+// This function copies the current (or oldest not-yet-processed) QEntry to
+// a caller's contiguous buffer and updates the Tachyon chip's consumer index
+//
+// NOTE:
+//   An FC frame may consume one or many SFQ entries.  We know the total
+//   length from the completion message.  The caller passes a buffer large
+//   enough for the complete message (max 2k).
+
+static void CpqTsGetSFQEntry(
+         PTACHYON fcChip,
+         USHORT producerNdx,
+         ULONG *ulDestPtr,            // contiguous destination buffer
+	 BOOLEAN UpdateChip)
+{
+  ULONG total_bytes=0;
+  ULONG consumerIndex = fcChip->SFQ->consumerIndex;
+  
+				// check passed copy of SFQ producer index -
+				// is a new message waiting for us?
+				// equal indexes means SFS is copied
+
+  while( producerNdx != consumerIndex )
+  {                             // need to process message
+    total_bytes += 64;   // maintain count to prevent writing past buffer
+                   // don't allow copies over Fibre Channel defined length!
+    if( total_bytes <= 2048 )
+    {
+      memcpy( ulDestPtr, 
+              &fcChip->SFQ->QEntry[consumerIndex],
+              64 );  // each SFQ entry is 64 bytes
+      ulDestPtr += 16;   // advance pointer to next 64 byte block
+    }
+		         // Tachyon is producing,
+                         // and we are consuming
+
+    if( ++consumerIndex >= SFQ_LEN)// check for rollover
+      consumerIndex = 0L;        // reset it
+  }
+
+  // if specified, update the Tachlite chip ConsumerIndex...
+  if( UpdateChip )
+  {
+    fcChip->SFQ->consumerIndex = consumerIndex;
+    writel( fcChip->SFQ->consumerIndex,
+      fcChip->Registers.SFQconsumerIndex.address);
+  }
+}
+
+
+
+// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO,
+// and Exchange Request Queue (ERQ) on error recover - 
+// (e.g. whenever a LIP occurs).  Here
+// we routinely RESUME by clearing these bits, but only if the loop is up
+// to avoid ERROR IDLE messages forever.
+
+void CpqTsUnFreezeTachlite( void *pChip, int type )
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+  fcChip->Registers.TYcontrol.value = 
+    readl(fcChip->Registers.TYcontrol.address);
+            
+  // (bit 4 of value is GBIC LASER)
+  // if we 'unfreeze' the core machines before the loop is healthy
+  // (i.e. FLT, OS, LS failure bits set in FMstatus)
+  // we can get 'error idle' messages forever.  Verify that
+  // FMstatus (Link Status) is OK before unfreezing.
+
+  if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear?
+      !(fcChip->Registers.FMstatus.value & 0x80  ))  // Active LPSM?
+  {
+    fcChip->Registers.TYcontrol.value &=  ~0x300L; // clear FEQ, FFA
+    if( type == 1 )  // unfreeze ERQ only
+    {
+//      printk("Unfreezing ERQ\n");
+      fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ
+    }
+    else             // unfreeze both ERQ and FCP-ASSIST (SEST)
+    {
+//      printk("Unfreezing ERQ & FCP-ASSIST\n");
+
+                     // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ
+      fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ
+    }
+
+    writel( fcChip->Registers.TYcontrol.value,
+      fcChip->Registers.TYcontrol.address);
+              
+  }
+          // readback for verify (TachLite still frozen?)
+  fcChip->Registers.TYstatus.value = 
+    readl(fcChip->Registers.TYstatus.address);
+}
+
+
+// Whenever an FC Exchange Abort is required, we must manipulate the
+// Host/Tachyon shared memory SEST table.  Before doing this, we
+// must freeze Tachyon, which flushes certain buffers and ensure we
+// can manipulate the SEST without contention.
+// This freeze function will result in FCP & ERQ FROZEN completion
+// messages (per argument "type").
+
+void CpqTsFreezeTachlite( void *pChip, int type )
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+  fcChip->Registers.TYcontrol.value = 
+    readl(fcChip->Registers.TYcontrol.address);
+    
+                     //set FFA, FEQ - freezes SCSI assist and ERQ
+  if( type == 1)    // freeze ERQ only
+    fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser)
+  else              // freeze both FCP assists (SEST) and ERQ
+    fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser)
+  
+  writel( fcChip->Registers.TYcontrol.value,
+    fcChip->Registers.TYcontrol.address);
+              
+}
+
+
+
+
+// TL has two Frame Manager Link Status Registers, with three 8-bit
+// fields each. These eight bit counters are cleared after each read,
+// so we define six 32-bit accumulators for these TL counters. This
+// function breaks out each 8-bit field and adds the value to the existing
+// sum.  (s/w counters cleared independently)
+
+void fcParseLinkStatusCounters(PTACHYON fcChip)
+{
+  UCHAR bBuff;
+  ULONG ulBuff;
+
+
+// The BB0 timer usually increments when TL is initialized, resulting
+// in an initially bogus count.  If our own counter is ZERO, it means we
+// are reading this thing for the first time, so we ignore the first count.
+// Also, reading the register does not clear it, so we have to keep an
+// additional static counter to detect rollover (yuk).
+
+  if( fcChip->fcStats.lastBB0timer == 0L)  // TL was reset? (ignore 1st values)
+  {
+                           // get TL's register counter - the "last" count
+    fcChip->fcStats.lastBB0timer = 
+      fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+  }
+  else  // subsequent pass - check for rollover
+  {
+                              // "this" count
+    ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+    if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened
+    {
+                                // counter advanced to max...
+      fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer);
+      fcChip->fcStats.BB0_Timer += ulBuff;  // plus some more
+
+
+    }
+    else // no rollover -- more counts or no change
+    {
+      fcChip->fcStats.BB0_Timer +=  (ulBuff - fcChip->fcStats.lastBB0timer);
+
+    }
+
+    fcChip->fcStats.lastBB0timer = ulBuff;
+  }
+
+
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24);
+  fcChip->fcStats.LossofSignal += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16);
+  fcChip->fcStats.BadRXChar += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8);
+  fcChip->fcStats.LossofSync += bBuff;
+
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24);
+  fcChip->fcStats.Rx_EOFa += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16);
+  fcChip->fcStats.Dis_Frm += bBuff;
+
+  bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8);
+  fcChip->fcStats.Bad_CRC += bBuff;
+}
+
+
+void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip)
+{
+  ENTER("ClearLinkStatusCounters");
+  memset( &fcChip->fcStats, 0, sizeof( FCSTATS));
+  LEAVE("ClearLinkStatusCounters");
+
+}
+
+
+
+
+// The following function reads the I2C hardware to get the adapter's
+// World Wide Name (WWN).
+// If the WWN is "500805f1fadb43e8" (as printed on the card), the
+// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register
+// is fadb43e8.
+// In the NVRAM, the bytes appear as:
+// [2d] ..
+// [2e] .. 
+// [2f] 50
+// [30] 08
+// [31] 05
+// [32] f1
+// [33] fa
+// [34] db
+// [35] 43
+// [36] e8
+//
+// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will
+// be correctly loaded by Tachyon silicon.  In the login payload, bytes
+// must be correctly swapped for Big Endian format.
+
+int CpqTsReadWriteWWN( PVOID pChip, int Read)
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+  unsigned short i, count = NVRAM_SIZE;
+  UCHAR nvRam[NVRAM_SIZE], WWNbuf[8];
+  ULONG ulBuff;
+  int iStatus=-1;  // assume failure
+  int WWNoffset;
+
+  ENTER("ReadWriteWWN");
+  // Now try to read the WWN from the adapter's NVRAM
+
+  if( Read )  // READing NVRAM WWN?
+  {
+    ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address,
+                              fcChip->Registers.TYcontrol.address,
+                              count, &nvRam[0] );
+
+    if( ulBuff )   // NVRAM read successful?
+    {
+      iStatus = 0; // success!
+      
+                   // for engineering/ prototype boards, the data may be
+                   // invalid (GIGO, usually all "FF"); this prevents the
+                   // parse routine from working correctly, which means
+                   // nothing will be written to our passed buffer.
+
+      WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam );
+
+      if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly
+      {
+        printk( "CAUTION: Copying NVRAM data on fcChip\n");
+        for( i= 0; i < 8; i++)
+          WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work
+      }
+      
+      fcChip->Registers.wwn_hi = 0L;
+      fcChip->Registers.wwn_lo = 0L;
+      for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM
+      {
+        ulBuff = 0L;
+        ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i));
+        fcChip->Registers.wwn_hi |= ulBuff;
+      }
+      for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM
+      {
+        ulBuff = 0L;
+        ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i));
+        fcChip->Registers.wwn_lo |= ulBuff;
+      }
+    }  // done reading
+    else
+    {
+
+      printk( "cpqfcTS: NVRAM read failed\n");
+
+    }
+  }
+
+  else  // WRITE
+  {
+
+    // NOTE: WRITE not supported & not used in released driver.
+
+   
+    printk("ReadWriteNRAM: can't write NVRAM; aborting write\n");
+  }
+  
+  LEAVE("ReadWriteWWN");
+  return iStatus;
+}
+
+
+
+
+
+// The following function reads or writes the entire "NVRAM" contents of 
+// the I2C hardware (i.e. the NM24C03).  Note that HP's 5121A (TS 66Mhz)
+// adapter does not use the NM24C03 chip, so this function only works on
+// Compaq's adapters.
+
+int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read)
+{
+  PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+  ULONG ulBuff;
+  UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array
+  int iStatus=-1;  // assume failure
+
+     
+  if( Read )  // READing NVRAM?
+  {
+    ulBuff = cpqfcTS_ReadNVRAM(   // TRUE on success
+                fcChip->Registers.TYstatus.address,
+                fcChip->Registers.TYcontrol.address,
+                256,            // bytes to write
+                ucPtr );        // source ptr
+
+
+    if( ulBuff )
+      iStatus = 0; // success
+    else
+    {
+#ifdef DBG
+      printk( "CAUTION: NVRAM read failed\n");
+#endif
+    }
+  }  // done reading
+
+  else  // WRITING NVRAM 
+  {
+
+    printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n");
+  }
+    
+  return iStatus;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSi2c.c linux.18p17/drivers/scsi/cpqfcTSi2c.c
--- linux.vanilla/drivers/scsi/cpqfcTSi2c.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSi2c.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,494 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+// These functions control the NVRAM I2C hardware on 
+// non-intelligent Fibre Host Adapters.
+// The primary purpose is to read the HBA's NVRAM to get adapter's 
+// manufactured WWN to copy into Tachyon chip registers
+// Orignal source author unknown
+
+#include <linux/types.h>
+enum boolean { FALSE, TRUE } ;
+
+
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>  // struct pt_regs for IRQ handler & Port I/O
+
+#include "cpqfcTSchip.h"
+
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data );
+/*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout,
+  USHORT startOffset,  // e.g. 0x2f for WWN start
+  USHORT count,
+  UCHAR *buf );
+*/
+
+//
+// Tachlite GPIO2, GPIO3 (I2C) DEFINES
+// The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data)
+// GPIO2 drives SDA, and GPIO3 drives SCL
+// 
+// Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0
+// and clear writes 1. The input lines (read in TL status) is NOT inverted
+// This really helps confuse the code and debugging.
+
+#define SET_DATA_HI  0x0
+#define SET_DATA_LO  0x8
+#define SET_CLOCK_HI 0x0
+#define SET_CLOCK_LO 0x4
+
+#define SENSE_DATA_HI  0x8
+#define SENSE_DATA_LO  0x0
+#define SENSE_CLOCK_HI 0x4
+#define SENSE_CLOCK_LO 0x0
+
+#define SLAVE_READ_ADDRESS    0xA1
+#define SLAVE_WRITE_ADDRESS   0xA0
+					      
+
+static void i2c_delay(ULONG mstime);
+static void tl_i2c_clock_pulse( UCHAR , void* GPIOout);
+static UCHAR tl_read_i2c_data( void* );
+
+
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_RX_ACK
+//
+//      This routine receives an acknowledge over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout )
+{
+  unsigned long value;
+
+	// do clock pulse, let data line float high
+  tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+	// slave must drive data low for acknowledge
+  value = tl_read_i2c_data( GPIOin);
+  if (value & SENSE_DATA_HI )
+    return( FALSE );
+
+  return( TRUE );
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   READ_I2C_REG
+//
+//      This routine reads the I2C control register using the global
+//      IO address stored in gpioreg.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_read_i2c_data( void* gpioreg )
+{
+  return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   WRITE_I2C_REG
+//
+//      This routine writes the I2C control register using the global
+//      IO address stored in gpioreg.
+//      In Tachlite, we don't want to modify other bits in TL Control reg.
+//
+//-----------------------------------------------------------------------------
+static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value )
+{
+  ULONG  temp;
+
+	// First read the register and clear out the old bits
+  temp = readl( gpioregOUT ) & 0xfffffff3L;
+
+	// Now or in the new data and send it back out
+  writel( temp | value, gpioregOUT);
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_START
+//
+//      This routine transmits a start condition over the I2C bus.
+//      1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH,
+//      wait 5us to stabilize.
+//      2. With SCL still HIGH, drive SDA low.  The low transition marks
+//         the start condition to NM24Cxx (the chip)
+//      NOTE! In TL control reg., output 1 means chip sees LOW
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout )
+{
+  unsigned short i;
+  ULONG value;
+
+  if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI))
+  {
+    // start with clock high, let data float high
+    tl_write_i2c_reg(  GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+    // keep sending clock pulses if slave is driving data line
+    for (i = 0; i < 10; i++)
+    {
+      tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+      if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+	break;
+    }
+
+		// if he's still driving data low after 10 clocks, abort
+    value = tl_read_i2c_data( GPIOin ); // read status
+    if (!(value & 0x08) )
+      return( FALSE );
+  }
+
+
+	// To START, bring data low while clock high
+  tl_write_i2c_reg(  GPIOout, SET_CLOCK_HI | SET_DATA_LO );
+
+  i2c_delay(0);
+
+  return( TRUE );                           // TX start successful
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_STOP
+//
+//      This routine transmits a stop condition over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+
+static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout )
+{
+  int i;
+
+  for (i = 0; i < 10; i++) 
+  {
+  // Send clock pulse, drive data line low
+    tl_i2c_clock_pulse( SET_DATA_LO, GPIOout );
+
+  // To STOP, bring data high while clock high
+    tl_write_i2c_reg(  GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+  // Give the data line time to float high
+    i2c_delay(0);
+
+  // If slave is driving data line low, there's a problem; retry
+    if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+      return( TRUE );  // TX STOP successful!
+  }
+
+  return( FALSE );                      // error
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_TX_uchar
+//
+//      This routine transmits a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data )
+{
+  UCHAR bit;
+
+  for (bit = 0x80; bit; bit >>= 1)
+  {
+    if( data & bit )
+      tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout);
+    else
+      tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout);
+  }  
+}
+//-----------------------------------------------------------------------------
+//
+//      Name:   I2C_RX_uchar
+//
+//      This routine receives a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout )
+{
+  UCHAR bit;
+  UCHAR data = 0;
+
+
+  for (bit = 0x80; bit; bit >>= 1) {
+    // do clock pulse, let data line float high
+    tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+    // read data line
+    if ( tl_read_i2c_data( GPIOin) & 0x08 )
+      data |= bit;
+  }
+
+  return (data);
+}
+//*****************************************************************************
+//*****************************************************************************
+// Function:   read_i2c_nvram
+// Arguments:  UCHAR count     number of bytes to read
+//             UCHAR *buf      area to store the bytes read
+// Returns:    0 - failed
+//             1 - success
+//*****************************************************************************
+//*****************************************************************************
+unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+	UCHAR *buf )
+{
+  unsigned short i;
+
+  if( !( tl_i2c_tx_start(GPIOin, GPIOout) ))
+    return FALSE;
+
+  // Select the NVRAM for "dummy" write, to set the address
+  tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS );
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+    return( FALSE );
+
+  // Now send the address where we want to start reading  
+  tl_i2c_tx_byte( GPIOout , 0 );
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+    return( FALSE );
+
+  // Send a repeated start condition and select the
+  //  slave for reading now.
+  if( tl_i2c_tx_start(GPIOin, GPIOout) )
+    tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS );
+
+  if ( !tl_i2c_rx_ack(GPIOin, GPIOout) )
+    return( FALSE );
+
+  // this loop will now read out the data and store it
+  //  in the buffer pointed to by buf
+  for ( i=0; i<count; i++) 
+  {
+    *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout);
+
+    // Send ACK by holding data line low for 1 clock
+    if ( i < (count-1) )
+      tl_i2c_clock_pulse( 0x08, GPIOout );
+    else {
+	// Don't send ack for final byte
+      tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+    }
+  }
+
+  tl_i2c_tx_stop(GPIOin, GPIOout);
+
+  return( TRUE );
+}
+
+//****************************************************************
+//
+//
+//
+// routines to set and clear the data and clock bits
+//
+//
+//
+//****************************************************************
+
+static void tl_set_clock(void* gpioreg)
+{
+  ULONG ret_val;
+
+  ret_val = readl( gpioreg );
+  ret_val &= 0xffffffFBL;  // clear GPIO2 (SCL)
+  writel( ret_val, gpioreg);
+}
+
+static void tl_clr_clock(void* gpioreg)
+{
+  ULONG ret_val;
+
+  ret_val = readl( gpioreg );
+  ret_val |= SET_CLOCK_LO;
+  writel( ret_val, gpioreg);
+}
+
+//*****************************************************************
+//
+//
+// This routine will advance the clock by one period
+//
+//
+//*****************************************************************
+static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout  )
+{
+  ULONG ret_val;
+
+  // clear the clock bit
+  tl_clr_clock( GPIOout );
+
+  i2c_delay(0);
+
+
+  // read the port to preserve non-I2C bits
+  ret_val = readl( GPIOout );
+
+  // clear the data & clock bits
+  ret_val &= 0xFFFFFFf3;
+
+  // write the value passed in...
+  // data can only change while clock is LOW!
+  ret_val |= value;           // the data
+  ret_val |= SET_CLOCK_LO;    // the clock
+  writel( ret_val, GPIOout );
+
+  i2c_delay(0);
+
+
+  //set clock bit
+  tl_set_clock( GPIOout);
+}
+
+
+
+
+//*****************************************************************
+//
+//
+// This routine returns the 64-bit WWN
+//
+//
+//*****************************************************************
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf )
+{
+  ULONG len;
+  ULONG sub_len;
+  ULONG ptr_inc;
+  ULONG i;
+  ULONG j;
+  UCHAR *data_ptr;
+  UCHAR  z;
+  UCHAR  name;
+  UCHAR  sub_name;
+  UCHAR  done;
+  int iReturn=0;  // def. 0 offset is failure to find WWN field
+  
+
+	  
+  data_ptr = (UCHAR *)buf;
+
+  done = FALSE;
+  i = 0;
+
+  while ( (i < 128) && (!done) ) 
+  {
+    z = data_ptr[i];\
+    if ( !(z & 0x80) )  
+    {	
+      len  = 1 + (z & 0x07);
+
+      name = (z & 0x78) >> 3;
+      if (name == 0x0F)
+        done = TRUE;
+    }
+    else 
+    {
+      name = z & 0x7F;
+      len  = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8);
+           
+      switch (name) 
+      {
+      case 0x0D:
+	//
+	  j = i + 3;
+	  //
+	  if ( data_ptr[j] == 0x3b ) {
+	    len = 6;
+	    break;
+	  }
+
+	  while ( j<(i+len) ) {
+	    sub_name = (data_ptr[j] & 0x3f);
+	    sub_len  = data_ptr[j+1] + 
+	               (data_ptr[j+2] << 8);
+            ptr_inc  = sub_len + 3; 
+	    switch (sub_name) 
+	    {
+	    case 0x3C:
+              memcpy( wwnbuf, &data_ptr[j+3], 8);
+              iReturn = j+3;
+              break;
+            default:
+              break;
+	    }
+	    j += ptr_inc;
+          }
+	  break;
+        default:
+	  break;
+      }  
+    }  
+  //
+    i += len;
+  }  // end while 
+  return iReturn;
+}
+
+
+
+
+
+// define a short 5 micro sec delay, and longer (ms) delay
+
+static void i2c_delay(ULONG mstime)
+{
+  ULONG i;
+  
+// NOTE: we only expect to use these delays when reading
+// our adapter's NVRAM, which happens only during adapter reset.
+// Delay technique from "Linux Device Drivers", A. Rubini 
+// (1st Ed.) pg 137.
+
+//  printk(" delay %lx  ", mstime);
+  if( mstime ) // ms delay?
+  {
+    // delay technique
+    for( i=0; i < mstime; i++)
+      udelay(1000); // 1ms per loop
+	
+  }
+  else  // 5 micro sec delay
+  
+    udelay( 5 ); // micro secs
+  
+//  printk("done\n");
+}
+
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSinit.c linux.18p17/drivers/scsi/cpqfcTSinit.c
--- linux.vanilla/drivers/scsi/cpqfcTSinit.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSinit.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,1815 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+ * IOCTL and procfs added by Jouke Numan
+ * SMP testing by Chel Van Gennip
+ *
+ * portions copied from:
+ * QLogic CPQFCTS SCSI-FCP
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> 
+ * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200
+*/
+
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>  // request_region() prototype
+#include <linux/vmalloc.h> // ioremap()
+#ifdef __alpha__
+#define __KERNEL_SYSCALLS__
+#endif
+#include <asm/unistd.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>   // ioctl related
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+#include "hosts.h"
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+#include "cpqfcTS.h"
+
+#include <linux/module.h>
+/* Embedded module documentation macros - see module.h */
+MODULE_AUTHOR("Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA");
+
+// This struct was originally defined in 
+// /usr/src/linux/include/linux/proc_fs.h
+// since it's only partially implemented, we only use first
+// few fields...
+// NOTE: proc_fs changes in 2.4 kernel
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_cpqfcTS =
+{
+  PROC_SCSI_CPQFCTS,           // ushort low_ino (enumerated list)
+  7,                           // ushort namelen
+  DEV_NAME,                    // const char* name
+  S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode
+  2                            // nlink_t nlink
+	                       // etc. ...
+};
+
+
+#endif
+
+
+
+/* local function to load our per-HBA (local) data for chip
+   registers, FC link state, all FC exchanges, etc.
+
+   We allocate space and compute address offsets for the
+   most frequently accessed addresses; others (like World Wide
+   Name) are not necessary.
+   
+*/
+static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
+{
+             
+  cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
+
+  // since x86 port space is 64k, we only need the lower 16 bits
+  cpqfcHBAdata->fcChip.Registers.IOBaseL = 
+    PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+  
+  cpqfcHBAdata->fcChip.Registers.IOBaseU = 
+    PciDev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+  
+  // 32-bit memory addresses
+  cpqfcHBAdata->fcChip.Registers.MemBase = 
+    PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK;
+
+  cpqfcHBAdata->fcChip.Registers.ReMapMemBase = 
+    ioremap( PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK,
+             0x200);
+  
+  cpqfcHBAdata->fcChip.Registers.RAMBase = 
+    PciDev->base_address[4];
+  
+  cpqfcHBAdata->fcChip.Registers.SROMBase =  // NULL for HP TS adapter
+    PciDev->base_address[5];
+  
+  // now the Tachlite chip registers
+  // the REGISTER struct holds both the physical address & last
+  // written value (some TL registers are WRITE ONLY)
+
+  cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX;
+
+  cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX;
+      
+  // TL Frame Manager
+  cpqfcHBAdata->fcChip.Registers.FMconfig.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG;
+  cpqfcHBAdata->fcChip.Registers.FMcontrol.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL;
+  cpqfcHBAdata->fcChip.Registers.FMstatus.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS;
+  cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1;
+  cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2;
+  cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0;
+      
+      // TL Control Regs
+  cpqfcHBAdata->fcChip.Registers.TYconfig.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG;
+  cpqfcHBAdata->fcChip.Registers.TYcontrol.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL;
+  cpqfcHBAdata->fcChip.Registers.TYstatus.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS;
+  cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA;
+  cpqfcHBAdata->fcChip.Registers.ed_tov.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV;
+
+
+  cpqfcHBAdata->fcChip.Registers.INTEN.address = 
+	        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN;
+  cpqfcHBAdata->fcChip.Registers.INTPEND.address = 
+	        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND;
+  cpqfcHBAdata->fcChip.Registers.INTSTAT.address = 
+        cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT;
+
+  DEBUG_PCI(printk("  cpqfcHBAdata->fcChip.Registers. :\n"));
+  DEBUG_PCI(printk("    IOBaseL = %x\n", 
+    cpqfcHBAdata->fcChip.Registers.IOBaseL));
+  DEBUG_PCI(printk("    IOBaseU = %x\n", 
+    cpqfcHBAdata->fcChip.Registers.IOBaseU));
+  
+  printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+  
+  DEBUG_PCI(printk("    SFQconsumerIndex.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address));
+  DEBUG_PCI(printk("    ERQproducerIndex.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address));
+  DEBUG_PCI(printk("    TYconfig.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.TYconfig.address));
+  DEBUG_PCI(printk("    FMconfig.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.FMconfig.address));
+  DEBUG_PCI(printk("    FMcontrol.address = %p\n", 
+    cpqfcHBAdata->fcChip.Registers.FMcontrol.address));
+
+  // set default options for FC controller (chip)
+  cpqfcHBAdata->fcChip.Options.initiator = 1;  // default: SCSI initiator
+  cpqfcHBAdata->fcChip.Options.target = 0;     // default: SCSI target
+  cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC
+  cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip
+
+  // set highest and lowest FC-PH version the adapter/driver supports
+  // (NOT strict compliance)
+  cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3;
+  cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43;
+
+  // set function points for this controller / adapter
+  cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite;
+  cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite;
+  cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite;
+  cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues;
+  cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues;
+  cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite;  
+  cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl;  
+  cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry;
+  cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager;;  
+  cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
+  cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
+
+ 
+
+}
+
+
+/* (borrowed from linux/drivers/scsi/hosts.c) */
+static void launch_FCworker_thread(struct Scsi_Host *HostAdapter)
+{
+  DECLARE_MUTEX_LOCKED(sem);
+
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+  ENTER("launch_FC_worker_thread");
+             
+  cpqfcHBAdata->notify_wt = &sem;
+
+  kernel_thread((int (*)(void *))cpqfcTSWorkerThread, 
+                          (void *) HostAdapter, 0);
+  /*
+   * Now wait for the kernel error thread to initialize itself
+
+   */
+  down (&sem);
+  cpqfcHBAdata->notify_wt = NULL;
+
+  LEAVE("launch_FC_worker_thread");
+ 
+}
+
+
+/* "Entry" point to discover if any supported PCI 
+   bus adapter can be found
+*/
+// We're supporting:
+// Compaq 64-bit, 66MHz HBA with Tachyon TS
+// Agilent XL2 
+#define HBA_TYPES 2
+
+
+int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate)
+{
+  int NumberOfAdapters=0; // how many of our PCI adapters are found?
+  struct pci_dev *PciDev = NULL;
+  struct Scsi_Host *HostAdapter = NULL;
+  CPQFCHBA *cpqfcHBAdata = NULL; 
+  struct timer_list *cpqfcTStimer = NULL;
+  SupportedPCIcards PCIids[HBA_TYPES];
+  int i;
+  
+  ENTER("cpqfcTS_detect");
+  
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+  ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS;
+#else
+  ScsiHostTemplate->proc_name = "cpqfcTS";
+#endif
+  
+  if( pci_present() == 0) // no PCI busses?
+  {
+    printk( "  no PCI bus?@#!\n");
+    return NumberOfAdapters;
+  }
+
+  // what HBA adapters are we supporting?
+  PCIids[0].vendor_id = PCI_VENDOR_ID_COMPAQ;
+  PCIids[0].device_id = CPQ_DEVICE_ID;
+  PCIids[1].vendor_id = PCI_VENDOR_ID_HP; // i.e. 103Ch (Agilent == HP for now)
+  PCIids[1].device_id = AGILENT_XL2_ID;   // i.e. 1029h
+
+  for( i=0; i < HBA_TYPES; i++)
+  {
+    // look for all HBAs of each type
+
+    while( (PciDev =
+      pci_find_device( PCIids[i].vendor_id, PCIids[i].device_id, PciDev) ))
+    {
+      // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes...
+      printk(" scsi_register allocating %d bytes for FC HBA\n",
+		      (ULONG)sizeof(CPQFCHBA));
+
+      HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) );
+      DEBUG_PCI( printk("  HBA found!\n"));
+      DEBUG_PCI( printk("  HostAdapter->PciDev->irq = %u\n", PciDev->irq) );
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[0]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[1]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[2]));
+      DEBUG_PCI(printk("  PciDev->baseaddress[]= %lx\n", PciDev->base_address[3]));
+
+      
+      HostAdapter->irq = PciDev->irq;  // copy for Scsi layers
+      
+      // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper),
+      // for a total I/O port address space of 512 bytes.
+      // mask out the I/O port address (lower) & record
+      HostAdapter->io_port = (unsigned int)
+	     PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+      HostAdapter->n_io_port = 0xff;
+      
+      // i.e., expect 128 targets (arbitrary number), while the
+      //  RA-4000 supports 32 LUNs
+      HostAdapter->max_id =  0;   // incremented as devices log in    
+      HostAdapter->max_lun = CPQFCTS_MAX_LUN;         // LUNs per FC device
+      HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses?
+      HostAdapter->hostt->use_new_eh_code = 1; // new error handling
+      
+      // get the pointer to our HBA specific data... (one for
+      // each HBA on the PCI bus(ses)).
+      cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+      
+      // make certain our data struct is clear
+      memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) );
+
+
+      // initialize our HBA info
+      cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+      cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr
+      Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields
+     
+      cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+
+      // request necessary resources and check for conflicts
+      if( request_irq( HostAdapter->irq,
+		       cpqfcTS_intr_handler,
+	               SA_INTERRUPT | SA_SHIRQ,
+	               DEV_NAME,
+		       HostAdapter) )
+      {
+	printk(" IRQ %u already used\n", HostAdapter->irq);
+        scsi_unregister( HostAdapter);
+	continue;
+      }
+
+      // Since we have two 256-byte I/O port ranges (upper
+      // and lower), check them both
+      if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff) )
+      {
+	printk("  cpqfcTS address in use: %x\n", 
+			cpqfcHBAdata->fcChip.Registers.IOBaseU);
+	free_irq( HostAdapter->irq, HostAdapter);
+        scsi_unregister( HostAdapter);
+	continue;
+      }	
+      
+      if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff) )
+      {
+  	printk("  cpqfcTS address in use: %x\n", 
+	      			cpqfcHBAdata->fcChip.Registers.IOBaseL);
+	free_irq( HostAdapter->irq, HostAdapter);
+        scsi_unregister( HostAdapter);
+	continue;
+      }	
+      
+      // OK, we should be able to grab everything we need now.
+      request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff, DEV_NAME);
+      request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff, DEV_NAME);
+      DEBUG_PCI(printk("  Requesting 255 I/O addresses @ %x\n",
+        cpqfcHBAdata->fcChip.Registers.IOBaseL ));
+      DEBUG_PCI(printk("  Requesting 255 I/O addresses @ %x\n",
+        cpqfcHBAdata->fcChip.Registers.IOBaseU ));
+
+      
+      // start our kernel worker thread
+
+      launch_FCworker_thread(HostAdapter);
+
+
+      // start our TimerTask...
+
+      cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer;
+
+      init_timer( cpqfcTStimer); // Linux clears next/prev values
+      cpqfcTStimer->expires = jiffies + HZ; // one second
+      cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter
+      cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping
+
+      add_timer( cpqfcTStimer);  // give it to Linux
+
+
+      // now initialize our hardware...
+
+      cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1);
+
+      cpqfcHBAdata->fcStatsTime = jiffies;  // (for FC Statistics delta)
+      
+      // give our HBA time to initialize and login current devices...
+      {
+	// The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000,
+	// has the following algorithm for FL_Port startup:
+	// Time(sec) Action
+	// 0:        Device Plugin and LIP(F7,F7) transmission
+	// 1.0       LIP incoming
+        // 1.027     LISA incoming, no CLS! (link not up)
+	// 1.028     NOS incoming (switch test for N_Port)
+        // 1.577     ED_TOV expired, transmit LIPs again	
+	// 3.0       LIP(F8,F7) incoming (switch passes Tach Prim.Sig)
+	// 3.028     LILP received, link up, FLOGI starts
+	// slowest(worst) case, measured on 1Gb Finisar GT analyzer
+	
+	int wait_time;
+        for( wait_time = jiffies + 4*HZ; wait_time > jiffies; )
+	  schedule();  // (our worker task needs to run)
+
+      }
+      
+      NumberOfAdapters++; 
+    } // end of while()
+  }
+
+  LEAVE("cpqfcTS_detect");
+ 
+  return NumberOfAdapters;
+}
+
+
+static void my_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+    struct request * req;
+    
+    req = &SCpnt->request;
+    req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+  
+    if (req->sem != NULL) {
+	up(req->sem);
+    }
+}   
+
+
+
+int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
+{
+  int result = 0;
+  struct Scsi_Host *HostAdapter = ScsiDev->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  Scsi_Cmnd DumCmnd;
+  int i, j;
+  VENDOR_IOCTL_REQ ioc;
+  cpqfc_passthru_t *vendor_cmd;
+  Scsi_Device *SDpnt;
+  Scsi_Cmnd *ScsiPassThruCmnd;
+  unsigned long flags;
+
+  ENTER("cpqfcTS_ioctl");
+  
+  // can we find an FC device mapping to this SCSI target?
+  DumCmnd.channel = ScsiDev->channel;		// For searching
+  DumCmnd.target  = ScsiDev->id;
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+    &DumCmnd, // search Scsi Nexus
+    0,        // DON'T search linked list for FC port id
+    NULL,     // DON'T search linked list for FC WWN
+    NULL);    // DON'T care about end of list
+ 
+  if( pLoggedInPort == NULL )      // not found!
+  {
+    result = -ENXIO;
+  }
+ 
+  else  // we know what FC device to operate on...
+  {
+    switch (Cmnd) 
+    {
+      // Passthrough provides a mechanism to bypass the RAID
+      // or other controller and talk directly to the devices
+      // (e.g. physical disk drive)
+      // Passthrough commands, unfortunately, tend to be vendor
+      // specific; this is tailored to COMPAQ's RAID (RA4x00)
+      case CPQFCTS_SCSI_PASSTHRU:
+      {
+	void *buf = NULL; // for kernel space buffer for user data
+	
+	if( !arg)
+	  return -EINVAL;
+
+	// must be super user to send stuff directly to the
+	// controller and/or physical drives...
+	if( !suser() )
+	  return -EPERM;
+
+	// copy the caller's struct to our space.
+        copy_from_user_ret( &ioc, arg, 
+			  sizeof( VENDOR_IOCTL_REQ), -EFAULT);
+
+	vendor_cmd = ioc.argp;  // i.e., CPQ specific command struct
+
+	// If necessary, grab a kernel/DMA buffer
+	if( vendor_cmd->len)
+	{
+  	  buf = kmalloc( vendor_cmd->len, GFP_KERNEL);
+	  if( !buf)
+	    return -ENOMEM;
+	}
+
+        // Now build a SCSI_CMND to pass down...
+	// This function allocates and sets Scsi_Cmnd ptrs such as
+	//  ->channel, ->target, ->host
+        ScsiPassThruCmnd = scsi_allocate_device(NULL, ScsiDev, 1);
+
+        // Need data from user?
+	// make sure caller's buffer is in kernel space.
+	if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) &&
+	    vendor_cmd->len)
+          copy_from_user_ret( buf, vendor_cmd->bufp, vendor_cmd->len, -EFAULT);
+	    
+	// copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below)
+        memcpy( &ScsiPassThruCmnd->cmnd[0], 
+		&vendor_cmd->cdb[0], 
+		MAX_COMMAND_SIZE);  
+        // we want to copy all 16 bytes into the FCP-SCSI CDB,
+	// although the actual passthru only uses up to the
+	// first 12.
+	
+	ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB
+
+	// Unfortunately, the SCSI command cmnd[] field has only
+	// 12 bytes.  Ideally the MAX_COMMAND_SIZE should be increased
+	// to 16 for newer Fibre Channel and SCSI-3 larger CDBs.
+	// However, to avoid a mandatory kernel rebuild, we use the SCp
+	// spare field to store the extra 4 bytes ( ugly :-(
+
+	if( MAX_COMMAND_SIZE < 16)
+	{
+          memcpy( &ScsiPassThruCmnd->SCp.buffers_residual,
+		  &vendor_cmd->cdb[12], 4);
+	}	  
+                  
+	
+        ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU!
+	                                        // suppress LUN masking
+	                                        // and VSA logic
+
+	// Use spare fields to copy FCP-SCSI LUN address info...
+        ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus;
+	ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive;
+
+
+
+        // We copy the scheme used by scsi.c to submit commands
+	// to our own HBA.  We do this in order to stall the
+	// thread calling the IOCTL until it completes, and use
+	// the same "_quecommand" function for synchronizing
+	// FC Link events with our "worker thread".
+	
+        spin_lock_irqsave(&io_request_lock, flags);
+        {
+          DECLARE_MUTEX_LOCKED(sem);
+          ScsiPassThruCmnd->request.sem = &sem;
+          // eventually gets us to our own _quecommand routine
+          scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0], 
+	       buf, 
+	       vendor_cmd->len, 
+	       my_ioctl_done, 
+	       10*HZ, 1);// timeout,retries
+          spin_unlock_irqrestore(&io_request_lock, flags);
+          // Other I/Os can now resume; we wait for our ioctl
+	  // command to complete
+	  down(&sem);
+          spin_lock_irqsave(&io_request_lock, flags);
+          ScsiPassThruCmnd->request.sem = NULL;
+        }
+	
+        result = ScsiPassThruCmnd->result;
+
+        // copy any sense data back to caller
+        if( result != 0 )
+	{
+	  memcpy( vendor_cmd->sense_data, // see struct def - size=40
+		  ScsiPassThruCmnd->sense_buffer, 
+		  sizeof(ScsiPassThruCmnd->sense_buffer)); 
+	}
+        SDpnt = ScsiPassThruCmnd->device;
+        scsi_release_command(ScsiPassThruCmnd); // "de-allocate"
+        ScsiPassThruCmnd = NULL;
+
+        if (!SDpnt->was_reset && SDpnt->scsi_request_fn)
+          (*SDpnt->scsi_request_fn)();
+
+        wake_up(&SDpnt->device_wait);
+        spin_unlock_irqrestore(&io_request_lock, flags);
+
+	// need to pass data back to user (space)?
+	if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) &&
+	     vendor_cmd->len )
+          copy_to_user_ret( vendor_cmd->bufp, buf, vendor_cmd->len, -EFAULT);
+
+        if( buf) 
+	  kfree( buf);
+	
+        return result;
+      }
+      
+      case CPQFCTS_GETPCIINFO:
+      {
+	cpqfc_pci_info_struct pciinfo;
+	
+	if( !arg)
+	  return -EINVAL;
+
+         	
+	
+        pciinfo.bus = cpqfcHBAdata->PciDev->bus->number;
+        pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn;  
+	pciinfo.board_id = cpqfcHBAdata->PciDev->device |
+			  (cpqfcHBAdata->PciDev->vendor <<16); 
+	      
+        copy_to_user_ret( arg, &pciinfo, 
+			  sizeof(cpqfc_pci_info_struct), -EFAULT);
+        return 0;
+      }
+
+      case CPQFCTS_GETDRIVVER:
+      {
+	DriverVer_type DriverVer = 
+		CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR);
+	
+	if( !arg)
+	  return -EINVAL;
+
+        copy_to_user_ret( arg, &DriverVer, 
+			  sizeof(DriverVer), -EFAULT);
+        return 0;
+      }
+
+
+
+      case SCSI_IOCTL_FC_TARGET_ADDRESS:
+      result = 
+        verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
+      if (result) 
+	break;
+ 
+      put_user(pLoggedInPort->port_id,
+		&((Scsi_FCTargAddress *) arg)->host_port_id);
+ 
+      for( i=3,j=0; i>=0; i--)   	// copy the LOGIN port's WWN
+        put_user(pLoggedInPort->u.ucWWN[i], 
+		&((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+      for( i=7; i>3; i--)		// copy the LOGIN port's WWN
+        put_user(pLoggedInPort->u.ucWWN[i], 
+		&((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+        break;
+    default:
+      result = -EINVAL;
+      break;
+    }
+  }
+
+  LEAVE("cpqfcTS_ioctl");
+  return result;
+}
+
+
+/* "Release" the Host Bus Adapter...
+   disable interrupts, stop the HBA, release the interrupt,
+   and free all resources */
+
+int cpqfcTS_release(struct Scsi_Host *HostAdapter)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; 
+
+
+  ENTER("cpqfcTS_release");
+	
+  DEBUG_PCI( printk(" cpqfcTS: delete timer...\n"));
+  del_timer( &cpqfcHBAdata->cpqfcTStimer);  
+    
+  // disable the hardware...
+  DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n"));
+  cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS);
+
+  // kill kernel thread
+  if( cpqfcHBAdata->worker_thread ) // (only if exists)
+  {
+    DECLARE_MUTEX_LOCKED(sem);  // synchronize thread kill
+
+    cpqfcHBAdata->notify_wt = &sem;
+    DEBUG_PCI( printk(" killing kernel thread\n"));
+    send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1);
+    down( &sem);
+    cpqfcHBAdata->notify_wt = NULL;
+    
+  }
+
+  // free Linux resources
+  DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
+  free_irq( HostAdapter->irq, HostAdapter);
+  scsi_unregister( HostAdapter);
+  release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff);
+  release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff);
+ /* we get "vfree: bad address" executing this - need to investigate... 
+  if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) !=
+      cpqfcHBAdata->fcChip.Registers.ReMapMemBase)
+    vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+*/
+
+  LEAVE("cpqfcTS_release");
+  return 0;
+}
+
+
+const char * cpqfcTS_info(struct Scsi_Host *HostAdapter)
+{
+  static char buf[300];
+  CPQFCHBA *cpqfcHBA;
+  int BusSpeed, BusWidth;
+  
+  // get the pointer to our Scsi layer HBA buffer  
+  cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+
+  BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ?
+               64 : 32;
+
+  if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000)
+    BusSpeed = 66;
+  else
+    BusSpeed = 33;
+
+  sprintf(buf, 
+"%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d",
+      cpqfcHBA->fcChip.Name, 
+      cpqfcHBA->fcChip.Registers.wwn_hi,
+      cpqfcHBA->fcChip.Registers.wwn_lo,
+      cpqfcHBA->PciDev->bus->number,
+      cpqfcHBA->PciDev->device,  
+      HostAdapter->irq,
+      cpqfcHBA->fcChip.Registers.IOBaseL,
+      cpqfcHBA->fcChip.Registers.MemBase,
+      BusWidth,
+      BusSpeed,
+      VER_MAJOR, VER_MINOR, VER_SUBMINOR
+);
+
+  
+  cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  return buf;
+}
+
+//
+// /proc/scsi support. The following routines allow us to do 'normal'
+// sprintf like calls to return the currently requested piece (buflenght
+// chars, starting at bufoffset) of the file. Although procfs allows for
+// a 1 Kb bytes overflow after te supplied buffer, I consider it bad 
+// programming to use it to make programming a little simpler. This piece
+// of coding is borrowed from ncr53c8xx.c with some modifications 
+//
+struct info_str
+{
+        char *buffer;			// Pointer to output buffer
+        int buflength;			// It's length
+        int bufoffset;			// File offset corresponding with buf[0]
+	int buffillen;			// Current filled length 
+        int filpos;			// Current file offset
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int datalen)
+{
+
+  if (info->filpos < info->bufoffset) {	// Current offset before buffer offset
+    if (info->filpos + datalen <= info->bufoffset) {
+      info->filpos += datalen; 		// Discard if completely before buffer
+      return;
+    } else {				// Partial copy, set to begin
+      data += (info->bufoffset - info->filpos);
+      datalen  -= (info->bufoffset - info->filpos);
+      info->filpos = info->bufoffset;
+    }
+  }
+
+  info->filpos += datalen;		// Update current offset
+
+  if (info->buffillen == info->buflength) // Buffer full, discard
+    return;
+
+  if (info->buflength - info->buffillen < datalen)  // Overflows buffer ?
+    datalen = info->buflength - info->buffillen;
+
+  memcpy(info->buffer + info->buffillen, data, datalen);
+  info->buffillen += datalen;
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+        va_list args;
+        char buf[400];
+        int len;
+
+        va_start(args, fmt);
+        len = vsprintf(buf, fmt, args);
+        va_end(args);
+
+        copy_mem_info(info, buf, len);
+        return len;
+}
+
+
+// Routine to get data for /proc RAM filesystem
+//
+int cpqfcTS_proc_info (char *buffer, char **start, off_t offset, int length, 
+		       int hostno, int inout)
+{
+  struct Scsi_Host *host;
+  Scsi_Cmnd DumCmnd;
+  int Chan, Targ, i;
+  struct info_str info;
+  CPQFCHBA *cpqfcHBA;
+  PTACHYON fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  char buf[81];
+
+  // Search the Scsi host list for our controller
+  for (host=scsi_hostlist; host; host=host->next)
+    if (host->host_no == hostno)
+      break;
+
+  if (!host) return -ESRCH;
+
+  if (inout) return -EINVAL;
+
+  // get the pointer to our Scsi layer HBA buffer  
+  cpqfcHBA = (CPQFCHBA *)host->hostdata;
+  fcChip = &cpqfcHBA->fcChip;
+  
+  *start 	  = buffer;
+
+  info.buffer     = buffer;
+  info.buflength  = length;
+  info.bufoffset  = offset;
+  info.filpos     = 0;
+  info.buffillen  = 0;
+  copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR); 
+  cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]);
+  cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+  copy_info(&info, "%s\n", buf); 
+		  
+
+#define DISPLAY_WWN_INFO
+#ifdef DISPLAY_WWN_INFO
+  copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n");
+  for ( Chan=0; Chan <= host->max_channel; Chan++) {
+    DumCmnd.channel = Chan;
+    for (Targ=0; Targ <= host->max_id; Targ++) {
+      DumCmnd.target = Targ;
+      if ((pLoggedInPort = fcFindLoggedInPort( fcChip,
+	    			&DumCmnd, // search Scsi Nexus
+    				0,        // DON'T search list for FC port id
+    				NULL,     // DON'T search list for FC WWN
+    				NULL))){   // DON'T care about end of list
+	copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ",
+			   hostno, Chan, Targ);
+        for( i=3; i>=0; i--)        // copy the LOGIN port's WWN
+          copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+        for( i=7; i>3; i--)             // copy the LOGIN port's WWN
+          copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+	copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id); 
+      }
+    }
+  }
+#endif
+  
+  
+// Unfortunately, the proc_info buffer isn't big enough
+// for everything we would like...
+// For FC stats, compile this and turn off WWN stuff above  
+//#define DISPLAY_FC_STATS
+#ifdef DISPLAY_FC_STATS
+// get the Fibre Channel statistics
+  {
+    int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ;
+    int days,hours,minutes,secs;
+    
+    days = DeltaSecs / (3600*24); // days
+    hours = (DeltaSecs% (3600*24)) / 3600; // hours
+    minutes = (DeltaSecs%3600 /60); // minutes
+    secs =  DeltaSecs%60;  // secs
+copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n",
+      days, hours, minutes, secs);
+  }
+    
+  cpqfcHBA->fcStatsTime = jiffies;  // (for next delta)
+
+  copy_info( &info, "  LinkUp           %9u     LinkDown      %u\n",
+        fcChip->fcStats.linkUp, fcChip->fcStats.linkDown);
+        
+  copy_info( &info, "  Loss of Signal   %9u     Loss of Sync  %u\n",
+    fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync);
+		  
+  copy_info( &info, "  Discarded Frames %9u     Bad CRC Frame %u\n",
+    fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC);
+
+  copy_info( &info, "  TACH LinkFailTX  %9u     TACH LinkFailRX     %u\n",
+    fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX);
+  
+  copy_info( &info, "  TACH RxEOFa      %9u     TACH Elastic Store  %u\n",
+    fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores);
+
+  copy_info( &info, "  BufferCreditWait %9uus   TACH FM Inits %u\n",
+    fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits );
+	
+  copy_info( &info, "  FC-2 Timeouts    %9u     FC-2 Logouts  %u\n",
+    fcChip->fcStats.timeouts, fcChip->fcStats.logouts); 
+        
+  copy_info( &info, "  FC-2 Aborts      %9u     FC-4 Aborts   %u\n",
+    fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted);
+   
+  // clear the counters
+  cpqfcTSClearLinkStatusCounters( fcChip);
+#endif
+	
+  return info.buffillen;
+}
+
+
+#if DEBUG_CMND
+
+UCHAR *ScsiToAscii( UCHAR ScsiCommand)
+{
+
+/*++
+
+Routine Description:
+
+   Converts a SCSI command to a text string for debugging purposes.
+
+
+Arguments:
+
+   ScsiCommand -- hex value SCSI Command
+
+
+Return Value:
+
+   An ASCII, null-terminated string if found, else returns NULL.
+
+Original code from M. McGowen, Compaq
+--*/
+
+
+   switch (ScsiCommand)
+   {
+      case 0x00:
+         return( "Test Unit Ready" );
+
+      case 0x01:
+         return( "Rezero Unit or Rewind" );
+
+      case 0x02:
+         return( "Request Block Address" );
+
+      case 0x03:
+         return( "Requese Sense" );
+
+      case 0x04:
+         return( "Format Unit" );
+
+      case 0x05:
+         return( "Read Block Limits" );
+
+      case 0x07:
+         return( "Reassign Blocks" );
+
+      case 0x08:
+         return( "Read (6)" );
+
+      case 0x0a:
+         return( "Write (6)" );
+
+      case 0x0b:
+         return( "Seek (6)" );
+
+      case 0x12:
+         return( "Inquiry" );
+
+      case 0x15:
+         return( "Mode Select (6)" );
+
+      case 0x16:
+         return( "Reserve" );
+
+      case 0x17:
+         return( "Release" );
+
+      case 0x1a:
+         return( "ModeSen(6)" );
+
+      case 0x1b:
+         return( "Start/Stop Unit" );
+
+      case 0x1c:
+         return( "Receive Diagnostic Results" );
+
+      case 0x1d:
+         return( "Send Diagnostic" );
+
+      case 0x25:
+         return( "Read Capacity" );
+
+      case 0x28:
+         return( "Read (10)" );
+
+      case 0x2a:
+         return( "Write (10)" );
+
+      case 0x2b:
+         return( "Seek (10)" );
+
+      case 0x2e:
+         return( "Write and Verify" );
+
+      case 0x2f:
+         return( "Verify" );
+
+      case 0x34:
+         return( "Pre-Fetch" );
+
+      case 0x35:
+         return( "Synchronize Cache" );
+
+      case 0x37:
+         return( "Read Defect Data (10)" );
+
+      case 0x3b:
+         return( "Write Buffer" );
+
+      case 0x3c:
+         return( "Read Buffer" );
+
+      case 0x3e:
+         return( "Read Long" );
+
+      case 0x3f:
+         return( "Write Long" );
+
+      case 0x41:
+         return( "Write Same" );
+
+      case 0x4c:
+         return( "Log Select" );
+
+      case 0x4d:
+         return( "Log Sense" );
+
+      case 0x56:
+         return( "Reserve (10)" );
+
+      case 0x57:
+         return( "Release (10)" );
+
+      case 0xa0:
+         return( "ReportLuns" );
+
+      case 0xb7:
+         return( "Read Defect Data (12)" );
+
+      case 0xca:
+         return( "Peripheral Device Addressing SCSI Passthrough" );
+
+      case 0xcb:
+         return( "Compaq Array Firmware Passthrough" );
+
+      default:
+         return( NULL );
+   }
+
+} // end ScsiToAscii()
+
+void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd)
+{
+
+printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n", 
+    ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len);
+
+if( cmd->cmnd[0] == 0)   // Test Unit Ready?
+{
+  int i;
+
+  printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n",
+    cmd->request_bufflen, cmd->use_sg, cmd->bufflen);
+  printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n",
+    cmd->request_buffer, cmd->sglist_len, cmd->buffer);
+  for (i = 0; i < cmd->cmd_len; i++)
+    printk("0x%02x ", cmd->cmnd[i]);
+  printk("\n");
+}
+
+}
+
+#endif				/* DEBUG_CMND */
+
+
+
+
+static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int i;
+
+  for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+  {    // find spare slot
+    if( cpqfcHBAdata->BoardLockCmnd[i] == NULL )
+    {
+      cpqfcHBAdata->BoardLockCmnd[i] = Cmnd;
+//      printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+//        i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      break;
+    }
+  }
+  if( i >= CPQFCTS_REQ_QUEUE_LEN)
+  {
+    printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd);
+  }
+
+}
+
+
+static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int indx;
+
+  // Remember the command ptr so we can return; we'll complete when
+  // the device comes back, causing immediate retry
+  for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++)
+  {
+    if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available?
+    {
+#ifdef DUMMYCMND_DBG
+      printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx);
+#endif
+      cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd;
+      break;
+    }
+  }
+
+  if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd??
+  {
+    // this will result in an _abort call later (with possible trouble)
+    printk("no buffer for LinkDnCmnd!! %p\n", Cmnd);
+  }
+}
+
+
+
+
+
+// The file "hosts.h" says not to call scsi_done from
+// inside _queuecommand, so we'll do it from the heartbeat timer
+
+static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+  int i;
+    //    printk(" can't find target %d\n", Cmnd->target);
+
+  for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+  {    // find spare slot
+    if( cpqfcHBAdata->BadTargetCmnd[i] == NULL )
+    {
+      cpqfcHBAdata->BadTargetCmnd[i] = Cmnd;
+//      printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+//          i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      break;
+    }
+  }
+}
+
+
+// This is the "main" entry point for Linux Scsi commands --
+// it all starts here.
+
+int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *))
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  TachFCHDR_GCMND fchs;  // only use for FC destination id field  
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  ULONG ulStatus, SESTtype;
+  LONG ExchangeID;
+
+
+
+
+  ENTER("cpqfcTS_queuecommand");
+      
+  PCI_TRACEO( (ULONG)Cmnd, 0x98)
+      
+  
+  Cmnd->scsi_done = done;
+#ifdef DEBUG_CMND  
+  cpqfcTS_print_scsi_cmd( Cmnd);
+#endif
+
+  // prevent board contention with kernel thread...  
+  
+   if( cpqfcHBAdata->BoardLock )
+  {
+//    printk(" @BrdLck Hld@ ");
+    QueCmndOnBoardLock( cpqfcHBAdata, Cmnd);
+  }
+  
+  else
+  {
+
+    // in the current system (2.2.12), this routine is called
+    // after spin_lock_irqsave(), so INTs are disabled. However,
+    // we might have something pending in the LinkQ, which
+    // might cause the WorkerTask to run.  In case that
+    // happens, make sure we lock it out.
+    
+    
+    
+    PCI_TRACE( 0x98) 
+    CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+    PCI_TRACE( 0x98) 
+	    
+  // can we find an FC device mapping to this SCSI target?
+    pLoggedInPort = fcFindLoggedInPort( fcChip,
+      Cmnd,     // search Scsi Nexus
+      0,        // DON'T search linked list for FC port id
+      NULL,     // DON'T search linked list for FC WWN
+      NULL);    // DON'T care about end of list
+ 
+    if( pLoggedInPort == NULL )      // not found!
+    {
+//    printk(" @Q bad targ cmnd %p@ ", Cmnd);
+      QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+    }
+
+    else  // we know what FC device to send to...
+    {
+
+      // does this device support FCP target functions?
+      // (determined by PRLI field)
+
+      if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+      {
+        printk(" Doesn't support TARGET functions port_id %Xh\n",
+          pLoggedInPort->port_id );
+        QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+      }
+
+    // In this case (previous login OK), the device is temporarily
+    // unavailable waiting for re-login, in which case we expect it
+    // to be back in between 25 - 500ms.  
+    // If the FC port doesn't log back in within several seconds
+    // (i.e. implicit "logout"), or we get an explicit logout,
+    // we set "device_blocked" in Scsi_Device struct; in this
+    // case 30 seconds will elapse before Linux/Scsi sends another
+    // command to the device.
+      else if( pLoggedInPort->prli != TRUE )
+      {
+//      printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n",
+//        Cmnd->channel, Cmnd->target, pLoggedInPort->port_id);
+        QueLinkDownCmnd( cpqfcHBAdata, Cmnd);
+//    Need to use "blocked" flag??   	
+//	Cmnd->device->device_blocked = TRUE; // just let it timeout
+      }
+      else  // device supports TARGET functions, and is logged in...
+      {
+      // (context of fchs is to "reply" to...)
+        fchs.s_id = pLoggedInPort->port_id; // destination FC address
+
+      // what is the data direction?  For data TO the device,
+      // we need IWE (Intiator Write Entry).  Otherwise, IRE.
+
+        if( Cmnd->cmnd[0] == WRITE_10 ||
+  	  Cmnd->cmnd[0] == WRITE_6 ||
+	  Cmnd->cmnd[0] == WRITE_BUFFER ||      
+	  Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE ||  // CPQ specific 
+	  Cmnd->cmnd[0] == MODE_SELECT )
+        {
+          SESTtype = SCSI_IWE; // data from HBA to Device
+        }
+        else
+          SESTtype = SCSI_IRE; // data from Device to HBA
+    	  
+        ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          SESTtype,     // e.g. Initiator Read Entry (IRE)
+          &fchs,        // we are originator; only use d_id
+          Cmnd,         // Linux SCSI command (with scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+        if( !ulStatus ) // Exchange setup?
+   
+        {
+          if( cpqfcHBAdata->BoardLock )
+          {
+    TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+	    printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID);
+          }
+
+	  ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+	  if( !ulStatus )
+          {
+            PCI_TRACEO( ExchangeID, 0xB8) 
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+	  }
+          else
+            // check reason for Exchange not being started - we might
+            // want to Queue and start later, or fail with error
+          {
+            printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus );
+          }
+        }            // end good BuildExchange status
+        
+        else  // SEST table probably full  -- why? hardware hang?
+        {
+	  printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus);
+        }
+      }  // end can't do FCP-SCSI target functions
+    } // end can't find target (FC device)
+
+    CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+  }
+	
+  PCI_TRACEO( (ULONG)Cmnd, 0x9C) 
+  LEAVE("cpqfcTS_queuecommand");
+  return 0;
+}    
+
+
+// Entry point for upper Scsi layer intiated abort.  Typically
+// this is called if the command (for hard disk) fails to complete
+// in 30 seconds.  This driver intends to complete all disk commands
+// within Exchange ".timeOut" seconds (now 7) with target status, or
+// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes
+// immediate retry.
+// If any disk commands get the _abort call, except for the case that
+// the physical device was removed or unavailable due to hardware
+// errors, it should be considered a driver error and reported to
+// the author.
+
+int cpqfcTS_abort(Scsi_Cmnd *Cmnd)
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  // get the pointer to our Scsi layer HBA buffer  
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  int i;
+  ENTER("cpqfcTS_abort");
+
+  Cmnd->result = DID_ABORT <<16;  // assume we'll find it
+
+  printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd);
+  // See if we can find a Cmnd pointer that matches...
+  // The most likely case is we accepted the command
+  // from Linux Scsi (e.g. ceated a SEST entry) and it
+  // got lost somehow.  If we can't find any reference
+  // to the passed pointer, we can only presume it
+  // got completed as far as our driver is concerned.
+  // If we found it, we will try to abort it through
+  // common mechanism.  If FC ABTS is successful (ACC)
+  // or is rejected (RJT) by target, we will call
+  // Scsi "done" quickly.  Otherwise, the ABTS will timeout
+  // and we'll call "done" later.
+
+  // Search the SEST exchanges for a matching Cmnd ptr.
+  for( i=0; i< TACH_SEST_LEN; i++)
+  {
+    if( Exchanges->fcExchange[i].Cmnd == Cmnd )
+    {
+      
+      // found it!
+      printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type);
+
+      Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default
+      Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later)
+
+      // Since we need to immediately return the aborted Cmnd to Scsi 
+      // upper layers, we can't make future reference to any of it's 
+      // fields (e.g the Nexus).
+
+      cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i);
+
+      break;
+    }
+  }
+
+  if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST?
+  {
+    // now search our non-SEST buffers (i.e. Cmnd waiting to
+    // start on the HBA or waiting to complete with error for retry).
+    
+    // first check BadTargetCmnd
+    for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+    { 
+      if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd )
+      {
+        cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+	printk("in BadTargetCmnd Q\n");
+	goto Done; // exit
+      }
+    }
+
+    // if not found above...
+
+    for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++)
+    {
+      if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd ) 
+      {
+	cpqfcHBAdata->LinkDnCmnd[i] = NULL;
+	printk("in LinkDnCmnd Q\n");
+	goto Done;
+      }
+    }
+
+
+    for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+    {    // find spare slot
+      if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd )
+      {
+        cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+	printk("in BoardLockCmnd Q\n");
+	goto Done;
+      }
+    }
+    
+    Cmnd->result = DID_ERROR <<16;  // Hmmm...
+    printk("Not found! ");
+//    panic("_abort");
+  }
+  
+Done:
+  
+//    panic("_abort");
+  LEAVE("cpqfcTS_abort");
+  return 0;  // (see scsi.h)
+}    
+
+
+
+
+// To be done...	
+int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
+{
+  int return_status = SUCCESS;
+
+  ENTER("cpqfcTS_reset");
+
+
+            
+
+  LEAVE("cpqfcTS_reset");
+  return return_status;
+}    
+
+
+
+/* This function determines the bios parameters for a given
+   harddisk. These tend to be numbers that are made up by the
+   host adapter.  Parameters:
+   size, device number, list (heads, sectors,cylinders).
+   (from hosts.h)
+*/
+
+int cpqfcTS_biosparam(Disk *disk, kdev_t n, int ip[])
+{
+  int size = disk->capacity;
+  
+  ENTER("cpqfcTS_biosparam");
+  ip[0] = 64;
+  ip[1] = 32;
+  ip[2] = size >> 11;
+  
+  if( ip[2] > 1024 )
+  {
+    ip[0] = 255;
+    ip[1] = 63;
+    ip[2] = size / (ip[0] * ip[1]);
+  }
+
+  LEAVE("cpqfcTS_biosparam");
+  return 0;
+}    
+
+
+
+void cpqfcTS_intr_handler( int irq, 
+		void *dev_id, 
+		struct pt_regs *regs)
+{
+
+  unsigned long flags, InfLoopBrk=0;
+  struct Scsi_Host *HostAdapter = dev_id;
+  CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+  int MoreMessages = 1; // assume we have something to do
+  UCHAR IntPending;
+  
+  ENTER("intr_handler");
+
+  spin_lock_irqsave( &io_request_lock, flags);
+  // is this our INT?
+  IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address);
+
+  // broken boards can generate messages forever, so
+  // prevent the infinite loop
+#define INFINITE_IMQ_BREAK 10000
+  if( IntPending )
+  {
+    
+    // mask our HBA interrupts until we handle it...
+    writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+    if( IntPending & 0x4) // "INT" - Tach wrote to IMQ
+    {
+      while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) ) 
+      {
+        MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done
+      }
+      if( InfLoopBrk >= INFINITE_IMQ_BREAK )
+      {
+        printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n");
+        printk("or investigate alternate causes (e.g. physical FC layer)\n");
+      }
+
+      else  // working normally - re-enable INTs and continue
+        writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address);
+    
+    }  // (...ProcessIMQEntry() clears INT by writing IMQ consumer)
+    else  // indications of errors or problems...
+          // these usually indicate critical system hardware problems.
+    {
+      if( IntPending & 0x10 )
+  	printk(" cpqfcTS adapter external memory parity error detected\n");
+      if( IntPending & 0x8 )
+  	printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n");
+      if( IntPending & 0x2 )
+	printk(" cpqfcTS adapter DMA error detected\n");
+      if( IntPending & 0x1 )
+  	printk(" cpqfcTS adapter PCI error detected\n");
+    }      
+  }
+  spin_unlock_irqrestore( &io_request_lock, flags);
+  LEAVE("intr_handler");
+}
+
+
+
+
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[])
+{
+        // Verify GBIC type (if any) and correct Tachyon Port State Machine
+        // (GBIC) module definition is:
+        // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0.  The input states appear
+        // to be inverted -- i.e., a setting of 111 is read when there is NO
+        // GBIC present.  The Module Def (MD) spec says 000 is "no GBIC"
+        // Hard code the bit states to detect Copper, 
+        // Long wave (single mode), Short wave (multi-mode), and absent GBIC
+
+  ULONG ulBuff;
+
+  sprintf( cErrorString, "\nGBIC detected: ");
+
+  ulBuff = fcChip->Registers.TYstatus.value & 0x13; 
+  switch( ulBuff )
+  {
+  case 0x13:  // GPIO4, GPIO1, GPIO0 = 111; no GBIC!
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "NONE! ");
+    return FALSE;          
+          
+       
+  case 0x11:   // Copper GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "Copper. ");
+    break;
+
+  case 0x10:   // Long-wave (single mode) GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+        "Long-wave. ");
+    break;
+  case 0x1:    // Short-wave (multi mode) GBIC detected
+    sprintf( &cErrorString[ strlen( cErrorString)],
+        "Short-wave. ");
+    break;
+  default:     // unknown GBIC - presumably it will work (?)
+    sprintf( &cErrorString[ strlen( cErrorString)],
+            "Unknown. ");
+          
+    break;
+  }  // end switch GBIC detection
+
+  return TRUE;
+}
+
+
+
+
+
+
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[])
+{
+  // Tachyon's Frame Manager LPSM in LinkDown state?
+  // (For non-loop port, check PSM instead.)
+  // return string with state and FALSE is Link Down
+
+  int LinkUp;
+
+  if( fcChip->Registers.FMstatus.value & 0x80 ) 
+    LinkUp = FALSE;
+  else
+    LinkUp = TRUE;
+
+  sprintf( &cErrorString[ strlen( cErrorString)],
+    " LPSM %Xh ", 
+     (fcChip->Registers.FMstatus.value >>4) & 0xf );
+
+
+  switch( fcChip->Registers.FMstatus.value & 0xF0)
+  {
+                    // bits set in LPSM
+    case 0x10:
+      sprintf( &cErrorString[ strlen( cErrorString)], "ARB");
+      break;
+    case 0x20:
+      sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon");
+      break;
+    case 0x30:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OPEN");
+      break;
+    case 0x40:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OPENed");
+      break;
+    case 0x50:
+      sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS");
+      break;
+    case 0x60:
+      sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS");
+      break;
+    case 0x70:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Xfer");
+      break;
+    case 0x80:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Init");
+      break;
+    case 0x90:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin");
+      break;
+    case 0xa0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol");
+      break;
+    case 0xb0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd");
+      break;
+    case 0xc0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "HostControl");
+      break;
+    case 0xd0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail");
+      break;
+    case 0xe0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Offline");
+      break;
+    case 0xf0:
+      sprintf( &cErrorString[ strlen( cErrorString)], "OldPort");
+      break;
+    case 0:
+    default:
+      sprintf( &cErrorString[ strlen( cErrorString)], "Monitor");
+      break;
+
+  }
+
+  return LinkUp;
+}
+
+
+
+
+#include "linux/malloc.h"
+
+// Dynamic memory allocation alignment routines
+// HP's Tachyon Fibre Channel Controller chips require
+// certain memory queues and register pointers to be aligned
+// on various boundaries, usually the size of the Queue in question.
+// Alignment might be on 2, 4, 8, ... or even 512 byte boundaries.
+// Since most O/Ss don't allow this (usually only Cache aligned -
+// 32-byte boundary), these routines provide generic alignment (after
+// O/S allocation) at any boundary, and store the original allocated
+// pointer for deletion (O/S free function).  Typically, we expect
+// these functions to only be called at HBA initialization and
+// removal time (load and unload times)
+// ALGORITHM notes:
+// Memory allocation varies by compiler and platform.  In the worst case,
+// we are only assured BYTE allignment, but in the best case, we can
+// request allocation on any desired boundary.  Our strategy: pad the
+// allocation request size (i.e. waste memory) so that we are assured
+// of passing desired boundary near beginning of contiguous space, then
+// mask out lower address bits.
+// We define the following algorithm:
+//   allocBoundary - compiler/platform specific address alignment
+//                   in number of bytes (default is single byte; i.e. 1)
+//   n_alloc       - number of bytes application wants @ aligned address
+//   ab            - alignment boundary, in bytes (e.g. 4, 32, ...)
+//   t_alloc       - total allocation needed to ensure desired boundary
+//   mask          - to clear least significant address bits for boundary
+//   Compute:
+//   t_alloc = n_alloc + (ab - allocBoundary)
+//   allocate t_alloc bytes @ alloc_address
+//   mask =  NOT (ab - 1)
+//       (e.g. if ab=32  _0001 1111  -> _1110 0000
+//   aligned_address = alloc_address & mask
+//   set n_alloc bytes to 0
+//   return aligned_address (NULL if failed)
+//
+// If u32_AlignedAddress is non-zero, then search for BaseAddress (stored
+// from previous allocation).  If found, invoke call to FREE the memory.
+// Return NULL if BaseAddress not found
+
+// we need about 8 allocations per HBA.  Figuring at most 10 HBAs per server
+// size the dynamic_mem array at 80.
+
+void* fcMemManager( ALIGNED_MEM *dynamic_mem, ULONG n_alloc, ULONG ab,
+                   ULONG u32_AlignedAddress)
+{
+  USHORT allocBoundary=1;   // compiler specific - worst case 1
+                                  // best case - replace malloc() call
+                                  // with function that allocates exactly
+                                  // at desired boundary
+
+  unsigned long ulAddress;
+  ULONG t_alloc, i;
+  void *alloc_address = 0;  // def. error code / address not found
+  LONG mask;                // must be 32-bits wide!
+
+  ENTER("fcMemManager");
+  if( u32_AlignedAddress )          // are we freeing existing memory?
+  {
+//    printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress);
+    for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for the base address
+    {
+//    printk("dynamic_mem[%u].AlignedAddress %lX\n", i, dynamic_mem[i].AlignedAddress);
+      if( dynamic_mem[i].AlignedAddress == u32_AlignedAddress )
+      {
+        alloc_address = dynamic_mem[i].BaseAllocated; // 'success' status
+        kfree( dynamic_mem[i].BaseAllocated);  // return pages to kernel
+        dynamic_mem[i].BaseAllocated = 0;   // clear for next use
+        dynamic_mem[i].AlignedAddress = 0;
+        break;                        // quit for loop; done
+      }
+    }
+  }
+  else if( n_alloc )                   // want new memory?
+  {
+    t_alloc = n_alloc + (ab - allocBoundary); // pad bytes for alignment
+//    printk("kmalloc() for Tach alignment: %ld bytes\n", t_alloc);
+
+    alloc_address =                  // total bytes (NumberOfBytes)
+      kmalloc( t_alloc, GFP_KERNEL); // allow thread block to free pages 
+
+
+                                  // now mask off least sig. bits of address
+    if( alloc_address )           // (only if non-NULL)
+    {
+                                  // find place to store ptr, so we
+                                  // can free it later...
+      for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for free slot
+      {
+        if( dynamic_mem[i].BaseAllocated == 0) // take 1st available
+        {
+          dynamic_mem[i].BaseAllocated = alloc_address;// address from O/S
+          break;
+        }
+      }
+      mask = (LONG)(ab - 1);            // mask all low-order bits
+      mask = ~mask;                            // invert bits
+
+      ulAddress = (unsigned long)alloc_address;
+      
+      ulAddress += (ab - allocBoundary);    // add the alignment bytes-
+                                            // then truncate address...
+      alloc_address = (void*)(ulAddress & mask);
+      
+      dynamic_mem[i].AlignedAddress = 
+	(ULONG)(ulAddress & mask); // 32bit Tach address
+      memset( alloc_address, 0, n_alloc );  // clear new memory
+    }
+    else  // O/S dynamic mem alloc failed!
+      alloc_address = 0;  // (for debugging breakpt)
+
+  }
+
+  LEAVE("fcMemManager");
+  return alloc_address;  // good (or NULL) address
+}
+
+
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = CPQFCTS;
+
+#include "scsi_module.c"
+
+
+#endif
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSioctl.h linux.18p17/drivers/scsi/cpqfcTSioctl.h
--- linux.vanilla/drivers/scsi/cpqfcTSioctl.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSioctl.h	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,84 @@
+// for user apps, make sure data size types are defined
+// with 
+
+
+#define CCPQFCTS_IOC_MAGIC 'Z'
+
+typedef struct 
+{
+  __u8 bus;
+  __u8 dev_fn;
+  __u32 board_id;
+} cpqfc_pci_info_struct;
+
+typedef __u32 DriverVer_type;
+/*
+typedef union
+{
+  struct  // Peripheral Unit Device
+  { 
+    __u8 Bus:6;
+    __u8 Mode:2;  // b00
+    __u8 Dev;
+  } PeripDev;
+  struct  // Volume Set Address
+  { 
+    __u8 DevMSB:6;
+    __u8 Mode:2;  // b01
+    __u8 DevLSB;
+  } LogDev;
+  struct  // Logical Unit Device (SCSI-3, SCC-2 defined)
+  { 
+    __u8 Targ:6;
+    __u8 Mode:2;  // b10
+    __u8 Dev:5;
+    __u8 Bus:3;
+
+  } LogUnit;
+} SCSI3Addr_struct;
+
+
+typedef struct
+{
+  SCSI3Addr_struct FCP_Nexus;
+  __u8 cdb[16];
+} PassThru_Command_struct;
+*/
+
+/* this is nearly duplicated in idashare.h */
+typedef struct {
+  int	lc;    		/* Controller number */
+  int	node;		/* Node (box) number */
+  int	ld;		/* Logical Drive on this box, if required */
+  __u32 nexus;          /* SCSI Nexus */
+  void	*argp;		/* Argument pointer */
+} VENDOR_IOCTL_REQ;
+
+
+typedef struct {
+  char	cdb[16];	/* SCSI CDB for the pass-through */
+  ushort bus;		/* Target bus on the box */
+  ushort pdrive;	/* Physical drive on the box */
+  int len;              /* Length of the data area of the CDB */
+  int sense_len;	/* Length of the sense data */
+  char sense_data[40];  /* Sense data */
+  void *bufp;		/* Data area for the CDB */
+  char rw_flag;		/* Read CDB or Write CDB */
+} cpqfc_passthru_t;
+
+
+
+
+/*
+** Defines for the IOCTLS.
+*/
+
+#define VENDOR_READ_OPCODE			0x26
+#define VENDOR_WRITE_OPCODE			0x27
+
+#define CPQFCTS_GETPCIINFO _IOR( CCPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct)
+#define CPQFCTS_GETDRIVVER _IOR( CCPQFCTS_IOC_MAGIC, 9, DriverVer_type)
+
+#define CPQFCTS_SCSI_PASSTHRU _IOWR( CCPQFCTS_IOC_MAGIC,11, VENDOR_IOCTL_REQ)
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSstructs.h linux.18p17/drivers/scsi/cpqfcTSstructs.h
--- linux.vanilla/drivers/scsi/cpqfcTSstructs.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSstructs.h	Sun Oct  1 16:01:12 2000
@@ -0,0 +1,1497 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *  
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSSTRUCTS_H
+#define CPQFCTSSTRUCTS_H
+
+#include <linux/timer.h>  // timer declaration in our host data
+#include <linux/tqueue.h> // task queue sched
+#include <asm/atomic.h>
+#include "cpqfcTSioctl.h"
+
+#define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \
+                         for( wait_time=jiffies + (secs*HZ); \
+		         wait_time > jiffies ;) ; }
+#define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
+#define VER_MAJOR 1
+#define VER_MINOR 3
+#define VER_SUBMINOR 4
+
+// Macros for kernel (esp. SMP) tracing using a PCI analyzer
+// (e.g. x86).
+//#define PCI_KERNEL_TRACE
+#ifdef PCI_KERNEL_TRACE
+#define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x);
+#define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y));
+#else
+
+#define PCI_TRACE(x) 
+#define PCI_TRACEO(x,y)
+#endif
+
+			 
+//#define DEBUG_CMND 1   // debug output for Linux Scsi CDBs
+//#define DUMMYCMND_DBG 1
+
+//#define DEBUG_CPQFCTS 1
+//#undef DEBUG_CPQFCTS 
+#ifdef DEBUG_CPQFCTS
+#define ENTER(x)	printk("cpqfcts : entering %s()\n", x);
+#define LEAVE(x)	printk("cpqfcts : leaving %s()\n", x);
+#define DEBUG(x)	x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif				/* DEBUG_CPQFCTS */
+
+//#define DEBUG_CPQFCTS_PCI 1
+//#undef DEBUG_CPQFCTS_PCI
+#if DEBUG_CPQFCTS_PCI
+#define DEBUG_PCI(x)	x
+#else
+#define DEBUG_PCI(x)
+#endif				/* DEBUG_CPQFCTS_PCI */
+
+#define STACHLITE66_TS12  "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2"
+#define STACHLITE66_TS13  "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3"
+#define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??"
+#define SAGILENT_XL2_21   "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1"
+
+// PDA is Peripheral Device Address, VSA is Volume Set Addressing
+// Linux SCSI parameters
+#define CPQFCTS_MAX_TARGET_ID 64
+#define CPQFCTS_MAX_LUN 8    // The RA-4x00 supports 32 (Linux SCSI supports 8)
+#define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA
+
+#define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0 
+#define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN
+
+#ifndef LinuxVersionCode
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#endif
+#ifndef DECLARE_MUTEX_LOCKED
+#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED
+#endif
+
+#define DEV_NAME "cpqfcTS"
+
+#define CPQ_DEVICE_ID     0xA0FC
+#define AGILENT_XL2_ID    0x1029
+
+typedef struct
+{
+  __u16 vendor_id;
+  __u16 device_id;
+} SupportedPCIcards;
+			 
+// nn:nn denotes bit field
+                            // TachyonHeader struct def.
+                            // the fields shared with ODB
+                            // need to have same value
+
+// Queues for TachLite not in original Tachyon
+// ERQ       - Exchange Request Queue (for outbound commands)
+// SFQ       - Single Frame Queue (for incoming frames)
+
+                            // Define Tachyon Outbound Command Que
+                            // (Since many Tachyon registers are Read
+                            // only, maintain copies for debugging)
+                            // most Tach ques need power-of-2 sizes,
+                            // where registers are loaded with po2 -1
+#define TACH_SEST_LEN 512   // TachLite SEST
+
+#define ELS_EXCHANGES 64    // e.g. PLOGI, RSCN, ...
+// define the total number of outstanding (simultaneous) exchanges
+#define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES)  // ELS exchanges
+
+#define ERQ_LEN 128         // power of 2, max 4096
+
+
+#ifndef HOSTS_C
+
+#ifndef BYTE
+//typedef UCHAR BYTE;
+typedef __u8 BYTE;
+#endif
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef LONG
+typedef __s32 LONG;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+#ifndef PVOID
+typedef void * PVOID;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+
+
+// macro for FC-PH reject codes
+// payload format for LS_RJT (FC payloads are big endian):
+//     byte  0         1         2         3  (MSB)
+// DWORD 0   01        00        00        00
+// DWORD 1   resvd     code      expl.     vendor
+
+#define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16))
+
+
+#define TachLiteSTATUS 0x12
+
+// Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software
+// 32-bit ERROR word defines
+#define INVALID_ARGS 0x1
+#define LNKDWN_OSLS  0x2
+#define LNKDWN_LASER 0x4
+#define OUTQUE_FULL  0x8
+#define DRIVERQ_FULL 0x10
+#define SEST_FULL    0x20
+#define BAD_ALPA     0x40
+#define OVERFLOW     0x80  // inbound CM
+#define COUNT_ERROR     0x100  // inbound CM
+#define LINKFAIL_RX     0x200  // inbound CM
+#define ABORTSEQ_NOTIFY 0x400  // outbound CM
+#define LINKFAIL_TX     0x800  // outbound CM
+#define HOSTPROG_ERR     0x1000  // outbound CM
+#define FRAME_TO         0x2000  // outbound CM
+#define INV_ENTRY        0x4000  // outbound CM
+#define SESTPROG_ERR     0x8000  // outbound CM
+#define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM
+#define INITIATOR_ABORT  0x20000L // initiator exchange timeout or O/S ABORT
+#define MEMPOOL_FAIL     0x40000L // O/S memory pool allocation failed
+#define FC2_TIMEOUT      0x80000L // driver timeout for lost frames
+#define TARGET_ABORT     0x100000L // ABTS received from FC port
+#define EXCHANGE_QUEUED  0x200000L // e.g. Link State was LDn on fcStart
+#define PORTID_CHANGED   0x400000L // fc Port address changed
+#define DEVICE_REMOVED   0x800000L // fc Port address changed
+// Several error scenarios result in SEST Exchange frames 
+// unexpectedly arriving in the SFQ
+#define SFQ_FRAME        0x1000000L // SFQ frames from open Exchange
+
+// Maximum number of Host Bus Adapters (HBA) / controllers supported
+// only important for mem allocation dimensions - increase as necessary
+
+#define MAX_ADAPTERS 8
+#define MAX_RX_PAYLOAD 1024  // hardware dependent max frame payload
+// Tach header struc defines
+#define SOFi3 0x7
+#define SOFf  0x8
+#define SOFn3 0xB
+#define EOFn  0x5
+#define EOFt  0x6
+
+// FCP R_CTL defines
+#define FCP_CMND 0x6
+#define FCP_XFER_RDY 0x5
+#define FCP_RSP 0x7
+#define FCP_RESPONSE 0x777 // (arbitrary #)
+#define NEED_FCP_RSP 0x77  // (arbitrary #)
+#define FCP_DATA 0x1
+
+#define RESET_TACH 0x100 // Reset Tachyon/TachLite
+#define SCSI_IWE 0x2000  // initiator write entry (for SEST)
+#define SCSI_IRE 0x3000  // initiator read entry (for SEST)
+#define SCSI_TRE 0x400  // target read entry (for SEST)
+#define SCSI_TWE 0x500  // target write entry (for SEST)
+#define TOGGLE_LASER 0x800
+#define LIP 0x900
+#define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports
+#define FMINIT 0x707     // (arbitrary) for Frame Manager Init command
+
+// BLS == Basic Link Service
+// ELS == Extended Link Service
+#define BLS_NOP 4
+#define BLS_ABTS 0x10   // FC-PH Basic Link Service Abort Sequence
+#define BLS_ABTS_ACC 0x100  // FC-PH Basic Link Service Abort Sequence Accept
+#define BLS_ABTS_RJT 0x101  // FC-PH Basic Link Service Abort Sequence Reject
+#define ELS_PLOGI 0x03  // FC-PH Port Login (arbitrary assign)
+#define ELS_SCR   0x70  // (arb assign) State Change Registration (Fabric)
+#define FCS_NSR   0x72  // (arb assign) Name Service Request (Fabric)
+#define ELS_FLOGI 0x44  // (arb assign) Fabric Login
+#define ELS_FDISC 0x41  // (arb assign) Fabric Discovery (Login)
+#define ELS_PDISC 0x50  // FC-PH2 Port Discovery
+#define ELS_ABTX  0x06  // FC-PH Abort Exchange 
+#define ELS_LOGO 0x05   // FC-PH Port Logout
+#define ELS_PRLI 0x20   // FCP-SCSI Process Login
+#define ELS_PRLO 0x21   // FCP-SCSI Process Logout
+#define ELS_LOGO_ACC 0x07   // {FC-PH} Port Logout Accept
+#define ELS_PLOGI_ACC 0x08  // {FC-PH} Port Login Accept
+#define ELS_ACC 0x18        // {FC-PH} (generic) ACCept
+#define ELS_PRLI_ACC 0x22  // {FCP-SCSI} Process Login Accept
+#define ELS_RJT 0x1000000
+#define SCSI_REPORT_LUNS 0x0A0
+#define REPORT_LUNS 0xA0 // SCSI-3 command op-code
+
+#define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame
+
+#define SFQ_UNASSISTED_FCP  1  // ICM, DWord3, "Type" unassisted FCP
+#define SFQ_UNKNOWN         0x31 // (arbitrary) ICM, DWord3, "Type" unknown
+
+// these "LINK" bits refer to loop or non-loop
+#define LINKACTIVE 0x2    // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set
+#define LINKDOWN 0xf2     // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set
+
+//#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1
+
+typedef struct      // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST)
+{
+  ULONG reserved;   // dword 0 (don't use)
+  ULONG sof_eof;
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;         // dword 7 - relative offset
+} TachFCHDR;
+
+                    // NOTE!! the following struct MUST be 64 bytes.
+typedef struct      // 32 bytes hdr + 32 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use - must clear to 0)
+  ULONG sof_eof;    // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[8];              // dwords 8-15 frame data payload
+} TachFCHDR_CMND;
+
+
+typedef struct      // 32 bytes hdr + 120 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use - must clear to 0)
+  ULONG sof_eof;    // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[30];              // largest necessary payload (for LOGIN cmnds)
+} TachFCHDR_GCMND;
+
+typedef struct      // 32 bytes hdr + 64 bytes payload
+{
+  ULONG reserved;   // dword 0 (don't use)
+  ULONG sof_eof;
+  ULONG d_id;       // dword 2 - 31:24 R_CTL, 23:0 D_ID
+  ULONG s_id;       // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+  ULONG f_ctl;      // dword 4 - 31:24 Type,  23:0 F_CTL
+  ULONG seq_cnt;    // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+  ULONG ox_rx_id;   // dword 6 - 31:16 OX_ID,  15:0 RX_ID
+  ULONG ro;  // dword 7 - relative offset
+//---------
+  __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes
+} TachFCHDR_RSP;
+
+
+
+
+
+
+// Inbound Message Queue structures...
+typedef struct              // each entry 8 words (32 bytes)
+{
+  ULONG type;               // IMQ completion message types
+  ULONG word[7];            // remainder of structure
+                            // interpreted by IMQ type
+} TachyonIMQE;
+
+
+
+// Inbound Message Queue structures...
+#define IMQ_LEN 512              // minimum 4 entries [(power of 2) - 1]
+typedef struct                   // 8 words - 32 bytes
+{
+  TachyonIMQE QEntry[IMQ_LEN];
+  ULONG producerIndex;   // IMQ Producer Index register
+                                 // @32 byte align
+  ULONG consumerIndex;   // Consumer Index register (in Tachyon)
+  ULONG length;          // Length register
+  ULONG base;
+} TachyonIMQ;                    // @ 32 * IMQ_LEN align
+
+
+
+typedef struct           // inbound completion message
+{
+  ULONG Type;
+  ULONG Index;
+  ULONG TransferLength;
+} TachyonInbCM;
+
+
+
+// arbitrary numeric tags for TL structures
+#define TL_FCHS 1  // TachLite Fibre Channel Header Structure
+#define TL_IWE 2  // initiator write entry (for SEST)
+#define TL_TWE 3  // target write entry (for SEST)
+#define TL_IRE 4  // initiator read entry (for SEST)
+#define TL_TRE 5  // target read entry (for SEST)
+#define TL_IRB 6  // I/O request block
+
+                                // for INCOMING frames
+#define SFQ_LEN 32              // minimum 32 entries, max 4096
+
+typedef struct                  // Single Frame Que
+{
+  TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!!
+  ULONG producerIndex;   // IMQ Producer Index register
+                                 // @32 byte align
+  ULONG consumerIndex;   // Consumer Index register (in Tachyon)
+  ULONG length;          // Length register
+  ULONG base;
+} TachLiteSFQ;
+
+
+typedef struct                 // I/O Request Block flags
+{
+  UCHAR  BRD : 1;
+  UCHAR      : 1; // reserved
+  UCHAR  SFA : 1;
+  UCHAR  DNC : 1;
+  UCHAR  DIN : 1;
+  UCHAR  DCM : 1;
+  UCHAR  CTS : 1;
+  UCHAR  SBV : 1;  // IRB entry valid - IRB'B' only
+} IRBflags;
+
+typedef struct                  // I/O Request Block
+{                          // Request 'A'
+  ULONG Req_A_SFS_Len;     // total frame len (hdr + payload), min 32
+  ULONG Req_A_SFS_Addr;    // 32-bit pointer to FCHS struct (to be sent)
+  ULONG Req_A_SFS_D_ID;    // 24-bit FC destination (i.e. 8 bit al_pa)
+  ULONG Req_A_Trans_ID;    // X_ID (OX_ID or RX_ID) and/or Index in SEST
+                           // Request 'B'
+  ULONG Req_B_SFS_Len;     // total frame len (hdr + payload), min 32
+  ULONG Req_B_SFS_Addr;    // 32-bit pointer to FCHS struct (to be sent)
+  ULONG Req_B_SFS_D_ID;    // 24-bit FC destination (i.e. 8 bit al_pa)
+  ULONG Req_B_Trans_ID;    // X_ID (OX_ID or RX_ID) and/or Index in SEST
+} TachLiteIRB;
+
+
+typedef struct           // TachLite placeholder for IRBs
+{                        // aligned @sizeof(ERQ) for TachLite
+                         // MAX commands is sum of SEST len and ERQ
+                         // we know that each SEST entry requires an
+                         // IRB (ERQ) entry; in addition, we provide
+                         // ERQ_LEN
+  TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea.
+  ULONG consumerIndex;   // Consumer Index register
+  ULONG producerIndex;   // ERQ Producer Index register
+  ULONG length;          // Length register
+  ULONG base;            // copy of base ptr for debug
+                         // struct is sized for largest expected cmnd (LOGIN)
+} TachLiteERQ;
+
+
+#define TL_MAX_SGPAGES 4  // arbitrary limit to # of TL Ext. S/G pages
+                          // stores array of allocated page blocks used
+                          // in extended S/G lists.  Affects amount of static
+                          // memory consumed by driver.
+#define TL_EXT_SG_PAGE_COUNT 256  // Number of Extended Scatter/Gather a/l PAIRS
+                                  // Tachyon register (IOBaseU 0x68)
+                                  // power-of-2 value ONLY!  4 min, 256 max
+
+                          // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG
+#define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4)
+
+
+
+// SEST entry types: IWE, IRE, TWE, TRE
+typedef struct 
+{
+  ULONG Hdr_Len;
+  ULONG Hdr_Addr;
+  ULONG RSP_Len;
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Link;
+  ULONG RX_ID;
+  ULONG Data_Len;
+  ULONG Exp_RO;
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Gather Len/Address pairs
+  ULONG GLen1;
+  ULONG GAddr1;
+  ULONG GLen2;
+  ULONG GAddr2;
+  ULONG GLen3;
+  ULONG GAddr3;
+} TachLiteIWE;
+
+
+typedef struct 
+{
+  ULONG Seq_Accum;
+  ULONG reserved;       // must clear to 0
+  ULONG RSP_Len;
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Buff_Index;           // ULONG 5
+  ULONG Exp_RO;
+  ULONG Byte_Count;
+  ULONG reserved_;      // ULONG 8
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Scatter Len/Address pairs
+  ULONG SLen1;
+  ULONG SAddr1;
+  ULONG SLen2;
+  ULONG SAddr2;
+  ULONG SLen3;
+  ULONG SAddr3;
+} TachLiteIRE;
+
+
+typedef struct          // Target Write Entry
+{
+  ULONG Seq_Accum;      // dword 0
+  ULONG reserved;       // dword 1  must clear to 0
+  ULONG Remote_Node_ID;
+  ULONG reserved1;      // dword 3  must clear to 0
+  ULONG Buff_Off;
+  ULONG Buff_Index;     // ULONG 5
+  ULONG Exp_RO;
+  ULONG Byte_Count;
+  ULONG reserved_;      // ULONG 8
+  ULONG Exp_Byte_Cnt;
+   // --- extended/local Scatter Len/Address pairs
+  ULONG SLen1;
+  ULONG SAddr1;
+  ULONG SLen2;
+  ULONG SAddr2;
+  ULONG SLen3;
+  ULONG SAddr3;
+} TachLiteTWE;
+
+typedef struct      
+{
+  ULONG Hdr_Len;
+  ULONG Hdr_Addr;
+  ULONG RSP_Len;        // DWord 2
+  ULONG RSP_Addr;
+  ULONG Buff_Off;
+  ULONG Buff_Index;     // DWord 5
+  ULONG reserved;
+  ULONG Data_Len;
+  ULONG reserved_;
+  ULONG reserved__;
+   // --- extended/local Gather Len/Address pairs
+  ULONG GLen1;          // DWord A
+  ULONG GAddr1;
+  ULONG GLen2;
+  ULONG GAddr2;
+  ULONG GLen3;
+  ULONG GAddr3;
+} TachLiteTRE;
+
+typedef struct
+{
+  void *PoolPage[TL_MAX_SGPAGES];
+} SGPAGES, *PSGPAGES; // linked list of S/G pairs, by Exchange
+
+
+
+typedef struct                  // SCSI Exchange State Table
+{
+  union                         // Entry can be IWE, IRE, TWE, TRE
+  {                             // 64 bytes per entry
+    TachLiteIWE IWE;
+    TachLiteIRE IRE;
+    TachLiteTWE TWE;
+    TachLiteTRE TRE;
+  } u[TACH_SEST_LEN];
+
+  TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl)
+  TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame
+  SGPAGES sgPages[TACH_SEST_LEN]; // array of Pool-allocations
+  ULONG length;          // Length register
+  ULONG base;            // copy of base ptr for debug
+} TachSEST;
+
+
+
+typedef struct                  // each register has it's own address
+                                // and value (used for write-only regs)
+{
+  void* address;
+  volatile ULONG value;
+} FCREGISTER;
+
+typedef struct         // Host copy - TachLite Registers
+{
+  ULONG IOBaseL, IOBaseU;  // I/O port lower and upper TL register addresses
+  ULONG MemBase;           // memory mapped register addresses
+  void* ReMapMemBase;      // O/S VM reference for MemBase
+  ULONG wwn_hi;            // WWN is set once at startup
+  ULONG wwn_lo;
+  ULONG my_al_pa;          // al_pa received after LIP()
+  ULONG ROMCTR;            // flags for on-board RAM/ROM
+  ULONG RAMBase;           // on-board RAM (i.e. some Tachlites)
+  ULONG SROMBase;          // on-board EEPROM (some Tachlites)
+  ULONG PCIMCTR;           // PCI Master Control Reg (has bus width)
+
+  FCREGISTER INTEN;        // copy of interrupt enable mask
+  FCREGISTER INTPEND;      // interrupt pending
+  FCREGISTER INTSTAT;      // interrupt status
+  FCREGISTER SFQconsumerIndex; 
+  FCREGISTER ERQproducerIndex; 
+  FCREGISTER TYconfig;   // TachYon (chip level)
+  FCREGISTER TYcontrol;
+  FCREGISTER TYstatus;
+  FCREGISTER FMconfig;   // Frame Manager (FC loop level)
+  FCREGISTER FMcontrol;
+  FCREGISTER FMstatus;
+  FCREGISTER FMLinkStatus1;
+  FCREGISTER FMLinkStatus2;
+  FCREGISTER FMBB_CreditZero;
+  FCREGISTER status;
+  FCREGISTER ed_tov;     // error detect time-out value
+  FCREGISTER rcv_al_pa;  // received arb. loop physical address
+  FCREGISTER primitive;  // e.g. LIP(), OPN(), ...
+} TL_REGISTERS;
+
+
+
+typedef struct 
+{
+  ULONG ok;
+  ULONG invalidArgs;
+  ULONG linkDown;
+  ULONG linkUp;
+  ULONG outQueFull;
+  ULONG SESTFull;
+  ULONG hpe;    // host programming err (from Tach)
+  ULONG FC4aborted; // aborts from Application or upper driver layer
+  ULONG FC2aborted; // aborts from our driver's timeouts
+  ULONG timeouts;   // our driver timeout (on individual exchanges)
+  ULONG logouts;    // explicit - sent LOGO; implicit - device removed
+  ULONG retries;
+  ULONG linkFailTX;
+  ULONG linkFailRX;
+  ULONG CntErrors;  // byte count expected != count received (typ. SEST)
+  ULONG e_stores;   // elastic store errs
+  ULONG resets;     // hard or soft controller resets
+  ULONG FMinits;    // TACH Frame Manager Init (e.g. LIPs)
+  ULONG lnkQueFull;  // too many LOGIN, loop commands
+  ULONG ScsiQueFull; // too many FCP-SCSI inbound frames
+  ULONG LossofSignal;   // FM link status 1 regs
+  ULONG BadRXChar;   // FM link status 1 regs
+  ULONG LossofSync;   // FM link status 1 regs
+  ULONG Rx_EOFa;   // FM link status 2 regs (received EOFa)
+  ULONG Dis_Frm;   // FM link status 2 regs (discarded frames)
+  ULONG Bad_CRC;   // FM link status 2 regs
+  ULONG BB0_Timer; //  FM BB_Credit Zero Timer Reg
+  ULONG loopBreaks; // infinite loop exits
+  ULONG lastBB0timer;  // static accum. buffer needed by Tachlite
+} FCSTATS;
+
+
+typedef struct               // Config Options
+{                            // LS Bit first
+  USHORT        : 1;           // bit0:
+  USHORT  flogi : 1;           // bit1: We sent FLOGI - wait for Fabric logins
+  USHORT  fabric: 1;           // bit2: Tachyon detected Fabric (FM stat LG)
+  USHORT  LILPin: 1;           // bit3: We can use an FC-AL LILP frame
+  USHORT  target: 1;           // bit4: this Port has SCSI target capability
+  USHORT  initiator:    1;     // bit5: this Port has SCSI initiator capability
+  USHORT  extLoopback:  1;     // bit6: loopback at GBIC
+  USHORT  intLoopback:  1;     // bit7: loopback in HP silicon
+  USHORT        : 1;           // bit8:
+  USHORT        : 1;           // bit9:
+  USHORT        : 1;           // bit10:
+  USHORT        : 1;           // bit11:
+  USHORT        : 1;           // bit12:
+  USHORT        : 1;           // bit13:
+  USHORT        : 1;           // bit14:
+  USHORT        : 1;           // bit15:
+} FC_OPTIONS;
+
+
+
+typedef struct dyn_mem_pair
+{
+  void *BaseAllocated;  // address as allocated from O/S;
+  unsigned long AlignedAddress; // aligned address (used by Tachyon DMA)
+} ALIGNED_MEM;
+
+
+
+
+// these structs contain only CRUCIAL (stuff we actually use) parameters
+// from FC-PH(n) logins.  (Don't save entire LOGIN payload to save mem.)
+
+// Implicit logout happens when the loop goes down - we require PDISC
+// to restore.  Explicit logout is when WE decide never to talk to someone,
+// or when a target refuses to talk to us, i.e. sends us a LOGO frame or
+// LS_RJT reject in response to our PLOGI request.
+
+#define IMPLICIT_LOGOUT 1
+#define EXPLICIT_LOGOUT 2
+
+typedef struct 
+{
+  UCHAR channel; // SCSI "bus"
+  UCHAR target;
+  UCHAR InqDeviceType;  // byte 0 from SCSI Inquiry response
+  UCHAR VolumeSetAddressing;  // FCP-SCSI LUN coding (40h for VSA)
+  UCHAR LunMasking;     // True if selective presentation supported
+  UCHAR lun[CPQFCTS_MAX_LUN];
+} SCSI_NEXUS;
+
+
+typedef struct        
+{
+  union 
+  {
+    UCHAR ucWWN[8];  // a FC 64-bit World Wide Name/ PortID of target
+                     // addressing of single target on single loop...
+    u64 liWWN;
+  } u;
+
+  ULONG port_id;     // a FC 24-bit address of port (lower 8 bits = al_pa)
+
+  Scsi_Cmnd ScsiCmnd;   // command buffer for Report Luns
+#define REPORT_LUNS_PL 256  
+  UCHAR ReportLunsPayload[REPORT_LUNS_PL];
+  
+  SCSI_NEXUS ScsiNexus; // LUNs per FC device
+
+  ULONG LOGO_counter; // might try several times before logging out for good
+  ULONG LOGO_timer;   // after LIP, ports expecting PDISC must time-out and
+                      // LOGOut if successful PDISC not completed in 2 secs
+
+  ULONG concurrent_seq;  // must be 1 or greater
+  ULONG rx_data_size;    // e.g. 128, 256, 1024, 2048 per FC-PH spec
+  ULONG BB_credit;
+  ULONG EE_credit;
+
+  ULONG fcp_info;        // from PRLI (i.e. INITIATOR/ TARGET flags)
+                         // flags for login process
+  BOOLEAN Originator;    // Login sequence Originated (if false, we
+                         // responded to another port's login sequence)
+  BOOLEAN plogi;         // PLOGI frame ACCepted (originated or responded)
+  BOOLEAN pdisc;         // PDISC frame was ORIGINATED (self-login logic)
+  BOOLEAN prli;          // PRLI frame ACCepted (originated or responded)
+  BOOLEAN flogi;         // FLOGI frame ACCepted (originated or responded)
+  BOOLEAN logo;          // port permanently logged out (invalid login param)
+  BOOLEAN flogiReq;      // Fabric login required (set in LIP process)
+  UCHAR highest_ver;
+  UCHAR lowest_ver;
+
+  
+  // when the "target" (actually FC Port) is waiting for login
+  // (e.g. after Link reset), set the device_blocked bit;
+  // after Port completes login, un-block target.
+  UCHAR device_blocked; // see Scsi_Device struct
+
+                    // define singly-linked list of logged-in ports
+                    // once a port_id is identified, it is remembered,
+                    // even if the port is removed indefinitely
+  PVOID pNextPort;  // actually, type PFC_LOGGEDIN_PORT; void for Compiler
+
+} FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT;
+
+
+
+// This serves as the ESB (Exchange Status Block),
+// and has timeout counter; used for ABORTs
+typedef struct                
+{                                  // FC-1 X_IDs
+  ULONG type;            // ELS_PLOGI, SCSI_IWE, ... (0 if free)
+  PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange
+  Scsi_Cmnd *Cmnd;       // Linux SCSI command packet includes S/G list
+  ULONG timeOut;         // units of ??, DEC by driver, Abort when 0
+  ULONG reTries;         // need one or more retries?
+  ULONG status;          // flags indicating errors (0 if none)
+  TachLiteIRB IRB;       // I/O Request Block, gets copied to ERQ
+  TachFCHDR_GCMND fchs;  // location of IRB's Req_A_SFS_Addr
+} FC_EXCHANGE, *PFC_EXCHANGE;
+
+// Unfortunately, Linux limits our kmalloc() allocations to 128k.
+// Because of this and the fact that our ScsiRegister allocation
+// is also constrained, we move this large structure out for
+// allocation after Scsi Register.
+// (In other words, this cumbersome indirection is necessary
+// because of kernel memory allocation constraints!)
+
+typedef struct // we will allocate this dynamically
+{
+  FC_EXCHANGE fcExchange[ TACH_MAX_XID ];
+} FC_EXCHANGES;
+
+
+
+
+
+
+
+
+
+
+
+typedef struct
+{
+  char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus")
+  //PVOID  pAdapterDevExt; // back pointer to device object/extension
+  ULONG ChipType;        // local numeric key for Tachyon Type / Rev.
+  ULONG status;              // our Driver - logical status
+  
+  TL_REGISTERS Registers;    // reg addresses & host memory copies
+                             // FC-4 mapping of 'transaction' to X_IDs
+  UCHAR LILPmap[32*4];       // Loop Position Map of ALPAs (late FC-AL only)
+  FC_OPTIONS Options;        // e.g. Target, Initiator, loopback...
+  UCHAR highest_FCPH_ver;    // FC-PH version limits
+  UCHAR lowest_FCPH_ver;     // FC-PH version limits
+
+  FC_EXCHANGES *Exchanges;  
+  ULONG fcLsExchangeLRU;       // Least Recently Used counter (Link Service)
+  ULONG fcSestExchangeLRU;       // Least Recently Used counter (FCP-SCSI)
+  FC_LOGGEDIN_PORT fcPorts;  // linked list of every FC port ever seen
+  FCSTATS fcStats;           // FC comm err counters
+
+                             // Host memory QUEUE pointers
+  TachLiteERQ *ERQ;          // Exchange Request Que 
+  TachyonIMQ *IMQ;           // Inbound Message Que 
+  TachLiteSFQ *SFQ;          // Single Frame Queue
+  TachSEST *SEST;            // SCSI Exchange State Table
+
+  // these function pointers are for "generic" functions, which are
+  // replaced with Host Bus Adapter types at
+  // runtime.
+  int (*CreateTachyonQues)( void* , int);
+  int (*DestroyTachyonQues)( void* , int);
+  int (*LaserControl)(void*, int );   // e.g. On/Off
+  int (*ResetTachyon)(void*, int );
+  void (*FreezeTachyon)(void*, int );
+  void (*UnFreezeTachyon)(void*, int );
+  int (*InitializeTachyon)(void*, int, int );
+  int (*InitializeFrameManager)(void*, int );
+  int (*ProcessIMQEntry)(void*);
+  int (*ReadWriteWWN)(void*, int ReadWrite);
+  int (*ReadWriteNVRAM)(void*, void*, int ReadWrite);
+
+} TACHYON, *PTACHYON;
+
+
+void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip);
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode);
+int CpqTsDestroyTachLiteQues( void* , int);
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2);
+
+int CpqTsProcessIMQEntry(void* pHBA);
+int CpqTsResetTachLite(void *pHBA, int type);
+void CpqTsFreezeTachlite(void *pHBA, int type);
+void CpqTsUnFreezeTachlite(void *pHBA, int type);
+int CpqTsInitializeFrameManager(void *pHBA, int);
+int CpqTsLaserControl( void* addrBase, int opcode );
+int CpqTsReadWriteWWN(void*, int ReadWrite);
+int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite);
+
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter);
+void cpqfcTSWorkerThread( void *host);
+
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf );
+ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+	UCHAR *buf );
+
+BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout,
+  USHORT startOffset,  // e.g. 0x2f for WWN start
+  USHORT count,
+  UCHAR *buf );
+
+
+// define misc functions 
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]);
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]);
+void* fcMemManager( ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab,
+                   ULONG ulAlignedAddress);
+
+void BigEndianSwap(  UCHAR *source, UCHAR *dest,  USHORT cnt);
+
+//ULONG virt_to_phys( PVOID virtaddr );
+                  
+
+// Linux interrupt handler
+void cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs);
+void cpqfcTSheartbeat( unsigned long ptr );
+
+
+
+// The biggest Q element we deal with is Aborts - we
+// need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes)
+//#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4)
+#define LINKQ_ITEM_SIZE (3*16)
+typedef struct
+{
+  ULONG Type;              // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ...
+  ULONG ulBuff[ LINKQ_ITEM_SIZE ];
+} LINKQ_ITEM;
+
+#define FC_LINKQ_DEPTH TACH_MAX_XID
+typedef struct
+{
+  ULONG producer;
+  ULONG consumer;  // when producer equals consumer, Q empty
+
+  LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ];
+
+} FC_LINK_QUE, *PFC_LINK_QUE;
+
+
+     // DPC routines post to here on Inbound SCSI frames
+     // User thread processes
+#define FC_SCSIQ_DEPTH 32
+
+typedef struct
+{
+  int Type;              // e.g. SCSI
+  ULONG ulBuff[ 3*16 ];
+} SCSIQ_ITEM;
+
+typedef struct
+{
+  ULONG producer;
+  ULONG consumer;  // when producer equals consumer, Q empty
+
+  SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ];
+
+} FC_SCSI_QUE, *PFC_SCSI_QUE;
+
+
+
+
+
+#define DYNAMIC_ALLOCATIONS 4  // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
+
+// Linux space allocated per HBA (chip state, etc.)
+typedef struct 
+{
+  struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct
+
+  TACHYON fcChip;    // All Tachyon registers, Queues, functions
+  ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS];
+
+  struct pci_dev *PciDev;
+
+  Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn
+                                                // (for Acceptable targets)
+  Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full
+  
+  Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets
+
+  u_char HBAnum;     // 0-based host number
+
+
+  struct timer_list cpqfcTStimer; // FC utility timer for implicit
+                                  // logouts, FC protocol timeouts, etc.
+  int fcStatsTime;  // Statistics delta reporting time
+
+  struct task_struct *worker_thread; // our kernel thread
+  int PortDiscDone;    // set by SendLogins(), cleared by LDn
+  
+  struct semaphore *TachFrozen;
+  struct semaphore *TYOBcomplete;    // handshake for Tach outbound frames
+  struct semaphore *fcQueReady;      // FibreChannel work for our kernel thread
+  struct semaphore *notify_wt;       // synchronizes kernel thread kill
+  struct semaphore *BoardLock;
+  
+  PFC_LINK_QUE fcLQ;             // the WorkerThread operates on this
+
+  spinlock_t hba_spinlock;           // held/released by WorkerThread
+
+} CPQFCHBA;
+
+#define	CPQ_SPINLOCK_HBA( x )   spin_lock(&x->hba_spinlock);
+#define CPQ_SPINUNLOCK_HBA(x)   spin_unlock(&x->hba_spinlock);
+
+
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pFcPort);
+
+
+void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int );
+
+PFC_LOGGEDIN_PORT fcPortLoggedIn( 
+   CPQFCHBA *cpqfcHBAdata, 
+   TachFCHDR_GCMND* fchs, 
+   BOOLEAN, 
+   BOOLEAN);
+void fcProcessLoggedIn( 
+   CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs);
+
+
+ULONG cpqfcTSBuildExchange( 
+  CPQFCHBA *cpqfcHBAdata,
+  ULONG type, // e.g. PLOGI
+  TachFCHDR_GCMND* InFCHS,  // incoming FCHS
+  void *Data,               // the CDB, scatter/gather, etc.  
+  LONG *ExchangeID );       // allocated exchange ID
+
+ULONG cpqfcTSStartExchange( 
+  CPQFCHBA *cpqfcHBAdata,
+  LONG ExchangeID );
+
+void cpqfcTSCompleteExchange( 
+       PTACHYON fcChip, 
+       ULONG exchange_ID);
+
+
+PFC_LOGGEDIN_PORT  fcFindLoggedInPort( 
+  PTACHYON fcChip, 
+  Scsi_Cmnd *Cmnd,  // (We want the channel/target/lun Nexus from Cmnd)
+  ULONG port_id,  // search linked list for al_pa, or
+  UCHAR wwn[8],    // search linked list for WWN, or...
+  PFC_LOGGEDIN_PORT *pLastLoggedInPort
+);
+
+// don't do this unless you have the right hardware!
+#define TRIGGERABLE_HBA 1
+#ifdef TRIGGERABLE_HBA
+void TriggerHBA( void*, int);
+#endif
+
+void cpqfcTSPutLinkQue( 
+  CPQFCHBA *cpqfcHBAdata, 
+  int Type, 
+  void *QueContent);
+
+void fcPutScsiQue( 
+  CPQFCHBA *cpqfcHBAdata, 
+  int Type, 
+  void *QueContent);
+
+void fcLinkQReset(
+   CPQFCHBA *);
+void fcScsiQReset(
+   CPQFCHBA *);
+void fcSestReset(
+   CPQFCHBA *);
+
+
+
+
+
+extern const UCHAR valid_al_pa[];
+extern const int number_of_al_pa;
+
+#define FCP_RESID_UNDER   0x80000
+#define FCP_RESID_OVER    0x40000
+#define FCP_SNS_LEN_VALID 0x20000
+#define FCP_RSP_LEN_VALID 0x10000
+
+// RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34)
+#define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000
+#define FCP_CMND_FIELD_INVALID     0x2000000
+#define FCP_DATA_RO_NOT_XRDY_RO    0x3000000
+#define FCP_TASKFUNCTION_NS        0x4000000
+#define FCP_TASKFUNCTION_FAIL      0x5000000
+
+// FCP-SCSI response status struct
+typedef struct  // see "TachFCHDR_RSP" definition - 64 bytes
+{
+  __u32 reserved;
+  __u32 reserved1;
+  __u32 fcp_status;    // field validity and SCSI status
+  __u32 fcp_resid;
+  __u32 fcp_sns_len;   // length of FCP_SNS_INFO field
+  __u32 fcp_rsp_len;   // length of FCP_RSP_INFO field (expect 8)
+  __u32 fcp_rsp_info;  // 4 bytes of FCP protocol response information
+  __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8)
+  __u8  fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ)
+
+} FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE;
+
+
+// Fabric State Change Registration
+typedef struct scrpl
+{
+  __u32 command;
+  __u32 function;
+} SCR_PL;
+
+// Fabric Name Service Request
+typedef struct nsrpl
+{
+  __u32 CT_Rev;  // (& IN_ID)   WORD 0
+  __u32 FCS_Type;            // WORD 1
+  __u32 Command_code;        // WORD 2
+  __u32 reason_code;         // WORD 3
+  __u32 FCP;                 // WORD 4 (lower byte)
+  
+} NSR_PL;
+
+
+
+// "FC.H"
+#define MAX_RX_SIZE		0x800	// Max Receive Buffer Size is 2048
+#define MIN_RX_SIZE		0x100	// Min Size is 256, per FC-PLDA Spec
+#define	MAX_TARGET_RXIDS	SEST_DEPTH
+#define TARGET_RX_SIZE		SEST_BUFFER_LENGTH
+
+#define CLASS_1			0x01
+#define CLASS_2			0x02
+#define CLASS_3			0x03
+
+#define FC_PH42			0x08
+#define FC_PH43			0x09
+#define FC_PH3			0x20
+
+#define RR_TOV			2	// Minimum Time for target to wait for
+					// PDISC after a LIP.
+#define E_D_TOV			2	// Minimum Time to wait for Sequence
+					// Completion.
+#define R_A_TOV			0	// Minimum Time for Target to wait 
+					// before reclaiming resources.
+//
+//	R_CTL Field
+//
+//	Routing Bits (31-28)
+//
+#define FC4_DEVICE_DATA		0x00000000
+#define EXT_LINK_DATA		0x20000000
+#define FC4_LINK_DATA		0x30000000
+#define VIDEO_DATA		0x40000000
+#define BASIC_LINK_DATA		0x80000000
+#define LINK_CONTROL		0xC0000000
+#define ROUTING_MASK		0xF0000000
+
+//
+//	Information Bits (27-24)
+//
+#define UNCAT_INFORMATION	0x00000000
+#define SOLICITED_DATA		0x01000000
+#define UNSOLICITED_CONTROL	0x02000000
+#define SOLICITED_CONTROL	0x03000000
+#define UNSOLICITED_DATA	0x04000000
+#define DATA_DESCRIPTOR		0x05000000
+#define UNSOLICITED_COMMAND	0x06000000
+#define COMMAND_STATUS		0x07000000
+#define INFO_MASK		0x0F000000
+//
+//	(Link Control Codes)
+//
+#define ACK_1			0x00000000
+#define ACK_0_OR_N		0x01000000
+#define P_RJT			0x02000000 
+#define F_RJT			0x03000000 
+#define P_BSY			0x04000000
+#define FABRIC_BUSY_TO_DF	0x05000000	// Fabric Busy to Data Frame
+#define FABRIC_BUSY_TO_LC	0x06000000	// Fabric Busy to Link Ctl Frame
+#define LINK_CREDIT_RESET	0x07000000
+//
+//	(Link Service Command Codes)
+//
+//#define LS_RJT			0x01000000	// LS Reject
+
+#define LS_ACC			0x02000000	// LS Accept
+#define LS_PLOGI		0x03000000	// N_PORT Login
+#define LS_FLOGI		0x04000000	// F_PORT Login
+#define LS_LOGO			0x05000000	// Logout
+#define LS_ABTX			0x06000000	// Abort Exchange
+#define LS_RCS			0x07000000	// Read Connection Status
+#define LS_RES			0x08000000	// Read Exchange Status
+#define LS_RSS			0x09000000	// Read Sequence Status
+#define LS_RSI			0x0A000000	// Request Seq Initiative
+#define LS_ESTS			0x0B000000	// Establish Steaming
+#define LS_ESTC			0x0C000000	// Estimate Credit
+#define LS_ADVC			0x0D000000	// Advice Credit
+#define LS_RTV			0x0E000000	// Read Timeout Value
+#define LS_RLS			0x0F000000	// Read Link Status
+#define LS_ECHO			0x10000000	// Echo
+#define LS_TEST			0x11000000	// Test
+#define LS_RRQ			0x12000000	// Reinstate Rec. Qual.
+#define LS_PRLI			0x20000000	// Process Login
+#define LS_PRLO			0x21000000	// Process Logout
+#define LS_TPRLO		0x24000000	// 3rd Party Process Logout
+#define LS_PDISC		0x50000000	// Process Discovery
+#define LS_FDISC		0x51000000	// Fabric Discovery
+#define LS_ADISC		0x52000000	// Discover Address
+#define LS_RNC			0x53000000	// Report Node Capability
+#define LS_SCR                  0x62000000      // State Change Registration
+#define LS_MASK			0xFF000000	
+
+//
+// 	TYPE Bit Masks
+//
+#define BASIC_LINK_SERVICE	0x00000000
+#define EXT_LINK_SERVICE	0x01000000
+
+#define LLC			0x04000000
+#define LLC_SNAP		0x05000000
+#define SCSI_FCP		0x08000000
+#define SCSI_GPP		0x09000000
+#define IPI3_MASTER		0x11000000
+#define IPI3_SLAVE		0x12000000
+#define IPI3_PEER		0x13000000
+#define CP_IPI3_MASTER		0x15000000
+#define CP_IPI3_SLAVE		0x16000000
+#define CP_IPI3_PEER		0x17000000
+#define SBCCS_CHANNEL		0x19000000
+#define SBCCS_CONTROL		0x1A000000
+#define FIBRE_SERVICES		0x20000000
+#define FC_FG			0x21000000
+#define FC_XS			0x22000000
+#define FC_AL			0x23000000
+#define SNMP			0x24000000
+#define HIPPI_FP		0x40000000
+#define TYPE_MASK		0xFF000000
+
+typedef struct {
+	UCHAR seq_id_valid;
+	UCHAR seq_id;
+	USHORT reserved;  // 2 bytes reserved
+	ULONG ox_rx_id;
+	USHORT low_seq_cnt;
+	USHORT high_seq_cnt;
+} BA_ACC_PAYLOAD;
+
+typedef struct {
+	UCHAR reserved;
+	UCHAR reason_code;
+	UCHAR reason_explain;
+	UCHAR vendor_unique;
+} BA_RJT_PAYLOAD;
+
+
+typedef struct {
+	ULONG 	command_code;
+	ULONG 	sid;
+	USHORT	ox_id;
+	USHORT	rx_id;
+} RRQ_MESSAGE;
+
+typedef struct {
+	ULONG command_code;
+	UCHAR vendor;
+	UCHAR explain;
+	UCHAR reason;
+	UCHAR reserved;
+} REJECT_MESSAGE;
+
+
+#define	N_OR_F_PORT		0x1000
+#define RANDOM_RELATIVE_OFFSET	0x4000
+#define CONTINUOSLY_INCREASING	0x8000
+
+#define CLASS_VALID		0x8000
+#define INTERMIX_MODE		0x4000
+#define TRANSPARENT_STACKED	0x2000
+#define LOCKDOWN_STACKED	0x1000
+#define SEQ_DELIVERY		0x800
+
+#define XID_NOT_SUPPORTED	0x00
+#define XID_SUPPORTED		0x4000
+#define XID_REQUIRED		0xC000
+
+#define ASSOCIATOR_NOT_SUPPORTED	0x00
+#define ASSOCIATOR_SUPPORTED	0x1000
+#define ASSOCIATOR_REQUIRED	0x3000
+
+#define	INIT_ACK0_SUPPORT	0x800
+#define INIT_ACKN_SUPPORT	0x400
+
+#define	RECIP_ACK0_SUPPORT	0x8000
+#define RECIP_ACKN_SUPPORT	0x4000
+
+#define X_ID_INTERLOCK		0x2000
+
+#define ERROR_POLICY		0x1800		// Error Policy Supported
+#define ERROR_DISCARD		0x00		// Only Discard Supported
+#define ERROR_DISC_PROCESS	0x02		// Discard and process supported
+
+#define NODE_ID			0x01
+#define IEEE_EXT		0x20
+
+//
+// Categories Supported Per Sequence
+//
+#define	CATEGORIES_PER_SEQUENCE	0x300
+#define ONE_CATEGORY_SEQUENCE	0x00		// 1 Category per Sequence
+#define TWO_CATEGORY_SEQUENCE	0x01		// 2 Categories per Sequence
+#define MANY_CATEGORY_SEQUENCE	0x03		// > 2 Categories/Sequence
+
+typedef struct {
+
+	USHORT initiator_control;
+	USHORT service_options;
+
+	USHORT rx_data_size;
+	USHORT recipient_control;
+
+	USHORT ee_credit;
+	USHORT concurrent_sequences;
+
+	USHORT reserved;
+	USHORT open_sequences;
+
+} CLASS_PARAMETERS;
+
+typedef struct {
+	ULONG	login_cmd;
+	//
+	// Common Service Parameters
+	//
+	struct {
+
+		USHORT bb_credit;
+		UCHAR lowest_ver;
+		UCHAR highest_ver;
+
+		USHORT bb_rx_size;
+		USHORT common_features;
+
+		USHORT rel_offset;
+		USHORT concurrent_seq;
+
+
+		ULONG e_d_tov;
+	} cmn_services;
+
+	//
+	// Port Name
+	//
+	UCHAR port_name[8];
+
+	//
+	// Node/Fabric Name
+	//
+	UCHAR node_name[8];
+
+	//
+	// Class 1, 2 and 3 Service Parameters
+	//
+	CLASS_PARAMETERS	class1;
+	CLASS_PARAMETERS	class2;
+	CLASS_PARAMETERS	class3;
+
+	ULONG reserved[4];
+
+	//
+	// Vendor Version Level
+	//
+	UCHAR		vendor_id[2];
+	UCHAR		vendor_version[6];
+	ULONG		buffer_size;
+	USHORT		rxid_start;
+	USHORT		total_rxids;
+} LOGIN_PAYLOAD;
+
+
+typedef struct
+{
+  ULONG cmd;  // 4 bytes
+  UCHAR n_port_identifier[3];
+  UCHAR reserved;
+  UCHAR port_name[8];
+} LOGOUT_PAYLOAD;
+
+
+//
+//	PRLI Request Service Parameter Defines
+//
+#define PRLI_ACC			0x01
+#define PRLI_REQ			0x02
+#define ORIG_PROCESS_ASSOC_VALID	0x8000
+#define RESP_PROCESS_ASSOC_VALID	0x4000
+#define ESTABLISH_PAIR			0x2000
+#define DATA_OVERLAY_ALLOWED		0x40
+#define	INITIATOR_FUNCTION		0x20
+#define	TARGET_FUNCTION			0x10
+#define CMD_DATA_MIXED			0x08
+#define DATA_RESP_MIXED			0x04
+#define READ_XFER_RDY			0x02
+#define WRITE_XFER_RDY			0x01
+
+#define RESPONSE_CODE_MASK	0xF00
+#define REQUEST_EXECUTED	0x100
+#define NO_RESOURCES		0x200
+#define INIT_NOT_COMPLETE	0x300
+#define IMAGE_DOES_NOT_EXIST	0x400
+#define BAD_PREDEFINED_COND	0x500
+#define REQ_EXEC_COND		0x600
+#define NO_MULTI_PAGE		0x700
+
+typedef struct {
+	USHORT	payload_length;
+	UCHAR	page_length;
+	UCHAR	cmd;
+
+
+	ULONG	valid;
+
+	ULONG	orig_process_associator;
+
+	ULONG	resp_process_associator;
+	
+	ULONG	fcp_info;
+} PRLI_REQUEST;
+
+typedef struct {
+
+	USHORT	payload_length;
+	UCHAR	page_length;
+	UCHAR	cmd;
+
+	ULONG	valid;
+	ULONG	orig_process_associator;
+
+	ULONG	resp_process_associator;
+	ULONG	reserved;
+} PRLO_REQUEST;
+
+typedef struct {
+	ULONG	cmd;
+
+	ULONG	hard_address;
+	
+	UCHAR	port_name[8];
+
+	UCHAR	node_name[8];
+
+	ULONG	s_id;
+} ADISC_PAYLOAD;
+
+// J. McCarty's LINK.H
+//
+//	LS_RJT Reason Codes
+//
+
+#define INVALID_COMMAND_CODE	0x01
+#define LOGICAL_ERROR		0x03
+#define LOGICAL_BUSY		0x05
+#define PROTOCOL_ERROR		0x07
+#define UNABLE_TO_PERFORM	0x09
+#define COMMAND_NOT_SUPPORTED	0x0B
+#define LS_VENDOR_UNIQUE	0xFF
+
+//
+// 	LS_RJT Reason Codes Explanations
+//
+#define NO_REASON		0x00
+#define OPTIONS_ERROR		0x01
+#define INITIATOR_CTL_ERROR	0x03
+#define RECIPIENT_CTL_ERROR	0x05
+#define DATA_FIELD_SIZE_ERROR	0x07
+#define CONCURRENT_SEQ_ERROR	0x09
+#define CREDIT_ERROR		0x0B
+#define INVALID_PORT_NAME	0x0D
+#define INVALID_NODE_NAME	0x0E
+#define INVALID_CSP		0x0F	// Invalid Service Parameters
+#define INVALID_ASSOC_HDR	0x11	// Invalid Association Header
+#define ASSOC_HDR_REQUIRED	0x13	// Association Header Required
+#define LS_INVALID_S_ID		0x15
+#define INVALID_OX_RX_ID	0x17	// Invalid OX_ID RX_ID Combination
+#define CMD_IN_PROCESS		0x19
+#define INVALID_IDENTIFIER	0x1F	// Invalid N_PORT Identifier
+#define INVALID_SEQ_ID		0x21
+#define ABT_INVALID_XCHNG	0x23 	// Attempt to Abort an invalid Exchange
+#define ABT_INACTIVE_XCHNG	0x25 	// Attempt to Abort an inactive Exchange
+#define NEED_REC_QUAL		0x27	// Recovery Qualifier required
+#define NO_LOGIN_RESOURCES	0x29	// No resources to support login
+#define NO_DATA			0x2A	// Unable to supply requested data
+#define	REQUEST_NOT_SUPPORTED	0x2C	// Request Not Supported
+
+//
+//	Link Control Codes
+//
+
+//
+//	P_BSY Action Codes
+//
+#define SEQUENCE_TERMINATED	0x01000000
+#define SEQUENCE_ACTIVE		0x02000000
+
+//
+//	P_BSY Reason Codes
+//
+#define PHYS_NPORT_BUSY		0x010000
+#define NPORT_RESOURCE_BUSY	0x020000
+
+//
+// 	P_RJT, F_RJT Action Codes
+//
+
+#define RETRYABLE_ERROR		0x01000000
+#define NON_RETRYABLE_ERROR	0x02000000
+
+//
+// 	P_RJT, F_RJT Reason Codes
+//
+#define INVALID_D_ID		0x010000
+#define INVALID_S_ID		0x020000
+#define NPORT_NOT_AVAIL_TMP	0x030000
+#define NPORT_NOT_AVAIL_PERM	0x040000
+#define CLASS_NOT_SUPPORTED	0x050000
+#define USAGE_ERROR		0x060000
+#define TYPE_NOT_SUPPORTED	0x070000
+#define INVAL_LINK_CONTROL	0x080000
+#define INVAL_R_CTL		0x090000
+#define INVAL_F_CTL		0x0A0000
+#define INVAL_OX_ID		0x0B0000
+#define INVAL_RX_ID		0x0C0000
+#define INVAL_SEQ_ID		0x0D0000
+#define INVAL_DF_CTL		0x0E0000
+#define INVAL_SEQ_CNT		0x0F0000
+#define INVAL_PARAMS		0x100000
+#define EXCHANGE_ERROR		0x110000
+#define LS_PROTOCOL_ERROR	0x120000
+#define INCORRECT_LENGTH	0x130000
+#define UNEXPECTED_ACK		0x140000
+#define LOGIN_REQ		0x160000
+#define EXCESSIVE_SEQ		0x170000
+#define NO_EXCHANGE		0x180000
+#define SEC_HDR_NOT_SUPPORTED	0x190000
+#define NO_FABRIC		0x1A0000
+#define P_VENDOR_UNIQUE		0xFF0000
+
+//
+// 	BA_RJT Reason Codes
+//
+#define BA_INVALID_COMMAND	0x00010000
+#define BA_LOGICAL_ERROR	0x00030000
+#define BA_LOGICAL_BUSY		0x00050000
+#define BA_PROTOCOL_ERROR	0x00070000
+#define BA_UNABLE_TO_PERFORM	0x00090000
+
+//
+// 	BA_RJT Reason Explanation Codes
+//
+#define BA_NO_REASON		0x00000000
+#define BA_INVALID_OX_RX	0x00000300
+#define BA_SEQUENCE_ABORTED	0x00000500
+
+
+#endif
+#endif /* CPQFCTSSTRUCTS_H	*/
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTStrigger.c linux.18p17/drivers/scsi/cpqfcTStrigger.c
--- linux.vanilla/drivers/scsi/cpqfcTStrigger.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTStrigger.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,30 @@
+// Routine to trigger Finisar GTA analyzer. Runs of GPIO2
+// NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation
+// since it writes directly to the Tachyon board.  This function
+// developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB)
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+
+void TriggerHBA( void* IOBaseUpper, int Print)
+{
+  __u32 long value;
+
+  // get initial value in hopes of not modifying any other GPIO line
+  IOBaseUpper += 0x188;  // TachTL/TS Control reg
+  
+  value = readl( IOBaseUpper);
+  // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA)
+  // The Finisar anaylzer triggers on low-to-high TTL transition
+  value |= 0x01; // set bit 0
+
+  writel( value, IOBaseUpper);
+
+  if( Print)
+    printk( " -GPIO0 set- ");
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqfcTSworker.c linux.18p17/drivers/scsi/cpqfcTSworker.c
--- linux.vanilla/drivers/scsi/cpqfcTSworker.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqfcTSworker.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,6238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation 
+ * Fibre Channel Host Bus Adapter 
+ * 64-bit, 66MHz PCI 
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
+ *          SP# P225CXCBFIEL6T, Rev XC
+ *          SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+
+#define __KERNEL_SYSCALLS__
+
+#define SHUTDOWN_SIGS	(sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+
+
+#include "sd.h"
+#include "hosts.h"   // struct Scsi_Host definition for T handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define LOGIN_DBG 1
+
+// REMARKS:
+// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec
+// to empty an outgoing frame from its FIFO to the Fibre Channel stream,
+// we cannot do everything we need to in the interrupt handler.  Specifically,
+// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be
+// suspended until the login sequences have been completed.  Login commands
+// are frames just like SCSI commands are frames; they are subject to the same
+// timeout issues and delays.  Also, various specs provide up to 2 seconds for
+// devices to log back in (i.e. respond with ACC to a login frame), so I/O to
+// that device has to be suspended.
+// A serious problem here occurs on highly loaded FC-AL systems.  If our FC port
+// has a low priority (e.g. high arbitrated loop physical address, alpa), and
+// some other device is hogging bandwidth (permissible under FC-AL), we might
+// time out thinking the link is hung, when it's simply busy.  Many such
+// considerations complicate the design.  Although Tachyon assumes control
+// (in silicon) for many link-specific issues, the Linux driver is left with the
+// rest, which turns out to be a difficult, time critical chore.
+
+// These "worker" functions will handle things like FC Logins; all
+// processes with I/O to our device must wait for the Login to complete
+// and (if successful) I/O to resume.  In the event of a malfunctioning or  
+// very busy loop, it may take hundreds of millisecs or even seconds to complete
+// a frame send.  We don't want to hang up the entire server (and all
+// processes which don't depend on Fibre) during this wait.
+
+// The Tachyon chip can have around 30,000 I/O operations ("exchanges")
+// open at one time.  However, each exchange must be initiated 
+// synchronously (i.e. each of the 30k I/O had to be started one at a
+// time by sending a starting frame via Tachyon's outbound que).  
+
+// To accomodate kernel "module" build, this driver limits the exchanges
+// to 256, because of the contiguous physical memory limitation of 128M.
+
+// Typical FC Exchanges are opened presuming the FC frames start without errors,
+// while Exchange completion is handled in the interrupt handler.  This
+// optimizes performance for the "everything's working" case.
+// However, when we have FC related errors or hot plugging of FC ports, we pause
+// I/O and handle FC-specific tasks in the worker thread.  These FC-specific
+// functions will handle things like FC Logins and Aborts.  As the Login sequence
+// completes to each and every target, I/O can resume to that target.  
+
+// Our kernel "worker thread" must share the HBA with threads calling 
+// "queuecommand".  We define a "BoardLock" semaphore which indicates
+// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a
+// board lock Q.  When the worker thread finishes with the board, the board
+// lock Q commands are completed with status causing immediate retry.
+// Typically, the board is locked while Logins are in progress after an
+// FC Link Down condition.  When Cmnds are re-queued after board lock, the
+// particular Scsi channel/target may or may not have logged back in.  When
+// the device is waiting for login, the "prli" flag is clear, in which case
+// commands are passed to a Link Down Q.  Whenever the login finally completes,
+// the LinkDown Q is completed, again with status causing immediate retry.
+// When FC devices are logged in, we build and start FC commands to the
+// devices.
+
+// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices 
+// that never log back in (e.g. physically removed) is NOT completely
+// understood.  I've still seen instances of system hangs on failed Write 
+// commands (possibly from the ext2 layer?) on device removal.  Such special
+// cases need to be evaluated from a system/application view - e.g., how
+// exactly does the system want me to complete commands when the device is
+// physically removed??
+
+// local functions
+
+static void SetLoginFields(
+  PFC_LOGGEDIN_PORT pLoggedInPort,
+  TachFCHDR_GCMND* fchs,
+  BOOLEAN PDisc,
+  BOOLEAN Originator);
+
+static void AnalyzeIncomingFrame( 
+       CPQFCHBA *cpqfcHBAdata,
+       ULONG QNdx );
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds );
+
+static int verify_PLOGI( PTACHYON fcChip,
+      TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+
+static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type);
+static void BuildLinkServicePayload( 
+              PTACHYON fcChip, ULONG type, void* payload);
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, 
+        PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID);
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata);
+
+static void RevalidateSEST( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void IssueReportLunsCommand( 
+              CPQFCHBA* cpqfcHBAdata, 
+	      TachFCHDR_GCMND* fchs);
+
+
+// (see scsi_error.c comments on kernel task creation)
+
+void cpqfcTSWorkerThread( void *host)
+{
+  struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; 
+#ifdef PCI_KERNEL_TRACE
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+#endif
+  struct fs_struct *fs;
+  DECLARE_MUTEX_LOCKED(fcQueReady);
+  DECLARE_MUTEX_LOCKED(fcTYOBcomplete); 
+  DECLARE_MUTEX_LOCKED(TachFrozen);  
+  DECLARE_MUTEX_LOCKED(BoardLock);  
+
+  ENTER("WorkerThread");
+
+  lock_kernel();
+	/*
+	 * If we were started as result of loading a module, close all of the
+	 * user space pages.  We don't need them, and if we didn't close them
+	 * they would be locked into memory.
+	 */
+  exit_mm(current);
+
+  current->session = 1;
+  current->pgrp = 1;
+	
+  /* Become as one with the init task */
+	
+  exit_fs(current);	/* current->fs->count--; */
+  fs = init_task.fs;
+  // Some kernels compiled for SMP, while actually running
+  // on a uniproc machine, will return NULL for this call
+  if( !fs)
+  {
+    printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n ");
+  }
+ 
+  else
+  { 
+    current->fs = fs;
+    atomic_inc(&fs->count);
+  }
+
+  siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+
+
+  /*
+   * Set the name of this process.
+   */
+  sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no);
+
+  cpqfcHBAdata->fcQueReady = &fcQueReady;  // primary wait point
+  cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete;
+  cpqfcHBAdata->TachFrozen = &TachFrozen;
+    
+ 
+  cpqfcHBAdata->worker_thread = current;
+  
+  unlock_kernel();
+
+  if( cpqfcHBAdata->notify_wt != NULL )
+    up( cpqfcHBAdata->notify_wt); // OK to continue
+
+  while(1)
+  {
+    unsigned long flags;
+
+    down_interruptible( &fcQueReady);  // wait for something to do
+
+    if (signal_pending(current) )
+      break;
+    
+    PCI_TRACE( 0x90)
+    // first, take the IO lock so the SCSI upper layers can't call
+    // into our _quecommand function (this also disables INTs)
+    spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+    PCI_TRACE( 0x90)
+         
+    CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+    // next, set this pointer to indicate to the _quecommand function
+    // that the board is in use, so it should que the command and 
+    // immediately return (we don't actually require the semaphore function
+    // in this driver rev)
+
+    cpqfcHBAdata->BoardLock = &BoardLock;
+
+    PCI_TRACE( 0x90)
+
+    // release the IO lock (and re-enable interrupts)
+    spin_unlock_irqrestore( &io_request_lock, flags);
+
+    // disable OUR HBA interrupt (keep them off as much as possible
+    // during error recovery)
+    disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+    // OK, let's process the Fibre Channel Link Q and do the work
+    cpqfcTS_WorkTask( HostAdapter);
+
+    // hopefully, no more "work" to do;
+    // re-enable our INTs for "normal" completion processing
+    enable_irq( cpqfcHBAdata->HostAdapter->irq);
+ 
+
+    cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued
+    CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+
+    // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+    CompleteBoardLockCmnd( cpqfcHBAdata);
+  
+
+  }
+  // hopefully, the signal was for our module exit...
+  if( cpqfcHBAdata->notify_wt != NULL )
+    up( cpqfcHBAdata->notify_wt); // yep, we're outta here
+}
+
+
+// Freeze Tachyon routine.
+// If Tachyon is already frozen, return FALSE
+// If Tachyon is not frozen, call freeze function, return TRUE
+//
+static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  BOOLEAN FrozeTach = FALSE;
+  // It's possible that the chip is already frozen; if so,
+  // "Freezing" again will NOT! generate another Freeze
+  // Completion Message.
+
+  if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000)
+  {  // (need to freeze...)
+    fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+
+    // 2. Get Tach freeze confirmation
+    // (synchronize SEST manipulation with Freeze Completion Message)
+    // we need INTs on so semaphore can be set.	
+    enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore
+    down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem.
+    // can we TIMEOUT semaphore wait?? TBD
+    disable_irq( cpqfcHBAdata->HostAdapter->irq); 
+
+    FrozeTach = TRUE;
+  }  // (else, already frozen)
+ 
+  return FrozeTach;
+}  
+
+
+
+
+// This is the kernel worker thread task, which processes FC
+// tasks which were queued by the Interrupt handler or by
+// other WorkTask functions.
+
+#define DBG 1
+//#undef DBG
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG QconsumerNdx;
+  LONG ExchangeID;
+  ULONG ulStatus=0;
+  TachFCHDR_GCMND fchs;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+
+  ENTER("WorkTask");
+
+  // copy current index to work on
+  QconsumerNdx = fcLQ->consumer;
+
+  PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90)
+  
+
+  // NOTE: when this switch completes, we will "consume" the Que item
+//  printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type);
+  switch( fcLQ->Qitem[QconsumerNdx].Type )
+  {
+      // incoming frame - link service (ACC, UNSOL REQ, etc.)
+      // or FCP-SCSI command
+    case SFQ_UNKNOWN:  
+      AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx );
+
+      break;
+  
+    
+    
+    case EXCHANGE_QUEUED:  // an Exchange (i.e. FCP-SCSI) was previously
+                           // Queued because the link was down.  The  
+                           // heartbeat timer detected it and Queued it here.
+                           // We attempt to start it again, and if
+                           // successful we clear the EXCHANGE_Q flag.
+                           // If the link doesn't come up, the Exchange
+                           // will eventually time-out.
+
+      ExchangeID = (LONG)  // x_ID copied from DPC timeout function
+                   fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+
+      // It's possible that a Q'd exchange could have already
+      // been started by other logic (e.g. ABTS process)
+      // Don't start if already started (Q'd flag clear)
+
+      if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED )
+      {
+//        printk(" *Start Q'd x_ID %Xh: type %Xh ", 
+//          ExchangeID, Exchanges->fcExchange[ExchangeID].type);
+      
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID);
+        if( !ulStatus )
+        {
+//          printk("success* ");
+        }	
+        else
+        {
+#ifdef DBG
+      
+          if( ulStatus == EXCHANGE_QUEUED)
+            printk("Queued* ");
+          else
+            printk("failed* ");
+		
+#endif
+	} 
+      }
+      break;
+
+
+    case LINKDOWN:  
+      // (lots of things already done in INT handler) future here?
+      break;
+    
+    
+    case LINKACTIVE:   // Tachyon set the Lup bit in FM status
+                       // NOTE: some misbehaving FC ports (like Tach2.1)
+                       // can re-LIP immediately after a LIP completes.
+      
+      // if "initiator", need to verify LOGs with ports
+//      printk("\n*LNKUP* ");
+
+      if( fcChip->Options.initiator )
+        SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data
+                  // if SendLogins successfully completes, PortDiscDone
+                  // will be set.
+      
+      
+      // If SendLogins was successful, then we expect to get incoming
+      // ACCepts or REJECTs, which are handled below.
+
+      break;
+
+    // LinkService and Fabric request/reply processing
+    case ELS_FDISC:      // need to send Fabric Discovery (Login)
+    case ELS_FLOGI:      // need to send Fabric Login
+    case ELS_SCR:        // need to send State Change Registration
+    case FCS_NSR:        // need to send Name Service Request
+    case ELS_PLOGI:      // need to send PLOGI
+    case ELS_ACC:        // send generic ACCept
+    case ELS_PLOGI_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
+    case ELS_PRLI_ACC:   // need to send ELS ACCept frame to recv'd PRLI
+    case ELS_LOGO:      // need to send ELS LOGO (logout)
+    case ELS_LOGO_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
+    case ELS_RJT:         // ReJecT reply
+    case ELS_PRLI:       // need to send ELS PRLI
+ 
+    
+//      printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
+      // if PortDiscDone is not set, it means the SendLogins routine
+      // failed to complete -- assume that LDn occured, so login frames
+      // are invalid
+      if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+      {
+        printk("Discard Q'd ELS login frame\n");
+        break;  
+      }
+
+      ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI
+          (TachFCHDR_GCMND*)
+            fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+          NULL,         // no data (no scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        if( !ulStatus )
+        {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+        }
+        else
+          // check reason for Exchange not being started - we might
+          // want to Queue and start later, or fail with error
+        {
+
+        }
+      }
+
+      else   // Xchange setup failed...
+        printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+
+      break;
+
+    case SCSI_REPORT_LUNS:
+      // pass the incoming frame (actually, it's a PRLI frame)
+      // so we can send REPORT_LUNS, in order to determine VSA/PDU
+      // FCP-SCSI Lun address mode
+      IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*)
+            fcLQ->Qitem[QconsumerNdx].ulBuff); 
+
+      break;
+      
+
+
+
+    case BLS_ABTS:       // need to ABORT one or more exchanges
+    {
+      LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+      BOOLEAN FrozeTach = FALSE;   
+     
+      if( x_ID > TACH_SEST_LEN )  // (in)sanity check
+      {
+//	printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID);
+	break;
+      }
+
+
+      if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE
+      {
+//	printk(" ABTS %Xh Scsi Cmnd null! ", x_ID);
+	
+       break;  // nothing to abort!
+      }
+
+//#define ABTS_DBG
+#ifdef ABTS_DBG
+      printk("INV SEST[%X] ", x_ID); 
+      if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT)
+      {
+        printk("FC2TO");
+      }
+      if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)
+      {
+        printk("IA");
+      }
+      if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED)
+      {
+        printk("PORTID");
+      }
+      if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED)
+      {
+        printk("DEVRM");
+      }
+      if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX)
+      {
+        printk("LKF");
+      }
+      if( Exchanges->fcExchange[x_ID].status & FRAME_TO)
+      {
+        printk("FRMTO");
+      }
+      if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY)
+      {
+        printk("ABSQ");
+      }
+      if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME)
+      {
+        printk("SFQFR");
+      }
+
+      if( Exchanges->fcExchange[ x_ID].type == 0x2000)
+        printk(" WR");
+      else if( Exchanges->fcExchange[ x_ID].type == 0x3000)
+        printk(" RD");
+      else if( Exchanges->fcExchange[ x_ID].type == 0x10)
+        printk(" ABTS");
+      else
+        printk(" %Xh", Exchanges->fcExchange[ x_ID].type); 
+
+      if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT))
+      {
+	printk(" Cmd %p, ", 
+          Exchanges->fcExchange[ x_ID].Cmnd);
+
+        printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", 
+          cpqfcHBAdata->HBAnum,
+          Exchanges->fcExchange[ x_ID].Cmnd->channel,
+          Exchanges->fcExchange[ x_ID].Cmnd->target,
+          Exchanges->fcExchange[ x_ID].Cmnd->lun,
+          Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+      }
+      else  // assume that Cmnd ptr is invalid on _abort()
+      {
+	printk(" Cmd ptr invalid\n");
+      }
+     
+#endif      
+
+      
+      // Steps to ABORT a SEST exchange:
+      // 1. Freeze TL SCSI assists & ERQ (everything)
+      // 2. Receive FROZEN inbound CM (must succeed!)
+      // 3. Invalidate x_ID SEST entry 
+      // 4. Resume TL SCSI assists & ERQ (everything)
+      // 5. Build/start on exchange - change "type" to BLS_ABTS,
+      //    timeout to X sec (RA_TOV from PLDA is actually 0)
+      // 6. Set Exchange Q'd status if ABTS cannot be started,
+      //    or simply complete Exchange in "Terminate" condition
+
+  PCI_TRACEO( x_ID, 0xB4)
+      
+      // 1 & 2 . Freeze Tach & get confirmation of freeze
+      FrozeTach = FreezeTach( cpqfcHBAdata);
+
+      // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange.
+      // FC2_TIMEOUT means we are originating the abort, while
+      // TARGET_ABORT means we are ACCepting an abort.
+      // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are 
+      // all from Tachyon:
+      // Exchange was corrupted by LDn or other FC physical failure
+      // INITIATOR_ABORT means the upper layer driver/application
+      // requested the abort.
+
+
+	  
+      // clear bit 31 (VALid), to invalidate & take control from TL
+      fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+
+
+      // examine and Tach's "Linked List" for IWEs that 
+      // received (nearly) simultaneous transfer ready (XRDY) 
+      // repair linked list if necessary (TBD!)
+      // (If we ignore the "Linked List", we will time out
+      // WRITE commands where we received the FCP-SCSI XFRDY
+      // frame (because Tachyon didn't processes it).  Linked List
+      // management should be done as an optimization.
+
+//       readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST ));
+
+
+      
+
+      // 4. Resume all Tachlite functions (for other open Exchanges)
+      // as quickly as possible to allow other exchanges to other ports
+      // to resume.  Freezing Tachyon may cause cascading errors, because
+      // any received SEST frame cannot be processed by the SEST.
+      // Don't "unfreeze" unless Link is operational
+      if( FrozeTach )  // did we just freeze it (above)?
+        fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+      
+
+  PCI_TRACEO( x_ID, 0xB4)
+
+      // Note there is no confirmation that the chip is "unfrozen".  Also,
+      // if the Link is down when unfreeze is called, it has no effect.
+      // Chip will unfreeze when the Link is back up.
+
+      // 5. Now send out Abort commands if possible
+      // Some Aborts can't be "sent" (Port_id changed or gone);
+      // if the device is gone, there is no port_id to send the ABTS to.
+
+      if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED)
+			  &&
+          !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) )
+      {
+        Exchanges->fcExchange[ x_ID].type = BLS_ABTS;
+        fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id;
+        ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          BLS_ABTS,
+          &fchs,        // (uses only s_id)
+          NULL,         // (no scatter/gather list for ABTS)
+          &x_ID );// ABTS on this Exchange ID
+
+        if( !ulStatus ) // Exchange setup build OK?
+        {
+
+            // ABTS may be needed because an Exchange was corrupted
+            // by a Link disruption.  If the Link is UP, we can
+	    // presume that this ABTS can start immediately; otherwise,
+	    // set Que'd status so the Login functions
+            // can restart it when the FC physical Link is restored
+          if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init?
+          {			    
+//                printk(" *set Q status x_ID %Xh on LDn* ", x_ID);
+                Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED;
+          }
+
+          else  // what FC device (port_id) does the Cmd belong to?
+          {
+            PFC_LOGGEDIN_PORT pLoggedInPort = 
+              Exchanges->fcExchange[ x_ID].pLoggedInPort;
+            
+            // if Port is logged in, we might start the abort.
+	
+            if( (pLoggedInPort != NULL) 
+			      &&
+                (pLoggedInPort->prli == TRUE) ) 
+            {
+              // it's possible that an Exchange has already been Queued
+              // to start after Login completes.  Check and don't
+	      // start it (again) here if Q'd status set
+//	    printk(" ABTS xchg %Xh ", x_ID);            
+ 	      if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED)
+	      {
+//		    printk("already Q'd ");
+	      }
+	      else
+	      {
+//	            printk("starting ");
+		
+                fcChip->fcStats.FC2aborted++; 
+                ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+                if( !ulStatus )
+                {
+                    // OK
+                    // submitted to Tach's Outbound Que (ERQ PI incremented)
+                }
+                else
+                {
+/*                   printk("ABTS exchange start failed -status %Xh, x_ID %Xh ",
+                        ulStatus, x_ID);
+*/
+                } 
+	      }
+	    }
+	    else
+	    {
+/*         	  printk(" ABTS NOT starting xchg %Xh, %p ",
+			       x_ID, pLoggedInPort);
+	          if( pLoggedInPort )
+	            printk("prli %d ", pLoggedInPort->prli);
+*/
+	    }		
+ 	  }
+        }
+        else  // what the #@!
+        {  // how do we fail to build an Exchange for ABTS??
+              printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n",
+                ulStatus, x_ID);
+        }
+      }
+      else   // abort without ABTS -- just complete exchange/Cmnd to Linux
+      {
+//            printk(" *Terminating x_ID %Xh on %Xh* ", 
+//		    x_ID, Exchanges->fcExchange[x_ID].status);
+        cpqfcTSCompleteExchange( fcChip, x_ID);
+
+
+      }
+    } // end of ABTS case
+      break;
+
+
+
+    case BLS_ABTS_ACC:   // need to ACCept one ABTS
+                         // (NOTE! this code not updated for Linux yet..)
+      
+
+      printk(" *ABTS_ACC* ");
+      // 1. Freeze TL
+
+      fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+
+      memcpy(  // copy the incoming ABTS frame
+        &fchs,
+        fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+        sizeof( fchs));
+
+      // 3. OK, Tachyon is frozen so we can invalidate SEST entry 
+      // (if necessary)
+      // Status FC2_TIMEOUT means we are originating the abort, while
+      // TARGET_ABORT means we are ACCepting an abort
+      
+      ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange
+//      printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID);
+
+
+      // sanity check on received ExchangeID
+      if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT )
+      {
+          // clear bit 31 (VALid), to invalidate & take control from TL
+//          printk("Invalidating SEST exchange %Xh\n", ExchangeID);
+          fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF;
+      }
+
+
+      // 4. Resume all Tachlite functions (for other open Exchanges)
+      // as quickly as possible to allow other exchanges to other ports
+      // to resume.  Freezing Tachyon for too long may royally screw
+      // up everything!
+      fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+      
+      // Note there is no confirmation that the chip is "unfrozen".  Also,
+      // if the Link is down when unfreeze is called, it has no effect.
+      // Chip will unfreeze when the Link is back up.
+
+      // 5. Now send out Abort ACC reply for this exchange
+      Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC;
+      
+      fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id;
+      ulStatus = cpqfcTSBuildExchange(
+            cpqfcHBAdata,
+            BLS_ABTS_ACC,
+            &fchs,
+            NULL,         // no data (no scatter/gather list)
+            &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        if( !ulStatus )
+        {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+        }
+        else
+          // check reason for Exchange not being started - we might
+          // want to Queue and start later, or fail with error
+        {
+
+        } 
+      }
+      break;
+
+
+    case BLS_ABTS_RJT:   // need to ReJecT one ABTS; reject implies the
+                         // exchange doesn't exist in the TARGET context.
+                         // ExchangeID has to come from LinkService space.
+
+      printk(" *ABTS_RJT* ");
+      ulStatus = cpqfcTSBuildExchange(
+            cpqfcHBAdata,
+            BLS_ABTS_RJT,
+            (TachFCHDR_GCMND*)
+              fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+            NULL,         // no data (no scatter/gather list)
+            &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+      if( !ulStatus ) // Exchange setup OK?
+      {
+        ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+        // If it fails, we aren't required to retry.
+      }
+      if( ulStatus )
+      {
+        printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+      }
+      else
+      {
+        printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+      
+      }
+
+      break;
+
+
+
+    default:
+      break;
+  }                   // end switch
+//doNothing:
+    // done with this item - now set the NEXT index
+
+  if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test
+  {
+    fcLQ->consumer = 0;
+  }
+  else
+  { 
+    fcLQ->consumer++;
+  }
+
+  PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94)
+
+  LEAVE("WorkTask");
+  return;
+}
+
+
+
+
+// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login)
+// commands come in, post to the LinkQ so that action can be taken outside the
+// interrupt handler.  
+// This circular Q works like Tachyon's que - the producer points to the next
+// (unused) entry.  Called by Interrupt handler, WorkerThread, Timer
+// sputlinkq
+void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata,
+  int Type, 
+  void *QueContent)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+//  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  ULONG ndx;
+  
+  ENTER("cpqfcTSPutLinkQ");
+
+  ndx = fcLQ->producer;
+  
+  ndx += 1;  // test for Que full
+
+
+  
+  if( ndx >= FC_LINKQ_DEPTH ) // rollover test
+    ndx = 0;
+
+  if( ndx == fcLQ->consumer )   // QUE full test
+  {
+                       // QUE was full! lost LK command (fatal to logic)
+    fcChip->fcStats.lnkQueFull++;
+
+    printk("*LinkQ Full!*");
+    TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+/*
+    {
+      int i;
+      printk("LinkQ PI %d, CI %d\n", fcLQ->producer,
+        fcLQ->consumer);
+		      
+      for( i=0; i< FC_LINKQ_DEPTH; )
+      {
+	printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type);
+	if( (++i %8) == 0) printk("\n");
+      }
+  
+    }
+*/    
+    printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung
+  }
+  else                        // QUE next element
+  {
+    // Prevent certain multiple (back-to-back) requests.
+    // This is important in that we don't want to issue multiple
+    // ABTS for the same Exchange, or do multiple FM inits, etc.
+    // We can never be sure of the timing of events reported to
+    // us by Tach's IMQ, which can depend on system/bus speeds,
+    // FC physical link circumstances, etc.
+     
+    if( (fcLQ->producer != fcLQ->consumer)
+	    && 
+        (Type == FMINIT)  )
+    {
+      LONG lastNdx;  // compute previous producer index
+      if( fcLQ->producer)
+        lastNdx = fcLQ->producer- 1;
+      else
+	lastNdx = FC_LINKQ_DEPTH-1;
+
+
+      if( fcLQ->Qitem[lastNdx].Type == FMINIT)
+      {
+//        printk(" *skip FMINIT Q post* ");
+//        goto DoneWithPutQ;
+      }
+
+    }
+
+    // OK, add the Q'd item...
+    
+    fcLQ->Qitem[fcLQ->producer].Type = Type;
+   
+    memcpy(
+        fcLQ->Qitem[fcLQ->producer].ulBuff,
+        QueContent, 
+        sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff));
+
+    fcLQ->producer = ndx;  // increment Que producer
+
+    // set semaphore to wake up Kernel (worker) thread
+    // 
+    up( cpqfcHBAdata->fcQueReady );
+  }
+
+//DoneWithPutQ:
+
+  LEAVE("cpqfcTSPutLinkQ");
+}
+
+
+
+
+// reset device ext FC link Q
+void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata)
+   
+{
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  fcLQ->producer = 0;
+  fcLQ->consumer = 0;
+
+}
+
+
+
+
+
+// When Tachyon gets an unassisted FCP-SCSI frame, post here so
+// an arbitrary context thread (e.g. IOCTL loopback test function)
+// can process it.
+
+// (NOTE: Not revised for Linux)
+// This Q works like Tachyon's que - the producer points to the next
+// (unused) entry.
+void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata,
+  int Type, 
+  void *QueContent)
+{
+//  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+//  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+//  ULONG ndx;
+
+//  ULONG *pExchangeID;
+//  LONG ExchangeID;
+
+/*
+  KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock);
+  ndx = pDevExt->fcScsiQue.producer + 1;  // test for Que full
+
+  if( ndx >= FC_SCSIQ_DEPTH ) // rollover test
+    ndx = 0;
+
+  if( ndx == pDevExt->fcScsiQue.consumer )   // QUE full test
+  {
+                       // QUE was full! lost LK command (fatal to logic)
+    fcChip->fcStats.ScsiQueFull++;
+#ifdef DBG
+    printk( "fcPutScsiQue - FULL!\n");
+#endif
+
+  }
+  else                        // QUE next element
+  {
+    pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type;
+    
+    if( Type == FCP_RSP )
+    {
+      // this TL inbound message type means that a TL SEST exchange has
+      // copied an FCP response frame into a buffer pointed to by the SEST
+      // entry.  That buffer is allocated in the SEST structure at ->RspHDR.
+      // Copy the RspHDR for use by the Que handler.
+      pExchangeID = (ULONG *)QueContent;
+      
+      memcpy(
+	      pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+        &fcChip->SEST->RspHDR[ *pExchangeID ],
+	      sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size)
+      
+    }
+    else
+    {
+      memcpy(
+	      pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+        QueContent, 
+	      sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff));
+    }
+      
+    pDevExt->fcScsiQue.producer = ndx;  // increment Que
+
+
+    KeSetEvent( &pDevExt->TYIBscsi,  // signal any waiting thread
+       0,                    // no priority boost
+		   FALSE );              // no waiting later for this event
+  }
+  KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock);
+*/
+}
+
+
+
+
+
+
+
+static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pFcPort)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+  if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric
+  {
+    fcChip->fcStats.logouts++;
+    printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", 
+        (ULONG)pFcPort->u.liWWN,
+        (ULONG)(pFcPort->u.liWWN >>32),
+	pFcPort->port_id);
+
+  // Terminate I/O with this (Linux) Scsi target
+    cpqfcTSTerminateExchange( cpqfcHBAdata, 
+                            &pFcPort->ScsiNexus,
+	                    DEVICE_REMOVED);
+  }
+			
+  // Do an "implicit logout" - we can't really Logout the device
+  // (i.e. with LOGOut Request) because of port_id confusion
+  // (i.e. the Other port has no port_id).
+  // A new login for that WWN will have to re-write port_id (0 invalid)
+  pFcPort->port_id = 0;  // invalid!
+  pFcPort->pdisc = FALSE;
+  pFcPort->prli = FALSE;
+  pFcPort->plogi = FALSE;
+  pFcPort->flogi = FALSE;
+  pFcPort->LOGO_timer = 0;
+  pFcPort->device_blocked = TRUE; // block Scsi Requests
+}
+
+  
+// On FC-AL, there is a chance that a previously known device can
+// be quietly removed (e.g. with non-managed hub), 
+// while a NEW device (with different WWN) took the same alpa or
+// even 24-bit port_id.  This chance is unlikely but we must always
+// check for it.
+static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata,
+		PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  // set "other port" at beginning of fcPorts list
+  PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort;
+  while( pOtherPortWithPortId ) 
+  {
+    if( (pOtherPortWithPortId->port_id == 
+         pLoggedInPort->port_id) 
+		    &&
+         (pOtherPortWithPortId != pLoggedInPort) )
+    {
+      // trouble!  (Implicitly) Log the other guy out
+      printk(" *port_id %Xh is duplicated!* ", 
+        pOtherPortWithPortId->port_id);
+      cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId); 
+   }
+    pOtherPortWithPortId = pOtherPortWithPortId->pNextPort;
+  }
+}
+
+
+
+
+
+
+// Dynamic Memory Allocation for newly discovered FC Ports.
+// For simplicity, maintain fcPorts structs for ALL
+// for discovered devices, including those we never do I/O with
+// (e.g. Fabric addresses)
+
+static PFC_LOGGEDIN_PORT CreateFcPort( 
+	  CPQFCHBA* cpqfcHBAdata, 
+	  PFC_LOGGEDIN_PORT pLastLoggedInPort, 
+	  TachFCHDR_GCMND* fchs,
+	  LOGIN_PAYLOAD* plogi)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL;
+  int i;
+
+
+  printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id);
+  for( i=3; i>=0; i--)   // copy the LOGIN port's WWN
+    printk("%02X", plogi->port_name[i]);
+  for( i=7; i>3; i--)   // copy the LOGIN port's WWN
+    printk("%02X", plogi->port_name[i]);
+
+
+  // allocate mem for new port
+  // (these are small and rare allocations...)
+  pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC );
+
+    
+  // allocation succeeded?  Fill out NEW PORT
+  if( pNextLoggedInPort )
+  {    
+                              // clear out any garbage (sometimes exists)
+    memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT));
+
+
+    // If we login to a Fabric, we don't want to treat it
+    // as a SCSI device...
+    if( (fchs->s_id & 0xFFF000) != 0xFFF000)
+    {
+      int i;
+      
+      // create a unique "virtual" SCSI Nexus (for now, just a
+      // new target ID) -- we will update channel/target on REPORT_LUNS
+      // special case for very first SCSI target...
+      if( cpqfcHBAdata->HostAdapter->max_id == 0)
+      {
+        pNextLoggedInPort->ScsiNexus.target = 0;
+        fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub"
+      }
+      else
+      {
+        pNextLoggedInPort->ScsiNexus.target =
+          cpqfcHBAdata->HostAdapter->max_id;
+      }
+
+      // initialize the lun[] Nexus struct for lun masking      
+      for( i=0; i< CPQFCTS_MAX_LUN; i++)
+        pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED
+      
+      pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port
+      
+      printk(" SCSI Chan/Trgt %d/%d", 
+          pNextLoggedInPort->ScsiNexus.channel,
+          pNextLoggedInPort->ScsiNexus.target);
+ 
+      // tell Scsi layers about the new target...
+      cpqfcHBAdata->HostAdapter->max_id++; 
+//    printk("HostAdapter->max_id = %d\n",
+//      cpqfcHBAdata->HostAdapter->max_id);		    
+    }                          
+    else
+    {
+      // device is NOT SCSI (in case of Fabric)
+      pNextLoggedInPort->ScsiNexus.target = -1;  // invalid
+    }
+
+	  // create forward link to new port
+    pLastLoggedInPort->pNextPort = pNextLoggedInPort;
+    printk("\n");
+
+  }     
+  return pNextLoggedInPort;  // NULL on allocation failure
+}   // end NEW PORT (WWN) logic
+
+
+
+// For certain cases, we want to terminate exchanges without
+// sending ABTS to the device.  Examples include when an FC
+// device changed it's port_id after Loop re-init, or when
+// the device sent us a logout.  In the case of changed port_id,
+// we want to complete the command and return SOFT_ERROR to
+// force a re-try.  In the case of LOGOut, we might return
+// BAD_TARGET if the device is really gone.
+// Since we must ensure that Tachyon is not operating on the
+// exchange, we have to freeze the chip
+// sterminateex
+void cpqfcTSTerminateExchange( 
+  CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG x_ID;
+
+  if( ScsiNexus )
+  {
+//    printk("TerminateExchange: ScsiNexus chan/target %d/%d\n",
+//		    ScsiNexus->channel, ScsiNexus->target);
+
+  } 
+  
+  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+  {
+    if( Exchanges->fcExchange[x_ID].type )  // in use?
+    {
+      if( ScsiNexus == NULL ) // our HBA changed - term. all
+      {
+	Exchanges->fcExchange[x_ID].status = TerminateStatus;
+        cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); 
+      }
+      else
+      {
+	// If a device, according to WWN, has been removed, it's
+	// port_id may be used by another working device, so we
+	// have to terminate by SCSI target, NOT port_id.
+        if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress?
+	{	                 
+	  if( (Exchanges->fcExchange[x_ID].Cmnd->target == ScsiNexus->target)
+			&&
+            (Exchanges->fcExchange[x_ID].Cmnd->channel == ScsiNexus->channel)) 
+          {
+            Exchanges->fcExchange[x_ID].status = TerminateStatus;
+            cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out
+          }
+	}
+
+	// (in case we ever need it...)
+	// all SEST structures have a remote node ID at SEST DWORD 2
+        //          if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8)
+        //                ==  port_id)
+      } 
+    }
+  }
+}
+
+
+static void ProcessELS_Request( 
+              CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+//  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+//  ULONG ox_id = (fchs->ox_rx_id >>16);
+  PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort;
+  BOOLEAN NeedReject = FALSE;
+  ULONG ls_reject_code = 0; // default don'n know??
+
+
+  // Check the incoming frame for a supported ELS type
+  switch( fchs->pl[0] & 0xFFFF)
+  {
+  case 0x0050: //  PDISC?
+
+    // Payload for PLOGI and PDISC is identical (request & reply)
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      
+      // PDISC payload OK. If critical login fields
+      // (e.g. WWN) matches last login for this port_id,
+      // we may resume any prior exchanges
+      // with the other port
+
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort != NULL)   // WWN found (prior login OK)
+      { 
+           
+ 	if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+	{
+          // Yes.  We were expecting PDISC?
+          if( pLoggedInPort->pdisc )
+	  {
+	    // Yes; set fields accordingly.     (PDISC, not Originator)
+            SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE);
+       
+            // send 'ACC' reply 
+            cpqfcTSPutLinkQue( cpqfcHBAdata, 
+                          ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+                          fchs );
+
+	    // OK to resume I/O...
+	  }
+	  else
+ 	  {
+	    printk("Not expecting PDISC (pdisc=FALSE)\n");
+	    NeedReject = TRUE;
+	    // set reject reason code 
+            ls_reject_code = 
+              LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+	  }
+	}
+	else
+	{
+	  if( pLoggedInPort->port_id != 0)
+	  {
+  	    printk("PDISC PortID change: old %Xh, new %Xh\n",
+              pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+	  }
+          NeedReject = TRUE;
+          // set reject reason code 
+          ls_reject_code = 
+	    LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+		  
+	}
+      }
+      else
+      {
+	printk("PDISC Request from unknown WWN\n");
+        NeedReject = TRUE;
+          
+	// set reject reason code 
+        ls_reject_code = 
+          LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME);
+      }
+
+    }
+    else // Payload unacceptable
+    {
+      printk("payload unacceptable\n");
+      NeedReject = TRUE;  // reject code already set
+      
+    }
+
+    if( NeedReject)
+    {
+      ULONG port_id;
+      // The PDISC failed.  Set login struct flags accordingly,
+      // terminate any I/O to this port, and Q a PLOGI
+      if( pLoggedInPort )
+      {
+        pLoggedInPort->pdisc = FALSE;
+        pLoggedInPort->prli = FALSE;
+        pLoggedInPort->plogi = FALSE;
+	
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+          &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+	port_id = pLoggedInPort->port_id;
+      }
+      else
+      {
+	port_id = fchs->s_id &0xFFFFFF;
+      }
+      fchs->reserved = ls_reject_code; // borrow this (unused) field
+      cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+    }
+   
+    break;
+
+
+
+  case 0x0003: //  PLOGI?
+
+    // Payload for PLOGI and PDISC is identical (request & reply)
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      BOOLEAN NeedReject = FALSE;
+      
+      // PDISC payload OK. If critical login fields
+      // (e.g. WWN) matches last login for this port_id,
+      // we may resume any prior exchanges
+      // with the other port
+
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,       // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort == NULL)   // WWN not found -New Port
+      { 
+      	pLoggedInPort = CreateFcPort( 
+			  cpqfcHBAdata, 
+			  pLastLoggedInPort, 
+			  fchs,
+			  &logi);
+        if( pLoggedInPort == NULL )
+        {
+          printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+          // Now Q a LOGOut Request, since we won't be talking to that device
+	
+          NeedReject = TRUE;  
+	  
+          // set reject reason code 
+          ls_reject_code = 
+            LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES);
+	    
+	}
+      }
+      if( !NeedReject )
+      {
+      
+        // OK - we have valid fcPort ptr; set fields accordingly.   
+	//                         (not PDISC, not Originator)
+        SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); 
+
+        // send 'ACC' reply 
+        cpqfcTSPutLinkQue( cpqfcHBAdata, 
+                      ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+                      fchs );
+      }
+    }
+    else // Payload unacceptable
+    {
+      printk("payload unacceptable\n");
+      NeedReject = TRUE;  // reject code already set
+    }
+
+    if( NeedReject)
+    {
+      // The PDISC failed.  Set login struct flags accordingly,
+      // terminate any I/O to this port, and Q a PLOGI
+      pLoggedInPort->pdisc = FALSE;
+      pLoggedInPort->prli = FALSE;
+      pLoggedInPort->plogi = FALSE;
+	
+      fchs->reserved = ls_reject_code; // borrow this (unused) field
+
+      // send 'RJT' reply 
+      cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+    }
+   
+    // terminate any exchanges with this device...
+    if( pLoggedInPort )
+    {
+      cpqfcTSTerminateExchange( cpqfcHBAdata, 
+        &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+    }
+    break;
+
+
+
+  case 0x1020:  // PRLI?
+  {
+    BOOLEAN NeedReject = TRUE;
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,       // don't search Scsi Nexus
+	   (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
+           NULL,     // DON'T search linked list for WWN
+           NULL);    // don't care
+      
+    if( pLoggedInPort == NULL ) 
+    {
+      // huh?
+      printk(" Unexpected PRLI Request -not logged in!\n");
+
+      // set reject reason code 
+      ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+      
+      // Q a LOGOut here?
+    }
+    else
+    {
+      // verify the PRLI ACC payload
+      if( !verify_PRLI( fchs, &ls_reject_code) )
+      {
+        // PRLI Reply is acceptable; were we expecting it?
+        if( pLoggedInPort->plogi ) 
+        { 
+  	  // yes, we expected the PRLI ACC  (not PDISC; not Originator)
+          SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+          // Q an ACCept Reply
+	  cpqfcTSPutLinkQue( cpqfcHBAdata,
+                        ELS_PRLI_ACC, 
+                        fchs );   
+	  
+	  NeedReject = FALSE;
+	}
+        else
+        {
+          // huh?
+          printk(" (unexpected) PRLI REQEST with plogi FALSE\n");
+
+          // set reject reason code 
+          ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+    
+    	  // Q a LOGOut here?
+	  
+        }
+      }
+      else
+      {
+        printk(" PRLI REQUEST payload failed verify\n");
+        // (reject code set by "verify")
+
+        // Q a LOGOut here?
+      }
+    }
+
+    if( NeedReject )
+    {
+      // Q a ReJecT Reply with reason code
+      fchs->reserved = ls_reject_code;
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );  
+    }
+  }
+    break;
+ 
+
+    
+
+  case  0x0005:  // LOGOut?
+  {
+  // was this LOGOUT because we sent a ELS_PDISC to an FC device
+  // with changed (or new) port_id, or does the port refuse 
+  // to communicate to us?
+  // We maintain a logout counter - if we get 3 consecutive LOGOuts,
+  // give up!
+    LOGOUT_PAYLOAD logo;
+    BOOLEAN GiveUpOnDevice = FALSE;
+    ULONG ls_reject_code = 0;
+    
+    BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo));
+
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,     // don't search Scsi Nexus
+	   0,        // don't search linked list for port_id
+           &logo.port_name[0],     // search linked list for WWN
+           NULL);    // don't care about end of list
+    
+    if( pLoggedInPort ) // found the device?
+    {
+      // Q an ACC reply 
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_LOGO_ACC, // Q Type
+                    fchs );       // device to respond to
+
+      // set login struct fields (LOGO_counter increment)
+      SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+      
+      // are we an Initiator?
+      if( fcChip->Options.initiator)  
+      {
+        // we're an Initiator, so check if we should 
+	// try (another?) login
+
+	// Fabrics routinely log out from us after
+	// getting device info - don't try to log them
+	// back in.
+	if( (fchs->s_id & 0xFFF000) == 0xFFF000 )
+	{
+	  ; // do nothing
+	}
+	else if( pLoggedInPort->LOGO_counter <= 3)
+	{
+	  // try (another) login (PLOGI request)
+	  
+          cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_PLOGI, // Q Type
+                    fchs );  
+	
+	  // Terminate I/O with "retry" potential
+	  cpqfcTSTerminateExchange( cpqfcHBAdata, 
+			            &pLoggedInPort->ScsiNexus,
+				    PORTID_CHANGED);
+	}
+	else
+	{
+	  printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n",
+			  fchs->s_id &&0xFFFFFF);
+	  GiveUpOnDevice = TRUE;
+	}
+      }
+      else
+      {
+	GiveUpOnDevice = TRUE;
+      }
+
+
+      if( GiveUpOnDevice == TRUE )
+      {
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+	                          &pLoggedInPort->ScsiNexus,
+		                  DEVICE_REMOVED);
+      }
+    }  
+    else  // we don't know this WWN!
+    {
+      // Q a ReJecT Reply with reason code
+      fchs->reserved = ls_reject_code;
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );  
+    }
+  }
+    break;
+
+
+
+
+  // FABRIC only case
+  case 0x0461:  // ELS RSCN (Registered State Change Notification)?
+  {
+    int Ports;
+    int i;
+    __u32 Buff;
+    // Typically, one or more devices have been added to or dropped
+    // from the Fabric.
+    // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997)
+    // The first 32-bit word has a 2-byte Payload Length, which
+    // includes the 4 bytes of the first word.  Consequently,
+    // this PL len must never be less than 4, must be a multiple of 4,
+    // and has a specified max value 256.
+    // (Endianess!)
+    Ports = ((fchs->pl[0] >>24) - 4) / 4;
+    Ports = Ports > 63 ? 63 : Ports;
+    
+    printk(" RSCN ports: %d\n", Ports);
+    if( Ports <= 0 )  // huh?
+    {
+      // ReJecT the command
+      fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0);
+    
+      cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs ); 
+      
+      break;
+    }
+    else  // Accept the command
+    {
+       cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_ACC, // Q Type
+                    fchs ); 
+    }
+    
+      // Check the "address format" to determine action.
+      // We have 3 cases:
+      // 0 = Port Address; 24-bit address of affected device
+      // 1 = Area Address; MS 16 bits valid
+      // 2 = Domain Address; MS 8 bits valid
+    for( i=0; i<Ports; i++)
+    { 
+      BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4);
+      switch( Buff & 0xFF000000)
+      {
+
+      case 0:  // Port Address?
+	
+      case 0x01000000: // Area Domain?
+      case 0x02000000: // Domain Address
+        // For example, "port_id" 0x201300 
+	// OK, let's try a Name Service Request (Query)
+      fchs->s_id = 0xFFFFFC;  // Name Server Address
+      cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+      break;
+	
+	
+      default:  // huh? new value on version change?
+      break;
+      }
+    }
+  }    
+  break;    
+
+
+
+    
+  default:  // don't support this request (yet)
+    // set reject reason code 
+    fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 
+		                    REQUEST_NOT_SUPPORTED);
+    
+    cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_RJT, // Q Type
+                    fchs );     
+    break;  
+  }
+}
+
+
+static void ProcessELS_Reply( 
+		CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ox_id = (fchs->ox_rx_id >>16);
+  ULONG ls_reject_code;
+  PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+  
+  // If this is a valid reply, then we MUST have sent a request.
+  // Verify that we can find a valid request OX_ID corresponding to
+  // this reply
+
+  
+  if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+  {
+    printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ", 
+		    ox_id, fchs->ox_rx_id & 0xffff);
+    goto Quit;  // exit this routine
+  }
+
+
+  // Is the reply a RJT (reject)?
+  if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply?
+  {
+//  ******  REJECT REPLY  ********
+    switch( Exchanges->fcExchange[ox_id].type )
+    {
+	  
+    case ELS_FDISC:  // we sent out Fabric Discovery
+    case ELS_FLOGI:  // we sent out FLOGI
+
+      printk("RJT received on Fabric Login from %Xh, reason %Xh\n", 
+        fchs->s_id, fchs->pl[1]);    
+
+    break;
+
+    default:
+    break;
+    }
+      
+    goto Done;
+  }
+
+  // OK, we have an ACCept...
+  // What's the ACC type? (according to what we sent)
+  switch( Exchanges->fcExchange[ox_id].type )
+  {
+	  
+  case ELS_PLOGI:  // we sent out PLOGI
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      
+      // login ACC payload acceptable; search for WWN in our list
+      // of fcPorts
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort == NULL)         // WWN not found - new port
+      {
+
+	pLoggedInPort = CreateFcPort( 
+			  cpqfcHBAdata, 
+			  pLastLoggedInPort, 
+			  fchs,
+			  &logi);
+
+        if( pLoggedInPort == NULL )
+        {
+          printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+          // Now Q a LOGOut Request, since we won't be talking to that device
+	
+          goto Done;  // exit with error! dropped login frame
+	}
+      }
+      else      // WWN was already known.  Ensure that any open
+	        // exchanges for this WWN are terminated.
+      	// NOTE: It's possible that a device can change its 
+	// 24-bit port_id after a Link init or Fabric change 
+	// (e.g. LIP or Fabric RSCN).  In that case, the old
+	// 24-bit port_id may be duplicated, or no longer exist.
+      {
+
+        cpqfcTSTerminateExchange( cpqfcHBAdata, 
+          &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+      }
+
+      // We have an fcPort struct - set fields accordingly
+                                    // not PDISC, originator 
+      SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+			
+      // We just set a "port_id"; is it duplicated?
+      TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+      // For Fabric operation, we issued PLOGI to 0xFFFFFC
+      // so we can send SCR (State Change Registration) 
+      // Check for this special case...
+      if( fchs->s_id == 0xFFFFFC ) 
+      {
+        // PLOGI ACC was a Fabric response... issue SCR
+	fchs->s_id = 0xFFFFFD;  // address for SCR
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs);
+      }
+
+      else
+      {
+      // Now we need a PRLI to enable FCP-SCSI operation
+      // set flags and Q up a ELS_PRLI
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs);
+      }
+    }
+    else
+    {
+      // login payload unacceptable - reason in ls_reject_code
+      // Q up a Logout Request
+      printk("Login Payload unacceptable\n");
+
+    }
+    break;
+
+
+  // PDISC logic very similar to PLOGI, except we never want
+  // to allocate mem for "new" port, and we set flags differently
+  // (might combine later with PLOGI logic for efficiency)  
+  case ELS_PDISC:  // we sent out PDISC
+    if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+    {
+      LOGIN_PAYLOAD logi;       // FC-PH Port Login
+      BOOLEAN NeedLogin = FALSE;
+      
+      // login payload acceptable; search for WWN in our list
+      // of (previously seen) fcPorts
+      
+      BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+   
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+	     NULL,     // don't search Scsi Nexus
+	     0,        // don't search linked list for port_id
+             &logi.port_name[0],     // search linked list for WWN
+             &pLastLoggedInPort);  // must return non-NULL; when a port_id
+                                   // is not found, this pointer marks the
+                                   // end of the singly linked list
+    
+      if( pLoggedInPort != NULL)   // WWN found?
+      {
+        // WWN has same port_id as last login?  (Of course, a properly
+	// working FC device should NEVER ACCept a PDISC if it's
+	// port_id changed, but check just in case...)
+	if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+	{
+          // Yes.  We were expecting PDISC?
+          if( pLoggedInPort->pdisc )
+	  {
+            int i;
+	    
+	    
+	    // PDISC expected -- set fields.  (PDISC, Originator)
+            SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE);
+
+	    // We are ready to resume FCP-SCSI to this device...
+            // Do we need to start anything that was Queued?
+
+            for( i=0; i< TACH_SEST_LEN; i++)
+            {
+              // see if any exchange for this PDISC'd port was queued
+              if( ((fchs->s_id &0xFFFFFF) == 
+                   (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF))
+                      &&
+                  (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED))
+              {
+                fchs->reserved = i; // copy ExchangeID
+//                printk(" *Q x_ID %Xh after PDISC* ",i);
+
+                cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs );
+              }
+            }
+
+	    // Complete commands Q'd while we were waiting for Login
+
+	    UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+	  }
+	  else
+	  {
+	    printk("Not expecting PDISC (pdisc=FALSE)\n");
+	    NeedLogin = TRUE;
+	  }
+	}
+	else
+	{
+	  printk("PDISC PortID change: old %Xh, new %Xh\n",
+            pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+          NeedLogin = TRUE;
+		  
+	}
+      }
+      else
+      {
+	printk("PDISC ACC from unknown WWN\n");
+        NeedLogin = TRUE;
+      }
+
+      if( NeedLogin)
+      {
+	
+        // The PDISC failed.  Set login struct flags accordingly,
+	// terminate any I/O to this port, and Q a PLOGI
+	if( pLoggedInPort )  // FC device previously known?
+	{
+
+          cpqfcTSPutLinkQue( cpqfcHBAdata,
+                    ELS_LOGO, // Q Type
+                    fchs );   // has port_id to send to 
+
+	  // There are a variety of error scenarios which can result
+  	  // in PDISC failure, so as a catchall, add the check for
+	  // duplicate port_id.
+	  TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+//    TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+          pLoggedInPort->pdisc = FALSE;
+          pLoggedInPort->prli = FALSE;
+          pLoggedInPort->plogi = FALSE;
+	
+          cpqfcTSTerminateExchange( cpqfcHBAdata, 
+	    &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+        }
+        cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs );
+      }
+    }
+    else
+    {
+      // login payload unacceptable - reason in ls_reject_code
+      // Q up a Logout Request
+      printk("ERROR: Login Payload unacceptable!\n");
+
+    }
+   
+    break;
+    
+
+
+  case ELS_PRLI:  // we sent out PRLI
+
+
+    pLoggedInPort = fcFindLoggedInPort( 
+           fcChip, 
+           NULL,       // don't search Scsi Nexus
+	   (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
+           NULL,     // DON'T search linked list for WWN
+           NULL);    // don't care
+      
+    if( pLoggedInPort == NULL ) 
+    {
+      // huh?
+      printk(" Unexpected PRLI ACCept frame!\n");
+
+      // Q a LOGOut here?
+
+      goto Done;
+    }
+
+    // verify the PRLI ACC payload
+    if( !verify_PRLI( fchs, &ls_reject_code) )
+    {
+      // PRLI Reply is acceptable; were we expecting it?
+      if( pLoggedInPort->plogi ) 
+      { 
+	// yes, we expected the PRLI ACC  (not PDISC; Originator)
+	SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+        // OK, let's send a REPORT_LUNS command to determine
+	// whether VSA or PDA FCP-LUN addressing is used.
+	
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+	
+	// It's possible that a device we were talking to changed 
+	// port_id, and has logged back in.  This function ensures
+	// that I/O will resume.
+        UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+
+      }
+      else
+      {
+        // huh?
+        printk(" (unexpected) PRLI ACCept with plogi FALSE\n");
+
+        // Q a LOGOut here?
+        goto Done;
+      }
+    }
+    else
+    {
+      printk(" PRLI ACCept payload failed verify\n");
+
+      // Q a LOGOut here?
+    }
+
+    break;
+ 
+  case ELS_FLOGI:  // we sent out FLOGI (Fabric Login)
+
+    // update the upper 16 bits of our port_id in Tachyon
+    // the switch adds those upper 16 bits when responding
+    // to us (i.e. we are the destination_id)
+    fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF);
+    writel( fcChip->Registers.my_al_pa,  
+      fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+
+    // now send out a PLOGI to the well known port_id 0xFFFFFC
+    fchs->s_id = 0xFFFFFC;
+    cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs);
+  
+   break; 
+
+
+  case ELS_FDISC:  // we sent out FDISC (Fabric Discovery (Login))
+
+   printk( " ELS_FDISC success ");
+   break;
+   
+
+  case ELS_SCR:  // we sent out State Change Registration
+    // now we can issue Name Service Request to find any
+    // Fabric-connected devices we might want to login to.
+   
+	
+    fchs->s_id = 0xFFFFFC;  // Name Server Address
+    cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+    
+
+    break;
+
+    
+  default:
+    printk(" *Discarding unknown ACC frame, xID %04X/%04X* ", 
+   		    ox_id, fchs->ox_rx_id & 0xffff);
+    break;
+  }
+
+  
+Done:
+  // Regardless of whether the Reply is valid or not, the
+  // the exchange is done - complete
+  cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+	  
+Quit:    
+  return;
+}
+
+
+
+
+
+
+// ****************  Fibre Channel Services  **************
+// This is where we process the Directory (Name) Service Reply
+// to know which devices are on the Fabric
+
+static void ProcessFCS_Reply( 
+	CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ox_id = (fchs->ox_rx_id >>16);
+//  ULONG ls_reject_code;
+//  PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+  
+  // If this is a valid reply, then we MUST have sent a request.
+  // Verify that we can find a valid request OX_ID corresponding to
+  // this reply
+
+  if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+  {
+    printk(" *Discarding Reply frame, xID %04X/%04X* ", 
+		    ox_id, fchs->ox_rx_id & 0xffff);
+    goto Quit;  // exit this routine
+  }
+
+
+  // OK, we were expecting it.  Now check to see if it's a
+  // "Name Service" Reply, and if so force a re-validation of
+  // Fabric device logins (i.e. Start the login timeout and
+  // send PDISC or PLOGI)
+  // (Endianess Byte Swap?)
+  if( fchs->pl[1] == 0x02FC )  // Name Service
+  {
+    // got a new (or NULL) list of Fabric attach devices... 
+    // Invalidate current logins
+    
+    PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+    while( pLoggedInPort ) // for all ports which are expecting
+                           // PDISC after the next LIP, set the
+                           // logoutTimer
+    {
+
+      if( (pLoggedInPort->port_id & 0xFFFF00)  // Fabric device?
+		      &&
+          (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port
+      {
+        pLoggedInPort->LOGO_timer = 6;  // what's the Fabric timeout??
+                                // suspend any I/O in progress until
+                                // PDISC received...
+        pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands
+      }
+	    
+      pLoggedInPort = pLoggedInPort->pNextPort;
+    }
+    
+    if( fchs->pl[2] == 0x0280)  // ACCept?
+    {
+      // Send PLOGI or PDISC to these Fabric devices
+      SendLogins( cpqfcHBAdata, &fchs->pl[4] );  
+    }
+
+
+    // As of this writing, the only reason to reject is because NO
+    // devices are left on the Fabric.  We already started
+    // "logged out" timers; if the device(s) don't come
+    // back, we'll do the implicit logout in the heart beat 
+    // timer routine
+    else  // ReJecT
+    {
+      // this just means no Fabric device is visible at this instant
+    } 
+  }
+
+  // Regardless of whether the Reply is valid or not, the
+  // the exchange is done - complete
+  cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+	  
+Quit:    
+  return;
+}
+
+
+
+
+
+
+
+static void AnalyzeIncomingFrame( 
+        CPQFCHBA *cpqfcHBAdata,
+        ULONG QNdx )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+  TachFCHDR_GCMND* fchs = 
+    (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff;
+//  ULONG ls_reject_code;  // reason for rejecting login
+  LONG ExchangeID;
+//  FC_LOGGEDIN_PORT *pLoggedInPort;
+  BOOLEAN AbortAccept;  
+
+  ENTER("AnalyzeIncomingFrame");
+
+
+
+  switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown
+  {
+
+  case SFQ_UNKNOWN:  // unknown frame (e.g. LIP position frame, NOP, etc.)
+ 
+
+      // *********  FC-4 Device Data/ Fibre Channel Service *************
+    if( ((fchs->d_id &0xF0000000) == 0)   // R_CTL (upper nibble) 0x0?
+                &&   
+      (fchs->f_ctl & 0x20000000) )  // TYPE 20h is Fibre Channel Service
+    {
+
+      // ************** FCS Reply **********************
+
+      if( (fchs->d_id & 0xff000000L) == 0x03000000L)  // (31:23 R_CTL)
+      {
+	ProcessFCS_Reply( cpqfcHBAdata, fchs );
+
+      }  // end of  FCS logic
+
+    }
+    
+
+      // ***********  Extended Link Service **************
+
+    else if( fchs->d_id & 0x20000000   // R_CTL 0x2?
+                  &&   
+      (fchs->f_ctl & 0x01000000) )  // TYPE = 1
+    {
+
+                           // these frames are either a response to
+                           // something we sent (0x23) or "unsolicited"
+                           // frames (0x22).
+
+
+      // **************Extended Link REPLY **********************
+                           // R_CTL Solicited Control Reply
+
+      if( (fchs->d_id & 0xff000000L) == 0x23000000L)  // (31:23 R_CTL)
+      {
+
+	ProcessELS_Reply( cpqfcHBAdata, fchs );
+
+      }  // end of  "R_CTL Solicited Control Reply"
+
+
+
+
+       // **************Extended Link REQUEST **********************
+       // (unsolicited commands from another port or task...)
+
+                           // R_CTL Ext Link REQUEST
+      else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+              (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame)
+      {
+
+
+
+	ProcessELS_Request( cpqfcHBAdata, fchs );
+
+      }
+
+
+
+        // ************** LILP **********************
+      else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+               (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames)
+
+      {
+        // SANMark specifies that when available, we must use
+	// the LILP frame to determine which ALPAs to send Port Discovery
+	// to...
+
+        if( fchs->pl[0] == 0x0711L) //  ELS_PLOGI?
+	{
+//	  UCHAR *ptr = (UCHAR*)&fchs->pl[1];
+//	  printk(" %d ALPAs found\n", *ptr);
+	  memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs
+	  fcChip->Options.LILPin = 1; // our LILPmap is valid!
+	  // now post to make Port Discovery happen...
+          cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs);  
+	}
+      }
+    }
+
+     
+    // *****************  BASIC LINK SERVICE *****************
+    
+    else if( fchs->d_id & 0x80000000  // R_CTL:
+                    &&           // Basic Link Service Request
+           !(fchs->f_ctl & 0xFF000000) )  // type=0 for BLS
+    {
+
+      // Check for ABTS (Abort Sequence)
+      if( (fchs->d_id & 0x8F000000) == 0x81000000)
+      {
+        // look for OX_ID, S_ID pair that matches in our
+        // fcExchanges table; if found, reply with ACCept and complete
+        // the exchange
+
+        // Per PLDA, an ABTS is sent by an initiator; therefore
+        // assume that if we have an exhange open to the port who
+        // sent ABTS, it will be the d_id of what we sent.  
+        for( ExchangeID = 0, AbortAccept=FALSE;
+             ExchangeID < TACH_SEST_LEN; ExchangeID++)
+        {
+            // Valid "target" exchange 24-bit port_id matches? 
+            // NOTE: For the case of handling Intiator AND Target
+            // functions on the same chip, we can have TWO Exchanges
+            // with the same OX_ID -- OX_ID/FFFF for the CMND, and
+            // OX_ID/RX_ID for the XRDY or DATA frame(s).  Ideally,
+            // we would like to support ABTS from Initiators or Targets,
+            // but it's not clear that can be supported on Tachyon for
+            // all cases (requires more investigation).
+            
+          if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE ||
+               Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE)
+                  &&
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+              
+              // target xchnge port_id matches -- how about OX_ID?
+            if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000)
+                    == (fchs->ox_rx_id & 0xFFFF0000) )
+                    // yes! post ACCept response; will be completed by fcStart
+            {
+              Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT;
+                
+                // copy (add) rx_id field for simplified ACCept reply
+              fchs->ox_rx_id = 
+                Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id;
+                
+              cpqfcTSPutLinkQue( cpqfcHBAdata,
+                            BLS_ABTS_ACC, // Q Type 
+                            fchs );    // void QueContent
+              AbortAccept = TRUE;
+      printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n", 
+             fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id);
+              break;      // ABTS can affect only ONE exchange -exit loop
+            }
+          }
+        }  // end of FOR loop
+        if( !AbortAccept ) // can't ACCept ABTS - send Reject
+        {
+      printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n", 
+            fchs->ox_rx_id);
+          if( Exchanges->fcExchange[ ExchangeID].type 
+                &&
+              !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len
+               & 0x80000000))
+          {
+            cpqfcTSCompleteExchange( fcChip, ExchangeID);
+          }
+          else
+          {
+            printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n", 
+              ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len);
+          }
+        }
+      }
+
+      // Check for BLS {ABTS? (Abort Sequence)} ACCept
+      else if( (fchs->d_id & 0x8F000000) == 0x84000000)
+      {
+        // target has responded with ACC for our ABTS;
+	// complete the indicated exchange with ABORTED status 
+        // Make no checks for correct RX_ID, since
+	// all we need to conform ABTS ACC is the OX_ID.
+        // Verify that the d_id matches!
+ 
+        ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+//	printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n", 
+//          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff,
+//          Exchanges->fcExchange[ExchangeID].status);
+
+
+	
+        if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+        {
+            // Does "target" exchange 24-bit port_id match? 
+            // (See "NOTE" above for handling Intiator AND Target in
+            // the same device driver)
+            // First, if this is a target response, then we originated
+	    // (initiated) it with BLS_ABTS:
+	  
+          if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+                  &&
+	    // Second, does the source of this ACC match the destination
+            // of who we originally sent it to?
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+            cpqfcTSCompleteExchange( fcChip, ExchangeID );
+	  }
+        }              
+      }
+      // Check for BLS {ABTS? (Abort Sequence)} ReJecT
+      else if( (fchs->d_id & 0x8F000000) == 0x85000000)
+      {
+        // target has responded with RJT for our ABTS;
+	// complete the indicated exchange with ABORTED status 
+        // Make no checks for correct RX_ID, since
+	// all we need to conform ABTS ACC is the OX_ID.
+        // Verify that the d_id matches!
+ 
+        ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+//	printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n", 
+//          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff);
+
+        if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+	{  
+            // Does "target" exchange 24-bit port_id match? 
+            // (See "NOTE" above for handling Intiator AND Target in
+            // the same device driver)
+            // First, if this is a target response, then we originated
+	    // (initiated) it with BLS_ABTS:
+		  
+          if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+                  &&
+	    // Second, does the source of this ACC match the destination
+            // of who we originally sent it to?
+             ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+             (fchs->s_id & 0xFFFFFF)) )
+          {
+	    // YES! NOTE: There is a bug in CPQ's RA-4000 box 
+	    // where the "reason code" isn't returned in the payload
+	    // For now, simply presume the reject is because the target
+	    // already completed the exchange...
+	    
+//            printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID);
+            cpqfcTSCompleteExchange( fcChip, ExchangeID );
+	  }
+	} 
+      }  // end of ABTS check
+    }  // end of Basic Link Service Request
+    break;
+  
+    default:
+      printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n",
+        fcLQ->Qitem[QNdx].Type,
+        fcLQ->Qitem[QNdx].Type);
+    break;
+  }
+}
+
+
+// Function for Port Discovery necessary after every FC 
+// initialization (e.g. LIP).
+// Also may be called if from Fabric Name Service logic.
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ulStatus=0;
+  TachFCHDR_GCMND fchs;  // copy fields for transmission
+  int i;
+  ULONG loginType;
+  LONG ExchangeID;
+  PFC_LOGGEDIN_PORT pLoggedInPort;
+  __u32 PortIds[ number_of_al_pa];
+  int NumberOfPorts=0;
+
+  // We're going to presume (for now) that our limit of Fabric devices
+  // is the same as the number of alpa on a private loop (126 devices).
+  // (Of course this could be changed to support however many we have
+  // memory for).
+  memset( &PortIds[0], 0, sizeof(PortIds));
+   
+  // First, check if this login is for our own Link Initialization
+  // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices
+  // from a switch.  If we are logging into Fabric devices, we'll
+  // have a non-NULL FabricPortId pointer
+  
+  if( FabricPortIds != NULL) // may need logins
+  {
+    int LastPort=FALSE;
+    i = 0;
+    while( !LastPort)
+    {
+      // port IDs From NSR payload; byte swap needed?
+      BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4);
+ 
+//      printk("FPortId[%d] %Xh ", i, PortIds[i]);
+      if( PortIds[i] & 0x80000000)
+	LastPort = TRUE;
+      
+      PortIds[i] &= 0xFFFFFF; // get 24-bit port_id
+      // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge)
+      // erroneously use ALPA 0.
+      if( PortIds[i]  ) // need non-zero port_id...
+        i++;
+      
+      if( i >= number_of_al_pa ) // (in)sanity check
+	break;
+      FabricPortIds++;  // next...
+    }
+
+    NumberOfPorts = i;
+//    printk("NumberOf Fabric ports %d", NumberOfPorts);
+  }
+  
+  else  // need to send logins on our "local" link
+  {
+  
+    // are we a loop port?  If so, check for reception of LILP frame,
+    // and if received use it (SANMark requirement)
+    if( fcChip->Options.LILPin )
+    {
+      int j=0;
+      // sanity check on number of ALPAs from LILP frame...
+      // For format of LILP frame, see FC-AL specs or 
+      // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8)
+      // First byte is number of ALPAs
+      i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0];
+      NumberOfPorts = i;
+//      printk(" LILP alpa count %d ", i);
+      while( i > 0)
+      {
+	PortIds[j] = fcChip->LILPmap[1+ j];
+	j++; i--;
+      }
+    }
+    else  // have to send login to everybody
+    {
+      int j=0;
+      i = number_of_al_pa;
+      NumberOfPorts = i;
+      while( i > 0)
+      {
+        PortIds[j] = valid_al_pa[j]; // all legal ALPAs
+	j++; i--;
+      }
+    }
+  }
+
+
+  // Now we have a copy of the port_ids (and how many)...
+  for( i = 0; i < NumberOfPorts; i++)
+  {
+    // 24-bit FC Port ID
+    fchs.s_id = PortIds[i];  // note: only 8-bits used for ALPA
+
+
+    // don't log into ourselves (Linux Scsi disk scan will stop on
+    // no TARGET support error on us, and quit trying for rest of devices)
+    if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) )
+      continue;
+
+    // fabric login needed?
+    if( (fchs.s_id == 0) || 
+        (fcChip->Options.fabric == 1) )
+    {
+      fcChip->Options.flogi = 1;  // fabric needs longer for login
+      // Do we need FLOGI or FDISC?
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+             NULL,           // don't search SCSI Nexus
+             0xFFFFFC,       // search linked list for Fabric port_id
+             NULL,           // don't search WWN
+             NULL);          // (don't care about end of list)
+
+      if( pLoggedInPort )    // If found, we have prior experience with
+                             // this port -- check whether PDISC is needed
+      {
+        if( pLoggedInPort->flogi )
+	{
+	  // does the switch support FDISC?? (FLOGI for now...)
+          loginType = ELS_FLOGI;  // prior FLOGI still valid
+	}
+        else
+          loginType = ELS_FLOGI;  // expired FLOGI
+      }
+      else                      // first FLOGI?
+        loginType = ELS_FLOGI;  
+
+
+      fchs.s_id = 0xFFFFFE;   // well known F_Port address
+
+      // Fabrics are not required to support FDISC, and
+      // it's not clear if that helps us anyway, since
+      // we'll want a Name Service Request to re-verify
+      // visible devices...
+      // Consequently, we always want our upper 16 bit
+      // port_id to be zero (we'll be rejected if we
+      // use our prior port_id if we've been plugged into
+      // a different switch port).
+      // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87)
+      // If our ALPA is 55h for instance, we want the FC frame
+      // s_id to be 0x000055, while Tach's my_al_pa register
+      // must be 0x000155, to force an OPN at ALPA 0 
+      // (the Fabric port)
+      fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI
+      writel( fcChip->Registers.my_al_pa | 0x0100,  
+        fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+    }
+
+    else // not FLOGI...
+    {
+      // should we send PLOGI or PDISC?  Check if any prior port_id
+      // (e.g. alpa) completed a PLOGI/PRLI exchange by checking 
+      // the pdisc flag.
+
+      pLoggedInPort = fcFindLoggedInPort( 
+             fcChip, 
+             NULL,           // don't search SCSI Nexus
+             fchs.s_id,      // search linked list for al_pa
+             NULL,           // don't search WWN
+             NULL);          // (don't care about end of list)
+
+             
+
+      if( pLoggedInPort )      // If found, we have prior experience with
+                             // this port -- check whether PDISC is needed
+      {
+        if( pLoggedInPort->pdisc )
+	{
+          loginType = ELS_PDISC;  // prior PLOGI and PRLI maybe still valid
+           
+	}
+        else
+          loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
+      }
+      else                      // never talked to this port_id before
+        loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
+    }
+
+
+    
+    ulStatus = cpqfcTSBuildExchange(
+          cpqfcHBAdata,
+          loginType,            // e.g. PLOGI
+          &fchs,        // no incoming frame (we are originator)
+          NULL,         // no data (no scatter/gather list)
+          &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+    if( !ulStatus ) // Exchange setup OK?
+    {
+      ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+      if( !ulStatus )
+      {
+          // submitted to Tach's Outbound Que (ERQ PI incremented)
+          // waited for completion for ELS type (Login frames issued
+          // synchronously)
+
+	if( loginType == ELS_PDISC )
+	{
+	  // now, we really shouldn't Revalidate SEST exchanges until
+	  // we get an ACC reply from our target and verify that
+	  // the target address/WWN is unchanged.  However, when a fast
+	  // target gets the PDISC, they can send SEST Exchange data
+	  // before we even get around to processing the PDISC ACC.
+	  // Consequently, we lose the I/O.
+	  // To avoid this, go ahead and Revalidate when the PDISC goes
+	  // out, anticipating that the ACC will be truly acceptable
+	  // (this happens 99.9999....% of the time).
+	  // If we revalidate a SEST write, and write data goes to a
+	  // target that is NOT the one we originated the WRITE to,
+	  // that target is required (FCP-SCSI specs, etc) to discard 
+	  // our WRITE data.
+
+          // Re-validate SEST entries (Tachyon hardware assists)
+          RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort); 
+    //TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+	}
+      }
+      else  // give up immediately on error
+      {
+#ifdef LOGIN_DBG
+        printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus );
+#endif
+        break;
+      }
+
+              
+      if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc.
+      {
+        ulStatus = LNKDWN_OSLS;
+#ifdef LOGIN_DBG
+        printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id);
+#endif
+        break;
+      }
+        // Check the exchange for bad status (i.e. FrameTimeOut),
+        // and complete on bad status (most likely due to BAD_ALPA)
+        // on LDn, DPC function may already complete (ABORT) a started
+        // exchange, so check type first (type = 0 on complete).
+      if( Exchanges->fcExchange[ExchangeID].status )
+      {
+#ifdef LOGIN_DBG 
+ 	printk("completing x_ID %X on status %Xh\n", 
+          ExchangeID, Exchanges->fcExchange[ExchangeID].status);
+#endif
+        cpqfcTSCompleteExchange( fcChip, ExchangeID);
+      }
+    }
+    else   // Xchange setup failed...
+    {
+#ifdef LOGIN_DBG
+      printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+#endif
+      break;
+    }
+  }
+  if( !ulStatus )
+  {
+    // set the event signifying that all ALPAs were sent out.
+#ifdef LOGIN_DBG
+    printk("SendLogins: PortDiscDone\n");
+#endif
+    cpqfcHBAdata->PortDiscDone = 1;
+
+
+    // TL/TS UG, pg. 184
+    // 0x0065 = 100ms for RT_TOV
+    // 0x01f5 = 500ms for ED_TOV
+    fcChip->Registers.ed_tov.value = 0x006501f5L; 
+    writel( fcChip->Registers.ed_tov.value,
+      (fcChip->Registers.ed_tov.address));
+
+    // set the LP_TOV back to ED_TOV (i.e. 500 ms)
+    writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+  }
+  else
+  {
+    printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n", 
+      ExchangeID, fchs.s_id, ulStatus);
+  }
+  LEAVE("SendLogins");
+
+}
+
+
+// for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi",
+// D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9)
+static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
+{
+  struct Scsi_Host *HostAdapter = Cmnd->host;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LOGGEDIN_PORT pLoggedInPort; 
+  int LunListLen=0;
+  int i;
+  ULONG x_ID = 0xFFFFFFFF;
+  UCHAR *ucBuff = Cmnd->request_buffer;
+
+//  printk("cpqfcTS: ReportLunsDone \n");
+  // first, we need to find the Exchange for this command,
+  // so we can find the fcPort struct to make the indicated
+  // changes.
+  for( i=0; i< TACH_SEST_LEN; i++)
+  {
+    if( Exchanges->fcExchange[i].type   // exchange defined?
+                   &&
+       (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches?
+	      
+    {
+      x_ID = i;  // found exchange!
+      break;
+    }
+  }
+  if( x_ID == 0xFFFFFFFF)
+  {
+//    printk("cpqfcTS: ReportLuns failed - no FC Exchange\n");
+    goto Done;  // Report Luns FC Exchange gone; 
+                // exchange probably Terminated by Implicit logout
+  }
+
+
+  // search linked list for the port_id we sent INQUIRY to
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+    NULL,     // DON'T search Scsi Nexus (we will set it)
+    Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,        
+    NULL,     // DON'T search linked list for FC WWN
+    NULL);    // DON'T care about end of list
+ 
+  if( !pLoggedInPort )
+  {
+//    printk("cpqfcTS: ReportLuns failed - device gone\n");
+    goto Done; // error! can't find logged in Port
+  }    
+  LunListLen = ucBuff[3];
+  LunListLen += ucBuff[2]>>8;
+
+  if( !LunListLen )  // failed
+  {
+    // generically speaking, a soft error means we should retry...
+    if( (Cmnd->result >> 16) == DID_SOFT_ERROR )
+    {
+      if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+	        (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+      {
+        TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs;
+      // did we fail because of "check condition, device reset?"
+      // e.g. the device was reset (i.e., at every power up)
+      // retry the Report Luns
+      
+      // who are we sending it to?
+      // we know this because we have a copy of the command
+      // frame from the original Report Lun command -
+      // switch the d_id/s_id fields, because the Exchange Build
+      // context is "reply to source".
+      
+        fchs->s_id = fchs->d_id; // (temporarily re-use the struct)
+        cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+      }
+    }
+    else  // probably, the device doesn't support Report Luns
+      pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0;  
+  }
+  else  // we have LUN info - check VSA mode
+  {
+    // for now, assume all LUNs will have same addr mode
+    // for VSA, payload byte 8 will be 0x40; otherwise, 0
+    pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8];  
+      
+    // Since we got a Report Luns answer, set lun masking flag
+    pLoggedInPort->ScsiNexus.LunMasking = 1;
+
+    if( LunListLen > 8*CPQFCTS_MAX_LUN)   // We expect CPQFCTS_MAX_LUN max
+      LunListLen = 8*CPQFCTS_MAX_LUN;
+
+/*   
+    printk("Device WWN %08X%08X Reports Luns @: ", 
+          (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), 
+          (ULONG)(pLoggedInPort->u.liWWN>>32));
+	    
+    for( i=8; i<LunListLen+8; i+=8)
+    {  
+      printk("%02X%02X ", ucBuff[i], ucBuff[i+1] );
+    }
+    printk("\n");
+*/
+    
+    // Since the device was kind enough to tell us where the
+    // LUNs are, lets ensure they are contiguous for Linux's
+    // SCSI driver scan, which expects them to start at 0.
+    // Since Linux only supports 8 LUNs, only copy the first
+    // eight from the report luns command
+
+    // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report
+    // LUNs 4001, 4004, etc., because other LUNs are masked from
+    // this HBA (owned by someone else).  We'll make those appear as
+    // LUN 0, 1... to Linux
+    {
+      int j;
+      int AppendLunList = 0;
+      // Walk through the LUN list.  The 'j' array number is
+      // Linux's lun #, while the value of .lun[j] is the target's
+      // lun #.
+      // Once we build a LUN list, it's possible for a known device 
+      // to go offline while volumes (LUNs) are added.  Later,
+      // the device will do another PLOGI ... Report Luns command,
+      // and we must not alter the existing Linux Lun map.
+      // (This will be very rare).
+      for( j=0; j < CPQFCTS_MAX_LUN; j++)
+      {
+        if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF )
+	{
+	  AppendLunList = 1;
+	  break;
+	}
+      }
+      if( AppendLunList )
+      {
+	int k;
+        int FreeLunIndex;
+//        printk("cpqfcTS: AppendLunList\n");
+
+	// If we get a new Report Luns, we cannot change
+	// any existing LUN mapping! (Only additive entry)
+	// For all LUNs in ReportLun list
+	// if RL lun != ScsiNexus lun
+	//   if RL lun present in ScsiNexus lun[], continue
+	//   else find ScsiNexus lun[]==FF and add, continue
+	
+        for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+	{
+          if( pLoggedInPort->ScsiNexus.lun[j] != ucBuff[i+1] )
+	  {
+	    // something changed from the last Report Luns
+	    printk(" cpqfcTS: Report Lun change!\n");
+	    for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN; 
+                 k < CPQFCTS_MAX_LUN; k++)
+            {
+	      if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF)
+	      {
+		FreeLunIndex = k;
+		break;
+	      }
+              if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] )
+		break; // we already masked this lun
+	    }
+	    if( k >= CPQFCTS_MAX_LUN )
+	    {
+	      printk(" no room for new LUN %d\n", ucBuff[i+1]);
+	    }
+	    else if( k == FreeLunIndex )  // need to add LUN
+	    {
+	      pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1];
+//	      printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]);
+	      
+	    }
+	    else
+	    {
+	      // lun already known
+	    }
+	    break;
+	  }
+	}
+	// print out the new list...
+	for( j=0; j< CPQFCTS_MAX_LUN; j++)
+	{
+	  if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF)
+	    break; // done
+//	  printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+	}
+      }
+      else
+      {
+//	  printk("Linux SCSI LUNs[] -> Device LUNs: ");
+	// first time - this is easy
+        for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+	{
+          pLoggedInPort->ScsiNexus.lun[j] = ucBuff[i+1];
+//	  printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+	}
+//	printk("\n");
+      }
+    }
+  }
+
+Done:  
+}
+
+// After successfully getting a "Process Login" (PRLI) from an
+// FC port, we want to Discover the LUNs so that we know the
+// addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral
+// Unit Device), and whether SSP (Selective Storage Presentation or
+// Lun Masking) has made the LUN numbers non-zero based or 
+// non-contiguous.  To remain backward compatible with the SCSI-2
+// driver model, which expects a contiguous LUNs starting at 0,
+// will use the ReportLuns info to map from "device" to "Linux" 
+// LUNs.
+static void IssueReportLunsCommand( 
+              CPQFCHBA* cpqfcHBAdata, 
+	      TachFCHDR_GCMND* fchs)
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  PFC_LOGGEDIN_PORT pLoggedInPort; 
+  Scsi_Cmnd *Cmnd;
+  LONG x_ID;
+  ULONG ulStatus;
+  UCHAR *ucBuff;
+
+
+  if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+  {
+    printk("Discard Q'd ReportLun command\n");
+    goto Done;
+  }
+
+  // find the device (from port_id) we're talking to
+  pLoggedInPort = fcFindLoggedInPort( fcChip,
+        NULL,     // DON'T search Scsi Nexus 
+  	fchs->s_id & 0xFFFFFF,        
+	NULL,     // DON'T search linked list for FC WWN
+        NULL);    // DON'T care about end of list
+  if( pLoggedInPort ) // we'd BETTER find it!
+  {
+
+
+    if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+      goto Done;  // forget it - FC device not a "target"
+
+    // now use the port's Scsi Command buffer for the 
+    // Report Luns Command
+ 
+    Cmnd = &pLoggedInPort->ScsiCmnd; 
+    ucBuff = pLoggedInPort->ReportLunsPayload;
+    
+    memset( Cmnd, 0, sizeof(Scsi_Cmnd));
+    memset( ucBuff, 0, REPORT_LUNS_PL);
+    
+    Cmnd->scsi_done = ScsiReportLunsDone;
+    Cmnd->host = cpqfcHBAdata->HostAdapter;
+
+    Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload; 
+    Cmnd->request_bufflen = REPORT_LUNS_PL; 
+	    
+    Cmnd->cmnd[0] = 0xA0;
+    Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8;
+    Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL;
+    Cmnd->cmd_len = 12;
+
+    Cmnd->channel = pLoggedInPort->ScsiNexus.channel;
+    Cmnd->target = pLoggedInPort->ScsiNexus.target;
+    
+	    
+    ulStatus = cpqfcTSBuildExchange(
+      cpqfcHBAdata,
+      SCSI_IRE, 
+      fchs,
+      Cmnd,         // buffer for Report Lun data
+      &x_ID );// fcController->fcExchanges index, -1 if failed
+
+    if( !ulStatus ) // Exchange setup?
+    {
+      ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+      if( !ulStatus )
+      {
+        // submitted to Tach's Outbound Que (ERQ PI incremented)
+        // waited for completion for ELS type (Login frames issued
+        // synchronously)
+      }
+      else
+        // check reason for Exchange not being started - we might
+        // want to Queue and start later, or fail with error
+      {
+  
+      }
+    }
+
+    else   // Xchange setup failed...
+      printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+  }
+  else     // like, we just got a PRLI ACC, and now the port is gone?
+  {
+    printk(" can't send ReportLuns - no login for port_id %Xh\n",
+	    fchs->s_id & 0xFFFFFF);
+  }
+
+
+
+Done:
+
+}
+
+
+
+
+
+
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata)
+{
+  int i;
+  for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--)
+  {
+    if( cpqfcHBAdata->BoardLockCmnd[i] != NULL )
+    {
+      Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i];
+      cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+      Cmnd->result = (DID_SOFT_ERROR << 16);  // ask for retry
+//      printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n",
+//        i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+      if( Cmnd->scsi_done != NULL)
+        (*Cmnd->scsi_done)(Cmnd);
+    }
+  }
+}
+
+
+
+
+
+
+// runs every 1 second for FC exchange timeouts and implicit FC device logouts
+
+void cpqfcTSheartbeat( unsigned long ptr )
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; 
+  ULONG i;
+  unsigned long flags;
+  DECLARE_MUTEX_LOCKED(BoardLock);
+  
+  PCI_TRACE( 0xA8)
+
+  if( cpqfcHBAdata->BoardLock) // Worker Task Running?
+    goto Skip;
+
+  spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+
+  PCI_TRACE( 0xA8)
+
+
+  cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing
+  
+  // release the IO lock (and re-enable interrupts)
+  spin_unlock_irqrestore( &io_request_lock, flags);
+  
+  // Ensure no contention from  _quecommand or Worker process 
+  CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+  
+  PCI_TRACE( 0xA8)
+  
+
+  disable_irq( cpqfcHBAdata->HostAdapter->irq);  // our IRQ
+
+  // Complete the "bad target" commands (normally only used during
+  // initialization, since we aren't supposed to call "scsi_done"
+  // inside the queuecommand() function).
+
+  for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+  {
+    if( cpqfcHBAdata->BadTargetCmnd[i] )
+    {
+      Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i];
+      cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+      Cmnd->result = (DID_BAD_TARGET << 16);
+      if( Cmnd->scsi_done != NULL)
+        (*Cmnd->scsi_done)(Cmnd);
+    }
+    else
+      break;
+  }
+
+  
+  // logged in ports -- re-login check (ports required to verify login with
+  // PDISC after LIP within 2 secs)
+
+  // prevent contention
+  while( pLoggedInPort ) // for all ports which are expecting
+                         // PDISC after the next LIP, check to see if
+                         // time is up!
+  {
+      // Important: we only detect "timeout" condition on TRANSITION
+      // from non-zero to zero
+    if( pLoggedInPort->LOGO_timer )  // time-out "armed"?
+    {
+      if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0?
+      {
+          // LOGOUT time!  Per PLDA, PDISC hasn't complete in 2 secs, so
+          // issue LOGO request and destroy all I/O with other FC port(s).
+        
+/*          
+        printk(" ~cpqfcTS heartbeat: LOGOut!~ ");
+        printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n", 
+        pLoggedInPort->ScsiNexus.channel, 
+        pLoggedInPort->ScsiNexus.target, 
+	pLoggedInPort->port_id,
+          (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), 
+          (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+*/
+        cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+
+      }
+      // else simply decremented - maybe next time...
+    }
+    pLoggedInPort = pLoggedInPort->pNextPort;
+  }
+
+
+
+  
+  
+  // ************  FC EXCHANGE TIMEOUT CHECK **************
+  
+  for( i=0; i< TACH_MAX_XID; i++)
+  {
+    if( Exchanges->fcExchange[i].type )  // exchange defined?
+    {
+
+      if( !Exchanges->fcExchange[i].timeOut ) // time expired
+      {
+        // Set Exchange timeout status
+        Exchanges->fcExchange[i].status |= FC2_TIMEOUT;
+
+        if( i >= TACH_SEST_LEN ) // Link Service Exchange
+        {
+          cpqfcTSCompleteExchange( fcChip, i);  // Don't "abort" LinkService
+        }
+        
+        else  // SEST Exchange TO -- may post ABTS to Worker Thread Que
+        {
+	  // (Make sure we don't keep timing it out; let other functions
+	  // complete it or set the timeOut as needed)
+	  Exchanges->fcExchange[i].timeOut = 30000; // seconds default
+
+          if( Exchanges->fcExchange[i].type 
+                  & 
+              (BLS_ABTS | BLS_ABTS_ACC )  )
+          {
+	    // For BLS_ABTS*, an upper level might still have
+	    // an outstanding command waiting for low-level completion.
+	    // Also, in the case of a WRITE, we MUST get confirmation
+	    // of either ABTS ACC or RJT before re-using the Exchange.
+	    // It's possible that the RAID cache algorithm can hang
+	    // if we fail to complete a WRITE to a LBA, when a READ
+	    // comes later to that same LBA.  Therefore, we must
+	    // ensure that the target verifies receipt of ABTS for
+	    // the exchange
+	   
+	    printk("~TO Q'd ABTS (x_ID %Xh)~ ", i); 
+//            TriggerHBA( fcChip->Registers.ReMapMemBase);
+
+	    // On timeout of a ABTS exchange, check to
+	    // see if the FC device has a current valid login.
+	    // If so, restart it.
+	    pLoggedInPort = fcFindLoggedInPort( fcChip,
+              Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus
+              0,        // DON'T search linked list for FC port id
+	      NULL,     // DON'T search linked list for FC WWN
+              NULL);    // DON'T care about end of list
+
+	    // device exists?
+	    if( pLoggedInPort ) // device exists?
+	    {
+	      if( pLoggedInPort->prli ) // logged in for FCP-SCSI?
+	      {
+		// attempt to restart the ABTS
+		printk(" ~restarting ABTS~ ");
+                cpqfcTSStartExchange( cpqfcHBAdata, i );
+
+	      }
+	    }
+          }
+	  else  // not an ABTS
+	  { 
+           
+            // We expect the WorkerThread to change the xchng type to
+            // abort and set appropriate timeout.
+            cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out
+	  }
+        }
+      }
+      else  // time not expired...
+      {
+        // decrement timeout: 1 or more seconds left
+        --Exchanges->fcExchange[i].timeOut;
+      }
+    }
+  }
+
+
+  enable_irq( cpqfcHBAdata->HostAdapter->irq);
+ 
+
+  CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+  cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued
+
+  // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+  CompleteBoardLockCmnd( cpqfcHBAdata);
+ 
+
+  // restart the timer to run again (1 sec later)
+Skip:
+  mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ);
+  
+  PCI_TRACEO( i, 0xA8)
+  return;
+}
+
+
+// put valid FC-AL physical address in spec order
+static const UCHAR valid_al_pa[]={
+    0xef, 0xe8, 0xe4, 0xe2, 
+    0xe1, 0xE0, 0xDC, 0xDA, 
+    0xD9, 0xD6, 0xD5, 0xD4, 
+    0xD3, 0xD2, 0xD1, 0xCe, 
+    0xCd, 0xCc, 0xCb, 0xCa, 
+    0xC9, 0xC7, 0xC6, 0xC5, 
+    0xC3, 0xBc, 0xBa, 0xB9,
+    0xB6, 0xB5, 0xB4, 0xB3, 
+    0xB2, 0xB1, 0xae, 0xad,
+    0xAc, 0xAb, 0xAa, 0xA9, 
+
+    0xA7, 0xA6, 0xA5, 0xA3, 
+    0x9f, 0x9e, 0x9d, 0x9b, 
+    0x98, 0x97, 0x90, 0x8f, 
+    0x88, 0x84, 0x82, 0x81, 
+    0x80, 0x7c, 0x7a, 0x79, 
+    0x76, 0x75, 0x74, 0x73, 
+    0x72, 0x71, 0x6e, 0x6d, 
+    0x6c, 0x6b, 0x6a, 0x69, 
+    0x67, 0x66, 0x65, 0x63, 
+    0x5c, 0x5a, 0x59, 0x56, 
+    
+    0x55, 0x54, 0x53, 0x52, 
+    0x51, 0x4e, 0x4d, 0x4c, 
+    0x4b, 0x4a, 0x49, 0x47, 
+    0x46, 0x45, 0x43, 0x3c,
+    0x3a, 0x39, 0x36, 0x35, 
+    0x34, 0x33, 0x32, 0x31, 
+    0x2e, 0x2d, 0x2c, 0x2b, 
+    0x2a, 0x29, 0x27, 0x26, 
+    0x25, 0x23, 0x1f, 0x1E,
+    0x1d, 0x1b, 0x18, 0x17, 
+
+    0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case
+
+const int number_of_al_pa = (sizeof(valid_al_pa) );
+
+
+
+// this function looks up an al_pa from the table of valid al_pa's
+// we decrement from the last decimal loop ID, because soft al_pa
+// (our typical case) are assigned with highest priority (and high al_pa)
+// first.  See "In-Depth FC-AL", R. Kembel pg. 38
+// INPUTS:
+//   al_pa - 24 bit port identifier (8 bit al_pa on private loop)
+// RETURN:
+//  Loop ID - serves are index to array of logged in ports
+//  -1      - invalid al_pa (not all 8 bit values are legal)
+
+#if (0)
+static int GetLoopID( ULONG al_pa )
+{
+  int i;
+
+  for( i = number_of_al_pa -1; i >= 0; i--)  // dec.
+  {
+    if( valid_al_pa[i] == (UCHAR)al_pa )  // take lowest 8 bits
+      return i;  // success - found valid al_pa; return decimal LoopID
+  }
+  return -1; // failed - not found
+}
+#endif
+
+
+// Search the singly (forward) linked list "fcPorts" looking for 
+// either the SCSI target (if != -1), port_id (if not NULL), 
+// or WWN (if not null), in that specific order.
+// If we find a SCSI nexus (from Cmnd arg), set the SCp.phase
+// field according to VSA or PDU
+// RETURNS:
+//   Ptr to logged in port struct if found
+//     (NULL if not found)
+//   pLastLoggedInPort - ptr to last struct (for adding new ones)
+// 
+PFC_LOGGEDIN_PORT  fcFindLoggedInPort( 
+  PTACHYON fcChip, 
+  Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun)
+  ULONG port_id,   // search linked list for al_pa, or
+  UCHAR wwn[8],    // search linked list for WWN, or...
+  PFC_LOGGEDIN_PORT *pLastLoggedInPort )
+             
+{
+  PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; 
+  BOOLEAN target_id_valid=FALSE;
+  BOOLEAN port_id_valid=FALSE;
+  BOOLEAN wwn_valid=FALSE;
+  int i;
+
+
+  if( Cmnd != NULL )
+    target_id_valid = TRUE;
+  
+  else if( port_id ) // note! 24-bit NULL address is illegal
+    port_id_valid = TRUE;
+
+  else
+  {
+    for( i=0; i<8; i++)  // valid WWN passed?  NULL WWN invalid
+    {
+      if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN)
+      {
+        if( wwn[i] != 0 )
+          wwn_valid = TRUE;  // any non-zero byte makes (presumably) valid
+      }
+    }
+  }
+                // check other options ...
+
+
+  // In case multiple search options are given, we use a priority
+  // scheme:
+  // While valid pLoggedIn Ptr
+  //   If port_id is valid
+  //     if port_id matches, return Ptr
+  //   If wwn is valid
+  //     if wwn matches, return Ptr
+  //   Next Ptr in list
+  //
+  // Return NULL (not found)
+ 
+      
+  while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid)
+  {
+    if( pLastLoggedInPort ) // caller's pointer valid?
+      *pLastLoggedInPort = pLoggedInPort;  // end of linked list
+    
+    if( target_id_valid )
+    {
+      // check Linux Scsi Cmnd for channel/target Nexus match
+      // (all luns are accessed through matching "pLoggedInPort")
+      if( (pLoggedInPort->ScsiNexus.target == Cmnd->target)
+                &&
+          (pLoggedInPort->ScsiNexus.channel == Cmnd->channel))
+      {
+        // For "passthru" modes, the IOCTL caller is responsible
+	// for setting the FCP-LUN addressing
+	if( !Cmnd->SCp.sent_command ) // NOT passthru?
+	{
+	
+          // set the FCP-LUN addressing type
+          Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;	
+
+          // set the Device Type we got from the snooped INQUIRY string
+	  Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType;
+
+	  // handle LUN masking; if not "default" (illegal) lun value,
+	  // the use it.  These lun values are set by a successful
+	  // Report Luns command
+          if( pLoggedInPort->ScsiNexus.LunMasking == 1) 
+	  {
+            // we KNOW all the valid LUNs... 0xFF is invalid!
+            Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun];
+	  }
+	  else
+	    Cmnd->SCp.have_data_in = Cmnd->lun; // Linux & target luns match
+	}
+	break; // found it!
+      }
+    }
+    
+    if( port_id_valid ) // look for alpa first
+    {
+      if( pLoggedInPort->port_id == port_id )
+        break;  // found it!
+    }
+    if( wwn_valid ) // look for wwn second
+    {
+
+      if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8))
+      {  
+                 // all 8 bytes of WWN match
+        break;   // found it!
+      }
+    }
+                
+    pLoggedInPort = pLoggedInPort->pNextPort; // try next port
+  }
+
+  return pLoggedInPort;
+}
+
+
+
+
+// 
+// We need to examine the SEST table and re-validate
+// any open Exchanges for this LoggedInPort
+// To make Tachyon pay attention, Freeze FCP assists,
+// set VAL bits, Unfreeze FCP assists
+static void RevalidateSEST( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG x_ID;
+  BOOLEAN TachFroze = FALSE;
+  
+  
+  // re-validate any SEST exchanges that are permitted
+  // to survive the link down (e.g., good PDISC performed)
+  for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+  {
+
+    // If the SEST entry port_id matches the pLoggedInPort,
+    // we need to re-validate
+    if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE)
+         || 
+	(Exchanges->fcExchange[ x_ID].type == SCSI_IWE))
+    {
+		     
+      if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF)  // (24-bit port ID)
+            == pLoggedInPort->port_id) 
+      {
+//	printk(" re-val xID %Xh ", x_ID);
+        if( !TachFroze )  // freeze if not already frozen
+          TachFroze |= FreezeTach( cpqfcHBAdata);
+        fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit
+      }
+    } 
+  }
+
+  if( TachFroze) 
+  { 
+    fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
+  }
+} 
+
+
+// Complete an Linux Cmnds that we Queued because
+// our FC link was down (cause immediate retry)
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, 
+		        PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+//  Scsi_Device *sdev = HostAdapter->host_queue;
+  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+  Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0];
+  Scsi_Cmnd *Cmnd;
+  int indx;
+
+ 
+  
+  // if the device was previously "blocked", make sure
+  // we unblock it so Linux SCSI will resume
+
+  pLoggedInPort->device_blocked = FALSE; // clear our flag
+
+  // check the Link Down command ptr buffer;
+  // we can complete now causing immediate retry
+  for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++)
+  {
+    if( *SCptr != NULL ) // scsi command to complete?
+    {
+#ifdef DUMMYCMND_DBG
+      printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx);
+#endif
+      Cmnd = *SCptr;
+
+
+      // Are there any Q'd commands for this target?
+      if( (Cmnd->target == pLoggedInPort->ScsiNexus.target)
+	       &&
+          (Cmnd->channel == pLoggedInPort->ScsiNexus.channel) )
+      {
+        Cmnd->result = (DID_SOFT_ERROR <<16); // force retry
+        if( Cmnd->scsi_done != NULL)
+          (*Cmnd->scsi_done)(Cmnd);
+        else
+          printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
+		  pLoggedInPort->port_id);
+        *SCptr = NULL;  // free this slot for next use
+      }
+    }
+  }
+}
+
+  
+//#define WWN_DBG 1
+
+static void SetLoginFields(
+  PFC_LOGGEDIN_PORT pLoggedInPort,
+  TachFCHDR_GCMND* fchs,
+  BOOLEAN PDisc,
+  BOOLEAN Originator)
+{
+  LOGIN_PAYLOAD logi;       // FC-PH Port Login
+  PRLI_REQUEST prli;  // copy for BIG ENDIAN switch
+  int i;
+#ifdef WWN_DBG
+  ULONG ulBuff;
+#endif
+
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+  pLoggedInPort->Originator = Originator;
+  pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF;
+  
+  switch( fchs->pl[0] & 0xffff )
+  {
+  case 0x00000002:  //  PLOGI or PDISC ACCept?
+    if( PDisc )     // PDISC accept
+      goto PDISC_case;
+
+  case 0x00000003:  //  ELS_PLOGI or ELS_PLOGI_ACC
+
+  // Login BB_credit typically 0 for Tachyons
+    pLoggedInPort->BB_credit = logi.cmn_services.bb_credit;
+
+    // e.g. 128, 256, 1024, 2048 per FC-PH spec
+    // We have to use this when setting up SEST Writes,
+    // since that determines frame size we send.
+    pLoggedInPort->rx_data_size = logi.class3.rx_data_size;
+    pLoggedInPort->plogi = TRUE;
+    pLoggedInPort->pdisc = FALSE;
+    pLoggedInPort->prli = FALSE;    // ELS_PLOGI resets
+    pLoggedInPort->flogi = FALSE;   // ELS_PLOGI resets
+    pLoggedInPort->logo = FALSE;    // ELS_PLOGI resets
+    pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets
+    pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets
+
+    // was this PLOGI to a Fabric?
+    if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address
+      pLoggedInPort->flogi = TRUE;
+
+
+    for( i=0; i<8; i++)   // copy the LOGIN port's WWN
+      pLoggedInPort->u.ucWWN[i] = logi.port_name[i];  
+
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PLOGI port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort);
+#endif
+    break;
+
+
+
+  
+  case 0x00000005:  //  ELS_LOGO (logout)
+
+
+    pLoggedInPort->plogi = FALSE;
+    pLoggedInPort->pdisc = FALSE;
+    pLoggedInPort->prli = FALSE;   // ELS_PLOGI resets
+    pLoggedInPort->flogi = FALSE;  // ELS_PLOGI resets
+    pLoggedInPort->logo = TRUE;    // ELS_PLOGI resets
+    pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets
+    pLoggedInPort->LOGO_timer = 0;
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("LOGO port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh\n", ulBuff);
+#endif
+    break;
+
+
+
+PDISC_case:
+  case 0x00000050: //  ELS_PDISC or ELS_PDISC_ACC
+    pLoggedInPort->LOGO_timer = 0;  // stop the time-out
+      
+    pLoggedInPort->prli = TRUE;     // ready to accept FCP-SCSI I/O
+    
+
+      
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PDISC port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+    printk("%08Xh\n", ulBuff);
+#endif
+
+
+    
+    break;
+
+
+    
+  case  0x1020L: //  PRLI?
+  case  0x1002L: //  PRLI ACCept?
+    BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+    pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags
+    pLoggedInPort->prli = TRUE;  // PLOGI resets, PDISC doesn't
+
+    pLoggedInPort->pdisc = TRUE;  // expect to send (or receive) PDISC
+                                  // next time
+    pLoggedInPort->LOGO_timer = 0;  // will be set next LinkDown
+#ifdef WWN_DBG
+    ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+    if( pLoggedInPort->Originator)
+      printk("o");
+    else
+      printk("r");
+    printk("PRLI port_id %Xh, WWN %08X",
+      pLoggedInPort->port_id, ulBuff);
+
+    ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+      printk("%08Xh\n", ulBuff);
+#endif
+
+    break;
+
+  }
+
+  return;
+}
+
+
+
+
+
+
+static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload)
+{
+  LOGIN_PAYLOAD *plogi;  // FC-PH Port Login
+  LOGIN_PAYLOAD PlogiPayload;   // copy for BIG ENDIAN switch
+  PRLI_REQUEST  *prli;          // FCP-SCSI Process Login
+  PRLI_REQUEST  PrliPayload;    // copy for BIG ENDIAN switch
+  LOGOUT_PAYLOAD  *logo;
+  LOGOUT_PAYLOAD  LogoutPayload;
+//  PRLO_REQUEST  *prlo;
+//  PRLO_REQUEST  PrloPayload;
+  REJECT_MESSAGE rjt, *prjt;
+
+  memset( &PlogiPayload, 0, sizeof( PlogiPayload));
+  plogi = &PlogiPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+
+
+  switch( type )  // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ...
+  {
+    case ELS_FDISC:
+    case ELS_FLOGI:
+    case ELS_PLOGI_ACC:   // FC-PH PORT Login Accept
+    case ELS_PLOGI:   // FC-PH PORT Login
+    case ELS_PDISC:   // FC-PH2 Port Discovery - same payload as ELS_PLOGI
+      plogi->login_cmd = LS_PLOGI;
+      if( type == ELS_PDISC)
+        plogi->login_cmd = LS_PDISC;
+      else if( type == ELS_PLOGI_ACC )
+        plogi->login_cmd = LS_ACC;
+
+      plogi->cmn_services.bb_credit = 0x00;
+      plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver;
+      plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver;
+      plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE;
+      plogi->cmn_services.common_features = CONTINUOSLY_INCREASING |
+              RANDOM_RELATIVE_OFFSET;
+
+             // fill in with World Wide Name based Port Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, plogi->port_name, 0);
+             // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, plogi->node_name, 1);
+
+	// For Seagate Drives.
+	//
+      plogi->cmn_services.common_features |= 0x800;
+      plogi->cmn_services.rel_offset = 0xFE;
+      plogi->cmn_services.concurrent_seq = 1;
+      plogi->class1.service_options = 0x00;
+      plogi->class2.service_options = 0x00;
+      plogi->class3.service_options = CLASS_VALID;
+      plogi->class3.initiator_control = 0x00;
+      plogi->class3.rx_data_size = MAX_RX_PAYLOAD;
+      plogi->class3.recipient_control =
+             ERROR_DISCARD | ONE_CATEGORY_SEQUENCE;
+      plogi->class3.concurrent_sequences = 1;
+      plogi->class3.open_sequences = 1;
+      plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q';
+      plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q';
+      plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0';
+      plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0';
+
+
+      // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1)
+      if( (type == ELS_FLOGI) || (type == ELS_FDISC) )
+      {
+        if( type == ELS_FLOGI )
+  	  plogi->login_cmd = LS_FLOGI;  
+	else
+  	  plogi->login_cmd = LS_FDISC;  
+
+        plogi->cmn_services.lowest_ver = 0x20;
+        plogi->cmn_services.common_features = 0x0800;
+        plogi->cmn_services.rel_offset = 0;
+        plogi->cmn_services.concurrent_seq = 0;
+
+        plogi->class3.service_options = 0x8800;
+        plogi->class3.rx_data_size = 0;
+        plogi->class3.recipient_control = 0;
+        plogi->class3.concurrent_sequences = 0;
+        plogi->class3.open_sequences = 0;
+      }
+      
+              // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&PlogiPayload, payload,  sizeof(PlogiPayload));
+      break;
+
+    
+    case ELS_ACC:       // generic Extended Link Service ACCept	    
+      plogi->login_cmd = LS_ACC;
+              // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&PlogiPayload, payload,  4);
+      break;
+
+
+      
+    case ELS_SCR:    // Fabric State Change Registration
+    {
+      SCR_PL scr;     // state change registration
+
+      memset( &scr, 0, sizeof(scr));
+
+      scr.command = LS_SCR;  // 0x62000000
+                             // see FC-FLA, Rev 2.7, Table A.22 (pg 82)
+      scr.function = 3;      // 1 = Events detected by Fabric
+                             // 2 = N_Port detected registration
+                             // 3 = Full registration
+      
+      // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&scr, payload,  sizeof(SCR_PL));
+    }
+    
+    break;
+
+    
+    case FCS_NSR:    // Fabric Name Service Request
+    {
+      NSR_PL nsr;    // Name Server Req. payload
+
+      memset( &nsr, 0, sizeof(NSR_PL));
+
+                             // see Brocade Fabric Programming Guide,
+                             // Rev 1.3, pg 4-44
+      nsr.CT_Rev = 0x01000000;
+      nsr.FCS_Type = 0xFC020000;
+      nsr.Command_code = 0x01710000;
+      nsr.FCP = 8;
+     
+      // copy back to caller's buff, w/ BIG ENDIAN swap
+      BigEndianSwap( (UCHAR*)&nsr, payload,  sizeof(NSR_PL));
+    }
+    
+    break;
+
+
+
+    
+    case ELS_LOGO:   // FC-PH PORT LogOut
+      logo = &LogoutPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+      logo->cmd = LS_LOGO;
+                                // load the 3 UCHARs of the node name
+                                // (if private loop, upper two UCHARs 0)
+      logo->reserved = 0;
+
+      logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa);
+      logo->n_port_identifier[1] =
+                     (UCHAR)(fcChip->Registers.my_al_pa>>8);
+      logo->n_port_identifier[2] =
+                     (UCHAR)(fcChip->Registers.my_al_pa>>16);
+             // fill in with World Wide Name based Port Name - 8 UCHARs
+             // get from Tach registers WWN hi & lo
+      LoadWWN( fcChip, logo->port_name, 0);
+
+      BigEndianSwap( (UCHAR*)&LogoutPayload,
+                     payload,  sizeof(LogoutPayload) );  // 16 UCHAR struct
+      break;
+
+
+    case ELS_LOGO_ACC:     // Logout Accept (FH-PH pg 149, table 74)
+      logo = &LogoutPayload;    // load into stack buffer,
+                                // then BIG-ENDIAN switch a copy to caller
+      logo->cmd = LS_ACC;
+      BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 );  // 4 UCHAR cmnd
+      break;
+      
+
+    case ELS_RJT:          // ELS_RJT link service reject (FH-PH pg 155)
+
+      prjt = (REJECT_MESSAGE*)payload;  // pick up passed data
+      rjt.command_code = ELS_RJT;
+                       // reverse fields, because of Swap that follows...
+      rjt.vendor = prjt->reserved; // vendor specific
+      rjt.explain = prjt->reason; //
+      rjt.reason = prjt->explain; //
+      rjt.reserved = prjt->vendor; //
+                       // BIG-ENDIAN switch a copy to caller
+      BigEndianSwap( (UCHAR*)&rjt, payload, 8 );  // 8 UCHAR cmnd
+      break;
+
+
+
+
+
+    case ELS_PRLI_ACC:  // Process Login ACCept
+    case ELS_PRLI:  // Process Login
+    case ELS_PRLO:  // Process Logout
+      memset( &PrliPayload, 0, sizeof( PrliPayload));
+      prli = &PrliPayload;      // load into stack buffer,
+
+      if( type == ELS_PRLI )
+        prli->cmd = 0x20;  // Login
+      else if( type == ELS_PRLO )
+        prli->cmd = 0x21;  // Logout
+      else if( type == ELS_PRLI_ACC )
+      {
+        prli->cmd = 0x02;  // Login ACCept
+        prli->valid = REQUEST_EXECUTED;
+      }
+
+
+      prli->valid |= SCSI_FCP | ESTABLISH_PAIR;
+      prli->fcp_info = READ_XFER_RDY;
+      prli->page_length = 0x10;
+      prli->payload_length = 20;
+                                // Can be initiator AND target
+
+      if( fcChip->Options.initiator )
+        prli->fcp_info |= INITIATOR_FUNCTION;
+      if( fcChip->Options.target )
+        prli->fcp_info |= TARGET_FUNCTION;
+
+      BigEndianSwap( (UCHAR*)&PrliPayload, payload,  prli->payload_length);
+      break;
+
+
+
+    default:  // no can do - programming error
+      printk(" BuildLinkServicePayload unknown!\n");
+      break;
+  }
+}
+
+// loads 8 UCHARs for PORT name or NODE name base on
+// controller's WWN.
+void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type)
+{
+  UCHAR* bPtr, i;
+
+  switch( type )
+  {
+    case 0:  // Port_Name
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+      for( i =0; i<4; i++)
+        dest[i] = *bPtr++;
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+      for( i =4; i<8; i++)
+        dest[i] = *bPtr++;
+      break;
+    case 1:  // Node/Fabric _Name
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+      for( i =0; i<4; i++)
+        dest[i] = *bPtr++;
+      bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+      for( i =4; i<8; i++)
+        dest[i] = *bPtr++;
+      break;
+  }
+  
+}
+
+
+
+// We check the Port Login payload for required values.  Note that
+// ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload.
+
+
+int verify_PLOGI( PTACHYON fcChip,
+                  TachFCHDR_GCMND* fchs, 
+                  ULONG* reject_explain)
+{
+  LOGIN_PAYLOAD	login;
+
+                  // source, dest, len (should be mult. of 4)
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login,  sizeof(login));
+
+                            // check FC version
+                            // if other port's highest supported version
+                            // is less than our lowest, and 
+                            // if other port's lowest
+  if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver ||
+      login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver )
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+                            // Receive Data Field Size must be >=128
+                            // per FC-PH
+  if (login.cmn_services.bb_rx_size < 128)
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+                            // Only check Class 3 params
+  if( login.class3.service_options & CLASS_VALID)
+  {
+    if (login.class3.rx_data_size < 128)
+    {
+      *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP);
+      return LOGICAL_ERROR;
+    }
+    if( login.class3.initiator_control & XID_REQUIRED)
+    {
+      *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR);
+      return LOGICAL_ERROR;
+    }
+  }
+  return 0;   // success
+}
+
+
+
+
+int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain)
+{
+  PRLI_REQUEST prli;  // buffer for BIG ENDIAN
+
+                  // source, dest, len (should be mult. of 4)
+  BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli,  sizeof(prli));
+
+  if( prli.fcp_info == 0 )  // i.e., not target or initiator?
+  {
+    *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+    return LOGICAL_ERROR;
+  }
+
+  return 0;  // success
+}
+
+
+// SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN)
+// INPUTS:
+//   source   - ptr to LITTLE ENDIAN ULONGS
+//   cnt      - number of UCHARs to switch (should be mult. of ULONG)
+// OUTPUTS:
+//   dest     - ptr to BIG ENDIAN copy
+// RETURN:
+//   none
+//
+void BigEndianSwap( UCHAR *source, UCHAR *dest,  USHORT cnt)
+{
+  int i,j;
+
+  source+=3;   // start at MSB of 1st ULONG
+  for( j=0; j < cnt; j+=4, source+=4, dest+=4)  // every ULONG
+  {
+    for( i=0; i<4; i++)  // every UCHAR in ULONG
+          *(dest+i) = *(source-i);
+  }
+}
+
+
+
+
+// Build FC Exchanges............
+
+static void  buildFCPstatus( 
+  PTACHYON fcChip, 
+  ULONG ExchangeID);
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type );
+
+static ULONG build_SEST_sgList( 
+  ULONG *SESTalPairStart,
+  Scsi_Cmnd *Cmnd,
+  ULONG *sgPairs,
+  PSGPAGES sgPages  // link list of TL Ext. S/G pages from O/S Pool
+);
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd, 
+  UCHAR* payload, ULONG type, ULONG fcp_dl );
+
+
+/*
+                             IRB
+      ERQ           __________________
+  |          |   / | Req_A_SFS_Len    |        ____________________
+  |----------|  /  | Req_A_SFS_Addr   |------->|  Reserved         |
+  |   IRB    | /   | Req_A_D_ID       |        | SOF EOF TimeStamp |
+  |-----------/    | Req_A_SEST_Index |-+      | R_CTL |   D_ID    |
+  |   IRB    |     | Req_B...         | |      | CS_CTL|   S_ID    | 
+  |-----------\    |                  | |      | TYPE  |   F_CTL   |
+  |   IRB    | \   |                  | |      | SEQ_ID  | SEQ_CNT |
+  |-----------  \  |                  | +-->+--| OX_ID   | RX_ID   |
+  |          |   \ |__________________|     |  |       RO          |
+                                            |  | pl (payload/cmnd) |
+                                            |  |        .....      |
+                                            |  |___________________|
+                                            |
+                                            |
++-------------------------------------------+
+|
+|
+|                        e.g. IWE    
+|    SEST           __________________             for FCP_DATA
+| |          |   / |       | Hdr_Len  |        ____________________
+| |----------|  /  |  Hdr_Addr_Addr   |------->|  Reserved         |
+| |   [0]    | /   |Remote_ID| RSP_Len|        | SOF EOF TimeStamp |
+| |-----------/    |   RSP_Addr       |---+    | R_CTL |   D_ID    |
++->   [1]    |     |       | Buff_Off |   |    | CS_CTL|   S_ID    | 
+  |-----------\    |BuffIndex| Link   |   |    | TYPE  |   F_CTL   |
+  |   [2]    | \   | Rsvd  |   RX_ID  |   |    | SEQ_ID  | SEQ_CNT |
+  |-----------  \  |    Data_Len      |   |    | OX_ID   | RX_ID   |
+  |    ...   |   \ |     Exp_RO       |   |    |       RO          |
+  |----------|     |   Exp_Byte_Cnt   |   |    |___________________|
+  | SEST_LEN |  +--|    Len           |   |                                             
+  |__________|  |  |   Address        |   |                                              
+                |  |    ...           |   |         for FCP_RSP  
+                |  |__________________|   |    ____________________
+                |                         +----|  Reserved         |   
+                |                              | SOF EOF TimeStamp |
+                |                              | R_CTL |   D_ID    |
+                |                              | CS_CTL|   S_ID    | 
+                +--- local or extended         |     ....          |
+                     scatter/gather lists
+                     defining upper-layer
+                     data (e.g. from user's App)
+
+
+*/
+// All TachLite commands must start with a SFS (Single Frame Sequence)
+// command.  In the simplest case (a NOP Basic Link command),
+// only one frame header and ERQ entry is required.  The most complex
+// case is the SCSI assisted command, which requires an ERQ entry,
+// SEST entry, and several frame headers and data buffers all
+// logically linked together.
+// Inputs:
+//   cpqfcHBAdata  - controller struct
+//   type          - PLOGI, SCSI_IWE, etc.
+//   InFCHS        - Incoming Tachlite FCHS which prompted this exchange
+//                   (only s_id set if we are originating)
+//   Data          - PVOID to data struct consistent with "type"
+//   fcExchangeIndex - pointer to OX/RD ID value of built exchange
+// Return:
+//   fcExchangeIndex - OX/RD ID value if successful
+//   0    - success
+//  INVALID_ARGS    - NULL/ invalid passed args
+//  BAD_ALPA        - Bad source al_pa address
+//  LNKDWN_OSLS     - Link Down (according to this controller)
+//  OUTQUE_FULL     - Outbound Que full
+//  DRIVERQ_FULL    - controller's Exchange array full
+//  SEST_FULL       - SEST table full
+//
+// Remarks:
+// Psuedo code:
+// Check for NULL pointers / bad args
+// Build outgoing FCHS - the header/payload struct
+// Build IRB (for ERQ entry)
+// if SCSI command, build SEST entry (e.g. IWE, TRE,...)
+// return success
+
+//sbuildex
+ULONG cpqfcTSBuildExchange(
+  CPQFCHBA *cpqfcHBAdata,
+  ULONG type, // e.g. PLOGI
+  TachFCHDR_GCMND* InFCHS,  // incoming FCHS
+  void *Data,               // the CDB, scatter/gather, etc.  
+  LONG *fcExchangeIndex )   // points to allocated exchange, 
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG ulStatus = 0;  // assume OK
+  USHORT ox_ID, rx_ID=0xFFFF;
+  ULONG SfsLen=0L;
+  TachLiteIRB* pIRB;
+  IRBflags IRB_flags;
+  UCHAR *pIRB_flags = (UCHAR*)&IRB_flags;
+  TachFCHDR_GCMND* CMDfchs;
+  TachFCHDR* dataHDR;     // 32 byte HEADER ONLY FCP-DATA buffer
+  TachFCHDR_RSP* rspHDR;     // 32 byte header + RSP payload
+  Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data;   // Linux Scsi CDB, S/G, ...
+  TachLiteIWE* pIWE;
+  TachLiteIRE* pIRE;
+  TachLiteTWE* pTWE;
+  TachLiteTRE* pTRE;
+  ULONG fcp_dl;           // total byte length of DATA transfered
+  ULONG fl;               // frame length (FC frame size, 128, 256, 512, 1024)
+  ULONG sgPairs;          // number of valid scatter/gather pairs
+  int FCP_SCSI_command;
+  BA_ACC_PAYLOAD *ba_acc;
+  BA_RJT_PAYLOAD *ba_rjt;
+
+                          // check passed ARGS
+  if( !fcChip->ERQ )      // NULL ptr means uninitialized Tachlite chip
+    return INVALID_ARGS;
+
+
+  if( type == SCSI_IRE ||
+      type == SCSI_TRE ||
+      type == SCSI_IWE ||
+      type == SCSI_TWE)
+    FCP_SCSI_command = 1;
+
+  else
+    FCP_SCSI_command = 0;
+
+
+                     // for commands that pass payload data (e.g. SCSI write)
+                     // examine command struct - verify that the
+                     // length of s/g buffers is adequate for total payload
+                     // length (end of list is NULL address)
+
+  if( FCP_SCSI_command )
+  {
+    if( Data )     // must have data descriptor (S/G list -- at least
+                   // one address with at least 1 byte of data)
+    {
+      // something to do (later)?
+    }
+
+    else
+      return INVALID_ARGS;  // invalid DATA ptr
+  }
+
+    
+
+         // we can build an Exchange for later Queuing (on the TL chip)
+         // if an empty slot is available in the DevExt for this controller
+         // look for available Exchange slot...
+
+  if( type != FCP_RESPONSE &&
+      type != BLS_ABTS &&
+      type != BLS_ABTS_ACC )  // already have Exchange slot!
+    *fcExchangeIndex = FindFreeExchange( fcChip, type );
+
+  if( *fcExchangeIndex != -1 )   // Exchange is available?
+  {
+                     // assign tmp ptr (shorthand)
+    CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs; 
+
+
+    if( Cmnd != NULL ) // (necessary for ABTS cases)
+    {
+      Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi
+      Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort = 
+	fcFindLoggedInPort( fcChip,
+          Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus
+          0,        // DON'T search linked list for FC port id
+	  NULL,     // DON'T search linked list for FC WWN
+          NULL);    // DON'T care about end of list
+
+    }
+
+
+                     // Build the command frame header (& data) according
+                     // to command type
+
+                     // fields common for all SFS frame types
+    CMDfchs->reserved = 0L; // must clear
+    CMDfchs->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; LCr=0, no TS
+    
+             // get the destination port_id from incoming FCHS
+             // (initialized before calling if we're Originator)
+             // Frame goes to port it was from - the source_id
+    
+    CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later)
+    CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+    // now enter command-specific fields
+    switch( type )
+    {
+
+    case BLS_NOP:   // FC defined basic link service command NO-OP
+                // ensure unique X_IDs! (use tracking function)
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32L;        // add len to LSB (header only - no payload)
+
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->d_id |= 0x80000000L;  // R_CTL = 80 for NOP (Basic Link Ser.)
+      CMDfchs->f_ctl = 0x00310000L;  // xchng originator, 1st seq,....
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0xFFFF;        // RX_ID for now; OX_ID on start
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      CMDfchs->pl[0] = 0xaabbccddL;   // words 8-15 frame data payload (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds
+                                      // (NOP should complete ~instantly)
+      break;
+
+
+    
+    
+    case BLS_ABTS_ACC:  // Abort Sequence ACCept
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32 + 12;    // add len to LSB (header + 3 DWORD payload)
+
+      CMDfchs->d_id |= 0x84000000L;  // R_CTL = 84 for BASIC ACCept
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x00910000L;  // xchnge responder, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+                        // (Timeout in case of weird error)
+      
+      // now set the ACCept payload...
+      ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0];
+      memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD));
+      // Since PLDA requires (only) entire Exchange aborts, we don't need
+      // to worry about what the last sequence was.
+
+      // We expect that a "target" task is accepting the abort, so we
+      // can use the OX/RX ID pair 
+      ba_acc->ox_rx_id = CMDfchs->ox_rx_id;
+ 
+      // source, dest, #bytes
+      BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4);
+
+      ba_acc->low_seq_cnt = 0;
+      ba_acc->high_seq_cnt = 0xFFFF;
+
+
+      break;
+    
+
+    case BLS_ABTS_RJT:  // Abort Sequence ACCept
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32 + 12;    // add len to LSB (header + 3 DWORD payload)
+
+      CMDfchs->d_id |= 0x85000000L;  // R_CTL = 85 for BASIC ReJecT
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+                   // TYPE[31-24] 00 Basic Link Service
+      CMDfchs->f_ctl = 0x00910000L;  // xchnge responder, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+                        // (Timeout in case of weird error)
+      
+      CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender!
+      
+      // now set the ReJecT payload...
+      ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0];
+      memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD));
+
+      // We expect that a "target" task couldn't find the Exhange in the
+      // array of active exchanges, so we use a new LinkService X_ID.
+      // See Reject payload description in FC-PH (Rev 4.3), pg. 140
+      ba_rjt->reason_code = 0x09; // "unable to perform command request"
+      ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair
+
+
+      break;
+    
+    
+    
+    case BLS_ABTS:   // FC defined basic link service command ABTS 
+                     // Abort Sequence
+                     
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 32L;        // add len to LSB (header only - no payload)
+
+                   // TYPE[31-24] 00 Basic Link Service
+                   // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+      CMDfchs->d_id |= 0x81000000L;  // R_CTL = 81 for ABTS
+      CMDfchs->f_ctl = 0x00110000L;  // xchnge originator, last seq, xfer SI
+                   // CMDfchs->seq_id & count might be set from DataHdr?
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                        // (ABTS must timeout when responder is gone)
+      break;
+
+    
+    
+    case FCS_NSR:    // Fabric Name Service Request
+       Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                         // OX_ID, linked to Driver Transaction ID
+                         // (fix-up at Queing time)
+      CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+                                    // OX_ID set at ERQueing time
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload)
+
+      CMDfchs->d_id |= 0x02000000L;  // R_CTL = 02 for -
+                                   // Name Service Request: Unsolicited 
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x20210000L;
+                   // OX_ID will be fixed-up at Tachyon enqueing time
+      CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+   
+    
+    
+    
+    
+      break;
+    
+    
+    
+    
+    case ELS_PLOGI:  // FC-PH extended link service command Port Login
+      // (May, 2000)
+      // NOTE! This special case facilitates SANMark testing.  The SANMark
+      // test script for initialization-timeout.fcal.SANMark-1.fc
+      // "eats" the OPN() primitive without issuing an R_RDY, causing
+      // Tachyon to report LST (loop state timeout), which causes a
+      // LIP.  To avoid this, simply send out the frame (i.e. assuming a
+      // buffer credit of 1) without waiting for R_RDY.  Many FC devices
+      // (other than Tachyon) have been doing this for years.  We don't
+      // ever want to do this for non-Link Service frames unless the
+      // other device really did report non-zero login BB credit (i.e.
+      // in the PLOGI ACCept frame).
+//      CMDfchs->sof_eof |= 0x00000400L;  // LCr=1
+    
+    case ELS_FDISC:  // Fabric Discovery (Login)
+    case ELS_FLOGI:  // Fabric Login
+    case ELS_SCR:    // Fabric State Change Registration
+    case ELS_LOGO:   // FC-PH extended link service command Port Logout
+    case ELS_PDISC:  // FC-PH extended link service cmnd Port Discovery
+    case ELS_PRLI:   // FC-PH extended link service cmnd Process Login
+
+      Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+                         // OX_ID, linked to Driver Transaction ID
+                         // (fix-up at Queing time)
+      CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+                                    // OX_ID set at ERQueing time
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      if( type == ELS_LOGO )
+        SfsLen += (32L + 16L); //  add len (header & PLOGI payload)
+      else if( type == ELS_PRLI )
+        SfsLen += (32L + 20L); //  add len (header & PRLI payload)
+      else if( type == ELS_SCR )
+        SfsLen += (32L + sizeof(SCR_PL)); //  add len (header & SCR payload)
+      else
+        SfsLen += (32L + 116L); //  add len (header & PLOGI payload)
+
+      CMDfchs->d_id |= 0x22000000L;  // R_CTL = 22 for -
+                                   // Extended Link_Data: Unsolicited Control
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+      CMDfchs->f_ctl = 0x01210000L;
+                   // OX_ID will be fixed-up at Tachyon enqueing time
+      CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+      break;
+
+
+
+    case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+    case ELS_RJT:          // extended link service reject (add reason)
+    case ELS_ACC:      // ext. link service generic accept
+    case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+    case ELS_PRLI_ACC: // ext. link service process login accept
+
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done
+                // ensure unique X_IDs! (use tracking function)
+                // OX_ID from initiator cmd
+      ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16); 
+      rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (not SEST index)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      if( type == ELS_RJT )
+      {
+        SfsLen += (32L + 8L); //  add len (header + payload)
+
+        // ELS_RJT reason codes (utilize unused "reserved" field)
+        CMDfchs->pl[0] = 1;
+        CMDfchs->pl[1] = InFCHS->reserved;  
+          
+      }
+      else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC)  )
+        SfsLen += (32L + 4L); //  add len (header + payload)
+      else if( type == ELS_PLOGI_ACC )
+        SfsLen += (32L + 116L); //  add len (header + payload)
+      else if( type == ELS_PRLI_ACC )
+        SfsLen += (32L + 20L); //  add len (header + payload)
+
+      CMDfchs->d_id |= 0x23000000L;  // R_CTL = 23 for -
+                                   // Extended Link_Data: Control Reply
+                   // TYPE[31-24] 01 Extended Link Service
+                   // f_ctl[23:0] exchg responder, last seq, e_s, tsi
+      CMDfchs->f_ctl = 0x01990000L;
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0L;        // clear
+      CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits
+      CMDfchs->ox_rx_id <<= 16;      // shift them
+
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+      BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+      break;
+
+
+                         // Fibre Channel SCSI 'originator' sequences...
+                         // (originator means 'initiator' in FCP-SCSI)
+    case SCSI_IWE: // TachLite Initiator Write Entry
+    {
+      PFC_LOGGEDIN_PORT pLoggedInPort = 
+        Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort;
+
+      Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout
+                       
+      // first, build FCP_CMND
+      // unique X_ID fix-ups in StartExchange 
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS FCP-CMND (not SEST index)
+
+      // NOTE: unlike FC LinkService login frames, normal
+      // SCSI commands are sent without outgoing verification
+      IRB_flags.DCM = 1;    // Disable completion message for Cmnd frame
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 64L;        // add len to LSB (header & CMND payload)
+
+      CMDfchs->d_id |= (0x06000000L);  // R_CTL = 6 for command
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+                   //             valid RO
+      CMDfchs->f_ctl = 0x08210008L;
+      CMDfchs->seq_cnt = 0x0L;
+      CMDfchs->ox_rx_id = 0L;        // clear for now (-or- in later)
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-DATA header
+                   // (use buffer inside SEST object)
+      dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+      dataHDR->reserved = 0L; // must clear
+      dataHDR->sof_eof = 0x75002000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+      dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] xfer S.I.| valid RO
+      dataHDR->f_ctl = 0x08010008L;
+      dataHDR->seq_cnt = 0x02000000L;  // sequence ID: df_ctl : seqence count
+      dataHDR->ox_rx_id = 0L;        // clear; fix-up dataHDR fields later
+      dataHDR->ro = 0x0L;    // relative offset (n/a)
+
+                   // Now setup the SEST entry
+      pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE;
+      
+                   // fill out the IWE:
+
+                // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len
+      pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes
+      
+      
+      // from login parameters with other port, what's the largest frame
+      // we can send? 
+      if( pLoggedInPort == NULL) 
+      {
+	ulStatus = INVALID_ARGS;  // failed! give up
+	break;
+      }
+      if( pLoggedInPort->rx_data_size  >= 2048)
+        fl = 0x00020000;  // 2048 code (only support 1024!)
+      else if( pLoggedInPort->rx_data_size  >= 1024)
+        fl = 0x00020000;  // 1024 code
+      else if( pLoggedInPort->rx_data_size  >= 512)
+        fl = 0x00010000;  // 512 code
+      else
+	fl = 0;  // 128 bytes -- should never happen
+      
+      
+      pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase
+      pIWE->Hdr_Addr = virt_to_bus( dataHDR );
+      pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+      pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      
+      memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0, 
+        sizeof( FCP_STATUS_RESPONSE) );  // clear out previous status
+  
+      pIWE->RSP_Addr = virt_to_bus(
+                         &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pIWE->GLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+                             // Now that we know total data length in
+                             // the passed S/G buffer, set FCP CMND frame
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pIWE->Buff_Off = 0x78000000L; // extended data | (no offset)
+      else               // local data pointers (in SEST)
+        pIWE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+                              // ULONG 5
+      pIWE->Link = 0x0000ffffL;   // Buff_Index | Link
+
+      pIWE->RX_ID = 0x0L;     // DWord 6: RX_ID set by target XFER_RDY
+
+                                      // DWord 7
+      pIWE->Data_Len = 0L;    // TL enters rcv'd XFER_RDY BURST_LEN
+      pIWE->Exp_RO = 0L;      // DWord 8
+                              // DWord 9
+      pIWE->Exp_Byte_Cnt = fcp_dl;  // sum of gather buffers
+    }
+    break;
+
+
+
+
+
+    case SCSI_IRE: // TachLite Initiator Read Entry
+
+      if( Cmnd->timeout != 0)
+      {
+//	printk("Cmnd->timeout %d\n", Cmnd->timeout);
+	// per Linux Scsi
+        Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout;
+      }
+      else  // use our best guess, based on FC & device
+      {
+
+        if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY)	
+	{
+	  // turn off our timeouts (for now...)
+          Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF; 
+	}
+	else
+	{
+          Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+          Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req.
+	}
+      }
+
+  
+      // first, build FCP_CMND
+
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS FCP-CMND (not SEST index)
+                            // NOTE: unlike FC LinkService login frames,
+                            // normal SCSI commands are sent "open loop"
+      IRB_flags.DCM = 1;    // Disable completion message for Cmnd frame
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += 64L;        // add len to LSB (header & CMND payload)
+
+      CMDfchs->d_id |= (0x06000000L);  // R_CTL = 6 for command
+
+             // TYPE[31-24] 8 for FCP SCSI
+             // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+             //             valid RO
+      CMDfchs->f_ctl = 0x08210008L;
+      CMDfchs->seq_cnt = 0x0L;
+      // x_ID & data direction bit set later
+      CMDfchs->ox_rx_id = 0xFFFF;        // clear
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+
+
+                   // Now setup the SEST entry
+      pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE;
+
+      // fill out the IRE:
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP
+
+      pIRE->reserved = 0L;
+      pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+      pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+                            
+      pIRE->RSP_Addr = virt_to_bus(
+                              &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+      
+      
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pIRE->SLen1, 
+        Cmnd,       // SCSI command Data desc. with S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+	// It is permissible to have a ZERO LENGTH Read command.
+	// If there is the case, simply set fcp_dl (and Exp_Byte_Cnt)
+	// to 0 and continue.
+	if( Cmnd->request_bufflen == 0 )
+	{
+	  fcp_dl = 0; // no FC DATA frames expected
+
+	}
+	else
+	{
+          ulStatus = MEMPOOL_FAIL;
+          break;      // give up
+	}
+      }
+
+      // now that we know the S/G length, build CMND payload
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset
+      else
+        pIRE->Buff_Off = 0x80000000; // local data, no offset
+      
+      pIRE->Buff_Index = 0x0L;    // DWord 5: Buff_Index | Reserved
+
+      pIRE->Exp_RO  = 0x0L;       // DWord 6: Expected Rel. Offset
+
+      pIRE->Byte_Count = 0;  // DWord 7: filled in by TL on err
+      pIRE->reserved_ = 0;   // DWord 8: reserved
+                             // NOTE: 0 length READ is OK.
+      pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers
+      
+      break;
+
+
+
+
+                         // Fibre Channel SCSI 'responder' sequences...
+                         // (originator means 'target' in FCP-SCSI)
+    case SCSI_TWE: // TachLite Target Write Entry
+
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+                       // first, build FCP_CMND
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (XFER_RDY)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+      CMDfchs->d_id |= (0x05000000L);  // R_CTL = 5 for XFER_RDY
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg responder, 1st seq, xfer S.I.
+                //             valid RO
+      CMDfchs->f_ctl = 0x08810008L;
+      CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count
+                       // use originator (other port's) OX_ID
+      CMDfchs->ox_rx_id = InFCHS->ox_rx_id;     // we want upper 16 bits
+      CMDfchs->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-RSP header
+                   // (use buffer inside SEST object)
+
+      rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+      rspHDR->reserved = 0L; // must clear
+      rspHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+      rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] responder|last seq| xfer S.I.
+      rspHDR->f_ctl = 0x08910000L;
+      rspHDR->seq_cnt = 0x03000000;  // sequence ID
+      rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID
+      rspHDR->ro = 0x0L;    // relative offset (n/a)
+
+
+                   // Now setup the SEST entry
+                   
+      pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE;
+
+      // fill out the TWE:
+
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pTWE->Seq_Accum = 0xC4000000L;  // upper word flags
+      pTWE->reserved = 0L;
+      pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change)
+      pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pTWE->SLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+      // now that we know the S/G length, build CMND payload
+      build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset
+      else
+        pTWE->Buff_Off = 0x80000000; // local data, no offset
+      
+      pTWE->Buff_Index = 0;     // Buff_Index | Link
+      pTWE->Exp_RO = 0;
+      pTWE->Byte_Count = 0;  // filled in by TL on err
+      pTWE->reserved_ = 0;
+      pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers
+      
+      break;
+
+
+
+
+
+
+    case SCSI_TRE: // TachLite Target Read Entry
+
+      // It doesn't make much sense for us to "time-out" a READ,
+      // but we'll use it for design consistency and internal error recovery.
+      Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+      // I/O request block settings...
+      *pIRB_flags = 0;      // clear IRB flags
+                                  // check PRLI (process login) info
+                                  // to see if Initiator Requires XFER_RDY
+                                  // if not, don't send one!
+                                  // { PRLI check...}
+      IRB_flags.SFA = 0;    // don't send XFER_RDY - start data
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+
+      
+      // now, fill out FCP-DATA header
+                   // (use buffer inside SEST object)
+      dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+
+      dataHDR->reserved = 0L; // must clear
+      dataHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn no UAM; no CLS,noLCr,no TS
+      dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+      dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I.
+                   //             valid RO
+      dataHDR->f_ctl = 0x08810008L;
+      dataHDR->seq_cnt = 0x01000000;  // sequence ID (no XRDY)
+      dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits
+      dataHDR->ro = 0x0L;    // relative offset (n/a)
+
+                   // now, fill out FCP-RSP header
+                   // (use buffer inside SEST object)
+      rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+
+      rspHDR->reserved = 0L; // must clear
+      rspHDR->sof_eof = 0x75000000L;  // SOFi3:EOFn  no UAM; no CLS, noLCr, no TS
+      rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+      rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+                   // TYPE[31-24] 8 for FCP SCSI
+                   // f_ctl[23:0] responder|last seq| xfer S.I.
+      rspHDR->f_ctl = 0x08910000L;
+      rspHDR->seq_cnt = 0x02000000;  // sequence ID: df_ctl: sequence count
+
+      rspHDR->ro = 0x0L;    // relative offset (n/a)
+
+
+      // Now setup the SEST entry
+      pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE;
+
+
+      // VALid entry:Dir outbound:enable CM:enal INT:
+      pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes
+      pTRE->Hdr_Addr = virt_to_bus( dataHDR );
+      pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame)
+      pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+      pTRE->RSP_Addr = virt_to_bus( rspHDR );
+
+
+                   // Do we need local or extended gather list?
+                   // depends on size - we can handle 3 len/addr pairs
+                   // locally.
+
+      fcp_dl = build_SEST_sgList( 
+        &pTRE->GLen1, 
+        Cmnd,       // S/G list
+        &sgPairs,   // return # of pairs in S/G list (from "Data" descriptor)
+        &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+      
+      
+      if( !fcp_dl ) // error building S/G list?
+      {
+        ulStatus = MEMPOOL_FAIL;
+        break;      // give up
+      }
+
+      // no payload or command to build -- READ doesn't need XRDY
+
+      
+      if( sgPairs > 3 )  // need extended s/g list
+        pTRE->Buff_Off = 0x78000000L; // extended data | (no offset)
+      else               // local data pointers (in SEST)
+        pTRE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+                                            // ULONG 5
+      pTRE->Buff_Index = 0L;   // Buff_Index | reserved
+      pTRE->reserved = 0x0L;   // DWord 6
+
+                                     // DWord 7: NOTE: zero length will
+                                     // hang TachLite!
+      pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers
+
+      pTRE->reserved_ = 0L;     // DWord 8
+      pTRE->reserved__ = 0L;    // DWord 9
+
+      break;
+
+
+
+
+
+    
+
+    case FCP_RESPONSE: 
+                  // Target response frame: this sequence uses an OX/RX ID
+                  // pair from a completed SEST exchange.  We built most
+                  // of the response frame when we created the TWE/TRE.
+
+      *pIRB_flags = 0;      // clear IRB flags
+      IRB_flags.SFA = 1;    // send SFS (RSP)
+      SfsLen = *pIRB_flags;
+
+      SfsLen <<= 24;        // shift flags to MSB
+      SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload)
+      
+
+      Exchanges->fcExchange[ *fcExchangeIndex].type = 
+        FCP_RESPONSE; // change Exchange type to "response" phase
+
+      // take advantage of prior knowledge of OX/RX_ID pair from
+      // previous XFER outbound frame (still in fchs of exchange)
+      fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id = 
+        CMDfchs->ox_rx_id;
+
+      // Check the status of the DATA phase of the exchange so we can report
+      // status to the initiator
+      buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields
+
+      memcpy(
+        CMDfchs,  // re-use same XFER fchs for Response frame
+        &fcChip->SEST->RspHDR[ *fcExchangeIndex ],
+        sizeof( TachFCHDR_RSP ));
+      
+        
+      break;
+
+    default:
+      printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type);
+      break;
+
+    }
+
+    
+    
+    if( !ulStatus)  // no errors above?
+    {
+      // FCHS is built; now build IRB
+
+      // link the just built FCHS (the "command") to the IRB entry 
+      // for this Exchange.
+      pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB; 
+    
+                          // len & flags according to command type above
+      pIRB->Req_A_SFS_Len = SfsLen;  // includes IRB flags & len
+      pIRB->Req_A_SFS_Addr = virt_to_bus(CMDfchs); // TL needs physical addr
+                                                  // of frame to send
+      pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent!
+
+    // Exchange is complete except for "fix-up" fields to be set
+    // at Tachyon Queuing time:
+    //    IRB->Req_A_Trans_ID (OX_ID/ RX_ID):  
+    //        for SEST entry, lower bits correspond to actual FC Exchange ID
+    //    fchs->OX_ID or RX_ID
+    }
+    else
+    {
+#ifdef DBG     
+      printk( "FC Error: SEST build Pool Allocation failed\n");
+#endif
+      // return resources...
+      cpqfcTSCompleteExchange( fcChip, *fcExchangeIndex);  // SEST build failed
+    }
+  }
+  else  // no Exchanges available
+  {
+    ulStatus = SEST_FULL;
+    printk( "FC Error: no fcExchanges available\n");
+  }
+  return ulStatus;
+}
+
+
+
+
+
+
+// set RSP payload fields
+static void  buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID) 
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID];  // shorthand
+  PFCP_STATUS_RESPONSE pFcpStatus;
+  
+  memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0,
+    sizeof( FCP_STATUS_RESPONSE) );
+  if( pExchange->status ) // something wrong?
+  {
+    pFcpStatus = (PFCP_STATUS_RESPONSE)  // cast RSP buffer for this xchng
+      &fcChip->SEST->RspHDR[ ExchangeID ].pl;
+    if( pExchange->status & COUNT_ERROR )
+    {
+      
+      // set FCP response len valid (so we can report count error)
+      pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID;
+      pFcpStatus->fcp_rsp_len = 0x04000000;  // 4 byte len (BIG Endian)
+
+      pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE
+    }
+  }
+}
+
+
+
+
+// This routine builds scatter/gather lists into SEST entries
+// INPUTS:
+//   SESTalPair - SEST address @DWordA "Local Buffer Length"
+//   sgList     - Scatter/Gather linked list of Len/Address data buffers
+// OUTPUT:
+//   sgPairs - number of valid address/length pairs
+// Remarks:
+//   The SEST data buffer pointers only depend on number of
+//   length/ address pairs, NOT on the type (IWE, TRE,...)
+//   Up to 3 pairs can be referenced in the SEST - more than 3
+//   require this Extended S/G list page.  The page holds 4, 8, 16...
+//   len/addr pairs, per Scatter/Gather List Page Length Reg.
+//   TachLite allows pages to be linked to any depth.
+
+//#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages
+
+static ULONG build_SEST_sgList( 
+    ULONG *SESTalPairStart,  // the 3 len/address buffers in SEST
+    Scsi_Cmnd *Cmnd,
+    ULONG *sgPairs, 
+    PSGPAGES sgPages)  // link list of TL Ext. S/G pages from O/S Pool
+    
+{
+  ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations
+  ULONG* alPair = SESTalPairStart;
+  ULONG alignedPageAddress;  // TL hardware alignment requirement
+  int PairCount;
+  unsigned long ulBuff;
+  ULONG total_data_len=0; // (in bytes)
+  ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum)
+  ULONG thisMappingLen;
+  struct scatterlist *sgl;  // S/G list (Linux format)
+
+
+
+  if( !Cmnd->use_sg )  // no S/G list?
+  {
+    *sgPairs = 1;      // use "local" S/G pair in SEST entry
+                       // (for now, ignore address bits above #31)
+    *alPair++ = bytes_to_go & 0x7ffff; // bits 18-0, length
+    ulBuff = virt_to_bus( Cmnd->request_buffer);
+#if BITS_PER_LONG > 32
+    if( ulBuff >>32 )
+    {
+      printk("FATAL! Tachyon DMA address %p exceeds 32 bits\n", (void*)ulBuff );
+      return 0;
+    }
+#endif
+    *alPair = (ULONG)ulBuff;      
+    return bytes_to_go;
+  }
+
+
+  // [TBD - update for Linux to support > 32 bits addressing]
+  // since the format for local & extended S/G lists is different,
+  // check if S/G pairs exceeds 3.
+  *sgPairs = Cmnd->use_sg;
+  sgl = (struct scatterlist*)Cmnd->request_buffer;  
+  
+  if( *sgPairs <= 3 ) // need "local" SEST list
+  {
+    while( bytes_to_go)
+    {
+      thisMappingLen = sgl->length;  // we want them ALL on every pass
+      bytes_to_go = bytes_to_go - thisMappingLen;
+
+      // we have L/A pair; L = thisMappingLen, A = physicalAddress
+      // load into SEST...
+      total_data_len += thisMappingLen & 0x7ffff;  // mask in valid bits
+                                                   // per SEST format
+      *alPair = thisMappingLen & 0x7ffff; // bits 18-0, length
+//      physicalAddress.HighPart <= 19;  // shift to bit 19
+
+                   // pick up bits 44-32 of upper 64-bit address
+                   // and load into 31-19 LBAU (upper addr) of SEST entry
+//      *alPair++ |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); 
+      // on Tachlite TS's local S/G, we can handle 13 extra address bits
+      // i.e., bits 31-19 are actually bits  44-32 of physicalAddress
+
+      alPair++;
+
+      ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+      if( ulBuff >>32 )
+      {
+        printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+        return 0;
+      }
+#endif
+      *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+      ++sgl;  // next S/G pair
+#ifdef DBG_SEST_SGLIST
+      printk(" thisLen %d ", thisMappingLen);
+      printk(" remain %d\n", bytes_to_go);
+#endif
+
+    }
+  }
+
+
+
+
+  else    // more than 3 pairs requires Extended S/G page (Pool Allocation)
+  {
+    // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic)
+
+
+    
+    for( i=2; i<6; i++)
+      alPair[i] = 0;
+
+    PairCount = TL_EXT_SG_PAGE_COUNT;    // forces initial page allocation
+
+    while( bytes_to_go )
+    {
+
+
+      // Per SEST format, we can support 524287 byte lenghts per
+      // S/G pair.  Typical user buffers are 4k, and very rarely
+      // exceed 12k due to fragmentation of physical memory pages.
+      // However, on certain O/S system (not "user") buffers (on platforms 
+      // with huge memories like 256Meg), it's possible to exceed this
+      // length in a single S/G address/len mapping.
+      //
+      // Check for Tachyon length boundary
+      //
+      if( sgl->length > 0x7ffff )
+      {
+        // never ask for more than we can handle
+  	thisMappingLen = sgl->length & 0x7ffff;  
+      }
+      else
+        thisMappingLen = sgl->length;        
+      
+
+
+      // should we load into "this" extended S/G page, or allocate
+      // new page?
+
+      if( PairCount >= TL_EXT_SG_PAGE_COUNT )
+      {
+        // have we exceeded the max possible extended pages?      
+        if( AllocatedPages >= TL_MAX_SGPAGES)
+        {
+          printk("Error: aborted loop on %d Ext. S/G page allocations\n",
+            AllocatedPages);
+
+          total_data_len = 0;  // failure!! Ext. S/G is All-or-none affair
+          break; // failed
+        }
+        
+        // Allocate the TL Extended S/G list page from O/S pool.  We have
+        // to allocated twice what we want to ensure required TL alignment
+        // (Tachlite TL/TS User Man. Rev 6.0, p 168)
+        // We store the original allocated PVOID so we can free later
+
+        sgPages->PoolPage[ AllocatedPages] = 
+          kmalloc( TL_EXT_SG_PAGE_BYTELEN*2,GFP_ATOMIC); // double for alignment
+
+        
+        if( !sgPages->PoolPage[ AllocatedPages] )  // Allocation failed?
+        {
+
+          printk("Error: Allocation failed @ %d S/G page allocations\n",
+            AllocatedPages);
+
+          total_data_len = 0;  // failure!! Ext. S/G is All-or-none affair
+          break;               // give up
+        }
+                               // clear out memory we just allocated                     
+        memset( sgPages->PoolPage[AllocatedPages], 0,
+          TL_EXT_SG_PAGE_BYTELEN*2);
+
+      
+        // align the memory - TL requires sizeof() Ext. S/G page alignment.
+        // We doubled the actual required size so we could mask off LSBs 
+        // to get desired offset
+
+        ulBuff = virt_to_bus( sgPages->PoolPage[AllocatedPages]);
+
+#if BITS_PER_LONG > 32
+        if( ulBuff >>32 )
+        {
+          printk("cqpfcTS: Tach ext. S/G DMA address %p > 32 bits\n", 
+		  (void*)ulBuff );
+          return 0;
+        }
+#endif
+	
+        ulBuff += TL_EXT_SG_PAGE_BYTELEN; // ensures we pass align. boundary
+        ulBuff &= (0xFFFFFFFF - (TL_EXT_SG_PAGE_BYTELEN -1) );// mask off LSBs
+         
+        alignedPageAddress = (ULONG)ulBuff;
+#ifdef DBG_SEST_SGLIST
+        printk("new PoolPage: %p, alignedPageAddress %lXh\n", 
+          sgPages->PoolPage[AllocatedPages], ulBuff);
+#endif
+
+
+        // set pointer, in SEST if first Ext. S/G page, or in last pair
+        // of linked Ext. S/G pages...
+        // (Only 32-bit PVOIDs, so just load lower 32 bits)
+        // NOTE: the Len field must be '0' if this is the first Ext. S/G
+        // pointer in SEST, and not 0 otherwise.
+        if( alPair == SESTalPairStart) // initial Ext. S/G list?
+          *alPair = 0;
+        else // not the SEST entry... Len must be non-0, so
+             // arbitrarily set it to number bytes remaining
+          *alPair = ( bytes_to_go & 0x7ffff);
+
+#ifdef DBG_SEST_SGLIST
+        printk("PairCount %d @%p even %Xh, ", 
+          PairCount, alPair, *alPair);
+#endif
+        alPair++;  // next DWORD
+
+        *alPair = alignedPageAddress; // TL needs 32-bit physical
+#ifdef DBG_SEST_SGLIST
+        printk("odd %Xh\n", *alPair);
+#endif
+                   
+        // now reset the pointer to the ACTUAL (Extended) S/G page
+        // which will accept the Len/ PhysicalAddress pairs
+        alPair = bus_to_virt(alignedPageAddress);
+        
+        AllocatedPages++;
+        PairCount = 1;  // starting new Ext. S/G page
+      }  // end of new TL Ext. S/G page allocation
+
+      
+      *alPair = thisMappingLen; // bits 18-0, length (range check above)
+      
+      
+//      physicalAddress.HighPart <= 19;  // shift to bit 19
+      
+                   // pick up bits 44-32 of upper 64-bit address
+                   // and load into 31-19 LBAU (upper addr) of SEST entry
+//      *alPair |=(ULONG)((physicalAddress.HighPart & 0xFFF8)); 
+
+      
+#ifdef DBG_SEST_SGLIST
+      printk("PairCount %d @%p, even %Xh, ", 
+        PairCount, alPair, *alPair);
+#endif
+
+      alPair++;    // next DWORD
+      // on Tachlite TS's local S/G, we can handle 13 extra address bits
+      // i.e., bits 31-19 are actually bits  44-32 of physicalAddress
+
+
+      ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+      if( ulBuff >>32 )
+      {
+        printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+        return 0;
+      }
+#endif
+      *alPair = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+
+#ifdef DBG_SEST_SGLIST
+      printk("odd %Xh\n", *alPair);
+#endif
+      alPair++;    // next DWORD
+                                           
+
+      PairCount++; // next Length/Address pair
+      bytes_to_go -= thisMappingLen;
+      total_data_len += thisMappingLen;  
+      sgl++;  // next S/G pair
+    }
+  }
+  return total_data_len;
+}
+
+
+
+// The Tachlite SEST table is referenced to OX_ID (or RX_ID).  To optimize
+// performance and debuggability, we index the Exchange structure to FC X_ID
+// This enables us to build exchanges for later en-queing to Tachyon,
+// provided we have an open X_ID slot. At Tachyon queing time, we only 
+// need an ERQ slot; then "fix-up" references in the 
+// IRB, FCHS, etc. as needed.
+// RETURNS:
+// 0 if successful
+// non-zero on error
+//sstartex
+ULONG cpqfcTSStartExchange( 
+  CPQFCHBA *cpqfcHBAdata,                      
+  LONG ExchangeID )
+{
+  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand
+  USHORT producer, consumer;
+  ULONG ulStatus=0;
+  short int ErqIndex;
+  BOOLEAN CompleteExchange = FALSE;  // e.g. ACC replies are complete
+  BOOLEAN SestType=FALSE;
+  ULONG InboundData=0;
+
+  // We will manipulate Tachlite chip registers here to successfully
+  // start exchanges. 
+
+  // Check that link is not down -- we can't start an exchange on a
+  // down link!
+
+  if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline?
+  {
+printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n",
+         fcChip->Registers.FMstatus.value & 0xFF,
+         ExchangeID,
+         pExchange->type,
+         pExchange->fchs.d_id);
+
+    if( ExchangeID >= TACH_SEST_LEN )  // Link Service Outbound frame?
+    {
+      // Our most popular LinkService commands are port discovery types
+      // (PLOGI/ PDISC...), which are implicitly nullified by Link Down
+      // events, so it makes no sense to Que them.  However, ABTS should
+      // be queued, since exchange sequences are likely destroyed by
+      // Link Down events, and we want to notify other ports of broken
+      // sequences by aborting the corresponding exchanges.
+      if( pExchange->type != BLS_ABTS )
+      {
+	ulStatus = LNKDWN_OSLS;
+	goto Done;
+        // don't Que most LinkServ exchanges on LINK DOWN
+      }
+    }
+
+    printk("fcStartExchange: Que x_ID %Xh, type %Xh\n", 
+      ExchangeID, pExchange->type);
+    pExchange->status |= EXCHANGE_QUEUED;
+    ulStatus = EXCHANGE_QUEUED;
+    goto Done;
+  }
+
+  // Make sure ERQ has available space.
+  
+  producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith.
+  consumer = (USHORT)fcChip->ERQ->consumerIndex;
+  producer++;  // We are testing for full que by incrementing
+  
+  if( producer >= ERQ_LEN )  // rollover condition?
+    producer = 0;
+  if( consumer != producer ) // ERQ not full?
+  {
+    // ****************** Need Atomic access to chip registers!!********
+    
+    // remember ERQ PI for copying IRB
+    ErqIndex = (USHORT)fcChip->ERQ->producerIndex; 
+    fcChip->ERQ->producerIndex = producer; // this is written to Tachyon
+               // we have an ERQ slot! If SCSI command, need SEST slot
+               // otherwise we are done.
+
+    // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be
+    // set according to direction of data to/from Tachyon for SEST assists.
+    // For consistency, enforce this rule for Link Service (non-SEST)
+    // exchanges as well.
+
+    // fix-up the X_ID field in IRB
+    pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field
+
+    // fix-up the X_ID field in fchs -- depends on Originator or Responder,
+    // outgoing or incoming data?
+    switch( pExchange->type )
+    {
+               // ORIGINATOR types...  we're setting our OX_ID and
+               // defaulting the responder's RX_ID to 0xFFFF
+    
+    case SCSI_IRE:
+      // Requirement: set MSB of x_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+      InboundData = 0x8000;
+
+    case SCSI_IWE:   
+      SestType = TRUE;
+      pExchange->fchs.ox_rx_id = (ExchangeID | InboundData);
+      pExchange->fchs.ox_rx_id <<= 16;     // MSW shift
+      pExchange->fchs.ox_rx_id |= 0xffff;  // add default RX_ID
+      
+      // now fix-up the Data HDR OX_ID (TL automatically does rx_id)
+      // (not necessary for IRE -- data buffer unused)
+      if( pExchange->type == SCSI_IWE)
+      {
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id = 
+          pExchange->fchs.ox_rx_id;
+
+      }
+
+      break;
+
+
+    case FCS_NSR:  // ext. link service Name Service Request
+    case ELS_SCR:  // ext. link service State Change Registration
+    case ELS_FDISC:// ext. link service login
+    case ELS_FLOGI:// ext. link service login
+    case ELS_LOGO: // FC-PH extended link service logout
+    case BLS_NOP:  // Basic link service No OPeration
+    case ELS_PLOGI:// ext. link service login (PLOGI)
+    case ELS_PDISC:// ext. link service login (PDISC)
+    case ELS_PRLI: // ext. link service process login
+
+      pExchange->fchs.ox_rx_id = ExchangeID;
+      pExchange->fchs.ox_rx_id <<= 16;  // MSW shift
+      pExchange->fchs.ox_rx_id |= 0xffff;  // and RX_ID
+
+      break;
+      
+
+
+
+               // RESPONDER types... we must set our RX_ID while preserving
+               // sender's OX_ID
+               // outgoing (or no) data
+    case ELS_RJT:       // extended link service reject 
+    case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+    case ELS_ACC:      // ext. generic link service accept
+    case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+    case ELS_PRLI_ACC: // ext. link service process login accept
+
+      CompleteExchange = TRUE;   // Reply (ACC or RJT) is end of exchange
+      pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF);
+
+      break;
+
+
+      // since we are a Responder, OX_ID should already be set by
+      // cpqfcTSBuildExchange().  We need to -OR- in RX_ID
+    case SCSI_TWE:
+      SestType = TRUE;
+      // Requirement: set MSB of x_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+
+      pExchange->fchs.ox_rx_id &= 0xFFFF0000;  // clear RX_ID
+      // Requirement: set MSB of RX_ID for Incoming TL data
+      // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+      pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000);
+      break;
+          
+    
+    case SCSI_TRE:
+      SestType = TRUE;
+      
+      // there is no XRDY for SEST target read; the data
+      // header needs to be updated. Also update the RSP
+      // exchange IDs for the status frame, in case it is sent automatically
+      fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID;
+      fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id = 
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+      
+      // for easier FCP response logic (works for TWE and TRE), 
+      // copy exchange IDs.  (Not needed if TRE 'RSP' bit set)
+      pExchange->fchs.ox_rx_id =
+        fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+      break;
+
+
+    case FCP_RESPONSE:  // using existing OX_ID/ RX_ID pair,
+                        // start SFS FCP-RESPONSE frame
+      // OX/RX_ID should already be set! (See "fcBuild" above)
+      CompleteExchange = TRUE;   // RSP is end of FCP-SCSI exchange
+
+      
+      break;
+
+
+    case BLS_ABTS_RJT:  // uses new RX_ID, since SEST x_ID non-existent
+    case BLS_ABTS_ACC:  // using existing OX_ID/ RX_ID pair from SEST entry
+      CompleteExchange = TRUE;   // ACC or RJT marks end of FCP-SCSI exchange
+    case BLS_ABTS:  // using existing OX_ID/ RX_ID pair from SEST entry
+
+
+      break;
+
+
+    default:
+      printk("Error on fcStartExchange: undefined type %Xh(%d)\n",
+        pExchange->type, pExchange->type);
+      return INVALID_ARGS;
+    }
+    
+    
+      // X_ID fields are entered -- copy IRB to Tachyon's ERQ
+    
+
+    memcpy(
+        &fcChip->ERQ->QEntry[ ErqIndex ],  // dest.
+        &pExchange->IRB,
+        32);  // fixed (hardware) length!
+
+    PCI_TRACEO( ExchangeID, 0xA0)
+
+    // ACTION!  May generate INT and IMQ entry
+    writel( fcChip->ERQ->producerIndex,
+          fcChip->Registers.ERQproducerIndex.address);
+
+  
+    if( ExchangeID >= TACH_SEST_LEN )  // Link Service Outbound frame?
+    {
+    
+      // wait for completion! (TDB -- timeout and chip reset)
+      
+
+  PCI_TRACEO( ExchangeID, 0xA4)
+  
+      enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem.
+      
+      down_interruptible( cpqfcHBAdata->TYOBcomplete); 
+  
+      disable_irq( cpqfcHBAdata->HostAdapter->irq);
+  PCI_TRACE( 0xA4)
+
+      // On login exchanges, BAD_ALPA (non-existent port_id) results in 
+      // FTO (Frame Time Out) on the Outbound Completion message.
+      // If we got an FTO status, complete the exchange (free up slot)
+      if( CompleteExchange ||   // flag from Reply frames
+          pExchange->status )   // typically, can get FRAME_TO
+      {
+    	cpqfcTSCompleteExchange( fcChip, ExchangeID);  
+      }
+    }
+
+    else                         // SEST Exchange
+    {
+      ulStatus = 0;   // ship & pray success (e.g. FCP-SCSI)
+      
+      if( CompleteExchange )   // by Type of exchange (e.g. end-of-xchng)
+      {
+    	cpqfcTSCompleteExchange( fcChip, ExchangeID);  
+      }
+       
+      else
+        pExchange->status &= ~EXCHANGE_QUEUED;  // clear ExchangeQueued flag 
+
+    }
+  }
+
+  
+  else                // ERQ 'producer' = 'consumer' and QUE is full
+  {
+    ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full
+  }
+ 
+Done: 
+  PCI_TRACE( 0xA0)
+  return ulStatus; 
+}
+
+
+
+
+
+// Scan fcController->fcExchanges array for a usuable index (a "free"
+// exchange).
+// Inputs:
+//   fcChip - pointer to TachLite chip structure
+// Return:
+//  index - exchange array element where exchange can be built
+//  -1    - exchange array is full
+// REMARKS:
+// Although this is a (yuk!) linear search, we presume
+// that the system will complete exchanges about as quickly as
+// they are submitted.  A full Exchange array (and hence, max linear
+// search time for free exchange slot) almost guarantees a Fibre problem 
+// of some sort.
+// In the interest of making exchanges easier to debug, we want a LRU
+// (Least Recently Used) scheme.
+
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type )
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  ULONG i;
+  ULONG ulStatus=-1;  // assume failure
+
+
+  if( type == SCSI_IRE ||
+      type == SCSI_TRE ||
+      type == SCSI_IWE ||
+      type == SCSI_TWE)
+  {
+        // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1
+    if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover?
+      fcChip->fcSestExchangeLRU = 0;
+    i = fcChip->fcSestExchangeLRU; // typically it's already free!
+
+    if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+    {
+      ulStatus = 0; // success!
+    }
+    
+    else
+    {         // YUK! we need to do a linear search for free element.
+              // Fragmentation of the fcExchange array is due to excessively
+              // long completions or timeouts.
+      
+      while( TRUE )
+      {
+        if( ++i >= TACH_SEST_LEN ) // rollover check
+          i = 0;  // beginning of SEST X_IDs
+
+//        printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n", 
+//         i, Exchanges->fcExchange[i].type);
+
+        if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+        {
+          ulStatus = 0; // success!
+          break;
+        }
+        if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array?
+        {
+          printk( "SEST X_ID space full\n");
+          break;       // failed - prevent inf. loop
+        }
+      }
+    }
+    fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass)
+  }
+
+  
+  
+  else  // Link Service type - X_IDs should be from TACH_SEST_LEN 
+        // to TACH_MAX_XID
+  {
+    if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check
+        fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup)
+      fcChip->fcLsExchangeLRU = TACH_SEST_LEN;
+
+    i = fcChip->fcLsExchangeLRU; // typically it's already free!
+    if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+    {
+      ulStatus = 0; // success!
+    }
+    
+    else
+    {         // YUK! we need to do a linear search for free element
+              // Fragmentation of the fcExchange array is due to excessively
+              // long completions or timeouts.
+      
+      while( TRUE )
+      {
+        if( ++i >= TACH_MAX_XID ) // rollover check
+          i = TACH_SEST_LEN;// beginning of Link Service X_IDs
+
+//        printk( "looping for xchng ID: i=%d, type=%Xh\n", 
+//         i, Exchanges->fcExchange[i].type);
+
+        if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+        {
+          ulStatus = 0; // success!
+          break;
+        }
+        if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array?
+        {
+          printk( "LinkService X_ID space full\n");
+          break;       // failed - prevent inf. loop
+        }
+      }
+    }
+    fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass)
+
+  }
+
+  if( !ulStatus )  // success?
+    Exchanges->fcExchange[i].type = type; // allocate it.
+  
+  else
+    i = -1;  // error - all exchanges "open"
+
+  return i;  
+}
+
+
+
+
+
+// We call this routine to free an Exchange for any reason:
+// completed successfully, completed with error, aborted, etc.
+
+// returns FALSE if Exchange failed and "retry" is acceptable
+// returns TRUE if Exchange was successful, or retry is impossible
+// (e.g. port/device gone).
+//scompleteexchange
+
+void cpqfcTSCompleteExchange( 
+       PTACHYON fcChip, 
+       ULONG x_ID)
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  
+  if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange)
+  {
+    if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@!
+    {
+//      TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+      printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID,
+			Exchanges->fcExchange[ x_ID ].type);
+
+      goto CleanUpSestResources;  // this path should be very rare.
+    }
+
+    // we have Linux Scsi Cmnd ptr..., now check our Exchange status
+    // to decide how to complete this SEST FCP exchange
+
+    if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem,
+                                             // or abnormal exchange completion
+    {
+      // set FCP Link statistics
+     
+      if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+        fcChip->fcStats.timeouts++;
+      if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT)
+        fcChip->fcStats.FC4aborted++;
+      if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR)
+        fcChip->fcStats.CntErrors++;
+      if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX)
+        fcChip->fcStats.linkFailTX++;
+      if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX)
+        fcChip->fcStats.linkFailRX++;
+      if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW)
+        fcChip->fcStats.CntErrors++;
+
+      // First, see if the Scsi upper level initiated an ABORT on this
+      // exchange...
+      if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT )
+      {
+        printk(" DID_ABORT, x_ID %Xh, Cmnd %p ", 
+            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        goto CleanUpSestResources;  // (we don't expect Linux _aborts)
+      }
+
+      // Did our driver timeout the Exchange, or did Tachyon indicate
+      // a failure during transmission?  Ask for retry with "SOFT_ERROR"
+      else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) 
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+      }
+      
+      // Did frame(s) for an open exchange arrive in the SFQ,
+      // meaning the SEST was unable to process them?
+      else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME) 
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+      }
+      
+      // Did our driver timeout the Exchange, or did Tachyon indicate
+      // a failure during transmission?  Ask for retry with "SOFT_ERROR"
+      else if( 
+               (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) ||
+               (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) ||
+	       (Exchanges->fcExchange[ x_ID ].status & FRAME_TO)    ||
+	       (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY)    ||
+	       (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY)    )
+
+
+      {
+//        printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", 
+//            x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+
+
+      }
+
+      // e.g., a LOGOut happened, or device never logged back in.
+      else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED) 
+      {
+//	printk(" *LOGOut or timeout on login!* ");
+	// trigger?
+//        TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16);
+      }      
+		
+		      
+      // Did Tachyon indicate a CNT error?  We need further analysis
+      // to determine if the exchange is acceptable
+      else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR)
+      {
+        UCHAR ScsiStatus;
+        FCP_STATUS_RESPONSE *pFcpStatus = 
+	  (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+
+      	ScsiStatus = pFcpStatus->fcp_status >>24;
+  
+	// If the command is a SCSI Read/Write type, we don't tolerate
+	// count errors of any kind; assume the count error is due to
+	// a dropped frame and ask for retry...
+	
+	if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) ||
+	    (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) ||		
+            (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) ||
+            (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) )
+	                   &&
+                     ScsiStatus == 0 )
+	{
+          // ask for retry
+/*          printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n", 
+            x_ID, Exchanges->fcExchange[ x_ID ].status,
+            Exchanges->fcExchange[ x_ID ].Cmnd);*/
+          Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+	}
+	
+	else  // need more analysis
+	{
+	  cpqfcTSCheckandSnoopFCP(fcChip, x_ID);  // (will set ->result)
+	}
+      }
+      
+      // default: NOTE! We don't ever want to get here.  Getting here
+      // implies something new is happening that we've never had a test
+      // case for.  Need code maintenance!  Return "ERROR"
+      else
+      {
+        printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p\n", 
+          Exchanges->fcExchange[ x_ID ].status, x_ID, 
+	  Exchanges->fcExchange[ x_ID ].Cmnd);
+        Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16);
+      }
+    }
+    else    // definitely no Tach problem, but perhaps an FCP problem
+    {
+      // set FCP Link statistic
+      fcChip->fcStats.ok++;
+      cpqfcTSCheckandSnoopFCP( fcChip, x_ID);  // (will set ->result)    
+    }
+
+    // OK, we've set the Scsi "->result" field, so proceed with calling
+    // Linux Scsi "done" (if not NULL), and free any kernel memory we
+    // may have allocated for the exchange.
+
+  PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC);
+    // complete the command back to upper Scsi drivers
+    if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL)
+    {
+      // Calling "done" on an Linux _abort() aborted
+      // Cmnd causes a kernel panic trying to re-free mem.
+      // Actually, we shouldn't do anything with an _abort CMND
+      if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) )
+      {
+        PCI_TRACE(0xAC)
+        (*Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done)
+	   (Exchanges->fcExchange[ x_ID ].Cmnd);
+      }
+      else
+      {
+
+//	printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
+//			x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+      }
+    }
+    else{
+      printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
+	Exchanges->fcExchange[ x_ID ].type, 
+	Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);	      
+      printk(" cpqfcTS: Null scsi_done function pointer!\n");
+    }
+
+
+    // Now, clean up non-Scsi_Cmnd items...
+CleanUpSestResources:
+    
+    // Was an Extended Scatter/Gather page allocated?  We know
+    // this by checking DWORD 4, bit 31 ("LOC") of SEST entry
+    if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000))
+    {
+      int i = 0;
+
+      // extended S/G list was used -- Free the allocated ext. S/G pages
+
+      while( fcChip->SEST->sgPages[x_ID].PoolPage[i] && 
+             (i < TL_MAX_SGPAGES) )
+      {
+        kfree( fcChip->SEST->sgPages[x_ID].PoolPage[i]);
+        fcChip->SEST->sgPages[x_ID].PoolPage[i] = NULL;
+        i++;
+      }
+    }
+  
+    Exchanges->fcExchange[ x_ID ].Cmnd = NULL; 
+  }  // Done with FCP (SEST) exchanges
+
+
+  // the remaining logic is common to ALL Exchanges: 
+  // FCP(SEST) and LinkServ.
+
+  Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE!  
+  Exchanges->fcExchange[ x_ID ].status = 0; 
+
+  PCI_TRACEO( x_ID, 0xAC)
+     
+  
+  return;
+}   // (END of CompleteExchange function)
+ 
+
+
+
+// Unfortunately, we must snoop all command completions in
+// order to manipulate certain return fields, and take note of
+// device types, etc., to facilitate the Fibre-Channel to SCSI
+// "mapping".  
+// (Watch for BIG Endian confusion on some payload fields)
+void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID)
+{
+  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+  Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd;
+  FCP_STATUS_RESPONSE *pFcpStatus = 
+    (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+  UCHAR ScsiStatus;
+
+  ScsiStatus = pFcpStatus->fcp_status >>24;
+
+#ifdef FCP_COMPLETION_DBG
+  printk("ScsiStatus = 0x%X\n", ScsiStatus);
+#endif	
+
+  // First, check FCP status
+  if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID )
+  {
+    // check response code (RSP_CODE) -- most popular is bad len
+    // 1st 4 bytes of rsp info -- only byte 3 interesting
+    if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN )
+    { 
+
+      // do we EVER get here?
+      printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID);
+    }
+  }
+
+  // for now, go by the ScsiStatus, and manipulate certain
+  // commands when necessary...
+  if( ScsiStatus == 0) // SCSI status byte "good"?
+  {
+    Cmnd->result = 0; // everything's OK
+
+    if( (Cmnd->cmnd[0] == INQUIRY)) 
+    {
+      UCHAR *InquiryData = Cmnd->request_buffer;
+      PFC_LOGGEDIN_PORT pLoggedInPort;
+
+      // We need to manipulate INQUIRY
+      // strings for COMPAQ RAID controllers to force
+      // Linux to scan additional LUNs.  Namely, set
+      // the Inquiry string byte 2 (ANSI-approved version)
+      // to 2.
+
+      if( !memcmp( &InquiryData[8], "COMPAQ", 6 ))
+      {
+        InquiryData[2] = 0x2;  // claim SCSI-2 compliance,
+                               // so multiple LUNs may be scanned.
+                               // (no SCSI-2 problems known in CPQ)
+      }
+        
+      // snoop the Inquiry to detect Disk, Tape, etc. type
+      // (search linked list for the port_id we sent INQUIRY to)
+      pLoggedInPort = fcFindLoggedInPort( fcChip,
+        NULL,     // DON'T search Scsi Nexus (we will set it)
+        Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,        
+        NULL,     // DON'T search linked list for FC WWN
+        NULL);    // DON'T care about end of list
+ 
+      if( pLoggedInPort )
+      {
+        pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0];
+      }
+      else
+      {
+	printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n",
+          Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+      }
+    }
+  }
+
+
+  // Scsi Status not good -- pass it back to caller 
+
+  else
+  {
+    Cmnd->result = ScsiStatus; // SCSI status byte is 1st
+    
+    // check for valid "sense" data
+
+    if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID ) 
+    {            // limit Scsi Sense field length!
+      int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte
+      
+      SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ? 
+        sizeof( Cmnd->sense_buffer) : SenseLen;
+	   
+
+#ifdef FCP_COMPLETION_DBG	    
+      printk("copy sense_buffer %p, len %d, result %Xh\n",
+        Cmnd->sense_buffer, SenseLen, Cmnd->result);
+#endif	  
+
+      // NOTE: There is some dispute over the FCP response
+      // format.  Most FC devices assume that FCP_RSP_INFO
+      // is 8 bytes long, in spite of the fact that FCP_RSP_LEN
+      // is (virtually) always 0 and the field is "invalid".  
+      // Some other devices assume that
+      // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0)
+      // when the FCP_RSP is invalid (this almost appears to be
+      // one of those "religious" issues).
+      // Consequently, we test the usual position of FCP_SNS_INFO
+      // for 7Xh, since the SCSI sense format says the first
+      // byte ("error code") should be 0x70 or 0x71.  In practice,
+      // we find that every device does in fact have 0x70 or 0x71
+      // in the first byte position, so this test works for all
+      // FC devices.  
+      // (This logic is especially effective for the CPQ/DEC HSG80
+      // & HSG60 controllers).
+
+      if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 )
+        memcpy( Cmnd->sense_buffer, 
+          &pFcpStatus->fcp_sns_info[0], SenseLen);
+      else
+      {
+        unsigned char *sbPtr = 
+		(unsigned char *)&pFcpStatus->fcp_sns_info[0];
+        sbPtr -= 8;  // back up 8 bytes hoping to find the
+	             // start of the sense buffer
+        memcpy( Cmnd->sense_buffer, sbPtr, SenseLen);
+      }
+
+      // in the special case of Device Reset, tell upper layer
+      // to immediately retry (with SOFT_ERROR status)
+      // look for Sense Key Unit Attention (0x6) with ASC Device
+      // Reset (0x29)
+      //	    printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n",
+      //		    SenseLen, Cmnd->sense_buffer[2], 
+      //                   Cmnd->sense_buffer[12]);
+      if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+	        (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+      {
+        Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd
+      }
+ 
+      // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure
+      else if(  ((Cmnd->sense_buffer[2] & 0xF) == 0x4) &&  // "hardware error"
+      	        (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code 
+      {
+//        printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n",
+//		Cmnd->channel, Cmnd->target, Cmnd->lun);
+      	Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd
+      }
+      
+    }  // (end of sense len valid)
+
+    // there is no sense data to help out Linux's Scsi layers...
+    // We'll just return the Scsi status and hope he will "do the 
+    // right thing"
+    else
+    {
+      // as far as we know, the Scsi status is sufficient
+      Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd
+    }
+  }
+}
+
+
+
+//PPPPPPPPPPPPPPPPPPPPPPPPP  PAYLOAD  PPPPPPPPP
+// build data PAYLOAD; SCSI FCP_CMND I.U.
+// remember BIG ENDIAN payload - DWord values must be byte-reversed
+// (hence the affinity for byte pointer building).
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd, 
+      UCHAR* payload, ULONG type, ULONG fcp_dl )
+{
+  int i;
+
+  
+  switch( type)
+  {
+		  
+    case SCSI_IWE: 
+    case SCSI_IRE:        
+      // 8 bytes FCP_LUN
+      // Peripheral Device or Volume Set addressing, and LUN mapping
+      // When the FC port was looked up, we copied address mode
+      // and any LUN mask to the scratch pad SCp.phase & .mode
+
+      *payload++ = (UCHAR)Cmnd->SCp.phase;
+
+      // Now, because of "lun masking" 
+      // (aka selective storage presentation),
+      // the contiguous Linux Scsi lun number may not match the
+      // device's lun number, so we may have to "map".  
+      
+      *payload++ = (UCHAR)Cmnd->SCp.have_data_in;
+      
+      // We don't know of anyone in the FC business using these 
+      // extra "levels" of addressing.  In fact, confusion still exists
+      // just using the FIRST level... ;-)
+      
+      *payload++ = 0;  // 2nd level addressing
+      *payload++ = 0;
+      *payload++ = 0;  // 3rd level addressing
+      *payload++ = 0;
+      *payload++ = 0;  // 4th level addressing
+      *payload++ = 0;
+
+      // 4 bytes Control Field FCP_CNTL
+      *payload++ = 0;    // byte 0: (MSB) reserved
+      *payload++ = 0;    // byte 1: task codes
+      *payload++ = 0;    // byte 2: task management flags
+		      // byte 3: (LSB) execution management codes
+		      // bit 0 write, bit 1 read (don't set together)
+      
+      if( fcp_dl != 0 )
+      {
+        if( type == SCSI_IWE )         // WRITE
+          *payload++ = 1;
+        else                           // READ
+          *payload++ = 2;
+      }
+      else
+      {
+	// On some devices, if RD or WR bits are set,
+	// and fcp_dl is 0, they will generate an error on the command.
+	// (i.e., if direction is specified, they insist on a length).
+	*payload++ = 0;                // no data (necessary for CPQ)
+      }
+
+
+      // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16
+      // FCP_CDB allows 16 byte SCSI command descriptor blk;
+      // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...)
+      for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
+	*payload++ = Cmnd->cmnd[i];
+
+      if( Cmnd->cmd_len == 16 )
+      {
+        memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
+      }
+      payload+= (16 - i);  
+
+		      // FCP_DL is largest number of expected data bytes
+		      // per CDB (i.e. read/write command)
+      *payload++ = (UCHAR)(fcp_dl >>24);  // (MSB) 8 bytes data len FCP_DL
+      *payload++ = (UCHAR)(fcp_dl >>16);
+      *payload++ = (UCHAR)(fcp_dl >>8);
+      *payload++ = (UCHAR)fcp_dl;    // (LSB)
+      break;
+
+    case SCSI_TWE:          // need FCP_XFER_RDY
+      *payload++ = 0;     // (4 bytes) DATA_RO (MSB byte 0)
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;     // LSB (byte 3)
+			     // (4 bytes) BURST_LEN
+			     // size of following FCP_DATA payload
+      *payload++ = (UCHAR)(fcp_dl >>24);  // (MSB) 8 bytes data len FCP_DL
+      *payload++ = (UCHAR)(fcp_dl >>16);
+      *payload++ = (UCHAR)(fcp_dl >>8);
+      *payload++ = (UCHAR)fcp_dl;    // (LSB)
+		       // 4 bytes RESERVED
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;
+      *payload++ = 0;
+      break;
+
+    default:
+      break;
+  }
+
+  return 0;
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/cpqioctl.c linux.18p17/drivers/scsi/cpqioctl.c
--- linux.vanilla/drivers/scsi/cpqioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/cpqioctl.c	Fri Sep 15 22:44:39 2000
@@ -0,0 +1,76 @@
+// Test program for CPQFCTS ioctl calls
+// build with:
+// gcc -o cpqioctl cpqioctl.c
+// ld -o cpqioctl /lib/crt0.o cpqioctl.o -lc
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include "../../include/scsi/scsi.h"
+#include "cpqfcTSioctl.h"
+
+typedef struct scsi_fctargaddress {
+        unsigned long host_port_id;
+        unsigned char host_wwn[8];
+} Scsi_FCTargAddress;
+
+int main(int argc, char **argv) {
+
+ int fd, i;
+ Scsi_FCTargAddress targ;
+ int uselect=0;
+
+
+
+  if ( argc < 2 ) {
+    printf("usage: cpqioctl <Devfile>\n");
+    exit(1);
+  }
+
+  if ( (fd = open(argv[1], O_RDONLY)) == -1) {
+    perror("open");
+    exit(1);
+  }
+
+  if ( ioctl(fd, SCSI_IOCTL_FC_TARGET_ADDRESS, &targ) ) {
+    perror("ioctl");
+    exit(1);
+  }
+
+
+  printf("portid: %08x. wwn: ", targ.host_port_id);
+
+  for (i=0;i<8;i++) printf(" %02x", targ.host_wwn[i]);
+  printf("\n");
+
+ while( uselect != 27 ) // not ESC key
+  {
+    printf("\n IOCTL \n");
+    printf( "1. Get PCI info\n");
+    printf( "2. Send Passthru\n");
+    printf( " ==> ");
+    scanf("%c", &uselect);
+
+    switch( uselect  )
+    {
+      case '1':
+      {
+        cciss_pci_info_struct pciinfo;
+	
+  	if( ioctl( fd, CCPQFCTS_GETPCIINFO ,&pciinfo ))
+          perror("ioctl");
+	else
+          printf( "\nPCI bus %d, dev_fn %d, board_id %Xh\n",
+            pciinfo.bus, pciinfo.dev_fn, pciinfo.board_id);
+      }
+
+    }
+  }
+
+
+  close(fd);
+  return 0;
+} 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/eata_dma.c linux.18p17/drivers/scsi/eata_dma.c
--- linux.vanilla/drivers/scsi/eata_dma.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/eata_dma.c	Sat Sep 30 18:35:22 2000
@@ -687,7 +687,8 @@
 int eata_reset(Scsi_Cmnd * cmd, unsigned int resetflags)
 {
     uint x; 
-    ulong loop = loops_per_sec / 3;
+    /* 10 million PCI reads take at least one third of a second */
+    ulong loop = 10 * 1000 * 1000;
     ulong flags;
     unchar success = FALSE;
     Scsi_Cmnd *sp; 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth.c linux.18p17/drivers/scsi/gdth.c
--- linux.vanilla/drivers/scsi/gdth.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/gdth.c	Mon Jul 24 10:32:20 2000
@@ -20,9 +20,46 @@
  * along with this kernel; if not, write to the Free Software           *
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
  *                                                                      *
- * Tested with Linux 1.2.13, ..., 2.2.12                                *
+ * Tested with Linux 1.2.13, ..., 2.2.16, ..., 2.4.0-test4              *
  *                                                                      *
  * $Log: gdth.c,v $
+ * Revision 1.42  2000/07/20 09:04:50  achim
+ * Small changes for kernel 2.4
+ *
+ * Revision 1.41  2000/07/04 14:11:11  achim
+ * gdth_analyse_hdrive() added to rescan drives after online expansion
+ *
+ * Revision 1.40  2000/06/27 11:24:16  achim
+ * Changes Clustering, Screenservice
+ *
+ * Revision 1.39  2000/06/15 13:09:04  achim
+ * Changes for gdth_do_cmd()
+ *
+ * Revision 1.38  2000/06/15 12:08:43  achim
+ * Bugfix gdth_sync_event(), service SCREENSERVICE
+ * Data direction for command 0xc2 changed to DOU
+ *
+ * Revision 1.37  2000/05/25 13:50:10  achim
+ * New driver parameter virt_ctr added
+ *
+ * Revision 1.36  2000/05/04 08:50:46  achim
+ * Event buffer now in gdth_ha_str
+ *
+ * Revision 1.35  2000/03/03 10:44:08  achim
+ * New event_string only valid for the RP controller family
+ *
+ * Revision 1.34  2000/03/02 14:55:29  achim
+ * New mechanism for async. event handling implemented
+ *
+ * Revision 1.33  2000/02/21 15:37:37  achim
+ * Bugfix Alpha platform + DPMEM above 4GB
+ *
+ * Revision 1.32  2000/02/14 16:17:37  achim
+ * Bugfix sense_buffer[] + raw devices
+ *
+ * Revision 1.31  2000/02/10 10:29:00  achim
+ * Delete sense_buffer[0], if command OK
+ *
  * Revision 1.30  1999/11/02 13:42:39  achim
  * ARRAY_DRV_LIST2 implemented
  * Now 255 log. and 100 host drives supported
@@ -143,7 +180,7 @@
  * Initial revision
  *
  ************************************************************************/
-#ident "$Id: gdth.c,v 1.30 1999/11/02 13:42:39 achim Exp $" 
+#ident "$Id: gdth.c,v 1.42 2000/07/20 09:04:50 achim Exp $" 
 
 /* All GDT Disk Array Controllers are fully supported by this driver.
  * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
@@ -170,10 +207,12 @@
  * max_ids:x                    x - target ID count per channel (1..MAXID)
  * rescan:Y                     rescan all channels/IDs 
  * rescan:N                     use all devices found until now
+ * virt_ctr:Y                   map every channel to a virtual controller 
+ * virt_ctr:N                   use multi channel support 
  * hdr_channel:x                x - number of virtual bus for host drives
  *
- * The default value is: "gdth=disable:N,reserve_mode:1,reverse_scan:N,
- *                        max_ids:127,rescan:N,hdr_channel:0".
+ * The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N,
+ *                          max_ids:127,rescan:N,virt_ctr:N,hdr_channel:0".
  * Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y".
  * 
  * When loading the gdth driver as a module, the same options are available. 
@@ -183,7 +222,7 @@
  * '1' in place of 'Y' and '0' in place of 'N'.
  * 
  * Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0
- *                         max_ids=127 rescan=0 hdr_channel=0"
+ *                         max_ids=127 rescan=0 virt_ctr=0 hdr_channel=0"
  * The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1".
  */
 
@@ -204,6 +243,9 @@
 #include <linux/proc_fs.h>
 #include <linux/time.h>
 #include <linux/timer.h>
+#ifdef GDTH_RTC
+#include <linux/mc146818rtc.h>
+#endif
 #if LINUX_VERSION_CODE >= 0x020100
 #include <linux/reboot.h>
 #else
@@ -213,7 +255,9 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
-#if LINUX_VERSION_CODE >= 0x02015F
+#if LINUX_VERSION_CODE >= 0x020322
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= 0x02015F
 #include <asm/spinlock.h>
 #endif
 
@@ -236,7 +280,7 @@
 static void gdth_interrupt(int irq,struct pt_regs *regs);
 #endif
 static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
-static int gdth_async_event(int hanum,int service);
+static int gdth_async_event(int hanum);
 static void gdth_log_event(gdth_evt_data *dvr, char *buffer);
 
 static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
@@ -271,6 +315,7 @@
 static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1,
                              ulong32 p2,ulong32 p3);
 static int gdth_search_drives(int hanum);
+static int gdth_analyse_hdrive(int hanum, ushort hdrive);
 
 static void *gdth_mmap(ulong paddr, ulong size);
 static void gdth_munmap(void *addr);
@@ -289,8 +334,6 @@
 
 #ifdef DEBUG_GDTH
 static unchar   DebugState = DEBUG_GDTH;
-extern int sys_syslog(int,char*,int);
-#define LOGEN sys_syslog(7,NULL,0)
 
 #ifdef __SERIAL__
 #define MAX_SERBUF 160
@@ -359,9 +402,9 @@
 #define TRACE3(a)   {if (DebugState!=0) {ser_printk a;}}
 
 #else /* !__SERIAL__ */
-#define TRACE(a)    {if (DebugState==1) {LOGEN;printk a;}}
-#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {LOGEN;printk a;}}
-#define TRACE3(a)   {if (DebugState!=0) {LOGEN;printk a;}}
+#define TRACE(a)    {if (DebugState==1) {printk a;}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {printk a;}}
+#define TRACE3(a)   {if (DebugState!=0) {printk a;}}
 #endif
 
 #else /* !DEBUG */
@@ -441,7 +484,7 @@
 #define gdth_readl(addr)        (ulong32)readl((ulong)(addr))
 #define gdth_writeb(b,addr)     writeb((b),(ulong)(addr))
 #define gdth_writew(b,addr)     writew((b),(ulong)(addr))
-#define gdth_writel(b,addr)     writel((b),(ulong)(addr))
+#define gdth_writel(b,addr)     writel((ulong32)(b),(ulong)(addr))
 
 #else
 static void *gdth_mmap(ulong paddr, ulong size) 
@@ -457,7 +500,7 @@
 #define gdth_readl(addr)        (ulong32)readl((ulong)(addr))
 #define gdth_writeb(b,addr)     writeb((b),(ulong)(addr))
 #define gdth_writew(b,addr)     writew((b),(ulong)(addr))
-#define gdth_writel(b,addr)     writel((b),(ulong)(addr))
+#define gdth_writel(b,addr)     writel((ulong32)(b),(ulong)(addr))
 #endif
 
 
@@ -493,17 +536,21 @@
     DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
     DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DOU,DUN,DNO,DUN,DOU,DOU,
     DOU,DOU,DOU,DNO,DUN,DIN,DOU,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
-    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
     DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
     DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
     DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
 };
 
 /* __initfunc, __initdata macros */
-#if LINUX_VERSION_CODE >= 0x020126
+#if LINUX_VERSION_CODE >= 0x020322
+#define GDTH_INITFUNC(type, func)       type __init func 
+#include <linux/init.h>
+#elif LINUX_VERSION_CODE >= 0x020126
+#define GDTH_INITFUNC(type, func)       __initfunc(type func)
 #include <linux/init.h>
 #else
-#define __initfunc(A) A
+#define GDTH_INITFUNC(type, func)       type func
 #define __initdata
 #define __init
 #endif
@@ -550,6 +597,8 @@
 static int max_ids = MAXID;
 /* rescan all IDs */
 static int rescan = 0;
+/* map channels to virtual controllers */
+static int virt_ctr = 0;
 
 #ifdef MODULE
 #if LINUX_VERSION_CODE >= 0x02011A
@@ -562,6 +611,7 @@
 MODULE_PARM(hdr_channel, "i");
 MODULE_PARM(max_ids, "i");
 MODULE_PARM(rescan, "i");
+MODULE_PARM(virt_ctr, "i");
 MODULE_AUTHOR("Achim Leubner");
 #endif
 #endif
@@ -569,10 +619,12 @@
 /* /proc support */
 #if LINUX_VERSION_CODE >= 0x010300
 #include <linux/stat.h> 
+#if LINUX_VERSION_CODE < 0x020322
 struct proc_dir_entry proc_scsi_gdth = {
     PROC_SCSI_GDTH, 4, "gdth",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
+#endif
 #include "gdth_proc.h"
 #include "gdth_proc.c"
 #endif
@@ -621,7 +673,7 @@
 
 /* controller search and initialization functions */
 
-__initfunc (static int gdth_search_eisa(ushort eisa_adr))
+GDTH_INITFUNC(static int, gdth_search_eisa(ushort eisa_adr))
 {
     ulong32 id;
     
@@ -639,7 +691,7 @@
 }
 
 
-__initfunc (static int gdth_search_isa(ulong32 bios_adr))
+GDTH_INITFUNC(static int, gdth_search_isa(ulong32 bios_adr))
 {
     void *addr;
     ulong32 id;
@@ -655,9 +707,9 @@
 }
 
 
-__initfunc (static int gdth_search_pci(gdth_pci_str *pcistr))
+GDTH_INITFUNC(static int, gdth_search_pci(gdth_pci_str *pcistr))
 {
-    ulong32 base0, base1, base2;
+    ulong base0, base1, base2;
     ushort device_id, cnt;
 #if LINUX_VERSION_CODE >= 0x2015C
     struct pci_dev *pdev;
@@ -674,7 +726,43 @@
         if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
             device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
             continue;
-#if LINUX_VERSION_CODE >= 0x2015C
+#if LINUX_VERSION_CODE >= 0x20363
+        pdev = NULL;
+        while ((pdev = pci_find_device(PCI_VENDOR_ID_VORTEX,device_id,pdev)) 
+               != NULL) {
+            if (pci_enable_device(pdev))
+                continue;
+            if (cnt >= MAXHA)
+                return cnt;
+            /* GDT PCI controller found, resources are already in pdev */
+            pcistr[cnt].pdev = pdev;
+            pcistr[cnt].device_id = device_id;
+            pcistr[cnt].bus = pdev->bus->number;
+            pcistr[cnt].device_fn = pdev->devfn;
+            pcistr[cnt].irq = pdev->irq;
+            base0 = pci_resource_flags(pdev, 0);
+            base1 = pci_resource_flags(pdev, 1);
+            base2 = pci_resource_flags(pdev, 2);
+            if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B ||   /* GDT6000/B */
+                device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) {  /* MPR */
+                if (!(base0 & IORESOURCE_MEM)) 
+                    continue;
+                pcistr[cnt].dpmem = pci_resource_start(pdev, 0);
+            } else {                                  /* GDT6110, GDT6120, .. */
+                if (!(base0 & IORESOURCE_MEM) ||
+                    !(base2 & IORESOURCE_MEM) ||
+                    !(base1 & IORESOURCE_IO)) 
+                    continue;
+                pcistr[cnt].dpmem = pci_resource_start(pdev, 2);
+                pcistr[cnt].io_mm = pci_resource_start(pdev, 0);
+                pcistr[cnt].io    = pci_resource_start(pdev, 1);
+            }
+            TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n",
+                    pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), 
+                    pcistr[cnt].irq, pcistr[cnt].dpmem));
+            cnt++;
+        }       
+#elif LINUX_VERSION_CODE >= 0x2015C
         pdev = NULL;
         while ((pdev = pci_find_device(PCI_VENDOR_ID_VORTEX,device_id,pdev)) 
                != NULL) {
@@ -695,7 +783,7 @@
                     PCI_BASE_ADDRESS_SPACE_MEMORY)
                     continue;
                 pcistr[cnt].dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
-            } else {                                    /* GDT6110, GDT6120, .. */
+            } else {                                  /* GDT6110, GDT6120, .. */
                 if ((base0 & PCI_BASE_ADDRESS_SPACE) !=
                     PCI_BASE_ADDRESS_SPACE_MEMORY ||
                     (base2 & PCI_BASE_ADDRESS_SPACE) !=
@@ -707,9 +795,9 @@
                 pcistr[cnt].io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
                 pcistr[cnt].io    = base1 & PCI_BASE_ADDRESS_IO_MASK;
             }
-            TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n",
+            TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n",
                     pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), 
-                    pcistr[cnt].irq, (int)pcistr[cnt].dpmem));
+                    pcistr[cnt].irq, pcistr[cnt].dpmem));
             cnt++;
         }       
 #else   
@@ -762,7 +850,7 @@
                 pcistr[cnt].io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
                 pcistr[cnt].io    = base1 & PCI_BASE_ADDRESS_IO_MASK;
             }
-            TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n",
+            TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n",
                     pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), 
                     pcistr[cnt].irq, pcistr[cnt].dpmem));
             cnt++;
@@ -773,7 +861,7 @@
 }
 
 
-__initfunc (static void gdth_sort_pci(gdth_pci_str *pcistr, int cnt))
+GDTH_INITFUNC(static void, gdth_sort_pci(gdth_pci_str *pcistr, int cnt))
 {    
     gdth_pci_str temp;
     int i, changed;
@@ -811,7 +899,7 @@
 }
 
 
-__initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha))
+GDTH_INITFUNC(static int, gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha))
 {
     ulong32 retries,id;
     unchar prot_ver,eisacf,i,irq_found;
@@ -900,7 +988,7 @@
 }
 
        
-__initfunc (static int gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha))
+GDTH_INITFUNC(static int, gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha))
 {
     register gdt2_dpram_str *dp2_ptr;
     int i;
@@ -997,7 +1085,7 @@
 }
 
 
-__initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha))
+GDTH_INITFUNC(static int, gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha))
 {
     register gdt6_dpram_str *dp6_ptr;
     register gdt6c_dpram_str *dp6c_ptr;
@@ -1165,7 +1253,18 @@
         }
 
         /* manipulate config. space to enable DPMEM, start RP controller */
-#if LINUX_VERSION_CODE >= 0x2015C
+#if LINUX_VERSION_CODE >= 0x20363
+        pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command);
+        command |= 6;
+        pci_write_config_word(pcistr->pdev, PCI_COMMAND, command);
+        if (pci_resource_start(pcistr->pdev, 8) == 1UL)
+            pci_resource_start(pcistr->pdev, 8) = 0UL;
+        i = 0xFEFF0001UL;
+        pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, i);
+        gdth_delay(1);
+        pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS,
+                               pci_resource_start(pcistr->pdev, 8));
+#elif LINUX_VERSION_CODE >= 0x2015C
         pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command);
         command |= 6;
         pci_write_config_word(pcistr->pdev, PCI_COMMAND, command);
@@ -1193,13 +1292,13 @@
         pcibios_write_config_dword(pcistr->bus, pcistr->device_fn,
                                    PCI_ROM_ADDRESS, rom_addr);
 #endif
-	
+        
         /* check and reset interface area */
         dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
         gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u);
         if (gdth_readl(&dp6m_ptr->u) != DPMEM_MAGIC) {
-            printk("GDT-PCI: Cannot access DPMEM at 0x%x (shadowed?)\n", 
-                   (int)pcistr->dpmem);
+            printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n", 
+                   pcistr->dpmem);
             found = FALSE;
             for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
                 gdth_munmap(ha->brd);
@@ -1298,7 +1397,7 @@
 
 /* controller protocol functions */
 
-__initfunc (static void gdth_enable_int(int hanum))
+GDTH_INITFUNC(static void, gdth_enable_int(int hanum))
 {
     gdth_ha_str *ha;
     ulong flags;
@@ -1609,6 +1708,12 @@
             cmd_ptr->u.raw.bus        = (unchar)p2;
             cmd_ptr->u.raw.target     = (unchar)p3;
             cmd_ptr->u.raw.lun        = (unchar)(p3 >> 8);
+        } else if (service == SCREENSERVICE) {
+            if (opcode == GDT_REALTIME) {
+                *(ulong32 *)&cmd_ptr->u.screen.su.data[0] = p1;
+                *(ulong32 *)&cmd_ptr->u.screen.su.data[4] = p2;
+                *(ulong32 *)&cmd_ptr->u.screen.su.data[8] = p3;
+            }
         }
         ha->cmd_len          = sizeof(gdth_cmd_str);
         ha->cmd_offs_dpmem   = 0;
@@ -1631,20 +1736,21 @@
 
 /* search for devices */
 
-__initfunc (static int gdth_search_drives(int hanum))
+GDTH_INITFUNC(static int, gdth_search_drives(int hanum))
 {
     register gdth_ha_str *ha;
     ushort cdev_cnt, i;
-    int drv_cyls, drv_hds, drv_secs;
-    ulong32 bus_no;
-    ulong32 drv_cnt, drv_no, j;
+    ulong32 bus_no, drv_cnt, drv_no, j;
     gdth_getch_str *chn;
     gdth_drlist_str *drl;
     gdth_iochan_str *ioc;
     gdth_raw_iochan_str *iocr;
     gdth_arcdl_str *alst;
     gdth_alist_str *alst2;
-        
+#ifdef GDTH_RTC
+    unchar rtc[12];
+#endif     
+   
     TRACE(("gdth_search_drives() hanum %d\n",hanum));
     ha = HADATA(gdth_ctr_tab[hanum]);
 
@@ -1655,7 +1761,28 @@
         return 0;
     }
     TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
-    
+
+#ifdef GDTH_RTC
+    /* read realtime clock info, send to controller */
+    /* 1. wait for the falling edge of update flag */
+    for (j = 0; j < 1000000; ++j)
+        if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+            break;
+    for (j = 0; j < 1000000; ++j)
+        if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+            break;
+    /* 2. read info */
+    do {
+        for (j = 0; j < 12; ++j) 
+            rtc[j] = CMOS_READ(j);
+    } while (rtc[0] != CMOS_READ(0));
+    TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(ulong32 *)&rtc[0],
+            *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]));
+    /* 3. send to controller firmware */
+    gdth_internal_cmd(hanum,SCREENSERVICE,GDT_REALTIME, *(ulong32 *)&rtc[0],
+                      *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]);
+#endif   
+ 
     /* initialize cache service */
     if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
         printk("GDT: Initialization error cache service (code %d)\n",
@@ -1664,6 +1791,7 @@
     }
     TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
     cdev_cnt = (ushort)ha->info;
+    ha->fw_vers = ha->service;
 
     /* detect number of buses - try new IOCTL */
     iocr = (gdth_raw_iochan_str *)ha->pscratch;
@@ -1723,7 +1851,8 @@
     ha->more_proc = FALSE;
     if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO,
                           INVALID_CHANNEL,sizeof(gdth_binfo_str))) {
-        ha->binfo = *(gdth_binfo_str *)ha->pscratch;
+        memcpy(&ha->binfo, (gdth_binfo_str *)ha->pscratch,
+               sizeof(gdth_binfo_str));
         if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_FEATURES,
                               INVALID_CHANNEL,sizeof(gdth_bfeat_str))) {
             TRACE2(("BOARD_INFO/BOARD_FEATURES supported\n"));
@@ -1798,14 +1927,14 @@
                     }
                 }
             }
-	    alst = (gdth_arcdl_str *)ha->pscratch;
-	    alst->entries_avail = MAX_LDRIVES;
-	    alst->first_entry = 0;
-	    alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]);
+            alst = (gdth_arcdl_str *)ha->pscratch;
+            alst->entries_avail = MAX_LDRIVES;
+            alst->first_entry = 0;
+            alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]);
             if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
                                   ARRAY_DRV_LIST2 | LA_CTRL_PATTERN, 
                                   INVALID_CHANNEL, sizeof(gdth_arcdl_str) +
-				  (alst->entries_avail-1) * sizeof(gdth_alist_str))) { 
+                                  (alst->entries_avail-1) * sizeof(gdth_alist_str))) { 
                 for (j = 0; j < alst->entries_init; ++j) {
                     ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd;
                     ha->hdr[j].is_master = alst->list[j].is_master;
@@ -1813,9 +1942,9 @@
                     ha->hdr[j].is_hotfix = alst->list[j].is_hotfix;
                     ha->hdr[j].master_no = alst->list[j].cd_handle;
                 }
-	    } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
-					 ARRAY_DRV_LIST | LA_CTRL_PATTERN,
-					 0, 35 * sizeof(gdth_alist_str))) {
+            } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+                                         ARRAY_DRV_LIST | LA_CTRL_PATTERN,
+                                         0, 35 * sizeof(gdth_alist_str))) {
                 for (j = 0; j < 35; ++j) {
                     alst2 = &((gdth_alist_str *)ha->pscratch)[j];
                     ha->hdr[j].is_arraydrv = alst2->is_arrayd;
@@ -1881,60 +2010,70 @@
         }
     }
 
-    /* scanning for cache devices */
-    for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
-        TRACE(("gdth_search_drives() cachedev. %d\n",i));
-        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
-            ha->hdr[i].present = TRUE;
-            ha->hdr[i].size = ha->info;
-
-            /* evaluate mapping (sectors per head, heads per cylinder) */
-            ha->hdr[i].size &= ~SECS32;
-            if (ha->info2 == 0) {
-                gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs);
-            } else {
-                drv_hds = ha->info2 & 0xff;
-                drv_secs = (ha->info2 >> 8) & 0xff;
-                drv_cyls = ha->hdr[i].size /drv_hds/drv_secs;
-            }
-            ha->hdr[i].heads = (unchar)drv_hds;
-            ha->hdr[i].secs  = (unchar)drv_secs;
-            /* round size */
-            ha->hdr[i].size  = drv_cyls * drv_hds * drv_secs;
-            TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n",
-                   i,ha->hdr[i].size,drv_hds,drv_secs));
-            
-            /* get informations about device */
-            if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
-                                  0,0)) {
-                TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
-                       i,ha->info));
-                ha->hdr[i].devtype = (ushort)ha->info;
-            }
+    /* scanning for host drives */
+    for (i = 0; i < cdev_cnt; ++i) 
+        gdth_analyse_hdrive(hanum,i);
+    
+    TRACE(("gdth_search_drives() OK\n"));
+    return 1;
+}
 
-            /* cluster info */
-            if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,i,
-                                  0,0)) {
-                TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
-                       i,ha->info));
-                ha->hdr[i].cluster_type = (unchar)ha->info;
-            } else {
-                ha->hdr[i].cluster_type = 0;
-            }           
+static int gdth_analyse_hdrive(int hanum,ushort hdrive)
+{
+    register gdth_ha_str *ha;
+    int drv_cyls, drv_hds, drv_secs;
 
-            /* R/W attributes */
-            if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,i,
-                                  0,0)) {
-                TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
-                       i,ha->info));
-                ha->hdr[i].rw_attribs = (unchar)ha->info;
-            } else {
-                ha->hdr[i].rw_attribs = 0;
-            }           
-        }
+    TRACE(("gdth_analyse_hdrive() hanum %d drive %d\n",hanum,hdrive));
+    if (hdrive >= MAX_HDRIVES)
+        return 0;
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,hdrive,0,0)) 
+        return 0;
+    ha->hdr[hdrive].present = TRUE;
+    ha->hdr[hdrive].size = ha->info;
+
+    /* evaluate mapping (sectors per head, heads per cylinder) */
+    ha->hdr[hdrive].size &= ~SECS32;
+    if (ha->info2 == 0) {
+        gdth_eval_mapping(ha->hdr[hdrive].size,&drv_cyls,&drv_hds,&drv_secs);
+    } else {
+        drv_hds = ha->info2 & 0xff;
+        drv_secs = (ha->info2 >> 8) & 0xff;
+        drv_cyls = ha->hdr[hdrive].size /drv_hds/drv_secs;
+    }
+    ha->hdr[hdrive].heads = (unchar)drv_hds;
+    ha->hdr[hdrive].secs  = (unchar)drv_secs;
+    /* round size */
+    ha->hdr[hdrive].size  = drv_cyls * drv_hds * drv_secs;
+    TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n",
+            hdrive,ha->hdr[hdrive].size,drv_hds,drv_secs));
+            
+    /* get informations about device */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,hdrive,0,0)) {
+        TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
+                hdrive,ha->info));
+        ha->hdr[hdrive].devtype = (ushort)ha->info;
     }
 
-    TRACE(("gdth_search_drives() OK\n"));
+    /* cluster info */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,hdrive,0,0)) {
+        TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
+                hdrive,ha->info));
+        ha->hdr[hdrive].cluster_type = (unchar)ha->info;
+    } else {
+        ha->hdr[hdrive].cluster_type = 0;
+    }           
+
+    /* R/W attributes */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,hdrive,0,0)) {
+        TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
+                hdrive,ha->info));
+        ha->hdr[hdrive].rw_attribs = (unchar)ha->info;
+    } else {
+        ha->hdr[hdrive].rw_attribs = 0;
+    }           
+
     return 1;
 }
 
@@ -1954,11 +2093,7 @@
     GDTH_LOCK_HA(ha, flags);
 
     scp->SCp.this_residual = (int)priority;
-#if LINUX_VERSION_CODE >= 0x020000
-    b = scp->channel;
-#else
-    b = NUMDATA(nscp->host)->busnum;
-#endif
+    b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
     t = scp->target;
 #if LINUX_VERSION_CODE >= 0x010300
     if (priority >= DEFAULT_PRI) {
@@ -2019,11 +2154,7 @@
     for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
         if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
             pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
-#if LINUX_VERSION_CODE >= 0x020000
-        b = nscp->channel;
-#else
-        b = NUMDATA(nscp->host)->busnum;
-#endif
+        b = virt_ctr ? NUMDATA(nscp->host)->busnum : nscp->channel;
         t = nscp->target;
         if (nscp->SCp.this_residual >= DEFAULT_PRI) {
             if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) ||
@@ -2085,21 +2216,8 @@
             if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY &&
                 nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE &&
                 (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) {
-                if (!(ha->hdr[t].cluster_type & CLUSTER_MOUNTED)) {
-                    /* cluster drive NOT MOUNTED */
-                    if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) {
-                        /* cluster drive NOT RESERVED */
-                        nscp->SCp.Status = GDT_MOUNT;
-                    } else {
-                        /* cluster drive RESERVED (on the other node) */
-                        nscp->SCp.Status = GDT_CLUST_INFO;
-                    }
-                } else {
-                    if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) {
-                        /* cluster drive MOUNTED and not RESERVED */
-                        nscp->SCp.Status = GDT_CLUST_INFO;
-                    }
-                }
+                /* always GDT_CLUST_INFO! */
+                nscp->SCp.Status = GDT_CLUST_INFO;
             }
         }
         }
@@ -2177,6 +2295,7 @@
                 if ( (nscp->cmnd[4]&1) && !(ha->hdr[t].devtype&1) ) {
                     TRACE(("Prevent r. nonremov. drive->do nothing\n"));
                     nscp->result = DID_OK << 16;
+                    nscp->sense_buffer[0] = 0;
                     if (!nscp->SCp.have_data_in)
                         nscp->SCp.have_data_in++;
                     else {
@@ -2353,6 +2472,7 @@
         break;
     }
     scp->result = DID_OK << 16;
+    scp->sense_buffer[0] = 0;
 
     if (!scp->SCp.have_data_in)
         scp->SCp.have_data_in++;
@@ -2545,7 +2665,7 @@
         cmdp->u.raw.sense_len  = 16;
         cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
         cmdp->u.raw.direction  = 
-            gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+            gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN;
         memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
 
         if (scp->use_sg) {
@@ -2677,8 +2797,12 @@
 
     if (ebuffer[elastidx].event_source == source &&
         ebuffer[elastidx].event_idx == idx &&
-        !memcmp((char *)&ebuffer[elastidx].event_data.eu,
-            (char *)&evt->eu, evt->size)) {
+        ((evt->size != 0 && ebuffer[elastidx].event_data.size != 0 &&
+            !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+            (char *)&evt->eu, evt->size)) ||
+        (evt->size == 0 && ebuffer[elastidx].event_data.size == 0 &&
+            !strcmp((char *)&ebuffer[elastidx].event_data.event_string,
+            (char *)&evt->event_string)))) { 
         e = &ebuffer[elastidx];
         do_gettimeofday(&tv);
         e->last_stamp = tv.tv_sec;
@@ -2791,11 +2915,9 @@
     gdt6_dpram_str *dp6_ptr;
     gdt2_dpram_str *dp2_ptr;
     Scsi_Cmnd *scp;
-    int hanum, rval;
+    int hanum, rval, i;
     unchar IStatus;
-    ushort CmdStatus, Service = 0;
-    ulong32 InfoBytes, InfoBytes2 = 0;
-    gdth_evt_data dvr;
+    ushort Service;
     ulong flags = 0;
 
     TRACE(("gdth_interrupt() IRQ %d\n",irq));
@@ -2813,9 +2935,7 @@
 
     /* search controller */
     if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
-        /*
-        TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
-        */
+        /* spurious interrupt */
         if (!gdth_polling)
             GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags);
         return;
@@ -2829,34 +2949,28 @@
     if (ha->type == GDT_EISA) {
         if (IStatus & 0x80) {                       /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = inw(ha->bmic + MAILBOXREG+8);
-            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
-            if (IStatus == ASYNCINDEX) {            /* async. event ? */
-                Service = inw(ha->bmic + MAILBOXREG+10);
-                InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
-            }
+            ha->status = inw(ha->bmic + MAILBOXREG+8);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
         } else                                      /* no error */
-            CmdStatus = S_OK;
-        InfoBytes = inl(ha->bmic + MAILBOXREG+12);
-        if (gdth_polling)                           /* init. -> more info */
-            InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+            ha->status = S_OK;
+        ha->info = inl(ha->bmic + MAILBOXREG+12);
+        ha->service = inw(ha->bmic + MAILBOXREG+10);
+        ha->info2 = inl(ha->bmic + MAILBOXREG+4);
+
         outb(0xff, ha->bmic + EDOORREG);            /* acknowledge interrupt */
         outb(0x00, ha->bmic + SEMA1REG);            /* reset status semaphore */
     } else if (ha->type == GDT_ISA) {
         dp2_ptr = (gdt2_dpram_str *)ha->brd;
         if (IStatus & 0x80) {                       /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = gdth_readw(&dp2_ptr->u.ic.Status);
-            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
-            if (IStatus == ASYNCINDEX) {            /* async. event ? */
-                Service = gdth_readw(&dp2_ptr->u.ic.Service);
-                InfoBytes2 = gdth_readl(&dp2_ptr->u.ic.Info[1]);
-            }
+            ha->status = gdth_readw(&dp2_ptr->u.ic.Status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
         } else                                      /* no error */
-            CmdStatus = S_OK;
-        InfoBytes = gdth_readl(&dp2_ptr->u.ic.Info[0]);
-        if (gdth_polling)                           /* init. -> more info */
-            InfoBytes2 = gdth_readl(&dp2_ptr->u.ic.Info[1]);
+            ha->status = S_OK;
+        ha->info = gdth_readl(&dp2_ptr->u.ic.Info[0]);
+        ha->service = gdth_readw(&dp2_ptr->u.ic.Service);
+        ha->info2 = gdth_readl(&dp2_ptr->u.ic.Info[1]);
+
         gdth_writeb(0xff, &dp2_ptr->io.irqdel);     /* acknowledge interrupt */
         gdth_writeb(0, &dp2_ptr->u.ic.Cmd_Index);   /* reset command index */
         gdth_writeb(0, &dp2_ptr->io.Sema1);         /* reset status semaphore */
@@ -2864,52 +2978,56 @@
         dp6_ptr = (gdt6_dpram_str *)ha->brd;
         if (IStatus & 0x80) {                       /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = gdth_readw(&dp6_ptr->u.ic.Status);
-            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
-            if (IStatus == ASYNCINDEX) {        /* async. event ? */
-                Service = gdth_readw(&dp6_ptr->u.ic.Service);
-                InfoBytes2 = gdth_readl(&dp6_ptr->u.ic.Info[1]);
-            }
+            ha->status = gdth_readw(&dp6_ptr->u.ic.Status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
         } else                                      /* no error */
-            CmdStatus = S_OK;
-        InfoBytes = gdth_readl(&dp6_ptr->u.ic.Info[0]);
-        if (gdth_polling)                           /* init. -> more info */
-            InfoBytes2 = gdth_readl(&dp6_ptr->u.ic.Info[1]);
+            ha->status = S_OK;
+        ha->info = gdth_readl(&dp6_ptr->u.ic.Info[0]);
+        ha->service = gdth_readw(&dp6_ptr->u.ic.Service);
+        ha->info2 = gdth_readl(&dp6_ptr->u.ic.Info[1]);
+
         gdth_writeb(0xff, &dp6_ptr->io.irqdel);     /* acknowledge interrupt */
         gdth_writeb(0, &dp6_ptr->u.ic.Cmd_Index);   /* reset command index */
         gdth_writeb(0, &dp6_ptr->io.Sema1);         /* reset status semaphore */
     } else if (ha->type == GDT_PCINEW) {
         if (IStatus & 0x80) {                       /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = inw(PTR2USHORT(&ha->plx->status));
-            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
-            if (IStatus == ASYNCINDEX) {            /* async. event ? */
-                Service = inw(PTR2USHORT(&ha->plx->service));
-                InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
-            }
+            ha->status = inw(PTR2USHORT(&ha->plx->status));
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
         } else
-            CmdStatus = S_OK;
+            ha->status = S_OK;
+        ha->info = inl(PTR2USHORT(&ha->plx->info[0]));
+        ha->service = inw(PTR2USHORT(&ha->plx->service));
+        ha->info2 = inl(PTR2USHORT(&ha->plx->info[1]));
 
-        InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
-        if (gdth_polling)                           /* init. -> more info */
-            InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
         outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); 
         outb(0x00, PTR2USHORT(&ha->plx->sema1_reg)); 
     } else if (ha->type == GDT_PCIMPR) {
         dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
         if (IStatus & 0x80) {                       /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = gdth_readw(&dp6m_ptr->i960r.status);
-            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
-            if (IStatus == ASYNCINDEX) {            /* async. event ? */
-                Service = gdth_readw(&dp6m_ptr->i960r.service);
-                InfoBytes2 = gdth_readl(&dp6m_ptr->i960r.info[1]);
-            }
+            ha->status = gdth_readw(&dp6m_ptr->i960r.status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
         } else                                      /* no error */
-            CmdStatus = S_OK;
-        InfoBytes = gdth_readl(&dp6m_ptr->i960r.info[0]);
-        if (gdth_polling)                           /* init. -> more info */
-            InfoBytes2 = gdth_readl(&dp6m_ptr->i960r.info[1]);
+            ha->status = S_OK;
+        ha->info = gdth_readl(&dp6m_ptr->i960r.info[0]);
+        ha->service = gdth_readw(&dp6m_ptr->i960r.service);
+        ha->info2 = gdth_readl(&dp6m_ptr->i960r.info[1]);
+
+        /* event string */
+        if (IStatus == ASYNCINDEX) {
+            if (ha->service != SCREENSERVICE &&
+                (ha->fw_vers & 0xff) >= 0x1a) {
+                ha->dvr.severity =   
+                    gdth_readb(&((gdt6m_dpram_str *)ha->brd)->i960r.severity);
+                for (i = 0; i < 256; ++i) {
+                    ha->dvr.event_string[i] = gdth_readb
+                        (&((gdt6m_dpram_str *)ha->brd)->i960r.evt_str[i]);
+                    if (ha->dvr.event_string[i] == 0)
+                        break;
+                }
+            }
+        }
         gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
         gdth_writeb(0, &dp6m_ptr->i960r.sema1_reg);
     } else {
@@ -2920,10 +3038,7 @@
     }
 
     TRACE(("gdth_interrupt() index %d stat %d info %d\n",
-           IStatus,CmdStatus,InfoBytes));
-    ha->status = CmdStatus;
-    ha->info   = InfoBytes;
-    ha->info2  = InfoBytes2;
+           IStatus,ha->status,ha->info));
 
     if (gdth_from_wait) {
         wait_hanum = hanum;
@@ -2932,7 +3047,7 @@
 
     if (IStatus == ASYNCINDEX) {
         TRACE2(("gdth_interrupt() async. event\n"));
-        gdth_async_event(hanum,Service);
+        gdth_async_event(hanum);
         if (!gdth_polling)
             GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags);
         gdth_next(hanum);
@@ -2941,9 +3056,9 @@
 
     if (IStatus == SPEZINDEX) {
         TRACE2(("Service unknown or not initialized !\n"));
-        dvr.size = sizeof(dvr.eu.driver);
-        dvr.eu.driver.ionode = hanum;
-        gdth_store_event(ha, ES_DRIVER, 4, &dvr);
+        ha->dvr.size = sizeof(ha->dvr.eu.driver);
+        ha->dvr.eu.driver.ionode = hanum;
+        gdth_store_event(ha, ES_DRIVER, 4, &ha->dvr);
         if (!gdth_polling)
             GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags);
         return;
@@ -2953,10 +3068,10 @@
     ha->cmd_tab[IStatus-2].cmnd = UNUSED_CMND;
     if (scp == UNUSED_CMND) {
         TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
-        dvr.size = sizeof(dvr.eu.driver);
-        dvr.eu.driver.ionode = hanum;
-        dvr.eu.driver.index = IStatus;
-        gdth_store_event(ha, ES_DRIVER, 1, &dvr);
+        ha->dvr.size = sizeof(ha->dvr.eu.driver);
+        ha->dvr.eu.driver.ionode = hanum;
+        ha->dvr.eu.driver.index = IStatus;
+        gdth_store_event(ha, ES_DRIVER, 1, &ha->dvr);
         if (!gdth_polling)
             GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags);
         return;
@@ -2987,9 +3102,7 @@
     register gdth_ha_str *ha;
     gdth_msg_str *msg;
     gdth_cmd_str *cmdp;
-    char c='\r';
-    ushort i;
-    gdth_evt_data dvr;
+    unchar b;
 
     ha   = HADATA(gdth_ctr_tab[hanum]);
     cmdp = ha->pccb;
@@ -3017,11 +3130,11 @@
             cmdp->OpCode        = GDT_READ;
             cmdp->BoardNode     = LOCALBOARD;
             cmdp->u.screen.reserved  = 0;
-            cmdp->u.screen.msg_handle= msg->msg_handle;
-            cmdp->u.screen.msg_addr  = virt_to_bus(msg);
+            cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
+            cmdp->u.screen.su.msg.msg_addr  = virt_to_bus(msg);
             ha->scratch_busy = TRUE;
             ha->cmd_offs_dpmem = 0;
-            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) 
                 + sizeof(ulong32);
             ha->cmd_cnt = 0;
             gdth_copy_command(hanum);
@@ -3030,22 +3143,19 @@
         }
 
         if (msg->msg_answer && msg->msg_alen) {
-            for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
-                /* getchar() ?? */           
-                /* .. */
-                if (c == '\r')
-                    break;
-                msg->msg_text[i] = c; 
-            }
-            msg->msg_alen -= i;
-            if (c!='\r' && msg->msg_alen!=0) {
-                msg->msg_answer = 1;
-                msg->msg_ext    = 1;
+            /* default answers (getchar() not possible) */
+            if (msg->msg_alen == 1) {
+                msg->msg_alen = 0;
+                msg->msg_len = 1;
+                msg->msg_text[0] = 0;
             } else {
-                msg->msg_ext    = 0;
-                msg->msg_answer = 0;
+                msg->msg_alen -= 2;
+                msg->msg_len = 2;
+                msg->msg_text[0] = 1;
+                msg->msg_text[1] = 0;
             }
-            msg->msg_len = i;
+            msg->msg_ext    = 0;
+            msg->msg_answer = 0;
             while (gdth_test_busy(hanum))
                 gdth_delay(0);
             cmdp->Service       = SCREENSERVICE;
@@ -3055,11 +3165,11 @@
             cmdp->OpCode        = GDT_WRITE;
             cmdp->BoardNode     = LOCALBOARD;
             cmdp->u.screen.reserved  = 0;
-            cmdp->u.screen.msg_handle= msg->msg_handle;
-            cmdp->u.screen.msg_addr  = virt_to_bus(msg);
+            cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
+            cmdp->u.screen.su.msg.msg_addr  = virt_to_bus(msg);
             ha->scratch_busy = TRUE;
             ha->cmd_offs_dpmem = 0;
-            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) 
                 + sizeof(ulong32);
             ha->cmd_cnt = 0;
             gdth_copy_command(hanum);
@@ -3069,8 +3179,9 @@
         printk("\n");
 
     } else {
-        if (scp->SCp.Status == -1 && scp->channel != ha->virt_bus) {
-            ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[scp->target]--;
+        b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
+        if (scp->SCp.Status == -1 && b != ha->virt_bus) {
+            ha->raw[BUS_L2P(ha,b)].io_cnt[scp->target]--;
         }
         /* cache or raw service */
         if (ha->status == S_OK) {
@@ -3107,6 +3218,7 @@
                     scp->sense_buffer[0] = 0x70;
                     scp->sense_buffer[2] = UNIT_ATTENTION;
                     scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+                    scp->SCp.Message = (int)(ha->info<<16|S_BSY);
                 } else {
                     scp->SCp.Status = -1;
                     /* retry */
@@ -3121,6 +3233,7 @@
                     ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED;
                 }           
                 scp->result = DID_OK << 16;
+                scp->sense_buffer[0] = 0;
             }
         } else if (ha->status == S_BSY) {
             TRACE2(("Controller busy -> retry !\n"));
@@ -3131,9 +3244,6 @@
             return 2;
         } else {
             scp->SCp.Message = (int)((ha->info<<16)|ha->status);
-            memset((char*)scp->sense_buffer,0,16);
-            scp->sense_buffer[0] = 0x70;
-            scp->sense_buffer[2] = NOT_READY;
 
             if (scp->SCp.Status != -1) {
                 TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n",
@@ -3145,34 +3255,44 @@
                     scp->SCp.this_residual = HIGH_PRI;
                     return 2;
                 }
+                memset((char*)scp->sense_buffer,0,16);
+                scp->sense_buffer[0] = 0x70;
+                scp->sense_buffer[2] = NOT_READY;
                 scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
             } else if (scp->cmnd[0] == RESERVE ||
                        scp->cmnd[0] == RELEASE) {
+                memset((char*)scp->sense_buffer,0,16);
+                scp->sense_buffer[0] = 0x70;
+                scp->sense_buffer[2] = NOT_READY;
                 scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
             } else if (service == CACHESERVICE) {
-		if (ha->status == S_CACHE_UNKNOWN &&
-		    (ha->hdr[scp->target].cluster_type & 
-		     CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) {
-		    /* bus reset -> force GDT_CLUST_INFO */
-		    ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED;
-		}
+                if (ha->status == S_CACHE_UNKNOWN &&
+                    (ha->hdr[scp->target].cluster_type & 
+                     CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) {
+                    /* bus reset -> force GDT_CLUST_INFO */
+                    ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED;
+                }
+                memset((char*)scp->sense_buffer,0,16);
+                scp->sense_buffer[0] = 0x70;
+                scp->sense_buffer[2] = NOT_READY;
                 scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
 #if LINUX_VERSION_CODE >= 0x010300
                 if (scp->done != gdth_scsi_done)
 #endif  
                 {
-                    dvr.size = sizeof(dvr.eu.sync);
-                    dvr.eu.sync.ionode  = hanum;
-                    dvr.eu.sync.service = service;
-                    dvr.eu.sync.status  = ha->status;
-                    dvr.eu.sync.info    = ha->info;
-                    dvr.eu.sync.hostdrive = scp->target;
+                    ha->dvr.size = sizeof(ha->dvr.eu.sync);
+                    ha->dvr.eu.sync.ionode  = hanum;
+                    ha->dvr.eu.sync.service = service;
+                    ha->dvr.eu.sync.status  = ha->status;
+                    ha->dvr.eu.sync.info    = ha->info;
+                    ha->dvr.eu.sync.hostdrive = scp->target;
                     if (ha->status >= 0x8000)
-                        gdth_store_event(ha, ES_SYNC, 0, &dvr);
+                        gdth_store_event(ha, ES_SYNC, 0, &ha->dvr);
                     else
-                        gdth_store_event(ha, ES_SYNC, service, &dvr);
+                        gdth_store_event(ha, ES_SYNC, service, &ha->dvr);
                 }
             } else {
+                /* sense buffer filled from controller firmware (DMA) */
                 if (ha->status!=S_RAW_SCSI || ha->info>=0x100) {
                     scp->result = DID_BAD_TARGET << 16;
                 } else {
@@ -3336,12 +3456,17 @@
         "GDT HA %u, Uncorrectable DRAM error detected with ECC",
 /*72*/  "\011\000\002\012\001\013\001\014\001"
         "GDT HA %u, SCSI bus %u, ID %u, LUN %u: reassigning block",
+/*73*/  "\005\000\002\006\002"
+        "GDT HA %u, Host drive %u resetted locally",
+/*74*/  "\005\000\002\006\002"
+        "GDT HA %u, Host drive %u resetted remotely",
+/*75*/  "\003\000\002"
+        "GDT HA %u, async. status 75 unknown",
 };
 
 
-static int gdth_async_event(int hanum,int service)
+static int gdth_async_event(int hanum)
 {
-    gdth_evt_data dvr;
     gdth_ha_str *ha;
     gdth_msg_str *msg;
     gdth_cmd_str *cmdp;
@@ -3351,9 +3476,9 @@
     cmdp= ha->pccb;
     msg = (gdth_msg_str *)ha->pscratch;
     TRACE2(("gdth_async_event() ha %d serv %d\n",
-            hanum,service));
+            hanum,ha->service));
 
-    if (service == SCREENSERVICE) {
+    if (ha->service == SCREENSERVICE) {
         if (ha->status == MSG_REQUEST) {
             while (gdth_test_busy(hanum))
                 gdth_delay(0);
@@ -3364,11 +3489,11 @@
             cmdp->OpCode        = GDT_READ;
             cmdp->BoardNode     = LOCALBOARD;
             cmdp->u.screen.reserved  = 0;
-            cmdp->u.screen.msg_handle= MSG_INV_HANDLE;
-            cmdp->u.screen.msg_addr  = virt_to_bus(msg);
+            cmdp->u.screen.su.msg.msg_handle= MSG_INV_HANDLE;
+            cmdp->u.screen.su.msg.msg_addr  = virt_to_bus(msg);
             ha->scratch_busy = TRUE;
             ha->cmd_offs_dpmem = 0;
-            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) 
                 + sizeof(ulong32);
             ha->cmd_cnt = 0;
             gdth_copy_command(hanum);
@@ -3383,14 +3508,29 @@
         }
 
     } else {
-        dvr.size = sizeof(dvr.eu.async);
-        dvr.eu.async.ionode   = hanum;
-        dvr.eu.async.service = service;
-        dvr.eu.async.status  = ha->status;
-        dvr.eu.async.info    = ha->info;
-        *(ulong32 *)dvr.eu.async.scsi_coord  = ha->info2;
-        gdth_store_event(ha, ES_ASYNC, service, &dvr);
-        gdth_log_event( &dvr, NULL );
+        if (ha->type == GDT_PCIMPR && 
+            (ha->fw_vers & 0xff) >= 0x1a) {
+            ha->dvr.size = 0;
+            ha->dvr.eu.async.ionode = hanum;
+            ha->dvr.eu.async.status  = ha->status;
+            /* severity and event_string already set! */
+        } else {        
+            ha->dvr.size = sizeof(ha->dvr.eu.async);
+            ha->dvr.eu.async.ionode   = hanum;
+            ha->dvr.eu.async.service = ha->service;
+            ha->dvr.eu.async.status  = ha->status;
+            ha->dvr.eu.async.info    = ha->info;
+            *(ulong32 *)ha->dvr.eu.async.scsi_coord  = ha->info2;
+        }
+        gdth_store_event( ha, ES_ASYNC, ha->service, &ha->dvr );
+        gdth_log_event( &ha->dvr, NULL );
+    
+        /* new host drive from expand? */
+        if (ha->service == CACHESERVICE && ha->status == 56) {
+            TRACE2(("gdth_async_event(): new host drive %d created\n",
+                    (ushort)ha->info));
+            gdth_analyse_hdrive(hanum, (ushort)ha->info);
+        }   
     }
     return 1;
 }
@@ -3402,7 +3542,14 @@
     int i,j;
 
     TRACE2(("gdth_log_event()\n"));
-    if (dvr->eu.async.service == CACHESERVICE && 
+    if (dvr->size == 0) {
+        if (buffer == NULL) {
+            printk("Adapter %d: %s\n",dvr->eu.async.ionode,dvr->event_string); 
+        } else {
+            sprintf(buffer,"Adapter %d: %s\n",
+                dvr->eu.async.ionode,dvr->event_string); 
+        }
+    } else if (dvr->eu.async.service == CACHESERVICE && 
         INDEX_OK(dvr->eu.async.status, async_cache_tab)) {
         TRACE2(("GDT: Async. event cache service, event no.: %d\n",
                 dvr->eu.async.status));
@@ -3474,7 +3621,7 @@
 #endif
 
 
-__initfunc (int gdth_detect(Scsi_Host_Template *shtp))
+GDTH_INITFUNC(int, gdth_detect(Scsi_Host_Template *shtp))
 {
     struct Scsi_Host *shp;
     gdth_ha_str *ha;
@@ -3522,6 +3669,9 @@
                 scsi_unregister(shp);
                 continue;
             }
+#ifdef __ia64__
+            break;
+#else
             /* controller found and initialized */
             printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n",
                    isa_bios,ha->irq,ha->drq);
@@ -3559,7 +3709,12 @@
             NUMDATA(shp)->busnum= 0;
 
             ha->pccb = CMDDATA(shp);
+#if LINUX_VERSION_CODE >= 0x020322
+            ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 
+                                                     GDTH_SCRATCH_ORD);
+#else
             ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+#endif
             ha->scratch_busy = FALSE;
             ha->req_first = NULL;
             ha->tid_cnt = MAX_HDRIVES;
@@ -3574,7 +3729,11 @@
                 --gdth_ctr_count;
                 --gdth_ctr_vcount;
                 if (ha->pscratch != NULL)
+#if LINUX_VERSION_CODE >= 0x020322
+                    free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
+#else
                     scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+#endif
 #if LINUX_VERSION_CODE >= 0x010346 
                 free_irq(ha->irq,NULL);
 #else
@@ -3590,21 +3749,26 @@
 #if LINUX_VERSION_CODE >= 0x020000
             shp->max_id      = ha->tid_cnt;
             shp->max_lun     = MAXLUN;
-            shp->max_channel = ha->bus_cnt;
-#else
-            /* register addit. SCSI channels as virtual controllers */
-            for (b=1; b<ha->bus_cnt+1; ++b) {
-                shp = scsi_register(shtp,sizeof(gdth_num_str));
-                shp->unchecked_isa_dma = 1;
-                shp->irq = ha->irq;
-                shp->dma_channel = ha->drq;
-                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
-                NUMDATA(shp)->hanum = (ushort)hanum;
-                NUMDATA(shp)->busnum = b;
-            }
+            shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+            if (virt_ctr)  
 #endif
+            {
+                virt_ctr = 1;
+                /* register addit. SCSI channels as virtual controllers */
+                for (b = 1; b < ha->bus_cnt + 1; ++b) {
+                    shp = scsi_register(shtp,sizeof(gdth_num_str));
+                    shp->unchecked_isa_dma = 1;
+                    shp->irq = ha->irq;
+                    shp->dma_channel = ha->drq;
+                    gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                    NUMDATA(shp)->hanum = (ushort)hanum;
+                    NUMDATA(shp)->busnum = b;
+                }
+            }  
+
             GDTH_INIT_LOCK_HA(ha);
             gdth_enable_int(hanum);
+#endif /* !__ia64__ */
         }
     }
 
@@ -3646,7 +3810,12 @@
                     NUMDATA(shp)->hanum));
 
             ha->pccb = CMDDATA(shp);
+#if LINUX_VERSION_CODE >= 0x020322
+            ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 
+                                                     GDTH_SCRATCH_ORD);
+#else
             ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+#endif
             ha->scratch_busy = FALSE;
             ha->req_first = NULL;
             ha->tid_cnt = MAX_HDRIVES;
@@ -3661,7 +3830,11 @@
                 --gdth_ctr_count;
                 --gdth_ctr_vcount;
                 if (ha->pscratch != NULL)
+#if LINUX_VERSION_CODE >= 0x020322
+                    free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
+#else
                     scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+#endif
 #if LINUX_VERSION_CODE >= 0x010346 
                 free_irq(ha->irq,NULL);
 #else
@@ -3677,22 +3850,23 @@
 #if LINUX_VERSION_CODE >= 0x020000
             shp->max_id      = ha->tid_cnt;
             shp->max_lun     = MAXLUN;
-            shp->max_channel = ha->bus_cnt;
-#else
-            /* register addit. SCSI channels as virtual controllers */
-            for (b=1; b<ha->bus_cnt+1; ++b) {
-                shp = scsi_register(shtp,sizeof(gdth_num_str));
-                shp->unchecked_isa_dma = 0;
-                shp->irq = ha->irq;
-                shp->dma_channel = 0xff;
-                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
-                NUMDATA(shp)->hanum = (ushort)hanum;
-                NUMDATA(shp)->busnum = b;
-                TRACE2(("EISA detect Bus %d: shp %x hanum %d\n",
-                        NUMDATA(shp)->busnum,(ulong32)shp,
-                        NUMDATA(shp)->hanum));
-            }
+            shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+            if (virt_ctr)  
 #endif
+            {
+                virt_ctr = 1;
+                /* register addit. SCSI channels as virtual controllers */
+                for (b = 1; b < ha->bus_cnt + 1; ++b) {
+                    shp = scsi_register(shtp,sizeof(gdth_num_str));
+                    shp->unchecked_isa_dma = 0;
+                    shp->irq = ha->irq;
+                    shp->dma_channel = 0xff;
+                    gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                    NUMDATA(shp)->hanum = (ushort)hanum;
+                    NUMDATA(shp)->busnum = b;
+                }
+            }  
+
             GDTH_INIT_LOCK_HA(ha);
             gdth_enable_int(hanum);
         }
@@ -3745,7 +3919,12 @@
             NUMDATA(shp)->busnum= 0;
 
             ha->pccb = CMDDATA(shp);
+#if LINUX_VERSION_CODE >= 0x020322
+            ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 
+                                                     GDTH_SCRATCH_ORD);
+#else
             ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA);
+#endif
             ha->scratch_busy = FALSE;
             ha->req_first = NULL;
             ha->tid_cnt = pcistr[ctr].device_id >= 0x200 ? MAXID : MAX_HDRIVES;
@@ -3760,7 +3939,11 @@
                 --gdth_ctr_count;
                 --gdth_ctr_vcount;
                 if (ha->pscratch != NULL)
+#if LINUX_VERSION_CODE >= 0x020322
+                    free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
+#else
                     scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+#endif
 #if LINUX_VERSION_CODE >= 0x010346 
                 free_irq(ha->irq,NULL);
 #else
@@ -3776,19 +3959,23 @@
 #if LINUX_VERSION_CODE >= 0x020000
             shp->max_id      = ha->tid_cnt;
             shp->max_lun     = MAXLUN;
-            shp->max_channel = ha->bus_cnt;
-#else
-            /* register addit. SCSI channels as virtual controllers */
-            for (b=1; b<ha->bus_cnt+1; ++b) {
-                shp = scsi_register(shtp,sizeof(gdth_num_str));
-                shp->unchecked_isa_dma = 0;
-                shp->irq = ha->irq;
-                shp->dma_channel = 0xff;
-                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
-                NUMDATA(shp)->hanum = (ushort)hanum;
-                NUMDATA(shp)->busnum = b;
-            }
+            shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+            if (virt_ctr)  
 #endif
+            {
+                virt_ctr = 1;
+                /* register addit. SCSI channels as virtual controllers */
+                for (b = 1; b < ha->bus_cnt + 1; ++b) {
+                    shp = scsi_register(shtp,sizeof(gdth_num_str));
+                    shp->unchecked_isa_dma = 0;
+                    shp->irq = ha->irq;
+                    shp->dma_channel = 0xff;
+                    gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                    NUMDATA(shp)->hanum = (ushort)hanum;
+                    NUMDATA(shp)->busnum = b;
+                }
+            }  
+
             GDTH_INIT_LOCK_HA(ha);
             gdth_enable_int(hanum);
         }
@@ -3833,10 +4020,16 @@
             free_irq(shp->irq);
 #endif
         }
+#ifndef __ia64__
         if (shp->dma_channel != 0xff) {
             free_dma(shp->dma_channel);
         }
+#endif
+#if LINUX_VERSION_CODE >= 0x020322
+        free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD);
+#else
         scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH);
+#endif
         gdth_ctr_released++;
         TRACE2(("gdth_release(): HA %d of %d\n", 
                 gdth_ctr_released, gdth_ctr_count));
@@ -3937,26 +4130,26 @@
     gdth_ha_str *ha;
     ulong flags;
     Scsi_Cmnd *cmnd;
+    unchar b;
 
     TRACE2(("gdth_eh_bus_reset()\n"));
     hanum = NUMDATA(scp->host)->hanum;
+    b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
     ha    = HADATA(gdth_ctr_tab[hanum]);
 
     /* clear command tab */
     GDTH_LOCK_HA(ha, flags);
     for (i = 0; i < GDTH_MAXCMDS; ++i) {
         cmnd = ha->cmd_tab[i].cmnd;
-        if (!SPECIAL_SCP(cmnd) && cmnd->channel == scp->channel)
+        if (!SPECIAL_SCP(cmnd) && cmnd->channel == b)
             ha->cmd_tab[i].cmnd = UNUSED_CMND;
     }
     GDTH_UNLOCK_HA(ha, flags);
 
-    if (scp->channel == ha->virt_bus) {
+    if (b == ha->virt_bus) {
         /* host drives */
         for (i = 0; i < MAX_HDRIVES; ++i) {
-            if (ha->hdr[i].present && 
-                (ha->hdr[i].cluster_type & CLUSTER_RESERVED) 
-                == CLUSTER_RESERVED) { 
+            if (ha->hdr[i].present) {
                 GDTH_LOCK_HA(ha, flags);
                 gdth_polling = TRUE;
                 while (gdth_test_busy(hanum))
@@ -3972,12 +4165,12 @@
         /* raw devices */
         GDTH_LOCK_HA(ha, flags);
         for (i = 0; i < MAXID; ++i)
-            ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0;
+            ha->raw[BUS_L2P(ha,b)].io_cnt[i] = 0;
         gdth_polling = TRUE;
         while (gdth_test_busy(hanum))
             gdth_delay(0);
         gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS,
-                          BUS_L2P(ha,scp->channel), 0, 0);
+                          BUS_L2P(ha,b), 0, 0);
         gdth_polling = FALSE;
         GDTH_UNLOCK_HA(ha, flags);
     }
@@ -3997,17 +4190,17 @@
 int gdth_bios_param(Disk *disk,int dev,int *ip)
 #endif
 {
-    unchar t;
+    unchar b, t;
     int hanum;
     gdth_ha_str *ha;
 
     hanum = NUMDATA(disk->device->host)->hanum;
+    b = virt_ctr ? NUMDATA(disk->device->host)->busnum : disk->device->channel;
     t = disk->device->id;
-    TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", 
-            hanum, disk->device->channel, t));
+    TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t)); 
     ha = HADATA(gdth_ctr_tab[hanum]);
 
-    if (disk->device->channel != ha->virt_bus || ha->hdr[t].heads == 0) {
+    if (b != ha->virt_bus || ha->hdr[t].heads == 0) {
         /* raw device or host drive without mapping information */
         TRACE2(("Evaluate mapping\n"));
         gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]);
@@ -4075,19 +4268,32 @@
 {
     int             i;
     gdth_ha_str     *ha;
+    gdth_cmd_str    gdtcmd;
+#if LINUX_VERSION_CODE >= 0x020322
+    Scsi_Cmnd       *scp;
+    Scsi_Device     *sdev;
+#else
     Scsi_Cmnd       scp;
     Scsi_Device     sdev;
-    gdth_cmd_str    gdtcmd;
-    char            cmnd[12];   
+#endif
+    char            cmnd[MAX_COMMAND_SIZE];   
+    memset(cmnd, 0xff, 12);
 
     TRACE2(("gdth_flush() hanum %d\n",hanum));
     ha = HADATA(gdth_ctr_tab[hanum]);
+
+#if LINUX_VERSION_CODE >= 0x020322
+    sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+    scp  = scsi_allocate_device(sdev, 1, FALSE);
+    scp->cmd_len = 12;
+    scp->use_sg = 0;
+#else
     memset(&sdev,0,sizeof(Scsi_Device));
     memset(&scp, 0,sizeof(Scsi_Cmnd));
-    memset(cmnd, 0xff, 12);
     sdev.host = scp.host = gdth_ctr_tab[hanum];
     sdev.id = scp.target = sdev.host->this_id;
     scp.device = &sdev;
+#endif
 
     for (i = 0; i < MAX_HDRIVES; ++i) {
         if (ha->hdr[i].present) {
@@ -4098,9 +4304,17 @@
             gdtcmd.u.cache.BlockNo = 1;
             gdtcmd.u.cache.sg_canz = 0;
             TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i));
+#if LINUX_VERSION_CODE >= 0x020322
+            gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#else
             gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+#endif
         }
     }
+#if LINUX_VERSION_CODE >= 0x020322
+    scsi_release_command(scp);
+    scsi_free_host_dev(sdev);
+#endif
 }
 
 /* shutdown routine */
@@ -4112,10 +4326,15 @@
 {
     int             hanum;
 #ifndef __alpha__
+    gdth_cmd_str    gdtcmd;
+#if LINUX_VERSION_CODE >= 0x020322
+    Scsi_Cmnd       *scp;
+    Scsi_Device     *sdev;
+#else
     Scsi_Cmnd       scp;
     Scsi_Device     sdev;
-    gdth_cmd_str    gdtcmd;
-    char            cmnd[12];
+#endif
+    char            cmnd[MAX_COMMAND_SIZE];   
 #endif
 
 #if LINUX_VERSION_CODE >= 0x020100
@@ -4136,19 +4355,32 @@
 
 #ifndef __alpha__
         /* controller reset */
+        memset(cmnd, 0xff, 12);
+#if LINUX_VERSION_CODE >= 0x020322
+        sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+        scp  = scsi_allocate_device(sdev, 1, FALSE);
+        scp->cmd_len = 12;
+        scp->use_sg = 0;
+#else
         memset(&sdev,0,sizeof(Scsi_Device));
         memset(&scp, 0,sizeof(Scsi_Cmnd));
-        memset(cmnd, 0xff, 12);
         sdev.host = scp.host = gdth_ctr_tab[hanum];
         sdev.id = scp.target = sdev.host->this_id;
         scp.device = &sdev;
+#endif
 
         gdtcmd.BoardNode = LOCALBOARD;
         gdtcmd.Service = CACHESERVICE;
         gdtcmd.OpCode = GDT_RESET;
         TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+#if LINUX_VERSION_CODE >= 0x020322
+        gdth_do_cmd(scp, &gdtcmd, cmnd, 10);
+        scsi_release_command(scp);
+        scsi_free_host_dev(sdev);
+#else
         gdth_do_cmd(&scp, &gdtcmd, cmnd, 10);
 #endif
+#endif
     }
     printk("Done.\n");
 
@@ -4156,7 +4388,9 @@
     del_timer(&gdth_timer);
 #endif
 #if LINUX_VERSION_CODE >= 0x020100
+#if LINUX_VERSION_CODE < 0x020322
     unregister_reboot_notifier(&gdth_notifier);
+#endif
     return NOTIFY_OK;
 #endif
 }
@@ -4164,7 +4398,7 @@
 
 
 /* called from init/main.c */
-__initfunc (void gdth_setup(char *str,int *ints))
+GDTH_INITFUNC(void, gdth_setup(char *str,int *ints))
 {
     int i, argc;
     char *cur_str, *argv;
@@ -4207,6 +4441,8 @@
             max_ids = val;
         else if (!strncmp(argv, "rescan:", 7))
             rescan = val;
+        else if (!strncmp(argv, "virt_ctr:", 9))
+            virt_ctr = val;
         else if (!strncmp(argv, "reserve_list:", 13)) {
             reserve_list[0] = val;
             for (i = 1; i < MAX_RES_ARGS; i++) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth.h linux.18p17/drivers/scsi/gdth.h
--- linux.vanilla/drivers/scsi/gdth.h	Wed May  3 22:31:20 2000
+++ linux.18p17/drivers/scsi/gdth.h	Thu Oct 19 01:37:49 2000
@@ -10,7 +10,7 @@
  *
  * <achim@vortex.de>
  *
- * $Id: gdth.h,v 1.24 1999/11/02 13:43:49 achim Exp $
+ * $Id: gdth.h,v 1.33 2000/07/24 09:29:25 achim Exp $
  */
 
 #include <linux/version.h>
@@ -29,9 +29,9 @@
 /* defines, macros */
 
 /* driver version */
-#define GDTH_VERSION_STR        "1.17"
+#define GDTH_VERSION_STR        "1.23"
 #define GDTH_VERSION            1
-#define GDTH_SUBVERSION         17
+#define GDTH_SUBVERSION         23
 
 /* protocol version */
 #define PROTOCOL_VERSION        1
@@ -126,7 +126,8 @@
 #endif
 
 /* limits */
-#define GDTH_SCRATCH    4096                    /* 4KB scratch buffer */
+#define GDTH_SCRATCH    PAGE_SIZE               /* 4KB scratch buffer */
+#define GDTH_SCRATCH_ORD 0                      /* order 0 means 1 page */
 #define GDTH_MAXCMDS    124
 #define GDTH_MAXC_P_L   16                      /* max. cmds per lun */
 #define GDTH_MAX_RAW    2                       /* max. cmds per raw device */
@@ -180,7 +181,7 @@
 #define CLUSTER_RESERVED      4
 #define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED)
 
-/* cache/raw service commands */
+/* commands for all services, cache service */
 #define GDT_INIT        0                       /* service initialization */
 #define GDT_READ        1                       /* read command */
 #define GDT_WRITE       2                       /* write command */
@@ -202,7 +203,7 @@
 #define GDT_RW_ATTRIBS  23                      /* R/W attribs (write thru,..)*/
 #define GDT_CLUST_RESET 24                      /* releases the cluster drives*/
 
-/* additional raw service commands */
+/* raw service commands */
 #define GDT_RESERVE     14                      /* reserve dev. to raw serv. */
 #define GDT_RELEASE     15                      /* release device */
 #define GDT_RESERVE_ALL 16                      /* reserve all devices */
@@ -211,6 +212,9 @@
 #define GDT_SCAN_START  19                      /* start device scan */
 #define GDT_SCAN_END    20                      /* stop device scan */  
 
+/* screen service commands */
+#define GDT_REALTIME    3                       /* realtime clock to screens. */
+
 /* IOCTL command defines */
 #define SCSI_DR_INFO    0x00                    /* SCSI drive info */                   
 #define SCSI_CHAN_CNT   0x05                    /* SCSI channel count */   
@@ -235,18 +239,9 @@
 #define IO_CHANNEL      0x00020000L             /* default IO channel */
 #define INVALID_CHANNEL 0x0000ffffL             /* invalid channel */
 
-/* IOCTLs */
-#define GDTIOCTL_MASK       ('J'<<8)
-#define GDTIOCTL_GENERAL    (GDTIOCTL_MASK | 0) /* general IOCTL */
-#define GDTIOCTL_DRVERS     (GDTIOCTL_MASK | 1) /* get driver version */
-#define GDTIOCTL_CTRTYPE    (GDTIOCTL_MASK | 2) /* get controller type */
-#define GDTIOCTL_CTRCNT     (GDTIOCTL_MASK | 5) /* get controller count */
-#define GDTIOCTL_LOCKDRV    (GDTIOCTL_MASK | 6) /* lock host drive */
-#define GDTIOCTL_LOCKCHN    (GDTIOCTL_MASK | 7) /* lock channel */
-#define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
-
 /* service errors */
 #define S_OK            1                       /* no error */
+#define S_GENERR        6                       /* general error */
 #define S_BSY           7                       /* controller busy */
 #define S_CACHE_UNKNOWN 12                      /* cache serv.: drive unknown */
 #define S_RAW_SCSI      12                      /* raw serv.: target error */
@@ -263,10 +258,8 @@
 #define HIGH_PRI        0x08
 
 /* data directions */
-#ifndef HOSTS_C
-#define DATA_IN         0x01000000L             /* data from target */
-#define DATA_OUT        0x00000000L             /* data to target */
-#endif
+#define GDTH_DATA_IN    0x01000000L             /* data from target */
+#define GDTH_DATA_OUT   0x00000000L             /* data to target */
 
 /* BMIC registers (EISA controllers) */
 #define ID0REG          0x0c80                  /* board ID */
@@ -472,11 +465,11 @@
 } PACKED gdth_alist_str;
 
 typedef struct {
-    ulong32	entries_avail;			/* allocated entries */
-    ulong32	entries_init;			/* returned entries */
-    ulong32	first_entry;			/* first entry number */
-    ulong32	list_offset;			/* offset of following list */
-    gdth_alist_str list[1];			/* list */
+    ulong32     entries_avail;                  /* allocated entries */
+    ulong32     entries_init;                   /* returned entries */
+    ulong32     first_entry;                    /* first entry number */
+    ulong32     list_offset;                    /* offset of following list */
+    gdth_alist_str list[1];                     /* list */
 } PACKED gdth_arcdl_str;
 
 /* cache info/config IOCTL */
@@ -609,8 +602,13 @@
         } PACKED ioctl;                         /* IOCTL command structure */
         struct {
             ushort      reserved;
-            ulong32     msg_handle;             /* message handle */
-            ulong32     msg_addr;               /* message buffer address */
+            union {
+                struct {
+                    ulong32  msg_handle;        /* message handle */
+                    ulong32  msg_addr;          /* message buffer address */
+                } PACKED msg;
+                unchar       data[12];          /* buffer for rtc data, ... */
+            } su;
         } PACKED screen;                        /* screen service cmd. str. */
         struct {
             ushort      reserved;
@@ -673,6 +671,8 @@
             ulong32         l1, l2, l3, l4;
         } PACKED test;
     } eu;
+    ulong32                 severity;
+    unchar                  event_string[256];          
 } PACKED gdth_evt_data;
 
 typedef struct {
@@ -822,7 +822,9 @@
     unchar              unused5[7];
     unchar              edoor_en_reg;           /* board interrupts enable */
     unchar              unused6[27];
-    ulong32             unused7[1004];          /* size: 4 KB */
+    ulong32             unused7[939];         
+    ulong32             severity;       
+    char                evt_str[256];           /* event string */
 } PACKED gdt6m_i960_regs;
 
 /* DPRAM PCI MPR controllers */
@@ -856,6 +858,7 @@
     ushort              type;                   /* controller class */
     ushort              raw_feat;               /* feat. raw service (s/g,..) */
     ulong32             stype;                  /* controller subtype */
+    ushort              fw_vers;                /* firmware version */
     ushort              cache_feat;             /* feat. cache serv. (s/g,..) */
     ushort              bmic;                   /* BMIC address (EISA) */
     void                *brd;                   /* DPRAM address */
@@ -868,6 +871,7 @@
     unchar              irq;                    /* IRQ */
     unchar              drq;                    /* DRQ (ISA controllers) */
     ushort              status;                 /* command status */
+    ushort              service;                /* service/firmware ver./.. */
     ulong32             info;
     ulong32             info2;                  /* additional info */
     Scsi_Cmnd           *req_first;             /* top of request queue */
@@ -914,6 +918,7 @@
     gdth_cpar_str       cpar;                   /* controller cache par. */
     gdth_bfeat_str      bfeat;                  /* controller features */
     gdth_binfo_str      binfo;                  /* controller info */
+    gdth_evt_data       dvr;                    /* event structure */
 #if LINUX_VERSION_CODE >= 0x02015F
     spinlock_t          smp_lock;
 #endif
@@ -1004,7 +1009,38 @@
 #endif
 const char *gdth_info(struct Scsi_Host *);
 
-#if LINUX_VERSION_CODE >= 0x02015F
+#if LINUX_VERSION_CODE >= 0x020322
+int gdth_bios_param(Disk *,kdev_t,int *);
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+int gdth_eh_abort(Scsi_Cmnd *scp);
+int gdth_eh_device_reset(Scsi_Cmnd *scp);
+int gdth_eh_bus_reset(Scsi_Cmnd *scp);
+int gdth_eh_host_reset(Scsi_Cmnd *scp);
+#define GDTH { proc_name:       "gdth",                          \
+               proc_info:       gdth_proc_info,                  \
+               name:            "GDT SCSI Disk Array Controller",\
+               detect:          gdth_detect,                     \
+               release:         gdth_release,                    \
+               info:            gdth_info,                       \
+               command:         gdth_command,                    \
+               queuecommand:    gdth_queuecommand,               \
+               eh_abort_handler: gdth_eh_abort,                  \
+               eh_device_reset_handler: gdth_eh_device_reset,    \
+               eh_bus_reset_handler: gdth_eh_bus_reset,          \
+               eh_host_reset_handler: gdth_eh_host_reset,        \
+               abort:           gdth_abort,                      \
+               reset:           gdth_reset,                      \
+               bios_param:      gdth_bios_param,                 \
+               can_queue:       GDTH_MAXCMDS,                    \
+               this_id:         -1,                              \
+               sg_tablesize:    GDTH_MAXSG,                      \
+               cmd_per_lun:     GDTH_MAXC_P_L,                   \
+               present:         0,                               \
+               unchecked_isa_dma: 1,                             \
+               use_clustering:  ENABLE_CLUSTERING,               \
+               use_new_eh_code: 1       /* use new error code */ }    
+
+#elif LINUX_VERSION_CODE >= 0x02015F
 int gdth_bios_param(Disk *,kdev_t,int *);
 extern struct proc_dir_entry proc_scsi_gdth;
 int gdth_proc_info(char *,char **,off_t,int,int,int);
@@ -1035,6 +1071,7 @@
                unchecked_isa_dma: 1,                             \
                use_clustering:  ENABLE_CLUSTERING,               \
                use_new_eh_code: 1       /* use new error code */ }    
+
 #elif LINUX_VERSION_CODE >= 0x010300
 int gdth_bios_param(Disk *,kdev_t,int *);
 extern struct proc_dir_entry proc_scsi_gdth;
@@ -1059,6 +1096,7 @@
                    0,                                   \
                    1,                                   \
                    ENABLE_CLUSTERING}
+
 #else
 int gdth_bios_param(Disk *,int,int *);
 #define GDTH { NULL, NULL,                              \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth_ioctl.h linux.18p17/drivers/scsi/gdth_ioctl.h
--- linux.vanilla/drivers/scsi/gdth_ioctl.h	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/gdth_ioctl.h	Mon Jul 24 10:32:20 2000
@@ -2,7 +2,7 @@
 #define _GDTH_IOCTL_H
 
 /* gdth_ioctl.h
- * $Id: gdth_ioctl.h,v 1.3 1999/05/26 11:49:57 achim Exp $
+ * $Id: gdth_ioctl.h,v 1.6 2000/07/24 09:29:43 achim Exp $
  */
 
 /* IOCTLs */
@@ -18,8 +18,10 @@
 #define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
 #define GDTIOCTL_SCSI       (GDTIOCTL_MASK | 9) /* SCSI command */
 #define GDTIOCTL_RESET_BUS  (GDTIOCTL_MASK |10) /* reset SCSI bus */
+#define GDTIOCTL_RESCAN     (GDTIOCTL_MASK |11) /* rescan host drives */
 
-#define GDTIOCTL_MAGIC      0xaffe0001UL
+#define GDTIOCTL_MAGIC      0xaffe0003UL
+#define EVENT_SIZE          294 
 
 
 /* IOCTL structure (write) */
@@ -46,7 +48,7 @@
         struct {
             int             erase;              /* erase event ? */
             int             handle;
-            unchar          evt[34];            /* event structure */
+            unchar          evt[EVENT_SIZE];    /* event structure */
         } event;
         struct {
             unchar          bus;                /* SCSI bus */
@@ -87,7 +89,7 @@
         } ctrcnt;
         struct {
             int             handle;
-            unchar          evt[34];            /* event structure */
+            unchar          evt[EVENT_SIZE];    /* event structure */
         } event;
         struct {
             unchar          bus;                /* SCSI bus, 0xff: invalid */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth_proc.c linux.18p17/drivers/scsi/gdth_proc.c
--- linux.vanilla/drivers/scsi/gdth_proc.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/gdth_proc.c	Mon Jul 24 10:32:20 2000
@@ -1,5 +1,5 @@
 /* gdth_proc.c 
- * $Id: gdth_proc.c,v 1.16 1999/11/02 13:44:11 achim Exp $
+ * $Id: gdth_proc.c,v 1.22 2000/07/24 09:30:28 achim Exp $
  */
 
 #include "gdth_ioctl.h"
@@ -31,18 +31,30 @@
 static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
 {
     int             ret_val;
+#if LINUX_VERSION_CODE >= 0x020322
+    Scsi_Cmnd       *scp;
+    Scsi_Device     *sdev;
+#else
     Scsi_Cmnd       scp;
     Scsi_Device     sdev;
+#endif
     gdth_iowr_str   *piowr;
 
     TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
     piowr = (gdth_iowr_str *)buffer;
 
+#if LINUX_VERSION_CODE >= 0x020322
+    sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]);
+    scp  = scsi_allocate_device(sdev, 1, FALSE);
+    scp->cmd_len = 12;
+    scp->use_sg = 0;
+#else
     memset(&sdev,0,sizeof(Scsi_Device));
     memset(&scp, 0,sizeof(Scsi_Cmnd));
     sdev.host = scp.host = gdth_ctr_vtab[vh];
     sdev.id = scp.target = sdev.host->this_id;
     scp.device = &sdev;
+#endif
 
     if (length >= 4) {
         if (strncmp(buffer,"gdth",4) == 0) {
@@ -52,23 +64,33 @@
         } else if (piowr->magic == GDTIOCTL_MAGIC) {
             ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
         } else {
-            printk("GDT: Wrong signature: %6s\n",buffer);
+            printk("GDT: Wrong signature: %x\n",piowr->magic);
             ret_val = -EINVAL;
         }
     } else {
         ret_val = -EINVAL;
     }
+#if LINUX_VERSION_CODE >= 0x020322
+    scsi_release_command(scp);
+    scsi_free_host_dev(sdev);
+#endif
     return ret_val;
 }
          
+#if LINUX_VERSION_CODE >= 0x020322
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp)
+#else
 static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+#endif
 {
     int             orig_length, drive, wb_mode;
     int             i, found;
     gdth_ha_str     *ha;
     gdth_cmd_str    gdtcmd;
     gdth_cpar_str   *pcpar;
-    char            cmnd[12];
+
+    char            cmnd[MAX_COMMAND_SIZE];
+    memset(cmnd, 0xff, 12);
 
     TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
     ha = HADATA(gdth_ctr_tab[hanum]);
@@ -76,7 +98,6 @@
     drive = -1;
     wb_mode = 0;
     found = FALSE;
-    memset(cmnd, 0xff, 12);
 
     if (length >= 5 && strncmp(buffer,"flush",5)==0) {
         buffer += 6;
@@ -103,7 +124,11 @@
                 gdtcmd.u.cache.DeviceNo = i;
                 gdtcmd.u.cache.BlockNo = 1;
                 gdtcmd.u.cache.sg_canz = 0;
+#if LINUX_VERSION_CODE >= 0x020322
+                gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#else
                 gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+#endif
             }
         }
         if (!found)
@@ -156,7 +181,11 @@
         gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
         gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
         pcpar->write_back = wb_mode==1 ? 0:1;
+#if LINUX_VERSION_CODE >= 0x020322
+        gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#else
         gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+#endif
         gdth_ioctl_free(hanum);
         printk("Done.\n");
         return(orig_length);
@@ -166,9 +195,14 @@
     return(-EINVAL);
 }
 
+#if LINUX_VERSION_CODE >= 0x020322
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp)
+#else
 static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+#endif
 {
     unchar          i, j;
+    ushort          k, hdr_cnt;
     gdth_ha_str     *ha;
     gdth_iowr_str   *piowr;
     gdth_iord_str   *piord;
@@ -177,9 +211,11 @@
     ulong32         *ppadd, add_size;
     ulong32         *ppadd2, add_size2;
     ulong           flags;
-    char            cmnd[12];   
     gdth_cmd_str    gdtcmd;
 
+    char            cmnd[MAX_COMMAND_SIZE];   
+    memset(cmnd, 0xff, 12);
+
     TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
     ha = HADATA(gdth_ctr_tab[hanum]);
     piowr = (gdth_iowr_str *)buffer;
@@ -187,7 +223,6 @@
     pcmd = NULL;
     ppadd = ppadd2 = NULL;
     add_size = add_size2 = 0;
-    memset(cmnd, 0xff, 12);
 
     if (length < GDTOFFSOF(gdth_iowr_str,iu))
         return(-EINVAL);
@@ -242,8 +277,13 @@
             *ppadd2 = virt_to_bus(piord->iu.general.data+add_size);
         }
         /* do IOCTL */
+#if LINUX_VERSION_CODE >= 0x020322
+        gdth_do_cmd(scp, pcmd, cmnd, piowr->timeout);
+        piord->status = (ulong32)scp->SCp.Message;
+#else
         gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout);
         piord->status = (ulong32)scp.SCp.Message;
+#endif
         break;
 
       case GDTIOCTL_DRVERS:
@@ -387,12 +427,20 @@
             return(-EBUSY);
         piord = (gdth_iord_str *)ha->pscratch;
         piord->size = sizeof(gdth_iord_str);
-        scp.target = scp.device->id = piowr->iu.scsi.target;
-        scp.channel = scp.device->channel = piowr->iu.scsi.bus;
         memcpy(cmnd, piowr->iu.scsi.cmd, 12);
+#if LINUX_VERSION_CODE >= 0x020322
+        scp->target = scp->device->id = piowr->iu.scsi.target;
+        scp->channel = scp->device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus;
+        scp->cmd_len = piowr->iu.scsi.cmd_len;
+        gdth_do_cmd(scp, pcmd, cmnd, piowr->timeout);
+        piord->status = (ulong32)scp->SCp.Message;
+#else
+        scp.target = scp.device->id = piowr->iu.scsi.target;
+        scp.channel = scp.device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus;
         scp.cmd_len = piowr->iu.scsi.cmd_len;
         gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout);
-        piord->status = (ulong32)scp.result;
+        piord->status = (ulong32)scp.SCp.Message;
+#endif
         break;
 
       case GDTIOCTL_RESET_BUS:
@@ -400,8 +448,23 @@
             return(-EBUSY);
         piord = (gdth_iord_str *)ha->pscratch;
         piord->size = sizeof(gdth_iord_str);
-        scp.channel = scp.device->channel = piowr->iu.scsi.bus;
+#if LINUX_VERSION_CODE >= 0x020322
+        scp->channel = scp->device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus;
+        piord->status = (ulong32)gdth_eh_bus_reset( scp );
+        if (piord->status == SUCCESS)
+            piord->status = S_OK;
+        else
+            piord->status = S_GENERR;
+#elif LINUX_VERSION_CODE >= 0x02015F
+        scp.channel = scp.device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus;
         piord->status = (ulong32)gdth_eh_bus_reset( &scp );
+        if (piord->status == SUCCESS)
+            piord->status = S_OK;
+        else
+            piord->status = S_GENERR;
+#else
+        piord->status = S_OK;
+#endif
         break;
 
       case GDTIOCTL_HDRLIST:
@@ -423,22 +486,50 @@
                     gdtcmd.u.cache.DeviceNo = i;
                     gdtcmd.u.cache.BlockNo = 0;
                     gdtcmd.u.cache.sg_canz = 0;
+#if LINUX_VERSION_CODE >= 0x020322
+                    gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                    if ((scp->SCp.Message & 0xffff) == S_OK)
+                        piord->iu.hdr_list[i].cluster_type = 
+                            (unchar)(scp->SCp.Message>>16);
+#else
                     gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
                     if ((scp.SCp.Message & 0xffff) == S_OK)
                         piord->iu.hdr_list[i].cluster_type = 
                             (unchar)(scp.SCp.Message>>16);
-		}
+#endif
+                }
             } else {
                 piord->iu.hdr_list[i].bus = 0xff;
             }
         }
         break;
 
+      case GDTIOCTL_RESCAN:
+        if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ))
+            return(-EBUSY);
+        piord = (gdth_iord_str *)ha->pscratch;
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        GDTH_LOCK_HA(ha, flags);
+        gdth_polling = TRUE;
+        while (gdth_test_busy(hanum))
+            gdth_delay(0);
+        /* initialize cache service */
+        gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0);
+        hdr_cnt = (ushort)ha->info;
+        /* scanning for host drives */
+        for (k = 0; k < MAX_HDRIVES; ++k)
+            ha->hdr[k].present = FALSE;
+        for (k = 0; k < hdr_cnt; ++k) 
+            gdth_analyse_hdrive(hanum, k);
+        gdth_polling = FALSE;
+        GDTH_UNLOCK_HA(ha, flags);
+        break;
+
       default:
         return(-EINVAL);
     }
-    /* we return a buffer ID to detect the right buffer during READ-IOCTL */
-    return 1;
+    return length;
 }
 
 static int gdth_get_info(char *buffer,char **start,off_t offset,
@@ -454,9 +545,13 @@
 
     gdth_cmd_str gdtcmd;
     gdth_evt_str estr;
+#if LINUX_VERSION_CODE >= 0x020322
+    Scsi_Cmnd *scp;
+    Scsi_Device *sdev;
+#else
     Scsi_Cmnd scp;
     Scsi_Device sdev;
-    char cmnd[12];
+#endif
     char hrec[161];
     struct timeval tv;
 
@@ -467,19 +562,27 @@
     gdth_cdrinfo_str *pcdi;
     gdth_hget_str *phg;
 
+    char cmnd[MAX_COMMAND_SIZE];
+    memset(cmnd, 0xff, 12);
+
     TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
     ha = HADATA(gdth_ctr_tab[hanum]);
-    id = length;
 
+#if LINUX_VERSION_CODE >= 0x020322
+    sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]);
+    scp  = scsi_allocate_device(sdev, 1, FALSE);
+    scp->cmd_len = 12;
+    scp->use_sg = 0;
+#else
     memset(&sdev,0,sizeof(Scsi_Device));
     memset(&scp, 0,sizeof(Scsi_Cmnd));
-    memset(cmnd, 0xff, 12);
     sdev.host = scp.host = gdth_ctr_vtab[vh];
     sdev.id = scp.target = sdev.host->this_id;
     scp.device = &sdev;
+#endif
 
-    /* look for buffer ID in length */
-    if (id > 1) {
+    /* ioctl from tool? */
+    if (!gdth_ioctl_check_bin(hanum, (ushort)length)) {
         /* request is i.e. "cat /proc/scsi/gdth/0" */ 
         /* format: %-15s\t%-10s\t%-15s\t%s */
         /* driver parameters */
@@ -514,11 +617,10 @@
         /* controller information */
         size = sprintf(buffer+len,"\nDisk Array Controller Information:\n");
         len += size;  pos = begin + len;
-#if LINUX_VERSION_CODE >= 0x020000
-        strcpy(hrec, ha->binfo.type_string);
-#else
-        sprintf(hrec, "%s (Bus %d)", ha->binfo.type_string, busnum);
-#endif
+        if (virt_ctr)
+            sprintf(hrec, "%s (Bus %d)", ha->binfo.type_string, busnum);
+        else
+            strcpy(hrec, ha->binfo.type_string);
         size = sprintf(buffer+len,
                        " Number:       \t%d         \tName:          \t%s\n",
                        hanum, hrec);
@@ -586,11 +688,16 @@
                     sizeof(pds->list[0]);
                 if (pds->entries > cnt)
                     pds->entries = cnt;
+#if LINUX_VERSION_CODE >= 0x020322
+                gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                if ((scp->SCp.Message & 0xffff) != S_OK) 
+#else
                 gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
                 if ((scp.SCp.Message & 0xffff) != S_OK) 
+#endif
+                { 
                     pds->count = 0;
-                TRACE2(("pdr_statistics() entries %d status %d\n",
-                        pds->count, scp.SCp.Message));
+                }
 
                 /* other IOCTLs must fit into area GDTH_SCRATCH/4 */
                 for (j = 0; j < ha->raw[i].pdev_cnt; ++j) {
@@ -606,8 +713,14 @@
                     gdtcmd.u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
                     gdtcmd.u.ioctl.channel = 
                         ha->raw[i].address | ha->raw[i].id_list[j];
+#if LINUX_VERSION_CODE >= 0x020322
+                    gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                    if ((scp->SCp.Message & 0xffff) == S_OK) 
+#else
                     gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
-                    if ((scp.SCp.Message & 0xffff) == S_OK) {
+                    if ((scp.SCp.Message & 0xffff) == S_OK) 
+#endif
+                    {
                         strncpy(hrec,pdi->vendor,8);
                         strncpy(hrec+8,pdi->product,16);
                         strncpy(hrec+24,pdi->revision,4);
@@ -657,8 +770,14 @@
                         gdtcmd.u.ioctl.channel = 
                             ha->raw[i].address | ha->raw[i].id_list[j];
                         pdef->sddc_type = 0x08;
+#if LINUX_VERSION_CODE >= 0x020322
+                        gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                        if ((scp->SCp.Message & 0xffff) == S_OK) 
+#else
                         gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
-                        if ((scp.SCp.Message & 0xffff) == S_OK) {
+                        if ((scp.SCp.Message & 0xffff) == S_OK) 
+#endif
+                        {
                             size = sprintf(buffer+len,
                                            " Grown Defects:\t%d\n",
                                            pdef->sddc_cnt);
@@ -704,9 +823,16 @@
                     gdtcmd.u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
                     gdtcmd.u.ioctl.subfunc = CACHE_DRV_INFO;
                     gdtcmd.u.ioctl.channel = drv_no;
+#if LINUX_VERSION_CODE >= 0x020322
+                    gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                    if ((scp->SCp.Message & 0xffff) != S_OK)
+#else
                     gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
                     if ((scp.SCp.Message & 0xffff) != S_OK)
+#endif
+                    {
                         break;
+                    }
                     pcdi->ld_dtype >>= 16;
                     j++;
                     if (pcdi->ld_dtype > 2) {
@@ -801,8 +927,14 @@
                 gdtcmd.u.ioctl.param_size = sizeof(gdth_arrayinf_str);
                 gdtcmd.u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
                 gdtcmd.u.ioctl.channel = i;
+#if LINUX_VERSION_CODE >= 0x020322
+                gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                if ((scp->SCp.Message & 0xffff) == S_OK) 
+#else
                 gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
-                if ((scp.SCp.Message & 0xffff) == S_OK) {
+                if ((scp.SCp.Message & 0xffff) == S_OK) 
+#endif
+                {
                     if (pai->ai_state == 0)
                         strcpy(hrec, "idle");
                     else if (pai->ai_state == 2)
@@ -876,8 +1008,14 @@
                 gdtcmd.u.ioctl.channel = i;
                 phg->entries = MAX_HDRIVES;
                 phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]); 
+#if LINUX_VERSION_CODE >= 0x020322
+                gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+                if ((scp->SCp.Message & 0xffff) != S_OK) 
+#else
                 gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
-                if ((scp.SCp.Message & 0xffff) != S_OK) {
+                if ((scp.SCp.Message & 0xffff) != S_OK) 
+#endif
+                {
                     ha->hdr[i].ldr_no = i;
                     ha->hdr[i].rw_attribs = 0;
                     ha->hdr[i].start_sec = 0;
@@ -891,8 +1029,6 @@
                         ha->hdr[k].start_sec = phg->entry[j].start_sec;
                     }
                 }
-                TRACE2(("host_get entries %d status %d\n",
-                        phg->entries, scp.SCp.Message));
             }
             gdth_ioctl_free(hanum);
 
@@ -959,17 +1095,14 @@
         length = piord->size;
         memcpy(buffer+len, (char *)piord, length);
         gdth_ioctl_free(hanum);
-        len += length; pos = begin + len;
-
-        if (pos < offset) {
-            len = 0;
-            begin = pos;
-        }
-        if (pos > offset + length)
-            goto stop_output;
+        len = length; 
     }
 
 stop_output:
+#if LINUX_VERSION_CODE >= 0x020322
+    scsi_release_command(scp);
+    scsi_free_host_dev(sdev);
+#endif
     *start = buffer +(offset-begin);
     len -= (offset-begin);
     if (len > length)
@@ -982,8 +1115,12 @@
 static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd, 
                         char *cmnd, int timeout)
 {
-    struct semaphore sem = MUTEX_LOCKED;
     unsigned bufflen;
+#if LINUX_VERSION_CODE >= 0x020322
+    DECLARE_MUTEX_LOCKED(sem);
+#else
+    struct semaphore sem = MUTEX_LOCKED;
+#endif
 
     TRACE2(("gdth_do_cmd()\n"));
     if (gdtcmd != NULL) { 
@@ -995,9 +1132,13 @@
     }
     scp->request.rq_status = RQ_SCSI_BUSY;
     scp->request.sem = &sem;
+#if LINUX_VERSION_CODE >= 0x020322
+    scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1);
+#else
     GDTH_LOCK_SCSI_DOCMD();
     scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1);
     GDTH_UNLOCK_SCSI_DOCMD();
+#endif
     down(&sem);
 }
 
@@ -1046,26 +1187,41 @@
     GDTH_UNLOCK_HA(ha, flags);
 }
 
+static int gdth_ioctl_check_bin(int hanum, ushort size)
+{
+    gdth_ha_str *ha;
+    ulong flags;
+    int ret_val;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    GDTH_LOCK_HA(ha, flags);
+
+    ret_val = FALSE;
+    if (ha->scratch_busy) {
+        if (((gdth_iord_str *)ha->pscratch)->size == (ulong32)size)
+            ret_val = TRUE;
+    }
+    GDTH_UNLOCK_HA(ha, flags);
+    return ret_val;
+}
+
+
 static void gdth_wait_completion(int hanum, int busnum, int id)
 {
     gdth_ha_str *ha;
     ulong flags;
     int i;
     Scsi_Cmnd *scp;
+    unchar b;
 
     ha = HADATA(gdth_ctr_tab[hanum]);
     GDTH_LOCK_HA(ha, flags);
 
     for (i = 0; i < GDTH_MAXCMDS; ++i) {
         scp = ha->cmd_tab[i].cmnd;
-#if LINUX_VERSION_CODE >= 0x020000
-        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
-            scp->channel == (unchar)busnum)
-#else
-        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
-            NUMDATA(scp->host)->busnum == (unchar)busnum)
-#endif
-        {
+        b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
+        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && 
+            b == (unchar)busnum) {
             scp->SCp.have_data_in = 0;
             GDTH_UNLOCK_HA(ha, flags);
             while (!scp->SCp.have_data_in)
@@ -1084,19 +1240,14 @@
     gdth_ha_str *ha;
     ulong flags;
     Scsi_Cmnd *scp;
+    unchar b;
 
     ha = HADATA(gdth_ctr_tab[hanum]);
     GDTH_LOCK_HA(ha, flags);
 
     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
-#if LINUX_VERSION_CODE >= 0x020000
-        if (scp->target == (unchar)id &&
-            scp->channel == (unchar)busnum)
-#else
-        if (scp->target == (unchar)id &&
-            NUMDATA(scp->host)->busnum == (unchar)busnum)
-#endif
-        {
+        b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
+        if (scp->target == (unchar)id && b == (unchar)busnum) {
             TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
             scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
         }
@@ -1109,19 +1260,14 @@
     gdth_ha_str *ha;
     ulong flags;
     Scsi_Cmnd *scp;
+    unchar b;
 
     ha = HADATA(gdth_ctr_tab[hanum]);
     GDTH_LOCK_HA(ha, flags);
 
     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
-#if LINUX_VERSION_CODE >= 0x020000
-        if (scp->target == (unchar)id &&
-            scp->channel == (unchar)busnum)
-#else
-        if (scp->target == (unchar)id &&
-            NUMDATA(scp->host)->busnum == (unchar)busnum)
-#endif
-        {
+        b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel;
+        if (scp->target == (unchar)id && b == (unchar)busnum) {
             TRACE2(("gdth_start_timeout(): update_timeout()\n"));
             gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual);
         }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gdth_proc.h linux.18p17/drivers/scsi/gdth_proc.h
--- linux.vanilla/drivers/scsi/gdth_proc.h	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/gdth_proc.h	Mon Jul 24 10:32:20 2000
@@ -2,24 +2,31 @@
 #define _GDTH_PROC_H
 
 /* gdth_proc.h 
- * $Id: gdth_proc.h,v 1.7 1999/05/26 11:49:32 achim Exp $
+ * $Id: gdth_proc.h,v 1.10 2000/07/24 09:30:01 achim Exp $
  */
 
 static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
-static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
-static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
 static int gdth_get_info(char *buffer,char **start,off_t offset,
                          int length,int vh,int hanum,int busnum);
 
+#if LINUX_VERSION_CODE >= 0x020322
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp);
+#else 
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+#endif
+static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd, 
+                        char *cmnd, int timeout);
+
 static int gdth_ioctl_alloc(int hanum, ushort size);
 static void gdth_ioctl_free(int hanum);
+static int gdth_ioctl_check_bin(int hanum, ushort size);
 static void gdth_wait_completion(int hanum, int busnum, int id);
 static void gdth_stop_timeout(int hanum, int busnum, int id);
 static void gdth_start_timeout(int hanum, int busnum, int id);
 static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout);
 
-static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd, 
-                        char *cmnd, int timeout);
 void gdth_scsi_done(Scsi_Cmnd *scp);
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/gvp11.c linux.18p17/drivers/scsi/gvp11.c
--- linux.vanilla/drivers/scsi/gvp11.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/gvp11.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.c linux.18p17/drivers/scsi/hosts.c
--- linux.vanilla/drivers/scsi/hosts.c	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/hosts.c	Fri Sep 15 22:44:39 2000
@@ -131,6 +131,10 @@
 #include "aha1740.h"
 #endif
 
+#ifdef CONFIG_SCSI_AACRAID
+#include "aacraid/include/linit.h"
+#endif
+
 #ifdef CONFIG_SCSI_AIC7XXX
 #include "aic7xxx.h"
 #endif
@@ -361,6 +365,11 @@
 #include "imm.h"
 #endif
 
+#ifdef CONFIG_SCSI_CPQFCTS
+#include "cpqfcTS.h"
+#endif 
+
+
 /*
 static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
 */
@@ -484,6 +493,9 @@
 #ifdef CONFIG_SCSI_AHA1740
     AHA1740,
 #endif
+#ifdef CONFIG_SCSI_AACRAID
+    AAC_HOST_TEMPLATE_ENTRY,
+#endif
 #ifdef CONFIG_SCSI_AIC7XXX
     AIC7XXX,
 #endif
@@ -634,6 +646,10 @@
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
+#endif
+/* CPQFCTS needs to be last because it can never be the boot device. */
+#ifdef CONFIG_SCSI_CPQFCTS
+   CPQFCTS,
 #endif
 };
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ibmmca.c linux.18p17/drivers/scsi/ibmmca.c
--- linux.vanilla/drivers/scsi/ibmmca.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/ibmmca.c	Mon Jul 31 20:57:07 2000
@@ -7,7 +7,7 @@
  * See the file README.ibmmca for a detailed description of this driver,
  * the commandline arguments and the history of its development.
  * See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
- * updates and info.
+ * updates, info and ADF-files for adapters supported by this driver.
  */
 
 /******************* HEADER FILE INCLUDES ************************************/
@@ -16,14 +16,12 @@
 #endif
 
 /* choose adaption for Kernellevel */
-#define local_LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-#if LINUX_VERSION_CODE < local_LinuxVersionCode(2,1,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,65)
 #define OLDKERN
 #else
 #undef OLDKERN
 #endif
 
-#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/ctype.h>
@@ -36,10 +34,13 @@
 #include <linux/stat.h>
 #include <linux/mca.h>
 #include <asm/system.h>
-#ifndef OLDKERN
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 #include <asm/spinlock.h>
 #endif
 #include <asm/io.h>
+#include <linux/init.h>
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -49,6 +50,7 @@
 
 /******************* LOCAL DEFINES *******************************************/
 
+/* milliseconds of delay for timing out reset. */
 #ifndef mdelay
 #define mdelay(a)    udelay((a) * 1000)
 #endif
@@ -56,7 +58,7 @@
 /*--------------------------------------------------------------------*/
 
 /* current version of this driver-source: */
-#define IBMMCA_SCSI_DRIVER_VERSION "3.1e"
+#define IBMMCA_SCSI_DRIVER_VERSION "3.2"
 
 /*--------------------------------------------------------------------*/
 
@@ -110,6 +112,7 @@
 /*note: the lower nibble specifies the device(0-14), or subsystem(15) */
 #define IM_SCB_CMD_COMPLETED               0x10
 #define IM_SCB_CMD_COMPLETED_WITH_RETRIES  0x50
+#define IM_LOOP_SCATTER_BUFFER_FULL        0x60
 #define IM_ADAPTER_HW_FAILURE              0x70
 #define IM_IMMEDIATE_CMD_COMPLETED         0xa0
 #define IM_CMD_COMPLETED_WITH_FAILURE      0xc0
@@ -153,7 +156,7 @@
 	    unsigned short length;	/*block length, on SCSI device */
 	  }
 	blk;
-	unsigned char scsi_command[12];		/*other scsi command */
+	unsigned char scsi_command[12];	   /*other scsi command */
       }
     u2;
   };
@@ -165,6 +168,31 @@
     unsigned long byte_length;
   };
 
+/*structure returned by a get_pos_info command: */
+struct im_pos_info
+  {
+     unsigned short pos_id;         /* adapter id */
+     unsigned char pos_3a;          /* pos 3 (if pos 6 = 0) */
+     unsigned char pos_2;           /* pos 2 */
+     unsigned char int_level;       /* interrupt level IRQ 11 or 14 */
+     unsigned char pos_4a;          /* pos 4 (if pos 6 = 0) */
+     unsigned short connector_size; /* MCA connector size: 16 or 32 Bit */
+     unsigned char num_luns;        /* number of supported luns per device */
+     unsigned char num_puns;        /* number of supported puns */
+     unsigned char pacing_factor;   /* pacing factor */
+     unsigned char num_ldns;        /* number of ldns available */
+     unsigned char eoi_off;         /* time EOI and interrupt inactive */
+     unsigned char max_busy;        /* time between reset and busy on */
+     unsigned short cache_stat;     /* ldn cachestat. Bit=1 = not cached */
+     unsigned short retry_stat;     /* retry status of ldns. Bit=1=disabled */
+     unsigned char pos_4b;          /* pos 4 (if pos 6 = 1) */
+     unsigned char pos_3b;          /* pos 3 (if pos 6 = 1) */
+     unsigned char pos_6;           /* pos 6 */
+     unsigned char pos_5;           /* pos 5 */ 
+     unsigned short max_overlap;    /* maximum overlapping requests */
+     unsigned short num_bus;        /* number of SCSI-busses */
+  };
+
 /*values for SCB command word */
 #define IM_NO_SYNCHRONOUS      0x0040	/*flag for any command */
 #define IM_NO_DISCONNECT       0x0080	/*flag for any command */
@@ -175,6 +203,7 @@
 #define IM_REQUEST_SENSE_CMD   0x1c08
 #define IM_READ_CAPACITY_CMD   0x1c09
 #define IM_DEVICE_INQUIRY_CMD  0x1c0b
+#define IM_READ_LOGICAL_CMD    0x1c2a
 #define IM_OTHER_SCSI_CMD_CMD  0x241f
 
 /* unused, but supported, SCB commands */
@@ -190,6 +219,7 @@
 #define IM_RETRY_ENABLE              0x2000
 #define IM_POINTER_TO_LIST           0x1000
 #define IM_SUPRESS_EXCEPTION_SHORT   0x0400
+#define IM_BYPASS_BUFFER             0x0200
 #define IM_CHAIN_ON_NO_ERROR         0x0001
 
 /*TSB (Termination Status Block) structure */
@@ -211,15 +241,17 @@
   };
 
 /*subsystem uses interrupt request level 14 */
-#define IM_IRQ  14
+#define IM_IRQ     14
+/*SCSI-2 F/W may evade to interrupt 11 */
+#define IM_IRQ_FW  11
 
 /*--------------------------------------------------------------------*/
 /*
 	The model 95 doesn't have a standard activity light.  Instead it
-	has a row of LEDs on the front.  We use the last one as the activity
-	indicator if we think we're on a model 95.  I suspect the model id
-	check will be either too narrow or too general, and some machines
-	won't have an activity indicator.  Oh well...
+	has a row of alphanumerial LEDs on the front.  We use the last one 
+        as the activity indicator if we think we're on a model 95.  I suspect
+        the model id check will be either too narrow or too general, and some 
+        machines won't have an activity indicator.  Oh well...
 
 	The regular PS/2 disk led is turned on/off by bits 6,7 of system
 	control port.
@@ -229,6 +261,12 @@
 #define MOD95_LED_PORT	   0x108
 /* system-control-register of PS/2s with diskindicator */
 #define PS2_SYS_CTR        0x92
+/* activity displaying methods */
+#define LED_DISP           1
+#define LED_ADISP          2
+#define LED_ACTIVITY       4
+
+#define CMD_FAIL           255
 
 /* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
    displays. ldn is no longer displayed here, because the ldn mapping is now 
@@ -237,30 +275,39 @@
    interest, debugging or just for having fun. The left number gives the
    host-adapter number and the right shows the accessed SCSI-ID. */
 
-/* use_display is set by the ibmmcascsi=display command line arg */
-static int use_display = 0;
-/* use_adisplay is set by ibmmcascsi=adisplay, which offers a higher
- * level of displayed luxus on PS/2 95 (really fancy! :-))) */
-static int use_adisplay = 0;
+/* display_mode is set by the ibmmcascsi= command line arg */
+static int display_mode = 0;
+/* set default adapter timeout */
+static unsigned int adapter_timeout = 45;
+/* for probing on feature-command: */
+static unsigned int global_command_error_excuse = 0;
+/* global setting by command line for adapter_speed */
+static int global_adapter_speed = 0; /* full speed by default */
 
+/* Panel / LED on, do it right for F/W addressin, too. adisplay will
+ * just ignore ids>7, as the panel has only 7 digits available */
 #define PS2_DISK_LED_ON(ad,id) {\
-	if( use_display ) { outb((char)(id+48), MOD95_LED_PORT ); \
-        outb((char)(ad+48), MOD95_LED_PORT+1); } \
-        else if( use_adisplay ) { if (id<7) outb((char)(id+48), \
-	MOD95_LED_PORT+1+id); outb((char)(ad+48), MOD95_LED_PORT); } \
-	else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \
+        if (display_mode & LED_DISP) { \
+           if (id>9) \
+	     outw((ad+48)|((id+55)<<8), MOD95_LED_PORT ); \
+           else \
+             outw((ad+48)|((id+48)<<8), MOD95_LED_PORT ); } \
+        else if (display_mode & LED_ADISP) { \
+           if (id<7) outb((char)(id+48),MOD95_LED_PORT+1+id); \
+           outb((char)(ad+48), MOD95_LED_PORT); } \
+        if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
+           outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \
 }
-
+/* Panel / LED off */
 /* bug fixed, Dec 15, 1997, where | was replaced by & here */
 #define PS2_DISK_LED_OFF() {\
-	if( use_display ) { outb( ' ', MOD95_LED_PORT ); \
-        outb(' ', MOD95_LED_PORT+1); } \
-        if ( use_adisplay ) { outb(' ',MOD95_LED_PORT ); \
-	outb(' ',MOD95_LED_PORT+1); outb(' ',MOD95_LED_PORT+2); \
-        outb(' ',MOD95_LED_PORT+3); outb(' ',MOD95_LED_PORT+4); \
-        outb(' ',MOD95_LED_PORT+5); outb(' ',MOD95_LED_PORT+6); \
-	outb(' ',MOD95_LED_PORT+7); } \
-	else outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); \
+	if (display_mode & LED_DISP) \
+           outw(0x2020, MOD95_LED_PORT ); \
+        else if (display_mode & LED_ADISP) { \
+           outl(0x20202020,MOD95_LED_PORT); \
+           outl(0x20202020,MOD95_LED_PORT+4); } \
+	if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
+           outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); \
 }
 
 /*--------------------------------------------------------------------*/
@@ -272,17 +319,29 @@
     char *description;
   };
 
+/* types of different supported hardware that goes to hostdata special */
+#define IBM_SCSI2_FW     0
+#define IBM_7568_WCACHE  1
+#define IBM_EXP_UNIT     2
+#define IBM_SCSI_WCACHE  3
+#define IBM_SCSI         4
+
+/* other special flags for hostdata structure */
+#define FORCED_DETECTION         100
+#define INTEGRATED_SCSI          101
+
 /* List of possible IBM-SCSI-adapters */
 struct subsys_list_struct subsys_list[] =
 {
-  {0x8efc, "IBM Fast SCSI-2 Adapter"}, /* special = 0 */
-  {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"}, /* special = 1 */
+  {0x8efc, "IBM SCSI-2 F/W Adapter"}, /* special = 0 */
+  {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/Cache"}, /* special = 1 */
   {0x8ef8, "IBM Expansion Unit SCSI Controller"},/* special = 2 */
   {0x8eff, "IBM SCSI Adapter w/Cache"}, /* special = 3 */
   {0x8efe, "IBM SCSI Adapter"}, /* special = 4 */
 };                   
 
-/*for /proc filesystem */
+/*for /proc filesystem, only valid in older kernel releases */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,27)
 struct proc_dir_entry proc_scsi_ibmmca =
 {
   PROC_SCSI_IBMMCA, 6, "ibmmca",
@@ -290,6 +349,7 @@
   0, 0, 0, NULL, NULL, NULL, NULL,
   NULL, NULL, NULL 
 };
+#endif
 
 /* Max number of logical devices (can be up from 0 to 14).  15 is the address
 of the adapter itself. */
@@ -302,11 +362,12 @@
     struct im_tsb tsb; /* SCSI command complete status block structure */
     struct im_sge sge[16]; /* scatter gather list structure */
     unsigned char buf[256]; /* SCSI command return data buffer */
-    Scsi_Cmnd *cmd;  /* SCSI-command that is currently in progress */
-     
+    Scsi_Cmnd *cmd;  /* SCSI-command that is currently in progress */     
     int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
 		        for interpretation of the possible values */
     int block_length;/* blocksize of a particular logical SCSI-device */
+    int cache_flag; /* 1 if this is uncached, 0 if cache is present for ldn */
+    int retry_flag; /* 1 if adapter retry is disabled, 0 if enabled */
   };
 
 /* statistics of the driver during operations (for proc_info) */
@@ -318,6 +379,8 @@
       int ldn_write_access[MAX_LOG_DEV+1];   /* total write-access on a ldn */
       int ldn_inquiry_access[MAX_LOG_DEV+1]; /* total inquiries on a ldn */
       int ldn_modeselect_access[MAX_LOG_DEV+1]; /* total mode selects on ldn */
+      int scbs;                              /* short SCBs queued */
+      int long_scbs;                         /* long SCBs queued */
       int total_accesses;                    /* total accesses on all ldns */
       int total_interrupts;                  /* total interrupts (should be
 						same as total_accesses) */
@@ -336,10 +399,10 @@
    /* array of logical devices: */
    struct logical_device _ld[MAX_LOG_DEV+1];   
    /* array to convert (pun, lun) into logical device number: */
-   unsigned char _get_ldn[8][8];
+   unsigned char _get_ldn[16][8];
    /*array that contains the information about the physical SCSI-devices
     attached to this host adapter: */
-   unsigned char _get_scsi[8][8];
+   unsigned char _get_scsi[16][8];
    /* used only when checking logical devices: */
    int _local_checking_phase_flag;
    /* report received interrupt: */
@@ -352,21 +415,30 @@
    int _last_scsi_command[MAX_LOG_DEV+1];
    /* identifier of the last SCSI-command type */
    int _last_scsi_type[MAX_LOG_DEV+1];
+   /* last blockcount */
+   int _last_scsi_blockcount[MAX_LOG_DEV+1];
+   /* last locgical block address */
+   unsigned long _last_scsi_logical_block[MAX_LOG_DEV+1];
    /* Counter that points on the next reassignable ldn for dynamical 
     remapping. The default value is 7, that is the first reassignable 
     number in the list at boottime: */
    int _next_ldn;
    /* Statistics-structure for this IBM-SCSI-host: */
    struct Driver_Statistics _IBM_DS;
-   /* This hostadapters pos-registers pos2 and pos3 */
-   unsigned _pos2, _pos3;
+   /* This hostadapters pos-registers pos2 until pos6 */
+   unsigned _pos2, _pos3, _pos4, _pos5, _pos6;
    /* assign a special variable, that contains dedicated info about the
     adaptertype */
    int _special;
+   /* connector size on the MCA bus */
+   int _connector_size;
+   /* synchronous SCSI transfer rate bitpattern */
+   int _adapter_speed;
 };
 
 /* macros to access host data structure */
 #define subsystem_pun(hi) (hosts[(hi)]->this_id)
+#define subsystem_maxid(hi) (hosts[(hi)]->max_id)
 #define ld(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_ld)
 #define get_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_ldn)
 #define get_scsi(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_scsi)
@@ -376,11 +448,19 @@
 #define reset_status(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_reset_status)
 #define last_scsi_command(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_command)
 #define last_scsi_type(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_type)
+#define last_scsi_blockcount(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_blockcount)
+#define last_scsi_logical_block(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_logical_block)
+#define last_scsi_type(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_type)
 #define next_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_next_ldn)
 #define IBM_DS(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_IBM_DS)
 #define special(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_special)
+#define subsystem_connector_size(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_connector_size)
+#define adapter_speed(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_adapter_speed)
 #define pos2(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos2)
 #define pos3(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos3)
+#define pos4(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos4)
+#define pos5(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos5)
+#define pos6(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos6)
 
 /* Define a arbitrary number as subsystem-marker-type. This number is, as 
    described in the ANSI-SCSI-standard, not occupied by other device-types. */
@@ -412,10 +492,6 @@
 #define IM_RESET_NOT_IN_PROGRESS_NO_INT  4
 #define IM_RESET_FINISHED_OK_NO_INT      5
 
-/* special flags for hostdata structure */
-#define FORCED_DETECTION         100
-#define INTEGRATED_SCSI          101
-
 /* define undefined SCSI-command */
 #define NO_SCSI                  0xffff
 
@@ -427,7 +503,10 @@
 
 /* fill module-parameters only, when this define is present.
    (that is kernel version 2.1.x) */
-#ifdef MODULE_PARM
+#if defined(MODULE)
+static char *boot_options = NULL;
+#include <linux/module.h>
+MODULE_PARM(boot_options, "s");
 MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
 MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); 
 MODULE_PARM(display, "1i");
@@ -452,8 +531,10 @@
 
 /* host information */
 static int found = 0;
-static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL };
-static unsigned int pos[8]; /* whole pos register-line */
+static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL, NULL, NULL, NULL, 
+                                                   NULL, NULL, NULL, NULL,
+                                                   NULL };
+static unsigned int pos[8]; /* whole pos register-line for diagnosis */
 /* Taking into account the additions, made by ZP Gu.
  * This selects now the preset value from the configfile and
  * offers the 'normal' commandline option to be accepted */
@@ -473,25 +554,30 @@
 #endif
 static void issue_cmd (int, unsigned long, unsigned char);
 static void internal_done (Scsi_Cmnd * cmd);
-static void check_devices (int);
+static void check_devices (int, int);
 static int immediate_assign(int, unsigned int, unsigned int, unsigned int, 
                             unsigned int);
+static int immediate_feature(int, unsigned int, unsigned int);
 #ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
 static int immediate_reset(int, unsigned int);
 #endif
 static int device_inquiry(int, int);
 static int read_capacity(int, int);
+static int get_pos_info(int);
 static char *ti_p(int);
 static char *ti_l(int);
+static char *ibmrate(unsigned int, int);
+static int probe_display(int);
+static int probe_bus_mode(int);
 static int device_exists (int, int, int *, int *);
-static struct Scsi_Host *ibmmca_register(Scsi_Host_Template *, 
-					 int, int, char *);
-
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template *,
+					 int, int, int, char *);
 /* local functions needed for proc_info */
 static int ldn_access_load(int, int);
 static int ldn_access_total_read_write(int);
 
 static int bypass_controller = 0;   /* bypass integrated SCSI-cmd set flag */
+
 /*--------------------------------------------------------------------*/
 
 /******************* LOCAL FUNCTIONS IMPLEMENTATION *************************/
@@ -515,20 +601,17 @@
    unsigned int intr_reg;
    unsigned int cmd_result;
    unsigned int ldn;
-   static unsigned long flags;
+   unsigned long flags;
    Scsi_Cmnd *cmd;
-   int errorflag;
-   int interror;
-
-   host_index=0; /* make sure, host_index is 0, else this won't work and
-		    never dare to ask, what happens, if an interrupt-handler
-		    does not work :-((( .... */
+   int lastSCSI;
    
+   host_index = 0; /* make sure, host_index is 0 */
+
    /* search for one adapter-response on shared interrupt */
    while (hosts[host_index]
 	  && !(inb(IM_STAT_REG(host_index)) & IM_INTR_REQUEST))
      host_index++;
-   
+      
    /* return if some other device on this IRQ caused the interrupt */
    if (!hosts[host_index]) return;
 
@@ -541,11 +624,6 @@
 	return;
      }
    
-   /*get command result and logical device */
-   intr_reg = inb (IM_INTR_REG(host_index));
-   cmd_result = intr_reg & 0xf0;
-   ldn = intr_reg & 0x0f;
-
    /*must wait for attention reg not busy, then send EOI to subsystem */
    while (1) 
      {
@@ -555,7 +633,8 @@
 #else
 	spin_lock_irqsave(&intr_lock, flags);
 #endif	
-	if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) 
+	/* if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) */
+	if ((inb(IM_STAT_REG(host_index)) & 0xf) == (IM_CMD_REG_EMPTY | IM_INTR_REQUEST))
 	  break;
 #ifdef OLDKERN	
 	restore_flags(flags);
@@ -563,78 +642,107 @@
 	spin_unlock_irqrestore(&intr_lock, flags);
 #endif
      }
-   outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+   /*get command result and logical device */
+   intr_reg = (unsigned char)(inb (IM_INTR_REG(host_index)));
+   cmd_result = intr_reg & 0xf0;
+   ldn = intr_reg & 0x0f;
+
    /* get the last_scsi_command here */
-   interror = last_scsi_command(host_index)[ldn];   
-#ifdef OLDKERN
-   restore_flags(flags);
-#else
-   spin_unlock_irqrestore(&intr_lock, flags);
-#endif      
-   errorflag = 0; /* no errors by default */   
+   lastSCSI = last_scsi_command(host_index)[ldn];
+   
    /*these should never happen (hw fails, or a local programming bug) */
-   if (cmd_result == IM_ADAPTER_HW_FAILURE)
-     {
-	printk("\n");
-	printk("IBM MCA SCSI: ERROR - subsystem hardware failure!\n");
-	printk("              Last SCSI-command=0x%X, ldn=%d, host=%d.\n",
-	       last_scsi_command(host_index)[ldn],ldn,host_index);
-	errorflag = 1;
-     }
-   if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR)
-     {
-	printk("\n");
-	printk("IBM MCA SCSI: ERROR - software sequencing error!\n");
-	printk("              Last SCSI-command=0x%X, ldn=%d, host=%d.\n",
-	       last_scsi_command(host_index)[ldn],ldn,host_index);
-	errorflag = 1;	
+   if (!global_command_error_excuse)
+     {	
+	switch (cmd_result)
+	  { /* Prevent from Ooopsing on error to show the real reason */      
+	   case IM_ADAPTER_HW_FAILURE:
+	   case IM_SOFTWARE_SEQUENCING_ERROR:
+	   case IM_CMD_ERROR:
+	     printk("\nIBM MCA SCSI: Fatal Subsystem ERROR!\n");
+	     printk("              Last cmd=0x%x, ena=%x, len=",lastSCSI,
+		    ld(host_index)[ldn].scb.enable);
+	     if (ld(host_index)[ldn].cmd)
+	       printk("%ld/%ld",(long)(ld(host_index)[ldn].cmd->request_bufflen),
+		      (long)(ld(host_index)[ldn].scb.sys_buf_length));
+	     else
+	       printk("none");
+	     printk(", ");
+	     if (ld(host_index)[ldn].cmd)
+	       printk("Blocksize=%d",ld(host_index)[ldn].scb.u2.blk.length);
+	     else
+	       printk("Blocksize=none");
+	     printk(", host=0x%x, ldn=0x%x\n",host_index, ldn);
+	     if (ld(host_index)[ldn].cmd)
+	       {		  
+		  printk("Blockcount=%d/%d\n",last_scsi_blockcount(host_index)[ldn],
+			 ld(host_index)[ldn].scb.u2.blk.count);
+		  printk("Logical block=%lx/%lx\n",last_scsi_logical_block(host_index)[ldn],
+			 ld(host_index)[ldn].scb.u1.log_blk_adr);
+	       }	     			 
+	     printk("Reason given: %s\n",
+		    (cmd_result==IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" :
+		    (cmd_result==IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" :
+		    (cmd_result==IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN");	
+	     /* if errors appear, enter this section to give detailed info */
+	     printk("IBM MCA SCSI: Subsystem Error-Status follows:\n");
+	     printk("              Command Type................: %x\n",
+		    last_scsi_type(host_index)[ldn]);
+	     printk("              Attention Register..........: %x\n",
+		    inb (IM_ATTN_REG(host_index)));
+	     printk("              Basic Control Register......: %x\n",
+		    inb (IM_CTR_REG(host_index)));
+	     printk("              Interrupt Status Register...: %x\n",
+		    intr_reg);
+	     printk("              Basic Status Register.......: %x\n",
+		    inb (IM_STAT_REG(host_index)));
+	     if ((last_scsi_type(host_index)[ldn]==IM_SCB)||
+		 (last_scsi_type(host_index)[ldn]==IM_LONG_SCB))
+	       {
+	          printk("              SCB-Command.................: %x\n",
+			 ld(host_index)[ldn].scb.command);
+		  printk("              SCB-Enable..................: %x\n",
+			 ld(host_index)[ldn].scb.enable);
+		  printk("              SCB-logical block address...: %lx\n",
+			 ld(host_index)[ldn].scb.u1.log_blk_adr);
+		  printk("              SCB-system buffer address...: %lx\n",
+			 ld(host_index)[ldn].scb.sys_buf_adr);
+		  printk("              SCB-system buffer length....: %lx\n",
+			 ld(host_index)[ldn].scb.sys_buf_length);
+		  printk("              SCB-tsb address.............: %lx\n",
+			 ld(host_index)[ldn].scb.tsb_adr);
+		  printk("              SCB-Chain address...........: %lx\n",
+			 ld(host_index)[ldn].scb.scb_chain_adr);
+		  printk("              SCB-block count.............: %x\n",
+			 ld(host_index)[ldn].scb.u2.blk.count);
+		  printk("              SCB-block length............: %x\n",
+			 ld(host_index)[ldn].scb.u2.blk.length);
+	       }
+	     printk("              Send this report to the maintainer.\n");
+	     panic("IBM MCA SCSI: Fatal errormessage from the subsystem (0x%X,0x%X)!\n",
+		   lastSCSI,cmd_result);
+	     break;
+	  }  
      }
-   if (cmd_result == IM_CMD_ERROR)
-     {
-	printk("\n");
-	printk("IBM MCA SCSI: ERROR - command error!\n");
-	printk("              Last SCSI-command=0x%X, ldn=%d, host=%d.\n",
-	       last_scsi_command(host_index)[ldn],ldn,host_index);
-	errorflag = 1;	
-     }
-   if (errorflag)
-     { /* if errors appear, enter this section to give detailed info */
-	printk("IBM MCA SCSI: Subsystem Error-Status follows:\n");
-	printk("              Command Type................: %x\n",
-	       last_scsi_type(host_index)[ldn]);
-	printk("              Attention Register..........: %x\n",
-	       inb (IM_ATTN_REG(host_index)));
-        printk("              Basic Control Register......: %x\n",
-	       inb (IM_CTR_REG(host_index)));
-	printk("              Interrupt Status Register...: %x\n",
-	       intr_reg);
-	printk("              Basic Status Register.......: %x\n",
-	       inb (IM_STAT_REG(host_index)));
-	if ((last_scsi_type(host_index)[ldn]==IM_SCB)||
-	    (last_scsi_type(host_index)[ldn]==IM_LONG_SCB))
-	  {
-	     printk("              SCB End Status Word.........: %x\n",
-		    ld(host_index)[ldn].tsb.end_status);
-	     printk("              Command Status..............: %x\n",
-		    ld(host_index)[ldn].tsb.cmd_status);
-	     printk("              Device Status...............: %x\n",
-		    ld(host_index)[ldn].tsb.dev_status);
-	     printk("              Command Error...............: %x\n",
-		    ld(host_index)[ldn].tsb.cmd_error);
-	     printk("              Device Error................: %x\n",
-		    ld(host_index)[ldn].tsb.dev_error);
-	     printk("              Last SCB Address (LSW)......: %x\n",
-		    ld(host_index)[ldn].tsb.low_of_last_scb_adr);
-	     printk("              Last SCB Address (MSW)......: %x\n",
-		    ld(host_index)[ldn].tsb.high_of_last_scb_adr);
+   else 
+     { /* The command error handling is made silent, but we tell the
+	* calling function, that there is a reported error from the
+	* adapter. */
+	switch (cmd_result)
+	  {
+	   case IM_ADAPTER_HW_FAILURE:
+	   case IM_SOFTWARE_SEQUENCING_ERROR:
+	   case IM_CMD_ERROR:
+	     global_command_error_excuse = CMD_FAIL;
+	     break;
+	   default:
+	     global_command_error_excuse = 0;
+	     break;
 	  }
-	printk("              Send report to the maintainer.\n");
-	panic("IBM MCA SCSI: Fatal errormessage from the subsystem!\n");
-     }
+     }   
    
    /* if no panic appeared, increase the interrupt-counter */
    IBM_DS(host_index).total_interrupts++;
-
+   
    /*only for local checking phase */
    if (local_checking_phase_flag(host_index))
      {
@@ -642,6 +750,13 @@
 	got_interrupt(host_index) = 1;
 	reset_status(host_index) = IM_RESET_FINISHED_OK;
 	last_scsi_command(host_index)[ldn] = NO_SCSI;
+	
+	outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&intr_lock, flags);
+#endif      
 	return;
      }   
    /*handling of commands coming from upper level of scsi driver */
@@ -668,6 +783,13 @@
 		    }
 		  stat_result(host_index) = cmd_result;
 		  last_scsi_command(host_index)[ldn] = NO_SCSI;
+		  last_scsi_type(host_index)[ldn] = 0;
+		  outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+		  restore_flags(flags);
+#else
+		  spin_unlock_irqrestore(&intr_lock, flags);
+#endif      
 		  return;
 	       }
 	     else if (last_scsi_command(host_index)[ldn] == IM_ABORT_IMM_CMD)
@@ -678,14 +800,22 @@
 		  disk_rw_in_progress = 0;
 		  PS2_DISK_LED_OFF();
 		  cmd = ld(host_index)[ldn].cmd;
+		  ld(host_index)[ldn].cmd = NULL;
 		  if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
 		    cmd->result = DID_NO_CONNECT << 16;
 		  else
 		    cmd->result = DID_ABORT << 16;
 		  stat_result(host_index) = cmd_result;
 		  last_scsi_command(host_index)[ldn] = NO_SCSI;
+		  last_scsi_type(host_index)[ldn] = 0;		  
+		  outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+		  restore_flags(flags);
+#else
+		  spin_unlock_irqrestore(&intr_lock, flags);
+#endif      		  
 		  if (cmd->scsi_done)
-		    (cmd->scsi_done) (cmd); /* should be the internal_done */
+		    (cmd->scsi_done)(cmd); /* should be the internal_done */
 		  return;
 	       }
 	     else
@@ -695,26 +825,49 @@
 		  reset_status(host_index) = IM_RESET_FINISHED_OK;
 		  stat_result(host_index) = cmd_result;
 		  last_scsi_command(host_index)[ldn] = NO_SCSI;
+		  
+		  outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+		  restore_flags(flags);
+#else
+		  spin_unlock_irqrestore(&intr_lock, flags);
+#endif      		  
 		  return;
 	       }	     
 	  }
-	last_scsi_command(host_index)[ldn] = NO_SCSI;	     
+	last_scsi_command(host_index)[ldn] = NO_SCSI;
+	last_scsi_type(host_index)[ldn] = 0;
 	cmd = ld(host_index)[ldn].cmd;
+	ld(host_index)[ldn].cmd = NULL;
 #ifdef IM_DEBUG_TIMEOUT
 	if (cmd)
 	  {
-	     if ((cmd->target == TIMEOUT_PUN)&&(cmd->lun == TIMEOUT_LUN))
+	     if ((cmd->target == TIMEOUT_PUN)&&
+		 (cmd->lun == TIMEOUT_LUN))
 	       {
 		  printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n",
 			 cmd->target, cmd->lun);
+		  outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+		  restore_flags(flags);
+#else
+		  spin_unlock_irqrestore(&intr_lock, flags);
+#endif      		  		  
 		  return;
 	       }
 	  }
 #endif
 	/*if no command structure, just return, else clear cmd */
 	if (!cmd)
-	  return;
-	ld(host_index)[ldn].cmd = NULL;
+	  {
+	     outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+	     restore_flags(flags);
+#else
+	     spin_unlock_irqrestore(&intr_lock, flags);
+#endif      		  
+	     return;
+	  }		
 	
 #ifdef IM_DEBUG_INT
 	printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", 
@@ -743,19 +896,37 @@
 	  }
 
 	/* IBM describes the status-mask to be 0x1e, but this is not conform
-	 * with SCSI-defintion, I suppose, it is a printing error in the
-	 * technical reference and assume as mask 0x3e. (ML) */
-	cmd->result = (ld(host_index)[ldn].tsb.dev_status & 0x3e);
-	/* write device status into cmd->result, and call done function */
+	 * with SCSI-defintion, I suppose, the reason for it is that IBM 
+	 * adapters do not support CMD_TERMINATED, TASK_SET_FULL and 
+	 * ACA_ACTIVE as returning statusbyte information. (ML) */
 	if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
-	  IBM_DS(host_index).total_errors++;
-	if (interror == NO_SCSI) /* unexpected interrupt :-( */
-	  cmd->result |= DID_BAD_INTR << 16;
+	  {
+	     cmd->result = (unsigned char)(ld(host_index)[ldn].tsb.dev_status & 0x1e);
+	     IBM_DS(host_index).total_errors++;
+	  }
 	else
+	  cmd->result = 0;
+	/* write device status into cmd->result, and call done function */
+	if (lastSCSI == NO_SCSI) /* unexpected interrupt :-( */
+	  cmd->result |= DID_BAD_INTR << 16;
+	else /* things went right :-) */
 	  cmd->result |= DID_OK << 16;
-	(cmd->scsi_done) (cmd);
-     }
-   if (interror == NO_SCSI)
+	outb (IM_EOI | ldn, IM_ATTN_REG(host_index));
+#ifdef OLDKERN
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&intr_lock, flags);
+#endif
+	/* This is for Kernel 2.2.x. Something weired happens here. 
+	 * Between the command got queued and the interrupt is released,
+	 * the flags sometimes contain different values, which must
+	 * be a strange thing. E.g. it appears when cold-booting with a
+	 * tape drive at id0. */
+	cmd->flags &= 0x3f;
+	if (cmd->scsi_done)
+	  (cmd->scsi_done)(cmd);
+     }   
+   if (lastSCSI == NO_SCSI)
      printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n");
    return;
 }
@@ -775,7 +946,7 @@
 #else
 	spin_lock_irqsave(&issue_lock, flags);
 #endif	
-	if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY))
+	if (!(inb(IM_STAT_REG(host_index)) & IM_BUSY))
 	  break;
 #ifdef OLDKERN	
 	restore_flags(flags);
@@ -791,6 +962,7 @@
 #else
    spin_unlock_irqrestore(&issue_lock, flags);
 #endif
+   return;
 }
 
 /*--------------------------------------------------------------------*/
@@ -798,6 +970,7 @@
 static void internal_done (Scsi_Cmnd * cmd)
 {
    cmd->SCp.Status++;
+   return;
 }
 
 /*--------------------------------------------------------------------*/
@@ -827,18 +1000,18 @@
      {
 	if (bypass_controller)
 	  { /* bypass the hardware integrated command set */
-	     scb->command = IM_OTHER_SCSI_CMD_CMD;      
-	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; 
+	     scb->command = IM_OTHER_SCSI_CMD_CMD | IM_NO_DISCONNECT;      
+	     scb->enable  = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
 	     scb->u1.scsi_cmd_length = cmd.cmd_len;
 	     memcpy (scb->u2.scsi_command, &(cmd.cmnd), cmd.cmd_len);
 	     last_scsi_command(host_index)[ldn] = INQUIRY;
-	     last_scsi_type(host_index)[ldn] = IM_SCB;
+	     last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 	  }
 	else
 	  {
 	     /*fill scb with inquiry command */
-	     scb->command = IM_DEVICE_INQUIRY_CMD;
-	     scb->enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+	     scb->command = IM_DEVICE_INQUIRY_CMD | IM_NO_DISCONNECT;
+	     scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
 	     last_scsi_command(host_index)[ldn] = IM_DEVICE_INQUIRY_CMD;
 	     last_scsi_type(host_index)[ldn] = IM_SCB;	     
 	  }
@@ -848,16 +1021,17 @@
 	
 	/*issue scb to passed ldn, and busy wait for interrupt */
 	got_interrupt(host_index) = 0;
-	issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
+	if ((scb->command & IM_OTHER_SCSI_CMD_CMD) == IM_OTHER_SCSI_CMD_CMD)
+	  issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn);
+	else
+	  issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
 	while (!got_interrupt(host_index))
 	  barrier ();
 	
 	/*if command succesful, break */
 	if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED)||
 	    (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
-	  {
-	     return 1;
-	  }
+	  return 1;
      }
    
    /*if all three retries failed, return "no device at this ldn" */
@@ -892,7 +1066,7 @@
 	if (bypass_controller)
 	  { /* bypass the SCSI-command */
 	     scb->command = IM_OTHER_SCSI_CMD_CMD;      
-	     scb->enable |= IM_READ_CONTROL;
+	     scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
 	     scb->u1.scsi_cmd_length = cmd.cmd_len;
 	     memcpy (scb->u2.scsi_command, &(cmd.cmnd), cmd.cmd_len);
 	     last_scsi_command(host_index)[ldn] = READ_CAPACITY;
@@ -901,7 +1075,7 @@
 	else
 	  {
 	     scb->command = IM_READ_CAPACITY_CMD;
-	     scb->enable = IM_READ_CONTROL;
+	     scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
 	     last_scsi_command(host_index)[ldn] = IM_READ_CAPACITY_CMD;
 	     last_scsi_type(host_index)[ldn] = IM_SCB;	     
 	  }
@@ -911,16 +1085,17 @@
 	
 	/*issue scb to passed ldn, and busy wait for interrupt */
 	got_interrupt(host_index) = 0;
-	issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
+	if ((scb->command & IM_OTHER_SCSI_CMD_CMD) == IM_OTHER_SCSI_CMD_CMD)
+	  issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn);
+	else
+	  issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
 	while (!got_interrupt(host_index))
 	  barrier ();
 	     
-	     /*if got capacity, get block length and return one device found */
+	/*if got capacity, get block length and return one device found */
 	if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED)||
 	    (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
-	  {
-	     return 1;
-	  }
+	  return 1;
      }
    /*if all three retries failed, return "no device at this ldn" */
    if (retries >= 3)
@@ -929,6 +1104,52 @@
      return 1;
 }
 
+static int get_pos_info(int host_index)
+{
+   int retries;
+   struct im_scb *scb;
+   struct im_tsb *tsb;
+   unsigned char *buf;
+   
+   scb = &(ld(host_index)[MAX_LOG_DEV].scb);
+   tsb = &(ld(host_index)[MAX_LOG_DEV].tsb);
+   buf = (unsigned char *)(&(ld(host_index)[MAX_LOG_DEV].buf));
+   ld(host_index)[MAX_LOG_DEV].tsb.dev_status = 0;
+
+   for (retries = 0; retries < 3; retries++)
+     {
+	/*fill scb with get_pos_info command */
+	scb->command = IM_GET_POS_INFO_CMD;
+	scb->enable = IM_READ_CONTROL | IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE | IM_BYPASS_BUFFER | IM_SUPRESS_EXCEPTION_SHORT;
+	last_scsi_command(host_index)[MAX_LOG_DEV] = IM_GET_POS_INFO_CMD;
+	last_scsi_type(host_index)[MAX_LOG_DEV] = IM_SCB;	     
+
+	scb->sys_buf_adr = virt_to_bus(buf);
+	if (special(host_index)==IBM_SCSI2_FW)
+	  scb->sys_buf_length = 256; /* get all info from F/W adapter */
+	else
+	  scb->sys_buf_length = 18; /* get exactly 18 bytes for other SCSI */
+	
+	scb->tsb_adr = virt_to_bus(tsb);
+	
+	/*issue scb to ldn=15, and busy wait for interrupt */
+	got_interrupt(host_index) = 0;
+	issue_cmd (host_index, virt_to_bus(scb), IM_SCB | MAX_LOG_DEV);	
+	while (!got_interrupt(host_index))
+	  barrier ();
+	     
+	/*if got POS-stuff, get block length and return one device found */
+	if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED)||
+	    (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
+	  return 1;
+     }
+   /* if all three retries failed, return "no device at this ldn" */
+   if (retries >= 3)
+     return 0;
+   else
+     return 1;
+}
+
 /* SCSI-immediate-command for assign. This functions maps/unmaps specific
  ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
  subsystem and for dynamical remapping od ldns. */
@@ -941,26 +1162,80 @@
    
    for (retries=0; retries<3; retries ++)
      {
-        imm_command = inl(IM_CMD_REG(host_index));
-        imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */
-        imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD);
-        imm_command |= (unsigned long)((lun & 7) << 24);
-        imm_command |= (unsigned long)((operation & 1) << 23);
-        imm_command |= (unsigned long)((pun & 7) << 20);
-        imm_command |= (unsigned long)((ldn & 15) << 16);
-	
-	last_scsi_command(host_index)[0xf] = IM_ASSIGN_IMM_CMD;
-	last_scsi_type(host_index)[0xf] = IM_IMM_CMD;
+	/* select mutation level of the SCSI-adapter */
+	switch (special(host_index))
+	  {
+	   case IBM_SCSI2_FW:
+	     imm_command = (unsigned long)(IM_ASSIGN_IMM_CMD);
+	     imm_command |= (unsigned long)((lun & 7) << 24);
+	     imm_command |= (unsigned long)((operation & 1) << 23);
+	     imm_command |= (unsigned long)((pun & 7)<< 20)|((pun & 8)<< 24);
+	     imm_command |= (unsigned long)((ldn & 15) << 16);
+	     break;
+	   default:
+	     imm_command = inl(IM_CMD_REG(host_index));
+	     imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */
+	     imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD);
+	     imm_command |= (unsigned long)((lun & 7) << 24);
+	     imm_command |= (unsigned long)((operation & 1) << 23);
+	     imm_command |= (unsigned long)((pun & 7) << 20);
+	     imm_command |= (unsigned long)((ldn & 15) << 16);
+	     break;
+	  }		
+	last_scsi_command(host_index)[MAX_LOG_DEV] = IM_ASSIGN_IMM_CMD;
+	last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD;
         got_interrupt(host_index) = 0;
-        issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | 0xf);
+        issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | MAX_LOG_DEV);
         while (!got_interrupt(host_index))
 	  barrier ();
 	
         /*if command succesful, break */
 	if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
+	  return 1;
+     }
+   if (retries >= 3) 
+     return 0;
+   else
+     return 1;
+}
+
+static int immediate_feature(int host_index, unsigned int speed, 
+			     unsigned int timeout)
+{
+   int retries;
+   unsigned long imm_command;
+   
+   for (retries=0; retries<3; retries ++)
+     {
+	/* select mutation level of the SCSI-adapter */
+	switch (special(host_index))
 	  {
-	     return 1;
+	   default:
+	     imm_command  = IM_FEATURE_CTR_IMM_CMD;
+	     imm_command |= (unsigned long)((speed & 0x7) << 29);
+	     imm_command |= (unsigned long)((timeout & 0x1fff) << 16);
+	     break;
+	  }		
+	last_scsi_command(host_index)[MAX_LOG_DEV] = IM_FEATURE_CTR_IMM_CMD;
+	last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD;
+        got_interrupt(host_index) = 0;
+	/* we need to run into command errors in order to probe for the
+	 * right speed! */
+	global_command_error_excuse = 1;
+        issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | MAX_LOG_DEV);
+        while (!got_interrupt(host_index))
+	  barrier ();
+	if (global_command_error_excuse == CMD_FAIL)
+	  {
+	     global_command_error_excuse = 0;	 
+	     return 2;
 	  }
+	else
+	  global_command_error_excuse = 0;
+	
+        /*if command succesful, break */
+	if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
+	  return 1;
      }
    
    if (retries >= 3) 
@@ -1004,9 +1279,7 @@
 	  }
         /*if command succesful, break */
 	if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
-	  {
-	     return 1;
-	  }
+	  return 1;
      }
    
    if (retries >= 3) 
@@ -1052,6 +1325,97 @@
    return (char *)&answer;
 }
 
+/* transfers bitpattern of the feature command to values in MHz */
+static char *ibmrate(unsigned int speed, int adaptertype)
+{
+   int i;
+   i=adaptertype;
+   switch (speed)
+     {
+      case 0: if (i) return "5.00"; else return "10.00"; break;
+      case 1: if (i) return "4.00"; else return "8.00"; break;
+      case 2: if (i) return "3.33"; else return "6.66"; break;
+      case 3: if (i) return "2.86"; else return "5.00"; break;
+      case 4: if (i) return "2.50"; else return "4.00"; break;
+      case 5: if (i) return "2.22"; else return "3.10"; break;
+      case 6: if (i) return "2.00"; else return "2.50"; break;
+      case 7: if (i) return "1.82"; else return "2.00"; break;
+     }
+   return "---";
+}
+
+static int probe_display(int what)
+{
+   static int rotator = 0;
+   const char rotor[] = "|/-\\";
+   
+   if (!(display_mode & LED_DISP))
+     return 0;
+   if (!what)
+     {
+	outl(0x20202020,MOD95_LED_PORT);
+	outl(0x20202020,MOD95_LED_PORT+4);
+     }
+   else
+     {
+	outb('S',MOD95_LED_PORT+7);
+	outb('C',MOD95_LED_PORT+6);
+	outb('S',MOD95_LED_PORT+5);
+	outb('I',MOD95_LED_PORT+4);
+	outb('i',MOD95_LED_PORT+3);
+	outb('n',MOD95_LED_PORT+2);
+	outb('i',MOD95_LED_PORT+1);
+	outb((char)(rotor[rotator]),MOD95_LED_PORT);
+	rotator++;
+	if (rotator>3) 
+	  rotator=0;
+     }
+   return 0;  
+}
+
+static int probe_bus_mode(int host_index)
+{
+   struct im_pos_info *info;
+   int num_bus = 0;
+   int ldn;
+   
+   info = (struct im_pos_info *)(&(ld(host_index)[MAX_LOG_DEV].buf));
+   
+   if (get_pos_info(host_index))
+     {
+	if (info->connector_size & 0xf000)
+	  subsystem_connector_size(host_index)=16;
+	else
+	  subsystem_connector_size(host_index)=32;	
+	num_bus |= (info->pos_4b & 8) >> 3;
+	for (ldn=0; ldn<=MAX_LOG_DEV; ldn++)
+	  {
+	     if ((special(host_index)==IBM_SCSI_WCACHE)||
+		 (special(host_index)==IBM_7568_WCACHE))
+	       {     		  
+		  if (!((info->cache_stat >> ldn) & 1))
+		    ld(host_index)[ldn].cache_flag = 0;
+	       }
+	     if (!((info->retry_stat >> ldn) & 1))
+	       ld(host_index)[ldn].retry_flag = 0;
+	  }
+#ifdef IM_DEBUG_PROBE	
+	printk("IBM MCA SCSI: SCSI-Cache bits: ");
+	for (ldn=0; ldn<=MAX_LOG_DEV; ldn++)
+	  {
+	     printk("%d",ld(host_index)[ldn].cache_flag);
+	  }	
+	printk("\nIBM MCA SCSI: SCSI-Retry bits: ");
+	for (ldn=0; ldn<=MAX_LOG_DEV; ldn++)
+	  {
+	     printk("%d",ld(host_index)[ldn].retry_flag);
+	  }
+	printk("\n");	
+#endif		
+     }
+   return num_bus;
+}
+
 /* 
  The following routine probes the SCSI-devices in four steps:
  1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter.
@@ -1072,27 +1436,31 @@
  The assignment of ALL ldns avoids dynamical remapping by the adapter
  itself.
  */
-static void check_devices (int host_index)
+static void check_devices (int host_index, int adaptertype)
 {
    int id, lun, ldn, ticks;
    int count_devices; /* local counter for connected device */
-   
+   int max_pun;
+   int num_bus;
+   int speedrun; /* local adapter_speed check variable */
+      
    /* assign default values to certain variables */
-
    ticks = 0;
    count_devices = 0;
    IBM_DS(host_index).dyn_flag = 0; /* normally no need for dynamical ldn management */
    IBM_DS(host_index).total_errors = 0; /* set errorcounter to 0 */
    next_ldn(host_index) = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/
+
+   /* initialize the very important driver-informational arrays/structs */
+   memset (ld(host_index), 0, 
+	   sizeof(ld(host_index)));   
    for (ldn=0; ldn<=MAX_LOG_DEV; ldn++)
      {
 	last_scsi_command(host_index)[ldn] = NO_SCSI; /* emptify last SCSI-command storage */
 	last_scsi_type(host_index)[ldn] = 0;
+	ld(host_index)[ldn].cache_flag = 1;
+	ld(host_index)[ldn].retry_flag = 1;
      }
-
-   /* initialize the very important driver-informational arrays/structs */
-   memset (ld(host_index), 0, 
-	   sizeof(ld(host_index)));
    memset (get_ldn(host_index), TYPE_NO_DEVICE, 
 	   sizeof(get_ldn(host_index))); /* this is essential ! */
    memset (get_scsi(host_index), TYPE_NO_DEVICE,
@@ -1105,14 +1473,59 @@
 								ldn is active for all
 								luns. */
      }
+
+   probe_display(0); /* Supercool display usage during SCSI-probing. */
+                     /* This makes sense, when booting without any */
+                     /* monitor connected on model XX95. */
    
    /* STEP 1: */
+   adapter_speed(host_index) = global_adapter_speed;
+   speedrun = adapter_speed(host_index);
+   while (immediate_feature(host_index,speedrun,adapter_timeout)==2)
+     {
+	probe_display(1);
+	if (speedrun==7)
+	  panic("IBM MCA SCSI: Cannot set Synchronous-Transfer-Rate!\n");
+	speedrun++;
+	if (speedrun>7) 
+	  speedrun=7;
+     }
+   adapter_speed(host_index) = speedrun;
+   /* Get detailed information about the current adapter, necessary for
+    * device operations: */
+   num_bus=probe_bus_mode(host_index);
+   
+   /* num_bus contains only valid data for the F/W adapter! */
+   if (adaptertype==IBM_SCSI2_FW) /* F/W SCSI adapter: */
+     {
+	/* F/W adapter PUN-space extension evaluation: */
+	if (num_bus)	  
+	  {	     
+	     printk("IBM MCA SCSI: Seperate bus mode (wide-addressing enabled)\n");
+	     subsystem_maxid(host_index) = 16;
+	  }	
+	else
+	  {	     
+	     printk("IBM MCA SCSI: Combined bus mode (wide-addressing disabled)\n");
+	     subsystem_maxid(host_index) = 8;
+	  }	
+	printk("IBM MCA SCSI: Sync.-Rate (F/W: 20, Int.: 10, Ext.: %s) MBytes/s\n",
+	       ibmrate(speedrun,adaptertype));
+     }
+   else /* all other IBM SCSI adapters: */
+     printk("IBM MCA SCSI: Synchronous-SCSI-Transfer-Rate: %s MBytes/s\n",
+	    ibmrate(speedrun,adaptertype));
+   
+   /* assign correct PUN device space */
+   max_pun = subsystem_maxid(host_index);
+     
 #ifdef IM_DEBUG_PROBE
    printk("IBM MCA SCSI: Current SCSI-host index: %d\n",host_index);
 #endif
    printk("IBM MCA SCSI: Removing default logical SCSI-device mapping.");
    for (ldn=0; ldn<MAX_LOG_DEV; ldn++)
      {
+	probe_display(1);
 #ifdef IM_DEBUG_PROBE
 	printk(".");
 #endif
@@ -1122,19 +1535,25 @@
    lun = 0; /* default lun is 0 */
    
    /* STEP 2: */
-   printk("\nIBM MCA SCSI: Probing SCSI-devices.");
-   for (id=0; id<8; id++)
+   printk("\nIBM MCA SCSI: Probing SCSI-devices");
+#ifndef IM_DEBUG_PROBE
+   printk(" (this can take up to 2 minutes)");
+#endif
+   printk(".");
+   
+   for (id=0; id<max_pun ; id++)
 #ifdef CONFIG_SCSI_MULTI_LUN
      for (lun=0; lun<8; lun++)
 #endif
      {
+	probe_display(1);
 #ifdef IM_DEBUG_PROBE
 	printk(".");
 #endif
 	if (id != subsystem_pun(host_index))
 	  {            /* if pun is not the adapter: */
 	     /*set ldn=0 to pun,lun*/
-	     immediate_assign(host_index,id,lun,PROBE_LDN,SET_LDN); 
+	     immediate_assign(host_index,id,lun,PROBE_LDN,SET_LDN);
 	     if (device_inquiry(host_index, PROBE_LDN)) /* probe device */
 	       {
 		  get_scsi(host_index)[id][lun]=
@@ -1144,7 +1563,7 @@
 		    count_devices++; /* a existing device is found */
 	       }
 	     /* remove ldn */
-	     immediate_assign(host_index,id,lun,PROBE_LDN,REMOVE_LDN); 
+	     immediate_assign(host_index,id,lun,PROBE_LDN,REMOVE_LDN);
 	  }
      }
    
@@ -1157,8 +1576,9 @@
 #ifdef CONFIG_SCSI_MULTI_LUN   
    for (lun=0; lun<8 && ldn<MAX_LOG_DEV; lun++)
 #endif
-     for (id=0; id<8 && ldn<MAX_LOG_DEV; id++)
+     for (id=0; id<max_pun && ldn<MAX_LOG_DEV; id++)
      {
+	probe_display(1);
 #ifdef IM_DEBUG_PROBE
 	printk(".");
 #endif
@@ -1208,11 +1628,12 @@
    
    /* map remaining ldns to non-existing devices */
    for (lun=1; lun<8 && ldn<MAX_LOG_DEV; lun++)
-     for (id=0; id<8 && ldn<MAX_LOG_DEV; id++)
+     for (id=0; id<max_pun && ldn<MAX_LOG_DEV; id++)
      {
 	if (get_scsi(host_index)[id][lun] == TYPE_NO_LUN ||
 	    get_scsi(host_index)[id][lun] == TYPE_NO_DEVICE)
 	  {
+	     probe_display(1);	     
 	     /* Map remaining ldns only to NON-existing pun,lun
 	      combinations to make sure an inquiry will fail. 
 	      For MULTI_LUN, it is needed to avoid adapter autonome
@@ -1234,7 +1655,7 @@
    printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
    printk("    Physical SCSI-Device Map               Logical SCSI-Device Map\n");
    printk("ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
-   for (id=0; id<8; id++)
+   for (id=0; id<max_pun; id++)
      {
         printk("%2d     ",id);
 	for (lun=0; lun<8; lun++)
@@ -1261,6 +1682,8 @@
      printk("IBM MCA SCSI: Warning: No SCSI-devices found/assigned!\n");
    
    /* reset the counters for statistics on the current adapter */
+   IBM_DS(host_index).scbs = 0;
+   IBM_DS(host_index).long_scbs = 0;
    IBM_DS(host_index).total_accesses = 0;
    IBM_DS(host_index).total_interrupts = 0;
    IBM_DS(host_index).dynamical_assignments = 0;
@@ -1276,7 +1699,7 @@
 	   sizeof (IBM_DS(host_index).ldn_modeselect_access));
    memset (IBM_DS(host_index).ldn_assignments, 0x0, 
 	   sizeof (IBM_DS(host_index).ldn_assignments));
-   
+   probe_display(0);
    return;
 }
 
@@ -1375,7 +1798,7 @@
    
 #ifdef CONFIG_SCSI_IBMMCA
 
-void ibmmca_scsi_setup (char *str, int *ints)
+void internal_ibmmca_scsi_setup (char *str, int *ints)
 {
    int i, j, io_base, id_base;
    char *token;
@@ -1389,36 +1812,30 @@
 	j = 0;
 	while (token)
 	  {
+	     if (!strcmp(token,"activity"))
+	       display_mode |= LED_ACTIVITY;
 	     if (!strcmp(token,"display"))
-	       {
-		  use_display = 1;
-	       }
+	       display_mode |= LED_DISP;
 	     if (!strcmp(token,"adisplay"))
-	       {
-		  use_adisplay = 1;
-	       }
+	       display_mode |= LED_ADISP;
 	     if (!strcmp(token,"bypass"))
-	       {
-		  bypass_controller = 1;
-	       }
+	       bypass_controller = 1;
 	     if (!strcmp(token,"normal"))
-	       {
-		  ibm_ansi_order = 0;
-	       }
+	       ibm_ansi_order = 0;
 	     if (!strcmp(token,"ansi"))
-	       {
-		  ibm_ansi_order = 1;
-	       }
+	       ibm_ansi_order = 1;
+	     if (!strcmp(token,"fast"))
+	       global_adapter_speed = 0;
+	     if (!strcmp(token,"medium"))
+	       global_adapter_speed = 4;
+	     if (!strcmp(token,"slow"))
+	       global_adapter_speed = 7;	     
 	     if ( (*token == '-') || (isdigit(*token)) )
 	       {
 		  if (!(j%2) && (io_base < IM_MAX_HOSTS))
-		    {
-		       io_port[io_base++] = simple_strtoul(token,NULL,0);
-		    }
+		    io_port[io_base++] = simple_strtoul(token,NULL,0);
 		  if ((j%2) && (id_base < IM_MAX_HOSTS))
-		    {
-		       scsi_id[id_base++] = simple_strtoul(token,NULL,0);
-		    }
+		    scsi_id[id_base++] = simple_strtoul(token,NULL,0);
 		  j++;
 	       }
 	     token = strtok(NULL,",");
@@ -1442,7 +1859,7 @@
 static int ibmmca_getinfo (char *buf, int slot, void *dev)
 {
    struct Scsi_Host *shpnt;
-   int len, special;
+   int len, speciale,connectore;
    unsigned int pos2, pos3;
    static unsigned long flags;
      
@@ -1456,21 +1873,22 @@
    shpnt = dev; /* assign host-structure to local pointer */
    len = 0; /* set filled text-buffer index to 0 */
    /* get the _special contents of the hostdata structure */
-   special = ((struct ibmmca_hostdata *)shpnt->hostdata)->_special;
+   speciale = ((struct ibmmca_hostdata *)shpnt->hostdata)->_special;
+   connectore = ((struct ibmmca_hostdata *)shpnt->hostdata)->_connector_size;
    pos2 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2;
    pos3 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3;
    
-   if (special == FORCED_DETECTION) /* forced detection */
+   if (speciale == FORCED_DETECTION) /* forced detection */
      {
-	len += sprintf (buf + len, "Adapter cathegory: forced detected\n");
+	len += sprintf (buf + len, "Adapter category: forced detected\n");
 	len += sprintf(buf + len, "***************************************\n");
 	len += sprintf(buf + len, "***  Forced detected SCSI Adapter   ***\n");
 	len += sprintf(buf + len, "***  No chip-information available  ***\n");
 	len += sprintf(buf + len, "***************************************\n");
      }
-   else if (special == INTEGRATED_SCSI)
+   else if (speciale == INTEGRATED_SCSI)
      { /* if the integrated subsystem has been found automatically: */
-	len += sprintf (buf + len, "Adapter cathegory: integrated\n");
+	len += sprintf (buf + len, "Adapter category: integrated\n");
 	len += sprintf (buf + len, "Chip revision level: %d\n",
 			((pos2 & 0xf0) >> 4));
 	len += sprintf (buf + len, "Chip status: %s\n",
@@ -1478,27 +1896,32 @@
 	len += sprintf (buf + len, "8 kByte NVRAM status: %s\n",
 			(pos2 & 2) ? "locked" : "accessible");
      }
-   else if ((special>=0)&&
-	   (special<(sizeof(subsys_list)/sizeof(struct subsys_list_struct))))
-     { /* if the subsystem is a slot adapter */
-	len += sprintf (buf + len, "Adapter cathegory: slot-card\n");
-	len += sprintf (buf + len, "Chip revision level: %d\n",
-			((pos2 & 0xf0) >> 4));
+   else if ((speciale>=0)&&
+	   (speciale<(sizeof(subsys_list)/sizeof(struct subsys_list_struct))))
+     { /* if the subsystem is a slot adapter */	
+	len += sprintf (buf + len, "Adapter category: slot-card\n");
+	len += sprintf (buf + len, "ROM Segment Address: ");
+	if ((pos2 & 0xf0) == 0xf0)
+	  len += sprintf (buf + len, "off\n");
+	else
+	  len += sprintf (buf + len, "0x%x\n",
+			((pos2 & 0xf0) << 13) + 0xc0000);
 	len += sprintf (buf + len, "Chip status: %s\n",
 			(pos2 & 1) ? "enabled" : "disabled");
-	len += sprintf (buf + len, "Port offset: 0x%x\n",
+	len += sprintf (buf + len, "Adapter I/O Offset: 0x%x\n",
 			((pos2 & 0x0e) << 2));
      }
    else
      {
-	len += sprintf (buf + len, "Adapter cathegory: unknown\n");
+	len += sprintf (buf + len, "Adapter category: unknown\n");
      }
    /* common subsystem information to write to the slotn file */
    len += sprintf (buf + len, "Subsystem PUN: %d\n", shpnt->this_id);
-   len += sprintf (buf + len, "I/O base address range: 0x%x-0x%x",
+   len += sprintf (buf + len, "I/O base address range: 0x%x-0x%x\n",
 		   (unsigned int)(shpnt->io_port), 
 		   (unsigned int)(shpnt->io_port+7));
-   /* Now make sure, the bufferlength is devideable by 4 to avoid
+   len += sprintf (buf + len, "MCA-slot size: %d bits",connectore);
+   /* Now make sure, the bufferlength is devidable by 4 to avoid
     * paging problems of the buffer. */
    while ( len % sizeof( int ) != ( sizeof ( int ) - 1 ) )
      {
@@ -1518,17 +1941,31 @@
 {
    struct Scsi_Host *shpnt;
    int port, id, i, j, list_size, slot;
+   int devices_on_irq_11 = 0;
+   int devices_on_irq_14 = 0;
+   int IRQ14_registered = 0;
+   int IRQ11_registered = 0;
    
    found = 0; /* make absolutely sure, that found is set to 0 */
-   
+
+   /* First of all, print the version number of the driver. This is 
+    * important to allow better user bugreports in case of already
+    * having problems with the MCA_bus probing. */
+   printk("IBM MCA SCSI: Version %s\n",IBMMCA_SCSI_DRIVER_VERSION);
    /* if this is not MCA machine, return "nothing found" */
    if (!MCA_bus)
      {
-	printk("IBM MCA SCSI: No Microchannel-bus support present -> Aborting.\n");
+	printk("IBM MCA SCSI: No Microchannel-bus present --> Aborting.\n");
+	printk("              This machine does not have any IBM MCA-bus\n");
+	printk("              or the MCA-Kernel-support is not enabled!\n");
 	return 0;
      }
-   else
-     printk("IBM MCA SCSI: Version %s\n",IBMMCA_SCSI_DRIVER_VERSION);
+
+#ifdef MODULE
+   /* If the driver is run as module, read from conf.modules or cmd-line */
+   if (boot_options) 
+     option_setup(boot_options);
+#endif   
    
    /* get interrupt request level */
 #ifdef OLDKERN
@@ -1542,7 +1979,9 @@
 	printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n", IM_IRQ);
 	return 0;
      }
-   
+   else
+     IRQ14_registered++;
+         
    /* if ibmmcascsi setup option was passed to kernel, return "found" */
    for (i = 0; i < IM_MAX_HOSTS; i++)
      if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8)
@@ -1550,16 +1989,21 @@
 	printk("IBM MCA SCSI: forced detected SCSI Adapter, io=0x%x, scsi id=%d.\n",
 	       io_port[i], scsi_id[i]);
 	if ((shpnt = ibmmca_register(scsi_template, io_port[i], scsi_id[i],
+		     FORCED_DETECTION,
 		     "forced detected SCSI Adapter")))
 	  {
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = 0;
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = 0;
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = 0;
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = 0;
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = 0;	     
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = 
 	       FORCED_DETECTION;
 	     mca_set_adapter_name(MCA_INTEGSCSI, "forced detected SCSI Adapter");
 	     mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
 				    shpnt);
 	     mca_mark_as_used(MCA_INTEGSCSI);
+	     devices_on_irq_14++;
 	  }	
      }
    if (found) return found;
@@ -1592,12 +2036,14 @@
    for (j=0;j<8;j++) /* read the pos-information */
      pos[j] = mca_read_stored_pos(MCA_INTEGSCSI,j);
    /* pos2 = pos3 = 0xff if there is no integrated SCSI-subsystem present  */
-   if (( pos[2] != 0xff) || (pos[3] != 0xff ))
+   /* if (( pos[2] != 0xff) || (pos[3] != 0xff )) */
+   /* Previous if-arguments do fail! Therefore, we use now the following to
+    * make sure, we see a real integrated onboard SCSI-interface: */
+   if ((!pos[0] && !pos[1] && pos[2]>0 && pos[3]>0 && !pos[4] && !pos[5] && !pos[6] && !pos[7]) ||
+       (pos[0]==0xff && pos[1]==0xff && pos[2]<0xff && pos[3]<0xff && pos[4]==0xff && pos[5]==0xff && pos[6]==0xff && pos[7]==0xff))
      {
 	if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */
-	  {
-	     port = IM_IO_PORT;
-	  } 
+	  port = IM_IO_PORT;
 	else 
 	  { /* if disabled, no IRQs will be generated, as the chip won't
 	     * listen to the incomming commands and will do really nothing,
@@ -1622,16 +2068,21 @@
 
 	/* register the found integrated SCSI-subsystem */
 	if ((shpnt = ibmmca_register(scsi_template, port, id,
+		     INTEGRATED_SCSI,
 		     "IBM Integrated SCSI Controller")))
 	  {
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = pos[2];
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = pos[3];
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = pos[4];
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5];
+	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6];
 	     ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = 
 	       INTEGRATED_SCSI;
 	     mca_set_adapter_name(MCA_INTEGSCSI, "IBM Integrated SCSI Controller");
 	     mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
 				    shpnt);
 	     mca_mark_as_used(MCA_INTEGSCSI);
+	     devices_on_irq_14++;
 	  }
      }
    
@@ -1657,42 +2108,177 @@
 		  printk("IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
 		  printk("              SCSI-operations may not work.\n");
 	       }      	     
-	     id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
+	     if ((i==IBM_SCSI2_FW)&&(pos[6]!=0))	       
+	       {
+		  printk("IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
+		  printk("              Impossible to determine adapter PUN!\n");
+		  printk("              Guessing adapter PUN = 7.\n");
+		  id = 7;
+	       }
+	     else
+	       {		  
+		  id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
+		  if (i==IBM_SCSI2_FW)
+		    {		       
+		       id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit
+						    * for F/W adapters */
+		    }		  
+	       }	     
+	     if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0))
+	       {  /* IRQ11 is used by SCSI-2 F/W Adapter/A */
+		  printk("IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
+		  /* get interrupt request level */
+#ifdef OLDKERN
+		  if (request_irq (IM_IRQ_FW, interrupt_handler, SA_SHIRQ, 
+				   "ibmmcascsi", hosts))
+#else
+		  if (request_irq (IM_IRQ_FW, do_interrupt_handler, SA_SHIRQ,
+				   "ibmmcascsi", hosts))
+#endif
+		    {
+		       printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n",
+			      IM_IRQ_FW);
+		    }
+		  else
+		    IRQ11_registered++;		  
+	       }
 	     printk("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d,\n",
 		     subsys_list[i].description, slot + 1, port, id);
-	     printk("              chip rev.=%d, port-offset=0x%x, subsystem=%s\n",
-		    ((pos[2] & 0xf0) >> 4), 
+	     if ((pos[2] & 0xf0) == 0xf0)
+	       printk("              ROM Addr.=off,");
+	     else
+	       printk("              ROM Addr.=0x%x,",
+		      ((pos[2] & 0xf0) << 13) + 0xc0000);
+	     printk(" port-offset=0x%x, subsystem=%s\n",
 		    ((pos[2] & 0x0e) << 2),
 		    (pos[2] & 1) ? "enabled." : "disabled.");
 	     
 	     /* register the hostadapter */
-	     if ((shpnt = ibmmca_register(scsi_template, port, id,
+	     if ((shpnt = ibmmca_register(scsi_template, port, id, i,
 		          subsys_list[i].description)))
 	       {
 		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = pos[2];
 		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = pos[3];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = pos[4];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6];
 		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = i;
 
 		  mca_set_adapter_name (slot, subsys_list[i].description);
 		  mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
 					  shpnt);
 		  mca_mark_as_used(slot);
+		  if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0))
+		    devices_on_irq_11++;
+		  else
+		    devices_on_irq_14++;
 	       }
 	     slot++; /* advance to next slot */
 	  } /* advance to next adapter id in the list of IBM-SCSI-subsystems*/
      }
+
    
-   if (!found) 
-     { /* maybe ESDI, or other producers' SCSI-hosts */
-	free_irq (IM_IRQ, hosts);
-	printk("IBM MCA SCSI: No IBM SCSI-subsystem adapter attached.\n");
-     }  
+   /* now look for SCSI-adapters, by bugs mapped to the integrated SCSI
+    * area. E.g. a W/Cache in MCA-slot 9 ???? Arrrrgh!! */
+   list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct);
+   for (i = 0; i < list_size; i++)
+     { /* scan each slot for a fitting adapter id */
+	slot = mca_find_adapter(subsys_list[i].mca_id, MCA_INTEGSCSI);
+	if (slot != MCA_NOTFOUND)
+	  { /* scan through all slots */
+	     for (j=0;j<8;j++) /* read the pos-information */
+	       pos[j] = mca_read_stored_pos(slot, j);
+	     if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */
+	       { /* (explanations see above) */
+		  port = IM_IO_PORT + ((pos[2] & 0x0e) << 2);
+	       } 
+	     else 
+	       { /* anyway, set the portnumber and warn */
+		  port = IM_IO_PORT + ((pos[2] & 0x0e) << 2); 
+		  printk("IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
+		  printk("              SCSI-operations may not work.\n");
+	       }
+	     if ((i==IBM_SCSI2_FW)&&(pos[6]!=0))	       
+	       {
+		  printk("IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
+		  printk("              Impossible to determine adapter PUN!\n");
+		  printk("              Guessing adapter PUN = 7.\n");
+		  id = 7;
+	       }
+	     else
+	       {		  
+		  id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
+		  if (i==IBM_SCSI2_FW)
+		    {		       
+		       id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit
+						    * for F/W adapters */
+		    }		  
+	       }	     
+	     if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0))
+	       {  /* IRQ11 is used by SCSI-2 F/W Adapter/A */
+		  printk("IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
+		  /* get interrupt request level */
+#ifdef OLDKERN
+		  if (request_irq (IM_IRQ_FW, interrupt_handler, SA_SHIRQ, 
+				   "ibmmcascsi", hosts))
+#else
+		  if (request_irq (IM_IRQ_FW, do_interrupt_handler, SA_SHIRQ,
+				   "ibmmcascsi", hosts))
+#endif
+		    {
+		       printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n",
+			      IM_IRQ_FW);
+		    }
+		  else
+		    IRQ11_registered++;		  
+	       }
+	     printk("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d,\n",
+		     subsys_list[i].description, slot + 1, port, id);
+	     if ((pos[2] & 0xf0) == 0xf0)
+	       printk("              ROM Addr.=off,");
+	     else
+	       printk("              ROM Addr.=0x%x,",
+		      ((pos[2] & 0xf0) << 13) + 0xc0000);
+	     printk(" port-offset=0x%x, subsystem=%s\n",
+		    ((pos[2] & 0x0e) << 2),
+		    (pos[2] & 1) ? "enabled." : "disabled.");
+	     
+	     /* register the hostadapter */
+	     if ((shpnt = ibmmca_register(scsi_template, port, id, i,
+		          subsys_list[i].description)))
+	       {
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = pos[2];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = pos[3];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = pos[4];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6];
+		  ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = i;
+
+		  mca_set_adapter_name (slot, subsys_list[i].description);
+		  mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
+					  shpnt);
+		  mca_mark_as_used(slot);
+		  if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0))
+		    devices_on_irq_11++;
+		  else
+		    devices_on_irq_14++;
+	       }
+	     slot++; /* advance to next slot */
+	  } /* advance to next adapter id in the list of IBM-SCSI-subsystems*/
+     }
+   
+   if ( IRQ11_registered && !devices_on_irq_11 )
+     free_irq(IM_IRQ_FW, hosts); /* no devices on IRQ 11 */
+   if ( IRQ14_registered && !devices_on_irq_14 )
+     free_irq(IM_IRQ, hosts); /* no devices on IRQ 14 */
+   if ( !devices_on_irq_11 && !devices_on_irq_14 )
+     printk("IBM MCA SCSI: No IBM SCSI-subsystem adapter attached.\n");
    return found; /* return the number of found SCSI hosts. Should be 1 or 0. */
 }
 
 static struct Scsi_Host *
-ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id,
-		char *hostname)
+ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, 
+		int adaptertype, char *hostname)
 {
    struct Scsi_Host *shpnt;
    int i, j;
@@ -1716,15 +2302,18 @@
    
    /* request I/O region */
    request_region(port, IM_N_IO_PORT, hostname);
-
+      
    hosts[found] = shpnt; /* add new found hostadapter to the list */
+   special(found) = adaptertype; /* important assignment or else crash! */
+   subsystem_connector_size(found) = 0; /* preset slot-size */
    shpnt->irq = IM_IRQ; /* assign necessary stuff for the adapter */
    shpnt->io_port = port;
    shpnt->n_io_port = IM_N_IO_PORT;
    shpnt->this_id = id;
+   shpnt->max_id = 8; /* 8 PUNs are default */
    /* now, the SCSI-subsystem is connected to Linux */
-
-   ctrl = (unsigned int)(inb(IM_CTR_REG(found))); /* get control-register status */
+   
+   ctrl = (unsigned int)(inb(IM_CTR_REG(found))); /* get control-register status */   
 #ifdef IM_DEBUG_PROBE
    printk("IBM MCA SCSI: Control Register contents: %x, status: %x\n",
 	  ctrl,inb(IM_STAT_REG(found)));
@@ -1738,13 +2327,14 @@
    
    reset_status(found) = IM_RESET_NOT_IN_PROGRESS;
 
-   for (i = 0; i < 8; i++) /* reset the tables */
+   for (i = 0; i < 16; i++) /* reset the tables */
      for (j = 0; j < 8; j++)
      get_ldn(found)[i][j] = MAX_LOG_DEV;
 
    /* check which logical devices exist */
-   local_checking_phase_flag(found) = 1;
-   check_devices(found); /* call by value, using the global variable hosts*/
+   /* after this line, local interrupting is possible: */
+   local_checking_phase_flag(found) = 1;   
+   check_devices(found,adaptertype); /* call by value, using the global variable hosts*/
    local_checking_phase_flag(found) = 0;
    
    found++; /* now increase index to be prepared for next found subsystem */
@@ -1802,30 +2392,43 @@
    int id,lun;
    int target;
    int host_index;
-   
-   if (ibm_ansi_order)
-     target = 6 - cmd->target;
-   else
-     target = cmd->target;
+   int max_pun;
+   int i;
+   struct scatterlist *sl;
    
    shpnt = cmd->host;
-   
+
    /* search for the right hostadapter */
    for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++);
    
    if (!hosts[host_index])
      { /* invalid hostadapter descriptor address */
 	cmd->result = DID_NO_CONNECT << 16;
-	done (cmd);
+	if (done)
+	  done (cmd);
 	return 0;
      }
    
+   max_pun = subsystem_maxid(host_index);
+   
+   if (ibm_ansi_order)
+     {	
+	target = max_pun - 1 - cmd->target;
+	if ((target <= subsystem_pun(host_index))&&(cmd->target <= subsystem_pun(host_index)))
+	  target--;
+	else if ((target >= subsystem_pun(host_index))&&(cmd->target >= subsystem_pun(host_index)))
+	  target++;
+     }   	
+   else
+     target = cmd->target;
+   
    /*if (target,lun) is NO LUN or not existing at all, return error */
    if ((get_scsi(host_index)[target][cmd->lun] == TYPE_NO_LUN)||
        (get_scsi(host_index)[target][cmd->lun] == TYPE_NO_DEVICE))
      {
 	cmd->result = DID_NO_CONNECT << 16;
-	done (cmd);
+	if (done)
+	  done (cmd);
 	return 0;
      }
    
@@ -1848,13 +2451,14 @@
 		       printk("              Reporting DID_NO_CONNECT for device (%d,%d).\n",
 			      target, cmd->lun);
 		       cmd->result = DID_NO_CONNECT << 16;/* return no connect*/
-		       done (cmd);
+		       if (done)
+			 done (cmd);
 		       return 0;
 		    }
 	       }
 	     
 	     /* unmap non-processing ldn */
-	     for (id=0; id<8; id ++)
+	     for (id=0; id<max_pun; id ++)
 	       for (lun=0; lun<8; lun++)
 	       {
 		  if (get_ldn(host_index)[id][lun] == next_ldn(host_index))
@@ -1907,7 +2511,8 @@
 	else
 	  {  /* wall against Linux accesses to the subsystem adapter */	 
 	     cmd->result = DID_BAD_TARGET << 16;
-	     done (cmd);
+	     if (done)
+	       done (cmd);
 	     return 0;
 	  }
      }
@@ -1923,17 +2528,19 @@
    /*fill scb information independent of the scsi command */
    scb = &(ld(host_index)[ldn].scb);
    ld(host_index)[ldn].tsb.dev_status = 0;
-   scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR;
+   scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE;
    scb->tsb_adr = virt_to_bus(&(ld(host_index)[ldn].tsb));
+   scsi_cmd = cmd->cmnd[0];
+   
    if (cmd->use_sg)
      {
-	int i = cmd->use_sg;
-	struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer;
+	i = cmd->use_sg;
+	sl = (struct scatterlist *)(cmd->request_buffer);
 	if (i > 16)
 	  panic ("IBM MCA SCSI: scatter-gather list too long.\n");
 	while (--i >= 0)
 	  {
-	     ld(host_index)[ldn].sge[i].address = (void *) virt_to_bus(sl[i].address);
+	     ld(host_index)[ldn].sge[i].address = (void *)(virt_to_bus(sl[i].address));
 	     ld(host_index)[ldn].sge[i].byte_length = sl[i].length;
 	  }
 	scb->enable |= IM_POINTER_TO_LIST;
@@ -1943,11 +2550,26 @@
    else
      {
 	scb->sys_buf_adr = virt_to_bus(cmd->request_buffer);
-	scb->sys_buf_length = cmd->request_bufflen;
+	/* recent Linux midlevel SCSI places 1024 byte for inquiry
+	 * command. Far too much for old PS/2 hardware. */	
+	switch (scsi_cmd)
+	  { /* avoid command errors by setting bufferlengths to
+	     * ANSI-standard. */
+	   case INQUIRY:
+	   case REQUEST_SENSE:
+	   case MODE_SENSE:
+	   case MODE_SELECT:
+	     scb->sys_buf_length = 255;
+	     break;
+	   case TEST_UNIT_READY:
+	     scb->sys_buf_length = 0;
+	     break;
+	   default:
+	     scb->sys_buf_length = cmd->request_bufflen;
+	     break;
+	  }
      }
-   
    /*fill scb information dependent on scsi command */
-   scsi_cmd = cmd->cmnd[0];
    
 #ifdef IM_DEBUG_CMD
    printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
@@ -1962,8 +2584,7 @@
    
    /* for possible panics store current command */
    last_scsi_command(host_index)[ldn] = scsi_cmd; 
-   last_scsi_type(host_index)[ldn] = IM_SCB;
-   
+   last_scsi_type(host_index)[ldn] = IM_SCB;   
    /* update statistical info */
    IBM_DS(host_index).total_accesses++;
    IBM_DS(host_index).ldn_access[ldn]++;
@@ -1976,16 +2597,9 @@
       case WRITE_10:
       case READ_12:
       case WRITE_12:       
-	/* statistics for proc_info */
-	if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12))
-	  IBM_DS(host_index).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
-	else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)||
-		 (scsi_cmd == WRITE_12))
-	  IBM_DS(host_index).ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/
-	
 	/* Distinguish between disk and other devices. Only disks (that are the
 	   most frequently accessed devices) should be supported by the 
-         IBM-SCSI-Subsystem commands. */
+           IBM-SCSI-Subsystem commands. */
 	switch (ld(host_index)[ldn].device_type)
 	  {
 	   case TYPE_DISK: /* for harddisks enter here ... */
@@ -1994,31 +2608,32 @@
 	     if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || 
 		 scsi_cmd == READ_12)
 	       { /* read command preparations */
+		  scb->enable |= IM_READ_CONTROL;
+		  IBM_DS(host_index).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
 		  if (bypass_controller)
 		    {
 		       scb->command = IM_OTHER_SCSI_CMD_CMD;
-		       scb->enable |= IM_READ_CONTROL;
+		       scb->enable |= IM_BYPASS_BUFFER;
 		       scb->u1.scsi_cmd_length = cmd->cmd_len;
 		       memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len);
+		       last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 		    }
 		  else
-		    {
-		       scb->command = IM_READ_DATA_CMD;
-		       scb->enable |= IM_READ_CONTROL;
-		    }
+		    scb->command = IM_READ_DATA_CMD | IM_NO_DISCONNECT;
 	       }
 	     else
 	       { /* write command preparations */
+		  IBM_DS(host_index).ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/
 		  if (bypass_controller)
 		    {
 		       scb->command = IM_OTHER_SCSI_CMD_CMD;
+		       scb->enable |= IM_BYPASS_BUFFER;
 		       scb->u1.scsi_cmd_length = cmd->cmd_len;
 		       memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len);
+		       last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 		    }
 		  else
-		    { 
-		       scb->command = IM_WRITE_DATA_CMD;
-		    }
+		    scb->command = IM_WRITE_DATA_CMD | IM_NO_DISCONNECT;
 	       }
 	     
 	     if (!bypass_controller)
@@ -2039,6 +2654,8 @@
 		       scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
 			 (((unsigned) cmd->cmnd[7]) << 8);
 		    }
+		  last_scsi_logical_block(host_index)[ldn] = scb->u1.log_blk_adr;
+		  last_scsi_blockcount(host_index)[ldn] = scb->u2.blk.count;
 		  scb->u2.blk.length = ld(host_index)[ldn].block_length;
 	       }	    
 	     if (++disk_rw_in_progress == 1)
@@ -2052,8 +2669,7 @@
 	   case TYPE_PROCESSOR:
 	   case TYPE_WORM:
 	   case TYPE_SCANNER:
-	   case TYPE_MEDIUM_CHANGER:
-	     
+	   case TYPE_MEDIUM_CHANGER:	     
 	     /* If there is a sequential-device, IBM recommends to use
 	      IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. 
 	      Good/modern CD-ROM-drives are capable of
@@ -2068,15 +2684,14 @@
 	     scb->command = IM_OTHER_SCSI_CMD_CMD;
 	     if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || 
 		 scsi_cmd == READ_12) /* enable READ */
-	       {
-		  scb->enable |= IM_READ_CONTROL;
-	       }
-	     
+	       scb->enable |= IM_READ_CONTROL;
+	     scb->enable |= IM_BYPASS_BUFFER;
 	     scb->u1.scsi_cmd_length = cmd->cmd_len;
 	     memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	     last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 	     
 	     /* Read/write on this non-disk devices is also displayworthy, 
-	      so flash-up the LED/display. */
+	        so flash-up the LED/display. */
 	     if (++disk_rw_in_progress == 1)
 	       PS2_DISK_LED_ON (shpnt->host_no, target);
 	     break;
@@ -2087,17 +2702,27 @@
 	if (bypass_controller)
 	  {
 	     scb->command = IM_OTHER_SCSI_CMD_CMD;
-	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+	     scb->u1.log_blk_adr = 0;
 	     scb->u1.scsi_cmd_length = cmd->cmd_len;
 	     memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	     last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 	  }
 	else
 	  {
 	     scb->command = IM_DEVICE_INQUIRY_CMD;
 	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+	     scb->u1.log_blk_adr = 0;
 	  }
-	break;	
-
+	break;
+      case TEST_UNIT_READY:
+	scb->command = IM_OTHER_SCSI_CMD_CMD;
+	scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+	scb->u1.log_blk_adr = 0;
+	scb->u1.scsi_cmd_length = 6;
+	memcpy (scb->u2.scsi_command, cmd->cmnd, 6);
+	last_scsi_type(host_index)[ldn] = IM_LONG_SCB;	
+	break;
       case READ_CAPACITY:
 	/* the length of system memory buffer must be exactly 8 bytes */
 	if (scb->sys_buf_length > 8)
@@ -2105,55 +2730,67 @@
 	if (bypass_controller)
 	  {
 	     scb->command = IM_OTHER_SCSI_CMD_CMD;
-	     scb->enable |= IM_READ_CONTROL;
+	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
 	     scb->u1.scsi_cmd_length = cmd->cmd_len;
-	     memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);	     
+	     memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	     last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
 	  }
 	else
 	  {
 	     scb->command = IM_READ_CAPACITY_CMD;
-	     scb->enable |= IM_READ_CONTROL;
+	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
 	  }
 	break;
 	
-	/* Commands that need read-only-mode (system <- device): */
+      /* Commands that need read-only-mode (system <- device): */
       case REQUEST_SENSE:
-	if (bypass_controller)
+        if (bypass_controller)
 	  {
 	     scb->command = IM_OTHER_SCSI_CMD_CMD;
-	     scb->enable |= IM_READ_CONTROL;
+	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
 	     scb->u1.scsi_cmd_length = cmd->cmd_len;
 	     memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	     last_scsi_type(host_index)[ldn] = IM_LONG_SCB;	     
 	  }
 	else
 	  {
 	     scb->command = IM_REQUEST_SENSE_CMD;
-	     scb->enable |= IM_READ_CONTROL;
+	     scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
 	  }
 	break;
 	
-	/* Commands that need write-only-mode (system -> device): */
+      /* Commands that need write-only-mode (system -> device): */
       case MODE_SELECT:
       case MODE_SELECT_10:
 	IBM_DS(host_index).ldn_modeselect_access[ldn]++;
 	scb->command = IM_OTHER_SCSI_CMD_CMD;      
-	scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/
+	scb->enable |= IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; /*Select needs WRITE-enabled*/
 	scb->u1.scsi_cmd_length = cmd->cmd_len;
 	memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	last_scsi_type(host_index)[ldn] = IM_LONG_SCB;	
 	break;
 	
 	/* For other commands, read-only is useful. Most other commands are 
 	 running without an input-data-block. */
       default:
 	scb->command = IM_OTHER_SCSI_CMD_CMD;
-	scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+	scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
 	scb->u1.scsi_cmd_length = cmd->cmd_len;
 	memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	last_scsi_type(host_index)[ldn] = IM_LONG_SCB;	
 	break;
      }
-   
    /*issue scb command, and return */
-   issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
+   if (last_scsi_type(host_index)[ldn] == IM_LONG_SCB)
+     {	
+	issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn);
+	IBM_DS(host_index).long_scbs++;
+     }   
+   else
+     {
+	issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn);
+	IBM_DS(host_index).scbs++;
+     }
    return 0;
 }
 
@@ -2170,6 +2807,7 @@
    void (*saved_done) (Scsi_Cmnd *);
    int target;
    int host_index;
+   int max_pun;
    static unsigned long flags;
    unsigned long imm_command;
 
@@ -2180,12 +2818,7 @@
    cli();   
 #else
    spin_lock_irqsave(&abort_lock, flags);
-#endif         
-   if (ibm_ansi_order)
-     target = 6 - cmd->target;
-   else
-     target = cmd->target;
-   
+#endif            
    shpnt = cmd->host;
 
    /* search for the right hostadapter */
@@ -2195,9 +2828,22 @@
      { /* invalid hostadapter descriptor address */
 	cmd->result = DID_NO_CONNECT << 16;
 	if (cmd->scsi_done)
-	  (cmd->done) (cmd);
+	  (cmd->scsi_done) (cmd);
 	return SCSI_ABORT_SNOOZE;
      }
+   
+   max_pun = subsystem_maxid(host_index);
+       
+   if (ibm_ansi_order)
+     {
+	target = max_pun - 1 - cmd->target;
+	if ((target <= subsystem_pun(host_index))&&(cmd->target <= subsystem_pun(host_index)))
+	  target--;
+	else if ((target >= subsystem_pun(host_index))&&(cmd->target >= subsystem_pun(host_index)))
+	  target++;		
+     }   
+   else
+     target = cmd->target;
  
    /*get logical device number, and disable system interrupts */
    printk ("IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n",
@@ -2294,7 +2940,12 @@
    int host_index;
    static unsigned long flags;
    unsigned long imm_command;
-   
+
+   if (cmd == NULL)
+     {
+	printk("IBM MCA SCSI: Reset called with NULL-command!\n");
+	return(SCSI_RESET_SNOOZE);
+     }      
 #ifdef OLDKERN   
    save_flags(flags);
    cli();   
@@ -2310,9 +2961,19 @@
      { /* invalid hostadapter descriptor address */
 	if (!local_checking_phase_flag(host_index))
 	  {
-	     cmd->result = DID_NO_CONNECT << 16;
-	     if (cmd->scsi_done)
-	       (cmd->done) (cmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
+	     if (flags & SCSI_RESET_SYNCHRONOUS)
+	       {
+#ifdef OLDKERN	
+		  restore_flags(flags);
+#else
+		  spin_unlock_irqrestore(&reset_lock, flags);
+#endif				  
+		  cmd->result = DID_NO_CONNECT << 16;	     
+		  if (cmd->scsi_done)
+		    (cmd->scsi_done) (cmd);
+	       }	     
+#endif	     
 	  }
 	return SCSI_ABORT_SNOOZE;
      }
@@ -2399,7 +3060,7 @@
    }
    
    /* so reset finished ok - call outstanding done's, and return success */
-   printk ("IBM MCA SCSI: Reset completed without known error.\n");
+   printk ("IBM MCA SCSI: Reset successfully completed.\n");
 #ifdef OLDKERN   
    restore_flags(flags);
 #else
@@ -2412,10 +3073,22 @@
 	  {
 	     ld(host_index)[i].cmd = NULL;
 	     cmd_aid->result = DID_RESET << 16;
-	     (cmd_aid->scsi_done) (cmd_aid);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
+	     if (flags & SCSI_RESET_SYNCHRONOUS)
+	       {
+		  cmd_aid->result = DID_BUS_BUSY << 16;
+		  if (cmd_aid->scsi_done)
+		    (cmd_aid->scsi_done) (cmd_aid);
+	       }	     
+#endif	     
 	  }
      }
-   return SCSI_RESET_SUCCESS;
+   if (flags & SCSI_RESET_SUGGEST_HOST_RESET)
+     return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET);
+   else if (flags & SCSI_RESET_SUGGEST_BUS_RESET)
+     return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);
+   else
+     return SCSI_RESET_SUCCESS;
 }
 
 /*--------------------------------------------------------------------*/
@@ -2492,7 +3165,8 @@
    int i,id,lun,host_index;
    struct Scsi_Host *shpnt;
    unsigned long flags;
-
+   int max_pun;
+   
 #ifdef OLDKERN   
    save_flags(flags);
    cli();
@@ -2504,10 +3178,11 @@
    shpnt = hosts[i];
    host_index = i;
    if (!shpnt) {
-       len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno);
+       len += sprintf(buffer+len, "\nIBM MCA SCSI: Can't find adapter for host number %d\n", hostno);
        return len;
    }
-
+   max_pun = subsystem_maxid(host_index);
+   
    len += sprintf(buffer+len, "\n             IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n",
 		  IBMMCA_SCSI_DRIVER_VERSION);
    len += sprintf(buffer+len, " SCSI Access-Statistics:\n");
@@ -2528,8 +3203,12 @@
 		  (bypass_controller) ? "software" : "hardware integrated");
    len += sprintf(buffer+len, "               Total Interrupts.........: %d\n",
 		  IBM_DS(host_index).total_interrupts);
-   len += sprintf(buffer+len, "               Total SCSI Accesses......: %d\n",
+   len += sprintf(buffer+len, "               Total SCSI Accesses......: %d\n",		  
 		  IBM_DS(host_index).total_accesses);
+   len += sprintf(buffer+len, "               Total short SCBs.........: %d\n",
+		  IBM_DS(host_index).scbs);
+   len += sprintf(buffer+len, "               Total long SCBs..........: %d\n",
+		  IBM_DS(host_index).long_scbs);   
    len += sprintf(buffer+len, "                 Total SCSI READ/WRITE..: %d\n",
 		  ldn_access_total_read_write(host_index));
    len += sprintf(buffer+len, "                 Total SCSI Inquiries...: %d\n",
@@ -2554,7 +3233,7 @@
    len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n");
    len += sprintf(buffer+len, "               Number of physical SCSI-devices..: %d (+ Adapter)\n",
 		  IBM_DS(host_index).total_scsi_devices);
-   len += sprintf(buffer+len, "               Dynamical Assignment necessaray..: %s\n", 
+   len += sprintf(buffer+len, "               Dynamical Assignment necessary...: %s\n", 
 		  IBM_DS(host_index).dyn_flag ? "Yes" : "No ");
    len += sprintf(buffer+len, "               Next LDN to be assigned..........: 0x%x\n",
 		  next_ldn(host_index));
@@ -2564,7 +3243,7 @@
    len += sprintf(buffer+len, "\n Current SCSI-Device-Mapping:\n");
    len += sprintf(buffer+len, "        Physical SCSI-Device Map               Logical SCSI-Device Map\n");
    len += sprintf(buffer+len, "    ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
-   for (id=0; id<=7; id++)
+   for (id=0; id<max_pun; id++)
      {
 	len += sprintf(buffer+len, "    %2d     ",id);
 	for (lun=0; lun<8; lun++)
@@ -2590,6 +3269,30 @@
 #endif   
    return len;
 }
+
+void ibmmca_scsi_setup (char *str, int *ints)
+{
+   internal_ibmmca_scsi_setup (str, ints);
+}
+
+static int option_setup(char *str)
+{
+   int ints[IM_MAX_HOSTS];
+   char *cur = str;
+   int i = 1;
+   
+   while (cur && isdigit(*cur) && i <= IM_MAX_HOSTS) {
+      ints[i++] = simple_strtoul(cur, NULL, 0);
+      if ((cur = strchr(cur,',')) != NULL) cur++;
+   }   
+   ints[0] = i - 1;
+   internal_ibmmca_scsi_setup(cur, ints);
+   return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,18)
+__setup("ibmmcascsi=", option_setup);
+#endif
 
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ibmmca.h linux.18p17/drivers/scsi/ibmmca.h
--- linux.vanilla/drivers/scsi/ibmmca.h	Wed May  3 22:31:20 2000
+++ linux.18p17/drivers/scsi/ibmmca.h	Thu Oct 19 01:37:49 2000
@@ -1,3 +1,9 @@
+/* 
+ * Low Level Driver for the IBM Microchannel SCSI Subsystem
+ * (Headerfile, see README.ibmmca for description of the IBM MCA SCSI-driver
+ * For use under the GNU public license within the Linux-kernel project.
+ */
+
 #ifndef _IBMMCA_H
 #define _IBMMCA_H
 
@@ -5,18 +11,15 @@
 #include <linux/version.h>
 #endif
 
-#ifndef ibmmca_header_linux_version
-#define ibmmca_header_linux_version(v,p,s) (((v)<<16)+((p)<<8)+(s))
-#endif
-
-/* 
- * Low Level Driver for the IBM Microchannel SCSI Subsystem
- * (Headerfile, see README.ibmmca for description of the IBM MCA SCSI-driver)
- */
+/* Note to the Linux-toplevel-maintainers: 
+ * This file contains the unified header for all available Linux-distributions.
+ * For reasons of maintenance, it is recommended to keep this unmodified to
+ * be downward compatible until I no longer get any requests from people 
+ * using older kernel releases on their PS/2 machines. (23 Apr 2000, M.Lang) */
 
 /* Common forward declarations for all Linux-versions: */
 
-/*services provided to the higher level of Linux SCSI driver */
+/* Interfaces to the midlevel Linux SCSI driver */
 extern int ibmmca_proc_info (char *, char **, off_t, int, int, int);
 extern int ibmmca_detect (Scsi_Host_Template *);
 extern int ibmmca_release (struct Scsi_Host *);
@@ -29,16 +32,18 @@
 /*structure for /proc filesystem */
 extern struct proc_dir_entry proc_scsi_ibmmca;
 
-#if LINUX_VERSION_CODE >= ibmmca_header_linux_version(2,1,0)
-/* Stuff for Linux >= 2.1.0: */
-/*initialization for Scsi_host_template type (Linux >= 2.1.0) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,75)
+/* Stuff for Linux >= 2.1.75: */
 /*
  * 2/8/98
  * Note to maintainer of IBMMCA.  Do not change this initializer back to
  * the old format.  Please ask eric@andante.jic.com if you have any questions
  * about this, but it will break things in the future.
  */
-#define IBMMCA {						      \
+/*initialization for Scsi_host_template type (Linux >= 2.1.75 && < 2.3.27) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,27)
+#define IBMMCA {                                                      \
           proc_dir:       &proc_scsi_ibmmca,    /*proc_dir*/          \
 	  proc_info:	  ibmmca_proc_info,     /*proc info fn*/      \
           name:           "IBM SCSI-Subsystem", /*name*/              \
@@ -56,11 +61,30 @@
           unchecked_isa_dma: 0,                 /*32-Bit Busmaster */ \
           use_clustering: ENABLE_CLUSTERING     /*use_clustering*/    \
           }
-
 #else
-/* Stuff for Linux < 2.1.0: */
+#define IBMMCA {                                                      \
+          proc_name:      "ibmmca",             /*proc_name*/         \
+	  proc_info:	  ibmmca_proc_info,     /*proc info fn*/      \
+          name:           "IBM SCSI-Subsystem", /*name*/              \
+          detect:         ibmmca_detect,        /*detect fn*/         \
+          release:        ibmmca_release,       /*release fn*/        \
+          command:        ibmmca_command,       /*command fn*/        \
+          queuecommand:   ibmmca_queuecommand,  /*queuecommand fn*/   \
+          abort:          ibmmca_abort,         /*abort fn*/          \
+          reset:          ibmmca_reset,         /*reset fn*/          \
+          bios_param:     ibmmca_biosparam,     /*bios fn*/           \
+          can_queue:      16,                   /*can_queue*/         \
+          this_id:        7,                    /*set by detect*/     \
+          sg_tablesize:   16,                   /*sg_tablesize*/      \
+          cmd_per_lun:    1,                    /*cmd_per_lun*/       \
+          unchecked_isa_dma: 0,                 /*32-Bit Busmaster */ \
+          use_clustering: ENABLE_CLUSTERING     /*use_clustering*/    \
+          }
+#endif
+#else
+/* Stuff for Linux < 2.1.75: */
 
-/*initialization for Scsi_host_template type (Linux < 2.1.0) */
+/*initialization for Scsi_host_template type (Linux < 2.1.75) */
 #define IBMMCA {                                      \
           NULL,                 /*next*/              \
           NULL,                 /*usage_count*/       \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ide-scsi.c linux.18p17/drivers/scsi/ide-scsi.c
--- linux.vanilla/drivers/scsi/ide-scsi.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/scsi/ide-scsi.c	Sun Oct 15 22:14:04 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/inia100.h linux.18p17/drivers/scsi/inia100.h
--- linux.vanilla/drivers/scsi/inia100.h	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/inia100.h	Thu Oct 19 01:37:49 2000
@@ -351,6 +351,7 @@
 #define ORC_EXECSCSI	0x00	/* SCSI initiator command with residual */
 #define ORC_BUSDEVRST	0x01	/* SCSI Bus Device Reset  */
 
+#ifndef HOSTS_C
 /* Status of ORCSCB_Status */
 #define SCB_COMPLETE	0x00	/* SCB request completed  */
 #define SCB_POST	0x01	/* SCB is posted by the HOST      */
@@ -401,6 +402,7 @@
 #define	MSG_DEVRST	0x0C
 #define	MSG_STAG	0x20
 
+#endif
 /***********************************************************************
 		Target Device Control Structure
 **********************************************************************/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ips.c linux.18p17/drivers/scsi/ips.c
--- linux.vanilla/drivers/scsi/ips.c	Sun Jun 11 21:44:15 2000
+++ linux.18p17/drivers/scsi/ips.c	Tue Sep  5 21:54:35 2000
@@ -77,18 +77,41 @@
 /*            2.3.18 and later                                               */
 /*          - Sync with other changes from the 2.3 kernels                   */
 /* 4.00.06  - Fix timeout with initial FFDC command                          */
+/* 4.10.00  - Add support for ServeRAID 4M/4L                                */
+/* 4.10.13  - Fix for dynamic unload and proc file system                    */
+/* 4.20.03  - Rename version to coincide with new release schedules          */
+/*            Performance fixes                                              */
+/*            Fix truncation of /proc files with cat                         */
+/*            Merge in changes through kernel 2.4.0test1ac21                 */
+/* 4.20.13  - Fix some failure cases / reset code                            */
+/*          - Hook into the reboot_notifier to flush the controller cache    */
 /*                                                                           */
 /*****************************************************************************/
 
 /*
  * Conditional Compilation directives for this driver:
  *
- * NO_IPS_RESET         - Don't reset the controller (no matter what)
- * IPS_DEBUG            - More verbose error messages
- * IPS_PCI_PROBE_DEBUG  - Print out more detail on the PCI probe
+ * IPS_DEBUG            - Turn on debugging info
  *
+ *
+ * Parameters:
+ *
+ * debug:<number>       - Set debug level to <number>
+ *                        NOTE: only works when IPS_DEBUG compile directive
+ *                              is used.
+ *
+ *       1              - Normal debug messages
+ *       2              - Verbose debug messages
+ *       11             - Method trace (non interrupt)
+ *       12             - Method trace (includes interrupt)
+ *
+ * noreset              - Don't reset the controller
+ * nocmdline            - Turn off passthru support
+ * noi2o                - Don't use I2O Queues (ServeRAID 4 only)
+ * nommap               - Don't use memory mapped I/O
  */
 
+
 #if defined (MODULE)
    #include <linux/module.h>
 #endif /* MODULE */
@@ -102,10 +125,12 @@
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
+#include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
+#include <linux/reboot.h>
 
 #include <linux/blk.h>
 #include <linux/types.h>
@@ -128,25 +153,29 @@
 #include <asm/spinlock.h>
 #endif
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#include <linux/init.h>
+#endif
+
 #include <linux/smp.h>
 
+#ifdef MODULE
+   static char *ips = NULL;
+   MODULE_PARM(ips, "s");
+#endif
+
 /*
  * DRIVER_VER
  */
-#define IPS_VERSION_HIGH        "4.00"  /* MUST be 4 chars */
-#define IPS_VERSION_LOW         ".06 "  /* MUST be 4 chars */
+#define IPS_VERSION_HIGH        "4.20"
+#define IPS_VERSION_LOW         ".20 "
 
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
 struct proc_dir_entry proc_scsi_ips = {
-#if !defined(PROC_SCSI_IPS)
-   0,                                /* Use dynamic inode allocation */
-#else
-   PROC_SCSI_IPS,
-#endif
+   0,
    3, "ips",
    S_IFDIR | S_IRUGO | S_IXUGO, 2
-}
-;
+};
 #endif
 
 #if !defined(__i386__)
@@ -161,12 +190,14 @@
    #error "To use the command-line interface you need to define SG_BIG_BUFF"
 #endif
 
-#if IPS_DEBUG >= 12
-   #define DBG(s)       printk(KERN_NOTICE s "\n"); MDELAY(2*IPS_ONE_SEC)
-#elif IPS_DEBUG >= 11
-   #define DBG(s)       printk(KERN_NOTICE s "\n")
+#ifdef IPS_DEBUG
+   #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
+   #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n");
+   #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
 #else
-   #define DBG(s)
+   #define METHOD_TRACE(s, i)
+   #define DEBUG(i, s)
+   #define DEBUG_VAR(i, s, v...)
 #endif
 
 /*
@@ -175,11 +206,26 @@
 static const char * ips_name = "ips";
 static struct Scsi_Host * ips_sh[IPS_MAX_ADAPTERS];  /* Array of host controller structures */
 static ips_ha_t * ips_ha[IPS_MAX_ADAPTERS];          /* Array of HA structures */
+static unsigned int ips_next_controller = 0;
 static unsigned int ips_num_controllers = 0;
+static unsigned int ips_released_controllers = 0;
 static int          ips_cmd_timeout = 60;
 static int          ips_reset_timeout = 60 * 5;
+static int          ips_force_memio = 1;             /* Always use Memory Mapped I/O    */
+static int          ips_force_i2o = 1;               /* Always use I2O command delivery */
+static int          ips_resetcontroller = 1;         /* Reset the controller            */
+static int          ips_cmdline = 1;                 /* Support for passthru            */
+
+#ifdef IPS_DEBUG
+static int          ips_debug = 0;                   /* Debug mode                      */
+#endif
+
+/*
+ * Necessary forward function protoypes
+ */
+static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
 
-#define MAX_ADAPTER_NAME 7
+#define MAX_ADAPTER_NAME 9
 
 static char ips_adapter_name[][30] = {
    "ServeRAID",
@@ -188,7 +234,13 @@
    "ServeRAID on motherboard",
    "ServeRAID 3H",
    "ServeRAID 3L",
-   "ServeRAID 4H"
+   "ServeRAID 4H",
+   "ServeRAID 4M",
+   "ServeRAID 4L"
+};
+
+static struct notifier_block ips_notifier = {
+   ips_halt, NULL, 0
 };
 
 /*
@@ -253,8 +305,6 @@
  */
 int ips_detect(Scsi_Host_Template *);
 int ips_release(struct Scsi_Host *);
-int ips_abort(Scsi_Cmnd *);
-int ips_reset(Scsi_Cmnd *, unsigned int);
 int ips_eh_abort(Scsi_Cmnd *);
 int ips_eh_reset(Scsi_Cmnd *);
 int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
@@ -262,21 +312,26 @@
 const char * ips_info(struct Scsi_Host *);
 void do_ipsintr(int, void *, struct pt_regs *);
 static int ips_hainit(ips_ha_t *);
-static int ips_map_status(ips_scb_t *, ips_stat_t *);
+static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
 static int ips_send(ips_ha_t *, ips_scb_t *, ips_scb_callback);
 static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
 static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
-static int ips_chkstatus(ips_ha_t *);
 static int ips_online(ips_ha_t *, ips_scb_t *);
 static int ips_inquiry(ips_ha_t *, ips_scb_t *);
 static int ips_rdcap(ips_ha_t *, ips_scb_t *);
 static int ips_msense(ips_ha_t *, ips_scb_t *);
 static int ips_reqsen(ips_ha_t *, ips_scb_t *);
 static int ips_allocatescbs(ips_ha_t *);
-static int ips_reset_adapter(ips_ha_t *);
-static int ips_statupd(ips_ha_t *);
-static int ips_issue(ips_ha_t *, ips_scb_t *);
-static int ips_isintr(ips_ha_t *);
+static int ips_reset_copperhead(ips_ha_t *);
+static int ips_reset_copperhead_memio(ips_ha_t *);
+static int ips_reset_morpheus(ips_ha_t *);
+static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
+static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
+static int ips_isintr_copperhead(ips_ha_t *);
+static int ips_isintr_copperhead_memio(ips_ha_t *);
+static int ips_isintr_morpheus(ips_ha_t *);
 static int ips_wait(ips_ha_t *, int, int);
 static int ips_write_driver_status(ips_ha_t *, int);
 static int ips_read_adapter_status(ips_ha_t *, int);
@@ -284,7 +339,22 @@
 static int ips_read_config(ips_ha_t *, int);
 static int ips_clear_adapter(ips_ha_t *, int);
 static int ips_readwrite_page5(ips_ha_t *, int, int);
-static void ips_intr(ips_ha_t *);
+static int ips_init_copperhead(ips_ha_t *);
+static int ips_init_copperhead_memio(ips_ha_t *);
+static int ips_init_morpheus(ips_ha_t *);
+static int ips_isinit_copperhead(ips_ha_t *);
+static int ips_isinit_copperhead_memio(ips_ha_t *);
+static int ips_isinit_morpheus(ips_ha_t *);
+static u32 ips_statupd_copperhead(ips_ha_t *);
+static u32 ips_statupd_copperhead_memio(ips_ha_t *);
+static u32 ips_statupd_morpheus(ips_ha_t *);
+static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
+static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
+static void ips_enable_int_copperhead(ips_ha_t *);
+static void ips_enable_int_copperhead_memio(ips_ha_t *);
+static void ips_enable_int_morpheus(ips_ha_t *);
+static void ips_intr_copperhead(ips_ha_t *);
+static void ips_intr_morpheus(ips_ha_t *);
 static void ips_next(ips_ha_t *, int);
 static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
 static void ipsintr_done(ips_ha_t *, struct ips_scb *);
@@ -293,6 +363,7 @@
 static void ips_init_scb(ips_ha_t *, ips_scb_t *);
 static void ips_freescb(ips_ha_t *, ips_scb_t *);
 static void ips_statinit(ips_ha_t *);
+static void ips_statinit_memio(ips_ha_t *);
 static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
 static void ips_ffdc_reset(ips_ha_t *, int);
 static void ips_ffdc_time(ips_ha_t *, int);
@@ -312,6 +383,9 @@
 static int ips_erase_bios(ips_ha_t *);
 static int ips_program_bios(ips_ha_t *, char *, int);
 static int ips_verify_bios(ips_ha_t *, char *, int);
+static int ips_erase_bios_memio(ips_ha_t *);
+static int ips_program_bios_memio(ips_ha_t *, char *, int);
+static int ips_verify_bios_memio(ips_ha_t *, char *, int);
 
 #ifndef NO_IPS_CMDLINE
 static int ips_is_passthru(Scsi_Cmnd *);
@@ -332,6 +406,76 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_setup                                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   setup parameters to the driver                                         */
+/*                                                                          */
+/****************************************************************************/
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+static int
+ips_setup(char *ips_str) {
+#else
+void
+ips_setup(char *ips_str, int *dummy) {
+#endif
+   int        i;
+   char      *p;
+   char      *key;
+   char      *value;
+   char       tokens[3] = {',', '.', 0};
+   IPS_OPTION options[] = {
+      {"noreset", &ips_resetcontroller, 0},
+#ifdef IPS_DEBUG
+      {"debug", &ips_debug, 1},
+#endif
+      {"noi2o", &ips_force_i2o, 0},
+      {"nommap", &ips_force_memio, 0},
+      {"nocmdline", &ips_cmdline, 0},
+   };
+
+   METHOD_TRACE("ips_setup", 1);
+
+   for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) {
+      p = key;
+
+      /* Search for value */
+      while ((p) && (*p != ':'))
+         p++;
+
+      if (p) {
+         *p = '\0';
+         value = p+1;
+      } else
+         value = NULL;
+
+      /*
+       * We now have key/value pairs.
+       * Update the variables
+       */
+      for (i = 0; i < (sizeof(options) / sizeof(options[0])); i++) {
+         if (strnicmp(key, options[i].option_name, strlen(ips_str)) == 0) {
+            if (value)
+               *options[i].option_flag = simple_strtoul(value, NULL, 0);
+            else
+               *options[i].option_flag = options[i].option_value;
+
+            break;
+         }
+      }
+   }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+   return (1);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+__setup("ips=", ips_setup);
+#endif
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_detect                                                 */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -346,15 +490,38 @@
    struct Scsi_Host *sh;
    ips_ha_t         *ha;
    u32               io_addr;
+   u32               mem_addr;
+   u32               io_len;
+   u32               mem_len;
    u16               planer;
    u8                revision_id;
    u8                bus;
    u8                func;
    u8                irq;
-   int               index;
-   struct pci_dev   *dev = NULL;
-
-   DBG("ips_detect");
+   u16               deviceID[2];
+   int               i;
+   int               j;
+   char             *ioremap_ptr;
+   char             *mem_ptr;
+   struct pci_dev   *dev[2];
+   struct pci_dev   *morpheus = NULL;
+   struct pci_dev   *trombone = NULL;
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14)
+   u32               currbar;
+   u32               maskbar;
+   u8                barnum;
+#endif
+
+   METHOD_TRACE("ips_detect", 1);
+
+#ifdef MODULE
+   if (ips)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+      ips_setup(ips);
+#else
+      ips_setup(ips, NULL);
+#endif
+#endif
 
    SHT->proc_info = ips_proc_info;
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
@@ -367,232 +534,522 @@
 
    /* initalize number of controllers */
    ips_num_controllers = 0;
+   ips_next_controller = 0;
+   ips_released_controllers = 0;
 
    if (!pci_present())
       return (0);
 
-   for (index = 0; index < IPS_MAX_ADAPTERS; index++) {
+   morpheus = pci_find_device(IPS_VENDORID, IPS_MORPHEUS_DEVICEID, morpheus);
+   trombone = pci_find_device(IPS_VENDORID, IPS_COPPERHEAD_DEVICEID, trombone);
 
-      if (!(dev = pci_find_device(IPS_VENDORID, IPS_DEVICEID, dev)))
-         break;
+   /* determine which controller to probe first */
+   if (!morpheus) {
+      /* we only have trombone */
+      dev[0] = trombone;
+      dev[1] = NULL;
+      deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+   } else if (!trombone) {
+      /* we only have morpheus */
+      dev[0] = morpheus;
+      dev[1] = NULL;
+      deviceID[0] = IPS_MORPHEUS_DEVICEID;
+   } else {
+      /* we have both in the system */
+      if (trombone->bus < morpheus->bus) {
+         dev[0] = trombone;
+         dev[1] = morpheus;
+         deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+         deviceID[1] = IPS_MORPHEUS_DEVICEID;
+      } else if (trombone->bus > morpheus->bus) {
+         dev[0] = morpheus;
+         dev[1] = trombone;
+         deviceID[0] = IPS_MORPHEUS_DEVICEID;
+         deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+      } else {
+         /* further detection required */
+         if (trombone->devfn < morpheus->devfn) {
+            dev[0] = trombone;
+            dev[1] = morpheus;
+            deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+            deviceID[1] = IPS_MORPHEUS_DEVICEID;
+         } else {
+            dev[0] = morpheus;
+            dev[1] = trombone;
+            deviceID[0] = IPS_MORPHEUS_DEVICEID;
+            deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+         }
+      }
+   }
 
-      /* stuff that we get in dev */
-      irq = dev->irq;
-      bus = dev->bus->number;
-      func = dev->devfn;
+   /* Now scan the controllers */
+   for (i = 0; i < 2; i++) {
+      if (!dev[i])
+         break;
 
-      /* get planer status */
-      if (pci_read_config_word(dev, 0x04, &planer)) {
-         printk(KERN_WARNING "(%s%d) can't get planer status.\n",
-                ips_name, index);
+      do {
+         if (ips_next_controller >= IPS_MAX_ADAPTERS)
+            break;
 
-         continue;
-      }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+         if (pci_enable_device(dev[i]))
+            break;
+#endif
 
-      /* check I/O address */
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,13)
-      io_addr = dev->base_address[0];
+         /* stuff that we get in dev */
+         irq = dev[i]->irq;
+         bus = dev[i]->bus->number;
+         func = dev[i]->devfn;
+
+         /* Init MEM/IO addresses to 0 */
+         mem_addr = 0;
+         io_addr = 0;
+         mem_len = 0;
+         io_len = 0;
+
+         for (j = 0; j < 2; j++) {
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+            if (!pci_resource_start(dev[i], j))
+               break;
 
-      if ((io_addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
-         continue;
+            if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) {
+               io_addr = pci_resource_start(dev[i], j);
+               io_len = pci_resource_len(dev[i], j);
+            } else {
+               mem_addr = pci_resource_start(dev[i], j);
+               mem_len = pci_resource_len(dev[i], j);
+            }
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14)
+            if (!dev[i]->resource[j].start)
+               break;
 
-      /* get the BASE IO Address */
-      io_addr &= PCI_BASE_ADDRESS_IO_MASK;
+            if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+               io_addr = dev[i]->resource[j].start;
+               io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+            } else {
+               mem_addr = dev[i]->resource[j].start;
+               mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+            }
 #else
-      io_addr = dev->resource[0].start;
+            if (!dev[i]->base_address[j])
+               break;
 
-      if ((dev->resource[0].flags & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
-         continue;
+            if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+               io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
+
+               /* Get Size */
+               pci_read_config_dword(dev[i], barnum, &currbar);
+               pci_write_config_dword(dev[i], barnum, ~0);
+               pci_read_config_dword(dev[i], barnum, &maskbar);
+               pci_write_config_dword(dev[i], barnum, currbar);
+
+               io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
+            } else {
+               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+               mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
+
+               /* Get Size */
+               pci_read_config_dword(dev[i], barnum, &currbar);
+               pci_write_config_dword(dev[i], barnum, ~0);
+               pci_read_config_dword(dev[i], barnum, &maskbar);
+               pci_write_config_dword(dev[i], barnum, currbar);
+
+               mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
+            }
 #endif
+         }
 
-      /* check to see if an onboard planer controller is disabled */
-      if (!(planer & 0x000C)) {
+         /* setup memory mapped area (if applicable) */
+         if (mem_addr) {
+            u32 base;
+            u32 offs;
+
+            DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
+                      ips_name, ips_next_controller, mem_addr, mem_len);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+            if (check_mem_region(mem_addr, mem_len)) {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+                      ips_name, ips_next_controller, io_addr, io_len);
 
-   #ifdef IPS_PCI_PROBE_DEBUG
-         printk(KERN_NOTICE "(%s%d) detect, Onboard ServeRAID disabled by BIOS\n",
-                ips_name, index);
-   #endif
+               ips_next_controller++;
 
-         continue;
-      }
+               continue;
+            }
 
-   #ifdef IPS_PCI_PROBE_DEBUG
-      printk(KERN_NOTICE "(%s%d) detect bus %d, func %x, irq %d, io %x\n",
-             ips_name, index, bus, func, irq, io_addr);
-   #endif
-
-      /* get the revision ID */
-      if (pci_read_config_byte(dev, 0x08, &revision_id)) {
-         printk(KERN_WARNING "(%s%d) can't get revision id.\n",
-                ips_name, index);
+            request_mem_region(mem_addr, mem_len, "ips");
+#endif
 
-         continue;
-      }
+            base = mem_addr & PAGE_MASK;
+            offs = mem_addr - base;
 
-      /* found a controller */
-      sh = scsi_register(SHT, sizeof(ips_ha_t));
+            ioremap_ptr = ioremap(base, PAGE_SIZE);
+            mem_ptr = ioremap_ptr + offs;
+         } else {
+            ioremap_ptr = NULL;
+            mem_ptr = NULL;
+         }
 
-      if (sh == NULL) {
-         printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
-                ips_name, index);
+         /* setup I/O mapped area (if applicable) */
+         if (io_addr) {
+            DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
+                      ips_name, ips_next_controller, io_addr, io_len);
+
+            if (check_region(io_addr, io_len)) {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+                      ips_name, ips_next_controller, io_addr, io_len);
 
-         continue;
-      }
+               ips_next_controller++;
 
-      ha = IPS_HA(sh);
-      memset(ha, 0, sizeof(ips_ha_t));
+               continue;
+            }
 
-      /* Initialize spin lock */
-      spin_lock_init(&ha->scb_lock);
-      spin_lock_init(&ha->copp_lock);
-      spin_lock_init(&ha->ips_lock);
-      spin_lock_init(&ha->copp_waitlist.lock);
-      spin_lock_init(&ha->scb_waitlist.lock);
-      spin_lock_init(&ha->scb_activelist.lock);
-
-      ips_sh[ips_num_controllers] = sh;
-      ips_ha[ips_num_controllers] = ha;
-      ips_num_controllers++;
-      ha->active = 1;
-
-      ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL|GFP_DMA);
-
-      if (!ha->enq) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
-                ips_name, index);
+            request_region(io_addr, io_len, "ips");
+         }
 
-         ha->active = 0;
+         /* get planer status */
+         if (pci_read_config_word(dev[i], 0x04, &planer)) {
+            printk(KERN_WARNING "(%s%d) can't get planer status.\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->adapt) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
-                ips_name, index);
+         /* check to see if an onboard planer controller is disabled */
+         if (!(planer & 0x000C)) {
 
-         ha->active = 0;
+            DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS",
+                      ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->conf) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
-                ips_name, index);
+         DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %x",
+                   ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, (u32) mem_ptr);
 
-         ha->active = 0;
+         /* get the revision ID */
+         if (pci_read_config_byte(dev[i], 0x08, &revision_id)) {
+            printk(KERN_WARNING "(%s%d) can't get revision id.\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->nvram) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
-                ips_name, index);
+         /* found a controller */
+         sh = scsi_register(SHT, sizeof(ips_ha_t));
 
-         ha->active = 0;
+         if (sh == NULL) {
+            printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->subsys) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
-                ips_name, index);
+         ha = IPS_HA(sh);
+         memset(ha, 0, sizeof(ips_ha_t));
 
-         ha->active = 0;
+         /* Initialize spin lock */
+         spin_lock_init(&ha->scb_lock);
+         spin_lock_init(&ha->copp_lock);
+         spin_lock_init(&ha->ips_lock);
+         spin_lock_init(&ha->copp_waitlist.lock);
+         spin_lock_init(&ha->scb_waitlist.lock);
+         spin_lock_init(&ha->scb_activelist.lock);
+
+         ips_sh[ips_next_controller] = sh;
+         ips_ha[ips_next_controller] = ha;
+         ips_num_controllers++;
+         ha->active = 1;
+
+         ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_ATOMIC|GFP_DMA);
+
+         if (!ha->enq) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         continue;
-      }
+            continue;
+         }
 
-      ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL|GFP_DMA);
+         ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_ATOMIC|GFP_DMA);
 
-      if (!ha->dummy) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
-                ips_name, index);
+         if (!ha->adapt) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->conf = kmalloc(sizeof(IPS_CONF), GFP_ATOMIC|GFP_DMA);
 
-      ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_KERNEL|GFP_DMA);
-      ha->ioctl_datasize = IPS_IOCTL_SIZE;
-      if (!ha->ioctl_data) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
-                ips_name, index);
+         if (!ha->conf) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_ATOMIC|GFP_DMA);
 
-      /* Store away needed values for later use */
-      sh->io_port = io_addr;
-      sh->n_io_port = 255;
-      sh->unique_id = io_addr;
-      sh->irq = irq;
-      sh->select_queue_depths = NULL;
-      sh->sg_tablesize = sh->hostt->sg_tablesize;
-      sh->can_queue = sh->hostt->can_queue;
-      sh->cmd_per_lun = sh->hostt->cmd_per_lun;
-      sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
-      sh->use_clustering = sh->hostt->use_clustering;
-
-      /* Store info in HA structure */
-      ha->io_addr = io_addr;
-      ha->irq = irq;
-      ha->host_num = index;
-      ha->revision_id = revision_id;
-
-      /* install the interrupt handler */
-      if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
-         printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
-                ips_name, index);
+         if (!ha->nvram) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_ATOMIC|GFP_DMA);
 
-      /*
-       * Allocate a temporary SCB for initialization
-       */
-      ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
-      if (!ha->scbs) {
-         /* couldn't allocate a temp SCB */
-         printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+         if (!ha->subsys) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_ATOMIC|GFP_DMA);
 
-      memset(ha->scbs, 0, sizeof(ips_scb_t));
-      ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
-      if (!ha->scbs->sg_list) {
-         /* couldn't allocate a temp SCB S/G list */
-         printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+         if (!ha->dummy) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
+
+         ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_ATOMIC|GFP_DMA);
+         ha->ioctl_datasize = IPS_IOCTL_SIZE;
+         if (!ha->ioctl_data) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         /* Store away needed values for later use */
+         sh->io_port = io_addr;
+         sh->n_io_port = io_addr ? 255 : 0;
+         sh->unique_id = (io_addr) ? io_addr : mem_addr;
+         sh->irq = irq;
+         sh->select_queue_depths = ips_select_queue_depth;
+         sh->sg_tablesize = sh->hostt->sg_tablesize;
+         sh->can_queue = sh->hostt->can_queue;
+         sh->cmd_per_lun = sh->hostt->cmd_per_lun;
+         sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
+         sh->use_clustering = sh->hostt->use_clustering;
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32)
+         sh->wish_block = FALSE;
+#endif
+
+         /* Store info in HA structure */
+         ha->irq = irq;
+         ha->io_addr = io_addr;
+         ha->io_len = io_len;
+         ha->mem_addr = mem_addr;
+         ha->mem_len = mem_len;
+         ha->mem_ptr = mem_ptr;
+         ha->ioremap_ptr = ioremap_ptr;
+         ha->host_num = ips_next_controller;
+         ha->revision_id = revision_id;
+         ha->device_id = deviceID[i];
+         ha->pcidev = dev[i];
+
+         /*
+          * Setup Functions
+          */
+         if (IPS_IS_MORPHEUS(ha)) {
+            /* morpheus */
+            ha->func.isintr = ips_isintr_morpheus;
+            ha->func.isinit = ips_isinit_morpheus;
+            ha->func.issue = ips_issue_i2o_memio;
+            ha->func.init = ips_init_morpheus;
+            ha->func.statupd = ips_statupd_morpheus;
+            ha->func.reset = ips_reset_morpheus;
+            ha->func.intr = ips_intr_morpheus;
+            ha->func.enableint = ips_enable_int_morpheus;
+         } else if (IPS_USE_MEMIO(ha)) {
+            /* copperhead w/MEMIO */
+            ha->func.isintr = ips_isintr_copperhead_memio;
+            ha->func.isinit = ips_isinit_copperhead_memio;
+            ha->func.init = ips_init_copperhead_memio;
+            ha->func.statupd = ips_statupd_copperhead_memio;
+            ha->func.statinit = ips_statinit_memio;
+            ha->func.reset = ips_reset_copperhead_memio;
+            ha->func.intr = ips_intr_copperhead;
+            ha->func.erasebios = ips_erase_bios_memio;
+            ha->func.programbios = ips_program_bios_memio;
+            ha->func.verifybios = ips_verify_bios_memio;
+            ha->func.enableint = ips_enable_int_copperhead_memio;
+
+            if (IPS_USE_I2O_DELIVER(ha))
+               ha->func.issue = ips_issue_i2o_memio;
+            else
+               ha->func.issue = ips_issue_copperhead_memio;
+         } else {
+            /* copperhead */
+            ha->func.isintr = ips_isintr_copperhead;
+            ha->func.isinit = ips_isinit_copperhead;
+            ha->func.init = ips_init_copperhead;
+            ha->func.statupd = ips_statupd_copperhead;
+            ha->func.statinit = ips_statinit;
+            ha->func.reset = ips_reset_copperhead;
+            ha->func.intr = ips_intr_copperhead;
+            ha->func.erasebios = ips_erase_bios;
+            ha->func.programbios = ips_program_bios;
+            ha->func.verifybios = ips_verify_bios;
+            ha->func.enableint = ips_enable_int_copperhead;
+
+            if (IPS_USE_I2O_DELIVER(ha))
+               ha->func.issue = ips_issue_i2o;
+            else
+               ha->func.issue = ips_issue_copperhead;
+         }
+
+         /*
+          * Initialize the card if it isn't already
+          */
+         if (!(*ha->func.isinit)(ha)) {
+            if (!(*ha->func.init)(ha)) {
+               /*
+                * Initialization failed
+                */
+               printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n",
+                      ips_name, ips_next_controller);
+
+               ha->active = 0;
+               ips_free(ha);
+               ips_next_controller++;
+               ips_num_controllers--;
+
+               continue;
+            }
+         }
+
+         /* install the interrupt handler */
+         if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+            printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         /*
+          * Allocate a temporary SCB for initialization
+          */
+         ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
+         if (!ha->scbs) {
+            /* couldn't allocate a temp SCB */
+            printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            free_irq(ha->irq, ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         memset(ha->scbs, 0, sizeof(ips_scb_t));
+         ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
+         if (!ha->scbs->sg_list) {
+            /* couldn't allocate a temp SCB S/G list */
+            printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            free_irq(ha->irq, ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         ha->max_cmds = 1;
+
+         ips_next_controller++;
+      } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i])));
+   }
+
+   /*
+    * Do Phase 2 Initialization
+    * Controller init
+    */
+   for (i = 0; i < ips_next_controller; i++) {
+      ha = ips_ha[i];
+      sh = ips_sh[i];
+
+      if (!ha->active) {
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
 
          continue;
       }
 
-      ha->max_cmds = 1;
-
       if (!ips_hainit(ha)) {
          printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping\n",
-                ips_name, index);
+                ips_name, i);
 
          ha->active = 0;
+         ips_free(ha);
+         free_irq(ha->irq, ha);
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
+         ips_num_controllers--;
 
          continue;
       }
@@ -607,9 +1064,15 @@
       /* allocate CCBs */
       if (!ips_allocatescbs(ha)) {
          printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+                ips_name, i);
 
          ha->active = 0;
+         ips_free(ha);
+         free_irq(ha->irq, ha);
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
+         ips_num_controllers--;
 
          continue;
       }
@@ -617,9 +1080,12 @@
       /* finish setting values */
       sh->max_id = ha->ntargets;
       sh->max_lun = ha->nlun;
-      sh->max_channel = ha->nbus;
+      sh->max_channel = ha->nbus - 1;
       sh->can_queue = ha->max_cmds-1;
-   } /* end for */
+   }
+
+   if (ips_num_controllers > 0)
+      register_reboot_notifier(&ips_notifier);
 
    return (ips_num_controllers);
 
@@ -645,7 +1111,7 @@
    ips_ha_t  *ha;
    int        i;
 
-   DBG("ips_release");
+   METHOD_TRACE("ips_release", 1);
 
    for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++);
 
@@ -688,17 +1154,85 @@
    /* free extra memory */
    ips_free(ha);
 
+   /* Free I/O Region */
+   if (ha->io_addr)
+      release_region(ha->io_addr, ha->io_len);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+   if (ha->mem_addr)
+      release_mem_region(ha->mem_addr, ha->mem_len);
+#endif
+
    /* free IRQ */
    free_irq(ha->irq, ha);
 
-   /* unregister with SCSI sub system */
-   scsi_unregister(sh);
+   ips_released_controllers++;
+
+   if (ips_num_controllers == ips_released_controllers)
+      unregister_reboot_notifier(&ips_notifier);
 
    return (FALSE);
 }
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_halt                                                   */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Perform cleanup when the system reboots                                */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_halt(struct notifier_block *nb, ulong event, void *buf) {
+   ips_scb_t *scb;
+   ips_ha_t  *ha;
+   int        i;
+
+   if ((event != SYS_RESTART) && (event != SYS_HALT) &&
+       (event != SYS_POWER_OFF))
+      return (NOTIFY_DONE);
+
+   for (i = 0; i < ips_next_controller; i++) {
+      ha = (ips_ha_t *) ips_ha[i];
+
+      if (!ha)
+         continue;
+
+      if (!ha->active)
+         continue;
+
+      /* flush the cache on the controller */
+      scb = &ha->scbs[ha->max_cmds-1];
+
+      ips_init_scb(ha, scb);
+
+      scb->timeout = ips_cmd_timeout;
+      scb->cdb[0] = IPS_CMD_FLUSH;
+
+      scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+      scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+      scb->cmd.flush_cache.state = IPS_NORM_STATE;
+      scb->cmd.flush_cache.reserved = 0;
+      scb->cmd.flush_cache.reserved2 = 0;
+      scb->cmd.flush_cache.reserved3 = 0;
+      scb->cmd.flush_cache.reserved4 = 0;
+
+      printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num);
+
+      /* send command */
+      if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
+         printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num);
+      else
+         printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num);
+   }
+
+   unregister_reboot_notifier(&ips_notifier);
+   return (NOTIFY_OK);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_eh_abort                                               */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -711,7 +1245,7 @@
    ips_ha_t         *ha;
    ips_copp_wait_item_t *item;
 
-   DBG("ips_eh_abort");
+   METHOD_TRACE("ips_eh_abort", 1);
 
    if (!SC)
       return (FAILED);
@@ -726,9 +1260,7 @@
 
    if (SC->serial_number != SC->serial_number_at_timeout) {
       /* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+      DEBUG(1, "Abort called with bogus scsi command");
 
       return (FAILED);
    }
@@ -767,121 +1299,49 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_abort                                                  */
+/* Routine Name: ips_eh_reset                                               */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Abort a command                                                        */
+/*   Reset the controller (with new eh error code)                          */
+/*                                                                          */
+/* NOTE: this routine is called under the io_request_lock spinlock          */
 /*                                                                          */
 /****************************************************************************/
 int
-ips_abort(Scsi_Cmnd *SC) {
-   ips_ha_t         *ha;
+ips_eh_reset(Scsi_Cmnd *SC) {
+   int                   ret;
+   int                   i;
+   u32                   cpu_flags;
+   ips_ha_t             *ha;
+   ips_scb_t            *scb;
    ips_copp_wait_item_t *item;
 
-   DBG("ips_abort");
+   METHOD_TRACE("ips_eh_reset", 1);
 
-   if (!SC)
-      return (SCSI_ABORT_SNOOZE);
+#ifdef NO_IPS_RESET
+   return (FAILED);
+#else
 
-   ha = (ips_ha_t *) SC->host->hostdata;
+   if (!SC) {
+      DEBUG(1, "Reset called with NULL scsi command");
 
-   if (!ha)
-      return (SCSI_ABORT_SNOOZE);
+      return (FAILED);
+   }
 
-   if (!ha->active)
-      return (SCSI_ABORT_SNOOZE);
+   ha = (ips_ha_t *) SC->host->hostdata;
 
-   if (SC->serial_number != SC->serial_number_at_timeout) {
-      /* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+   if (!ha) {
+      DEBUG(1, "Reset called with NULL ha struct");
 
-      return (SCSI_ABORT_NOT_RUNNING);
+      return (FAILED);
    }
 
-   if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
-      return (SCSI_ABORT_SNOOZE);
-
-   /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
-   item = ha->copp_waitlist.head;
-   while ((item) && (item->scsi_cmd != SC))
-      item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-
-   if (item) {
-      /* Found it */
-      ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_PENDING);
-   }
-
-   /* See if the command is on the wait queue */
-   if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
-      /* command not sent yet */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_PENDING);
-   } else {
-      /* command must have already been sent */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_SNOOZE);
-   }
-}
-
-/****************************************************************************/
-/*                                                                          */
-/* Routine Name: ips_eh_reset                                               */
-/*                                                                          */
-/* Routine Description:                                                     */
-/*                                                                          */
-/*   Reset the controller (with new eh error code)                          */
-/*                                                                          */
-/* NOTE: this routine is called under the io_request_lock spinlock          */
-/*                                                                          */
-/****************************************************************************/
-int
-ips_eh_reset(Scsi_Cmnd *SC) {
-   u32                   cpu_flags;
-   ips_ha_t             *ha;
-   ips_scb_t            *scb;
-   ips_copp_wait_item_t *item;
-
-   DBG("ips_eh_reset");
-
-#ifdef NO_IPS_RESET
-   return (FAILED);
-#else
-
-   if (!SC) {
-
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
-
-      return (FAILED);
-   }
-
-   ha = (ips_ha_t *) SC->host->hostdata;
-
-   if (!ha) {
-
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
-
-      return (FAILED);
-   }
-
-   if (!ha->active)
-      return (FAILED);
-
-   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
-      return (FAILED);
+   if (!ha->active)
+      return (FAILED);
+
+   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
+      return (FAILED);
 
    /* See if the command is on the copp queue */
    IPS_QUEUE_LOCK(&ha->copp_waitlist);
@@ -910,149 +1370,72 @@
     * command must have already been sent
     * reset the controller
     */
-   if (!ips_reset_adapter(ha)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
-      return (FAILED);
-   }
-
-   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
-      return (FAILED);
-   }
-
-   /* FFDC */
-   if (ha->subsys->param[3] & 0x300000) {
-      struct timeval tv;
-
-      do_gettimeofday(&tv);
-      IPS_HA_LOCK(cpu_flags);
-      ha->last_ffdc = tv.tv_sec;
-      ha->reset_count++;
-      IPS_HA_UNLOCK(cpu_flags);
-      ips_ffdc_reset(ha, IPS_INTR_IORL);
-   }
-
-   /* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "(%s%d) Failing active commands\n",
+   printk(KERN_NOTICE "(%s%d) Resetting controller.\n",
           ips_name, ha->host_num);
-#endif
-   while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
-      scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
-      scb->scsi_cmd->scsi_done(scb->scsi_cmd);
-      ips_freescb(ha, scb);
-   }
-
-   /* Reset the number of active IOCTLs */
-   IPS_HA_LOCK(cpu_flags);
-   ha->num_ioctl = 0;
-   IPS_HA_UNLOCK(cpu_flags);
-
-   clear_bit(IPS_IN_RESET, &ha->flags);
-
-   if (!test_bit(IPS_IN_INTR, &ha->flags)) {
-      /*
-       * Only execute the next command when
-       * we are not being called from the
-       * interrupt handler.  The interrupt
-       * handler wants to do this and since
-       * interrupts are turned off here....
-       */
-      ips_next(ha, IPS_INTR_IORL);
-   }
-
-   return (SUCCESS);
-
-#endif /* NO_IPS_RESET */
-
-}
-
-/****************************************************************************/
-/*                                                                          */
-/* Routine Name: ips_reset                                                  */
-/*                                                                          */
-/* Routine Description:                                                     */
-/*                                                                          */
-/*   Reset the controller                                                   */
-/*                                                                          */
-/* NOTE: this routine is called under the io_request_lock spinlock          */
-/*                                                                          */
-/****************************************************************************/
-int
-ips_reset(Scsi_Cmnd *SC, unsigned int flags) {
-   u32                   cpu_flags;
-   ips_ha_t             *ha;
-   ips_scb_t            *scb;
-   ips_copp_wait_item_t *item;
-
-   DBG("ips_reset");
+   ret = (*ha->func.reset)(ha);
 
-#ifdef NO_IPS_RESET
-   return (SCSI_RESET_SNOOZE);
-#else
+   if (!ret) {
+      Scsi_Cmnd *scsi_cmd;
 
-   if (!SC) {
+      printk(KERN_NOTICE 
+             "(%s%d) Controller reset failed - controller now offline.\n",
+             ips_name, ha->host_num);
 
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
+      /* Now fail all of the active commands */
+      DEBUG_VAR(1, "(%s%d) Failing active commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+         scb->scsi_cmd->result = DID_ERROR << 16;
+         scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+         ips_freescb(ha, scb);
+      }
 
-   ha = (ips_ha_t *) SC->host->hostdata;
+      /* Now fail all of the pending commands */
+      DEBUG_VAR(1, "(%s%d) Failing pending commands",
+                ips_name, ha->host_num);
 
-   if (!ha) {
+      while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+         scsi_cmd->result = DID_ERROR;
+         scsi_cmd->scsi_done(scsi_cmd);
+      }
 
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
+      ha->active = FALSE;
+      clear_bit(IPS_IN_RESET, &ha->flags);
 
-      return (SCSI_RESET_SNOOZE);
+      return (FAILED);
    }
 
-   if (!ha->active)
-      return (SCSI_RESET_SNOOZE);
-
-   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
-      return (SCSI_RESET_SNOOZE);
-
-   /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
-   item = ha->copp_waitlist.head;
-   while ((item) && (item->scsi_cmd != SC))
-      item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-
-   if (item) {
-      /* Found it */
-      ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_RESET, &ha->flags);
+   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+      Scsi_Cmnd *scsi_cmd;
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      printk(KERN_NOTICE 
+             "(%s%d) Controller reset failed - controller now offline.\n",
+             ips_name, ha->host_num);
 
-   /* See if the command is on the wait queue */
-   if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
-      /* command not sent yet */
-      clear_bit(IPS_IN_RESET, &ha->flags);
+      /* Now fail all of the active commands */
+      DEBUG_VAR(1, "(%s%d) Failing active commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+         scb->scsi_cmd->result = DID_ERROR << 16;
+         scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+         ips_freescb(ha, scb);
+      }
 
-   /* reset the controller */
-   if (!ips_reset_adapter(ha)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
+      /* Now fail all of the pending commands */
+      DEBUG_VAR(1, "(%s%d) Failing pending commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_ERROR);
-   }
+      while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+         scsi_cmd->result = DID_ERROR << 16;
+         scsi_cmd->scsi_done(scsi_cmd);
+      }
 
-   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+      ha->active = FALSE;
       clear_bit(IPS_IN_RESET, &ha->flags);
 
-      return (SCSI_RESET_ERROR);
+      return (FAILED);
    }
 
    /* FFDC */
@@ -1068,16 +1451,19 @@
    }
 
    /* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "(%s%d) Failing active commands\n",
-          ips_name, ha->host_num);
-#endif
+   DEBUG_VAR(1, "(%s%d) Failing active commands",
+             ips_name, ha->host_num);
+
    while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
       scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
       scb->scsi_cmd->scsi_done(scb->scsi_cmd);
       ips_freescb(ha, scb);
    }
 
+   /* Reset DCDB active command bits */
+   for (i = 1; i < ha->nbus; i++)
+      ha->dcdb_active[i-1] = 0;
+
    /* Reset the number of active IOCTLs */
    IPS_HA_LOCK(cpu_flags);
    ha->num_ioctl = 0;
@@ -1096,7 +1482,7 @@
       ips_next(ha, IPS_INTR_IORL);
    }
 
-   return (SCSI_RESET_SUCCESS);
+   return (SUCCESS);
 
 #endif /* NO_IPS_RESET */
 
@@ -1124,7 +1510,7 @@
    DECLARE_MUTEX_LOCKED(sem);
 #endif
 
-   DBG("ips_queue");
+   METHOD_TRACE("ips_queue", 1);
 
    ha = (ips_ha_t *) SC->host->hostdata;
 
@@ -1132,7 +1518,7 @@
       return (1);
 
    if (!ha->active)
-      return (1);
+      return (DID_ERROR);
 
 #ifndef NO_IPS_CMDLINE
    if (ips_is_passthru(SC)) {
@@ -1165,24 +1551,28 @@
 
    SC->scsi_done = done;
 
-#if IPS_DEBUG >= 10
-   printk(KERN_NOTICE "%s: ips_queue: cmd 0x%X (%d %d %d)\n",
-          ips_name,
-          SC->cmnd[0],
-          SC->channel,
-          SC->target,
-          SC->lun);
-#if IPS_DEBUG >= 11
-        MDELAY(2*IPS_ONE_SEC);
-#endif
-#endif
+   DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
+             ips_name,
+             ha->host_num,
+             SC->cmnd[0],
+             SC->channel,
+             SC->target,
+             SC->lun);
+
+   /* Check for command to initiator IDs */
+   if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) {
+      SC->result = DID_NO_CONNECT << 16;
+      done(SC);
+
+      return (0);
+   }
 
 #ifndef NO_IPS_CMDLINE
    if (ips_is_passthru(SC)) {
       ips_copp_wait_item_t *scratch;
 
       /* allocate space for the scribble */
-      scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_KERNEL);
+      scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC);
 
       if (!scratch) {
          SC->result = DID_ERROR << 16;
@@ -1238,10 +1628,8 @@
       datasize = *((u32 *) &SC->cmnd[8]);
 
       if (copy_to_user(user_area, kern_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy out user data\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data",
+                   ips_name, ha->host_num);
 
          SC->result = DID_ERROR << 16;
          SC->scsi_done(SC);
@@ -1269,7 +1657,7 @@
    int               sectors;
    int               cylinders;
 
-   DBG("ips_biosparam");
+   METHOD_TRACE("ips_biosparam", 1);
 
    ha = (ips_ha_t *) disk->device->host->hostdata;
 
@@ -1295,10 +1683,8 @@
 
    cylinders = disk->capacity / (heads * sectors);
 
-#if IPS_DEBUG >= 2
-   printk(KERN_NOTICE "Geometry: heads: %d, sectors: %d, cylinders: %d\n",
-          heads, sectors, cylinders);
-#endif
+   DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
+             heads, sectors, cylinders);
 
    geom[0] = heads;
    geom[1] = sectors;
@@ -1309,6 +1695,40 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_select_queue_depth                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Select queue depths for the devices on the contoller                   */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
+   Scsi_Device *device;
+   ips_ha_t    *ha;
+   int          count = 0;
+
+   ha = IPS_HA(host);
+
+   for (device = scsi_devs; device; device = device->next) {
+     if (device->host == host) {
+        if ((device->channel == 0) && (device->type == 0))
+           count++;
+     }
+   }
+
+   for (device = scsi_devs; device; device = device->next) {
+      if (device->host == host) {
+         if ((device->channel == 0) && (device->type == 0))
+            device->queue_depth = ha->max_cmds / count - 1;
+         else
+            device->queue_depth = 2;
+      }
+   }
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: do_ipsintr                                                 */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -1321,7 +1741,7 @@
    ips_ha_t         *ha;
    u32               cpu_flags;
 
-   DBG("do_ipsintr");
+   METHOD_TRACE("do_ipsintr", 2);
 
    ha = (ips_ha_t *) dev_id;
 
@@ -1347,7 +1767,7 @@
       return;
    }
 
-   ips_intr(ha);
+   (*ha->func.intr)(ha);
 
    clear_bit(IPS_IN_INTR, &ha->flags);
 
@@ -1359,7 +1779,7 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_intr                                                   */
+/* Routine Name: ips_intr_copperhead                                        */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -1369,13 +1789,14 @@
 /*                                                                          */
 /****************************************************************************/
 void
-ips_intr(ips_ha_t *ha) {
+ips_intr_copperhead(ips_ha_t *ha) {
    ips_stat_t       *sp;
    ips_scb_t        *scb;
-   int               status;
+   IPS_STATUS        cstatus;
+   int               intrstatus;
    u32               cpu_flags;
 
-   DBG("ips_intr");
+   METHOD_TRACE("ips_intr", 2);
 
    if (!ha)
       return;
@@ -1384,16 +1805,36 @@
       return;
 
    IPS_HA_LOCK(cpu_flags);
-   while (ips_isintr(ha)) {
+
+   intrstatus = (*ha->func.isintr)(ha);
+
+   if (!intrstatus) {
+      /*
+       * Unexpected/Shared interrupt
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+
+      return;
+   }
+
+   while (TRUE) {
       sp = &ha->sp;
 
-      if ((status = ips_chkstatus(ha)) < 0) {
-         /* unexpected interrupt - no ccb */
+      intrstatus = (*ha->func.isintr)(ha);
+
+      if (!intrstatus)
+         break;
+      else
+         cstatus.value = (*ha->func.statupd)(ha);
+
+      if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
          printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
                 ips_name, ha->host_num);
-         continue ;
+
+         continue;
       }
 
+      ips_chkstatus(ha, &cstatus);
       scb = (ips_scb_t *) sp->scb_addr;
 
       /*
@@ -1403,27 +1844,103 @@
       IPS_HA_UNLOCK(cpu_flags);
       (*scb->callback) (ha, scb);
       IPS_HA_LOCK(cpu_flags);
-   }
+   } /* end while */
 
    IPS_HA_UNLOCK(cpu_flags);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_info                                                   */
+/* Routine Name: ips_intr_morpheus                                          */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Return info about the driver                                           */
+/*   Polling interrupt handler                                              */
+/*                                                                          */
+/*   ASSUMES interrupts are disabled                                        */
 /*                                                                          */
 /****************************************************************************/
-const char *
-ips_info(struct Scsi_Host *SH) {
-   static char  buffer[256];
-   char        *bp;
-   ips_ha_t    *ha;
-
-   DBG("ips_info");
+void
+ips_intr_morpheus(ips_ha_t *ha) {
+   ips_stat_t       *sp;
+   ips_scb_t        *scb;
+   IPS_STATUS        cstatus;
+   int               intrstatus;
+   u32               cpu_flags;
+
+   METHOD_TRACE("ips_intr_morpheus", 2);
+
+   if (!ha)
+      return;
+
+   if (!ha->active)
+      return;
+
+   IPS_HA_LOCK(cpu_flags);
+
+   intrstatus = (*ha->func.isintr)(ha);
+
+   if (!intrstatus) {
+      /*
+       * Unexpected/Shared interrupt
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+
+      return;
+   }
+
+   while (TRUE) {
+      sp = &ha->sp;
+
+      intrstatus = (*ha->func.isintr)(ha);
+
+      if (!intrstatus)
+         break;
+      else
+         cstatus.value = (*ha->func.statupd)(ha);
+
+      if (cstatus.value == 0xffffffff)
+         /* No more to process */
+         break;
+
+      if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
+         printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
+                ips_name, ha->host_num);
+
+         continue;
+      }
+
+      ips_chkstatus(ha, &cstatus);
+      scb = (ips_scb_t *) sp->scb_addr;
+
+      /*
+       * use the callback function to finish things up
+       * NOTE: interrupts are OFF for this
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+      (*scb->callback) (ha, scb);
+      IPS_HA_LOCK(cpu_flags);
+   } /* end while */
+
+   IPS_HA_UNLOCK(cpu_flags);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_info                                                   */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Return info about the driver                                           */
+/*                                                                          */
+/****************************************************************************/
+const char *
+ips_info(struct Scsi_Host *SH) {
+   static char  buffer[256];
+   char        *bp;
+   ips_ha_t    *ha;
+
+   METHOD_TRACE("ips_info", 1);
 
    ha = IPS_HA(SH);
 
@@ -1463,10 +1980,10 @@
    int           ret;
    ips_ha_t     *ha = NULL;
 
-   DBG("ips_proc_info");
+   METHOD_TRACE("ips_proc_info", 1);
 
    /* Find our host structure */
-   for (i = 0; i < ips_num_controllers; i++) {
+   for (i = 0; i < ips_next_controller; i++) {
       if (ips_sh[i] && ips_sh[i]->host_no == hostno) {
          ha = (ips_ha_t *) ips_sh[i]->hostdata;
 
@@ -1508,7 +2025,7 @@
 /****************************************************************************/
 static int
 ips_is_passthru(Scsi_Cmnd *SC) {
-   DBG("ips_is_passthru");
+   METHOD_TRACE("ips_is_passthru", 1);
 
    if (!SC)
       return (0);
@@ -1542,24 +2059,20 @@
 ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
    ips_passthru_t *pt;
 
-   DBG("ips_make_passthru");
+   METHOD_TRACE("ips_make_passthru", 1);
 
    if (!SC->request_bufflen || !SC->request_buffer) {
       /* no data */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) No passthru structure\n",
-             ips_name, ha->host_num);
-#endif
+      DEBUG_VAR(1, "(%s%d) No passthru structure",
+                ips_name, ha->host_num);
 
       return (IPS_FAILURE);
    }
 
    if (SC->request_bufflen < sizeof(ips_passthru_t)) {
       /* wrong size */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
+      DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
              ips_name, ha->host_num);
-#endif
 
       return (IPS_FAILURE);
    }
@@ -1569,10 +2082,8 @@
        (((char *) SC->request_buffer)[2] != 'P') ||
        (((char *) SC->request_buffer)[3] != 'P')) {
       /* signature doesn't match */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) Wrong signature on passthru structure.\n",
-             ips_name, ha->host_num);
-#endif
+      DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.",
+                ips_name, ha->host_num);
 
       return (IPS_FAILURE);
    }
@@ -1616,10 +2127,8 @@
       if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
          if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) {
             /* wrong size */
-   #if IPS_DEBUG_PT >= 1
-            printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
-                   ips_name, ha->host_num);
-   #endif
+            DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+                      ips_name, ha->host_num);
 
             return (IPS_FAILURE);
          }
@@ -1631,10 +2140,8 @@
       } else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
          if (SC->request_bufflen < (sizeof(ips_passthru_t))) {
             /* wrong size */
-   #if IPS_DEBUG_PT >= 1
-            printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
-                   ips_name, ha->host_num);
-   #endif
+            DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+                      ips_name, ha->host_num);
 
             return (IPS_FAILURE);
          }
@@ -1653,14 +2160,23 @@
          return (IPS_FAILURE);
 
       /* don't flash the BIOS on future cards */
-      if (ha->revision_id > IPS_REVID_TROMBONE64) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unsupported controller\n",
-                ips_name, ha->host_num);
-#endif
+      if ((ha->device_id != IPS_COPPERHEAD_DEVICEID) ||
+          (ha->revision_id > IPS_REVID_TROMBONE64)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unsupported controller",
+                   ips_name, ha->host_num);
+
          return (IPS_FAILURE);
       }
 
+      /*
+       * Check to make sure we have functions
+       * to handle the request
+       */
+      if ((!ha->func.programbios) ||
+          (!ha->func.erasebios) ||
+          (!ha->func.verifybios))
+         return (IPS_FAILURE);
+
       /* copy in the size/buffer ptr from the scsi command */
       memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4);
       memcpy(&pt->CmdBSize, &SC->cmnd[8], 4);
@@ -1674,7 +2190,7 @@
          void *bigger_struct;
 
          /* try to allocate a bigger struct */
-         bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+         bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
          if (bigger_struct) {
             /* free the old memory */
             kfree(ha->ioctl_data);
@@ -1688,37 +2204,29 @@
 
       /* copy in the buffer */
       if (copy_from_user(ha->ioctl_data, pt->CmdBuffer, pt->CmdBSize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to copy user buffer\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to copy user buffer",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_erase_bios(ha)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to erase flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.erasebios)(ha)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to erase flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_program_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to program flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.programbios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to program flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_verify_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to verify flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.verifybios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to verify flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
@@ -1742,7 +2250,7 @@
 ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
    IPS_SG_LIST *sg_list;
 
-   DBG("ips_usrcmd");
+   METHOD_TRACE("ips_usrcmd", 1);
 
    if ((!scb) || (!pt) || (!ha))
       return (0);
@@ -1825,7 +2333,7 @@
    char           *kern_area;
    u32             datasize;
 
-   DBG("ips_usrcmd");
+   METHOD_TRACE("ips_usrcmd", 1);
 
    if ((!scb) || (!pt) || (!ha))
       return (0);
@@ -1862,7 +2370,7 @@
          void *bigger_struct;
 
          /* try to allocate a bigger struct */
-         bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+         bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
          if (bigger_struct) {
             /* free the old memory */
             kfree(ha->ioctl_data);
@@ -1883,10 +2391,8 @@
       datasize = *((u32 *) &scb->scsi_cmd->cmnd[8]);
 
       if (copy_from_user(kern_area, user_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy in user data\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy in user data",
+                   ips_name, ha->host_num);
 
          return (0);
       }
@@ -1937,12 +2443,11 @@
 ips_cleanup_passthru(ips_ha_t *ha, ips_scb_t *scb) {
    ips_passthru_t *pt;
 
-   DBG("ips_cleanup_passthru");
+   METHOD_TRACE("ips_cleanup_passthru", 1);
 
    if ((!scb) || (!scb->scsi_cmd) || (!scb->scsi_cmd->request_buffer)) {
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "IPS couldn't cleanup\n");
-#endif
+      DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
+                ips_name, ha->host_num);
 
       return ;
    }
@@ -1976,12 +2481,13 @@
 ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
    IPS_INFOSTR info;
 
-   DBG("ips_host_info");
+   METHOD_TRACE("ips_host_info", 1);
 
    info.buffer = ptr;
    info.length = len;
    info.offset = offset;
    info.pos = 0;
+   info.localpos = 0;
 
    copy_info(&info, "\nIBM ServeRAID General Information:\n\n");
 
@@ -1991,7 +2497,16 @@
    else
       copy_info(&info, "\tController Type                   : Unknown\n");
 
-   copy_info(&info, "\tIO port address                   : 0x%lx\n", ha->io_addr);
+   if (ha->io_addr)
+      copy_info(&info, "\tIO region                         : 0x%lx (%d bytes)\n",
+                ha->io_addr, ha->io_len);
+
+   if (ha->mem_addr) {
+      copy_info(&info, "\tMemory region                     : 0x%lx (%d bytes)\n",
+                ha->mem_addr, ha->mem_len);
+      copy_info(&info, "\tShared memory address             : 0x%lx\n", ha->mem_ptr);
+   }
+
    copy_info(&info, "\tIRQ number                        : %d\n", ha->irq);
 
    if (ha->nvram->signature == IPS_NVRAM_P5_SIG)
@@ -2031,7 +2546,7 @@
 
    copy_info(&info, "\n");
 
-   return (info.pos > info.offset ? info.pos - info.offset : 0);
+   return (info.localpos);
 }
 
 /****************************************************************************/
@@ -2045,10 +2560,7 @@
 /****************************************************************************/
 static void
 copy_mem_info(IPS_INFOSTR *info, char *data, int len) {
-   DBG("copy_mem_info");
-
-   if (info->pos + len > info->length)
-      len = info->length - info->pos;
+   METHOD_TRACE("copy_mem_info", 1);
 
    if (info->pos + len < info->offset) {
       info->pos += len;
@@ -2057,12 +2569,17 @@
 
    if (info->pos < info->offset) {
       data += (info->offset - info->pos);
-      len  -= (info->offset - info->pos);
+      len -= (info->offset - info->pos);
+      info->pos += (info->offset - info->pos);
    }
 
+   if (info->localpos + len > info->length)
+      len = info->length - info->localpos;
+
    if (len > 0) {
-      memcpy(info->buffer + info->pos, data, len);
+      memcpy(info->buffer + info->localpos, data, len);
       info->pos += len;
+      info->localpos += len;
    }
 }
 
@@ -2078,10 +2595,10 @@
 static int
 copy_info(IPS_INFOSTR *info, char *fmt, ...) {
    va_list args;
-   char buf[81];
+   char buf[128];
    int len;
 
-   DBG("copy_info");
+   METHOD_TRACE("copy_info", 1);
 
    va_start(args, fmt);
    len = vsprintf(buf, fmt, args);
@@ -2105,57 +2622,32 @@
 /****************************************************************************/
 static int
 ips_hainit(ips_ha_t *ha) {
-   int       i;
+   int            i;
+   struct timeval tv;
 
-   DBG("ips_hainit");
+   METHOD_TRACE("ips_hainit", 1);
 
    if (!ha)
       return (0);
 
-   /* initialize status queue */
-   ips_statinit(ha);
+   if (ha->func.statinit)
+      (*ha->func.statinit)(ha);
+
+   if (ha->func.enableint)
+      (*ha->func.enableint)(ha);
 
+   /* Send FFDC */
    ha->reset_count = 1;
+   do_gettimeofday(&tv);
+   ha->last_ffdc = tv.tv_sec;
+   ips_ffdc_reset(ha, IPS_INTR_IORL);
 
-   /* Setup HBA ID's */
    if (!ips_read_config(ha, IPS_INTR_IORL)) {
-
-#ifndef NO_IPS_RESET
-
-      ha->reset_count++;
-
-      /* Try to reset the controller and try again */
-      if (!ips_reset_adapter(ha)) {
-         printk(KERN_WARNING "(%s%d) unable to reset controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-
-      if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
-         printk(KERN_WARNING "(%s%d) unable to initialize controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-
-#endif
-
-      if (!ips_read_config(ha, IPS_INTR_IORL)) {
-         printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-   } /* end if */
-
-   /* write driver version */
-   if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
-      printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+      printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
              ips_name, ha->host_num);
 
       return (0);
-   }
+   } /* end if */
 
    if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
       printk(KERN_WARNING "(%s%d) unable to read controller status.\n",
@@ -2171,19 +2663,18 @@
       return (0);
    }
 
-   /* FFDC */
-   if (ha->subsys->param[3] & 0x300000) {
-      struct timeval tv;
+   /* write nvram user page 5 */
+   if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
+      printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+             ips_name, ha->host_num);
 
-      do_gettimeofday(&tv);
-      ha->last_ffdc = tv.tv_sec;
-      ips_ffdc_reset(ha, IPS_INTR_IORL);
+      return (0);
    }
 
    /* set limits on SID, LUN, BUS */
    ha->ntargets = IPS_MAX_TARGETS + 1;
    ha->nlun = 1;
-   ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS);
+   ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
 
    switch (ha->conf->logical_drive[0].ucStripeSize) {
    case 4:
@@ -2254,13 +2745,14 @@
    ips_scb_t            *scb;
    Scsi_Cmnd            *SC;
    Scsi_Cmnd            *p;
+   Scsi_Cmnd            *q;
    ips_copp_wait_item_t *item;
    int                   ret;
    int                   intr_status;
    u32                   cpu_flags;
    u32                   cpu_flags2;
 
-   DBG("ips_next");
+   METHOD_TRACE("ips_next", 1);
 
    if (!ha)
       return ;
@@ -2406,7 +2898,8 @@
 
       IPS_HA_UNLOCK(cpu_flags);
 
-      SC = ips_removeq_wait(&ha->scb_waitlist, p);
+      q = p;
+      SC = ips_removeq_wait(&ha->scb_waitlist, q);
 
       SC->result = DID_OK;
       SC->host_scribble = NULL;
@@ -2433,28 +2926,41 @@
 
          sg = SC->request_buffer;
 
-         for (i = 0; i < SC->use_sg; i++) {
-            scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
-            scb->sg_list[i].length = sg[i].length;
+         if (SC->use_sg == 1) {
+            if (sg[0].length > ha->max_xfer) {
+               scb->breakup = 1;
+               scb->data_len = ha->max_xfer;
+            } else
+               scb->data_len = sg[0].length;
 
-            if (scb->data_len + sg[i].length > ha->max_xfer) {
-               /*
-                * Data Breakup required
-                */
-               scb->breakup = i;
-               break;
-            }
+            scb->dcdb.transfer_length = scb->data_len;
+            scb->data_busaddr = VIRT_TO_BUS(sg[0].address);
+            scb->sg_len = 0;
+         } else {
 
-            scb->data_len += sg[i].length;
-         }
+            for (i = 0; i < SC->use_sg; i++) {
+               scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
+               scb->sg_list[i].length = sg[i].length;
 
-         if (!scb->breakup)
-            scb->sg_len = SC->use_sg;
-         else
-            scb->sg_len = scb->breakup;
+               if (scb->data_len + sg[i].length > ha->max_xfer) {
+                  /*
+                   * Data Breakup required
+                   */
+                  scb->breakup = i;
+                  break;
+               }
+
+               scb->data_len += sg[i].length;
+            }
+
+            if (!scb->breakup)
+               scb->sg_len = SC->use_sg;
+            else
+               scb->sg_len = scb->breakup;
 
-         scb->dcdb.transfer_length = scb->data_len;
-         scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+            scb->dcdb.transfer_length = scb->data_len;
+            scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+         }
       } else {
          if (SC->request_bufflen) {
             if (SC->request_bufflen > ha->max_xfer) {
@@ -2541,7 +3047,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) {
-   DBG("ips_putq_scb_head");
+   METHOD_TRACE("ips_putq_scb_head", 1);
 
    if (!item)
       return ;
@@ -2572,7 +3078,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_scb_tail(ips_scb_queue_t *queue, ips_scb_t *item) {
-   DBG("ips_putq_scb_tail");
+   METHOD_TRACE("ips_putq_scb_tail", 1);
 
    if (!item)
       return ;
@@ -2609,7 +3115,7 @@
 ips_removeq_scb_head(ips_scb_queue_t *queue) {
    ips_scb_t  *item;
 
-   DBG("ips_removeq_scb_head");
+   METHOD_TRACE("ips_removeq_scb_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2649,7 +3155,7 @@
 ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) {
    ips_scb_t  *p;
 
-   DBG("ips_removeq_scb");
+   METHOD_TRACE("ips_removeq_scb", 1);
 
    if (!item)
       return (NULL);
@@ -2700,7 +3206,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
-   DBG("ips_putq_wait_head");
+   METHOD_TRACE("ips_putq_wait_head", 1);
 
    if (!item)
       return ;
@@ -2731,7 +3237,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_wait_tail(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
-   DBG("ips_putq_wait_tail");
+   METHOD_TRACE("ips_putq_wait_tail", 1);
 
    if (!item)
       return ;
@@ -2768,7 +3274,7 @@
 ips_removeq_wait_head(ips_wait_queue_t *queue) {
    Scsi_Cmnd  *item;
 
-   DBG("ips_removeq_wait_head");
+   METHOD_TRACE("ips_removeq_wait_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2808,7 +3314,7 @@
 ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
    Scsi_Cmnd  *p;
 
-   DBG("ips_removeq_wait");
+   METHOD_TRACE("ips_removeq_wait", 1);
 
    if (!item)
       return (NULL);
@@ -2859,7 +3365,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
-   DBG("ips_putq_copp_head");
+   METHOD_TRACE("ips_putq_copp_head", 1);
 
    if (!item)
       return ;
@@ -2890,7 +3396,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_copp_tail(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
-   DBG("ips_putq_copp_tail");
+   METHOD_TRACE("ips_putq_copp_tail", 1);
 
    if (!item)
       return ;
@@ -2927,7 +3433,7 @@
 ips_removeq_copp_head(ips_copp_queue_t *queue) {
    ips_copp_wait_item_t *item;
 
-   DBG("ips_removeq_copp_head");
+   METHOD_TRACE("ips_removeq_copp_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2967,7 +3473,7 @@
 ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
    ips_copp_wait_item_t *p;
 
-   DBG("ips_removeq_copp");
+   METHOD_TRACE("ips_removeq_copp", 1);
 
    if (!item)
       return (NULL);
@@ -3016,7 +3522,7 @@
 /****************************************************************************/
 static void
 ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ipsintr_blocking");
+   METHOD_TRACE("ipsintr_blocking", 2);
 
    if ((ha->waitflag == TRUE) &&
        (ha->cmd_in_progress == scb->cdb[0])) {
@@ -3037,7 +3543,14 @@
 /****************************************************************************/
 static void
 ipsintr_done(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ipsintr_done");
+   METHOD_TRACE("ipsintr_done", 2);
+
+   if (!scb) {
+      printk(KERN_WARNING "(%s%d) Spurious interrupt; scb NULL.\n",
+             ips_name, ha->host_num);
+
+      return ;
+   }
 
    if (scb->scsi_cmd == NULL) {
       /* unexpected interrupt */
@@ -3064,7 +3577,7 @@
    int ret;
    u32 cpu_flags;
 
-   DBG("ips_done");
+   METHOD_TRACE("ips_done", 1);
 
    if (!scb)
       return ;
@@ -3096,31 +3609,46 @@
 
             sg = scb->scsi_cmd->request_buffer;
 
-            scb->data_len = 0;
-
-            for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
-               scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
-               scb->sg_list[i - bk_save].length = sg[i].length;
-
-               if (scb->data_len + sg[i].length > ha->max_xfer) {
-                  /*
-                   * Data Breakup required
-                   */
-                  scb->breakup = i;
-                  break;
+            if (scb->scsi_cmd->use_sg == 1) {
+               if (sg[0].length - (bk_save * ha->max_xfer)) {
+                  /* Further breakup required */
+                  scb->data_len = ha->max_xfer;
+                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+                  scb->breakup = bk_save + 1;
+               } else {
+                  scb->data_len = sg[0].length - (bk_save * ha->max_xfer);
+                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
                }
 
-               scb->data_len += sg[i].length;
-            }
+               scb->dcdb.transfer_length = scb->data_len;
+               scb->sg_len = 0;
+            } else {
+               scb->data_len = 0;
 
-            if (!scb->breakup)
-               scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
-            else
-               scb->sg_len = scb->breakup - bk_save;
+               for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
+                  scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
+                  scb->sg_list[i - bk_save].length = sg[i].length;
+
+                  if (scb->data_len + sg[i].length > ha->max_xfer) {
+                     /*
+                      * Data Breakup required
+                      */
+                     scb->breakup = i;
+                     break;
+                  }
 
-            scb->dcdb.transfer_length = scb->data_len;
-            scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
-         } else {
+                  scb->data_len += sg[i].length;
+               }
+
+               if (!scb->breakup)
+                  scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
+               else
+                  scb->sg_len = scb->breakup - bk_save;
+
+               scb->dcdb.transfer_length = scb->data_len;
+               scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+           }
+        } else {
             /* Non S/G Request */
             if (scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) {
                /* Further breakup required */
@@ -3199,31 +3727,29 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
+ips_map_status(ips_ha_t *ha, ips_scb_t *scb, ips_stat_t *sp) {
    int       errcode;
+   int       device_error;
 
-   DBG("ips_map_status");
+   METHOD_TRACE("ips_map_status", 1);
 
    if (scb->bus) {
-#if IPS_DEBUG >= 10
-      printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n",
-             ips_name,
-             scb->basic_status,
-             scb->extended_status,
-             scb->dcdb.sense_info[2] & 0xf,
-             scb->dcdb.sense_info[12],
-             scb->dcdb.sense_info[13]);
-#endif
-
-      /* copy SCSI status and sense data for DCDB commands */
-      memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
-             sizeof(scb->scsi_cmd->sense_buffer));
-      scb->scsi_cmd->result = scb->dcdb.scsi_status;
-   } else
-      scb->scsi_cmd->result = 0;
+      DEBUG_VAR(2, "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
+                ips_name,
+                ha->host_num,
+                scb->scsi_cmd->channel,
+                scb->scsi_cmd->target,
+                scb->scsi_cmd->lun,
+                scb->basic_status,
+                scb->extended_status,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
+   }
 
    /* default driver error */
    errcode = DID_ERROR;
+   device_error = 0;
 
    switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
    case IPS_CMD_TIMEOUT:
@@ -3238,25 +3764,19 @@
       break;
 
    case IPS_PHYS_DRV_ERROR:
-      /*
-       * For physical drive errors that
-       * are not on a logical drive should
-       * be DID_OK.  The SCSI errcode will
-       * show what the real error is.
-       */
-      if (scb->bus)
-         errcode = DID_OK;
-
       switch (scb->extended_status) {
       case IPS_ERR_SEL_TO:
-         if (scb->bus) {
-            scb->scsi_cmd->result |= DID_TIME_OUT << 16;
+         if (scb->bus)
+            errcode = DID_NO_CONNECT;
 
-            return (0);
-         }
          break;
+
       case IPS_ERR_OU_RUN:
          if ((scb->bus) && (scb->dcdb.transfer_length < scb->data_len)) {
+            /* Underrun - set default to no error */
+            errcode = DID_OK;
+
+            /* Restrict access to physical DASD */
             if ((scb->scsi_cmd->cmnd[0] == INQUIRY) &&
                 ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
                /* underflow -- no error               */
@@ -3264,23 +3784,16 @@
                errcode = DID_TIME_OUT;
                break;
             }
-
-            /* normal underflow Occured */
-            if (scb->dcdb.transfer_length >= scb->scsi_cmd->underflow) {
-               scb->scsi_cmd->result |= DID_OK << 16;
-
-               return (0);
-            }
-         }
+         } else
+            errcode = DID_ERROR;
 
          break;
+
       case IPS_ERR_RECOVERY:
          /* don't fail recovered errors */
-         if (scb->bus) {
-            scb->scsi_cmd->result |= DID_OK << 16;
+         if (scb->bus)
+            errcode = DID_OK;
 
-            return (0);
-         }
          break;
 
       case IPS_ERR_HOST_RESET:
@@ -3289,11 +3802,25 @@
          break;
 
       case IPS_ERR_CKCOND:
+         if (scb->bus) {
+            memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
+                   sizeof(scb->scsi_cmd->sense_buffer));
+
+            device_error = 2; /* check condition */
+         }
+
+         errcode = DID_OK;
+
+         break;
+
+      default:
+         errcode = DID_ERROR;
          break;
+
       } /* end switch */
    } /* end switch */
 
-   scb->scsi_cmd->result |= (errcode << 16);
+   scb->scsi_cmd->result = device_error | (errcode << 16);
 
    return (1);
 }
@@ -3311,7 +3838,7 @@
 ips_send(ips_ha_t *ha, ips_scb_t *scb, ips_scb_callback callback) {
    int ret;
 
-   DBG("ips_send");
+   METHOD_TRACE("ips_send", 1);
 
    scb->callback = callback;
 
@@ -3333,7 +3860,7 @@
 ips_send_wait(ips_ha_t *ha, ips_scb_t *scb, int timeout, int intr) {
    int       ret;
 
-   DBG("ips_send_wait");
+   METHOD_TRACE("ips_send_wait", 1);
 
    ha->waitflag = TRUE;
    ha->cmd_in_progress = scb->cdb[0];
@@ -3361,7 +3888,7 @@
 ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) {
    int       ret;
 
-   DBG("ips_send_cmd");
+   METHOD_TRACE("ips_send_cmd", 1);
 
    ret = IPS_SUCCESS;
 
@@ -3590,7 +4117,7 @@
       memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len);
    }
 
-   return (ips_issue(ha, scb));
+   return ((*ha->func.issue)(ha, scb));
 }
 
 /****************************************************************************/
@@ -3602,60 +4129,57 @@
 /*   Check the status of commands to logical drives                         */
 /*                                                                          */
 /****************************************************************************/
-static int
-ips_chkstatus(ips_ha_t *ha) {
+static void
+ips_chkstatus(ips_ha_t *ha, IPS_STATUS *pstatus) {
    ips_scb_t  *scb;
-   ips_stat_t *sp = &ha->sp;
+   ips_stat_t *sp;
    u8          basic_status;
    u8          ext_status;
-   int         command_id;
    int         errcode;
-   int         ret;
 
-   DBG("ips_chkstatus");
+   METHOD_TRACE("ips_chkstatus", 1);
 
-   command_id = ips_statupd(ha);
+   scb = &ha->scbs[pstatus->fields.command_id];
+   scb->basic_status = basic_status = pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
+   scb->extended_status = ext_status = pstatus->fields.extended_status;
 
-   if (command_id > (IPS_MAX_CMDS-1)) {
-      printk(KERN_NOTICE "(%s%d) invalid command id received: %d\n",
-             ips_name, ha->host_num, command_id);
-
-      return (-1);
-   }
-
-   scb = &ha->scbs[command_id];
-   sp->scb_addr = (u32) scb;
+   sp = &ha->sp;
    sp->residue_len = 0;
-   scb->basic_status = basic_status = ha->adapt->p_status_tail->basic_status & IPS_BASIC_STATUS_MASK;
-   scb->extended_status = ext_status = ha->adapt->p_status_tail->extended_status;
+   sp->scb_addr = (u32) scb;
 
    /* Remove the item from the active queue */
    ips_removeq_scb(&ha->scb_activelist, scb);
 
    if (!scb->scsi_cmd)
       /* internal commands are handled in do_ipsintr */
-      return (0);
+      return ;
+
+   DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
+             ips_name,
+             ha->host_num,
+             scb->cdb[0],
+             scb->cmd.basic_io.command_id,
+             scb->bus,
+             scb->target_id,
+             scb->lun);
 
 #ifndef NO_IPS_CMDLINE
    if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
       /* passthru - just returns the raw result */
-      return (0);
+      return ;
 #endif
 
    errcode = DID_OK;
-   ret = 0;
 
    if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
        ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
 
       if (scb->bus == 0) {
-#if IPS_DEBUG >= 1
          if ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR) {
-            printk(KERN_NOTICE "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
-                   ips_name, ha->host_num,
-                   scb->cmd.basic_io.op_code, basic_status, ext_status);
+            DEBUG_VAR(1, "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+                      ips_name, ha->host_num,
+                      scb->cmd.basic_io.op_code, basic_status, ext_status);
          }
-#endif
 
          switch (scb->scsi_cmd->cmnd[0]) {
          case ALLOW_MEDIUM_REMOVAL:
@@ -3664,7 +4188,6 @@
          case WRITE_FILEMARKS:
          case SPACE:
             errcode = DID_ERROR;
-            ret = 1;
             break;
 
          case START_STOP:
@@ -3673,7 +4196,6 @@
          case TEST_UNIT_READY:
             if (!ips_online(ha, scb)) {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3682,7 +4204,6 @@
                ips_inquiry(ha, scb);
             } else {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3701,7 +4222,6 @@
          case MODE_SENSE:
             if (!ips_online(ha, scb) || !ips_msense(ha, scb)) {
                errcode = DID_ERROR;
-               ret = 1;
             }
             break;
 
@@ -3710,7 +4230,6 @@
                ips_rdcap(ha, scb);
             else {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3720,7 +4239,6 @@
 
          case FORMAT_UNIT:
             errcode = DID_ERROR;
-            ret = 1;
             break;
 
          case SEEK_10:
@@ -3732,7 +4250,6 @@
 
          default:
             errcode = DID_ERROR;
-            ret = 1;
          } /* end switch */
 
          scb->scsi_cmd->result = errcode << 16;
@@ -3742,23 +4259,17 @@
              ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
 
             scb->scsi_cmd->result = DID_TIME_OUT << 16;
-
-            ret = 1;
          }
       } /* else */
    } else { /* recovered error / success */
-#if IPS_DEBUG >= 1
       if (scb->bus == 0) {
-         printk(KERN_NOTICE "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
-                ips_name, ha->host_num,
-                scb->cmd.basic_io.op_code, basic_status, ext_status);
+         DEBUG_VAR(1, "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+                   ips_name, ha->host_num,
+                   scb->cmd.basic_io.op_code, basic_status, ext_status);
       }
-#endif
 
-      ret = ips_map_status(scb, sp);
+      ips_map_status(ha, scb, sp);
    } /* else */
-
-   return (ret);
 }
 
 /****************************************************************************/
@@ -3772,7 +4283,7 @@
 /****************************************************************************/
 static int
 ips_online(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ips_online");
+   METHOD_TRACE("ips_online", 1);
 
    if (scb->target_id >= IPS_MAX_LD)
       return (0);
@@ -3806,7 +4317,7 @@
 ips_inquiry(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_INQ_DATA inq;
 
-   DBG("ips_inquiry");
+   METHOD_TRACE("ips_inquiry", 1);
 
    memset(&inq, 0, sizeof(IPS_INQ_DATA));
 
@@ -3837,7 +4348,7 @@
 ips_rdcap(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_CAPACITY *cap;
 
-   DBG("ips_rdcap");
+   METHOD_TRACE("ips_rdcap", 1);
 
    if (scb->scsi_cmd->bufflen < 8)
       return (0);
@@ -3866,7 +4377,7 @@
    u32          cylinders;
    ips_mdata_t  mdata;
 
-   DBG("ips_msense");
+   METHOD_TRACE("ips_msense", 1);
 
    if (ha->enq->ulDriveSize[scb->target_id] > 0x400000 &&
        (ha->enq->ucMiscFlag & 0x8) == 0) {
@@ -3944,7 +4455,7 @@
 ips_reqsen(ips_ha_t *ha, ips_scb_t *scb) {
    char *sp;
 
-   DBG("ips_reqsen");
+   METHOD_TRACE("ips_reqsen", 1);
 
    sp = (char *) scb->scsi_cmd->sense_buffer;
    memset(sp, 0, sizeof(scb->scsi_cmd->sense_buffer));
@@ -3970,7 +4481,7 @@
 ips_free(ips_ha_t *ha) {
    int i;
 
-   DBG("ips_free");
+   METHOD_TRACE("ips_free", 1);
 
    if (ha) {
       if (ha->enq) {
@@ -4018,6 +4529,14 @@
          kfree(ha->scbs);
          ha->scbs = NULL;
       } /* end if */
+
+      /* free memory mapped (if applicable) */
+      if (ha->mem_ptr) {
+         iounmap(ha->ioremap_ptr);
+         ha->ioremap_ptr = NULL;
+         ha->mem_ptr = NULL;
+         ha->mem_addr = 0;
+      }
    }
 }
 
@@ -4035,10 +4554,10 @@
    ips_scb_t *scb_p;
    int        i;
 
-   DBG("ips_allocatescbs");
+   METHOD_TRACE("ips_allocatescbs", 1);
 
    /* Allocate memory for the CCBs */
-   ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
+   ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
 
    memset(ha->scbs, 0, ha->max_cmds * sizeof(ips_scb_t));
 
@@ -4046,7 +4565,7 @@
       scb_p = &ha->scbs[i];
 
       /* allocate S/G list */
-      scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
+      scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
 
       if (! scb_p->sg_list)
          return (0);
@@ -4075,7 +4594,7 @@
 ips_init_scb(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_SG_LIST *sg_list;
 
-   DBG("ips_init_scb");
+   METHOD_TRACE("ips_init_scb", 1);
 
    if (scb == NULL)
       return ;
@@ -4116,7 +4635,7 @@
    ips_scb_t     *scb;
    u32            cpu_flags;
 
-   DBG("ips_getscb");
+   METHOD_TRACE("ips_getscb", 1);
 
    IPS_SCB_LOCK(cpu_flags);
    if ((scb = ha->scb_freelist) == NULL) {
@@ -4150,7 +4669,7 @@
 ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
    u32          cpu_flags;
 
-   DBG("ips_freescb");
+   METHOD_TRACE("ips_freescb", 1);
 
    /* check to make sure this is not our "special" scb */
    if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
@@ -4163,7 +4682,7 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_reset_adapter                                          */
+/* Routine Name: ips_isinit_copperhead                                      */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -4171,21 +4690,402 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_reset_adapter(ips_ha_t *ha) {
-   u8  Isr;
+ips_isinit_copperhead(ips_ha_t *ha) {
+   u8 scpr;
+   u8 isr;
+
+   METHOD_TRACE("ips_isinit_copperhead", 1);
+
+   isr = inb(ha->io_addr + IPS_REG_HISR);
+   scpr = inb(ha->io_addr + IPS_REG_SCPR);
+
+   if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isinit_copperhead_memio                                */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isinit_copperhead_memio(ips_ha_t *ha) {
+   u8 isr;
+   u8 scpr;
+
+   METHOD_TRACE("ips_is_init_copperhead_memio", 1);
+
+   isr = readb(ha->mem_ptr + IPS_REG_HISR);
+   scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
+
+   if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isinit_morpheus                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isinit_morpheus(ips_ha_t *ha) {
+   u32 post;
+   u32 bits;
+
+   METHOD_TRACE("ips_is_init_morpheus", 1);
+
+   post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+   bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (post == 0)
+       return (0);
+   else if (bits & 0x3)
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_copperhead                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead(ips_ha_t *ha) {
+   METHOD_TRACE("ips_enable_int_copperhead", 1);
+
+   outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_copperhead_memio                            */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead_memio(ips_ha_t *ha) {
+   METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
+
+   writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_morpheus                                    */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_morpheus(ips_ha_t *ha) {
+   u32              Oimr;
+
+   METHOD_TRACE("ips_enable_int_morpheus", 1);
+
+   Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+   Oimr &= ~0x08;
+   writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_copperhead                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a copperhead controller                                     */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_copperhead(ips_ha_t *ha) {
+   u8  Isr;
+   u8  Cbsp;
+   u8  PostByte[IPS_MAX_POST_BYTES];
+   u8  ConfigByte[IPS_MAX_CONFIG_BYTES];
+   int i, j;
+
+   METHOD_TRACE("ips_init_copperhead", 1);
+
+   for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+      for (j = 0; j < 45; j++) {
+         Isr = inb(ha->io_addr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC);
+      }
+
+      if (j >= 45)
+         /* error occured */
+         return (0);
+
+      PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+             ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+      return (0);
+   }
+
+   for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+      for (j = 0; j < 240; j++) {
+         Isr = inb(ha->io_addr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC); /* 100 msec */
+      }
+
+      if (j >= 240)
+         /* error occured */
+         return (0);
+
+      ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   for (i = 0; i < 240; i++) {
+      Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+
+      if ((Cbsp & IPS_BIT_OP) == 0)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 240)
+      /* reset failed */
+      return (0);
+
+   /* setup CCCR */
+   outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+
+   /* Enable busmastering */
+   outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      /* fix for anaconda64 */
+      outl(0, ha->io_addr + IPS_REG_NDAE);
+
+   /* Enable interrupts */
+   outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_copperhead_memio                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a copperhead controller with memory mapped I/O              */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_copperhead_memio(ips_ha_t *ha) {
+   u8  Isr;
    u8  Cbsp;
    u8  PostByte[IPS_MAX_POST_BYTES];
    u8  ConfigByte[IPS_MAX_CONFIG_BYTES];
    int i, j;
+
+   METHOD_TRACE("ips_init_copperhead_memio", 1);
+
+   for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+      for (j = 0; j < 45; j++) {
+         Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC);
+      }
+
+      if (j >= 45)
+         /* error occured */
+         return (0);
+
+      PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+   }
+
+   if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+             ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+      return (0);
+   }
+
+   for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+      for (j = 0; j < 240; j++) {
+         Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC); /* 100 msec */
+      }
+
+      if (j >= 240)
+         /* error occured */
+         return (0);
+
+      ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+   }
+
+   for (i = 0; i < 240; i++) {
+      Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
+
+      if ((Cbsp & IPS_BIT_OP) == 0)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 240)
+      /* error occured */
+      return (0);
+
+   /* setup CCCR */
+   writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
+
+   /* Enable busmastering */
+   writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
+
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      /* fix for anaconda64 */
+      writel(0, ha->mem_ptr + IPS_REG_NDAE);
+
+   /* Enable interrupts */
+   writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+
+   /* if we get here then everything went OK */
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_morpheus                                          */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a morpheus controller                                       */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_morpheus(ips_ha_t *ha) {
+   u32 Post;
+   u32 Config;
+   u32 Isr;
+   u32 Oimr;
+   int i;
+
+   METHOD_TRACE("ips_init_morpheus", 1);
+
+   /* Wait up to 45 secs for Post */
+   for (i = 0; i < 45; i++) {
+      Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+      if (Isr & IPS_BIT_I960_MSG0I)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 45) {
+      /* error occured */
+      printk(KERN_WARNING "(%s%d) timeout waiting for post.\n",
+             ips_name, ha->host_num);
+
+      return (0);
+   }
+
+   Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+
+   /* Clear the interrupt bit */
+   Isr = (u32) IPS_BIT_I960_MSG0I;
+   writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (Post < (IPS_GOOD_POST_STATUS << 8)) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x).\n",
+             ips_name, ha->host_num, Post);
+
+      return (0);
+   }
+
+   /* Wait up to 240 secs for config bytes */
+   for (i = 0; i < 240; i++) {
+      Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+      if (Isr & IPS_BIT_I960_MSG1I)
+         break;
+
+      MDELAY(IPS_ONE_SEC); /* 100 msec */
+   }
+
+   if (i >= 240) {
+      /* error occured */
+      printk(KERN_WARNING "(%s%d) timeout waiting for config.\n",
+             ips_name, ha->host_num);
+
+      return (0);
+   }
+
+   Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
+
+   /* Clear interrupt bit */
+   Isr = (u32) IPS_BIT_I960_MSG1I;
+   writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   /* Turn on the interrupts */
+   Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+   Oimr &= ~0x8;
+   writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+
+   /* if we get here then everything went OK */
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_copperhead                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_copperhead(ips_ha_t *ha) {
    int reset_counter;
    u32 cpu_flags;
 
-   DBG("ips_reset_adapter");
+   METHOD_TRACE("ips_reset_copperhead", 1);
 
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "ips_reset_adapter: io addr: %x, irq: %d\n",
-          ha->io_addr, ha->irq);
-#endif
+   DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->io_addr, ha->irq);
 
    IPS_HA_LOCK(cpu_flags);
 
@@ -4199,109 +5099,107 @@
       outb(0, ha->io_addr + IPS_REG_SCPR);
       MDELAY(IPS_ONE_SEC);
 
-      for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
-         for (j = 0; j < 45; j++) {
-            Isr = inb(ha->io_addr + IPS_REG_HISR);
-            if (Isr & IPS_BIT_GHI)
-               break;
-
-            MDELAY(IPS_ONE_SEC);
-         }
-
-         if (j >= 45) {
-            /* error occured */
-            if (reset_counter < 2)
-               continue;
-            else {
-               /* reset failed */
-               IPS_HA_UNLOCK(cpu_flags);
-
-               return (0);
-            }
-         }
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
+         IPS_HA_UNLOCK(cpu_flags);
 
-         PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
-         outb(Isr, ha->io_addr + IPS_REG_HISR);
+         return (0);
       }
+   }
 
-      if (PostByte[0] < IPS_GOOD_POST_STATUS) {
-         printk("(%s%d) reset controller fails (post status %x %x).\n",
-                ips_name, ha->host_num, PostByte[0], PostByte[1]);
+   IPS_HA_UNLOCK(cpu_flags);
 
-         IPS_HA_UNLOCK(cpu_flags);
+   return (1);
+}
 
-         return (0);
-      }
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_copperhead_memio                                 */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_copperhead_memio(ips_ha_t *ha) {
+   int reset_counter;
+   u32 cpu_flags;
 
-      for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
-         for (j = 0; j < 240; j++) {
-            Isr = inb(ha->io_addr + IPS_REG_HISR);
-            if (Isr & IPS_BIT_GHI)
-               break;
+   METHOD_TRACE("ips_reset_copperhead_memio", 1);
 
-            MDELAY(IPS_ONE_SEC); /* 100 msec */
-         }
+   DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-         if (j >= 240) {
-            /* error occured */
-            if (reset_counter < 2)
-               continue;
-            else {
-               /* reset failed */
-               IPS_HA_UNLOCK(cpu_flags);
+   IPS_HA_LOCK(cpu_flags);
 
-               return (0);
-            }
-         }
+   reset_counter = 0;
 
-         ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
-         outb(Isr, ha->io_addr + IPS_REG_HISR);
-      }
+   while (reset_counter < 2) {
+      reset_counter++;
 
-      if (ConfigByte[0] == 0 && ConfigByte[1] == 2) {
-         printk("(%s%d) reset controller fails (status %x %x).\n",
-                ips_name, ha->host_num, ConfigByte[0], ConfigByte[1]);
+      writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
+      MDELAY(IPS_ONE_SEC);
+      writeb(0, ha->mem_ptr + IPS_REG_SCPR);
+      MDELAY(IPS_ONE_SEC);
 
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
          IPS_HA_UNLOCK(cpu_flags);
 
          return (0);
       }
+   }
 
-      for (i = 0; i < 240; i++) {
-         Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+   IPS_HA_UNLOCK(cpu_flags);
 
-         if ((Cbsp & IPS_BIT_OP) == 0)
-            break;
+   return (1);
+}
 
-         MDELAY(IPS_ONE_SEC);
-      }
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_morpheus                                         */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_morpheus(ips_ha_t *ha) {
+   int reset_counter;
+   u8  junk;
+   u32 cpu_flags;
 
-      if (i >= 240) {
-         /* error occured */
-         if (reset_counter < 2)
-            continue;
-         else {
-            /* reset failed */
-            IPS_HA_UNLOCK(cpu_flags);
+   METHOD_TRACE("ips_reset_morpheus", 1);
 
-            return (0);
-         }
-      }
+   DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-      /* setup CCCR */
-      outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+   IPS_HA_LOCK(cpu_flags);
 
-      /* Enable busmastering */
-      outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+   reset_counter = 0;
 
-      /* setup status queues */
-      ips_statinit(ha);
+   while (reset_counter < 2) {
+      reset_counter++;
 
-      /* Enable interrupts */
-      outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+      writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
 
-      /* if we get here then everything went OK */
-      break;
+      /* Delay for 300 msec */
+      MDELAY(300 * IPS_ONE_MSEC);
+
+      /* Do a PCI config read to wait for adapter */
+      pci_read_config_byte(ha->pcidev, 4, &junk);
+
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
+         IPS_HA_UNLOCK(cpu_flags);
+
+         return (0);
+      }
    }
 
    IPS_HA_UNLOCK(cpu_flags);
@@ -4322,7 +5220,7 @@
 ips_statinit(ips_ha_t *ha) {
    u32        phys_status_start;
 
-   DBG("ips_statinit");
+   METHOD_TRACE("ips_statinit", 1);
 
    ha->adapt->p_status_start = ha->adapt->status;
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
@@ -4340,18 +5238,45 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_statupd                                                */
+/* Routine Name: ips_statinit_memio                                         */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Remove an element from the status queue                                */
+/*   Initialize the status queues on the controller                         */
 /*                                                                          */
 /****************************************************************************/
-static int
-ips_statupd(ips_ha_t *ha) {
-   int       command_id;
+static void
+ips_statinit_memio(ips_ha_t *ha) {
+   u32        phys_status_start;
+
+   METHOD_TRACE("ips_statinit_memio", 1);
+
+   ha->adapt->p_status_start = ha->adapt->status;
+   ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
+   ha->adapt->p_status_tail = ha->adapt->status;
+
+   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
+   writel(phys_status_start + IPS_STATUS_Q_SIZE, ha->mem_ptr + IPS_REG_SQER);
+   writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
+   writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
+
+   ha->adapt->hw_status_start = phys_status_start;
+   ha->adapt->hw_status_tail = phys_status_start;
+}
 
-   DBG("ips_statupd");
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_statupd_copperhead                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Remove an element from the status queue                                */
+/*                                                                          */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead(ips_ha_t *ha) {
+   METHOD_TRACE("ips_statupd_copperhead", 1);
 
    if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
       ha->adapt->p_status_tail++;
@@ -4363,47 +5288,87 @@
 
    outl(ha->adapt->hw_status_tail, ha->io_addr + IPS_REG_SQTR);
 
-   command_id = ha->adapt->p_status_tail->command_id;
+   return (ha->adapt->p_status_tail->value);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_statupd_copperhead_memio                               */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Remove an element from the status queue                                */
+/*                                                                          */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead_memio(ips_ha_t *ha) {
+   METHOD_TRACE("ips_statupd_copperhead_memio", 1);
+
+   if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
+      ha->adapt->p_status_tail++;
+      ha->adapt->hw_status_tail += sizeof(IPS_STATUS);
+   } else {
+      ha->adapt->p_status_tail = ha->adapt->p_status_start;
+      ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
+   }
+
+   writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
 
-   return (command_id);
+   return (ha->adapt->p_status_tail->value);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_issue                                                  */
+/* Routine Name: ips_statupd_morpheus                                       */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Send a command down to the controller                                  */
+/*   Remove an element from the status queue                                */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/****************************************************************************/
+static u32
+ips_statupd_morpheus(ips_ha_t *ha) {
+   u32       val;
+
+   METHOD_TRACE("ips_statupd_morpheus", 1);
+
+   val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
+
+   return (val);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_copperhead                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_issue(ips_ha_t *ha, ips_scb_t *scb) {
+ips_issue_copperhead(ips_ha_t *ha, ips_scb_t *scb) {
    u32       TimeOut;
    u16       val;
    u32       cpu_flags;
 
-   DBG("ips_issue");
+   METHOD_TRACE("ips_issue_copperhead", 1);
 
-#if IPS_DEBUG >= 10
-   if (scb->scsi_cmd)
-      printk(KERN_NOTICE "%s: ips_issue: cmd 0x%X id %d (%d %d %d)\n",
-             ips_name,
-             scb->cdb[0],
-             scb->cmd.basic_io.command_id,
-             scb->bus,
-             scb->target_id,
-             scb->lun);
-   else
-      printk(KERN_NOTICE "%s: ips_issue: logical cmd id %d\n",
-             ips_name,
-             scb->cmd.basic_io.command_id);
-#if IPS_DEBUG >= 11
-      MDELAY(IPS_ONE_SEC);
-#endif
-#endif
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
 
    IPS_HA_LOCK(cpu_flags);
 
@@ -4430,14 +5395,189 @@
    outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR);
    outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR);
 
-   IPS_HA_UNLOCK(cpu_flags);
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_copperhead_memio                                 */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_copperhead_memio(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       TimeOut;
+   u32       val;
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_copperhead_memio", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   TimeOut = 0;
+
+   while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
+      UDELAY(1000);
+
+      if (++TimeOut >= IPS_SEM_TIMEOUT) {
+         if (!(val & IPS_BIT_START_STOP))
+            break;
+
+         printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n",
+                ips_name, ha->host_num, val);
+         printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n",
+                ips_name, ha->host_num);
+
+         IPS_HA_UNLOCK(cpu_flags);
+
+         return (IPS_FAILURE);
+      } /* end if */
+   } /* end while */
+
+   writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
+   writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_i2o                                              */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_i2o(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_i2o", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_i2o_memio                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_i2o_memio(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_i2o_memio", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isintr_copperhead                                      */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Test to see if an interrupt is for us                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isintr_copperhead(ips_ha_t *ha) {
+   u8 Isr;
+
+   METHOD_TRACE("ips_isintr_copperhead", 2);
+
+   Isr = inb(ha->io_addr + IPS_REG_HISR);
+
+   if (Isr == 0xFF)
+      /* ?!?! Nothing really there */
+      return (0);
 
-   return (IPS_SUCCESS);
+   if (Isr & IPS_BIT_SCE)
+      return (1);
+   else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
+      /* status queue overflow or GHI */
+      /* just clear the interrupt */
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   return (0);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_isintr                                                 */
+/* Routine Name: ips_isintr_copperhead_memio                                */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -4445,12 +5585,12 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_isintr(ips_ha_t *ha) {
+ips_isintr_copperhead_memio(ips_ha_t *ha) {
    u8 Isr;
 
-   DBG("ips_isintr");
+   METHOD_TRACE("ips_isintr_memio", 2);
 
-   Isr = inb(ha->io_addr + IPS_REG_HISR);
+   Isr = readb(ha->mem_ptr + IPS_REG_HISR);
 
    if (Isr == 0xFF)
       /* ?!?! Nothing really there */
@@ -4461,7 +5601,7 @@
    else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
       /* status queue overflow or GHI */
       /* just clear the interrupt */
-      outb(Isr, ha->io_addr + IPS_REG_HISR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
    }
 
    return (0);
@@ -4469,6 +5609,29 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_isintr_morpheus                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Test to see if an interrupt is for us                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isintr_morpheus(ips_ha_t *ha) {
+   u32 Isr;
+
+   METHOD_TRACE("ips_isintr_morpheus", 2);
+
+   Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (Isr & IPS_BIT_I2O_OPQI)
+      return (1);
+   else
+      return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_wait                                                   */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -4481,7 +5644,7 @@
    int        ret;
    u8         done;
 
-   DBG("ips_wait");
+   METHOD_TRACE("ips_wait", 1);
 
    ret = IPS_FAILURE;
    done = FALSE;
@@ -4516,7 +5679,7 @@
          while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
             UDELAY(1000);
 
-         ips_intr(ha);
+         (*ha->func.intr)(ha);
 
          clear_bit(IPS_IN_INTR, &ha->flags);
       } else if (intr == IPS_INTR_HAL) {
@@ -4542,7 +5705,7 @@
          while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
             UDELAY(1000);
 
-         ips_intr(ha);
+         (*ha->func.intr)(ha);
 
          clear_bit(IPS_IN_INTR, &ha->flags);
 
@@ -4567,7 +5730,7 @@
 /****************************************************************************/
 static int
 ips_write_driver_status(ips_ha_t *ha, int intr) {
-   DBG("ips_write_driver_status");
+   METHOD_TRACE("ips_write_driver_status", 1);
 
    if (!ips_readwrite_page5(ha, FALSE, intr)) {
       printk(KERN_WARNING "(%s%d) unable to read NVRAM page 5.\n",
@@ -4579,21 +5742,18 @@
    /* check to make sure the page has a valid */
    /* signature */
    if (ha->nvram->signature != IPS_NVRAM_P5_SIG) {
-#if IPS_DEBUG >= 1
-      printk("(%s%d) NVRAM page 5 has an invalid signature: %X.\n",
-             ips_name, ha->host_num, ha->nvram->signature);
-#endif
+      DEBUG_VAR(1, "(%s%d) NVRAM page 5 has an invalid signature: %X.",
+                ips_name, ha->host_num, ha->nvram->signature);
+
       return (1);
    }
 
-#if IPS_DEBUG >= 2
-   printk("(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.\n",
-          ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
-          ha->nvram->bios_high[0], ha->nvram->bios_high[1],
-          ha->nvram->bios_high[2], ha->nvram->bios_high[3],
-          ha->nvram->bios_low[0], ha->nvram->bios_low[1],
-          ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
-#endif
+   DEBUG_VAR(2, "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
+             ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
+             ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+             ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+             ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+             ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
 
    /* save controller type */
    ha->ad_type = ha->nvram->adapter_type;
@@ -4628,7 +5788,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_read_adapter_status");
+   METHOD_TRACE("ips_read_adapter_status", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4647,8 +5807,9 @@
    scb->cmd.basic_io.reserved = 0;
 
    /* send command */
-   ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4668,7 +5829,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_read_subsystem_parameters");
+   METHOD_TRACE("ips_read_subsystem_parameters", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4687,8 +5848,9 @@
    scb->cmd.basic_io.reserved = 0;
 
    /* send command */
-   ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4709,7 +5871,7 @@
    int        i;
    int        ret;
 
-   DBG("ips_read_config");
+   METHOD_TRACE("ips_read_config", 1);
 
    /* set defaults for initiator IDs */
    ha->conf->init_id[0] = IPS_ADAPTER_ID;
@@ -4759,7 +5921,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_readwrite_page5");
+   METHOD_TRACE("ips_readwrite_page5", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4803,7 +5965,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_clear_adapter");
+   METHOD_TRACE("ips_clear_adapter", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4821,8 +5983,9 @@
    scb->cmd.config_sync.reserved3 = 0;
 
    /* issue command */
-   ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    /* send unlock stripe command */
@@ -4840,8 +6003,9 @@
    scb->cmd.unlock_stripe.reserved3 = 0;
 
    /* issue command */
-   ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4860,7 +6024,7 @@
 ips_ffdc_reset(ips_ha_t *ha, int intr) {
    ips_scb_t *scb;
 
-   DBG("ips_ffdc_reset");
+   METHOD_TRACE("ips_ffdc_reset", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4893,12 +6057,10 @@
 ips_ffdc_time(ips_ha_t *ha, int intr) {
    ips_scb_t *scb;
 
-   DBG("ips_ffdc_time");
+   METHOD_TRACE("ips_ffdc_time", 1);
 
-#if IPS_DEBUG >= 1
-   printk(KERN_NOTICE "(%s%d) Sending time update.\n",
-          ips_name, ha->host_num);
-#endif
+   DEBUG_VAR(1, "(%s%d) Sending time update.",
+             ips_name, ha->host_num);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4947,6 +6109,8 @@
                                  {30, 30},
                                  {31, 31} };
 
+   METHOD_TRACE("ips_fix_ffdc_time", 1);
+
    days = current_time / IPS_SECS_DAY;
    rem = current_time % IPS_SECS_DAY;
 
@@ -4995,6 +6159,8 @@
    int   timeout;
    u8    status;
 
+   METHOD_TRACE("ips_erase_bios", 1);
+
    /* Clear the status register */
    outl(0, ha->io_addr + IPS_REG_FLAP);
    if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5091,6 +6257,115 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_erase_bios_memio                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Erase the BIOS on the adapter                                          */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_erase_bios_memio(ips_ha_t *ha) {
+   int   timeout;
+   u8    status;
+
+   METHOD_TRACE("ips_erase_bios_memio", 1);
+
+   /* Clear the status register */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Setup */
+   writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Confirm */
+   writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Status */
+   writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   timeout = 80000; /* 80 seconds */
+
+   while (timeout > 0) {
+      if (ha->revision_id == IPS_REVID_TROMBONE64) {
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         UDELAY(5); /* 5 us */
+      }
+
+      status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+      if (status & 0x80)
+         break;
+
+      MDELAY(1);
+      timeout--;
+   }
+
+   /* check for timeout */
+   if (timeout <= 0) {
+      /* timeout */
+
+      /* try to suspend the erase */
+      writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      /* wait for 10 seconds */
+      timeout = 10000;
+      while (timeout > 0) {
+         if (ha->revision_id == IPS_REVID_TROMBONE64) {
+            writel(0, ha->mem_ptr + IPS_REG_FLAP);
+            UDELAY(5); /* 5 us */
+         }
+
+         status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+         if (status & 0xC0)
+            break;
+
+         MDELAY(1);
+         timeout--;
+      }
+
+      return (1);
+   }
+
+   /* check for valid VPP */
+   if (status & 0x08)
+      /* VPP failure */
+      return (1);
+
+   /* check for succesful flash */
+   if (status & 0x30)
+      /* sequence error */
+      return (1);
+
+   /* Otherwise, we were successful */
+   /* clear status */
+   writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* enable reads */
+   writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_program_bios                                           */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -5103,6 +6378,8 @@
    int   timeout;
    u8    status;
 
+   METHOD_TRACE("ips_program_bios", 1);
+
    for (i = 0; i < buffersize; i++) {
       /* write a byte */
       outl(i, ha->io_addr + IPS_REG_FLAP);
@@ -5176,6 +6453,93 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_program_bios_memio                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Program the BIOS on the adapter                                        */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_program_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+   int   i;
+   int   timeout;
+   u8    status;
+
+   METHOD_TRACE("ips_program_bios_memio", 1);
+
+   for (i = 0; i < buffersize; i++) {
+      /* write a byte */
+      writel(i, ha->mem_ptr + IPS_REG_FLAP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      /* wait up to one second */
+      timeout = 1000;
+      while (timeout > 0) {
+         if (ha->revision_id == IPS_REVID_TROMBONE64) {
+            writel(0, ha->mem_ptr + IPS_REG_FLAP);
+            UDELAY(5); /* 5 us */
+         }
+
+         status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+         if (status & 0x80)
+            break;
+
+         MDELAY(1);
+         timeout--;
+      }
+
+      if (timeout == 0) {
+         /* timeout error */
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         return (1);
+      }
+
+      /* check the status */
+      if (status & 0x18) {
+         /* programming error */
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         return (1);
+      }
+   } /* end for */
+
+   /* Enable reading */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_verify_bios                                            */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -5187,6 +6551,8 @@
    u8    checksum;
    int   i;
 
+   METHOD_TRACE("ips_verify_bios", 1);
+
    /* test 1st byte */
    outl(0, ha->io_addr + IPS_REG_FLAP);
    if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5209,6 +6575,53 @@
          UDELAY(5); /* 5 us */
 
       checksum = (u8) checksum + inb(ha->io_addr + IPS_REG_FLDP);
+   }
+
+   if (checksum != 0)
+      /* failure */
+      return (1);
+   else
+      /* success */
+      return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_verify_bios_memio                                      */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Verify the BIOS on the adapter                                         */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_verify_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+   u8    checksum;
+   int   i;
+
+   METHOD_TRACE("ips_verify_bios_memio", 1);
+
+   /* test 1st byte */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
+      return (1);
+
+   writel(1, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+   if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
+      return (1);
+
+   checksum = 0xff;
+   for (i = 2; i < buffersize; i++) {
+
+      writel(i, ha->mem_ptr + IPS_REG_FLAP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      checksum = (u8) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
    }
 
    if (checksum != 0)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ips.h linux.18p17/drivers/scsi/ips.h
--- linux.vanilla/drivers/scsi/ips.h	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/ips.h	Sun Oct 15 23:30:14 2000
@@ -1,5 +1,5 @@
 /*****************************************************************************/
-/* ips.h -- driver for the IBM ServeRAID adapter                             */
+/* ips.h -- driver for the IBM ServeRAID controller                          */
 /*                                                                           */
 /* Written By: Keith Mitchell, IBM Corporation                               */
 /*                                                                           */
@@ -47,14 +47,13 @@
 #ifndef _IPS_H_
    #define _IPS_H_
 
+   #include <linux/config.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
 
    /* Prototypes */
    extern int ips_detect(Scsi_Host_Template *);
    extern int ips_release(struct Scsi_Host *);
-   extern int ips_abort(Scsi_Cmnd *);
-   extern int ips_reset(Scsi_Cmnd *, unsigned int);
    extern int ips_eh_abort(Scsi_Cmnd *);
    extern int ips_eh_reset(Scsi_Cmnd *);
    extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
@@ -71,7 +70,21 @@
 
    #define IPS_HA(x)                   ((ips_ha_t *) x->hostdata)
    #define IPS_COMMAND_ID(ha, scb)     (int) (scb - ha->scbs)
-   
+   #define IPS_IS_TROMBONE(ha)         (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \
+                                         (ha->revision_id >= IPS_REVID_TROMBONE32) && \
+                                         (ha->revision_id <= IPS_REVID_TROMBONE64)) ? 1 : 0)
+   #define IPS_IS_CLARINET(ha)         (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \
+                                         (ha->revision_id >= IPS_REVID_CLARINETP1) && \
+                                         (ha->revision_id <= IPS_REVID_CLARINETP3)) ? 1 : 0)
+   #define IPS_IS_MORPHEUS(ha)         (ha->device_id == IPS_MORPHEUS_DEVICEID)
+   #define IPS_USE_I2O_DELIVER(ha)     ((IPS_IS_MORPHEUS(ha) || \
+                                         (IPS_IS_TROMBONE(ha) && \
+                                          (ips_force_i2o))) ? 1 : 0)
+   #define IPS_USE_I2O_STATUS(ha)      (IPS_IS_MORPHEUS(ha))
+   #define IPS_USE_MEMIO(ha)           ((IPS_IS_MORPHEUS(ha) || \
+                                         ((IPS_IS_TROMBONE(ha) || IPS_IS_CLARINET(ha)) && \
+                                          (ips_force_memio))) ? 1 : 0)
+
    #ifndef VIRT_TO_BUS
       #define VIRT_TO_BUS(x)           (unsigned int)virt_to_bus((void *) x)
    #endif
@@ -79,7 +92,7 @@
    #ifndef UDELAY
       #define UDELAY udelay
    #endif
-   
+
    #ifndef MDELAY
       #define MDELAY mdelay
    #endif
@@ -87,27 +100,27 @@
    #ifndef verify_area_20
       #define verify_area_20(t,a,sz)   (0) /* success */
    #endif
-   
+
    #ifndef PUT_USER
       #define PUT_USER                 put_user
    #endif
-   
+
    #ifndef __PUT_USER
       #define __PUT_USER               __put_user
    #endif
-   
+
    #ifndef PUT_USER_RET
       #define PUT_USER_RET             put_user_ret
    #endif
-   
+
    #ifndef GET_USER
       #define GET_USER                 get_user
    #endif
-   
+
    #ifndef __GET_USER
       #define __GET_USER               __get_user
    #endif
-   
+
    #ifndef GET_USER_RET
       #define GET_USER_RET             get_user_ret
    #endif
@@ -137,6 +150,14 @@
    #define IPS_REG_CBSP                 0x07    /* CBSP register               */
    #define IPS_REG_FLAP                 0x18    /* Flash address port          */
    #define IPS_REG_FLDP                 0x1C    /* Flash data port             */
+   #define IPS_REG_NDAE                 0x38    /* Anaconda 64 NDAE Register   */
+   #define IPS_REG_I2O_INMSGQ           0x40    /* I2O Inbound Message Queue   */
+   #define IPS_REG_I2O_OUTMSGQ          0x44    /* I2O Outbound Message Queue  */
+   #define IPS_REG_I2O_HIR              0x30    /* I2O Interrupt Status        */
+   #define IPS_REG_I960_IDR             0x20    /* i960 Inbound Doorbell       */
+   #define IPS_REG_I960_MSG0            0x18    /* i960 Outbound Reg 0         */
+   #define IPS_REG_I960_MSG1            0x1C    /* i960 Outbound Reg 1         */
+   #define IPS_REG_I960_OIMR            0x34    /* i960 Oubound Int Mask Reg   */
 
    /*
     * Adapter register bit equates
@@ -152,6 +173,9 @@
    #define IPS_BIT_EBM                  0x02    /* SCPR Enable Bus Master      */
    #define IPS_BIT_EI                   0x80    /* HISR Enable Interrupts      */
    #define IPS_BIT_OP                   0x01    /* OP bit in CBSP              */
+   #define IPS_BIT_I2O_OPQI             0x08    /* General Host Interrupt      */
+   #define IPS_BIT_I960_MSG0I           0x01    /* Message Register 0 Interrupt*/
+   #define IPS_BIT_I960_MSG1I           0x02    /* Message Register 1 Interrupt*/
 
    /*
     * Adapter Command ID Equates
@@ -202,13 +226,15 @@
    #define IPS_INTR_HAL                 2
    #define IPS_ADAPTER_ID               0xF
    #define IPS_VENDORID                 0x1014
-   #define IPS_DEVICEID                 0x002E
+   #define IPS_COPPERHEAD_DEVICEID      0x002E
+   #define IPS_MORPHEUS_DEVICEID        0x01BD
    #define IPS_IOCTL_SIZE               8192
    #define IPS_STATUS_SIZE              4
    #define IPS_STATUS_Q_SIZE            (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE
+   #define IPS_MEMMAP_SIZE              128
    #define IPS_ONE_MSEC                 1
    #define IPS_ONE_SEC                  1000
-   
+
    /*
     * Geometry Settings
     */
@@ -336,10 +362,12 @@
    /*
     * Scsi_Host Template
     */
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
  #define IPS {                            \
     next : NULL,                          \
     module : NULL,                        \
     proc_info : NULL,                     \
+    proc_dir : NULL,                      \
     name : NULL,                          \
     detect : ips_detect,                  \
     release : ips_release,                \
@@ -351,8 +379,8 @@
     eh_device_reset_handler : NULL,       \
     eh_bus_reset_handler : NULL,          \
     eh_host_reset_handler : ips_eh_reset, \
-    abort : ips_abort,                    \
-    reset : ips_reset,                    \
+    abort : NULL,                         \
+    reset : NULL,                         \
     slave_attach : NULL,                  \
     bios_param : ips_biosparam,           \
     can_queue : 0,                        \
@@ -363,7 +391,37 @@
     unchecked_isa_dma : 0,                \
     use_clustering : ENABLE_CLUSTERING,   \
     use_new_eh_code : 1                   \
- }
+}
+#else
+ #define IPS {                            \
+    next : NULL,                          \
+    module : NULL,                        \
+    proc_info : NULL,                     \
+    name : NULL,                          \
+    detect : ips_detect,                  \
+    release : ips_release,                \
+    info : ips_info,                      \
+    command : NULL,                       \
+    queuecommand : ips_queue,             \
+    eh_strategy_handler : NULL,           \
+    eh_abort_handler : ips_eh_abort,      \
+    eh_device_reset_handler : NULL,       \
+    eh_bus_reset_handler : NULL,          \
+    eh_host_reset_handler : ips_eh_reset, \
+    abort : NULL,                         \
+    reset : NULL,                         \
+    slave_attach : NULL,                  \
+    bios_param : ips_biosparam,           \
+    can_queue : 0,                        \
+    this_id: -1,                          \
+    sg_tablesize : IPS_MAX_SG,            \
+    cmd_per_lun: 16,                      \
+    present : 0,                          \
+    unchecked_isa_dma : 0,                \
+    use_clustering : ENABLE_CLUSTERING,   \
+    use_new_eh_code : 1                   \
+}
+#endif
 
 /*
  * IBM PCI Raid Command Formats
@@ -531,11 +589,15 @@
    u8         reserved2[3];
 } IPS_DCDB_TABLE, *PIPS_DCDB_TABLE;
 
-typedef struct {
-   volatile u8      reserved;
-   volatile u8      command_id;
-   volatile u8      basic_status;
-   volatile u8      extended_status;
+typedef union {
+   struct {
+      volatile u8  reserved;
+      volatile u8  command_id;
+      volatile u8  basic_status;
+      volatile u8  extended_status;
+   } fields;
+
+   volatile u32    value;
 } IPS_STATUS, *PIPS_STATUS;
 
 typedef struct {
@@ -610,7 +672,7 @@
    u8        ucCompression;
    u8        ucNvramType;
    u32       ulNvramSize;
-} IPS_HARDWARE, *PIPS_HARDWARE;  
+} IPS_HARDWARE, *PIPS_HARDWARE;
 
 typedef struct {
    u8             ucLogDriveCount;
@@ -777,8 +839,15 @@
    int   length;
    int   offset;
    int   pos;
+   int   localpos;
 } IPS_INFOSTR;
 
+typedef struct {
+   char *option_name;
+   int  *option_flag;
+   int   option_value;
+} IPS_OPTION;
+
 /*
  * Status Info
  */
@@ -823,6 +892,24 @@
    spinlock_t                 lock;
 } ips_copp_queue_t;
 
+/* forward decl for host structure */
+struct ips_ha;
+
+typedef struct {
+   int  (*reset)(struct ips_ha *);
+   int  (*issue)(struct ips_ha *, struct ips_scb *);
+   int  (*isinit)(struct ips_ha *);
+   int  (*isintr)(struct ips_ha *);
+   int  (*init)(struct ips_ha *);
+   int  (*erasebios)(struct ips_ha *);
+   int  (*programbios)(struct ips_ha *, char *, int);
+   int  (*verifybios)(struct ips_ha *, char *, int);
+   u32  (*statupd)(struct ips_ha *);
+   void (*statinit)(struct ips_ha *);
+   void (*intr)(struct ips_ha *);
+   void (*enableint)(struct ips_ha *);
+} ips_hw_func_t;
+
 typedef struct ips_ha {
    u8                 ha_id[IPS_MAX_CHANNELS+1];
    u32                dcdb_active[IPS_MAX_CHANNELS];
@@ -857,12 +944,18 @@
    u16                reset_count;        /* number of resets           */
    u32                last_ffdc;          /* last time we sent ffdc info*/
    u8                 revision_id;        /* Revision level             */
-
-   #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+   u16                device_id;          /* PCI device ID              */
+   u8                 reserved;
+   u32                mem_addr;           /* Memory mapped address      */
+   u32                io_len;             /* Size of IO Address         */
+   u32                mem_len;            /* Size of memory address     */
+   char              *mem_ptr;            /* Memory mapped Ptr          */
+   char              *ioremap_ptr;        /* ioremapped memory pointer  */
+   ips_hw_func_t      func;               /* hw function pointers       */
+   struct pci_dev    *pcidev;             /* PCI device handle          */
    spinlock_t         scb_lock;
    spinlock_t         copp_lock;
    spinlock_t         ips_lock;
-   #endif
 } ips_ha_t;
 
 typedef void (*ips_scb_callback) (ips_ha_t *, struct ips_scb *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/mac_NCR5380.c linux.18p17/drivers/scsi/mac_NCR5380.c
--- linux.vanilla/drivers/scsi/mac_NCR5380.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/mac_NCR5380.c	Thu Jan  1 01:00:00 1970
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/mac_esp.c linux.18p17/drivers/scsi/mac_esp.c
--- linux.vanilla/drivers/scsi/mac_esp.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/mac_esp.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/mac_esp.h linux.18p17/drivers/scsi/mac_esp.h
--- linux.vanilla/drivers/scsi/mac_esp.h	Fri Apr 21 23:20:05 2000
+++ linux.18p17/drivers/scsi/mac_esp.h	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/mac_scsi.c linux.18p17/drivers/scsi/mac_scsi.c
--- linux.vanilla/drivers/scsi/mac_scsi.c	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/mac_scsi.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/mac_scsi.h linux.18p17/drivers/scsi/mac_scsi.h
--- linux.vanilla/drivers/scsi/mac_scsi.h	Fri Apr 21 12:46:28 2000
+++ linux.18p17/drivers/scsi/mac_scsi.h	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/megaraid.c linux.18p17/drivers/scsi/megaraid.c
--- linux.vanilla/drivers/scsi/megaraid.c	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/megaraid.c	Wed Sep 13 16:18:07 2000
@@ -2,19 +2,19 @@
  *
  *                    Linux MegaRAID device driver
  *
- * Copyright 1999 American Megatrends Inc.
+ * Copyright 2000  American Megatrends Inc.
  *
  *              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.
  *
- * Version : 1.07b
+ * Version : 1b08b
  * 
  * Description: Linux device driver for AMI MegaRAID controller
  *
- * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 490
- * 
+ * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490
+ * 					493.
  * History:
  *
  * Version 0.90:
@@ -119,16 +119,67 @@
  *    Fixed the problem of unnecessary aborts in the abort entry point, which
  *      also enables the driver to handle large amount of I/O requests for
  *      long duration of time.
- *
+ * Version 1.06
+ * 		Intel Release
  * Version 1.07
  *    Removed the usage of uaccess.h file for kernel versions less than
  *    2.0.36, as this file is not present in those versions.
  *
- * Version 1.07b
- *    The MegaRAID 466 cards with 3.00 firmware lockup and seem to very
- *    occasionally hang. We check such cards and report them. You can
- *    get firmware upgrades to flash the board to 3.10 for free.
- *
+ * Version 108
+ *    Modified mega_ioctl so that 40LD megamanager would run
+ *    Made some changes for 2.3.XX compilation , esp wait structures
+ *    Code merge between 1.05 and 1.06 .
+ *    Bug fixed problem with ioctl interface for concurrency between 
+ *    8ld and 40ld firwmare
+ *    Removed the flawed semaphore logic for handling new config command
+ *    Added support for building own scatter / gather list for big user 
+ *    mode buffers
+ *    Added /proc file system support ,so that information is available in 
+ *    human readable format
+ *
+ * Version 1a08
+ *    Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag 
+ *
+ * Version 1b08
+ *    Include file changes.
+ * Version 1b08b
+ *    Change PCI ID value for the 471 card, use #defines when searching
+ *    for megaraid cards.
+ *
+ * Version 1.10
+ *	  
+ *	I) Changes made to make following ioctl commands work in 0x81 interface
+ *		a)DCMD_DELETE_LOGDRV	
+ *		b)DCMD_GET_DISK_CONFIG
+ *		c)DCMD_DELETE_DRIVEGROUP
+ *		d)NC_SUBOP_ENQUIRY3
+ *		e)DCMD_CHANGE_LDNO	
+ *	 	f)DCMD_CHANGE_LOOPID
+ *		g)DCMD_FC_READ_NVRAM_CONFIG
+ *    	h)DCMD_WRITE_CONFIG
+ * 	II) Added mega_build_kernel_sg function
+ *  III)Firmware flashing option
 added
+ *	
+ * Version 1.10a
+ *
+ *	I)Dell updates included in the source code. 
+ * 		Note:	This change is not tested due to the unavailability of IA64 kernel 
+ *	and it is in the #ifdef DELL_MODIFICATION macro which is not defined
+ *				
+ * Version 1.10b
+ *
+ *	I)In IOCTL_CMD_NEW command the wrong way of copying the data 
+ *    to the user address corrected
+ *
+ * Version 1.10c
+ *
+ *	I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes. 
+ *
+ * Version 1.11
+ *	I)  Version number changed from 1.10c to 1.11
+ *  II)	DCMD_WRITE_CONFIG(0x0D) command in the driver changed from 
+ *  	scatter/gather list mode to direct pointer mode.. 
+ * 
  * BUGS:
  *     Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that
  *     fails to detect the controller as a pci device on the system.
@@ -143,8 +194,8 @@
 #define CRLFSTR "\n"
 #define IOCTL_CMD_NEW  0x81
 
-#define MEGARAID_VERSION "v107 (December 22, 1999)"
-
+#define MEGARAID_VERSION "v1.11 (Aug 23, 2000)" 
+#define MEGARAID_IOCTL_VERSION 108
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -163,6 +214,7 @@
 
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
@@ -173,6 +225,8 @@
 #include <linux/wait.h>
 #include <linux/tqueue.h>
 #include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <asm/pgtable.h>
 
 #include <linux/sched.h>
 #include <linux/stat.h>
@@ -181,7 +235,11 @@
 #if LINUX_VERSION_CODE < 0x20100
 #include <linux/bios32.h>
 #else
-#include <asm/spinlock.h>
+#if LINUX_VERSION_CODE < 0x20300
+# include <asm/spinlock.h>
+#else
+# include <linux/spinlock.h>
+#endif
 #endif
 
 #include <asm/io.h>
@@ -229,22 +287,22 @@
 #define COM_BASE 0x2f8
 
 
-u32 RDINDOOR (mega_host_config * megaCfg)
+ulong RDINDOOR (mega_host_config * megaCfg)
 {
   return readl (megaCfg->base + 0x20);
 }
 
-void WRINDOOR (mega_host_config * megaCfg, u32 value)
+void WRINDOOR (mega_host_config * megaCfg, ulong value)
 {
   writel (value, megaCfg->base + 0x20);
 }
 
-u32 RDOUTDOOR (mega_host_config * megaCfg)
+ulong RDOUTDOOR (mega_host_config * megaCfg)
 {
   return readl (megaCfg->base + 0x2C);
 }
 
-void WROUTDOOR (mega_host_config * megaCfg, u32 value)
+void WROUTDOOR (mega_host_config * megaCfg, ulong value)
 {
   writel (value, megaCfg->base + 0x2C);
 }
@@ -276,7 +334,12 @@
 
 
 #if LINUX_VERSION_CODE > 0x020100
+#if LINUX_VERSION_CODE < 0x20300
 #  include <asm/spinlock.h>
+# else
+#  include <linux/spinlock.h>
+#endif
+
 #  include <linux/smp.h>
 #  define cpuid smp_processor_id()
 #  if LINUX_VERSION_CODE < 0x020195
@@ -329,6 +392,19 @@
 static int ser_printk (const char *fmt,...);
 #endif
 
+#ifdef CONFIG_PROC_FS
+#define COPY_BACK if (offset > megaCfg->procidx) { \
+        *eof = TRUE; \
+        megaCfg->procidx = 0; \
+        megaCfg->procbuf[0] = 0; \
+	return 0;} \
+  if ((count + offset) > megaCfg->procidx) { \
+      count = megaCfg->procidx - offset; \
+      *eof = TRUE; } \
+      memcpy(page, &megaCfg->procbuf[offset], count); \
+      megaCfg->procidx = 0; \
+      megaCfg->procbuf[0] = 0; 
+#endif
 /*================================================================
  *
  *                    Global variables
@@ -340,6 +416,8 @@
     XX scsi id on each channel.  Used for Madrona motherboard, where SAF_TE
     processor id cannot be scanned */
 
+
+
 static char *megaraid;
 #if LINUX_VERSION_CODE > 0x20100
 #ifdef MODULE
@@ -350,6 +428,7 @@
 
 static int numCtlrs = 0;
 static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = {0};
+struct proc_dir_entry *mega_proc_dir_entry;
 
 #if DEBUG
 static u32 maxCmdTime = 0;
@@ -362,11 +441,20 @@
 volatile static spinlock_t serial_lock;
 #endif
 
+
+#if LINUX_VERSION_CODE < 0x20300
 struct proc_dir_entry proc_scsi_megaraid =
 {
   PROC_SCSI_MEGARAID, 8, "megaraid",
   S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
+#else
+struct proc_dir_entry *proc_scsi_megaraid;
+#endif
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry proc_root;
+#endif
 
 #if SERDEBUG
 static char strbuf[MAX_SERBUF + 1];
@@ -554,7 +642,7 @@
   return 0;
 }
 
-/* Run through the list of completed requests */
+/* Run through the list of completed requests  and finish it*/
 static void mega_rundoneq (mega_host_config *megaCfg)
 {
   Scsi_Cmnd *SCpnt;
@@ -597,6 +685,8 @@
   mega_passthru *pthru;
   mega_mailbox *mbox;
 
+
+
   if (pScb == NULL) {
 	TRACE(("NULL pScb in mega_cmd_done!"));
 	printk("NULL pScb in mega_cmd_done!");
@@ -628,6 +718,7 @@
 
 if ((SCpnt->cmnd[0] & 0x80) ) {/* i.e. ioctl cmd such as 0x80, 0x81 of megamgr*/
     switch (status) {
+      case 2:
       case 0xF0:
       case 0xF4:
 	SCpnt->result=(DID_BAD_TARGET<<16)|status;
@@ -662,12 +753,7 @@
     break;
   }
  }
-  if ( SCpnt->cmnd[0]!=IOCTL_CMD_NEW ) 
-  /* not IOCTL_CMD_NEW SCB, freeSCB()*/
-  /* For IOCTL_CMD_NEW SCB, delay freeSCB() in megaraid_queue()
-   * after copy data back to user space*/
      mega_freeSCB(megaCfg, pScb);
-
   /* Add Scsi_Command to end of completed queue */
     if( megaCfg->qCompletedH == NULL ) {
       megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt;
@@ -698,8 +784,8 @@
   char islogical;
   char lun = SCpnt->lun;
 
-  if ((SCpnt->cmnd[0] == 0x80)  || (SCpnt->cmnd[0] == IOCTL_CMD_NEW) )  /* ioctl */
-    return mega_ioctl (megaCfg, SCpnt);
+  if ((SCpnt->cmnd[0] == 0x80)  || (SCpnt->cmnd[0] == IOCTL_CMD_NEW) )  
+    return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */
  
   islogical = (SCpnt->channel == megaCfg->host->max_channel);
 
@@ -715,7 +801,7 @@
 	return NULL;
   }
 
-  if ( islogical ) {
+  if (islogical) {
 	lun = (SCpnt->target * 8) + lun;
         if ( lun > FC_MAX_LOGICAL_DRIVES ){
             SCpnt->result = (DID_BAD_TARGET << 16);
@@ -797,6 +883,15 @@
 	  ((u32) SCpnt->cmnd[2] << 8) |
 	  (u32) SCpnt->cmnd[3];
 	mbox->lba &= 0x1FFFFF;
+
+        if (*SCpnt->cmnd == READ_6) { 
+		megaCfg->nReads[(int)lun]++;
+                megaCfg->nReadBlocks[(int)lun] += mbox->numsectors;
+        }
+        else {
+		megaCfg->nWrites[(int)lun]++;
+                megaCfg->nWriteBlocks[(int)lun] += mbox->numsectors;
+       }
       }
 
       /* 10-byte */
@@ -809,6 +904,15 @@
 	  ((u32) SCpnt->cmnd[3] << 16) |
 	  ((u32) SCpnt->cmnd[4] << 8) |
 	  (u32) SCpnt->cmnd[5];
+
+        if (*SCpnt->cmnd == READ_10) { 
+		megaCfg->nReads[(int)lun]++;
+                megaCfg->nReadBlocks[(int)lun] += mbox->numsectors;
+        }
+        else {
+		megaCfg->nWrites[(int)lun]++;
+                megaCfg->nWriteBlocks[(int)lun] += mbox->numsectors;
+        }
       }
 
       /* Calculate Scatter-Gather info */
@@ -865,6 +969,42 @@
   return NULL;
 }
 
+/* Handle Driver Level IOCTLs 
+ * Return value of 0 indicates this function could not handle , so continue
+ * processing 
+*/
+
+static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+  unsigned char *data = (unsigned char *)SCpnt->request_buffer;
+  mega_driver_info driver_info;
+
+  /* If this is not our command dont do anything */
+  if (SCpnt->cmnd[0] != DRIVER_IOCTL_INTERFACE)
+	return 0;
+
+  switch(SCpnt->cmnd[1]) {
+  case GET_DRIVER_INFO:
+                if (SCpnt->request_bufflen < sizeof(driver_info)) {
+			SCpnt->result = DID_BAD_TARGET << 16 ;
+			callDone(SCpnt);
+			return 1;
+		}
+		
+		driver_info.size = sizeof(driver_info) - sizeof(int);
+		driver_info.version = MEGARAID_IOCTL_VERSION;
+		memcpy( data, &driver_info, sizeof(driver_info));
+		break;
+   default:
+		SCpnt->result = DID_BAD_TARGET << 16;
+  }  
+
+ callDone(SCpnt);
+ return 1;
+}
+  
+
+
 /*--------------------------------------------------------------------
  * build RAID commands for controller, passed down through ioctl()
  *--------------------------------------------------------------------*/
@@ -875,9 +1015,9 @@
   mega_mailbox *mailbox;
   mega_passthru *pthru;
   u8 *mboxdata;
-  long seg;
+  long seg, i;
   unsigned char *data = (unsigned char *)SCpnt->request_buffer;
-  int i;
+  
 
   if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) {
     SCpnt->result = (DID_ERROR << 16);
@@ -911,8 +1051,8 @@
 					 (u32 *) & pthru->dataxferaddr,
 					 (u32 *) & pthru->dataxferlen);
 
-    for (i=0;i<(SCpnt->request_bufflen-cdblen-7);i++) {
-       data[i] = data[i+cdblen+7];
+    for (i = 0 ; i < (SCpnt->request_bufflen - cdblen - 7) ; i++) {
+       data[i] = data[i + cdblen + 7];
     }
 
     return pScb;
@@ -928,29 +1068,65 @@
  */
 
   if (SCpnt->cmnd[0] == IOCTL_CMD_NEW) { 
-            /* use external data area for large xfers  */
+      /* use external data area for large xfers  */
      /* If cmnd[0] is set to IOCTL_CMD_NEW then *
       *   cmnd[4..7] = external user buffer     *
       *   cmnd[8..11] = length of buffer        *
       *                                         */
-      char *kern_area;
       char *user_area = *((char **)&SCpnt->cmnd[4]);
       u32 xfer_size = *((u32 *)&SCpnt->cmnd[8]);
-      if (verify_area(VERIFY_READ, user_area, xfer_size)) {
-          printk("megaraid: Got bad user address.\n");
+      switch (data[0])
+      {
+		case FW_FIRE_WRITE:
+		case FW_FIRE_FLASH:
+	     if ((ulong)user_area & (PAGE_SIZE - 1)) {
+          printk("megaraid:user address not aligned on 4K boundary.Error.\n");
+          SCpnt->result = (DID_ERROR << 16);
+          callDone (SCpnt);
+          return NULL;
+	     }
+	     break;
+		default:
+		 break;
+	  }
+      if(!(pScb->buff_ptr = kmalloc(xfer_size, GFP_KERNEL))) {
+          printk("megaraid: Insufficient mem for IOCTL_CMD_NEW.\n");
           SCpnt->result = (DID_ERROR << 16);
           callDone (SCpnt);
           return NULL;
       }
-      kern_area = kmalloc(xfer_size, GFP_ATOMIC | GFP_DMA);
-      if (kern_area == NULL) {
-          printk("megaraid: Couldn't allocate kernel mem.\n");
-	  SCpnt->result = (DID_ERROR << 16);
-	  callDone (SCpnt);
-	  return NULL;
-      }
-      copy_from_user(kern_area,user_area,xfer_size);
-      pScb->kern_area = kern_area;
+
+      copy_from_user(pScb->buff_ptr,user_area,xfer_size);
+      pScb->sgList[0].address = virt_to_bus(pScb->buff_ptr);
+      pScb->sgList[0].length = xfer_size;
+	  pScb->iDataSize = xfer_size;
+      mbox->xferaddr = virt_to_bus(pScb->sgList);
+      mbox->numsgelements = 1;
+	  
+      switch (data[0])
+      {
+		case DCMD_FC_CMD:
+		switch (data[1])
+		{
+  		  case DCMD_FC_READ_NVRAM_CONFIG: 
+      	  case DCMD_GET_DISK_CONFIG:
+          {
+	  		if ((ulong) pScb->buff_ptr & (PAGE_SIZE - 1)) {
+          	  printk("megaraid:user address not sufficient Error.\n");
+              SCpnt->result = (DID_ERROR << 16);
+              callDone (SCpnt);
+              return NULL;
+	        }
+			//building SG list
+     		mega_build_kernel_sg(pScb->buff_ptr, xfer_size, pScb, mbox);
+	        break;
+	    }
+	    default:
+		  break;
+	  }//switch (data[1])
+	  break;
+    }
+
   }
 #endif
 
@@ -961,16 +1137,59 @@
   mbox->logdrv = data[4];
 
   if(SCpnt->cmnd[0] == IOCTL_CMD_NEW) {
-      if(data[0]==DCMD_FC_CMD){ /*i.e. 0xA1, then override some mbox data */
+      switch (data[0]) {
+        case FW_FIRE_WRITE:
+	      mbox->cmd = FW_FIRE_WRITE;
+	      mbox->channel = data[1]; /* Current Block Number */
+          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	      mbox->numsgelements = 0;
+	      break;
+		case FW_FIRE_FLASH:
+	      mbox->cmd = FW_FIRE_FLASH;
+	      mbox->channel = data[1] | 0x80 ; /* Origin */
+          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	      mbox->numsgelements = 0;
+	      break;
+		case DCMD_FC_CMD:
           *(mboxdata+0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD*/
-          *(mboxdata+2) = data[2]; /*sub command*/
-          *(mboxdata+3) = 0;       /*number of elements in SG list*/
-          mbox->xferaddr           /*i.e. mboxdata byte 0x8 to 0xb*/
-                        = virt_to_bus(pScb->kern_area);
-      }
-      else{
-         mbox->xferaddr = virt_to_bus(pScb->kern_area);
-         mbox->numsgelements = 0;
+          *(mboxdata+2) = data[1]; /*sub command*/
+	      switch (data[1]) 
+	      {
+			case DCMD_FC_READ_NVRAM_CONFIG: 
+              *(mboxdata+3) = mbox->numsgelements;       /*number of elements in SG list*/
+	          break;
+    		case DCMD_WRITE_CONFIG:
+	          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	          mbox->numsgelements = 0;
+			  break;
+        	case DCMD_GET_DISK_CONFIG:
+          	  *(mboxdata+3) = data[2];       /*number of elements in SG list*/
+          	  *(mboxdata+4) = mbox->numsgelements;       /*number of elements in SG list*/
+			  break;
+        	case DCMD_DELETE_LOGDRV:	
+        	case DCMD_DELETE_DRIVEGROUP:
+    		case NC_SUBOP_ENQUIRY3:
+              *(mboxdata+3) = data[2]; 
+	          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	          mbox->numsgelements = 0;
+			  break;
+        	case DCMD_CHANGE_LDNO:	
+        	case DCMD_CHANGE_LOOPID:
+              *(mboxdata+3) = data[2]; 
+              *(mboxdata+4) = data[3]; 
+	          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	    	  mbox->numsgelements = 0;
+			  break;
+			default:
+	          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+	          mbox->numsgelements = 0;
+			  break;
+	      }//switch
+		  break;
+		default:
+          mbox->xferaddr = virt_to_bus(pScb->buff_ptr);
+          mbox->numsgelements = 0;
+		  break;
       }
   } 
   else {
@@ -987,6 +1206,37 @@
   return (pScb);
 }
 
+void mega_build_kernel_sg(char *barea, ulong xfersize, mega_scb *pScb,
+			 mega_ioctl_mbox *mbox) 
+{
+    ulong i, buffer_area, len, end, end_page, x, idx = 0;
+
+    buffer_area = (ulong)barea;
+    i = buffer_area;
+    end = buffer_area + xfersize;
+    end_page = (end) & ~(PAGE_SIZE - 1);
+
+    do {
+    	len =  PAGE_SIZE - (i % PAGE_SIZE);
+    	x = pScb->sgList[idx].address = virt_to_bus((volatile void *)i);
+    	pScb->sgList[idx].length = len;
+        i += len;
+        idx++;
+	} while (i < end_page);
+ 
+    if ((end - i)< 0) {
+          printk("megaraid:Error in user address\n");
+    }       
+	
+	if (end - i) {
+    	pScb->sgList[idx].address = virt_to_bus((volatile void *)i);
+    	pScb->sgList[idx].length = end - i;
+		idx++; 
+	}
+	mbox->xferaddr = virt_to_bus(pScb->sgList);
+	mbox->numsgelements = idx;
+}
+
 #if DEBUG
 static void showMbox(mega_scb *pScb)
 {
@@ -1065,7 +1315,7 @@
 #if LINUX_VERSION_CODE >= 0x20100
     IO_LOCK;
 #endif
-
+    megaCfg->nInterrupts++;
     qCnt = 0xff;
     while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) 
       ;
@@ -1135,17 +1385,21 @@
           continue;
 	}
 
-        if (*(pScb->SCpnt->cmnd)==IOCTL_CMD_NEW) 
-        {    /* external user buffer */
-           up(&pScb->sem);
-        }
-	/* Mark command as completed */
-	mega_cmd_done(megaCfg, pScb, qStatus);
-
+	/* We don't want the ISR routine to touch IOCTL_CMD_NEW commands, so
+	 * don't mark them as complete, instead we pop their semaphore so
+	 * that the queue routine can finish them off
+	 */
+	if(pScb->SCpnt->cmnd[0] == IOCTL_CMD_NEW) {
+		/* save the status byte for the queue routine to use */
+		pScb->SCpnt->result = qStatus;
+		up(&pScb->ioctl_sem);
+	} else {
+		/* Mark command as completed */
+		mega_cmd_done(megaCfg, pScb, qStatus);
+	}
       }
       else {
         printk("megaraid: wrong cmd id completed from firmware:id=%x\n",sIdx);
-	for(;;);
       }
     }
 
@@ -1200,19 +1454,23 @@
 	      mega_scb * pScb,
 	      int intr)
 {
-  mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
+  volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
   u_char byte;
-  u32 cmdDone;
+
+#ifdef __LP64__
+  u64 phys_mbox;
+#else
   u32 phys_mbox;
+#endif
   u8 retval=-1;
 
-  mboxData[0x1] = (pScb ? pScb->idx + 1: 0x0);   /* Set cmdid */
+  mboxData[0x1] = (pScb ? pScb->idx + 1: 0xFE);   /* Set cmdid */
   mboxData[0xF] = 1;		/* Set busy */
 
   phys_mbox = virt_to_bus (megaCfg->mbox);
 
 #if DEBUG
-  showMbox(pScb);
+  ShowMbox(pScb);
 #endif
 
   /* Wait until mailbox is free */
@@ -1237,7 +1495,7 @@
 
   /* Copy mailbox data into host structure */
   megaCfg->mbox64->xferSegment = 0;
-  memcpy (mbox, mboxData, 16);
+  memcpy ((char *)mbox, mboxData, 16);
 
   /* Kick IO */
   if (intr) {
@@ -1261,10 +1519,18 @@
     if (megaCfg->flag & BOARD_QUARTZ) {
       mbox->mraid_poll = 0;
       mbox->mraid_ack = 0;
+      mbox->numstatus = 0xFF;
+      mbox->status = 0xFF;
       WRINDOOR (megaCfg, phys_mbox | 0x1);
 
-      while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234);
-      WROUTDOOR (megaCfg, cmdDone);
+     while (mbox->numstatus == 0xFF);
+     while (mbox->status == 0xFF);
+     while (mbox->mraid_poll != 0x77);
+     mbox->mraid_poll = 0;
+     mbox->mraid_ack = 0x77;
+ 
+    /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234);
+      WROUTDOOR (megaCfg, cmdDone);*/
 
       if (pScb) {
 	mega_cmd_done (megaCfg, pScb, mbox->status);
@@ -1361,9 +1627,15 @@
 {
   /* align on 16-byte boundry */
   megaCfg->mbox = &megaCfg->mailbox64.mailbox;
-  megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xfffffff0);
+#ifdef __LP64__
+  megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ( (ulong)(-1) ^ 0x0F)  );
   megaCfg->mbox64 = (mega_mailbox64 *) (megaCfg->mbox - 4);
-  paddr = (paddr + 4 + 16) & 0xfffffff0;
+  paddr = (paddr + 4 + 16) & ( (u64)(-1) ^ 0x0F );
+#else
+  megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0);
+  megaCfg->mbox64 = (mega_mailbox64 *) (megaCfg->mbox - 4);
+  paddr = (paddr + 4 + 16) & 0xFFFFFFF0;
+#endif
 
   /* Register mailbox area with the firmware */
   if (!(megaCfg->flag & BOARD_QUARTZ)) {
@@ -1509,10 +1781,10 @@
 	   megaCfg->productInfo.BiosVer[2] >> 8,
 	   megaCfg->productInfo.BiosVer[2] & 0x0f);
 #else
-  memcpy (megaCfg->fwVer, megaCfg->productInfo.FwVer, 4);
+  memcpy (megaCfg->fwVer, (char *)megaCfg->productInfo.FwVer, 4);
   megaCfg->fwVer[4] = 0;
 
-  memcpy (megaCfg->biosVer, megaCfg->productInfo.BiosVer, 4);
+  memcpy (megaCfg->biosVer, (char *)megaCfg->productInfo.BiosVer, 4);
   megaCfg->biosVer[4] = 0;
 #endif
 
@@ -1537,6 +1809,7 @@
 		    int length, int host_no, int inout)
 {
   *start = buffer;
+  
   return 0;
 }
 
@@ -1544,37 +1817,83 @@
 	  u16 pciVendor, u16 pciDev,
 	  long flag)
 {
-  mega_host_config *megaCfg;
-  struct Scsi_Host *host;
+  mega_host_config *megaCfg = NULL;
+  struct Scsi_Host *host = NULL;
   u_char pciBus, pciDevFun, megaIrq;
+
+#ifdef __LP64__
+  u64 megaBase;
+#else
   u32 megaBase;
+#endif
+
   u16 pciIdx = 0;
   u16 numFound = 0;
 
 #if LINUX_VERSION_CODE < 0x20100
   while (!pcibios_find_device (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) {
 #else
-  
+#if LINUX_VERSION_CODE > 0x20300
+  struct pci_dev *pdev = NULL;
+#else  
   struct pci_dev *pdev = pci_devices;
+#endif
   
   while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
+
+#ifdef DELL_MODIFICATION 
+    if (pci_enable_device(pdev))
+    	continue;
+#endif	
     pciBus = pdev->bus->number;
     pciDevFun = pdev->devfn;
 #endif
     if ((flag & BOARD_QUARTZ) && (skip_id == -1)) {
       u16 magic;
-#if LINUX_VERSION_CODE < 0x20100
       pcibios_read_config_word (pciBus, pciDevFun,
 				PCI_CONF_AMISIG,
 				&magic);
-#else
-      pci_read_config_word (pdev, PCI_CONF_AMISIG, &magic);
-#endif
       if ((magic != AMI_SIGNATURE) && (magic != AMI_SIGNATURE_471) ){
         pciIdx++;
 	continue;		/* not an AMI board */
       }
     }
+
+/* Hmmm...Should we not make this more modularized so that in future we dont add
+   for each firmware */
+	
+	if (flag & BOARD_QUARTZ) {
+      /* Check to see if this is a Dell PERC RAID controller model 466 */
+			u16 subsysid, subsysvid;
+#if LINUX_VERSION_CODE < 0x20100
+			pcibios_read_config_word (pciBus, pciDevFun,
+							PCI_SUBSYSTEM_VENDOR_ID,
+							&subsysvid);
+			pcibios_read_config_word (pciBus, pciDevFun,
+						PCI_SUBSYSTEM_ID,
+						&subsysid);
+#else
+			pci_read_config_word (pdev,
+						 PCI_SUBSYSTEM_VENDOR_ID,
+						 &subsysvid);
+			pci_read_config_word (pdev, 
+						PCI_SUBSYSTEM_ID,
+						 &subsysid);
+#endif
+			if ( (subsysid == 0x1111) && (subsysvid == 0x1111) &&
+				(!strcmp(megaCfg->fwVer,"3.00") || 						!strcmp(megaCfg->fwVer,"3.01"))) {
+				printk(KERN_WARNING
+				"megaraid: Your card is a Dell PERC 2/SC RAID controller with firmware\n"
+				"megaraid: 3.00 or 3.01.  This driver is known to have corruption issues\n"
+				"megaraid: with those firmware versions on this specific card.  In order\n"
+				"megaraid: to protect your data, please upgrade your firmware to version\n"
+				"megaraid: 3.10 or later, available from the Dell Technical Support web\n"
+				"megaraid: site at\n"
+				"http://support.dell.com/us/en/filelib/download/index.asp?fileid=2940\n");
+			continue;
+			}
+		}
+
     printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n",
 	    pciVendor,
 	    pciDev,
@@ -1590,21 +1909,21 @@
     pcibios_read_config_byte (pciBus, pciDevFun,
 			      PCI_INTERRUPT_LINE,
 			      &megaIrq);
-#else
+#elif LINUX_VERSION_CODE < 0x20300
     megaBase = pdev->base_address[0];
     megaIrq  = pdev->irq;
+#else
+
+    megaBase = pci_resource_start (pdev, 0);
+    megaIrq  = pdev->irq;
 #endif
-    pciIdx++;
 
-    if (flag & BOARD_QUARTZ) {
+    pciIdx++;
 
-      megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
-      megaBase = (long) ioremap (megaBase, 128);
-    }
-    else {
-      megaBase &= PCI_BASE_ADDRESS_IO_MASK;
-      megaBase += 0x10;
-    }
+    if (flag & BOARD_QUARTZ)
+       megaBase = (long) ioremap (megaBase, 128);
+    else
+       megaBase += 0x10;
 
     /* Initialize SCSI Host structure */
     host = scsi_register (pHostTmpl, sizeof (mega_host_config));
@@ -1632,6 +1951,7 @@
     megaCfg->host->n_io_port = 16;
     megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
     megaCtlrs[numCtlrs++] = megaCfg; 
+
     if (flag != BOARD_QUARTZ) {
       /* Request our IO Range */
       if (check_region (megaBase, 16)) {
@@ -1654,42 +1974,9 @@
     mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg->mailbox64));
     mega_i_query_adapter (megaCfg);
    
-    if (flag == BOARD_QUARTZ) {
-      /* Check to see if this is a Dell PERC RAID controller model 466 */
-      u16 subsysid, subsysvid;
-#if LINUX_VERSION_CODE < 0x20100
-      pcibios_read_config_word (pciBus, pciDevFun,
-				PCI_SUBSYSTEM_VENDOR_ID,
-				&subsysvid);
-      pcibios_read_config_word (pciBus, pciDevFun,
-				PCI_SUBSYSTEM_ID,
-				&subsysid);
-#else
-      pci_read_config_word (pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid);
-      pci_read_config_word (pdev, PCI_SUBSYSTEM_ID, &subsysid);
-#endif
-      if ( (subsysid == 0x1111) && (subsysvid == 0x1111) &&
-           (!strcmp(megaCfg->fwVer,"3.00") || !strcmp(megaCfg->fwVer,"3.01"))) {
-	printk(KERN_WARNING
-"megaraid: Your card is a Dell PERC 2/SC RAID controller with firmware\n"
-"megaraid: 3.00 or 3.01.  This driver is known to have corruption issues\n"
-"megaraid: with those firmware versions on this specific card.  In order\n"
-"megaraid: to protect your data, please upgrade your firmware to version\n"
-"megaraid: 3.10 or later, available from the Dell Technical Support web\n"
-"megaraid: site at\n"
-"http://support.dell.com/us/en/filelib/download/index.asp?fileid=2940\n");
-	megaraid_release (host);
-#ifdef MODULE	
-	continue;
-#else
-	while(1) schedule_timeout(1 * HZ);
-#endif	
-      }
-    }
-
     /* Initialize SCBs */
     if (mega_initSCB (megaCfg)) {
-      megaraid_release (host);
+      scsi_unregister (host);
       continue;
     }
 
@@ -1703,9 +1990,13 @@
  *---------------------------------------------------------*/
 int megaraid_detect (Scsi_Host_Template * pHostTmpl)
 {
-  int count = 0;
+  int ctlridx = 0, count = 0;
 
+#if LINUX_VERSION_CODE < 0x20300
   pHostTmpl->proc_dir = &proc_scsi_megaraid;
+#else
+  pHostTmpl->proc_name = "megaraid";
+#endif
 
 #if LINUX_VERSION_CODE < 0x20100
   if (!pcibios_present ()) {
@@ -1726,9 +2017,31 @@
 
   printk ("megaraid: " MEGARAID_VERSION CRLFSTR);
 
-  count += mega_findCard (pHostTmpl, 0x101E, 0x9010, 0);
-  count += mega_findCard (pHostTmpl, 0x101E, 0x9060, 0);
-  count += mega_findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
+  count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+			  PCI_DEVICE_ID_AMI_MEGARAID, 0);
+  count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+			  PCI_DEVICE_ID_AMI_MEGARAID2, 0);
+  count += mega_findCard (pHostTmpl, 0x8086,
+			  PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ);
+  count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+			  PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ);
+
+#ifdef CONFIG_PROC_FS
+  if (count) {
+#if LINUX_VERSION_CODE > 0x20300 
+        mega_proc_dir_entry = proc_mkdir("megaraid", &proc_root);
+#else
+        mega_proc_dir_entry = create_proc_entry("megaraid", 
+						S_IFDIR | S_IRUGO | S_IXUGO,
+						&proc_root);    
+#endif
+        if (!mega_proc_dir_entry)
+        	printk("megaraid: failed to create megaraid root\n");
+        else
+  		for (ctlridx = 0; ctlridx < count ; ctlridx++) 
+			mega_create_proc_entry(ctlridx, mega_proc_dir_entry);
+  }
+#endif
 
   return count;
 }
@@ -1741,6 +2054,7 @@
   mega_host_config *megaCfg;
   mega_mailbox *mbox;
   u_char mboxData[16];
+  int i;
 
   megaCfg = (mega_host_config *) pSHost->hostdata;
   mbox = (mega_mailbox *) mboxData;
@@ -1766,6 +2080,22 @@
   mega_freeSgList(megaCfg);
   scsi_unregister (pSHost);
 
+#ifdef CONFIG_PROC_FS
+  if (megaCfg->controller_proc_dir_entry) {
+	  remove_proc_entry("stat",megaCfg->controller_proc_dir_entry);
+	  remove_proc_entry("status", megaCfg->controller_proc_dir_entry);
+	  remove_proc_entry("config", megaCfg->controller_proc_dir_entry);
+	  remove_proc_entry("mailbox", megaCfg->controller_proc_dir_entry);
+          for (i = 0; i < numCtlrs; i++) {
+                char buf[12];
+                memset(buf,0,12);
+		sprintf(buf,"%d",i);
+          	remove_proc_entry(buf,mega_proc_dir_entry);
+	 }
+         remove_proc_entry("megaraid", &proc_root);
+  }  
+#endif
+
   return 0;
 }
 
@@ -1818,6 +2148,7 @@
   DRIVER_LOCK_T
   mega_host_config *megaCfg;
   mega_scb *pScb;
+  char *user_area = NULL;
 
   megaCfg = (mega_host_config *) SCpnt->host->hostdata;
   DRIVER_LOCK(megaCfg);
@@ -1835,6 +2166,9 @@
 
   SCpnt->scsi_done = pktComp;
 
+  if (mega_driver_ioctl(megaCfg, SCpnt))
+	return 0;
+
   /* If driver in abort or reset.. cancel this command */
   if (megaCfg->flag & IN_ABORT) {
     SCpnt->result = (DID_ABORT << 16);
@@ -1886,31 +2220,31 @@
     megaCfg->qPcnt++;
 
       mega_runpendq(megaCfg);
+  if(pScb->SCpnt->cmnd[0] == IOCTL_CMD_NEW) {
+    pScb->ioctl_sem = MUTEX_LOCKED;
+    spin_unlock_irq(&io_request_lock);
+    down(&pScb->ioctl_sem);
+    user_area = *((char **)&pScb->SCpnt->cmnd[4]);
+    if(copy_to_user(user_area,pScb->buff_ptr,pScb->iDataSize)) {
+       printk("megaraid: Error copying ioctl return value to user buffer.\n");
+       pScb->SCpnt->result = (DID_ERROR << 16);
+    }
+    spin_lock_irq(&io_request_lock);
+    DRIVER_LOCK(megaCfg);
+    kfree(pScb->buff_ptr);
+    pScb->buff_ptr = NULL;
+    mega_cmd_done(megaCfg, pScb, pScb->SCpnt->result);
+    mega_rundoneq(megaCfg);
+    mega_runpendq(megaCfg);
+    DRIVER_UNLOCK(megaCfg);
+  }
 
-#if LINUX_VERSION_CODE > 0x020024
-    if ( SCpnt->cmnd[0]==IOCTL_CMD_NEW )
-    {  /* user data from external user buffer */
-          char *user_area;
-          u32  xfer_size;
-
-          pScb->sem=MUTEX_LOCKED;
-          down(&pScb->sem);
-
-          user_area = *((char **)&pScb->SCpnt->cmnd[4]);
-          xfer_size = *((u32 *)&pScb->SCpnt->cmnd[8]);
-
-          copy_to_user(user_area,pScb->kern_area,xfer_size);
-
-          kfree(pScb->kern_area);
+  megaCfg->flag &= ~IN_QUEUE;
 
-          mega_freeSCB(megaCfg, pScb);
-    }
-#endif
   }
 
-  megaCfg->flag &= ~IN_QUEUE;
-  DRIVER_UNLOCK(megaCfg);
 
+  DRIVER_UNLOCK(megaCfg);
   return 0;
 }
 
@@ -1919,7 +2253,13 @@
  *----------------------------------------------------------------------*/
 volatile static int internal_done_flag = 0;
 volatile static int internal_done_errcode = 0;
-static struct wait_queue *internal_wait = NULL;
+
+#if LINUX_VERSION_CODE < 0x20300
+	static struct wait_queue *internal_wait = NULL;
+#else
+	static DECLARE_WAIT_QUEUE_HEAD(internal_wait);
+#endif
+
 
 static void internal_done (Scsi_Cmnd * SCpnt)
 {
@@ -2079,6 +2419,221 @@
   mega_rundoneq(megaCfg);
   return rc;
 }
+
+
+#ifdef CONFIG_PROC_FS
+/* Following code handles /proc fs  */
+static int proc_printf (mega_host_config *megaCfg, const char *fmt,...)
+{
+  va_list args;
+  int i;
+  
+  if (megaCfg->procidx > PROCBUFSIZE)
+     return 0;
+
+  va_start (args, fmt);
+  i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args);
+  va_end (args);
+  
+  megaCfg->procidx += i;
+  return i;
+}
+
+
+static int proc_read_config(char *page, char **start, off_t offset,
+				int count, int *eof, void *data) 
+{
+
+   mega_host_config *megaCfg = (mega_host_config *)data; 
+  
+   *start = page;
+
+   if (megaCfg->productInfo.ProductName[0] != 0)
+  	 proc_printf(megaCfg,"%s\n",megaCfg->productInfo.ProductName);
+	
+   proc_printf(megaCfg,"Controller Type: ");
+
+   if (megaCfg->flag & BOARD_QUARTZ)
+ 	proc_printf(megaCfg,"438/466/467/471/493\n");
+   else
+	proc_printf(megaCfg,"418/428/434\n");
+
+   if (megaCfg->flag & BOARD_40LD)
+       proc_printf(megaCfg,"Controller Supports 40 Logical Drives\n");
+
+   proc_printf(megaCfg,"Base = %08x, Irq = %d, ",megaCfg->base,
+						 megaCfg->host->irq);
+
+   proc_printf(megaCfg, "Logical Drives = %d, Channels = %d\n",
+			megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent);
+
+   proc_printf(megaCfg, "Version =%s:%s, DRAM = %dMb\n",
+			megaCfg->fwVer,megaCfg->biosVer, 
+			megaCfg->productInfo.DramSize);
+
+   proc_printf(megaCfg,"Controller Queue Depth = %d, Driver Queue Depth = %d\n",
+		megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds);   
+   COPY_BACK;
+   return count;
+}
+
+
+
+static int proc_read_stat(char *page, char **start, off_t offset,
+				int count, int *eof, void *data) 
+{
+   int i;
+   mega_host_config *megaCfg = (mega_host_config *)data; 
+
+   *start = page;
+
+   proc_printf(megaCfg,"Statistical Information for this controller\n");
+   proc_printf(megaCfg,"Interrupts Collected = %d\n", megaCfg->nInterrupts);
+
+   for (i = 0; i < megaCfg->numldrv; i++) {
+	proc_printf(megaCfg,"Logical Drive %d:\n", i);
+        proc_printf(megaCfg,"\tReads Issued = %10d, Writes Issued = %10d\n",
+			megaCfg->nReads[i], megaCfg->nWrites[i]);
+        
+        proc_printf(megaCfg,"\tSectors Read = %10d, Sectors Written = %10d\n\n",				megaCfg->nReadBlocks[i],
+				megaCfg->nWriteBlocks[i]);
+   }
+
+   COPY_BACK;
+   return count;
+}
+
+        
+
+static int proc_read_status(char *page, char **start, off_t offset,
+				int count, int *eof, void *data) 
+{
+ mega_host_config *megaCfg = (mega_host_config *)data;
+ *start = page;
+
+ proc_printf(megaCfg,"TBD\n");
+ COPY_BACK;
+ return count;
+}
+
+
+static int proc_read_mbox(char *page, char **start, off_t offset,
+				int count, int *eof, void *data) 
+{
+// int i;
+// char *dta = NULL;
+ mega_host_config *megaCfg = (mega_host_config *)data;
+ volatile mega_mailbox *mbox = megaCfg->mbox;
+
+ *start = page;
+
+ proc_printf(megaCfg,"Contents of Mail Box Structure\n");
+ proc_printf(megaCfg,"  Fw Command   = 0x%02x\n", mbox->cmd);
+ proc_printf(megaCfg,"  Cmd Sequence = 0x%02x\n", mbox->cmdid);
+ proc_printf(megaCfg,"  No of Sectors= %04d\n", mbox->numsectors);
+ proc_printf(megaCfg,"  LBA          = 0x%02x\n", mbox->lba);
+ proc_printf(megaCfg,"  DTA          = 0x%08x\n", mbox->xferaddr);
+ proc_printf(megaCfg,"  Logical Drive= 0x%02x\n", mbox->logdrv);
+ proc_printf(megaCfg,"  No of SG Elmt= 0x%02x\n", mbox->numsgelements);
+ proc_printf(megaCfg,"  Busy         = %01x\n", mbox->busy); 
+ proc_printf(megaCfg,"  Status       = 0x%02x\n", mbox->status);
+
+/* proc_printf(megaCfg, "Dump of MailBox\n");
+ for (i = 0; i < 16; i++)
+	proc_printf(megaCfg, "%02x ",*(mbox + i));
+
+proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus);
+
+ for (i = 0; i < 46; i++) {
+	proc_printf(megaCfg,"%02d ",*(mbox + 16 + i));
+        if (i%16)
+		proc_printf(megaCfg,"\n");
+}
+
+if (!mbox->numsgelements) { 
+	dta = phys_to_virt(mbox->xferaddr);
+	for (i = 0; i < mbox->numsgelements; i++) 
+		if (dta) {
+			proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i));			proc_printf(megaCfg,"Length = %08x\n", 
+				(ulong)*(dta + i + 4));
+		}
+}*/
+ COPY_BACK;
+ return count;
+}
+
+
+#if LINUX_VERSION_CODE > 0x20300
+#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \
+					 S_IRUSR | S_IFREG,\
+					 controller_proc_dir_entry,\
+					 fxn, megaCfg)
+#else 
+#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg)
+
+struct proc_dir_entry *create_proc_read_entry(const char *string,
+			int mode,
+			struct proc_dir_entry *parent,
+ 			read_proc_t *fxn, 
+			mega_host_config *megaCfg) 
+{
+	struct proc_dir_entry *temp = NULL;
+	
+        temp = kmalloc(sizeof(struct proc_dir_entry),
+			GFP_KERNEL);
+        if (!temp)
+		return NULL;
+	memset(temp, 0, sizeof(struct proc_dir_entry));
+
+        if ( (temp->name = kmalloc(strlen(string) + 1, GFP_KERNEL)) == NULL) {
+		kfree(temp);
+		return NULL;
+	}
+
+	strcpy((char *)temp->name, string);
+	temp->namelen = strlen(string);
+        temp->mode = mode ; /*S_IFREG | S_IRUSR*/;
+	temp->data = (void *)megaCfg;
+        temp->read_proc = fxn;
+	proc_register(parent, temp);
+        return temp;
+}
+#endif
+
+		
+	 
+void mega_create_proc_entry(int index, struct proc_dir_entry *parent)
+{
+    u_char string[64] = {0};
+    mega_host_config *megaCfg = megaCtlrs[index];
+    struct proc_dir_entry *controller_proc_dir_entry = NULL;
+
+    sprintf(string,"%d", index);
+
+#if LINUX_VERSION_CODE > 0x20300
+    controller_proc_dir_entry = 
+    megaCfg->controller_proc_dir_entry = 
+		proc_mkdir(string, parent);
+#else
+    controller_proc_dir_entry = 
+    megaCfg->controller_proc_dir_entry = 
+        create_proc_entry(string,S_IFDIR | S_IRUGO | S_IXUGO, parent);
+#endif    
+
+    if (!controller_proc_dir_entry)
+   	printk("\nmegaraid: proc_mkdir failed\n");
+    else 
+    { 	
+	megaCfg->proc_read = CREATE_READ_PROC("config", proc_read_config);
+    	megaCfg->proc_status = CREATE_READ_PROC("status", proc_read_status);
+    	megaCfg->proc_stat = CREATE_READ_PROC("stat", proc_read_stat);
+    	megaCfg->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox);
+    }
+   
+}
+#endif /* CONFIG_PROC_FS */
+ 
+	
 
 /*-------------------------------------------------------------
  * Return the disk geometry for a particular disk
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/megaraid.h linux.18p17/drivers/scsi/megaraid.h
--- linux.vanilla/drivers/scsi/megaraid.h	Fri Jun 23 00:51:16 2000
+++ linux.18p17/drivers/scsi/megaraid.h	Thu Oct 19 01:37:49 2000
@@ -20,6 +20,11 @@
 #define SCB_ABORTED  0x5
 #define SCB_RESET    0x6
 
+/* IOCTL DRIVER INFO */
+#define DRIVER_IOCTL_INTERFACE 0x82
+/* Methods */
+#define GET_DRIVER_INFO 1
+
 #define MEGA_CMD_TIMEOUT        10
 
 /* Feel free to fiddle with these.. max values are:
@@ -94,13 +99,41 @@
 #define ENABLE_INTR(base)     WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE)
 #define DISABLE_INTR(base)    WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE)
 
+/* Special Adapter Commands */
+#define FW_FIRE_WRITE 	0x2C
+#define FW_FIRE_FLASH  	0x2D
+
+#define FC_NEW_CONFIG               0xA1
+#define DCMD_FC_CMD                 0xA1
+    #define DCMD_FC_PROCEED		0x02
+    #define DCMD_DELETE_LOGDRV		0x03
+    #define DCMD_FC_READ_NVRAM_CONFIG	0x04 
+    #define DCMD_FC_READ_FINAL_CONFIG	0x05
+    #define DCMD_GET_DISK_CONFIG	0x06
+    #define DCMD_CHANGE_LDNO		0x07
+    #define DCMD_COMPACT_CONFIG		0x08
+    #define DCMD_DELETE_DRIVEGROUP	0x09
+    #define DCMD_GET_LOOPID_INFO	0x0A
+    #define DCMD_CHANGE_LOOPID		0x0B
+    #define DCMD_GET_NUM_SCSI_CHANS	0x0C
+    #define DCMD_WRITE_CONFIG		0x0D /* writes 40-ld config */
+    #define NC_SUBOP_PRODUCT_INFO       0x0E
+    #define NC_SUBOP_ENQUIRY3           0x0F
+	#define ENQ3_GET_SOLICITED_NOTIFY_ONLY  0x01
+	#define ENQ3_GET_SOLICITED_FULL         0x02
+	#define ENQ3_GET_UNSOLICITED            0x03
+
 /* Define AMI's PCI codes */
 #undef PCI_VENDOR_ID_AMI
 #undef PCI_DEVICE_ID_AMI_MEGARAID
+#undef PCI_DEVICE_ID_AMI_MEGARAID2
+#undef PCI_DEVICE_ID_AMI_MEGARAID3
 
 #ifndef PCI_VENDOR_ID_AMI
 #define PCI_VENDOR_ID_AMI          0x101E
 #define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
+#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
 #endif
 
 #define PCI_CONF_BASE_ADDR_OFFSET  0x10
@@ -526,8 +559,8 @@
     /* 0x10 */ u8 numstatus;
     /* 0x11 */ u8 status;
     /* 0x12 */ u8 completed[46];
-    u8 mraid_poll;
-    u8 mraid_ack;
+    volatile u8 mraid_poll;
+    volatile u8 mraid_ack;
     u8 pad[16]; /* for alignment purposes */
 }__attribute__((packed));
 typedef struct _mega_mailbox mega_mailbox;
@@ -572,9 +605,9 @@
     mega_passthru  pthru;
     Scsi_Cmnd     *SCpnt;
     mega_sglist   *sgList;
-    char          *kern_area;  /* Only used for large ioctl xfers */
-    struct wait_queue  *ioctl_wait;
-    struct semaphore   sem;
+    struct semaphore  ioctl_sem;
+    void	  *buff_ptr;
+	u32			iDataSize;
     mega_scb      *next;
 };
 
@@ -582,7 +615,12 @@
 typedef struct _mega_host_config {
     u8 numldrv;
     u32 flag;
+
+#ifdef __LP64__
+    u64 base;
+#else
     u32 base;
+#endif
  
     mega_scb *qFreeH;
     mega_scb *qFreeT;
@@ -596,8 +634,10 @@
     u32 qCcnt;
 
     u32 nReads[FC_MAX_LOGICAL_DRIVES];
+    u32 nReadBlocks[FC_MAX_LOGICAL_DRIVES];
     u32 nWrites[FC_MAX_LOGICAL_DRIVES];
-
+    u32 nWriteBlocks[FC_MAX_LOGICAL_DRIVES];
+    u32 nInterrupts;
     /* Host adapter parameters */
     u8 fwVer[7];
     u8 biosVer[7];
@@ -620,9 +660,22 @@
 
     u8 max_cmds;
     mega_scb scbList[MAX_COMMANDS];
+#define PROCBUFSIZE 4096
+    char procbuf[PROCBUFSIZE];
+    int   procidx;
+    struct proc_dir_entry *controller_proc_dir_entry;
+    struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox;
 } mega_host_config;
 
+typedef struct _driver_info {
+	int size;
+	ulong version;
+} mega_driver_info;
+
+
+#if LINUX_VERSION_CODE < 0x20300
 extern struct proc_dir_entry proc_scsi_megaraid;
+#endif
 
 const char *megaraid_info(struct Scsi_Host *);
 int megaraid_detect(Scsi_Host_Template *);
@@ -635,4 +688,6 @@
 int megaraid_proc_info(char *buffer, char **start, off_t offset,
 		       int length, int hostno, int inout);
 
+void mega_build_kernel_sg(char *, ulong , mega_scb *, mega_ioctl_mbox *); 
+void mega_create_proc_entry(int index, struct proc_dir_entry *);
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ncr53c8xx.c linux.18p17/drivers/scsi/ncr53c8xx.c
--- linux.vanilla/drivers/scsi/ncr53c8xx.c	Fri Apr 21 12:46:30 2000
+++ linux.18p17/drivers/scsi/ncr53c8xx.c	Sat Sep  2 19:38:56 2000
@@ -73,8 +73,6 @@
 */
 
 /*
-**	July 20 1999, version 3.2a-2
-**
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
 **	    Wide negotiation        (depends on the NCR Chip)
@@ -83,7 +81,7 @@
 **	    Parity checking
 **	    Etc...
 **
-**	Supported NCR chips:
+**	Supported NCR/SYMBIOS chips:
 **		53C810		(8 bits, Fast SCSI-2, no rom BIOS) 
 **		53C815		(8 bits, Fast SCSI-2, on board rom BIOS)
 **		53C820		(Wide,   Fast SCSI-2, no rom BIOS)
@@ -93,7 +91,8 @@
 **		53C895		(Wide,   Fast 40,     on board rom BIOS)
 **		53C895A		(Wide,   Fast 40,     on board rom BIOS)
 **		53C896		(Wide,   Fast 40,     on board rom BIOS)
-**		53C1510D		(Wide,   Fast 40,     on board rom BIOS)
+**		53C897		(Wide,   Fast 40,     on board rom BIOS)
+**		53C1510D	(Wide,   Fast 40,     on board rom BIOS)
 **
 **	Other features:
 **		Memory mapped IO (linux-1.3.X and above only)
@@ -104,7 +103,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx - version 3.2a-2"
+#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx-3.4.1-20000726"
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)
 
@@ -124,7 +123,9 @@
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
 #include <asm/spinlock.h>
 #endif
 #include <linux/delay.h>
@@ -143,15 +144,19 @@
 #include <linux/version.h>
 #include <linux/blk.h>
 
+#ifdef CONFIG_ALL_PPC
+#include <asm/prom.h>
+#endif
+
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
 #include <linux/init.h>
-#else
-#ifndef	__initdata
-#define	__initdata
 #endif
-#ifndef	__initfunc
-#define	__initfunc(__arginit) __arginit
+
+#ifndef	__init
+#define	__init
 #endif
+#ifndef	__initdata
+#define	__initdata
 #endif
 
 #if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
@@ -182,98 +187,23 @@
 */
 typedef u32 u_int32;
 typedef u64 u_int64;
-
+typedef	u_long		vm_offset_t;
 #include "ncr53c8xx.h"
 
-/*==========================================================
-**
-**	A la VMS/CAM-3 queue management.
-**	Implemented from linux list management.
-**
-**==========================================================
+/*
+**	Donnot compile integrity checking code for Linux-2.3.0 
+**	and above since SCSI data structures are not ready yet.
 */
+/* #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,0) */
+#if 0
+#define	SCSI_NCR_INTEGRITY_CHECKING
+#endif
 
-typedef struct xpt_quehead {
-	struct xpt_quehead *flink;	/* Forward  pointer */
-	struct xpt_quehead *blink;	/* Backward pointer */
-} XPT_QUEHEAD;
-
-#define xpt_que_init(ptr) do { \
-	(ptr)->flink = (ptr); (ptr)->blink = (ptr); \
-} while (0)
-
-static inline void __xpt_que_add(struct xpt_quehead * new,
-	struct xpt_quehead * blink,
-	struct xpt_quehead * flink)
-{
-	flink->blink	= new;
-	new->flink	= flink;
-	new->blink	= blink;
-	blink->flink	= new;
-}
-
-static inline void __xpt_que_del(struct xpt_quehead * blink,
-	struct xpt_quehead * flink)
-{
-	flink->blink = blink;
-	blink->flink = flink;
-}
-
-static inline int xpt_que_empty(struct xpt_quehead *head)
-{
-	return head->flink == head;
-}
-
-static inline void xpt_que_splice(struct xpt_quehead *list,
-	struct xpt_quehead *head)
-{
-	struct xpt_quehead *first = list->flink;
-
-	if (first != list) {
-		struct xpt_quehead *last = list->blink;
-		struct xpt_quehead *at   = head->flink;
-
-		first->blink = head;
-		head->flink  = first;
-
-		last->flink = at;
-		at->blink   = last;
-	}
-}
-
-#define xpt_que_entry(ptr, type, member) \
-	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-
-#define xpt_insque(new, pos)		__xpt_que_add(new, pos, (pos)->flink)
-
-#define xpt_remque(el)			__xpt_que_del((el)->blink, (el)->flink)
-
-#define xpt_insque_head(new, head)	__xpt_que_add(new, head, (head)->flink)
-
-static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
-{
-	struct xpt_quehead *elem = head->flink;
-
-	if (elem != head)
-		__xpt_que_del(head, elem->flink);
-	else
-		elem = 0;
-	return elem;
-}
-
-#define xpt_insque_tail(new, head)	__xpt_que_add(new, (head)->blink, head)
-
-static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
-{
-	struct xpt_quehead *elem = head->blink;
+#define NAME53C			"ncr53c"
+#define NAME53C8XX		"ncr53c8xx"
+#define DRIVER_SMP_LOCK		ncr53c8xx_lock
 
-	if (elem != head)
-		__xpt_que_del(elem->blink, head);
-	else
-		elem = 0;
-	return elem;
-}
+#include "sym53c8xx_comm.h"
 
 /*==========================================================
 **
@@ -313,33 +243,6 @@
 
 /*==========================================================
 **
-**	On x86 architecture, write buffers management does 
-**	not reorder writes to memory. So, using compiler 
-**	optimization barriers is enough to guarantee some 
-**	ordering when the CPU is writing data accessed by 
-**	the NCR.
-**	On Alpha architecture, explicit memory barriers have 
-**	to be used.
-**	Other architectures are defaulted to mb() macro if  
-**	defined, otherwise use compiler barrier.
-**
-**==========================================================
-*/
-
-#if defined(__i386__)
-#define MEMORY_BARRIER()	barrier()
-#elif defined(__alpha__)
-#define MEMORY_BARRIER()	mb()
-#else
-#  ifdef mb
-#  define MEMORY_BARRIER()	mb()
-#  else
-#  define MEMORY_BARRIER()	barrier()
-#  endif
-#endif
-
-/*==========================================================
-**
 **	Configuration and Debugging
 **
 **==========================================================
@@ -369,8 +272,9 @@
 **    We need to deal with power of 2, for alignment constraints.
 */
 #if	SCSI_NCR_MAX_TAGS > 64
-#undef	SCSI_NCR_MAX_TAGS
-#define	SCSI_NCR_MAX_TAGS (64)
+#define	MAX_TAGS (64)
+#else
+#define	MAX_TAGS SCSI_NCR_MAX_TAGS
 #endif
 
 #define NO_TAG	(255)
@@ -378,7 +282,7 @@
 /*
 **	Choose appropriate type for tag bitmap.
 */
-#if	SCSI_NCR_MAX_TAGS > 32
+#if	MAX_TAGS > 32
 typedef u_int64 tagmap_t;
 #else
 typedef u_int32 tagmap_t;
@@ -428,7 +332,17 @@
 #ifdef SCSI_NCR_CAN_QUEUE
 #define MAX_START   (SCSI_NCR_CAN_QUEUE + 4)
 #else
-#define MAX_START   (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS)
+#define MAX_START   (MAX_TARGET + 7 * MAX_TAGS)
+#endif
+
+/*
+**   We limit the max number of pending IO to 250.
+**   since we donnot want to allocate more than 1 
+**   PAGE for 'scripth'.
+*/
+#if	MAX_START > 250
+#undef	MAX_START
+#define	MAX_START 250
 #endif
 
 /*
@@ -451,264 +365,11 @@
 #endif
 
 /*
-**    Io mapped or memory mapped.
-*/
-
-#if defined(SCSI_NCR_IOMAPPED)
-#define NCR_IOMAPPED
-#endif
-
-/*
 **	other
 */
 
 #define NCR_SNOOP_TIMEOUT (1000000)
 
-/*==========================================================
-**
-**	Defines for Linux.
-**
-**	Linux and Bsd kernel functions are quite different.
-**	These defines allow a minimum change of the original
-**	code.
-**
-**==========================================================
-*/
-
- /*
- **	Obvious definitions
- */
-
-#define u_char		unsigned char
-#define u_short		unsigned short
-#define u_int		unsigned int
-#define u_long		unsigned long
-
-typedef	u_long		vm_offset_t;
-typedef	int		vm_size_t;
-
-#ifndef bcopy
-#define bcopy(s, d, n)	memcpy((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(d, n)	memset((d), 0, (n))
-#endif
- 
-#ifndef offsetof
-#define offsetof(t, m)	((size_t) (&((t *)0)->m))
-#endif
-
-/*
-**	SMP threading.
-**
-**	Assuming that SMP systems are generally high end systems and may 
-**	use several SCSI adapters, we are using one lock per controller 
-**	instead of some global one. For the moment (linux-2.1.95), driver's 
-**	entry points are called with the 'io_request_lock' lock held, so:
-**	- We are uselessly loosing a couple of micro-seconds to lock the 
-**	  controller data structure.
-**	- But the driver is not broken by design for SMP and so can be 
-**	  more resistant to bugs or bad changes in the IO sub-system code.
-**	- A small advantage could be that the interrupt code is grained as 
-**	  wished (e.g.: threaded by controller).
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
-
-#if 0	/* not yet needed */
-static spinlock_t driver_lock;
-#define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&driver_lock, flags)
-#define	NCR_UNLOCK_DRIVER(flags)   spin_unlock_irqrestore(&driver_lock, flags)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np)      spin_lock_init(&np->smp_lock);
-#define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
-#define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
-
-#	if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99)
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags) \
-		spin_lock_irqsave(&io_request_lock, flags)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags) \
-		spin_unlock_irqrestore(&io_request_lock, flags)
-
-#	else
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
-
-#	endif
-
-#else
-
-#if 0	/* not yet needed */
-#define	NCR_LOCK_DRIVER(flags)     do {;} while (0)
-#define	NCR_UNLOCK_DRIVER(flags)   do {;} while (0)
-#endif
-
-#define	NCR_INIT_LOCK_NCB(np)      do { } while (0)
-#define	NCR_LOCK_NCB(np, flags)    do { save_flags(flags); cli(); } while (0)
-#define	NCR_UNLOCK_NCB(np, flags)  do { restore_flags(flags); } while (0)
-
-#define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
-#define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
-
-#endif
-
-/*
-**	Address translation
-**
-**	The driver has to provide physical memory addresses to 
-**	the script processor. Because some architectures use 
-**	different physical addresses from the PCI BUS, we must 
-**	use virt_to_bus instead of virt_to_phys.
-*/
-
-#define vtophys(p)	virt_to_bus(p)
-
-/*
-**	Memory mapped IO
-**
-**	Since linux-2.1, we must use ioremap() to map the io memory space.
-**	iounmap() to unmap it. That allows portability.
-**	Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater 
-**	than the highest physical memory address to kernel virtual pages with 
-**	vremap() / vfree(). That was not portable but worked with i386 
-**	architecture.
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
-#define ioremap vremap
-#define iounmap vfree
-#endif
-
-#if defined (__sparc__)
-#include <asm/irq.h>
-#elif defined (__alpha__)
-#define bus_dvma_to_mem(p)		((p) & 0xfffffffful)
-#else
-#define bus_dvma_to_mem(p)		(p)
-#endif
-
-#if defined(__i386__) || !defined(NCR_IOMAPPED)
-__initfunc(
-static vm_offset_t remap_pci_mem(u_long base, u_long size)
-)
-{
-	u_long page_base	= ((u_long) base) & PAGE_MASK;
-	u_long page_offs	= ((u_long) base) - page_base;
-	u_long page_remapped	= (u_long) ioremap(page_base, page_offs+size);
-
-	return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL);
-}
-
-__initfunc(
-static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
-)
-{
-	if (vaddr)
-		iounmap((void *) (vaddr & PAGE_MASK));
-}
-#endif	/* __i386__ || !NCR_IOMAPPED */
-
-/*
-**	Insert a delay in micro-seconds and milli-seconds.
-**	-------------------------------------------------
-**	Under Linux, udelay() is restricted to delay < 1 milli-second.
-**	In fact, it generally works for up to 1 second delay.
-**	Since 2.1.105, the mdelay() function is provided for delays 
-**	in milli-seconds.
-**	Under 2.0 kernels, udelay() is an inline function that is very 
-**	inaccurate on Pentium processors.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
-#define UDELAY udelay
-#define MDELAY mdelay
-#else
-static void UDELAY(long us) { udelay(us); }
-static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
-#endif
-
-/*
-**	Internal data structure allocation.
-**
-**	Linux scsi memory poor pool is adjusted for the need of
-**	middle-level scsi driver.
-**	We allocate our control blocks in the kernel memory pool
-**	to avoid scsi pool shortage.
-**
-**	kmalloc() only ensures 8 bytes boundary alignment.
-**	The NCR need better alignment for cache line bursting.
-**	The global header is moved between the NCB and CCBs and needs 
-**	origin and destination addresses to have same lower four bits.
-**
-**	We use 32 boundary alignment for NCB and CCBs and offset multiple 
-**	of 32 for global header fields. That's too much but at least enough.
-*/
-
-#define ALIGN_SIZE(shift)	(1UL << shift)
-#define ALIGN_MASK(shift)	(~(ALIGN_SIZE(shift)-1))
-
-#define CACHE_LINE_SHIFT	5
-#define CACHE_LINE_SIZE		ALIGN_SIZE(CACHE_LINE_SHIFT)
-#define CACHE_LINE_MASK		ALIGN_MASK(CACHE_LINE_SHIFT)
-
-static void *m_alloc(int size, int a_shift)
-{
-	u_long addr;
-	void *ptr;
-	u_long a_size, a_mask;
-
-	if (a_shift < 3)
-		a_shift = 3;
-
-	a_size	= ALIGN_SIZE(a_shift);
-	a_mask	= ALIGN_MASK(a_shift);
-
-	ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
-	if (ptr) {
-		addr	= (((u_long) ptr) + a_size) & a_mask;
-		*((void **) (addr - sizeof(void *))) = ptr;
-		ptr	= (void *) addr;
-	}
-
-	return ptr;
-}
-
-#ifdef MODULE
-static void m_free(void *ptr, int size)
-{
-	u_long addr;
-
-	if (ptr) {
-		addr	= (u_long) ptr;
-		ptr	= *((void **) (addr - sizeof(void *)));
-
-		kfree(ptr);
-	}
-}
-#endif
-
-/*
-**	Transfer direction
-**
-**	Low-level scsi drivers under Linux do not receive the expected 
-**	data transfer direction from upper scsi drivers.
-**	The driver will only check actual data direction for common 
-**	scsi opcodes. Other ones may cause problem, since they may 
-**	depend on device type or be vendor specific.
-**	I would prefer to never trust the device for data direction, 
-**	but that is not possible.
-**
-**	The original driver requires the expected direction to be known.
-**	The Linux version of the driver has been enhanced in order to 
-**	be able to transfer data in the direction choosen by the target. 
-*/
-
-#define XFER_IN		(1)
-#define XFER_OUT	(2)
-
 /*
 **	Head of list of NCR boards
 **
@@ -720,42 +381,8 @@
 static struct Scsi_Host		*first_host	= NULL;
 static Scsi_Host_Template	*the_template	= NULL;	
 
-
-/*
-**	/proc directory entry and proc_info function
-*/
-
-static struct proc_dir_entry proc_scsi_ncr53c8xx = {
-    PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
-			int length, int hostno, int func);
-#endif
-
-/*
-**	Driver setup.
-**
-**	This structure is initialized from linux config options.
-**	It can be overridden at boot-up by the boot command line.
-*/
-static struct ncr_driver_setup
-	driver_setup			= SCSI_NCR_DRIVER_SETUP;
-
-#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-static struct ncr_driver_setup
-	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
-# ifdef	MODULE
-char *ncr53c8xx = 0;	/* command line passed by insmod */
-#  if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(ncr53c8xx, "s");
-#  endif
-# endif
-#endif
-
 /*
-**	Other Linux definitions
+**	Other definitions
 */
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
@@ -769,358 +396,98 @@
 #define bootverbose (np->verbose)
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
-static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+static u_char Tekram_sync[16] __initdata =
+	{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-/*
-**	Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to 
-**	transmit device configuration to the ncr_attach() function.
-*/
-typedef struct {
-	int	bus;
-	u_char	device_fn;
-	u_long	base;
-	u_long	base_2;
-	u_long	io_port;
-	int	irq;
-/* port and reg fields to use INB, OUTB macros */
-	u_long	port;
-	volatile struct ncr_reg	*reg;
-} ncr_slot;
-
-typedef struct {
-	int type;
-#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
-#define	SCSI_NCR_TEKRAM_NVRAM	(2)
-#ifdef	SCSI_NCR_NVRAM_SUPPORT
-	union {
-		Symbios_nvram Symbios;
-		Tekram_nvram Tekram;
-	} data;
-#endif
-} ncr_nvram;
-
-/*
-**	Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
-**	to save data on each detected board for ncr_attach().
-*/
-typedef struct {
-	ncr_slot  slot;
-	ncr_chip  chip;
-	ncr_nvram *nvram;
-	u_char	  host_id;
-	int attach_done;
-} ncr_device;
-
 /*==========================================================
 **
-**	Debugging tags
+**	Command control block states.
 **
 **==========================================================
 */
 
-#define DEBUG_ALLOC    (0x0001)
-#define DEBUG_PHASE    (0x0002)
-#define DEBUG_POLL     (0x0004)
-#define DEBUG_QUEUE    (0x0008)
-#define DEBUG_RESULT   (0x0010)
-#define DEBUG_SCATTER  (0x0020)
-#define DEBUG_SCRIPT   (0x0040)
-#define DEBUG_TINY     (0x0080)
-#define DEBUG_TIMING   (0x0100)
-#define DEBUG_NEGO     (0x0200)
-#define DEBUG_TAGS     (0x0400)
-#define DEBUG_FREEZE   (0x0800)
-#define DEBUG_RESTART  (0x1000)
+#define HS_IDLE		(0)
+#define HS_BUSY		(1)
+#define HS_NEGOTIATE	(2)	/* sync/wide data transfer*/
+#define HS_DISCONNECT	(3)	/* Disconnected by target */
+
+#define HS_DONEMASK	(0x80)
+#define HS_COMPLETE	(4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT	(5|HS_DONEMASK)	/* Selection timeout      */
+#define HS_RESET	(6|HS_DONEMASK)	/* SCSI reset	          */
+#define HS_ABORTED	(7|HS_DONEMASK)	/* Transfer aborted       */
+#define HS_TIMEOUT	(8|HS_DONEMASK)	/* Software timeout       */
+#define HS_FAIL		(9|HS_DONEMASK)	/* SCSI or PCI bus errors */
+#define HS_UNEXPECTED	(10|HS_DONEMASK)/* Unexpected disconnect  */
 
 /*
-**    Enable/Disable debug messages.
-**    Can be changed at runtime too.
+**	Invalid host status values used by the SCRIPTS processor 
+**	when the nexus is not fully identified.
+**	Shall never appear in a CCB.
 */
 
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-	#define DEBUG_FLAGS ncr_debug
-#else
-	#define DEBUG_FLAGS	SCSI_NCR_DEBUG_FLAGS
-#endif
-
+#define HS_INVALMASK	(0x40)
+#define	HS_SELECTING	(0|HS_INVALMASK)
+#define	HS_IN_RESELECT	(1|HS_INVALMASK)
+#define	HS_STARTING	(2|HS_INVALMASK)
 
+/*
+**	Flags set by the SCRIPT processor for commands 
+**	that have been skipped.
+*/
+#define HS_SKIPMASK	(0x20)
 
 /*==========================================================
 **
-**	assert ()
+**	Software Interrupt Codes
 **
 **==========================================================
+*/
+
+#define	SIR_BAD_STATUS		(1)
+#define	SIR_XXXXXXXXXX		(2)
+#define	SIR_NEGO_SYNC		(3)
+#define	SIR_NEGO_WIDE		(4)
+#define	SIR_NEGO_FAILED		(5)
+#define	SIR_NEGO_PROTO		(6)
+#define	SIR_REJECT_RECEIVED	(7)
+#define	SIR_REJECT_SENT		(8)
+#define	SIR_IGN_RESIDUE		(9)
+#define	SIR_MISSING_SAVE	(10)
+#define	SIR_RESEL_NO_MSG_IN	(11)
+#define	SIR_RESEL_NO_IDENTIFY	(12)
+#define	SIR_RESEL_BAD_LUN	(13)
+#define	SIR_RESEL_BAD_TARGET	(14)
+#define	SIR_RESEL_BAD_I_T_L	(15)
+#define	SIR_RESEL_BAD_I_T_L_Q	(16)
+#define	SIR_DONE_OVERFLOW	(17)
+#define	SIR_MAX			(17)
+
+/*==========================================================
 **
-**	modified copy from 386bsd:/usr/include/sys/assert.h
+**	Extended error codes.
+**	xerr_status field of struct ccb.
 **
-**----------------------------------------------------------
+**==========================================================
 */
 
-#define	assert(expression) { \
-	if (!(expression)) { \
-		(void)printk(KERN_ERR \
-			"assertion \"%s\" failed: file \"%s\", line %d\n", \
-			#expression, \
-			__FILE__, __LINE__); \
-	} \
-}
+#define	XE_OK		(0)
+#define	XE_EXTRA_DATA	(1)	/* unexpected data phase */
+#define	XE_BAD_PHASE	(2)	/* illegal phase (4/5)   */
 
 /*==========================================================
 **
-**	Big/Little endian support.
+**	Negotiation status.
+**	nego_status field	of struct ccb.
 **
 **==========================================================
 */
 
-/*
-**	If the NCR uses big endian addressing mode over the 
-**	PCI, actual io register addresses for byte and word 
-**	accesses must be changed according to lane routing.
-**	Btw, ncr_offb() and ncr_offw() macros only apply to 
-**	constants and so donnot generate bloated code.
-*/
-
-#if	defined(SCSI_NCR_BIG_ENDIAN)
-
-#define ncr_offb(o)	(((o)&~3)+((~((o)&3))&3))
-#define ncr_offw(o)	(((o)&~3)+((~((o)&3))&2))
-
-#else
-
-#define ncr_offb(o)	(o)
-#define ncr_offw(o)	(o)
-
-#endif
-
-/*
-**	If the CPU and the NCR use same endian-ness addressing,
-**	no byte reordering is needed for script patching.
-**	Macro cpu_to_scr() is to be used for script patching.
-**	Macro scr_to_cpu() is to be used for getting a DWORD 
-**	from the script.
-*/
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw)	cpu_to_le32(dw)
-#define scr_to_cpu(dw)	le32_to_cpu(dw)
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw)	cpu_to_be32(dw)
-#define scr_to_cpu(dw)	be32_to_cpu(dw)
-
-#else
-
-#define cpu_to_scr(dw)	(dw)
-#define scr_to_cpu(dw)	(dw)
-
-#endif
-
-/*==========================================================
-**
-**	Access to the controller chip.
-**
-**	If NCR_IOMAPPED is defined, the driver will use 
-**	normal IOs instead of the MEMORY MAPPED IO method  
-**	recommended by PCI specifications.
-**	If all PCI bridges, host brigdes and architectures 
-**	would have been correctly designed for PCI, this 
-**	option would be useless.
-**
-**==========================================================
-*/
-
-/*
-**	If the CPU and the NCR use same endian-ness addressing,
-**	no byte reordering is needed for accessing chip io 
-**	registers. Functions suffixed by '_raw' are assumed 
-**	to access the chip over the PCI without doing byte 
-**	reordering. Functions suffixed by '_l2b' are 
-**	assumed to perform little-endian to big-endian byte 
-**	reordering, those suffixed by '_b2l' blah, blah,
-**	blah, ...
-*/
-
-#if defined(NCR_IOMAPPED)
-
-/*
-**	IO mapped only input / ouput
-*/
-
-#define	INB_OFF(o)		inb (np->port + ncr_offb(o))
-#define	OUTB_OFF(o, val)	outb ((val), np->port + ncr_offb(o))
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define	INW_OFF(o)		inw_l2b (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_l2b (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_b2l ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_b2l ((val), np->port + (o))
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define	INW_OFF(o)		inw_b2l (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_b2l (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_l2b ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_l2b ((val), np->port + (o))
-
-#else
-
-#define	INW_OFF(o)		inw_raw (np->port + ncr_offw(o))
-#define	INL_OFF(o)		inl_raw (np->port + (o))
-
-#define	OUTW_OFF(o, val)	outw_raw ((val), np->port + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_raw ((val), np->port + (o))
-
-#endif	/* ENDIANs */
-
-#else	/* defined NCR_IOMAPPED */
-
-/*
-**	MEMORY mapped IO input / output
-*/
-
-#define INB_OFF(o)		readb((char *)np->reg + ncr_offb(o))
-#define OUTB_OFF(o, val)	writeb((val), (char *)np->reg + ncr_offb(o))
-
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o)		readw_l2b((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_l2b((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_b2l((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_b2l((val), (char *)np->reg + (o))
-
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o)		readw_b2l((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_b2l((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_l2b((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_l2b((val), (char *)np->reg + (o))
-
-#else
-
-#define INW_OFF(o)		readw_raw((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_raw((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val)	writew_raw((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_raw((val), (char *)np->reg + (o))
-
-#endif
-
-#endif	/* defined NCR_IOMAPPED */
-
-#define INB(r)		INB_OFF (offsetof(struct ncr_reg,r))
-#define INW(r)		INW_OFF (offsetof(struct ncr_reg,r))
-#define INL(r)		INL_OFF (offsetof(struct ncr_reg,r))
-
-#define OUTB(r, val)	OUTB_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTW(r, val)	OUTW_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTL(r, val)	OUTL_OFF (offsetof(struct ncr_reg,r), (val))
-
-/*
-**	Set bit field ON, OFF 
-*/
-
-#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
-#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
-#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
-#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
-#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
-#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
-
-
-/*==========================================================
-**
-**	Command control block states.
-**
-**==========================================================
-*/
-
-#define HS_IDLE		(0)
-#define HS_BUSY		(1)
-#define HS_NEGOTIATE	(2)	/* sync/wide data transfer*/
-#define HS_DISCONNECT	(3)	/* Disconnected by target */
-
-#define HS_DONEMASK	(0x80)
-#define HS_COMPLETE	(4|HS_DONEMASK)
-#define HS_SEL_TIMEOUT	(5|HS_DONEMASK)	/* Selection timeout      */
-#define HS_RESET	(6|HS_DONEMASK)	/* SCSI reset	          */
-#define HS_ABORTED	(7|HS_DONEMASK)	/* Transfer aborted       */
-#define HS_TIMEOUT	(8|HS_DONEMASK)	/* Software timeout       */
-#define HS_FAIL		(9|HS_DONEMASK)	/* SCSI or PCI bus errors */
-#define HS_UNEXPECTED	(10|HS_DONEMASK)/* Unexpected disconnect  */
-
-/*
-**	Invalid host status values used by the SCRIPTS processor 
-**	when the nexus is not fully identified.
-**	Shall never appear in a CCB.
-*/
-
-#define HS_INVALMASK	(0x40)
-#define	HS_SELECTING	(0|HS_INVALMASK)
-#define	HS_IN_RESELECT	(1|HS_INVALMASK)
-#define	HS_STARTING	(2|HS_INVALMASK)
-
-/*
-**	Flags set by the SCRIPT processor for commands 
-**	that have been skipped.
-*/
-#define HS_SKIPMASK	(0x20)
-
-/*==========================================================
-**
-**	Software Interrupt Codes
-**
-**==========================================================
-*/
-
-#define	SIR_BAD_STATUS		(1)
-#define	SIR_XXXXXXXXXX		(2)
-#define	SIR_NEGO_SYNC		(3)
-#define	SIR_NEGO_WIDE		(4)
-#define	SIR_NEGO_FAILED		(5)
-#define	SIR_NEGO_PROTO		(6)
-#define	SIR_REJECT_RECEIVED	(7)
-#define	SIR_REJECT_SENT		(8)
-#define	SIR_IGN_RESIDUE		(9)
-#define	SIR_MISSING_SAVE	(10)
-#define	SIR_RESEL_NO_MSG_IN	(11)
-#define	SIR_RESEL_NO_IDENTIFY	(12)
-#define	SIR_RESEL_BAD_LUN	(13)
-#define	SIR_RESEL_BAD_TARGET	(14)
-#define	SIR_RESEL_BAD_I_T_L	(15)
-#define	SIR_RESEL_BAD_I_T_L_Q	(16)
-#define	SIR_DONE_OVERFLOW	(17)
-#define	SIR_MAX			(17)
-
-/*==========================================================
-**
-**	Extended error codes.
-**	xerr_status field of struct ccb.
-**
-**==========================================================
-*/
-
-#define	XE_OK		(0)
-#define	XE_EXTRA_DATA	(1)	/* unexpected data phase */
-#define	XE_BAD_PHASE	(2)	/* illegal phase (4/5)   */
-
-/*==========================================================
-**
-**	Negotiation status.
-**	nego_status field	of struct ccb.
-**
-**==========================================================
-*/
-
-#define NS_SYNC		(1)
-#define NS_WIDE		(2)
+#define NS_NOCHANGE	(0)
+#define NS_SYNC		(1)
+#define NS_WIDE		(2)
+#define NS_PPR		(4)
 
 /*==========================================================
 **
@@ -1192,50 +559,12 @@
 #define UC_SETORDER	13
 #define UC_SETWIDE	14
 #define UC_SETFLAG	15
-#define UC_CLEARPROF	16
 #define UC_SETVERBOSE	17
 
 #define	UF_TRACE	(0x01)
 #define	UF_NODISC	(0x02)
 #define	UF_NOSCAN	(0x04)
 
-/*---------------------------------------
-**
-**	Timestamps for profiling
-**
-**---------------------------------------
-*/
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-
-struct tstamp {
-	u_long start;
-	u_long end;
-	u_long command;
-	u_long status;
-	u_long disconnect;
-	u_long reselect;
-};
-
-/*
-**	profiling data (per device)
-*/
-
-struct profile {
-	u_long	num_trans;
-	u_long	num_kbytes;
-	u_long	rest_bytes;
-	u_long	num_disc;
-	u_long	num_break;
-	u_long	num_int;
-	u_long	num_fly;
-	u_long	ms_setup;
-	u_long	ms_data;
-	u_long	ms_disc;
-	u_long	ms_post;
-};
-#endif
-
 /*========================================================================
 **
 **	Declaration of structs:		target control block
@@ -1313,6 +642,13 @@
 /*2*/	u_char	widedone;
 /*3*/	u_char	wval;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	u_char 	ic_min_sync;
+	u_char 	ic_max_width;
+	u_char 	ic_maximums_set;
+	u_char 	ic_done;
+#endif
+
 	/*----------------------------------------------------------------
 	**	User settable limits and options.
 	**	These limits are read from the NVRAM if present.
@@ -1384,7 +720,7 @@
 	*/
 	u_char		ia_tag;		/* Allocation index		*/
 	u_char		if_tag;		/* Freeing index		*/
-	u_char cb_tags[SCSI_NCR_MAX_TAGS];	/* Circular tags buffer	*/
+	u_char cb_tags[MAX_TAGS];	/* Circular tags buffer	*/
 	u_char		usetags;	/* Command queuing is active	*/
 	u_char		maxtags;	/* Max nr of tags asked by user	*/
 	u_char		numtags;	/* Current number of tags	*/
@@ -1470,14 +806,6 @@
 	*/
 	ccb_p	cp;
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*----------------------------------------------------------------
-	**	Space for some timestamps to gather profiling data.
-	**----------------------------------------------------------------
-	*/
-	struct tstamp	stamp;
-#endif
-
 	/*----------------------------------------------------------------
 	**	Status fields.
 	**----------------------------------------------------------------
@@ -1630,7 +958,8 @@
 	**----------------------------------------------------------------
 	*/
 	Scsi_Cmnd	*cmd;		/* SCSI command 		*/
-	u_long		tlimit;		/* Deadline for this job	*/
+	u_char		cdb_buf[16];	/* Copy of CDB			*/
+	u_char		sense_buf[64];
 	int		data_len;	/* Total data length		*/
 
 	/*----------------------------------------------------------------
@@ -1761,11 +1090,12 @@
 	**	General controller parameters and configuration.
 	**----------------------------------------------------------------
 	*/
+	pcidev_t	pdev;
 	u_short		device_id;	/* PCI device id		*/
 	u_char		revision_id;	/* PCI device revision id	*/
-	u_char		pci_bus;	/* PCI bus number		*/
-	u_char		pci_devfn;	/* PCI device and function	*/
-	u_long		port;		/* IO space base address	*/
+	u_char		bus;		/* PCI BUS number		*/
+	u_char		device_fn;	/* PCI BUS device and function	*/
+	u_long		base_io;	/* IO space base address	*/
 	u_int		irq;		/* IRQ level			*/
 	u_int		features;	/* Chip features map		*/
 	u_char		myaddr;		/* SCSI id of the adapter	*/
@@ -1803,11 +1133,6 @@
 	*/
 	struct ncr_reg	regdump;	/* Register dump		*/
 	u_long		regtime;	/* Time it has been done	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	struct profile	profile;	/* Profiling data		*/
-	u_int		disc_phys;	/* Disconnection counters 	*/
-	u_int		disc_ref;
-#endif
 
 	/*----------------------------------------------------------------
 	**	Miscellaneous buffers accessed by the scripts-processor.
@@ -1829,6 +1154,7 @@
 	u_char		order;		/* Tag order to use		*/
 	u_char		verbose;	/* Verbosity for this controller*/
 	int		ncr_cache;	/* Used for cache test at init.	*/
+	u_long		p_ncb;		/* BUS address of this NCB	*/
 
 	/*----------------------------------------------------------------
 	**	Command completion handling.
@@ -1845,6 +1171,17 @@
 	struct ccb	*ccb;		/* Global CCB			*/
 	struct usrcmd	user;		/* Command from user		*/
 	u_char		release_stage;	/* Synchronisation stage on release  */
+
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	/*----------------------------------------------------------------
+	**	Fields that are used for integrity check
+	**----------------------------------------------------------------
+	*/
+	unsigned char check_integrity; /* Enable midlayer integ.check on
+					* bus scan. */
+	unsigned char check_integ_par;  /* Set if par or Init. Det. error
+					 * used only during integ check */
+#endif
 };
 
 #define NCB_SCRIPT_PHYS(np,lbl)	 (np->p_script  + offsetof (struct script, lbl))
@@ -1884,19 +1221,11 @@
 	ncrcmd	send_ident	[  9];
 	ncrcmd	prepare		[  6];
 	ncrcmd	prepare2	[  7];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd  command		[  9];
-#else
 	ncrcmd  command		[  6];
-#endif
 	ncrcmd  dispatch	[ 32];
 	ncrcmd  clrack		[  4];
 	ncrcmd	no_data		[ 17];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd  status		[ 11];
-#else
 	ncrcmd  status		[  8];
-#endif
 	ncrcmd  msg_in		[  2];
 	ncrcmd  msg_in2		[ 16];
 	ncrcmd  msg_bad		[  4];
@@ -1915,22 +1244,14 @@
 #endif
 	ncrcmd  save_dp		[  7];
 	ncrcmd  restore_dp	[  5];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd  disconnect	[ 28];
-#else
 	ncrcmd  disconnect	[ 17];
-#endif
 	ncrcmd	msg_out		[  9];
 	ncrcmd	msg_out_done	[  7];
 	ncrcmd  idle		[  2];
 	ncrcmd	reselect	[  8];
 	ncrcmd	reselected	[  8];
 	ncrcmd	resel_dsa	[  6];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd	loadpos1	[  7];
-#else
 	ncrcmd	loadpos1	[  4];
-#endif
 	ncrcmd  resel_lun	[  6];
 	ncrcmd	resel_tag	[  6];
 	ncrcmd	jump_to_nexus	[  4];
@@ -2023,20 +1344,21 @@
 static  void    ncr_int_sto     (ncb_p np);
 static	u_long	ncr_lookup	(char* id);
 static	void	ncr_negotiate	(struct ncb* np, struct tcb* tp);
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-static	void	ncb_profile	(ncb_p np, ccb_p cp);
+static	int	ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr);
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+static	int	ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr);
 #endif
 
 static	void	ncr_script_copy_and_bind
 				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
 static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
-static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
+static	int	ncr_scatter	(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
 static	void	ncr_getsync	(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
 static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static	void	ncr_setup_tags	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide, u_char ack);
 static	int	ncr_show_msg	(u_char * msg);
+static  void    ncr_print_msg   (ccb_p cp, char *label, u_char *msg);
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
@@ -2061,24 +1383,6 @@
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
-static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
-#endif
-
-/*==========================================================
-**
-**
-**      Global static data.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
-#endif
-
 static inline char *ncr_name (ncb_p np)
 {
 	return np->inst_name;
@@ -2107,7 +1411,9 @@
 #define	RELOC_SOFTC	0x40000000
 #define	RELOC_LABEL	0x50000000
 #define	RELOC_REGISTER	0x60000000
+#if 0
 #define	RELOC_KVAR	0x70000000
+#endif
 #define	RELOC_LABELH	0x80000000
 #define	RELOC_MASK	0xf0000000
 
@@ -2116,19 +1422,21 @@
 #define PADDRH(label)   (RELOC_LABELH | offsetof(struct scripth, label))
 #define	RADDR(label)	(RELOC_REGISTER | REG(label))
 #define	FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#if 0
 #define	KVAR(which)	(RELOC_KVAR | (which))
+#endif
 
+#if 0
 #define	SCRIPT_KVAR_JIFFIES	(0)
-
 #define	SCRIPT_KVAR_FIRST		SCRIPT_KVAR_JIFFIES
 #define	SCRIPT_KVAR_LAST		SCRIPT_KVAR_JIFFIES
-
 /*
  * Kernel variables referenced in the scripts.
  * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
  */
 static void *script_kvars[] __initdata =
 	{ (void *)&jiffies };
+#endif
 
 static	struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
@@ -2294,14 +1602,6 @@
 		PADDR (dispatch),
 
 }/*-------------------------< COMMAND >--------------------*/,{
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**	... set a timestamp ...
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.command),
-#endif
 	/*
 	**	... and send the command
 	*/
@@ -2407,14 +1707,6 @@
 		PADDR (no_data),
 
 }/*-------------------------< STATUS >--------------------*/,{
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**	set the timestamp.
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.status),
-#endif
 	/*
 	**	get the status
 	*/
@@ -2641,24 +1933,6 @@
 	*/
 	SCR_WAIT_DISC,
 		0,
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**	Profiling:
-	**	Set a time stamp,
-	**	and count the disconnects.
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.disconnect),
-	SCR_COPY (4),
-		NADDR (disc_phys),
-		RADDR (scratcha),
-	SCR_REG_REG (scratcha, SCR_ADD, 0x01),
-		0,
-	SCR_COPY (4),
-		RADDR (scratcha),
-		NADDR (disc_phys),
-#endif
 	/*
 	**	Status is: DISCONNECTED.
 	*/
@@ -2799,14 +2073,6 @@
 }/*-------------------------< LOADPOS1 >-------------------*/,{
 		0,
 		NADDR (header),
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**      Set a time stamp for this reselection
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.reselect),
-#endif
 	/*
 	**	The DSA contains the data structure address.
 	*/
@@ -3578,9 +2844,7 @@
 **==========================================================
 */
 
-__initfunc(
-void ncr_script_fill (struct script * scr, struct scripth * scrh)
-)
+void __init ncr_script_fill (struct script * scr, struct scripth * scrh)
 {
 	int	i;
 	ncrcmd	*p;
@@ -3655,9 +2919,8 @@
 **==========================================================
 */
 
-__initfunc(
-static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
-)
+static void __init 
+ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
 {
 	ncrcmd  opcode, new, old, tmp1, tmp2;
 	ncrcmd	*start, *end;
@@ -3700,11 +2963,15 @@
 			*/
 			relocs = 2;
 			tmp1 = src[0];
+#ifdef	RELOC_KVAR
 			if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
 				tmp1 = 0;
+#endif
 			tmp2 = src[1];
+#ifdef	RELOC_KVAR
 			if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
 				tmp2 = 0;
+#endif
 			if ((tmp1 ^ tmp2) & 3) {
 				printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
 					ncr_name(np), (int) (src-start-1));
@@ -3757,7 +3024,7 @@
 				switch (old & RELOC_MASK) {
 				case RELOC_REGISTER:
 					new = (old & ~RELOC_MASK)
-						+ bus_dvma_to_mem(np->paddr);
+						+ pcivtobus(np->paddr);
 					break;
 				case RELOC_LABEL:
 					new = (old & ~RELOC_MASK) + np->p_script;
@@ -3766,8 +3033,9 @@
 					new = (old & ~RELOC_MASK) + np->p_scripth;
 					break;
 				case RELOC_SOFTC:
-					new = (old & ~RELOC_MASK) + vtophys(np);
+					new = (old & ~RELOC_MASK) + np->p_ncb;
 					break;
+#ifdef	RELOC_KVAR
 				case RELOC_KVAR:
 					if (((old & ~RELOC_MASK) <
 					     SCRIPT_KVAR_FIRST) ||
@@ -3777,6 +3045,7 @@
 					new = vtophys(script_kvars[old &
 					    ~RELOC_MASK]);
 					break;
+#endif
 				case 0:
 					/* Don't relocate a 0 address. */
 					if (old == 0) {
@@ -3816,17 +3085,6 @@
 
 struct host_data {
      struct ncb *ncb;
-
-     char ncb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct ncb _ncb_data;
-
-     char ccb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct ccb _ccb_data;
-
-     char scr_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
-     struct script script_data;
-
-     struct scripth scripth_data;
 };
 
 /*
@@ -3913,10 +3171,8 @@
 **	Get target set-up from Symbios format NVRAM.
 */
 
-__initfunc(
-static void
-	ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
-)
+static void __init 
+ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
 {
 	tcb_p tp = &np->target[target];
 	Symbios_target *tn = &nvram->target[target];
@@ -3924,7 +3180,7 @@
 	tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
 	tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
 	tp->usrtags =
-		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? MAX_TAGS : 0;
 
 	if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
 		tp->usrflag |= UF_NODISC;
@@ -3936,10 +3192,8 @@
 **	Get target set-up from Tekram format NVRAM.
 */
 
-__initfunc(
-static void
-	ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
-)
+static void __init 
+ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
 {
 	tcb_p tp = &np->target[target];
 	struct Tekram_target *tn = &nvram->target[target];
@@ -3947,7 +3201,7 @@
 
 	if (tn->flags & TEKRAM_SYNC_NEGO) {
 		i = tn->sync_index & 0xf;
-		tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+		tp->usrsync = Tekram_sync[i];
 	}
 
 	tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
@@ -3965,9 +3219,7 @@
 }
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-__initfunc(
-static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
-)
+static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
 {
 	u_char	burst_max;
 	u_long	period;
@@ -4137,30 +3389,53 @@
 	ncr_init_burst(np, burst_max);
 
 	/*
-	**	Set differential mode and LED support.
-	**	Ignore these features for boards known to use a 
-	**	specific GPIO wiring (Tekram only for now).
-	**	Probe initial setting of GPREG and GPCNTL for 
-	**	other ones.
-	*/
-	if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+	**	Set SCSI BUS mode.
+	**
+	**	- ULTRA2 chips (895/895A/896) report the current 
+	**	  BUS mode through the STEST4 IO register.
+	**	- For previous generation chips (825/825A/875), 
+	**	  user has to tell us how to check against HVD, 
+	**	  since a 100% safe algorithm is not possible.
+	*/
+	np->scsi_mode = SMODE_SE;
+	if	(np->features & FE_ULTRA2)
+		np->scsi_mode = (np->sv_stest4 & SMODE);
+	else if	(np->features & FE_DIFF) {
 		switch(driver_setup.diff_support) {
-		case 3:
+		case 4:	/* Trust previous settings if present, then GPIO3 */
+			if (np->sv_scntl3) {
+				if (np->sv_stest2 & 0x20)
+					np->scsi_mode = SMODE_HVD;
+				break;
+			}
+		case 3:	/* SYMBIOS controllers report HVD through GPIO3 */
+			if (nvram && nvram->type != SCSI_NCR_SYMBIOS_NVRAM)
+				break;
 			if (INB(nc_gpreg) & 0x08)
+				break;
+		case 2:	/* Set HVD unconditionally */
+			np->scsi_mode = SMODE_HVD;
+		case 1:	/* Trust previous settings for HVD */
+			if (np->sv_stest2 & 0x20)
+				np->scsi_mode = SMODE_HVD;
 			break;
-		case 2:
-			np->rv_stest2	|= 0x20;
-			break;
-		case 1:
-			np->rv_stest2	|= (np->sv_stest2 & 0x20);
-			break;
-		default:
+		default:/* Don't care about HVD */	
 			break;
 		}
 	}
+	if (np->scsi_mode == SMODE_HVD)
+		np->rv_stest2 |= 0x20;
+
+	/*
+	**	Set LED support from SCRIPTS.
+	**	Ignore this feature for boards known to use a 
+	**	specific GPIO wiring and for the 895A or 896 
+	**	that drive the LED directly.
+	**	Also probe initial setting of GPIO0 as output.
+	*/
 	if ((driver_setup.led_pin ||
 	     (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
-	    !(np->sv_gpcntl & 0x01))
+	    !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01))
 		np->features |= FE_LED0;
 
 	/*
@@ -4210,7 +3485,7 @@
 #endif
 			tp->usrsync = driver_setup.default_sync;
 			tp->usrwide = driver_setup.max_wide;
-			tp->usrtags = SCSI_NCR_MAX_TAGS;
+			tp->usrtags = MAX_TAGS;
 			if (!driver_setup.disconnection)
 				np->target[i].usrflag = UF_NODISC;
 		}
@@ -4248,92 +3523,6 @@
 	return 0;
 }
 
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-
-__initfunc(
-void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
-)
-{
-	int i;
-
-	/* display Symbios nvram host data */
-	printk(KERN_DEBUG "%s: HOST ID=%d%s%s%s%s%s\n",
-		ncr_name(np), nvram->host_id & 0x0f,
-		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
-		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
-		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERBOSE"	:"", 
-		(nvram->flags  & SYMBIOS_CHS_MAPPING)	? " CHS_ALT"	:"", 
-		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
-
-	/* display Symbios nvram drive data */
-	for (i = 0 ; i < 15 ; i++) {
-		struct Symbios_target *tn = &nvram->target[i];
-		printk(KERN_DEBUG "%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
-		ncr_name(np), i,
-		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
-		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
-		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
-		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
-		tn->bus_width,
-		tn->sync_period / 4,
-		tn->timeout);
-	}
-}
-
-static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
-
-__initfunc(
-void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
-)
-{
-	int i, tags, boot_delay;
-	char *rem;
-
-	/* display Tekram nvram host data */
-	tags = 2 << nvram->max_tags_index;
-	boot_delay = 0;
-	if (nvram->boot_delay_index < 6)
-		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
-	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
-	default:
-	case 0:	rem = "";			break;
-	case 1: rem = " REMOVABLE=boot device";	break;
-	case 2: rem = " REMOVABLE=all";		break;
-	}
-
-	printk(KERN_DEBUG
-		"%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
-		ncr_name(np), nvram->host_id & 0x0f,
-		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
-		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"	:"",
-		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
-		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
-		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
-		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
-		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
-		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
-		rem, boot_delay, tags);
-
-	/* display Tekram nvram drive data */
-	for (i = 0; i <= 15; i++) {
-		int sync, j;
-		struct Tekram_target *tn = &nvram->target[i];
-		j = tn->sync_index & 0xf;
-		sync = j < 12 ? Tekram_sync[j] : 255;
-		printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
-		ncr_name(np), i,
-		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
-		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
-		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
-		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
-		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
-		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
-		sync);
-	}
-}
-#endif /* SCSI_NCR_DEBUG_NVRAM */
-
 /*
 **	Host attach and initialisations.
 **
@@ -4344,47 +3533,55 @@
 **	start the timer daemon.
 */
 
-__initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
-)
+static int __init 
+ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
 {
         struct host_data *host_data;
-	ncb_p np;
+	ncb_p np = 0;
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
 	ncr_nvram *nvram = device->nvram;
 	int i;
 
+	printk(KERN_INFO "ncr53c%s-%d: rev 0x%x on pci bus %d device %d function %d "
 #ifdef __sparc__
-printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%s\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, __irq_itoa(device->slot.irq));
+		"irq %s\n",
 #else
-printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, device->slot.irq);
+		"irq %d\n",
 #endif
- 
+		device->chip.name, unit, device->chip.revision_id,
+		device->slot.bus, (device->slot.device_fn & 0xf8) >> 3,
+		device->slot.device_fn & 7,
+#ifdef __sparc__
+		__irq_itoa(device->slot.irq));
+#else
+		device->slot.irq);
+#endif
+
 	/*
 	**	Allocate host_data structure
 	*/
         if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
 	        goto attach_error;
-
-	/*
-	**	Initialize structure.
-	*/
 	host_data = (struct host_data *) instance->hostdata;
-	bzero (host_data, sizeof(*host_data));
 
 	/*
-	**	Align np and first ccb to 32 boundary for cache line 
-	**	bursting when copying the global header.
+	**	Allocate the host control block.
 	*/
-	np = (ncb_p) (((u_long) &host_data->_ncb_data) & CACHE_LINE_MASK);
+	np = __m_calloc_dma(device->pdev, sizeof(struct ncb), "NCB");
+	if (!np)
+		goto attach_error;
 	NCR_INIT_LOCK_NCB(np);
+	np->pdev  = device->pdev;
+	np->p_ncb = vtobus(np);
 	host_data->ncb = np;
-	np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CACHE_LINE_MASK);
+
+	/*
+	**	Allocate the default CCB.
+	*/
+	np->ccb = (ccb_p) m_calloc_dma(sizeof(struct ccb), "CCB");
+	if (!np->ccb)
+		goto attach_error;
 
 	/*
 	**	Store input informations in the host data structure.
@@ -4395,17 +3592,25 @@
 	sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
 	np->device_id	= device->chip.device_id;
 	np->revision_id	= device->chip.revision_id;
-	np->pci_bus	= device->slot.bus;
-	np->pci_devfn	= device->slot.device_fn;
+	np->bus		= device->slot.bus;
+	np->device_fn	= device->slot.device_fn;
 	np->features	= device->chip.features;
 	np->clock_divn	= device->chip.nr_divisor;
 	np->maxoffs	= device->chip.offset_max;
 	np->maxburst	= device->chip.burst_max;
 	np->myaddr	= device->host_id;
 
+	/*
+	**	Allocate SCRIPTS areas.
+	*/
 	np->script0  = (struct script *)
-		(((u_long) &host_data->script_data) & CACHE_LINE_MASK);
-	np->scripth0 = &host_data->scripth_data;
+			m_calloc_dma(sizeof(struct script), "SCRIPT");
+	if (!np->script0)
+		goto attach_error;
+	np->scripth0  = (struct scripth *)
+			m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
+	if (!np->scripth0)
+		goto attach_error;
 
 	/*
 	**    Initialize timer structure
@@ -4423,7 +3628,7 @@
 	np->paddr	= device->slot.base;
 	np->paddr2	= (np->features & FE_RAM)? device->slot.base_2 : 0;
 
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 	np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
 	if (!np->vaddr) {
 		printk(KERN_ERR
@@ -4443,27 +3648,26 @@
 
 	np->reg = (struct ncr_reg*) np->vaddr;
 
-#endif /* !defined NCR_IOMAPPED */
+#endif /* !defined SCSI_NCR_IOMAPPED */
 
 	/*
 	**	Try to map the controller chip into iospace.
 	*/
 
 	request_region(device->slot.io_port, 128, "ncr53c8xx");
-	np->port = device->slot.io_port;
-	np->myaddr = 255;
+	np->base_io = device->slot.io_port;
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
 	if (nvram) {
 		switch(nvram->type) {
 		case SCSI_NCR_SYMBIOS_NVRAM:
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+			ncr_display_Symbios_nvram(&nvram->data.Symbios);
 #endif
 			break;
 		case SCSI_NCR_TEKRAM_NVRAM:
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+			ncr_display_Tekram_nvram(&nvram->data.Tekram);
 #endif
 			break;
 		default:
@@ -4493,29 +3697,47 @@
 	instance->this_id       = np->myaddr;
 	instance->max_id	= np->maxwide ? 16 : 8;
 	instance->max_lun	= SCSI_NCR_MAX_LUN;
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29)
+	instance->base		= (unsigned long) np->reg;
+#else
 	instance->base		= (char *) np->reg;
 #endif
+#endif
 	instance->irq		= device->slot.irq;
 	instance->unique_id	= device->slot.io_port;
 	instance->io_port	= device->slot.io_port;
 	instance->n_io_port	= 128;
 	instance->dma_channel	= 0;
+	instance->cmd_per_lun	= MAX_TAGS;
+	instance->can_queue	= (MAX_START-4);
 	instance->select_queue_depths = ncr53c8xx_select_queue_depths;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	np->check_integrity	  = 0;
+	instance->check_integrity = 0;
+
+#ifdef SCSI_NCR_ENABLE_INTEGRITY_CHECK
+	if ( !(driver_setup.bus_check & 0x04) ) {
+		np->check_integrity	  = 1;
+		instance->check_integrity = 1;
+	}
+#endif
+#endif
 	/*
 	**	Patch script to physical addresses
 	*/
 	ncr_script_fill (&script0, &scripth0);
 
 	np->scripth	= np->scripth0;
-	np->p_scripth	= vtophys(np->scripth);
+	np->p_scripth	= vtobus(np->scripth);
 
-	np->p_script	= (np->paddr2) ? bus_dvma_to_mem(np->paddr2) : vtophys(np->script0);
+	np->p_script	= (np->paddr2) ?
+			  pcivtobus(np->paddr2) : vtobus(np->script0);
 
 	ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
 	ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
-	np->ccb->p_ccb		= vtophys (np->ccb);
+	np->ccb->p_ccb		= vtobus (np->ccb);
 
 	/*
 	**    Patch the script for LED support.
@@ -4651,19 +3873,21 @@
 attach_error:
 	if (!instance) return -1;
 	printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
-#ifndef NCR_IOMAPPED
+	if (!np)
+		goto unregister;
+#ifndef SCSI_NCR_IOMAPPED
 	if (np->vaddr) {
 #ifdef DEBUG_NCR53C8XX
 		printk(KERN_DEBUG "%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 #endif
 		unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
 	}
-#endif /* !NCR_IOMAPPED */
-	if (np->port) {
+#endif /* !SCSI_NCR_IOMAPPED */
+	if (np->base_io) {
 #ifdef DEBUG_NCR53C8XX
-		printk(KERN_DEBUG "%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+		printk(KERN_DEBUG "%s: releasing IO region %x[%d]\n", ncr_name(np), np->base_io, 128);
 #endif
-		release_region(np->port, 128);
+		release_region(np->base_io, 128);
 	}
 	if (np->irq) {
 #ifdef DEBUG_NCR53C8XX
@@ -4676,6 +3900,15 @@
 #endif
 		free_irq(np->irq, np);
 	}
+	if (np->scripth0)
+		m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+	if (np->script0)
+		m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+	if (np->ccb)
+		m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+	m_free_dma(np, sizeof(struct ncb), "NCB");
+
+unregister:
 	scsi_unregister(instance);
 
         return -1;
@@ -4703,6 +3936,7 @@
 */
 static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
 {
+	unmap_scsi_data(np, cmd);
 	cmd->host_scribble = (char *) np->done_list;
 	np->done_list = cmd;
 }
@@ -4718,6 +3952,270 @@
 	}
 }
 
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message for integrity check,
+**	if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+static int ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+	u_char no_increase;
+
+	if (tp->inq_done) {
+
+		if (!tp->ic_maximums_set) {
+			tp->ic_maximums_set = 1;
+
+			/* check target and host adapter capabilities */
+			if ( (tp->inq_byte7 & INQ7_WIDE16) && 
+					np->maxwide && tp->usrwide ) 
+				tp->ic_max_width = 1;
+			else
+				tp->ic_max_width = 0;
+
+			if ((tp->inq_byte7 & INQ7_SYNC) && tp->maxoffs) {
+				tp->ic_min_sync   = (tp->minsync < np->minsync) ?
+							np->minsync : tp->minsync;
+			}
+			else 
+				tp->ic_min_sync   = 255;
+			
+			tp->period   = 1;
+			tp->widedone = 1;
+		}
+
+		if (DEBUG_FLAGS & DEBUG_IC) {
+			printk("%s: cmd->ic_nego %d, 1st byte 0x%2X\n",
+				ncr_name(np), cmd->ic_nego, cmd->cmnd[0]);
+		}
+
+		/* First command from integrity check routine will request
+		 * a PPR message.  Disable.
+		 */
+		if ((cmd->ic_nego & NS_PPR) == NS_PPR)
+			cmd->ic_nego &= ~NS_PPR;
+		/* Previous command recorded a parity or an initiator
+		 * detected error condition. Force bus to narrow for this
+		 * target. Clear flag. Negotation on request sense.
+		 * Note: kernel forces 2 bus resets :o( but clears itself out.
+		 * Minor bug? in scsi_obsolete.c (ugly)
+		 */
+		if (np->check_integ_par) {
+			printk("%s: Parity Error. Target set to narrow.\n",
+				ncr_name(np));
+			tp->ic_max_width = 0;
+			tp->widedone = tp->period = 0;
+		}
+		
+		/* In case of a bus reset, ncr_negotiate will reset 
+                 * the flags tp->widedone and tp->period to 0, forcing
+		 * a new negotiation. 
+		 */
+		no_increase = 0;
+		if (tp->widedone == 0) {
+			cmd->ic_nego = NS_WIDE;
+			tp->widedone = 1;
+			no_increase = 1;
+		}
+		else if (tp->period == 0) {
+			cmd->ic_nego = NS_SYNC;
+			tp->period = 1;
+			no_increase = 1;
+		}
+			
+		switch (cmd->ic_nego) {
+		case NS_WIDE:
+			/*
+			**	negotiate wide transfers ?
+			**	Do NOT negotiate if device only supports
+			**	narrow.	
+			*/
+			if (tp->ic_max_width | np->check_integ_par) {
+				nego = NS_WIDE;
+
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 2;
+				msgptr[msglen++] = M_X_WIDE_REQ;
+				msgptr[msglen++] = cmd->ic_nego_width & tp->ic_max_width;
+			}
+			else
+				cmd->ic_nego_width &= tp->ic_max_width;
+              
+			break;
+
+		case NS_SYNC:
+			/*
+			**	negotiate synchronous transfers?
+			**	Target must support sync transfers.
+			**
+			**	If period becomes longer than max, reset to async
+			*/
+
+			if (tp->inq_byte7 & INQ7_SYNC) {
+
+				nego = NS_SYNC;
+
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 3;
+				msgptr[msglen++] = M_X_SYNC_REQ;
+
+				switch (cmd->ic_nego_sync) {
+				case 2: /* increase the period */
+					if (!no_increase) {
+					  if (tp->ic_min_sync <= 0x0A)
+					      tp->ic_min_sync = 0x0C;
+					  else if (tp->ic_min_sync <= 0x0C)
+					      tp->ic_min_sync = 0x19;
+					  else if (tp->ic_min_sync <= 0x19)
+					      tp->ic_min_sync *= 2;
+					  else {
+						tp->ic_min_sync = 255;
+						cmd->ic_nego_sync = 0;
+						tp->maxoffs = 0;
+					   }
+					}
+					msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0;
+					msgptr[msglen++] = tp->maxoffs;
+					break;
+
+				case 1: /* nego. to maximum */
+					msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0;
+					msgptr[msglen++] = tp->maxoffs;
+					break;
+
+				case 0:	/* nego to async */
+				default:
+					msgptr[msglen++] = 0;
+					msgptr[msglen++] = 0;
+					break;
+				};
+			}
+			else
+				cmd->ic_nego_sync = 0;
+			break;
+
+		case NS_NOCHANGE:
+		default:
+			break;
+		};
+	};
+
+	cp->nego_status = nego;
+	np->check_integ_par = 0;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+			  "wide/narrow msgout": "sync/async msgout", msgptr);
+		};
+	};
+
+	return msglen;
+}
+#endif /* SCSI_NCR_INTEGRITY_CHECKING */
+
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+
+static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+
+	if (tp->inq_done) {
+
+		/*
+		**	negotiate wide transfers ?
+		*/
+
+		if (!tp->widedone) {
+			if (tp->inq_byte7 & INQ7_WIDE16) {
+				nego = NS_WIDE;
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+				if (tp->ic_done)
+		       			 tp->usrwide &= tp->ic_max_width;
+#endif
+			} else
+				tp->widedone=1;
+
+		};
+
+		/*
+		**	negotiate synchronous transfers?
+		*/
+
+		if (!nego && !tp->period) {
+			if (tp->inq_byte7 & INQ7_SYNC) {
+				nego = NS_SYNC;
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+				if ((tp->ic_done) &&
+				 	      (tp->minsync < tp->ic_min_sync))
+		       			 tp->minsync = tp->ic_min_sync;
+#endif
+			} else {
+				tp->period  =0xffff;
+				PRINT_TARGET(np, cp->target);
+				printk ("target did not report SYNC.\n");
+			};
+		};
+	};
+
+	switch (nego) {
+	case NS_SYNC:
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 3;
+		msgptr[msglen++] = M_X_SYNC_REQ;
+		msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
+		msgptr[msglen++] = tp->maxoffs;
+		break;
+	case NS_WIDE:
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 2;
+		msgptr[msglen++] = M_X_WIDE_REQ;
+		msgptr[msglen++] = tp->usrwide;
+		break;
+	};
+
+	cp->nego_status = nego;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+					  "wide msgout":"sync_msgout", msgptr);
+		};
+	};
+
+	return msglen;
+}
+
+
 
 /*==========================================================
 **
@@ -4736,7 +4234,7 @@
 	ccb_p cp;
 
 	int	segments;
-	u_char	nego, idmsg, *msgptr;
+	u_char	idmsg, *msgptr;
 	u_int  msglen;
 	int	direction;
 	u_int32	lastp, goalp;
@@ -4782,9 +4280,10 @@
 	**
 	**----------------------------------------------------
 	*/
-	if (np->settle_time && cmd->timeout_per_command >= HZ &&
-		np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
-		np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+	if (np->settle_time && cmd->timeout_per_command >= HZ) {
+		u_long tlimit = ktime_get(cmd->timeout_per_command - HZ);
+		if (ktime_dif(np->settle_time, tlimit) > 0)
+			np->settle_time = tlimit;
 	}
 
         if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
@@ -4806,63 +4305,6 @@
 	}
 #endif
 
-	/*---------------------------------------------------
-	**
-	**	timestamp
-	**
-	**----------------------------------------------------
-	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
-	cp->phys.header.stamp.start = jiffies;
-#endif
-
-	/*---------------------------------------------------
-	**
-	**	negotiation required?
-	**
-	**---------------------------------------------------
-	*/
-
-	nego = 0;
-
-	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) {
-
-		/*
-		**	negotiate wide transfers ?
-		*/
-
-		if (!tp->widedone) {
-			if (tp->inq_byte7 & INQ7_WIDE16) {
-				nego = NS_WIDE;
-			} else
-				tp->widedone=1;
-		};
-
-		/*
-		**	negotiate synchronous transfers?
-		*/
-
-		if (!nego && !tp->period) {
-			if (tp->inq_byte7 & INQ7_SYNC) {
-				nego = NS_SYNC;
-			} else {
-				tp->period  =0xffff;
-				PRINT_TARGET(np, cmd->target);
-				printk ("device did not report SYNC.\n");
-			};
-		};
-
-		/*
-		**	remember nego is pending for the target.
-		**	Avoid to start a nego for all queued commands 
-		**	when tagged command queuing is enabled.
-		*/
-
-		if (nego)
-			tp->nego_cp = cp;
-	};
-
 	/*----------------------------------------------------
 	**
 	**	Build the identify / tag / sdtr message
@@ -4887,7 +4329,7 @@
 		**	Force ordered tag if necessary to avoid timeouts 
 		**	and to preserve interactivity.
 		*/
-		if (lp && time_before_eq(lp->tags_stime + 3*HZ, jiffies)) {
+		if (lp && ktime_exp(lp->tags_stime)) {
 			if (lp->tags_smap) {
 				order = M_ORDERED_TAG;
 				if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ 
@@ -4895,7 +4337,7 @@
 					printk("ordered tag forced.\n");
 				}
 			}
-			lp->tags_stime = jiffies;
+			lp->tags_stime = ktime_get(3*HZ);
 			lp->tags_smap = lp->tags_umap;
 		}
 
@@ -4922,34 +4364,6 @@
 		msgptr[msglen++] = (cp->tag << 1) + 1;
 	}
 
-	switch (nego) {
-	case NS_SYNC:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 3;
-		msgptr[msglen++] = M_X_SYNC_REQ;
-		msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
-		msgptr[msglen++] = tp->maxoffs;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-5]);
-			printk (".\n");
-		};
-		break;
-	case NS_WIDE:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 2;
-		msgptr[msglen++] = M_X_WIDE_REQ;
-		msgptr[msglen++] = tp->usrwide;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-4]);
-			printk (".\n");
-		};
-		break;
-	};
-
 	/*----------------------------------------------------
 	**
 	**	Build the data descriptors
@@ -4957,78 +4371,128 @@
 	**----------------------------------------------------
 	*/
 
-	segments = ncr_scatter (cp, cp->cmd);
-
-	if (segments < 0) {
-		ncr_free_ccb(np, cp);
-		return(DID_ERROR);
+	direction = scsi_data_direction(cmd);
+	if (direction != SCSI_DATA_NONE) {
+		segments = ncr_scatter (np, cp, cp->cmd);
+		if (segments < 0) {
+			ncr_free_ccb(np, cp);
+			return(DID_ERROR);
+		}
 	}
-
-	/*----------------------------------------------------
-	**
-	**	Guess xfer direction.
-	**	Spare some CPU by testing here frequently opcode.
-	**
-	**----------------------------------------------------
-	*/
-	if (!cp->data_len)
-		direction = 0;
 	else {
-		switch((int) cmd->cmnd[0]) {
-		case 0x08:  /*	READ(6)				08 */
-		case 0x28:  /*	READ(10)			28 */
-		case 0xA8:  /*	READ(12)			A8 */
-			direction = XFER_IN;
-			break;
-		case 0x0A:  /*	WRITE(6)			0A */
-		case 0x2A:  /*	WRITE(10)			2A */
-		case 0xAA:  /*	WRITE(12)			AA */
-			direction = XFER_OUT;
-			break;
-		default:
-			direction = (XFER_IN|XFER_OUT);
-			break;
-		}
+		cp->data_len = 0;
+		segments = 0;
 	}
 
-	/*----------------------------------------------------
+	/*---------------------------------------------------
+	**
+	**	negotiation required?
 	**
-	**	Set the SAVED_POINTER.
+	**	(nego_status is filled by ncr_prepare_nego())
 	**
-	**----------------------------------------------------
+	**---------------------------------------------------
 	*/
 
-	/*
-	**	Default to no data transfer.
-	*/
-	lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+	cp->nego_status = 0;
 
-	/*
-	**	Compute data out pointers, if needed.
-	*/
-	if (direction & XFER_OUT) {
-		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
-		if (segments <= MAX_SCATTERL)
-			lastp = goalp - 8 - (segments * 16);
-		else {
-			lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
-			lastp -= (segments - MAX_SCATTERL) * 16;
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	if ((np->check_integrity && tp->ic_done) || !np->check_integrity) {
+		 if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+			msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
+		 }
+	}
+	else if (np->check_integrity && (cmd->ic_in_progress)) { 
+		msglen += ncr_ic_nego (np, cp, cmd, msgptr + msglen);
+        }
+	else if (np->check_integrity && cmd->ic_complete) {
+                /*
+                 * Midlayer signal to the driver that all of the scsi commands
+                 * for the integrity check have completed. Save the negotiated
+                 * parameters (extracted from sval and wval). 
+                 */ 
+
+		{
+			u_char idiv;
+			idiv = (tp->wval>>4) & 0x07;
+			if ((tp->sval&0x1f) && idiv )
+				tp->period = (((tp->sval>>5)+4)  
+						*div_10M[idiv-1])/np->clock_khz;
+			else
+				tp->period = 0xffff;
 		}
 		/*
-		**	If actual data direction is unknown, save pointers 
-		**	in header. The SCRIPTS will swap them to current 
-		**	if target decision will be data out.
-		*/
-		if (direction & XFER_IN) {
-			cp->phys.header.wgoalp	= cpu_to_scr(goalp);
-			cp->phys.header.wlastp	= cpu_to_scr(lastp);
+		 * tp->period contains 10 times the transfer period, 
+		 * which itself is 4 * the requested negotiation rate.
+		 */
+		if	(tp->period <= 250)	tp->ic_min_sync = 10;
+		else if	(tp->period <= 303)	tp->ic_min_sync = 11;
+		else if	(tp->period <= 500)	tp->ic_min_sync = 12;
+		else				
+				tp->ic_min_sync = (tp->period + 40 - 1) / 40;
+
+
+		/*
+                 * Negotiation for this target it complete.
+                 */
+		tp->ic_max_width =  (tp->wval & EWS) ? 1: 0;
+		tp->ic_done = 1;
+		tp->widedone = 1;
+
+		printk("%s: Integrity Check Complete: \n", ncr_name(np)); 
+
+		printk("%s: %s %s SCSI", ncr_name(np), 
+				(tp->sval&0x1f)?"SYNC":"ASYNC",
+				tp->ic_max_width?"WIDE":"NARROW");
+
+		if (tp->sval&0x1f) {
+                        u_long mbs = 10000 * (tp->ic_max_width + 1);
+
+                        printk(" %d.%d  MB/s", 
+                                (int) (mbs / tp->period), (int) (mbs % tp->period));
+
+			printk(" (%d ns, %d offset)\n", 
+				  tp->period/10, tp->sval&0x1f);
 		}
+		else
+                        printk(" %d MB/s. \n ", (tp->ic_max_width+1)*5);
+        }
+#else
+	if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+		msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
 	}
+#endif /* SCSI_NCR_INTEGRITY_CHECKING */
+
+	/*----------------------------------------------------
+	**
+	**	Determine xfer direction.
+	**
+	**----------------------------------------------------
+	*/
+	if (!cp->data_len)
+		direction = SCSI_DATA_NONE;
 
 	/*
-	**	Compute data in pointers, if needed.
+	**	If data direction is UNKNOWN, speculate DATA_READ 
+	**	but prepare alternate pointers for WRITE in case 
+	**	of our speculation will be just wrong.
+	**	SCRIPTS will swap values if needed.
 	*/
-	if (direction & XFER_IN) {
+	switch(direction) {
+	case SCSI_DATA_UNKNOWN:
+	case SCSI_DATA_WRITE:
+		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
+		if (segments <= MAX_SCATTERL)
+			lastp = goalp - 8 - (segments * 16);
+		else {
+			lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
+			lastp -= (segments - MAX_SCATTERL) * 16;
+		}
+		if (direction != SCSI_DATA_UNKNOWN)
+			break;
+		cp->phys.header.wgoalp	= cpu_to_scr(goalp);
+		cp->phys.header.wlastp	= cpu_to_scr(lastp);
+		/* fall through */
+	case SCSI_DATA_READ:
 		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
 		if (segments <= MAX_SCATTERL)
 			lastp = goalp - 8 - (segments * 16);
@@ -5036,6 +4500,11 @@
 			lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
 			lastp -= (segments - MAX_SCATTERL) * 16;
 		}
+		break;
+	default:
+	case SCSI_DATA_NONE:
+		lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+		break;
 	}
 
 	/*
@@ -5045,7 +4514,7 @@
 	cp->phys.header.lastp = cpu_to_scr(lastp);
 	cp->phys.header.goalp = cpu_to_scr(goalp);
 
-	if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+	if (direction == SCSI_DATA_UNKNOWN)
 		cp->phys.header.savep = 
 			cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
 	else
@@ -5088,19 +4557,19 @@
 	/*
 	**	command
 	*/
-	cp->phys.cmd.addr		= cpu_to_scr(vtophys (&cmd->cmnd[0]));
+	memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
+	cp->phys.cmd.addr		= cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
 	cp->phys.cmd.size		= cpu_to_scr(cmd->cmd_len);
 
 	/*
 	**	status
 	*/
 	cp->actualquirks		= tp->quirks;
-	cp->host_status			= nego ? HS_NEGOTIATE : HS_BUSY;
+	cp->host_status			= cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
 	cp->scsi_status			= S_ILLEGAL;
 	cp->parity_status		= 0;
 
 	cp->xerr_status			= XE_OK;
-	cp->nego_status			= nego;
 #if 0
 	cp->sync_status			= tp->sval;
 	cp->wide_status			= tp->wval;
@@ -5116,12 +4585,6 @@
 	/*
 	**	activate this job.
 	*/
-
-	/* Compute a time limit greater than the middle-level driver one */
-	if (cmd->timeout_per_command > 0)
-		cp->tlimit	= jiffies + cmd->timeout_per_command + HZ;
-	else
-		cp->tlimit	= jiffies + 86400 * HZ;/* No timeout=24 hours */
 	cp->magic		= CCB_MAGIC;
 
 	/*
@@ -5228,7 +4691,7 @@
 	u_int32 term;
 	int retv = 0;
 
-	np->settle_time	= jiffies + settle_delay * HZ;
+	np->settle_time	= ktime_get(settle_delay * HZ);
 
 	if (bootverbose > 1)
 		printk("%s: resetting, "
@@ -5258,11 +4721,13 @@
 	**	We are expecting RESET to be TRUE and other signals to be 
 	**	FALSE.
 	*/
-	term =	INB(nc_sstat0);				/* rst, sdp0 */
-	term =	((term & 2) << 7) + ((term & 1) << 16);
-	term |= ((INB(nc_sstat2) & 0x01) << 25) |	/* sdp1 */
-		(INW(nc_sbdl) << 9) |			/* d15-0 */
-		INB(nc_sbcl);	/* req, ack, bsy, sel, atn, msg, cd, io */
+
+	term =	INB(nc_sstat0);
+	term =	((term & 2) << 7) + ((term & 1) << 17);	/* rst sdp0 */
+	term |= ((INB(nc_sstat2) & 0x01) << 26) |	/* sdp1     */
+		((INW(nc_sbdl) & 0xff)   << 9)  |	/* d7-0     */
+		((INW(nc_sbdl) & 0xff00) << 10) |	/* d15-8    */
+		INB(nc_sbcl);	/* req ack bsy sel atn msg cd io    */
 
 	if (!(np->features & FE_WIDE))
 		term &= 0x3ffff;
@@ -5403,7 +4868,6 @@
 	**	script to abort the command.
 	*/
 
-	cp->tlimit = 0;
 	switch(cp->host_status) {
 	case HS_BUSY:
 	case HS_NEGOTIATE:
@@ -5516,17 +4980,17 @@
 	**	Release Memory mapped IO region and IO mapped region
 	*/
 
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 #ifdef DEBUG_NCR53C8XX
 	printk("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 #endif
 	unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
-#endif /* !NCR_IOMAPPED */
+#endif /* !SCSI_NCR_IOMAPPED */
 
 #ifdef DEBUG_NCR53C8XX
-	printk("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+	printk("%s: releasing IO region %x[%d]\n", ncr_name(np), np->base_io, 128);
 #endif
-	release_region(np->port, 128);
+	release_region(np->base_io, 128);
 
 	/*
 	**	Free allocated ccb(s)
@@ -5541,7 +5005,7 @@
 #ifdef DEBUG_NCR53C8XX
 	printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
 #endif
-		m_free(cp, sizeof(*cp));
+		m_free_dma(cp, sizeof(*cp), "CCB");
 	}
 
 	/*
@@ -5557,12 +5021,20 @@
 	printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
 #endif
 				if (lp->jump_ccb != &lp->jump_ccb_0)
-					m_free(lp->jump_ccb, 256);
-				m_free(lp, sizeof(*lp));
+					m_free_dma(lp->jump_ccb,256,"JUMP_CCB");
+				m_free_dma(lp, sizeof(*lp), "LCB");
 			}
 		}
 	}
 
+	if (np->scripth0)
+		m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+	if (np->script0)
+		m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+	if (np->ccb)
+		m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+	m_free_dma(np, sizeof(struct ncb), "NCB");
+
 	printk("%s: host resources successfully released\n", ncr_name(np));
 
 	return 1;
@@ -5593,15 +5065,11 @@
 		return;
 
 	/*
-	**	timestamp
-	**	Optional, spare some CPU time
+	**	Print minimal debug information.
 	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncb_profile (np, cp);
-#endif
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
-		printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp & 0xfff,
+		printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp,
 			cp->host_status,cp->scsi_status);
 
 	/*
@@ -5716,6 +5184,7 @@
 		*/
 		if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
 		    cmd->cmnd[4] >= 7 && !cmd->use_sg) {
+			sync_scsi_data(np, cmd);	/* SYNC the data */
 			ncr_setup_lcb (np, cmd->target, cmd->lun,
 				       (char *) cmd->request_buffer);
 		}
@@ -5742,6 +5211,12 @@
 		*/
 		cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
 
+		/*
+		**	Copy back sense data to caller's buffer.
+		*/
+		memcpy(cmd->sense_buffer, cp->sense_buf,
+		       MIN(sizeof(cmd->sense_buffer), sizeof(cp->sense_buf)));
+
 		if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
 			u_char * p = (u_char*) & cmd->sense_buffer;
 			int i;
@@ -5750,7 +5225,13 @@
 			for (i=0; i<14; i++) printk (" %x", *p++);
 			printk (".\n");
 		}
-
+	} else if ((cp->host_status == HS_COMPLETE)
+		&& (cp->scsi_status == S_CONFLICT)) {
+		/*
+		**   Reservation Conflict condition code
+		*/
+		cmd->result = ScsiResult(DID_OK, S_CONFLICT);
+	
 	} else if ((cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == S_BUSY ||
 		    cp->scsi_status == S_QUEUE_FULL)) {
@@ -6073,13 +5554,11 @@
 	/*
 	**	DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
 	**	Disable overlapped arbitration.
-	**	The 896 Rev 1 is also affected by this errata.
+	**	All 896 chips are also affected by this errata.
 	*/
-	if (np->device_id == PCI_DEVICE_ID_NCR_53C875 &&
-	    np->revision_id >= 0x10 && np->revision_id <= 0x15)
+	if (np->device_id == PCI_DEVICE_ID_NCR_53C875)
 		OUTB (nc_ctest0, (1<<5));
-	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
-	         np->revision_id <= 0x1)
+	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896)
 		OUTB (nc_ccntl0, DPR);
 
 	/*
@@ -6114,16 +5593,15 @@
 	/*
 	**    Start script processor.
 	*/
-	MEMORY_BARRIER();
 	if (np->paddr2) {
 		if (bootverbose)
 			printk ("%s: Downloading SCSI SCRIPTS.\n",
 				ncr_name(np));
-		OUTL (nc_scratcha, vtophys(np->script0));
-		OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, start_ram));
+		OUTL (nc_scratcha, vtobus(np->script0));
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, start_ram));
 	}
 	else
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
 }
 
 /*==========================================================
@@ -6593,12 +6071,6 @@
 			tp->usrflag = np->user.data;
 		};
 		break;
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	case UC_CLEARPROF:
-		bzero(&np->profile, sizeof(np->profile));
-		break;
-#endif
 	}
 	np->user.cmd=0;
 }
@@ -6620,7 +6092,7 @@
 
 static void ncr_timeout (ncb_p np)
 {
-	u_long	thistime = jiffies;
+	u_long	thistime = ktime_get(0);
 
 	/*
 	**	If release process in progress, let's go
@@ -6633,7 +6105,7 @@
 		return;
 	}
 
-	np->timer.expires = jiffies + SCSI_NCR_TIMER_INTERVAL;
+	np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
 	add_timer(&np->timer);
 
 	/*
@@ -6661,15 +6133,6 @@
 		**	block ncr interrupts
 		*/
 		np->lasttime = thistime;
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-		/*
-		**	Reset profile data to avoid ugly overflow
-		**	(Limited to 1024 GB for 32 bit architecture)
-		*/
-		if (np->profile.num_kbytes > (~0UL >> 2))
-			bzero(&np->profile, sizeof(np->profile));
-#endif
 	}
 
 #ifdef SCSI_NCR_BROKEN_INTR
@@ -6813,19 +6276,12 @@
 		OUTB (nc_istat, (istat & SIGP) | INTF);
 		istat = INB (nc_istat);
 		if (DEBUG_FLAGS & DEBUG_TINY) printk ("F ");
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-		np->profile.num_fly++;
-#endif
 		ncr_wakeup_done (np);
 	};
 
 	if (!(istat & (SIP|DIP)))
 		return;
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	np->profile.num_int++;
-#endif
-
 	if (istat & CABRT)
 		OUTB (nc_istat, CABRT);
 
@@ -6880,7 +6336,7 @@
 				ncr_name(np), istat, dstat, sist);
 			return;
 		}
-		OUTONB (nc_dcntl, (STD|NOCOM));
+		OUTONB_STD ();
 		return;
 	};
 
@@ -6927,8 +6383,9 @@
 	**	We are more soft for UDC.
 	**=========================================================
 	*/
-	if (jiffies - np->regtime > 10*HZ) {
-		np->regtime = jiffies;
+
+	if (ktime_exp(np->regtime)) {
+		np->regtime = ktime_get(10*HZ);
 		for (i = 0; i<sizeof(np->regdump); i++)
 			((char*)&np->regdump)[i] = INB_OFF(i);
 		np->regdump.nc_dstat = dstat;
@@ -6956,7 +6413,7 @@
 	if (sist & UDC) {
 		printk ("%s: unexpected disconnect\n", ncr_name(np));
 		OUTB (HS_PRT, HS_UNEXPECTED);
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, cleanup));
 		return;
 	};
 
@@ -7008,7 +6465,7 @@
 	**	repair start queue and jump to start point.
 	*/
 
-	OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sto_restart));
+	OUTL_DSP (NCB_SCRIPTH_PHYS (np, sto_restart));
 	return;
 }
 
@@ -7044,7 +6501,7 @@
 		**	Suspend command processing for 1 second and 
 		**	reinitialize all except the chip.
 		*/
-		np->settle_time	= jiffies + HZ;
+		np->settle_time	= ktime_get(1*HZ);
 		ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
 		return 1;
 	}
@@ -7103,6 +6560,14 @@
 	else
 		msg = M_ID_ERROR;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	/*
+	**      Save error message. For integrity check use only.
+	*/
+	if (np->check_integrity)
+		np->check_integ_par = msg;
+#endif
+
 	/*
 	 *	If the NCR stopped on a MOVE ^ DATA_IN, we jump to a 
 	 *	script that will ignore all data in bytes until phase 
@@ -7118,7 +6583,7 @@
 	OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
 
 	np->msgout[0] = msg;
-	OUTL (nc_dsp, jmp);
+	OUTL_DSP (jmp);
 	return 1;
 
 reset_all:
@@ -7147,6 +6612,7 @@
 	u_int32	dsp;
 	u_int32	dsa;
 	u_int32	nxtdsp;
+	u_int32	newtmp;
 	u_int32	*vdsp;
 	u_int32	oadr, olen;
 	u_int32	*tblp;
@@ -7241,11 +6707,11 @@
 		nxtdsp = dsp;
 	}
 	else if (cp) {
-		if	(dsp == vtophys (&cp->patch[2])) {
+		if	(dsp == CCB_PHYS (cp, patch[2])) {
 			vdsp = &cp->patch[0];
 			nxtdsp = scr_to_cpu(vdsp[3]);
 		}
-		else if (dsp == vtophys (&cp->patch[6])) {
+		else if (dsp == CCB_PHYS (cp, patch[6])) {
 			vdsp = &cp->patch[4];
 			nxtdsp = scr_to_cpu(vdsp[3]);
 		}
@@ -7340,7 +6806,11 @@
 	*/
 
 	newcmd = cp->patch;
-	if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+	newtmp = CCB_PHYS (cp, patch);
+	if (newtmp == scr_to_cpu(cp->phys.header.savep)) {
+		newcmd = &cp->patch[4];
+		newtmp = CCB_PHYS (cp, patch[4]);
+	}
 
 	/*
 	**	fillin the commands
@@ -7364,11 +6834,8 @@
 	**	fake the return address (to the patch).
 	**	and restart script processor at dispatcher.
 	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	np->profile.num_break++;
-#endif
-	OUTL (nc_temp, vtophys (newcmd));
-	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+	OUTL (nc_temp, newtmp);
+	OUTL_DSP (NCB_SCRIPT_PHYS (np, dispatch));
 	return;
 
 	/*
@@ -7430,7 +6897,7 @@
 	}
 
 	if (nxtdsp) {
-		OUTL (nc_dsp, nxtdsp);
+		OUTL_DSP (nxtdsp);
 		return;
 	}
 
@@ -7504,7 +6971,7 @@
 		ncr_put_start_queue(np, cp);
 		if (disc_cnt)
 			INB (nc_ctest2);		/* Clear SIGP */
-		OUTL (nc_dsp,	NCB_SCRIPT_PHYS (np, reselect));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, reselect));
 		return;
 	case S_TERMINATED:
 	case S_CHECK_COND:
@@ -7536,15 +7003,14 @@
 		*/
 		cp->sensecmd[0]		= 0x03;
 		cp->sensecmd[1]		= cmd->lun << 5;
-		cp->sensecmd[4]		= sizeof(cmd->sense_buffer);
+		cp->sensecmd[4]		= sizeof(cp->sense_buf);
 
 		/*
 		**	sense data
 		*/
-		cp->phys.sense.addr	=
-				cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
-		cp->phys.sense.size	=
-				cpu_to_scr(sizeof(cmd->sense_buffer));
+		bzero(cp->sense_buf, sizeof(cp->sense_buf));
+		cp->phys.sense.addr	= cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+		cp->phys.sense.size	= cpu_to_scr(sizeof(cp->sense_buf));
 
 		/*
 		**	requeue the command.
@@ -7573,12 +7039,12 @@
 
 		ncr_put_start_queue(np, cp);
 
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
 		return;
 	}
 
 out:
-	OUTONB (nc_dcntl, (STD|NOCOM));
+	OUTONB_STD ();
 	return;
 }
 
@@ -7609,6 +7075,16 @@
 	return (1);
 }
 
+static void ncr_print_msg ( ccb_p cp, char *label, u_char *msg)
+{
+	if (cp)
+		PRINT_ADDR(cp->cmd);
+	if (label)
+		printk("%s: ", label);
+	
+	(void) ncr_show_msg (msg);
+	printk(".\n");
+}
 
 void ncr_int_sir (ncb_p np)
 {
@@ -7631,7 +7107,7 @@
 		**	We just assume lun=0, 1 CCB, no tag.
 		*/
 		if (tp->lp[0]) { 
-			OUTL (nc_dsp, scr_to_cpu(tp->lp[0]->jump_ccb[0]));
+			OUTL_DSP (scr_to_cpu(tp->lp[0]->jump_ccb[0]));
 			return;
 		}
 	case SIR_RESEL_BAD_TARGET:	/* Will send a TARGET RESET message */
@@ -7853,13 +7329,13 @@
 					**	Answer wasn't acceptable.
 					*/
 					ncr_setsync (np, cp, 0, 0xe0);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+					OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
 					ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
+					OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
 
@@ -7892,7 +7368,7 @@
 		}
 
 		if (!ofs) {
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+			OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad));
 			return;
 		}
 		np->msgin [0] = M_NOOP;
@@ -7950,13 +7426,13 @@
 					**	Answer wasn't acceptable.
 					*/
 					ncr_setwide (np, cp, 0, 1);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+					OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
 					ncr_setwide (np, cp, wide, 1);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
+					OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
 
@@ -8064,7 +7540,7 @@
 	};
 
 out:
-	OUTONB (nc_dcntl, (STD|NOCOM));
+	OUTONB_STD ();
 }
 
 /*==========================================================
@@ -8164,7 +7640,7 @@
 	if (lp) {
 		if (tag != NO_TAG) {
 			++lp->ia_tag;
-			if (lp->ia_tag == SCSI_NCR_MAX_TAGS)
+			if (lp->ia_tag == MAX_TAGS)
 				lp->ia_tag = 0;
 			lp->tags_umap |= (((tagmap_t) 1) << tag);
 		}
@@ -8212,7 +7688,7 @@
 	if (lp) {
 		if (cp->tag != NO_TAG) {
 			lp->cb_tags[lp->if_tag++] = cp->tag;
-			if (lp->if_tag == SCSI_NCR_MAX_TAGS)
+			if (lp->if_tag == MAX_TAGS)
 				lp->if_tag = 0;
 			lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag);
 			lp->tags_smap &= lp->tags_umap;
@@ -8253,7 +7729,7 @@
 
 
 #define ncr_reg_bus_addr(r) \
-	(bus_dvma_to_mem(np->paddr) + offsetof (struct ncr_reg, r))
+	(pcivtobus(np->paddr) + offsetof (struct ncr_reg, r))
 
 /*------------------------------------------------------------------------
 **	Initialize the fixed part of a CCB structure.
@@ -8267,7 +7743,7 @@
 	/*
 	**	Remember virtual and bus address of this ccb.
 	*/
-	cp->p_ccb 	   = vtophys(cp);
+	cp->p_ccb 	   = vtobus(cp);
 	cp->phys.header.cp = cp;
 
 	/*
@@ -8282,10 +7758,10 @@
 	**	JUMP @(sched_point)
 	*/
 	cp->start.setup_dsa[0]	 = cpu_to_scr(copy_4);
-	cp->start.setup_dsa[1]	 = cpu_to_scr(vtophys(&cp->start.p_phys));
+	cp->start.setup_dsa[1]	 = cpu_to_scr(CCB_PHYS(cp, start.p_phys));
 	cp->start.setup_dsa[2]	 = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
 	cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
-	cp->start.p_phys	 = cpu_to_scr(vtophys(&cp->phys));
+	cp->start.p_phys	 = cpu_to_scr(CCB_PHYS(cp, phys));
 
 	bcopy(&cp->start, &cp->restart, sizeof(cp->restart));
 
@@ -8308,15 +7784,10 @@
 	/*
 	**	Allocate memory for this CCB.
 	*/
-	cp = m_alloc(sizeof(struct ccb), 5);
+	cp = m_calloc_dma(sizeof(struct ccb), "CCB");
 	if (!cp)
 		return;
 
-	if (DEBUG_FLAGS & DEBUG_ALLOC) {
-		PRINT_LUN(np, tn, ln);
-		printk ("new ccb @%p.\n", cp);
-	}
-
 	/*
 	**	Count it and initialyze it.
 	*/
@@ -8374,7 +7845,7 @@
 	**	COPY @(tp->sval), @(sxfer)
 	*/
 	tp->getscr[0] =	cpu_to_scr(copy_1);
-	tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+	tp->getscr[1] = cpu_to_scr(vtobus (&tp->sval));
 	tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
   
 	/*
@@ -8382,7 +7853,7 @@
 	**	COPY @(tp->wval), @(scntl3)
 	*/
 	tp->getscr[3] =	cpu_to_scr(copy_1);
-	tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+	tp->getscr[4] = cpu_to_scr(vtobus (&tp->wval));
 	tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
 
 	/*
@@ -8407,7 +7878,7 @@
 	/*
 	**	Link this target control block to the JUMP chain.
 	*/
-	np->jump_tcb[th].l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+	np->jump_tcb[th].l_paddr = cpu_to_scr(vtobus (&tp->jump_tcb));
 
 	/*
 	**	These assert's should be moved at driver initialisations.
@@ -8442,17 +7913,12 @@
 	/*
 	**	Allocate the lcb.
 	*/
-	lp = m_alloc(sizeof(struct lcb), 3);
+	lp = m_calloc_dma(sizeof(struct lcb), "LCB");
 	if (!lp)
 		goto fail;
 	bzero(lp, sizeof(*lp));
 	tp->lp[ln] = lp;
 
-	if (DEBUG_FLAGS & DEBUG_ALLOC) {
-		PRINT_LUN(np, tn, ln);
-		printk ("new lcb @%p.\n", lp);
-	}
-
 	/*
 	**	Initialize the target control block if not yet.
 	*/
@@ -8473,7 +7939,7 @@
 	*/
 	lp->maxnxs	= 1;
 	lp->jump_ccb	= &lp->jump_ccb_0;
-	lp->p_jump_ccb	= cpu_to_scr(vtophys(lp->jump_ccb));
+	lp->p_jump_ccb	= cpu_to_scr(vtobus(lp->jump_ccb));
 
 	/*
 	**	Initilialyze the reselect script:
@@ -8491,7 +7957,7 @@
 	lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
 
 	lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
-	lp->load_jump_ccb[1] = cpu_to_scr(vtophys (&lp->p_jump_ccb));
+	lp->load_jump_ccb[1] = cpu_to_scr(vtobus (&lp->p_jump_ccb));
 	lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
 
 	lp->jump_tag.l_cmd   = cpu_to_scr(SCR_JUMP);
@@ -8500,7 +7966,7 @@
 	/*
 	**	Link this lun control block to the JUMP chain.
 	*/
-	tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
+	tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtobus (&lp->jump_lcb));
 
 	/*
 	**	Initialize command queuing control.
@@ -8584,19 +8050,19 @@
 	*/
 	if ((inq_byte7 & INQ7_QUEUE) && lp->jump_ccb == &lp->jump_ccb_0) {
 		int i;
-		lp->jump_ccb = m_alloc(256, 8);
+		lp->jump_ccb = m_calloc_dma(256, "JUMP_CCB");
 		if (!lp->jump_ccb) {
 			lp->jump_ccb = &lp->jump_ccb_0;
 			goto fail;
 		}
-		lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+		lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
 		for (i = 0 ; i < 64 ; i++)
 			lp->jump_ccb[i] =
 				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
-		for (i = 0 ; i < SCSI_NCR_MAX_TAGS ; i++)
+		for (i = 0 ; i < MAX_TAGS ; i++)
 			lp->cb_tags[i] = i;
-		lp->maxnxs = SCSI_NCR_MAX_TAGS;
-		lp->tags_stime = jiffies;
+		lp->maxnxs = MAX_TAGS;
+		lp->tags_stime = ktime_get(3*HZ);
 	}
 
 	/*
@@ -8641,7 +8107,7 @@
 **	sizes to the data segment array.
 */
 
-static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+static	int	ncr_scatter(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
 {
 	struct scr_tblmove *data;
 	int segment	= 0;
@@ -8652,8 +8118,10 @@
 
 	if (!use_sg) {
 		if (cmd->request_bufflen) {
+			u_long baddr = map_scsi_single_data(np, cmd);
+
 			data = &data[MAX_SCATTER - 1];
-			data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+			data[0].addr = cpu_to_scr(baddr);
 			data[0].size = cpu_to_scr(cmd->request_bufflen);
 			cp->data_len = cmd->request_bufflen;
 			segment = 1;
@@ -8662,13 +8130,16 @@
 	else if (use_sg <= MAX_SCATTER) {
 		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
 
+		use_sg = map_scsi_sg_data(np, cmd);
 		data = &data[MAX_SCATTER - use_sg];
+
 		while (segment < use_sg) {
-			data[segment].addr =
-				cpu_to_scr(vtophys(scatter[segment].address));
-			data[segment].size =
-				cpu_to_scr(scatter[segment].length);
-			cp->data_len	   += scatter[segment].length;
+			u_long baddr = scsi_sg_dma_address(&scatter[segment]);
+			unsigned int len = scsi_sg_dma_len(&scatter[segment]);
+
+			data[segment].addr = cpu_to_scr(baddr);
+			data[segment].size = cpu_to_scr(len);
+			cp->data_len	  += len;
 			++segment;
 		}
 	}
@@ -8690,10 +8161,8 @@
 **==========================================================
 */
 
-#ifndef NCR_IOMAPPED
-__initfunc(
-static int ncr_regtest (struct ncb* np)
-)
+#ifndef SCSI_NCR_IOMAPPED
+static int __init ncr_regtest (struct ncb* np)
 {
 	register volatile u_int32 data;
 	/*
@@ -8717,13 +8186,11 @@
 }
 #endif
 
-__initfunc(
-static int ncr_snooptest (struct ncb* np)
-)
+static int __init ncr_snooptest (struct ncb* np)
 {
 	u_int32	ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc;
 	int	i, err=0;
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 	if (np->reg) {
             err |= ncr_regtest (np);
             if (err) return (err);
@@ -8743,7 +8210,7 @@
 	/*
 	**	Start script (exchange values)
 	*/
-	OUTL (nc_dsp, pc);
+	OUTL_DSP (pc);
 	/*
 	**	Wait 'til done (with timeout)
 	*/
@@ -8807,73 +8274,6 @@
 /*==========================================================
 **
 **
-**	Profiling the drivers and targets performance.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-
-/*
-**	Compute the difference in jiffies ticks.
-*/
-
-#define ncr_delta(from, to) \
-	( ((to) && (from))? (to) - (from) : -1 )
-
-#define PROFILE  cp->phys.header.stamp
-static	void ncb_profile (ncb_p np, ccb_p cp)
-{
-	long co, st, en, di, re, post, work, disc;
-	u_int diff;
-
-	PROFILE.end = jiffies;
-
-	st = ncr_delta (PROFILE.start,PROFILE.status);
-	if (st<0) return;	/* status  not reached  */
-
-	co = ncr_delta (PROFILE.start,PROFILE.command);
-	if (co<0) return;	/* command not executed */
-
-	en = ncr_delta (PROFILE.start,PROFILE.end),
-	di = ncr_delta (PROFILE.start,PROFILE.disconnect),
-	re = ncr_delta (PROFILE.start,PROFILE.reselect);
-	post = en - st;
-
-	/*
-	**	@PROFILE@  Disconnect time invalid if multiple disconnects
-	*/
-
-	if (di>=0) disc = re - di; else  disc = 0;
-
-	work = (st - co) - disc;
-
-	diff = (scr_to_cpu(np->disc_phys) - np->disc_ref) & 0xff;
-	np->disc_ref += diff;
-
-	np->profile.num_trans	+= 1;
-	if (cp->cmd) {
-		np->profile.num_kbytes	+= (cp->cmd->request_bufflen >> 10);
-		np->profile.rest_bytes	+= (cp->cmd->request_bufflen & (0x400-1));
-		if (np->profile.rest_bytes >= 0x400) {
-			++np->profile.num_kbytes;
-			np->profile.rest_bytes	-= 0x400;
-		}
-	}
-	np->profile.num_disc	+= diff;
-	np->profile.ms_setup	+= co;
-	np->profile.ms_data	+= work;
-	np->profile.ms_disc	+= disc;
-	np->profile.ms_post	+= post;
-}
-#undef PROFILE
-
-#endif /* SCSI_NCR_PROFILE_SUPPORT */
-
-/*==========================================================
-**
-**
 **	Device lookup.
 **
 **	@GENSCSI@ should be integrated to scsiconf.c
@@ -8982,11 +8382,10 @@
 /*
  *	calculate NCR SCSI clock frequency (in KHz)
  */
-__initfunc(
-static unsigned ncrgetfreq (ncb_p np, int gen)
-)
+static unsigned __init ncrgetfreq (ncb_p np, int gen)
 {
 	unsigned ms = 0;
+	char count = 0;
 
 	/*
 	 * Measure GEN timer delay in order 
@@ -9011,8 +8410,10 @@
 	OUTB (nc_scntl3, 4);	/* set pre-scaler to divide by 3 */
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
 	OUTB (nc_stime1, gen);	/* set to nominal delay of 1<<gen * 125us */
-	while (!(INW(nc_sist) & GEN) && ms++ < 100000)
-		UDELAY (1000);	/* count ms */
+	while (!(INW(nc_sist) & GEN) && ms++ < 100000) {
+		for (count = 0; count < 10; count ++)
+			UDELAY (100);	/* count ms */
+	}
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
  	/*
  	 * set prescaler to divide by whatever 0 means
@@ -9032,9 +8433,7 @@
 /*
  *	Get/probe NCR SCSI clock frequency
  */
-__initfunc(
-static void ncr_getclock (ncb_p np, int mult)
-)
+static void __init ncr_getclock (ncb_p np, int mult)
 {
 	unsigned char scntl3 = INB(nc_scntl3);
 	unsigned char stest1 = INB(nc_stest1);
@@ -9071,970 +8470,36 @@
 
 		if (f1 > f2) f1 = f2;		/* trust lower result	*/
 
-		if	(f1 <	45000)		f1 =  40000;
-		else if (f1 <	55000)		f1 =  50000;
-		else				f1 =  80000;
-
-		if (f1 < 80000 && mult > 1) {
-			if (bootverbose >= 2)
-				printk ("%s: clock multiplier assumed\n", ncr_name(np));
-			np->multiplier	= mult;
-		}
-	} else {
-		if	((scntl3 & 7) == 3)	f1 =  40000;
-		else if	((scntl3 & 7) == 5)	f1 =  80000;
-		else 				f1 = 160000;
-
-		f1 /= np->multiplier;
-	}
-
-	/*
-	**	Compute controller synchronous parameters.
-	*/
-	f1		*= np->multiplier;
-	np->clock_khz	= f1;
-}
-
-/*===================== LINUX ENTRY POINTS SECTION ==========================*/
-
-#ifndef uchar
-#define uchar unsigned char
-#endif
-
-#ifndef ushort
-#define ushort unsigned short
-#endif
-
-#ifndef ulong
-#define ulong unsigned long
-#endif
-
-/* ---------------------------------------------------------------------
-**
-**	Driver setup from the boot command line
-**
-** ---------------------------------------------------------------------
-*/
-
-#ifdef MODULE
-#define	ARG_SEP	' '
-#else
-#define	ARG_SEP	','
-#endif
-
-__initfunc(
-void ncr53c8xx_setup(char *str, int *ints)
-)
-{
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-	char *cur = str;
-	char *pc, *pv;
-	int val;
-	int base;
-	int c;
-	int xi = 0;
-
-	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
-		char *pe;
-
-		val = 0;
-		pv = pc;
-		c = *++pv;
-
-		if	(c == 'n')
-			val = 0;
-		else if	(c == 'y')
-			val = 1;
-		else {
-			base = 0;
-			val = (int) simple_strtoul(pv, &pe, base);
-		}
-		if	(!strncmp(cur, "tags:", 5)) {
-			int i;
-			driver_setup.default_tags = val;
-			if (pe && *pe == '/') {
-				i = 0;
-				while (*pe && *pe != ARG_SEP && 
-					i < sizeof(driver_setup.tag_ctrl)-1) {
-					driver_setup.tag_ctrl[i++] = *pe++;
-				}
-				driver_setup.tag_ctrl[i] = '\0';
-			}
-		}
-		else if	(!strncmp(cur, "mpar:", 5))
-			driver_setup.master_parity	= val;
-		else if	(!strncmp(cur, "spar:", 5))
-			driver_setup.scsi_parity	= val;
-		else if	(!strncmp(cur, "disc:", 5))
-			driver_setup.disconnection	= val;
-		else if	(!strncmp(cur, "specf:", 6))
-			driver_setup.special_features = val;
-		else if	(!strncmp(cur, "ultra:", 6))
-			driver_setup.ultra_scsi	= val;
-		else if	(!strncmp(cur, "fsn:", 4))
-			driver_setup.force_sync_nego	= val;
-		else if	(!strncmp(cur, "revprob:", 8))
-			driver_setup.reverse_probe	= val;
-		else if	(!strncmp(cur, "sync:", 5))
-			driver_setup.default_sync	= val;
-		else if	(!strncmp(cur, "verb:", 5))
-			driver_setup.verbose	= val;
-		else if	(!strncmp(cur, "debug:", 6))
-			driver_setup.debug	= val;
-		else if	(!strncmp(cur, "burst:", 6))
-			driver_setup.burst_max	= val;
-		else if	(!strncmp(cur, "led:", 4))
-			driver_setup.led_pin	= val;
-		else if	(!strncmp(cur, "wide:", 5))
-			driver_setup.max_wide	= val? 1:0;
-		else if	(!strncmp(cur, "settle:", 7))
-			driver_setup.settle_delay= val;
-		else if	(!strncmp(cur, "diff:", 5))
-			driver_setup.diff_support= val;
-		else if	(!strncmp(cur, "irqm:", 5))
-			driver_setup.irqm	= val;
-		else if	(!strncmp(cur, "pcifix:", 7))
-			driver_setup.pci_fix_up	= val;
-		else if	(!strncmp(cur, "buschk:", 7))
-			driver_setup.bus_check	= val;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-		else if	(!strncmp(cur, "nvram:", 6))
-			driver_setup.use_nvram	= val;
-#endif
-
-		else if	(!strncmp(cur, "safe:", 5) && val)
-			memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
-		else if	(!strncmp(cur, "excl:", 5)) {
-			if (xi < SCSI_NCR_MAX_EXCLUDES)
-				driver_setup.excludes[xi++] = val;
-		}
-		else if	(!strncmp(cur, "hostid:", 7))
-			driver_setup.host_id	= val;
-		else
-			printk("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
-
-		if ((cur = strchr(cur, ARG_SEP)) != NULL)
-			++cur;
-	}
-#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
-}
-
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
-	     uchar bus, uchar device_fn, ncr_device *device);
-
-/*
-**   Linux entry point for NCR53C8XX devices detection routine.
-**
-**   Called by the middle-level scsi drivers at initialization time,
-**   or at module installation.
-**
-**   Read the PCI configuration and try to attach each
-**   detected NCR board.
-**
-**   If NVRAM is present, try to attach boards according to 
-**   the used defined boot order.
-**
-**   Returns the number of boards successfully attached.
-*/
-
-__initfunc(
-static void ncr_print_driver_setup(void)
-)
-{
-#define YesNo(y)	y ? 'y' : 'n'
-	printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
-		"burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
-		YesNo(driver_setup.disconnection),
-		driver_setup.special_features,
-		driver_setup.ultra_scsi,
-		driver_setup.default_tags,
-		driver_setup.default_sync,
-		driver_setup.burst_max,
-		YesNo(driver_setup.max_wide),
-		driver_setup.diff_support,
-		YesNo(driver_setup.reverse_probe),
-		driver_setup.bus_check);
-
-	printk ("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
-		"led:%c,settle:%d,irqm:%d,nvram:0x%x,pcifix:0x%x\n",
-		YesNo(driver_setup.master_parity),
-		YesNo(driver_setup.scsi_parity),
-		YesNo(driver_setup.force_sync_nego),
-		driver_setup.verbose,
-		driver_setup.debug,
-		YesNo(driver_setup.led_pin),
-		driver_setup.settle_delay,
-		driver_setup.irqm,
-		driver_setup.use_nvram,
-		driver_setup.pci_fix_up);
-#undef YesNo
-}
-
-/*
-**   NCR53C8XX devices description table and chip ids list.
-*/
-
-static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
-static ushort	ncr_chip_ids[]   __initdata	= SCSI_NCR_CHIP_IDS;
-
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-__initfunc(
-static int
-ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
-)
-{
-	int i, j;
-	int attach_count = 0;
-	ncr_nvram  *nvram;
-	ncr_device *devp = 0;	/* to shut up gcc */
-
-	if (!nvram_index)
-		return 0;
-
-	/* find first Symbios NVRAM if there is one as we need to check it for host boot order */
-	for (i = 0, nvram_index = -1; i < count; i++) {
-		devp  = &device[i];
-		nvram = devp->nvram;
-		if (!nvram)
-			continue;
-		if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
-			if (nvram_index == -1)
-				nvram_index = i;
-#ifdef SCSI_NCR_DEBUG_NVRAM
-			printk("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
-				devp->chip.name, devp->slot.bus, 
-				(int) (devp->slot.device_fn & 0xf8) >> 3, 
-				(int) devp->slot.device_fn & 7);
-			for (j = 0 ; j < 4 ; j++) {
-				Symbios_host *h = &nvram->data.Symbios.host[j];
-			printk("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
-				j,		h->device_id,	h->vendor_id,
-				h->device_fn,	h->io_port,
-				(h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
-			}
-		}
-		else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
-			/* display Tekram nvram data */
-			printk("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
-				devp->chip.name, devp->slot.bus, 
-				(int) (devp->slot.device_fn & 0xf8) >> 3, 
-				(int) devp->slot.device_fn & 7);
-#endif
-		}
-	}
-
-	if (nvram_index >= 0 && nvram_index < count)
-		nvram = device[nvram_index].nvram;
-	else
-		nvram = 0;
-
-	if (!nvram)
-		goto out;
-
-	/* 
-	** check devices in the boot record against devices detected. 
-	** attach devices if we find a match. boot table records that 
-	** do not match any detected devices will be ignored. 
-	** devices that do not match any boot table will not be attached
-	** here but will attempt to be attached during the device table 
-	** rescan.
-	*/
-     	for (i = 0; i < 4; i++) {
-		Symbios_host *h = &nvram->data.Symbios.host[i];
-		for (j = 0 ; j < count ; j++) {
-			devp = &device[j];
-			if (h->device_fn == devp->slot.device_fn &&
-#if 0	/* bus number location in nvram ? */
-			    h->bus	 == devp->slot.bus	 &&
-#endif
-			    h->device_id == devp->chip.device_id)
-				break;
-		}
-		if (j < count && !devp->attach_done) {
-			if (!ncr_attach (tpnt, attach_count, devp))
-				attach_count++;
-			devp->attach_done = 1;
-		}
-	}
-
-out:
-	return attach_count;
-}
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
-
-__initfunc(
-int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
-)
-{
-	int i, j;
-	int chips;
-	int count = 0;
-	uchar bus, device_fn;
-	short index;
-	int attach_count = 0;
-	ncr_device device[8];
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	ncr_nvram  nvram[4];
-	int k, nvrams;
-#endif
-	int hosts;
-
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	int nvram_index = 0;
-#endif
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-	ncr_debug = driver_setup.debug;
-#endif
-
-     tpnt->proc_dir = &proc_scsi_ncr53c8xx;
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-     tpnt->proc_info = ncr53c8xx_proc_info;
-#endif
-
-#if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
-if (ncr53c8xx)
-	ncr53c8xx_setup(ncr53c8xx, (int *) 0);
-#endif
-
-	if (initverbose >= 2)
-		ncr_print_driver_setup();
-
-	/* 
-	** Detect all 53c8xx hosts and then attach them.
-	**
-	** If we are using NVRAM, once all hosts are detected, we need to check
-	** any NVRAM for boot order in case detect and boot order differ and
-	** attach them using the order in the NVRAM.
-	**
-	** If no NVRAM is found or data appears invalid attach boards in the 
-	** the order they are detected.
-	*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92)
-	if (!pci_present())
-#else
-	if (!pcibios_present())
-#endif
-		return 0;
-
-	chips	= sizeof(ncr_chip_ids)	/ sizeof(ncr_chip_ids[0]);
-	hosts	= sizeof(device)	/ sizeof(device[0]);
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	k = 0;
-	if (driver_setup.use_nvram & 0x1)
-		nvrams	= sizeof(nvram)	/ sizeof(nvram[0]);
-	else
-		nvrams	= 0;
-#endif
-
-	for (j = 0; j < chips ; ++j) {
-		i = driver_setup.reverse_probe ? chips-1 - j : j;
-		for (index = 0; ; index++) {
-			char *msg = "";
-			if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-						index, &bus, &device_fn)) ||
-			    (count == hosts))
-				break;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-			device[count].nvram = k < nvrams ? &nvram[k] : 0;
-#else
-			device[count].nvram = 0;
-#endif
-			if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
-				device[count].nvram = 0;
-				continue;
-			}
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-			if (device[count].nvram) {
-				++k;
-				nvram_index |= device[count].nvram->type;
-				switch (device[count].nvram->type) {
-				case SCSI_NCR_TEKRAM_NVRAM:
-					msg = "with Tekram NVRAM";
-					break;
-				case SCSI_NCR_SYMBIOS_NVRAM:
-					msg = "with Symbios NVRAM";
-					break;
-				default:
-					msg = "";
-					device[count].nvram = 0;
-					--k;
-				}
-			}
-#endif
-			printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
-				device[count].chip.name, msg);
-			++count;
-		}
-	}
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
-#endif
-	/* 
-	** rescan device list to make sure all boards attached.
-	** devices without boot records will not be attached yet
-	** so try to attach them here.
-	*/
-	for (i= 0; i < count; i++) {
-		if (!device[i].attach_done && 
-		    !ncr_attach (tpnt, attach_count, &device[i])) {
-			attach_count++;
-		}
-	}
-
-	return attach_count;
-}
-
-/*
-**   Generically read a base address from the PCI configuration space.
-**   Return the offset immediately after the base address that has 
-**   been read. Btw, we blindly assume that the high 32 bits of 64 bit 
-**   base addresses are set to zero on 32 bit architectures.
-**
-*/
-#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
-__initfunc(
-static int 
-pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base)
-)
-{
-	u_int32 tmp;
-
-	pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-	*base = tmp;
-	offset += sizeof(u_int32);
-	if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-		*base |= (((u_long)tmp) << 32);
-#endif
-		offset += sizeof(u_int32);
-	}
-	return offset;
-}
-#else	/* LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) */
-__initfunc(
-static int 
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-)
-{
-	*base = pdev->base_address[index++];
-	if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		*base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
-		++index;
-	}
-	return index;
-}
-#endif
-
-/*
-**   Read and check the PCI configuration for any detected NCR 
-**   boards and save data for attaching after all boards have 
-**   been detected.
-*/
-
-__initfunc(
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
-			      uchar bus, uchar device_fn, ncr_device *device)
-)
-{
-	ushort vendor_id, device_id, command;
-	uchar cache_line_size, latency_timer;
-	uchar revision;
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	struct pci_dev *pdev;
-	uint irq;
-#else
-	uchar irq;
-#endif
-	ulong base, base_2, io_port; 
-	int i;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	ncr_nvram *nvram = device->nvram;
-#endif
-	ncr_chip *chip;
-
-	/*
-	**    Read info from the PCI config space.
-	**    pcibios_read_config_xxx() functions are assumed to be used for 
-	**    successfully detected PCI devices.
-	*/
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	pdev = pci_find_slot(bus, device_fn);
-	vendor_id = pdev->vendor;
-	device_id = pdev->device;
-	irq = pdev->irq;
-	i =	0;
-	i =	pci_get_base_address(pdev, i, &io_port);
-	i =	pci_get_base_address(pdev, i, &base);
-	(void)	pci_get_base_address(pdev, i, &base_2);
-#else
-	pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id);
-	pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id);
-	pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq);
-	i =	PCI_BASE_ADDRESS_0;
-	i =	pci_read_base_address(bus, device_fn, i, &io_port);
-	i =	pci_read_base_address(bus, device_fn, i, &base);
-	(void)	pci_read_base_address(bus, device_fn, i, &base_2);
-#endif
-	pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-	pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION, &revision);
-	pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE,
-				 &cache_line_size);
-	pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER,
-				 &latency_timer);
-
-	/*
-	**	If user excludes this chip, donnot initialize it.
-	*/
-	for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
-		if (driver_setup.excludes[i] ==
-				(io_port & PCI_BASE_ADDRESS_IO_MASK))
-			return -1;
-	}
-	/*
-	 *	Check if the chip is supported
-	 */
-	chip = 0;
-	for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
-		if (device_id != ncr_chip_table[i].device_id)
-			continue;
-		if (revision > ncr_chip_table[i].revision_id)
-			continue;
-		chip = &device->chip;
-		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
-		chip->revision_id = revision;
-		break;
-	}
-
-#if defined(__i386__)
-	/*
-	 *	Ignore Symbios chips controlled by SISL RAID controller.
-	 */
-	if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
-		unsigned int ScriptsSize, MagicValue;
-		vm_offset_t ScriptsRAM;
-
-		if (chip->features & FE_RAM8K)
-			ScriptsSize = 8192;
-		else
-			ScriptsSize = 4096;
-
-		ScriptsRAM = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
-					   ScriptsSize);
-		if (ScriptsRAM) {
-			MagicValue = readl(ScriptsRAM + ScriptsSize - 16);
-			unmap_pci_mem(ScriptsRAM, ScriptsSize);
-			if (MagicValue == 0x52414944)
-				return -1;
-		}
-	}
-#endif
-
-	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
-		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
-
-	if (!chip) {
-		printk("ncr53c8xx: not initializing, device not supported\n");
-		return -1;
-	}
-
-#ifdef __powerpc__
-	/*
-	 *	Several fix-up for power/pc.
-	 *	Should not be performed by the driver.
-	 */
-	if (!(command & PCI_COMMAND_MASTER)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER...");
-		command |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_MASTER)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-	if (!(command & PCI_COMMAND_IO)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_IO...");
-		command |= PCI_COMMAND_IO;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_IO)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-	if (!(command & PCI_COMMAND_MEMORY)) {
-		printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY...");
-		command |= PCI_COMMAND_MEMORY;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-		if (!(command & PCI_COMMAND_MEMORY)) {
-			printk("failed!\n");
-		} else {
-			printk("succeeded.\n");
-		}
-	}
-
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,140)
-	if ( is_prep ) {
-		if (io_port >= 0x10000000) {
-			printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
-			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
-		}
-		if (base >= 0x10000000) {
-			printk("ncr53c8xx: reallocating base (Wacky IBM)");
-			base = (base & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
-		}
-		if (base_2 >= 0x10000000) {
-			printk("ncr53c8xx: reallocating base2 (Wacky IBM)");
-			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, base_2);
-		}
-	}
-#endif
-#endif	/* __powerpc__ */
-
-#ifdef __sparc__
-	/*
-	 *	Severall fix-ups for sparc.
-	 *
-	 *	Should not be performed by the driver, but how can OBP know
-	 *	each and every PCI card, if they don't use Fcode?
-	 */
-
-	base = __pa(base);
-	base_2 = __pa(base_2);
-
-	if (!(command & PCI_COMMAND_MASTER)) {
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
-		command |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-	}
-
-	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
-		command |= PCI_COMMAND_INVALIDATE;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-	}
-
-	if ((chip->features & FE_CLSE) && !cache_line_size) {
-		/* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
-		cache_line_size = 64 / sizeof(u_int32);
-		if (initverbose >= 2)
-			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n",
-			       cache_line_size);
-		pcibios_write_config_byte(bus, device_fn,
-					  PCI_CACHE_LINE_SIZE, cache_line_size);
-		pcibios_read_config_byte(bus, device_fn,
-					 PCI_CACHE_LINE_SIZE, &cache_line_size);
-	}
-
-	if (!latency_timer) {
-		unsigned char min_gnt;
-
-		pcibios_read_config_byte(bus, device_fn,
-					 PCI_MIN_GNT, &min_gnt);
-		if (min_gnt == 0)
-			latency_timer = 128;
-		else
-			latency_timer = ((min_gnt << 3) & 0xff);
-		printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
-		pcibios_write_config_byte(bus, device_fn,
-					  PCI_LATENCY_TIMER, latency_timer);
-		pcibios_read_config_byte(bus, device_fn,
-					 PCI_LATENCY_TIMER, &latency_timer);
-	}
-#endif	/* __sparc__ */
-
-	/*
-	 * Check availability of IO space, memory space and master capability.
-	 */
-	if (command & PCI_COMMAND_IO) { 
-		if ((io_port & 3) != 1) {
-			printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
-				"           bits 0..1 indicate a non-IO mapping\n", (int) io_port);
-			io_port = 0;
-		}
-		else
-			io_port &= PCI_BASE_ADDRESS_IO_MASK;
-	}
-	else
-		io_port = 0;
-
-	if (command & PCI_COMMAND_MEMORY) {
-		if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
-			printk("ncr53c8xx: disabling memory mapping since base address 1\n"
-				"            contains a non-memory mapping\n");
-			base = 0;
-		}
-		else 
-			base &= PCI_BASE_ADDRESS_MEM_MASK;
-	}
-	else
-		base = 0;
-	
-	if (!io_port && !base) {
-		printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
-		return -1;
-	}
-
-	base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
-
-	if (io_port && check_region (io_port, 128)) {
-#ifdef __sparc__
-		printk("ncr53c8xx: IO region 0x%lx to 0x%lx is in use\n",
-			io_port, (io_port + 127));
-#else
-		printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
-			(int) io_port, (int) (io_port + 127));
-#endif
-		return -1;
-	}
-	
-	if (!(command & PCI_COMMAND_MASTER)) {
-		printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
-		return -1;
-	}
-
-	/*
-	 * Fix some features according to driver setup.
-	 */
-	if (!(driver_setup.special_features & 1))
-		chip->features &= ~FE_SPECIAL_SET;
-	else {
-		if (driver_setup.special_features & 2)
-			chip->features &= ~FE_WRIE;
-	}
-	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
-		chip->features |=  FE_ULTRA;
-		chip->features &= ~FE_ULTRA2;
-	}
-	if (driver_setup.ultra_scsi < 1)
-		chip->features &= ~FE_ULTRA;
-	if (!driver_setup.max_wide)
-		chip->features &= ~FE_WIDE;
-
-
-#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
-
-	/*
-	 * Try to fix up PCI config according to wished features.
-	 */
-#if defined(__i386__) && !defined(MODULE)
-	if ((driver_setup.pci_fix_up & 1) &&
-	    (chip->features & FE_CLSE) && cache_line_size == 0) {
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
-		extern char x86;
-		switch(x86) {
-#else
-		switch(boot_cpu_data.x86) {
-#endif
-		case 4:	cache_line_size = 4; break;
-		case 6:
-		case 5:	cache_line_size = 8; break;
-		}
-		if (cache_line_size)
-			(void) pcibios_write_config_byte(bus, device_fn,
-					PCI_CACHE_LINE_SIZE, cache_line_size);
-		if (initverbose)
-			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
-	}
-
-	if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
-	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		command |= PCI_COMMAND_INVALIDATE;
-		(void) pcibios_write_config_word(bus, device_fn,
-						PCI_COMMAND, command);
-		if (initverbose)
-			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
-	}
-#endif
-	/*
-	 * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
-	 * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
-	 *   and donnot enable READ LINE.
-	 * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). 
-	 */
-
-	if (!(chip->features & FE_CLSE)) {
-		int burst_max = chip->burst_max;
-		if (cache_line_size == 0) {
-			chip->features	&= ~FE_ERL;
-			if (burst_max > 3)
-				burst_max = 3;
-		}
-		else {
-			while (cache_line_size < (1 << burst_max))
-				--burst_max;
-		}
-		chip->burst_max = burst_max;
-	}
-
-	/*
-	 * Tune PCI LATENCY TIMER according to burst max length transfer.
-	 * (latency timer >= burst length + 6, we add 10 to be quite sure)
-	 * If current value is zero, the device has probably been configured 
-	 * for no bursting due to some broken hardware.
-	 */
-
-	if (latency_timer == 0 && chip->burst_max)
-		printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
-
-	if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
-		uchar lt = (1 << chip->burst_max) + 6 + 10;
-		if (latency_timer < lt) {
-			latency_timer = lt;
-			if (initverbose)
-				printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
-			 (void) pcibios_write_config_byte(bus, device_fn,
-					PCI_LATENCY_TIMER, latency_timer);
-		}
-	}
-
-	/*
-	 * Fix up for recent chips that support CACHE LINE SIZE.
-	 * If PCI config space is not OK, remove features that shall not be 
-	 * used by the chip. No need to trigger possible chip bugs.
-	 */
-
-	if ((chip->features & FE_CLSE) && cache_line_size == 0) {
-		chip->features &= ~FE_CACHE_SET;
-		printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
-	}
-
-	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		chip->features &= ~FE_WRIE;
-		printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
-	}
-
-#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
-
- 	/* initialise ncr_device structure with items required by ncr_attach */
-	device->slot.bus	= bus;
-	device->slot.device_fn	= device_fn;
-	device->slot.base	= base;
-	device->slot.base_2	= base_2;
-	device->slot.io_port	= io_port;
-	device->slot.irq	= irq;
-	device->attach_done	= 0;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-	if (!nvram)
-		goto out;
-
-	/*
-	** Get access to chip IO registers
-	*/
-#ifdef NCR_IOMAPPED
-	request_region(io_port, 128, "ncr53c8xx");
-	device->slot.port = io_port;
-#else
-	device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
-	if (!device->slot.reg)
-		goto out;
-#endif
+		if	(f1 <	45000)		f1 =  40000;
+		else if (f1 <	55000)		f1 =  50000;
+		else				f1 =  80000;
 
-	/*
-	** Try to read SYMBIOS nvram.
-	** Data can be used to order booting of boards.
-	**
-	** Data is saved in ncr_device structure if NVRAM found. This
-	** is then used to find drive boot order for ncr_attach().
-	**
-	** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
-	** for any device set up.
-	**
-	** Try to read TEKRAM nvram if Symbios nvram not found.
-	*/
+		if (f1 < 80000 && mult > 1) {
+			if (bootverbose >= 2)
+				printk ("%s: clock multiplier assumed\n", ncr_name(np));
+			np->multiplier	= mult;
+		}
+	} else {
+		if	((scntl3 & 7) == 3)	f1 =  40000;
+		else if	((scntl3 & 7) == 5)	f1 =  80000;
+		else 				f1 = 160000;
+
+		f1 /= np->multiplier;
+	}
 
-	if	(!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
-		nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
-	else if	(!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
-		nvram->type = SCSI_NCR_TEKRAM_NVRAM;
-	else
-		nvram->type = 0;
-out:
 	/*
-	** Release access to chip IO registers
+	**	Compute controller synchronous parameters.
 	*/
-#ifdef NCR_IOMAPPED
-	release_region(device->slot.port, 128);
-#else
-	unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
-#endif
-
-#endif	/* SCSI_NCR_NVRAM_SUPPORT */
-	return 0;     
+	f1		*= np->multiplier;
+	np->clock_khz	= f1;
 }
 
+/*===================== LINUX ENTRY POINTS SECTION ==========================*/
+
 /*
 **   Linux select queue depths function
 */
 
-#define DEF_DEPTH	(driver_setup.default_tags)
-#define ALL_TARGETS	-2
-#define NO_TARGET	-1
-#define ALL_LUNS	-2
-#define NO_LUN		-1
-
-static int device_queue_depth(ncb_p np, int target, int lun)
-{
-	int c, h, t, u, v;
-	char *p = driver_setup.tag_ctrl;
-	char *ep;
-
-	h = -1;
-	t = NO_TARGET;
-	u = NO_LUN;
-	while ((c = *p++) != 0) {
-		v = simple_strtoul(p, &ep, 0);
-		switch(c) {
-		case '/':
-			++h;
-			t = ALL_TARGETS;
-			u = ALL_LUNS;
-			break;
-		case 't':
-			if (t != target)
-				t = (target == v) ? v : NO_TARGET;
-			u = ALL_LUNS;
-			break;
-		case 'u':
-			if (u != lun)
-				u = (lun == v) ? v : NO_LUN;
-			break;
-		case 'q':
-			if (h == np->unit &&
-				(t == ALL_TARGETS || t == target) &&
-				(u == ALL_LUNS    || u == lun))
-				return v;
-			break;
-		case '-':
-			t = ALL_TARGETS;
-			u = ALL_LUNS;
-			break;
-		default:
-			break;
-		}
-		p = ep;
-	}
-	return DEF_DEPTH;
-}
-
 static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
 {
 	struct scsi_device *device;
@@ -10058,7 +8523,7 @@
 		**	Use at least 2.
 		**	Donnot use more than our maximum.
 		*/
-		numtags = device_queue_depth(np, device->id, device->lun);
+		numtags = device_queue_depth(np->unit, device->id, device->lun);
 		if (numtags > tp->usrtags)
 			numtags = tp->usrtags;
 		if (!device->tagged_supported)
@@ -10066,8 +8531,8 @@
 		device->queue_depth = numtags;
 		if (device->queue_depth < 2)
 			device->queue_depth = 2;
-		if (device->queue_depth > SCSI_NCR_MAX_TAGS)
-			device->queue_depth = SCSI_NCR_MAX_TAGS;
+		if (device->queue_depth > MAX_TAGS)
+			device->queue_depth = MAX_TAGS;
 
 		/*
 		**	Since the queue depth is not tunable under Linux,
@@ -10088,22 +8553,6 @@
 }
 
 /*
-**   Linux entry point for info() function
-*/
-const char *ncr53c8xx_info (struct Scsi_Host *host)
-{
-#ifdef __sparc__
-	/* Ok to do this on all archs? */
-	static char buffer[80];
-	ncb_p np = ((struct host_data *) host->hostdata)->ncb;
-	sprintf (buffer, "%s\nPCI bus %02x device %02x", SCSI_NCR_DRIVER_NAME, np->pci_bus, np->pci_devfn);
-	return buffer;
-#else
-	return SCSI_NCR_DRIVER_NAME;
-#endif
-}
-
-/*
 **   Linux entry point of queuecommand() function
 */
 
@@ -10119,6 +8568,10 @@
 
      cmd->scsi_done     = done;
      cmd->host_scribble = NULL;
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+     cmd->__data_mapped = 0;
+     cmd->__data_mapping = 0;
+#endif
 
      NCR_LOCK_NCB(np, flags);
 
@@ -10135,8 +8588,10 @@
 
      NCR_UNLOCK_NCB(np, flags);
 
-     if (sts != DID_OK)
+     if (sts != DID_OK) {
+          unmap_scsi_data(np, cmd);
           done(cmd);
+     }
 
      return sts;
 }
@@ -10497,8 +8952,6 @@
 		uc->cmd = UC_SETDEBUG;
 	else if	((arg_len = is_keyword(ptr, len, "setflag")) != 0)
 		uc->cmd = UC_SETFLAG;
-	else if	((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
-		uc->cmd = UC_CLEARPROF;
 	else
 		arg_len = 0;
 
@@ -10558,8 +9011,6 @@
 				uc->data |= DEBUG_ALLOC;
 			else if	((arg_len = is_keyword(ptr, len, "phase")))
 				uc->data |= DEBUG_PHASE;
-			else if	((arg_len = is_keyword(ptr, len, "poll")))
-				uc->data |= DEBUG_POLL;
 			else if	((arg_len = is_keyword(ptr, len, "queue")))
 				uc->data |= DEBUG_QUEUE;
 			else if	((arg_len = is_keyword(ptr, len, "result")))
@@ -10576,10 +9027,6 @@
 				uc->data |= DEBUG_NEGO;
 			else if	((arg_len = is_keyword(ptr, len, "tags")))
 				uc->data |= DEBUG_TAGS;
-			else if	((arg_len = is_keyword(ptr, len, "freeze")))
-				uc->data |= DEBUG_FREEZE;
-			else if	((arg_len = is_keyword(ptr, len, "restart")))
-				uc->data |= DEBUG_RESTART;
 			else
 				return -EINVAL;
 			ptr += arg_len; len -= arg_len;
@@ -10618,106 +9065,53 @@
 
 #endif	/* SCSI_NCR_USER_COMMAND_SUPPORT */
 
-#ifdef SCSI_NCR_USER_INFO_SUPPORT
-
-struct info_str
-{
-	char *buffer;
-	int length;
-	int offset;
-	int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
-	if (info->pos + len > info->length)
-		len = info->length - info->pos;
-
-	if (info->pos + len < info->offset) {
-		info->pos += len;
-		return;
-	}
-	if (info->pos < info->offset) {
-		data += (info->offset - info->pos);
-		len  -= (info->offset - info->pos);
-	}
-
-	if (len > 0) {
-		memcpy(info->buffer + info->pos, data, len);
-		info->pos += len;
-	}
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
-	va_list args;
-	char buf[81];
-	int len;
-
-	va_start(args, fmt);
-	len = vsprintf(buf, fmt, args);
-	va_end(args);
-
-	copy_mem_info(info, buf, len);
-	return len;
-}
 
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
 /*
-**	Copy formatted profile information into the input buffer.
+**	Copy formatted information into the input buffer.
 */
 
-#define to_ms(t) ((t) * 1000 / HZ)
-
 static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
 {
 	struct info_str info;
+#ifdef CONFIG_ALL_PPC
+	struct device_node* of_node;
+#endif
 
 	info.buffer	= ptr;
 	info.length	= len;
 	info.offset	= offset;
 	info.pos	= 0;
 
-	copy_info(&info, "General information:\n");
-	copy_info(&info, "  Chip NCR53C%s, ",	np->chip_name);
-	copy_info(&info, "device id 0x%x, ",	np->device_id);
-	copy_info(&info, "revision id 0x%x\n",	np->revision_id);
-
-	copy_info(&info, "  IO port address 0x%lx, ", (u_long) np->port);
+	copy_info(&info, "  Chip NCR53C%s, device id 0x%x, "
+			 "revision id 0x%x\n",
+			 np->chip_name, np->device_id,	np->revision_id);
+	copy_info(&info, "  On PCI bus %d, device %d, function %d, "
 #ifdef __sparc__
-	copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq));
-	/* Ok to do this on all archs? */
-	copy_info(&info, "PCI bus %02x device %02x\n", np->pci_bus, np->pci_devfn);
+		"IRQ %s\n",
 #else
-	copy_info(&info, "IRQ number %d\n", (int) np->irq);
+		"IRQ %d\n",
 #endif
-
-#ifndef NCR_IOMAPPED
-	if (np->reg)
-		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
-		                  (u_long) np->reg);
+		np->bus, (np->device_fn & 0xf8) >> 3, np->device_fn & 7,
+#ifdef __sparc__
+		__irq_itoa(np->irq));
+#else
+		(int) np->irq);
 #endif
-	copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
-	copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+#ifdef CONFIG_ALL_PPC
+	of_node = find_pci_device_OFnode(np->bus, np->device_fn);
+	if (of_node && of_node->full_name)
+	    copy_info(&info, "PPC OpenFirmware path : %s\n", of_node->full_name);
+#endif
+	copy_info(&info, "  Synchronous period factor %d, "
+			 "max commands per lun %d\n",
+			 (int) np->minsync, MAX_TAGS);
 
 	if (driver_setup.debug || driver_setup.verbose > 1) {
-		copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
-		copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+		copy_info(&info, "  Debug flags 0x%x, verbosity level %d\n",
+			  driver_setup.debug, driver_setup.verbose);
 	}
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	copy_info(&info, "Profiling information:\n");
-	copy_info(&info, "  %-12s = %lu\n", "num_trans",np->profile.num_trans);
-	copy_info(&info, "  %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes);
-	copy_info(&info, "  %-12s = %lu\n", "num_disc",	np->profile.num_disc);
-	copy_info(&info, "  %-12s = %lu\n", "num_break",np->profile.num_break);
-	copy_info(&info, "  %-12s = %lu\n", "num_int",	np->profile.num_int);
-	copy_info(&info, "  %-12s = %lu\n", "num_fly",	np->profile.num_fly);
-	copy_info(&info, "  %-12s = %lu\n", "ms_setup",	to_ms(np->profile.ms_setup));
-	copy_info(&info, "  %-12s = %lu\n", "ms_data",	to_ms(np->profile.ms_data));
-	copy_info(&info, "  %-12s = %lu\n", "ms_disc",	to_ms(np->profile.ms_disc));
-	copy_info(&info, "  %-12s = %lu\n", "ms_post",	to_ms(np->profile.ms_post));
-#endif
-	
 	return info.pos > info.offset? info.pos - info.offset : 0;
 }
 
@@ -10772,7 +9166,6 @@
 	return retv;
 }
 
-
 /*=========================================================================
 **	End of proc file system stuff
 **=========================================================================
@@ -10780,458 +9173,104 @@
 #endif
 
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-
-/* ---------------------------------------------------------------------
+/*==========================================================
 **
-**	Try reading Symbios format nvram
+**	/proc directory entry.
 **
-** ---------------------------------------------------------------------
+**==========================================================
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_ncr53c8xx = {
+    PROC_SCSI_NCR53C8XX, 9, NAME53C8XX,
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+/*==========================================================
 **
-** GPOI0 - data in/data out
-** GPIO1 - clock
+**	Boot command line.
 **
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
-*/
-
-#define SET_BIT 0
-#define CLR_BIT 1
-#define SET_CLK 2
-#define CLR_CLK 3
-
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
-static void nvram_start(ncr_slot *np, u_char *gpreg);
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
-static void nvram_stop(ncr_slot *np, u_char *gpreg);
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
-
-__initfunc(
-static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
-)
-{
-	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
-	u_char	gpcntl, gpreg;
-	u_char	old_gpcntl, old_gpreg;
-	u_short	csum;
-	u_char	ack_data;
-	int	retv = 1;
-
-	/* save current state of GPCNTL and GPREG */
-	old_gpreg	= INB (nc_gpreg);
-	old_gpcntl	= INB (nc_gpcntl);
-	gpcntl		= old_gpcntl & 0xfc;
-
-	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
-	OUTB (nc_gpreg,  old_gpreg);
-	OUTB (nc_gpcntl, gpcntl);
-
-	/* this is to set NVRAM into a known state with GPIO0/1 both low */
-	gpreg = old_gpreg;
-	nvram_setBit(np, 0, &gpreg, CLR_CLK);
-	nvram_setBit(np, 0, &gpreg, CLR_BIT);
-		
-	/* now set NVRAM inactive with GPIO0/1 both high */
-	nvram_stop(np, &gpreg);
-	
-	/* activate NVRAM */
-	nvram_start(np, &gpreg);
-
-	/* write device code and random address MSB */
-	nvram_write_byte(np, &ack_data,
-		0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* write random address LSB */
-	nvram_write_byte(np, &ack_data,
-		(SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* regenerate START state to set up for reading */
-	nvram_start(np, &gpreg);
-	
-	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
-	nvram_write_byte(np, &ack_data,
-		0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
-	if (ack_data & 0x01)
-		goto out;
-
-	/* now set up GPIO0 for inputting data */
-	gpcntl |= 0x01;
-	OUTB (nc_gpcntl, gpcntl);
-		
-	/* input all active data - only part of total NVRAM */
-	csum = nvram_read_data(np,
-			(u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
-
-	/* finally put NVRAM back in inactive mode */
-	gpcntl &= 0xfe;
-	OUTB (nc_gpcntl, gpcntl);
-	nvram_stop(np, &gpreg);
-	
-#ifdef SCSI_NCR_DEBUG_NVRAM
-printk("ncr53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
-	nvram->type,
-	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
-	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
-	nvram->byte_count, sizeof(*nvram) - 12,
-	nvram->checksum, csum);
+**==========================================================
+*/
+#ifdef	MODULE
+char *ncr53c8xx = 0;	/* command line passed by insmod */
+# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(ncr53c8xx, "s");
+# endif
 #endif
 
-	/* check valid NVRAM signature, verify byte count and checksum */
-	if (nvram->type == 0 &&
-	    !memcmp(nvram->trailer, Symbios_trailer, 6) &&
-	    nvram->byte_count == sizeof(*nvram) - 12 &&
-	    csum == nvram->checksum)
-		retv = 0;
-out:
-	/* return GPIO0/1 to original states after having accessed NVRAM */
-	OUTB (nc_gpcntl, old_gpcntl);
-	OUTB (nc_gpreg,  old_gpreg);
-
-	return retv;
-}
-
-/*
- * Read Symbios NvRAM data and compute checksum.
- */
-__initfunc(
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
-)
-{
-	int	x;
-	u_short	csum;
-
-	for (x = 0; x < len; x++) 
-		nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
-
-	for (x = 6, csum = 0; x < len - 6; x++)
-		csum += data[x];
-
-	return csum;
-}
-
-/*
- * Send START condition to NVRAM to wake it up.
- */
-__initfunc(
-static void nvram_start(ncr_slot *np, u_char *gpreg)
-)
-{
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-}
-
-/*
- * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
- * GPIO0 must already be set as an output
- */
-__initfunc(
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
-)
-{
-	int x;
-	
-	for (x = 0; x < 8; x++)
-		nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
-		
-	nvram_readAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * READ a byte from the NVRAM and then send an ACK to say we have got it,
- * GPIO0 must already be set as an input
- */
-__initfunc(
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
-)
-{
-	int x;
-	u_char read_bit;
-
-	*read_data = 0;
-	for (x = 0; x < 8; x++) {
-		nvram_doBit(np, &read_bit, 1, gpreg);
-		*read_data |= ((read_bit & 0x01) << (7 - x));
-	}
-
-	nvram_writeAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
- */
-__initfunc(
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
-)
-{
-	OUTB (nc_gpcntl, *gpcntl & 0xfe);
-	nvram_doBit(np, 0, write_bit, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
- */
-__initfunc(
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
-)
-{
-	OUTB (nc_gpcntl, *gpcntl | 0x01);
-	nvram_doBit(np, read_bit, 1, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
- */
-__initfunc(
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
-)
+int __init ncr53c8xx_setup(char *str)
 {
-	nvram_setBit(np, write_bit, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	if (read_bit)
-		*read_bit = INB (nc_gpreg);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
+	return sym53c8xx__setup(str);
 }
 
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
- */
-__initfunc(
-static void nvram_stop(ncr_slot *np, u_char *gpreg)
-)
-{
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-}
-
-/*
- * Set/clear data/clock bit in GPIO0
- */
-__initfunc(
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
-)
-{
-	UDELAY (5);
-	switch (bit_mode){
-	case SET_BIT:
-		*gpreg |= write_bit;
-		break;
-	case CLR_BIT:
-		*gpreg &= 0xfe;
-		break;
-	case SET_CLK:
-		*gpreg |= 0x02;
-		break;
-	case CLR_CLK:
-		*gpreg &= 0xfd;
-		break;
-
-	}
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (5);
-}
-
-#undef SET_BIT 0
-#undef CLR_BIT 1
-#undef SET_CLK 2
-#undef CLR_CLK 3
-
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("ncr53c8xx=", ncr53c8xx_setup);
+#endif
+#endif
 
-/* ---------------------------------------------------------------------
-**
-**	Try reading Tekram format nvram
-**
-** ---------------------------------------------------------------------
+/*===================================================================
 **
-** GPOI0 - data in
-** GPIO1 - data out
-** GPIO2 - clock
-** GPIO4 - chip select
+**   SYM53C8XX supported device list
 **
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**===================================================================
 */
 
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
-
-__initfunc(
-static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
-)
-{
-	u_char gpcntl, gpreg;
-	u_char old_gpcntl, old_gpreg;
-	u_short csum;
-
-	/* save current state of GPCNTL and GPREG */
-	old_gpreg	= INB (nc_gpreg);
-	old_gpcntl	= INB (nc_gpcntl);
-
-	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
-	   1/2/4 out */
-	gpreg = old_gpreg & 0xe9;
-	OUTB (nc_gpreg, gpreg);
-	gpcntl = (old_gpcntl & 0xe9) | 0x09;
-	OUTB (nc_gpcntl, gpcntl);
-
-	/* input all of NVRAM, 64 words */
-	csum = Tnvram_read_data(np, (u_short *) nvram,
-			sizeof(*nvram) / sizeof(short), &gpreg);
-	
-	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
-	OUTB (nc_gpcntl, old_gpcntl);
-	OUTB (nc_gpreg,  old_gpreg);
-
-	/* check data valid */
-	if (csum != 0x1234)
-		return 1;
-
-	return 0;
-}
-
-/*
- * Read Tekram NvRAM data and compute checksum.
- */
-__initfunc(
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
-)
-{
-	u_char	read_bit;
-	u_short	csum;
-	int	x;
-
-	for (x = 0, csum = 0; x < len; x++)  {
-
-		/* output read command and address */
-		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
-		if (read_bit & 0x01)
-			return 0; /* Force bad checksum */
-
-		Tnvram_Read_Word(np, &data[x], gpreg);
-		csum += data[x];
-
-		Tnvram_Stop(np, gpreg);
-	}
-
-	return csum;
-}
-
-/*
- * Send read command and address to NVRAM
- */
-__initfunc(
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
-)
-{
-	int x;
-
-	/* send 9 bits, start bit (1), command (2), address (6)  */
-	for (x = 0; x < 9; x++)
-		Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
-
-	*read_bit = INB (nc_gpreg);
-}
-
-/*
- * READ a byte from the NVRAM
- */
-__initfunc(
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
-)
-{
-	int x;
-	u_char read_bit;
-
-	*nvram_data = 0;
-	for (x = 0; x < 16; x++) {
-		Tnvram_Read_Bit(np, &read_bit, gpreg);
-
-		if (read_bit & 0x01)
-			*nvram_data |=  (0x01 << (15 - x));
-		else
-			*nvram_data &= ~(0x01 << (15 - x));
-	}
-}
-
-/* 
- * Read bit from NVRAM
- */
-__initfunc(
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-)
-{
-	UDELAY (2);
-	Tnvram_Clk(np, gpreg);
-	*read_bit = INB (nc_gpreg);
-}
+static u_short	ncr_chip_ids[]   __initdata = {
+	PCI_DEVICE_ID_NCR_53C810,
+	PCI_DEVICE_ID_NCR_53C815,
+	PCI_DEVICE_ID_NCR_53C820,
+	PCI_DEVICE_ID_NCR_53C825,
+	PCI_DEVICE_ID_NCR_53C860,
+	PCI_DEVICE_ID_NCR_53C875,
+	PCI_DEVICE_ID_NCR_53C875J,
+	PCI_DEVICE_ID_NCR_53C885,
+	PCI_DEVICE_ID_NCR_53C895,
+	PCI_DEVICE_ID_NCR_53C896,
+	PCI_DEVICE_ID_NCR_53C895A,
+	PCI_DEVICE_ID_NCR_53C1510D
+};
 
-/*
- * Write bit to GPIO0
- */
-__initfunc(
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
-)
+/*==========================================================
+**
+**	Chip detection entry point.
+**
+**==========================================================
+*/
+int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 {
-	if (write_bit & 0x01)
-		*gpreg |= 0x02;
-	else
-		*gpreg &= 0xfd;
-		
-	*gpreg |= 0x10;
-		
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
-
-	Tnvram_Clk(np, gpreg);
-}
+	/*
+	**    Initialize driver general stuff.
+	*/
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+     tpnt->proc_dir  = &proc_scsi_ncr53c8xx;
+#else
+     tpnt->proc_name = NAME53C8XX;
+#endif
+     tpnt->proc_info = ncr53c8xx_proc_info;
+#endif
 
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
- */
-__initfunc(
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
-)
-{
-	*gpreg &= 0xef;
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
+#if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+	ncr53c8xx_setup(ncr53c8xx);
+#endif
 
-	Tnvram_Clk(np, gpreg);
+	return sym53c8xx__detect(tpnt, ncr_chip_ids,
+				 sizeof(ncr_chip_ids)/sizeof(ncr_chip_ids[0]));
 }
 
-/*
- * Pulse clock bit in GPIO0
- */
-__initfunc(
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
-)
+/*==========================================================
+**
+**   Entry point for info() function
+**
+**==========================================================
+*/
+const char *ncr53c8xx_info (struct Scsi_Host *host)
 {
-	OUTB (nc_gpreg, *gpreg | 0x04);
-	UDELAY (2);
-	OUTB (nc_gpreg, *gpreg);
+	return SCSI_NCR_DRIVER_NAME;
 }
-
-#endif	/* SCSI_NCR_NVRAM_SUPPORT */
 
 /*
 **	Module stuff
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/oktagon_esp.c linux.18p17/drivers/scsi/oktagon_esp.c
--- linux.vanilla/drivers/scsi/oktagon_esp.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/oktagon_esp.c	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/oktagon_esp.h linux.18p17/drivers/scsi/oktagon_esp.h
--- linux.vanilla/drivers/scsi/oktagon_esp.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/oktagon_esp.h	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/oktagon_io.S linux.18p17/drivers/scsi/oktagon_io.S
--- linux.vanilla/drivers/scsi/oktagon_io.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/oktagon_io.S	Fri Oct 13 23:57:54 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pci2220i.c linux.18p17/drivers/scsi/pci2220i.c
--- linux.vanilla/drivers/scsi/pci2220i.c	Fri Apr 21 12:46:31 2000
+++ linux.18p17/drivers/scsi/pci2220i.c	Sat Sep 30 18:17:49 2000
@@ -373,7 +373,7 @@
 static int HardReset (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigot)
 	{
 	DEB (printk ("\npci2220i:RESET  spigot = %X  devices = %d, %d", spigot, pdev->deviceID[0], pdev->deviceID[1]));
-	udelay (100000);										// just wait 100 mSec to let drives flush	
+	mdelay (100);										// just wait 100 mSec to let drives flush	
 	SelectSpigot (padapter, spigot | SEL_IRQ_OFF);
 	
 	outb_p (0x0E, padapter->regAltStat);					// reset the suvivor
@@ -2953,7 +2953,7 @@
 		{
 		padapter->reconOn = FALSE;						// shut down the hot reconstruct
 		if ( padapter->reconPhase )
-			udelay (300000);
+			mdelay (300);
 		if ( padapter->reconTimer.data )				// is the timer running?
 			{
 			del_timer (&padapter->reconTimer);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.c linux.18p17/drivers/scsi/scsi.c
--- linux.vanilla/drivers/scsi/scsi.c	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/scsi.c	Sun Sep  3 14:00:38 2000
@@ -303,6 +303,8 @@
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
 {"CANON","IPUBJD","*", BLIST_SPARSELUN},
 {"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+{"DEC","HSG80","*", BLIST_FORCELUN},
+{"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN},
 {"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"MATSHITA","PD-1","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
@@ -312,6 +314,8 @@
 {"DGC",  "DISK",      "*", BLIST_SPARSELUN}, /* Dell PV 650F (no tgt @ LUN 0) */
 {"DELL", "PV530F",    "*", BLIST_SPARSELUN}, /* Dell PV 530F */
 {"SONY", "TSL",       "*", BLIST_FORCELUN},  /* DDS3 & DDS4 autoloaders */
+{"DELL", "PERCRAID", "*", BLIST_FORCELUN},
+{"HP", "NetRAID-4M", "*", BLIST_FORCELUN},
 /*
  * Must be at end of list...
  */
@@ -798,7 +802,8 @@
   memcpy (SDpnt->rev, scsi_result + 32, 4);
 
   SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
-  SDpnt->online = TRUE;
+  if (((scsi_result[0] >> 5) & 7) == 1)  SDpnt->online = FALSE;
+  else SDpnt->online = TRUE;
   SDpnt->lockable = SDpnt->removable;
   SDpnt->changed = 0;
   SDpnt->access_count = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sd.c linux.18p17/drivers/scsi/sd.c
--- linux.vanilla/drivers/scsi/sd.c	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/sd.c	Sat Sep  2 23:48:15 2000
@@ -150,36 +150,43 @@
      * This is also used to lock out further access when the partition table
      * is being re-read.
      */
-
     while (rscsi_disks[target].device->busy)
         barrier();
-    if(rscsi_disks[target].device->removable) {
+
+    /*
+     * When opening removable disks, we check media...
+     * ... unless media don't matter, due to O_NONBLOCK.
+     */
+    if ( rscsi_disks[target].device->removable
+         && !(filp->f_flags & O_NONBLOCK) )
+    {
+        /*
+         * Note disk change and/or removal.
+         * Also try to read new partition table (if any).
+         */
 	check_disk_change(inode->i_rdev);
 
-	/*
-	 * If the drive is empty, just let the open fail.
-	 */
+        /*
+         * If the drive is empty, let the open fail.
+         */
 	if ( !rscsi_disks[target].ready )
-	    return -ENXIO;
+            return -ENXIO;
 
-	/*
-	 * Similarly, if the device has the write protect tab set,
-	 * have the open fail if the user expects to be able to write
-	 * to the thing.
-	 */
-	if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
-	    return -EROFS;
+        /*
+         * If the device has the write protect tab set,
+         * let the open fail if the user expects to be able to write.
+         */
+        if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
+            return -EROFS;
     }
 
     /*
-     * It is possible that the disk changing stuff resulted in the device being taken
-     * offline.  If this is the case, report this to the user, and don't pretend that
-     * the open actually succeeded.
+     * It is possible that the disk changing stuff (or something
+     * else?) resulted in the device being taken offline.
+     * If so, let the open fail.
      */
-    if( !rscsi_disks[target].device->online )
-    {
+    if ( !rscsi_disks[target].device->online )
         return -ENXIO;
-    }
 
     /*
      * See if we are requesting a non-existent partition.  Do this
@@ -1068,7 +1075,6 @@
     int retval;
     int target;
     struct inode inode;
-    int flag = 0;
 
     target =  DEVICE_NR(full_dev);
 
@@ -1120,11 +1126,11 @@
      * presence of disk in the drive. This is kept in the Scsi_Disk
      * struct and tested at open !  Daniel Roche ( dan@lectra.fr )
      */
-
     rscsi_disks[target].ready = 1;	/* FLOPTICAL */
 
     retval = rscsi_disks[target].device->changed;
-    if(!flag) rscsi_disks[target].device->changed = 0;
+    rscsi_disks[target].device->changed = 0;
+
     return retval;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/seagate.c linux.18p17/drivers/scsi/seagate.c
--- linux.vanilla/drivers/scsi/seagate.c	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/seagate.c	Thu Aug 31 15:34:27 2000
@@ -1708,124 +1708,16 @@
   return SCSI_RESET_WAKEUP;
 }
 
-
-int seagate_st0x_biosparam (Disk * disk, kdev_t dev, int *ip)
-{
-  unsigned char buf[256 + sizeof (Scsi_Ioctl_Command)], 
-                cmd[6], *data, *page;
-  Scsi_Ioctl_Command *sic = (Scsi_Ioctl_Command *) buf;
-  int result, formatted_sectors, total_sectors;
-  int cylinders, heads, sectors;
-  int capacity;
-
 /*
- * Only SCSI-I CCS drives and later implement the necessary mode sense
- * pages.
+ * There used to be some biosparam() code here, but the
+ * author misunderstood the purpose of this routine and
+ * tried to determine physical reality by reading mode
+ * sense pages and the like. But that yields values like
+ * 424 sectors/track, completely unusable for partition tables.
+ * (And after the translation part we may have heads > 255,
+ * equally unusable.)
+ * So, we delete all of this stuff and rely on the defaults.
  */
-
-  if (disk->device->scsi_level < 2)
-    return -1;
-
-  data = sic->data;
-
-  cmd[0] = MODE_SENSE;
-  cmd[1] = (disk->device->lun << 5) & 0xe5;
-  cmd[2] = 0x04;                        /* Read page 4, rigid disk geometry
-                                           page current values */
-  cmd[3] = 0;
-  cmd[4] = 255;
-  cmd[5] = 0;
-
-/*
- * We are transferring 0 bytes in the out direction, and expect to get back
- * 24 bytes for each mode page.
- */
-  sic->inlen = 0;
-  sic->outlen = 256;
-
-  memcpy (data, cmd, 6);
-
-  if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND,
-                                    sic)))
-  {
-/*
- * The mode page lies beyond the MODE SENSE header, with length 4, and
- * the BLOCK DESCRIPTOR, with length header[3].
- */
-    page = data + 4 + data[3];
-    heads = (int) page[5];
-    cylinders = (page[2] << 16) | (page[3] << 8) | page[4];
-
-    cmd[2] = 0x03;                      /* Read page 3, format page current
-                                           values */
-    memcpy (data, cmd, 6);
-
-    if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND,
-                                      sic)))
-    {
-      page = data + 4 + data[3];
-      sectors = (page[10] << 8) | page[11];
-/*
- * Get the total number of formatted sectors from the block descriptor,
- * so we can tell how many are being used for alternates.
- */
-      formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) 
-                          | data[4 + 3];
-
-      total_sectors = (heads * cylinders * sectors);
-
-/*
- * Adjust the real geometry by subtracting
- * (spare sectors / (heads * tracks)) cylinders from the number of cylinders.
- *
- * It appears that the CE cylinder CAN be a partial cylinder.
- */
-
-      printk ("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
-              hostno, heads, cylinders, sectors, total_sectors,
-              formatted_sectors);
-
-      if (!heads || !sectors || !cylinders)
-        result = -1;
-      else
-        cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
-
-/*
- * Now, we need to do a sanity check on the geometry to see if it is
- * BIOS compatible.  The maximum BIOS geometry is 1024 cylinders *
- * 256 heads * 64 sectors.
- */
-
-      if ((cylinders > 1024) || (sectors > 64))
-      {
-        /* The Seagate's seem to have some mapping.  Multiply
-           heads*sectors*cyl to get capacity.  Then start rounding down.
-         */
-        capacity = heads * sectors * cylinders;         
-      
-        /* Old MFM Drives use this, so does the Seagate */
-        sectors = 17;
-        heads = 2;
-        capacity = capacity / sectors;
-        while (cylinders > 1024)
-        {
-          heads *= 2;                   /* For some reason, they go in
-                                           multiples */
-          cylinders = capacity / heads;
-        }
-      }
-      ip[0] = heads;
-      ip[1] = sectors;
-      ip[2] = cylinders;
-/*
- * There should be an alternate mapping for things the seagate doesn't
- * understand, but I couldn't say what it is with reasonable certainty.
- */
-    }
-  }
-
-  return result;
-}
 
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/seagate.h linux.18p17/drivers/scsi/seagate.h
--- linux.vanilla/drivers/scsi/seagate.h	Fri Apr 21 23:20:05 2000
+++ linux.18p17/drivers/scsi/seagate.h	Thu Aug 31 15:34:27 2000
@@ -29,7 +29,6 @@
 			 queuecommand:   seagate_st0x_queue_command,	\
 			 abort:          seagate_st0x_abort,		\
 			 reset:          seagate_st0x_reset,		\
-			 bios_param:     seagate_st0x_biosparam,	\
 			 can_queue:      1,				\
 			 this_id:        7,				\
 			 sg_tablesize:   SG_ALL,			\
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr.c linux.18p17/drivers/scsi/sr.c
--- linux.vanilla/drivers/scsi/sr.c	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/sr.c	Mon Oct  2 10:21:52 2000
@@ -20,7 +20,7 @@
  *          Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
  *          generic cdrom interface
  *
- *       Modified by Jens Axboe <axboe@image.dk> - Uniform sr_packet()
+ *       Modified by Jens Axboe <axboe@suse.de> - Uniform sr_packet()
  *       interface, capabilities probe additions, ioctl cleanups, etc.
  *
  *	Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr_ioctl.c linux.18p17/drivers/scsi/sr_ioctl.c
--- linux.vanilla/drivers/scsi/sr_ioctl.c	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/sr_ioctl.c	Fri Sep 15 12:47:12 2000
@@ -314,6 +314,8 @@
     u_char  sr_cmd[10];    
     int result, target  = MINOR(cdi->dev);
     unsigned char buffer[32];
+
+    memset(sr_cmd, 0, sizeof(sr_cmd));
     
     switch (cmd) 
     {
@@ -324,10 +326,7 @@
 	sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
 	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
-	sr_cmd[6] = 0;
-	sr_cmd[7] = 0;              /* MSB of length (12) */
 	sr_cmd[8] = 12;             /* LSB of length */
-	sr_cmd[9] = 0;
 	
 	result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1);
 	
@@ -346,9 +345,7 @@
           (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
 	sr_cmd[6] = tocentry->cdte_track;
-	sr_cmd[7] = 0;             /* MSB of length (12)  */
 	sr_cmd[8] = 12;            /* LSB of length */
-	sr_cmd[9] = 0;
 	
 	result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0);
 	
@@ -373,9 +370,7 @@
 		sr_cmd[0] = MODE_SENSE;
 		sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
 		sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-		sr_cmd[3] = 0;
 		sr_cmd[4] = 28;
-		sr_cmd[5] = 0;
 	
 		if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL)
 			return -ENOMEM;
@@ -436,9 +431,7 @@
 		sr_cmd[0] = MODE_SENSE;
 		sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
 		sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-		sr_cmd[3] = 0;
 		sr_cmd[4] = 28;
-		sr_cmd[5] = 0;
 	
 		if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL)
 			return -ENOMEM;
@@ -455,6 +448,20 @@
 		volctrl->channel3 = buffer[27];
 
 		scsi_free(buffer, 512);
+		break;
+	}
+
+    case CDROMPLAYTRKIND: {
+		struct cdrom_ti* ti = (struct cdrom_ti*)arg;
+
+		sr_cmd[0] = GPCMD_PLAYAUDIO_TI;
+		sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+		sr_cmd[4] = ti->cdti_trk0;
+		sr_cmd[5] = ti->cdti_ind0;
+		sr_cmd[7] = ti->cdti_trk1;
+		sr_cmd[8] = ti->cdti_ind1;
+	
+		result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
 		break;
 	}
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/st.c linux.18p17/drivers/scsi/st.c
--- linux.vanilla/drivers/scsi/st.c	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/st.c	Mon Sep 11 21:20:33 2000
@@ -11,7 +11,7 @@
   Copyright 1992 - 2000 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Sat Jun 17 17:29:35 2000 by makisara@kai.makisara.local
+  Last modified: Tue Sep  5 23:17:08 2000 by makisara@kai.makisara.local
   Some small formal changes - aeb, 950809
 */
 
@@ -1459,8 +1459,11 @@
 
 		if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */
 		    if (STp->block_size == 0) {
-			if (transfer <= 0)
-			    transfer = 0;
+			if (transfer < 0) {
+			    if (STps->drv_block >= 0)
+				STps->drv_block += 1;
+			    return (-ENOMEM);
+			}
 			(STp->buffer)->buffer_bytes = bytes - transfer;
 		    }
 		    else {
@@ -2932,6 +2935,8 @@
 	   i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
 	       mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
 	       mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+	       mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
+	       mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
 	       mtc.mt_op == MTCOMPRESSION;
        }
        i = flush_buffer(inode, file, i);
@@ -3099,6 +3104,51 @@
 }
 
 
+/* The following two functions are nearly identical to scsi_init_malloc and
+   scsi_init_free. The only difference is that the GFP_DMA flag is not set
+   by default. */
+static void * st_malloc(unsigned int size, int gfp_mask)
+{
+    void * retval;
+
+    /*
+     * For buffers used by the DMA pool, we assume page aligned 
+     * structures.
+     */
+    if ((size % PAGE_SIZE) == 0) {
+	int order, a_size;
+	for (order = 0, a_size = PAGE_SIZE;
+             a_size < size; order++, a_size <<= 1)
+            ;
+	retval = (void *) __get_free_pages(gfp_mask, order);
+    } else
+	retval = kmalloc(size, gfp_mask);
+
+    if (retval)
+	memset(retval, 0, size);
+    return retval;
+}
+
+
+static void st_free(char * ptr, unsigned int size)
+{
+    /*
+     * We need this special code here because the DMA pool assumes
+     * page aligned data.  Besides, it is wasteful to allocate
+     * page sized chunks with kmalloc.
+     */
+    if ((size % PAGE_SIZE) == 0) {
+    	int order, a_size;
+
+	for (order = 0, a_size = PAGE_SIZE;
+	     a_size < size; order++, a_size <<= 1)
+	    ;
+	free_pages((unsigned long)ptr, order);
+    } else
+	kfree(ptr);
+}
+
+
 /* Try to allocate a new tape buffer */
 	static ST_buffer *
 new_tape_buffer( int from_initialization, int need_dma )
@@ -3115,7 +3165,7 @@
     priority = GFP_KERNEL;
 
   i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist);
-  tb = (ST_buffer *)scsi_init_malloc(i, priority);
+  tb = (ST_buffer *)st_malloc(i, priority);
   if (tb) {
     tb->this_size = i;
     if (need_dma)
@@ -3128,7 +3178,7 @@
 	b_size /= 2;
     for ( ; b_size >= PAGE_SIZE; b_size /= 2) {
 	tb->sg[0].address =
-	    (unsigned char *)scsi_init_malloc(b_size, priority);
+	    (unsigned char *)st_malloc(b_size, priority);
 	if (tb->sg[0].address != NULL) {
 	    tb->sg[0].alt_address = NULL;
 	    tb->sg[0].length = b_size;
@@ -3136,7 +3186,7 @@
 	}
     }
     if (tb->sg[segs].address == NULL) {
-	scsi_init_free((char *)tb, tb->this_size);
+	st_free((char *)tb, tb->this_size);
 	tb = NULL;
     }
     else {  /* Got something, continue */
@@ -3148,7 +3198,7 @@
 	for (segs=1, got=tb->sg[0].length;
 	     got < st_buffer_size && segs < ST_FIRST_SG; ) {
 	    tb->sg[segs].address =
-		(unsigned char *)scsi_init_malloc(b_size, priority);
+		(unsigned char *)st_malloc(b_size, priority);
 	    if (tb->sg[segs].address == NULL) {
 		if (st_buffer_size - got <=
 		    (ST_FIRST_SG - segs) * b_size / 2) {
@@ -3156,8 +3206,8 @@
 		    continue;
 		}
 		for (i=0; i < segs - 1; i++)
-		    scsi_init_free(tb->sg[i].address, tb->sg[i].length);
-		scsi_init_free((char *)tb, tb->this_size);
+		    st_free(tb->sg[i].address, tb->sg[i].length);
+		st_free((char *)tb, tb->this_size);
 		tb = NULL;
 		break;
 	    }
@@ -3220,7 +3270,7 @@
   for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
        segs < max_segs && got < new_size; ) {
       STbuffer->sg[segs].address =
-	  (unsigned char *)scsi_init_malloc(b_size, priority);
+	  (unsigned char *)st_malloc(b_size, priority);
       if (STbuffer->sg[segs].address == NULL) {
 	  if (new_size - got <= (max_segs - segs) * b_size / 2) {
 	      b_size /= 2;  /* Large enough for the rest of the buffers */
@@ -3256,7 +3306,7 @@
   int i;
 
   for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
-      scsi_init_free(STbuffer->sg[i].address, STbuffer->sg[i].length);
+      st_free(STbuffer->sg[i].address, STbuffer->sg[i].length);
       STbuffer->buffer_size -= STbuffer->sg[i].length;
   }
 #if DEBUG
@@ -3489,7 +3539,7 @@
   if (st_template.dev_max > 128 / ST_NBR_MODES)
     printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
   scsi_tapes =
-    (Scsi_Tape *) scsi_init_malloc(st_template.dev_max * sizeof(Scsi_Tape),
+    (Scsi_Tape *) st_malloc(st_template.dev_max * sizeof(Scsi_Tape),
 				   GFP_ATOMIC);
   if (scsi_tapes == NULL) {
     printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
@@ -3506,7 +3556,7 @@
   for (i=0; i < st_template.dev_max; ++i) {
     STp = &(scsi_tapes[i]);
     STp->capacity = 0xfffff;
-    STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget),
+    STp->mt_status = (struct mtget *) st_malloc(sizeof(struct mtget),
 						       GFP_ATOMIC);
     /* Initialize status */
     memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
@@ -3514,12 +3564,12 @@
 
   /* Allocate the buffers */
   st_buffers =
-    (ST_buffer **) scsi_init_malloc(st_template.dev_max * sizeof(ST_buffer *),
+    (ST_buffer **) st_malloc(st_template.dev_max * sizeof(ST_buffer *),
 				    GFP_ATOMIC);
   if (st_buffers == NULL) {
     printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
     unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-    scsi_init_free((char *) scsi_tapes,
+    st_free((char *) scsi_tapes,
 		   st_template.dev_max * sizeof(Scsi_Tape));
     return 1;
   }
@@ -3532,6 +3582,8 @@
     target_nbr = ST_EXTRA_DEVS;
   if (target_nbr > st_max_buffers)
     target_nbr = st_max_buffers;
+  if (target_nbr > st_template.dev_noticed)
+    target_nbr = st_template.dev_noticed;
 
   for (i=st_nbr_buffers=0; i < target_nbr; i++) {
     if (!new_tape_buffer(TRUE, TRUE)) {
@@ -3539,9 +3591,9 @@
 #if 0
 	printk(KERN_ERR "Can't continue without at least one tape buffer.\n");
 	unregister_chrdev(SCSI_TAPE_MAJOR, "st");
-	scsi_init_free((char *) st_buffers,
+	st_free((char *) st_buffers,
 		       st_template.dev_max * sizeof(ST_buffer *));
-	scsi_init_free((char *) scsi_tapes,
+	st_free((char *) scsi_tapes,
 		       st_template.dev_max * sizeof(Scsi_Tape));
 	return 1;
 #else
@@ -3608,19 +3660,19 @@
   unregister_chrdev(SCSI_TAPE_MAJOR, "st");
   st_registered--;
   if(scsi_tapes != NULL) {
-    scsi_init_free((char *) scsi_tapes,
+    st_free((char *) scsi_tapes,
 		   st_template.dev_max * sizeof(Scsi_Tape));
 
     if (st_buffers != NULL) {
       for (i=0; i < st_nbr_buffers; i++)
 	if (st_buffers[i] != NULL) {
 	  for (j=0; j < st_buffers[i]->sg_segs; j++)
-	      scsi_init_free((char *) st_buffers[i]->sg[j].address,
+	      st_free((char *) st_buffers[i]->sg[j].address,
 			     st_buffers[i]->sg[j].length);
-	  scsi_init_free((char *) st_buffers[i], st_buffers[i]->this_size);
+	  st_free((char *) st_buffers[i], st_buffers[i]->this_size);
 	}
 
-      scsi_init_free((char *) st_buffers,
+      st_free((char *) st_buffers,
 		     st_template.dev_max * sizeof(ST_buffer *));
     }
   }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sun3x_esp.c linux.18p17/drivers/scsi/sun3x_esp.c
--- linux.vanilla/drivers/scsi/sun3x_esp.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/sun3x_esp.c	Sat Oct 14 00:01:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sun3x_esp.h linux.18p17/drivers/scsi/sun3x_esp.h
--- linux.vanilla/drivers/scsi/sun3x_esp.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/sun3x_esp.h	Sat Oct 14 00:01:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx.c linux.18p17/drivers/scsi/sym53c8xx.c
--- linux.vanilla/drivers/scsi/sym53c8xx.c	Sat Sep  9 18:42:41 2000
+++ linux.18p17/drivers/scsi/sym53c8xx.c	Sun Sep 10 14:49:10 2000
@@ -1,7 +1,7 @@
 /******************************************************************************
 **  High Performance device driver for the Symbios 53C896 controller.
 **
-**  Copyright (C) 1998  Gerard Roudier <groudier@club-internet.fr>
+**  Copyright (C) 1998-2000  Gerard Roudier <groudier@club-internet.fr>
 **
 **  This driver also supports all the Symbios 53C8XX controller family, 
 **  except 53C810 revisions < 16, 53C825 revisions < 16 and all 
@@ -55,8 +55,6 @@
 */
 
 /*
-**	July 24 1999, sym53c8xx version 1.3g
-**
 **	Supported SCSI features:
 **	    Synchronous data transfers
 **	    Wide16 SCSI BUS
@@ -64,7 +62,7 @@
 **	    Tagged command queuing
 **	    SCSI Parity checking
 **
-**	Supported NCR chips:
+**	Supported NCR/SYMBIOS chips:
 **		53C810A	  (8 bits, Fast 10,	 no rom BIOS) 
 **		53C825A	  (Wide,   Fast 10,	 on-board rom BIOS)
 **		53C860	  (8 bits, Fast 20,	 no rom BIOS)
@@ -73,7 +71,10 @@
 **		53C895	  (Wide,   Fast 40,	 on-board rom BIOS)
 **		53C895A	  (Wide,   Fast 40,	 on-board rom BIOS)
 **		53C896	  (Wide,   Fast 40 Dual, on-board rom BIOS)
-**		53C1510D	  (Wide,   Fast 40 Dual, on-board rom BIOS)
+**		53C897	  (Wide,   Fast 40 Dual, on-board rom BIOS)
+**		53C1510D  (Wide,   Fast 40 Dual, on-board rom BIOS)
+**		53C1010	  (Wide,   Fast 80 Dual, on-board rom BIOS)
+**		53C1010_66(Wide,   Fast 80 Dual, on-board rom BIOS, 33/66MHz PCI)
 **
 **	Other features:
 **		Memory mapped IO
@@ -84,11 +85,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"sym53c8xx - version 1.3g"
-
-/* #define DEBUG_896R1 */
-#define SCSI_NCR_OPTIMIZE_896
-/* #define SCSI_NCR_OPTIMIZE_896_1 */
+#define SCSI_NCR_DRIVER_NAME	"sym53c8xx-1.7.1-20000726"
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)
 
@@ -111,7 +108,9 @@
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
 #include <asm/spinlock.h>
 #endif
 #include <linux/delay.h>
@@ -130,15 +129,19 @@
 #include <linux/version.h>
 #include <linux/blk.h>
 
+#ifdef CONFIG_ALL_PPC
+#include <asm/prom.h>
+#endif
+
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
 #include <linux/init.h>
-#else
-#ifndef	__initdata
-#define	__initdata
 #endif
-#ifndef	__initfunc
-#define	__initfunc(__arginit) __arginit
+
+#ifndef	__init
+#define	__init
 #endif
+#ifndef	__initdata
+#define	__initdata
 #endif
 
 #if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
@@ -172,6 +175,27 @@
 
 #include "sym53c8xx.h"
 
+/*
+**	Donnot compile integrity checking code for Linux-2.3.0 
+**	and above since SCSI data structures are not ready yet.
+*/
+/* #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,0) */
+#if 0
+#define	SCSI_NCR_INTEGRITY_CHECKING
+#endif
+
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
+
+/*
+**	Hmmm... What complex some PCI-HOST bridges actually are, 
+**	despite the fact that the PCI specifications are looking 
+**	so smart and simple! ;-)
+*/
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define SCSI_NCR_DYNAMIC_DMA_MAPPING
+#endif
+
 /*==========================================================
 **
 **	A la VMS/CAM-3 queue management.
@@ -264,48 +288,6 @@
 
 /*==========================================================
 **
-**	On x86 architecture, write buffers management does 
-**	not reorder writes to memory. So, using compiler 
-**	optimization barriers is enough to guarantee some 
-**	ordering when the CPU is writing data accessed by 
-**	the NCR.
-**	On Alpha architecture, explicit memory barriers have 
-**	to be used.
-**	Other architectures are defaulted to mb() macro if  
-**	defined, otherwise use compiler barrier.
-**
-**==========================================================
-*/
-
-#if defined(__i386__)
-#define MEMORY_BARRIER()	barrier()
-#elif defined(__alpha__)
-#define MEMORY_BARRIER()	mb()
-#else
-#  ifdef mb
-#  define MEMORY_BARRIER()	mb()
-#  else
-#  define MEMORY_BARRIER()	barrier()
-#  endif
-#endif
-
-/*==========================================================
-**
-**	This driver ensures that no PCI self-mastering will 
-**	be attempted by the PCI chip at any time, provided 
-**	that we can load the on-chip RAM from the C code.
-**	For now, I can only check that on x86, and the 
-**	the SCSI_NCR_PCI_MEM_NOT_SUPPORTED option is here 
-**	to provide previous behaviour for other platforms.
-**
-**==========================================================
-*/
-#if	!defined(__i386__) && !defined(__sparc__)
-#define	SCSI_NCR_PCI_MEM_NOT_SUPPORTED
-#endif
-
-/*==========================================================
-**
 **	Configuration and Debugging
 **
 **==========================================================
@@ -331,26 +313,34 @@
 #endif
 
 /*
-**    TAGS are actually limited to 64 tags/lun.
-**    We need to deal with power of 2, for alignment constraints.
+**    TAGS are actually unlimited (256 tags/lun).
+**    But Linux only supports 255. :)
 */
-#if	SCSI_NCR_MAX_TAGS > 64
-#undef	SCSI_NCR_MAX_TAGS
-#define	SCSI_NCR_MAX_TAGS (64)
+#if	SCSI_NCR_MAX_TAGS > 255
+#define	MAX_TAGS	255
+#else
+#define	MAX_TAGS SCSI_NCR_MAX_TAGS
 #endif
 
-#define NO_TAG	(255)
-
 /*
-**	Choose appropriate type for tag bitmap.
+**    Since the ncr chips only have a 8 bit ALU, we try to be clever 
+**    about offset calculation in the TASK TABLE per LUN that is an 
+**    array of DWORDS = 4 bytes.
 */
-#if	SCSI_NCR_MAX_TAGS > 32
-typedef u_int64 tagmap_t;
+#if	MAX_TAGS > (512/4)
+#define MAX_TASKS  (1024/4)
+#elif	MAX_TAGS > (256/4) 
+#define MAX_TASKS  (512/4)
 #else
-typedef u_int32 tagmap_t;
+#define MAX_TASKS  (256/4)
 #endif
 
 /*
+**    This one means 'NO TAG for this job'
+*/
+#define NO_TAG	(256)
+
+/*
 **    Number of targets supported by the driver.
 **    n permits target numbers 0..n-1.
 **    Default is 16, meaning targets #0..#15.
@@ -394,53 +384,28 @@
 #ifdef SCSI_NCR_CAN_QUEUE
 #define MAX_START   (SCSI_NCR_CAN_QUEUE + 4)
 #else
-#define MAX_START   (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS)
+#define MAX_START   (MAX_TARGET + 7 * MAX_TAGS)
 #endif
 
 /*
-**    The maximum number of segments a transfer is split into.
-**    We support up to 127 segments for both read and write.
-**    Since we try to avoid phase mismatches by testing the PHASE 
-**    before each MOV, the both DATA_IN and DATA_OUT scripts do 
-**    not fit in the 4K on-chip RAM. For this reason, the data 
-**    scripts are broken into 2 sub-scripts.
-**    80 (MAX_SCATTERL) segments are moved from a sub-script 
-**    in on-chip RAM. This makes data transfers shorter than 
-**    80k (assuming 1k fs) as fast as possible.
-**    The 896 allows to handle phase mismatches from SCRIPTS.
-**    So, for this chip, we use a simple array of MOV's.
-**    Perhaps, using a simple array of MOV's and going with 
-**    the phase mismatch interrupt is also the best solution 
-**    for the 895 in Ultra2-mode, since the PHASE test + MOV 
-**    latency may be enough to fill the SCSI offset for very  
-**    fast disks like the Cheatah Wide LVD and so, may waste 
-**    SCSI BUS bandwitch.
+**    We donnot want to allocate more than 1 PAGE for the 
+**    the start queue and the done queue. We hard-code entry 
+**    size to 8 in order to let cpp do the checking.
+**    Allows 512-4=508 pending IOs for i386 but Linux seems for 
+**    now not able to provide the driver with this amount of IOs.
 */
-
-#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
-
-#ifdef	SCSI_NCR_OPTIMIZE_896
-#define	SCR_SG_SIZE	(2)
-#define MAX_SCATTERL	MAX_SCATTER
-#define MAX_SCATTERH	0
-#else
-#if (MAX_SCATTER > 80)
-#define	SCR_SG_SIZE	(4)
-#define MAX_SCATTERL	80
-#define	MAX_SCATTERH	(MAX_SCATTER - MAX_SCATTERL)
-#else
-#define MAX_SCATTERL	MAX_SCATTER
-#define	MAX_SCATTERH	0
+#if	MAX_START > PAGE_SIZE/8
+#undef	MAX_START
+#define MAX_START (PAGE_SIZE/8)
 #endif
-#endif	/* SCSI_NCR_OPTIMIZE_896 */
 
 /*
-**    Io mapped or memory mapped.
+**    The maximum number of segments a transfer is split into.
+**    We support up to 127 segments for both read and write.
 */
 
-#if defined(SCSI_NCR_IOMAPPED) || defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
-#define NCR_IOMAPPED
-#endif
+#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
+#define	SCR_SG_SIZE	(2)
 
 /*
 **	other
@@ -472,6 +437,133 @@
 #define offsetof(t, m)	((size_t) (&((t *)0)->m))
 #endif
 
+/*
+**	Simple Wrapper to kernel PCI bus interface.
+**
+**	This wrapper allows to get rid of old kernel PCI interface 
+**	and still allows to preserve linux-2.0 compatibilty.
+**	In fact, it is mostly an incomplete emulation of the new 
+**	PCI code for pre-2.2 kernels. When kernel-2.0 support 
+**	will be dropped, we will just have to remove most of this 
+**	code.
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL		(0)
+#define PciBusNumber(d)		(d)->bus->number
+#define PciDeviceFn(d)		(d)->devfn
+#define PciVendorId(d)		(d)->vendor
+#define PciDeviceId(d)		(d)->device
+#define PciIrqLine(d)		(d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->resource[index].start;
+	if ((pdev->resource[index].flags & 0x7) == 0x4)
+		++index;
+	return ++index;
+}
+#else
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->base_address[index++];
+	if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		*base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+		++index;
+	}
+	return index;
+}
+#endif
+
+#else	/* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL		(~0u)
+#define PciBusNumber(d)		((d)>>8)
+#define PciDeviceFn(d)		((d)&0xff)
+#define __PciDev(busn, devfn)	(((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+	pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+	pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+	pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+	pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+	pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+	pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+	static unsigned short pci_index;
+	int retv;
+	unsigned char bus_number, device_fn;
+
+	if (prev == PCIDEV_NULL)
+		pci_index = 0;
+	else
+		++pci_index;
+	retv = pcibios_find_device (vendor, device, pci_index,
+				    &bus_number, &device_fn);
+	return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+	u_short vendor_id;
+	pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+	return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+	u_short device_id;
+	pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+	return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+	u_char irq;
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	return irq;
+}
+
+static int __init 
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+	u_int32 tmp;
+	
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+	*base = tmp;
+	offset += sizeof(u_int32);
+	if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+		*base |= (((u_long)tmp) << 32);
+#endif
+		offset += sizeof(u_int32);
+	}
+	return offset;
+}
+
+#endif	/* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
 /*==========================================================
 **
 **	Debugging tags
@@ -481,17 +573,15 @@
 
 #define DEBUG_ALLOC    (0x0001)
 #define DEBUG_PHASE    (0x0002)
-#define DEBUG_POLL     (0x0004)
 #define DEBUG_QUEUE    (0x0008)
 #define DEBUG_RESULT   (0x0010)
-#define DEBUG_SCATTER  (0x0020)
+#define DEBUG_POINTER  (0x0020)
 #define DEBUG_SCRIPT   (0x0040)
 #define DEBUG_TINY     (0x0080)
 #define DEBUG_TIMING   (0x0100)
 #define DEBUG_NEGO     (0x0200)
 #define DEBUG_TAGS     (0x0400)
-#define DEBUG_FREEZE   (0x0800)
-#define DEBUG_RESTART  (0x1000)
+#define DEBUG_IC       (0x0800)
 
 /*
 **    Enable/Disable debug messages.
@@ -522,7 +612,7 @@
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
 
-spinlock_t sym53c8xx_lock;
+spinlock_t sym53c8xx_lock = SPIN_LOCK_UNLOCKED;
 #define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&sym53c8xx_lock, flags)
 #define	NCR_UNLOCK_DRIVER(flags)   spin_unlock_irqrestore(&sym53c8xx_lock,flags)
 
@@ -530,20 +620,11 @@
 #define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
 #define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
 
-#	if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99)
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags) \
+#define	NCR_LOCK_SCSI_DONE(np, flags) \
 		spin_lock_irqsave(&io_request_lock, flags)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags) \
+#define	NCR_UNLOCK_SCSI_DONE(np, flags) \
 		spin_unlock_irqrestore(&io_request_lock, flags)
 
-#	else
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
-
-#	endif
-
 #else
 
 #define	NCR_LOCK_DRIVER(flags)     do { save_flags(flags); cli(); } while (0)
@@ -559,17 +640,6 @@
 #endif
 
 /*
-**	Address translation
-**
-**	The driver has to provide bus memory addresses to 
-**	the script processor. Because some architectures use 
-**	different physical addressing scheme from the PCI BUS, 
-**	we use virt_to_bus() instead of virt_to_phys().
-*/
-
-#define vtobus(p)	virt_to_bus(p)
-
-/*
 **	Memory mapped IO
 **
 **	Since linux-2.1, we must use ioremap() to map the io memory space.
@@ -587,10 +657,13 @@
 
 #ifdef __sparc__
 #  include <asm/irq.h>
-#  define ioremap(base, size)		((u_long) __va(base))
-#  define iounmap(vaddr)
-#  define pcivtobus(p)			((p) & pci_dvma_mask)
-#  define memcpy_to_pci(a, b, c)	memcpy_toio((void *) (a), (b), (c))
+#  if LINUX_VERSION_CODE < LinuxVersionCode(2,3,0)
+     /* ioremap/iounmap broken in 2.2.x on Sparc. -DaveM */
+#    define ioremap(base, size)		((u_long) __va(base))
+#    define iounmap(vaddr)
+#  endif
+#  define pcivtobus(p)			bus_dvma_to_mem(p)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((void *)(a), (const void *)(b), (c))
 #elif defined(__alpha__)
 #  define pcivtobus(p)			((p) & 0xfffffffful)
 #  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
@@ -600,9 +673,7 @@
 #endif
 
 #ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
-__initfunc(
-static u_long remap_pci_mem(u_long base, u_long size)
-)
+static u_long __init remap_pci_mem(u_long base, u_long size)
 {
 	u_long page_base	= ((u_long) base) & PAGE_MASK;
 	u_long page_offs	= ((u_long) base) - page_base;
@@ -611,9 +682,7 @@
 	return page_remapped? (page_remapped + page_offs) : 0UL;
 }
 
-__initfunc(
-static void unmap_pci_mem(u_long vaddr, u_long size)
-)
+static void __init unmap_pci_mem(u_long vaddr, u_long size)
 {
 	if (vaddr)
 		iounmap((void *) (vaddr & PAGE_MASK));
@@ -649,44 +718,76 @@
 **	this allocator allows simple and fast address calculations  
 **	from the SCRIPTS code. In addition, cache line alignment 
 **	is guaranteed for power of 2 cache line size.
+**	Enhanced in linux-2.3.44 to provide a memory pool per pcidev 
+**	to support dynamic dma mapping. (I would have preferred a 
+**	real bus astraction, btw).
 */
 
-#define MEMO_SHIFT	4	/* 16 bytes minimum memory chunk */
-#define MEMO_PAGE_ORDER	0	/* 1 PAGE maximum (for now (ever?) */
-typedef unsigned long addr;	/* Enough bits to bit-hack addresses */
-
-#define MEMO_FREE_UNUSED	/* Free unused pages immediately */
-
-struct m_link {
-	struct m_link *next;	/* Simple links are enough */
-};
-
-#ifndef GFP_DMA_32BIT
-#define GFP_DMA_32BIT	0	/* Will this flag ever exist */
-#endif
-
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
-#define get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
 #else
-#define get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order, 0)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)
 #endif
 
-/*
-**	Lists of available memory chunks.
-**	Starts with 16 bytes chunks until 1 PAGE chunks.
-*/
-static struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+#define MEMO_SHIFT	4	/* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER	0	/* 1 PAGE  maximum */
+#else
+#define MEMO_PAGE_ORDER	1	/* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED	/* Free unused pages immediately */
+#define MEMO_WARN	1
+#define MEMO_GFP_FLAGS	GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT	(PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE	(1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK	(MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t;	/* Enough bits to bit-hack addresses */
+typedef pcidev_t m_bush_t;	/* Something that addresses DMAable */
+
+typedef struct m_link {		/* Link between free memory chunks */
+	struct m_link *next;
+} m_link_s;
+
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+typedef struct m_vtob {		/* Virtual to Bus address translation */
+	struct m_vtob *next;
+	m_addr_t vaddr;
+	m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT		5
+#define VTOB_HASH_SIZE		(1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK		(VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m)	\
+	((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif
+
+typedef struct m_pool {		/* Memory pool of a given kind */
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+	m_bush_t bush;
+	m_addr_t (*getp)(struct m_pool *);
+	void (*freep)(struct m_pool *, m_addr_t);
+#define M_GETP()		mp->getp(mp)
+#define M_FREEP(p)		mp->freep(mp, p)
+#define GetPages()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define FreePages(p)		free_pages(p, MEMO_PAGE_ORDER)
+	int nump;
+	m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+	struct m_pool *next;
+#else
+#define M_GETP()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define M_FREEP(p)		free_pages(p, MEMO_PAGE_ORDER)
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+	struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
 
-/*
-**	Allocate a memory area aligned on the lowest power of 2 
-**	greater than the requested size.
-*/
-static void *__m_alloc(int size)
+static void *___m_alloc(m_pool_s *mp, int size)
 {
 	int i = 0;
 	int s = (1 << MEMO_SHIFT);
 	int j;
-	addr a ;
+	m_addr_t a;
+	m_link_s *h = mp->h;
 
 	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
 		return 0;
@@ -699,7 +800,7 @@
 	j = i;
 	while (!h[j].next) {
 		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
-			h[j].next = (struct m_link *)get_pages(MEMO_PAGE_ORDER);
+			h[j].next = (m_link_s *) M_GETP();
 			if (h[j].next)
 				h[j].next->next = 0;
 			break;
@@ -707,36 +808,32 @@
 		++j;
 		s <<= 1;
 	}
-	a = (addr) h[j].next;
+	a = (m_addr_t) h[j].next;
 	if (a) {
 		h[j].next = h[j].next->next;
 		while (j > i) {
 			j -= 1;
 			s >>= 1;
-			h[j].next = (struct m_link *) (a+s);
+			h[j].next = (m_link_s *) (a+s);
 			h[j].next->next = 0;
 		}
 	}
 #ifdef DEBUG
-	printk("m_alloc(%d) = %p\n", size, (void *) a);
+	printk("___m_alloc(%d) = %p\n", size, (void *) a);
 #endif
 	return (void *) a;
 }
 
-/*
-**	Free a memory area allocated using m_alloc().
-**	Coalesce buddies.
-**	Free pages that become unused if MEMO_FREE_UNUSED is defined.
-*/
-static void __m_free(void *ptr, int size)
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
 {
 	int i = 0;
 	int s = (1 << MEMO_SHIFT);
-	struct m_link *q;
-	addr a, b;
+	m_link_s *q;
+	m_addr_t a, b;
+	m_link_s *h = mp->h;
 
 #ifdef DEBUG
-	printk("m_free(%p, %d)\n", ptr, size);
+	printk("___m_free(%p, %d)\n", ptr, size);
 #endif
 
 	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
@@ -747,23 +844,23 @@
 		++i;
 	}
 
-	a = (addr) ptr;
+	a = (m_addr_t) ptr;
 
 	while (1) {
 #ifdef MEMO_FREE_UNUSED
 		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
-			free_pages(a, MEMO_PAGE_ORDER);
+			M_FREEP(a);
 			break;
 		}
 #endif
 		b = a ^ s;
 		q = &h[i];
-		while (q->next && q->next != (struct m_link *) b) {
+		while (q->next && q->next != (m_link_s *) b) {
 			q = q->next;
 		}
 		if (!q->next) {
-			((struct m_link *) a)->next = h[i].next;
-			h[i].next = (struct m_link *) a;
+			((m_link_s *) a)->next = h[i].next;
+			h[i].next = (m_link_s *) a;
 			break;
 		}
 		q->next = q->next->next;
@@ -773,357 +870,537 @@
 	}
 }
 
-#define MEMO_WARN	1
-
-/*
-**	The memory pool is shared by all instances.
-**	We use a global SMP LOCK to be SMP safe.
-*/
-
-static void *m_calloc(int size, char *name, int uflags)
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
 {
-	u_long flags;
 	void *p;
 
-	NCR_LOCK_DRIVER(flags);
-	p = __m_alloc(size);
-	NCR_UNLOCK_DRIVER(flags);
+	p = ___m_alloc(mp, size);
 
 	if (DEBUG_FLAGS & DEBUG_ALLOC)
 		printk ("new %-10s[%4d] @%p.\n", name, size, p);
 
 	if (p)
-		memset(p, 0, size);
+		bzero(p, size);
 	else if (uflags & MEMO_WARN)
 		printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
 
 	return p;
 }
 
-static void m_free(void *ptr, int size, char *name)
-{
-	u_long flags;
+#define __m_calloc(mp, s, n)	__m_calloc2(mp, s, n, MEMO_WARN)
 
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
 	if (DEBUG_FLAGS & DEBUG_ALLOC)
 		printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
 
-	NCR_LOCK_DRIVER(flags);
-	__m_free(ptr, size);
-	NCR_UNLOCK_DRIVER(flags);
+	___m_free(mp, ptr, size);
+
 }
 
 /*
-**	Transfer direction
-**
-**	Low-level scsi drivers under Linux do not receive the expected 
-**	data transfer direction from upper scsi drivers.
-**	The driver will only check actual data direction for common 
-**	scsi opcodes. Other ones may cause problem, since they may 
-**	depend on device type or be vendor specific.
-**	I would prefer to never trust the device for data direction, 
-**	but that is not possible.
-**
-**	The original driver requires the expected direction to be known.
-**	The Linux version of the driver has been enhanced in order to 
-**	be able to transfer data in the direction choosen by the target. 
-*/
+ * With pci bus iommu support, we use a default pool of unmapped memory 
+ * for memory we donnot need to DMA from/to and one pool per pcidev for 
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
 
-#define XFER_IN		(1)
-#define XFER_OUT	(2)
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
 
-/*
-**	Head of list of NCR boards
-**
-**	For kernel version < 1.3.70, host is retrieved by its irq level.
-**	For later kernels, the internal host control block address 
-**	(struct ncb) is used as device id parameter of the irq stuff.
-*/
+static m_pool_s mp0;
 
-static struct Scsi_Host	*first_host = NULL;
+#else
 
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+	m_addr_t m = GetPages();
+	if (m)
+		++mp->nump;
+	return m;
+}
 
-/*
-**	/proc directory entry and proc_info function
-*/
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+	FreePages(m);
+	--mp->nump;
+}
 
-static struct proc_dir_entry proc_scsi_sym53c8xx = {
-    PROC_SCSI_SYM53C8XX, 9, NAME53C8XX,
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset,
-			int length, int hostno, int func);
-#endif
+static m_pool_s mp0 = {0, ___mp0_getp, ___mp0_freep};
 
-/*
-**	Driver setup.
-**
-**	This structure is initialized from linux config options.
-**	It can be overridden at boot-up by the boot command line.
-*/
-static struct ncr_driver_setup
-	driver_setup			= SCSI_NCR_DRIVER_SETUP;
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
 
-#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-static struct ncr_driver_setup
-	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
-# ifdef	MODULE
-char *sym53c8xx = 0;	/* command line passed by insmod */
-#  if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(sym53c8xx, "s");
-#  endif
-# endif
-#endif
+static void *m_calloc(int size, char *name)
+{
+	u_long flags;
+	void *m;
+	NCR_LOCK_DRIVER(flags);
+	m = __m_calloc(&mp0, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+	return m;
+}
+
+static void m_free(void *ptr, int size, char *name)
+{
+	u_long flags;
+	NCR_LOCK_DRIVER(flags);
+	__m_free(&mp0, ptr, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+}
 
 /*
-**	Other Linux definitions
-*/
-#define SetScsiResult(cmd, h_sts, s_sts) \
-	cmd->result = (((h_sts) << 16) + ((s_sts) & 0x7f))
+ * DMAable pools.
+ */
 
-static void sym53c8xx_select_queue_depths(
-	struct Scsi_Host *host, struct scsi_device *devlist);
-static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
-static void sym53c8xx_timeout(unsigned long np);
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
 
-#define initverbose (driver_setup.verbose)
-#define bootverbose (np->verbose)
+/* Without pci bus iommu support, all the memory is assumed DMAable */
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
+#define __m_calloc_dma(b, s, n)		m_calloc(s, n)
+#define __m_free_dma(b, p, s, n)	m_free(p, s, n)
+#define __vtobus(b, p)			virt_to_bus(p)
+
+#else
 
 /*
-**	Structures used by sym53c8xx_detect/sym53c8xx_pci_init to 
-**	transmit device configuration to the ncr_attach() function.
-*/
-typedef struct {
-	int	bus;
-	u_char	device_fn;
-	u_long	base;
-	u_long	base_2;
-	u_long	io_port;
-	int	irq;
-/* port and reg fields to use INB, OUTB macros */
-	u_long	base_io;
-	volatile struct ncr_reg	*reg;
-} ncr_slot;
+ * With pci bus iommu support, we maintain one pool per pcidev and a 
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+	m_addr_t vp;
+	m_vtob_s *vbp;
 
-typedef struct {
-	int type;
-#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
-#define	SCSI_NCR_TEKRAM_NVRAM	(2)
-#ifdef	SCSI_NCR_NVRAM_SUPPORT
-	union {
-		Symbios_nvram Symbios;
-		Tekram_nvram Tekram;
-	} data;
-#endif
-} ncr_nvram;
+	vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+	if (vbp) {
+		dma_addr_t daddr;
+		vp = (m_addr_t) pci_alloc_consistent(mp->bush,
+						PAGE_SIZE<<MEMO_PAGE_ORDER,
+						&daddr);
+		if (vp) {
+			int hc = VTOB_HASH_CODE(vp);
+			vbp->vaddr = vp;
+			vbp->baddr = daddr;
+			vbp->next = mp->vtob[hc];
+			mp->vtob[hc] = vbp;
+			++mp->nump;
+			return vp;
+		}
+		else
+			__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+	}
+	return 0;
+}
 
-/*
-**	Structure used by sym53c8xx_detect/sym53c8xx_pci_init
-**	to save data on each detected board for ncr_attach().
-*/
-typedef struct {
-	ncr_slot  slot;
-	ncr_chip  chip;
-	ncr_nvram *nvram;
-	u_char host_id;
-#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
-	u_char pqs_pds;
-#endif
-	int attach_done;
-} ncr_device;
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+	m_vtob_s **vbpp, *vbp;
+	int hc = VTOB_HASH_CODE(m);
 
-/*==========================================================
-**
-**	assert ()
-**
-**==========================================================
-**
-**	modified copy from 386bsd:/usr/include/sys/assert.h
-**
-**----------------------------------------------------------
-*/
+	vbpp = &mp->vtob[hc];
+	while (*vbpp && (*vbpp)->vaddr != m)
+		vbpp = &(*vbpp)->next;
+	if (*vbpp) {
+		vbp = *vbpp;
+		*vbpp = (*vbpp)->next;
+		pci_free_consistent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+				    (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+		__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+		--mp->nump;
+	}
+}
 
-#define	assert(expression) { \
-	if (!(expression)) { \
-		(void)panic( \
-			"assertion \"%s\" failed: file \"%s\", line %d\n", \
-			#expression, \
-			__FILE__, __LINE__); \
-	} \
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+	return mp;
 }
 
-/*==========================================================
-**
-**	Big/Little endian support.
-**
-**==========================================================
-*/
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+	if (mp) {
+		bzero(mp, sizeof(*mp));
+		mp->bush = bush;
+		mp->getp = ___dma_getp;
+		mp->freep = ___dma_freep;
+		mp->next = mp0.next;
+		mp0.next = mp;
+	}
+	return mp;
+}
 
-/*
-**	If the NCR uses big endian addressing mode over the 
-**	PCI, actual io register addresses for byte and word 
-**	accesses must be changed according to lane routing.
-**	Btw, ncr_offb() and ncr_offw() macros only apply to 
-**	constants and so donnot generate bloated code.
-*/
+static void ___del_dma_pool(m_pool_s *p)
+{
+	struct m_pool **pp = &mp0.next;
 
-#if	defined(SCSI_NCR_BIG_ENDIAN)
+	while (*pp && *pp != p)
+		pp = &(*pp)->next;
+	if (*pp) {
+		*pp = (*pp)->next;
+		__m_free(&mp0, p, sizeof(*p), "MPOOL");
+	}
+}
 
-#define ncr_offb(o)	(((o)&~3)+((~((o)&3))&3))
-#define ncr_offw(o)	(((o)&~3)+((~((o)&3))&2))
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+	void *m = 0;
 
-#else
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (!mp)
+		mp = ___cre_dma_pool(bush);
+	if (mp)
+		m = __m_calloc(mp, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
 
-#define ncr_offb(o)	(o)
-#define ncr_offw(o)	(o)
+	return m;
+}
 
-#endif
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp)
+		__m_free(mp, m, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+	u_long flags;
+	m_pool_s *mp;
+	int hc = VTOB_HASH_CODE(m);
+	m_vtob_s *vp = 0;
+	m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp) {
+		vp = mp->vtob[hc];
+		while (vp && (m_addr_t) vp->vaddr != a)
+			vp = vp->next;
+	}
+	NCR_UNLOCK_DRIVER(flags);
+	return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define _m_calloc_dma(np, s, n)		__m_calloc_dma(np->pdev, s, n)
+#define _m_free_dma(np, p, s, n)	__m_free_dma(np->pdev, p, s, n)
+#define m_calloc_dma(s, n)		_m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n)		_m_free_dma(np, p, s, n)
+#define _vtobus(np, p)			__vtobus(np->pdev, p)
+#define vtobus(p)			_vtobus(np, p)
 
 /*
-**	If the CPU and the NCR use same endian-ness adressing,
-**	no byte reordering is needed for script patching.
-**	Macro cpu_to_scr() is to be used for script patching.
-**	Macro scr_to_cpu() is to be used for getting a DWORD 
-**	from the script.
-*/
+ *  Deal with DMA mapping/unmapping.
+ */
 
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
 
-#define cpu_to_scr(dw)	cpu_to_le32(dw)
-#define scr_to_cpu(dw)	le32_to_cpu(dw)
+/* Linux versions prior to pci bus iommu kernel interface */
 
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+#define __unmap_scsi_data(pdev, cmd)	do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd)	((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd)	do {; } while (0)
 
-#define cpu_to_scr(dw)	cpu_to_be32(dw)
-#define scr_to_cpu(dw)	be32_to_cpu(dw)
+#define scsi_sg_dma_address(sc)		vtobus((sc)->address)
+#define scsi_sg_dma_len(sc)		((sc)->length)
 
 #else
 
-#define cpu_to_scr(dw)	(dw)
-#define scr_to_cpu(dw)	(dw)
+/* Linux version with pci bus iommu kernel interface */
 
-#endif
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped	SCp.phase
+#define __data_mapping	SCp.have_data_in
 
-/*==========================================================
-**
-**	Access to the controller chip.
-**
-**	If NCR_IOMAPPED is defined, the driver will use 
-**	normal IOs instead of the MEMORY MAPPED IO method  
-**	recommended by PCI specifications.
-**	If all PCI bridges, host brigdes and architectures 
-**	would have been correctly designed for PCI, this 
-**	option would be useless.
-**
-**==========================================================
-*/
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
 
-/*
-**	If the CPU and the NCR use same endian-ness adressing,
-**	no byte reordering is needed for accessing chip io 
-**	registers. Functions suffixed by '_raw' are assumed 
-**	to access the chip over the PCI without doing byte 
-**	reordering. Functions suffixed by '_l2b' are 
-**	assumed to perform little-endian to big-endian byte 
-**	reordering, those suffixed by '_b2l' blah, blah,
-**	blah, ...
-*/
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_unmap_single(pdev, cmd->__data_mapping,
+				 cmd->request_bufflen, dma_dir);
+		break;
+	}
+	cmd->__data_mapped = 0;
+}
 
-#if defined(NCR_IOMAPPED)
+static u_long __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	dma_addr_t mapping;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
 
-/*
-**	IO mapped only input / ouput
-*/
+	if (cmd->request_bufflen == 0)
+		return 0;
 
-#define	INB_OFF(o)		inb (np->base_io + ncr_offb(o))
-#define	OUTB_OFF(o, val)	outb ((val), np->base_io + ncr_offb(o))
+	mapping = pci_map_single(pdev, cmd->request_buffer,
+				 cmd->request_bufflen, dma_dir);
+	cmd->__data_mapped = 1;
+	cmd->__data_mapping = mapping;
 
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+	return mapping;
+}
 
-#define	INW_OFF(o)		inw_l2b (np->base_io + ncr_offw(o))
-#define	INL_OFF(o)		inl_l2b (np->base_io + (o))
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int use_sg;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
 
-#define	OUTW_OFF(o, val)	outw_b2l ((val), np->base_io + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_b2l ((val), np->base_io + (o))
+	if (cmd->use_sg == 0)
+		return 0;
 
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+	cmd->__data_mapped = 2;
+	cmd->__data_mapping = use_sg;
 
-#define	INW_OFF(o)		inw_b2l (np->base_io + ncr_offw(o))
-#define	INL_OFF(o)		inl_b2l (np->base_io + (o))
+	return use_sg;
+}
 
-#define	OUTW_OFF(o, val)	outw_l2b ((val), np->base_io + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_l2b ((val), np->base_io + (o))
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
 
-#else
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_dma_sync_single(pdev, cmd->__data_mapping,
+				    cmd->request_bufflen, dma_dir);
+		break;
+	}
+}
+
+#define scsi_sg_dma_address(sc)		sg_dma_address(sc)
+#define scsi_sg_dma_len(sc)		sg_dma_len(sc)
 
-#define	INW_OFF(o)		inw_raw (np->base_io + ncr_offw(o))
-#define	INL_OFF(o)		inl_raw (np->base_io + (o))
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
 
-#define	OUTW_OFF(o, val)	outw_raw ((val), np->base_io + ncr_offw(o))
-#define	OUTL_OFF(o, val)	outl_raw ((val), np->base_io + (o))
+#define unmap_scsi_data(np, cmd)	__unmap_scsi_data(np->pdev, cmd)
+#define map_scsi_single_data(np, cmd)	__map_scsi_single_data(np->pdev, cmd)
+#define map_scsi_sg_data(np, cmd)	__map_scsi_sg_data(np->pdev, cmd)
+#define sync_scsi_data(np, cmd)		__sync_scsi_data(np->pdev, cmd)
 
-#endif	/* ENDIANs */
 
-#else	/* defined NCR_IOMAPPED */
+/*
+ * Print out some buffer.
+ */
+static void ncr_print_hex(u_char *p, int n)
+{
+	while (n-- > 0)
+		printk (" %x", *p++);
+}
+
+static void ncr_printl_hex(char *label, u_char *p, int n)
+{
+	printk("%s", label);
+	ncr_print_hex(p, n);
+	printk (".\n");
+}
 
 /*
-**	MEMORY mapped IO input / output
+**	Transfer direction
+**
+**	Until some linux kernel version near 2.3.40, low-level scsi 
+**	drivers were not told about data transfer direction.
+**	We check the existence of this feature that has been expected 
+**	for a _long_ time by all SCSI driver developers by just 
+**	testing against the definition of SCSI_DATA_UNKNOWN. Indeed 
+**	this is a hack, but testing against a kernel version would 
+**	have been a shame. ;-)
 */
+#ifdef	SCSI_DATA_UNKNOWN
+
+#define scsi_data_direction(cmd)	(cmd->sc_data_direction)
+
+#else
+
+#define	SCSI_DATA_UNKNOWN	0
+#define	SCSI_DATA_WRITE		1
+#define	SCSI_DATA_READ		2
+#define	SCSI_DATA_NONE		3
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+	int direction;
 
-#define INB_OFF(o)		readb((char *)np->reg + ncr_offb(o))
-#define OUTB_OFF(o, val)	writeb((val), (char *)np->reg + ncr_offb(o))
+	switch((int) cmd->cmnd[0]) {
+	case 0x08:  /*	READ(6)				08 */
+	case 0x28:  /*	READ(10)			28 */
+	case 0xA8:  /*	READ(12)			A8 */
+		direction = SCSI_DATA_READ;
+		break;
+	case 0x0A:  /*	WRITE(6)			0A */
+	case 0x2A:  /*	WRITE(10)			2A */
+	case 0xAA:  /*	WRITE(12)			AA */
+		direction = SCSI_DATA_WRITE;
+		break;
+	default:
+		direction = SCSI_DATA_UNKNOWN;
+		break;
+	}
 
-#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+	return direction;
+}
 
-#define INW_OFF(o)		readw_l2b((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_l2b((char *)np->reg + (o))
+#endif	/* SCSI_DATA_UNKNOWN */
 
-#define OUTW_OFF(o, val)	writew_b2l((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_b2l((val), (char *)np->reg + (o))
+/*
+**	Head of list of NCR boards
+**
+**	For kernel version < 1.3.70, host is retrieved by its irq level.
+**	For later kernels, the internal host control block address 
+**	(struct ncb) is used as device id parameter of the irq stuff.
+*/
 
-#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+static struct Scsi_Host	*first_host = NULL;
 
-#define INW_OFF(o)		readw_b2l((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_b2l((char *)np->reg + (o))
 
-#define OUTW_OFF(o, val)	writew_l2b((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_l2b((val), (char *)np->reg + (o))
+/*
+**	/proc directory entry and proc_info function
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_sym53c8xx = {
+    PROC_SCSI_SYM53C8XX, 9, NAME53C8XX,
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset,
+			int length, int hostno, int func);
+#endif
 
-#else
+/*
+**	Driver setup.
+**
+**	This structure is initialized from linux config options.
+**	It can be overridden at boot-up by the boot command line.
+*/
+static struct ncr_driver_setup
+	driver_setup			= SCSI_NCR_DRIVER_SETUP;
 
-#define INW_OFF(o)		readw_raw((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o)		readl_raw((char *)np->reg + (o))
+#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
+# ifdef	MODULE
+char *sym53c8xx = 0;	/* command line passed by insmod */
+#  if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(sym53c8xx, "s");
+#  endif
+# endif
+#endif
 
-#define OUTW_OFF(o, val)	writew_raw((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val)	writel_raw((val), (char *)np->reg + (o))
+/*
+**	Other Linux definitions
+*/
+#define SetScsiResult(cmd, h_sts, s_sts) \
+	cmd->result = (((h_sts) << 16) + ((s_sts) & 0x7f))
 
+/* We may have to remind our amnesiac SCSI layer of the reason of the abort */
+#if 0
+#define SetScsiAbortResult(cmd)	\
+	  SetScsiResult(	\
+	    cmd, 		\
+	    (cmd)->abort_reason == DID_TIME_OUT ? DID_TIME_OUT : DID_ABORT, \
+	    0xff)
+#else
+#define SetScsiAbortResult(cmd) SetScsiResult(cmd, DID_ABORT, 0xff)
 #endif
 
-#endif	/* defined NCR_IOMAPPED */
+static void sym53c8xx_select_queue_depths(
+	struct Scsi_Host *host, struct scsi_device *devlist);
+static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
+static void sym53c8xx_timeout(unsigned long np);
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static u_char Tekram_sync[16] __initdata =
+	{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-#define INB(r)		INB_OFF (offsetof(struct ncr_reg,r))
-#define INW(r)		INW_OFF (offsetof(struct ncr_reg,r))
-#define INL(r)		INL_OFF (offsetof(struct ncr_reg,r))
+/*
+**	Structures used by sym53c8xx_detect/sym53c8xx_pci_init to 
+**	transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+	int	bus;
+	u_char	device_fn;
+	u_long	base;
+	u_long	base_2;
+	u_long	io_port;
+	int	irq;
+/* port and reg fields to use INB, OUTB macros */
+	u_long	base_io;
+	volatile struct ncr_reg	*reg;
+} ncr_slot;
 
-#define OUTB(r, val)	OUTB_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTW(r, val)	OUTW_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTL(r, val)	OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+typedef struct {
+	int type;
+#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
+#define	SCSI_NCR_TEKRAM_NVRAM	(2)
+#ifdef	SCSI_NCR_NVRAM_SUPPORT
+	union {
+		Symbios_nvram Symbios;
+		Tekram_nvram Tekram;
+	} data;
+#endif
+} ncr_nvram;
 
 /*
-**	Set bit field ON, OFF 
+**	Structure used by sym53c8xx_detect/sym53c8xx_pci_init
+**	to save data on each detected board for ncr_attach().
 */
+typedef struct {
+	pcidev_t  pdev;
+	ncr_slot  slot;
+	ncr_chip  chip;
+	ncr_nvram *nvram;
+	u_char host_id;
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+	u_char pqs_pds;
+#endif
+	int attach_done;
+} ncr_device;
 
-#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
-#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
-#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
-#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
-#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
-#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
+/*==========================================================
+**
+**	assert ()
+**
+**==========================================================
+**
+**	modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
 
+#define	assert(expression) { \
+	if (!(expression)) { \
+		(void)panic( \
+			"assertion \"%s\" failed: file \"%s\", line %d\n", \
+			#expression, \
+			__FILE__, __LINE__); \
+	} \
+}
 
 /*==========================================================
 **
@@ -1157,36 +1434,42 @@
 
 #define	SIR_BAD_STATUS		(1)
 #define	SIR_SEL_ATN_NO_MSG_OUT	(2)
-#define	SIR_NEGO_SYNC		(3)
-#define	SIR_NEGO_WIDE		(4)
+#define	SIR_MSG_RECEIVED	(3)
+#define	SIR_MSG_WEIRD		(4)
 #define	SIR_NEGO_FAILED		(5)
 #define	SIR_NEGO_PROTO		(6)
-#define	SIR_REJECT_RECEIVED	(7)
+#define	SIR_SCRIPT_STOPPED	(7)
 #define	SIR_REJECT_TO_SEND	(8)
-#define	SIR_IGN_RESIDUE		(9)
-#define	SIR_MISSING_SAVE	(10)
+#define	SIR_SWIDE_OVERRUN	(9)
+#define	SIR_SODL_UNDERRUN	(10)
 #define	SIR_RESEL_NO_MSG_IN	(11)
 #define	SIR_RESEL_NO_IDENTIFY	(12)
 #define	SIR_RESEL_BAD_LUN	(13)
-#define	SIR_UNUSED_14		(14)
+#define	SIR_TARGET_SELECTED	(14)
 #define	SIR_RESEL_BAD_I_T_L	(15)
 #define	SIR_RESEL_BAD_I_T_L_Q	(16)
-#define	SIR_UNUSED_17		(17)
+#define	SIR_ABORT_SENT		(17)
 #define	SIR_RESEL_ABORTED	(18)
 #define	SIR_MSG_OUT_DONE	(19)
-#define	SIR_MAX			(19)
+#define	SIR_AUTO_SENSE_DONE	(20)
+#define	SIR_DUMMY_INTERRUPT	(21)
+#define	SIR_DATA_OVERRUN	(22)
+#define	SIR_BAD_PHASE		(23)
+#define	SIR_MAX			(23)
 
 /*==========================================================
 **
-**	Extended error codes.
+**	Extended error bits.
 **	xerr_status field of struct ccb.
 **
 **==========================================================
 */
 
-#define	XE_OK		(0)
-#define	XE_EXTRA_DATA	(1)	/* unexpected data phase */
-#define	XE_BAD_PHASE	(2)	/* illegal phase (4/5)   */
+#define	XE_EXTRA_DATA	(1)	/* unexpected data phase	 */
+#define	XE_BAD_PHASE	(2)	/* illegal phase (4/5)		 */
+#define	XE_PARITY_ERR	(4)	/* unrecovered SCSI parity error */
+#define XE_SODL_UNRUN   (1<<3)
+#define XE_SWIDE_OVRUN  (1<<4)
 
 /*==========================================================
 **
@@ -1196,8 +1479,10 @@
 **==========================================================
 */
 
+#define NS_NOCHANGE	(0)
 #define NS_SYNC		(1)
 #define NS_WIDE		(2)
+#define NS_PPR		(4)
 
 /*==========================================================
 **
@@ -1209,9 +1494,6 @@
 */
 
 #define	QUIRK_AUTOSAVE	(0x01)
-#define	QUIRK_NOMSG	(0x02)
-#define QUIRK_NOSYNC	(0x10)
-#define QUIRK_NOWIDE16	(0x20)
 
 /*==========================================================
 **
@@ -1273,36 +1555,14 @@
 #define UC_SETORDER	13
 #define UC_SETWIDE	14
 #define UC_SETFLAG	15
-#define UC_CLEARPROF	16
 #define UC_SETVERBOSE	17
+#define UC_RESETDEV	18
+#define UC_CLEARDEV	19
 
 #define	UF_TRACE	(0x01)
 #define	UF_NODISC	(0x02)
 #define	UF_NOSCAN	(0x04)
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-/*
-**	profiling data (per host)
-*/
-
-struct profile {
-	u_long	num_trans;
-	u_long	num_disc;
-	u_long	num_disc0;
-	u_long	num_break;
-	u_long	num_int;
-	u_long	num_fly;
-	u_long	num_kbytes;
-#if 000
-	u_long	num_br1k;
-	u_long	num_br2k;
-	u_long	num_br4k;
-	u_long	num_br8k;
-	u_long	num_brnk;
-#endif
-};
-#endif
-
 /*========================================================================
 **
 **	Declaration of structs:		target control block
@@ -1318,6 +1578,7 @@
 	*/
 	u_int32		*luntbl;	/* lcbs bus address table	*/
 	u_int32		b_luntbl;	/* bus address of this table	*/
+	u_int32		b_lun0;		/* bus address of lun0		*/
 	lcb_p		l0p;		/* lcb of LUN #0 (normal case)	*/
 #if MAX_LUN > 1
 	lcb_p		*lmp;		/* Other lcb's [1..MAX_LUN]	*/
@@ -1330,23 +1591,22 @@
 	u_char		inq_byte7;	/* Contains these capabilities	*/
 
 	/*----------------------------------------------------------------
-	**	Pointer to the ccb used for negotiation.
-	**	Prevent from starting a negotiation for all queued commands 
-	**	when tagged command queuing is enabled.
+	**	Some flags.
 	**----------------------------------------------------------------
 	*/
-	ccb_p   nego_cp;
+	u_char		to_reset;	/* This target is to be reset	*/
 
 	/*----------------------------------------------------------------
-	**	statistical data
+	**	Pointer to the ccb used for negotiation.
+	**	Prevent from starting a negotiation for all queued commands 
+	**	when tagged command queuing is enabled.
 	**----------------------------------------------------------------
 	*/
-	u_long	transfers;
-	u_long	bytes;
+	ccb_p   nego_cp;
 
 	/*----------------------------------------------------------------
 	**	negotiation of wide and synch transfer and device quirks.
-	**	sval and wval are read from SCRIPTS and so have alignment 
+	**	sval, wval and uval are read from SCRIPTS and so have alignment 
 	**	constraints.
 	**----------------------------------------------------------------
 	*/
@@ -1357,6 +1617,15 @@
 /*1*/	u_char	quirks;
 /*2*/	u_char	widedone;
 /*3*/	u_char	wval;
+/*0*/	u_char	uval;
+
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+	u_char ic_min_sync;
+	u_char ic_max_width;
+	u_char ic_done;
+#endif
+	u_char ic_maximums_set;
+	u_char ppr_negotiation;
 
 	/*----------------------------------------------------------------
 	**	User settable limits and options.
@@ -1365,7 +1634,7 @@
 	*/
 	u_char	usrsync;
 	u_char	usrwide;
-	u_char	usrtags;
+	u_short	usrtags;
 	u_char	usrflag;
 };
 
@@ -1403,11 +1672,11 @@
 	*/
 	XPT_QUEHEAD	busy_ccbq;	/* Queue of busy CCBs		*/
 	XPT_QUEHEAD	wait_ccbq;	/* Queue of waiting for IO CCBs	*/
-	u_char		busyccbs;	/* CCBs busy for this lun	*/
-	u_char		queuedccbs;	/* CCBs queued to the controller*/
-	u_char		queuedepth;	/* Queue depth for this lun	*/
-	u_char		scdev_depth;	/* SCSI device queue depth	*/
-	u_char		maxnxs;		/* Max possible nexuses		*/
+	u_short		busyccbs;	/* CCBs busy for this lun	*/
+	u_short		queuedccbs;	/* CCBs queued to the controller*/
+	u_short		queuedepth;	/* Queue depth for this lun	*/
+	u_short		scdev_depth;	/* SCSI device queue depth	*/
+	u_short		maxnxs;		/* Max possible nexuses		*/
 
 	/*----------------------------------------------------------------
 	**	Control of tagged command queuing.
@@ -1415,22 +1684,23 @@
 	**	This avoids using a loop for tag allocation.
 	**----------------------------------------------------------------
 	*/
-	u_char		ia_tag;		/* Tag allocation index		*/
-	u_char		if_tag;		/* Tag release index		*/
-	u_char cb_tags[SCSI_NCR_MAX_TAGS]; /* Circular tags buffer	*/
+	u_short		ia_tag;		/* Tag allocation index		*/
+	u_short		if_tag;		/* Tag release index		*/
+	u_char		*cb_tags;	/* Circular tags buffer		*/
+	u_char		inq_byte7;	/* Store unit CmdQ capability	*/
 	u_char		usetags;	/* Command queuing is active	*/
-	u_char		maxtags;	/* Max NR of tags asked by user	*/
-	u_char		numtags;	/* Current number of tags	*/
-	u_char		inq_byte7;	/* Store unit CmdQ capabitility	*/
+	u_char		to_clear;	/* User wants to clear all tasks*/
+	u_short		maxtags;	/* Max NR of tags asked by user	*/
+	u_short		numtags;	/* Current number of tags	*/
 
 	/*----------------------------------------------------------------
 	**	QUEUE FULL and ORDERED tag control.
 	**----------------------------------------------------------------
 	*/
 	u_short		num_good;	/* Nr of GOOD since QUEUE FULL	*/
-	tagmap_t	tags_umap;	/* Used tags bitmap		*/
-	tagmap_t	tags_smap;	/* Tags in use at 'tag_stime'	*/
-	u_long		tags_stime;	/* Last time we set smap=umap	*/
+	u_short		tags_sum[2];	/* Tags sum counters		*/
+	u_char		tags_si;	/* Current index to tags sum	*/
+	u_long		tags_stime;	/* Last time we switch tags_sum	*/
 };
 
 /*========================================================================
@@ -1513,7 +1783,6 @@
 	**	Status fields.
 	**----------------------------------------------------------------
 	*/
-	u_char		scr_st[4];	/* script status		*/
 	u_char		status[4];	/* host status			*/
 };
 
@@ -1532,18 +1801,10 @@
 /*
 **	The status bytes are used by the host and the script processor.
 **
-**	The last four bytes (status[4]) are copied to the scratchb register
+**	The four bytes (status[4]) are copied to the scratchb register
 **	(declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
 **	and copied back just after disconnecting.
 **	Inside the script the XX_REG are used.
-**
-**	The first four bytes (scr_st[4]) are used inside the script by 
-**	"LOAD/STORE" commands.
-**	Because source and destination must have the same alignment
-**	in a DWORD, the fields HAVE to be at the choosen offsets.
-**		xerr_st		0	(0x34)	scratcha
-**		sync_st		1	(0x05)	sxfer
-**		wide_st		3	(0x03)	scntl3
 */
 
 /*
@@ -1572,23 +1833,19 @@
 #define HF_IN_PM1	(1u<<1)
 #define HF_ACT_PM	(1u<<2)
 #define HF_DP_SAVED	(1u<<3)
-#define HF_PAR_ERR	(1u<<4)
-#define HF_DATA_ST	(1u<<5)
+#define HF_AUTO_SENSE	(1u<<4)
+#define HF_DATA_IN	(1u<<5)
 #define HF_PM_TO_C	(1u<<6)
+#define HF_EXT_ERR	(1u<<7)
 
-/*
-**	First four bytes (script)
-*/
-#define  xerr_st       header.scr_st[0]
-#define  sync_st       header.scr_st[1]
-#define  nego_st       header.scr_st[2]
-#define  wide_st       header.scr_st[3]
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define HF_HINT_IARB	(1u<<7)
+#endif
 
 /*
-**	First four bytes (host)
+**	This one is stolen from QU_REG.:)
 */
-#define  xerr_status   phys.xerr_st
-#define  nego_status   phys.nego_st
+#define HF_DATA_ST	(1u<<7)
 
 /*==========================================================
 **
@@ -1620,8 +1877,10 @@
 
 	struct scr_tblsel  select;
 	struct scr_tblmove smsg  ;
+	struct scr_tblmove smsg_ext ;
 	struct scr_tblmove cmd   ;
 	struct scr_tblmove sense ;
+	struct scr_tblmove wresid;
 	struct scr_tblmove data [MAX_SCATTER];
 
 	/*
@@ -1632,13 +1891,6 @@
 
 	struct pm_ctx pm0;
 	struct pm_ctx pm1;
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**	Disconnection counter
-	*/
-	u_int32 num_disc;
-#endif
 };
 
 
@@ -1663,8 +1915,10 @@
 	**----------------------------------------------------------------
 	*/
 	Scsi_Cmnd	*cmd;		/* SCSI command 		*/
-	u_long		tlimit;		/* Deadline for this job	*/
+	u_char		cdb_buf[16];	/* Copy of CDB			*/
+	u_char		sense_buf[64];
 	int		data_len;	/* Total data length		*/
+	int		segments;	/* Number of SG segments	*/
 
 	/*----------------------------------------------------------------
 	**	Message areas.
@@ -1680,21 +1934,42 @@
 	u_char		scsi_smsg2[8];
 
 	/*----------------------------------------------------------------
+	**	Miscellaneous status'.
+	**----------------------------------------------------------------
+	*/
+	u_char		nego_status;	/* Negotiation status		*/
+	u_char		xerr_status;	/* Extended error flags		*/
+	u_int32		extra_bytes;	/* Extraneous bytes transferred	*/
+
+	/*----------------------------------------------------------------
+	**	Saved info for auto-sense
+	**----------------------------------------------------------------
+	*/
+	u_char		sv_scsi_status;
+	u_char		sv_xerr_status;
+
+	/*----------------------------------------------------------------
 	**	Other fields.
 	**----------------------------------------------------------------
 	*/
 	u_long		p_ccb;		/* BUS address of this CCB	*/
 	u_char		sensecmd[6];	/* Sense command		*/
-	u_char		tag;		/* Tag for this transfer	*/
-					/*  255 means no tag		*/
+	u_char		to_abort;	/* This CCB is to be aborted	*/
+	u_short		tag;		/* Tag for this transfer	*/
+					/*  NO_TAG means no tag		*/
+	u_char		tags_si;	/* Lun tags sum index (0,1)	*/
+
 	u_char		target;
 	u_char		lun;
-	u_char		queued;
-	u_char		auto_sense;
+	u_short		queued;
 	ccb_p		link_ccb;	/* Host adapter CCB chain	*/
 	ccb_p		link_ccbh;	/* Host adapter CCB hash chain	*/
 	XPT_QUEHEAD	link_ccbq;	/* Link to unit CCB queue	*/
 	u_int32		startp;		/* Initial data pointer		*/
+	u_int32		lastp0;		/* Initial 'lastp'		*/
+	int		ext_sg;		/* Extreme data pointer, used	*/
+	int		ext_ofs;	/*  to calculate the residual.	*/
+	int		resid;
 };
 
 #define CCB_PHYS(cp,lbl)	(cp->p_ccb + offsetof(struct ccb, lbl))
@@ -1764,7 +2039,7 @@
 	**----------------------------------------------------------------
 	*/
 	u_char	sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4,
-		sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4;
+		sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_stest1, sv_scntl4;
 
 	/*----------------------------------------------------------------
 	**	Actual initial value of IO register bits used by the 
@@ -1773,7 +2048,7 @@
 	**----------------------------------------------------------------
 	*/
 	u_char	rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest3, rv_ctest4, 
-		rv_ctest5, rv_stest2, rv_ccntl0, rv_ccntl1;
+		rv_ctest5, rv_stest2, rv_ccntl0, rv_ccntl1, rv_scntl4;
 
 	/*----------------------------------------------------------------
 	**	Target data.
@@ -1805,7 +2080,7 @@
 	**	SCRIPTS virtual and physical bus addresses.
 	**	'script'  is loaded in the on-chip RAM if present.
 	**	'scripth' stays in main memory for all chips except the 
-	**	53C896 that provides 8K on-chip RAM.
+	**	53C895A and 53C896 that provide 8K on-chip RAM.
 	**----------------------------------------------------------------
 	*/
 	struct script	*script0;	/* Copies of script and scripth	*/
@@ -1818,11 +2093,11 @@
 	**	General controller parameters and configuration.
 	**----------------------------------------------------------------
 	*/
+	pcidev_t	pdev;
 	u_short		device_id;	/* PCI device id		*/
 	u_char		revision_id;	/* PCI device revision id	*/
-	u_char		pci_bus;	/* PCI bus number		*/
-	u_char		pci_devfn;	/* PCI device and function	*/
-	u_int		features;	/* Chip features map		*/
+	u_char		bus;		/* PCI BUS number		*/
+	u_char		device_fn;	/* PCI BUS device and function	*/
 	u_char		myaddr;		/* SCSI id of the adapter	*/
 	u_char		maxburst;	/* log base 2 of dwords burst	*/
 	u_char		maxwide;	/* Maximum transfer width	*/
@@ -1832,6 +2107,17 @@
 	u_char		multiplier;	/* Clock multiplier (1,2,4)	*/
 	u_char		clock_divn;	/* Number of clock divisors	*/
 	u_long		clock_khz;	/* SCSI clock frequency in KHz	*/
+	u_int		features;	/* Chip features map		*/
+
+	/*----------------------------------------------------------------
+	**	Range for the PCI clock frequency measurement result
+	**	that ensures the algorithm used by the driver can be 
+	**	trusted for the SCSI clock frequency measurement.
+	**	(Assuming a PCI clock frequency of 33 MHz).
+	**----------------------------------------------------------------
+	*/
+	u_int		pciclock_min;
+	u_int		pciclock_max;
 
 	/*----------------------------------------------------------------
 	**	Start queue management.
@@ -1839,7 +2125,8 @@
 	**	SCRIPTS processor in order to start SCSI commands.
 	**----------------------------------------------------------------
 	*/
-	u_int32		*squeue;	/* Start queue			*/
+	u_long		p_squeue;	/* Start queue BUS address	*/
+	u_int32		*squeue;	/* Start queue virtual address	*/
 	u_short		squeueput;	/* Next free slot of the queue	*/
 	u_short		actccbs;	/* Number of allocated CCBs	*/
 	u_short		queuedepth;	/* Start queue depth		*/
@@ -1866,9 +2153,6 @@
 	*/
 	struct ncr_reg	regdump;	/* Register dump		*/
 	u_long		regtime;	/* Time it has been done	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	struct profile	profile;	/* Profiling data		*/
-#endif
 
 	/*----------------------------------------------------------------
 	**	Miscellaneous buffers accessed by the scripts-processor.
@@ -1876,8 +2160,8 @@
 	**	written with a script command.
 	**----------------------------------------------------------------
 	*/
-	u_char		msgout[8];	/* Buffer for MESSAGE OUT 	*/
-	u_char		msgin [8];	/* Buffer for MESSAGE IN	*/
+	u_char		msgout[12];	/* Buffer for MESSAGE OUT 	*/
+	u_char		msgin [12];	/* Buffer for MESSAGE IN	*/
 	u_int32		lastmsg;	/* Last SCSI message sent	*/
 	u_char		scratch;	/* Scratch for SCSI receive	*/
 
@@ -1889,6 +2173,7 @@
 	u_char		order;		/* Tag order to use		*/
 	u_char		verbose;	/* Verbosity for this controller*/
 	u_int32		ncr_cache;	/* Used for cache test at init.	*/
+	u_long		p_ncb;		/* BUS address of this NCB	*/
 
 	/*----------------------------------------------------------------
 	**	CCB lists and queue.
@@ -1899,6 +2184,24 @@
 	XPT_QUEHEAD	free_ccbq;	/* Queue of available CCBs	*/
 
 	/*----------------------------------------------------------------
+	**	IMMEDIATE ARBITRATION (IARB) control.
+	**	We keep track in 'last_cp' of the last CCB that has been 
+	**	queued to the SCRIPTS processor and clear 'last_cp' when 
+	**	this CCB completes. If last_cp is not zero at the moment 
+	**	we queue a new CCB, we set a flag in 'last_cp' that is 
+	**	used by the SCRIPTS as a hint for setting IARB.
+	**	We donnot set more than 'iarb_max' consecutive hints for 
+	**	IARB in order to leave devices a chance to reselect.
+	**	By the way, any non zero value of 'iarb_max' is unfair. :)
+	**----------------------------------------------------------------
+	*/
+#ifdef SCSI_NCR_IARB_SUPPORT
+	struct ccb	*last_cp;	/* Last queud CCB used for IARB	*/
+	u_short		iarb_max;	/* Max. # consecutive IARB hints*/
+	u_short		iarb_count;	/* Actual # of these hints	*/
+#endif
+
+	/*----------------------------------------------------------------
 	**	We need the LCB in order to handle disconnections and 
 	**	to count active CCBs for task management. So, we use 
 	**	a unique CCB for LUNs we donnot have the LCB yet.
@@ -1911,7 +2214,18 @@
 	**	We use a different scatter function for 896 rev 1.
 	**----------------------------------------------------------------
 	*/
-	int (*scatter) (ccb_p, Scsi_Cmnd *);
+	int (*scatter) (ncb_p, ccb_p, Scsi_Cmnd *);
+
+	/*----------------------------------------------------------------
+	**	Command abort handling.
+	**	We need to synchronize tightly with the SCRIPTS 
+	**	processor in order to handle things correctly.
+	**----------------------------------------------------------------
+	*/
+	u_char		abrt_msg[4];	/* Message to send buffer	*/
+	struct scr_tblmove abrt_tbl;	/* Table for the MOV of it 	*/
+	struct scr_tblsel  abrt_sel;	/* Sync params for selection	*/
+	u_char		istat_sem;	/* Tells the chip to stop (SEM)	*/
 
 	/*----------------------------------------------------------------
 	**	Fields that should be removed or changed.
@@ -1919,8 +2233,20 @@
 	*/
 	struct usrcmd	user;		/* Command from user		*/
 	u_char		release_stage;	/* Synchronisation stage on release  */
+
+	/*----------------------------------------------------------------
+	**	Fields that are used (primarily) for integrity check
+	**----------------------------------------------------------------
+	*/
+	unsigned char  check_integrity; /* Enable midlayer integ. check on
+					 * bus scan. */
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+	unsigned char check_integ_par;	/* Set if par or Init. Det. error
+					 * used only during integ check */
+#endif
 };
 
+#define NCB_PHYS(np, lbl)	 (np->p_ncb + offsetof(struct ncb, lbl))
 #define NCB_SCRIPT_PHYS(np,lbl)	 (np->p_script  + offsetof (struct script, lbl))
 #define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl))
 #define NCB_SCRIPTH0_PHYS(np,lbl) (np->p_scripth0+offsetof (struct scripth,lbl))
@@ -1948,87 +2274,120 @@
 
 /*
 **	Script fragments which are loaded into the on-chip RAM 
-**	of 825A, 875, 876, 895 and 896 chips.
+**	of 825A, 875, 876, 895, 895A and 896 chips.
 */
 struct script {
-	ncrcmd	start		[ 10];
+	ncrcmd	start		[ 14];
 	ncrcmd	getjob_begin	[  4];
 	ncrcmd	getjob_end	[  4];
-	ncrcmd	select		[  4];
+	ncrcmd	select		[  8];
 	ncrcmd	wf_sel_done	[  2];
 	ncrcmd	send_ident	[  2];
-	ncrcmd	select2		[  6];
+#ifdef SCSI_NCR_IARB_SUPPORT
+	ncrcmd	select2		[  8];
+#else
+	ncrcmd	select2		[  2];
+#endif
 	ncrcmd  command		[  2];
-	ncrcmd  dispatch	[ 26];
+	ncrcmd  dispatch	[ 28];
 	ncrcmd  sel_no_cmd	[ 10];
 	ncrcmd  init		[  6];
 	ncrcmd  clrack		[  4];
-	ncrcmd  databreak	[  2];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd  dataphase	[  4];
+	ncrcmd  disp_status	[  4];
+	ncrcmd  datai_done	[ 26];
+	ncrcmd  datao_done	[ 12];
+	ncrcmd  ign_i_w_r_msg	[  4];
+	ncrcmd  datai_phase	[  2];
+	ncrcmd  datao_phase	[  4];
+	ncrcmd  msg_in		[  2];
+	ncrcmd  msg_in2		[ 10];
+#ifdef SCSI_NCR_IARB_SUPPORT
+	ncrcmd  status		[ 14];
 #else
-	ncrcmd  dataphase	[  2];
+	ncrcmd  status		[ 10];
 #endif
-	ncrcmd  status		[  8];
-	ncrcmd  msg_in		[  2];
-	ncrcmd  msg_in2		[ 16];
-	ncrcmd  msg_bad		[  6];
 	ncrcmd  complete	[  8];
-	ncrcmd  complete2	[  6];
+#ifdef SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+	ncrcmd  complete2	[ 12];
+#else
+	ncrcmd  complete2	[ 10];
+#endif
+#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR
+	ncrcmd	done		[ 18];
+#else
 	ncrcmd	done		[ 14];
+#endif
 	ncrcmd	done_end	[  2];
 	ncrcmd  save_dp		[  8];
 	ncrcmd  restore_dp	[  4];
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncrcmd  disconnect	[ 32];
-#else
 	ncrcmd  disconnect	[ 20];
-#endif
+#ifdef SCSI_NCR_IARB_SUPPORT
+	ncrcmd  idle		[  4];
+#else
 	ncrcmd  idle		[  2];
+#endif
+#ifdef SCSI_NCR_IARB_SUPPORT
+	ncrcmd  ungetjob	[  6];
+#else
 	ncrcmd  ungetjob	[  4];
+#endif
 	ncrcmd	reselect	[  4];
-	ncrcmd	reselected	[ 44];
-	ncrcmd	resel_tag	[  6];
+	ncrcmd	reselected	[ 20];
+	ncrcmd	resel_scntl4	[ 30];
+#if   MAX_TASKS*4 > 512
+	ncrcmd	resel_tag	[ 18];
+#elif MAX_TASKS*4 > 256
+	ncrcmd	resel_tag	[ 12];
+#else
+	ncrcmd	resel_tag	[  8];
+#endif
 	ncrcmd	resel_go	[  6];
-	ncrcmd	resel_notag	[  4];
+	ncrcmd	resel_notag	[  2];
 	ncrcmd	resel_dsa	[  8];
-	ncrcmd  data_in		[MAX_SCATTERL * SCR_SG_SIZE];
+	ncrcmd  data_in		[MAX_SCATTER * SCR_SG_SIZE];
 	ncrcmd  data_in2	[  4];
-	ncrcmd  data_out	[MAX_SCATTERL * SCR_SG_SIZE];
+	ncrcmd  data_out	[MAX_SCATTER * SCR_SG_SIZE];
 	ncrcmd  data_out2	[  4];
-	ncrcmd  pm0_data	[ 16];
-	ncrcmd  pm1_data	[ 16];
+	ncrcmd  pm0_data	[ 12];
+	ncrcmd  pm0_data_out	[  6];
+	ncrcmd  pm0_data_end	[  6];
+	ncrcmd  pm1_data	[ 12];
+	ncrcmd  pm1_data_out	[  6];
+	ncrcmd  pm1_data_end	[  6];
 };
 
 /*
 **	Script fragments which stay in main memory for all chips 
-**	except for the 896 that support 8K on-chip RAM.
+**	except for the 895A and 896 that support 8K on-chip RAM.
 */
 struct scripth {
 	ncrcmd	start64		[  2];
-	ncrcmd	select_no_atn	[  4];
+	ncrcmd	no_data		[  2];
+	ncrcmd	sel_for_abort	[ 18];
+	ncrcmd	sel_for_abort_1	[  2];
+	ncrcmd	select_no_atn	[  8];
 	ncrcmd	wf_sel_done_no_atn [ 4];
-	ncrcmd	cancel		[  4];
-	ncrcmd	msg_reject	[  8];
-	ncrcmd	msg_ign_residue	[ 24];
-	ncrcmd  msg_extended	[ 10];
-	ncrcmd  msg_ext_2	[ 10];
-	ncrcmd	msg_wdtr	[ 14];
+
+	ncrcmd	msg_in_etc	[ 14];
+	ncrcmd	msg_received	[  4];
+	ncrcmd	msg_weird_seen	[  4];
+	ncrcmd	msg_extended	[ 20];
+	ncrcmd  msg_bad		[  6];
+	ncrcmd	msg_weird	[  4];
+	ncrcmd	msg_weird1	[  8];
+
+	ncrcmd	wdtr_resp	[  6];
 	ncrcmd	send_wdtr	[  4];
-	ncrcmd  msg_ext_3	[ 10];
-	ncrcmd	msg_sdtr	[ 14];
+	ncrcmd	sdtr_resp	[  6];
 	ncrcmd	send_sdtr	[  4];
+	ncrcmd	ppr_resp	[  6];
+	ncrcmd	send_ppr	[  4];
 	ncrcmd	nego_bad_phase	[  4];
-	ncrcmd	msg_out_abort	[ 12];
-	ncrcmd	msg_out		[  6];
+	ncrcmd	msg_out		[  4];
 	ncrcmd	msg_out_done	[  4];
-	ncrcmd	no_data		[ 16];
-#if	MAX_SCATTERH != 0
-	ncrcmd  hdata_in	[MAX_SCATTERH * SCR_SG_SIZE];
-	ncrcmd  hdata_in2	[  2];
-	ncrcmd  hdata_out	[MAX_SCATTERH * SCR_SG_SIZE];
-	ncrcmd  hdata_out2	[  2];
-#endif
+	ncrcmd	data_ovrun	[  2];
+	ncrcmd	data_ovrun1	[ 22];
+	ncrcmd	data_ovrun2	[  8];
 	ncrcmd	abort_resel	[ 16];
 	ncrcmd	resend_ident	[  4];
 	ncrcmd	ident_break	[  4];
@@ -2036,22 +2395,34 @@
 	ncrcmd	sdata_in	[  6];
 	ncrcmd  data_io		[  2];
 	ncrcmd  data_io_com	[  8];
-	ncrcmd  data_io_out	[ 10];
-	ncrcmd	bad_identify	[ 12];
+	ncrcmd  data_io_out	[ 12];
+	ncrcmd	resel_bad_lun	[  4];
 	ncrcmd	bad_i_t_l	[  4];
 	ncrcmd	bad_i_t_l_q	[  4];
-	ncrcmd	bad_status	[ 10];
+	ncrcmd	bad_status	[  6];
 	ncrcmd	tweak_pmj	[ 12];
 	ncrcmd	pm_handle	[ 20];
 	ncrcmd	pm_handle1	[  4];
 	ncrcmd	pm_save		[  4];
-	ncrcmd	pm0_save	[ 10];
-	ncrcmd	pm1_save	[ 10];
+	ncrcmd	pm0_save	[ 14];
+	ncrcmd	pm1_save	[ 14];
+
+	/* WSR handling */
+#ifdef SYM_DEBUG_PM_WITH_WSR
+	ncrcmd  pm_wsr_handle	[ 44];
+#else
+	ncrcmd  pm_wsr_handle	[ 42];
+#endif
+	ncrcmd  wsr_ma_helper	[  4];
 
 	/* Data area */
+	ncrcmd	zero		[  1];
+	ncrcmd	scratch		[  1];
+	ncrcmd	scratch1	[  1];
 	ncrcmd	pm0_data_addr	[  1];
 	ncrcmd	pm1_data_addr	[  1];
 	ncrcmd	saved_dsa	[  1];
+	ncrcmd	saved_drs	[  1];
 	ncrcmd	done_pos	[  1];
 	ncrcmd	startpos	[  1];
 	ncrcmd	targtbl		[  1];
@@ -2088,6 +2459,7 @@
 static	lcb_p	ncr_setup_lcb	(ncb_p np, u_char tn, u_char ln,
 				 u_char *inq_data);
 static	void	ncr_getclock	(ncb_p np, int mult);
+static	u_int	ncr_getpciclock (ncb_p np);
 static	void	ncr_selectclock	(ncb_p np, u_char scntl3);
 static	ccb_p	ncr_get_ccb	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_init	(ncb_p np, int reset, char * msg, u_long code);
@@ -2097,30 +2469,36 @@
 static	void	ncr_int_sir	(ncb_p np);
 static  void    ncr_int_sto     (ncb_p np);
 static  void    ncr_int_udc     (ncb_p np);
-static	u_long	ncr_lookup	(char* id);
-static	void	ncr_negotiate	(struct ncb* np, struct tcb* tp);
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-static	void	ncb_profile	(ncb_p np, ccb_p cp);
+static	void	ncr_negotiate	(ncb_p np, tcb_p tp);
+static	int	ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr);
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+static	int	ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr);
 #endif
 static	void	ncr_script_copy_and_bind
 				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
 static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
-static	int	ncr_scatter_896R1 (ccb_p cp, Scsi_Cmnd *cmd);
-static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
+static	int	ncr_scatter_896R1 (ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
+static	int	ncr_scatter	(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
 static	void	ncr_getsync	(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
-static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
+static  void    ncr_get_xfer_info(ncb_p np, tcb_p tp, u_char *factor, u_char *offset, u_char *width);
+static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer, u_char scntl4);
+static void 	ncr_set_sync_wide_status (ncb_p np, u_char target);
 static	void	ncr_setup_tags	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide, u_char ack);
+static	void	ncr_setsyncwide	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer, u_char scntl4, u_char wide);
 static	int	ncr_show_msg	(u_char * msg);
+static	void	ncr_print_msg	(ccb_p cp, char *label, u_char * msg);
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
 static  int     ncr_wakeup_done (ncb_p np);
 static	void	ncr_start_next_ccb (ncb_p np, lcb_p lp, int maxn);
 static	void	ncr_put_start_queue(ncb_p np, ccb_p cp);
+static	void	ncr_chip_reset	(ncb_p np);
 static	void	ncr_soft_reset	(ncb_p np);
 static	void	ncr_start_reset	(ncb_p np);
 static	int	ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
+static	int	ncr_compute_residual (ncb_p np, ccb_p cp);
 
 #ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 static	void	ncr_usercmd	(ncb_p np);
@@ -2139,9 +2517,10 @@
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
-static  void	ncr_get_nvram		(ncr_device *devp, ncr_nvram *nvp);
-static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
-static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
+static  void	ncr_get_nvram	       (ncr_device *devp, ncr_nvram *nvp);
+static  int	sym_read_Tekram_nvram  (ncr_slot *np, u_short device_id,
+				        Tekram_nvram *nvram);
+static  int	sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
 #endif
 
 /*==========================================================
@@ -2221,6 +2600,24 @@
 	*/
 	SCR_FROM_REG (ctest2),
 		0,
+
+	/*
+	**	Stop here if the C code wants to perform 
+	**	some error recovery procedure manually.
+	**	(Indicate this by setting SEM in ISTAT)
+	*/
+	SCR_FROM_REG (istat),
+		0,
+	/*
+	**	Report to the C code the next position in 
+	**	the start queue the SCRIPTS will schedule.
+	**	The C code must not change SCRATCHA.
+	*/
+	SCR_LOAD_ABS (scratcha, 4),
+		PADDRH (startpos),
+	SCR_INT ^ IFTRUE (MASK (SEM, SEM)),
+		SIR_SCRIPT_STOPPED,
+
 	/*
 	**	Start the next job.
 	**
@@ -2235,8 +2632,6 @@
 	**	may happen that the job address is not yet in the DSA 
 	**	and the the next queue position points to the next JOB.
 	*/
-	SCR_LOAD_ABS (scratcha, 4),
-		PADDRH (startpos),
 	SCR_LOAD_ABS (dsa, 4),
 		PADDRH (startpos),
 	SCR_LOAD_REL (temp, 4),
@@ -2300,6 +2695,19 @@
 	**	So we have to wait immediately for the next phase 
 	**	or the selection to complete or time-out.
 	*/
+
+	/*
+	**      load the savep (saved pointer) into
+	**      the actual data pointer.
+	*/
+	SCR_LOAD_REL (temp, 4),
+		offsetof (struct ccb, phys.header.savep),
+	/*
+	**      Initialize the status registers
+	*/
+	SCR_LOAD_REL (scr0, 4),
+		offsetof (struct ccb, phys.header.status),
+
 }/*-------------------------< WF_SEL_DONE >----------------------*/,{
 	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		SIR_SEL_ATN_NO_MSG_OUT,
@@ -2312,17 +2720,18 @@
 	SCR_MOVE_TBL ^ SCR_MSG_OUT,
 		offsetof (struct dsb, smsg),
 }/*-------------------------< SELECT2 >----------------------*/,{
+#ifdef SCSI_NCR_IARB_SUPPORT
 	/*
-	**      load the savep (saved pointer) into
-	**      the actual data pointer.
-	*/
-	SCR_LOAD_REL (temp, 4),
-		offsetof (struct ccb, phys.header.savep),
-	/*
-	**      Initialize the status registers
+	**	Set IMMEDIATE ARBITRATION if we have been given 
+	**	a hint to do so. (Some job to do after this one).
 	*/
-	SCR_LOAD_REL (scr0, 4),
-		offsetof (struct ccb, phys.header.status),
+	SCR_FROM_REG (HF_REG),
+		0,
+	SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)),
+		8,
+	SCR_REG_REG (scntl1, SCR_OR, IARB),
+		0,
+#endif
 	/*
 	**	Anticipate the COMMAND phase.
 	**	This is the PHASE we expect at this point.
@@ -2346,9 +2755,9 @@
 	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
 		PADDR (msg_in),
 	SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT)),
-		PADDR (dataphase),
+		PADDR (datao_phase),
 	SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN)),
-		PADDR (dataphase),
+		PADDR (datai_phase),
 	SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
 		PADDR (status),
 	SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
@@ -2356,23 +2765,25 @@
 	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
 		PADDRH (msg_out),
 	/*
-	**      Discard one illegal phase byte, if required.
-	*/
-	SCR_LOAD_REG (scratcha, XE_BAD_PHASE),
-		0,
-	SCR_STORE_REL (scratcha, 1),
-		offsetof (struct ccb, xerr_status),
-	SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)),
-		8,
+	 *  Discard as many illegal phases as 
+	 *  required and tell the C code about.
+	 */
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_OUT)),
+		16,
 	SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
 		NADDR (scratch),
-	SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)),
-		8,
+	SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_OUT)),
+		-16,
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_IN)),
+		16,
 	SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
 		NADDR (scratch),
+	SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_IN)),
+		-16,
+	SCR_INT,
+		SIR_BAD_PHASE,
 	SCR_JUMP,
 		PADDR (dispatch),
-
 }/*---------------------< SEL_NO_CMD >----------------------*/,{
 	/*
 	**	The target does not switch to command 
@@ -2422,37 +2833,137 @@
 	SCR_JUMP,
 		PADDR (dispatch),
 
-}/*-------------------------< DATABREAK >-------------------*/,{
+}/*-------------------------< DISP_STATUS >----------------------*/,{
 	/*
-	**	Jump to dispatcher.
+	**	Anticipate STATUS phase.
+	**
+	**	Does spare 3 SCRIPTS instructions when we have 
+	**	completed the INPUT of the data.
 	*/
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+		PADDR (status),
 	SCR_JUMP,
 		PADDR (dispatch),
 
-}/*-------------------------< DATAPHASE >------------------*/,{
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST),
+}/*-------------------------< DATAI_DONE >-------------------*/,{
+	/*
+	 *  If the device wants us to send more data,
+	 *  we must count the extra bytes.
+	 */
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_IN)),
+		PADDRH (data_ovrun),
+	/*
+	**	If the SWIDE is not full, jump to dispatcher.
+	**	We anticipate a STATUS phase.
+	**	If we get later an IGNORE WIDE RESIDUE, we 
+	**	will alias it as a MODIFY DP (-1).
+	*/
+	SCR_FROM_REG (scntl2),
+		0,
+	SCR_JUMP ^ IFFALSE (MASK (WSR, WSR)),
+		PADDR (disp_status),
+	/*
+	**	The SWIDE is full.
+	**	Clear this condition.
+	*/
+	SCR_REG_REG (scntl2, SCR_OR, WSR),
+		0,
+	/*
+         *	We are expecting an IGNORE RESIDUE message
+         *	from the device, otherwise we are in data
+         *	overrun condition. Check against MSG_IN phase.
+	*/
+	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		SIR_SWIDE_OVERRUN,	
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (disp_status),
+	/*
+	 *	We are in MSG_IN phase,
+	 *	Read the first byte of the message.
+	 *	If it is not an IGNORE RESIDUE message,
+	 *	signal overrun and jump to message
+	 *	processing.
+	 */
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin[0]),
+	SCR_INT ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+		SIR_SWIDE_OVERRUN,	
+	SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+		PADDR (msg_in2),
+
+	/*
+	 *	We got the message we expected.
+	 *	Read the 2nd byte, and jump to dispatcher.
+	 */
+	SCR_CLR (SCR_ACK),
 		0,
-#endif
-	SCR_RETURN,
- 		0,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin[1]),
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP,
+		PADDR (disp_status),
 
-}/*-------------------------< STATUS >--------------------*/,{
+}/*-------------------------< DATAO_DONE >-------------------*/,{
 	/*
-	**	get the status
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_STATUS,
-		NADDR (scratch),
+	 *  If the device wants us to send more data,
+	 *  we must count the extra bytes.
+	 */
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+		PADDRH (data_ovrun),
 	/*
-	**	save status to scsi_status.
-	**	mark as complete.
+	**	If the SODL is not full jump to dispatcher.
+	**	We anticipate a MSG IN phase or a STATUS phase.
 	*/
-	SCR_TO_REG (SS_REG),
+	SCR_FROM_REG (scntl2),
 		0,
-	SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+	SCR_JUMP ^ IFFALSE (MASK (WSS, WSS)),
+		PADDR (disp_status),
+	/*
+	**	The SODL is full, clear this condition.
+	*/
+	SCR_REG_REG (scntl2, SCR_OR, WSS),
 		0,
+	/*
+	**	And signal a DATA UNDERRUN condition 
+	**	to the C code.
+	*/
+	SCR_INT,
+		SIR_SODL_UNDERRUN,
 	SCR_JUMP,
 		PADDR (dispatch),
+
+}/*-------------------------< IGN_I_W_R_MSG >--------------*/,{
+	/*
+	**	We jump here from the phase mismatch interrupt, 
+	**	When we have a SWIDE and the device has presented 
+	**	a IGNORE WIDE RESIDUE message on the BUS.
+	**	We just have to throw away this message and then 
+	**	to jump to dispatcher.
+	*/
+	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+		NADDR (scratch),
+	/*
+	**	Clear ACK and jump to dispatcher.
+	*/
+	SCR_JUMP,
+		PADDR (clrack),
+
+}/*-------------------------< DATAI_PHASE >------------------*/,{
+	SCR_RETURN,
+		0,
+}/*-------------------------< DATAO_PHASE >------------------*/,{
+	/*
+	**	Patch for 53c1010_66 only - to allow A0 part
+	**	to operate properly in a 33MHz PCI bus.
+	**
+	** 	SCR_REG_REG(scntl4, SCR_OR, 0x0c),
+	**		0,
+	*/
+	SCR_NO_OP,
+		0,
+	SCR_RETURN,
+		0,
 }/*-------------------------< MSG_IN >--------------------*/,{
 	/*
 	**	Get the first byte of the message.
@@ -2464,7 +2975,8 @@
 		NADDR (msgin[0]),
 }/*-------------------------< MSG_IN2 >--------------------*/,{
 	/*
-	**	Handle this message.
+	**	Check first against 1 byte messages 
+	**	that we handle from SCRIPTS.
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
 		PADDR (complete),
@@ -2474,31 +2986,47 @@
 		PADDR (save_dp),
 	SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
 		PADDR (restore_dp),
-	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
-		PADDRH (msg_extended),
-	SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
-		PADDR (clrack),
-	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDRH (msg_reject),
-	SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
-		PADDRH (msg_ign_residue),
 	/*
-	**	Rest of the messages left as
-	**	an exercise ...
-	**
-	**	Unimplemented messages:
-	**	fall through to MSG_BAD.
+	**	We handle all other messages from the 
+	**	C code, so no need to waste on-chip RAM 
+	**	for those ones.
 	*/
-}/*-------------------------< MSG_BAD >------------------*/,{
+	SCR_JUMP,
+		PADDRH (msg_in_etc),
+
+}/*-------------------------< STATUS >--------------------*/,{
 	/*
-	**	unimplemented message - reject it.
+	**	get the status
 	*/
-	SCR_INT,
-		SIR_REJECT_TO_SEND,
-	SCR_SET (SCR_ATN),
+	SCR_MOVE_ABS (1) ^ SCR_STATUS,
+		NADDR (scratch),
+#ifdef SCSI_NCR_IARB_SUPPORT
+	/*
+	**	If STATUS is not GOOD, clear IMMEDIATE ARBITRATION, 
+	**	since we may have to tamper the start queue from 
+	**	the C code.
+	*/
+	SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)),
+		8,
+	SCR_REG_REG (scntl1, SCR_AND, ~IARB),
+		0,
+#endif
+	/*
+	**	save status to scsi_status.
+	**	mark as complete.
+	*/
+	SCR_TO_REG (SS_REG),
+		0,
+	SCR_LOAD_REG (HS_REG, HS_COMPLETE),
 		0,
+	/*
+	**	Anticipate the MESSAGE PHASE for 
+	**	the TASK COMPLETE message.
+	*/
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+		PADDR (msg_in),
 	SCR_JUMP,
-		PADDR (clrack),
+		PADDR (dispatch),
 
 }/*-------------------------< COMPLETE >-----------------*/,{
 	/*
@@ -2535,6 +3063,18 @@
 	SCR_STORE_REL (scr0, 4),
 		offsetof (struct ccb, phys.header.status),
 
+#ifdef SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+	/*
+	**	Some bridges may reorder DMA writes to memory.
+	**	We donnot want the CPU to deal with completions  
+	**	without all the posted write having been flushed 
+	**	to memory. This DUMMY READ should flush posted 
+	**	buffers prior to the CPU having to deal with 
+	**	completions.
+	*/
+	SCR_LOAD_REL (scr0, 4),	/* DUMMY READ */
+		offsetof (struct ccb, phys.header.status),
+#endif
 	/*
 	**	If command resulted in not GOOD status,
 	**	call the C code if needed.
@@ -2544,7 +3084,35 @@
 	SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
 		PADDRH (bad_status),
 
+	/*
+	**	If we performed an auto-sense, call 
+	**	the C code to synchronyze task aborts 
+	**	with UNIT ATTENTION conditions.
+	*/
+	SCR_FROM_REG (HF_REG),
+		0,
+	SCR_INT ^ IFTRUE (MASK (HF_AUTO_SENSE, HF_AUTO_SENSE)),
+		SIR_AUTO_SENSE_DONE,
+
 }/*------------------------< DONE >-----------------*/,{
+#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR
+	/*
+	**	It seems that some bridges flush everything 
+	**	when the INTR line is raised. For these ones, 
+	**	we can just ensure that the INTR line will be 
+	**	raised before each completion. So, if it happens 
+	**	that we have been faster that the CPU, we just 
+	**	have to synchronize with it. A dummy programmed 
+	**	interrupt will do the trick.
+	**	Note that we overlap at most 1 IO with the CPU 
+	**	in this situation and that the IRQ line must not 
+	**	be shared.
+	*/
+	SCR_FROM_REG (istat),
+		0,
+	SCR_INT ^ IFTRUE (MASK (INTF, INTF)),
+		SIR_DUMMY_INTERRUPT,
+#endif
 	/*
 	**	Copy the DSA to the DONE QUEUE and 
 	**	signal completion to the host.
@@ -2626,25 +3194,6 @@
 	*/
 	SCR_WAIT_DISC,
 		0,
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	/*
-	**	Count the disconnects.
-	**	Disconnect without DATA PHASE having been 
-	**	entered are counted in bits 8..15.
-	*/
-	SCR_LOAD_REL (scratcha, 4),
-		offsetof (struct ccb, phys.num_disc),
-	SCR_FROM_REG (HF_REG),
-		0,
-	SCR_JUMPR ^ IFTRUE (MASK (HF_DATA_ST, HF_DATA_ST)),
-		8,
-	SCR_REG_REG (scratcha1, SCR_ADD, 0x01),
-		0,
-	SCR_REG_REG (scratcha, SCR_ADD, 0x01),
-		0,
-	SCR_STORE_REL (scratcha, 4),
-		offsetof (struct ccb, phys.num_disc),
-#endif
 	/*
 	**	Status is: DISCONNECTED.
 	*/
@@ -2684,7 +3233,20 @@
 	*/
 	SCR_NO_OP,
 		0,
+#ifdef SCSI_NCR_IARB_SUPPORT
+	SCR_JUMPR,
+		8,
+#endif
 }/*-------------------------< UNGETJOB >-----------------*/,{
+#ifdef SCSI_NCR_IARB_SUPPORT
+	/*
+	**	Set IMMEDIATE ARBITRATION, for the next time.
+	**	This will give us better chance to win arbitration 
+	**	for the job we just wanted to do.
+	*/
+	SCR_REG_REG (scntl1, SCR_OR, IARB),
+		0,
+#endif
 	/*
 	**	We are not able to restart the SCRIPTS if we are 
 	**	interrupted and these instruction haven't been 
@@ -2743,32 +3305,44 @@
 		offsetof(struct tcb, wval),
 	SCR_LOAD_REL (sxfer, 1),
 		offsetof(struct tcb, sval),
+}/*-------------------------< RESEL_SCNTL4 >------------------*/,{
 	/*
-	**	If MESSAGE IN  phase as expected,
-	**	read the data directly from the BUS DATA lines.
-	**	This helps to support very old SCSI devices that 
-	**	may reselect without sending an IDENTIFY.
+	**	Write with uval value. Patch if device
+	**	does not support Ultra3.
+	**	
+	**	SCR_LOAD_REL (scntl4, 1),
+	**		offsetof(struct tcb, uval),
 	*/
+
+	SCR_NO_OP,
+		0,
+        /*
+         *  We expect MESSAGE IN phase.
+         *  If not, get help from the C code.
+         */
 	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
 		SIR_RESEL_NO_MSG_IN,
-	SCR_FROM_REG (sbdl),
-		0,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
+
 	/*
-	**	If message phase but not an IDENTIFY,
-	**	get some help from the C code.
-	**	Old SCSI device may behave so.
-	*/
+	 *  If IDENTIFY LUN #0, use a faster path 
+	 *  to find the LCB structure.
+	 */
+	SCR_JUMPR ^ IFTRUE (MASK (0x80, 0xbf)),
+		56,
+	/*
+	 *  If message isn't an IDENTIFY, 
+	 *  tell the C code about.
+	 */
 	SCR_INT ^ IFFALSE (MASK (0x80, 0x80)),
 		SIR_RESEL_NO_IDENTIFY,
 	/*
-	**	It is an IDENTIFY message,
-	**	Load the LUN control block address.
-	**	Avoid nasty address calculation if LUN #0.
-	*/
+	 *  It is an IDENTIFY message,
+	 *  Load the LUN control block address.
+	 */
 	SCR_LOAD_REL (dsa, 4),
 		offsetof(struct tcb, b_luntbl),
-	SCR_JUMPR ^ IFTRUE (MASK (0x0, 0x3f)),
-		24,
 	SCR_SFBR_REG (dsa, SCR_SHL, 0),
 		0,
 	SCR_REG_REG (dsa, SCR_SHL, 0),
@@ -2777,6 +3351,14 @@
 		0,
 	SCR_LOAD_REL (dsa, 4),
 		0,
+	SCR_JUMPR,
+		8,
+	/*
+	**	LUN 0 special case (but usual one :))
+	*/
+	SCR_LOAD_REL (dsa, 4),
+		offsetof(struct tcb, b_lun0),
+
 	/*
 	**	Load the reselect task action for this LUN.
 	**	Load the tasks DSA array for this LUN.
@@ -2788,15 +3370,20 @@
 		offsetof(struct lcb, b_tasktbl),
 	SCR_RETURN,
 		0,
-
 }/*-------------------------< RESEL_TAG >-------------------*/,{
 	/*
+	**	ACK the IDENTIFY or TAG previously received
+	*/
+
+	SCR_CLR (SCR_ACK),
+		0,
+	/*
 	**	Read IDENTIFY + SIMPLE + TAG using a single MOVE.
 	**	Agressive optimization, is'nt it?
 	**	No need to test the SIMPLE TAG message, since the 
 	**	driver only supports conformant devices for tags. ;-)
 	*/
-	SCR_MOVE_ABS (3) ^ SCR_MSG_IN,
+	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
 		NADDR (msgin),
 	/*
 	**	Read the TAG from the SIDL.
@@ -2807,6 +3394,23 @@
 	*/
 	SCR_REG_SFBR (sidl, SCR_SHL, 0),
 		0,
+#if MAX_TASKS*4 > 512
+	SCR_JUMPR ^ IFFALSE (CARRYSET),
+		8,
+	SCR_REG_REG (dsa1, SCR_OR, 2),
+		0,
+	SCR_REG_REG (sfbr, SCR_SHL, 0),
+		0,
+	SCR_JUMPR ^ IFFALSE (CARRYSET),
+		8,
+	SCR_REG_REG (dsa1, SCR_OR, 1),
+		0,
+#elif MAX_TASKS*4 > 256
+	SCR_JUMPR ^ IFFALSE (CARRYSET),
+		8,
+	SCR_REG_REG (dsa1, SCR_OR, 1),
+		0,
+#endif
 	/*
 	**	Retrieve the DSA of this task.
 	**	JUMP indirectly to the restart point of the CCB.
@@ -2820,14 +3424,9 @@
 		offsetof(struct ccb, phys.header.go.restart),
 	SCR_RETURN,
 		0,
+	/* In normal situations we branch to RESEL_DSA */
 }/*-------------------------< RESEL_NOTAG >-------------------*/,{
 	/*
-	**	No tag expected.
-	**	Read an throw away the IDENTIFY.
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	/*
 	**	JUMP indirectly to the restart point of the CCB.
 	*/
 	SCR_JUMP,
@@ -2859,13 +3458,11 @@
 }/*-------------------------< DATA_IN >--------------------*/,{
 /*
 **	Because the size depends on the
-**	#define MAX_SCATTERL parameter,
+**	#define MAX_SCATTER parameter,
 **	it is filled in at runtime.
 **
-**  ##===========< i=0; i<MAX_SCATTERL >=========
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||		PADDR (databreak),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
+**  ##===========< i=0; i<MAX_SCATTER >=========
+**  ||	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 **  ||		offsetof (struct dsb, data[ i]),
 **  ##==========================================
 **
@@ -2874,19 +3471,17 @@
 0
 }/*-------------------------< DATA_IN2 >-------------------*/,{
 	SCR_CALL,
-		PADDR (databreak),
+		PADDR (datai_done),
 	SCR_JUMP,
-		PADDRH (no_data),
+		PADDRH (data_ovrun),
 }/*-------------------------< DATA_OUT >--------------------*/,{
 /*
 **	Because the size depends on the
-**	#define MAX_SCATTERL parameter,
+**	#define MAX_SCATTER parameter,
 **	it is filled in at runtime.
 **
-**  ##===========< i=0; i<MAX_SCATTERL >=========
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
-**  ||		PADDR (databreak),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
+**  ##===========< i=0; i<MAX_SCATTER >=========
+**  ||	SCR_CHMOV_TBL ^ SCR_DATA_OUT,
 **  ||		offsetof (struct dsb, data[ i]),
 **  ##==========================================
 **
@@ -2895,32 +3490,63 @@
 0
 }/*-------------------------< DATA_OUT2 >-------------------*/,{
 	SCR_CALL,
-		PADDR (databreak),
+		PADDR (datao_done),
 	SCR_JUMP,
-		PADDRH (no_data),
+		PADDRH (data_ovrun),
 
 }/*-------------------------< PM0_DATA >--------------------*/,{
 	/*
-	**	Keep track we are executing the PM0 DATA 
-	**	mini-script.
+	**	Read our host flags to SFBR, so we will be able 
+	**	to check against the data direction we expect.
+	*/
+	SCR_FROM_REG (HF_REG),
+		0,
+	/*
+	**	Check against actual DATA PHASE.
+	*/
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDR (pm0_data_out),
+	/*
+	**	Actual phase is DATA IN.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (data_ovrun),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM0 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
 		0,
 	/*
-	**	MOVE the data according to the actual 
-	**	DATA direction.
+	**	Move the data to memory.
 	*/
-	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
-		16,
-	SCR_MOVE_TBL ^ SCR_DATA_IN,
+	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 		offsetof (struct ccb, phys.pm0.sg),
-	SCR_JUMPR,
-		8,
-	SCR_MOVE_TBL ^ SCR_DATA_OUT,
+	SCR_JUMP,
+		PADDR (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >----------------*/,{
+	/*
+	**	Actual phase is DATA OUT.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (data_ovrun),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM0 DATA mini-script.
+	*/
+	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+		0,
+	/*
+	**	Move the data from memory.
+	*/
+	SCR_CHMOV_TBL ^ SCR_DATA_OUT,
 		offsetof (struct ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >----------------*/,{
 	/*
-	**	Clear the flag that told we were in 
-	**	the PM0 DATA mini-script.
+	**	Clear the flag that told we were moving  
+	**	data from the PM0 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
 		0,
@@ -2935,26 +3561,57 @@
 		0,
 }/*-------------------------< PM1_DATA >--------------------*/,{
 	/*
-	**	Keep track we are executing the PM1 DATA 
-	**	mini-script.
+	**	Read our host flags to SFBR, so we will be able 
+	**	to check against the data direction we expect.
+	*/
+	SCR_FROM_REG (HF_REG),
+		0,
+	/*
+	**	Check against actual DATA PHASE.
+	*/
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDR (pm1_data_out),
+	/*
+	**	Actual phase is DATA IN.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (data_ovrun),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM1 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
 		0,
 	/*
-	**	MOVE the data according to the actual 
-	**	DATA direction.
+	**	Move the data to memory.
 	*/
-	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
-		16,
-	SCR_MOVE_TBL ^ SCR_DATA_IN,
+	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 		offsetof (struct ccb, phys.pm1.sg),
-	SCR_JUMPR,
-		8,
-	SCR_MOVE_TBL ^ SCR_DATA_OUT,
+	SCR_JUMP,
+		PADDR (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >----------------*/,{
+	/*
+	**	Actual phase is DATA OUT.
+	**	Check against expected direction.
+	*/
+	SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+		PADDRH (data_ovrun),
+	/*
+	**	Keep track we are moving data from the 
+	**	PM1 DATA mini-script.
+	*/
+	SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+		0,
+	/*
+	**	Move the data from memory.
+	*/
+	SCR_CHMOV_TBL ^ SCR_DATA_OUT,
 		offsetof (struct ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >----------------*/,{
 	/*
-	**	Clear the flag that told we were in 
-	**	the PM1 DATA mini-script.
+	**	Clear the flag that told we were moving  
+	**	data from the PM1 DATA mini-script.
 	*/
 	SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
 		0,
@@ -2970,15 +3627,76 @@
 }/*---------------------------------------------------------*/
 };
 
+
 static	struct scripth scripth0 __initdata = {
 /*------------------------< START64 >-----------------------*/{
 	/*
-	**	SCRIPT entry point for the 896.
+	**	SCRIPT entry point for the 895A and the 896.
 	**	For now, there is no specific stuff for that 
 	**	chip at this point, but this may come.
 	*/
 	SCR_JUMP,
 		PADDR (init),
+}/*-------------------------< NO_DATA >-------------------*/,{
+	SCR_JUMP,
+		PADDRH (data_ovrun),
+}/*-----------------------< SEL_FOR_ABORT >------------------*/,{
+	/*
+	**	We are jumped here by the C code, if we have 
+	**	some target to reset or some disconnected 
+	**	job to abort. Since error recovery is a serious 
+	**	busyness, we will really reset the SCSI BUS, if 
+	**	case of a SCSI interrupt occuring in this path.
+	*/
+
+	/*
+	**	Set initiator mode.
+	*/
+	SCR_CLR (SCR_TRG),
+		0,
+	/*
+	**      And try to select this target.
+	*/
+	SCR_SEL_TBL_ATN ^ offsetof (struct ncb, abrt_sel),
+		PADDR (reselect),
+
+	/*
+	**	Wait for the selection to complete or 
+	**	the selection to time out.
+	*/
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		-8,
+	/*
+	**	Call the C code.
+	*/
+	SCR_INT,
+		SIR_TARGET_SELECTED,
+	/*
+	**	The C code should let us continue here. 
+	**	Send the 'kiss of death' message.
+	**	We expect an immediate disconnect once 
+	**	the target has eaten the message.
+	*/
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+		0,
+	SCR_MOVE_TBL ^ SCR_MSG_OUT,
+		offsetof (struct ncb, abrt_tbl),
+	SCR_CLR (SCR_ACK|SCR_ATN),
+		0,
+	SCR_WAIT_DISC,
+		0,
+	/*
+	**	Tell the C code that we are done.
+	*/
+	SCR_INT,
+		SIR_ABORT_SENT,
+}/*-----------------------< SEL_FOR_ABORT_1 >--------------*/,{
+	/*
+	**	Jump at scheduler.
+	*/
+	SCR_JUMP,
+		PADDR (start),
+
 }/*------------------------< SELECT_NO_ATN >-----------------*/,{
 	/*
 	**	Set Initiator mode.
@@ -2989,6 +3707,18 @@
 		0,
 	SCR_SEL_TBL ^ offsetof (struct dsb, select),
 		PADDR (ungetjob),
+	/*
+	**      load the savep (saved pointer) into
+	**      the actual data pointer.
+	*/
+	SCR_LOAD_REL (temp, 4),
+		offsetof (struct ccb, phys.header.savep),
+	/*
+	**      Initialize the status registers
+	*/
+	SCR_LOAD_REL (scr0, 4),
+		offsetof (struct ccb, phys.header.status),
+
 }/*------------------------< WF_SEL_DONE_NO_ATN >-----------------*/,{
 	/*
 	**	Wait immediately for the next phase or 
@@ -2999,129 +3729,111 @@
 	SCR_JUMP,
 		PADDR (select2),
 
-}/*-------------------------< CANCEL >------------------------*/,{
+}/*-------------------------< MSG_IN_ETC >--------------------*/,{
 	/*
-	**	Load the host status.
+	**	If it is an EXTENDED (variable size message)
+	**	Handle it.
 	*/
-	SCR_LOAD_REG (HS_REG, HS_ABORTED),
-		0,
-	SCR_JUMP,
-		PADDR (complete2),
-
-}/*-------------------------< MSG_REJECT >---------------*/,{
+	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+		PADDRH (msg_extended),
 	/*
-	**	If a negotiation was in progress,
-	**	negotiation failed.
-	**	Otherwise just make host log this message
+	**	Let the C code handle any other 
+	**	1 byte message.
 	*/
-	SCR_FROM_REG (HS_REG),
+	SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)),
+		PADDRH (msg_received),
+	SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)),
+		PADDRH (msg_received),
+	/*
+	**	We donnot handle 2 bytes messages from SCRIPTS.
+	**	So, let the C code deal with these ones too.
+	*/
+	SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)),
+		PADDRH (msg_weird_seen),
+	SCR_CLR (SCR_ACK),
 		0,
-	SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
-		SIR_REJECT_RECEIVED,
-	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
-		SIR_NEGO_FAILED,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin[1]),
 	SCR_JUMP,
-		PADDR (clrack),
+		PADDRH (msg_received),
+
+}/*-------------------------< MSG_RECEIVED >--------------------*/,{
+	SCR_LOAD_REL (scratcha, 4),	/* DUMMY READ */
+		0,
+	SCR_INT,
+		SIR_MSG_RECEIVED,
+
+}/*-------------------------< MSG_WEIRD_SEEN >------------------*/,{
+	SCR_LOAD_REL (scratcha1, 4),	/* DUMMY READ */
+		0,
+	SCR_INT,
+		SIR_MSG_WEIRD,
 
-}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+}/*-------------------------< MSG_EXTENDED >--------------------*/,{
 	/*
-	**	Terminate cycle
+	**	Clear ACK and get the next byte 
+	**	assumed to be the message length.
 	*/
 	SCR_CLR (SCR_ACK),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get residue size.
-	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[1]),
 	/*
-	**	Size is 0 .. ignore message.
+	**	Try to catch some unlikely situations as 0 length 
+	**	or too large the length.
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (0)),
-		PADDR (clrack),
-	/*
-	**	Size is not 1 .. have to interrupt.
-	*/
-	SCR_JUMPR ^ IFFALSE (DATA (1)),
-		40,
-	/*
-	**	Check for residue byte in swide register
-	*/
-	SCR_FROM_REG (scntl2),
+		PADDRH (msg_weird_seen),
+	SCR_TO_REG (scratcha),
 		0,
-	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
-		16,
+	SCR_REG_REG (sfbr, SCR_ADD, (256-8)),
+		0,
+	SCR_JUMP ^ IFTRUE (CARRYSET),
+		PADDRH (msg_weird_seen),
 	/*
-	**	There IS data in the swide register.
-	**	Discard it.
+	**	We donnot handle extended messages from SCRIPTS.
+	**	Read the amount of data correponding to the 
+	**	message length and call the C code.
 	*/
-	SCR_REG_REG (scntl2, SCR_OR, WSR),
+	SCR_STORE_REL (scratcha, 1),
+		offsetof (struct dsb, smsg_ext.size),
+	SCR_CLR (SCR_ACK),
 		0,
+	SCR_MOVE_TBL ^ SCR_MSG_IN,
+		offsetof (struct dsb, smsg_ext),
 	SCR_JUMP,
-		PADDR (clrack),
+		PADDRH (msg_received),
+
+}/*-------------------------< MSG_BAD >------------------*/,{
 	/*
-	**	Load again the size to the sfbr register.
+	**	unimplemented message - reject it.
 	*/
-	SCR_FROM_REG (scratcha),
-		0,
 	SCR_INT,
-		SIR_IGN_RESIDUE,
+		SIR_REJECT_TO_SEND,
+	SCR_SET (SCR_ATN),
+		0,
 	SCR_JUMP,
 		PADDR (clrack),
 
-}/*-------------------------< MSG_EXTENDED >-------------*/,{
+}/*-------------------------< MSG_WEIRD >--------------------*/,{
 	/*
-	**	Terminate cycle
+	**	weird message received
+	**	ignore all MSG IN phases and reject it.
 	*/
-	SCR_CLR (SCR_ACK),
+	SCR_INT,
+		SIR_REJECT_TO_SEND,
+	SCR_SET (SCR_ATN),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get length.
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[1]),
-	/*
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (3)),
-		PADDRH (msg_ext_3),
-	SCR_JUMP ^ IFFALSE (DATA (2)),
-		PADDR (msg_bad),
-}/*-------------------------< MSG_EXT_2 >----------------*/,{
+}/*-------------------------< MSG_WEIRD1 >--------------------*/,{
 	SCR_CLR (SCR_ACK),
 		0,
 	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
 		PADDR (dispatch),
-	/*
-	**	get extended message code.
-	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[2]),
-	SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
-		PADDRH (msg_wdtr),
-	/*
-	**	unknown extended message
-	*/
+		NADDR (scratch),
 	SCR_JUMP,
-		PADDR (msg_bad)
-}/*-------------------------< MSG_WDTR >-----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get data bus width
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[3]),
-	/*
-	**	let the host do the real work.
-	*/
-	SCR_INT,
-		SIR_NEGO_WIDE,
+		PADDRH (msg_weird1),
+}/*-------------------------< WDTR_RESP >----------------*/,{
 	/*
 	**	let the target fetch our answer.
 	*/
@@ -3141,39 +3853,27 @@
 	SCR_JUMP,
 		PADDRH (msg_out_done),
 
-}/*-------------------------< MSG_EXT_3 >----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get extended message code.
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[2]),
-	SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
-		PADDRH (msg_sdtr),
+}/*-------------------------< SDTR_RESP >-------------*/,{
 	/*
-	**	unknown extended message
+	**	let the target fetch our answer.
 	*/
-	SCR_JUMP,
-		PADDR (msg_bad)
-
-}/*-------------------------< MSG_SDTR >-----------------*/,{
+	SCR_SET (SCR_ATN),
+		0,
 	SCR_CLR (SCR_ACK),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get period and offset
-	*/
-	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
-		NADDR (msgin[3]),
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		PADDRH (nego_bad_phase),
+
+}/*-------------------------< SEND_SDTR >-------------*/,{
 	/*
-	**	let the host do the real work.
+	**	Send the M_X_SYNC_REQ
 	*/
-	SCR_INT,
-		SIR_NEGO_SYNC,
+	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+		NADDR (msgout),
+	SCR_JUMP,
+		PADDRH (msg_out_done),
+
+}/*-------------------------< PPR_RESP >-------------*/,{
 	/*
 	**	let the target fetch our answer.
 	*/
@@ -3184,11 +3884,11 @@
 	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		PADDRH (nego_bad_phase),
 
-}/*-------------------------< SEND_SDTR >-------------*/,{
+}/*-------------------------< SEND_PPR >-------------*/,{
 	/*
-	**	Send the M_X_SYNC_REQ
+	**	Send the M_X_PPR_REQ
 	*/
-	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+	SCR_MOVE_ABS (8) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_JUMP,
 		PADDRH (msg_out_done),
@@ -3199,28 +3899,6 @@
 	SCR_JUMP,
 		PADDR (dispatch),
 
-}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
-	/*
-	**	After ABORT message,
-	**
-	**	expect an immediate disconnect, ...
-	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	SCR_WAIT_DISC,
-		0,
-	SCR_INT,
-		SIR_MSG_OUT_DONE,
-	/*
-	**	... and set the status to "ABORTED"
-	*/
-	SCR_LOAD_REG (HS_REG, HS_ABORTED),
-		0,
-	SCR_JUMP,
-		PADDR (complete2),
-
 }/*-------------------------< MSG_OUT >-------------------*/,{
 	/*
 	**	The target requests a message.
@@ -3228,11 +3906,6 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	/*
-	**	If it was no ABORT message ...
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
-		PADDRH (msg_out_abort),
-	/*
 	**	... wait for the next phase
 	**	if it's a message out, send it again, ...
 	*/
@@ -3250,78 +3923,67 @@
 	SCR_JUMP,
 		PADDR (dispatch),
 
-}/*-------------------------< NO_DATA >--------------------*/,{
+}/*-------------------------< DATA_OVRUN >-----------------------*/,{
 	/*
-	**	The target wants to tranfer too much data
-	**	or in the wrong direction.
-	**      Remember that in extended error.
-	*/
-	SCR_LOAD_REG (scratcha, XE_EXTRA_DATA),
-		0,
-	SCR_STORE_REL (scratcha, 1),
-		offsetof (struct ccb, xerr_status),
+	 *  Use scratcha to count the extra bytes.
+	 */
+	SCR_LOAD_ABS (scratcha, 4),
+		PADDRH (zero),
+}/*-------------------------< DATA_OVRUN1 >----------------------*/,{
 	/*
-	**      Discard one data byte, if required.
-	*/
+	 *  The target may want to transfer too much data.
+	 *
+	 *  If phase is DATA OUT write 1 byte and count it.
+	 */
 	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
-		8,
-	SCR_MOVE_ABS (1) ^ SCR_DATA_OUT,
-		NADDR (scratch),
-	SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
-		8,
-	SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+		16,
+	SCR_CHMOV_ABS (1) ^ SCR_DATA_OUT,
 		NADDR (scratch),
+	SCR_JUMP,
+		PADDRH (data_ovrun2),
 	/*
-	**      .. and repeat as required.
-	*/
-	SCR_CALL,
-		PADDR (databreak),
+	 *  If WSR is set, clear this condition, and 
+	 *  count this byte.
+	 */
+	SCR_FROM_REG (scntl2),
+		0,
+	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+		16,
+	SCR_REG_REG (scntl2, SCR_OR, WSR),
+		0,
 	SCR_JUMP,
-		PADDRH (no_data),
-
-#if	MAX_SCATTERH != 0
-
-}/*-------------------------< HDATA_IN >-------------------*/,{
-/*
-**	Because the size depends on the
-**	#define MAX_SCATTERH parameter,
-**	it is filled in at runtime.
-**
-**  ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||		PADDR (databreak),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
-**  ||		offsetof (struct dsb, data[ i]),
-**  ##===================================================
-**
-**---------------------------------------------------------
-*/
-0
-}/*-------------------------< HDATA_IN2 >------------------*/,{
+		PADDRH (data_ovrun2),
+	/*
+	 *  Finally check against DATA IN phase.
+	 *  Signal data overrun to the C code 
+	 *  and jump to dispatcher if not so.
+	 *  Read 1 byte otherwise and count it.
+	 */
+	SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_IN)),
+		16,
+	SCR_INT,
+		SIR_DATA_OVERRUN,
 	SCR_JUMP,
-		PADDR (data_in),
-
-}/*-------------------------< HDATA_OUT >-------------------*/,{
-/*
-**	Because the size depends on the
-**	#define MAX_SCATTERH parameter,
-**	it is filled in at runtime.
-**
-**  ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
-**  ||		PADDR (databreak),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
-**  ||		offsetof (struct dsb, data[ i]),
-**  ##===================================================
-**
-**---------------------------------------------------------
-*/
-0
-}/*-------------------------< HDATA_OUT2 >------------------*/,{
+		PADDR (dispatch),
+	SCR_CHMOV_ABS (1) ^ SCR_DATA_IN,
+		NADDR (scratch),
+}/*-------------------------< DATA_OVRUN2 >----------------------*/,{
+	/*
+	 *  Count this byte.
+	 *  This will allow to return a negative 
+	 *  residual to user.
+	 */
+	SCR_REG_REG (scratcha,  SCR_ADD,  0x01),
+		0,
+	SCR_REG_REG (scratcha1, SCR_ADDC, 0),
+		0,
+	SCR_REG_REG (scratcha2, SCR_ADDC, 0),
+		0,
+	/*
+	 *  .. and repeat as required.
+	 */
 	SCR_JUMP,
-		PADDR (data_out),
-
-#endif	/* MAX_SCATTERH */
+		PADDRH (data_ovrun1),
 
 }/*-------------------------< ABORT_RESEL >----------------*/,{
 	SCR_SET (SCR_ATN),
@@ -3366,13 +4028,12 @@
 	SCR_JUMP,
 		PADDR (select2),
 }/*-------------------------< SDATA_IN >-------------------*/,{
-	SCR_MOVE_TBL ^ SCR_DATA_IN,
+	SCR_CHMOV_TBL ^ SCR_DATA_IN,
 		offsetof (struct dsb, sense),
 	SCR_CALL,
-		PADDR (databreak),
+		PADDR (datai_done),
 	SCR_JUMP,
-		PADDRH (no_data),
-
+		PADDRH (data_ovrun),
 }/*-------------------------< DATA_IO >--------------------*/,{
 	/*
 	**	We jump here if the data direction was unknown at the 
@@ -3409,6 +4070,8 @@
 	/*
 	**	Direction is DATA OUT.
 	*/
+	SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+		0,
 	SCR_LOAD_REL  (scratcha, 4),
 		offsetof (struct ccb, phys.header.wlastp),
 	SCR_STORE_REL (scratcha, 4),
@@ -3420,29 +4083,14 @@
 	SCR_JUMP,
 		PADDRH(data_io_com),
 
-}/*-------------------------< BAD_IDENTIFY >---------------*/,{
-	/*
-	**	If message phase but not an IDENTIFY,
-	**	get some help from the C code.
-	**	Old SCSI device may behave so.
-	*/
-	SCR_JUMPR ^ IFTRUE (MASK (0x80, 0x80)),
-		16,
-	SCR_INT,
-		SIR_RESEL_NO_IDENTIFY,
-	SCR_JUMP,
-		PADDRH (abort_resel),
+}/*-------------------------< RESEL_BAD_LUN >---------------*/,{
 	/*
 	**	Message is an IDENTIFY, but lun is unknown.
-	**	Read the message, since we got it directly 
-	**	from the SCSI BUS data lines.
 	**	Signal problem to C code for logging the event.
 	**	Send a M_ABORT to clear all pending tasks.
 	*/
 	SCR_INT,
 		SIR_RESEL_BAD_LUN,
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
 	SCR_JUMP,
 		PADDRH (abort_resel),
 }/*-------------------------< BAD_I_T_L >------------------*/,{
@@ -3467,17 +4115,14 @@
 		PADDRH (abort_resel),
 }/*-------------------------< BAD_STATUS >-----------------*/,{
 	/*
-	**	If command resulted in either QUEUE FULL,
-	**	CHECK CONDITION or COMMAND TERMINATED,
-	**	call the C code.
+	**	Anything different from INTERMEDIATE 
+	**	CONDITION MET should be a bad SCSI status, 
+	**	given that GOOD status has already been tested.
+	**	Call the C code.
 	*/
 	SCR_LOAD_ABS (scratcha, 4),
 		PADDRH (startpos),
-	SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
-		SIR_BAD_STATUS,
-	SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)),
-		SIR_BAD_STATUS,
-	SCR_INT ^ IFTRUE (DATA (S_TERMINATED)),
+	SCR_INT ^ IFFALSE (DATA (S_COND_MET)),
 		SIR_BAD_STATUS,
 	SCR_RETURN,
 		0,
@@ -3574,6 +4219,17 @@
 	SCR_JUMP ^ IFTRUE (MASK (HF_ACT_PM, HF_ACT_PM)),
 		PADDRH (pm1_save),
 }/*-------------------------< PM0_SAVE >-------------------*/,{
+	SCR_STORE_REL (ia, 4),
+		offsetof(struct ccb, phys.pm0.ret),
+	/*
+	**	If WSR bit is set, either UA and RBC may 
+	**	have to be changed whatever the device wants 
+	**	to ignore this residue ot not.
+	*/
+	SCR_FROM_REG (scntl2),
+		0,
+	SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+		PADDRH (pm_wsr_handle),
 	/*
 	**	Save the remaining byte count, the updated 
 	**	address and the return address.
@@ -3582,16 +4238,25 @@
 		offsetof(struct ccb, phys.pm0.sg.size),
 	SCR_STORE_REL (ua, 4),
 		offsetof(struct ccb, phys.pm0.sg.addr),
-	SCR_STORE_REL (ia, 4),
-		offsetof(struct ccb, phys.pm0.ret),
 	/*
 	**	Set the current pointer at the PM0 DATA mini-script.
 	*/
 	SCR_LOAD_ABS (temp, 4),
 		PADDRH (pm0_data_addr),
 	SCR_JUMP,
-		PADDR (databreak),
+		PADDR (dispatch),
 }/*-------------------------< PM1_SAVE >-------------------*/,{
+	SCR_STORE_REL (ia, 4),
+		offsetof(struct ccb, phys.pm1.ret),
+	/*
+	**	If WSR bit is set, either UA and RBC may 
+	**	have been changed whatever the device wants 
+	**	to ignore this residue or not.
+	*/
+	SCR_FROM_REG (scntl2),
+		0,
+	SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+		PADDRH (pm_wsr_handle),
 	/*
 	**	Save the remaining byte count, the updated 
 	**	address and the return address.
@@ -3600,21 +4265,136 @@
 		offsetof(struct ccb, phys.pm1.sg.size),
 	SCR_STORE_REL (ua, 4),
 		offsetof(struct ccb, phys.pm1.sg.addr),
-	SCR_STORE_REL (ia, 4),
-		offsetof(struct ccb, phys.pm1.ret),
 	/*
 	**	Set the current pointer at the PM1 DATA mini-script.
 	*/
 	SCR_LOAD_ABS (temp, 4),
 		PADDRH (pm1_data_addr),
 	SCR_JUMP,
-		PADDR (databreak),
+		PADDR (dispatch),
+}/*--------------------------< PM_WSR_HANDLE >-----------------------*/,{
+	/*
+	 *  Phase mismatch handling from SCRIPT with WSR set.
+	 *  Such a condition can occur if the chip wants to 
+	 *  execute a CHMOV(size > 1) when the WSR bit is 
+	 *  set and the target changes PHASE.
+	 */
+#ifdef	SYM_DEBUG_PM_WITH_WSR
+	/*
+	 *  Some debugging may still be needed.:)
+	 */ 
+	SCR_INT,
+		SIR_PM_WITH_WSR,
+#endif
+	/*
+	 *  We must move the residual byte to memory.
+	 *
+	 *  UA contains bit 0..31 of the address to 
+	 *  move the residual byte.
+	 *  Move it to the table indirect.
+	 */
+	SCR_STORE_REL (ua, 4),
+		offsetof (struct ccb, phys.wresid.addr),
+	/*
+	 *  Increment UA (move address to next position).
+	 */
+	SCR_REG_REG (ua, SCR_ADD, 1),
+		0,
+	SCR_REG_REG (ua1, SCR_ADDC, 0),
+		0,
+	SCR_REG_REG (ua2, SCR_ADDC, 0),
+		0,
+	SCR_REG_REG (ua3, SCR_ADDC, 0),
+		0,
+	/*
+	 *  Compute SCRATCHA as:
+	 *  - size to transfer = 1 byte.
+	 *  - bit 24..31 = high address bit [32...39].
+	 */
+	SCR_LOAD_ABS (scratcha, 4),
+		PADDRH (zero),
+	SCR_REG_REG (scratcha, SCR_OR, 1),
+		0,
+	SCR_FROM_REG (rbc3),
+		0,
+	SCR_TO_REG (scratcha3),
+		0,
+	/*
+	 *  Move this value to the table indirect.
+	 */
+	SCR_STORE_REL (scratcha, 4),
+		offsetof (struct ccb, phys.wresid.size),
+	/*
+	 *  Wait for a valid phase.
+	 *  While testing with bogus QUANTUM drives, the C1010 
+	 *  sometimes raised a spurious phase mismatch with 
+	 *  WSR and the CHMOV(1) triggered another PM.
+	 *  Waiting explicitely for the PHASE seemed to avoid 
+	 *  the nested phase mismatch. Btw, this didn't happen 
+	 *  using my IBM drives.
+	 */
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		0,
+	/*
+	 *  Perform the move of the residual byte.
+	 */
+	SCR_CHMOV_TBL ^ SCR_DATA_IN,
+		offsetof (struct ccb, phys.wresid),
+	/*
+	 *  We can now handle the phase mismatch with UA fixed.
+	 *  RBC[0..23]=0 is a special case that does not require 
+	 *  a PM context. The C code also checks against this.
+	 */
+	SCR_FROM_REG (rbc),
+		0,
+	SCR_RETURN ^ IFFALSE (DATA (0)),
+		0,
+	SCR_FROM_REG (rbc1),
+		0,
+	SCR_RETURN ^ IFFALSE (DATA (0)),
+		0,
+	SCR_FROM_REG (rbc2),
+		0,
+	SCR_RETURN ^ IFFALSE (DATA (0)),
+		0,
+	/*
+	 *  RBC[0..23]=0.
+	 *  Not only we donnot need a PM context, but this would 
+	 *  lead to a bogus CHMOV(0). This condition means that 
+	 *  the residual was the last byte to move from this CHMOV.
+	 *  So, we just have to move the current data script pointer 
+	 *  (i.e. TEMP) to the SCRIPTS address following the 
+	 *  interrupted CHMOV and jump to dispatcher.
+	 */
+	SCR_STORE_ABS (ia, 4),
+		PADDRH (scratch),
+	SCR_LOAD_ABS (temp, 4),
+		PADDRH (scratch),
+	SCR_JUMP,
+		PADDR (dispatch),
+}/*--------------------------< WSR_MA_HELPER >-----------------------*/,{
+	/*
+	 *  Helper for the C code when WSR bit is set.
+	 *  Perform the move of the residual byte.
+	 */
+	SCR_CHMOV_TBL ^ SCR_DATA_IN,
+		offsetof (struct ccb, phys.wresid),
+	SCR_JUMP,
+		PADDR (dispatch),
+}/*-------------------------< ZERO >------------------------*/,{
+	SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH >---------------------*/,{
+	SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH1 >--------------------*/,{
+	SCR_DATA_ZERO,
 }/*-------------------------< PM0_DATA_ADDR >---------------*/,{
 	SCR_DATA_ZERO,
 }/*-------------------------< PM1_DATA_ADDR >---------------*/,{
 	SCR_DATA_ZERO,
 }/*-------------------------< SAVED_DSA >-------------------*/,{
 	SCR_DATA_ZERO,
+}/*-------------------------< SAVED_DRS >-------------------*/,{
+	SCR_DATA_ZERO,
 }/*-------------------------< DONE_POS >--------------------*/,{
 	SCR_DATA_ZERO,
 }/*-------------------------< STARTPOS >--------------------*/,{
@@ -3646,7 +4426,7 @@
 
 }/*-------------------------< START_RAM64 >--------------------*/,{
 	/*
-	**	Load the RAM and start for 64 bit PCI (896).
+	**	Load the RAM and start for 64 bit PCI (895A,896).
 	**	Both scripts (script and scripth) are loaded into 
 	**	the RAM which is 8K (4K for 825A/875/895).
 	**	We also need to load some 32-63 bit segments 
@@ -3702,57 +4482,23 @@
 **==========================================================
 */
 
-__initfunc(
-void ncr_script_fill (struct script * scr, struct scripth * scrh)
-)
+void __init ncr_script_fill (struct script * scr, struct scripth * scrh)
 {
 	int	i;
 	ncrcmd	*p;
 
-#if	MAX_SCATTERH != 0
-	p = scrh->hdata_in;
-	for (i=0; i<MAX_SCATTERH; i++) {
-#if	SCR_SG_SIZE == 4
-		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
-		*p++ =PADDR (databreak);
-#endif
-		*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
-		*p++ =offsetof (struct dsb, data[i]);
-	};
-	assert ((u_long)p == (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in));
-#endif
-
 	p = scr->data_in;
-	for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
-#if	SCR_SG_SIZE == 4
-		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
-		*p++ =PADDR (databreak);
-#endif
-		*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+	for (i=0; i<MAX_SCATTER; i++) {
+		*p++ =SCR_CHMOV_TBL ^ SCR_DATA_IN;
 		*p++ =offsetof (struct dsb, data[i]);
 	};
-	assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
-#if	MAX_SCATTERH != 0
-	p = scrh->hdata_out;
-	for (i=0; i<MAX_SCATTERH; i++) {
-#if	SCR_SG_SIZE == 4
-		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
-		*p++ =PADDR (databreak);
-#endif
-		*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
-		*p++ =offsetof (struct dsb, data[i]);
-	};
-	assert ((u_long)p==(u_long)&scrh->hdata_out + sizeof (scrh->hdata_out));
-#endif
+	assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
 	p = scr->data_out;
-	for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
-#if SCR_SG_SIZE == 4
-		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
-		*p++ =PADDR (databreak);
-#endif
-		*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+
+	for (i=0; i<MAX_SCATTER; i++) {
+		*p++ =SCR_CHMOV_TBL ^ SCR_DATA_OUT;
 		*p++ =offsetof (struct dsb, data[i]);
 	};
 
@@ -3768,9 +4514,8 @@
 **==========================================================
 */
 
-__initfunc(
-static void ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,int len)
-)
+static void __init 
+ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,int len)
 {
 	ncrcmd  opcode, new, old, tmp1, tmp2;
 	ncrcmd	*start, *end;
@@ -3860,11 +4605,22 @@
 
 		case 0x0:
 			/*
-			**	MOVE (absolute address)
+			**	MOVE/CHMOV (absolute address)
 			*/
+			if (!(np->features & FE_WIDE))
+				dst[-1] = cpu_to_scr(opcode | OPC_MOVE);
 			relocs = 1;
 			break;
 
+		case 0x1:
+			/*
+			**	MOVE/CHMOV (table indirect)
+			*/
+			if (!(np->features & FE_WIDE))
+				dst[-1] = cpu_to_scr(opcode | OPC_MOVE);
+			relocs = 0;
+			break;
+
 		case 0x8:
 			/*
 			**	JUMP / CALL
@@ -3908,10 +4664,11 @@
 				new = (old & ~RELOC_MASK) + np->p_scripth;
 				break;
 			case RELOC_SOFTC:
-				new = (old & ~RELOC_MASK) + vtobus(np);
+				new = (old & ~RELOC_MASK) + np->p_ncb;
 				break;
 #ifdef	RELOC_KVAR
 			case RELOC_KVAR:
+				new=0;
 				if (((old & ~RELOC_MASK) < SCRIPT_KVAR_FIRST) ||
 				    ((old & ~RELOC_MASK) > SCRIPT_KVAR_LAST))
 					panic("ncr KVAR out of range");
@@ -3926,6 +4683,7 @@
 				}
 				/* fall through */
 			default:
+				new = 0;	/* For 'cc' not to complain */
 				panic("ncr_script_copy_and_bind: "
 				      "weird relocation %x\n", old);
 				break;
@@ -3993,9 +4751,16 @@
 **	Prepare io register values used by ncr_init() according 
 **	to selected and supported features.
 **
-**	NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 
-**	transfers. 32,64,128 are only supported by 825A, 875, 895 
-**	and 896 chips.
+**	NCR/SYMBIOS chips allow burst lengths of 2, 4, 8, 16, 32, 64,
+**	128 transfers. All chips support at least 16 transfers bursts. 
+**	The 825A, 875 and 895 chips support bursts of up to 128 
+**	transfers and the 895A and 896 support bursts of up to 64 
+**	transfers. All other chips support up to 16 transfers bursts.
+**
+**	For PCI 32 bit data transfers each transfer is a DWORD (4 bytes).
+**	It is a QUADWORD (8 bytes) for PCI 64 bit data transfers.
+**	Only the 896 is able to perform 64 bit data transfers.
+**
 **	We use log base 2 (burst length) as internal code, with 
 **	value 0 meaning "burst disabled".
 **
@@ -4038,10 +4803,8 @@
 **	Get target set-up from Symbios format NVRAM.
 */
 
-__initfunc(
-static void
-	ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
-)
+static void __init 
+ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
 {
 	tcb_p tp = &np->target[target];
 	Symbios_target *tn = &nvram->target[target];
@@ -4049,7 +4812,7 @@
 	tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
 	tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
 	tp->usrtags =
-		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? MAX_TAGS : 0;
 
 	if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
 		tp->usrflag |= UF_NODISC;
@@ -4061,10 +4824,8 @@
 **	Get target set-up from Tekram format NVRAM.
 */
 
-__initfunc(
-static void
-	ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
-)
+static void __init
+ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
 {
 	tcb_p tp = &np->target[target];
 	struct Tekram_target *tn = &nvram->target[target];
@@ -4072,7 +4833,7 @@
 
 	if (tn->flags & TEKRAM_SYNC_NEGO) {
 		i = tn->sync_index & 0xf;
-		tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+		tp->usrsync = Tekram_sync[i];
 	}
 
 	tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
@@ -4090,28 +4851,48 @@
 }
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-__initfunc(
-static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
-)
+/*
+**	Save initial settings of some IO registers.
+**	Assumed to have been set by BIOS.
+*/
+static void __init ncr_save_initial_setting(ncb_p np)
 {
-	u_char	burst_max;
-	u_long	period;
-	int i;
-
-	/*
-	**	Save assumed BIOS setting
-	*/
-
 	np->sv_scntl0	= INB(nc_scntl0) & 0x0a;
-	np->sv_scntl3	= INB(nc_scntl3) & 0x07;
 	np->sv_dmode	= INB(nc_dmode)  & 0xce;
 	np->sv_dcntl	= INB(nc_dcntl)  & 0xa8;
 	np->sv_ctest3	= INB(nc_ctest3) & 0x01;
 	np->sv_ctest4	= INB(nc_ctest4) & 0x80;
-	np->sv_ctest5	= INB(nc_ctest5) & 0x24;
 	np->sv_gpcntl	= INB(nc_gpcntl);
 	np->sv_stest2	= INB(nc_stest2) & 0x20;
 	np->sv_stest4	= INB(nc_stest4);
+	np->sv_stest1	= INB(nc_stest1);
+
+ 	np->sv_scntl3   = INB(nc_scntl3) & 0x07;
+ 
+ 	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+ 	 	(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66) ){
+ 		/*
+ 		** C1010 always uses large fifo, bit 5 rsvd
+ 		** scntl4 used ONLY with C1010
+ 		*/
+ 		np->sv_ctest5 = INB(nc_ctest5) & 0x04 ; 
+ 		np->sv_scntl4 = INB(nc_scntl4); 
+         }
+         else {
+ 		np->sv_ctest5 = INB(nc_ctest5) & 0x24 ; 
+ 		np->sv_scntl4 = 0;
+         }
+}
+
+/*
+**	Prepare io register values used by ncr_init() 
+**	according to selected and supported features.
+*/
+static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+{
+	u_char	burst_max;
+	u_long	period;
+	int i;
 
 	/*
 	**	Wide ?
@@ -4139,15 +4920,35 @@
 
 	/*
 	 * Divisor to be used for async (timer pre-scaler).
+	 *
+	 * Note: For C1010 the async divisor is 2(8) if he
+	 * quadrupler is disabled (enabled).
 	 */
-	i = np->clock_divn - 1;
-	while (--i >= 0) {
-		if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
-			++i;
-			break;
+
+	if ( (np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+		(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+
+		np->rv_scntl3 = 0; 
+	}
+	else
+	{
+		i = np->clock_divn - 1;
+		while (--i >= 0) {
+			if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz 
+							> div_10M[i]) {
+				++i;
+				break;
+			}
 		}
+		np->rv_scntl3 = i+1;
 	}
-	np->rv_scntl3 = i+1;
+
+
+	/*
+	 * Save the ultra3 register for the C1010/C1010_66
+	 */
+
+	np->rv_scntl4 = np->sv_scntl4;
 
 	/*
 	 * Minimum synchronous period factor supported by the chip.
@@ -4161,13 +4962,28 @@
 	else				np->minsync = (period + 40 - 1) / 40;
 
 	/*
+	 * Fix up. If sync. factor is 10 (160000Khz clock) and chip
+	 * supports ultra3, then min. sync. period 12.5ns and the factor is 9 
+	 */
+
+	if ((np->minsync == 10) && (np->features & FE_ULTRA3))
+		np->minsync = 9;
+
+	/*
 	 * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+	 *
+	 * Transfer period minimums: SCSI-1 200 (50); Fast 100 (25)
+	 *			Ultra 50 (12); Ultra2 (6); Ultra3 (3)		
 	 */
 
-	if	(np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+	if	(np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2|FE_ULTRA3)))
 		np->minsync = 25;
-	else if	(np->minsync < 12 && !(np->features & FE_ULTRA2))
+	else if	(np->minsync < 12 && (np->features & FE_ULTRA))
 		np->minsync = 12;
+	else if	(np->minsync < 10 && (np->features & FE_ULTRA2))
+		np->minsync = 10;
+	else if	(np->minsync < 9 && (np->features & FE_ULTRA3))
+		np->minsync = 9;
 
 	/*
 	 * Maximum synchronous period factor supported by the chip.
@@ -4177,7 +4993,7 @@
 	np->maxsync = period > 2540 ? 254 : period / 10;
 
 	/*
-	**	64 bit (53C896) ?
+	**	64 bit (53C895A or 53C896) ?
 	*/
 	if (np->features & FE_64BIT)
 #ifdef SCSI_NCR_USE_64BIT_DAC
@@ -4187,8 +5003,8 @@
 #endif
 
 	/*
-	**	Phase mismatch handled by SCRIPTS (53C896) ?
-	*/
+	**	Phase mismatch handled by SCRIPTS (53C895A, 53C896 or C1010) ?
+  	*/
 	if (np->features & FE_NOPM)
 		np->rv_ccntl0	|= (ENPMJ);
 
@@ -4231,6 +5047,14 @@
 		np->features &= ~(FE_WRIE|FE_ERL|FE_ERMP);
 
 	/*
+	**	DEL ? - 53C1010 Rev 1 - Part Number 609-0393638
+	**	64-bit Slave Cycles must be disabled.
+	*/
+	if ( ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) && (np->revision_id < 0x02) )
+		|| (np->device_id == PCI_DEVICE_ID_LSI_53C1010_66 ) )
+		np->rv_ccntl1  |=  0x10;
+
+	/*
 	**	Select all supported special features.
 	**	If we are using on-board RAM for scripts, prefetch (PFEN) 
 	**	does not help, but burst op fetch (BOF) does.
@@ -4242,7 +5066,7 @@
 		np->rv_dmode	|= BOF;		/* Burst Opcode Fetch */
 	if (np->features & FE_ERMP)
 		np->rv_dmode	|= ERMP;	/* Enable Read Multiple */
-#ifdef SCSI_NCR_OPTIMIZE_896
+#if 1
 	if ((np->features & FE_PFEN) && !np->base2_ba)
 #else
 	if (np->features & FE_PFEN)
@@ -4252,8 +5076,13 @@
 		np->rv_dcntl	|= CLSE;	/* Cache Line Size Enable */
 	if (np->features & FE_WRIE)
 		np->rv_ctest3	|= WRIE;	/* Write and Invalidate */
-	if (np->features & FE_DFS)
+
+
+	if ( (np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66) &&
+			(np->features & FE_DFS))
 		np->rv_ctest5	|= DFS;		/* Dma Fifo Size */
+						/* C1010/C1010_66 always large fifo */
 
 	/*
 	**	Select some other
@@ -4299,28 +5128,51 @@
 	ncr_init_burst(np, burst_max);
 
 	/*
-	**	Set differential mode and LED support.
-	**	Ignore these features for boards known to use a 
-	**	specific GPIO wiring (Tekram only for now) and 
-	**	for the 896 that drives the LED directly.
-	**	Probe initial setting of GPREG and GPCNTL for 
-	**	other ones.
-	*/
-	if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+	**	Set SCSI BUS mode.
+	**
+	**	- ULTRA2 chips (895/895A/896) 
+	**	  and ULTRA 3 chips (1010) report the current 
+	**	  BUS mode through the STEST4 IO register.
+	**	- For previous generation chips (825/825A/875), 
+	**	  user has to tell us how to check against HVD, 
+	**	  since a 100% safe algorithm is not possible.
+	*/
+	np->scsi_mode = SMODE_SE;
+	if	(np->features & (FE_ULTRA2 | FE_ULTRA3))
+		np->scsi_mode = (np->sv_stest4 & SMODE);
+	else if	(np->features & FE_DIFF) {
 		switch(driver_setup.diff_support) {
-		case 3:
+		case 4:	/* Trust previous settings if present, then GPIO3 */
+			if (np->sv_scntl3) {
+				if (np->sv_stest2 & 0x20)
+					np->scsi_mode = SMODE_HVD;
+				break;
+			}
+		case 3:	/* SYMBIOS controllers report HVD through GPIO3 */
+			if (nvram && nvram->type != SCSI_NCR_SYMBIOS_NVRAM)
+				break;
 			if (INB(nc_gpreg) & 0x08)
+				break;
+		case 2:	/* Set HVD unconditionally */
+			np->scsi_mode = SMODE_HVD;
+		case 1:	/* Trust previous settings for HVD */
+			if (np->sv_stest2 & 0x20)
+				np->scsi_mode = SMODE_HVD;
 			break;
-		case 2:
-			np->rv_stest2	|= 0x20;
-			break;
-		case 1:
-			np->rv_stest2	|= (np->sv_stest2 & 0x20);
-			break;
-		default:
+		default:/* Don't care about HVD */	
 			break;
 		}
 	}
+	if (np->scsi_mode == SMODE_HVD)
+		np->rv_stest2 |= 0x20;
+
+	/*
+	**	Set LED support from SCRIPTS.
+	**	Ignore this feature for boards known to use a 
+	**	specific GPIO wiring and for the 895A or 896 
+	**	that drive the LED directly.
+	**	Also probe initial setting of GPIO0 as output.
+	*/
 	if ((driver_setup.led_pin ||
 	     (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
 	    !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01))
@@ -4373,7 +5225,7 @@
 #endif
 			tp->usrsync = driver_setup.default_sync;
 			tp->usrwide = driver_setup.max_wide;
-			tp->usrtags = SCSI_NCR_MAX_TAGS;
+			tp->usrtags = MAX_TAGS;
 			if (!driver_setup.disconnection)
 				np->target[i].usrflag = UF_NODISC;
 		}
@@ -4388,7 +5240,8 @@
 		i  == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
 		(i == SCSI_NCR_TEKRAM_NVRAM  ? "Tekram format NVRAM, " : ""),
 		np->myaddr,
-		np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+		np->minsync < 10 ? 80 : 
+			(np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10) ),
 		(np->rv_scntl0 & 0xa)	? ", Parity Checking"	: ", NO Parity",
 		(np->rv_stest2 & 0x20)	? ", Differential"	: "");
 
@@ -4414,9 +5267,7 @@
 
 #ifdef SCSI_NCR_DEBUG_NVRAM
 
-__initfunc(
-void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
-)
+void __init ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
 {
 	int i;
 
@@ -4446,9 +5297,7 @@
 
 static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
 
-__initfunc(
-void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
-)
+void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
 {
 	int i, tags, boot_delay;
 	char *rem;
@@ -4483,7 +5332,7 @@
 		int sync, j;
 		struct Tekram_target *tn = &nvram->target[i];
 		j = tn->sync_index & 0xf;
-		sync = j < 12 ? Tekram_sync[j] : 255;
+		sync = Tekram_sync[j];
 		printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
 		ncr_name(np), i,
 		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
@@ -4507,9 +5356,8 @@
 **	start the timer daemon.
 */
 
-__initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
-)
+static int __init 
+ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
 {
         struct host_data *host_data;
 	ncb_p np = 0;
@@ -4518,16 +5366,21 @@
 	ncr_nvram *nvram = device->nvram;
 	int i;
 
+	printk(KERN_INFO NAME53C "%s-%d: rev 0x%x on pci bus %d device %d function %d "
+#ifdef __sparc__
+		"irq %s\n",
+#else
+		"irq %d\n",
+#endif
+		device->chip.name, unit, device->chip.revision_id,
+		device->slot.bus, (device->slot.device_fn & 0xf8) >> 3,
+		device->slot.device_fn & 7,
 #ifdef __sparc__
-printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=0x%x\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, device->slot.irq);
-#else
-printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, device->slot.irq);
+		__irq_itoa(device->slot.irq));
+#else
+		device->slot.irq);
 #endif
- 
+
 	/*
 	**	Allocate host_data structure
 	*/
@@ -4538,10 +5391,12 @@
 	/*
 	**	Allocate the host control block.
 	*/
-	np = m_calloc(sizeof(struct ncb), "NCB", MEMO_WARN);
+	np = __m_calloc_dma(device->pdev, sizeof(struct ncb), "NCB");
 	if (!np)
 		goto attach_error;
 	NCR_INIT_LOCK_NCB(np);
+	np->pdev  = device->pdev;
+	np->p_ncb = vtobus(np);
 	host_data->ncb = np;
 
 	/*
@@ -4553,8 +5408,8 @@
 	sprintf(np->inst_name, NAME53C "%s-%d", np->chip_name, np->unit);
 	np->device_id	= device->chip.device_id;
 	np->revision_id	= device->chip.revision_id;
-	np->pci_bus	= device->slot.bus;
-	np->pci_devfn	= device->slot.device_fn;
+	np->bus		= device->slot.bus;
+	np->device_fn	= device->slot.device_fn;
 	np->features	= device->chip.features;
 	np->clock_divn	= device->chip.nr_divisor;
 	np->maxoffs	= device->chip.offset_max;
@@ -4565,22 +5420,23 @@
 	**	Allocate the start queue.
 	*/
 	np->squeue = (ncrcmd *)
-		m_calloc(sizeof(ncrcmd)*(MAX_START*2), "SQUEUE", MEMO_WARN);
+		m_calloc_dma(sizeof(ncrcmd)*(MAX_START*2), "SQUEUE");
 	if (!np->squeue)
 		goto attach_error;
+	np->p_squeue = vtobus(np->squeue);
 
 	/*
 	**	Allocate the done queue.
 	*/
 	np->dqueue = (ncrcmd *)
-		m_calloc(sizeof(ncrcmd)*(MAX_START*2), "DQUEUE", MEMO_WARN);
+		m_calloc_dma(sizeof(ncrcmd)*(MAX_START*2), "DQUEUE");
 	if (!np->dqueue)
 		goto attach_error;
 
 	/*
 	**	Allocate the target bus address array.
 	*/
-	np->targtbl = (u_int32 *) m_calloc(256, "TARGTBL", MEMO_WARN);
+	np->targtbl = (u_int32 *) m_calloc_dma(256, "TARGTBL");
 	if (!np->targtbl)
 		goto attach_error;
 
@@ -4588,11 +5444,11 @@
 	**	Allocate SCRIPTS areas
 	*/
 	np->script0	= (struct script *) 
-		m_calloc(sizeof(struct script),  "SCRIPT",  MEMO_WARN);
+		m_calloc_dma(sizeof(struct script),  "SCRIPT");
 	if (!np->script0)
 		goto attach_error;
 	np->scripth0	= (struct scripth *)
-		m_calloc(sizeof(struct scripth), "SCRIPTH", MEMO_WARN);
+		m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
 	if (!np->scripth0)
 		goto attach_error;
 
@@ -4622,7 +5478,7 @@
 	np->base_ws	= (np->features & FE_IO256)? 256 : 128;
 	np->base2_ba	= (np->features & FE_RAM)? device->slot.base_2 : 0;
 
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 	np->base_va = remap_pci_mem(np->base_ba, np->base_ws);
 	if (!np->base_va) {
 		printk(KERN_ERR "%s: can't map PCI MMIO region\n",ncr_name(np));
@@ -4639,7 +5495,15 @@
 
 	np->reg = (struct ncr_reg *) np->base_va;
 
-#endif /* !defined NCR_IOMAPPED */
+#endif /* !defined SCSI_NCR_IOMAPPED */
+
+	/*
+	**	If on-chip RAM is used, make sure SCRIPTS isn't too large.
+	*/
+	if (np->base2_ba && sizeof(struct script) > 4096) {
+		printk(KERN_ERR "%s: script too large.\n", ncr_name(np));
+		goto attach_error;
+	}
 
 	/*
 	**	Try to map the controller chip into iospace.
@@ -4672,14 +5536,69 @@
 	}
 #endif
 
+ 	/*
+	**	Save setting of some IO registers, so we will 
+	**	be able to probe specific implementations.
+	*/
+	ncr_save_initial_setting (np);
+
+	/*
+	**	Reset the chip now, since it has been reported 
+	**	that SCSI clock calibration may not work properly 
+	**	if the chip is currently active.
+	*/
+	ncr_chip_reset (np);
+
 	/*
 	**	Do chip dependent initialization.
 	*/
-	if (np->base2_ba && sizeof(struct script) > 4096) {
-		printk(KERN_ERR "%s: script too large.\n", ncr_name(np));
+	(void) ncr_prepare_setting(np, nvram);
+
+	/*
+	**	Check the PCI clock frequency if needed.
+	**	
+	**	Must be done after ncr_prepare_setting since it destroys 
+	**	STEST1 that is used to probe for the clock multiplier.
+	**
+	**	The range is currently [22688 - 45375 Khz], given 
+	**	the values used by ncr_getclock().
+	**	This calibration of the frequecy measurement 
+	**	algorithm against the PCI clock frequency is only 
+	**	performed if the driver has had to measure the SCSI 
+	**	clock due to other heuristics not having been enough 
+	**	to deduce the SCSI clock frequency.
+	**
+	**	When the chip has been initialized correctly by the 
+	**	SCSI BIOS, the driver deduces the presence of the 
+	**	clock multiplier and the value of the SCSI clock from 
+	**	initial values of IO registers, and therefore no 
+	**	clock measurement is performed.
+	**	Normally the driver should never have to measure any 
+	**	clock, unless the controller may use a 80 MHz clock 
+	**	or has a clock multiplier and any of the following 
+	**	condition is met:
+	**
+	**	- No SCSI BIOS is present.
+	**	- SCSI BIOS did'nt enable the multiplier for some reason.
+	**	- User has disabled the controller from the SCSI BIOS.
+	**	- User booted the O/S from another O/S that did'nt enable 
+	**	  the multiplier for some reason.
+	**
+	**	As a result, the driver may only have to measure some 
+	**	frequency in very unusual situations.
+	**
+	**	For this reality test against the PCI clock to really 
+	**	protect against flaws in the udelay() calibration or 
+	**	driver problem that affect the clock measurement 
+	**	algorithm, the actual PCI clock frequency must be 33 MHz.
+	*/
+	i = np->pciclock_max ? ncr_getpciclock(np) : 0;
+	if (i && (i < np->pciclock_min  || i > np->pciclock_max)) {
+		printk(KERN_ERR "%s: PCI clock (%u KHz) is out of range "
+			"[%u KHz - %u KHz].\n",
+		       ncr_name(np), i, np->pciclock_min, np->pciclock_max);
 		goto attach_error;
 	}
-	(void) ncr_prepare_setting(np, nvram);
 
 	/*
 	**	Patch script to physical addresses
@@ -4722,6 +5641,15 @@
 	np->scripth0->pm1_data_addr[0] = 
 			cpu_to_scr(NCB_SCRIPT_PHYS(np, pm1_data));
 
+	/*
+	**	Patch if not Ultra 3 - Do not write to scntl4
+	*/
+	if (np->features & FE_ULTRA3) {
+		np->script0->resel_scntl4[0] = cpu_to_scr(SCR_LOAD_REL (scntl4, 1));
+		np->script0->resel_scntl4[1] = cpu_to_scr(offsetof(struct tcb, uval));
+	}
+
+
 #ifdef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
 	np->scripth0->script0_ba[0]	= cpu_to_scr(vtobus(np->script0));
 	np->scripth0->script0_ba64[0]	= cpu_to_scr(vtobus(np->script0));
@@ -4733,40 +5661,41 @@
 	*/
 	np->idletask.start	= cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 	np->idletask.restart	= cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l));
-	np->p_idletask		= vtobus(&np->idletask);
+	np->p_idletask		= NCB_PHYS(np, idletask);
 
 	np->notask.start	= cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 	np->notask.restart	= cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l));
-	np->p_notask		= vtobus(&np->notask);
+	np->p_notask		= NCB_PHYS(np, notask);
 
 	np->bad_i_t_l.start	= cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 	np->bad_i_t_l.restart	= cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l));
-	np->p_bad_i_t_l		= vtobus(&np->bad_i_t_l);
+	np->p_bad_i_t_l		= NCB_PHYS(np, bad_i_t_l);
 
 	np->bad_i_t_l_q.start	= cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 	np->bad_i_t_l_q.restart	= cpu_to_scr(NCB_SCRIPTH_PHYS (np,bad_i_t_l_q));
-	np->p_bad_i_t_l_q	= vtobus(&np->bad_i_t_l_q);
+	np->p_bad_i_t_l_q	= NCB_PHYS(np, bad_i_t_l_q);
 
 	/*
 	**	Allocate and prepare the bad lun table.
 	*/
-	np->badluntbl = m_calloc(256, "BADLUNTBL", MEMO_WARN);
+	np->badluntbl = m_calloc_dma(256, "BADLUNTBL");
 	if (!np->badluntbl)
 		goto attach_error;
 
 	assert (offsetof(struct lcb, resel_task) == 0);
-	np->resel_badlun = cpu_to_scr(NCB_SCRIPTH_PHYS(np, bad_identify));
+	np->resel_badlun = cpu_to_scr(NCB_SCRIPTH_PHYS(np, resel_bad_lun));
 
 	for (i = 0 ; i < 64 ; i++)
-		np->badluntbl[i] = cpu_to_scr(vtobus(&np->resel_badlun));
+		np->badluntbl[i] = cpu_to_scr(NCB_PHYS(np, resel_badlun));
 
 	/*
 	**	Prepare the target bus address array.
 	*/
 	np->scripth0->targtbl[0] = cpu_to_scr(vtobus(np->targtbl));
 	for (i = 0 ; i < MAX_TARGET ; i++) {
-		np->targtbl[i] = cpu_to_scr(vtobus(&np->target[i]));
+		np->targtbl[i] = cpu_to_scr(NCB_PHYS(np, target[i]));
 		np->target[i].b_luntbl = cpu_to_scr(vtobus(np->badluntbl));
+		np->target[i].b_lun0   = cpu_to_scr(NCB_PHYS(np, resel_badlun));
 	}
 
 	/*
@@ -4783,20 +5712,43 @@
 	}
 
 	/*
+	**	Patch the script to provide an extra clock cycle on
+	**	data out phase - 53C1010_66MHz part only.
+	*/
+	if (np->device_id == PCI_DEVICE_ID_LSI_53C1010_66){
+		np->script0->datao_phase[0] =
+				cpu_to_scr(SCR_REG_REG(scntl4, SCR_OR, 0x0c));
+	}
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+	/*
+	**    If user does not want to use IMMEDIATE ARBITRATION
+	**    when we are reselected while attempting to arbitrate,
+	**    patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+	*/
+	if (!(driver_setup.iarb & 1))
+		np->script0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+	/*
+	**    If user wants IARB to be set when we win arbitration 
+	**    and have other jobs, compute the max number of consecutive 
+	**    settings of IARB hint before we leave devices a chance to 
+	**    arbitrate for reselection.
+	*/
+	np->iarb_max = (driver_setup.iarb >> 4);
+#endif
+
+	/*
 	**	DEL 472 - 53C896 Rev 1 - Part Number 609-0393055 - ITEM 5.
 	*/
 	if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
 	    np->revision_id <= 0x1 && (np->features & FE_NOPM)) {
 		np->scatter = ncr_scatter_896R1;
-#ifndef SCSI_NCR_PROFILE_SUPPORT
-#define XXX	0
-#else
-#define XXX	3
-#endif
-		np->script0->dataphase[XXX] = cpu_to_scr(SCR_JUMP);
-		np->script0->dataphase[XXX+1] = 
+		np->script0->datai_phase[0] = cpu_to_scr(SCR_JUMP);
+		np->script0->datai_phase[1] = 
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, tweak_pmj));
+		np->script0->datao_phase[0] = cpu_to_scr(SCR_JUMP);
+		np->script0->datao_phase[1] = 
 				cpu_to_scr(NCB_SCRIPTH_PHYS (np, tweak_pmj));
-#undef XXX
 	}
 	else
 #ifdef DEBUG_896R1
@@ -4810,10 +5762,11 @@
 	**	We should use ncr_soft_reset(), but we donnot want to do 
 	**	so, since we may not be safe if ABRT interrupt occurs due 
 	**	to the BIOS or previous O/S having enable this interrupt.
+	**
+	**	For C1010 need to set ABRT bit prior to SRST if SCRIPTs
+	**	are running. Not true in this case.
 	*/
-	OUTB (nc_istat, SRST);
-	UDELAY(10);
-	OUTB (nc_istat, 0);
+	ncr_chip_reset(np);
 
 	/*
 	**	Now check the cache handling of the pci chipset.
@@ -4826,14 +5779,20 @@
 
 	/*
 	**	Install the interrupt handler.
+	**	If we synchonize the C code with SCRIPTS on interrupt, 
+	**	we donnot want to share the INTR line at all.
 	*/
 	if (request_irq(device->slot.irq, sym53c8xx_intr,
+#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR
+			((driver_setup.irqm & 0x20) ? 0 : SA_INTERRUPT),
+#else
 			((driver_setup.irqm & 0x10) ? 0 : SA_SHIRQ) |
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
 			((driver_setup.irqm & 0x20) ? 0 : SA_INTERRUPT),
 #else
 			0,
 #endif
+#endif
 			NAME53C8XX, np)) {
 		printk(KERN_ERR "%s: request irq %d failure\n",
 			ncr_name(np), device->slot.irq);
@@ -4896,17 +5855,37 @@
 	instance->this_id	= np->myaddr;
 	instance->max_id	= np->maxwide ? 16 : 8;
 	instance->max_lun	= MAX_LUN;
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29)
+	instance->base		= (unsigned long) np->reg;
+#else
 	instance->base		= (char *) np->reg;
 #endif
+#endif
 	instance->irq		= np->irq;
 	instance->unique_id	= np->base_io;
 	instance->io_port	= np->base_io;
 	instance->n_io_port	= np->base_ws;
 	instance->dma_channel	= 0;
-	instance->select_queue_depths = sym53c8xx_select_queue_depths;
+	instance->cmd_per_lun	= MAX_TAGS;
+	instance->can_queue	= (MAX_START-4);
 
-	NCR_UNLOCK_NCB(np, flags);
+	np->check_integrity       = 0;
+
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+	instance->check_integrity = 0;
+
+#ifdef SCSI_NCR_ENABLE_INTEGRITY_CHECK
+	if ( !(driver_setup.bus_check & 0x04) ) {
+		np->check_integrity       = 1;
+		instance->check_integrity = 1;
+	}
+#endif
+#endif
+	
+	instance->select_queue_depths = sym53c8xx_select_queue_depths;
+
+	NCR_UNLOCK_NCB(np, flags);
 
 	/*
 	**	Now let the generic SCSI driver
@@ -4946,21 +5925,21 @@
 		unmap_pci_mem(np->base2_va, np->base2_ws);
 #endif
 	if (np->scripth0)
-		m_free(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+		m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
 	if (np->script0)
-		m_free(np->script0, sizeof(struct script), "SCRIPT");
+		m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
 	if (np->squeue)
-		m_free(np->squeue, sizeof(ncrcmd)*(MAX_START*2), "SQUEUE");
+		m_free_dma(np->squeue, sizeof(ncrcmd)*(MAX_START*2), "SQUEUE");
 	if (np->dqueue)
-		m_free(np->dqueue, sizeof(ncrcmd)*(MAX_START*2),"DQUEUE");
+		m_free_dma(np->dqueue, sizeof(ncrcmd)*(MAX_START*2),"DQUEUE");
 
 	while ((cp = np->ccbc) != NULL) {
 		np->ccbc = cp->link_ccb;
-		m_free(cp, sizeof(*cp), "CCB");
+		m_free_dma(cp, sizeof(*cp), "CCB");
 	}
 
 	if (np->badluntbl)
-		m_free(np->badluntbl, 256,"BADLUNTBL");
+		m_free_dma(np->badluntbl, 256,"BADLUNTBL");
 
 	for (target = 0; target < MAX_TARGET ; target++) {
 		tp = &np->target[target];
@@ -4969,16 +5948,23 @@
 			if (!lp)
 				continue;
 			if (lp->tasktbl != &lp->tasktbl_0)
-				m_free(lp->tasktbl, 256, "TASKTBL");
-			m_free(lp, sizeof(*lp), "LCB");
+				m_free_dma(lp->tasktbl, MAX_TASKS*4, "TASKTBL");
+			if (lp->cb_tags)
+				m_free(lp->cb_tags, MAX_TAGS, "CB_TAGS");
+			m_free_dma(lp, sizeof(*lp), "LCB");
 		}
 #if MAX_LUN > 1
 		if (tp->lmp)
 			m_free(tp->lmp, MAX_LUN * sizeof(lcb_p), "LMP");
+		if (tp->luntbl)
+			m_free_dma(tp->luntbl, 256, "LUNTBL");
 #endif 
 	}
 
-	m_free(np, sizeof(*np), "NCB");
+	if (np->targtbl)
+		m_free_dma(np->targtbl, 256, "TARGTBL");
+
+	m_free_dma(np, sizeof(*np), "NCB");
 }
 
 
@@ -5002,6 +5988,7 @@
 */
 static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
 {
+	unmap_scsi_data(np, cmd);
 	cmd->host_scribble = (char *) np->done_list;
 	np->done_list = cmd;
 }
@@ -5017,6 +6004,472 @@
 	}
 }
 
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message for integrity check,
+**	if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**	If tp->ppr_negotiation is 1 and a M_REJECT occurs, then
+**	we disable ppr_negotiation.  If the first ppr_negotiation is
+**	successful, set this flag to 2.
+**
+**==========================================================
+*/
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+static int ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+	u_char new_width, new_offset, new_period;
+	u_char no_increase;
+
+	if (tp->ppr_negotiation == 1)	/* PPR message successful */
+		tp->ppr_negotiation = 2;
+
+	if (tp->inq_done) {
+
+		if (!tp->ic_maximums_set) {
+			tp->ic_maximums_set = 1;
+
+			/* 
+			 * Check against target, host and user limits  
+			 */
+			if ( (tp->inq_byte7 & INQ7_WIDE16) && 
+					np->maxwide  && tp->usrwide) 
+				tp->ic_max_width = 1;
+			else
+				tp->ic_max_width = 0;
+			
+
+			if ((tp->inq_byte7 & INQ7_SYNC) && tp->maxoffs)
+				tp->ic_min_sync = (tp->minsync < np->minsync) ?
+							np->minsync : tp->minsync;
+			else 
+				tp->ic_min_sync = 255;
+			
+			tp->period   = 1;
+			tp->widedone = 1;
+
+			/*
+			 * Enable PPR negotiation - only if Ultra3 support
+			 * is accessible.
+			 */
+
+#if 0
+			if (tp->ic_max_width && (tp->ic_min_sync != 255 ))
+				tp->ppr_negotiation = 1;
+#endif
+			tp->ppr_negotiation = 0;
+			if (np->features & FE_ULTRA3) {
+			    if (tp->ic_max_width && (tp->ic_min_sync == 0x09))
+				tp->ppr_negotiation = 1;
+			}
+
+			if (!tp->ppr_negotiation)
+				cmd->ic_nego &= ~NS_PPR;
+		}
+
+		if (DEBUG_FLAGS & DEBUG_IC) {
+			printk("%s: cmd->ic_nego %d, 1st byte 0x%2X\n",
+				ncr_name(np), cmd->ic_nego, cmd->cmnd[0]);
+		}
+
+		/* Previous command recorded a parity or an initiator
+		 * detected error condition. Force bus to narrow for this
+		 * target. Clear flag. Negotation on request sense.
+		 * Note: kernel forces 2 bus resets :o( but clears itself out. 
+		 * Minor bug? in scsi_obsolete.c (ugly)
+		 */
+		if (np->check_integ_par) { 
+			printk("%s: Parity Error. Target set to narrow.\n",
+				ncr_name(np));
+			tp->ic_max_width = 0;
+			tp->widedone = tp->period = 0;
+		}
+
+		/* Initializing:
+		 * If ic_nego == NS_PPR, we are in the initial test for
+		 * PPR messaging support. If driver flag is clear, then
+		 * either we don't support PPR nego (narrow or async device)
+		 * or this is the second TUR and we have had a M. REJECT 
+		 * or unexpected disconnect on the first PPR negotiation.  
+		 * Do not negotiate, reset nego flags (in case a reset has
+		 * occurred), clear ic_nego and return.
+		 * General case: Kernel will clear flag on a fallback. 
+		 * Do only SDTR or WDTR in the future.
+		 */
+                if (!tp->ppr_negotiation &&  (cmd->ic_nego == NS_PPR )) {
+			tp->ppr_negotiation = 0;
+			cmd->ic_nego &= ~NS_PPR;
+			tp->widedone = tp->period = 1;
+			return msglen;
+		}
+		else if (( tp->ppr_negotiation && !(cmd->ic_nego & NS_PPR )) || 
+                        (!tp->ppr_negotiation &&  (cmd->ic_nego & NS_PPR )) ) {
+			tp->ppr_negotiation = 0;
+			cmd->ic_nego &= ~NS_PPR;
+		}
+
+		/*
+		 * Always check the PPR nego. flag bit if ppr_negotiation
+		 * is set.  If the ic_nego PPR bit is clear,
+		 * there must have been a fallback. Do only
+		 * WDTR / SDTR in the future.
+		 */
+		if ((tp->ppr_negotiation) && (!(cmd->ic_nego & NS_PPR)))
+			tp->ppr_negotiation = 0;
+
+		/* In case of a bus reset, ncr_negotiate will reset 
+                 * the flags tp->widedone and tp->period to 0, forcing
+		 * a new negotiation.  Do WDTR then SDTR. If PPR, do both.
+		 * Do NOT increase the period.  It is possible for the Scsi_Cmnd
+		 * flags to be set to increase the period when a bus reset 
+		 * occurs - we don't want to change anything.
+		 */
+
+		no_increase = 0;
+
+		if (tp->ppr_negotiation && (!tp->widedone) && (!tp->period) ) {
+			cmd->ic_nego = NS_PPR;
+			tp->widedone = tp->period = 1;
+			no_increase = 1;
+		}
+		else if (!tp->widedone) {
+			cmd->ic_nego = NS_WIDE;
+			tp->widedone = 1;
+			no_increase = 1;
+		}
+		else if (!tp->period) {
+			cmd->ic_nego = NS_SYNC;
+			tp->period = 1;
+			no_increase = 1;
+		}
+
+		new_width = cmd->ic_nego_width & tp->ic_max_width;
+
+		switch (cmd->ic_nego_sync) {
+		case 2: /* increase the period */
+			if (!no_increase) {
+			    if (tp->ic_min_sync <= 0x09)      
+				tp->ic_min_sync = 0x0A;
+			    else if (tp->ic_min_sync <= 0x0A) 
+				tp->ic_min_sync = 0x0C;
+			    else if (tp->ic_min_sync <= 0x0C) 
+				tp->ic_min_sync = 0x19;
+			    else if (tp->ic_min_sync <= 0x19) 
+				tp->ic_min_sync *= 2;
+			    else  {
+				tp->ic_min_sync = 255;
+				cmd->ic_nego_sync = 0;
+				tp->maxoffs = 0;
+			    }
+			}
+			new_period  = tp->maxoffs?tp->ic_min_sync:0;
+			new_offset  = tp->maxoffs;
+			break;
+
+		case 1: /* nego. to maximum */
+			new_period  = tp->maxoffs?tp->ic_min_sync:0;
+			new_offset  = tp->maxoffs;
+			break;
+
+		case 0:	/* nego to async */
+		default:
+			new_period = 0;
+			new_offset = 0;
+			break;
+		};
+		
+
+		nego = NS_NOCHANGE;
+		if (tp->ppr_negotiation) { 
+			u_char options_byte = 0;
+
+			/*
+			** Must make sure data is consistent.
+			** If period is 9 and sync, must be wide and DT bit set.
+			** else period must be larger. If the width is 0, 
+			** reset bus to wide but increase the period to 0x0A.
+			** Note: The strange else clause is due to the integrity check.
+			** If fails at 0x09, wide, the I.C. code will redo at the same
+			** speed but a narrow bus. The driver must take care of slowing
+			** the bus speed down.
+			**
+			** The maximum offset in ST mode is 31, in DT mode 62 (1010/1010_66 only)
+			*/
+			if ( (new_period==0x09) && new_offset) {
+				if (new_width) 
+					options_byte = 0x02;
+				else {
+					tp->ic_min_sync = 0x0A;
+					new_period = 0x0A;
+					cmd->ic_nego_width = 1;
+					new_width = 1;
+					new_offset &= 0x1f;
+				}
+			}
+			else if (new_period > 0x09)
+				new_offset &= 0x1f;
+
+			nego = NS_PPR;
+			
+			msgptr[msglen++] = M_EXTENDED;
+			msgptr[msglen++] = 6;
+			msgptr[msglen++] = M_X_PPR_REQ;
+			msgptr[msglen++] = new_period;
+			msgptr[msglen++] = 0;
+			msgptr[msglen++] = new_offset;
+			msgptr[msglen++] = new_width;
+			msgptr[msglen++] = options_byte;
+
+		}
+		else {
+			switch (cmd->ic_nego & ~NS_PPR) {
+			case NS_WIDE:
+			    /*
+			    **	WDTR negotiation on if device supports
+			    **  wide or if wide device forced narrow
+			    **	due to a parity error. 
+			    */
+
+			    cmd->ic_nego_width &= tp->ic_max_width;
+
+			    if (tp->ic_max_width | np->check_integ_par) {
+				nego = NS_WIDE;
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 2;
+				msgptr[msglen++] = M_X_WIDE_REQ;
+				msgptr[msglen++] = new_width;
+			    }
+		 	    break;
+
+			case NS_SYNC:
+			    /*
+			    **	negotiate synchronous transfers
+			    **	Target must support sync transfers.
+			    **  Min. period = 0x0A, maximum offset of 31=0x1f.
+		    	    */
+
+			    if (tp->inq_byte7 & INQ7_SYNC) {
+
+				if (new_offset && (new_period < 0x0A)) {
+					tp->ic_min_sync = 0x0A;
+					new_period = 0x0A;
+				}
+				nego = NS_SYNC;
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 3;
+				msgptr[msglen++] = M_X_SYNC_REQ;
+				msgptr[msglen++] = new_period;
+				msgptr[msglen++] = new_offset & 0x1f;
+			    }
+			    else 
+				cmd->ic_nego_sync = 0;
+			    break;
+
+			case NS_NOCHANGE:
+			    break;
+			}
+		}
+
+	};
+
+	cp->nego_status = nego;
+	np->check_integ_par = 0;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+				  "wide/narrow msgout":
+				(nego == NS_SYNC ? "sync/async msgout" : "ppr msgout"), 
+				msgptr);
+		};
+	};
+
+	return msglen;
+}
+#endif	/* SCSI_NCR_INTEGRITY_CHECKING */
+
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+
+static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+	u_char width, offset, factor, last_byte;
+
+	if (!np->check_integrity) {
+		/* If integrity checking disabled, enable PPR messaging
+		 * if device supports wide, sync and ultra 3
+		 */
+		if (tp->ppr_negotiation == 1) /* PPR message successful */
+			tp->ppr_negotiation = 2;
+
+		if ((tp->inq_done) && (!tp->ic_maximums_set)) {
+			tp->ic_maximums_set = 1;
+
+			/*
+			 * Issue PPR only if board is capable
+			 * and set-up for Ultra3 transfers.
+			 */
+			tp->ppr_negotiation = 0;
+			if ( (np->features & FE_ULTRA3) &&
+				(tp->usrwide) && (tp->maxoffs) &&
+				(tp->minsync == 0x09) )
+					tp->ppr_negotiation = 1;
+		}
+	}
+
+	if (tp->inq_done) {
+		/*
+		 * Get the current width, offset and period
+		 */
+		ncr_get_xfer_info( np, tp, &factor,
+						&offset, &width);
+
+		/*
+		**	negotiate wide transfers ?
+		*/
+
+		if (!tp->widedone) {
+			if (tp->inq_byte7 & INQ7_WIDE16) {
+				if (tp->ppr_negotiation)
+					nego = NS_PPR;
+				else
+					nego = NS_WIDE;
+
+				width = tp->usrwide;
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+				if (tp->ic_done)
+		       			 width &= tp->ic_max_width;
+#endif
+			} else
+				tp->widedone=1;
+
+		};
+
+		/*
+		**	negotiate synchronous transfers?
+		*/
+
+		if ((nego != NS_WIDE) && !tp->period) {
+			if (tp->inq_byte7 & INQ7_SYNC) {
+				if (tp->ppr_negotiation)
+					nego = NS_PPR;
+				else
+					nego = NS_SYNC;
+				
+				/* Check for async flag */
+				if (tp->maxoffs == 0) {
+				    offset = 0;
+				    factor = 0;
+				}
+				else {
+				    offset = tp->maxoffs;
+				    factor = tp->minsync;
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+			 	    if ((tp->ic_done) && 
+						(factor < tp->ic_min_sync))
+		       			 factor = tp->ic_min_sync;
+#endif
+				}
+
+			} else {
+				offset = 0;
+				factor = 0;
+				tp->period  =0xffff;
+				PRINT_TARGET(np, cp->target);
+				printk ("target did not report SYNC.\n");
+			};
+		};
+	};
+
+	switch (nego) {
+	case NS_PPR:
+		/*
+		** Must make sure data is consistent.
+		** If period is 9 and sync, must be wide and DT bit set
+		** else period must be larger. 
+		** Maximum offset is 31=0x1f is ST mode, 62 if DT mode
+		*/
+		last_byte = 0;
+		if ( (factor==9) && offset) {
+			if (!width) {
+				factor = 0x0A;
+				offset &= 0x1f;
+			}
+			else 
+				last_byte = 0x02;
+		}
+		else if (factor > 0x09)
+			offset &= 0x1f;
+
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 6;
+		msgptr[msglen++] = M_X_PPR_REQ;
+		msgptr[msglen++] = factor;
+		msgptr[msglen++] = 0;
+		msgptr[msglen++] = offset;
+		msgptr[msglen++] = width;
+		msgptr[msglen++] = last_byte;
+		break;
+	case NS_SYNC:
+		/*
+		** Never negotiate faster than Ultra 2 (25ns periods)
+		*/
+		if (offset && (factor < 0x0A)) {
+			factor = 0x0A;
+			tp->minsync = 0x0A;
+		}
+
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 3;
+		msgptr[msglen++] = M_X_SYNC_REQ;
+		msgptr[msglen++] = factor;
+		msgptr[msglen++] = offset & 0x1f;
+		break;
+	case NS_WIDE:
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 2;
+		msgptr[msglen++] = M_X_WIDE_REQ;
+		msgptr[msglen++] = width;
+		break;
+	};
+
+	cp->nego_status = nego;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+				  "wide msgout":
+				(nego == NS_SYNC ? "sync msgout" : "ppr msgout"), 
+				msgptr);
+		};
+	};
+
+	return msglen;
+}
 
 /*==========================================================
 **
@@ -5034,9 +6487,8 @@
 	lcb_p lp		      = ncr_lp(np, tp, cmd->lun);
 	ccb_p cp;
 
-	int	segments;
-	u_char	nego, idmsg, *msgptr;
-	u_int  msglen;
+	u_char	idmsg, *msgptr;
+	u_int   msglen;
 	int	direction;
 	u_int32	lastp, goalp;
 
@@ -5081,9 +6533,10 @@
 	**
 	**----------------------------------------------------
 	*/
-	if (np->settle_time && cmd->timeout_per_command >= HZ &&
-		np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
-		np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+	if (np->settle_time && cmd->timeout_per_command >= HZ) {
+		u_long tlimit = ktime_get(cmd->timeout_per_command - HZ);
+		if (ktime_dif(np->settle_time, tlimit) > 0)
+			np->settle_time = tlimit;
 	}
 
         if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
@@ -5105,56 +6558,6 @@
 	}
 #endif
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	cp->phys.num_disc = 0;
-#endif
-
-	/*---------------------------------------------------
-	**
-	**	negotiation required?
-	**
-	**---------------------------------------------------
-	*/
-
-	nego = 0;
-
-	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) {
-
-		/*
-		**	negotiate wide transfers ?
-		*/
-
-		if (!tp->widedone) {
-			if (tp->inq_byte7 & INQ7_WIDE16) {
-				nego = NS_WIDE;
-			} else
-				tp->widedone=1;
-		};
-
-		/*
-		**	negotiate synchronous transfers?
-		*/
-
-		if (!nego && !tp->period) {
-			if (tp->inq_byte7 & INQ7_SYNC) {
-				nego = NS_SYNC;
-			} else {
-				tp->period  =0xffff;
-				PRINT_TARGET(np, cp->target);
-				printk ("target did not report SYNC.\n");
-			};
-		};
-
-		/*
-		**	remember nego is pending for the target.
-		**	Avoid to start a nego for all queued commands 
-		**	when tagged command queuing is enabled.
-		*/
-
-		if (nego)
-			tp->nego_cp = cp;
-	};
-
 	/*----------------------------------------------------
 	**
 	**	Build the identify / tag / sdtr message
@@ -5178,16 +6581,16 @@
 		**	Force ordered tag if necessary to avoid timeouts 
 		**	and to preserve interactivity.
 		*/
-		if (lp && lp->tags_stime + (3*HZ) <= jiffies) {
-			if (lp->tags_smap) {
+		if (lp && ktime_exp(lp->tags_stime)) {
+			lp->tags_si = !(lp->tags_si);
+			if (lp->tags_sum[lp->tags_si]) {
 				order = M_ORDERED_TAG;
-				if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ 
+				if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>0){ 
 					PRINT_ADDR(cmd);
 					printk("ordered tag forced.\n");
 				}
 			}
-			lp->tags_stime = jiffies;
-			lp->tags_smap = lp->tags_umap;
+			lp->tags_stime = ktime_get(3*HZ);
 		}
 
 		if (order == 0) {
@@ -5206,41 +6609,19 @@
 		}
 		msgptr[msglen++] = order;
 		/*
-		**	Actual tags are numbered 1,3,5,..2*MAXTAGS+1,
-		**	since we may have to deal with devices that have 
-		**	problems with #TAG 0 or too great #TAG numbers.
+		**	For less than 128 tags, actual tags are numbered 
+		**	1,3,5,..2*MAXTAGS+1,since we may have to deal 
+		**	with devices that have problems with #TAG 0 or too 
+		**	great #TAG numbers. For more tags (up to 256), 
+		**	we use directly our tag number.
 		*/
+#if MAX_TASKS > (512/4)
+		msgptr[msglen++] = cp->tag;
+#else
 		msgptr[msglen++] = (cp->tag << 1) + 1;
+#endif
 	}
 
-	switch (nego) {
-	case NS_SYNC:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 3;
-		msgptr[msglen++] = M_X_SYNC_REQ;
-		msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
-		msgptr[msglen++] = tp->maxoffs;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-5]);
-			printk (".\n");
-		};
-		break;
-	case NS_WIDE:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 2;
-		msgptr[msglen++] = M_X_WIDE_REQ;
-		msgptr[msglen++] = tp->usrwide;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-4]);
-			printk (".\n");
-		};
-		break;
-	};
-
 	cp->host_flags	= 0;
 
 	/*----------------------------------------------------
@@ -5250,102 +6631,130 @@
 	**----------------------------------------------------
 	*/
 
-	segments = np->scatter (cp, cp->cmd);
-
-	if (segments < 0) {
-		ncr_free_ccb(np, cp);
-		return(DID_ERROR);
+	direction = scsi_data_direction(cmd);
+	if (direction != SCSI_DATA_NONE) {
+		cp->segments = np->scatter (np, cp, cp->cmd);
+		if (cp->segments < 0) {
+			ncr_free_ccb(np, cp);
+			return(DID_ERROR);
+		}
+	}
+	else {
+		cp->data_len = 0;
+		cp->segments = 0;
 	}
 
-	/*----------------------------------------------------
+	/*---------------------------------------------------
 	**
-	**	Determine xfer direction.
+	**	negotiation required?
 	**
-	**----------------------------------------------------
+	**	(nego_status is filled by ncr_prepare_nego())
+	**
+	**---------------------------------------------------
 	*/
-	if (!cp->data_len)
-		direction = 0;
-	else {
-		switch((int) cmd->cmnd[0]) {
-		case 0x08:  /*	READ(6)				08 */
-		case 0x28:  /*	READ(10)			28 */
-		case 0xA8:  /*	READ(12)			A8 */
-			direction = XFER_IN;
-			break;
-		case 0x0A:  /*	WRITE(6)			0A */
-		case 0x2A:  /*	WRITE(10)			2A */
-		case 0xAA:  /*	WRITE(12)			AA */
-			direction = XFER_OUT;
-			break;
-		default:
-			direction = (XFER_IN|XFER_OUT);
-			break;
+
+	cp->nego_status = 0;
+
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+	if ((np->check_integrity && tp->ic_done) || !np->check_integrity) {
+		 if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+			msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
+		 }
+	}
+	else if (np->check_integrity && (cmd->ic_in_progress)) { 
+		msglen += ncr_ic_nego (np, cp, cmd, msgptr + msglen);
+        }
+	else if (np->check_integrity && cmd->ic_complete) {
+		u_long current_period;
+		u_char current_offset, current_width, current_factor;
+
+		ncr_get_xfer_info (np, tp, &current_factor,
+					&current_offset, &current_width);
+
+		tp->ic_max_width = current_width;
+		tp->ic_min_sync  = current_factor;
+
+		if      (current_factor == 9) 	current_period = 125;
+		else if (current_factor == 10) 	current_period = 250;
+		else if (current_factor == 11) 	current_period = 303;
+		else if (current_factor == 12) 	current_period = 500;
+		else  			current_period = current_factor * 40;
+
+		/*
+                 * Negotiation for this target is complete. Update flags.
+                 */
+		tp->period = current_period;
+		tp->widedone = 1;
+		tp->ic_done = 1;
+
+		printk("%s: Integrity Check Complete: \n", ncr_name(np)); 
+
+		printk("%s: %s %s SCSI", ncr_name(np), 
+				current_offset?"SYNC":"ASYNC",
+				tp->ic_max_width?"WIDE":"NARROW");
+		if (current_offset) {
+			u_long mbs = 10000 * (tp->ic_max_width + 1); 
+
+			printk(" %d.%d  MB/s", 
+				(int) (mbs / current_period), (int) (mbs % current_period));
+
+			printk(" (%d ns, %d offset)\n", 
+				  (int) current_period/10, current_offset);
 		}
+		else 
+			printk(" %d MB/s. \n ", (tp->ic_max_width+1)*5);
+        }
+#else
+	if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+		msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
 	}
+#endif	/* SCSI_NCR_INTEGRITY_CHECKING */
+
 
 	/*----------------------------------------------------
 	**
-	**	Set the DATA POINTER.
+	**	Determine xfer direction.
 	**
 	**----------------------------------------------------
 	*/
+	if (!cp->data_len)
+		direction = SCSI_DATA_NONE;
 
 	/*
-	**	Default to no data transfer.
+	**	If data direction is UNKNOWN, speculate DATA_READ 
+	**	but prepare alternate pointers for WRITE in case 
+	**	of our speculation will be just wrong.
+	**	SCRIPTS will swap values if needed.
 	*/
-	lastp = goalp = NCB_SCRIPTH_PHYS (np, no_data);
+	switch(direction) {
+	case SCSI_DATA_UNKNOWN:
+	case SCSI_DATA_WRITE:
+		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
+		lastp = goalp - 8 - (cp->segments * (SCR_SG_SIZE*4));
+		if (direction != SCSI_DATA_UNKNOWN)
+			break;
+		cp->phys.header.wgoalp	= cpu_to_scr(goalp);
+		cp->phys.header.wlastp	= cpu_to_scr(lastp);
+		/* fall through */
+	case SCSI_DATA_READ:
+		cp->host_flags |= HF_DATA_IN;
+		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
+		lastp = goalp - 8 - (cp->segments * (SCR_SG_SIZE*4));
+		break;
+	default:
+	case SCSI_DATA_NONE:
+		lastp = goalp = NCB_SCRIPTH_PHYS (np, no_data);
+		break;
+	}
 
 	/*
-	**	Compute data out pointers, if needed.
-	*/
-	if (direction & XFER_OUT) {
-		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
-#if	MAX_SCATTERH != 0
-		if (segments <= MAX_SCATTERL)
-			lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
-		else {
-			lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
-			lastp -= (segments - MAX_SCATTERL) * (SCR_SG_SIZE*4);
-		}
-#else
-		lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
-#endif
-		/*
-		**	If actual data direction is unknown, save pointers 
-		**	in header. The SCRIPTS will swap them to current 
-		**	if target decision will be data out.
-		*/
-		if (direction & XFER_IN) {
-			cp->phys.header.wgoalp	= cpu_to_scr(goalp);
-			cp->phys.header.wlastp	= cpu_to_scr(lastp);
-		}
-	}
-
-	/*
-	**	Compute data in pointers, if needed.
-	*/
-	if (direction & XFER_IN) {
-		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
-#if	MAX_SCATTERH != 0
-		if (segments <= MAX_SCATTERL)
-			lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
-		else {
-			lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
-			lastp -= (segments - MAX_SCATTERL) * (SCR_SG_SIZE*4);
-		}
-#else
-		lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
-#endif
-	}
-
-	/*
-	**	Set all pointers values needed by SCRIPTS.
-	**	If direction is unknown, start at data_io.
+	**	Set all pointers values needed by SCRIPTS.
+	**	If direction is unknown, start at data_io.
 	*/
 	cp->phys.header.lastp = cpu_to_scr(lastp);
 	cp->phys.header.goalp = cpu_to_scr(goalp);
 
-	if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+	if (direction == SCSI_DATA_UNKNOWN)
 		cp->phys.header.savep = 
 			cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
 	else
@@ -5354,8 +6763,13 @@
 	/*
 	**	Save the initial data pointer in order to be able 
 	**	to redo the command.
+	**	We also have to save the initial lastp, since it 
+	**	will be changed to DATA_IO if we don't know the data 
+	**	direction and the device completes the command with 
+	**	QUEUE FULL status (without entering the data phase).
 	*/
 	cp->startp = cp->phys.header.savep;
+	cp->lastp0 = cp->phys.header.lastp;
 
 	/*----------------------------------------------------
 	**
@@ -5379,27 +6793,35 @@
 	cp->phys.select.sel_id		= cp->target;
 	cp->phys.select.sel_scntl3	= tp->wval;
 	cp->phys.select.sel_sxfer	= tp->sval;
+	cp->phys.select.sel_scntl4	= tp->uval;
 	/*
 	**	message
 	*/
-	cp->phys.smsg.addr		= cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
-	cp->phys.smsg.size		= cpu_to_scr(msglen);
+	cp->phys.smsg.addr	= cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+	cp->phys.smsg.size	= cpu_to_scr(msglen);
 
 	/*
 	**	command
 	*/
-	cp->phys.cmd.addr		= cpu_to_scr(vtobus (&cmd->cmnd[0]));
-	cp->phys.cmd.size		= cpu_to_scr(cmd->cmd_len);
+	memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
+	cp->phys.cmd.addr	= cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
+	cp->phys.cmd.size	= cpu_to_scr(cmd->cmd_len);
 
 	/*
 	**	status
 	*/
-	cp->actualquirks		= tp->quirks;
-	cp->host_status			= nego ? HS_NEGOTIATE : HS_BUSY;
-	cp->scsi_status			= S_ILLEGAL;
+	cp->actualquirks	= tp->quirks;
+	cp->host_status		= cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+	cp->scsi_status		= S_ILLEGAL;
+	cp->xerr_status		= 0;
+	cp->extra_bytes		= 0;
 
-	cp->xerr_status			= XE_OK;
-	cp->nego_status			= nego;
+	/*
+	**	extreme data pointer.
+	**	shall be positive, so -1 is lower than lowest.:)
+	*/
+	cp->ext_sg  = -1;
+	cp->ext_ofs = 0;
 
 	/*----------------------------------------------------
 	**
@@ -5412,17 +6834,10 @@
 	**	activate this job.
 	*/
 
-	/* Compute a time limit greater than the middle-level driver one */
-	if (cmd->timeout_per_command > 0)
-		cp->tlimit	= jiffies + cmd->timeout_per_command + HZ;
-	else
-		cp->tlimit	= jiffies + 86400 * HZ;/* No timeout=24 hours */
-
 	/*
 	**	insert next CCBs into start queue.
 	**	2 max at a time is enough to flush the CCB wait queue.
 	*/
-	cp->auto_sense = 0;
 	if (lp)
 		ncr_start_next_ccb(np, lp, 2);
 	else
@@ -5468,6 +6883,24 @@
 {
 	u_short	qidx;
 
+#ifdef SCSI_NCR_IARB_SUPPORT
+	/*
+	**	If the previously queued CCB is not yet done, 
+	**	set the IARB hint. The SCRIPTS will go with IARB 
+	**	for this job when starting the previous one.
+	**	We leave devices a chance to win arbitration by 
+	**	not using more than 'iarb_max' consecutive 
+	**	immediate arbitrations.
+	*/
+	if (np->last_cp && np->iarb_count < np->iarb_max) {
+		np->last_cp->host_flags |= HF_HINT_IARB;
+		++np->iarb_count;
+	}
+	else
+		np->iarb_count = 0;
+	np->last_cp = cp;
+#endif
+	
 	/*
 	**	insert into start queue.
 	*/
@@ -5489,7 +6922,7 @@
 	**	Wake it up.
 	*/
 	MEMORY_BARRIER();
-	OUTB (nc_istat, SIGP);
+	OUTB (nc_istat, SIGP|np->istat_sem);
 }
 
 
@@ -5506,6 +6939,13 @@
 **==========================================================
 */
 
+static void ncr_chip_reset (ncb_p np)
+{
+	OUTB (nc_istat, SRST);
+	UDELAY (10);
+	OUTB (nc_istat, 0);
+}
+
 static void ncr_soft_reset(ncb_p np)
 {
 	u_char istat;
@@ -5527,16 +6967,13 @@
 	if (!i)
 		printk("%s: unable to abort current chip operation.\n",
 			ncr_name(np));
-	OUTB (nc_istat, SRST);
-	UDELAY(10);
-	OUTB (nc_istat, 0);
+	ncr_chip_reset(np);
 }
 
 /*==========================================================
 **
 **
 **	Start reset process.
-**	If reset in progress do nothing.
 **	The interrupt handler will reinitialize the chip.
 **	The timeout handler will wait for settle_time before 
 **	clearing it and so resuming command processing.
@@ -5546,17 +6983,15 @@
 */
 static void ncr_start_reset(ncb_p np)
 {
-	if (!np->settle_time) {
-		(void) ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay);
- 	}
- }
+	(void) ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay);
+}
  
 static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
 {
 	u_int32 term;
 	int retv = 0;
 
-	np->settle_time	= jiffies + settle_delay * HZ;
+	np->settle_time	= ktime_get(settle_delay * HZ);
 
 	if (bootverbose > 1)
 		printk("%s: resetting, "
@@ -5584,11 +7019,12 @@
 	**	We are expecting RESET to be TRUE and other signals to be 
 	**	FALSE.
 	*/
-	term =	INB(nc_sstat0);				/* rst, sdp0 */
-	term =	((term & 2) << 7) + ((term & 1) << 16);
-	term |= ((INB(nc_sstat2) & 0x01) << 25) |	/* sdp1 */
-		(INW(nc_sbdl) << 9) |			/* d15-0 */
-		INB(nc_sbcl);	/* req, ack, bsy, sel, atn, msg, cd, io */
+	term =	INB(nc_sstat0);
+	term =	((term & 2) << 7) + ((term & 1) << 17);	/* rst sdp0 */
+	term |= ((INB(nc_sstat2) & 0x01) << 26) |	/* sdp1     */
+		((INW(nc_sbdl) & 0xff)   << 9)  |	/* d7-0     */
+		((INW(nc_sbdl) & 0xff00) << 10) |	/* d15-8    */
+		INB(nc_sbcl);	/* req ack bsy sel atn msg cd io    */
 
 	if (!(np->features & FE_WIDE))
 		term &= 0x3ffff;
@@ -5690,14 +7126,12 @@
 {
 /*	Scsi_Device        *device    = cmd->device; */
 	ccb_p cp;
-	int found;
-	int retv;
 
 /*
  * First, look for the scsi command in the waiting list
  */
 	if (remove_from_waiting_list(np, cmd)) {
-		SetScsiResult(cmd, DID_ABORT, 0);
+		SetScsiAbortResult(cmd);
 		ncr_queue_done_cmd(np, cmd);
 		return SCSI_ABORT_SUCCESS;
 	}
@@ -5705,58 +7139,41 @@
 /*
  * Then, look in the wakeup list
  */
-	for (found=0, cp=np->ccbc; cp; cp=cp->link_ccb) {
+	for (cp=np->ccbc; cp; cp=cp->link_ccb) {
 		/*
 		**	look for the ccb of this command.
 		*/
 		if (cp->host_status == HS_IDLE) continue;
-		if (cp->cmd == cmd) {
-			found = 1;
+		if (cp->cmd == cmd)
 			break;
-		}
 	}
 
-	if (!found) {
+	if (!cp) {
 		return SCSI_ABORT_NOT_RUNNING;
 	}
 
-	if (np->settle_time) {
-		return SCSI_ABORT_SNOOZE;
-	}
-
 	/*
-	**	If the CCB is active, patch schedule jumps for the 
-	**	script to abort the command.
+	**	Keep track we have to abort this job.
 	*/
+	cp->to_abort = 1;
 
-	cp->tlimit = 0;
-	switch(cp->host_status) {
-	case HS_BUSY:
-	case HS_NEGOTIATE:
-		printk ("%s: abort ccb=%p (cancel)\n", ncr_name (np), cp);
-			cp->phys.header.go.start =
-				cpu_to_scr(NCB_SCRIPTH_PHYS (np, cancel));
-		retv = SCSI_ABORT_PENDING;
-		break;
-	case HS_DISCONNECT:
-		cp->phys.header.go.restart =
-				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
-		retv = SCSI_ABORT_PENDING;
-		break;
-	default:
-		retv = SCSI_ABORT_NOT_RUNNING;
-		break;
-
-	}
+	/*
+	**	Tell the SCRIPTS processor to stop 
+	**	and synchronize with us.
+	*/
+	np->istat_sem = SEM;
 
 	/*
 	**      If there are no requests, the script
 	**      processor will sleep on SEL_WAIT_RESEL.
 	**      Let's wake it up, since it may have to work.
 	*/
-	OUTB (nc_istat, SIGP);
+	OUTB (nc_istat, SIGP|SEM);
 
-	return retv;
+	/*
+	**	Tell user we are working for him.
+	*/
+	return SCSI_ABORT_PENDING;
 }
 
 /*==========================================================
@@ -5795,9 +7212,7 @@
 */
 
 	printk("%s: resetting chip\n", ncr_name(np));
-	OUTB (nc_istat,  SRST);
-	UDELAY (100);
-	OUTB (nc_istat,  0   );
+	ncr_chip_reset(np);
 
 /*
 **	Restore bios setting for automatic clock detection.
@@ -5843,11 +7258,8 @@
 		return;
 
 	/*
-	**	Gather profiling data
+	**	Print some debugging info.
 	*/
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	ncb_profile (np, cp);
-#endif
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
 		printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp,
@@ -5871,41 +7283,56 @@
 	if (cp == tp->nego_cp)
 		tp->nego_cp = 0;
 
+#ifdef SCSI_NCR_IARB_SUPPORT
 	/*
-	**	If auto-sense performed, change scsi status.
+	**	We just complete the last queued CCB.
+	**	Clear this info that is no more relevant.
 	*/
-	if (cp->auto_sense) {
-		cp->scsi_status = cp->auto_sense;
-	}
+	if (cp == np->last_cp)
+		np->last_cp = 0;
+#endif
 
 	/*
-	**	Check for parity errors.
+	**	If auto-sense performed, change scsi status, 
+	**	Otherwise, compute the residual.
 	*/
-
-	if (cp->host_flags & HF_PAR_ERR) {
-		PRINT_ADDR(cmd);
-		printk ("unrecovered SCSI parity error.\n");
-		if (cp->host_status==HS_COMPLETE)
-			cp->host_status = HS_FAIL;
+	if (cp->host_flags & HF_AUTO_SENSE) {
+		cp->scsi_status = cp->sv_scsi_status;
+		cp->xerr_status = cp->sv_xerr_status;
+	}
+	else {
+		cp->resid = 0;
+		if (cp->xerr_status ||
+		    cp->phys.header.lastp != cp->phys.header.goalp)
+			cp->resid = ncr_compute_residual(np, cp);
 	}
 
 	/*
 	**	Check for extended errors.
 	*/
 
-	if (cp->xerr_status != XE_OK) {
-		PRINT_ADDR(cmd);
-		switch (cp->xerr_status) {
-		case XE_EXTRA_DATA:
+	if (cp->xerr_status) {
+		if (cp->xerr_status & XE_PARITY_ERR) {
+			PRINT_ADDR(cmd);
+			printk ("unrecovered SCSI parity error.\n");
+		}
+		if (cp->xerr_status & XE_EXTRA_DATA) {
+			PRINT_ADDR(cmd);
 			printk ("extraneous data discarded.\n");
-			break;
-		case XE_BAD_PHASE:
+		}
+		if (cp->xerr_status & XE_BAD_PHASE) {
+			PRINT_ADDR(cmd);
 			printk ("illegal scsi phase (4/5).\n");
-			break;
-		default:
-			printk ("extended error %d.\n", cp->xerr_status);
-			break;
 		}
+		if (cp->xerr_status & XE_SODL_UNRUN) {
+			PRINT_ADDR(cmd);
+			printk ("ODD transfer in DATA OUT phase.\n");
+		}
+		if (cp->xerr_status & XE_SWIDE_OVRUN){
+			PRINT_ADDR(cmd);
+			printk ("ODD transfer in DATA IN phase.\n");
+		}
+
 		if (cp->host_status==HS_COMPLETE)
 			cp->host_status = HS_FAIL;
 	}
@@ -5914,13 +7341,22 @@
 	**	Print out any error for debugging purpose.
 	*/
 	if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
-		if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD) {
+		if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD ||
+		    cp->resid) {
 			PRINT_ADDR(cmd);
-			printk ("ERROR: cmd=%x host_status=%x scsi_status=%x\n",
-				cmd->cmnd[0], cp->host_status, cp->scsi_status);
+			printk ("ERROR: cmd=%x host_status=%x scsi_status=%x "
+				"data_len=%d residual=%d\n",
+				cmd->cmnd[0], cp->host_status, cp->scsi_status,
+				cp->data_len, cp->resid);
 		}
 	}
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,99)
+	/*
+	**	Move residual byte count to user structure.
+	*/
+	cmd->resid = cp->resid;
+#endif
 	/*
 	**	Check the status.
 	*/
@@ -5935,13 +7371,6 @@
 		SetScsiResult(cmd, DID_OK, cp->scsi_status);
 
 		/*
-		**	@RESID@
-		**	Could dig out the correct value for resid,
-		**	but it would be quite complicated.
-		*/
-		/* if (cp->phys.header.lastp != cp->phys.header.goalp) */
-
-		/*
 		**	Allocate the lcb if not yet.
 		*/
 		if (!lp)
@@ -5954,13 +7383,11 @@
 		*/
 		if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
 		    cmd->cmnd[4] >= 7 && !cmd->use_sg) {
+			sync_scsi_data(np, cmd);	/* SYNC the data */
 			ncr_setup_lcb (np, cp->target, cp->lun,
 				       (char *) cmd->request_buffer);
 		}
 
-		tp->bytes     += cp->data_len;
-		tp->transfers ++;
-
 		/*
 		**	If tags was reduced due to queue full,
 		**	increase tags if 1000 good status received.
@@ -5981,13 +7408,15 @@
 		SetScsiResult(cmd, DID_OK, S_CHECK_COND);
 
 		if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
-			u_char * p = (u_char*) & cmd->sense_buffer;
-			int i;
 			PRINT_ADDR(cmd);
-			printk ("sense data:");
-			for (i=0; i<14; i++) printk (" %x", *p++);
-			printk (".\n");
+			ncr_printl_hex("sense data:", cmd->sense_buffer, 14);
 		}
+	} else if ((cp->host_status == HS_COMPLETE)
+		&& (cp->scsi_status == S_CONFLICT)) {
+		/*
+		**   Reservation Conflict condition code
+		*/
+		SetScsiResult(cmd, DID_OK, S_CONFLICT);
 
 	} else if ((cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == S_BUSY ||
@@ -6018,9 +7447,10 @@
 		/*
 		**   Transfer aborted
 		*/
-		SetScsiResult(cmd, DID_ABORT, cp->scsi_status);
+		SetScsiAbortResult(cmd);
 
 	} else {
+		int did_status;
 
 		/*
 		**  Other protocol messes
@@ -6029,7 +7459,11 @@
 		printk ("COMMAND FAILED (%x %x) @%p.\n",
 			cp->host_status, cp->scsi_status, cp);
 
-		SetScsiResult(cmd, DID_ERROR, cp->scsi_status);
+		did_status = DID_ERROR;
+		if (cp->xerr_status & XE_PARITY_ERR)
+			did_status = DID_PARITY;
+
+		SetScsiResult(cmd, did_status, cp->scsi_status);
 	}
 
 	/*
@@ -6037,12 +7471,9 @@
 	*/
 
 	if (tp->usrflag & UF_TRACE) {
-		u_char * p;
-		int i;
 		PRINT_ADDR(cmd);
 		printk (" CMD:");
-		p = (u_char*) &cmd->cmnd[0];
-		for (i=0; i<cmd->cmd_len; i++) printk (" %x", *p++);
+		ncr_print_hex(cmd->cmnd, cmd->cmd_len);
 
 		if (cp->host_status==HS_COMPLETE) {
 			switch (cp->scsi_status) {
@@ -6051,9 +7482,7 @@
 				break;
 			case S_CHECK_COND:
 				printk ("  SENSE:");
-				p = (u_char*) &cmd->sense_buffer;
-				for (i=0; i<14; i++)
-					printk (" %x", *p++);
+				ncr_print_hex(cmd->sense_buffer, 14);
 				break;
 			default:
 				printk ("  STAT: %x\n", cp->scsi_status);
@@ -6099,6 +7528,10 @@
 /*
 **	The NCR has completed CCBs.
 **	Look at the DONE QUEUE.
+**
+**	On architectures that may reorder LOAD/STORE operations, 
+**	a memory barrier may be needed after the reading of the 
+**	so-called `flag' and prior to dealing with the data.
 */
 int ncr_wakeup_done (ncb_p np)
 {
@@ -6118,6 +7551,7 @@
 
 		cp = ncr_ccb_from_dsa(np, dsa);
 		if (cp) {
+			MEMORY_BARRIER();
 			ncr_complete (np, cp);
 			++n;
 		}
@@ -6180,7 +7614,7 @@
 	/*
 	**	Clear Start Queue
 	*/
-	phys = vtobus(np->squeue);
+	phys = np->p_squeue;
 	np->queuedepth = MAX_START - 1;	/* 1 entry needed as end marker */
 	for (i = 0; i < MAX_START*2; i += 2) {
 		np->squeue[i]   = cpu_to_scr(np->p_idletask);
@@ -6239,40 +7673,54 @@
 	OUTB (nc_ctest3, np->rv_ctest3);	/* Write and invalidate */
 	OUTB (nc_ctest4, np->rv_ctest4);	/* Master parity checking */
 
-	OUTB (nc_stest2, EXT|np->rv_stest2); /* Extended Sreq/Sack filtering */
+	if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+		(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66)){
+		OUTB (nc_stest2, EXT|np->rv_stest2); 
+		/* Extended Sreq/Sack filtering, not supported in C1010/C1010_66 */
+	}
 	OUTB (nc_stest3, TE);			/* TolerANT enable */
 	OUTB (nc_stime0, 0x0c);			/* HTH disabled  STO 0.25 sec */
 
 	/*
 	**	DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
-	**	Disable overlapped arbitration.
-	**	The 896 Rev 1 needs also this work-around to be applied.
+	**	Disable overlapped arbitration for all dual-function 
+	**	devices, regardless revision id.
+	**	We may consider it is a post-chip-design feature. ;-)
+ 	**
+ 	**	Errata applies to all 896 and 1010 parts.
 	*/
-	if (np->device_id == PCI_DEVICE_ID_NCR_53C875 &&
-	    np->revision_id >= 0x10 && np->revision_id <= 0x15)
+	if (np->device_id == PCI_DEVICE_ID_NCR_53C875)
 		OUTB (nc_ctest0, (1<<5));
-	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
-		 np->revision_id <= 0x1)
+ 	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896  ||
+ 		 np->device_id == PCI_DEVICE_ID_LSI_53C1010 ||
+ 		 np->device_id == PCI_DEVICE_ID_LSI_53C1010_66 )
 		np->rv_ccntl0 |= DPR;
 
 	/*
-	**	If 64 bit (53C896) enable 40 bit address table 
-	**	indirect addressing for MOVE.
+	**	C1010_66MHz rev 0 part requies AIPCNTL1 bit 3 to be set.
 	*/
+	if (np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)
+		OUTB(nc_aipcntl1, (1<<3));
 
+	/*
+	**	If 64 bit (895A/896/1010/1010_66) write the CCNTL1 register to 
+	**	enable 40 bit address table indirect addressing for MOVE.
+	**	Also write CCNTL0 if 64 bit chip, since this register seems 
+	**	to only be used by 64 bit cores.
+	*/
 	if (np->features & FE_64BIT) {
+		OUTB (nc_ccntl0, np->rv_ccntl0);
 		OUTB (nc_ccntl1, np->rv_ccntl1);
 	}
 
 	/*
-	**	If phase mismatch handled by scripts (53C896),
-	**	set PM jump addresses.
+ 	**	If phase mismatch handled by scripts (53C895A or 53C896 
+ 	**	or 53C1010 or 53C1010_66), set PM jump addresses. 
 	*/
 
 	if (np->features & FE_NOPM) {
 		printk(KERN_INFO "%s: handling phase mismatch from SCRIPTS.\n", 
 		       ncr_name(np));
-		OUTB (nc_ccntl0, np->rv_ccntl0);
 		OUTL (nc_pmjad1, NCB_SCRIPTH_PHYS (np, pm_handle));
 		OUTL (nc_pmjad2, NCB_SCRIPTH_PHYS (np, pm_handle));
 	}
@@ -6296,9 +7744,10 @@
 	OUTB (nc_dien , MDPE|BF|SSI|SIR|IID);
 
 	/*
-	**	For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+	**	For 895/895A/896/c1010
+	**	Enable SBMC interrupt and save current SCSI bus mode.
 	*/
-	if (np->features & FE_ULTRA2) {
+	if ( (np->features & FE_ULTRA2) || (np->features & FE_ULTRA3) ) {
 		OUTONW (nc_sien, SBMC);
 		np->scsi_mode = INB (nc_stest4) & SMODE;
 	}
@@ -6313,8 +7762,11 @@
 	for (i=0;i<MAX_TARGET;i++) {
 		tcb_p tp = &np->target[i];
 
+		tp->to_reset = 0;
+
 		tp->sval    = 0;
 		tp->wval    = np->rv_scntl3;
+		tp->uval    = np->rv_scntl4;
 
 		if (tp->usrsync != 255) {
 			if (tp->usrsync <= np->maxsync) {
@@ -6339,7 +7791,6 @@
 	**    For platforms that may not support PCI memory mapping,
 	**    we use a simple SCRIPTS that performs MEMORY MOVEs.
 	*/
-	MEMORY_BARRIER();
 	if (np->base2_ba) {
 		if (bootverbose)
 			printk ("%s: Downloading SCSI SCRIPTS.\n",
@@ -6366,8 +7817,10 @@
 	else
 		phys = NCB_SCRIPT_PHYS (np, init);
 
-	OUTL (nc_dsa, vtobus(np));
-	OUTL (nc_dsp, phys);
+	np->istat_sem = 0;
+
+	OUTL (nc_dsa, np->p_ncb);
+	OUTL_DSP (phys);
 }
 
 /*==========================================================
@@ -6443,6 +7896,10 @@
 
 	/*
 	**	Compute the synchronous period in tenths of nano-seconds
+	**	from sfac.
+	**
+	**	Note, if sfac == 9, DT is being used. Double the period of 125
+	**	to 250. 
 	*/
 	if	(sfac <= 10)	per = 250;
 	else if	(sfac == 11)	per = 303;
@@ -6490,7 +7947,76 @@
 	**	Compute and return sync parameters for the ncr
 	*/
 	*fakp		= fak - 4;
-	*scntl3p	= ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+
+	/*
+	** If sfac < 25, and 8xx parts, desire that the chip operate at 
+	** least at Ultra speeds.  Must set bit 7 of scntl3.
+	** For C1010, do not set this bit. If operating at Ultra3 speeds,
+	**	set the U3EN bit instead.
+	*/ 
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010)  ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+		*scntl3p	= (div+1) << 4;
+		*fakp		= 0;
+	}
+	else {
+		*scntl3p	= ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+		*fakp		= fak - 4;
+	}
+}
+
+/*==========================================================
+**
+**	Utility routine to return the current bus width	
+**	synchronous period and offset.
+**	Utilizes target sval, wval and uval  
+**
+**==========================================================
+*/
+static void ncr_get_xfer_info(ncb_p np, tcb_p tp, u_char *factor, 
+			u_char *offset, u_char *width)
+{
+
+	u_char idiv;
+	u_long period;
+
+	*width = (tp->wval & EWS) ? 1 : 0;
+
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+		(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66))
+		*offset  = (tp->sval & 0x3f);
+	else
+		*offset  = (tp->sval & 0x1f);
+
+        /*
+	 * Midlayer signal to the driver that all of the scsi commands
+	 * for the integrity check have completed. Save the negotiated
+ 	 * parameters (extracted from sval, wval and uval). 
+	 * See ncr_setsync for alg. details.
+	 */
+
+	idiv = (tp->wval>>4) & 0x07;
+
+	if ( *offset && idiv ) {
+	  	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) || 
+	  		(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)){
+		    if (tp->uval & 0x80)
+			period = (2*div_10M[idiv-1])/np->clock_khz;
+	    	    else 
+	    		period = (4*div_10M[idiv-1])/np->clock_khz;
+	  	}
+	  	else
+	   	    period = (((tp->sval>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+	}
+	else
+		period = 0xffff;
+
+	if	(period <= 125)		*factor =   9;
+	else if	(period <= 250)		*factor =  10;
+	else if	(period <= 303)		*factor  = 11;
+	else if	(period <= 500)		*factor  = 12;
+	else				*factor  = (period + 40 - 1) / 40;
+
 }
 
 
@@ -6504,14 +8030,20 @@
 
 static void ncr_set_sync_wide_status (ncb_p np, u_char target)
 {
-	ccb_p cp;
+	ccb_p cp = np->ccbc;
 	tcb_p tp = &np->target[target];
 
 	/*
 	**	set actual value and sync_status
+	**
+	**	TEMP register contains current scripts address
+	**	which is data type/direction/dependent.
 	*/
 	OUTB (nc_sxfer, tp->sval);
 	OUTB (nc_scntl3, tp->wval);
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010)  ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) 
+		OUTB (nc_scntl4, tp->uval); 
 
 	/*
 	**	patch ALL ccbs of this target.
@@ -6523,6 +8055,9 @@
 			continue;
 		cp->phys.select.sel_scntl3 = tp->wval;
 		cp->phys.select.sel_sxfer  = tp->sval;
+		if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+				(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66))
+			cp->phys.select.sel_scntl4 = tp->uval;
 	};
 }
 
@@ -6533,11 +8068,13 @@
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer, 
+					u_char scntl4)
 {
 	tcb_p tp;
 	u_char target = INB (nc_sdid) & 0x0f;
 	u_char idiv;
+	u_char offset;
 
 	assert (cp);
 	if (!cp) return;
@@ -6546,9 +8083,21 @@
 
 	tp = &np->target[target];
 
-	if (!scntl3 || !(sxfer & 0x1f))
-		scntl3 = np->rv_scntl3;
-	scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+		offset = sxfer & 0x3f; /* bits 5-0 */
+		scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS);
+		scntl4 = (scntl4 & 0x80);
+	}
+	else {
+		offset = sxfer & 0x1f; /* bits 4-0 */
+		if (!scntl3 || !offset)
+			scntl3 = np->rv_scntl3;
+
+		scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | 
+				(np->rv_scntl3 & 0x07);
+	}
+	
 
 	/*
 	**	Deduce the value of controller sync period from scntl3.
@@ -6556,23 +8105,42 @@
 	*/
 
 	idiv = ((scntl3 >> 4) & 0x7);
-	if ((sxfer & 0x1f) && idiv)
-		tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+	if ( offset && idiv) {
+		if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+			/* Note: If extra data hold clocks are used,
+			 * the formulas below must be modified.
+			 * When scntl4 == 0, ST mode.
+			 */
+			if (scntl4 & 0x80)
+				tp->period = (2*div_10M[idiv-1])/np->clock_khz;
+			else
+				tp->period = (4*div_10M[idiv-1])/np->clock_khz;
+		}
+		else 
+			tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+	}
 	else
 		tp->period = 0xffff;
 
+
 	/*
 	**	 Stop there if sync parameters are unchanged
 	*/
-	if (tp->sval == sxfer && tp->wval == scntl3) return;
+	if (tp->sval == sxfer && tp->wval == scntl3 && tp->uval == scntl4) return;
 	tp->sval = sxfer;
 	tp->wval = scntl3;
+	tp->uval = scntl4;
 
 	/*
 	**	Bells and whistles   ;-)
+	**	Donnot announce negotiations due to auto-sense, 
+	**	unless user really want us to be verbose. :)
 	*/
+	if ( bootverbose < 2 && (cp->host_flags & HF_AUTO_SENSE))
+		goto next;
 	PRINT_TARGET(np, target);
-	if (sxfer & 0x01f) {
+	if (offset) {
 		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
 		unsigned mb10 = (f10 + tp->period/2) / tp->period;
 		char *scsi;
@@ -6580,22 +8148,26 @@
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
-		if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
+		if ((tp->period <= 2000) && 
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+				OUTOFFB (nc_stest2, EXT);
 
 		/*
 		**	Bells and whistles   ;-)
 		*/
-		if	(tp->period < 500)	scsi = "FAST-40";
+		if	(tp->period < 250)	scsi = "FAST-80";
+		else if	(tp->period < 500)	scsi = "FAST-40";
 		else if	(tp->period < 1000)	scsi = "FAST-20";
 		else if	(tp->period < 2000)	scsi = "FAST-10";
 		else				scsi = "FAST-5";
 
 		printk ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
 			tp->widedone > 1 ? "WIDE " : "",
-			mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+			mb10 / 10, mb10 % 10, tp->period / 10, offset);
 	} else
 		printk ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
-
+next:
 	/*
 	**	set actual value and sync_status
 	**	patch ALL ccbs of this target.
@@ -6603,6 +8175,7 @@
 	ncr_set_sync_wide_status(np, target);
 }
 
+
 /*==========================================================
 **
 **	Switch wide mode for current job and it's target
@@ -6656,6 +8229,126 @@
 	ncr_set_sync_wide_status(np, target);
 }
 
+
+/*==========================================================
+**
+**	Switch sync/wide mode for current job and it's target
+**	PPR negotiations only
+**
+**==========================================================
+*/
+
+static void ncr_setsyncwide (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer, 
+				u_char scntl4, u_char wide)
+{
+	tcb_p tp;
+	u_char target = INB (nc_sdid) & 0x0f;
+	u_char idiv;
+	u_char offset;
+
+	assert (cp);
+	if (!cp) return;
+
+	assert (target == (cp->target & 0xf));
+
+	tp = &np->target[target];
+	tp->widedone  =  wide+1;
+
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+		offset = sxfer & 0x3f; /* bits 5-0 */
+		scntl3 = (scntl3 & 0xf0) | (wide ? EWS : 0);
+		scntl4 = (scntl4 & 0x80);
+	}
+	else {
+		offset = sxfer & 0x1f; /* bits 4-0 */
+		if (!scntl3 || !offset)
+			scntl3 = np->rv_scntl3;
+
+		scntl3 = (scntl3 & 0xf0) | (wide ? EWS : 0) | 
+				(np->rv_scntl3 & 0x07);
+	}
+	
+
+	/*
+	**	Deduce the value of controller sync period from scntl3.
+	**	period is in tenths of nano-seconds.
+	*/
+
+	idiv = ((scntl3 >> 4) & 0x7);
+	if ( offset && idiv) {
+		if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+			/* Note: If extra data hold clocks are used,
+			 * the formulas below must be modified.
+			 * When scntl4 == 0, ST mode.
+			 */
+			if (scntl4 & 0x80)
+				tp->period = (2*div_10M[idiv-1])/np->clock_khz;
+			else
+				tp->period = (4*div_10M[idiv-1])/np->clock_khz;
+		}
+		else 
+			tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+	}
+	else
+		tp->period = 0xffff;
+
+
+	/*
+	**	 Stop there if sync parameters are unchanged
+	*/
+	if (tp->sval == sxfer && tp->wval == scntl3 && tp->uval == scntl4) return;
+	tp->sval = sxfer;
+	tp->wval = scntl3;
+	tp->uval = scntl4;
+
+	/*
+	**	Bells and whistles   ;-)
+	**	Donnot announce negotiations due to auto-sense, 
+	**	unless user really want us to be verbose. :)
+	*/
+	if ( bootverbose < 2 && (cp->host_flags & HF_AUTO_SENSE))
+		goto next;
+	PRINT_TARGET(np, target);
+	if (offset) {
+		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
+		unsigned mb10 = (f10 + tp->period/2) / tp->period;
+		char *scsi;
+
+		/*
+		**  Disable extended Sreq/Sack filtering
+		*/
+		if ((tp->period <= 2000) && 
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+				OUTOFFB (nc_stest2, EXT);
+
+		/*
+		**	Bells and whistles   ;-)
+		*/
+		if	(tp->period < 250)	scsi = "FAST-80";
+		else if	(tp->period < 500)	scsi = "FAST-40";
+		else if	(tp->period < 1000)	scsi = "FAST-20";
+		else if	(tp->period < 2000)	scsi = "FAST-10";
+		else				scsi = "FAST-5";
+
+		printk ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+			tp->widedone > 1 ? "WIDE " : "",
+			mb10 / 10, mb10 % 10, tp->period / 10, offset);
+	} else
+		printk ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
+next:
+	/*
+	**	set actual value and sync_status
+	**	patch ALL ccbs of this target.
+	*/
+	ncr_set_sync_wide_status(np, target);
+}
+
+
+
+
 /*==========================================================
 **
 **	Switch tagged mode for a target.
@@ -6667,7 +8360,7 @@
 {
 	tcb_p tp = &np->target[tn];
 	lcb_p lp = ncr_lp(np, tp, ln);
-	u_char   reqtags, maxdepth;
+	u_short reqtags, maxdepth;
 
 	/*
 	**	Just in case ...
@@ -6761,35 +8454,12 @@
 {
 	u_char t;
 	tcb_p tp;
+	int ln;
+	u_long size;
 
 	switch (np->user.cmd) {
-
 	case 0: return;
 
-	case UC_SETSYNC:
-		for (t=0; t<MAX_TARGET; t++) {
-			if (!((np->user.target>>t)&1)) continue;
-			tp = &np->target[t];
-			tp->usrsync = np->user.data;
-			ncr_negotiate (np, tp);
-		};
-		break;
-
-	case UC_SETTAGS:
-		for (t=0; t<MAX_TARGET; t++) {
-			int ln;
-			if (!((np->user.target>>t)&1)) continue;
-			np->target[t].usrtags = np->user.data;
-			for (ln = 0; ln < MAX_LUN; ln++) {
-				lcb_p lp = ncr_lp(np, &np->target[t], ln);
-				if (!lp)
-					continue;
-				lp->maxtags = lp->numtags = np->user.data;
-				ncr_setup_tags (np, t, ln);
-			}
-		};
-		break;
-
 	case UC_SETDEBUG:
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 		ncr_debug = np->user.data;
@@ -6804,33 +8474,70 @@
 		np->verbose = np->user.data;
 		break;
 
-	case UC_SETWIDE:
-		for (t=0; t<MAX_TARGET; t++) {
-			u_long size;
-			if (!((np->user.target>>t)&1)) continue;
+	default:
+		/*
+		**	We assume that other commands apply to targets.
+		**	This should always be the case and avoid the below 
+		**	4 lines to be repeated 5 times.
+		*/
+		for (t = 0; t < MAX_TARGET; t++) {
+			if (!((np->user.target >> t) & 1))
+				continue;
 			tp = &np->target[t];
-			size = np->user.data;
-			if (size > np->maxwide) size=np->maxwide;
-			tp->usrwide = size;
-			ncr_negotiate (np, tp);
-		};
-		break;
 
-	case UC_SETFLAG:
-		for (t=0; t<MAX_TARGET; t++) {
-			if (!((np->user.target>>t)&1)) continue;
-			tp = &np->target[t];
-			tp->usrflag = np->user.data;
-		};
-		break;
+			switch (np->user.cmd) {
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	case UC_CLEARPROF:
-		bzero(&np->profile, sizeof(np->profile));
-		break;
-#endif
-	}
-	np->user.cmd=0;
+			case UC_SETSYNC:
+				tp->usrsync = np->user.data;
+				ncr_negotiate (np, tp);
+				break;
+
+			case UC_SETWIDE:
+				size = np->user.data;
+				if (size > np->maxwide)
+					size=np->maxwide;
+				tp->usrwide = size;
+				ncr_negotiate (np, tp);
+				break;
+
+			case UC_SETTAGS:
+				tp->usrtags = np->user.data;
+				for (ln = 0; ln < MAX_LUN; ln++) {
+					lcb_p lp;
+					lp = ncr_lp(np, tp, ln);
+					if (!lp)
+						continue;
+					lp->numtags = np->user.data;
+					lp->maxtags = lp->numtags;
+					ncr_setup_tags (np, t, ln);
+				}
+				break;
+
+			case UC_RESETDEV:
+				tp->to_reset = 1;
+				np->istat_sem = SEM;
+				OUTB (nc_istat, SIGP|SEM);
+				break;
+
+			case UC_CLEARDEV:
+				for (ln = 0; ln < MAX_LUN; ln++) {
+					lcb_p lp;
+					lp = ncr_lp(np, tp, ln);
+					if (lp)
+						lp->to_clear = 1;
+				}
+				np->istat_sem = SEM;
+				OUTB (nc_istat, SIGP|SEM);
+				break;
+
+			case UC_SETFLAG:
+				tp->usrflag = np->user.data;
+				break;
+			}
+		}
+		break;
+	}
+	np->user.cmd=0;
 }
 #endif
 
@@ -6850,7 +8557,7 @@
 
 static void ncr_timeout (ncb_p np)
 {
-	u_long	thistime = jiffies;
+	u_long	thistime = ktime_get(0);
 
 	/*
 	**	If release process in progress, let's go
@@ -6863,7 +8570,11 @@
 		return;
 	}
 
-	np->timer.expires = jiffies + SCSI_NCR_TIMER_INTERVAL;
+#ifdef SCSI_NCR_PCIQ_BROKEN_INTR
+	np->timer.expires = ktime_get((HZ+9)/10);
+#else
+	np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
+#endif
 	add_timer(&np->timer);
 
 	/*
@@ -6887,7 +8598,19 @@
 		np->lasttime = thistime;
 	}
 
-#ifdef SCSI_NCR_BROKEN_INTR
+#ifdef SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS
+	/*
+	**	Some way-broken PCI bridges may lead to 
+	**	completions being lost when the clearing 
+	**	of the INTFLY flag by the CPU occurs 
+	**	concurrently with the chip raising this flag.
+	**	If this ever happen, lost completions will 
+	**	be reaped here.
+	*/
+	ncr_wakeup_done(np);
+#endif
+
+#ifdef SCSI_NCR_PCIQ_BROKEN_INTR
 	if (INB(nc_istat) & (INTF|SIP|DIP)) {
 
 		/*
@@ -6897,7 +8620,7 @@
 		ncr_exception (np);
 		if (DEBUG_FLAGS & DEBUG_TINY) printk ("}");
 	}
-#endif /* SCSI_NCR_BROKEN_INTR */
+#endif /* SCSI_NCR_PCIQ_BROKEN_INTR */
 }
 
 /*==========================================================
@@ -6924,7 +8647,7 @@
 **		dsp:	script adress (relative to start of script).
 **		dbc:	first word of script command.
 **
-**	First 16 register of the chip:
+**	First 24 register of the chip:
 **		r0..rf
 **
 **==========================================================
@@ -6973,7 +8696,7 @@
 	}
 
         printk ("%s: regdump:", ncr_name(np));
-        for (i=0; i<16;i++)
+        for (i=0; i<24;i++)
             printk (" %02x", (unsigned)INB_OFF(i));
         printk (".\n");
 }
@@ -7053,63 +8776,24 @@
 	u_short	sist;
 	int	i;
 
-#ifdef SCSI_NCR_OPTIMIZE_896_1
-	/*
-	**	This optimization when used with a 896 that handles 
-	**	phase mismatch from the SCRIPTS allows to only do 
-	**	PCI memory writes transactions from the CPU and so to 
-	**	take advantage of PCI posted writes.
-	**	Who wants his 500 MHz CPU to wait several micro-seconds 
-	**	for the PCI BUS to be granted when this can be avoided?
-	**	I don't, even for my slow 233 MHz PII. :-)
-	**
-	**	We assume we have been called for command completion.
-	**	If no completion found, go with normal handling.
-	**	Ordering is ensured by the SCRIPTS performing a read 
-	**	from main memory prior to raising INTFLY.
-	**	We have to raise SIGP since the chip may be currently 
-	**	going to a wait reselect instruction. IMO, SIGP should 
-	**	not be clearable in ISTAT since it can be polled and 
-	**	cleared by reading CTEST2. This tiny chip misdesign is a 
-	**	penalty here.
-	**
-	**	The MA interrupt and interrupt sharing may also have 
-	**	adverse effects on this optimization, so we only want 
-	**	to use it if it is enabled by user.
-	**	(BTW, this optimization seems to even have some goodness 
-	**	with my 895 that unfortunately suffers of the MA int.).
-	*/
-	if (driver_setup.optimize & 1) {
-		OUTB(nc_istat, (INTF | SIGP));
-		if (ncr_wakeup_done (np)) {
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-			++np->profile.num_fly;
-#endif
-			return;
-		}
-	}
-#endif /* SCSI_NCR_OPTIMIZE_896_1 */
-
 	/*
 	**	interrupt on the fly ?
+	**
+	**	A `dummy read' is needed to ensure that the 
+	**	clear of the INTF flag reaches the device 
+	**	before the scanning of the DONE queue.
 	*/
 	istat = INB (nc_istat);
 	if (istat & INTF) {
-		OUTB (nc_istat, (istat & SIGP) | INTF);
+		OUTB (nc_istat, (istat & SIGP) | INTF | np->istat_sem);
+		istat = INB (nc_istat);		/* DUMMY READ */
 		if (DEBUG_FLAGS & DEBUG_TINY) printk ("F ");
 		(void)ncr_wakeup_done (np);
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-		++np->profile.num_fly;
-#endif
 	};
 
 	if (!(istat & (SIP|DIP)))
 		return;
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	++np->profile.num_int;
-#endif
-
 #if 0	/* We should never get this one */
 	if (istat & CABRT)
 		OUTB (nc_istat, CABRT);
@@ -7150,6 +8834,12 @@
 			(unsigned)INL(nc_dsp),
 			(unsigned)INL(nc_dbc));
 
+	/*
+	**	On paper, a memory barrier may be needed here.
+	**	And since we are paranoid ... :)
+	*/
+	MEMORY_BARRIER();
+
 	/*========================================================
 	**	First, interrupts we want to service cleanly.
 	**
@@ -7170,7 +8860,7 @@
 		if	(sist & PAR)	ncr_int_par (np, sist);
 		else if (sist & MA)	ncr_int_ma (np);
 		else if (dstat & SIR)	ncr_int_sir (np);
-		else if (dstat & SSI)	OUTONB (nc_dcntl, (STD|NOCOM));
+		else if (dstat & SSI)	OUTONB_STD ();
 		else			goto unknown_int;
 		return;
 	};
@@ -7213,8 +8903,8 @@
 	**	Reset everything.
 	**=========================================================
 	*/
-	if (jiffies - np->regtime > 10*HZ) {
-		np->regtime = jiffies;
+	if (ktime_exp(np->regtime)) {
+		np->regtime = ktime_get(10*HZ);
 		for (i = 0; i<sizeof(np->regdump); i++)
 			((char*)&np->regdump)[i] = INB_OFF(i);
 		np->regdump.nc_dstat = dstat;
@@ -7223,6 +8913,28 @@
 
 	ncr_log_hard_error(np, sist, dstat);
 
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+		(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+		u_char ctest4_o, ctest4_m;
+		u_char shadow;
+
+		/* 
+		 * Get shadow register data 
+		 * Write 1 to ctest4
+		 */
+		ctest4_o = INB(nc_ctest4);
+
+		OUTB(nc_ctest4, ctest4_o | 0x10);
+		
+		ctest4_m = INB(nc_ctest4);
+		shadow = INW_OFF(0x42);
+
+		OUTB(nc_ctest4, ctest4_o);
+
+		printk("%s: ctest4/sist original 0x%x/0x%X  mod: 0x%X/0x%x\n", 
+			ncr_name(np), ctest4_o, sist, ctest4_m, shadow);
+	}
+
 	if ((sist & (GEN|HTH|SGE)) ||
 		(dstat & (MDPE|BF|ABRT|IID))) {
 		ncr_start_reset(np);
@@ -7287,6 +8999,8 @@
 	       dsp < NCB_SCRIPT_PHYS (np, getjob_end) + 1)) &&
 	    (!(dsp > NCB_SCRIPT_PHYS (np, ungetjob) &&
 	       dsp < NCB_SCRIPT_PHYS (np, reselect) + 1)) &&
+	    (!(dsp > NCB_SCRIPTH_PHYS (np, sel_for_abort) &&
+	       dsp < NCB_SCRIPTH_PHYS (np, sel_for_abort_1) + 1)) &&
 	    (!(dsp > NCB_SCRIPT_PHYS (np, done) &&
 	       dsp < NCB_SCRIPT_PHYS (np, done_end) + 1))) {
 		if (cp) {
@@ -7296,7 +9010,7 @@
 		OUTL (nc_dsa, DSA_INVALID);
 		OUTB (nc_ctest3, np->rv_ctest3 | CLF);	/* clear dma fifo  */
 		OUTB (nc_stest3, TE|CSF);		/* clear scsi fifo */
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
 	}
 	else
 		goto reset_all;
@@ -7330,7 +9044,6 @@
 	if (DEBUG_FLAGS & DEBUG_TINY) printk ("T");
 
 	if (dsp == NCB_SCRIPT_PHYS (np, wf_sel_done) + 8 ||
-	    dsp == NCB_SCRIPTH_PHYS (np, wf_sel_done_no_atn) + 8 ||
 	    !(driver_setup.recovery & 1))
 		ncr_recover_scsi_int(np, HS_SEL_TIMEOUT);
 	else
@@ -7347,6 +9060,20 @@
 */
 void ncr_int_udc (ncb_p np)
 {
+	u_int32 dsa = INL (nc_dsa);
+	ccb_p   cp  = ncr_ccb_from_dsa(np, dsa);
+	tcb_p   tp  = &np->target[cp->target];
+
+	/*
+	 * Fix Up. Some disks respond to a PPR negotation with
+	 * a bus free instead of a message reject. 
+	 * Disable ppr negotiation if this is first time
+	 * tried ppr negotiation.
+	 */
+	
+	if (tp->ppr_negotiation == 1)
+		tp->ppr_negotiation = 0;
+	
 	printk ("%s: unexpected disconnect\n", ncr_name(np));
 	ncr_recover_scsi_int(np, HS_UNEXPECTED);
 }
@@ -7382,7 +9109,7 @@
 	**	Suspend command processing for 1 second and 
 	**	reinitialize all except the chip.
 	*/
-	np->settle_time	= jiffies + HZ;
+	np->settle_time	= ktime_get(1*HZ);
 	ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
 }
 
@@ -7408,8 +9135,8 @@
 **	  The chip will then interrupt with both PAR and MA 
 **	  conditions set.
 **
-**	- A phase mismatch occurs before the MOV finished 
-**	  and phase errors are to be handled by SCRIPTS (896).
+**	- A phase mismatch occurs before the MOV finished and 
+**	  phase errors are to be handled by SCRIPTS (895A or 896).
 **	  The chip will load the DSP with the phase mismatch 
 **	  JUMP address and interrupt the host processor.
 **
@@ -7425,6 +9152,7 @@
 	u_char	sbcl	= INB (nc_sbcl);
 	u_char	cmd	= dbc >> 24;
 	int phase	= cmd & 7;
+	ccb_p	cp	= ncr_ccb_from_dsa(np, dsa);
 
 	printk("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n",
 		ncr_name(np), hsts, dbc, sbcl);
@@ -7444,7 +9172,7 @@
 	**	If the nexus is not clearly identified, reset the bus.
 	**	We will try to do better later.
 	*/
-	if (!ncr_ccb_from_dsa(np, dsa))
+	if (!cp)
 		goto reset_all;
 
 	/*
@@ -7457,35 +9185,44 @@
 	/*
 	**	Keep track of the parity error.
 	*/
-	OUTONB (HF_PRT, HF_PAR_ERR);
+	OUTONB (HF_PRT, HF_EXT_ERR);
+	cp->xerr_status |= XE_PARITY_ERR;
 
 	/*
 	**	Prepare the message to send to the device.
 	*/
 	np->msgout[0] = (phase == 7) ? M_PARITY : M_ID_ERROR;
 
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+	/*
+	**	Save error message. For integrity check use only.
+	*/
+	if (np->check_integrity) 
+		np->check_integ_par = np->msgout[0];
+#endif
+
 	/*
-	**	If the old phase was DATA IN phase, we have to deal with
-	**	the 3 situations described above.
+	**	If the old phase was DATA IN or DT DATA IN phase, 
+	** 	we have to deal with the 3 situations described above.
 	**	For other input phases (MSG IN and STATUS), the device 
 	**	must resend the whole thing that failed parity checking 
 	**	or signal error. So, jumping to dispatcher should be OK.
 	*/
-	if (phase == 1) {
+	if ((phase == 1) || (phase == 5)) {
 		/* Phase mismatch handled by SCRIPTS */
 		if (dsp == NCB_SCRIPTH_PHYS (np, pm_handle))
-			OUTL (nc_dsp, dsp);
+			OUTL_DSP (dsp);
 		/* Phase mismatch handled by the C code */
 		else if (sist & MA)
 			ncr_int_ma (np);
 		/* No phase mismatch occurred */
 		else {
 			OUTL (nc_temp, dsp);
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, databreak));
+			OUTL_DSP (NCB_SCRIPT_PHYS (np, dispatch));
 		}
 	}
 	else 
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
+		OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
 	return;
 
 reset_all:
@@ -7524,10 +9261,6 @@
 	struct pm_ctx *pm;
 	ccb_p	cp;
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	++np->profile.num_break;
-#endif
-
 	dsp	= INL (nc_dsp);
 	dbc	= INL (nc_dbc);
 	dsa	= INL (nc_dsa);
@@ -7541,44 +9274,64 @@
 	*/
 	cp = ncr_ccb_from_dsa(np, dsa);
 
+	if (DEBUG_FLAGS & DEBUG_PHASE)
+		printk("CCB = %2x %2x %2x %2x %2x %2x\n", 
+			cp->cmd->cmnd[0], cp->cmd->cmnd[1], cp->cmd->cmnd[2],
+			cp->cmd->cmnd[3], cp->cmd->cmnd[4], cp->cmd->cmnd[5]);
+
 	/*
 	**	Donnot take into account dma fifo and various buffers in 
-	**	DATA IN phase since the chip flushes everything before 
+	**	INPUT phase since the chip flushes everything before 
 	**	raising the MA interrupt for interrupted INPUT phases.
+	**	For DATA IN phase, we will check for the SWIDE later.
 	*/
-	if ((cmd & 7) != 1) {
+	if ((cmd & 7) != 1 && (cmd & 7) != 5) {
 		u_int32 dfifo;
 		u_char ss0, ss2;
 
 		/*
-		** Read DFIFO, CTEST[4-6] using 1 PCI bus ownership.
+		**  If C1010, DFBC contains number of bytes in DMA fifo.
+		**  else read DFIFO, CTEST[4-6] using 1 PCI bus ownership.
 		*/
-		dfifo = INL(nc_dfifo);
+		if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+				(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) 
+			delta = INL(nc_dfbc) & 0xffff;
+		else {
+			dfifo = INL(nc_dfifo);
 
-		/*
-		**	Calculate remaining bytes in DMA fifo.
-		**	(CTEST5 = dfifo >> 16)
-		*/
-		if (dfifo & (DFS << 16))
-			delta = ((((dfifo >> 8) & 0x300) |
-			          (dfifo & 0xff)) - rest) & 0x3ff;
-		else
-			delta = ((dfifo & 0xff) - rest) & 0x7f;
+			/*
+			**	Calculate remaining bytes in DMA fifo.
+			**	C1010 - always large fifo, value in dfbc
+			**	Otherwise, (CTEST5 = dfifo >> 16)
+			*/
+			if (dfifo & (DFS << 16))
+				delta = ((((dfifo >> 8) & 0x300) |
+				          (dfifo & 0xff)) - rest) & 0x3ff;
+			else
+				delta = ((dfifo & 0xff) - rest) & 0x7f;
 
-		/*
-		**	The data in the dma fifo has not been transfered to
-		**	the target -> add the amount to the rest
-		**	and clear the data.
-		**	Check the sstat2 register in case of wide transfer.
-		*/
+			/*
+			**	The data in the dma fifo has not been 
+			**	transferred to the target -> add the amount 
+			**	to the rest and clear the data.
+			**	Check the sstat2 register in case of wide
+			**	transfer.
+			*/
+
+		}
+		
 		rest += delta;
 		ss0  = INB (nc_sstat0);
 		if (ss0 & OLF) rest++;
-		if (ss0 & ORF) rest++;
+		if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) && 
+				(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66) && (ss0 & ORF)) 
+			rest++;
 		if (cp && (cp->phys.select.sel_scntl3 & EWS)) {
 			ss2 = INB (nc_sstat2);
 			if (ss2 & OLF1) rest++;
-			if (ss2 & ORF1) rest++;
+			if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+					(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66) && (ss2 & ORF)) 
+				rest++;
 		};
 
 		/*
@@ -7586,7 +9339,7 @@
 		*/
 		OUTB (nc_ctest3, np->rv_ctest3 | CLF);	/* dma fifo  */
 		OUTB (nc_stest3, TE|CSF);		/* scsi fifo */
-	};
+	}
 
 	/*
 	**	log the information
@@ -7658,9 +9411,11 @@
 
 	/*
 	**	check cmd against assumed interrupted script command.
+	**	If dt data phase, the MOVE instruction hasn't bit 4 of 
+	**	the phase.
 	*/
 
-	if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
+	if (((cmd & 2) ? cmd : (cmd & ~4)) != (scr_to_cpu(vdsp[0]) >> 24)) {
 		PRINT_ADDR(cp->cmd);
 		printk ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
 			(unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
@@ -7670,9 +9425,10 @@
 
 	/*
 	**	if old phase not dataphase, leave here.
+	**	C/D line is low if data.
 	*/
 
-	if (cmd & 0x06) {
+	if (cmd & 0x02) {
 		PRINT_ADDR(cp->cmd);
 		printk ("phase change %x-%x %d@%08x resid=%d.\n",
 			cmd&7, INB(nc_sbcl)&7, (unsigned)olen,
@@ -7685,8 +9441,8 @@
 	**
 	**	Look at the PM_SAVE SCRIPT if you want to understand 
 	**	this stuff. The equivalent code is implemented in 
-	**	SCRIPTS for the 896 that is able to handle PM from 
-	**	the SCRIPTS processor.
+	**	SCRIPTS for the 895A and 896 that are able to handle 
+	**	PM from the SCRIPTS processor.
 	*/
 
 	hflags0 = INB (HF_PRT);
@@ -7723,6 +9479,50 @@
 	pm->sg.size = cpu_to_scr(rest);
 	pm->ret     = cpu_to_scr(nxtdsp);
 
+	/*
+	**	If we have a SWIDE,
+	**	- prepare the address to write the SWIDE from SCRIPTS,
+	**	- compute the SCRIPTS address to restart from,
+	**	- move current data pointer context by one byte.
+	*/
+	nxtdsp = NCB_SCRIPT_PHYS (np, dispatch);
+	if ( ((cmd & 7) == 1  || (cmd & 7) == 5)  
+		&& cp && (cp->phys.select.sel_scntl3 & EWS) &&
+	    (INB (nc_scntl2) & WSR)) {
+		u32 tmp;
+
+#ifdef  SYM_DEBUG_PM_WITH_WSR
+		PRINT_ADDR(cp);
+		printf ("MA interrupt with WSR set - "
+			"pm->sg.addr=%x - pm->sg.size=%d\n",
+			pm->sg.addr, pm->sg.size);
+#endif
+		/*
+		 *  Set up the table indirect for the MOVE
+		 *  of the residual byte and adjust the data
+		 *  pointer context.
+		 */
+		tmp = scr_to_cpu(pm->sg.addr);
+		cp->phys.wresid.addr = cpu_to_scr(tmp);
+		pm->sg.addr = cpu_to_scr(tmp + 1);
+ 		tmp = scr_to_cpu(pm->sg.size);
+		cp->phys.wresid.size = cpu_to_scr((tmp&0xff000000) | 1);
+		pm->sg.size = cpu_to_scr(tmp - 1);
+
+		/*
+		 *  If only the residual byte is to be moved,
+		 *  no PM context is needed.
+		 */
+		if ((tmp&0xffffff) == 1)
+                        newcmd = pm->ret;
+
+		/*
+		 *  Prepare the address of SCRIPTS that will
+		 *  move the residual byte to memory.
+		 */
+		nxtdsp = NCB_SCRIPTH_PHYS (np, wsr_ma_helper);
+        }
+
 	if (DEBUG_FLAGS & DEBUG_PHASE) {
 		PRINT_ADDR(cp->cmd);
 		printk ("PM %x %x %x / %x %x %x.\n",
@@ -7733,12 +9533,11 @@
 	}
 
 	/*
-	**	fake the return address (to the patch).
-	**	and restart script processor at dispatcher.
+	**	Restart the SCRIPTS processor.
 	*/
 
 	OUTL (nc_temp, newcmd);
-	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, databreak));
+	OUTL_DSP (nxtdsp);
 	return;
 
 	/*
@@ -7798,7 +9597,8 @@
 				nxtdsp = NCB_SCRIPTH_PHYS (np, ident_break);
 		}
 		else if	(dsp == NCB_SCRIPTH_PHYS (np, send_wdtr) ||
-			 dsp == NCB_SCRIPTH_PHYS (np, send_sdtr)) {
+			 dsp == NCB_SCRIPTH_PHYS (np, send_sdtr) ||
+			 dsp == NCB_SCRIPTH_PHYS (np, send_ppr)) {
 			nxtdsp = NCB_SCRIPTH_PHYS (np, nego_bad_phase);
 		}
 		break;
@@ -7810,7 +9610,7 @@
 	}
 
 	if (nxtdsp) {
-		OUTL (nc_dsp, nxtdsp);
+		OUTL_DSP (nxtdsp);
 		return;
 	}
 
@@ -7842,6 +9642,10 @@
 **	requeue the CCB that failed in front of the LUN queue.
 **	I just hope this not to be performed too often. :)
 **
+**	If we are using IMMEDIATE ARBITRATION, we clear the 
+**	IARB hint for every commands we encounter in order not 
+**	to be stuck with a won arbitration and no job to queue 
+**	to a device.
 **----------------------------------------------------------
 */
 
@@ -7854,75 +9658,75 @@
 	int		busyccbs = 1;
 	u_int32		startp;
 	u_char		s_status = INB (SS_PRT);
+	int		msglen;
+	int		i, j;
+
 
 	/*
+	**	If the LCB is not yet available, then only 
+	**	1 IO is accepted, so we should have it.
+	*/
+	if (!lp)
+		goto next;	
+	/*
 	**	Remove all CCBs queued to the chip for that LUN and put 
 	**	them back in the LUN CCB wait queue.
 	*/
-	if (lp) {
-		int i = np->squeueput;
-		int j = (INL (nc_scratcha) - vtobus(np->squeue)) / 4;
-		int k = np->squeueput;
-
-		busyccbs = lp->queuedccbs;
-		while (1) {
-			if (i == j)
-				break;
-			if (i == 0)
-				i = MAX_START*2;
-			i = i - 2;
-			cp2 = ncr_ccb_from_dsa(np, scr_to_cpu(np->squeue[i]));
-			if (!cp2)
-				continue;
-			if (cp2->target != cp->target || cp2->lun != cp->lun)
-				continue;
+	busyccbs = lp->queuedccbs;
+	i = (INL (nc_scratcha) - np->p_squeue) / 4;
+	j = i;
+	while (i != np->squeueput) {
+		cp2 = ncr_ccb_from_dsa(np, scr_to_cpu(np->squeue[i]));
+		assert(cp2);
+#ifdef SCSI_NCR_IARB_SUPPORT
+		/* IARB hints may not be relevant any more. Forget them. */
+		cp2->host_flags &= ~HF_HINT_IARB;
+#endif
+		if (cp2 && cp2->target == cp->target && cp2->lun == cp->lun) {
 			xpt_remque(&cp2->link_ccbq);
 			xpt_insque_head(&cp2->link_ccbq, &lp->wait_ccbq);
 			--lp->queuedccbs;
 			cp2->queued = 0;
-			np->squeue[i] = DSA_INVALID;
-			k = i;
 		}
-
-		/*
-		**	Requeue the interrupted CCB in front of 
-		**	the LUN CCB wait queue.
-		*/
-		xpt_remque(&cp->link_ccbq);
-		xpt_insque_head(&cp->link_ccbq, &lp->wait_ccbq);
-		--lp->queuedccbs;
-		cp->queued = 0;
-
-		/*
-		**	Repair the startqueue if necessary.
-		*/
-		if (k != np->squeueput) {
-			j = k;
-			while (1) {
-				j += 2;
-				if (j >= MAX_START*2)
-					j = 0;
-				if (np->squeue[j] == DSA_INVALID)
-					continue;
-				np->squeue[k] = np->squeue[j];
-				if (j == np->squeueput)
-					break;
-				k += 2;
-				if (k >= MAX_START*2)
-					k = 0;
-			}
-			np->squeueput = k;
+		else {
+			if (i != j)
+				np->squeue[j] = np->squeue[i];
+			if ((j += 2) >= MAX_START*2) j = 0;
 		}
+		if ((i += 2) >= MAX_START*2) i = 0;
 	}
+	if (i != j)		/* Copy back the idle task if needed */
+		np->squeue[j] = np->squeue[i];
+	np->squeueput = j;	/* Update our current start queue pointer */
+
+	/*
+	**	Requeue the interrupted CCB in front of the 
+	**	LUN CCB wait queue to preserve ordering.
+	*/
+	xpt_remque(&cp->link_ccbq);
+	xpt_insque_head(&cp->link_ccbq, &lp->wait_ccbq);
+	--lp->queuedccbs;
+	cp->queued = 0;
+
+next:
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+	/* IARB hint may not be relevant any more. Forget it. */
+	cp->host_flags &= ~HF_HINT_IARB;
+	if (np->last_cp)
+		np->last_cp = 0;
+#endif
 
 	/*
 	**	Now we can restart the SCRIPTS processor safely.
 	*/
-	MEMORY_BARRIER();
-	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+	OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
 
 	switch(s_status) {
-	default:	/* Just for safety, should never happen */
+	default:
+	case S_BUSY:
+		ncr_complete(np, cp);
+		break;
 	case S_QUEUE_FULL:
 		if (!lp || !lp->queuedccbs) {
 			ncr_complete(np, cp);
@@ -7945,10 +9749,13 @@
 		/*
 		**	Repair the offending CCB.
 		*/
-		cp->phys.header.savep = cp->startp;
-		cp->host_status = HS_BUSY;
-		cp->scsi_status = S_ILLEGAL;
-		cp->host_flags	&= HF_PM_TO_C;
+		cp->phys.header.savep	= cp->startp;
+		cp->phys.header.lastp	= cp->lastp0;
+		cp->host_status 	= HS_BUSY;
+		cp->scsi_status 	= S_ILLEGAL;
+		cp->xerr_status		= 0;
+		cp->extra_bytes		= 0;
+		cp->host_flags		&= (HF_PM_TO_C|HF_DATA_IN);
 
 		break;
 
@@ -7957,12 +9764,20 @@
 		/*
 		**	If we were requesting sense, give up.
 		*/
-		if (cp->auto_sense) {
+		if (cp->host_flags & HF_AUTO_SENSE) {
 			ncr_complete(np, cp);
 			break;
 		}
 
 		/*
+		**	Save SCSI status and extended error.
+		**	Compute the data residual now.
+		*/
+		cp->sv_scsi_status = cp->scsi_status;
+		cp->sv_xerr_status = cp->xerr_status;
+		cp->resid = ncr_compute_residual(np, cp);
+
+		/*
 		**	Device returned CHECK CONDITION status.
 		**	Prepare all needed data strutures for getting 
 		**	sense data.
@@ -7972,8 +9787,73 @@
 		**	identify message
 		*/
 		cp->scsi_smsg2[0]	= M_IDENTIFY | cp->lun;
+		msglen = 1;
+
+		/*
+		**	If we are currently using anything different from 
+		**	async. 8 bit data transfers with that target,
+		**	start a negotiation, since the device may want 
+		**	to report us a UNIT ATTENTION condition due to 
+		**	a cause we currently ignore, and we donnot want 
+		**	to be stuck with WIDE and/or SYNC data transfer.
+		**
+		**	cp->nego_status is filled by ncr_prepare_nego().
+		**
+		**	Do NOT negotiate if performing integrity check
+		**	or if integrity check has completed, all check
+		**	conditions will have been cleared.
+		*/
+
+#ifdef	SCSI_NCR_INTEGRITY_CHECKING
+		if (DEBUG_FLAGS & DEBUG_IC) {
+		printk("%s: ncr_sir_to_redo: ic_done %2X, in_progress %2X\n",
+			ncr_name(np), tp->ic_done, cp->cmd->ic_in_progress);
+		}
+
+		/*
+		**	If parity error during integrity check,
+		**	set the target width to narrow. Otherwise,
+		**	do not negotiate on a request sense.
+		*/
+		if ( np->check_integ_par && np->check_integrity 
+						&& cp->cmd->ic_in_progress ) { 
+			cp->nego_status = 0;
+			msglen +=
+			    ncr_ic_nego (np, cp, cmd ,&cp->scsi_smsg2[msglen]);
+		}
+
+		if (!np->check_integrity || 
+		   	(np->check_integrity && 
+				(!cp->cmd->ic_in_progress && !tp->ic_done)) ) { 
+		    ncr_negotiate(np, tp);
+		    cp->nego_status = 0;
+		    {
+			u_char sync_offset;
+			if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+					(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66))
+				sync_offset = tp->sval & 0x3f;
+			else
+				sync_offset = tp->sval & 0x1f;
+
+		        if ((tp->wval & EWS) || sync_offset)
+			  msglen +=
+			    ncr_prepare_nego (np, cp, &cp->scsi_smsg2[msglen]);
+		    }
+
+		}
+#else
+		ncr_negotiate(np, tp);
+		cp->nego_status = 0;
+		if ((tp->wval & EWS) || (tp->sval & 0x1f))
+			msglen +=
+			    ncr_prepare_nego (np, cp, &cp->scsi_smsg2[msglen]);
+#endif	/* SCSI_NCR_INTEGRITY_CHECKING */
+
+		/*
+		**	Message table indirect structure.
+		*/
 		cp->phys.smsg.addr	= cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
-		cp->phys.smsg.size	= cpu_to_scr(1);
+		cp->phys.smsg.size	= cpu_to_scr(msglen);
 
 		/*
 		**	sense command
@@ -7986,43 +9866,34 @@
 		*/
 		cp->sensecmd[0]		= 0x03;
 		cp->sensecmd[1]		= cp->lun << 5;
-		cp->sensecmd[4]		= sizeof(cmd->sense_buffer);
+		cp->sensecmd[4]		= sizeof(cp->sense_buf);
 
 		/*
 		**	sense data
 		*/
-		cp->phys.sense.addr	=
-				cpu_to_scr(vtobus (&cmd->sense_buffer[0]));
-		cp->phys.sense.size	=
-				cpu_to_scr(sizeof(cmd->sense_buffer));
+		bzero(cp->sense_buf, sizeof(cp->sense_buf));
+		cp->phys.sense.addr	= cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+		cp->phys.sense.size	= cpu_to_scr(sizeof(cp->sense_buf));
 
 		/*
 		**	requeue the command.
 		*/
-		startp = cpu_to_scr(NCB_SCRIPTH_PHYS (np, sdata_in));
+		startp = NCB_SCRIPTH_PHYS (np, sdata_in);
 
-		cp->phys.header.savep	= startp;
-		cp->phys.header.goalp	= startp + 24;
-		cp->phys.header.lastp	= startp;
-		cp->phys.header.wgoalp	= startp + 24;
-		cp->phys.header.wlastp	= startp;
+		cp->phys.header.savep	= cpu_to_scr(startp);
+		cp->phys.header.goalp	= cpu_to_scr(startp + 16);
+		cp->phys.header.lastp	= cpu_to_scr(startp);
+		cp->phys.header.wgoalp	= cpu_to_scr(startp + 16);
+		cp->phys.header.wlastp	= cpu_to_scr(startp);
 
-		cp->host_status = HS_BUSY;
+		cp->host_status	= cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
 		cp->scsi_status = S_ILLEGAL;
-		cp->host_flags	= 0;
-		cp->auto_sense	= s_status;
+		cp->host_flags	= (HF_AUTO_SENSE|HF_DATA_IN);
 
 		cp->phys.header.go.start =
 			cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
 
 		/*
-		**	Select without ATN for quirky devices.
-		*/
-		if (tp->quirks & QUIRK_NOMSG)
-			cp->phys.header.go.start =
-			cpu_to_scr(NCB_SCRIPTH_PHYS (np, select_no_atn));
-
-		/*
 		**	If lp not yet allocated, requeue the command.
 		*/
 		if (!lp)
@@ -8039,111 +9910,784 @@
 	return;
 }
 
+/*----------------------------------------------------------
+**
+**	After a device has accepted some management message 
+**	as BUS DEVICE RESET, ABORT TASK, etc ..., or when 
+**	a device signals a UNIT ATTENTION condition, some 
+**	tasks are thrown away by the device. We are required 
+**	to reflect that on our tasks list since the device 
+**	will never complete these tasks.
+**
+**	This function completes all disconnected CCBs for a 
+**	given target that matches the following criteria:
+**	- lun=-1  means any logical UNIT otherwise a given one.
+**	- task=-1 means any task, otherwise a given one.
+**----------------------------------------------------------
+*/
+static int ncr_clear_tasks(ncb_p np, u_char hsts, 
+			   int target, int lun, int task)
+{
+	int i = 0;
+	ccb_p cp;
+
+	for (cp = np->ccbc; cp; cp = cp->link_ccb) {
+		if (cp->host_status != HS_DISCONNECT)
+			continue;
+		if (cp->target != target)
+			continue;
+		if (lun != -1 && cp->lun != lun)
+			continue;
+		if (task != -1 && cp->tag != NO_TAG && cp->scsi_smsg[2] != task)
+			continue;
+		cp->host_status = hsts;
+		cp->scsi_status = S_ILLEGAL;
+		ncr_complete(np, cp);
+		++i;
+	}
+	return i;
+}
 
 /*==========================================================
 **
+**	ncr chip handler for TASKS recovery.
 **
-**      ncr chip exception handler for programmed interrupts.
+**==========================================================
 **
+**	We cannot safely abort a command, while the SCRIPTS 
+**	processor is running, since we just would be in race 
+**	with it.
 **
-**==========================================================
+**	As long as we have tasks to abort, we keep the SEM 
+**	bit set in the ISTAT. When this bit is set, the 
+**	SCRIPTS processor interrupts (SIR_SCRIPT_STOPPED) 
+**	each time it enters the scheduler.
+**
+**	If we have to reset a target, clear tasks of a unit,
+**	or to perform the abort of a disconnected job, we 
+**	restart the SCRIPTS for selecting the target. Once 
+**	selected, the SCRIPTS interrupts (SIR_TARGET_SELECTED).
+**	If it loses arbitration, the SCRIPTS will interrupt again 
+**	the next time it will enter its scheduler, and so on ...
+**
+**	On SIR_TARGET_SELECTED, we scan for the more 
+**	appropriate thing to do:
+**
+**	- If nothing, we just sent a M_ABORT message to the 
+**	  target to get rid of the useless SCSI bus ownership.
+**	  According to the specs, no tasks shall be affected.
+**	- If the target is to be reset, we send it a M_RESET 
+**	  message.
+**	- If a logical UNIT is to be cleared , we send the 
+**	  IDENTIFY(lun) + M_ABORT.
+**	- If an untagged task is to be aborted, we send the 
+**	  IDENTIFY(lun) + M_ABORT.
+**	- If a tagged task is to be aborted, we send the 
+**	  IDENTIFY(lun) + task attributes + M_ABORT_TAG.
+**
+**	Once our 'kiss of death' :) message has been accepted 
+**	by the target, the SCRIPTS interrupts again 
+**	(SIR_ABORT_SENT). On this interrupt, we complete 
+**	all the CCBs that should have been aborted by the 
+**	target according to our message.
+**	
+**----------------------------------------------------------
 */
-
-static int ncr_show_msg (u_char * msg)
+static void ncr_sir_task_recovery(ncb_p np, int num)
 {
-	u_char i;
-	printk ("%x",*msg);
-	if (*msg==M_EXTENDED) {
-		for (i=1;i<8;i++) {
-			if (i-1>msg[1]) break;
-			printk ("-%x",msg[i]);
-		};
-		return (i+1);
-	} else if ((*msg & 0xf0) == 0x20) {
-		printk ("-%x",msg[1]);
-		return (2);
-	};
-	return (1);
-}
-
+	ccb_p cp;
+	tcb_p tp;
+	int target=-1, lun=-1, task;
+	int i, k;
+	u_char *p;
 
-void ncr_int_sir (ncb_p np)
-{
-	u_char scntl3;
-	u_char chg, ofs, per, fak, wide;
-	u_char num = INB (nc_dsps);
-	ccb_p	cp=0;
-	u_long	dsa    = INL (nc_dsa);
-	u_char	target = INB (nc_sdid) & 0x0f;
-	tcb_p	tp     = &np->target[target];
+	switch(num) {
+	/*
+	**	The SCRIPTS processor stopped before starting
+	**	the next command in order to allow us to perform 
+	**	some task recovery.
+	*/
+	case SIR_SCRIPT_STOPPED:
 
-	if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num);
+		/*
+		**	Do we have any target to reset or unit to clear ?
+		*/
+		for (i = 0 ; i < MAX_TARGET ; i++) {
+			tp = &np->target[i];
+			if (tp->to_reset || (tp->l0p && tp->l0p->to_clear)) {
+				target = i;
+				break;
+			}
+			if (!tp->lmp)
+				continue;
+			for (k = 1 ; k < MAX_LUN ; k++) {
+				if (tp->lmp[k] && tp->lmp[k]->to_clear) {
+					target	= i;
+					break;
+				}
+			}
+			if (target != -1)
+				break;
+		}
 
-	switch (num) {
-	case SIR_SEL_ATN_NO_MSG_OUT:
 		/*
-		**	The device didn't go to MSG OUT phase after having 
-		**	been selected with ATN. We donnot want to handle 
-		**	that.
+		**	If not, look at the CCB list for any 
+		**	disconnected CCB to be aborted.
 		*/
-		printk ("%s:%d: No MSG OUT phase after selection with ATN.\n",
-			ncr_name (np), target);
-		goto out_stuck;
-	case SIR_RESEL_NO_MSG_IN:
-	case SIR_RESEL_NO_IDENTIFY:
+		if (target == -1) {
+			for (cp = np->ccbc; cp; cp = cp->link_ccb) {
+				if (cp->host_status != HS_DISCONNECT)
+					continue;
+				if (cp->to_abort) {
+					target = cp->target;
+					break;
+				}
+			}
+		}
+
 		/*
-		**	If devices reselecting without sending an IDENTIFY 
-		**	message still exist, this should help.
-		**	We just assume lun=0, 1 CCB, no tag.
+		**	If some target is to be selected, 
+		**	prepare and start the selection.
 		*/
-		if (tp->l0p) { 
-			OUTL (nc_dsa, scr_to_cpu(tp->l0p->tasktbl[0]));
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, resel_go));
+		if (target != -1) {
+			tp = &np->target[target];
+			np->abrt_sel.sel_id	= target;
+			np->abrt_sel.sel_scntl3 = tp->wval;
+			np->abrt_sel.sel_sxfer  = tp->sval;
+			np->abrt_sel.sel_scntl4 = tp->uval;
+			OUTL(nc_dsa, np->p_ncb);
+			OUTL_DSP (NCB_SCRIPTH_PHYS (np, sel_for_abort));
 			return;
 		}
-	case SIR_RESEL_BAD_LUN:
-		np->msgout[0] = M_RESET;
-		goto out;
-	case SIR_RESEL_BAD_I_T_L:
-		np->msgout[0] = M_ABORT;
-		goto out;
-	case SIR_RESEL_BAD_I_T_L_Q:
-		np->msgout[0] = M_ABORT_TAG;
-		goto out;
-	case SIR_RESEL_ABORTED:
-		np->lastmsg = np->msgout[0];
-		np->msgout[0] = M_NOOP;
-		printk ("%s:%d: message %d sent on bad reselection.\n",
-			ncr_name (np), target, np->lastmsg);
-		goto out;
-	case SIR_MSG_OUT_DONE:
-		np->lastmsg = np->msgout[0];
-		np->msgout[0] = M_NOOP;
-		/* Should we really care of that */
-		if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR)
-			OUTOFFB (HF_PRT, HF_PAR_ERR);
-		goto out;
-	case SIR_BAD_STATUS:
-		cp = ncr_ccb_from_dsa(np, dsa);
-		if (!cp)
-			goto out;
-		ncr_sir_to_redo(np, num, cp);
-		return;
-	default:
+
 		/*
-		**	lookup the ccb
-		*/
-		cp = ncr_ccb_from_dsa(np, dsa);
-		if (!cp)
-			goto out;
-	}
+		**	Nothing is to be selected, so we donnot need 
+		**	to synchronize with the SCRIPTS anymore.
+		**	Remove the SEM flag from the ISTAT.
+		*/
+		np->istat_sem = 0;
+		OUTB (nc_istat, SIGP);
+
+		/*
+		**	Now look at CCBs to abort that haven't started yet.
+		**	Remove all those CCBs from the start queue and 
+		**	complete them with appropriate status.
+		**	Btw, the SCRIPTS processor is still stopped, so 
+		**	we are not in race.
+		*/
+		for (cp = np->ccbc; cp; cp = cp->link_ccb) {
+			if (cp->host_status != HS_BUSY &&
+			    cp->host_status != HS_NEGOTIATE)
+				continue;
+			if (!cp->to_abort)
+				continue;
+#ifdef SCSI_NCR_IARB_SUPPORT
+			/*
+			**    If we are using IMMEDIATE ARBITRATION, we donnot 
+			**    want to cancel the last queued CCB, since the 
+			**    SCRIPTS may have anticipated the selection.
+			*/
+			if (cp == np->last_cp) {
+				cp->to_abort = 0;
+				continue;
+			}
+#endif
+			/*
+			**	Compute index of next position in the start 
+			**	queue the SCRIPTS will schedule.
+			*/
+			i = (INL (nc_scratcha) - np->p_squeue) / 4;
 
-	switch (num) {
-/*-----------------------------------------------------------------------------
-**
-**	Was Sie schon immer ueber transfermode negotiation wissen wollten ...
-**
-**	We try to negotiate sync and wide transfer only after
+			/*
+			**	Remove the job from the start queue.
+			*/
+			k = -1;
+			while (1) {
+				if (i == np->squeueput)
+					break;
+				if (k == -1) {		/* Not found yet */
+					if (cp == ncr_ccb_from_dsa(np,
+						     scr_to_cpu(np->squeue[i])))
+						k = i;	/* Found */
+				}
+				else {
+					/*
+					**    Once found, we have to move 
+					**    back all jobs by 1 position.
+					*/
+					np->squeue[k] = np->squeue[i];
+					k += 2;
+					if (k >= MAX_START*2)
+						k = 0;
+				}
+
+				i += 2;
+				if (i >= MAX_START*2)
+					i = 0;
+			}
+			assert(k != -1);
+			if (k != 1) {
+				np->squeue[k] = np->squeue[i]; /* Idle task */
+				np->squeueput = k; /* Start queue pointer */
+				cp->host_status = HS_ABORTED;
+				cp->scsi_status = S_ILLEGAL;
+				ncr_complete(np, cp);
+			}
+		}
+		break;
+	/*
+	**	The SCRIPTS processor has selected a target 
+	**	we may have some manual recovery to perform for.
+	*/
+	case SIR_TARGET_SELECTED:
+		target = (INB (nc_sdid) & 0xf);
+		tp = &np->target[target];
+
+		np->abrt_tbl.addr = vtobus(np->abrt_msg);
+
+		/*
+		**	If the target is to be reset, prepare a 
+		**	M_RESET message and clear the to_reset flag 
+		**	since we donnot expect this operation to fail.
+		*/
+		if (tp->to_reset) {
+			np->abrt_msg[0] = M_RESET;
+			np->abrt_tbl.size = 1;
+			tp->to_reset = 0;
+			break;
+		}
+
+		/*
+		**	Otherwise, look for some logical unit to be cleared.
+		*/
+		if (tp->l0p && tp->l0p->to_clear)
+			lun = 0;
+		else if (tp->lmp) {
+			for (k = 1 ; k < MAX_LUN ; k++) {
+				if (tp->lmp[k] && tp->lmp[k]->to_clear) {
+					lun = k;
+					break;
+				}
+			}
+		}
+
+		/*
+		**	If a logical unit is to be cleared, prepare 
+		**	an IDENTIFY(lun) + ABORT MESSAGE.
+		*/
+		if (lun != -1) {
+			lcb_p lp = ncr_lp(np, tp, lun);
+			lp->to_clear = 0; /* We donnot expect to fail here */
+			np->abrt_msg[0] = M_IDENTIFY | lun;
+			np->abrt_msg[1] = M_ABORT;
+			np->abrt_tbl.size = 2;
+			break;
+		}
+
+		/*
+		**	Otherwise, look for some disconnected job to 
+		**	abort for this target.
+		*/
+		for (cp = np->ccbc; cp; cp = cp->link_ccb) {
+			if (cp->host_status != HS_DISCONNECT)
+				continue;
+			if (cp->target != target)
+				continue;
+			if (cp->to_abort)
+				break;
+		}
+
+		/*
+		**	If we have none, probably since the device has 
+		**	completed the command before we won abitration,
+		**	send a M_ABORT message without IDENTIFY.
+		**	According to the specs, the device must just 
+		**	disconnect the BUS and not abort any task.
+		*/
+		if (!cp) {
+			np->abrt_msg[0] = M_ABORT;
+			np->abrt_tbl.size = 1;
+			break;
+		}
+
+		/*
+		**	We have some task to abort.
+		**	Set the IDENTIFY(lun)
+		*/
+		np->abrt_msg[0] = M_IDENTIFY | cp->lun;
+
+		/*
+		**	If we want to abort an untagged command, we 
+		**	will send a IDENTIFY + M_ABORT.
+		**	Otherwise (tagged command), we will send 
+		**	a IDENTITFY + task attributes + ABORT TAG.
+		*/
+		if (cp->tag == NO_TAG) {
+			np->abrt_msg[1] = M_ABORT;
+			np->abrt_tbl.size = 2;
+		}
+		else {
+			np->abrt_msg[1] = cp->scsi_smsg[1];
+			np->abrt_msg[2] = cp->scsi_smsg[2];
+			np->abrt_msg[3] = M_ABORT_TAG;
+			np->abrt_tbl.size = 4;
+		}
+		cp->to_abort = 0; /* We donnot expect to fail here */
+		break;
+
+	/*
+	**	The target has accepted our message and switched 
+	**	to BUS FREE phase as we expected.
+	*/
+	case SIR_ABORT_SENT:
+		target = (INB (nc_sdid) & 0xf);
+		tp = &np->target[target];
+		
+		/*
+		**	If we didn't abort anything, leave here.
+		*/
+		if (np->abrt_msg[0] == M_ABORT)
+			break;
+
+		/*
+		**	If we sent a M_RESET, then a hardware reset has 
+		**	been performed by the target.
+		**	- Reset everything to async 8 bit
+		**	- Tell ourself to negotiate next time :-)
+		**	- Prepare to clear all disconnected CCBs for 
+		**	  this target from our task list (lun=task=-1)
+		*/
+		lun = -1;
+		task = -1;
+		if (np->abrt_msg[0] == M_RESET) {
+			tp->sval = 0;
+			tp->wval = np->rv_scntl3;
+			tp->uval = np->rv_scntl4; 
+			ncr_set_sync_wide_status(np, target);
+			ncr_negotiate(np, tp);
+		}
+
+		/*
+		**	Otherwise, check for the LUN and TASK(s) 
+		**	concerned by the cancelation.
+		**	If it is not ABORT_TAG then it is CLEAR_QUEUE 
+		**	or an ABORT message :-)
+		*/
+		else {
+			lun = np->abrt_msg[0] & 0x3f;
+			if (np->abrt_msg[1] == M_ABORT_TAG)
+				task = np->abrt_msg[2];
+		}
+
+		/*
+		**	Complete all the CCBs the device should have 
+		**	aborted due to our 'kiss of death' message.
+		*/
+		(void) ncr_clear_tasks(np, HS_ABORTED, target, lun, task);
+		break;
+
+	/*
+	**	We have performed a auto-sense that succeeded.
+	**	If the device reports a UNIT ATTENTION condition 
+	**	due to a RESET condition, we must complete all 
+	**	disconnect CCBs for this unit since the device 
+	**	shall have thrown them away.
+	**	Since I haven't time to guess what the specs are 
+	**	expecting for other UNIT ATTENTION conditions, I 
+	**	decided to only care about RESET conditions. :)
+	*/
+	case SIR_AUTO_SENSE_DONE:
+		cp = ncr_ccb_from_dsa(np, INL (nc_dsa));
+		if (!cp)
+			break;
+		memcpy(cp->cmd->sense_buffer, cp->sense_buf,
+		       sizeof(cp->cmd->sense_buffer));
+		p  = &cp->cmd->sense_buffer[0];
+
+		if (p[0] != 0x70 || p[2] != 0x6 || p[12] != 0x29)
+			break;
+#if 0
+		(void) ncr_clear_tasks(np, HS_RESET, cp->target, cp->lun, -1);
+#endif
+		break;
+	}
+
+	/*
+	**	Print to the log the message we intend to send.
+	*/
+	if (num == SIR_TARGET_SELECTED) {
+		PRINT_TARGET(np, target);
+		ncr_printl_hex("control msgout:", np->abrt_msg,
+			      np->abrt_tbl.size);
+		np->abrt_tbl.size = cpu_to_scr(np->abrt_tbl.size);
+	}
+
+	/*
+	**	Let the SCRIPTS processor continue.
+	*/
+	OUTONB_STD ();
+}
+
+
+/*==========================================================
+**
+**	Gérard's alchemy:) that deals with with the data 
+**	pointer for both MDP and the residual calculation.
+**
+**==========================================================
+**
+**	I didn't want to bloat the code by more than 200 
+**	lignes for the handling of both MDP and the residual.
+**	This has been achieved by using a data pointer 
+**	representation consisting in an index in the data 
+**	array (dp_sg) and a negative offset (dp_ofs) that 
+**	have the following meaning:
+**
+**	- dp_sg = MAX_SCATTER
+**	  we are at the end of the data script.
+**	- dp_sg < MAX_SCATTER
+**	  dp_sg points to the next entry of the scatter array 
+**	  we want to transfer.
+**	- dp_ofs < 0
+**	  dp_ofs represents the residual of bytes of the 
+**	  previous entry scatter entry we will send first.
+**	- dp_ofs = 0
+**	  no residual to send first.
+**
+**	The function ncr_evaluate_dp() accepts an arbitray 
+**	offset (basically from the MDP message) and returns 
+**	the corresponding values of dp_sg and dp_ofs.
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_evaluate_dp(ncb_p np, ccb_p cp, u_int32 scr, int *ofs)
+{
+	u_int32	dp_scr;
+	int	dp_ofs, dp_sg, dp_sgmin;
+	int	tmp;
+	struct pm_ctx *pm;
+
+	/*
+	**	Compute the resulted data pointer in term of a script 
+	**	address within some DATA script and a signed byte offset.
+	*/
+	dp_scr = scr;
+	dp_ofs = *ofs;
+	if	(dp_scr == NCB_SCRIPT_PHYS (np, pm0_data))
+		pm = &cp->phys.pm0;
+	else if (dp_scr == NCB_SCRIPT_PHYS (np, pm1_data))
+		pm = &cp->phys.pm1;
+	else
+		pm = 0;
+
+	if (pm) {
+		dp_scr  = scr_to_cpu(pm->ret);
+		dp_ofs -= scr_to_cpu(pm->sg.size);
+	}
+
+	/*
+	**	Deduce the index of the sg entry.
+	**	Keep track of the index of the first valid entry.
+	**	If result is dp_sg = MAX_SCATTER, then we are at the 
+	**	end of the data and vice-versa.
+	*/
+	tmp = scr_to_cpu(cp->phys.header.goalp);
+	dp_sg = MAX_SCATTER;
+	if (dp_scr != tmp)
+		dp_sg -= (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4);
+	dp_sgmin = MAX_SCATTER - cp->segments;
+
+	/*
+	**	Move to the sg entry the data pointer belongs to.
+	**
+	**	If we are inside the data area, we expect result to be:
+	**
+	**	Either,
+	**	    dp_ofs = 0 and dp_sg is the index of the sg entry
+	**	    the data pointer belongs to (or the end of the data)
+	**	Or,
+	**	    dp_ofs < 0 and dp_sg is the index of the sg entry 
+	**	    the data pointer belongs to + 1.
+	*/
+	if (dp_ofs < 0) {
+		int n;
+		while (dp_sg > dp_sgmin) {
+			--dp_sg;
+			tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+			n = dp_ofs + (tmp & 0xffffff);
+			if (n > 0) {
+				++dp_sg;
+				break;
+			}
+			dp_ofs = n;
+		}
+	}
+	else if (dp_ofs > 0) {
+		while (dp_sg < MAX_SCATTER) {
+			tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+			dp_ofs -= (tmp & 0xffffff);
+			++dp_sg;
+			if (dp_ofs <= 0)
+				break;
+		}
+	}
+
+	/*
+	**	Make sure the data pointer is inside the data area.
+	**	If not, return some error.
+	*/
+	if	(dp_sg < dp_sgmin || (dp_sg == dp_sgmin && dp_ofs < 0))
+		goto out_err;
+	else if	(dp_sg > MAX_SCATTER || (dp_sg == MAX_SCATTER && dp_ofs > 0))
+		goto out_err;
+
+	/*
+	**	Save the extreme pointer if needed.
+	*/
+	if (dp_sg > cp->ext_sg ||
+            (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) {
+		cp->ext_sg  = dp_sg;
+		cp->ext_ofs = dp_ofs;
+	}
+
+	/*
+	**	Return data.
+	*/
+	*ofs = dp_ofs;
+	return dp_sg;
+
+out_err:
+	return -1;
+}
+
+/*==========================================================
+**
+**	ncr chip handler for MODIFY DATA POINTER MESSAGE
+**
+**==========================================================
+**
+**	We also call this function on IGNORE WIDE RESIDUE 
+**	messages that do not match a SWIDE full condition.
+**	Btw, we assume in that situation that such a message 
+**	is equivalent to a MODIFY DATA POINTER (offset=-1).
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_modify_dp(ncb_p np, tcb_p tp, ccb_p cp, int ofs)
+{
+	int dp_ofs	= ofs;
+	u_int32 dp_scr	= INL (nc_temp);
+	u_int32	dp_ret;
+	u_int32	tmp;
+	u_char	hflags;
+	int	dp_sg;
+	struct pm_ctx *pm;
+
+	/*
+	**	Not supported for auto_sense;
+	*/
+	if (cp->host_flags & HF_AUTO_SENSE)
+		goto out_reject;
+
+	/*
+	**	Apply our alchemy:) (see comments in ncr_evaluate_dp()), 
+	**	to the resulted data pointer.
+	*/
+	dp_sg = ncr_evaluate_dp(np, cp, dp_scr, &dp_ofs);
+	if (dp_sg < 0)
+		goto out_reject;
+
+	/*
+	**	And our alchemy:) allows to easily calculate the data 
+	**	script address we want to return for the next data phase.
+	*/
+	dp_ret = cpu_to_scr(cp->phys.header.goalp);
+	dp_ret = dp_ret - 8 - (MAX_SCATTER - dp_sg) * (SCR_SG_SIZE*4);
+
+	/*
+	**	If offset / scatter entry is zero we donnot need 
+	**	a context for the new current data pointer.
+	*/
+	if (dp_ofs == 0) {
+		dp_scr = dp_ret;
+		goto out_ok;
+	}
+
+	/*
+	**	Get a context for the new current data pointer.
+	*/
+	hflags = INB (HF_PRT);
+
+	if (hflags & HF_DP_SAVED)
+		hflags ^= HF_ACT_PM;
+
+	if (!(hflags & HF_ACT_PM)) {
+		pm  = &cp->phys.pm0;
+		dp_scr = NCB_SCRIPT_PHYS (np, pm0_data);
+	}
+	else {
+		pm = &cp->phys.pm1;
+		dp_scr = NCB_SCRIPT_PHYS (np, pm1_data);
+	}
+
+	hflags &= ~(HF_DP_SAVED);
+
+	OUTB (HF_PRT, hflags);
+
+	/*
+	**	Set up the new current data pointer.
+	**	ofs < 0 there, and for the next data phase, we 
+	**	want to transfer part of the data of the sg entry 
+	**	corresponding to index dp_sg-1 prior to returning 
+	**	to the main data script.
+	*/
+	pm->ret = cpu_to_scr(dp_ret);
+	tmp  = scr_to_cpu(cp->phys.data[dp_sg-1].addr);
+	tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs;
+	pm->sg.addr = cpu_to_scr(tmp);
+	pm->sg.size = cpu_to_scr(-dp_ofs);
+
+out_ok:
+	OUTL (nc_temp, dp_scr);
+	OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
+	return;
+
+out_reject:
+	OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+}
+
+
+/*==========================================================
+**
+**	ncr chip calculation of the data residual.
+**
+**==========================================================
+**
+**	As I used to say, the requirement of data residual 
+**	in SCSI is broken, useless and cannot be achieved 
+**	without huge complexity.
+**	But most OSes and even the official CAM require it.
+**	When stupidity happens to be so widely spread inside 
+**	a community, it gets hard to convince.
+**
+**	Anyway, I don't care, since I am not going to use 
+**	any software that considers this data residual as 
+**	a relevant information. :)
+**	
+**----------------------------------------------------------
+*/
+
+static int ncr_compute_residual(ncb_p np, ccb_p cp)
+{
+	int dp_sg, dp_sgmin, tmp;
+	int resid=0;
+	int dp_ofs = 0;
+
+	/*
+	 *	Check for some data lost or just thrown away.
+	 *	We are not required to be quite accurate in this
+	 *	situation. Btw, if we are odd for output and the
+	 *	device claims some more data, it may well happen
+	 *	than our residual be zero. :-)
+	 */
+	if (cp->xerr_status & (XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN)) {
+		if (cp->xerr_status & XE_EXTRA_DATA)
+			resid -= cp->extra_bytes;
+		if (cp->xerr_status & XE_SODL_UNRUN)
+			++resid;
+		if (cp->xerr_status & XE_SWIDE_OVRUN)
+			--resid;
+	}
+
+
+	/*
+	**	If SCRIPTS reaches its goal point, then 
+	**	there is no additionnal residual.
+	*/
+	if (cp->phys.header.lastp == cp->phys.header.goalp)
+		return resid;
+
+	/*
+	**	If the last data pointer is data_io (direction 
+	**	unknown), then no data transfer should have 
+	**	taken place.
+	*/
+	if (cp->phys.header.lastp == NCB_SCRIPTH_PHYS (np, data_io))
+		return cp->data_len;
+
+	/*
+	**	If no data transfer occurs, or if the data
+	**	pointer is weird, return full residual.
+	*/
+	if (cp->startp == cp->phys.header.lastp ||
+	    ncr_evaluate_dp(np, cp, scr_to_cpu(cp->phys.header.lastp),
+			    &dp_ofs) < 0) {
+		return cp->data_len;
+	}
+
+	/*
+	**	We are now full comfortable in the computation 
+	**	of the data residual (2's complement).
+	*/
+	dp_sgmin = MAX_SCATTER - cp->segments;
+	resid = -cp->ext_ofs;
+	for (dp_sg = cp->ext_sg; dp_sg < MAX_SCATTER; ++dp_sg) {
+		tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+		resid += (tmp & 0xffffff);
+	}
+
+	/*
+	**	Hopefully, the result is not too wrong.
+	*/
+	return resid;
+}
+
+/*==========================================================
+**
+**	Print out the containt of a SCSI message.
+**
+**==========================================================
+*/
+
+static int ncr_show_msg (u_char * msg)
+{
+	u_char i;
+	printk ("%x",*msg);
+	if (*msg==M_EXTENDED) {
+		for (i=1;i<8;i++) {
+			if (i-1>msg[1]) break;
+			printk ("-%x",msg[i]);
+		};
+		return (i+1);
+	} else if ((*msg & 0xf0) == 0x20) {
+		printk ("-%x",msg[1]);
+		return (2);
+	};
+	return (1);
+}
+
+static void ncr_print_msg (ccb_p cp, char *label, u_char *msg)
+{
+	if (cp)
+		PRINT_ADDR(cp->cmd);
+	if (label)
+		printk ("%s: ", label);
+
+	(void) ncr_show_msg (msg);
+	printk (".\n");
+}
+
+/*===================================================================
+**
+**	Negotiation for WIDE and SYNCHRONOUS DATA TRANSFER.
+**
+**===================================================================
+**
+**	Was Sie schon immer ueber transfermode negotiation wissen wollten ...
+**
+**	We try to negotiate sync and wide transfer only after
 **	a successfull inquire command. We look at byte 7 of the
 **	inquire data to determine the capabilities of the target.
 **
@@ -8152,8 +10696,8 @@
 **	The host status field is set to HS_NEGOTIATE to mark this
 **	situation.
 **
-**	If the target doesn't answer this message immidiately
-**	(as required by the standard), the SIR_NEGO_FAIL interrupt
+**	If the target doesn't answer this message immediately
+**	(as required by the standard), the SIR_NEGO_FAILED interrupt
 **	will be raised eventually.
 **	The handler removes the HS_NEGOTIATE status, and sets the
 **	negotiated value to the default (async / nowide).
@@ -8171,375 +10715,811 @@
 **
 **	If the target doesn't fetch the answer (no message out phase),
 **	we assume the negotiation has failed, and fall back to default
-**	settings.
+**	settings (SIR_NEGO_PROTO interrupt).
 **
 **	When we set the values, we adjust them in all ccbs belonging 
 **	to this target, in the controller's register, and in the "phys"
 **	field of the controller's struct ncb.
 **
-**	Possible cases:		   hs  sir   msg_in value  send   goto
-**	We try to negotiate:
-**	-> target doesnt't msgin   NEG FAIL  noop   defa.  -      dispatch
-**	-> target rejected our msg NEG FAIL  reject defa.  -      dispatch
-**	-> target answered  (ok)   NEG SYNC  sdtr   set    -      clrack
-**	-> target answered (!ok)   NEG SYNC  sdtr   defa.  REJ--->msg_bad
-**	-> target answered  (ok)   NEG WIDE  wdtr   set    -      clrack
-**	-> target answered (!ok)   NEG WIDE  wdtr   defa.  REJ--->msg_bad
-**	-> any other msgin	   NEG FAIL  noop   defa.  -      dispatch
-**
-**	Target tries to negotiate:
-**	-> incoming message	   --- SYNC  sdtr   set    SDTR   -
-**	-> incoming message	   --- WIDE  wdtr   set    WDTR   -
-**      We sent our answer:
-**	-> target doesn't msgout   --- PROTO ?      defa.  -      dispatch
+**---------------------------------------------------------------------
+*/
+
+/*==========================================================
 **
-**-----------------------------------------------------------------------------
+**	ncr chip handler for SYNCHRONOUS DATA TRANSFER 
+**	REQUEST (SDTR) message.
+**
+**==========================================================
+**
+**	Read comments above.
+**
+**----------------------------------------------------------
 */
+static void ncr_sync_nego(ncb_p np, tcb_p tp, ccb_p cp)
+{
+	u_char	scntl3, scntl4;
+	u_char	chg, ofs, per, fak;
 
-	case SIR_NEGO_FAILED:
-		/*-------------------------------------------------------
-		**
-		**	Negotiation failed.
-		**	Target doesn't send an answer message,
-		**	or target rejected our message.
-		**
-		**      Remove negotiation request.
-		**
-		**-------------------------------------------------------
-		*/
-		OUTB (HS_PRT, HS_BUSY);
+	/*
+	**	Synchronous request message received.
+	*/
 
-		/* fall through */
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "sync msg in", np->msgin);
+	};
 
-	case SIR_NEGO_PROTO:
-		/*-------------------------------------------------------
-		**
-		**	Negotiation failed.
-		**	Target doesn't fetch the answer message.
-		**
-		**-------------------------------------------------------
-		*/
+	/*
+	**	get requested values.
+	*/
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("negotiation failed sir=%x status=%x.\n",
-				num, cp->nego_status);
-		};
+	chg = 0;
+	per = np->msgin[3];
+	ofs = np->msgin[4];
+	if (ofs==0) per=255;
 
-		/*
-		**	any error in negotiation:
-		**	fall back to default mode.
-		*/
-		switch (cp->nego_status) {
+	/*
+	**      if target sends SDTR message,
+	**	      it CAN transfer synch.
+	*/
+
+	if (ofs)
+		tp->inq_byte7 |= INQ7_SYNC;
+
+	/*
+	**	check values against driver limits.
+	*/
+
+	if (per < np->minsync)
+		{chg = 1; per = np->minsync;}
+	if (per < tp->minsync)
+		{chg = 1; per = tp->minsync;}
+	if (ofs > tp->maxoffs)
+		{chg = 1; ofs = tp->maxoffs;}
+
+	/*
+	**	Check against controller limits.
+	*/
+	fak	= 7;
+	scntl3	= 0;
+	scntl4  = 0;
+	if (ofs != 0) {
+		ncr_getsync(np, per, &fak, &scntl3);
+		if (fak > 7) {
+			chg = 1;
+			ofs = 0;
+		}
+	}
+	if (ofs == 0) {
+		fak	= 7;
+		per	= 0;
+		scntl3	= 0;
+		scntl4  = 0;
+		tp->minsync = 0;
+	}
+
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		PRINT_ADDR(cp->cmd);
+		printk ("sync: per=%d scntl3=0x%x scntl4=0x%x ofs=%d fak=%d chg=%d.\n",
+			per, scntl3, scntl4, ofs, fak, chg);
+	}
 
+	if (INB (HS_PRT) == HS_NEGOTIATE) {
+		OUTB (HS_PRT, HS_BUSY);
+		switch (cp->nego_status) {
 		case NS_SYNC:
-			ncr_setsync (np, cp, 0, 0xe0);
-			break;
+			/*
+			**      This was an answer message
+			*/
+			if (chg) {
+				/*
+				**	Answer wasn't acceptable.
+				*/
+				ncr_setsync (np, cp, 0, 0xe0, 0);
+				OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+			} else {
+				/*
+				**	Answer is ok.
+				*/
+				if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+					(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+				  ncr_setsync (np, cp, scntl3, (fak<<5)|ofs,0);
+				else
+				  ncr_setsync (np, cp, scntl3, ofs, scntl4);
+
+				OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
+			};
+			return;
 
 		case NS_WIDE:
 			ncr_setwide (np, cp, 0, 0);
 			break;
-
 		};
-		np->msgin [0] = M_NOOP;
-		np->msgout[0] = M_NOOP;
-		cp->nego_status = 0;
-		break;
+	};
 
-	case SIR_NEGO_SYNC:
-		/*
-		**	Synchronous request message received.
-		*/
+	/*
+	**	It was a request. Set value and
+	**      prepare an answer message
+	*/
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync msgin: ");
-			(void) ncr_show_msg (np->msgin);
-			printk (".\n");
-		};
+	if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+		ncr_setsync (np, cp, scntl3, (fak<<5)|ofs,0);
+	else
+		ncr_setsync (np, cp, scntl3, ofs, scntl4);
 
-		/*
-		**	get requested values.
-		*/
+	np->msgout[0] = M_EXTENDED;
+	np->msgout[1] = 3;
+	np->msgout[2] = M_X_SYNC_REQ;
+	np->msgout[3] = per;
+	np->msgout[4] = ofs;
 
-		chg = 0;
-		per = np->msgin[3];
-		ofs = np->msgin[4];
-		if (ofs==0) per=255;
+	cp->nego_status = NS_SYNC;
 
-		/*
-		**      if target sends SDTR message,
-		**	      it CAN transfer synch.
-		*/
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "sync msgout", np->msgout);
+	}
 
-		if (ofs)
-			tp->inq_byte7 |= INQ7_SYNC;
+	np->msgin [0] = M_NOOP;
 
-		/*
-		**	check values against driver limits.
-		*/
+	if (!ofs)
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+	else
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, sdtr_resp));
+}
 
-		if (per < np->minsync)
-			{chg = 1; per = np->minsync;}
-		if (per < tp->minsync)
-			{chg = 1; per = tp->minsync;}
-		if (ofs > tp->maxoffs)
-			{chg = 1; ofs = tp->maxoffs;}
+/*==========================================================
+**
+**	ncr chip handler for WIDE DATA TRANSFER REQUEST 
+**	(WDTR) message.
+**
+**==========================================================
+**
+**	Read comments above.
+**
+**----------------------------------------------------------
+*/
+static void ncr_wide_nego(ncb_p np, tcb_p tp, ccb_p cp)
+{
+	u_char	chg, wide;
 
-		/*
-		**	Check against controller limits.
-		*/
-		fak	= 7;
-		scntl3	= 0;
-		if (ofs != 0) {
-			ncr_getsync(np, per, &fak, &scntl3);
-			if (fak > 7) {
-				chg = 1;
-				ofs = 0;
-			}
-		}
-		if (ofs == 0) {
-			fak	= 7;
-			per	= 0;
-			scntl3	= 0;
-			tp->minsync = 0;
-		}
+	/*
+	**	Wide request message received.
+	*/
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "wide msgin", np->msgin);
+	};
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
-				per, scntl3, ofs, fak, chg);
-		}
+	/*
+	**	get requested values.
+	*/
 
-		if (INB (HS_PRT) == HS_NEGOTIATE) {
-			OUTB (HS_PRT, HS_BUSY);
-			switch (cp->nego_status) {
+	chg  = 0;
+	wide = np->msgin[3];
 
-			case NS_SYNC:
+	/*
+	**      if target sends WDTR message,
+	**	      it CAN transfer wide.
+	*/
+
+	if (wide)
+		tp->inq_byte7 |= INQ7_WIDE16;
+
+	/*
+	**	check values against driver limits.
+	*/
+
+	if (wide > tp->usrwide)
+		{chg = 1; wide = tp->usrwide;}
+
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		PRINT_ADDR(cp->cmd);
+		printk ("wide: wide=%d chg=%d.\n", wide, chg);
+	}
+
+	if (INB (HS_PRT) == HS_NEGOTIATE) {
+		OUTB (HS_PRT, HS_BUSY);
+		switch (cp->nego_status) {
+		case NS_WIDE:
+			/*
+			**      This was an answer message
+			*/
+			if (chg) {
 				/*
-				**      This was an answer message
+				**	Answer wasn't acceptable.
 				*/
-				if (chg) {
-					/*
-					**	Answer wasn't acceptable.
-					*/
-					ncr_setsync (np, cp, 0, 0xe0);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
-				} else {
-					/*
-					**	Answer is ok.
-					*/
-					ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
-				};
-				return;
-
-			case NS_WIDE:
-				ncr_setwide (np, cp, 0, 0);
-				break;
+				ncr_setwide (np, cp, 0, 1);
+				OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+			} else {
+				/*
+				**	Answer is ok.
+				*/
+				ncr_setwide (np, cp, wide, 1);
+				OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
 			};
+			return;
+
+		case NS_SYNC:
+			ncr_setsync (np, cp, 0, 0xe0, 0);
+			break;
 		};
+	};
 
-		/*
-		**	It was a request. Set value and
-		**      prepare an answer message
-		*/
+	/*
+	**	It was a request, set value and
+	**      prepare an answer message
+	*/
+
+	ncr_setwide (np, cp, wide, 1);
+
+	np->msgout[0] = M_EXTENDED;
+	np->msgout[1] = 2;
+	np->msgout[2] = M_X_WIDE_REQ;
+	np->msgout[3] = wide;
+
+	np->msgin [0] = M_NOOP;
+
+	cp->nego_status = NS_WIDE;
+
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "wide msgout", np->msgout);
+	}
+
+	OUTL_DSP (NCB_SCRIPTH_PHYS (np, wdtr_resp));
+}
+/*==========================================================
+**
+**	ncr chip handler for PARALLEL PROTOCOL REQUEST 
+**	(PPR) message.
+**
+**==========================================================
+**
+**	Read comments above.
+**
+**----------------------------------------------------------
+*/
+static void ncr_ppr_nego(ncb_p np, tcb_p tp, ccb_p cp)
+{
+	u_char	scntl3, scntl4;
+	u_char	chg, ofs, per, fak, wth, dt;
+
+	/*
+	**	PPR message received.
+	*/
+
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "ppr msg in", np->msgin);
+	};
+
+	/*
+	**	get requested values.
+	*/
+
+	chg = 0;
+	per = np->msgin[3];
+	ofs = np->msgin[5];
+	wth = np->msgin[6];
+	dt  = np->msgin[7];
+	if (ofs==0) per=255;
+
+	/*
+	**      if target sends sync (wide),
+	**	      it CAN transfer synch (wide).
+	*/
+
+	if (ofs)
+		tp->inq_byte7 |= INQ7_SYNC;
+
+	if (wth)
+		tp->inq_byte7 |= INQ7_WIDE16;
+
+	/*
+	**	check values against driver limits.
+	*/
+
+	if (wth > tp->usrwide)
+		{chg = 1; wth = tp->usrwide;}
+	if (per < np->minsync)
+		{chg = 1; per = np->minsync;}
+	if (per < tp->minsync)
+		{chg = 1; per = tp->minsync;}
+	if (ofs > tp->maxoffs)
+		{chg = 1; ofs = tp->maxoffs;}
+
+	/*
+	**	Check against controller limits.
+	*/
+	fak	= 7;
+	scntl3	= 0;
+	scntl4  = 0;
+	if (ofs != 0) {
+		scntl4 = dt ? 0x80 : 0;
+		ncr_getsync(np, per, &fak, &scntl3);
+		if (fak > 7) {
+			chg = 1;
+			ofs = 0;
+		}
+	}
+	if (ofs == 0) {
+		fak	= 7;
+		per	= 0;
+		scntl3	= 0;
+		scntl4  = 0;
+		tp->minsync = 0;
+	}
+
+	/*
+	**	If target responds with Ultra 3 speed
+	**	but narrow or not DT, reject.
+	**	If target responds with DT request 
+	**	but not Ultra3 speeds, reject message,
+	**	reset min sync for target to 0x0A and
+	**	set flags to re-negotiate.
+	*/
 
-		ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
+	if   ((per == 0x09) && ofs && (!wth || !dt))  
+		chg = 1;
+	else if (( (per > 0x09) && dt) ) 
+		chg = 2;
 
-		np->msgout[0] = M_EXTENDED;
-		np->msgout[1] = 3;
-		np->msgout[2] = M_X_SYNC_REQ;
-		np->msgout[3] = per;
-		np->msgout[4] = ofs;
 
-		cp->nego_status = NS_SYNC;
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		PRINT_ADDR(cp->cmd);
+		printk ("ppr: wth=%d per=%d scntl3=0x%x scntl4=0x%x ofs=%d fak=%d chg=%d.\n",
+			wth, per, scntl3, scntl4, ofs, fak, chg);
+	}
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync msgout: ");
-			(void) ncr_show_msg (np->msgout);
-			printk (".\n");
-		}
+	if (INB (HS_PRT) == HS_NEGOTIATE) {
+		OUTB (HS_PRT, HS_BUSY);
+		switch (cp->nego_status) {
+		case NS_PPR:
+			/*
+			**      This was an answer message
+			*/
+			if (chg) {
+				/*
+				**	Answer wasn't acceptable.
+				*/
+				if (chg == 2) {
+					/* Send message reject and reset flags for
+					** host to re-negotiate with min period 0x0A.
+					*/
+					tp->minsync = 0x0A;
+					tp->period = 0;
+					tp->widedone = 0;
+				}
+				ncr_setsyncwide (np, cp, 0, 0xe0, 0, 0);
+				OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+			} else {
+				/*
+				**	Answer is ok.
+				*/
+
+				if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+					(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+				  ncr_setsyncwide (np, cp, scntl3, (fak<<5)|ofs,0, wth);
+				else
+				  ncr_setsyncwide (np, cp, scntl3, ofs, scntl4, wth);
 
-		if (!ofs) {
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+				OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
+				
+			};
 			return;
-		}
-		np->msgin [0] = M_NOOP;
 
-		break;
+		case NS_SYNC:
+			ncr_setsync (np, cp, 0, 0xe0, 0);
+			break;
 
-	case SIR_NEGO_WIDE:
-		/*
-		**	Wide request message received.
-		*/
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide msgin: ");
-			(void) ncr_show_msg (np->msgin);
-			printk (".\n");
+		case NS_WIDE:
+			ncr_setwide (np, cp, 0, 0);
+			break;
 		};
+	};
 
-		/*
-		**	get requested values.
-		*/
+	/*
+	**	It was a request. Set value and
+	**      prepare an answer message
+	**
+	**	If narrow or not DT and requesting Ultra3
+	**	slow the bus down and force ST. If not
+	**	requesting Ultra3, force ST.
+	**	Max offset is 31=0x1f if ST mode.
+	*/
 
-		chg  = 0;
-		wide = np->msgin[3];
+	if  ((per == 0x09) && ofs && (!wth || !dt)) {
+		per = 0x0A;
+		dt = 0;
+                ofs &= 0x1f;
+	}
+	else if ( (per > 0x09) && dt) {
+		dt = 0;
+                ofs &= 0x1f;
+	}
 
-		/*
-		**      if target sends WDTR message,
-		**	      it CAN transfer wide.
-		*/
+	if ((np->device_id != PCI_DEVICE_ID_LSI_53C1010) &&
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66))
+		ncr_setsyncwide (np, cp, scntl3, (fak<<5)|ofs,0, wth);
+	else
+		ncr_setsyncwide (np, cp, scntl3, ofs, scntl4, wth);
 
-		if (wide)
-			tp->inq_byte7 |= INQ7_WIDE16;
+	np->msgout[0] = M_EXTENDED;
+	np->msgout[1] = 6;
+	np->msgout[2] = M_X_PPR_REQ;
+	np->msgout[3] = per;
+	np->msgout[4] = 0;		
+	np->msgout[5] = ofs;
+	np->msgout[6] = wth;
+	np->msgout[7] = dt;
 
-		/*
-		**	check values against driver limits.
-		*/
+	cp->nego_status = NS_PPR;
 
-		if (wide > tp->usrwide)
-			{chg = 1; wide = tp->usrwide;}
+	if (DEBUG_FLAGS & DEBUG_NEGO) {
+		ncr_print_msg(cp, "ppr msgout", np->msgout);
+	}
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide: wide=%d chg=%d.\n", wide, chg);
-		}
+	np->msgin [0] = M_NOOP;
 
-		if (INB (HS_PRT) == HS_NEGOTIATE) {
-			OUTB (HS_PRT, HS_BUSY);
-			switch (cp->nego_status) {
+	if (!ofs)
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+	else
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, ppr_resp));
+}
 
-			case NS_WIDE:
-				/*
-				**      This was an answer message
-				*/
-				if (chg) {
-					/*
-					**	Answer wasn't acceptable.
-					*/
-					ncr_setwide (np, cp, 0, 1);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
-				} else {
-					/*
-					**	Answer is ok.
-					*/
-					ncr_setwide (np, cp, wide, 1);
-					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
-				};
-				return;
 
-			case NS_SYNC:
-				ncr_setsync (np, cp, 0, 0xe0);
-				break;
-			};
-		};
 
-		/*
-		**	It was a request, set value and
-		**      prepare an answer message
-		*/
+/*
+**	Reset SYNC or WIDE to default settings.
+**	Called when a negotiation does not succeed either 
+**	on rejection or on protocol error.
+*/
+static void ncr_nego_default(ncb_p np, tcb_p tp, ccb_p cp)
+{
+	/*
+	**	any error in negotiation:
+	**	fall back to default mode.
+	*/
+	switch (cp->nego_status) {
 
-		ncr_setwide (np, cp, wide, 1);
+	case NS_SYNC:
+		ncr_setsync (np, cp, 0, 0xe0, 0);
+		break;
 
-		np->msgout[0] = M_EXTENDED;
-		np->msgout[1] = 2;
-		np->msgout[2] = M_X_WIDE_REQ;
-		np->msgout[3] = wide;
+	case NS_WIDE:
+		ncr_setwide (np, cp, 0, 0);
+		break;
 
-		np->msgin [0] = M_NOOP;
+	case NS_PPR:
+		/*
+		 * ppr_negotiation is set to 1 on the first ppr nego command.
+		 * If ppr is successful, it is reset to 2.
+		 * If unsuccessful it is reset to 0.
+		 */
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			tcb_p tp=&np->target[cp->target];
+			u_char factor, offset, width;
 
-		cp->nego_status = NS_WIDE;
+			ncr_get_xfer_info ( np, tp, &factor, &offset, &width);
 
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide msgout: ");
-			(void) ncr_show_msg (np->msgin);
-			printk (".\n");
+			printk("Current factor %d offset %d width %d\n",
+				factor, offset, width);	
+		}
+		if (tp->ppr_negotiation == 2)
+			ncr_setsyncwide (np, cp, 0, 0xe0, 0, 0);
+		else if (tp->ppr_negotiation == 1) {
+
+			/* First ppr command has received a  M REJECT.
+			 * Do not change the existing wide/sync parameter
+			 * values (asyn/narrow if this as the first nego;
+			 * may be different if target initiates nego.).
+			 */
+			tp->ppr_negotiation = 0;
+		}
+		else
+		{
+			tp->ppr_negotiation = 0;
+			ncr_setwide (np, cp, 0, 0);
 		}
 		break;
+	};
+	np->msgin [0] = M_NOOP;
+	np->msgout[0] = M_NOOP;
+	cp->nego_status = 0;
+}
 
-/*--------------------------------------------------------------------
+/*==========================================================
+**
+**	ncr chip handler for MESSAGE REJECT received for 
+**	a WIDE or SYNCHRONOUS negotiation.
+**
+**	clear the PPR negotiation flag, all future nego.
+**	will be SDTR and WDTR
 **
-**	Processing of special messages
+**==========================================================
+**
+**	Read comments above.
 **
-**--------------------------------------------------------------------
+**----------------------------------------------------------
 */
+static void ncr_nego_rejected(ncb_p np, tcb_p tp, ccb_p cp)
+{
+	ncr_nego_default(np, tp, cp);
+	OUTB (HS_PRT, HS_BUSY);
+}
 
-	case SIR_REJECT_RECEIVED:
-		/*-----------------------------------------------
-		**
-		**	We received a M_REJECT message.
-		**
-		**-----------------------------------------------
-		*/
-
-		PRINT_ADDR(cp->cmd);
-		printk ("M_REJECT received (%x:%x).\n",
-			(unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
-		break;
-
-	case SIR_REJECT_TO_SEND:
-		/*-----------------------------------------------
-		**
-		**	We received an unknown message
-		**
-		**-----------------------------------------------
-		*/
-
-		PRINT_ADDR(cp->cmd);
-		printk ("M_REJECT to send for ");
-		(void) ncr_show_msg (np->msgin);
-		printk (".\n");
-		np->msgout[0] = M_REJECT;
-		break;
 
-/*--------------------------------------------------------------------
+/*==========================================================
+**
+**
+**      ncr chip exception handler for programmed interrupts.
 **
-**	Processing of special messages
 **
-**--------------------------------------------------------------------
+**==========================================================
 */
 
-	case SIR_IGN_RESIDUE:
-		/*-----------------------------------------------
-		**
-		**	We received an IGNORE RESIDUE message,
-		**	which couldn't be handled by the script.
-		**
-		**-----------------------------------------------
-		*/
+void ncr_int_sir (ncb_p np)
+{
+	u_char	num	= INB (nc_dsps);
+	u_long	dsa	= INL (nc_dsa);
+	ccb_p	cp	= ncr_ccb_from_dsa(np, dsa);
+	u_char	target	= INB (nc_sdid) & 0x0f;
+	tcb_p	tp	= &np->target[target];
+	int	tmp;
 
-		PRINT_ADDR(cp->cmd);
-		printk ("M_IGN_RESIDUE received, but not yet implemented.\n");
-		break;
-#if 0
-	case SIR_MISSING_SAVE:
-		/*-----------------------------------------------
-		**
-		**	We received an DISCONNECT message,
-		**	but the datapointer wasn't saved before.
-		**
-		**-----------------------------------------------
-		*/
+	if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num);
 
-		PRINT_ADDR(cp->cmd);
-		printk ("M_DISCONNECT received, but datapointer not saved: "
-			"data=%x save=%x goal=%x.\n",
-			(unsigned) INL (nc_temp),
-			(unsigned) scr_to_cpu(np->header.savep),
-			(unsigned) scr_to_cpu(np->header.goalp));
-		break;
+	switch (num) {
+	/*
+	**	See comments in the SCRIPTS code.
+	*/
+#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR
+	case SIR_DUMMY_INTERRUPT:
+		goto out;
 #endif
+
+	/*
+	**	The C code is currently trying to recover from something.
+	**	Typically, user want to abort some command.
+	*/
+	case SIR_SCRIPT_STOPPED:
+	case SIR_TARGET_SELECTED:
+	case SIR_ABORT_SENT:
+	case SIR_AUTO_SENSE_DONE:
+		ncr_sir_task_recovery(np, num);
+		return;
+	/*
+	**	The device didn't go to MSG OUT phase after having 
+	**	been selected with ATN. We donnot want to handle 
+	**	that.
+	*/
+	case SIR_SEL_ATN_NO_MSG_OUT:
+		printk ("%s:%d: No MSG OUT phase after selection with ATN.\n",
+			ncr_name (np), target);
+		goto out_stuck;
+	/*
+	**	The device didn't switch to MSG IN phase after 
+	**	having reseleted the initiator.
+	*/
+	case SIR_RESEL_NO_MSG_IN:
+	/*
+	**	After reselection, the device sent a message that wasn't 
+	**	an IDENTIFY.
+	*/
+	case SIR_RESEL_NO_IDENTIFY:
+		/*
+		**	If devices reselecting without sending an IDENTIFY 
+		**	message still exist, this should help.
+		**	We just assume lun=0, 1 CCB, no tag.
+		*/
+		if (tp->l0p) { 
+			OUTL (nc_dsa, scr_to_cpu(tp->l0p->tasktbl[0]));
+			OUTL_DSP (NCB_SCRIPT_PHYS (np, resel_go));
+			return;
+		}
+	/*
+	**	The device reselected a LUN we donnot know of.
+	*/
+	case SIR_RESEL_BAD_LUN:
+		np->msgout[0] = M_RESET;
+		goto out;
+	/*
+	**	The device reselected for an untagged nexus and we 
+	**	haven't any.
+	*/
+	case SIR_RESEL_BAD_I_T_L:
+		np->msgout[0] = M_ABORT;
+		goto out;
+	/*
+	**	The device reselected for a tagged nexus that we donnot 
+	**	have.
+	*/
+	case SIR_RESEL_BAD_I_T_L_Q:
+		np->msgout[0] = M_ABORT_TAG;
+		goto out;
+	/*
+	**	The SCRIPTS let us know that the device has grabbed 
+	**	our message and will abort the job.
+	*/
+	case SIR_RESEL_ABORTED:
+		np->lastmsg = np->msgout[0];
+		np->msgout[0] = M_NOOP;
+		printk ("%s:%d: message %x sent on bad reselection.\n",
+			ncr_name (np), target, np->lastmsg);
+		goto out;
+	/*
+	**	The SCRIPTS let us know that a message has been 
+	**	successfully sent to the device.
+	*/
+	case SIR_MSG_OUT_DONE:
+		np->lastmsg = np->msgout[0];
+		np->msgout[0] = M_NOOP;
+		/* Should we really care of that */
+		if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) {
+			if (cp) {
+				cp->xerr_status &= ~XE_PARITY_ERR;
+                                if (!cp->xerr_status)
+					OUTOFFB (HF_PRT, HF_EXT_ERR);
+			}
+		}
+		goto out;
+	/*
+	**	The device didn't send a GOOD SCSI status.
+	**	We may have some work to do prior to allow 
+	**	the SCRIPTS processor to continue.
+	*/
+	case SIR_BAD_STATUS:
+		if (!cp)
+			goto out;
+		ncr_sir_to_redo(np, num, cp);
+		return;
+	/*
+	**	We are asked by the SCRIPTS to prepare a 
+	**	REJECT message.
+	*/
+	case SIR_REJECT_TO_SEND:
+		ncr_print_msg(cp, "M_REJECT to send for ", np->msgin);
+		np->msgout[0] = M_REJECT;
+		goto out;
+	/*
+	**	We have been ODD at the end of a DATA IN 
+	**	transfer and the device didn't send a 
+	**	IGNORE WIDE RESIDUE message.
+	**	It is a data overrun condition.
+	*/
+	case SIR_SWIDE_OVERRUN:
+                if (cp) {
+                        OUTONB (HF_PRT, HF_EXT_ERR);
+                        cp->xerr_status |= XE_SWIDE_OVRUN;
+                }
+		goto out;
+	/*
+	**	We have been ODD at the end of a DATA OUT 
+	**	transfer.
+	**	It is a data underrun condition.
+	*/
+	case SIR_SODL_UNDERRUN:
+                if (cp) {
+                        OUTONB (HF_PRT, HF_EXT_ERR);
+                        cp->xerr_status |= XE_SODL_UNRUN;
+                }
+		goto out;
+	/*
+	**	The device wants us to tranfer more data than 
+	**	expected or in the wrong direction.
+	**	The number of extra bytes is in scratcha.
+	**	It is a data overrun condition.
+	*/
+	case SIR_DATA_OVERRUN:
+		if (cp) {
+			OUTONB (HF_PRT, HF_EXT_ERR);
+			cp->xerr_status |= XE_EXTRA_DATA;
+			cp->extra_bytes += INL (nc_scratcha);
+		}
+		goto out;
+	/*
+	**	The device switched to an illegal phase (4/5).
+	*/
+	case SIR_BAD_PHASE:
+		if (cp) {
+			OUTONB (HF_PRT, HF_EXT_ERR);
+			cp->xerr_status |= XE_BAD_PHASE;
+		}
+		goto out;
+	/*
+	**	We received a message.
+	*/
+	case SIR_MSG_RECEIVED:
+		if (!cp)
+			goto out_stuck;
+		switch (np->msgin [0]) {
+		/*
+		**	We received an extended message.
+		**	We handle MODIFY DATA POINTER, SDTR, WDTR 
+		**	and reject all other extended messages.
+		*/
+		case M_EXTENDED:
+			switch (np->msgin [2]) {
+			case M_X_MODIFY_DP:
+				if (DEBUG_FLAGS & DEBUG_POINTER)
+					ncr_print_msg(cp,"modify DP",np->msgin);
+				tmp = (np->msgin[3]<<24) + (np->msgin[4]<<16) + 
+				      (np->msgin[5]<<8)  + (np->msgin[6]);
+				ncr_modify_dp(np, tp, cp, tmp);
+				return;
+			case M_X_SYNC_REQ:
+				ncr_sync_nego(np, tp, cp);
+				return;
+			case M_X_WIDE_REQ:
+				ncr_wide_nego(np, tp, cp);
+				return;
+			case M_X_PPR_REQ:
+				ncr_ppr_nego(np, tp, cp);
+				return;
+			default:
+				goto out_reject;
+			}
+			break;
+		/*
+		**	We received a 1/2 byte message not handled from SCRIPTS.
+		**	We are only expecting MESSAGE REJECT and IGNORE WIDE 
+		**	RESIDUE messages that haven't been anticipated by 
+		**	SCRIPTS on SWIDE full condition. Unanticipated IGNORE 
+		**	WIDE RESIDUE messages are aliased as MODIFY DP (-1).
+		*/
+		case M_IGN_RESIDUE:
+			if (DEBUG_FLAGS & DEBUG_POINTER)
+				ncr_print_msg(cp,"ign wide residue", np->msgin);
+			ncr_modify_dp(np, tp, cp, -1);
+			return;
+		case M_REJECT:
+			if (INB (HS_PRT) == HS_NEGOTIATE)
+				ncr_nego_rejected(np, tp, cp);
+			else {
+				PRINT_ADDR(cp->cmd);
+				printk ("M_REJECT received (%x:%x).\n",
+					scr_to_cpu(np->lastmsg), np->msgout[0]);
+			}
+			goto out_clrack;
+			break;
+		default:
+			goto out_reject;
+		}
+		break;
+	/*
+	**	We received an unknown message.
+	**	Ignore all MSG IN phases and reject it.
+	*/
+	case SIR_MSG_WEIRD:
+		ncr_print_msg(cp, "WEIRD message received", np->msgin);
+		OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_weird));
+		return;
+	/*
+	**	Negotiation failed.
+	**	Target does not send us the reply.
+	**	Remove the HS_NEGOTIATE status.
+	*/
+	case SIR_NEGO_FAILED:
+		OUTB (HS_PRT, HS_BUSY);
+	/*
+	**	Negotiation failed.
+	**	Target does not want answer message.
+	*/
+	case SIR_NEGO_PROTO:
+		ncr_nego_default(np, tp, cp);
+		goto out;
 	};
 
 out:
-	OUTONB (nc_dcntl, (STD|NOCOM));
+	OUTONB_STD ();
+	return;
+out_reject:
+	OUTL_DSP (NCB_SCRIPTH_PHYS (np, msg_bad));
+	return;
+out_clrack:
+	OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
+	return;
 out_stuck:
 }
 
+
 /*==========================================================
 **
 **
@@ -8553,7 +11533,7 @@
 {
 	tcb_p tp = &np->target[tn];
 	lcb_p lp = ncr_lp(np, tp, ln);
-	u_char tag = NO_TAG;
+	u_short tag = NO_TAG;
 	XPT_QUEHEAD *qp;
 	ccb_p cp = (ccb_p) 0;
 
@@ -8597,9 +11577,10 @@
 			if (lp->busyccbs < lp->maxnxs) {
 				tag = lp->cb_tags[lp->ia_tag];
 				++lp->ia_tag;
-				if (lp->ia_tag == SCSI_NCR_MAX_TAGS)
+				if (lp->ia_tag == MAX_TAGS)
 					lp->ia_tag = 0;
-				lp->tags_umap |= (((tagmap_t) 1) << tag);
+				cp->tags_si = lp->tags_si;
+				++lp->tags_sum[cp->tags_si];
 			}
 			else
 				goto out_free;
@@ -8616,6 +11597,7 @@
 	/*
 	**	Remember all informations needed to free this CCB.
 	*/
+	cp->to_abort = 0;
 	cp->tag	   = tag;
 	cp->target = tn;
 	cp->lun    = ln;
@@ -8659,10 +11641,9 @@
 	if (lp) {
 		if (cp->tag != NO_TAG) {
 			lp->cb_tags[lp->if_tag++] = cp->tag;
-			if (lp->if_tag == SCSI_NCR_MAX_TAGS)
+			if (lp->if_tag == MAX_TAGS)
 				lp->if_tag = 0;
-			lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag);
-			lp->tags_smap &= lp->tags_umap;
+			--lp->tags_sum[cp->tags_si];
 			lp->tasktbl[cp->tag] = cpu_to_scr(np->p_bad_i_t_l_q);
 		} else {
 			lp->tasktbl[0] = cpu_to_scr(np->p_bad_i_t_l);
@@ -8695,7 +11676,7 @@
 	/*
 	**	Allocate memory for this CCB.
 	*/
-	cp = m_calloc(sizeof(struct ccb), "CCB", MEMO_WARN);
+	cp = m_calloc_dma(sizeof(struct ccb), "CCB");
 	if (!cp)
 		return 0;
 
@@ -8723,6 +11704,11 @@
 	cp->phys.header.go.restart = cpu_to_scr(NCB_SCRIPTH_PHYS(np,bad_i_t_l));
 
 	/*
+	**	Initilialyze some other fields.
+	*/
+	cp->phys.smsg_ext.addr = cpu_to_scr(NCB_PHYS(np, msgin[2]));
+
+	/*
 	**	Chain into wakeup list and free ccb queue.
 	*/
 	cp->link_ccb	= np->ccbc;
@@ -8772,26 +11758,7 @@
 **------------------------------------------------------------------------
 */
 static void ncr_init_tcb (ncb_p np, u_char tn)
-{
-	tcb_p tp = &np->target[tn];
-
-	/*
-	**	Already bone.
-	*/
-	if (tp->luntbl)
-		return;
-	/*
-	**	Allocate the lcb bus address array.
-	*/
-	tp->luntbl = m_calloc(256, "LUNTBL", MEMO_WARN);
-	if (!tp->luntbl)
-		return;
-
-	/*
-	**	Compute the bus address of this table.
-	*/
-	tp->b_luntbl = cpu_to_scr(vtobus(tp->luntbl));
-
+{
 	/*
 	**	Check some alignments required by the chip.
 	*/	
@@ -8799,6 +11766,11 @@
 		offsetof(struct tcb    , sval    )) &3) == 0);
 	assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
 		offsetof(struct tcb    , wval    )) &3) == 0);
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+		(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)){
+		assert (( (offsetof(struct ncr_reg, nc_scntl4) ^
+			offsetof(struct tcb    , uval    )) &3) == 0);
+	}
 }
 
 /*------------------------------------------------------------------------
@@ -8823,33 +11795,46 @@
 	**	Initialize the target control block if not yet.
 	*/
 	ncr_init_tcb(np, tn);
-	if (!tp->luntbl)
-		goto fail;
+
+	/*
+	**	Allocate the lcb bus address array.
+	**	Compute the bus address of this table.
+	*/
+	if (ln && !tp->luntbl) {
+		int i;
+
+		tp->luntbl = m_calloc_dma(256, "LUNTBL");
+		if (!tp->luntbl)
+			goto fail;
+		for (i = 0 ; i < 64 ; i++)
+			tp->luntbl[i] = cpu_to_scr(NCB_PHYS(np, resel_badlun));
+		tp->b_luntbl = cpu_to_scr(vtobus(tp->luntbl));
+	}
 
 	/*
 	**	Allocate the table of pointers for LUN(s) > 0, if needed.
 	*/
 	if (ln && !tp->lmp) {
-		tp->lmp = m_calloc(MAX_LUN * sizeof(lcb_p), "LMP", MEMO_WARN);
+		tp->lmp = m_calloc(MAX_LUN * sizeof(lcb_p), "LMP");
 		if (!tp->lmp)
 			goto fail;
 	}
 
 	/*
 	**	Allocate the lcb.
+	**	Make it available to the chip.
 	*/
-	lp = m_calloc(sizeof(struct lcb), "LCB", MEMO_WARN);
+	lp = m_calloc_dma(sizeof(struct lcb), "LCB");
 	if (!lp)
 		goto fail;
-	if (ln)
+	if (ln) {
 		tp->lmp[ln] = lp;
-	else
+		tp->luntbl[ln] = cpu_to_scr(vtobus(lp));
+	}
+	else {
 		tp->l0p = lp;
-
-	/*
-	**	Make it available to the chip.
-	*/
-	tp->luntbl[ln] = cpu_to_scr(vtobus(lp));
+		tp->b_lun0 = cpu_to_scr(vtobus(lp));
+	}
 
 	/*
 	**	Initialize the CCB queue headers.
@@ -8899,14 +11884,16 @@
 	if (!lp && !(lp = ncr_alloc_lcb(np, tn, ln)))
 		goto fail;
 
+#if 0	/* No more used. Left here as provision */
 	/*
-	**	Get device quirks from a speciality table.
+	**	Get device quirks.
 	*/
-	tp->quirks = ncr_lookup (inq_data);
+	tp->quirks = 0;
 	if (tp->quirks && bootverbose) {
 		PRINT_LUN(np, tn, ln);
 		printk ("quirks=%x.\n", tp->quirks);
 	}
+#endif
 
 	/*
 	**	Evaluate trustable target/unit capabilities.
@@ -8949,18 +11936,23 @@
 	**	initialyze the task table if not yet.
 	*/
 	if ((inq_byte7 & INQ7_QUEUE) && lp->tasktbl == &lp->tasktbl_0) {
-		lp->tasktbl = m_calloc(256, "TASKTBL", MEMO_WARN);
+		lp->tasktbl = m_calloc_dma(MAX_TASKS*4, "TASKTBL");
 		if (!lp->tasktbl) {
 			lp->tasktbl = &lp->tasktbl_0;
 			goto fail;
 		}
 		lp->b_tasktbl = cpu_to_scr(vtobus(lp->tasktbl));
-		for (i = 0 ; i < 64 ; i++)
+		for (i = 0 ; i < MAX_TASKS ; i++)
 			lp->tasktbl[i] = cpu_to_scr(np->p_notask);
-		for (i = 0 ; i < SCSI_NCR_MAX_TAGS ; i++)
+
+		lp->cb_tags = m_calloc(MAX_TAGS, "CB_TAGS");
+		if (!lp->cb_tags)
+			goto fail;
+		for (i = 0 ; i < MAX_TAGS ; i++)
 			lp->cb_tags[i] = i;
-		lp->maxnxs = SCSI_NCR_MAX_TAGS;
-		lp->tags_stime = jiffies;
+
+		lp->maxnxs = MAX_TAGS;
+		lp->tags_stime = ktime_get(3*HZ);
 	}
 
 	/*
@@ -9008,7 +12000,7 @@
 /*
 **	For 64 bit systems, we use the 8 upper bits of the size field 
 **	to provide bus address bits 32-39 to the SCRIPTS processor.
-**	This allows the 896 to access up to 1 tera-bytes of memory.
+**	This allows the 895A and 896 to address up to 1 TB of memory.
 **	For 32 bit chips on 64 bit systems, we must be provided with 
 **	memory addresses that fit into the first 32 bit bus address 
 **	range and so, this does not matter and we expect an error from 
@@ -9033,7 +12025,7 @@
 
 #define CROSS_16MB(p, n) (((((u_long) p) + n - 1) ^ ((u_long) p)) & ~0xffffff)
 
-static	int ncr_scatter_no_sglist(ccb_p cp, Scsi_Cmnd *cmd)
+static	int ncr_scatter_no_sglist(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
 {
 	struct scr_tblmove *data = &cp->phys.data[MAX_SCATTER-1];
 	int segment;
@@ -9041,7 +12033,8 @@
 	cp->data_len = cmd->request_bufflen;
 
 	if (cmd->request_bufflen) {
-		u_long baddr = vtobus(cmd->request_buffer);
+		u_long baddr = map_scsi_single_data(np, cmd);
+
 		SCATTER_ONE(data, baddr, cmd->request_bufflen);
 		if (CROSS_16MB(baddr, cmd->request_bufflen)) {
 			cp->host_flags |= HF_PM_TO_C;
@@ -9072,7 +12065,7 @@
 **	nicely power-of-two sized and aligned. But, since this may change 
 **	at any time, a work-around was required.
 */
-static int ncr_scatter_896R1(ccb_p cp, Scsi_Cmnd *cmd)
+static int ncr_scatter_896R1(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
 {
 	int segn;
 	int use_sg = (int) cmd->use_sg;
@@ -9080,18 +12073,23 @@
 	cp->data_len = 0;
 
 	if (!use_sg)
-		segn = ncr_scatter_no_sglist(cp, cmd);
+		segn = ncr_scatter_no_sglist(np, cp, cmd);
 	else if (use_sg > MAX_SCATTER)
 		segn = -1;
 	else {
 		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
-		struct scr_tblmove *data = &cp->phys.data[MAX_SCATTER - use_sg];
+		struct scr_tblmove *data;
+
+		use_sg = map_scsi_sg_data(np, cmd);
+		data = &cp->phys.data[MAX_SCATTER - use_sg];
 
 		for (segn = 0; segn < use_sg; segn++) {
-			u_long baddr = vtobus(scatter[segn].address);
+			u_long baddr = scsi_sg_dma_address(&scatter[segn]);
+			unsigned int len = scsi_sg_dma_len(&scatter[segn]);
+
 			SCATTER_ONE(&data[segn],
 				    baddr,
-				    scatter[segn].length);
+				    len);
 			if (CROSS_16MB(baddr, scatter[segn].length)) {
 				cp->host_flags |= HF_PM_TO_C;
 #ifdef DEBUG_896R1
@@ -9099,14 +12097,14 @@
 	baddr, scatter[segn].length);
 #endif
 			}
-			cp->data_len += scatter[segn].length;
+			cp->data_len += len;
 		}
 	}
 
 	return segn;
 }
 
-static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+static int ncr_scatter(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
 {
 	int segment;
 	int use_sg = (int) cmd->use_sg;
@@ -9114,19 +12112,24 @@
 	cp->data_len = 0;
 
 	if (!use_sg)
-		segment = ncr_scatter_no_sglist(cp, cmd);
+		segment = ncr_scatter_no_sglist(np, cp, cmd);
 	else if (use_sg > MAX_SCATTER)
 		segment = -1;
 	else {
 		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
-		struct scr_tblmove *data = &cp->phys.data[MAX_SCATTER - use_sg];
+		struct scr_tblmove *data;
+
+		use_sg = map_scsi_sg_data(np, cmd);
+		data = &cp->phys.data[MAX_SCATTER - use_sg];
 
 		for (segment = 0; segment < use_sg; segment++) {
-			u_long baddr = vtobus(scatter[segment].address);
+			u_long baddr = scsi_sg_dma_address(&scatter[segment]);
+			unsigned int len = scsi_sg_dma_len(&scatter[segment]);
+
 			SCATTER_ONE(&data[segment],
 				    baddr,
-				    scatter[segment].length);
-			cp->data_len += scatter[segment].length;
+				    len);
+			cp->data_len += len;
 		}
 	}
 
@@ -9144,10 +12147,8 @@
 **==========================================================
 */
 
-#ifndef NCR_IOMAPPED
-__initfunc(
-static int ncr_regtest (struct ncb* np)
-)
+#ifndef SCSI_NCR_IOMAPPED
+static int __init ncr_regtest (struct ncb* np)
 {
 	register volatile u_int32 data;
 	/*
@@ -9171,13 +12172,11 @@
 }
 #endif
 
-__initfunc(
-static int ncr_snooptest (struct ncb* np)
-)
+static int __init ncr_snooptest (struct ncb* np)
 {
 	u_int32	ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc;
 	int	i, err=0;
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 	if (np->reg) {
             err |= ncr_regtest (np);
             if (err) return (err);
@@ -9197,8 +12196,8 @@
 	/*
 	**	Start script (exchange values)
 	*/
-	OUTL (nc_dsa, vtobus(np));
-	OUTL (nc_dsp, pc);
+	OUTL (nc_dsa, np->p_ncb);
+	OUTL_DSP (pc);
 	/*
 	**	Wait 'til done (with timeout)
 	*/
@@ -9256,100 +12255,6 @@
 
 /*==========================================================
 **
-**
-**	Profiling the drivers and targets performance.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-
-static	void ncb_profile (ncb_p np, ccb_p cp)
-{
-	int num_disc	= (cp->phys.num_disc & 0xff);
-	int num_disc0	= (cp->phys.num_disc >> 8);
-
-	++np->profile.num_trans;
-	np->profile.num_disc	+= num_disc;
-	np->profile.num_disc0	+= num_disc0;
-	np->profile.num_kbytes	+= (cp->data_len >> 10);
-#if 000
-	if (num_disc > num_disc0) {
-		if (cp->data_len <= 1024)
-			np->profile.num_br1k += (num_disc - num_disc0);
-		else if (cp->data_len <= 2048)
-			np->profile.num_br2k += (num_disc - num_disc0);
-		else if (cp->data_len <= 4096)
-			np->profile.num_br4k += (num_disc - num_disc0);
-		else if (cp->data_len <= 8192)
-			np->profile.num_br8k += (num_disc - num_disc0);
-		else
-			np->profile.num_brnk += (num_disc - num_disc0);
-	}
-#endif
-}
-
-#endif /* SCSI_NCR_PROFILE_SUPPORT */
-
-/*==========================================================
-**
-**
-**	Device lookup.
-**
-**	@GENSCSI@ should be integrated to scsiconf.c
-**
-**
-**==========================================================
-*/
-
-struct table_entry {
-	char *	manufacturer;
-	char *	model;
-	char *	version;
-	u_long	info;
-};
-
-static struct table_entry device_tab[] =
-{
-#if 0
-	{"", "", "", QUIRK_NOMSG},
-#endif
-	{"SONY", "SDT-5000", "3.17", QUIRK_NOMSG},
-	{"WangDAT", "Model 2600", "01.7", QUIRK_NOMSG},
-	{"WangDAT", "Model 3200", "02.2", QUIRK_NOMSG},
-	{"WangDAT", "Model 1300", "02.4", QUIRK_NOMSG},
-	{"", "", "", 0} /* catch all: must be last entry. */
-};
-
-static u_long ncr_lookup(char * id)
-{
-	struct table_entry * p = device_tab;
-	char *d, *r, c;
-
-	for (;;p++) {
-
-		d = id+8;
-		r = p->manufacturer;
-		while ((c=*r++)) if (c!=*d++) break;
-		if (c) continue;
-
-		d = id+16;
-		r = p->model;
-		while ((c=*r++)) if (c!=*d++) break;
-		if (c) continue;
-
-		d = id+32;
-		r = p->version;
-		while ((c=*r++)) if (c!=*d++) break;
-		if (c) continue;
-
-		return (p->info);
-	}
-}
-
-/*==========================================================
-**
 **	Determine the ncr's clock frequency.
 **	This is essential for the negotiation
 **	of the synchronous transfer rate.
@@ -9364,7 +12269,8 @@
 **	do not have a clock doubler and so are provided with a 
 **	80 MHz clock. All other fast20 boards incorporate a doubler 
 **	and so should be delivered with a 40 MHz clock.
-**	The recent fast40 chips (895/896) use a 40 Mhz base clock 
+**	The recent fast40 chips  (895/896/895A) and the
+**	fast80 chip (C1010) use a 40 Mhz base clock 
 **	and provide a clock quadrupler (160 Mhz). The code below 
 **	tries to deal as cleverly as possible with all this stuff.
 **
@@ -9385,14 +12291,20 @@
 		printk ("%s: enabling clock multiplier\n", ncr_name(np));
 
 	OUTB(nc_stest1, DBLEN);	   /* Enable clock multiplier		  */
-	if (np->multiplier > 2) {  /* Poll bit 5 of stest4 for quadrupler */
-		int i = 20;
+
+	if ( (np->device_id != PCI_DEVICE_ID_LSI_53C1010) && 
+			(np->device_id != PCI_DEVICE_ID_LSI_53C1010_66) && 
+						(np->multiplier > 2)) {  
+		int i = 20;	 /* Poll bit 5 of stest4 for quadrupler */
 		while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
 			UDELAY (20);
 		if (!i)
-			printk("%s: the chip cannot lock the frequency\n", ncr_name(np));
-	} else			/* Wait 20 micro-seconds for doubler	*/
-		UDELAY (20);
+		    printk("%s: the chip cannot lock the frequency\n",
+						 ncr_name(np));
+
+	} else			/* Wait 120 micro-seconds for multiplier*/
+		UDELAY (120);
+
 	OUTB(nc_stest3, HSC);		/* Halt the scsi clock		*/
 	OUTB(nc_scntl3,	scntl3);
 	OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier	*/
@@ -9403,11 +12315,11 @@
 /*
  *	calculate NCR SCSI clock frequency (in KHz)
  */
-__initfunc(
-static unsigned ncrgetfreq (ncb_p np, int gen)
-)
+static unsigned __init ncrgetfreq (ncb_p np, int gen)
 {
-	unsigned ms = 0;
+	unsigned int ms = 0;
+	unsigned int f;
+	int count;
 
 	/*
 	 * Measure GEN timer delay in order 
@@ -9424,16 +12336,21 @@
 	 * performed trust the higher delay 
 	 * (lower frequency returned).
 	 */
-	OUTB (nc_stest1, 0);	/* make sure clock doubler is OFF */
-	OUTW (nc_sien , 0);	/* mask all scsi interrupts */
+	OUTW (nc_sien , 0x0);/* mask all scsi interrupts */
+				/* enable general purpose timer */
 	(void) INW (nc_sist);	/* clear pending scsi interrupt */
 	OUTB (nc_dien , 0);	/* mask all dma interrupts */
 	(void) INW (nc_sist);	/* another one, just to be sure :) */
 	OUTB (nc_scntl3, 4);	/* set pre-scaler to divide by 3 */
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
 	OUTB (nc_stime1, gen);	/* set to nominal delay of 1<<gen * 125us */
-	while (!(INW(nc_sist) & GEN) && ms++ < 100000)
-		UDELAY (1000);	/* count ms */
+				/* Temporary fix for udelay issue with Alpha
+					platform */
+	while (!(INW(nc_sist) & GEN) && ms++ < 100000) {
+		/* count 1ms */
+		for (count = 0; count < 10; count++)
+			UDELAY (100);	
+	}
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
  	/*
  	 * set prescaler to divide by whatever 0 means
@@ -9442,30 +12359,51 @@
  	 */
  	OUTB (nc_scntl3, 0);
 
-	if (bootverbose >= 2)
-		printk ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
   	/*
  	 * adjust for prescaler, and convert into KHz 
+	 * scale values derived empirically. C1010 uses
+	 * different dividers
   	 */
-	return ms ? ((1 << gen) * 4340) / ms : 0;
+#if 0
+	if (np->device_id == PCI_DEVICE_ID_LSI_53C1010)
+		f = ms ? ((1 << gen) * 2866 ) / ms : 0;
+	else
+#endif
+	f = ms ? ((1 << gen) * 4340) / ms : 0;
+
+	if (bootverbose >= 2)
+		printk ("%s: Delay (GEN=%d): %u msec, %u KHz\n",
+			ncr_name(np), gen, ms, f);
+
+	return f;
+}
+
+static unsigned __init ncr_getfreq (ncb_p np)
+{
+	u_int f1, f2;
+	int gen = 11;
+
+	(void) ncrgetfreq (np, gen);	/* throw away first result */
+	f1 = ncrgetfreq (np, gen);
+	f2 = ncrgetfreq (np, gen);
+	if (f1 > f2) f1 = f2;		/* trust lower result	*/
+	return f1;
 }
 
 /*
  *	Get/probe NCR SCSI clock frequency
  */
-__initfunc(
-static void ncr_getclock (ncb_p np, int mult)
-)
+static void __init ncr_getclock (ncb_p np, int mult)
 {
-	unsigned char scntl3 = INB(nc_scntl3);
-	unsigned char stest1 = INB(nc_stest1);
+	unsigned char scntl3 = np->sv_scntl3;
+	unsigned char stest1 = np->sv_stest1;
 	unsigned f1;
 
 	np->multiplier = 1;
 	f1 = 40000;
 
 	/*
-	**	True with 875/895/896 with clock multiplier selected
+	**	True with 875/895/896/895A with clock multiplier selected
 	*/
 	if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
 		if (bootverbose >= 2)
@@ -9474,29 +12412,43 @@
 	}
 
 	/*
+	**	If multiplier not found but a C1010, assume a mult of 4.
 	**	If multiplier not found or scntl3 not 7,5,3,
 	**	reset chip and get frequency from general purpose timer.
 	**	Otherwise trust scntl3 BIOS setting.
 	*/
-	if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
-		unsigned f2;
-
-		OUTB(nc_istat, SRST); UDELAY (5); OUTB(nc_istat, 0);
-
-		(void) ncrgetfreq (np, 11);	/* throw away first result */
-		f1 = ncrgetfreq (np, 11);
-		f2 = ncrgetfreq (np, 11);
+	if ((np->device_id == PCI_DEVICE_ID_LSI_53C1010)  ||
+			(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)) {
+		f1=40000;
+		np->multiplier = mult;
+		if (bootverbose >= 2)
+			printk ("%s: clock multiplier assumed\n", ncr_name(np));
+	}
+	else if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+		OUTB (nc_stest1, 0);		/* make sure doubler is OFF */
+		f1 = ncr_getfreq (np);
 
 		if (bootverbose)
-			printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+			printk ("%s: NCR clock is %uKHz\n", ncr_name(np), f1);
 
-		if (f1 > f2) f1 = f2;		/* trust lower result	*/
-
-		if	(f1 <	45000)		f1 =  40000;
-		else if (f1 <	55000)		f1 =  50000;
+		if	(f1 < 55000)		f1 =  40000;
 		else				f1 =  80000;
 
-		if (f1 < 80000 && mult > 1) {
+		/*
+		**	Suggest to also check the PCI clock frequency 
+		**	to make sure our frequency calculation algorithm 
+		**	is not too biased.
+		*/
+		if (np->features & FE_66MHZ) {
+			np->pciclock_min = (66000*55+80-1)/80;
+			np->pciclock_max = (66000*55)/40;
+		}
+		else {
+			np->pciclock_min = (33000*55+80-1)/80;
+			np->pciclock_max = (33000*55)/40;
+		}
+
+		if (f1 == 40000 && mult > 1) {
 			if (bootverbose >= 2)
 				printk ("%s: clock multiplier assumed\n", ncr_name(np));
 			np->multiplier	= mult;
@@ -9516,6 +12468,20 @@
 	np->clock_khz	= f1;
 }
 
+/*
+ *	Get/probe PCI clock frequency
+ */
+static u_int __init ncr_getpciclock (ncb_p np)
+{
+	static u_int f;
+
+	OUTB (nc_stest1, SCLK);	/* Use the PCI clock as SCSI clock */
+	f = ncr_getfreq (np);
+	OUTB (nc_stest1, 0);
+
+	return f;
+}
+
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
 
 #ifndef uchar
@@ -9569,6 +12535,10 @@
 #define OPT_EXCLUDE		24
 #define OPT_HOST_ID		25
 
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB		26
+#endif
+
 static char setup_token[] __initdata = 
 	"tags:"   "mpar:"
 	"spar:"   "disc:"
@@ -9582,7 +12552,11 @@
 	"buschk:" "optim:"
 	"recovery:"
 	"safe:"   "nvram:"
-	"excl:"   "hostid:";
+	"excl:"   "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+	"iarb:"
+#endif
+	;	/* DONNOT REMOVE THIS ';' */
 
 #ifdef MODULE
 #define	ARG_SEP	' '
@@ -9590,9 +12564,7 @@
 #define	ARG_SEP	','
 #endif
 
-__initfunc(
-static int get_setup_token(char *p)
-)
+static int __init get_setup_token(char *p)
 {
 	char *cur = setup_token;
 	char *pc;
@@ -9609,14 +12581,13 @@
 }
 
 
-__initfunc(
-void sym53c8xx_setup(char *str, int *ints)
-)
+int __init sym53c8xx_setup(char *str)
 {
 #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 	char *cur = str;
 	char *pc, *pv;
-	int i, val, c;
+	unsigned long val;
+	int i,  c;
 	int xi = 0;
 
 	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
@@ -9719,6 +12690,11 @@
 		case OPT_HOST_ID:
 			driver_setup.host_id = val;
 			break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+		case OPT_IARB:
+			driver_setup.iarb = val;
+			break;
+#endif
 		default:
 			printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
 			break;
@@ -9728,10 +12704,17 @@
 			++cur;
 	}
 #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+	return 1;
 }
 
-static int sym53c8xx_pci_init(Scsi_Host_Template *tpnt,
-	     uchar bus, uchar device_fn, ncr_device *device);
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("sym53c8xx=", sym53c8xx_setup);
+#endif
+#endif
+
+static int 
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device);
 
 /*
 **   Linux entry point for SYM53C8XX devices detection routine.
@@ -9748,9 +12731,7 @@
 **   Returns the number of boards successfully attached.
 */
 
-__initfunc(
-static void ncr_print_driver_setup(void)
-)
+static void __init ncr_print_driver_setup(void)
 {
 #define YesNo(y)	y ? 'y' : 'n'
 	printk (NAME53C8XX ": setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
@@ -9809,31 +12790,30 @@
 #define	SCSI_NCR_MAX_PQS_BUS	16
 static int pqs_bus[SCSI_NCR_MAX_PQS_BUS] __initdata = { 0 };
 
-__initfunc(
-static void ncr_detect_pqs_pds(void)
-)
+static void __init ncr_detect_pqs_pds(void)
 {
 	short index;
+	pcidev_t dev = PCIDEV_NULL;
 
-	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index ++) {
-		u_char tmp, bus, device_fn;
+	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+		u_char tmp;
 
-		if (pcibios_find_device(0x101a, 0x0009, index, &bus, 
-					&device_fn) != PCIBIOS_SUCCESSFUL) {
+		dev = pci_find_device(0x101a, 0x0009, dev);
+		if (dev == PCIDEV_NULL) {
 			pqs_bus[index] = -1;
 			break;
 		}
-		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", bus);
-		pcibios_read_config_byte(bus, device_fn, 0x44, &tmp);
+		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+		pci_read_config_byte(dev, 0x44, &tmp);
 		/* bit 1: allow individual 875 configuration */
 		tmp |= 0x2;
-		pcibios_write_config_byte(bus, device_fn, 0x44, tmp);
-		pcibios_read_config_byte(bus, device_fn, 0x45, &tmp);
+		pci_write_config_byte(dev, 0x44, tmp);
+		pci_read_config_byte(dev, 0x45, &tmp);
 		/* bit 2: drive individual 875 interrupts to the bus */
 		tmp |= 0x4;
-		pcibios_write_config_byte(bus, device_fn, 0x45, tmp);
+		pci_write_config_byte(dev, 0x45, tmp);
 
-		pqs_bus[index] = bus;
+		pqs_bus[index] = PciBusNumber(dev);
 	}
 }
 #endif /* SCSI_NCR_PQS_PDS_SUPPORT */
@@ -9849,13 +12829,10 @@
 **    the the order they are detected.
 **===================================================================
 */
-__initfunc(
-int sym53c8xx_detect(Scsi_Host_Template *tpnt)
-)
+int __init sym53c8xx_detect(Scsi_Host_Template *tpnt)
 {
+	pcidev_t pcidev;
 	int i, j, chips, hosts, count;
-	u_char bus, device_fn;
-	short index;
 	int attach_count = 0;
 	ncr_device *devtbl, *devp;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
@@ -9865,24 +12842,24 @@
 	/*
 	**    PCI is required.
 	*/
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92)
 	if (!pci_present())
-#else
-	if (!pcibios_present())
-#endif
 		return 0;
 
 	/*
 	**    Initialize driver general stuff.
 	*/
 #ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
      tpnt->proc_dir  = &proc_scsi_sym53c8xx;
+#else
+     tpnt->proc_name = NAME53C8XX;
+#endif
      tpnt->proc_info = sym53c8xx_proc_info;
 #endif
 
 #if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
 if (sym53c8xx)
-	sym53c8xx_setup(sym53c8xx, (int *) 0);
+	sym53c8xx_setup(sym53c8xx);
 #endif
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 	ncr_debug = driver_setup.debug;
@@ -9896,7 +12873,7 @@
 	**	overflow the kernel stack.
 	**	1 x 4K PAGE is enough for more than 40 devices for i386.
 	*/
-	devtbl = m_calloc(PAGE_SIZE, "devtbl", MEMO_WARN);
+	devtbl = m_calloc(PAGE_SIZE, "devtbl");
 	if (!devtbl)
 		return 0;
 
@@ -9918,8 +12895,8 @@
 	nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
 #endif
 	j = 0;
-	index = 0;
 	count = 0;
+	pcidev = PCIDEV_NULL;
 	while (1) {
 		char *msg = "";
 		if (count >= hosts)
@@ -9927,17 +12904,24 @@
 		if (j >= chips)
 			break;
 		i = driver_setup.reverse_probe ? chips - 1 - j : j;
-		if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-					index, &bus, &device_fn)) {
+		pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+					 pcidev);
+		if (pcidev == PCIDEV_NULL) {
 			++j;
-			index = 0;
 			continue;
 		}
-		++index;
+		/* Some HW as the HP LH4 may report twice PCI devices */
+		for (i = 0; i < count ; i++) {
+			if (devtbl[i].slot.bus	     == PciBusNumber(pcidev) && 
+			    devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
+				break;
+		}
+		if (i != count)	/* Ignore this device if we already have it */
+			continue;
 		devp = &devtbl[count];
 		devp->host_id = driver_setup.host_id;
 		devp->attach_done = 0;
-		if (sym53c8xx_pci_init(tpnt, bus, device_fn, devp)) {
+		if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
 			continue;
 		}
 		++count;
@@ -10032,105 +13016,55 @@
 }
 
 /*===================================================================
-**   Generically read a base address from the PCI configuration space.
-**   Return the offset immediately after the base address that has 
-**   been read. Btw, we blindly assume that the high 32 bits of 64 bit 
-**   base addresses are set to zero on 32 bit architectures.
-**===================================================================
-*/
-#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
-__initfunc(
-static int 
-pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base)
-)
-{
-	u_int32 tmp;
-
-	pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-	*base = tmp;
-	offset += sizeof(u_int32);
-	if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-		*base |= (((u_long)tmp) << 32);
-#endif
-		offset += sizeof(u_int32);
-	}
-	return offset;
-}
-#else	/* LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) */
-__initfunc(
-static int 
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-)
-{
-	*base = pdev->base_address[index++];
-	if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		*base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
-		++index;
-	}
-	return index;
-}
-#endif
-
-/*===================================================================
 **   Read and check the PCI configuration for any detected NCR 
 **   boards and save data for attaching after all boards have 
 **   been detected.
 **===================================================================
 */
-__initfunc(
-static int sym53c8xx_pci_init(Scsi_Host_Template *tpnt,
-			      uchar bus, uchar device_fn, ncr_device *device)
-)
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
 {
-	u_short vendor_id, device_id, command;
+	u_short vendor_id, device_id, command, status_reg;
 	u_char cache_line_size, latency_timer;
 	u_char suggested_cache_line_size = 0;
+	u_char pci_fix_up = driver_setup.pci_fix_up;
 	u_char revision;
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	struct pci_dev *pdev;
 	u_int irq;
-#else
-	u_char irq;
-#endif
 	u_long base, base_2, io_port; 
 	int i;
 	ncr_chip *chip;
 
 	printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
-		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+		PciBusNumber(pdev),
+		(int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+		(int) (PciDeviceFn(pdev) & 7));
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+	if (!pci_dma_supported(pdev, (dma_addr_t) (0xffffffffUL))) {
+		printk(KERN_WARNING NAME53C8XX
+		       "32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+		return -1;
+	}
+#endif
+
 	/*
 	**    Read info from the PCI config space.
-	**    pcibios_read_config_xxx() functions are assumed to be used for 
+	**    pci_read_config_xxx() functions are assumed to be used for 
 	**    successfully detected PCI devices.
 	*/
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	pdev = pci_find_slot(bus, device_fn);
-	vendor_id = pdev->vendor;
-	device_id = pdev->device;
-	irq = pdev->irq;
+	vendor_id = PciVendorId(pdev);
+	device_id = PciDeviceId(pdev);
+	irq	  = PciIrqLine(pdev);
 	i =	0;
 	i =	pci_get_base_address(pdev, i, &io_port);
 	i =	pci_get_base_address(pdev, i, &base);
 	(void)	pci_get_base_address(pdev, i, &base_2);
-#else
-	pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id);
-	pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id);
-	pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq);
-	i =	PCI_BASE_ADDRESS_0;
-	i =	pci_read_base_address(bus, device_fn, i, &io_port);
-	i =	pci_read_base_address(bus, device_fn, i, &base);
-	(void)	pci_read_base_address(bus, device_fn, i, &base_2);
-#endif
-	pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-	pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION, &revision);
-	pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE,
-				 &cache_line_size);
-	pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER,
-				 &latency_timer);
+
+	pci_read_config_word(pdev, PCI_COMMAND,		&command);
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION,	&revision);
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,	&cache_line_size);
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER,	&latency_timer);
+	pci_read_config_word(pdev, PCI_STATUS,		&status_reg);
 
 #ifdef SCSI_NCR_PQS_PDS_SUPPORT
 	/*
@@ -10141,8 +13075,8 @@
 	*/
 	for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
 		u_char tmp;
-		if (pqs_bus[i] == bus) {
-			pcibios_read_config_byte(bus, device_fn, 0x84, &tmp);
+		if (pqs_bus[i] == PciBusNumber(pdev)) {
+			pci_read_config_byte(pdev, 0x84, &tmp);
 			device->pqs_pds = 1;
 			device->host_id = tmp;
 			break;
@@ -10201,7 +13135,7 @@
 			}
 		}
 	}
-#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */
+#endif /* i386 and PCI MEMORY accessible */
 
 	if (!chip) {
 		printk(NAME53C8XX ": not initializing, device not supported\n");
@@ -10219,7 +13153,7 @@
 		(command & PCI_COMMAND_IO)     ? "" : " PCI_COMMAND_IO",
 		(command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
 		command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
@@ -10227,26 +13161,26 @@
 		if (io_port >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
 			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_0, io_port);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_0, io_port);
 		}
 		if (base >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating base (Wacky IBM)");
 			base = (base & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_1, base);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_1, base);
 		}
 		if (base_2 >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
 			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_2, base_2);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_2, base_2);
 		}
 	}
 #endif
 #endif	/* __powerpc__ */
 
-#ifdef __sparc__
+#if defined(__sparc__) && (LINUX_VERSION_CODE < LinuxVersionCode(2,3,0))
 	/*
 	**    Fix-ups for sparc.
 	**
@@ -10297,9 +13231,9 @@
 	**    from attaching devices from the both drivers.
 	**    If you have a better idea, let me know.
 	*/
-/* #ifdef NCR_IOMAPPED */
+/* #ifdef SCSI_NCR_IOMAPPED */
 #if 1
-	if (!(command & PCI_COMMAND_IO) || !(io_port & 1)) { 
+	if (!(command & PCI_COMMAND_IO)) { 
 		printk(NAME53C8XX ": I/O base address (0x%lx) disabled.\n",
 			(long) io_port);
 		io_port = 0;
@@ -10314,7 +13248,7 @@
 	base	&= PCI_BASE_ADDRESS_MEM_MASK;
 	base_2	&= PCI_BASE_ADDRESS_MEM_MASK;
 
-/* #ifdef NCR_IOMAPPED */
+/* #ifdef SCSI_NCR_IOMAPPED */
 #if 1
 	if (io_port && check_region (io_port, 128)) {
 		printk(NAME53C8XX ": IO region 0x%lx[0..127] is in use\n",
@@ -10324,7 +13258,7 @@
 	if (!io_port)
 		return -1;
 #endif
-#ifndef NCR_IOMAPPED
+#ifndef SCSI_NCR_IOMAPPED
 	if (!base) {
 		printk(NAME53C8XX ": MMIO base address disabled.\n");
 		return -1;
@@ -10340,7 +13274,7 @@
 		(command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
 		(command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
 		command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 	/*
@@ -10354,33 +13288,82 @@
 		if (driver_setup.special_features & 4)
 			chip->features &= ~FE_NOPM;
 	}
+
+	/*
+	** Work around for errant bit in 895A. The 66Mhz
+	** capable bit is set erroneously. Clear this bit.
+	** (Item 1 DEL 533)
+	**
+	** Make sure Config space and Features agree.
+	**
+	** Recall: writes are not normal to status register -
+	** write a 1 to clear and a 0 to leave unchanged.
+	** Can only reset bits.
+	*/
+	if (chip->features & FE_66MHZ) {
+		if (!(status_reg & PCI_STATUS_66MHZ))
+			chip->features &= ~FE_66MHZ;
+	}
+	else {
+		if (status_reg & PCI_STATUS_66MHZ) {
+			status_reg = PCI_STATUS_66MHZ;
+			pci_write_config_word(pdev, PCI_STATUS, status_reg);
+			pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+		}
+	}
+
+	if (driver_setup.ultra_scsi < 3 && (chip->features & FE_ULTRA3)) {
+		chip->features |=  FE_ULTRA2;
+		chip->features &= ~FE_ULTRA3;
+	}
 	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
 		chip->features |=  FE_ULTRA;
 		chip->features &= ~FE_ULTRA2;
 	}
 	if (driver_setup.ultra_scsi < 1)
 		chip->features &= ~FE_ULTRA;
+
 	if (!driver_setup.max_wide)
 		chip->features &= ~FE_WIDE;
 
+	/*
+	 * C1010 Ultra3 support requires 16 bit data transfers.
+	 */
+	if (!driver_setup.max_wide && (chip->features & FE_ULTRA3)) {
+		chip->features |= FE_ULTRA2;
+		chip->features |= ~FE_ULTRA3;
+	}
+
+	/*
+	**	Some features are required to be enabled in order to 
+	**	work around some chip problems. :) ;)
+	**	(ITEM 12 of a DEL about the 896 I haven't yet).
+	**	We must ensure the chip will use WRITE AND INVALIDATE.
+	**	The revision number limit is for now arbitrary.
+	*/
+	if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
+		chip->features	|= (FE_WRIE | FE_CLSE);
+		pci_fix_up	|=  3;	/* Force appropriate PCI fix-up */
+	}
+
 #ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
 	/*
 	**    Try to fix up PCI config according to wished features.
 	*/
-	if ((driver_setup.pci_fix_up & 1) && (chip->features & FE_CLSE) && 
+	if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && 
 	    !cache_line_size && suggested_cache_line_size) {
 		cache_line_size = suggested_cache_line_size;
-		pcibios_write_config_byte(bus, device_fn,
-				PCI_CACHE_LINE_SIZE, cache_line_size);
+		pci_write_config_byte(pdev,
+				      PCI_CACHE_LINE_SIZE, cache_line_size);
 		printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
 			cache_line_size);
 	}
 
-	if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
+	if ((pci_fix_up & 2) && cache_line_size &&
 	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
 		printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
 		command |= PCI_COMMAND_INVALIDATE;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 	/*
@@ -10388,15 +13371,15 @@
 	**    (latency timer >= burst length + 6, we add 10 to be quite sure)
 	*/
 
-	if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+	if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
 		uchar lt = (1 << chip->burst_max) + 6 + 10;
 		if (latency_timer < lt) {
-			latency_timer = lt;
 			printk(NAME53C8XX 
-			       ": setting PCI_LATENCY_TIMER to %d (fix-up).\n",
-			       latency_timer);
-			pcibios_write_config_byte(bus, device_fn,
-					PCI_LATENCY_TIMER, latency_timer);
+			       ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+			       (int) latency_timer, (int) lt);
+			latency_timer = lt;
+			pci_write_config_byte(pdev,
+					      PCI_LATENCY_TIMER, latency_timer);
 		}
 	}
 
@@ -10405,8 +13388,9 @@
  	/*
 	**    Initialise ncr_device structure with items required by ncr_attach.
 	*/
-	device->slot.bus	= bus;
-	device->slot.device_fn	= device_fn;
+	device->pdev		= pdev;
+	device->slot.bus	= PciBusNumber(pdev);
+	device->slot.device_fn	= PciDeviceFn(pdev);
 	device->slot.base	= base;
 	device->slot.base_2	= base_2;
 	device->slot.io_port	= io_port;
@@ -10430,7 +13414,7 @@
 *===================================================================
 */
 #ifdef SCSI_NCR_NVRAM_SUPPORT
-__initfunc(static void ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp))
+static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
 {
 	devp->nvram = nvp;
 	if (!nvp)
@@ -10438,7 +13422,7 @@
 	/*
 	**    Get access to chip IO registers
 	*/
-#ifdef NCR_IOMAPPED
+#ifdef SCSI_NCR_IOMAPPED
 	request_region(devp->slot.io_port, 128, NAME53C8XX);
 	devp->slot.base_io = devp->slot.io_port;
 #else
@@ -10451,9 +13435,10 @@
 	**    Try to read SYMBIOS nvram.
 	**    Try to read TEKRAM nvram if Symbios nvram not found.
 	*/
-	if	(!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+	if	(!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
 		nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
-	else if	(!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram))
+	else if	(!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+					&nvp->data.Tekram))
 		nvp->type = SCSI_NCR_TEKRAM_NVRAM;
 	else {
 		nvp->type = 0;
@@ -10463,7 +13448,7 @@
 	/*
 	** Release access to chip IO registers
 	*/
-#ifdef NCR_IOMAPPED
+#ifdef SCSI_NCR_IOMAPPED
 	release_region(devp->slot.base_io, 128);
 #else
 	unmap_pci_mem((u_long) devp->slot.reg, 128ul);
@@ -10557,8 +13542,8 @@
 		device->queue_depth = numtags;
 		if (device->queue_depth < 2)
 			device->queue_depth = 2;
-		if (device->queue_depth > SCSI_NCR_MAX_TAGS)
-			device->queue_depth = SCSI_NCR_MAX_TAGS;
+		if (device->queue_depth > MAX_TAGS)
+			device->queue_depth = MAX_TAGS;
 
 		/*
 		**	Since the queue depth is not tunable under Linux,
@@ -10583,15 +13568,7 @@
 */
 const char *sym53c8xx_info (struct Scsi_Host *host)
 {
-#ifdef __sparc__
-	/* Ok to do this on all archs? */
-	static char buffer[80];
-	ncb_p np = ((struct host_data *) host->hostdata)->ncb;
-	sprintf (buffer, "%s\nPCI bus %02x device %02x", SCSI_NCR_DRIVER_NAME, np->pci_bus, np->pci_devfn);
-	return buffer;
-#else
 	return SCSI_NCR_DRIVER_NAME;
-#endif
 }
 
 /*
@@ -10612,6 +13589,10 @@
      cmd->host_scribble = NULL;
      cmd->SCp.ptr       = NULL;
      cmd->SCp.buffer    = NULL;
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+     cmd->__data_mapped = 0;
+     cmd->__data_mapping = 0;
+#endif
 
      NCR_LOCK_NCB(np, flags);
 
@@ -10628,8 +13609,10 @@
 
      NCR_UNLOCK_NCB(np, flags);
 
-     if (sts != DID_OK)
+     if (sts != DID_OK) {
+          unmap_scsi_data(np, cmd);
           done(cmd);
+     }
 
      return sts;
 }
@@ -10900,7 +13883,7 @@
 /*=========================================================================
 **	Proc file system stuff
 **
-**	A read operation returns profile information.
+**	A read operation returns adapter information.
 **	A write operation is a control command.
 **	The string is parsed in the driver code and the command is passed 
 **	to the ncr_usercmd() function.
@@ -10990,8 +13973,10 @@
 		uc->cmd = UC_SETDEBUG;
 	else if	((arg_len = is_keyword(ptr, len, "setflag")) != 0)
 		uc->cmd = UC_SETFLAG;
-	else if	((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
-		uc->cmd = UC_CLEARPROF;
+	else if	((arg_len = is_keyword(ptr, len, "resetdev")) != 0)
+		uc->cmd = UC_RESETDEV;
+	else if	((arg_len = is_keyword(ptr, len, "cleardev")) != 0)
+		uc->cmd = UC_CLEARDEV;
 	else
 		arg_len = 0;
 
@@ -11008,6 +13993,8 @@
 	case UC_SETTAGS:
 	case UC_SETWIDE:
 	case UC_SETFLAG:
+	case UC_RESETDEV:
+	case UC_CLEARDEV:
 		SKIP_SPACES(1);
 		if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
 			ptr += arg_len; len -= arg_len;
@@ -11051,14 +14038,12 @@
 				uc->data |= DEBUG_ALLOC;
 			else if	((arg_len = is_keyword(ptr, len, "phase")))
 				uc->data |= DEBUG_PHASE;
-			else if	((arg_len = is_keyword(ptr, len, "poll")))
-				uc->data |= DEBUG_POLL;
 			else if	((arg_len = is_keyword(ptr, len, "queue")))
 				uc->data |= DEBUG_QUEUE;
 			else if	((arg_len = is_keyword(ptr, len, "result")))
 				uc->data |= DEBUG_RESULT;
-			else if	((arg_len = is_keyword(ptr, len, "scatter")))
-				uc->data |= DEBUG_SCATTER;
+			else if	((arg_len = is_keyword(ptr, len, "pointer")))
+				uc->data |= DEBUG_POINTER;
 			else if	((arg_len = is_keyword(ptr, len, "script")))
 				uc->data |= DEBUG_SCRIPT;
 			else if	((arg_len = is_keyword(ptr, len, "tiny")))
@@ -11069,10 +14054,6 @@
 				uc->data |= DEBUG_NEGO;
 			else if	((arg_len = is_keyword(ptr, len, "tags")))
 				uc->data |= DEBUG_TAGS;
-			else if	((arg_len = is_keyword(ptr, len, "freeze")))
-				uc->data |= DEBUG_FREEZE;
-			else if	((arg_len = is_keyword(ptr, len, "restart")))
-				uc->data |= DEBUG_RESTART;
 			else
 				return -EINVAL;
 			ptr += arg_len; len -= arg_len;
@@ -11156,14 +14137,15 @@
 }
 
 /*
-**	Copy formatted profile information into the input buffer.
+**	Copy formatted information into the input buffer.
 */
 
-#define to_ms(t) ((t) * 1000 / HZ)
-
 static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
 {
 	struct info_str info;
+#ifdef CONFIG_ALL_PPC
+	struct device_node* of_node;
+#endif
 
 	info.buffer	= ptr;
 	info.length	= len;
@@ -11171,50 +14153,35 @@
 	info.pos	= 0;
 
 	copy_info(&info, "General information:\n");
-	copy_info(&info, "  Chip " NAME53C "%s, ", np->chip_name);
-	copy_info(&info, "device id 0x%x, ",	np->device_id);
-	copy_info(&info, "revision id 0x%x\n",	np->revision_id);
-
-	copy_info(&info, "  IO port address 0x%lx, ", (u_long) np->base_io);
+	copy_info(&info, "  Chip " NAME53C "%s, device id 0x%x, "
+			 "revision id 0x%x\n",
+			 np->chip_name, np->device_id,	np->revision_id);
+	copy_info(&info, "  On PCI bus %d, device %d, function %d, "
 #ifdef __sparc__
-	copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq));
-	/* Ok to do this on all archs? */
-	copy_info(&info, "PCI bus %02x device %02x\n", np->pci_bus, np->pci_devfn);
+		"IRQ %s\n",
 #else
-	copy_info(&info, "IRQ number %d\n", (int) np->irq);
+		"IRQ %d\n",
 #endif
-
-#ifndef NCR_IOMAPPED
-	if (np->reg)
-		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
-		                  (u_long) np->reg);
+		np->bus, (np->device_fn & 0xf8) >> 3, np->device_fn & 7,
+#ifdef __sparc__
+		__irq_itoa(np->irq));
+#else
+		(int) np->irq);
 #endif
-	copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
-	copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+#ifdef CONFIG_ALL_PPC
+	of_node = find_pci_device_OFnode(np->bus, np->device_fn);
+	if (of_node && of_node->full_name)
+	    copy_info(&info, "PPC OpenFirmware path : %s\n", of_node->full_name);
+#endif
+	copy_info(&info, "  Synchronous period factor %d, "
+			 "max commands per lun %d\n",
+			 (int) np->minsync, MAX_TAGS);
 
 	if (driver_setup.debug || driver_setup.verbose > 1) {
-		copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
-		copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+		copy_info(&info, "  Debug flags 0x%x, verbosity level %d\n",
+			  driver_setup.debug, driver_setup.verbose);
 	}
 
-#ifdef SCSI_NCR_PROFILE_SUPPORT
-	copy_info(&info, "Profiling information:\n");
-	copy_info(&info, "  %-12s = %lu\n", "num_fly",	np->profile.num_fly);
-	copy_info(&info, "  %-12s = %lu\n", "num_trans",np->profile.num_trans);
-	copy_info(&info, "  %-12s = %lu\n", "num_disc",	np->profile.num_disc);
-	copy_info(&info, "  %-12s = %lu\n", "num_disc0",np->profile.num_disc0);
-	copy_info(&info, "  %-12s = %lu\n", "num_break",np->profile.num_break);
-#if 000
-	copy_info(&info, "  %-12s = %lu\n", "num_br1k",np->profile.num_br1k);
-	copy_info(&info, "  %-12s = %lu\n", "num_br2k",np->profile.num_br2k);
-	copy_info(&info, "  %-12s = %lu\n", "num_br4k",np->profile.num_br4k);
-	copy_info(&info, "  %-12s = %lu\n", "num_br8k",np->profile.num_br8k);
-	copy_info(&info, "  %-12s = %lu\n", "num_brnk",np->profile.num_brnk);
-#endif
-	copy_info(&info, "  %-12s = %lu\n", "num_int",	np->profile.num_int);
-	copy_info(&info, "  %-12s = %lu\n","num_kbytes",np->profile.num_kbytes);
-#endif
-
 	return info.pos > info.offset? info.pos - info.offset : 0;
 }
 
@@ -11222,7 +14189,7 @@
 
 /*
 **	Entry point of the scsi proc fs of the driver.
-**	- func = 0 means read  (returns profile data)
+**	- func = 0 means read  (returns adapter infos)
 **	- func = 1 means write (parse user control command)
 */
 
@@ -11268,57 +14235,164 @@
 #endif
 	}
 
-	return retv;
+	return retv;
+}
+
+
+/*=========================================================================
+**	End of proc file system stuff
+**=========================================================================
+*/
+#endif
+
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+ *  24C16 EEPROM reading.
+ *
+ *  GPOI0 - data in/data out
+ *  GPIO1 - clock
+ *  Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ *  Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+{
+	UDELAY (5);
+	switch (bit_mode){
+	case SET_BIT:
+		*gpreg |= write_bit;
+		break;
+	case CLR_BIT:
+		*gpreg &= 0xfe;
+		break;
+	case SET_CLK:
+		*gpreg |= 0x02;
+		break;
+	case CLR_CLK:
+		*gpreg &= 0xfd;
+		break;
+
+	}
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (5);
+}
+
+/*
+ *  Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ *  Read or write a bit to the NVRAM,
+ *  read if GPIO0 input else write if GPIO0 output
+ */
+static void __init 
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+{
+	S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
 }
 
+/*
+ *  Output an ACK to the NVRAM after reading,
+ *  change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	S24C16_do_bit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
 
-/*=========================================================================
-**	End of proc file system stuff
-**=========================================================================
-*/
-#endif
+/*
+ *  Input an ACK from NVRAM after writing,
+ *  change GPIO0 to input and when done back to an output
+ */
+static void __init 
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	S24C16_do_bit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
 
+/*
+ *  WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ *  GPIO0 must already be set as an output
+ */
+static void __init 
+S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, 
+		  u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	
+	for (x = 0; x < 8; x++)
+		S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+		
+	S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
 
-#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+ *  READ a byte from the NVRAM and then send an ACK to say we have got it,
+ *  GPIO0 must already be set as an input
+ */
+static void __init 
+S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, 
+	         u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	u_char read_bit;
 
-/* ---------------------------------------------------------------------
-**
-**	Try reading Symbios format nvram
-**
-** ---------------------------------------------------------------------
-**
-** GPOI0 - data in/data out
-** GPIO1 - clock
-**
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
-*/
+	*read_data = 0;
+	for (x = 0; x < 8; x++) {
+		S24C16_do_bit(np, &read_bit, 1, gpreg);
+		*read_data |= ((read_bit & 0x01) << (7 - x));
+	}
 
-#define SET_BIT 0
-#define CLR_BIT 1
-#define SET_CLK 2
-#define CLR_CLK 3
+	S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
 
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
-static void nvram_start(ncr_slot *np, u_char *gpreg);
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
-static void nvram_stop(ncr_slot *np, u_char *gpreg);
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
-
-__initfunc(
-static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
-)
+/*
+ *  Read 'len' bytes starting at 'offset'.
+ */
+static int __init 
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
 {
-	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
 	u_char	gpcntl, gpreg;
 	u_char	old_gpcntl, old_gpreg;
-	u_short	csum;
 	u_char	ack_data;
 	int	retv = 1;
+	int	x;
 
 	/* save current state of GPCNTL and GPREG */
 	old_gpreg	= INB (nc_gpreg);
@@ -11331,33 +14405,33 @@
 
 	/* this is to set NVRAM into a known state with GPIO0/1 both low */
 	gpreg = old_gpreg;
-	nvram_setBit(np, 0, &gpreg, CLR_CLK);
-	nvram_setBit(np, 0, &gpreg, CLR_BIT);
+	S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
 		
 	/* now set NVRAM inactive with GPIO0/1 both high */
-	nvram_stop(np, &gpreg);
+	S24C16_stop(np, &gpreg);
 	
 	/* activate NVRAM */
-	nvram_start(np, &gpreg);
+	S24C16_start(np, &gpreg);
 
 	/* write device code and random address MSB */
-	nvram_write_byte(np, &ack_data,
-		0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	S24C16_write_byte(np, &ack_data,
+		0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
 	if (ack_data & 0x01)
 		goto out;
 
 	/* write random address LSB */
-	nvram_write_byte(np, &ack_data,
-		(SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+	S24C16_write_byte(np, &ack_data,
+		offset & 0xff, &gpreg, &gpcntl);
 	if (ack_data & 0x01)
 		goto out;
 
 	/* regenerate START state to set up for reading */
-	nvram_start(np, &gpreg);
+	S24C16_start(np, &gpreg);
 	
 	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
-	nvram_write_byte(np, &ack_data,
-		0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	S24C16_write_byte(np, &ack_data,
+		0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
 	if (ack_data & 0x01)
 		goto out;
 
@@ -11365,30 +14439,15 @@
 	gpcntl |= 0x01;
 	OUTB (nc_gpcntl, gpcntl);
 		
-	/* input all active data - only part of total NVRAM */
-	csum = nvram_read_data(np,
-			(u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+	/* input all requested data - only part of total NVRAM */
+	for (x = 0; x < len; x++) 
+		S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
 
 	/* finally put NVRAM back in inactive mode */
 	gpcntl &= 0xfe;
 	OUTB (nc_gpcntl, gpcntl);
-	nvram_stop(np, &gpreg);
-	
-#ifdef SCSI_NCR_DEBUG_NVRAM
-printk("sym53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
-	nvram->type,
-	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
-	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
-	nvram->byte_count, sizeof(*nvram) - 12,
-	nvram->checksum, csum);
-#endif
-
-	/* check valid NVRAM signature, verify byte count and checksum */
-	if (nvram->type == 0 &&
-	    !memcmp(nvram->trailer, Symbios_trailer, 6) &&
-	    nvram->byte_count == sizeof(*nvram) - 12 &&
-	    csum == nvram->checksum)
-		retv = 0;
+	S24C16_stop(np, &gpreg);
+	retv = 0;
 out:
 	/* return GPIO0/1 to original states after having accessed NVRAM */
 	OUTB (nc_gpcntl, old_gpcntl);
@@ -11397,190 +14456,170 @@
 	return retv;
 }
 
+#undef SET_BIT
+#undef CLR_BIT
+#undef SET_CLK
+#undef CLR_CLK
+
 /*
- * Read Symbios NvRAM data and compute checksum.
+ *  Try reading Symbios NVRAM.
+ *  Return 0 if OK.
  */
-__initfunc(
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
-)
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
 {
-	int	x;
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char *data = (u_char *) nvram;
+	int len  = sizeof(*nvram);
 	u_short	csum;
+	int x;
 
-	for (x = 0; x < len; x++) 
-		nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+	/* probe the 24c16 and read the SYMBIOS 24c16 area */
+	if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+		return 1;
 
+	/* check valid NVRAM signature, verify byte count and checksum */
+	if (nvram->type != 0 ||
+	    memcmp(nvram->trailer, Symbios_trailer, 6) ||
+	    nvram->byte_count != len - 12)
+		return 1;
+
+	/* verify checksum */
 	for (x = 6, csum = 0; x < len - 6; x++)
 		csum += data[x];
+	if (csum != nvram->checksum)
+		return 1;
 
-	return csum;
+	return 0;
 }
 
 /*
- * Send START condition to NVRAM to wake it up.
+ *  93C46 EEPROM reading.
+ *
+ *  GPOI0 - data in
+ *  GPIO1 - data out
+ *  GPIO2 - clock
+ *  GPIO4 - chip select
+ *
+ *  Used by Tekram.
  */
-__initfunc(
-static void nvram_start(ncr_slot *np, u_char *gpreg)
-)
-{
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-}
 
 /*
- * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
- * GPIO0 must already be set as an output
+ *  Pulse clock bit in GPIO0
  */
-__initfunc(
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
-)
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
 {
-	int x;
-	
-	for (x = 0; x < 8; x++)
-		nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
-		
-	nvram_readAck(np, ack_data, gpreg, gpcntl);
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	UDELAY (2);
+	OUTB (nc_gpreg, *gpreg);
 }
 
-/*
- * READ a byte from the NVRAM and then send an ACK to say we have got it,
- * GPIO0 must already be set as an input
+/* 
+ *  Read bit from NVRAM
  */
-__initfunc(
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
-)
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
 {
-	int x;
-	u_char read_bit;
-
-	*read_data = 0;
-	for (x = 0; x < 8; x++) {
-		nvram_doBit(np, &read_bit, 1, gpreg);
-		*read_data |= ((read_bit & 0x01) << (7 - x));
-	}
-
-	nvram_writeAck(np, ack_data, gpreg, gpcntl);
+	UDELAY (2);
+	T93C46_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
 }
 
 /*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
+ *  Write bit to GPIO0
  */
-__initfunc(
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
-)
+static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
 {
-	OUTB (nc_gpcntl, *gpcntl & 0xfe);
-	nvram_doBit(np, 0, write_bit, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
+	if (write_bit & 0x01)
+		*gpreg |= 0x02;
+	else
+		*gpreg &= 0xfd;
+		
+	*gpreg |= 0x10;
+		
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
 }
 
 /*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
  */
-__initfunc(
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
-)
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
 {
-	OUTB (nc_gpcntl, *gpcntl | 0x01);
-	nvram_doBit(np, read_bit, 1, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
 }
 
 /*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
+ *  Send read command and address to NVRAM
  */
-__initfunc(
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
-)
+static void __init 
+T93C46_Send_Command(ncr_slot *np, u_short write_data, 
+		    u_char *read_bit, u_char *gpreg)
 {
-	nvram_setBit(np, write_bit, gpreg, SET_BIT);
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	if (read_bit)
-		*read_bit = INB (nc_gpreg);
-	nvram_setBit(np, 0, gpreg, CLR_CLK);
-	nvram_setBit(np, 0, gpreg, CLR_BIT);
+	int x;
+
+	/* send 9 bits, start bit (1), command (2), address (6)  */
+	for (x = 0; x < 9; x++)
+		T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+	*read_bit = INB (nc_gpreg);
 }
 
 /*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ *  READ 2 bytes from the NVRAM
  */
-__initfunc(
-static void nvram_stop(ncr_slot *np, u_char *gpreg)
-)
+static void __init 
+T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
 {
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 1, gpreg, SET_BIT);
+	int x;
+	u_char read_bit;
+
+	*nvram_data = 0;
+	for (x = 0; x < 16; x++) {
+		T93C46_Read_Bit(np, &read_bit, gpreg);
+
+		if (read_bit & 0x01)
+			*nvram_data |=  (0x01 << (15 - x));
+		else
+			*nvram_data &= ~(0x01 << (15 - x));
+	}
 }
 
 /*
- * Set/clear data/clock bit in GPIO0
+ *  Read Tekram NvRAM data.
  */
-__initfunc(
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
-)
+static int __init 
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
 {
-	UDELAY (5);
-	switch (bit_mode){
-	case SET_BIT:
-		*gpreg |= write_bit;
-		break;
-	case CLR_BIT:
-		*gpreg &= 0xfe;
-		break;
-	case SET_CLK:
-		*gpreg |= 0x02;
-		break;
-	case CLR_CLK:
-		*gpreg &= 0xfd;
-		break;
-
-	}
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (5);
-}
+	u_char	read_bit;
+	int	x;
 
-#undef SET_BIT 0
-#undef CLR_BIT 1
-#undef SET_CLK 2
-#undef CLR_CLK 3
+	for (x = 0; x < len; x++)  {
 
+		/* output read command and address */
+		T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 1; /* Bad */
+		T93C46_Read_Word(np, &data[x], gpreg);
+		T93C46_Stop(np, gpreg);
+	}
 
-/* ---------------------------------------------------------------------
-**
-**	Try reading Tekram format nvram
-**
-** ---------------------------------------------------------------------
-**
-** GPOI0 - data in
-** GPIO1 - data out
-** GPIO2 - clock
-** GPIO4 - chip select
-**
-**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
-*/
+	return 0;
+}
 
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
-
-__initfunc(
-static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
-)
+/*
+ *  Try reading 93C46 Tekram NVRAM.
+ */
+static int __init 
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
 {
 	u_char gpcntl, gpreg;
 	u_char old_gpcntl, old_gpreg;
-	u_short csum;
+	int retv = 1;
 
 	/* save current state of GPCNTL and GPREG */
 	old_gpreg	= INB (nc_gpreg);
@@ -11594,140 +14633,54 @@
 	OUTB (nc_gpcntl, gpcntl);
 
 	/* input all of NVRAM, 64 words */
-	csum = Tnvram_read_data(np, (u_short *) nvram,
-			sizeof(*nvram) / sizeof(short), &gpreg);
+	retv = T93C46_Read_Data(np, (u_short *) nvram,
+				sizeof(*nvram) / sizeof(short), &gpreg);
 	
 	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
 	OUTB (nc_gpcntl, old_gpcntl);
 	OUTB (nc_gpreg,  old_gpreg);
 
-	/* check data valid */
-	if (csum != 0x1234)
-		return 1;
-
-	return 0;
+	return retv;
 }
 
 /*
- * Read Tekram NvRAM data and compute checksum.
+ *  Try reading Tekram NVRAM.
+ *  Return 0 if OK.
  */
-__initfunc(
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
-)
+static int __init 
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
 {
-	u_char	read_bit;
+	u_char *data = (u_char *) nvram;
+	int len = sizeof(*nvram);
 	u_short	csum;
-	int	x;
-
-	for (x = 0, csum = 0; x < len; x++)  {
-
-		/* output read command and address */
-		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
-		if (read_bit & 0x01)
-			return 0; /* Force bad checksum */
-
-		Tnvram_Read_Word(np, &data[x], gpreg);
-		csum += data[x];
-
-		Tnvram_Stop(np, gpreg);
-	}
-
-	return csum;
-}
-
-/*
- * Send read command and address to NVRAM
- */
-__initfunc(
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
-)
-{
 	int x;
 
-	/* send 9 bits, start bit (1), command (2), address (6)  */
-	for (x = 0; x < 9; x++)
-		Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
-
-	*read_bit = INB (nc_gpreg);
-}
-
-/*
- * READ a byte from the NVRAM
- */
-__initfunc(
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
-)
-{
-	int x;
-	u_char read_bit;
-
-	*nvram_data = 0;
-	for (x = 0; x < 16; x++) {
-		Tnvram_Read_Bit(np, &read_bit, gpreg);
-
-		if (read_bit & 0x01)
-			*nvram_data |=  (0x01 << (15 - x));
-		else
-			*nvram_data &= ~(0x01 << (15 - x));
+	switch (device_id) {
+	case PCI_DEVICE_ID_NCR_53C885:
+	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		break;
+	case PCI_DEVICE_ID_NCR_53C875:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		if (!x)
+			break;
+	default:
+		x = sym_read_T93C46_nvram(np, nvram);
+		break;
 	}
-}
-
-/* 
- * Read bit from NVRAM
- */
-__initfunc(
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-)
-{
-	UDELAY (2);
-	Tnvram_Clk(np, gpreg);
-	*read_bit = INB (nc_gpreg);
-}
-
-/*
- * Write bit to GPIO0
- */
-__initfunc(
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
-)
-{
-	if (write_bit & 0x01)
-		*gpreg |= 0x02;
-	else
-		*gpreg &= 0xfd;
-		
-	*gpreg |= 0x10;
-		
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
-
-	Tnvram_Clk(np, gpreg);
-}
-
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
- */
-__initfunc(
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
-)
-{
-	*gpreg &= 0xef;
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
+	if (x)
+		return 1;
 
-	Tnvram_Clk(np, gpreg);
-}
+	/* verify checksum */
+	for (x = 0, csum = 0; x < len - 1; x += 2)
+		csum += data[x] + (data[x+1] << 8);
+	if (csum != 0x1234)
+		return 1;
 
-/*
- * Pulse clock bit in GPIO0
- */
-__initfunc(
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
-)
-{
-	OUTB (nc_gpreg, *gpreg | 0x04);
-	UDELAY (2);
-	OUTB (nc_gpreg, *gpreg);
+	return 0;
 }
 
 #endif	/* SCSI_NCR_NVRAM_SUPPORT */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx.h linux.18p17/drivers/scsi/sym53c8xx.h
--- linux.vanilla/drivers/scsi/sym53c8xx.h	Wed May  3 22:31:20 2000
+++ linux.18p17/drivers/scsi/sym53c8xx.h	Thu Oct 19 01:37:49 2000
@@ -1,7 +1,7 @@
 /******************************************************************************
 **  High Performance device driver for the Symbios 53C896 controller.
 **
-**  Copyright (C) 1998  Gerard Roudier <groudier@club-internet.fr>
+**  Copyright (C) 1998-2000  Gerard Roudier <groudier@club-internet.fr>
 **
 **  This driver also supports all the Symbios 53C8XX controller family, 
 **  except 53C810 revisions < 16, 53C825 revisions < 16 and all 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx_comm.h linux.18p17/drivers/scsi/sym53c8xx_comm.h
--- linux.vanilla/drivers/scsi/sym53c8xx_comm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/scsi/sym53c8xx_comm.h	Sun Sep 10 14:49:10 2000
@@ -0,0 +1,2717 @@
+/******************************************************************************
+**  High Performance device driver for the Symbios 53C896 controller.
+**
+**  Copyright (C) 1998-2000  Gerard Roudier <groudier@club-internet.fr>
+**
+**  This driver also supports all the Symbios 53C8XX controller family, 
+**  except 53C810 revisions < 16, 53C825 revisions < 16 and all 
+**  revisions of 53C815 controllers.
+**
+**  This driver is based on the Linux port of the FreeBSD ncr driver.
+** 
+**  Copyright (C) 1994  Wolfgang Stanglmeier
+**  
+**-----------------------------------------------------------------------------
+**  
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+**  The Linux port of the FreeBSD ncr driver has been achieved in 
+**  november 1995 by:
+**
+**          Gerard Roudier              <groudier@club-internet.fr>
+**
+**  Being given that this driver originates from the FreeBSD version, and
+**  in order to keep synergy on both, any suggested enhancements and corrections
+**  received on Linux are automatically a potential candidate for the FreeBSD 
+**  version.
+**
+**  The original driver has been written for 386bsd and FreeBSD by
+**          Wolfgang Stanglmeier        <wolf@cologne.de>
+**          Stefan Esser                <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+**  Major contributions:
+**  --------------------
+**
+**  NVRAM detection and reading.
+**    Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+*******************************************************************************
+*/
+
+/*
+**	This file contains definitions and code that the 
+**	sym53c8xx and ncr53c8xx drivers should share.
+**	The sharing will be achieved in a further version  
+**	of the driver bundle. For now, only the ncr53c8xx 
+**	driver includes	this file.
+*/
+
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
+
+/*==========================================================
+**
+**	Hmmm... What complex some PCI-HOST bridges actually 
+**	are, despite the fact that the PCI specifications 
+**	are looking so smart and simple! ;-)
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define SCSI_NCR_DYNAMIC_DMA_MAPPING
+#endif
+
+/*==========================================================
+**
+**	Miscallaneous defines.
+**
+**==========================================================
+*/
+
+#define u_char		unsigned char
+#define u_short		unsigned short
+#define u_int		unsigned int
+#define u_long		unsigned long
+
+#ifndef bcopy
+#define bcopy(s, d, n)	memcpy((d), (s), (n))
+#endif
+
+#ifndef bcmp
+#define bcmp(s, d, n)	memcmp((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(d, n)	memset((d), 0, (n))
+#endif
+ 
+#ifndef offsetof
+#define offsetof(t, m)	((size_t) (&((t *)0)->m))
+#endif
+
+/*==========================================================
+**
+**	assert ()
+**
+**==========================================================
+**
+**	modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define	assert(expression) { \
+	if (!(expression)) { \
+		(void)panic( \
+			"assertion \"%s\" failed: file \"%s\", line %d\n", \
+			#expression, \
+			__FILE__, __LINE__); \
+	} \
+}
+
+/*==========================================================
+**
+**	Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC    (0x0001)
+#define DEBUG_PHASE    (0x0002)
+#define DEBUG_QUEUE    (0x0008)
+#define DEBUG_RESULT   (0x0010)
+#define DEBUG_POINTER  (0x0020)
+#define DEBUG_SCRIPT   (0x0040)
+#define DEBUG_TINY     (0x0080)
+#define DEBUG_TIMING   (0x0100)
+#define DEBUG_NEGO     (0x0200)
+#define DEBUG_TAGS     (0x0400)
+#define DEBUG_SCATTER  (0x0800)
+#define DEBUG_IC        (0x1000)
+
+/*
+**    Enable/Disable debug messages.
+**    Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+	#define DEBUG_FLAGS ncr_debug
+#else
+	#define DEBUG_FLAGS	SCSI_NCR_DEBUG_FLAGS
+#endif
+
+/*==========================================================
+**
+**	A la VMS/CAM-3 queue management.
+**	Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+	struct xpt_quehead *flink;	/* Forward  pointer */
+	struct xpt_quehead *blink;	/* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+	(ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+	struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink	= new;
+	new->flink	= flink;
+	new->blink	= blink;
+	blink->flink	= new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink = blink;
+	blink->flink = flink;
+}
+
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+	return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+	struct xpt_quehead *head)
+{
+	struct xpt_quehead *first = list->flink;
+
+	if (first != list) {
+		struct xpt_quehead *last = list->blink;
+		struct xpt_quehead *at   = head->flink;
+
+		first->blink = head;
+		head->flink  = first;
+
+		last->flink = at;
+		at->blink   = last;
+	}
+}
+
+#define xpt_que_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos)		__xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el)			__xpt_que_del((el)->blink, (el)->flink)
+
+#define xpt_insque_head(new, head)	__xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->flink;
+
+	if (elem != head)
+		__xpt_que_del(head, elem->flink);
+	else
+		elem = 0;
+	return elem;
+}
+
+#define xpt_insque_tail(new, head)	__xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->blink;
+
+	if (elem != head)
+		__xpt_que_del(elem->blink, head);
+	else
+		elem = 0;
+	return elem;
+}
+
+/*==========================================================
+**
+**	Simple Wrapper to kernel PCI bus interface.
+**
+**	This wrapper allows to get rid of old kernel PCI 
+**	interface and still allows to preserve linux-2.0 
+**	compatibilty. In fact, it is mostly an incomplete 
+**	emulation of the new PCI code for pre-2.2 kernels.
+**	When kernel-2.0 support will be dropped, we will 
+**	just have to remove most of this code.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL		(0)
+#define PciBusNumber(d)		(d)->bus->number
+#define PciDeviceFn(d)		(d)->devfn
+#define PciVendorId(d)		(d)->vendor
+#define PciDeviceId(d)		(d)->device
+#define PciIrqLine(d)		(d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->resource[index].start;
+	if ((pdev->resource[index].flags & 0x7) == 0x4)
+		++index;
+	return ++index;
+}
+#else
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->base_address[index++];
+	if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		*base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+		++index;
+	}
+	return index;
+}
+#endif
+
+#else	/* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL		(~0u)
+#define PciBusNumber(d)		((d)>>8)
+#define PciDeviceFn(d)		((d)&0xff)
+#define __PciDev(busn, devfn)	(((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+	pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+	pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+	pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+	pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+	pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+	pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+	static unsigned short pci_index;
+	int retv;
+	unsigned char bus_number, device_fn;
+
+	if (prev == PCIDEV_NULL)
+		pci_index = 0;
+	else
+		++pci_index;
+	retv = pcibios_find_device (vendor, device, pci_index,
+				    &bus_number, &device_fn);
+	return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+	u_short vendor_id;
+	pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+	return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+	u_short device_id;
+	pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+	return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+	u_char irq;
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	return irq;
+}
+
+static int __init 
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+	u_int32 tmp;
+	
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+	*base = tmp;
+	offset += sizeof(u_int32);
+	if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+		*base |= (((u_long)tmp) << 32);
+#endif
+		offset += sizeof(u_int32);
+	}
+	return offset;
+}
+
+#endif	/* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
+/*==========================================================
+**
+**	SMP threading.
+**
+**	Assuming that SMP systems are generally high end 
+**	systems and may use several SCSI adapters, we are 
+**	using one lock per controller instead of some global 
+**	one. For the moment (linux-2.1.95), driver's entry 
+**	points are called with the 'io_request_lock' lock 
+**	held, so:
+**	- We are uselessly loosing a couple of micro-seconds 
+**	  to lock the controller data structure.
+**	- But the driver is not broken by design for SMP and 
+**	  so can be more resistant to bugs or bad changes in 
+**	  the IO sub-system code.
+**	- A small advantage could be that the interrupt code 
+**	  is grained as wished (e.g.: by controller).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
+#define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&DRIVER_SMP_LOCK, flags)
+#define	NCR_UNLOCK_DRIVER(flags)   \
+		spin_unlock_irqrestore(&DRIVER_SMP_LOCK, flags)
+
+#define NCR_INIT_LOCK_NCB(np)      spin_lock_init(&np->smp_lock)
+#define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
+#define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
+
+#define	NCR_LOCK_SCSI_DONE(np, flags) \
+		spin_lock_irqsave(&io_request_lock, flags)
+#define	NCR_UNLOCK_SCSI_DONE(np, flags) \
+		spin_unlock_irqrestore(&io_request_lock, flags)
+
+#else
+
+#define	NCR_LOCK_DRIVER(flags)     do { save_flags(flags); cli(); } while (0)
+#define	NCR_UNLOCK_DRIVER(flags)   do { restore_flags(flags); } while (0)
+
+#define	NCR_INIT_LOCK_NCB(np)      do { } while (0)
+#define	NCR_LOCK_NCB(np, flags)    do { save_flags(flags); cli(); } while (0)
+#define	NCR_UNLOCK_NCB(np, flags)  do { restore_flags(flags); } while (0)
+
+#define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
+#define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
+
+#endif
+
+/*==========================================================
+**
+**	Memory mapped IO
+**
+**	Since linux-2.1, we must use ioremap() to map the io 
+**	memory space and iounmap() to unmap it. This allows 
+**	portability. Linux 1.3.X and 2.0.X allow to remap 
+**	physical pages addresses greater than the highest 
+**	physical memory address to kernel virtual pages with 
+**	vremap() / vfree(). That was not portable but worked 
+**	with i386 architecture.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+#ifdef __sparc__
+#  include <asm/irq.h>
+#  define pcivtobus(p)			bus_dvma_to_mem(p)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#elif defined(__alpha__)
+#  define pcivtobus(p)			((p) & 0xfffffffful)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#else	/* others */
+#  define pcivtobus(p)			(p)
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
+#endif
+
+#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
+static u_long __init remap_pci_mem(u_long base, u_long size)
+{
+	u_long page_base	= ((u_long) base) & PAGE_MASK;
+	u_long page_offs	= ((u_long) base) - page_base;
+	u_long page_remapped	= (u_long) ioremap(page_base, page_offs+size);
+
+	return page_remapped? (page_remapped + page_offs) : 0UL;
+}
+
+static void __init unmap_pci_mem(u_long vaddr, u_long size)
+{
+	if (vaddr)
+		iounmap((void *) (vaddr & PAGE_MASK));
+}
+
+#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */
+
+/*==========================================================
+**
+**	Insert a delay in micro-seconds and milli-seconds.
+**
+**	Under Linux, udelay() is restricted to delay < 
+**	1 milli-second. In fact, it generally works for up 
+**	to 1 second delay. Since 2.1.105, the mdelay() function 
+**	is provided for delays in milli-seconds.
+**	Under 2.0 kernels, udelay() is an inline function 
+**	that is very inaccurate on Pentium processors.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
+#define UDELAY udelay
+#define MDELAY mdelay
+#else
+static void UDELAY(long us) { udelay(us); }
+static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
+#endif
+
+/*==========================================================
+**
+**	Simple power of two buddy-like allocator.
+**
+**	This simple code is not intended to be fast, but to 
+**	provide power of 2 aligned memory allocations.
+**	Since the SCRIPTS processor only supplies 8 bit 
+**	arithmetic, this allocator allows simple and fast 
+**	address calculations  from the SCRIPTS code.
+**	In addition, cache line alignment is guaranteed for 
+**	power of 2 cache line size.
+**	Enhanced in linux-2.3.44 to provide a memory pool 
+**	per pcidev to support dynamic dma mapping. (I would 
+**	have preferred a real bus astraction, btw).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
+#else
+#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)
+#endif
+
+#define MEMO_SHIFT	4	/* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER	0	/* 1 PAGE  maximum */
+#else
+#define MEMO_PAGE_ORDER	1	/* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED	/* Free unused pages immediately */
+#define MEMO_WARN	1
+#define MEMO_GFP_FLAGS	GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT	(PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE	(1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK	(MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t;	/* Enough bits to bit-hack addresses */
+typedef pcidev_t m_bush_t;	/* Something that addresses DMAable */
+
+typedef struct m_link {		/* Link between free memory chunks */
+	struct m_link *next;
+} m_link_s;
+
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+typedef struct m_vtob {		/* Virtual to Bus address translation */
+	struct m_vtob *next;
+	m_addr_t vaddr;
+	m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT		5
+#define VTOB_HASH_SIZE		(1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK		(VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m)	\
+	((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif
+
+typedef struct m_pool {		/* Memory pool of a given kind */
+#ifdef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+	m_bush_t bush;
+	m_addr_t (*getp)(struct m_pool *);
+	void (*freep)(struct m_pool *, m_addr_t);
+#define M_GETP()		mp->getp(mp)
+#define M_FREEP(p)		mp->freep(mp, p)
+#define GetPages()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define FreePages(p)		free_pages(p, MEMO_PAGE_ORDER)
+	int nump;
+	m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+	struct m_pool *next;
+#else
+#define M_GETP()		__GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define M_FREEP(p)		free_pages(p, MEMO_PAGE_ORDER)
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+	struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
+
+static void *___m_alloc(m_pool_s *mp, int size)
+{
+	int i = 0;
+	int s = (1 << MEMO_SHIFT);
+	int j;
+	m_addr_t a;
+	m_link_s *h = mp->h;
+
+	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+		return 0;
+
+	while (size > s) {
+		s <<= 1;
+		++i;
+	}
+
+	j = i;
+	while (!h[j].next) {
+		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+			h[j].next = (m_link_s *) M_GETP();
+			if (h[j].next)
+				h[j].next->next = 0;
+			break;
+		}
+		++j;
+		s <<= 1;
+	}
+	a = (m_addr_t) h[j].next;
+	if (a) {
+		h[j].next = h[j].next->next;
+		while (j > i) {
+			j -= 1;
+			s >>= 1;
+			h[j].next = (m_link_s *) (a+s);
+			h[j].next->next = 0;
+		}
+	}
+#ifdef DEBUG
+	printk("___m_alloc(%d) = %p\n", size, (void *) a);
+#endif
+	return (void *) a;
+}
+
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
+{
+	int i = 0;
+	int s = (1 << MEMO_SHIFT);
+	m_link_s *q;
+	m_addr_t a, b;
+	m_link_s *h = mp->h;
+
+#ifdef DEBUG
+	printk("___m_free(%p, %d)\n", ptr, size);
+#endif
+
+	if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+		return;
+
+	while (size > s) {
+		s <<= 1;
+		++i;
+	}
+
+	a = (m_addr_t) ptr;
+
+	while (1) {
+#ifdef MEMO_FREE_UNUSED
+		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+			M_FREEP(a);
+			break;
+		}
+#endif
+		b = a ^ s;
+		q = &h[i];
+		while (q->next && q->next != (m_link_s *) b) {
+			q = q->next;
+		}
+		if (!q->next) {
+			((m_link_s *) a)->next = h[i].next;
+			h[i].next = (m_link_s *) a;
+			break;
+		}
+		q->next = q->next->next;
+		a = a & b;
+		s <<= 1;
+		++i;
+	}
+}
+
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
+{
+	void *p;
+
+	p = ___m_alloc(mp, size);
+
+	if (DEBUG_FLAGS & DEBUG_ALLOC)
+		printk ("new %-10s[%4d] @%p.\n", name, size, p);
+
+	if (p)
+		bzero(p, size);
+	else if (uflags & MEMO_WARN)
+		printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
+
+	return p;
+}
+
+#define __m_calloc(mp, s, n)	__m_calloc2(mp, s, n, MEMO_WARN)
+
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
+	if (DEBUG_FLAGS & DEBUG_ALLOC)
+		printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+	___m_free(mp, ptr, size);
+
+}
+
+/*
+ * With pci bus iommu support, we use a default pool of unmapped memory 
+ * for memory we donnot need to DMA from/to and one pool per pcidev for 
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
+
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+static m_pool_s mp0;
+
+#else
+
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+	m_addr_t m = GetPages();
+	if (m)
+		++mp->nump;
+	return m;
+}
+
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+	FreePages(m);
+	--mp->nump;
+}
+
+static m_pool_s mp0 = {0, ___mp0_getp, ___mp0_freep};
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+static void *m_calloc(int size, char *name)
+{
+	u_long flags;
+	void *m;
+	NCR_LOCK_DRIVER(flags);
+	m = __m_calloc(&mp0, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+	return m;
+}
+
+static void m_free(void *ptr, int size, char *name)
+{
+	u_long flags;
+	NCR_LOCK_DRIVER(flags);
+	__m_free(&mp0, ptr, size, name);
+	NCR_UNLOCK_DRIVER(flags);
+}
+
+/*
+ * DMAable pools.
+ */
+
+#ifndef	SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Without pci bus iommu support, all the memory is assumed DMAable */
+
+#define __m_calloc_dma(b, s, n)		m_calloc(s, n)
+#define __m_free_dma(b, p, s, n)	m_free(p, s, n)
+#define __vtobus(b, p)			virt_to_bus(p)
+
+#else
+
+/*
+ * With pci bus iommu support, we maintain one pool per pcidev and a 
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+	m_addr_t vp;
+	m_vtob_s *vbp;
+
+	vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+	if (vbp) {
+		dma_addr_t daddr;
+		vp = (m_addr_t) pci_alloc_consistent(mp->bush,
+						PAGE_SIZE<<MEMO_PAGE_ORDER,
+						&daddr);
+		if (vp) {
+			int hc = VTOB_HASH_CODE(vp);
+			vbp->vaddr = vp;
+			vbp->baddr = daddr;
+			vbp->next = mp->vtob[hc];
+			mp->vtob[hc] = vbp;
+			++mp->nump;
+			return vp;
+		}
+	}
+	if (vbp)
+		__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+	return 0;
+}
+
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+	m_vtob_s **vbpp, *vbp;
+	int hc = VTOB_HASH_CODE(m);
+
+	vbpp = &mp->vtob[hc];
+	while (*vbpp && (*vbpp)->vaddr != m)
+		vbpp = &(*vbpp)->next;
+	if (*vbpp) {
+		vbp = *vbpp;
+		*vbpp = (*vbpp)->next;
+		pci_free_consistent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+				    (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+		__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+		--mp->nump;
+	}
+}
+
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+	return mp;
+}
+
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+	m_pool_s *mp;
+	mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+	if (mp) {
+		bzero(mp, sizeof(*mp));
+		mp->bush = bush;
+		mp->getp = ___dma_getp;
+		mp->freep = ___dma_freep;
+		mp->next = mp0.next;
+		mp0.next = mp;
+	}
+	return mp;
+}
+
+static void ___del_dma_pool(m_pool_s *p)
+{
+	struct m_pool **pp = &mp0.next;
+
+	while (*pp && *pp != p)
+		pp = &(*pp)->next;
+	if (*pp) {
+		*pp = (*pp)->next;
+		__m_free(&mp0, p, sizeof(*p), "MPOOL");
+	}
+}
+
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+	void *m = 0;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (!mp)
+		mp = ___cre_dma_pool(bush);
+	if (mp)
+		m = __m_calloc(mp, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
+
+	return m;
+}
+
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+	u_long flags;
+	struct m_pool *mp;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp)
+		__m_free(mp, m, size, name);
+	if (mp && !mp->nump)
+		___del_dma_pool(mp);
+	NCR_UNLOCK_DRIVER(flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+	u_long flags;
+	m_pool_s *mp;
+	int hc = VTOB_HASH_CODE(m);
+	m_vtob_s *vp = 0;
+	m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+	NCR_LOCK_DRIVER(flags);
+	mp = ___get_dma_pool(bush);
+	if (mp) {
+		vp = mp->vtob[hc];
+		while (vp && (m_addr_t) vp->vaddr != a)
+			vp = vp->next;
+	}
+	NCR_UNLOCK_DRIVER(flags);
+	return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define _m_calloc_dma(np, s, n)		__m_calloc_dma(np->pdev, s, n)
+#define _m_free_dma(np, p, s, n)	__m_free_dma(np->pdev, p, s, n)
+#define m_calloc_dma(s, n)		_m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n)		_m_free_dma(np, p, s, n)
+#define _vtobus(np, p)			__vtobus(np->pdev, p)
+#define vtobus(p)			_vtobus(np, p)
+
+/*
+ *  Deal with DMA mapping/unmapping.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Linux versions prior to pci bus iommu kernel interface */
+
+#define __unmap_scsi_data(pdev, cmd)	do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd)	((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd)	do {; } while (0)
+
+#define scsi_sg_dma_address(sc)		vtobus((sc)->address)
+#define scsi_sg_dma_len(sc)		((sc)->length)
+
+#else
+
+/* Linux version with pci bus iommu kernel interface */
+
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped	SCp.phase
+#define __data_mapping	SCp.have_data_in
+
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_unmap_single(pdev, cmd->__data_mapping,
+				 cmd->request_bufflen, dma_dir);
+		break;
+	}
+	cmd->__data_mapped = 0;
+}
+
+static u_long __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	dma_addr_t mapping;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	if (cmd->request_bufflen == 0)
+		return 0;
+
+	mapping = pci_map_single(pdev, cmd->request_buffer,
+				 cmd->request_bufflen, dma_dir);
+	cmd->__data_mapped = 1;
+	cmd->__data_mapping = mapping;
+
+	return mapping;
+}
+
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int use_sg;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	if (cmd->use_sg == 0)
+		return 0;
+
+	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+	cmd->__data_mapped = 2;
+	cmd->__data_mapping = use_sg;
+
+	return use_sg;
+}
+
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	switch(cmd->__data_mapped) {
+	case 2:
+		pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+		break;
+	case 1:
+		pci_dma_sync_single(pdev, cmd->__data_mapping,
+				    cmd->request_bufflen, dma_dir);
+		break;
+	}
+}
+
+#define scsi_sg_dma_address(sc)		sg_dma_address(sc)
+#define scsi_sg_dma_len(sc)		sg_dma_len(sc)
+
+#endif	/* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define unmap_scsi_data(np, cmd)	__unmap_scsi_data(np->pdev, cmd)
+#define map_scsi_single_data(np, cmd)	__map_scsi_single_data(np->pdev, cmd)
+#define map_scsi_sg_data(np, cmd)	__map_scsi_sg_data(np->pdev, cmd)
+#define sync_scsi_data(np, cmd)		__sync_scsi_data(np->pdev, cmd)
+
+/*==========================================================
+**
+**	SCSI data transfer direction
+**
+**	Until some linux kernel version near 2.3.40, 
+**	low-level scsi drivers were not told about data 
+**	transfer direction. We check the existence of this 
+**	feature that has been expected for a _long_ time by 
+**	all SCSI driver developers by just testing against 
+**	the definition of SCSI_DATA_UNKNOWN. Indeed this is 
+**	a hack, but testing against a kernel version would 
+**	have been a shame. ;-)
+**
+**==========================================================
+*/
+#ifdef	SCSI_DATA_UNKNOWN
+
+#define scsi_data_direction(cmd)	(cmd->sc_data_direction)
+
+#else
+
+#define	SCSI_DATA_UNKNOWN	0
+#define	SCSI_DATA_WRITE		1
+#define	SCSI_DATA_READ		2
+#define	SCSI_DATA_NONE		3
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+	int direction;
+
+	switch((int) cmd->cmnd[0]) {
+	case 0x08:  /*	READ(6)				08 */
+	case 0x28:  /*	READ(10)			28 */
+	case 0xA8:  /*	READ(12)			A8 */
+		direction = SCSI_DATA_READ;
+		break;
+	case 0x0A:  /*	WRITE(6)			0A */
+	case 0x2A:  /*	WRITE(10)			2A */
+	case 0xAA:  /*	WRITE(12)			AA */
+		direction = SCSI_DATA_WRITE;
+		break;
+	default:
+		direction = SCSI_DATA_UNKNOWN;
+		break;
+	}
+
+	return direction;
+}
+
+#endif	/* SCSI_DATA_UNKNOWN */
+
+/*==========================================================
+**
+**	Driver setup.
+**
+**	This structure is initialized from linux config 
+**	options. It can be overridden at boot-up by the boot 
+**	command line.
+**
+**==========================================================
+*/
+static struct ncr_driver_setup
+	driver_setup			= SCSI_NCR_DRIVER_SETUP;
+
+#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+
+/*==========================================================
+**
+**	Structures used by the detection routine to transmit 
+**	device configuration to the attach function.
+**
+**==========================================================
+*/
+typedef struct {
+	int	bus;
+	u_char	device_fn;
+	u_long	base;
+	u_long	base_2;
+	u_long	io_port;
+	int	irq;
+/* port and reg fields to use INB, OUTB macros */
+	u_long	base_io;
+	volatile struct ncr_reg	*reg;
+} ncr_slot;
+
+/*==========================================================
+**
+**	Structure used to store the NVRAM content.
+**
+**==========================================================
+*/
+typedef struct {
+	int type;
+#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
+#define	SCSI_NCR_TEKRAM_NVRAM	(2)
+#ifdef	SCSI_NCR_NVRAM_SUPPORT
+	union {
+		Symbios_nvram Symbios;
+		Tekram_nvram Tekram;
+	} data;
+#endif
+} ncr_nvram;
+
+/*==========================================================
+**
+**	Structure used by detection routine to save data on 
+**	each detected board for attach.
+**
+**==========================================================
+*/
+typedef struct {
+	pcidev_t  pdev;
+	ncr_slot  slot;
+	ncr_chip  chip;
+	ncr_nvram *nvram;
+	u_char host_id;
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+	u_char pqs_pds;
+#endif
+	int attach_done;
+} ncr_device;
+
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
+
+/*==========================================================
+**
+**	NVRAM detection and reading.
+**	 
+**	Currently supported:
+**	- 24C16 EEPROM with both Symbios and Tekram layout.
+**	- 93C46 EEPROM with Tekram layout.
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+ *  24C16 EEPROM reading.
+ *
+ *  GPOI0 - data in/data out
+ *  GPIO1 - clock
+ *  Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ *  Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+{
+	UDELAY (5);
+	switch (bit_mode){
+	case SET_BIT:
+		*gpreg |= write_bit;
+		break;
+	case CLR_BIT:
+		*gpreg &= 0xfe;
+		break;
+	case SET_CLK:
+		*gpreg |= 0x02;
+		break;
+	case CLR_CLK:
+		*gpreg &= 0xfd;
+		break;
+
+	}
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (5);
+}
+
+/*
+ *  Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ *  Read or write a bit to the NVRAM,
+ *  read if GPIO0 input else write if GPIO0 output
+ */
+static void __init 
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+{
+	S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ *  Output an ACK to the NVRAM after reading,
+ *  change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	S24C16_do_bit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  Input an ACK from NVRAM after writing,
+ *  change GPIO0 to input and when done back to an output
+ */
+static void __init 
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	S24C16_do_bit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ *  GPIO0 must already be set as an output
+ */
+static void __init 
+S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, 
+		  u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	
+	for (x = 0; x < 8; x++)
+		S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+		
+	S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  READ a byte from the NVRAM and then send an ACK to say we have got it,
+ *  GPIO0 must already be set as an input
+ */
+static void __init 
+S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, 
+	         u_char *gpreg, u_char *gpcntl)
+{
+	int x;
+	u_char read_bit;
+
+	*read_data = 0;
+	for (x = 0; x < 8; x++) {
+		S24C16_do_bit(np, &read_bit, 1, gpreg);
+		*read_data |= ((read_bit & 0x01) << (7 - x));
+	}
+
+	S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  Read 'len' bytes starting at 'offset'.
+ */
+static int __init 
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
+{
+	u_char	gpcntl, gpreg;
+	u_char	old_gpcntl, old_gpreg;
+	u_char	ack_data;
+	int	retv = 1;
+	int	x;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+	gpcntl		= old_gpcntl & 0xfc;
+
+	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+	OUTB (nc_gpreg,  old_gpreg);
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* this is to set NVRAM into a known state with GPIO0/1 both low */
+	gpreg = old_gpreg;
+	S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+		
+	/* now set NVRAM inactive with GPIO0/1 both high */
+	S24C16_stop(np, &gpreg);
+	
+	/* activate NVRAM */
+	S24C16_start(np, &gpreg);
+
+	/* write device code and random address MSB */
+	S24C16_write_byte(np, &ack_data,
+		0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* write random address LSB */
+	S24C16_write_byte(np, &ack_data,
+		offset & 0xff, &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* regenerate START state to set up for reading */
+	S24C16_start(np, &gpreg);
+	
+	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+	S24C16_write_byte(np, &ack_data,
+		0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* now set up GPIO0 for inputting data */
+	gpcntl |= 0x01;
+	OUTB (nc_gpcntl, gpcntl);
+		
+	/* input all requested data - only part of total NVRAM */
+	for (x = 0; x < len; x++) 
+		S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+	/* finally put NVRAM back in inactive mode */
+	gpcntl &= 0xfe;
+	OUTB (nc_gpcntl, gpcntl);
+	S24C16_stop(np, &gpreg);
+	retv = 0;
+out:
+	/* return GPIO0/1 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+/*
+ *  Try reading Symbios NVRAM.
+ *  Return 0 if OK.
+ */
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+{
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char *data = (u_char *) nvram;
+	int len  = sizeof(*nvram);
+	u_short	csum;
+	int x;
+
+	/* probe the 24c16 and read the SYMBIOS 24c16 area */
+	if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+		return 1;
+
+	/* check valid NVRAM signature, verify byte count and checksum */
+	if (nvram->type != 0 ||
+	    memcmp(nvram->trailer, Symbios_trailer, 6) ||
+	    nvram->byte_count != len - 12)
+		return 1;
+
+	/* verify checksum */
+	for (x = 6, csum = 0; x < len - 6; x++)
+		csum += data[x];
+	if (csum != nvram->checksum)
+		return 1;
+
+	return 0;
+}
+
+/*
+ *  93C46 EEPROM reading.
+ *
+ *  GPOI0 - data in
+ *  GPIO1 - data out
+ *  GPIO2 - clock
+ *  GPIO4 - chip select
+ *
+ *  Used by Tekram.
+ */
+
+/*
+ *  Pulse clock bit in GPIO0
+ */
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
+{
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	UDELAY (2);
+	OUTB (nc_gpreg, *gpreg);
+}
+
+/* 
+ *  Read bit from NVRAM
+ */
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+{
+	UDELAY (2);
+	T93C46_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  Write bit to GPIO0
+ */
+static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+{
+	if (write_bit & 0x01)
+		*gpreg |= 0x02;
+	else
+		*gpreg &= 0xfd;
+		
+	*gpreg |= 0x10;
+		
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
+{
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
+
+	T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send read command and address to NVRAM
+ */
+static void __init 
+T93C46_Send_Command(ncr_slot *np, u_short write_data, 
+		    u_char *read_bit, u_char *gpreg)
+{
+	int x;
+
+	/* send 9 bits, start bit (1), command (2), address (6)  */
+	for (x = 0; x < 9; x++)
+		T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  READ 2 bytes from the NVRAM
+ */
+static void __init 
+T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+{
+	int x;
+	u_char read_bit;
+
+	*nvram_data = 0;
+	for (x = 0; x < 16; x++) {
+		T93C46_Read_Bit(np, &read_bit, gpreg);
+
+		if (read_bit & 0x01)
+			*nvram_data |=  (0x01 << (15 - x));
+		else
+			*nvram_data &= ~(0x01 << (15 - x));
+	}
+}
+
+/*
+ *  Read Tekram NvRAM data.
+ */
+static int __init 
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
+{
+	u_char	read_bit;
+	int	x;
+
+	for (x = 0; x < len; x++)  {
+
+		/* output read command and address */
+		T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 1; /* Bad */
+		T93C46_Read_Word(np, &data[x], gpreg);
+		T93C46_Stop(np, gpreg);
+	}
+
+	return 0;
+}
+
+/*
+ *  Try reading 93C46 Tekram NVRAM.
+ */
+static int __init 
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
+{
+	u_char gpcntl, gpreg;
+	u_char old_gpcntl, old_gpreg;
+	int retv = 1;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+
+	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+	   1/2/4 out */
+	gpreg = old_gpreg & 0xe9;
+	OUTB (nc_gpreg, gpreg);
+	gpcntl = (old_gpcntl & 0xe9) | 0x09;
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* input all of NVRAM, 64 words */
+	retv = T93C46_Read_Data(np, (u_short *) nvram,
+				sizeof(*nvram) / sizeof(short), &gpreg);
+	
+	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+/*
+ *  Try reading Tekram NVRAM.
+ *  Return 0 if OK.
+ */
+static int __init 
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
+{
+	u_char *data = (u_char *) nvram;
+	int len = sizeof(*nvram);
+	u_short	csum;
+	int x;
+
+	switch (device_id) {
+	case PCI_DEVICE_ID_NCR_53C885:
+	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		break;
+	case PCI_DEVICE_ID_NCR_53C875:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		if (!x)
+			break;
+	default:
+		x = sym_read_T93C46_nvram(np, nvram);
+		break;
+	}
+	if (x)
+		return 1;
+
+	/* verify checksum */
+	for (x = 0, csum = 0; x < len - 1; x += 2)
+		csum += data[x] + (data[x+1] << 8);
+	if (csum != 0x1234)
+		return 1;
+
+	return 0;
+}
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+
+/*===================================================================
+**
+**    Detect and try to read SYMBIOS and TEKRAM NVRAM.
+**
+**    Data can be used to order booting of boards.
+**
+**    Data is saved in ncr_device structure if NVRAM found. This
+**    is then used to find drive boot order for ncr_attach().
+**
+**    NVRAM data is passed to Scsi_Host_Template later during 
+**    ncr_attach() for any device set up.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
+{
+	devp->nvram = nvp;
+	if (!nvp)
+		return;
+	/*
+	**    Get access to chip IO registers
+	*/
+#ifdef SCSI_NCR_IOMAPPED
+	request_region(devp->slot.io_port, 128, NAME53C8XX);
+	devp->slot.base_io = devp->slot.io_port;
+#else
+	devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
+	if (!devp->slot.reg)
+		return;
+#endif
+
+	/*
+	**    Try to read SYMBIOS nvram.
+	**    Try to read TEKRAM nvram if Symbios nvram not found.
+	*/
+	if	(!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+		nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
+	else if	(!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+					&nvp->data.Tekram))
+		nvp->type = SCSI_NCR_TEKRAM_NVRAM;
+	else {
+		nvp->type = 0;
+		devp->nvram = 0;
+	}
+
+	/*
+	** Release access to chip IO registers
+	*/
+#ifdef SCSI_NCR_IOMAPPED
+	release_region(devp->slot.base_io, 128);
+#else
+	unmap_pci_mem((u_long) devp->slot.reg, 128ul);
+#endif
+
+}
+
+/*===================================================================
+**
+**	Display the content of NVRAM for debugging purpose.
+**
+**===================================================================
+*/
+#ifdef	SCSI_NCR_DEBUG_NVRAM
+static void __init ncr_display_Symbios_nvram(Symbios_nvram *nvram)
+{
+	int i;
+
+	/* display Symbios nvram host data */
+	printk(KERN_DEBUG NAME53C8XX ": HOST ID=%d%s%s%s%s%s\n",
+		nvram->host_id & 0x0f,
+		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
+		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERBOSE"	:"", 
+		(nvram->flags  & SYMBIOS_CHS_MAPPING)	? " CHS_ALT"	:"", 
+		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
+
+	/* display Symbios nvram drive data */
+	for (i = 0 ; i < 15 ; i++) {
+		struct Symbios_target *tn = &nvram->target[i];
+		printk(KERN_DEBUG NAME53C8XX 
+		"-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+		i,
+		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
+		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
+		tn->bus_width,
+		tn->sync_period / 4,
+		tn->timeout);
+	}
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+static void __init ncr_display_Tekram_nvram(Tekram_nvram *nvram)
+{
+	int i, tags, boot_delay;
+	char *rem;
+
+	/* display Tekram nvram host data */
+	tags = 2 << nvram->max_tags_index;
+	boot_delay = 0;
+	if (nvram->boot_delay_index < 6)
+		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+	default:
+	case 0:	rem = "";			break;
+	case 1: rem = " REMOVABLE=boot device";	break;
+	case 2: rem = " REMOVABLE=all";		break;
+	}
+
+	printk(KERN_DEBUG NAME53C8XX
+		": HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+		nvram->host_id & 0x0f,
+		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
+		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
+		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
+		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
+		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
+		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
+		rem, boot_delay, tags);
+
+	/* display Tekram nvram drive data */
+	for (i = 0; i <= 15; i++) {
+		int sync, j;
+		struct Tekram_target *tn = &nvram->target[i];
+		j = tn->sync_index & 0xf;
+		sync = Tekram_sync[j];
+		printk(KERN_DEBUG NAME53C8XX "-%d:%s%s%s%s%s%s PERIOD=%d\n",
+		i,
+		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
+		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
+		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
+		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
+		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
+		sync);
+	}
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+
+
+/*===================================================================
+**
+**	Utility routines that protperly return data through /proc FS.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+	char *buffer;
+	int length;
+	int offset;
+	int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+	if (info->pos + len > info->length)
+		len = info->length - info->pos;
+
+	if (info->pos + len < info->offset) {
+		info->pos += len;
+		return;
+	}
+	if (info->pos < info->offset) {
+		data += (info->offset - info->pos);
+		len  -= (info->offset - info->pos);
+	}
+
+	if (len > 0) {
+		memcpy(info->buffer + info->pos, data, len);
+		info->pos += len;
+	}
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+	va_list args;
+	char buf[81];
+	int len;
+
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+
+	copy_mem_info(info, buf, len);
+	return len;
+}
+
+#endif
+
+/*===================================================================
+**
+**	Driver setup from the boot command line
+**
+**===================================================================
+*/
+
+#ifdef MODULE
+#define	ARG_SEP	' '
+#else
+#define	ARG_SEP	','
+#endif
+
+#define OPT_TAGS		1
+#define OPT_MASTER_PARITY	2
+#define OPT_SCSI_PARITY		3
+#define OPT_DISCONNECTION	4
+#define OPT_SPECIAL_FEATURES	5
+#define OPT_ULTRA_SCSI		6
+#define OPT_FORCE_SYNC_NEGO	7
+#define OPT_REVERSE_PROBE	8
+#define OPT_DEFAULT_SYNC	9
+#define OPT_VERBOSE		10
+#define OPT_DEBUG		11
+#define OPT_BURST_MAX		12
+#define OPT_LED_PIN		13
+#define OPT_MAX_WIDE		14
+#define OPT_SETTLE_DELAY	15
+#define OPT_DIFF_SUPPORT	16
+#define OPT_IRQM		17
+#define OPT_PCI_FIX_UP		18
+#define OPT_BUS_CHECK		19
+#define OPT_OPTIMIZE		20
+#define OPT_RECOVERY		21
+#define OPT_SAFE_SETUP		22
+#define OPT_USE_NVRAM		23
+#define OPT_EXCLUDE		24
+#define OPT_HOST_ID		25
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB		26
+#endif
+
+static char setup_token[] __initdata = 
+	"tags:"   "mpar:"
+	"spar:"   "disc:"
+	"specf:"  "ultra:"
+	"fsn:"    "revprob:"
+	"sync:"   "verb:"
+	"debug:"  "burst:"
+	"led:"    "wide:"
+	"settle:" "diff:"
+	"irqm:"   "pcifix:"
+	"buschk:" "optim:"
+	"recovery:"
+	"safe:"   "nvram:"
+	"excl:"   "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+	"iarb:"
+#endif
+	;	/* DONNOT REMOVE THIS ';' */
+
+#ifdef MODULE
+#define	ARG_SEP	' '
+#else
+#define	ARG_SEP	','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+	char *cur = setup_token;
+	char *pc;
+	int i = 0;
+
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		++pc;
+		++i;
+		if (!strncmp(p, cur, pc - cur))
+			return i;
+		cur = pc;
+	}
+	return 0;
+}
+
+
+static int __init sym53c8xx__setup(char *str)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+	char *cur = str;
+	char *pc, *pv;
+	int i, val, c;
+	int xi = 0;
+
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		char *pe;
+
+		val = 0;
+		pv = pc;
+		c = *++pv;
+
+		if	(c == 'n')
+			val = 0;
+		else if	(c == 'y')
+			val = 1;
+		else
+			val = (int) simple_strtoul(pv, &pe, 0);
+
+		switch (get_setup_token(cur)) {
+		case OPT_TAGS:
+			driver_setup.default_tags = val;
+			if (pe && *pe == '/') {
+				i = 0;
+				while (*pe && *pe != ARG_SEP && 
+					i < sizeof(driver_setup.tag_ctrl)-1) {
+					driver_setup.tag_ctrl[i++] = *pe++;
+				}
+				driver_setup.tag_ctrl[i] = '\0';
+			}
+			break;
+		case OPT_MASTER_PARITY:
+			driver_setup.master_parity = val;
+			break;
+		case OPT_SCSI_PARITY:
+			driver_setup.scsi_parity = val;
+			break;
+		case OPT_DISCONNECTION:
+			driver_setup.disconnection = val;
+			break;
+		case OPT_SPECIAL_FEATURES:
+			driver_setup.special_features = val;
+			break;
+		case OPT_ULTRA_SCSI:
+			driver_setup.ultra_scsi	= val;
+			break;
+		case OPT_FORCE_SYNC_NEGO:
+			driver_setup.force_sync_nego = val;
+			break;
+		case OPT_REVERSE_PROBE:
+			driver_setup.reverse_probe = val;
+			break;
+		case OPT_DEFAULT_SYNC:
+			driver_setup.default_sync = val;
+			break;
+		case OPT_VERBOSE:
+			driver_setup.verbose = val;
+			break;
+		case OPT_DEBUG:
+			driver_setup.debug = val;
+			break;
+		case OPT_BURST_MAX:
+			driver_setup.burst_max = val;
+			break;
+		case OPT_LED_PIN:
+			driver_setup.led_pin = val;
+			break;
+		case OPT_MAX_WIDE:
+			driver_setup.max_wide = val? 1:0;
+			break;
+		case OPT_SETTLE_DELAY:
+			driver_setup.settle_delay = val;
+			break;
+		case OPT_DIFF_SUPPORT:
+			driver_setup.diff_support = val;
+			break;
+		case OPT_IRQM:
+			driver_setup.irqm = val;
+			break;
+		case OPT_PCI_FIX_UP:
+			driver_setup.pci_fix_up	= val;
+			break;
+		case OPT_BUS_CHECK:
+			driver_setup.bus_check = val;
+			break;
+		case OPT_OPTIMIZE:
+			driver_setup.optimize = val;
+			break;
+		case OPT_RECOVERY:
+			driver_setup.recovery = val;
+			break;
+		case OPT_USE_NVRAM:
+			driver_setup.use_nvram = val;
+			break;
+		case OPT_SAFE_SETUP:
+			memcpy(&driver_setup, &driver_safe_setup,
+				sizeof(driver_setup));
+			break;
+		case OPT_EXCLUDE:
+			if (xi < SCSI_NCR_MAX_EXCLUDES)
+				driver_setup.excludes[xi++] = val;
+			break;
+		case OPT_HOST_ID:
+			driver_setup.host_id = val;
+			break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+		case OPT_IARB:
+			driver_setup.iarb = val;
+			break;
+#endif
+		default:
+			printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+			break;
+		}
+
+		if ((cur = strchr(cur, ARG_SEP)) != NULL)
+			++cur;
+	}
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+	return 1;
+}
+
+/*===================================================================
+**
+**	Get device queue depth from boot command line.
+**
+**===================================================================
+*/
+#define DEF_DEPTH	(driver_setup.default_tags)
+#define ALL_TARGETS	-2
+#define NO_TARGET	-1
+#define ALL_LUNS	-2
+#define NO_LUN		-1
+
+static int device_queue_depth(int unit, int target, int lun)
+{
+	int c, h, t, u, v;
+	char *p = driver_setup.tag_ctrl;
+	char *ep;
+
+	h = -1;
+	t = NO_TARGET;
+	u = NO_LUN;
+	while ((c = *p++) != 0) {
+		v = simple_strtoul(p, &ep, 0);
+		switch(c) {
+		case '/':
+			++h;
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		case 't':
+			if (t != target)
+				t = (target == v) ? v : NO_TARGET;
+			u = ALL_LUNS;
+			break;
+		case 'u':
+			if (u != lun)
+				u = (lun == v) ? v : NO_LUN;
+			break;
+		case 'q':
+			if (h == unit &&
+				(t == ALL_TARGETS || t == target) &&
+				(u == ALL_LUNS    || u == lun))
+				return v;
+			break;
+		case '-':
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		default:
+			break;
+		}
+		p = ep;
+	}
+	return DEF_DEPTH;
+}
+
+/*===================================================================
+**
+**	Print out information about driver configuration.
+**
+**===================================================================
+*/
+static void __init ncr_print_driver_setup(void)
+{
+#define YesNo(y)	y ? 'y' : 'n'
+	printk (NAME53C8XX ": setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
+		"burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
+		YesNo(driver_setup.disconnection),
+		driver_setup.special_features,
+		driver_setup.ultra_scsi,
+		driver_setup.default_tags,
+		driver_setup.default_sync,
+		driver_setup.burst_max,
+		YesNo(driver_setup.max_wide),
+		driver_setup.diff_support,
+		YesNo(driver_setup.reverse_probe),
+		driver_setup.bus_check);
+
+	printk (NAME53C8XX ": setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
+		"led:%c,settle:%d,irqm:0x%x,nvram:0x%x,pcifix:0x%x\n",
+		YesNo(driver_setup.master_parity),
+		YesNo(driver_setup.scsi_parity),
+		YesNo(driver_setup.force_sync_nego),
+		driver_setup.verbose,
+		driver_setup.debug,
+		YesNo(driver_setup.led_pin),
+		driver_setup.settle_delay,
+		driver_setup.irqm,
+		driver_setup.use_nvram,
+		driver_setup.pci_fix_up);
+#undef YesNo
+}
+
+/*===================================================================
+**
+**   SYM53C8XX devices description table.
+**
+**===================================================================
+*/
+
+static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
+
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+/*===================================================================
+**
+**    Detect all NCR PQS/PDS boards and keep track of their bus nr.
+**
+**    The NCR PQS or PDS card is constructed as a DEC bridge
+**    behind which sit a proprietary NCR memory controller and
+**    four or two 53c875s as separate devices.  In its usual mode
+**    of operation, the 875s are slaved to the memory controller
+**    for all transfers.  We can tell if an 875 is part of a
+**    PQS/PDS or not since if it is, it will be on the same bus
+**    as the memory controller.  To operate with the Linux
+**    driver, the memory controller is disabled and the 875s
+**    freed to function independently.  The only wrinkle is that
+**    the preset SCSI ID (which may be zero) must be read in from
+**    a special configuration space register of the 875.
+**
+**===================================================================
+*/
+#define	SCSI_NCR_MAX_PQS_BUS	16
+static int pqs_bus[SCSI_NCR_MAX_PQS_BUS] __initdata = { 0 };
+
+static void __init ncr_detect_pqs_pds(void)
+{
+	short index;
+	pcidev_t dev = PCIDEV_NULL;
+
+	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+		u_char tmp;
+
+		dev = pci_find_device(0x101a, 0x0009, dev);
+		if (dev == PCIDEV_NULL) {
+			pqs_bus[index] = -1;
+			break;
+		}
+		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+		pci_read_config_byte(dev, 0x44, &tmp);
+		/* bit 1: allow individual 875 configuration */
+		tmp |= 0x2;
+		pci_write_config_byte(dev, 0x44, tmp);
+		pci_read_config_byte(dev, 0x45, &tmp);
+		/* bit 2: drive individual 875 interrupts to the bus */
+		tmp |= 0x4;
+		pci_write_config_byte(dev, 0x45, tmp);
+
+		pqs_bus[index] = PciBusNumber(dev);
+	}
+}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+/*===================================================================
+**
+**   Read and check the PCI configuration for any detected NCR 
+**   boards and save data for attaching after all boards have 
+**   been detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
+{
+	u_short vendor_id, device_id, command;
+	u_char cache_line_size, latency_timer;
+	u_char suggested_cache_line_size = 0;
+	u_char pci_fix_up = driver_setup.pci_fix_up;
+	u_char revision;
+	u_int irq;
+	u_long base, base_2, io_port; 
+	int i;
+	ncr_chip *chip;
+
+	printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
+		PciBusNumber(pdev),
+		(int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+		(int) (PciDeviceFn(pdev) & 7));
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+	if (!pci_dma_supported(pdev, (dma_addr_t) (0xffffffffUL))) {
+		printk(KERN_WARNING NAME53C8XX
+		       "32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+		return -1;
+	}
+#endif
+
+	/*
+	**    Read info from the PCI config space.
+	**    pci_read_config_xxx() functions are assumed to be used for 
+	**    successfully detected PCI devices.
+	*/
+	vendor_id = PciVendorId(pdev);
+	device_id = PciDeviceId(pdev);
+	irq	  = PciIrqLine(pdev);
+	i =	0;
+	i =	pci_get_base_address(pdev, i, &io_port);
+	i =	pci_get_base_address(pdev, i, &base);
+	(void)	pci_get_base_address(pdev, i, &base_2);
+
+	pci_read_config_word(pdev, PCI_COMMAND,		&command);
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION,	&revision);
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,	&cache_line_size);
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER,	&latency_timer);
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+	/*
+	**    Match the BUS number for PQS/PDS devices.
+	**    Read the SCSI ID from a special register mapped
+	**    into the configuration space of the individual
+	**    875s.  This register is set up by the PQS bios
+	*/
+	for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
+		u_char tmp;
+		if (pqs_bus[i] == PciBusNumber(pdev)) {
+			pci_read_config_byte(pdev, 0x84, &tmp);
+			device->pqs_pds = 1;
+			device->host_id = tmp;
+			break;
+		}
+	}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+	/*
+	**	If user excludes this chip, donnot initialize it.
+	*/
+	for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
+		if (driver_setup.excludes[i] ==
+				(io_port & PCI_BASE_ADDRESS_IO_MASK))
+			return -1;
+	}
+	/*
+	**    Check if the chip is supported
+	*/
+	if ((device_id == PCI_DEVICE_ID_LSI_53C1010) ||
+			(device_id == PCI_DEVICE_ID_LSI_53C1010_66)){
+		printk(NAME53C8XX ": not initializing, device not supported\n");
+		return -1;
+	}
+	chip = 0;
+	for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+		if (device_id != ncr_chip_table[i].device_id)
+			continue;
+		if (revision > ncr_chip_table[i].revision_id)
+			continue;
+		chip = &device->chip;
+		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+		chip->revision_id = revision;
+		break;
+	}
+
+	/*
+	**	Ignore Symbios chips controlled by SISL RAID controller.
+	**	This controller sets value 0x52414944 at RAM end - 16.
+	*/
+#if defined(__i386__) && !defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+	if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
+		unsigned int ram_size, ram_val;
+		u_long ram_ptr;
+
+		if (chip->features & FE_RAM8K)
+			ram_size = 8192;
+		else
+			ram_size = 4096;
+
+		ram_ptr = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
+					ram_size);
+		if (ram_ptr) {
+			ram_val = readl_raw(ram_ptr + ram_size - 16);
+			unmap_pci_mem(ram_ptr, ram_size);
+			if (ram_val == 0x52414944) {
+				printk(NAME53C8XX": not initializing, "
+				       "driven by SISL RAID controller.\n");
+				return -1;
+			}
+		}
+	}
+#endif /* i386 and PCI MEMORY accessible */
+
+	if (!chip) {
+		printk(NAME53C8XX ": not initializing, device not supported\n");
+		return -1;
+	}
+
+#ifdef __powerpc__
+	/*
+	**	Fix-up for power/pc.
+	**	Should not be performed by the driver.
+	*/
+	if ((command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+		    != (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+		printk(NAME53C8XX ": setting%s%s...\n",
+		(command & PCI_COMMAND_IO)     ? "" : " PCI_COMMAND_IO",
+		(command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
+		command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
+	if ( is_prep ) {
+		if (io_port >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
+			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_0, io_port);
+		}
+		if (base >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating base (Wacky IBM)");
+			base = (base & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_1, base);
+		}
+		if (base_2 >= 0x10000000) {
+			printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
+			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_2, base_2);
+		}
+	}
+#endif
+#endif	/* __powerpc__ */
+
+#if defined(__sparc__) && (LINUX_VERSION_CODE < LinuxVersionCode(2,3,0))
+	/*
+	 *	Severall fix-ups for sparc.
+	 *
+	 *	Should not be performed by the driver, which is why all
+	 *	this crap is cleaned up in 2.4.x
+	 */
+
+	base = __pa(base);
+	base_2 = __pa(base_2);
+
+	if (!(command & PCI_COMMAND_MASTER)) {
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
+		command |= PCI_COMMAND_MASTER;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+	}
+
+	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
+		command |= PCI_COMMAND_INVALIDATE;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+	}
+
+	if ((chip->features & FE_CLSE) && !cache_line_size) {
+		/* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
+		cache_line_size = 64 / sizeof(u_int32);
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n",
+			       cache_line_size);
+		pcibios_write_config_byte(bus, device_fn,
+					  PCI_CACHE_LINE_SIZE, cache_line_size);
+		pcibios_read_config_byte(bus, device_fn,
+					 PCI_CACHE_LINE_SIZE, &cache_line_size);
+	}
+
+	if (!latency_timer) {
+		unsigned char min_gnt;
+
+		pcibios_read_config_byte(bus, device_fn,
+					 PCI_MIN_GNT, &min_gnt);
+		if (min_gnt == 0)
+			latency_timer = 128;
+		else
+			latency_timer = ((min_gnt << 3) & 0xff);
+		printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
+		pcibios_write_config_byte(bus, device_fn,
+					  PCI_LATENCY_TIMER, latency_timer);
+		pcibios_read_config_byte(bus, device_fn,
+					 PCI_LATENCY_TIMER, &latency_timer);
+	}
+#endif /* __sparc__ && (LINUX_VERSION_CODE < LinuxVersionCode(2,3,0)) */
+
+#if defined(__i386__) && !defined(MODULE)
+	if (!cache_line_size) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+		extern char x86;
+		switch(x86) {
+#else
+		switch(boot_cpu_data.x86) {
+#endif
+		case 4:	suggested_cache_line_size = 4; break;
+		case 6:
+		case 5:	suggested_cache_line_size = 8; break;
+		}
+	}
+#endif	/* __i386__ */
+
+	/*
+	**    Check availability of IO space, memory space.
+	**    Enable master capability if not yet.
+	**
+	**    We shouldn't have to care about the IO region when 
+	**    we are using MMIO. But calling check_region() from 
+	**    both the ncr53c8xx and the sym53c8xx drivers prevents 
+	**    from attaching devices from the both drivers.
+	**    If you have a better idea, let me know.
+	*/
+/* #ifdef SCSI_NCR_IOMAPPED */
+#if 1
+	if (!(command & PCI_COMMAND_IO)) { 
+		printk(NAME53C8XX ": I/O base address (0x%lx) disabled.\n",
+			(long) io_port);
+		io_port = 0;
+	}
+#endif
+	if (!(command & PCI_COMMAND_MEMORY)) {
+		printk(NAME53C8XX ": PCI_COMMAND_MEMORY not set.\n");
+		base	= 0;
+		base_2	= 0;
+	}
+	io_port &= PCI_BASE_ADDRESS_IO_MASK;
+	base	&= PCI_BASE_ADDRESS_MEM_MASK;
+	base_2	&= PCI_BASE_ADDRESS_MEM_MASK;
+
+/* #ifdef SCSI_NCR_IOMAPPED */
+#if 1
+	if (io_port && check_region (io_port, 128)) {
+		printk(NAME53C8XX ": IO region 0x%lx[0..127] is in use\n",
+			(long) io_port);
+		io_port = 0;
+	}
+	if (!io_port)
+		return -1;
+#endif
+#ifndef SCSI_NCR_IOMAPPED
+	if (!base) {
+		printk(NAME53C8XX ": MMIO base address disabled.\n");
+		return -1;
+	}
+#endif
+
+/* The ncr53c8xx driver never did set the PCI parity bit.	*/
+/* Since setting this bit is known to trigger spurious MDPE	*/
+/* errors on some 895 controllers when noise on power lines is	*/
+/* too high, I donnot want to change previous ncr53c8xx driver	*/
+/* behaviour on that point (the sym53c8xx driver set this bit).	*/
+#if 0
+	/*
+	**    Set MASTER capable and PARITY bit, if not yet.
+	*/
+	if ((command & (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY))
+		     != (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)) {
+		printk(NAME53C8XX ": setting%s%s...(fix-up)\n",
+		(command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
+		(command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
+		command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+#else
+	/*
+	**    Set MASTER capable if not yet.
+	*/
+	if ((command & PCI_COMMAND_MASTER) != PCI_COMMAND_MASTER) {
+		printk(NAME53C8XX ": setting PCI_COMMAND_MASTER...(fix-up)\n");
+		command |= PCI_COMMAND_MASTER;
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+#endif
+
+	/*
+	**    Fix some features according to driver setup.
+	*/
+	if (!(driver_setup.special_features & 1))
+		chip->features &= ~FE_SPECIAL_SET;
+	else {
+		if (driver_setup.special_features & 2)
+			chip->features &= ~FE_WRIE;
+		if (driver_setup.special_features & 4)
+			chip->features &= ~FE_NOPM;
+	}
+	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+		chip->features |=  FE_ULTRA;
+		chip->features &= ~FE_ULTRA2;
+	}
+	if (driver_setup.ultra_scsi < 1)
+		chip->features &= ~FE_ULTRA;
+	if (!driver_setup.max_wide)
+		chip->features &= ~FE_WIDE;
+
+	/*
+	**	Some features are required to be enabled in order to 
+	**	work around some chip problems. :) ;)
+	**	(ITEM 12 of a DEL about the 896 I haven't yet).
+	**	We must ensure the chip will use WRITE AND INVALIDATE.
+	**	The revision number limit is for now arbitrary.
+	*/
+	if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
+		chip->features	|= (FE_WRIE | FE_CLSE);
+		pci_fix_up	|=  3;	/* Force appropriate PCI fix-up */
+	}
+
+#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
+	/*
+	**    Try to fix up PCI config according to wished features.
+	*/
+	if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && 
+	    !cache_line_size && suggested_cache_line_size) {
+		cache_line_size = suggested_cache_line_size;
+		pci_write_config_byte(pdev,
+				      PCI_CACHE_LINE_SIZE, cache_line_size);
+		printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
+			cache_line_size);
+	}
+
+	if ((pci_fix_up & 2) && cache_line_size &&
+	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
+		command |= PCI_COMMAND_INVALIDATE;
+		pci_write_config_word(pdev, PCI_COMMAND, command);
+	}
+
+	/*
+	**    Tune PCI LATENCY TIMER according to burst max length transfer.
+	**    (latency timer >= burst length + 6, we add 10 to be quite sure)
+	*/
+
+	if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
+		u_char lt = (1 << chip->burst_max) + 6 + 10;
+		if (latency_timer < lt) {
+			printk(NAME53C8XX 
+			       ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+			       (int) latency_timer, (int) lt);
+			latency_timer = lt;
+			pci_write_config_byte(pdev,
+					      PCI_LATENCY_TIMER, latency_timer);
+		}
+	}
+
+#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ 	/*
+	**    Initialise ncr_device structure with items required by ncr_attach.
+	*/
+	device->pdev		= pdev;
+	device->slot.bus	= PciBusNumber(pdev);
+	device->slot.device_fn	= PciDeviceFn(pdev);
+	device->slot.base	= base;
+	device->slot.base_2	= base_2;
+	device->slot.io_port	= io_port;
+	device->slot.irq	= irq;
+	device->attach_done	= 0;
+
+	return 0;
+}
+
+/*===================================================================
+**
+**    Detect all 53c8xx hosts and then attach them.
+**
+**    If we are using NVRAM, once all hosts are detected, we need to 
+**    check any NVRAM for boot order in case detect and boot order 
+**    differ and attach them using the order in the NVRAM.
+**
+**    If no NVRAM is found or data appears invalid attach boards in 
+**    the the order they are detected.
+**
+**===================================================================
+*/
+static int __init 
+sym53c8xx__detect(Scsi_Host_Template *tpnt, u_short ncr_chip_ids[], int chips)
+{
+	pcidev_t pcidev;
+	int i, j, hosts, count;
+	int attach_count = 0;
+	ncr_device *devtbl, *devp;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram  nvram0, nvram, *nvp;
+#endif
+
+	/*
+	**    PCI is required.
+	*/
+	if (!pci_present())
+		return 0;
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+	ncr_debug = driver_setup.debug;
+#endif
+	if (initverbose >= 2)
+		ncr_print_driver_setup();
+
+	/*
+	**	Allocate the device table since we donnot want to 
+	**	overflow the kernel stack.
+	**	1 x 4K PAGE is enough for more than 40 devices for i386.
+	*/
+	devtbl = m_calloc(PAGE_SIZE, "devtbl");
+	if (!devtbl)
+		return 0;
+
+	/*
+	**    Detect all NCR PQS/PDS memory controllers.
+	*/
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+	ncr_detect_pqs_pds();
+#endif
+
+	/* 
+	**    Detect all 53c8xx hosts.
+	**    Save the first Symbios NVRAM content if any 
+	**    for the boot order.
+	*/
+	hosts	= PAGE_SIZE		/ sizeof(*devtbl);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
+#endif
+	j = 0;
+	count = 0;
+	pcidev = PCIDEV_NULL;
+	while (1) {
+		char *msg = "";
+		if (count >= hosts)
+			break;
+		if (j >= chips)
+			break;
+		i = driver_setup.reverse_probe ? chips - 1 - j : j;
+		pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+					 pcidev);
+		if (pcidev == PCIDEV_NULL) {
+			++j;
+			continue;
+		}
+		/* Some HW as the HP LH4 may report twice PCI devices */
+		for (i = 0; i < count ; i++) {
+			if (devtbl[i].slot.bus	     == PciBusNumber(pcidev) && 
+			    devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
+				break;
+		}
+		if (i != count)	/* Ignore this device if we already have it */
+			continue;
+		devp = &devtbl[count];
+		devp->host_id = driver_setup.host_id;
+		devp->attach_done = 0;
+		if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
+			continue;
+		}
+		++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		if (nvp) {
+			ncr_get_nvram(devp, nvp);
+			switch(nvp->type) {
+			case SCSI_NCR_SYMBIOS_NVRAM:
+				/*
+				 *   Switch to the other nvram buffer, so that 
+				 *   nvram0 will contain the first Symbios 
+				 *   format NVRAM content with boot order.
+				 */
+				nvp = &nvram;
+				msg = "with Symbios NVRAM";
+				break;
+			case SCSI_NCR_TEKRAM_NVRAM:
+				msg = "with Tekram NVRAM";
+				break;
+			}
+		}
+#endif
+#ifdef	SCSI_NCR_PQS_PDS_SUPPORT
+		if (devp->pqs_pds)
+			msg = "(NCR PQS/PDS)";
+#endif
+		printk(KERN_INFO NAME53C8XX ": 53c%s detected %s\n",
+		       devp->chip.name, msg);
+	}
+
+	/*
+	**    If we have found a SYMBIOS NVRAM, use first the NVRAM boot 
+	**    sequence as device boot order.
+	**    check devices in the boot record against devices detected. 
+	**    attach devices if we find a match. boot table records that 
+	**    do not match any detected devices will be ignored. 
+	**    devices that do not match any boot table will not be attached
+	**    here but will attempt to be attached during the device table 
+	**    rescan.
+	*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
+		goto next;
+	for (i = 0; i < 4; i++) {
+		Symbios_host *h = &nvram0.data.Symbios.host[i];
+		for (j = 0 ; j < count ; j++) {
+			devp = &devtbl[j];
+			if (h->device_fn != devp->slot.device_fn ||
+			    h->bus_nr	 != devp->slot.bus	 ||
+			    h->device_id != devp->chip.device_id)
+				continue;
+			if (devp->attach_done)
+				continue;
+			if (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) {
+				ncr_get_nvram(devp, nvp);
+				if (!ncr_attach (tpnt, attach_count, devp))
+					attach_count++;
+			}
+#if 0	/* Restore previous behaviour of ncr53c8xx driver */
+			else if (!(driver_setup.use_nvram & 0x80))
+				printk(KERN_INFO NAME53C8XX
+				       ": 53c%s state OFF thus not attached\n",
+				       devp->chip.name);
+#endif
+			else
+				continue;
+
+			devp->attach_done = 1;
+			break;
+		}
+	}
+next:
+#endif
+
+	/* 
+	**    Rescan device list to make sure all boards attached.
+	**    Devices without boot records will not be attached yet
+	**    so try to attach them here.
+	*/
+	for (i= 0; i < count; i++) {
+		devp = &devtbl[i];
+		if (!devp->attach_done) {
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			ncr_get_nvram(devp, nvp);
+#endif
+			if (!ncr_attach (tpnt, attach_count, devp))
+				attach_count++;
+		}
+	}
+
+	m_free(devtbl, PAGE_SIZE, "devtbl");
+
+	return attach_count;
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx_defs.h linux.18p17/drivers/scsi/sym53c8xx_defs.h
--- linux.vanilla/drivers/scsi/sym53c8xx_defs.h	Sun Jun 11 21:44:17 2000
+++ linux.18p17/drivers/scsi/sym53c8xx_defs.h	Thu Oct 19 01:37:49 2000
@@ -1,7 +1,7 @@
 /******************************************************************************
 **  High Performance device driver for the Symbios 53C896 controller.
 **
-**  Copyright (C) 1998  Gerard Roudier <groudier@club-internet.fr>
+**  Copyright (C) 1998-2000  Gerard Roudier <groudier@club-internet.fr>
 **
 **  This driver also supports all the Symbios 53C8XX controller family, 
 **  except 53C810 revisions < 16, 53C825 revisions < 16 and all 
@@ -69,6 +69,7 @@
 #ifndef LinuxVersionCode
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 #endif
+
 /*
  * NCR PQS/PDS special device support.
  */
@@ -96,13 +97,18 @@
 #define SCSI_NCR_DEBUG_INFO_SUPPORT
 #define SCSI_NCR_PCI_FIX_UP_SUPPORT
 #ifdef	SCSI_NCR_PROC_INFO_SUPPORT
-#	ifdef	CONFIG_SCSI_NCR53C8XX_PROFILE
-#		define	SCSI_NCR_PROFILE_SUPPORT
-#	endif
 #	define	SCSI_NCR_USER_COMMAND_SUPPORT
 #	define	SCSI_NCR_USER_INFO_SUPPORT
 #endif
 
+/*
+**	To disable integrity checking, do not define the 
+**	following option.
+*/
+#ifdef	CONFIG_SCSI_NCR53C8XX_INTEGRITY_CHECK
+#	define SCSI_NCR_ENABLE_INTEGRITY_CHECK
+#endif
+
 /*==========================================================
 **
 ** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
@@ -122,10 +128,9 @@
 */
 
 /*
- * For Ultra2 SCSI support option, use special features and allow 40Mhz 
- * synchronous data transfers.
+ * For Ultra2 and Ultra3 SCSI support option, use special features. 
  *
- * Value 5 (default) means:
+ * Value (default) means:
  *	bit 0 : all features enabled, except:
  *		bit 1 : PCI Write And Invalidate.
  *		bit 2 : Data Phase Mismatch handling from SCRIPTS.
@@ -134,17 +139,29 @@
  * enabled by the driver.
  */
 #define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(3)
-#define SCSI_NCR_SETUP_ULTRA_SCSI		(2)
-#define SCSI_NCR_MAX_SYNC			(40)
 
 /*
- * Allow tags from 2 to 64, default 8
+ * For Ultra2 and Ultra3 SCSI support allow 80Mhz synchronous data transfers.
+ * Value means:
+ *  0 - Ultra speeds disabled
+ *  1 - Ultra enabled  (Maximum 20Mtrans/sec)
+ *  2 - Ultra2 enabled (Maximum 40Mtrans/sec)
+ *  3 - Ultra3 enabled (Maximum 80Mtrans/sec)
+ *
+ * Use boot options sym53c8xx=ultra:3 to enable Ultra3 support.
+ */
+
+#define SCSI_NCR_SETUP_ULTRA_SCSI		(3)
+#define SCSI_NCR_MAX_SYNC			(80)
+
+/*
+ * Allow tags from 2 to 256, default 8
  */
 #ifdef	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
 #if	CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
 #define SCSI_NCR_MAX_TAGS	(2)
-#elif	CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 64
-#define SCSI_NCR_MAX_TAGS	(64)
+#elif	CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 256
+#define SCSI_NCR_MAX_TAGS	(256)
 #else
 #define	SCSI_NCR_MAX_TAGS	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
 #endif
@@ -165,12 +182,16 @@
 #endif
 
 /*
- * Use normal IO if configured. Forced for alpha and ppc.
+ * Use normal IO if configured. Forced for alpha and powerpc.
+ * Powerpc fails copying to on-chip RAM using memcpy_toio().
  */
 #if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED)
 #define	SCSI_NCR_IOMAPPED
-#elif defined(__alpha__) || defined(__powerpc__)
+#elif defined(__alpha__)
 #define	SCSI_NCR_IOMAPPED
+#elif defined(__powerpc__)
+#define	SCSI_NCR_IOMAPPED
+#define SCSI_NCR_PCI_MEM_NOT_SUPPORTED
 #elif defined(__sparc__)
 #undef SCSI_NCR_IOMAPPED
 #endif
@@ -183,8 +204,22 @@
 #undef SCSI_NCR_USE_64BIT_DAC
 
 /*
+ * Immediate arbitration
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IARB)
+#define SCSI_NCR_IARB_SUPPORT
+#endif
+
+/*
+ * Should we enable DAC cycles on sparc64 platforms?
+ * Until further investigation we do not enable it
+ * anywhere at the moment.
+ */
+#undef SCSI_NCR_USE_64BIT_DAC
+
+/*
  * Sync transfer frequency at startup.
- * Allow from 5Mhz to 40Mhz default 20 Mhz.
+ * Allow from 5Mhz to 80Mhz default 20 Mhz.
  */
 #ifndef	CONFIG_SCSI_NCR53C8XX_SYNC
 #define	CONFIG_SCSI_NCR53C8XX_SYNC	(20)
@@ -201,8 +236,10 @@
 #define	SCSI_NCR_SETUP_DEFAULT_SYNC	(250/(CONFIG_SCSI_NCR53C8XX_SYNC))
 #elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 33
 #define	SCSI_NCR_SETUP_DEFAULT_SYNC	(11)
-#else
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 40
 #define	SCSI_NCR_SETUP_DEFAULT_SYNC	(10)
+#else
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC 	(9)
 #endif
 
 /*
@@ -246,7 +283,7 @@
  */
 #ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
 #define SCSI_NCR_SETUP_LED_PIN		(1)
-#define SCSI_NCR_SETUP_DIFF_SUPPORT	(3)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT	(4)
 #else
 #define SCSI_NCR_SETUP_LED_PIN		(0)
 #define SCSI_NCR_SETUP_DIFF_SUPPORT	(0)
@@ -258,6 +295,57 @@
 #define SCSI_NCR_SETUP_SETTLE_TIME	(2)
 
 /*
+**	Bridge quirks work-around option defaulted to 1.
+*/
+#ifndef	SCSI_NCR_PCIQ_WORK_AROUND_OPT
+#define	SCSI_NCR_PCIQ_WORK_AROUND_OPT	1
+#endif
+
+/*
+**	Work-around common bridge misbehaviour.
+**
+**	- Do not flush posted writes in the opposite 
+**	  direction on read.
+**	- May reorder DMA writes to memory.
+**
+**	This option should not affect performances 
+**	significantly, so it is the default.
+*/
+#if	SCSI_NCR_PCIQ_WORK_AROUND_OPT == 1
+#define	SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM
+#define	SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+#define	SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS
+
+/*
+**	Same as option 1, but also deal with 
+**	misconfigured interrupts.
+**
+**	- Edge triggerred instead of level sensitive.
+**	- No interrupt line connected.
+**	- IRQ number misconfigured.
+**	
+**	If no interrupt is delivered, the driver will 
+**	catch the interrupt conditions 10 times per 
+**	second. No need to say that this option is 
+**	not recommended.
+*/
+#elif	SCSI_NCR_PCIQ_WORK_AROUND_OPT == 2
+#define	SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM
+#define	SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+#define	SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS
+#define	SCSI_NCR_PCIQ_BROKEN_INTR
+
+/*
+**	Some bridge designers decided to flush 
+**	everything prior to deliver the interrupt.
+**	This option tries to deal with such a 
+**	behaviour.
+*/
+#elif	SCSI_NCR_PCIQ_WORK_AROUND_OPT == 3
+#define	SCSI_NCR_PCIQ_SYNC_ON_INTR
+#endif
+
+/*
 **	Other parameters not configurable with "make config"
 **	Avoid to change these constants, unless you know what you are doing.
 */
@@ -266,16 +354,16 @@
 #define SCSI_NCR_MAX_SCATTER	(127)
 #define SCSI_NCR_MAX_TARGET	(16)
 
-/* No need to use a too large adapter queue */
-#if SCSI_NCR_MAX_TAGS <= 32
-#define SCSI_NCR_CAN_QUEUE	(7*SCSI_NCR_MAX_TAGS)
-#else
-#define SCSI_NCR_CAN_QUEUE	(250)
-#endif
-
+/*
+**   Compute some desirable value for CAN_QUEUE 
+**   and CMD_PER_LUN.
+**   The driver will use lower values if these 
+**   ones appear to be too large.
+*/
+#define SCSI_NCR_CAN_QUEUE	(8*SCSI_NCR_MAX_TAGS + 2*SCSI_NCR_MAX_TARGET)
 #define SCSI_NCR_CMD_PER_LUN	(SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER)
 
+#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER)
 #define SCSI_NCR_TIMER_INTERVAL	(HZ)
 
 #if 1 /* defined CONFIG_SCSI_MULTI_LUN */
@@ -287,11 +375,22 @@
 #ifndef HOSTS_C
 
 /*
-**	IO functions definition for big/little endian support.
-**	For now, the NCR is only supported in little endian addressing mode, 
-**	and big endian byte ordering is only supported for the PPC.
-**	MMIO is not used on PPC.
+**	These simple macros limit expression involving 
+**	kernel time values (jiffies) to some that have 
+**	chance not to be too much incorrect. :-)
 */
+#define ktime_get(o)		(jiffies + (u_long) o)
+#define ktime_exp(b)		((long)(jiffies) - (long)(b) >= 0)
+#define ktime_dif(a, b)		((long)(a) - (long)(b))
+/* These ones are not used in this driver */
+#define ktime_add(a, o)		((a) + (u_long)(o))
+#define ktime_sub(a, o)		((a) - (u_long)(o))
+
+
+/*
+ *  IO functions definition for big/little endian CPU support.
+ *  For now, the NCR is only supported in little endian addressing mode, 
+ */
 
 #ifdef	__BIG_ENDIAN
 
@@ -299,19 +398,14 @@
 #error	"BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
 #endif
 
-#if defined(__powerpc__)
 #define	inw_l2b		inw
 #define	inl_l2b		inl
 #define	outw_b2l	outw
 #define	outl_b2l	outl
-#elif defined(__sparc__)
 #define	readw_l2b	readw
 #define	readl_l2b	readl
 #define	writew_b2l	writew
 #define	writel_b2l	writel
-#else
-#error	"Support for BIG ENDIAN is only available for PowerPC and SPARC"
-#endif
 
 #else	/* little endian */
 
@@ -327,7 +421,7 @@
 #define writew_raw(b,a)	((*(volatile unsigned short *) (a)) = (b))
 #define writel_raw(b,a)	((*(volatile unsigned int *) (a)) = (b))
 
-#else	/* Other little-endian (for now alpha) */
+#else	/* Other little-endian */
 #define	inw_raw		inw
 #define	inl_raw		inl
 #define	outw_raw	outw
@@ -344,6 +438,204 @@
 #error	"The NCR in BIG ENDIAN addressing mode is not (yet) supported"
 #endif
 
+
+/*
+ *  IA32 architecture does not reorder STORES and prevents
+ *  LOADS from passing STORES. It is called `program order' 
+ *  by Intel and allows device drivers to deal with memory 
+ *  ordering by only ensuring that the code is not reordered  
+ *  by the compiler when ordering is required.
+ *  Other architectures implement a weaker ordering that 
+ *  requires memory barriers (and also IO barriers when they 
+ *  make sense) to be used.
+ *  We want to be paranoid for ppc and ia64. :)
+ */
+
+#if	defined	__i386__
+#define MEMORY_BARRIER()	do { ; } while(0)
+#elif	defined	__powerpc__
+#define MEMORY_BARRIER()	__asm__ volatile("eieio; sync" : : : "memory")
+#elif	defined	__ia64__
+#define MEMORY_BARRIER()	__asm__ volatile("mf.a; mf" : : : "memory")
+#else
+#define MEMORY_BARRIER()	mb()
+#endif
+
+
+/*
+ *  If the NCR uses big endian addressing mode over the 
+ *  PCI, actual io register addresses for byte and word 
+ *  accesses must be changed according to lane routing.
+ *  Btw, ncr_offb() and ncr_offw() macros only apply to 
+ *  constants and so donnot generate bloated code.
+ */
+
+#if	defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o)	(((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o)	(((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o)	(o)
+#define ncr_offw(o)	(o)
+
+#endif
+
+/*
+ *  If the CPU and the NCR use same endian-ness adressing,
+ *  no byte reordering is needed for script patching.
+ *  Macro cpu_to_scr() is to be used for script patching.
+ *  Macro scr_to_cpu() is to be used for getting a DWORD 
+ *  from the script.
+ */
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw)	cpu_to_le32(dw)
+#define scr_to_cpu(dw)	le32_to_cpu(dw)
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw)	cpu_to_be32(dw)
+#define scr_to_cpu(dw)	be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw)	(dw)
+#define scr_to_cpu(dw)	(dw)
+
+#endif
+
+/*
+ *  Access to the controller chip.
+ *
+ *  If SCSI_NCR_IOMAPPED is defined, the driver will use 
+ *  normal IOs instead of the MEMORY MAPPED IO method  
+ *  recommended by PCI specifications.
+ *  If all PCI bridges, host brigdes and architectures 
+ *  would have been correctly designed for PCI, this 
+ *  option would be useless.
+ *
+ *  If the CPU and the NCR use same endian-ness adressing,
+ *  no byte reordering is needed for accessing chip io 
+ *  registers. Functions suffixed by '_raw' are assumed 
+ *  to access the chip over the PCI without doing byte 
+ *  reordering. Functions suffixed by '_l2b' are 
+ *  assumed to perform little-endian to big-endian byte 
+ *  reordering, those suffixed by '_b2l' blah, blah,
+ *  blah, ...
+ */
+
+#if defined(SCSI_NCR_IOMAPPED)
+
+/*
+ *  IO mapped only input / ouput
+ */
+
+#define	INB_OFF(o)		inb (np->base_io + ncr_offb(o))
+#define	OUTB_OFF(o, val)	outb ((val), np->base_io + ncr_offb(o))
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define	INW_OFF(o)		inw_l2b (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_l2b (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_b2l ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_b2l ((val), np->base_io + (o))
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define	INW_OFF(o)		inw_b2l (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_b2l (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_l2b ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_l2b ((val), np->base_io + (o))
+
+#else
+
+#define	INW_OFF(o)		inw_raw (np->base_io + ncr_offw(o))
+#define	INL_OFF(o)		inl_raw (np->base_io + (o))
+
+#define	OUTW_OFF(o, val)	outw_raw ((val), np->base_io + ncr_offw(o))
+#define	OUTL_OFF(o, val)	outl_raw ((val), np->base_io + (o))
+
+#endif	/* ENDIANs */
+
+#else	/* defined SCSI_NCR_IOMAPPED */
+
+/*
+ *  MEMORY mapped IO input / output
+ */
+
+#define INB_OFF(o)		readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val)	writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if	defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)		readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_b2l((val), (char *)np->reg + (o))
+
+#elif	defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)		readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_l2b((val), (char *)np->reg + (o))
+
+#else
+
+#define INW_OFF(o)		readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)		readl_raw((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)	writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)	writel_raw((val), (char *)np->reg + (o))
+
+#endif
+
+#endif	/* defined SCSI_NCR_IOMAPPED */
+
+#define INB(r)		INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r)		INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r)		INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val)	OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val)	OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val)	OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+ *  Set bit field ON, OFF 
+ */
+
+#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
+
+/*
+ *  We normally want the chip to have a consistent view
+ *  of driver internal data structures when we restart it.
+ *  Thus these macros.
+ */
+#define OUTL_DSP(v)				\
+	do {					\
+		MEMORY_BARRIER();		\
+		OUTL (nc_dsp, (v));		\
+	} while (0)
+
+#define OUTONB_STD()				\
+	do {					\
+		MEMORY_BARRIER();		\
+		OUTONB (nc_dcntl, (STD|NOCOM));	\
+	} while (0)
+
+
 /*
 **	NCR53C8XX Device Ids
 */
@@ -400,6 +692,15 @@
 #define PCI_DEVICE_ID_NCR_53C1510D 0xa
 #endif
 
+#ifndef PCI_DEVICE_ID_LSI_53C1010
+#define PCI_DEVICE_ID_LSI_53C1010 0x20
+#endif
+
+#ifndef PCI_DEVICE_ID_LSI_53C1010_66
+#define PCI_DEVICE_ID_LSI_53C1010_66 0x21
+#endif
+
+
 /*
 **   NCR53C8XX devices features table.
 */
@@ -407,31 +708,35 @@
 	unsigned short	device_id;
 	unsigned short	revision_id;
 	char	*name;
-	unsigned char	burst_max;
+	unsigned char	burst_max;	/* log-base-2 of max burst */
 	unsigned char	offset_max;
 	unsigned char	nr_divisor;
 	unsigned int	features;
 #define FE_LED0		(1<<0)
-#define FE_WIDE		(1<<1)
-#define FE_ULTRA	(1<<2)
-#define FE_ULTRA2	(1<<3)
-#define FE_DBLR		(1<<4)
-#define FE_QUAD		(1<<5)
-#define FE_ERL		(1<<6)
-#define FE_CLSE		(1<<7)
-#define FE_WRIE		(1<<8)
-#define FE_ERMP		(1<<9)
-#define FE_BOF		(1<<10)
-#define FE_DFS		(1<<11)
-#define FE_PFEN		(1<<12)
-#define FE_LDSTR	(1<<13)
-#define FE_RAM		(1<<14)
-#define FE_CLK80	(1<<15)
-#define FE_RAM8K	(1<<16)
-#define FE_64BIT	(1<<17)
-#define FE_IO256	(1<<18)
-#define FE_NOPM		(1<<19)
-#define FE_LEDC		(1<<20)
+#define FE_WIDE		(1<<1)    /* Wide data transfers */
+#define FE_ULTRA	(1<<2)	  /* Ultra speed 20Mtrans/sec */
+#define FE_ULTRA2	(1<<3)	  /* Ultra 2 - 40 Mtrans/sec */
+#define FE_DBLR		(1<<4)	  /* Clock doubler present */
+#define FE_QUAD		(1<<5)	  /* Clock quadrupler present */
+#define FE_ERL		(1<<6)    /* Enable read line */
+#define FE_CLSE		(1<<7)    /* Cache line size enable */
+#define FE_WRIE		(1<<8)    /* Write & Invalidate enable */
+#define FE_ERMP		(1<<9)    /* Enable read multiple */
+#define FE_BOF		(1<<10)   /* Burst opcode fetch */
+#define FE_DFS		(1<<11)   /* DMA fifo size */
+#define FE_PFEN		(1<<12)   /* Prefetch enable */
+#define FE_LDSTR	(1<<13)   /* Load/Store supported */
+#define FE_RAM		(1<<14)   /* On chip RAM present */
+#define FE_CLK80	(1<<15)   /* Board clock is 80 MHz */
+#define FE_RAM8K	(1<<16)   /* On chip RAM sized 8Kb */
+#define FE_64BIT	(1<<17)   /* Supports 64-bit addressing */
+#define FE_IO256	(1<<18)   /* Requires full 256 bytes in PCI space */
+#define FE_NOPM		(1<<19)   /* Scripts handles phase mismatch */
+#define FE_LEDC		(1<<20)   /* Hardware control of LED */
+#define FE_DIFF		(1<<21)   /* Support Differential SCSI */
+#define FE_ULTRA3	(1<<22)   /* Ultra-3 80Mtrans/sec */
+#define FE_66MHZ 	(1<<23)   /* 66MHz PCI Support */
+
 #define FE_CACHE_SET	(FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
 #define FE_SCSI_SET	(FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
 #define FE_SPECIAL_SET	(FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
@@ -465,49 +770,65 @@
  FE_WIDE|FE_ERL}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825",  4,  8, 4,			\
- FE_WIDE|FE_ERL|FE_BOF}							\
+ FE_WIDE|FE_ERL|FE_BOF|FE_DIFF}						\
  ,									\
  {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6,  8, 4,			\
- FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}		\
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|FE_DIFF}	\
  ,									\
  {PCI_DEVICE_ID_NCR_53C860, 0xff, "860",  4,  8, 5,			\
  FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}		\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0x0f, "875",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0x1f, "876",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0x2f, "875E",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0xff, "876",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM}								\
  ,									\
  {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  6, 16, 5,			\
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM|FE_DIFF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C895, 0xff, "895",  6, 31, 7,			\
- FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|	\
+ FE_RAM}								\
  ,									\
- {PCI_DEVICE_ID_NCR_53C896, 0xff, "896",  7, 31, 7,			\
- FE_WIDE|FE_ULTRA2|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}\
+ {PCI_DEVICE_ID_NCR_53C896, 0xff, "896",  6, 31, 7,			\
+ FE_WIDE|FE_ULTRA2|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}			\
  ,									\
  {PCI_DEVICE_ID_NCR_53C895A, 0xff, "895a",  6, 31, 7,			\
- FE_WIDE|FE_ULTRA2|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_WIDE|FE_ULTRA2|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}			\
  ,									\
  {PCI_DEVICE_ID_NCR_53C1510D, 0xff, "1510D",  7, 31, 7,			\
- FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|\
- FE_IO256}\
+ 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, 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, 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}	\
 }
 
 /*
@@ -526,7 +847,9 @@
 	PCI_DEVICE_ID_NCR_53C895,	\
 	PCI_DEVICE_ID_NCR_53C896,	\
 	PCI_DEVICE_ID_NCR_53C895A,	\
-	PCI_DEVICE_ID_NCR_53C1510D	\
+	PCI_DEVICE_ID_NCR_53C1510D,	\
+ 	PCI_DEVICE_ID_LSI_53C1010,	\
+ 	PCI_DEVICE_ID_LSI_53C1010_66	\
 }
 
 /*
@@ -560,7 +883,8 @@
 	u_char	optimize;
 	u_char	recovery;
 	u_char	host_id;
-	u_int	excludes[SCSI_NCR_MAX_EXCLUDES];
+	u_short	iarb;
+	u_long	excludes[SCSI_NCR_MAX_EXCLUDES];
 	char	tag_ctrl[100];
 };
 
@@ -579,7 +903,7 @@
 	0,					\
 	0,					\
 	1,					\
-	1,					\
+	0,					\
 	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
 	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
 	0x00,					\
@@ -592,7 +916,8 @@
 	1,					\
 	0,					\
 	0,					\
-	255					\
+	255,					\
+	0x00					\
 }
 
 /*
@@ -722,7 +1047,8 @@
 **	Tekram NvRAM data format.
 */
 #define TEKRAM_NVRAM_SIZE 64
-#define TEKRAM_NVRAM_ADDRESS 0
+#define TEKRAM_93C46_NVRAM_ADDRESS 0
+#define TEKRAM_24C16_NVRAM_ADDRESS 0x40
 
 struct Tekram_nvram {
 	struct Tekram_target {
@@ -771,6 +1097,7 @@
 /*01*/  u_char    nc_scntl1;    /* no reset                         */
         #define   ISCON   0x10  /* connected to scsi		    */
         #define   CRST    0x08  /* force reset                      */
+        #define   IARB    0x02  /* immediate arbitration            */
 
 /*02*/  u_char    nc_scntl2;    /* no disconnect expected           */
 	#define   SDU     0x80  /* cmd: disconnect will raise error */
@@ -781,12 +1108,14 @@
 /*03*/  u_char    nc_scntl3;    /* cnf system clock dependent       */
 	#define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
 	#define   ULTRA   0x80  /* cmd: ULTRA enable                */
+				/* bits 0-2, 7 rsvd for C1010       */
 
 /*04*/  u_char    nc_scid;	/* cnf host adapter scsi address    */
 	#define   RRE     0x40  /* r/w:e enable response to resel.  */
 	#define   SRE     0x20  /* r/w:e enable response to select  */
 
 /*05*/  u_char    nc_sxfer;	/* ### Sync speed and count         */
+				/* bits 6-7 rsvd for C1010          */
 
 /*06*/  u_char    nc_sdid;	/* ### Destination-ID               */
 
@@ -837,7 +1166,10 @@
         #define   DM      0x04  /* sta: DIFFSENS mismatch (895/6 only) */
         #define   LDSC    0x02  /* sta: disconnect & reconnect      */
 
-/*10*/  u_int32    nc_dsa;	/* --> Base page                    */
+/*10*/  u_char    nc_dsa;	/* --> Base page                    */
+/*11*/  u_char    nc_dsa1;
+/*12*/  u_char    nc_dsa2;
+/*13*/  u_char    nc_dsa3;
 
 /*14*/  u_char    nc_istat;	/* --> Main Command and status      */
         #define   CABRT   0x80  /* cmd: abort current operation     */
@@ -858,12 +1190,14 @@
 
 /*1a*/  u_char    nc_ctest2;
 	#define   CSIGP   0x40
+				/* bits 0-2,7 rsvd for C1010        */
 
 /*1b*/  u_char    nc_ctest3;
 	#define   FLF     0x08  /* cmd: flush dma fifo              */
 	#define   CLF	  0x04	/* cmd: clear dma fifo		    */
 	#define   FM      0x02  /* mod: fetch pin mode              */
 	#define   WRIE    0x01  /* mod: write and invalidate enable */
+				/* bits 4-7 rsvd for C1010          */
 
 /*1c*/  u_int32    nc_temp;	/* ### Temporary stack              */
 
@@ -874,6 +1208,7 @@
 
 /*22*/  u_char    nc_ctest5;
 	#define   DFS     0x20  /* mod: dma fifo size               */
+				/* bits 0-1, 3-7 rsvd for C1010          */
 /*23*/  u_char    nc_ctest6;
 
 /*24*/  u_int32    nc_dbc;	/* ### Byte count and command       */
@@ -894,7 +1229,7 @@
 	#define   BOF     0x02  /* mod: burst op code fetch         */
 
 /*39*/  u_char    nc_dien;
-/*3a*/  u_char    nc_dwt;
+/*3a*/  u_char    nc_sbr;
 
 /*3b*/  u_char    nc_dcntl;	/* --> Script execution control     */
 	#define   CLSE    0x80  /* mod: cache line size enable      */
@@ -905,6 +1240,7 @@
 	#define   STD     0x04  /* cmd: start dma mode              */
 	#define   IRQD    0x02  /* mod: irq disable                 */
  	#define	  NOCOM   0x01	/* cmd: protect sfbr while reselect */
+				/* bits 0-1 rsvd for C1010          */
 
 /*3c*/  u_int32    nc_adder;
 
@@ -934,6 +1270,7 @@
 /*4c*/  u_char    nc_stest0;
 
 /*4d*/  u_char    nc_stest1;
+	#define   SCLK    0x80	/* Use the PCI clock as SCSI clock	*/
 	#define   DBLEN   0x08	/* clock doubler running		*/
 	#define   DBLSEL  0x04	/* clock doubler selected		*/
   
@@ -954,6 +1291,7 @@
 	#define    SMODE_SE  0x80	/* Single Ended                    */
 	#define    SMODE_LVD 0xc0	/* Low Voltage Differential        */
 	#define   LCKFRQ 0x20	/* Frequency Lock (895/6 only)     */
+				/* bits 0-5 rsvd for C1010          */
 
 /*53*/  u_char    nc_53_;
 /*54*/  u_short   nc_sodl;	/* Lowlevel: data out to scsi data  */
@@ -967,6 +1305,7 @@
 
 /*57*/	u_char    nc_ccntl1;	/* Chip Control 1 (896)             */
 	#define   ZMOD   0x80	/* High Impedance Mode              */
+	#define	  DIC	 0x10	/* Disable Internal Cycles	    */
 	#define   DDAC   0x08	/* Disable Dual Address Cycle       */
 	#define   XTIMOD 0x04	/* 64-bit Table Ind. Indexing Mode  */
 	#define   EXTIBMV 0x02	/* Enable 64-bit Table Ind. BMOV    */
@@ -988,12 +1327,28 @@
 /*b0*/	u_int32   nc_sbms;	/* Static Block Move Selector       */
 /*b4*/	u_int32   nc_dbms;	/* Dynamic Block Move Selector      */
 /*b8*/	u_int32   nc_dnad64;	/* DMA Next Address 64              */
-/*bc*/	u_int32   nc_bc_;
+/*bc*/	u_short   nc_scntl4;    /* C1010 only                       */
+	#define   U3EN   0x80	/* Enable Ultra 3                   */
+	#define   AIPEN	 0x40   /* Allow check upper byte lanes     */
+	#define   XCLKH_DT 0x08 /* Extra clock of data hold on DT
+					transfer edge	            */
+	#define   XCLKH_ST 0x04 /* Extra clock of data hold on ST
+					transfer edge	            */
+
+/*be*/  u_char   nc_aipcntl0;	/* Epat Control 1 C1010 only        */
+/*bf*/  u_char   nc_aipcntl1;	/* AIP Control C1010_66 Only        */
 
 /*c0*/	u_int32   nc_pmjad1;	/* Phase Mismatch Jump Address 1    */
 /*c4*/	u_int32   nc_pmjad2;	/* Phase Mismatch Jump Address 2    */
-/*c8*/	u_int32   nc_rbc;	/* Remaining Byte Count             */
-/*cc*/	u_int32   nc_ua;	/* Updated Address                  */
+/*c8*/	u_char    nc_rbc;	/* Remaining Byte Count             */
+/*c9*/	u_char    nc_rbc1;	/*                                  */
+/*ca*/	u_char    nc_rbc2;	/*                                  */
+/*cb*/	u_char    nc_rbc3;	/*                                  */
+
+/*cc*/	u_char    nc_ua;	/* Updated Address                  */
+/*cd*/	u_char    nc_ua1;	/*                                  */
+/*ce*/	u_char    nc_ua2;	/*                                  */
+/*cf*/	u_char    nc_ua3;	/*                                  */
 /*d0*/	u_int32   nc_esa;	/* Entry Storage Address            */
 /*d4*/	u_char    nc_ia;	/* Instruction Address              */
 /*d5*/	u_char    nc_ia1;
@@ -1001,6 +1356,17 @@
 /*d7*/	u_char    nc_ia3;
 /*d8*/	u_int32   nc_sbc;	/* SCSI Byte Count (3 bytes only)   */
 /*dc*/	u_int32   nc_csbc;	/* Cumulative SCSI Byte Count       */
+
+                                /* Following for C1010 only         */
+/*e0*/ u_short    nc_crcpad;    /* CRC Value                        */
+/*e2*/ u_char     nc_crccntl0;  /* CRC control register             */
+	#define   SNDCRC  0x10	/* Send CRC Request                 */
+/*e3*/ u_char     nc_crccntl1;  /* CRC control register             */
+/*e4*/ u_int32    nc_crcdata;   /* CRC data register                */ 
+/*e8*/ u_int32	  nc_e8_;	/* rsvd 			    */
+/*ec*/ u_int32	  nc_ec_;	/* rsvd 			    */
+/*f0*/ u_short    nc_dfbc;      /* DMA FIFO byte count              */ 
+
 };
 
 /*-----------------------------------------------------------
@@ -1013,16 +1379,14 @@
 #define REGJ(p,r) (offsetof(struct ncr_reg, p ## r))
 #define REG(r) REGJ (nc_, r)
 
-#ifndef TARGET_MODE
-#define TARGET_MODE 0
-#endif
-
 typedef u_int32 ncrcmd;
 
 /*-----------------------------------------------------------
 **
 **	SCSI phases
 **
+**	DT phases illegal for ncr driver.
+**
 **-----------------------------------------------------------
 */
 
@@ -1030,11 +1394,14 @@
 #define	SCR_DATA_IN	0x01000000
 #define	SCR_COMMAND	0x02000000
 #define	SCR_STATUS	0x03000000
-#define SCR_ILG_OUT	0x04000000
-#define SCR_ILG_IN	0x05000000
+#define SCR_DT_DATA_OUT	0x04000000
+#define SCR_DT_DATA_IN	0x05000000
 #define SCR_MSG_OUT	0x06000000
 #define SCR_MSG_IN      0x07000000
 
+#define SCR_ILG_OUT	0x04000000
+#define SCR_ILG_IN	0x05000000
+
 /*-----------------------------------------------------------
 **
 **	Data transfer via SCSI.
@@ -1053,9 +1420,15 @@
 **-----------------------------------------------------------
 */
 
-#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l))
-#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l))
-#define SCR_MOVE_TBL     (0x18000000 ^ (TARGET_MODE << 1ul))
+#define OPC_MOVE          0x08000000
+
+#define SCR_MOVE_ABS(l) ((0x00000000 | OPC_MOVE) | (l))
+#define SCR_MOVE_IND(l) ((0x20000000 | OPC_MOVE) | (l))
+#define SCR_MOVE_TBL     (0x10000000 | OPC_MOVE)
+
+#define SCR_CHMOV_ABS(l) ((0x00000000) | (l))
+#define SCR_CHMOV_IND(l) ((0x20000000) | (l))
+#define SCR_CHMOV_TBL     (0x10000000)
 
 struct scr_tblmove {
         u_int32  size;
@@ -1068,7 +1441,7 @@
 **
 **-----------------------------------------------------------
 **
-**	SEL_ABS | SCR_ID (0..7)     [ | REL_JMP]
+**	SEL_ABS | SCR_ID (0..15)    [ | REL_JMP]
 **	<<alternate_address>>
 **
 **	SEL_TBL | << dnad_offset>>  [ | REL_JMP]
@@ -1083,7 +1456,7 @@
 #define	SCR_SEL_TBL_ATN	0x43000000
 
 struct scr_tblsel {
-        u_char  sel_0;
+        u_char  sel_scntl4;	
         u_char  sel_sxfer;
         u_char  sel_id;
         u_char  sel_scntl3;
@@ -1302,7 +1675,7 @@
 **	Conditions:
 **	     WHEN (phase)
 **	     IF   (phase)
-**	     CARRY
+**	     CARRYSET
 **	     DATA (data, mask)
 **
 **-----------------------------------------------------------
@@ -1367,6 +1740,7 @@
 #define	M_X_MODIFY_DP	(0x00)
 #define	M_X_SYNC_REQ	(0x01)
 #define	M_X_WIDE_REQ	(0x03)
+#define	M_X_PPR_REQ	(0x04)
 
 /*
 **	Status
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/wd33c93.c linux.18p17/drivers/scsi/wd33c93.c
--- linux.vanilla/drivers/scsi/wd33c93.c	Fri Apr 21 12:46:33 2000
+++ linux.18p17/drivers/scsi/wd33c93.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/wd33c93.h linux.18p17/drivers/scsi/wd33c93.h
--- linux.vanilla/drivers/scsi/wd33c93.h	Fri Apr 21 12:46:33 2000
+++ linux.18p17/drivers/scsi/wd33c93.h	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/Config.in linux.18p17/drivers/sound/Config.in
--- linux.vanilla/drivers/sound/Config.in	Sat Sep  9 18:42:42 2000
+++ linux.18p17/drivers/sound/Config.in	Fri Sep 29 22:19:15 2000
@@ -19,7 +19,9 @@
 
 dep_tristate 'Crystal CS4281' CONFIG_SOUND_CS4281 $CONFIG_SOUND
 dep_tristate 'Crystal SoundFusion (CS4280/461x)' CONFIG_SOUND_FUSION $CONFIG_SOUND
-dep_tristate 'Creative SBLive! (EMU10K1)' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND
+if [ "$CONFIG_PCI" = "y" ]; then
+   dep_tristate 'Creative SBLive! (EMU10K1)' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND
+fi
 dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
 if [ "$CONFIG_SOUND_ES1370" = "y" ]; then
    bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT
@@ -33,10 +35,7 @@
 fi
 
 dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
-
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
-fi
+dep_tristate 'ESS Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
 
 dep_tristate 'Intel ICH (810, 820, 440MX...)' CONFIG_SOUND_ICH $CONFIG_SOUND
 
@@ -98,6 +97,8 @@
    int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128
 fi
 
+dep_tristate 'VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_SOUND
+
 
 dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
 
@@ -200,8 +201,6 @@
       fi
   fi
   
-  dep_tristate 'VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_SOUND_OSS
-
   dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS
   if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then
     bool 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD
@@ -264,7 +263,7 @@
   fi
 
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    dep_tristate 'Support for AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND
+    dep_tristate 'Support for AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND_OSS
     if [ "$CONFIG_SOUND_AD1816" = "y" ]; then
       hex 'AD1816 audio I/O base 530, 604, E80 or F40' CONFIG_AD1816_BASE 530
       int 'AD1816 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_AD1816_IRQ 7
@@ -305,7 +304,7 @@
     dep_tristate 'Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS
     if [ "$CONFIG_SOUND_WAVEARTIST" != "n" ]; then
       hex '  WaveArtist I/O base' CONFIG_WAVEARTIST_BASE 250
-      int '  WaveArtist IRQ' CONFIG_WAVEARTIST_IRQ 28
+      int '  WaveArtist IRQ' CONFIG_WAVEARTIST_IRQ 12
       int '  WaveArtist DMA' CONFIG_WAVEARTIST_DMA 3
       int '  WaveArtist second DMA' CONFIG_WAVEARTIST_DMA2 7
     fi
@@ -313,6 +312,9 @@
   
   dep_tristate 'NM256AV/NM256ZX audio support' CONFIG_SOUND_NM256 $CONFIG_SOUND_OSS
   dep_tristate 'Yamaha PCI legacy mode support' CONFIG_SOUND_YMPCI $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_YMPCI" = "n" ]; then
+    dep_tristate 'Yamaha PCI native mode support (EXPERIMENTAL)' CONFIG_SOUND_YMFPCI $CONFIG_SOUND_OSS
+  fi
 
   # Additional low level drivers.
   
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/Makefile linux.18p17/drivers/sound/Makefile
--- linux.vanilla/drivers/sound/Makefile	Sat Sep  9 18:42:42 2000
+++ linux.18p17/drivers/sound/Makefile	Mon Oct  2 10:04:46 2000
@@ -60,7 +60,6 @@
 obj-$(CONFIG_SOUND_CS4232)	+= uart401.o
 obj-$(CONFIG_SOUND_GUS)		+= gus.o ad1848.o
 obj-$(CONFIG_SOUND_MAD16)	+= mad16.o ad1848.o sb.o uart401.o
-obj-$(CONFIG_SOUND_VIA82CXXX)	+= via82cxxx.o sb.o uart401.o 
 obj-$(CONFIG_SOUND_YMPCI)	+= ymfsb.o sb.o uart401.o ac97.o
 obj-$(CONFIG_SOUND_MAUI)	+= maui.o mpu401.o
 obj-$(CONFIG_SOUND_MPU401)	+= mpu401.o
@@ -97,6 +96,8 @@
 obj-$(CONFIG_SOUND_MAESTRO)	+= maestro.o
 obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
 obj-$(CONFIG_SOUND_TRIDENT)     += trident.o ac97_codec.o
+obj-$(CONFIG_SOUND_VIA82CXXX)	+= via82cxxx_audio.o ac97_codec.o
+obj-$(CONFIG_SOUND_YMFPCI)	+= ymfpci.o ac97_codec.o
 
 ifeq ($(CONFIG_SOUND_EMU10K1),y)
   obj-y				+= emu10k1/emu10k1.o
@@ -121,7 +122,7 @@
 sb-objs		:= sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o	\
 			   sb_ess.o
 softoss2-objs	:= softoss.o softoss_rs.o
-vidc_mod-objs	:= vidc.o vidc_audio.o vidc_fill.o vidc_mixer.o vidc_synth.o
+vidc_mod-objs	:= vidc.o vidc_fill.o vidc_synth.o
 wavefront-objs  := wavfront.o wf_midi.o yss225.o
 nm256-objs	:= nm256_audio.o ac97.o
 via82cxxx-objs  := via82cxxx_audio.o ac97.o
@@ -166,19 +167,19 @@
 
 # Translate to Rules.make lists.
 
-L_TARGET	:= sound.a
+O_TARGET	:= sounddrivers.o
 # This is a nice idea but needs depmod altering
 #MOD_LIST_NAME	:= SOUND_MODULES
 
-L_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
-LX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
+O_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
 M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
 MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
 MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
 MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
 
 ifeq ($(CONFIG_LOWLEVEL_SOUND),y)
-    L_OBJS	+= lowlevel/lowlevel.o
+    O_OBJS	+= lowlevel/lowlevel.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ac97_codec.c linux.18p17/drivers/sound/ac97_codec.c
--- linux.vanilla/drivers/sound/ac97_codec.c	Sat Sep  9 18:42:42 2000
+++ linux.18p17/drivers/sound/ac97_codec.c	Fri Sep 29 22:27:09 2000
@@ -66,6 +66,7 @@
 	{0x43525923, "Cirrus Logic CS4298"    , NULL},
 	{0x4352592B, "Cirrus Logic CS4294"    , NULL},
 	{0x43525931, "Cirrus Logic CS4299"    , NULL},
+	{0x43525934, "Cirrus Logic CS4299"    , NULL},
 	{0x4e534331, "National Semiconductor LM4549" ,	NULL},
 	{0x53494c22, "Silicon Laboratory Si3036"     ,	NULL},
 	{0x53494c23, "Silicon Laboratory Si3038"     ,  NULL},
@@ -74,8 +75,10 @@
 	{0x83847605, "SigmaTel STAC9704"      , NULL},
 	{0x83847608, "SigmaTel STAC9708"      , NULL},
 	{0x83847609, "SigmaTel STAC9721/23"   , sigmatel_init},
+	{0x54524103, "TriTech TR28023"	      , NULL},
 	{0x54524106, "TriTech TR28026"        , NULL},
 	{0x54524108, "TriTech TR28028"        , NULL},
+	{0x54524123, "TriTech TR?????"	      , NULL},	
 	{0x574D4C00, "Wolfson WM9704"         , NULL},
 	{0x00000000, NULL, NULL}
 };
@@ -122,20 +125,20 @@
 	unsigned int value;
 } mixer_defaults[SOUND_MIXER_NRDEVICES] = {
 	/* all values 0 -> 100 in bytes */
-	{SOUND_MIXER_VOLUME,	0x3232},
-	{SOUND_MIXER_BASS,	0x3232},
-	{SOUND_MIXER_TREBLE,	0x3232},
-	{SOUND_MIXER_PCM,	0x3232},
-	{SOUND_MIXER_SPEAKER,	0x3232},
-	{SOUND_MIXER_LINE,	0x3232},
-	{SOUND_MIXER_MIC,	0x3232},
-	{SOUND_MIXER_CD,	0x3232},
-	{SOUND_MIXER_ALTPCM,	0x3232},
-	{SOUND_MIXER_IGAIN,	0x3232},
-	{SOUND_MIXER_LINE1,	0x3232},
-	{SOUND_MIXER_PHONEIN,	0x3232},
-	{SOUND_MIXER_PHONEOUT,	0x3232},
-	{SOUND_MIXER_VIDEO,	0x3232},
+	{SOUND_MIXER_VOLUME,	0x4343},
+	{SOUND_MIXER_BASS,	0x4343},
+	{SOUND_MIXER_TREBLE,	0x4343},
+	{SOUND_MIXER_PCM,	0x4343},
+	{SOUND_MIXER_SPEAKER,	0x4343},
+	{SOUND_MIXER_LINE,	0x4343},
+	{SOUND_MIXER_MIC,	0x4343},
+	{SOUND_MIXER_CD,	0x4343},
+	{SOUND_MIXER_ALTPCM,	0x4343},
+	{SOUND_MIXER_IGAIN,	0x4343},
+	{SOUND_MIXER_LINE1,	0x4343},
+	{SOUND_MIXER_PHONEIN,	0x4343},
+	{SOUND_MIXER_PHONEOUT,	0x4343},
+	{SOUND_MIXER_VIDEO,	0x4343},
 	{-1,0}
 };
 
@@ -144,20 +147,20 @@
 	unsigned char offset;
 	int scale;
 } ac97_hw[SOUND_MIXER_NRDEVICES]= {
-	[SOUND_MIXER_VOLUME]	=	{AC97_MASTER_VOL_STEREO,63},
-	[SOUND_MIXER_BASS]	=	{AC97_MASTER_TONE,	15},
-	[SOUND_MIXER_TREBLE]	=	{AC97_MASTER_TONE,	15},
-	[SOUND_MIXER_PCM]	=	{AC97_PCMOUT_VOL,	31},
-	[SOUND_MIXER_SPEAKER]	=	{AC97_PCBEEP_VOL,	15},
-	[SOUND_MIXER_LINE]	=	{AC97_LINEIN_VOL,	31},
-	[SOUND_MIXER_MIC]	=	{AC97_MIC_VOL,		31},
-	[SOUND_MIXER_CD]	=	{AC97_CD_VOL,		31},
-	[SOUND_MIXER_ALTPCM]	=	{AC97_HEADPHONE_VOL,	63},
-	[SOUND_MIXER_IGAIN]	=	{AC97_RECORD_GAIN,	31},
-	[SOUND_MIXER_LINE1]	=	{AC97_AUX_VOL,		31},
-	[SOUND_MIXER_PHONEIN]	= 	{AC97_PHONE_VOL,	15},
-	[SOUND_MIXER_PHONEOUT]	= 	{AC97_MASTER_VOL_MONO,	63},
-	[SOUND_MIXER_VIDEO]	=	{AC97_VIDEO_VOL,	31},
+	[SOUND_MIXER_VOLUME]	=	{AC97_MASTER_VOL_STEREO,64},
+	[SOUND_MIXER_BASS]	=	{AC97_MASTER_TONE,	16},
+	[SOUND_MIXER_TREBLE]	=	{AC97_MASTER_TONE,	16},
+	[SOUND_MIXER_PCM]	=	{AC97_PCMOUT_VOL,	32},
+	[SOUND_MIXER_SPEAKER]	=	{AC97_PCBEEP_VOL,	16},
+	[SOUND_MIXER_LINE]	=	{AC97_LINEIN_VOL,	32},
+	[SOUND_MIXER_MIC]	=	{AC97_MIC_VOL,		32},
+	[SOUND_MIXER_CD]	=	{AC97_CD_VOL,		32},
+	[SOUND_MIXER_ALTPCM]	=	{AC97_HEADPHONE_VOL,	64},
+	[SOUND_MIXER_IGAIN]	=	{AC97_RECORD_GAIN,	16},
+	[SOUND_MIXER_LINE1]	=	{AC97_AUX_VOL,		32},
+	[SOUND_MIXER_PHONEIN]	= 	{AC97_PHONE_VOL,	32},
+	[SOUND_MIXER_PHONEOUT]	= 	{AC97_MASTER_VOL_MONO,	64},
+	[SOUND_MIXER_VIDEO]	=	{AC97_VIDEO_VOL,	32},
 };
 
 /* the following tables allow us to go from OSS <-> ac97 quickly. */
@@ -199,11 +202,14 @@
 {
 	u16 val;
 	int ret = 0;
+	int scale;
 	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
 
 	val = codec->codec_read(codec , mh->offset);
 
-	if (AC97_STEREO_MASK & (1 << oss_channel)) {
+	if (val & AC97_MUTE) {
+		ret = 0;
+	} else if (AC97_STEREO_MASK & (1 << oss_channel)) {
 		/* nice stereo mixers .. */
 		int left,right;
 
@@ -214,8 +220,14 @@
 			right = (right * 100) / mh->scale;
 			left = (left * 100) / mh->scale;
 		} else {
-			right = 100 - ((right * 100) / mh->scale);
-			left = 100 - ((left * 100) / mh->scale);
+			/* these may have 5 or 6 bit resolution */
+			if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
+				scale = (1 << codec->bit_resolution);
+			else
+				scale = mh->scale;
+
+			right = 100 - ((right * 100) / scale);
+			left = 100 - ((left * 100) / scale);
 		}
 		ret = left | (right << 8);
 	} else if (oss_channel == SOUND_MIXER_SPEAKER) {
@@ -223,7 +235,8 @@
 	} else if (oss_channel == SOUND_MIXER_PHONEIN) {
 		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
 	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {
-		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+		scale = (1 << codec->bit_resolution);
+		ret = 100 - (((val & 0x1f) * 100) / scale);
 	} else if (oss_channel == SOUND_MIXER_MIC) {
 		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
 		/*  the low bit is optional in the tone sliders and masking
@@ -250,6 +263,7 @@
 		      unsigned int left, unsigned int right)
 {
 	u16 val = 0;
+	int scale;
 	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
 
 #ifdef DEBUG
@@ -261,31 +275,45 @@
 
 	if (AC97_STEREO_MASK & (1 << oss_channel)) {
 		/* stereo mixers */
-		if (oss_channel == SOUND_MIXER_IGAIN) {
-			right = (right * mh->scale) / 100;
-			left = (left * mh->scale) / 100;
+		if (left == 0 && right == 0) {
+			val = AC97_MUTE;
 		} else {
-			right = ((100 - right) * mh->scale) / 100;
-			left = ((100 - left) * mh->scale) / 100;
-		}			
-		val = (left << 8) | right;
+			if (oss_channel == SOUND_MIXER_IGAIN) {
+				right = (right * mh->scale) / 100;
+				left = (left * mh->scale) / 100;
+			} else {
+				/* these may have 5 or 6 bit resolution */
+				if (oss_channel == SOUND_MIXER_VOLUME ||
+				    oss_channel == SOUND_MIXER_ALTPCM)
+					scale = (1 << codec->bit_resolution);
+				else
+					scale = mh->scale;
+
+				right = ((100 - right) * scale) / 100;
+				left = ((100 - left) * scale) / 100;
+			}			
+			val = (left << 8) | right;
+		}
+	} else if (oss_channel == SOUND_MIXER_BASS) {
+		val = codec->codec_read(codec , mh->offset) & ~0x0f00;
+		val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
+	} else if (oss_channel == SOUND_MIXER_TREBLE) {
+		val = codec->codec_read(codec , mh->offset) & ~0x000f;
+		val |= (((100 - left) * mh->scale) / 100) & 0x000e;
+	} else if(left == 0) {
+		val = AC97_MUTE;
 	} else if (oss_channel == SOUND_MIXER_SPEAKER) {
 		val = (((100 - left) * mh->scale) / 100) << 1;
 	} else if (oss_channel == SOUND_MIXER_PHONEIN) {
 		val = (((100 - left) * mh->scale) / 100);
 	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {
-		val = (((100 - left) * mh->scale) / 100);
+		scale = (1 << codec->bit_resolution);
+		val = (((100 - left) * scale) / 100);
 	} else if (oss_channel == SOUND_MIXER_MIC) {
 		val = codec->codec_read(codec , mh->offset) & ~0x801f;
 		val |= (((100 - left) * mh->scale) / 100);
 		/*  the low bit is optional in the tone sliders and masking
 		    it lets us avoid the 0xf 'bypass'.. */
-	} else if (oss_channel == SOUND_MIXER_BASS) {
-		val = codec->codec_read(codec , mh->offset) & ~0x0f00;
-		val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
-	} else if (oss_channel == SOUND_MIXER_TREBLE) {
-		val = codec->codec_read(codec , mh->offset) & ~0x000f;
-		val |= (((100 - left) * mh->scale) / 100) & 0x000e;
 	}
 #ifdef DEBUG
 	printk(" 0x%04x", val);
@@ -422,7 +450,8 @@
 
 	if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) {
 		codec->modcnt++;
-		get_user_ret(val, (int *)arg, -EFAULT);
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
 
 		switch (_IOC_NR(cmd)) {
 		case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
@@ -556,6 +585,7 @@
 	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
 	for (i = 0; i < arraysize(ac97_codec_ids); i++) {
 		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
+			codec->type = ac97_codec_ids[i].id;
 			codec->name = ac97_codec_ids[i].name;
 			codec->codec_init = ac97_codec_ids[i].init;
 			break;
@@ -586,18 +616,21 @@
 	if (!(cap & 0x10))
 		codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
 
+	/* detect bit resolution */
+	codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
+	if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x1f1f)
+		codec->bit_resolution = 5;
+	else
+		codec->bit_resolution = 6;
+
 	/* generic OSS to AC97 wrapper */
 	codec->read_mixer = ac97_read_mixer;
 	codec->write_mixer = ac97_write_mixer;
 	codec->recmask_io = ac97_recmask_io;
 	codec->mixer_ioctl = ac97_mixer_ioctl;
 
-	/* initialize volume level */
-	codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0L);
-	codec->codec_write(codec, AC97_PCMOUT_VOL, 0L);
-
 	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
-	if (codec->id != 0 && codec->codec_init != NULL) {
+	if (codec->codec_init != NULL) {
 		codec->codec_init(codec);
 	}
 
@@ -621,6 +654,11 @@
 
 static int sigmatel_init(struct ac97_codec * codec)
 {
+	if(codec->id == 0)
+		return 0;
+		
+	/* Only set up secondary codec */
+	
 	codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
 
 	/* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
@@ -647,6 +685,7 @@
 {
 	codec->codec_write(codec, AC97_POWER_CONTROL,
 		codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
+	return 0;
 }
 	
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs4281.c linux.18p17/drivers/sound/cs4281.c
--- linux.vanilla/drivers/sound/cs4281.c	Sat Sep  9 18:42:42 2000
+++ linux.18p17/drivers/sound/cs4281.c	Thu Oct 19 01:26:31 2000
@@ -75,24 +75,6 @@
 
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE < 0x02030d
-#ifdef MODULE
-#define __exit
-#define module_exit(x) void cleanup_module(void) { x(); }
-#define module_init(x) int init_module(void) { return x(); }
-#else
-#define __exit __attribute__ ((unused, __section__ (".text.init")))
-#define module_exit(x) // nothing 
-#define module_init(x) // nothing 
-#endif
-
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define wait_queue_head_t struct wait_queue *
-#define init_waitqueue_head(w) *(w) = 0
-#define init_MUTEX(m) *(m) = MUTEX
-#endif
-
 // MIDI buffer sizes 
 #define MIDIINBUF  500
 #define MIDIOUTBUF 500
@@ -123,7 +105,6 @@
         unsigned int pBA0phys, pBA1phys;
         char *pBA0, *pBA1; 
         unsigned int irq;
-	int endofbuffer;
 
         // mixer registers 
         struct {
@@ -182,7 +163,7 @@
 };
 
 
-struct cs4281_state *devs = NULL;
+static struct cs4281_state *devs = NULL;
 // --------------------------------------------------------------------- 
 //
 //		Hardware Interfaces For the CS4281
@@ -870,7 +851,7 @@
 static void cs4281_update_ptr(struct cs4281_state *s)
 {
         int diff;
-        unsigned hwptr, va, temp1;
+        unsigned hwptr, va;
 
         // update ADC pointer 
         if (s->ena & FMODE_READ) {
@@ -895,7 +876,7 @@
 	// check for end of buffer, means that we are going to wait for another interrupt
 	// to allow silence to fill the fifos on the part, to keep pops down to a minimum.
 	//
-        if ( (s->ena & FMODE_WRITE) && (!s->endofbuffer) )
+        if (s->ena & FMODE_WRITE)
 	{
                 hwptr = readl(s->pBA0+BA0_DCA0);          // Read play DMA address.
                 va = virt_to_bus(s->dma_dac.rawbuf);
@@ -910,15 +891,12 @@
                 } else {
                         s->dma_dac.count -= diff;
                         if (s->dma_dac.count <= 0) {
-                                s->ena &= ~FMODE_WRITE;
-                                temp1 = readl(s->pBA0+BA0_DCR0);
 			//
-			// fill with silence, and wait on turning off the DAC until interrupt routine.
-			// wait on "Poke(pBA0+BA0_DCR0, temp1 | DCRn_MSK);    // Stop Play DMA"
+			// fill with silence, and do not shut down the DAC.
+			// Continue to play silence until the _release.
 			//
 				memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, 
 					s->dma_dac.dmasize); 
-				s->endofbuffer = 1;
                         } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize
                                                          && !s->dma_dac.endcleared) {
                                 clear_advance(s->dma_dac.rawbuf, 
@@ -1927,7 +1905,6 @@
         s->rate = 8000;
         s->clkdiv = 96 | 0x80;
         s->ena = 0;
-	s->endofbuffer = 0;
         s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
         s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
         s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
@@ -2023,22 +2000,7 @@
         writel(HICR_IEV| HICR_CHGM, s->pBA0+BA0_HICR);  // Local EOI
         
         spin_lock(&s->lock);
-	//
-	// ok, at this point we assume that the fifos have been filled
-	// with silence and so we now turn off the DMA engine.
-	// if FMODE_WRITE is set that means that some thread
-	// attempted to start_dac, which probably means that an open
-	// occurred, so do not stop the dac in this case.
-	//
-	if(s->endofbuffer && !(s->ena & FMODE_WRITE))
-	{
-		writel(temp1|DCRn_MSK, s->pBA0+BA0_DCR0);    // Stop Play DMA
-		s->endofbuffer = 0;
-	}
-	else
-	{
-        	cs4281_update_ptr(s);
-	}
+        cs4281_update_ptr(s);
         cs4281_handle_midi(s);
         spin_unlock(&s->lock);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs46xx.c linux.18p17/drivers/sound/cs46xx.c
--- linux.vanilla/drivers/sound/cs46xx.c	Sat Sep  9 18:42:42 2000
+++ linux.18p17/drivers/sound/cs46xx.c	Thu Oct 19 01:30:12 2000
@@ -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;
 }
 
@@ -1141,10 +1117,16 @@
 	return mask;
 }
 
+/*
+ *	We let users mmap the ring buffer. Its not the real DMA buffer but
+ *	that side of the code is hidden in the IRQ handling. We do a software
+ *	emulation of DMA from a 64K or so buffer into a 2K FIFO. 
+ *	(the hardware probably deserves a moan here but Crystal send me nice
+ *	toys ;)).
+ */
+ 
 static int cs_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	return -EINVAL;
-#if 0	
 	struct cs_state *state = (struct cs_state *)file->private_data;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	int ret;
@@ -1171,7 +1153,6 @@
 	dmabuf->mapped = 1;
 
 	return 0;
-#endif	
 }
 
 static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
@@ -1822,34 +1803,15 @@
 	return cs461x_peekBA0(card, BA0_ACSDA);
 }
 
-/*
- *	Do we have the CD potentially enabled either left or right ?
- */
- 
-static int cd_active(int r)
-{
-	int l=(r>>8)&0x7;
-	r&=7;
-	if(l==1 || r==1)
-		return 1;		/* CD input */
-	if(l==5 || r==5)
-		return 1;		/* Mixer input */
-	if(l==6 || r==6)
-		return 1;		/* Mixer 16bit input */
-	return 0;
-}
-
 static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
 {
 	struct cs_card *card = dev->private_data;
 	int count;
 	int val2;
-	int val3;
 	
-	if(reg==AC97_RECORD_SELECT || reg == AC97_CD_VOL)
+	if(reg == AC97_CD_VOL)
 	{
-		val2 = cs_ac97_get(dev, AC97_RECORD_SELECT);
-		val3 = cs_ac97_get(dev, AC97_CD_VOL);
+		val2 = cs_ac97_get(dev, AC97_CD_VOL);
 	}
 	
 	/*
@@ -1907,35 +1869,31 @@
 	 *
 	 *	When the CD mute changes we adjust the power level if the
 	 *	CD was a valid input.
+	 *
+	 *      We also check for CD volume != 0, as the CD mute isn't
+	 *      normally tweaked from userspace.
 	 */
 	 
-	if(reg==AC97_RECORD_SELECT && cd_active(val)!=cd_active(val2))
-	{
-		int n=-1;
-		/* If we are turning on the port and it is not muted then
-		   bump the power level. If we are turning it off and its
-		   not muted drop the power level */
-		if(cd_active(val))
-			n=1;
-		if(!(val3 & 0x8000))
-			card->amplifier_ctrl(card, n);
-	}
-	
 	/* CD mute change ? */
 	
 	if(reg==AC97_CD_VOL)
 	{
-		if(cd_active(val2))
+		/* Mute bit change ? */
+		if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val))
 		{
-			/* Mute bit change ? */
-			if((val3^val)&0x8000)
-			{
-				/* Mute on */
-				if(val&0x8000)
-					card->amplifier_ctrl(card, -1);
-				else /* Mute off power on */
-					card->amplifier_ctrl(card, 1);
-			}
+			/* This is a hack but its cleaner than the alternatives.
+			   Right now card->ac97_codec[0] might be NULL as we are
+			   still doing codec setup. This does an early assignment
+			   to avoid the problem if it occurs */
+			   
+			if(card->ac97_codec[0]==NULL)
+				card->ac97_codec[0]=dev;
+				
+			/* Mute on */
+			if(val&0x8000 || val == 0x1f1f)
+				card->amplifier_ctrl(card, -1);
+			else /* Mute off power on */
+				card->amplifier_ctrl(card, 1);
 		}
 	}
 }
@@ -2036,12 +1994,6 @@
 		
 		card->ac97_features = eid;
 			
-		/* If the card has the CD enabled then bump the power to
-		   account for it */
-		   	
-		if(cd_active(cs_ac97_get(codec, AC97_RECORD_SELECT)))
-			card->amplifier_ctrl(card, 1);
-			
 		if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
 			printk(KERN_ERR "cs461x: couldn't register mixer!\n");
 			kfree(codec);
@@ -2343,7 +2295,7 @@
 	 *  generating bit clock (so we don't try to start the PLL without an
 	 *  input clock).
 	 */
-	mdelay(5);		/* 1 should be enough ?? */
+	mdelay(5);		/* 1 should be enough ?? (and pigs might fly) */
 
 	/*
 	 *  Set the serial port timing configuration, so that
@@ -2548,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)
@@ -2809,4 +2761,4 @@
 MODULE_PARM(external_amp, "i");
 MODULE_PARM(thinkpad, "i");
 
-#endif
\ No newline at end of file
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/8010.h linux.18p17/drivers/sound/emu10k1/8010.h
--- linux.vanilla/drivers/sound/emu10k1/8010.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/8010.h	Tue Sep  5 21:50:50 2000
@@ -41,10 +41,14 @@
 
 /* ------------------- DEFINES -------------------- */
 
-#define CMD_WRITEFN0	0x0
-#define CMD_READFN0	0x1
-#define CMD_WRITEPTR	0x2
-#define CMD_READPTR	0x3
+#define CMD_WRITEFN0		0x0
+#define CMD_READFN0		0x1
+#define CMD_WRITEPTR		0x2
+#define CMD_READPTR		0x3
+#define CMD_SETRECSRC		0x4
+#define CMD_GETRECSRC		0x5
+#define CMD_GETVOICEPARAM	0x6
+#define CMD_SETVOICEPARAM	0x7
 
 struct mixer_private_ioctl {
 	u32 cmd;
@@ -175,6 +179,7 @@
 #define HCFG_AC3ENABLE_MASK	0x0x0000e0	/* AC3 async input control - Not implemented	*/
 #define HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Channels 0 and 1 replace ZVIDEO		*/
 #define HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Channels 0 and 1 replace CDSPDIF		*/
+#define HCFG_AC3ENABLE_GPSPDIF  0x00000020      /* Channels 0 and 1 replace GPSPDIF             */
 #define HCFG_AUTOMUTE		0x00000010	/* When set, the async sample rate convertors	*/
 						/* will automatically mute their output when	*/
 						/* they are not rate-locked to the external	*/
@@ -337,10 +342,12 @@
 #define CCCA_CURRADDR		0x18000008
 
 #define CCR			0x09		/* Cache control register				*/
-#define CCR_CACHEINVALIDSIZE	0xfe000000	/* Number of invalid samples cache for this channel    	*/
+#define CCR_CACHEINVALIDSIZE	0x07190009
+#define CCR_CACHEINVALIDSIZE_MASK	0xfe000000	/* Number of invalid samples cache for this channel    	*/
 #define CCR_CACHELOOPFLAG	0x01000000	/* 1 = Cache has a loop service pending			*/
 #define CCR_INTERLEAVEDSAMPLES	0x00800000	/* 1 = A cache service will fetch interleaved samples	*/
 #define CCR_WORDSIZEDSAMPLES	0x00400000	/* 1 = A cache service will fetch word sized samples	*/
+#define CCR_READADDRESS		0x06100009
 #define CCR_READADDRESS_MASK	0x003f0000	/* Location of cache just beyond current cache service	*/
 #define CCR_LOOPINVALSIZE	0x0000fe00	/* Number of invalid samples in cache prior to loop	*/
 						/* NOTE: This is valid only if CACHELOOPFLAG is set	*/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/Makefile linux.18p17/drivers/sound/emu10k1/Makefile
--- linux.vanilla/drivers/sound/emu10k1/Makefile	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/Makefile	Tue Sep  5 21:50:50 2000
@@ -4,16 +4,16 @@
 
 ifeq ($(CONFIG_SOUND_EMU10K1),y)
     O_TARGET := emu10k1.o
-    O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o emuadxmg.o \
-             emu_wrapper.o hwaccess.o irqmgr.o main.o midi.o mixer.o \
-             recmgr.o timer.o voicemgr.o
+    O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \
+             emuadxmg.o emu_wrapper.o hwaccess.o irqmgr.o main.o midi.o \
+             mixer.o recmgr.o timer.o voicemgr.o
 else
   ifeq ($(CONFIG_SOUND_EMU10K1),m)
     M_OBJS := emu10k1.o
     O_TARGET := emu10k1.o
-    O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o emuadxmg.o \
-             emu_wrapper.o hwaccess.o irqmgr.o main.o midi.o mixer.o \
-             recmgr.o timer.o voicemgr.o
+    O_OBJS = audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \
+             emuadxmg.o emu_wrapper.o hwaccess.o irqmgr.o main.o midi.o \
+             mixer.o recmgr.o timer.o voicemgr.o
   endif
 endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/audio.c linux.18p17/drivers/sound/emu10k1/audio.c
--- linux.vanilla/drivers/sound/emu10k1/audio.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/audio.c	Mon Sep 11 17:07:42 2000
@@ -34,7 +34,13 @@
 #include <linux/module.h>
 #include <linux/poll.h>
 #include <linux/malloc.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
 #include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+
 
 #include "hwaccess.h"
 #include "cardwo.h"
@@ -47,7 +53,7 @@
 static void calculate_ifrag(struct wiinst *);
 
 /* Audio file operations */
-static loff_t emu10k1_audio_llseek(struct file *file, loff_t offset, int nOrigin)
+static loff_t emu10k1_audio_llseek(struct file *file, loff_t offset, int origin)
 {
 	return -ESPIPE;
 }
@@ -61,7 +67,7 @@
 
 	GET_INODE_STRUCT();
 
-	DPD(4, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count);
+	DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count);
 
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -112,7 +118,7 @@
 
 		spin_unlock_irqrestore(&wiinst->lock, flags);
 
-		DPD(4, "bytestocopy --> %d\n", bytestocopy);
+		DPD(3, "bytestocopy --> %d\n", bytestocopy);
 
 		if ((bytestocopy >= wiinst->buffer.fragment_size)
 		    || (bytestocopy >= count)) {
@@ -140,7 +146,7 @@
 		}
 	}
 
-	DPD(4, "bytes copied -> %d\n", (u32) ret);
+	DPD(3, "bytes copied -> %d\n", (u32) ret);
 
 	return ret;
 }
@@ -154,7 +160,7 @@
 
 	GET_INODE_STRUCT();
 
-	DPD(4, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count);
+	DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count);
 
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -200,7 +206,7 @@
 		emu10k1_waveout_getxfersize(woinst, &bytestocopy);
 		spin_unlock_irqrestore(&woinst->lock, flags);
 
-		DPD(4, "bytestocopy --> %d\n", bytestocopy);
+		DPD(3, "bytestocopy --> %d\n", bytestocopy);
 
 		if ((bytestocopy >= woinst->buffer.fragment_size)
 		    || (bytestocopy >= count)) {
@@ -238,7 +244,7 @@
 		}
 	}
 
-	DPD(4, "bytes copied -> %d\n", (u32) ret);
+	DPD(3, "bytes copied -> %d\n", (u32) ret);
 
 	return ret;
 }
@@ -278,7 +284,7 @@
 
 					/* Undo marking the pages as reserved */
 					for (i = 0; i < woinst->buffer.pages; i++)
-						set_bit(PG_reserved, &mem_map[MAP_NR(woinst->buffer.addr[i])].flags);
+						mem_map_reserve(MAP_NR(woinst->buffer.addr[i]));
 				}
 
 				emu10k1_waveout_close(wave_dev);
@@ -328,7 +334,7 @@
 
 					/* Undo marking the pages as reserved */
 					for (i = 0; i < woinst->buffer.pages; i++)
-						set_bit(PG_reserved, &mem_map[MAP_NR(woinst->buffer.addr[i])].flags);
+						mem_map_reserve(MAP_NR(woinst->buffer.addr[i]));
 				}
 
 				emu10k1_waveout_close(wave_dev);
@@ -367,7 +373,9 @@
 	case SNDCTL_DSP_SPEED:
 		DPF(2, "SNDCTL_DSP_SPEED:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		DPD(2, "val is %d\n", val);
 
 		if (val > 0) {
@@ -421,7 +429,9 @@
 	case SNDCTL_DSP_STEREO:
 		DPF(2, "SNDCTL_DSP_STEREO:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		DPD(2, " val is %d\n", val);
 
 		if (file->f_mode & FMODE_READ) {
@@ -466,7 +476,9 @@
 	case SNDCTL_DSP_CHANNELS:
 		DPF(2, "SNDCTL_DSP_CHANNELS:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		DPD(2, " val is %d\n", val);
 
 		if (val > 0) {
@@ -528,7 +540,9 @@
 	case SNDCTL_DSP_SETFMT:	/* Same as SNDCTL_DSP_SAMPLESIZE */
 		DPF(2, "SNDCTL_DSP_SETFMT:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		DPD(2, " val is %d\n", val);
 
 		if (val != AFMT_QUERY) {
@@ -630,7 +644,8 @@
 	case SNDCTL_DSP_SETTRIGGER:
 		DPF(2, "SNDCTL_DSP_SETTRIGGER:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
 
 		if (file->f_mode & FMODE_WRITE) {
 			spin_lock_irqsave(&woinst->lock, flags);
@@ -856,7 +871,8 @@
 	case SNDCTL_DSP_SETFRAGMENT:
 		DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
 
 		DPD(2, "val is 0x%x\n", val);
 
@@ -947,7 +963,7 @@
 
 			/* Now mark the pages as reserved, otherwise remap_page_range doesn't do what we want */
 			for (i = 0; i < woinst->buffer.pages; i++)
-				set_bit(PG_reserved, &mem_map[MAP_NR(woinst->buffer.addr[i])].flags);
+				mem_map_reserve(MAP_NR(woinst->buffer.addr[i]));
 		}
 
 		size = vma->vm_end - vma->vm_start;
@@ -995,7 +1011,7 @@
 	list_for_each(entry, &emu10k1_devs) {
 		card = list_entry(entry, struct emu10k1_card, list);
 
-		if (!((card->audio1_num ^ minor) & ~0xf) || !((card->audio2_num ^ minor) & ~0xf))
+		if (!((card->audio_num ^ minor) & ~0xf) || !((card->audio1_num ^ minor) & ~0xf))
 			break;
 	}
 
@@ -1026,7 +1042,10 @@
 			return -ENODEV;
 		}
 
-		switch (card->wavein->recsrc) {
+		wiinst->recsrc = card->wavein.recsrc;
+                wiinst->fxwc = card->wavein.fxwc;
+
+		switch (wiinst->recsrc) {
 		case WAVERECORD_AC97:
 			wiinst->format.samplingrate = 8000;
 			wiinst->format.bitsperchannel = 16;
@@ -1040,7 +1059,7 @@
 		case WAVERECORD_FX:
 			wiinst->format.samplingrate = 48000;
 			wiinst->format.bitsperchannel = 16;
-			wiinst->format.channels = 2;
+			wiinst->format.channels = hweight32(wiinst->fxwc);
 			break;
 		default:
 			BUG();
@@ -1049,7 +1068,6 @@
 
 		wiinst->state = WAVE_STATE_CLOSED;
 
-		wiinst->recsrc = card->wavein->recsrc;
 		wiinst->buffer.ossfragshift = 0;
 		wiinst->buffer.fragment_size = 0;
 		wiinst->buffer.numfrags = 0;
@@ -1087,7 +1105,7 @@
 		woinst->buffer.fragment_size = 0;
 		woinst->buffer.ossfragshift = 0;
 		woinst->buffer.numfrags = 0;
-		woinst->device = (card->audio2_num == minor);
+		woinst->device = (card->audio1_num == minor);
 
 		init_waitqueue_head(&woinst->wait_queue);
 
@@ -1149,9 +1167,11 @@
 static int emu10k1_audio_release(struct inode *inode, struct file *file)
 {
 	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
-	struct emu10k1_card *card = wave_dev->card;
+	struct emu10k1_card *card;
 	unsigned long flags;
 
+	card = wave_dev->card;
+
 	DPF(2, "emu10k1_audio_release()\n");
 
 	if (file->f_mode & FMODE_WRITE) {
@@ -1177,7 +1197,7 @@
 
 				/* Undo marking the pages as reserved */
 				for (i = 0; i < woinst->buffer.pages; i++)
-					set_bit(PG_reserved, &mem_map[MAP_NR(woinst->buffer.addr[i])].flags);
+					mem_map_reserve(MAP_NR(woinst->buffer.addr[i]));
 			}
 
 			emu10k1_waveout_close(wave_dev);
@@ -1451,7 +1471,7 @@
 	if (bytestocopy >= wiinst->buffer.fragment_size)
 		wake_up_interruptible(&wiinst->wait_queue);
 	else
-		DPD(4, "Not enough transfer size, %d\n", bytestocopy);
+		DPD(3, "Not enough transfer size, %d\n", bytestocopy);
 
 	return;
 }
@@ -1482,18 +1502,18 @@
 	if (bytestocopy >= woinst->buffer.fragment_size)
 		wake_up_interruptible(&woinst->wait_queue);
 	else
-		DPD(4, "Not enough transfer size -> %d\n", bytestocopy);
+		DPD(3, "Not enough transfer size -> %d\n", bytestocopy);
 
 	return;
 }
 
 struct file_operations emu10k1_audio_fops = {
-	llseek:emu10k1_audio_llseek,
-	read:emu10k1_audio_read,
-	write:emu10k1_audio_write,
-	poll:emu10k1_audio_poll,
-	ioctl:emu10k1_audio_ioctl,
-	mmap:emu10k1_audio_mmap,
-	open:emu10k1_audio_open,
-	release:emu10k1_audio_release,
+	llseek:		emu10k1_audio_llseek,
+	read:		emu10k1_audio_read,
+	write:		emu10k1_audio_write,
+	poll:		emu10k1_audio_poll,
+	ioctl:		emu10k1_audio_ioctl,
+	mmap:		emu10k1_audio_mmap,
+	open:		emu10k1_audio_open,
+	release:	emu10k1_audio_release,
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardmi.c linux.18p17/drivers/sound/emu10k1/cardmi.c
--- linux.vanilla/drivers/sound/emu10k1/cardmi.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardmi.c	Tue Sep  5 21:50:50 2000
@@ -81,7 +81,7 @@
 	DPF(2, "emu10k1_mpuin_open\n");
 
 	if (!(card_mpuin->status & FLAGS_AVAILABLE))
-		return CTSTATUS_INUSE;
+		return -1;
 
 	/* Copy open info and mark channel as in use */
 	card_mpuin->openinfo = *openinfo;
@@ -98,7 +98,7 @@
 	emu10k1_mpu_reset(card);
 	emu10k1_mpu_acquire(card);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int emu10k1_mpuin_close(struct emu10k1_card *card)
@@ -110,7 +110,7 @@
 	/* Check if there are pending input SysEx buffers */
 	if (card_mpuin->firstmidiq != NULL) {
 		ERROR();
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	/* Disable RX interrupt */
@@ -121,7 +121,7 @@
 	card_mpuin->status |= FLAGS_AVAILABLE;	/* set */
 	card_mpuin->status &= ~FLAGS_MIDM_STARTED;	/* clear */
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* Adds MIDI buffer to local queue list                         */
@@ -139,7 +139,7 @@
 
 	if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) {
 		/* Message lost */
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	midiq->next = NULL;
@@ -161,7 +161,7 @@
 
 	spin_unlock_irqrestore(&card_mpuin->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* First set the Time Stamp if MIDI IN has not started.         */
@@ -179,7 +179,7 @@
 	if (card_mpuin->status & FLAGS_MIDM_STARTED) {
 		DPF(2, "Time Stamp not changed\n");
 	} else {
-		while (emu10k1_mpu_read_data(card, &dummy) == CTSTATUS_SUCCESS);
+		while (!emu10k1_mpu_read_data(card, &dummy));
 
 		card_mpuin->status |= FLAGS_MIDM_STARTED;	/* set */
 
@@ -193,7 +193,7 @@
 		emu10k1_irq_enable(card, INTE_MIDIRXENABLE);
 	}
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* Disable the RX Irq.  If a partial recorded buffer            */
@@ -234,7 +234,7 @@
 		}
 	}
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* Disable the RX Irq.  If any buffer                           */
@@ -264,7 +264,7 @@
 	card_mpuin->lastmidiq = NULL;
 	card_mpuin->status &= ~FLAGS_MIDM_STARTED;
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* Passes the message with the data back to the client          */
@@ -307,7 +307,7 @@
 	/* Notify client that Sysex buffer has been sent */
 	emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 void emu10k1_mpuin_bh(unsigned long refdata)
@@ -349,13 +349,13 @@
 	idx = card_mpuin->qtail;
 
 	while (1) {
-		if (emu10k1_mpu_read_data(card, &MPUIvalue) == CTSTATUS_SUCCESS) {
+		if (emu10k1_mpu_read_data(card, &MPUIvalue) < 0) {
+			break;
+		} else {
 			++count;
 			card_mpuin->midiq[idx].data = MPUIvalue;
 			card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ;
 			idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
-		} else {
-			break;
 		}
 	}
 
@@ -365,7 +365,7 @@
 		tasklet_hi_schedule(&card_mpuin->tasklet);
 	}
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /*****************************************************************************/
@@ -385,7 +385,7 @@
 	card_mpuin->timestart = 0;
 	card_mpuin->timein = 0;
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* FIXME: This should be a macro */
@@ -430,7 +430,7 @@
 
 		case 0x7:
 			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0);
-			return CTSTATUS_ERROR;
+			return -1;
 
 		case 0x2:
 			card_mpuin->laststate = card_mpuin->curstate;
@@ -452,7 +452,7 @@
 
 	default:
 		DPF(2, "BUG: default case hit\n");
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
@@ -494,7 +494,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->data = data;
@@ -525,7 +525,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->curstate = STIN_3BYTE;
@@ -537,7 +537,7 @@
 
 	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data)
@@ -578,7 +578,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->curstate = STIN_2BYTE;
@@ -588,7 +588,7 @@
 
 	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data)
@@ -620,7 +620,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->curstate = card_mpuin->laststate;
@@ -630,7 +630,7 @@
 
 	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data)
@@ -662,7 +662,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->data = data;
@@ -694,7 +694,7 @@
 
 		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	card_mpuin->curstate = card_mpuin->laststate;
@@ -706,7 +706,7 @@
 
 	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data)
@@ -744,7 +744,7 @@
 			kfree(midiq);
 		}
 
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	if (card_mpuin->firstmidiq) {
@@ -780,7 +780,7 @@
 			kfree(midiq);
 		}
 
-		return CTSTATUS_SUCCESS;
+		return 0;
 	}
 
 	if (card_mpuin->firstmidiq) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardmo.c linux.18p17/drivers/sound/emu10k1/cardmo.c
--- linux.vanilla/drivers/sound/emu10k1/cardmo.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardmo.c	Tue Sep  5 21:50:50 2000
@@ -48,7 +48,7 @@
 	DPF(2, "emu10k1_mpuout_open()\n");
 
 	if (!(card_mpuout->status & FLAGS_AVAILABLE))
-		return CTSTATUS_INUSE;
+		return -1;
 
 	/* Copy open info and mark channel as in use */
 	card_mpuout->intr = 0;
@@ -61,7 +61,7 @@
 	emu10k1_mpu_reset(card);
 	emu10k1_mpu_acquire(card);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int emu10k1_mpuout_close(struct emu10k1_card *card)
@@ -96,7 +96,7 @@
 
 	spin_unlock_irqrestore(&card_mpuout->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* If there isn't enough buffer space, reject Midi Buffer.     *
@@ -113,14 +113,14 @@
 	DPF(2, "emu10k1_mpuout_add_buffer()\n");
 
 	if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND)
-		return CTSTATUS_SUCCESS;
+		return 0;
 
 	midihdr->flags |= MIDIBUF_INQUEUE;
 	midihdr->flags &= ~MIDIBUF_DONE;
 
 	if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) {
 		/* Message lost */
-		return CTSTATUS_NOMEMORY;
+		return -1;
 	}
 
 	midiq->next = NULL;
@@ -147,7 +147,7 @@
 
 	spin_unlock_irqrestore(&card_mpuout->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 void emu10k1_mpuout_bh(unsigned long refdata)
@@ -155,7 +155,6 @@
 	struct emu10k1_card *card = (struct emu10k1_card *) refdata;
 	struct emu10k1_mpuout *card_mpuout = card->mpuout;
 	int cByteSent = 0;
-	int status;
 	struct midi_queue *midiq;
 	struct midi_queue *doneq = NULL;
 	unsigned long flags;
@@ -166,14 +165,12 @@
 		midiq = card_mpuout->firstmidiq;
 
 		while (cByteSent < 4 && midiq->sizeLeft) {
-			status = emu10k1_mpu_write_data(card, *midiq->midibyte);
-
-			if (status == CTSTATUS_SUCCESS) {
+			if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) {
+				DPF(2, "emu10k1_mpuoutDpcCallback error!!\n");
+			} else {
 				++cByteSent;
 				--midiq->sizeLeft;
 				++midiq->midibyte;
-			} else {
-				DPF(2, "emu10k1_mpuoutDpcCallback error!!\n");
 			}
 		}
 
@@ -229,5 +226,5 @@
 
 	tasklet_hi_schedule(&card_mpuout->tasklet);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardwi.c linux.18p17/drivers/sound/emu10k1/cardwi.c
--- linux.vanilla/drivers/sound/emu10k1/cardwi.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardwi.c	Tue Sep  5 21:50:50 2000
@@ -67,20 +67,9 @@
 
 		break;
 
+	/* these can't be changed from the original values */
 	case WAVERECORD_MIC:
-		wave_fmt->channels = 1;
-		wave_fmt->samplingrate = 0x1F40;
-		wave_fmt->bitsperchannel = 16;
-		break;
-
 	case WAVERECORD_FX:
-		if (wave_fmt->channels > 16)
-			wave_fmt->channels = 16;
-		else if (wave_fmt->channels < 1)
-			wave_fmt->channels = 1;
-
-		wave_fmt->samplingrate = 0xBB80;
-		wave_fmt->bitsperchannel = 16;
 		break;
 
 	default:
@@ -123,13 +112,13 @@
 
 	switch (wiinst->recsrc) {
 	case WAVERECORD_AC97:
-		wiinst_tmp = &card->wavein->ac97;
+		wiinst_tmp = &card->wavein.ac97;
 		break;
 	case WAVERECORD_MIC:
-		wiinst_tmp = &card->wavein->mic;
+		wiinst_tmp = &card->wavein.mic;
 		break;
 	case WAVERECORD_FX:
-		wiinst_tmp = &card->wavein->fx;
+		wiinst_tmp = &card->wavein.fx;
 		break;
 	default:
 		BUG();
@@ -191,13 +180,13 @@
 	spin_lock_irqsave(&card->lock, flags);
 	switch (wave_dev->wiinst->recsrc) {
 	case WAVERECORD_AC97:
-		card->wavein->ac97 = NULL;
+		card->wavein.ac97 = NULL;
 		break;
 	case WAVERECORD_MIC:
-		card->wavein->mic = NULL;
+		card->wavein.mic = NULL;
 		break;
 	case WAVERECORD_FX:
-		card->wavein->fx = NULL;
+		card->wavein.fx = NULL;
 		break;
 	default:
 		BUG();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardwi.h linux.18p17/drivers/sound/emu10k1/cardwi.h
--- linux.vanilla/drivers/sound/emu10k1/cardwi.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardwi.h	Sun Oct 15 23:40:33 2000
@@ -48,7 +48,6 @@
         u32 addrreg;
         u32 idxreg;
         u32 adcctl;
-	u32 fxwc;
 	void *addr;
 	u8 cov;
 	dma_addr_t dma_handle;	
@@ -66,18 +65,8 @@
 	u32 blocks;
 	spinlock_t lock;
 	u8 recsrc;
+	u16 fxwc;
 };
-
-struct emu10k1_wavein 
-{
-	struct wiinst *ac97;
-	struct wiinst *mic;
-	struct wiinst *fx;
-
-	u8 recsrc;
-	u32 fxwc;
-};
-
 
 #define WAVEIN_MAXBUFSIZE         65536
 #define WAVEIN_MINBUFSIZE	  368
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardwo.c linux.18p17/drivers/sound/emu10k1/cardwo.c
--- linux.vanilla/drivers/sound/emu10k1/cardwo.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardwo.c	Tue Sep  5 21:50:50 2000
@@ -119,66 +119,86 @@
 
 static int get_voice(struct emu10k1_card *card, struct woinst *woinst)
 {
-	struct voice_allocdesc voice_allocdesc;
-
+	struct emu_voice *voice = &woinst->voice;
 	/* Allocate voices here, if no voices available, return error.
 	 * Init voice_allocdesc first.*/
 
-	voice_allocdesc.usage = VOICE_USAGE_PLAYBACK;
+	voice->usage = VOICE_USAGE_PLAYBACK;
 
-	voice_allocdesc.flags = 0;
+	voice->flags = 0;
 
 	if (woinst->format.channels == 2)
-		voice_allocdesc.flags |= VOICE_FLAGS_STEREO;
+		voice->flags |= VOICE_FLAGS_STEREO;
 
 	if (woinst->format.bitsperchannel == 16)
-		voice_allocdesc.flags |= VOICE_FLAGS_16BIT;
+		voice->flags |= VOICE_FLAGS_16BIT;
 
-	if (emu10k1_voice_alloc(card, &woinst->voice, &voice_allocdesc) < 0)
+	if (emu10k1_voice_alloc(card, voice) < 0)
 		return -1;
 
 	/* Calculate pitch */
-	woinst->voice.initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
-	woinst->voice.pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
+	voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
+	voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
+
+	DPD(2, "Initial pitch --> 0x%x\n", voice->initial_pitch);
+
+	voice->startloop = (woinst->buffer.emupageindex << 12) / woinst->format.bytespersample;
+	voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespersample;
+	voice->start = voice->startloop;
+
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		voice->params[0].send_a = card->waveout.send_a[1];
+		voice->params[0].send_b = card->waveout.send_b[1];
+		voice->params[0].send_c = card->waveout.send_c[1];
+		voice->params[0].send_d = card->waveout.send_d[1];
+
+		if (woinst->device)
+			voice->params[0].send_routing = 0xd23c;
+		else
+			voice->params[0].send_routing = card->waveout.send_routing[1];
 
-	DPD(2, "Initial pitch --> 0x%x\n", woinst->voice.initial_pitch);
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
+
+		voice->params[1].send_a = card->waveout.send_a[2];
+		voice->params[1].send_b = card->waveout.send_b[2];
+		voice->params[1].send_c = card->waveout.send_c[2];
+		voice->params[1].send_d = card->waveout.send_d[2];
 
-	woinst->voice.start = (woinst->buffer.emupageindex << 12) / woinst->format.bytespersample;
-	woinst->voice.end = woinst->voice.start + woinst->buffer.size / woinst->format.bytespersample;
-	woinst->voice.startloop = woinst->voice.start;
-	woinst->voice.endloop = woinst->voice.end;
-
-	woinst->voice.params[0].send_a=0x00;
-	woinst->voice.params[0].send_b=0x00;
-	woinst->voice.params[0].send_c=0xff;
-	woinst->voice.params[0].send_d=0x00;
-
-	woinst->voice.params[0].bus_routing= (woinst->device == 1) ? 0xd23c : 0xd01c;
-	woinst->voice.params[0].volume_target=0xffff;
-	woinst->voice.params[0].initial_fc=0xff;
-	woinst->voice.params[0].initial_attn=0x00;
-	woinst->voice.params[0].byampl_env_sustain = 0x7f;
-	woinst->voice.params[0].byampl_env_decay = 0x7f;
-
-	if (woinst->voice.flags & VOICE_FLAGS_STEREO) {
-		woinst->voice.params[1].send_a=0x00;
-		woinst->voice.params[1].send_b=0xff;		
-		woinst->voice.params[1].send_c=0x00;
-		woinst->voice.params[1].send_d=0x00;
-
-		woinst->voice.params[1].bus_routing= (woinst->device == 1) ? 0xd23c : 0xd01c;
-		woinst->voice.params[1].volume_target=0xffff;
-		woinst->voice.params[1].initial_fc=0xff;
-		woinst->voice.params[1].initial_attn=0x00;
-		woinst->voice.params[1].byampl_env_sustain = 0x7f;
-		woinst->voice.params[1].byampl_env_decay = 0x7f;
+		if (woinst->device)
+			voice->params[1].send_routing = 0xd23c;
+		else
+			voice->params[1].send_routing = card->waveout.send_routing[2];
+
+		voice->params[1].volume_target = 0xffff;
+		voice->params[1].initial_fc = 0xff;
+		voice->params[1].initial_attn = 0x00;
+		voice->params[1].byampl_env_sustain = 0x7f;
+		voice->params[1].byampl_env_decay = 0x7f;
 	} else {
-		woinst->voice.params[0].send_b=0xff;
+		voice->params[0].send_a = card->waveout.send_a[0];
+		voice->params[0].send_b = card->waveout.send_b[0];
+		voice->params[0].send_c = card->waveout.send_c[0];
+		voice->params[0].send_d = card->waveout.send_d[0];
+
+		if (woinst->device)
+                        voice->params[0].send_routing = 0xd23c;
+                else
+			voice->params[0].send_routing = card->waveout.send_routing[0];
+
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
 	}
 
-	DPD(2, "voice: start=0x%x, end=0x%x, startloop=0x%x, endloop=0x%x\n", woinst->voice.start, woinst->voice.end,woinst->voice.startloop, woinst->voice.endloop);
+	DPD(2, "voice: startloop=0x%x, endloop=0x%x\n", voice->startloop, voice->endloop);
 
-	emu10k1_voice_playback_setup(&woinst->voice);
+	emu10k1_voice_playback_setup(voice);
 
 	return 0;
 }
@@ -245,7 +265,8 @@
 
 	DPF(2, "emu10k1_waveout_start()\n");
 	/* Actual start */
-	emu10k1_voice_start(&woinst->voice);
+
+	emu10k1_voice_start(&woinst->voice, woinst->total_played);
 
 	emu10k1_timer_enable(card, &woinst->timer);
 
@@ -351,15 +372,20 @@
 
 	i = str / PAGE_SIZE;
 	j = str % PAGE_SIZE;
-	k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
-	copy_from_user(dst[i] + j, src, k);
-	len -= k;
-	while (len >= PAGE_SIZE) {
-		copy_from_user(dst[++i], src + k, PAGE_SIZE);
-		k += PAGE_SIZE;
-		len -= PAGE_SIZE;
-	}
-	copy_from_user(dst[++i], src + k, len);
+
+	if (len > PAGE_SIZE - j) {
+		k = PAGE_SIZE - j;
+		copy_from_user(dst[i] + j, src, k);
+		len -= k;
+		while (len > PAGE_SIZE) {
+                	copy_from_user(dst[++i], src + k, PAGE_SIZE);
+                	k += PAGE_SIZE;
+                	len -= PAGE_SIZE;
+        	}
+        	copy_from_user(dst[++i], src + k, len);
+
+	} else
+		copy_from_user(dst[i] + j, src, len);
 
 	return;
 }
@@ -370,14 +396,19 @@
 
 	i = str / PAGE_SIZE;
 	j = str % PAGE_SIZE;
-	k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
-	memset(dst[i] + j, src, k);
-	len -= k;
-	while (len >= PAGE_SIZE) {
-		memset(dst[++i], src, PAGE_SIZE);
-		len -= PAGE_SIZE;
-	}
-	memset(dst[++i], src, len);
+
+	if (len > PAGE_SIZE - j) {
+                k = PAGE_SIZE - j;
+                memset(dst[i] + j, src, k);
+                len -= k;
+                while (len > PAGE_SIZE) {
+                        memset(dst[++i], src, PAGE_SIZE);
+                        len -= PAGE_SIZE;
+                }
+                memset(dst[++i], src, len);
+
+        } else
+                memset(dst[i] + j, src, len);
 
 	return;
 }
@@ -463,22 +494,15 @@
 	if (!(woinst->state & WAVE_STATE_STARTED)) {
 		hw_pos = woinst->buffer.hw_pos;
 	} else {
-		u32 samples;
 		/* hw_pos in sample units */
-		emu10k1_voice_getcontrol(&woinst->voice, CCCA_CURRADDR, &hw_pos);
+		hw_pos = sblive_readptr(woinst->voice.card, CCCA_CURRADDR, woinst->voice.num);
 
-		hw_pos -= woinst->voice.start;
+		if(hw_pos < woinst->voice.start)
+			hw_pos += woinst->buffer.size / woinst->format.bytespersample - woinst->voice.start;
+		else
+			hw_pos -= woinst->voice.start;
 
 		hw_pos *= woinst->format.bytespersample;
-
-		/* Refer to voicemgr.c, CA is not started at zero.
-		 * We need to take this into account. */
-		samples = 64 * woinst->format.channels - 4 * woinst->format.bytesperchannel;
-
-		if (hw_pos >= samples)
-			hw_pos -= samples;
-		else
-			hw_pos += woinst->buffer.size - samples;
 	}
 
 	diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/cardwo.h linux.18p17/drivers/sound/emu10k1/cardwo.h
--- linux.vanilla/drivers/sound/emu10k1/cardwo.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/cardwo.h	Sun Oct 15 23:40:33 2000
@@ -44,7 +44,7 @@
 #define WAVEOUT_DEFAULTFRAGLEN	20 /* Time to play a fragment in ms (latency) */
 #define WAVEOUT_DEFAULTBUFLEN	500 /* Time to play the entire buffer in ms */
 
-#define WAVEOUT_MINFRAGSHIFT	4
+#define WAVEOUT_MINFRAGSHIFT	6
 
 struct waveout_buffer {
 	u16 ossfragshift;
@@ -76,16 +76,6 @@
         u32 blocks;
 	u8 device;
 	spinlock_t lock;
-};
-
-struct emu10k1_waveout
-{
-	u32 globalvol;
-	u32 mute;
-	u32 left;
-	u32 right;
-	u32 globalreverb;
-	u32 globalchorus;
 };
 
 int emu10k1_waveout_open(struct emu10k1_wavedevice *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/ecard.c linux.18p17/drivers/sound/emu10k1/ecard.c
--- linux.vanilla/drivers/sound/emu10k1/ecard.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/sound/emu10k1/ecard.c	Tue Sep  5 21:50:50 2000
@@ -0,0 +1,155 @@
+/*     
+ **********************************************************************
+ *     ecard.c - E-card initialization code
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version. 
+ * 
+ *     This program is distributed in the hope that it will be useful, 
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ *     GNU General Public License for more details. 
+ * 
+ *     You should have received a copy of the GNU General Public 
+ *     License along with this program; if not, write to the Free 
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */ 
+
+#include "ecard.h"
+#include "hwaccess.h"
+
+/* Private routines */
+static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16);
+static void ecard_write(struct emu10k1_card *, u32);
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain)
+{
+	u32 currbit;
+	ecard->adc_gain = gain;
+
+	/* Enable writing to the TRIM registers */
+	ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
+
+	/* Do it again to insure that we meet hold time requirements */
+	ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
+
+	for (currbit = (1L << 15); currbit; currbit >>= 1) {
+
+		u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA);
+
+		if (gain & currbit)
+		      value |= EC_TRIM_SDATA;
+
+		/* Clock the bit */
+		ecard_write(card, value);
+		ecard_write(card, value | EC_TRIM_SCLK);
+		ecard_write(card, value);
+	}
+
+	ecard_write(card, ecard->control_bits);
+}
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+static void ecard_write(struct emu10k1_card *card, u32 value)
+{
+	u16 count;
+	u32 data, hcvalue;
+
+	hcvalue = emu10k1_readfn0(card, HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT);
+
+	emu10k1_writefn0(card, HCFG, hcvalue);
+
+	for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) {
+	
+		/* Set up the value */
+		data = ((value & 0x1) ? PULSEN_BIT : 0);
+		value >>= 1;
+
+		emu10k1_writefn0(card, HCFG, hcvalue | data);
+
+		/* Clock the shift register */
+		emu10k1_writefn0(card, HCFG, hcvalue | data | HANDN_BIT);
+		emu10k1_writefn0(card, HCFG, hcvalue | data);
+	}
+
+	/* Latch the bits */
+	emu10k1_writefn0(card, HCFG, hcvalue | HOOKN_BIT);
+	emu10k1_writefn0(card, HCFG, hcvalue);
+}
+
+int __devinit emu10k1_ecard_init(struct emu10k1_card *card)
+{
+	u32 hcvalue;
+	struct ecard_state ecard;
+
+	/* Set up the initial settings */
+	ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL;
+	ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL;
+	ecard.mux2_setting = 0;
+	ecard.adc_gain = EC_DEFAULT_ADC_GAIN;
+	ecard.control_bits = EC_RAW_RUN_MODE | 
+                             EC_SPDIF0_SELECT(ecard.mux0_setting) |
+			     EC_SPDIF1_SELECT(ecard.mux1_setting);
+
+
+	/* Step 0: Set the codec type in the hardware control register 
+	 * and enable audio output */
+	hcvalue = emu10k1_readfn0(card, HCFG);
+	emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S);
+	emu10k1_readfn0(card, HCFG);
+
+	/* Step 1: Turn off the led and deassert TRIM_CS */
+	ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 2: Calibrate the ADC and DAC */
+	ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 3: Wait for awhile; FIXME: Is this correct? */
+
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(HZ);
+
+	/* Step 4: Switch off the DAC and ADC calibration.  Note
+	 * That ADC_CAL is actually an inverted signal, so we assert
+	 * it here to stop calibration.  */
+	ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 4: Switch into run mode */
+	ecard_write(card, ecard.control_bits);
+
+	/* Step 5: Set the analog input gain */
+	ecard_setadcgain(card, &ecard, ecard.adc_gain);
+	
+	return 0;
+}
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/ecard.h linux.18p17/drivers/sound/emu10k1/ecard.h
--- linux.vanilla/drivers/sound/emu10k1/ecard.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/sound/emu10k1/ecard.h	Sun Oct 15 23:40:37 2000
@@ -0,0 +1,113 @@
+/*     
+ **********************************************************************
+ *     ecard.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version. 
+ * 
+ *     This program is distributed in the hope that it will be useful, 
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ *     GNU General Public License for more details. 
+ * 
+ *     You should have received a copy of the GNU General Public 
+ *     License along with this program; if not, write to the Free 
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */ 
+
+#ifndef _ECARD_H
+#define _ECARD_H
+
+#include "8010.h"
+#include "hwaccess.h"
+#include <linux/init.h>
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT   (1L << 12)
+#define HANDN_BIT   (1L << 11)
+#define PULSEN_BIT  (1L << 10)
+
+#define EC_GDI1 (1 << 13)
+#define EC_GDI0 (1 << 14)
+
+#define EC_NUM_CONTROL_BITS 20
+
+#define EC_AC3_DATA_SELN  0x0001L
+#define EC_EE_DATA_SEL    0x0002L
+#define EC_EE_CNTRL_SELN  0x0004L
+#define EC_EECLK          0x0008L
+#define EC_EECS           0x0010L
+#define EC_EESDO          0x0020L
+#define EC_TRIM_CSN	  0x0040L
+#define EC_TRIM_SCLK	  0x0080L
+#define EC_TRIM_SDATA	  0x0100L
+#define EC_TRIM_MUTEN	  0x0200L
+#define EC_ADCCAL	  0x0400L
+#define EC_ADCRSTN	  0x0800L
+#define EC_DACCAL	  0x1000L
+#define EC_DACMUTEN	  0x2000L
+#define EC_LEDN		  0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17	
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory.  This should
+                                      * be incremented any time the EEPROM's
+                                      * format is changed.  */
+
+#define EC_EEPROM_SIZE	        0x40 /* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR 	0x22	/* MSW of board rev */ 
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADD	0x30	/* First word of serial number.  The number
+                                         * can be up to 30 characters in length
+                                         * and is stored as a NULL-terminated
+                                         * ASCII string.  Any unused bytes must be
+                                         * filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f    /* Location at which checksum is stored */
+
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware 
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC 
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE	(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN   0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL 0x0
+#define EC_DEFAULT_SPDIF1_SEL 0x4
+
+#define HC_EA 0x01L
+
+/* ECARD state structure.  This structure maintains the state
+ * for various portions of the the ECARD's onboard hardware.
+ */
+struct ecard_state {
+	u32 control_bits;
+	u16 adc_gain;
+	u16 mux0_setting;
+	u16 mux1_setting;
+	u16 mux2_setting;
+};
+
+int emu10k1_ecard_init(struct emu10k1_card *) __devinit;
+
+#endif /* _ECARD_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/emu_wrapper.c linux.18p17/drivers/sound/emu10k1/emu_wrapper.c
--- linux.vanilla/drivers/sound/emu10k1/emu_wrapper.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/emu_wrapper.c	Fri Sep  8 17:05:10 2000
@@ -100,7 +100,7 @@
                 if (!found)
                         count += pci_announce_device(drv, dev);
         }
-#endif        
+#endif
         return count;
 }
 
@@ -119,36 +119,10 @@
                 if (found) {
                         if (drv->remove)
                                 drv->remove(dev);
-                        drvmap[i].dev = NULL;
+                        drvmap[i - 1].dev = NULL;
                 }
         }
-#endif        
-}
-
-unsigned long pci_compat_get_size (struct pci_dev *dev, int n_base)
-{
-        u32 l, sz;
-        int reg = PCI_BASE_ADDRESS_0 + (n_base << 2);
-
-        /* XXX temporarily disable I/O and memory decoding for this device? */
-
-        pci_read_config_dword (dev, reg, &l);
-        if (l == 0xffffffff)
-                return 0;
-
-        pci_write_config_dword (dev, reg, ~0);
-        pci_read_config_dword (dev, reg, &sz);
-        pci_write_config_dword (dev, reg, l);
-
-        if (!sz || sz == 0xffffffff)
-                return 0;
-        if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
-                sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
-        } else {
-                sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
-        }
-
-        return sz + 1;
+#endif
 }
 
 int pci_compat_enable_device(struct pci_dev *dev)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/emu_wrapper.h linux.18p17/drivers/sound/emu10k1/emu_wrapper.h
--- linux.vanilla/drivers/sound/emu10k1/emu_wrapper.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/emu_wrapper.h	Sun Oct 15 23:40:33 2000
@@ -14,11 +14,6 @@
 #include <linux/tqueue.h>
 
 #define vma_get_pgoff(v)	vma_get_offset(v)
-#define wait_queue_head_t	struct wait_queue *
-#define DECLARE_WAITQUEUE(a, b)	struct wait_queue a = {b, NULL};
-#define init_waitqueue_head(a)	init_waitqueue(a)
-
-#define init_MUTEX(a)		*(a) = MUTEX
 
 #define UP_INODE_SEM(a)		up(a)
 #define DOWN_INODE_SEM(a)	down(a)
@@ -44,30 +39,8 @@
 #define __devexit
 #define __devexitdata
 
-/* Not sure what version aliases were introduced in, but certainly in 2.91.66.  */
-#ifdef MODULE
-  #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91)
-    #define module_init(x)      int init_module(void) __attribute__((alias(#x)));
-    #define module_exit(x)      void cleanup_module(void) __attribute__((alias(#x)));
-  #else
-    #define module_init(x)      int init_module(void) { return x(); }
-    #define module_exit(x)      void cleanup_module(void) { x(); }
-  #endif
-#else
-  #define module_init(x)
-  #define module_exit(x)
-#endif
-
 #define MODULE_DEVICE_TABLE(foo,bar)
 
-static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
-{
-        __list_add(new, head->prev, head);
-}
-
-#define list_for_each(pos, head) \
-        for (pos = (head)->next; pos != (head); pos = pos->next)
-
 #define pci_dma_supported(dev, mask) 1
 
 #define PCI_ANY_ID (~0)
@@ -88,7 +61,6 @@
 (((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_SPACE) ? \
  ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_IO_MASK) : \
  ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_MEM_MASK))
-#define pci_resource_len pci_compat_get_size
 
 struct pci_device_id {
         unsigned int vendor, device;
@@ -111,27 +83,11 @@
 const struct pci_device_id * pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev);
 int pci_compat_register_driver(struct pci_driver *drv);
 void pci_compat_unregister_driver(struct pci_driver *drv);
-unsigned long pci_compat_get_size (struct pci_dev *dev, int n_base);
 int pci_compat_enable_device(struct pci_dev *dev);
 void *compat_request_region (unsigned long start, unsigned long n, const char *name);
 void * pci_compat_get_driver_data (struct pci_dev *dev);
 void pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data);
 
-typedef u32 dma_addr_t;
-
-extern __inline__ int __compat_get_order(unsigned long size)
-{
-        int order;
-
-        size = (size-1) >> (PAGE_SHIFT-1);
-        order = -1;
-        do {
-                size >>= 1;
-                order++;
-        } while (size);
-        return order;
-}
-
 extern __inline__ void *
 pci_alloc_consistent(struct pci_dev *hwdev,
                      size_t size, dma_addr_t *dma_handle) {
@@ -140,7 +96,7 @@
 
         if (hwdev == NULL)
                 gfp |= GFP_DMA;
-        ret = (void *)__get_free_pages(gfp, __compat_get_order(size));
+        ret = (void *)__get_free_pages(gfp, get_order(size));
 
         if (ret != NULL) {
                 memset(ret, 0, size);
@@ -153,7 +109,7 @@
 pci_free_consistent(struct pci_dev *hwdev, size_t size,
                     void *vaddr, dma_addr_t dma_handle)
 {
-        free_pages((unsigned long)vaddr, __compat_get_order(size));
+        free_pages((unsigned long)vaddr, get_order(size));
 }
 
 static inline int pci_module_init(struct pci_driver *drv)
@@ -169,10 +125,5 @@
         
         return -ENODEV;
 }
-
-#define BUG() do { \
-        printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
-        __asm__ __volatile__(".byte 0x0f,0x0b"); \
-} while (0)
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/hwaccess.c linux.18p17/drivers/sound/emu10k1/hwaccess.c
--- linux.vanilla/drivers/sound/emu10k1/hwaccess.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/hwaccess.c	Tue Sep  5 21:50:50 2000
@@ -366,7 +366,7 @@
 
 	spin_unlock_irqrestore(&card->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_writeac97(struct emu10k1_card *card, u8 index, u16 data)
@@ -380,7 +380,7 @@
 
 	spin_unlock_irqrestore(&card->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int sblive_rmwac97(struct emu10k1_card *card, u8 index, u16 data, u16 mask)
@@ -399,7 +399,7 @@
 
 	spin_unlock_irqrestore(&card->lock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /*********************************************************
@@ -415,9 +415,9 @@
 
 	if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) {
 		outb(data, card->iobase + MUDATA);
-		ret = CTSTATUS_SUCCESS;
+		ret = 0;
 	} else
-		ret = CTSTATUS_BUSY;
+		ret = -1;
 
 	spin_unlock_irqrestore(&card->lock, flags);
 
@@ -433,9 +433,9 @@
 
 	if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) {
 		*data = inb(card->iobase + MUDATA);
-		ret = CTSTATUS_SUCCESS;
+		ret = 0;
 	} else
-		ret = CTSTATUS_NODATA;
+		ret = -1;
 
 	spin_unlock_irqrestore(&card->lock, flags);
 
@@ -473,12 +473,12 @@
 		spin_unlock_irqrestore(&card->lock, flags);
 
 		if (status == 0xfe)
-			return CTSTATUS_SUCCESS;
+			return 0;
 		else
-			return CTSTATUS_ERROR;
+			return -1;
 	}
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int emu10k1_mpu_acquire(struct emu10k1_card *card)
@@ -486,7 +486,7 @@
 	/* FIXME: This should be a macro */
 	++card->mpuacqcount;
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int emu10k1_mpu_release(struct emu10k1_card *card)
@@ -494,5 +494,5 @@
 	/* FIXME: this should be a macro */
 	--card->mpuacqcount;
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/hwaccess.h linux.18p17/drivers/sound/emu10k1/hwaccess.h
--- linux.vanilla/drivers/sound/emu10k1/hwaccess.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/hwaccess.h	Sun Oct 15 23:40:33 2000
@@ -47,14 +47,6 @@
 #define MAXPAGES	8192 
 #define BUFMAXPAGES     (MAXBUFSIZE / PAGE_SIZE)
 
-enum GlobalErrorCode
-{
-	CTSTATUS_SUCCESS = 0x0000,
-	CTSTATUS_ERROR,
-	CTSTATUS_NOMEMORY,
-	CTSTATUS_INUSE,
-};
-
 #define FLAGS_AVAILABLE     0x0001
 #define FLAGS_READY         0x0002
 
@@ -72,15 +64,36 @@
 #ifdef EMU10K1_DEBUG
 # define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0)
 # define DPF(level,x)   do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0)
-#define ERROR() DPF(1,"error\n");
 #else
 # define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */
 # define DPF(level,x) do { } while (0)
-#define ERROR() do { } while (0)
 #endif /* EMU10K1_DEBUG */
 
+#define ERROR() DPF(1,"error\n")
+
 /* DATA STRUCTURES */
 
+struct emu10k1_waveout
+{
+	u16 send_routing[3];
+
+	u8 send_a[3];
+	u8 send_b[3];
+	u8 send_c[3];
+	u8 send_d[3];
+};
+
+struct emu10k1_wavein
+{
+        struct wiinst *ac97;
+        struct wiinst *mic;
+        struct wiinst *fx;
+
+        u8 recsrc;
+        u32 fxwc;
+};
+
+
 struct emu10k1_card 
 {
 	struct list_head list;
@@ -104,13 +117,13 @@
 	unsigned short		model;
 	unsigned int irq; 
 
-	unsigned long	audio1_num;
-	unsigned long	audio2_num;
-	unsigned long	mixer_num;
-	unsigned long	midi_num;
+	int	audio_num;
+	int	audio1_num;
+	int	mixer_num;
+	int	midi_num;
 
-	struct emu10k1_waveout	*waveout;
-	struct emu10k1_wavein	*wavein;
+	struct emu10k1_waveout	waveout;
+	struct emu10k1_wavein	wavein;
 	struct emu10k1_mpuout	*mpuout;
 	struct emu10k1_mpuin	*mpuin;
 
@@ -126,6 +139,8 @@
 	u32	    has_toslink;	       // TOSLink detection
 
 	u8 chiprev;                    /* Chip revision                */
+
+	int isaps;
 };
 
 int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *);
@@ -146,10 +161,6 @@
 extern struct sblive_pcm_volume_rec sblive_pcm_volume[];
 extern u16 pcm_last_mixer;
 #endif
-
-
-#define ENABLE 			0xffffffff
-#define DISABLE 		0x00000000
 
 #define TIMEOUT 		    16384
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/main.c linux.18p17/drivers/sound/emu10k1/main.c
--- linux.vanilla/drivers/sound/emu10k1/main.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/main.c	Mon Sep 11 17:07:42 2000
@@ -43,6 +43,9 @@
  *	    moved bh's to tasklets, moved to the new PCI driver initialization style.
  *	0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels,
  *	    code reorganization and cleanup.
+ *      0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll().
+ *	    Support for setting external TRAM size.
+ *	
  **********************************************************************
  */
 
@@ -61,8 +64,9 @@
 #include "cardmo.h"
 #include "cardmi.h"
 #include "recmgr.h"
+#include "ecard.h"
 
-#define DRIVER_VERSION "0.6"
+#define DRIVER_VERSION "0.7"
 
 /* FIXME: is this right? */
 /* does the card support 32 bit bus master?*/
@@ -76,6 +80,8 @@
 #define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
 #endif
 
+#define EMU_APS_SUBID	0x40011102
+ 
 enum {
 	EMU10K1 = 0,
 };
@@ -103,32 +109,38 @@
 extern void emu10k1_interrupt(int, void *, struct pt_regs *s);
 extern int emu10k1_mixer_wrch(struct emu10k1_card *, unsigned int, int);
 
-static int __devinit audio_init(struct emu10k1_card *card)
+static void __devinit audio_init(struct emu10k1_card *card)
 {
-	if ((card->waveout = kmalloc(sizeof(struct emu10k1_waveout), GFP_KERNEL)) == NULL) {
-		printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_waveout: out of memory\n");
-		return -1;
-	}
-	memset(card->waveout, 0, sizeof(struct emu10k1_waveout));
-
-	/* Assign default global volume, reverb, chorus */
-	card->waveout->globalvol = 0xffffffff;
-	card->waveout->left = 0xffff;
-	card->waveout->right = 0xffff;
-	card->waveout->mute = 0;
-	card->waveout->globalreverb = 0xffffffff;
-	card->waveout->globalchorus = 0xffffffff;
-
-	if ((card->wavein = kmalloc(sizeof(struct emu10k1_wavein), GFP_KERNEL))
-	    == NULL) {
-		printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_wavein: out of memory\n");
-		return -1;
-	}
-	memset(card->wavein, 0, sizeof(struct emu10k1_wavein));
+	/* Assign default playback voice parameters */
+	/* mono voice */
+	card->waveout.send_a[0] = 0x00;
+	card->waveout.send_b[0] = 0xff;
+	card->waveout.send_c[0] = 0xff;
+	card->waveout.send_d[0] = 0x00;
+	card->waveout.send_routing[0] = 0xd01c;
+
+	/* stereo voice */
+	card->waveout.send_a[1] = 0x00;
+        card->waveout.send_b[1] = 0x00;
+        card->waveout.send_c[1] = 0xff;
+        card->waveout.send_d[1] = 0x00;
+        card->waveout.send_routing[1] = 0xd01c;
+
+	card->waveout.send_a[2] = 0x00;
+        card->waveout.send_b[2] = 0xff;
+        card->waveout.send_c[2] = 0x00;
+        card->waveout.send_d[2] = 0x00;
+        card->waveout.send_routing[2] = 0xd01c;
+
+	/* Assign default recording parameters */
+	if(card->isaps)
+		card->wavein.recsrc = WAVERECORD_FX;
+	else
+		card->wavein.recsrc = WAVERECORD_AC97;
 
-	card->wavein->recsrc = WAVERECORD_AC97;
+	card->wavein.fxwc = 0x0003;
 
-	return 0;
+	return;
 }
 
 static void __devinit mixer_init(struct emu10k1_card *card)
@@ -160,46 +172,49 @@
 		94, 95
 	};
 
-	/* Reset */
-	sblive_writeac97(card, AC97_RESET, 0);
-
-#if 0
-	/* Check status word */
-	{
-		u16 reg;
-
-		sblive_readac97(card, AC97_RESET, &reg);
-		DPD(2, "RESET 0x%x\n", reg);
-		sblive_readac97(card, AC97_MASTERTONE, &reg);
-		DPD(2, "MASTER_TONE 0x%x\n", reg);
+	for (count = 0; count < sizeof(card->digmix) / sizeof(card->digmix[0]); count++) {
+		card->digmix[count] = 0x80000000;
+		sblive_writeptr(card, FXGPREGBASE + 0x10 + count, 0, 0);
 	}
-#endif
 
-	/* Set default recording source to mic in */
-	sblive_writeac97(card, AC97_RECORDSELECT, 0);
+	card->modcnt = 0;       // Should this be here or in open() ?
 
-	/* Set default AC97 "PCM" volume to acceptable max */
-	//sblive_writeac97(card, AC97_PCMOUTVOLUME, 0);
-	//sblive_writeac97(card, AC97_LINE2, 0);
+	if (!card->isaps) {
 
-	/* Set default volumes for all mixer channels */
+		for (count = 0; count < sizeof(initdig) / sizeof(initdig[0]); count++) {
+			card->digmix[initdig[count]] = 0x7fffffff;
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + initdig[count], 0, 0x7fffffff);
+		}
 
-	for (count = 0; count < sizeof(card->digmix) / sizeof(card->digmix[0]); count++) {
-		card->digmix[count] = 0x80000000;
-		sblive_writeptr(card, FXGPREGBASE + 0x10 + count, 0, 0);
-	}
+		/* Reset */
+		sblive_writeac97(card, AC97_RESET, 0);
+
+#if 0
+		/* Check status word */
+		{
+			u16 reg;
+
+			sblive_readac97(card, AC97_RESET, &reg);
+			DPD(2, "RESET 0x%x\n", reg);
+			sblive_readac97(card, AC97_MASTERTONE, &reg);
+			DPD(2, "MASTER_TONE 0x%x\n", reg);
+		}
+#endif
+
+		/* Set default recording source to mic in */
+		sblive_writeac97(card, AC97_RECORDSELECT, 0);
 
-	for (count = 0; count < sizeof(initdig) / sizeof(initdig[0]); count++) {
-		card->digmix[initdig[count]] = 0x7fffffff;
-		sblive_writeptr(card, FXGPREGBASE + 0x10 + initdig[count], 0, 0x7fffffff);
+		/* Set default AC97 "PCM" volume to acceptable max */
+		//sblive_writeac97(card, AC97_PCMOUTVOLUME, 0);
+		//sblive_writeac97(card, AC97_LINE2, 0);
 	}
 
+	/* Set default volumes for all mixer channels */
+
 	for (count = 0; count < sizeof(initvol) / sizeof(initvol[0]); count++) {
 		emu10k1_mixer_wrch(card, initvol[count].mixch, initvol[count].vol);
 	}
 
-	card->modcnt = 0;	// Should this be here or in open() ?
-
 	return;
 }
 
@@ -236,7 +251,7 @@
 	spin_lock_init(&card->mpuin->lock);
 
 	/* Reset the MPU port */
-	if (emu10k1_mpu_reset(card) != CTSTATUS_SUCCESS) {
+	if (emu10k1_mpu_reset(card) < 0) {
 		ERROR();
 		return -1;
 	}
@@ -280,7 +295,10 @@
 
 static void __devinit fx_init(struct emu10k1_card *card)
 {
-	int i, j, k, l;
+	int i, j, k;
+#ifdef TONE_CONTROL
+	int l;
+#endif	
 	u32 pc = 0;
 
 	for (i = 0; i < 512; i++)
@@ -309,6 +327,7 @@
 			OP(0, 0x102, 0x102, 0x11a + k, 0x16 + j);
 			OP(0, 0x102, 0x102, 0x11c + k, 0x18 + j);
 			OP(0, 0x102, 0x102, 0x11e + k, 0x1a + j);
+#ifdef TONE_CONTROL
 			OP(0, 0x102, 0x102, 0x120 + k, 0x1c + j);
 
 			k = 0x1a0 + i * 8 + j * 4;
@@ -327,10 +346,13 @@
 			OP(0, l + 2, 0x56, l + 2, 0x196 + j);
 			OP(4, l + 2, 0x40, l + 2, 0x46);
 
-			if (i == 0)
-				OP(0, 0x20 + (i * 2) + j, 0x40, l + 2, 0x4e);	/* FIXME: Is this really needed? */
+			if ((i == 0) && !card->isaps)
+				OP(4, 0x20 + (i * 2) + j, 0x40, l + 2, 0x50);	/* FIXME: Is this really needed? */
 			else
 				OP(6, 0x20 + (i * 2) + j, l + 2, 0x40, 0x40);
+#else
+			OP(0, 0x20 + (i * 2) + j, 0x102, 0x120 + k, 0x1c + j);
+#endif
 		}
 	}
 	sblive_writeptr(card, DBG, 0, 0);
@@ -482,7 +504,7 @@
 	sblive_writeac97(card, AC97_PCMOUTVOLUME, 0);
 
 	if(card->model == 0x20 || card->model == 0xc400 ||
-	  (card->model == 0x21 && card->chiprev <= 3))
+	  (card->model == 0x21 && card->chiprev < 6))
 	        emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE  | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE);
 	else
 		emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE  | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE);
@@ -520,13 +542,6 @@
 	return 0;
 }
 
-static void __devinit audio_exit(struct emu10k1_card *card)
-{
-	kfree(card->waveout);
-	kfree(card->wavein);
-	return;
-}
-
 static void __devexit midi_exit(struct emu10k1_card *card)
 {
 	tasklet_unlock_wait(&card->mpuout->tasklet);
@@ -596,6 +611,7 @@
 static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
 {
 	struct emu10k1_card *card;
+	u32 subsysvid;
 
 	if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) {
 		printk(KERN_ERR "emu10k1: out of memory\n");
@@ -644,18 +660,21 @@
 		card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase,
 		card->iobase + card->length - 1, card->irq);
 
+	pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid);
+	card->isaps = (subsysvid == EMU_APS_SUBID);
+
 	spin_lock_init(&card->lock);
 	init_MUTEX(&card->open_sem);
 	card->open_mode = 0;
 	init_waitqueue_head(&card->open_wait);
 
 	/* Register devices */
-	if ((card->audio1_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) {
+	if ((card->audio_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) {
 		printk(KERN_ERR "emu10k1: cannot register first audio device!\n");
 		goto err_dev0;
 	}
 
-	if ((card->audio2_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) {
+	if ((card->audio1_num = register_sound_dsp(&emu10k1_audio_fops, -1)) < 0) {
 		printk(KERN_ERR "emu10k1: cannot register second audio device!\n");
 		goto err_dev1;
 	}
@@ -675,26 +694,22 @@
 		goto err_emu10k1_init;
 	}
 
-	if (audio_init(card) < 0) {
-		printk(KERN_ERR "emu10k1: cannot initialize audio!\n");
-		goto err_audio_init;
-	}
-
 	if (midi_init(card) < 0) {
 		printk(KERN_ERR "emu10k1: cannot initialize midi!\n");
 		goto err_midi_init;
 	}
 
+	audio_init(card);
 	mixer_init(card);
 
+	if (card->isaps)
+		emu10k1_ecard_init(card);
+
 	list_add(&card->list, &emu10k1_devs);
 
 	return 0;
 
       err_midi_init:
-	audio_exit(card);
-
-      err_audio_init:
 	emu10k1_exit(card);
 
       err_emu10k1_init:
@@ -704,10 +719,10 @@
 	unregister_sound_mixer(card->mixer_num);
 
       err_dev2:
-	unregister_sound_dsp(card->audio2_num);
+	unregister_sound_dsp(card->audio1_num);
 
       err_dev1:
-	unregister_sound_dsp(card->audio1_num);
+	unregister_sound_dsp(card->audio_num);
 
       err_dev0:
 	free_irq(card->irq, card);
@@ -724,13 +739,14 @@
 	struct emu10k1_card *card = PCI_GET_DRIVER_DATA(pci_dev);
 
 	midi_exit(card);
-	audio_exit(card);
 	emu10k1_exit(card);
 
 	unregister_sound_midi(card->midi_num);
+	
 	unregister_sound_mixer(card->mixer_num);
-	unregister_sound_dsp(card->audio2_num);
+
 	unregister_sound_dsp(card->audio1_num);
+	unregister_sound_dsp(card->audio_num);
 
 	free_irq(card->irq, card);
 	release_region(card->iobase, card->length);
@@ -772,6 +788,6 @@
 #ifndef MODULE
 int __init init_emu10k1(void)
 {
-	return emu10k1_init_module();
+        return emu10k1_init_module();
 }
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/midi.c linux.18p17/drivers/sound/emu10k1/midi.c
--- linux.vanilla/drivers/sound/emu10k1/midi.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/midi.c	Tue Sep  5 21:50:50 2000
@@ -33,6 +33,9 @@
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/malloc.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <asm/uaccess.h>
 
 #include "hwaccess.h"
@@ -40,7 +43,7 @@
 #include "cardmi.h"
 #include "midi.h"
 
-static spinlock_t midi_spinlock = SPIN_LOCK_UNLOCKED;
+static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED;
 
 static void init_midi_hdr(struct midi_hdr *midihdr)
 {
@@ -63,20 +66,20 @@
 	if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) {
 		ERROR();
 		kfree(midihdr);
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
-	if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) != CTSTATUS_SUCCESS) {
+	if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) {
 		ERROR();
 		kfree(midihdr->data);
 		kfree(midihdr);
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	*midihdrptr = midihdr;
 	list_add_tail(&midihdr->list, &midi_dev->mid_hdrs);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 static int emu10k1_midi_open(struct inode *inode, struct file *file)
@@ -142,8 +145,7 @@
 
 		dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
 
-		if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo)
-		    != CTSTATUS_SUCCESS) {
+		if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) {
 			ERROR();
 			kfree(midi_dev);
 			MOD_DEC_USE_COUNT;
@@ -151,13 +153,13 @@
 		}
 
 		/* Add two buffers to receive sysex buffer */
-		if (midiin_add_buffer(midi_dev, &midihdr1) != CTSTATUS_SUCCESS) {
+		if (midiin_add_buffer(midi_dev, &midihdr1) < 0) {
 			kfree(midi_dev);
 			MOD_DEC_USE_COUNT;
 			return -ENODEV;
 		}
 
-		if (midiin_add_buffer(midi_dev, &midihdr2) != CTSTATUS_SUCCESS) {
+		if (midiin_add_buffer(midi_dev, &midihdr2) < 0) {
 			list_del(&midihdr1->list);
 			kfree(midihdr1->data);
 			kfree(midihdr1);
@@ -172,8 +174,7 @@
 
 		dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
 
-		if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo)
-		    != CTSTATUS_SUCCESS) {
+		if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
 			ERROR();
 			kfree(midi_dev);
 			MOD_DEC_USE_COUNT;
@@ -193,8 +194,9 @@
 static int emu10k1_midi_release(struct inode *inode, struct file *file)
 {
 	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
-	struct emu10k1_card *card = midi_dev->card;
+	struct emu10k1_card *card;
 
+	card = midi_dev->card;
 	DPF(2, "emu10k1_midi_release()\n");
 
 	if (file->f_mode & FMODE_WRITE) {
@@ -259,8 +261,7 @@
 		return -EFAULT;
 
 	if (midi_dev->mistate == MIDIIN_STATE_STOPPED) {
-		if (emu10k1_mpuin_start(midi_dev->card)
-		    != CTSTATUS_SUCCESS) {
+		if (emu10k1_mpuin_start(midi_dev->card) < 0) {
 			ERROR();
 			return -EINVAL;
 		}
@@ -355,7 +356,7 @@
 
 	spin_lock_irqsave(&midi_spinlock, flags);
 
-	if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) != CTSTATUS_SUCCESS) {
+	if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) {
 		ERROR();
 		kfree(midihdr->data);
 		kfree(midihdr);
@@ -429,19 +430,19 @@
 		break;
 
 	default:		/* Unknown message */
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
 	spin_unlock_irqrestore(&midi_spinlock, flags);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 /* MIDI file operations */
 struct file_operations emu10k1_midi_fops = {
-	read:emu10k1_midi_read,
-	write:emu10k1_midi_write,
-	poll:emu10k1_midi_poll,
-	open:emu10k1_midi_open,
-	release:emu10k1_midi_release,
+	read:		emu10k1_midi_read,
+	write:		emu10k1_midi_write,
+	poll:		emu10k1_midi_poll,
+	open:		emu10k1_midi_open,
+	release:	emu10k1_midi_release,
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/mixer.c linux.18p17/drivers/sound/emu10k1/mixer.c
--- linux.vanilla/drivers/sound/emu10k1/mixer.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/mixer.c	Mon Sep 11 17:07:42 2000
@@ -35,10 +35,13 @@
 
 #define __NO_VERSION__		/* Kernel version only defined once */
 #include <linux/module.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
 
 #include "hwaccess.h"
 #include "8010.h"
+#include "recmgr.h"
 
 #define AC97_PESSIMISTIC
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
@@ -49,32 +52,13 @@
 #define vol_to_sw_5(hwvol) (((31 - (hwvol)) * 100) / 31)
 #define vol_to_sw_4(hwvol) (((15 - (hwvol)) * 100) / 15)
 
+#define DM_MUTE 0x80000000
+
 #ifdef PRIVATE_PCM_VOLUME
 struct sblive_pcm_volume_rec sblive_pcm_volume[MAX_PCM_CHANNELS];
 u16 pcm_last_mixer = 0x6464;
 #endif
 
-/* --------------------------------------------------------------------- */
-
-/*
- * hweightN: returns the hamming weight (i.e. the number
- * of bits set) of a N-bit word
- */
-
-#ifdef hweight32
-#undef hweight32
-#endif
-
-extern __inline__ unsigned int hweight32(unsigned int w)
-{
-	unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
-
-	res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
-	res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
-	res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
-	return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
 /* Mapping arrays */
 static const unsigned int recsrc[] = {
 	SOUND_MASK_MIC,
@@ -85,10 +69,10 @@
 	SOUND_MASK_VOLUME,
 	SOUND_MASK_OGAIN,	/* Used to be PHONEOUT */
 	SOUND_MASK_PHONEIN,
+#ifdef TONE_CONTROL
 	SOUND_MASK_TREBLE,
 	SOUND_MASK_BASS,
-	SOUND_MASK_MONITOR,
-	SOUND_MASK_PCM,
+#endif
 };
 
 static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = {
@@ -135,12 +119,25 @@
 	int nL, nR;
 
 	switch (ch) {
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_VOLUME:
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+        case SOUND_MIXER_BASS:
+#endif
+                return put_user(0x0000, (int *) arg);
+	default:
+		break;
+	}
+
+	if(card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
 	case SOUND_MIXER_LINE:
 	case SOUND_MIXER_CD:
 	case SOUND_MIXER_VIDEO:
 	case SOUND_MIXER_LINE1:
-	case SOUND_MIXER_PCM:
-	case SOUND_MIXER_VOLUME:
 		sblive_readac97(card, volreg[ch], &reg);
 		nL = ((~(reg >> 8) & 0x1f) * 100) / 32;
 		nR = (~(reg & 0x1f) * 100) / 32;
@@ -166,9 +163,6 @@
 		nR = (~(reg & 0x1f) * 100) / 16;
 		return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg);
 
-	case SOUND_MIXER_TREBLE:
-	case SOUND_MIXER_BASS:
-		return put_user(0x0000, (int *) arg);
 	default:
 		return -EINVAL;
 	}
@@ -205,6 +199,8 @@
 	[SOUND_MIXER_DIGITAL2] = 19
 };
 
+#ifdef TONE_CONTROL
+
 static const u32 bass_table[41][5] = {
 	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
 	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
@@ -317,6 +313,8 @@
 	}
 }
 
+#endif
+
 static const u32 db_table[101] = {
 	0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
 	0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
@@ -341,6 +339,29 @@
 	0x7fffffff,
 };
 
+static void aps_update_digital(struct emu10k1_card *card)
+{
+	int i, l1, r1, l2, r2;
+	
+	i = card->arrwVol[volidx[SOUND_MIXER_VOLUME]];
+	l1 = (i & 0xff);
+	r1 = ((i >> 8) & 0xff);
+
+	i = card->arrwVol[volidx[SOUND_MIXER_PCM]];
+	l2 = (i & 0xff);
+	r2 = ((i >> 8) & 0xff);
+	
+	for (i = 0; i < 108; i++) {
+		if (card->digmix[i] != DM_MUTE) {
+			if ((i % 18 >= 0) && (i % 18 < 4))
+				card->digmix[i] = ((i & 1) ? ((u64) db_table[r1] * (u64) db_table[r2]) : ((u64) db_table[l1] * (u64) db_table[l2])) >> 31;
+			else
+				card->digmix[i] = (i & 1) ? db_table[r1] : db_table[l1];
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, card->digmix[i]);
+		}
+	}
+}
+
 static void update_digital(struct emu10k1_card *card)
 {
 	int i, k, l1, r1, l2, r2, l3, r3, l4, r4;
@@ -378,7 +399,7 @@
 	}
 
 	for (i = 0; i < 36; i++) {
-		if (card->digmix[i] != 0x80000000) {
+		if (card->digmix[i] != DM_MUTE) {
 			if (((i >= 0) && (i < 4)) || ((i >= 18) && (i < 22)))
 				j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r3]) : ((u64) db_table[l1] * (u64) db_table[l3]);
 			else if ((i == 6) || (i == 7) || (i == 24) || (i == 25))
@@ -391,7 +412,7 @@
 	}
 
 	for (i = 72; i < 90; i++) {
-		if (card->digmix[i] != 0x80000000) {
+		if (card->digmix[i] != DM_MUTE) {
 			if ((i >= 72) && (i < 76))
 				j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r3]) : ((u64) db_table[l2] * (u64) db_table[l3]);
 			else if ((i == 78) || (i == 79))
@@ -406,15 +427,15 @@
 	for (i = 36; i <= 90; i += 18) {
 		if (i != 72) {
 			for (k = 0; k < 4; k++)
-				if (card->digmix[i + k] != 0x80000000) {
+				if (card->digmix[i + k] != DM_MUTE) {
 					card->digmix[i + k] = db_table[l3];
 					sblive_writeptr(card, FXGPREGBASE + 0x10 + i + k, 0, card->digmix[i + k]);
 				}
-			if (card->digmix[i + 6] != 0x80000000) {
+			if (card->digmix[i + 6] != DM_MUTE) {
 				card->digmix[i + 6] = db_table[l4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 6, 0, card->digmix[i + 6]);
 			}
-			if (card->digmix[i + 7] != 0x80000000) {
+			if (card->digmix[i + 7] != DM_MUTE) {
 				card->digmix[i + 7] = db_table[r4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 7, 0, card->digmix[i + 7]);
 			}
@@ -506,10 +527,11 @@
 
 	switch (ch) {
 	case SOUND_MIXER_VOLUME:
-	case SOUND_MIXER_DIGITAL1:
-	case SOUND_MIXER_LINE3:
-		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_VOLUME) ? "VOLUME" : (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
-		update_digital(card);
+		DPF(4, "SOUND_MIXER_VOLUME:\n");
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
 		return 0;
 	case SOUND_MIXER_PCM:
 		DPF(4, "SOUND_MIXER_PCM\n");
@@ -517,6 +539,34 @@
 		if (update_pcm_attn(card, l1, r1))
 			return 0;
 #endif
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
+		return 0;
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+                DPF(4, "SOUND_MIXER_TREBLE:\n");
+                set_treble(card, l1, r1);
+                return 0;
+
+        case SOUND_MIXER_BASS:
+                DPF(4, "SOUND_MIXER_BASS:\n");
+                set_bass(card, l1, r1);
+		return 0;
+#endif
+	default:
+		break;
+	}
+
+
+	if (card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
+	case SOUND_MIXER_DIGITAL1:
+	case SOUND_MIXER_LINE3:
+		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
 		update_digital(card);
 		return 0;
 	case SOUND_MIXER_DIGITAL2:
@@ -573,16 +623,6 @@
 		sblive_writeac97(card, volreg[ch], wval);
 		return 0;
 
-	case SOUND_MIXER_TREBLE:
-		DPF(4, "SOUND_MIXER_TREBLE:\n");
-		set_treble(card, l1, r1);
-		return 0;
-
-	case SOUND_MIXER_BASS:
-		DPF(4, "SOUND_MIXER_BASS:\n");
-		set_bass(card, l1, r1);
-		return 0;
-
 	default:
 		DPF(2, "Got unknown SOUND_MIXER ioctl\n");
 		return -EINVAL;
@@ -598,6 +638,10 @@
 /* Mixer file operations */
 
 /* FIXME: Do we need spinlocks in here? */
+/* WARNING! not all the ioctl's are supported by the emu-APS
+   (anything AC97 related). As a general rule keep the AC97 related ioctls
+   separate from the rest. This will make it easier to rewrite the mixer
+   using the kernel AC97 interface. */ 
 static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	static const char id[] = "SBLive";
@@ -659,11 +703,10 @@
 			return -EFAULT;
 
 		for (i = 0; i < sizeof(card->digmix) / sizeof(card->digmix[0]); i++)
-			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & 0x80000000) ? 0 : card->digmix[i]);
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & DM_MUTE) ? 0 : card->digmix[i]);
 		return 0;
 
 		break;
-#ifdef EMU10K1_DEBUG
 	case SOUND_MIXER_PRIVATE3: {
 			struct mixer_private_ioctl ctl;
 
@@ -671,18 +714,28 @@
 				return -EFAULT;
 
 			switch (ctl.cmd) {
+#ifdef EMU10K1_DEBUG
 			case CMD_WRITEFN0:
-				if(ctl.val[0] >= 0x7f)
-					return -EINVAL;
-
 				emu10k1_writefn0(card, ctl.val[0], ctl.val[1]);
 				return 0;
 				break;
 
-			case CMD_READFN0:
-				if(ctl.val[0] >= 0x7f)
+			case CMD_WRITEPTR:
+				if(ctl.val[1] >= 0x40)
+					return -EINVAL;
+
+				if(ctl.val[0] > 0xff)
 					return -EINVAL;
 
+				if((ctl.val[0] & 0x7ff) > 0x3f)
+					ctl.val[1] = 0x00;
+
+				sblive_writeptr(card, ctl.val[0], ctl.val[1], ctl.val[2]);
+
+				return 0;
+				break;
+#endif
+			case CMD_READFN0:
 				ctl.val[2] = emu10k1_readfn0(card, ctl.val[0]);
 
 				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
@@ -691,32 +744,68 @@
 				return 0;
 				break;
 
-			case CMD_WRITEPTR:
-				if(ctl.val[1] < 0x00 || ctl.val[1] >= 0x40)
+			case CMD_READPTR:
+				if(ctl.val[1] >= 0x40)
 					return -EINVAL;
 
-				if(ctl.val[0] >= 0x100)
+				if((ctl.val[0] & 0x7ff) > 0xff)
 					return -EINVAL;
 
-				if(ctl.val[0] >= 0x40)
+				if((ctl.val[0] & 0x7ff) > 0x3f)
 					ctl.val[1] = 0x00;
 
-				sblive_writeptr(card, ctl.val[0], ctl.val[1], ctl.val[2]);
+				ctl.val[2] = sblive_readptr(card, ctl.val[0], ctl.val[1]);
+
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
 
 				return 0;
 				break;
 
-			case CMD_READPTR:
-				if(ctl.val[1] < 0x00 || ctl.val[1] >= 0x40)
+			case CMD_SETRECSRC:
+				switch(ctl.val[0]){
+				case WAVERECORD_AC97:
+					if(card->isaps)
+						return -EINVAL;
+					card->wavein.recsrc = WAVERECORD_AC97;
+					break;
+				case WAVERECORD_MIC:	
+					card->wavein.recsrc = WAVERECORD_MIC;
+					break;
+				case WAVERECORD_FX:
+					card->wavein.recsrc = WAVERECORD_FX;
+					card->wavein.fxwc = ctl.val[1] & 0xffff;
+					if(!card->wavein.fxwc)
+						return -EINVAL;
+					break;
+				default:
 					return -EINVAL;
+				}
+				return 0;
+				break;
 
-				if(ctl.val[0] >= 0x100)
-					return -EINVAL;
+			case CMD_GETRECSRC:
+				ctl.val[0] = card->wavein.recsrc;
+				ctl.val[1] = card->wavein.fxwc;
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
 
-				if(ctl.val[0] > 0x40)
-					ctl.val[1] = 0x00;
+				return 0;
+				break;
 
-				ctl.val[2] = sblive_readptr(card, ctl.val[0], ctl.val[1]);
+			case CMD_GETVOICEPARAM:
+
+				ctl.val[0] = card->waveout.send_routing[0];
+				ctl.val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 |
+				             card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24;
+
+				ctl.val[2] = card->waveout.send_routing[1]; 
+				ctl.val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 |
+					     card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24;
+
+				ctl.val[4] = card->waveout.send_routing[2]; 
+				ctl.val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 |
+					     card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24;
 
 				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
 					return -EFAULT;
@@ -724,13 +813,35 @@
 				return 0;
 				break;
 
+			case CMD_SETVOICEPARAM:
+				card->waveout.send_routing[0] = ctl.val[0] & 0xffff;
+				card->waveout.send_a[0] = ctl.val[1] & 0xff;
+				card->waveout.send_b[0] = (ctl.val[1] >> 8) & 0xff;
+				card->waveout.send_c[0] = (ctl.val[1] >> 16) & 0xff;
+				card->waveout.send_d[0] = (ctl.val[1] >> 24) & 0xff;
+
+				card->waveout.send_routing[1] = ctl.val[2] & 0xffff;
+				card->waveout.send_a[1] = ctl.val[3] & 0xff;
+				card->waveout.send_b[1] = (ctl.val[3] >> 8) & 0xff;
+				card->waveout.send_c[1] = (ctl.val[3] >> 16) & 0xff;
+				card->waveout.send_d[1] = (ctl.val[3] >> 24) & 0xff;
+
+				card->waveout.send_routing[2] = ctl.val[4] & 0xffff;
+				card->waveout.send_a[2] = ctl.val[5] & 0xff;
+				card->waveout.send_b[2] = (ctl.val[5] >> 8) & 0xff;
+				card->waveout.send_c[2] = (ctl.val[5] >> 16) & 0xff;
+				card->waveout.send_d[2] = (ctl.val[5] >> 24) & 0xff;
+
+				return 0;
+				break;
+
 			default:
 				return -EINVAL;
 				break;
 			}
 		}
 		break;
-#endif
+
 	case SOUND_MIXER_PRIVATE4:{
 			u32 size;
 			int size_reg = 0;
@@ -797,57 +908,107 @@
 
 	if (_IOC_DIR(cmd) == _IOC_READ) {
 		switch (_IOC_NR(cmd)) {
-		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
-			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
-			sblive_readac97(card, AC97_RECORDSELECT, &reg);
-			return put_user(recsrc[reg & 7], (int *) arg);
-
-		case SOUND_MIXER_DEVMASK:	/* Arg contains a bit for each supported device */
-			DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			case SOUND_MIXER_DEVMASK:       /* Arg contains a bit for each supported device */
+                        DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			if (card->isaps)
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+						SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+						(int *) arg); 
+#else
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+						(int *) arg); 
+#endif
+		
+#ifdef TONE_CONTROL
 			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#else
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#endif
+
+			case SOUND_MIXER_RECMASK:       /* Arg contains a bit for each supported recording source */
+				DPF(2, "SOUND_MIXER_READ_RECMASK\n");
+				if (card->isaps)
+					return put_user(0, (int *) arg);
+
+				return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
+					SOUND_MASK_LINE1 | SOUND_MASK_LINE |
+					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN |
+					SOUND_MASK_PHONEIN, (int *) arg);
+
+			case SOUND_MIXER_STEREODEVS:    /* Mixer channels supporting stereo */
+				DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
+
+				if (card->isaps)
+#ifdef TONE_CONTROL
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        		SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+                                        		(int *) arg);
+#else
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+                                         		(int *) arg);
+#endif
+
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
 					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
-					SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
-					SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
-					SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
-		case SOUND_MIXER_RECMASK:	/* Arg contains a bit for each supported recording source */
-			DPF(2, "SOUND_MIXER_READ_RECMASK\n");
-			return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
-					SOUND_MASK_LINE1 | SOUND_MASK_LINE | 
-					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN | 
-					SOUND_MASK_PHONEIN | SOUND_MASK_MONITOR | 
-					SOUND_MASK_PCM, (int *) arg);
-
-		case SOUND_MIXER_STEREODEVS:	/* Mixer channels supporting stereo */
-			DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
-			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
+					SOUND_MASK_LINE2, (int *) arg);
+#else
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
 					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
-					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | 
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
 					SOUND_MASK_LINE2, (int *) arg);
+#endif
 
-		case SOUND_MIXER_CAPS:
-			DPF(2, "SOUND_MIXER_READ_CAPS\n");
-			return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
-
+			case SOUND_MIXER_CAPS:
+				DPF(2, "SOUND_MIXER_READ_CAPS\n");
+				return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
 #ifdef PRIVATE_PCM_VOLUME
-		case SOUND_MIXER_PCM:
-			/* needs to be before default: !!*/
-			{
-				int i;
-
-				for (i = 0; i < MAX_PCM_CHANNELS; i++) {
-					if (sblive_pcm_volume[i].files == current->files) {
-						return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
-					}
-				}
-			}
+                case SOUND_MIXER_PCM:
+                        /* needs to be before default: !!*/
+                        {
+                                int i;
+
+                                for (i = 0; i < MAX_PCM_CHANNELS; i++) {
+                                        if (sblive_pcm_volume[i].files == current->files) {
+                                                return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
+                                        }
+                                }
+                        }
 #endif
 		default:
+			break;
+		}
+
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
+			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
+			if (card->isaps)
+				return put_user(0, (int *) arg);
+
+			sblive_readac97(card, AC97_RECORDSELECT, &reg);
+			return put_user(recsrc[reg & 7], (int *) arg);
+
+		default:
 			i = _IOC_NR(cmd);
 			DPD(4, "SOUND_MIXER_READ(%d)\n", i);
 			if (i >= SOUND_MIXER_NRDEVICES)
@@ -862,6 +1023,7 @@
 #endif				/* OSS_DOCUMENTED_MIXER_SEMANTICS */
 		}
 	}
+
 	/* End of _IOC_READ */
 	if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE))
 		return -EINVAL;
@@ -873,7 +1035,12 @@
 	case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
 		DPF(2, "SOUND_MIXER_WRITE_RECSRC\n");
 
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (card->isaps)
+			return -EINVAL;
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		i = hweight32(val);
 		if (i == 0)
 			return 0;	/* val = mixer_recmask(s); */
@@ -897,7 +1064,9 @@
 
 		if (i >= SOUND_MIXER_NRDEVICES)
 			return -EINVAL;
-		get_user_ret(val, (int *) arg, -EFAULT);
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
 		if (emu10k1_mixer_wrch(card, i, val))
 			return -EINVAL;
 
@@ -936,14 +1105,14 @@
 
 static int emu10k1_mixer_release(struct inode *inode, struct file *file)
 {
-	DPF(3, "emu10k1_mixer_release()\n");
+	DPF(4, "emu10k1_mixer_release()\n");
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 struct file_operations emu10k1_mixer_fops = {
-	llseek:emu10k1_mixer_llseek,
-	ioctl:emu10k1_mixer_ioctl,
-	open:emu10k1_mixer_open,
-	release:emu10k1_mixer_release,
+	llseek:		emu10k1_mixer_llseek,
+	ioctl:		emu10k1_mixer_ioctl,
+	open:		emu10k1_mixer_open,
+	release:	emu10k1_mixer_release,
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/recmgr.c linux.18p17/drivers/sound/emu10k1/recmgr.c
--- linux.vanilla/drivers/sound/emu10k1/recmgr.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/recmgr.c	Tue Sep  5 21:50:50 2000
@@ -36,9 +36,6 @@
 {
 	DPF(2, "emu10k1_start_record()\n");
 
-	if(buffer->fxwc)
-		sblive_writeptr(card, FXWC, 0, buffer->fxwc);
-
 	sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval);
 
 	if (buffer->adcctl)
@@ -109,8 +106,6 @@
 		if (wiinst->format.channels == 2)
 			buffer->adcctl |= ADCCR_RCHANENABLE;
 
-			buffer->fxwc = 0;
-
 		break;
 
 	case WAVERECORD_MIC:
@@ -119,7 +114,6 @@
 		buffer->addrreg = MICBA;
 		buffer->idxreg = MICIDX_IDX;
 		buffer->adcctl = 0;
-		buffer->fxwc = 0;
 		break;
 
 	case WAVERECORD_FX:
@@ -128,7 +122,8 @@
 		buffer->addrreg = FXBA;
 		buffer->idxreg = FXIDX_IDX;
 		buffer->adcctl = 0;
-		buffer->fxwc = 0x0001;
+
+		sblive_writeptr(card, FXWC, 0, wiinst->fxwc);
 		break;
 	default:
 		BUG();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/voicemgr.c linux.18p17/drivers/sound/emu10k1/voicemgr.c
--- linux.vanilla/drivers/sound/emu10k1/voicemgr.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/voicemgr.c	Tue Sep  5 21:50:50 2000
@@ -32,7 +32,7 @@
 #include "voicemgr.h"
 #include "8010.h"
 
-int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice, struct voice_allocdesc *voiceallocdesc)
+int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice)
 {
 	u8 *voicetable = card->voicetable;
 	int i;
@@ -42,17 +42,17 @@
 
 	spin_lock_irqsave(&card->lock, flags);
 
-	if (voiceallocdesc->flags & VOICE_FLAGS_STEREO) {
+	if (voice->flags & VOICE_FLAGS_STEREO) {
 		for (i = 0; i < NUM_G; i += 2)
 			if ((voicetable[i] == VOICE_USAGE_FREE) && (voicetable[i + 1] == VOICE_USAGE_FREE)) {
-				voicetable[i] = voiceallocdesc->usage;
-				voicetable[i + 1] = voiceallocdesc->usage;
+				voicetable[i] = voice->usage;
+				voicetable[i + 1] = voice->usage;
 				break;
 			}
 	} else {
 		for (i = 0; i < NUM_G; i++)
 			if (voicetable[i] == VOICE_USAGE_FREE) {
-				voicetable[i] = voiceallocdesc->usage;
+				voicetable[i] = voice->usage;
 				break;
 			}
 	}
@@ -63,8 +63,6 @@
 		return -1;
 
 	voice->card = card;
-	voice->flags = voiceallocdesc->flags;
-	voice->usage = voiceallocdesc->usage;
 	voice->num = i;
 
 #ifdef PRIVATE_PCM_VOLUME
@@ -99,7 +97,6 @@
 {
 	struct emu10k1_card *card = voice->card;
 	int i;
-	u32 cra, sample;
 	unsigned long flags;
 
 	DPF(2, "emu10k1_voice_free()\n");
@@ -122,9 +119,7 @@
 #endif
 
 	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
-		int cra1;
-
-		DPD(2, " voice freed -> %d\n", voice->num + i);
+		DPD(2, " voice released -> %d\n", voice->num + i);
 
 		sblive_writeptr_tag(card, voice->num + i, DCYSUSV, 0, 
 							VTFT, 0x0000ffff,
@@ -132,15 +127,6 @@
 							CVCF, 0x0000ffff,
 							CPF, 0,
 							TAGLIST_END);
-
-		sample = (voice->flags & VOICE_FLAGS_16BIT) ? 0 : 0x80808080;
-		cra = sblive_readptr(card, CCR, voice->num) & CCR_READADDRESS_MASK;
-		cra1 = (cra >> 18) & 0xf;
-
-		sblive_writeptr_tag(card, voice->num + i, CCR, cra,
-							CD0 + cra1, sample,
-							CD0 + ((cra1 + 0x1) & 0xf), sample,
-							TAGLIST_END);
 	}
 
 	voice->usage = VOICE_USAGE_FREE;
@@ -157,91 +143,48 @@
 	return;
 }
 
-/*       Sets up a voices for Wave Playback */
-
 void emu10k1_voice_playback_setup(struct emu_voice *voice)
 {
 	struct emu10k1_card *card = voice->card;
-	u32 sample, cra = 0, start = 0;
+	u32 start;
 	int i;
 
 	DPF(2, "emu10k1_voice_playback_setup()\n");
 
-	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
-		sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].bus_routing << 16);
-
-		/* Stop CA */
-		/* Assumption that PT is alreadt 0 so no harm overwriting */
-		sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b);
-
-		if (i == 0) {
-			if (voice->flags & VOICE_FLAGS_STEREO) {
-				/* Set stereo bit */
-				cra = 64;
-				sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK);
-				sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK);
-			} else {
-				cra = 32;
-				sblive_writeptr(card, CPF, voice->num, 0);
-			}
-
-			if (voice->flags & VOICE_FLAGS_16BIT)
-				sample = 0;
-			else {
-				cra = cra * 2;
-				sample = 0x80808080;
-			}
-			cra -= 4;
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		/* Set stereo bit */
+		start = 28;
+		sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK);
+		sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK);
+	} else {
+		start = 30;
+		sblive_writeptr(card, CPF, voice->num, 0);
+	}
 
-			if (voice->flags & VOICE_FLAGS_STEREO) {
-				/* CCR_READADDRESS_MASK */
-				sblive_writeptr(card, CCR, voice->num, 0x3c << 16);
-				sblive_writeptr_tag(card, voice->num + 1, CCR, cra << 16, CDE, sample, CDF, sample, TAGLIST_END);
-				start = voice->start + cra / 2;
-			} else {
-				sblive_writeptr_tag(card, voice->num, CCR, 0x1c << 16,	/* FIXME: Is 0x1c correct? */
-						    CDE, sample, CDF, sample, TAGLIST_END);
-				start = voice->start + cra;
-			}
+	if(!(voice->flags & VOICE_FLAGS_16BIT))
+		start *= 2;
 
-			if (start > voice->endloop) {
-				start -= voice->endloop;
+	voice->start += start;
 
-				if (voice->flags & VOICE_FLAGS_STEREO)
-					cra = (cra << 25) | 0x1bc0000 | ((cra - start) << 9);
-				else
-					cra = (cra << 25) | 0x11c0000 | ((cra - start) << 9);
-
-				start += voice->startloop;
-
-				if (start >= voice->endloop)
-					start = voice->endloop - 1;
-			} else if (voice->flags & VOICE_FLAGS_STEREO)
-				cra = (cra << 25) | (0x3c << 16);
-			else
-				cra = (cra << 25) | (0x1c << 16);
+	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
+		sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].send_routing << 16);
 
-			start |= CCCA_INTERPROM_0;
-		}
+		/* Stop CA */
+		/* Assumption that PT is already 0 so no harm overwriting */
+		sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b);
 
-		/* CSL, ST, CA */
 		sblive_writeptr_tag(card, voice->num + i,
+				/* CSL, ST, CA */
 				    DSL, voice->endloop | (voice->params[i].send_d << 24),
 				    PSST, voice->startloop | (voice->params[i].send_c << 24),
-				    CCCA, start | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT),
+				    CCCA, (voice->start) | CCCA_INTERPROM_0 | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT),
 				    /* Clear filter delay memory */
 				    Z1, 0,
 				    Z2, 0,
 				    /* Invalidate maps */
 				    MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
-				    MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), TAGLIST_END);
-
-		/* Fill cache */
-		if (i == 0)
-			sblive_writeptr(card, CCR, voice->num, cra);
-
-		/* modulation envelope */
-		sblive_writeptr_tag(card, voice->num + i,
+				    MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
+				/* modulation envelope */
 				    CVCF, 0x0000ffff,
 				    VTFT, 0x0000ffff,
 				    ATKHLDM, 0,
@@ -252,12 +195,12 @@
 				    TREMFRQ, 0,
 				    FM2FRQ2, 0,
 				    ENVVAL, 0x8000,
-				    /* volume envelope */
+				/* volume envelope */
 				    ATKHLDV, 0x7f7f,
 				    ENVVOL, 0x8000,
-				    /* filter envelope */
+				/* filter envelope */
 				    PEFE_FILTERAMOUNT, 0x7f,
-				    /* pitch envelope */
+				/* pitch envelope */
 				    PEFE_PITCHAMOUNT, 0, TAGLIST_END);
 
 #ifdef PRIVATE_PCM_VOLUME
@@ -286,13 +229,49 @@
 	return;
 }
 
-void emu10k1_voice_start(struct emu_voice *voice)
+void emu10k1_voice_start(struct emu_voice *voice, int set)
 {
 	struct emu10k1_card *card = voice->card;
 	int i;
 
 	DPF(2, "emu10k1_voice_start()\n");
 
+	if (!set) {
+		u32 cra, ccis, cs, sample;
+		if (voice->flags & VOICE_FLAGS_STEREO) {
+			cra = 64;
+			ccis = 28;
+			cs = 4;
+		} else {
+			cra = 64;
+			ccis = 30;
+			cs = 2;
+		}
+
+		if(voice->flags & VOICE_FLAGS_16BIT) {
+			sample = 0x00000000;
+		} else {
+			sample = 0x80808080;		
+			ccis *= 2;
+		}
+
+		for(i = 0; i < cs; i++)
+	                sblive_writeptr(card, CD0 + i, voice->num, sample);
+
+		/* Reset cache */
+		sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, 0);
+		if (voice->flags & VOICE_FLAGS_STEREO)
+			sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num + 1, 0);
+
+		sblive_writeptr(card, CCR_READADDRESS, voice->num, cra);
+
+		if (voice->flags & VOICE_FLAGS_STEREO)
+			sblive_writeptr(card, CCR_READADDRESS, voice->num + 1, cra);
+
+		/* Fill cache */
+		sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, ccis);
+	}
+
 	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
 		sblive_writeptr_tag(card, voice->num + i,
 				    IFATN, (voice->params[i].initial_fc << 8) | voice->params[i].initial_attn,
@@ -301,18 +280,7 @@
 				    DCYSUSV, (voice->params[i].byampl_env_sustain << 8) | voice->params[i].byampl_env_decay,
 				    TAGLIST_END);
 
-		/* Using StopOnLoop for MIDI stops the playback
-		   too early, which may cause a DC level to be played
-		   until the note is released. */
-
-		if (voice->usage == VOICE_USAGE_MIDI)
-			emu10k1_clear_stop_on_loop(card, voice->num + i);
-		else {
-			if (voice->startloop > voice->end)
-				emu10k1_set_stop_on_loop(card, voice->num + i);
-			else
-				emu10k1_clear_stop_on_loop(card, voice->num + i);
-		}
+		emu10k1_clear_stop_on_loop(card, voice->num + i);
 
 		sblive_writeptr(card, PTRX_PITCHTARGET, voice->num + i, voice->pitch_target);
 
@@ -333,32 +301,16 @@
 	DPF(2, "emu10k1_voice_stop()\n");
 
 	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
-		sblive_writeptr_tag(card, voice->num + i, IFATN, 0xffff,
-				    VTFT, 0x0000ffff,
-				    PTRX_PITCHTARGET, 0,
-				    IP, 0,
-				    TAGLIST_END);
+		sblive_writeptr_tag(card, voice->num + i,
+					PTRX_PITCHTARGET, 0,
+					CPF_CURRENTPITCH, 0,
+					IFATN, 0xffff,
+					VTFT, 0x0000ffff,
+					CVCF, 0x0000ffff,
+					IP, 0,
+					TAGLIST_END);
 	}
 
 	return;
 }
 
-void emu10k1_voice_setcontrol(struct emu_voice *voice, struct voice_cntlset *setting, u32 numparam)
-{
-	struct emu10k1_card *card = voice->card;
-	int count;
-
-	for (count = 0; count < numparam; count++)
-		sblive_writeptr(card, setting[count].paramID, voice->num, setting[count].value);
-
-	return;
-}
-
-void emu10k1_voice_getcontrol(struct emu_voice *voice, u32 controlid, u32 * value)
-{
-	struct emu10k1_card *card = voice->card;
-
-	*value = sblive_readptr(card, controlid, voice->num);
-
-	return;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/emu10k1/voicemgr.h linux.18p17/drivers/sound/emu10k1/voicemgr.h
--- linux.vanilla/drivers/sound/emu10k1/voicemgr.h	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/emu10k1/voicemgr.h	Sun Oct 15 23:40:33 2000
@@ -47,7 +47,7 @@
 {
 	/* FX bus amount send */
 
-	u32 bus_routing;
+	u32 send_routing;
 
 	u32 send_a;
 	u32 send_b;
@@ -64,11 +64,6 @@
 	u32 byampl_env_decay;
 };
 
-struct voice_allocdesc
-{
-	u8 usage;			/* playback, Midi */
-	u8 flags;			/* stereo/mono rec/playback 8/16 bit*/
-};
 
 struct emu_voice
 {
@@ -77,10 +72,9 @@
 	u8 num;			/* Voice ID */
 	u8 flags;		/* Stereo/mono, 8/16 bit */
 
-	u32 start;
         u32 startloop;
         u32 endloop;
-        u32 end;
+	u32 start;
 
 	u32 initial_pitch;
 	u32 pitch_target;
@@ -88,18 +82,10 @@
 	struct voice_param params[2];
 };
 
-struct voice_cntlset
-{
-	u32 paramID;
-	u32 value;
-};
-
-int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *, struct voice_allocdesc *);
+int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *);
 void emu10k1_voice_free(struct emu_voice *);
 void emu10k1_voice_playback_setup(struct emu_voice *);
-void emu10k1_voice_start(struct emu_voice *);
+void emu10k1_voice_start(struct emu_voice *, int);
 void emu10k1_voice_stop(struct emu_voice *);
-void emu10k1_voice_setcontrol(struct emu_voice *, struct voice_cntlset *, u32);
-void emu10k1_voice_getcontrol(struct emu_voice *, u32, u32 *);
 
 #endif /* _VOICEMGR_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/es1370.c linux.18p17/drivers/sound/es1370.c
--- linux.vanilla/drivers/sound/es1370.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/es1370.c	Tue Sep  5 22:21:01 2000
@@ -164,21 +164,9 @@
 
 #ifdef MODULE
 #define __exit
-#define module_exit(x) void cleanup_module(void) { x(); }
-#define module_init(x) int init_module(void) { return x(); }
 #else
 #define __exit __attribute__ ((unused, __section__ (".text.init")))
-#define module_exit(x) /* nothing */
-#define module_init(x) /* nothing */
 #endif
-
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define wait_queue_head_t struct wait_queue *
-#define init_waitqueue_head(w) *(w) = 0
-#define init_MUTEX(m) *(m) = MUTEX
-#define __set_current_state(x) do { current->state = (x); } while (0)
-#define set_current_state(x) __set_current_state(x)
 
 /* --------------------------------------------------------------------- */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/es1371.c linux.18p17/drivers/sound/es1371.c
--- linux.vanilla/drivers/sound/es1371.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/es1371.c	Tue Sep  5 22:21:01 2000
@@ -132,21 +132,9 @@
 
 #ifdef MODULE
 #define __exit
-#define module_exit(x) void cleanup_module(void) { x(); }
-#define module_init(x) int init_module(void) { return x(); }
 #else
 #define __exit __attribute__ ((unused, __section__ (".text.init")))
-#define module_exit(x) /* nothing */
-#define module_init(x) /* nothing */
 #endif
-
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define wait_queue_head_t struct wait_queue *
-#define init_waitqueue_head(w) *(w) = 0
-#define init_MUTEX(m) *(m) = MUTEX
-#define __set_current_state(x) do { current->state = (x); } while (0)
-#define set_current_state(x) __set_current_state(x)
 
 /* --------------------------------------------------------------------- */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/esssolo1.c linux.18p17/drivers/sound/esssolo1.c
--- linux.vanilla/drivers/sound/esssolo1.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/esssolo1.c	Tue Sep  5 22:21:01 2000
@@ -133,21 +133,10 @@
 #if LINUX_VERSION_CODE < 0x02030d
 #ifdef MODULE
 #define __exit
-#define module_exit(x) void cleanup_module(void) { x(); }
-#define module_init(x) int init_module(void) { return x(); }
 #else
 #define __exit __attribute__ ((unused, __section__ (".text.init")))
-#define module_exit(x) /* nothing */
-#define module_init(x) /* nothing */
 #endif
 
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define wait_queue_head_t struct wait_queue *
-#define init_waitqueue_head(w) *(w) = 0
-#define init_MUTEX(m) *(m) = MUTEX
-#define __set_current_state(x) do { current->state = (x); } while (0)
-#define set_current_state(x) __set_current_state(x)
 #endif
 
 /* --------------------------------------------------------------------- */
@@ -229,7 +218,7 @@
 
 /* --------------------------------------------------------------------- */
 
-struct solo1_state *devs = NULL;
+static struct solo1_state *devs = NULL;
 
 /* --------------------------------------------------------------------- */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/i810_audio.c linux.18p17/drivers/sound/i810_audio.c
--- linux.vanilla/drivers/sound/i810_audio.c	Sat Sep  9 18:42:43 2000
+++ linux.18p17/drivers/sound/i810_audio.c	Fri Sep 29 22:21:09 2000
@@ -1591,6 +1591,7 @@
 	struct i810_card *card = dev->private_data;
 	int count = 1000;
 
+#if 0
 	if (card->pci_id != INTEL440MX) {
 		while(--count && (inl(card->iobase + GLOB_STA) & 0x0100))
 			udelay(10);
@@ -1598,6 +1599,7 @@
 			printk("i810_audio: AC97 access failed.\n");
 		count=1000;
 	}
+#endif
 	while(--count && (inb(card->iobase + CAS) & 1)) 
 		udelay(10);
 	if(!count)
@@ -1610,6 +1612,7 @@
 	struct i810_card *card = dev->private_data;
 	int count = 1000;
 
+#if 0
 	if (card->pci_id != INTEL440MX) {
 		while(--count && (inl(card->iobase + GLOB_STA) & 0x0100))
 			udelay(10);
@@ -1618,6 +1621,7 @@
 
 		count=1000;
 	}
+#endif	
 	while(--count && (inb(card->iobase + CAS) & 1)) 
 		udelay(10);
 	if(!count)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/maestro.c linux.18p17/drivers/sound/maestro.c
--- linux.vanilla/drivers/sound/maestro.c	Sat Sep  9 18:42:44 2000
+++ linux.18p17/drivers/sound/maestro.c	Fri Sep  1 22:12:09 2000
@@ -210,11 +210,8 @@
 #include <linux/version.h>
 #include <linux/module.h>
 
-#define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL}
-#define wait_queue_head_t struct wait_queue *
 #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)
 #define SILLY_INIT_SEM(SEM) SEM=MUTEX;
-#define init_waitqueue_head init_waitqueue
 #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC)
 #define SILLY_OFFSET(VMA) ((VMA)->vm_offset)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/msnd_pinnacle.c linux.18p17/drivers/sound/msnd_pinnacle.c
--- linux.vanilla/drivers/sound/msnd_pinnacle.c	Fri Apr 21 12:46:35 2000
+++ linux.18p17/drivers/sound/msnd_pinnacle.c	Fri Sep  1 13:19:00 2000
@@ -50,7 +50,9 @@
 #include "sound_config.h"
 #include "sound_firmware.h"
 #ifdef MSND_CLASSIC
+# ifndef __alpha__
 #  define SLOWIO
+# endif
 #endif
 #include "msnd.h"
 #ifdef MSND_CLASSIC
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/pas2_mixer.c linux.18p17/drivers/sound/pas2_mixer.c
--- linux.vanilla/drivers/sound/pas2_mixer.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/pas2_mixer.c	Sun Oct 15 21:32:09 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/softoss.c linux.18p17/drivers/sound/softoss.c
--- linux.vanilla/drivers/sound/softoss.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/softoss.c	Thu Aug 31 15:22:53 2000
@@ -1113,7 +1113,10 @@
 		return -ENOMEM;
 	}
 	if(copy_from_user(&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs))
+	{
+		vfree(patch);
 		return -EFAULT;
+	}
 
 	if (patch->mode & WAVE_ROM)
 	{
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sonicvibes.c linux.18p17/drivers/sound/sonicvibes.c
--- linux.vanilla/drivers/sound/sonicvibes.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/sonicvibes.c	Mon Sep 11 17:49:08 2000
@@ -115,22 +115,10 @@
 
 #ifdef MODULE
 #define __exit
-#define module_exit(x) void cleanup_module(void) { x(); }
-#define module_init(x) int init_module(void) { return x(); }
 #else
 #define __exit __attribute__ ((unused, __section__ (".text.init")))
-#define module_exit(x) /* nothing */
-#define module_init(x) /* nothing */
 #endif
 
-#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
-#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
-#define wait_queue_head_t struct wait_queue *
-#define init_waitqueue_head(w) *(w) = 0
-#define init_MUTEX(m) *(m) = MUTEX
-#define __set_current_state(x) do { current->state = (x); } while (0)
-#define set_current_state(x) __set_current_state(x)
-
 /* --------------------------------------------------------------------- */
 
 #ifndef PCI_VENDOR_ID_S3
@@ -536,7 +524,7 @@
 static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate)
 {
 	unsigned long flags;
-	unsigned char r, m, n;
+	unsigned char r=0, m=0, n=0;
 	unsigned xm, xn, xr, xd, metric = ~0U;
 	/* the warnings about m and n used uninitialized are bogus and may safely be ignored */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sound_core.c linux.18p17/drivers/sound/sound_core.c
--- linux.vanilla/drivers/sound/sound_core.c	Sat Sep  9 18:42:45 2000
+++ linux.18p17/drivers/sound/sound_core.c	Tue Sep 26 17:10:10 2000
@@ -52,42 +52,22 @@
 	struct sound_unit *next;
 };
 
-#ifdef CONFIG_SOUND_SONICVIBES
 extern int init_sonicvibes(void);
-#endif
-#ifdef CONFIG_SOUND_MAESTRO
 extern int init_maestro(void);
-#endif
-#ifdef CONFIG_SOUND_TRIDENT
 extern int init_trident(void);
-#endif
-#ifdef CONFIG_SOUND_ICH
 extern int i810_probe(void);
-#endif
-#ifdef CONFIG_SOUND_ES1370
 extern int init_es1370(void);
-#endif
-#ifdef CONFIG_SOUND_ES1371
 extern int init_es1371(void);
-#endif
-#ifdef CONFIG_SOUND_MSNDCLAS
 extern int msnd_classic_init(void);
-#endif
-#ifdef CONFIG_SOUND_MSNDPIN
 extern int msnd_pinnacle_init(void);
-#endif
-#ifdef CONFIG_SOUND_ESSSOLO1
 extern int init_solo1(void);
-#endif
-#ifdef CONFIG_SOUND_YMPCI
 extern int init_ymf7xxsb_module(void);
-#endif
-#ifdef CONFIG_SOUND_FUSION
 extern int cs_probe(void);
-#endif
-#ifdef CONFIG_SOUND_EMU10K1
 extern int init_emu10k1(void);
-#endif
+extern int cs4281_probe(void);
+extern void init_vwsnd(void);
+extern int ymf_probe(void);
+
 
 /*
  *	Low level list operator. Scan the ordered list, find a hole and
@@ -457,6 +437,9 @@
 #endif
 #ifdef CONFIG_SOUND_EMU10K1
 	init_emu10k1();
+#endif
+#ifdef CONFIG_SOUND_YMFPCI
+	ymf_probe();
 #endif
 	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/trident.c linux.18p17/drivers/sound/trident.c
--- linux.vanilla/drivers/sound/trident.c	Sat Sep  9 18:42:45 2000
+++ linux.18p17/drivers/sound/trident.c	Sun Oct 15 23:22:38 2000
@@ -30,6 +30,8 @@
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  *  History
+ *  v0.14.5b Sept 17 2000 Eric Brombaugh
+ *      Fix hang when trying ALi MIDI init on Trident
  *  v0.14.5 June 3 2000 Ollie Lho
  *  	Misc bug fix from the Net
  *	Backported from 2.4.0-test1 to 2.2.16 (Eric Brombaugh)
@@ -113,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"
@@ -2863,40 +2869,57 @@
 		return -ENODEV;
 	}
 	
-	if ((card->dev_midi = register_sound_midi(&trident_midi_fops, -1))<0)
-		printk(KERN_WARNING "trident: Unable to register midi.\n");
-	else
-	{
-		struct midi_stuff *midi = &card->midi;
-		init_waitqueue(&midi->iwait);
-		init_waitqueue(&midi->owait);
-		midi->ird = midi->iwr = midi->icnt = 0;
-		midi->ord = midi->owr = midi->ocnt = 0;
-		outb(0xbc, card->iobase+ALI_MPUR2);
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		/* ALi 5451 has special MIDI regs and FM emulation */
+		if ((card->dev_midi = register_sound_midi(&trident_midi_fops, -1))<0)
+			printk(KERN_WARNING "trident: Unable to register midi.\n");
+		else
+		{
+			struct midi_stuff *midi = &card->midi;
+			init_waitqueue(&midi->iwait);
+			init_waitqueue(&midi->owait);
+			midi->ird = midi->iwr = midi->icnt = 0;
+			midi->ord = midi->owr = midi->ocnt = 0;
+			outb(0xbc, card->iobase+ALI_MPUR2);
 		
-		/* reset command */
-		while (inb(card->iobase + ALI_MPUR1) & 0x40);
-		outb(0xff, card->iobase + ALI_MPUR1);
-		while (inb(card->iobase + ALI_MPUR1) & 0x80);
-		while ((inb(card->iobase + ALI_MPUR0) != 0xfe) && (inb(card->iobase+ALI_MPUR1) != 0x10)) 
+			/* reset command */
+			while (inb(card->iobase + ALI_MPUR1) & 0x40);
+			outb(0xff, card->iobase + ALI_MPUR1);
 			while (inb(card->iobase + ALI_MPUR1) & 0x80);
+			while ((inb(card->iobase + ALI_MPUR0) != 0xfe) && (inb(card->iobase+ALI_MPUR1) != 0x10)) 
+				while (inb(card->iobase + ALI_MPUR1) & 0x80);
 			
-		midi->ird = midi->iwr = midi->icnt = 0;
-	}
-		
-	if ((card->dev_dmfm = register_sound_special(&trident_dmfm_fops, 15))<0)
-		printk(KERN_WARNING "trident: Unable to register FM.\n");
+			midi->ird = midi->iwr = midi->icnt = 0;
+		}
 	
+		if ((card->dev_dmfm = register_sound_special(&trident_dmfm_fops, 15))<0)
+			printk(KERN_WARNING "trident: Unable to register FM.\n");
+	} else {
+		/* don't try to enable MIDI and FM on other chips */
+		card->dev_midi = -1;
+		card->dev_dmfm = -1;
+	}
+
 	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
-		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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/uart401.c linux.18p17/drivers/sound/uart401.c
--- linux.vanilla/drivers/sound/uart401.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/uart401.c	Thu Aug 31 14:55:00 2000
@@ -317,8 +317,8 @@
 	{
 		printk(KERN_WARNING "uart401: Failed to allocate memory\n");
 		kfree(midi_devs[devc->my_dev]);
-		kfree(devc);
 		sound_unload_mididev(devc->my_dev);
+		kfree(devc);
 		devc=NULL;
 		return;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/via82cxxx_audio.c linux.18p17/drivers/sound/via82cxxx_audio.c
--- linux.vanilla/drivers/sound/via82cxxx_audio.c	Sun Jun 11 21:44:18 2000
+++ linux.18p17/drivers/sound/via82cxxx_audio.c	Wed Sep 27 12:07:01 2000
@@ -5,48 +5,48 @@
  * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2.
  * See the "COPYING" file distributed with this software for more info.
  *
- * Documentation for this driver available as
- * linux/Documentation/sound/via82cxxx.txt.
+ * For a list of known bugs (errata) and documentation,
+ * see via-audio.pdf in linux/Documentation/DocBook.
+ * If this documentation does not exist, run "make pdfdocs".
+ * If "make pdfdocs" fails, obtain the documentation from
+ * the driver's Website at
+ * http://gtf.org/garzik/drivers/via82cxxx/
  *
- * Since the mixer is called from the OSS glue the kernel lock is always held
- * on our AC97 mixing
  */
  
 
-#define VIA_VERSION	"1.1.2.1"
+#define VIA_VERSION	"1.1.8-2.2"
+
 
-#include <linux/module.h>
 #include <linux/config.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/fs.h>
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/proc_fs.h>
-
+#include <linux/spinlock.h>
+#include <linux/sound.h>
+#include <linux/poll.h>
+#include <linux/soundcard.h>
+#include <linux/ac97_codec.h>
+#include <linux/smp_lock.h>
 #include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
 
 #include "sound_config.h"
-#include "soundmodule.h"
-#include "sb.h"
-#include "ac97.h"
-
-#ifndef SOUND_LOCK
-#define SOUND_LOCK do {} while (0)
-#define SOUND_LOCK_END do {} while (0)
-#endif
 
-#define VIA_DEBUG 0	/* define to 1 to enable debugging output and checks */
-#if VIA_DEBUG
+#undef VIA_DEBUG	/* define to enable debugging output and checks */
+#ifdef VIA_DEBUG
 /* note: prints function name for you */
 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
 #else
 #define DPRINTK(fmt, args...)
 #endif
 
-#define VIA_NDEBUG 0	/* define to 1 to disable lightweight runtime checks */
-#if VIA_NDEBUG
+#define VIA_NDEBUG	/* define to disable lightweight runtime checks */
+#ifdef VIA_NDEBUG
 #define assert(expr)
 #else
 #define assert(expr) \
@@ -58,27 +58,99 @@
 
 #define arraysize(x)            (sizeof(x)/sizeof(*(x)))
 
-#define MAX_CARDS	2
+#define MAX_CARDS	1
 
 #define	LINE_SIZE	10
 
 #define VIA_CARD_NAME	"VIA 82Cxxx Audio driver " VIA_VERSION
-#define VIA_MODULE_NAME "via_audio"
+#define VIA_MODULE_NAME "via82cxxx"
 #define PFX		VIA_MODULE_NAME ": "
 
 #define VIA_COUNTER_LIMIT	100000
 
+/* size of DMA buffers */
+#define VIA_DMA_BUFFERS		16
+#define VIA_DMA_BUF_SIZE	PAGE_SIZE
+
 /* 82C686 function 5 (audio codec) PCI configuration registers */
+#define VIA_ACLINK_CTRL		0x41
 #define VIA_FUNC_ENABLE		0x42
 #define VIA_PNP_CONTROL		0x43
-#define VIA_AC97_CTRL		0x80
+#define VIA_FM_NMI_CTRL		0x48
+
+/*
+ * controller base 0 (scatter-gather) registers
+ *
+ * NOTE: Via datasheet lists first channel as "read"
+ * channel and second channel as "write" channel.
+ * I changed the naming of the constants to be more
+ * clear than I felt the datasheet to be.
+ */
+
+#define VIA_BASE0_PCM_OUT_CHAN	0x00 /* output PCM to user */
+#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
+#define VIA_BASE0_PCM_OUT_CHAN_CTRL	0x01
+#define VIA_BASE0_PCM_OUT_CHAN_TYPE	0x02
+#define VIA_BASE0_PCM_OUT_BLOCK_COUNT	0x0C
+
+#define VIA_BASE0_PCM_IN_CHAN		0x10 /* input PCM from user */
+#define VIA_BASE0_PCM_IN_CHAN_STATUS	0x10
+#define VIA_BASE0_PCM_IN_CHAN_CTRL	0x11
+#define VIA_BASE0_PCM_IN_CHAN_TYPE	0x12
+
+/* offsets from base */
+#define VIA_PCM_STATUS			0x00
+#define VIA_PCM_CONTROL			0x01
+#define VIA_PCM_TYPE			0x02
+#define VIA_PCM_TABLE_ADDR		0x04
+
+/* XXX unused DMA channel for FM PCM data */
+#define VIA_BASE0_FM_OUT_CHAN		0x20
+#define VIA_BASE0_FM_OUT_CHAN_STATUS	0x20
+#define VIA_BASE0_FM_OUT_CHAN_CTRL	0x21
+#define VIA_BASE0_FM_OUT_CHAN_TYPE	0x22
+
+#define VIA_BASE0_AC97_CTRL		0x80
+#define VIA_BASE0_SGD_STATUS_SHADOW	0x84
+#define VIA_BASE0_GPI_INT_ENABLE	0x8C
+
+/* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */
+#define VIA_IRQ_ON_FLAG			(1<<0)	/* int on each flagged scatter block */
+#define VIA_IRQ_ON_EOL			(1<<1)	/* int at end of scatter list */
+#define VIA_INT_SEL_PCI_LAST_LINE_READ	(0)	/* int at PCI read of last line */
+#define VIA_INT_SEL_LAST_SAMPLE_SENT	(1<<2)	/* int at last sample sent */
+#define VIA_INT_SEL_ONE_LINE_LEFT	(1<<3)	/* int at less than one line to send */
+#define VIA_PCM_FMT_STEREO		(1<<4)	/* PCM stereo format (bit clear == mono) */
+#define VIA_PCM_FMT_16BIT		(1<<5)	/* PCM 16-bit format (bit clear == 8-bit) */
+#define VIA_PCM_FIFO			(1<<6)	/* enable FIFO?  documented as "reserved" */
+#define VIA_RESTART_SGD_ON_EOL		(1<<7)	/* restart scatter-gather at EOL */
+#define VIA_PCM_FMT_MASK		(VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT)
+#define VIA_CHAN_TYPE_MASK		(VIA_RESTART_SGD_ON_EOL | \
+					 VIA_PCM_FIFO | \
+					 VIA_IRQ_ON_FLAG | \
+					 VIA_IRQ_ON_EOL)
+#define VIA_CHAN_TYPE_INT_SELECT	(VIA_INT_SEL_LAST_SAMPLE_SENT)
 
 /* PCI configuration register bits and masks */
 #define VIA_CR40_AC97_READY	0x01
 #define VIA_CR40_AC97_LOW_POWER	0x02
 #define VIA_CR40_SECONDARY_READY 0x04
 
-#define VIA_CR41_ACLINK_ENABLE	0x80
+#define VIA_CR41_AC97_ENABLE	0x80 /* enable AC97 codec */
+#define VIA_CR41_AC97_RESET	0x40 /* clear bit to reset AC97 */
+#define VIA_CR41_AC97_WAKEUP	0x20 /* wake up from power-down mode */
+#define VIA_CR41_AC97_SDO	0x10 /* force Serial Data Out (SDO) high */
+#define VIA_CR41_VRA		0x08 /* enable variable sample rate */
+#define VIA_CR41_PCM_ENABLE	0x04 /* AC Link SGD Read Channel PCM Data Output */
+#define VIA_CR41_FM_PCM_ENABLE	0x02 /* AC Link FM Channel PCM Data Out */
+#define VIA_CR41_SB_PCM_ENABLE	0x01 /* AC Link SB PCM Data Output */
+#define VIA_CR41_BOOT_MASK	(VIA_CR41_AC97_ENABLE | \
+				 VIA_CR41_AC97_WAKEUP | \
+				 VIA_CR41_AC97_SDO)
+#define VIA_CR41_RUN_MASK	(VIA_CR41_AC97_ENABLE | \
+				 VIA_CR41_AC97_RESET | \
+				 VIA_CR41_VRA | \
+				 VIA_CR41_PCM_ENABLE)
 
 #define VIA_CR42_SB_ENABLE	0x01
 #define VIA_CR42_MIDI_ENABLE	0x02
@@ -88,642 +160,2268 @@
 #define VIA_CR44_SECOND_CODEC_SUPPORT	(1 << 6)
 #define VIA_CR44_AC_LINK_ACCESS		(1 << 7)
 
+#define VIA_CR48_FM_TRAP_TO_NMI		(1 << 2)
+
+/* controller base 0 register bitmasks */
+#define VIA_INT_DISABLE_MASK		(~(0x01|0x02))
+#define VIA_SGD_STOPPED			(1 << 2)
+#define VIA_SGD_ACTIVE			(1 << 7)
+#define VIA_SGD_TERMINATE		(1 << 6)
+#define VIA_SGD_FLAG			(1 << 0)
+#define VIA_SGD_EOL			(1 << 1)
+#define VIA_SGD_START			(1 << 7)
+
 #define VIA_CR80_FIRST_CODEC		0
 #define VIA_CR80_SECOND_CODEC		(1 << 30)
 #define VIA_CR80_FIRST_CODEC_VALID	(1 << 25)
+#define VIA_CR80_VALID			(1 << 25)
 #define VIA_CR80_SECOND_CODEC_VALID	(1 << 27)
 #define VIA_CR80_BUSY			(1 << 24)
-#define VIA_CR80_READ_MODE		(1 << 23)
+#define VIA_CR83_BUSY			(1)
+#define VIA_CR83_FIRST_CODEC_VALID	(1 << 1)
+#define VIA_CR80_READ			(1 << 23)
 #define VIA_CR80_WRITE_MODE		0
-#define VIA_CR80_REG_IDX(idx)		(((idx) & 0x7E) << 16)
+#define VIA_CR80_REG_IDX(idx)		((((idx) & 0xFF) >> 1) << 16)
 
-struct via_info {
-	struct address_info sb_data;
-	struct address_info opl3_data;
-	struct pci_dev *pdev;
-	struct ac97_hwint ac97;
-	int mixer_oss_dev;
-	int have_ac97;
+/* h r puff n stuff */
+#define VIA_FMT_STEREO	0x01
+#define VIA_FMT_16BIT	0x02
+#define VIA_FMT_MASK	0x03
+#define VIA_DAC_SHIFT	0   
+#define VIA_ADC_SHIFT	4
+
+/* undocumented(?) values for setting rate, from Via's source */
+#define VIA_SET_RATE_IN		0x00320000 /* set input rate */
+#define VIA_SET_RATE_OUT	0x002c0000 /* set output rate */
+
+
+/* scatter-gather DMA table entry, exactly as passed to hardware */
+struct via_sgd_table {
+	u32 addr;
+	u32 count;	/* includes additional bits also */
 };
-static struct via_info		cards [MAX_CARDS];
-static unsigned			num_cards = 0;
+#define VIA_EOL (1 << 31)
+#define VIA_FLAG (1 << 30)
 
 
-static const struct {
-	int revision;
-	const char *rev_name;
-} via_chip_revs[] __initdata = {
-	{ 0x10, "A" },
-	{ 0x11, "B" },
-	{ 0x12, "C" },
-	{ 0x13, "D" },
-	{ 0x14, "E" },
-	{ 0x20, "H" },
+enum via_channel_states {
+	sgd_stopped = 0,
+	sgd_in_progress = 1,
 };
 
-static inline void via_ac97_write32 (struct pci_dev *pdev, int port, u32 data)
-{
 
-	int iobase = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
-	
-	outw ((u16)data,iobase+port);	
-	outw ((u16)(data>>16),iobase+port+2);	
-}
+struct via_sgd_data {
+	dma_addr_t handle;
+	volatile void *cpuaddr;
+};
 
-static inline u32 via_ac97_read32 (struct pci_dev *pdev, int port)
-{
-	int iobase = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
 
-	return
-		((u32)inw (iobase+port)) |
-		(((u32)inw (iobase+port+2)) << 16);
-}
+struct via_channel {
+	unsigned rate;		/* sample rate */
 
-/****************************************************************
- *
- * Intel Audio Codec '97 interface
- *
- *
- */
- 
-static inline void via_ac97_wait_idle (struct pci_dev *pdev)
-{
-	u32 tmp;
-	int counter = VIA_COUNTER_LIMIT;
+	u8 pcm_fmt;		/* VIA_PCM_FMT_xxx */
 	
-	DPRINTK ("ENTER\n");
-
-	assert (pdev != NULL);
+	atomic_t state;
+	atomic_t buf_in_use;
+	atomic_t next_buf;
 	
-	do {
-		tmp = via_ac97_read32 (pdev,VIA_AC97_CTRL);
-	} while ((tmp & VIA_CR80_BUSY) && (counter-- > 0));
-
-	DPRINTK ("EXIT%s\n", counter > 0 ? "" : ", counter limit reached");
-}
+	volatile struct via_sgd_table *sgtable;
+	dma_addr_t sgt_handle;
+	
+	struct via_sgd_data sgbuf [VIA_DMA_BUFFERS];
+	
+	wait_queue_head_t wait;
+	
+	long iobase;
+};
 
 
-static int via_ac97_read_reg (struct ac97_hwint *dev, u8 reg)
-{
-	u32 data;
-	struct via_info *card;
+/* data stored for each chip */
+struct via_info {
 	struct pci_dev *pdev;
+	long baseaddr;
 	
-	DPRINTK ("ENTER\n");
+	struct ac97_codec ac97;
+	spinlock_t lock;
+	int card_num;		/* unique card number, from 0 */
 
-	assert (dev != NULL);
-	assert (dev->driver_private != NULL);
+	int dev_dsp;		/* /dev/dsp index from register_sound_dsp() */
+	
+	unsigned rev_h : 1;
 
-	card = (struct via_info *) dev->driver_private;
-	pdev = card->pdev;
-	assert (pdev != NULL);
+	wait_queue_head_t open_wait;
+	int open_mode;
+	
+	struct via_channel ch_in;
+	struct via_channel ch_out;
+};
 
-	via_ac97_wait_idle (pdev);
-	data =	VIA_CR80_FIRST_CODEC | VIA_CR80_FIRST_CODEC_VALID |
-		VIA_CR80_READ_MODE | VIA_CR80_REG_IDX(reg);
-	via_ac97_write32 (pdev,VIA_AC97_CTRL,data);
-	via_ac97_wait_idle (pdev);
-	data = via_ac97_read32 (pdev,VIA_AC97_CTRL);
 
-#if 0
-	if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
-		DPRINTK ("EXIT, first codec not valid, returning -1\n");
-		return -1;
-	}
-#endif
+/* number of cards, used for assigning unique numbers to cards */
+static unsigned via_num_cards;
 
-	DPRINTK ("EXIT, returning %d\n", data & 0xFFFF);
-	return data & 0xFFFF;
-}
+static void *via_driver_data;
+static struct pci_dev *via_pdev;
 
+/****************************************************************
+ *
+ * compatibility
+ *
+ *
+ */
 
-static int via_ac97_write_reg (struct ac97_hwint *dev, u8 reg, u16 value)
+static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+					 dma_addr_t *dma_handle)
 {
-	u32 data;
-	struct via_info *card;
-	struct pci_dev *pdev;
-	
-	DPRINTK ("ENTER\n");
-
-	assert (dev != NULL);
-	assert (dev->driver_private != NULL);
+	void *virt_ptr;
 
-	card = (struct via_info *) dev->driver_private;
-	pdev = card->pdev;
-	assert (pdev != NULL);
+	virt_ptr = kmalloc(size, GFP_KERNEL);
+	*dma_handle = virt_to_bus(virt_ptr);
+	return virt_ptr;
+}
+#define pci_free_consistent(cookie, size, ptr, dma_ptr)	kfree(ptr)
+#define pci_map_single(cookie, address, size, dir)	virt_to_bus(address)
+#define pci_unmap_single(cookie, address, size, dir)
 
-	via_ac97_wait_idle (pdev);
-	data =	VIA_CR80_FIRST_CODEC | VIA_CR80_FIRST_CODEC_VALID |
-		VIA_CR80_WRITE_MODE | VIA_CR80_REG_IDX(reg) | value;
-	via_ac97_write32 (pdev,VIA_AC97_CTRL,data);
+#define pci_resource_start(dev,bar) \
+(((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_SPACE) ? \
+ ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_IO_MASK) : \
+ ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_MEM_MASK))
 
-#if 0
-	if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
-		DPRINTK ("EXIT, first codec invalid, returning -1\n");
-		return -1;
-	}
-#endif
+static inline int pci_enable_device (struct pci_dev *pdev) { return 0; }
 
-	DPRINTK ("EXIT, returning 0\n");
+static inline int pci_set_power_state(struct pci_dev *dev, int new_state)
+{
 	return 0;
 }
+ 
+/****************************************************************
+ *
+ * prototypes
+ *
+ *
+ */
 
+static int via_init_one (struct pci_dev *dev);
+static void via_remove_one (struct pci_dev *pdev);
 
-static int via_ac97_reset (struct ac97_hwint *dev)
-{
-	struct via_info *card;
-	struct pci_dev *pdev;
-	
-	DPRINTK ("ENTER\n");
+static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait);
+static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int via_dsp_open (struct inode *inode, struct file *file);
+static int via_dsp_release(struct inode *inode, struct file *file);
+
+static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
+static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
+static u8 via_ac97_wait_idle (struct via_info *card);
+
+static void via_chan_free (struct via_info *card, struct via_channel *chan);
+static void via_chan_clear (struct via_channel *chan);
+static void via_chan_pcm_fmt (struct via_info *card,
+			      struct via_channel *chan, int reset);
 
-	assert (dev != NULL);
-	assert (dev->driver_private != NULL);
 
-	card = (struct via_info *) dev->driver_private;
-	pdev = card->pdev;
-	assert (pdev != NULL);
-	
-	pci_write_config_word (pdev, PCI_COMMAND, PCI_COMMAND_IO);
+/****************************************************************
+ *
+ * Low-level base 0 register read/write helpers
+ *
+ *
+ */
 
-	DPRINTK ("EXIT, returning 0\n");
-	return 0;
+/**
+ *	via_chan_stop - Terminate DMA on specified PCM channel
+ *	@iobase: PCI base address for SGD channel registers
+ *
+ *	Terminate scatter-gather DMA operation for given
+ *	channel (derived from @iobase), if DMA is active.
+ *
+ *	Note that @iobase is not the PCI base address,
+ *	but the PCI base address plus an offset to
+ *	one of three PCM channels supported by the chip.
+ *
+ */
+ 
+static inline void via_chan_stop (int iobase)
+{
+	if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE)
+		outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL);
 }
 
 
-static struct via_info *via_ac97_find_card_for_mixer (int dev)
+/**
+ *	via_chan_status_clear - Clear status flags on specified DMA channel
+ *	@iobase: PCI base address for SGD channel registers
+ *
+ *	Clear any pending status flags for the given
+ *	DMA channel (derived from @iobase), if any
+ *	flags are asserted.
+ *
+ *	Note that @iobase is not the PCI base address,
+ *	but the PCI base address plus an offset to
+ *	one of three PCM channels supported by the chip.
+ *
+ */
+ 
+static inline void via_chan_status_clear (int iobase)
 {
-	int x;
-
-	DPRINTK ("ENTER\n");
-
-	for (x = 0; x < num_cards; x++)
-		if (cards[x].mixer_oss_dev == dev) {
-			DPRINTK ("EXIT, returning %p\n", cards + x);
-			return cards + x;
-		}
-
-	DPRINTK ("EXIT, returning 0\n");
-	return NULL;
+	u8 tmp = inb (iobase + VIA_PCM_STATUS);
+	
+	if (tmp != 0)
+		outb (tmp, iobase + VIA_PCM_STATUS);
 }
 
 
-static int
-via_ac97_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+/**
+ *	sg_begin - Begin recording or playback on a PCM channel
+ *	@chan: Channel for which DMA operation shall begin
+ *
+ *	Start scatter-gather DMA for the given channel.
+ *
+ */
+ 
+static inline void sg_begin (struct via_channel *chan)
 {
-	int rc;
-	struct via_info *card = via_ac97_find_card_for_mixer (dev);
-
-	DPRINTK ("ENTER\n");
-
-	if (card != NULL) {
-		rc = ac97_mixer_ioctl (&card->ac97, cmd, arg);
-		DPRINTK ("EXIT, returning %d\n", rc);
-		return rc;
-	}
-	
-	DPRINTK ("EXIT, returning -ENODEV\n");
-	return -ENODEV;
-
+	outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL);
 }
 
-static struct mixer_operations via_ac97_mixer_operations =
-{
-	"VIA82Cxxx",
-	"via82cxxxAC97Mixer",
-	via_ac97_default_mixer_ioctl
-};
 
-static int __init via_attach_ac97 (struct via_info *card)
+/**
+ *	via_chan_bufs_in_use - Number of buffers waiting to be consumed
+ *	@chan: Channel for which DMA buffers will be counted
+ *
+ *	Count the number of buffers waiting to be consumed.  For a
+ *	playback operation, this is the number of buffers which have
+ *	yet to be sent to the DAC.  For a recording operation, this
+ *	is the number of buffers waiting to be consumed by software
+ *	calling read() system call.
+ *
+ */
+ 
+static inline int via_chan_bufs_in_use (struct via_channel *chan)
 {
-	int mixer;
-	struct ac97_hwint *mdev;
+	return atomic_read(&chan->next_buf) -
+	       atomic_read(&chan->buf_in_use);
+}
 
-	DPRINTK ("ENTER\n");
 
-	assert (card != NULL);
+/**
+ *	via_chan_full - Check for no-free-buffers condition
+ *	@chan: Channel for which DMA full condition will be checked
+ *
+ *	Count the number of buffers waiting to be consumed, and return
+ *	true (non-zero) if no buffers are available to be filled on the
+ *	given DMA channel.
+ *
+ */
+ 
+static inline int via_chan_full (struct via_channel *chan)
+{
+	return (via_chan_bufs_in_use (chan) == VIA_DMA_BUFFERS);
+}
 
-	mdev = &card->ac97;
 
-	memset (mdev, 0, sizeof (*mdev));
-	mdev->reset_device = via_ac97_reset;
-	mdev->read_reg = via_ac97_read_reg;
-	mdev->write_reg = via_ac97_write_reg;
-	mdev->driver_private = (void *) card;
+/**
+ *	via_chan_empty - Check for no-buffers-in-use condition
+ *	@chan: Channel for which DMA empty condition will be checked
+ *
+ *	Count the number of buffers waiting to be consumed, and return
+ *	true (non-zero) if no buffers are currently in use.
+ *
+ */
+ 
+static inline int via_chan_empty (struct via_channel *chan)
+{
+	return (atomic_read(&chan->next_buf) ==
+	        atomic_read(&chan->buf_in_use));
+}
 
-	if (ac97_init (mdev)) {
-		printk (KERN_ERR PFX "Unable to init AC97\n");
-		DPRINTK ("EXIT, returning -1\n");
-		return -1;
-	}
-	mixer = sound_alloc_mixerdev ();
-	if (mixer < 0 || num_mixers >= MAX_MIXER_DEV) {
-		printk (KERN_ERR PFX "Unable to alloc mixerdev\n");
-		DPRINTK ("EXIT, returning -1\n");
-		return -1;
-	}
-	mixer_devs[mixer] = &via_ac97_mixer_operations;
-	card->mixer_oss_dev = mixer;
 
-	/* Some reasonable default values.  */
-	ac97_set_mixer (mdev, SOUND_MIXER_VOLUME, (85 << 8) | 85);
-	ac97_set_mixer (mdev, SOUND_MIXER_SPEAKER, 100);
-	ac97_set_mixer (mdev, SOUND_MIXER_PCM, (65 << 8) | 65);
-	ac97_set_mixer (mdev, SOUND_MIXER_CD, (65 << 8) | 65);
+/****************************************************************
+ *
+ * Miscellaneous debris
+ *
+ *
+ */
 
-	printk (KERN_INFO PFX "Initialized AC97 mixer\n");
-	
-	card->have_ac97 = mixer;
-	
-	DPRINTK ("EXIT, returning 0\n");
-	return 0;
-}
 
+/**
+ *	via_stop_everything - Stop all audio operations
+ *	@card: Private info for specified board
+ *
+ *	Stops all DMA operations and interrupts, and clear
+ *	any pending status bits resulting from those operations.
+ */
 
-static void via_unload_ac97 (struct via_info *card)
+static void via_stop_everything (struct via_info *card)
 {
 	DPRINTK ("ENTER\n");
 
 	assert (card != NULL);
 
-	if (card->have_ac97 >= 0)
-		sound_unload_mixerdev (card->have_ac97);
+	/*
+	 * terminate any existing operations on audio read/write channels
+	 */
+	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
 
+	/*
+	 * clear any existing stops / flags (sanity check mainly)
+	 */
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+	
+	/*
+	 * clear any enabled interrupt bits, reset to 8-bit mono PCM mode
+	 */
+	outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
 	DPRINTK ("EXIT\n");
 }
 
-#if 0
-#ifdef CONFIG_PROC_FS
 
-/****************************************************************
+/**
+ *	via_set_rate - Set PCM rate for given channel
+ *	@card: Private info for specified board
+ *	@rate: Desired PCM sample rate, in Khz
+ *	@inhale_deeply: Boolean.  If non-zero (true), the recording sample rate
+ *			is set.  If zero (false), the playback sample rate
+ *			is set.
  *
- * /proc/driver/via82cxxx/info
+ *	Sets the PCM sample rate for a channel.
  *
+ *	Values for @rate are clamped to a range of 4000 Khz through 48000 Khz,
+ *	due to hardware constraints.
  *
+ *	FIXME:  @inhale_deeply argument is ignored, and %AC97_PCM_FRONT_DAC_RATE
+ *	is the only rate which is really set.  This needs to be fixed when
+ *	recording support is added.
  */
-static int via_info_read_proc (char *page, char **start, off_t off,
-			       int count, int *eof, void *data)
-{
-#define YN(val,bit) (((val) & (bit)) ? "yes" : "no")
-
-	int len = 0, i;
-	u8 r40, r41, r42, r44;
-	
-	DPRINTK ("ENTER\n");
 
-	len += sprintf (page+len, VIA_CARD_NAME "\n\n");
-	
-	for (i = 0; i < num_cards; i++) {
-		pci_read_config_byte (cards[i].pdev, 0x40, &r40);
-		pci_read_config_byte (cards[i].pdev, 0x42, &r41);
-		pci_read_config_byte (cards[i].pdev, 0x42, &r42);
-		pci_read_config_byte (cards[i].pdev, 0x44, &r44);
-	
-		len += sprintf (page+len,
-			"40  AC97 Codec Ready: %s\n"
-			"    AC97 Codec Low-power: %s\n"
-			"    Secondary Codec Ready: %s\n"
-
-			"41  AC-Link Interface Enable: %s\n"
+static int via_set_rate (struct via_info *card, unsigned rate, int inhale_deeply)
+{
 
-			"42  Game port enabled: %s\n"
-			"    SoundBlaster enabled: %s\n"
-			"    FM enabled: %s\n"
-			"    MIDI enabled: %s\n"
-			
-			"44  AC-Link Interface Access: %s\n"
-			"    Secondary Codec Support: %s\n"
-			
-			"\n",
-			
-			YN (r40, VIA_CR40_AC97_READY),
-			YN (r40, VIA_CR40_AC97_LOW_POWER),
-			YN (r40, VIA_CR40_SECONDARY_READY),
-
-			YN (r41, VIA_CR41_ACLINK_ENABLE),
-
-			YN (r42, VIA_CR42_GAME_ENABLE),
-			YN (r42, VIA_CR42_SB_ENABLE),
-			YN (r42, VIA_CR42_FM_ENABLE),
-			YN (r42, VIA_CR42_MIDI_ENABLE),
+	DPRINTK ("ENTER, rate = %d, inhale = %s\n",
+		 rate, inhale_deeply ? "yes" : "no");
 
-			YN (r44, VIA_CR44_AC_LINK_ACCESS),
-			YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
-			
-			);
-	}
+	if (rate > 48000)		rate = 48000;
+	if (rate < 4000) 		rate = 4000;
 	
-	DPRINTK("EXIT, returning %d\n", len);
-	return len;
+	via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL,
+		(via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200) |
+		0x0200);
+	via_ac97_write_reg (&card->ac97, AC97_PCM_FRONT_DAC_RATE, rate);
+	via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL,
+		via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200);
 
-#undef YN
+	DPRINTK ("EXIT, returning 0\n");
+	return rate;
 }
 
 
-/****************************************************************
- *
- * /proc/driver/via82cxxx
+/**
+ *	via_set_adc_rate - Set PCM rate for recording channel
+ *	@card: Private info for specified board
+ *	@rate: Desired PCM sample rate, in Khz
  *
+ *	Sets the PCM sample rate for a recording channel.
  *
+ *	FIXME:  @inhale_deeply argument to via_set_rate is ignored, and %AC97_PCM_FRONT_DAC_RATE
+ *	is the only rate which is really set.  Thus, this function will
+ *	not work until via_set_rate is fixed.
  */
 
-static int __init via_init_proc (void)
+static inline int via_set_adc_rate (struct via_info *card, int rate)
 {
-	DPRINTK ("ENTER\n");
-
-
-	proc_mkdir ("driver/via_audio", 0);
-	create_proc_read_entry ("driver/via_audio/info", 0, 0, via_info_read_proc, NULL);
-	
-	DPRINTK("EXIT\n");
-	return 0;
+	return via_set_rate (card, rate, 1);
 }
 
 
+/**
+ *	via_set_dac_rate - Set PCM rate for playback channel
+ *	@card: Private info for specified board
+ *	@rate: Desired PCM sample rate, in Khz
+ *
+ *	Sets the PCM sample rate for a playback channel.
+ */
 
-static void __exit via_cleanup_proc (void)
+static inline int via_set_dac_rate (struct via_info *card, int rate)
 {
-	DPRINTK ("ENTER\n");
-	remove_proc_entry ("driver/via_audio/info", NULL);
-	remove_proc_entry ("driver/via_audio", NULL);
-	DPRINTK("EXIT\n");
+	return via_set_rate (card, rate, 0);
 }
 
 
-#else
-#endif /* CONFIG_PROC_FS */
-
-#endif /* if 0 */
-
-static inline int via_init_proc (void) { return 0; }
-static inline void via_cleanup_proc (void) {}
-
-
-
 /****************************************************************
  *
- * Legacy SoundBlaster Pro, FM support via OSS
+ * Channel-specific operations
  *
  *
  */
+ 
+/**
+ *	via_chan_init - Initialize PCM channel
+ *	@card: Private audio chip info
+ *	@chan: Channel to be initialized
+ *	@chan_ofs: Offset from PCI address, which determines the
+ *		   set of SGD registers to use.
+ *
+ *	Performs all the preparations necessary to begin
+ *	using a PCM channel.
+ *
+ *	Currently the preparations include allocating the
+ *	scatter-gather DMA table and buffers, setting the
+ *	PCM channel to a known state, and passing the
+ *	address of the DMA table to the hardware.
+ *
+ *	Note that special care is taken when passing the
+ *	DMA table address to hardware, because it was found
+ *	during driver development that the hardware did not
+ *	always "take" the address.
+ */
 
-static void via_attach_sb(struct address_info *hw_config)
+static int via_chan_init (struct via_info *card, 
+			  struct via_channel *chan, long chan_ofs)
 {
+	int i;
+	unsigned long flags;
+	
 	DPRINTK ("ENTER\n");
 
-	if(!sb_dsp_init(hw_config))
-		hw_config->slots[0] = -1;
+	memset (chan, 0, sizeof (*chan));
 
-	DPRINTK("EXIT\n");
-}
+	/* alloc DMA-able memory for scatter-gather table */
+	chan->sgtable = pci_alloc_consistent (card->pdev,
+		(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+		&chan->sgt_handle);
+	if (!chan->sgtable) {
+		printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");
+		DPRINTK ("EXIT\n");
+		return -ENOMEM;
+	}
+	
+	memset ((void*)chan->sgtable, 0, 
+		(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS));
 
+	/* alloc DMA-able memory for scatter-gather buffers */
+	for (i = 0; i < VIA_DMA_BUFFERS; i++) {
+		chan->sgbuf[i].cpuaddr =
+			pci_alloc_consistent (card->pdev, VIA_DMA_BUF_SIZE,
+					      &chan->sgbuf[i].handle);
+
+		if (!chan->sgbuf[i].cpuaddr)
+			goto err_out_nomem;
+
+		if (i < (VIA_DMA_BUFFERS - 1))
+			chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG);
+		else
+			chan->sgtable[i].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL);
+		chan->sgtable[i].addr = cpu_to_le32 (chan->sgbuf[i].handle);
 
-static int via_probe_sb(struct address_info *hw_config)
-{
-	DPRINTK ("ENTER\n");
+#ifndef VIA_NDEBUG
+		memset (chan->sgbuf[i].cpuaddr, 0xBC, VIA_DMA_BUF_SIZE);
+#endif
 
-	if (check_region(hw_config->io_base, 16))
-	{
-		printk(KERN_DEBUG PFX "SBPro port 0x%x is already in use\n",
-		       hw_config->io_base);
-		return 0;
+#if 1
+		DPRINTK ("dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)\n",
+			 i, (long)chan->sgbuf[i].handle,
+			 (long)chan->sgtable[i].addr,
+			 virt_to_phys(chan->sgbuf[i].cpuaddr),
+			 chan->sgbuf[i].cpuaddr);
+#endif
 	}
-	DPRINTK("EXIT after sb_dsp_detect\n");
-	return sb_dsp_detect(hw_config, 0, 0);
-}
 
+	init_waitqueue_head (&chan->wait);
 
-static void via_unload_sb(struct address_info *hw_config)
-{
-	DPRINTK ("ENTER\n");
-
-	if(hw_config->slots[0] != -1)
-		sb_dsp_unload(hw_config, 1);
+	chan->pcm_fmt = VIA_PCM_FMT_MASK;
+	chan->iobase = card->baseaddr + chan_ofs;
+	
+	spin_lock_irqsave (&card->lock, flags);
 
-	DPRINTK("EXIT\n");
-}
+	/* stop any existing channel output */
+	via_chan_clear (chan);
+	via_chan_status_clear (chan->iobase);
+	via_chan_pcm_fmt (card, chan, 1);
+
+	spin_unlock_irqrestore (&card->lock, flags);
+
+	/* set location of DMA-able scatter-gather info table */
+	DPRINTK("outl (0x%X, 0x%04lX)\n",
+		cpu_to_le32 (chan->sgt_handle),
+		card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR);
+
+	via_ac97_wait_idle (card);
+	outl (cpu_to_le32 (chan->sgt_handle),
+	      card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR);
+	udelay (20);
+	via_ac97_wait_idle (card);
+	
+	DPRINTK("inl (0x%lX) = %x\n",
+		chan->iobase + VIA_PCM_TABLE_ADDR,
+		inl(chan->iobase + VIA_PCM_TABLE_ADDR));
 
+	DPRINTK ("EXIT\n");
+	return 0;
 
-static const struct {
-	int sb_irq,
-	    sb_dma,
-	    midi_base,
-	    sb_io_base;
-} via_pnp_data[] __initdata = {
-	{ 5, 0, 0x300, 0x220 },
-	{ 7, 1, 0x310, 0x240 },
-	{ 9, 2, 0x320, 0x260 },
-	{ 10,3, 0x330, 0x280 },
-};
+err_out_nomem:
+	printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");
+	via_chan_free (card, chan);
+	DPRINTK ("EXIT\n");
+	return -ENOMEM;
+}
 
 
-/****************************************************************
- *
- * Chip setup and kernel registration
+/**
+ *	via_chan_free - Release a PCM channel
+ *	@card: Private audio chip info
+ *	@chan: Channel to be released
  *
+ *	Performs all the functions necessary to clean up
+ *	an initialized channel.
  *
+ *	Currently these functions include disabled any
+ *	active DMA operations, setting the PCM channel
+ *	back to a known state, and releasing any allocated
+ *	sound buffers.
  */
-
-static int __init via82cxxx_install (struct pci_dev *pcidev)
+ 
+static void via_chan_free (struct via_info *card, struct via_channel *chan)
 {
-	int sb_io_base;
-	int sb_irq;
-	int sb_dma;
-	int midi_base, rc;
-	u8 tmp8;
-	struct via_info *card = &cards[num_cards];
+	int i;
+	unsigned long flags;
 	
 	DPRINTK ("ENTER\n");
-
-	card->pdev = pcidev;
-	card->have_ac97 = -1;
 	
-	/* turn off legacy features, if not already */
-	pci_read_config_byte (pcidev, VIA_FUNC_ENABLE, &tmp8);
-	tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
-		  VIA_CR42_FM_ENABLE);
-	pci_write_config_byte (pcidev, VIA_FUNC_ENABLE, tmp8);
+	synchronize_irq();
 
-	/* 
-	 * try to init AC97 mixer device
-	 */
-	rc = via_attach_ac97 (card);
-	if (rc) {
-		printk (KERN_WARNING PFX
-			"AC97 init failed, SB legacy mode only\n");
-	}
-	
-	/* turn on legacy features */
-	pci_read_config_byte (pcidev, VIA_FUNC_ENABLE, &tmp8);
-	tmp8 |= VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
-		VIA_CR42_FM_ENABLE;
-	pci_write_config_byte (pcidev, VIA_FUNC_ENABLE, tmp8);
-
-	/* read legacy PNP info byte */
-	pci_read_config_byte (pcidev, VIA_PNP_CONTROL, &tmp8);
-	pci_write_config_byte (pcidev, VIA_PNP_CONTROL, tmp8);
-	
-	sb_irq = via_pnp_data[((tmp8 >> 6) & 0x03)].sb_irq;
-	sb_dma = via_pnp_data[((tmp8 >> 4) & 0x03)].sb_dma;
-	midi_base = via_pnp_data[((tmp8 >> 2) & 0x03)].midi_base;
-	sb_io_base = via_pnp_data[(tmp8 & 0x03)].sb_io_base;
-
-	udelay(100);
-	
-	printk(KERN_INFO PFX "legacy "
-	       "MIDI: 0x%X, SB: 0x%X / %d IRQ / %d DMA\n",
-		midi_base, sb_io_base, sb_irq, sb_dma);
-		
-	card->sb_data.name = VIA_CARD_NAME;
-	card->sb_data.card_subtype = MDL_SBPRO;
-	card->sb_data.io_base = sb_io_base;
-	card->sb_data.irq = sb_irq;
-	card->sb_data.dma = sb_dma;
-	
-	/* register legacy SoundBlaster Pro */
-	if (!via_probe_sb (&card->sb_data)) {
-		printk (KERN_ERR PFX
-			"SB probe @ 0x%X failed, aborting\n",
-			sb_io_base);
-		DPRINTK ("EXIT, returning -1\n");
-		return -1;
-	}
-	via_attach_sb (&card->sb_data);
-
-	card->opl3_data.name = card->sb_data.name;
-	card->opl3_data.io_base = midi_base;
-	card->opl3_data.irq = -1;
-	
-	/* register legacy MIDI */
-	if (!probe_uart401 (&card->opl3_data)) {
-		printk (KERN_WARNING PFX
-			"MIDI probe @ 0x%X failed, continuing\n",
-			midi_base);
-		card->opl3_data.io_base = 0;
-	} else {
-		attach_uart401 (&card->opl3_data);
+	spin_lock_irqsave (&card->lock, flags);
+
+	/* stop any existing channel output */
+	via_chan_stop (chan->iobase);
+	via_chan_status_clear (chan->iobase);
+	via_chan_pcm_fmt (card, chan, 1);
+
+	spin_unlock_irqrestore (&card->lock, flags);
+
+	/* zero location of DMA-able scatter-gather info table */
+	via_ac97_wait_idle(card);
+	outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);
+
+	for (i = 0; i < VIA_DMA_BUFFERS; i++)
+		if (chan->sgbuf[i].cpuaddr) {
+			pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE,
+					     (void*)chan->sgbuf[i].cpuaddr,
+					     chan->sgbuf[i].handle);
+			chan->sgbuf[i].cpuaddr = NULL;
+			chan->sgbuf[i].handle = 0;
+		}
+
+	if (chan->sgtable) {
+		pci_free_consistent (card->pdev, 
+			(sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS),
+			(void*)chan->sgtable, chan->sgt_handle);
+		chan->sgtable = NULL;
 	}
 
-	num_cards++;	
-	DPRINTK ("EXIT, returning 0\n");
-	return 0;
+	DPRINTK ("EXIT\n");
 }
 
 
-/*
- * 	This loop walks the PCI configuration database and finds where
- *	the sound cards are.
- *
- *	Note - only a single PCI scan occurs, eliminating possibility
- *	of multiple audio chips
+/**
+ *	via_chan_pcm_fmt - Update PCM channel settings
+ *	@card: Private audio chip info
+ *	@chan: Channel to be updated
+ *	@reset: Boolean.  If non-zero, channel will be reset
+ *		to 8-bit mono mode.
  *
+ *	Stores the settings of the current PCM format,
+ *	8-bit or 16-bit, and mono/stereo, into the
+ *	hardware settings for the specified channel.
+ *	If @reset is non-zero, the channel is reset
+ *	to 8-bit mono mode.  Otherwise, the channel
+ *	is set to the values stored in the channel
+ *	information struct @chan.
  */
  
-static int __init probe_via82cxxx (void)
+static void via_chan_pcm_fmt (struct via_info *card,
+			      struct via_channel *chan, int reset)
 {
-	struct pci_dev *pcidev = NULL;
+	DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n",
+		 chan->pcm_fmt, reset ? "yes" : "no");
 
-	DPRINTK ("ENTER\n");
+	assert (card != NULL);
+	assert (chan != NULL);
 
-	pcidev = pci_find_device (PCI_VENDOR_ID_VIA,
-				  PCI_DEVICE_ID_VIA_82C686_5, NULL);
+	if (reset)
+		/* reset to 8-bit mono mode */
+		chan->pcm_fmt = 0;
+	
+	/* enable interrupts on FLAG and EOL */
+	chan->pcm_fmt |= VIA_CHAN_TYPE_MASK;
+	
+	/* set interrupt select bits where applicable (PCM & FM out channels) */
+	if (chan == &card->ch_out)
+		chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
+
+	outb (chan->pcm_fmt, chan->iobase + 2);
+
+	DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
+		 chan->pcm_fmt,
+		 inb (chan->iobase + 2));
+}
+
+
+/**
+ *	via_chan_clear - Stop DMA channel operation, and reset pointers
+ *	@chan: Channel to be cleared
+ *
+ *	Call via_chan_stop to halt DMA operations, and then resets
+ *	all software pointers which track DMA operation.
+ */
+
+static void via_chan_clear (struct via_channel *chan)
+{
+	via_chan_stop (chan->iobase);
+	atomic_set (&chan->state, sgd_stopped);
+	atomic_set (&chan->buf_in_use, 0);
+	atomic_set (&chan->next_buf, 0);
+}
+
+
+/**
+ *	via_chan_set_speed - Set PCM sample rate for given channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose sample rate will be adjusted
+ *	@val: New sample rate, in Khz
+ *
+ *	Helper function for the %SNDCTL_DSP_SPEED ioctl.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when the %SNDCTL_DSP_SPEED is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_set_rate to set the audio hardware
+ *	to the new rate.
+ */
+
+static int via_chan_set_speed (struct via_info *card,
+			       struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, requested rate = %d\n", val);
+
+	via_chan_clear (chan);
+
+	val = via_set_rate (card, val, chan == &card->ch_in);	
+	
+	DPRINTK ("EXIT, returning %d\n", val);
+	return val;
+}
+
+
+/**
+ *	via_chan_set_fmt - Set PCM sample size for given channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose sample size will be adjusted
+ *	@val: New sample size, use the %AFMT_xxx constants
+ *
+ *	Helper function for the %SNDCTL_DSP_SETFMT ioctl.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when the %SNDCTL_DSP_SETFMT is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_chan_pcm_fmt to set the audio hardware
+ *	to the new sample size, either 8-bit or 16-bit.
+ */
+
+static int via_chan_set_fmt (struct via_info *card,
+			     struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, val=%s\n",
+		 val == AFMT_U8 ? "AFMT_U8" :
+	 	 val == AFMT_S16_LE ? "AFMT_S16_LE" :
+		 "unknown");
+
+	via_chan_clear (chan);
+	
+	switch (val) {
+	case AFMT_U8:
+		chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT;
+		via_chan_pcm_fmt (card, chan, 0);
+		break;
+	case AFMT_S16_LE:
+		chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
+		via_chan_pcm_fmt (card, chan, 0);
+		break;
+	default:
+		printk (KERN_WARNING PFX "unknown AFMT\n");
+		val = -EINVAL;
+		break;
+	}
+	
+	DPRINTK ("EXIT, returning %d\n", val);
+	return val;
+}
+
+
+/**
+ *	via_chan_set_stereo - Enable or disable stereo for a DMA channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose stereo setting will be adjusted
+ *	@val: New sample size, use the %AFMT_xxx constants
+ *
+ *	Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_chan_pcm_fmt to set the audio hardware
+ *	to enable or disable stereo.
+ */
+
+static int via_chan_set_stereo (struct via_info *card,
+			        struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, channels = %d\n", val);
+
+	via_chan_clear (chan);
+	
+	switch (val) {
+
+	/* mono */
+	case 1:
+		chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO;
+		via_chan_pcm_fmt (card, chan, 0);
+		break;
+
+	/* stereo */
+	case 2:
+		chan->pcm_fmt |= VIA_PCM_FMT_STEREO;
+		via_chan_pcm_fmt (card, chan, 0);
+		break;
+
+	/* unknown */
+	default:
+		printk (KERN_WARNING PFX "unknown number of channels\n");
+		val = -EINVAL;
+		break;
+	}
+	
+	DPRINTK ("EXIT, returning %d\n", val);
+	return val;
+}
+
+
+#if 0
+/**
+ *	via_chan_dump_bufs - Display DMA table contents
+ *	@chan: Channel whose DMA table will be displayed
+ *
+ *	Debugging function which displays the contents of the
+ *	scatter-gather DMA table for the given channel @chan.
+ */
+
+static void via_chan_dump_bufs (struct via_channel *chan)
+{
+	int i;
+	
+	for (i = 0; i < VIA_DMA_BUFFERS; i++) {
+		DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
+			 i, chan->sgtable[i].addr,
+			 chan->sgtable[i].count & 0x00FFFFFF,
+			 chan->sgtable[i].count & VIA_FLAG ? 1 : 0,
+			 chan->sgtable[i].count & VIA_EOL ? 1 : 0);
+	}
+	DPRINTK ("buf_in_use = %d, nextbuf = %d\n",
+		 atomic_read (&chan->buf_in_use),
+		 atomic_read (&chan->next_buf));
+}
+#endif
+
+
+/****************************************************************
+ *
+ * Interface to ac97-codec module
+ *
+ *
+ */
+ 
+/**
+ *	via_ac97_wait_idle - Wait until AC97 codec is not busy
+ *	@card: Private info for specified board
+ *
+ *	Sleep until the AC97 codec is no longer busy.
+ *	Returns the final value read from the SGD
+ *	register being polled.
+ */
+
+static u8 via_ac97_wait_idle (struct via_info *card)
+{
+	u8 tmp8;
+	int counter = VIA_COUNTER_LIMIT;
+	
+	DPRINTK ("ENTER/EXIT\n");
+
+	assert (card != NULL);
+	assert (card->pdev != NULL);
+	
+	do {
+		if (current->need_resched)
+			schedule ();
+		else
+			udelay (10);
+
+		spin_lock_irq (&card->lock);
+		tmp8 = inb (card->baseaddr + 0x83);
+		spin_unlock_irq (&card->lock);
+	} while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0));
+
+	return tmp8;
+}
+
+
+/**
+ *	via_ac97_read_reg - Read AC97 standard register
+ *	@codec: Pointer to generic AC97 codec info
+ *	@reg: Index of AC97 register to be read
+ *
+ *	Read the value of a single AC97 codec register,
+ *	as defined by the Intel AC97 specification.
+ *
+ *	Defines the standard AC97 read-register operation
+ *	required by the kernel's ac97_codec interface.
+ *
+ *	Returns the 16-bit value stored in the specified
+ *	register.
+ */
+
+static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg)
+{
+	u32 data;
+	struct via_info *card;
+	int counter;
+	
+	DPRINTK ("ENTER\n");
+
+	assert (codec != NULL);
+	assert (codec->private_data != NULL);
+
+	card = codec->private_data;
+
+	data = (reg << 16) | VIA_CR80_READ;
+
+	outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
+	udelay (20);
+
+	for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
+		if (inl (card->baseaddr + 0x80) & VIA_CR80_VALID)
+			goto out;
+		udelay(10);
+		if (current->need_resched)
+			schedule ();
+	}
+
+	printk (KERN_WARNING PFX "timeout while reading AC97 codec\n");
+	goto err_out;
+
+out:
+	data = inl (card->baseaddr + 0x80);
+	outb (0x02, card->baseaddr + 0x83);
+
+	if (((data & 0x007F0000) >> 16) == reg) {
+		DPRINTK ("EXIT, success, data=0x%x, retval=0x%x\n",
+			 data, data & 0x0000FFFF);
+		return data & 0x0000FFFF;
+	}
+
+	DPRINTK ("WARNING: not our index: reg=0x%x, newreg=0x%x\n",
+		 reg, ((data & 0x007F0000) >> 16));
+
+err_out:
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/**
+ *	via_ac97_write_reg - Write AC97 standard register
+ *	@codec: Pointer to generic AC97 codec info
+ *	@reg: Index of AC97 register to be written
+ *	@value: Value to be written to AC97 register
+ *
+ *	Write the value of a single AC97 codec register,
+ *	as defined by the Intel AC97 specification.
+ *
+ *	Defines the standard AC97 write-register operation
+ *	required by the kernel's ac97_codec interface.
+ */
+
+static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value)
+{
+	u32 data;
+	struct via_info *card;
+	int counter;
+	
+	DPRINTK ("ENTER\n");
+
+	assert (codec != NULL);
+	assert (codec->private_data != NULL);
+
+	card = codec->private_data;
+
+	data = (reg << 16) + value;
+	outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
+	udelay (20);
+
+	for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
+		if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0)
+			goto out;
+		udelay(10);
+		if (current->need_resched)
+			schedule ();
+	}
+	
+	printk (KERN_WARNING PFX "timeout after AC97 codec write\n");
+
+out:
+	DPRINTK ("EXIT\n");
+}
+
+
+static int via_mixer_open (struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct via_info *card;
+	
+	DPRINTK ("ENTER\n");
+	
+	MOD_INC_USE_COUNT;
+
+	assert (via_driver_data != NULL);
+			
+	card = via_driver_data;
+	if (card->ac97.dev_mixer == minor)
+		goto match;
+
+	DPRINTK ("EXIT, returning -ENODEV\n");
+	MOD_DEC_USE_COUNT;
+	return -ENODEV;
+
+match:
+	file->private_data = &card->ac97;
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
+			    unsigned long arg)
+{
+	struct ac97_codec *codec = file->private_data;
+	int rc;
+
+	assert (codec != NULL);
+	
+	rc = codec->mixer_ioctl(codec, cmd, arg);
+	
+	return rc;
+}
+
+
+static loff_t via_llseek(struct file *file, loff_t offset, int origin)
+{
+	DPRINTK ("ENTER\n");
+
+	DPRINTK("EXIT, returning -ESPIPE\n");
+	return -ESPIPE;
+}
+
+
+
+static int via_mixer_release(struct inode *inode, struct file *file)
+{
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+
+static struct file_operations via_mixer_fops = {
+	open:		via_mixer_open,
+	release:	via_mixer_release,
+	llseek:		via_llseek,
+	ioctl:		via_mixer_ioctl,
+};
+
+
+#if 0 /* values reasoned from debugging dumps of via's driver */
+static struct {
+	u8 reg;
+	u16 data;
+} mixer_init_vals[] __devinitdata = {
+	{ 0x2, 0x404 },
+	{ 0x4, 0x404 },
+	{ 0x6, 0x404 },
+	{ 0x18, 0x404 },
+	{ 0x10, 0x404 },
+	{ 0x1a, 0x404 },
+	{ 0x1c, 0x404 },
+	{ 0x1a, 0x404 },
+	{ 0x1c, 0xc0c },
+	{ 0x12, 0x808 },
+	{ 0x10, 0x808 },
+	{ 0xe, 0x2 },
+	{ 0x2, 0x808 },
+	{ 0x18, 0x808 },
+};
+#endif
+
+
+static int __init via_ac97_reset (struct via_info *card)
+{
+	struct pci_dev *pdev = card->pdev;
+	u8 tmp8;
+	u16 tmp16;
+	
+	DPRINTK ("ENTER\n");
+
+	assert (pdev != NULL);
+	
+#ifndef NDEBUG
+	{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+
+		spin_lock_irq (&card->lock);
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+		spin_unlock_irq (&card->lock);
+		
+	}
+#endif
+
+        /*
+         * reset AC97 controller: enable, disable, enable
+         * pause after each command for good luck
+         */
+        pci_write_config_byte (pdev, VIA_ACLINK_CTRL, VIA_CR41_AC97_ENABLE |
+                               VIA_CR41_AC97_RESET | VIA_CR41_AC97_WAKEUP);
+        udelay (100);
+ 
+        pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0);
+        udelay (100);
+ 
+        pci_write_config_byte (pdev, VIA_ACLINK_CTRL,
+			       VIA_CR41_AC97_ENABLE | VIA_CR41_PCM_ENABLE |
+                               VIA_CR41_VRA | VIA_CR41_AC97_RESET);
+        udelay (100);
+
+#if 0 /* this breaks on K7M */
+	/* disable legacy stuff */
+	pci_write_config_byte (pdev, 0x42, 0x00);
+	udelay(10);
+#endif
+
+	/* route FM trap to IRQ, disable FM trap */
+	pci_write_config_byte (pdev, 0x48, 0x05);
+	udelay(10);
+	
+	/* disable all codec GPI interrupts */
+	outl (0, pci_resource_start (pdev, 0) + 0x8C);
+
+	/* enable variable rate */
+	tmp16 = via_ac97_read_reg (&card->ac97, 0x2A);
+	via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01);
+	
+	/* boost headphone vol if disabled */
+	tmp16 = via_ac97_read_reg (&card->ac97, AC97_HEADPHONE_VOL);
+	if (tmp16 == 0)
+		via_ac97_write_reg (&card->ac97, AC97_HEADPHONE_VOL, 0x1F1F);
+	
+	pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8);
+	if ((tmp8 & (VIA_CR41_AC97_ENABLE | VIA_CR41_AC97_RESET)) == 0) {
+		printk (KERN_ERR PFX "cannot enable AC97 controller, aborting\n");
+		DPRINTK ("EXIT, tmp8=%X, returning -ENODEV\n", tmp8);
+		return -ENODEV;
+	}
+	
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static int __init via_ac97_init (struct via_info *card)
+{
+	int rc;
+	u16 tmp16;
+	
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	memset (&card->ac97, 0, sizeof (card->ac97));	
+	card->ac97.private_data = card;
+	card->ac97.codec_read = via_ac97_read_reg;
+	card->ac97.codec_write = via_ac97_write_reg;
+	
+	card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
+	if (card->ac97.dev_mixer < 0) {
+		printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
+		DPRINTK("EXIT, returning -EIO\n");
+		return -EIO;
+	}
+
+	rc = via_ac97_reset (card);
+	if (rc) {
+		printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n");
+		goto err_out;
+	}
+
+	if (ac97_probe_codec (&card->ac97) == 0) {
+		printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n");
+		rc = -EIO;
+		goto err_out;
+	}
+
+	tmp16 = via_ac97_read_reg (&card->ac97, 0x2A);
+	via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01);
+	
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out:
+	unregister_sound_mixer (card->ac97.dev_mixer);
+	DPRINTK("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static void via_ac97_cleanup (struct via_info *card)
+{
+	DPRINTK("ENTER\n");
+	
+	assert (card != NULL);
+	assert (card->ac97.dev_mixer >= 0);
+	
+	unregister_sound_mixer (card->ac97.dev_mixer);
+
+	DPRINTK("EXIT\n");
+}
+
+
+
+/****************************************************************
+ *
+ * Interrupt-related code
+ *
+ */
+ 
+static inline void via_interrupt_write (struct via_channel *chan)
+{
+	assert (atomic_read(&chan->buf_in_use) <
+		atomic_read(&chan->next_buf));
+
+	/* XXX sanity check: read h/w counter to 
+	 * ensure no lost frames */
+
+	atomic_inc (&chan->buf_in_use);
+
+	/* if SG ptr catches up with userland ptr, stop playback */
+	if (atomic_read(&chan->buf_in_use) == atomic_read(&chan->next_buf)) {
+		atomic_set (&chan->state, sgd_stopped);
+		via_chan_stop (chan->iobase);
+	}
+
+	/* wake up anybody listening */
+	if (waitqueue_active (&chan->wait))
+		wake_up (&chan->wait);
+}
+
+
+
+static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct via_info *card = dev_id;
+	struct via_channel *chan;
+	u8 status;
+
+	status = inb (card->baseaddr + 0x00);
+	if (status) {
+		assert (card->open_mode & FMODE_WRITE);
+		
+		chan = &card->ch_out;
+		
+		if (status & VIA_SGD_FLAG) {
+			assert ((status & VIA_SGD_EOL) == 0);
+			outb (VIA_SGD_FLAG, chan->iobase + 0x00);
+			DPRINTK("FLAG intr, status=0x%02X\n", status);
+			via_interrupt_write (chan);
+		}
+		
+		if (status & VIA_SGD_EOL) {
+			assert ((status & VIA_SGD_FLAG) == 0);
+			outb (VIA_SGD_EOL, chan->iobase + 0x00);
+			DPRINTK("EOL intr, status=0x%02X\n", status);
+			via_interrupt_write (chan);
+		}
+		
+		if (status & VIA_SGD_STOPPED) {
+			outb (VIA_SGD_STOPPED, chan->iobase + 0x00);
+			DPRINTK("STOPPED intr, status=0x%02X\n", status);
+		}
+
+#if 0
+		via_chan_dump_bufs (&card->ch_out);
+#endif
+	}
+}
+
+
+static void via_interrupt_disable (struct via_info *card)
+{
+	u8 tmp8;
+	unsigned long flags;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	
+	spin_lock_irqsave (&card->lock, flags);
+
+	pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
+	if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) {
+		tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;
+		pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);
+	}
+
+	outb (inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE) &
+	      VIA_INT_DISABLE_MASK,
+	      card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);
+	outb (inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE) &
+	      VIA_INT_DISABLE_MASK,
+	      card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);
+	outb (inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE) &
+	      VIA_INT_DISABLE_MASK,
+	      card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);
+
+	spin_unlock_irqrestore (&card->lock, flags);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static int via_interrupt_init (struct via_info *card)
+{
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	assert (card->pdev != NULL);
+	
+	/* check for sane IRQ number. can this ever happen? */
+	if (card->pdev->irq < 2) {
+		printk (KERN_ERR PFX "insane IRQ %d, aborting\n",
+			card->pdev->irq);
+		DPRINTK ("EXIT, returning -EIO\n");
+		return -EIO;
+	}
+	
+	if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+		printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
+			card->pdev->irq);
+		DPRINTK ("EXIT, returning -EBUSY\n");
+		return -EBUSY;
+	}
+
+	/* we don't want interrupts until we're opened */
+	via_interrupt_disable (card);
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static void via_interrupt_cleanup (struct via_info *card)
+{
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	assert (card->pdev != NULL);
+
+	via_interrupt_disable (card);
+
+	free_irq (card->pdev->irq, card);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/****************************************************************
+ *
+ * OSS DSP device
+ *
+ */
+ 
+static struct file_operations via_dsp_fops = {
+	open:		via_dsp_open,
+	release:	via_dsp_release,
+	read:		via_dsp_read,
+	write:		via_dsp_write,
+	poll:		via_dsp_poll,
+	llseek: 	via_llseek,
+	ioctl:		via_dsp_ioctl,
+};
+
+
+static int __init via_dsp_init (struct via_info *card)
+{
+	u8 tmp8;
+	
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	/* turn off legacy features, if not already */
+	pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);
+	tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
+		  VIA_CR42_FM_ENABLE);
+	pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
 
-	if (!pcidev || via82cxxx_install (pcidev) != 0) {
-		printk (KERN_ERR PFX "audio init failed\n");
-		DPRINTK ("EXIT, returning -1\n");
-		return -1;
-	}
+	via_stop_everything (card);
 
+	card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1);
+	if (card->dev_dsp < 0) {
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		return -ENODEV;
+	}
 	DPRINTK ("EXIT, returning 0\n");
 	return 0;
 }
 
 
-/*
- *	This function is called when the user or kernel loads the 
- *	module into memory.
- */
+static void via_dsp_cleanup (struct via_info *card)
+{
+	DPRINTK ("ENTER\n");
 
+	assert (card != NULL);
+	assert (card->dev_dsp >= 0);
+
+	via_stop_everything (card);
+
+	unregister_sound_dsp (card->dev_dsp);
+
+	DPRINTK ("EXIT\n");
+}
 
-static int init_via82cxxx_module(void)
+
+static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
-	u8 tmp;
-	int i;
-	const char *rev = "unknown!";
+	struct via_info *card;
+
+	DPRINTK ("ENTER\n");
 	
-	memset (cards, 0, sizeof (cards));
+	assert (file != NULL);
+	assert (buffer != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	DPRINTK("EXIT, returning -EINVAL\n");
+	return -EINVAL;
+}
+
+
+
+#define sgcount(n) (sgtable[(n)].count & 0x00FFFFFF)
+#define NEXTBUF (atomic_read(&chan->next_buf) % VIA_DMA_BUFFERS)
+#define BUF_IN_USE (atomic_read(&chan->buf_in_use) % VIA_DMA_BUFFERS)
+#define STATE_STOPPED (atomic_read (state) == sgd_stopped)
+#define STATE_STARTED (atomic_read (state) == sgd_in_progress)
+static ssize_t via_dsp_do_write (struct via_info *card,
+				 const char *userbuf, size_t count,
+				 int non_blocking)
+{
+	const char *orig_userbuf = userbuf;
+	struct via_channel *chan = &card->ch_out;
+	volatile struct via_sgd_table *sgtable = chan->sgtable;
+	atomic_t *state = &chan->state;
+	size_t size;
+	int nextbuf, prevbuf, n, realcount;
+	ssize_t rc;
+	
+	while (count > 0) {
+		if (current->need_resched)
+			schedule ();
+
+		spin_lock_irq (&card->lock);
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+		spin_unlock_irq (&card->lock);
+		
+		size = (count < VIA_DMA_BUF_SIZE) ? count : VIA_DMA_BUF_SIZE;
+
+		/* case 1: SGD not active, list is ours for the mangling */
+
+		if (STATE_STOPPED) {
+			DPRINTK ("case 1\n");
+
+			if (copy_from_user ((void*)chan->sgbuf[0].cpuaddr,
+					     userbuf, size))
+				return -EFAULT;
+
+			assert (sgtable[0].addr == cpu_to_le32 (chan->sgbuf[0].handle));
+			sgtable[0].count = size | VIA_FLAG;
+
+			atomic_set (state, sgd_in_progress);
+			atomic_set (&chan->buf_in_use, 0);
+			atomic_set (&chan->next_buf, 1);
+			
+			count -= size;
+			userbuf += size;
+
+			spin_lock_irq (&card->lock);
+			sg_begin (chan);
+			spin_unlock_irq (&card->lock);
+
+			continue;
+		}
+
+		nextbuf = NEXTBUF;
+		if (nextbuf)
+			prevbuf = nextbuf - 1;
+		else
+			prevbuf = VIA_DMA_BUFFERS - 1;
+
+		/* case 2: if final buffer is (a) a fragment, and (b) not
+		 * currently being consumed by the SGD engine, then append
+		 * as much data as possible to the fragment. */
+
+		realcount = sgcount(prevbuf);
+		if (STATE_STARTED && (prevbuf != BUF_IN_USE) &&
+		    (realcount < VIA_DMA_BUF_SIZE)) {
+			DPRINTK ("case 2\n");
+			DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d, rc=%d\n",
+				 atomic_read (state),
+				 BUF_IN_USE,
+				 nextbuf,
+				 prevbuf,
+				 prevbuf /* n */,
+				 realcount);
+
+			n = prevbuf;
+
+			if ((VIA_DMA_BUF_SIZE - realcount) < size)
+				size = VIA_DMA_BUF_SIZE - realcount;
+
+			if (copy_from_user ((void*)(chan->sgbuf[n].cpuaddr +
+					     realcount),
+					     userbuf, size))
+				return -EFAULT;
+
+			/* slack way to try and prevent races */
+			if (prevbuf == BUF_IN_USE || !STATE_STARTED)
+				continue;
+
+			assert (sgtable[n].addr == cpu_to_le32 (chan->sgbuf[n].handle));
+			if (n == (VIA_DMA_BUFFERS - 1))
+				sgtable[n].count = (realcount + size) | VIA_EOL;
+			else
+				sgtable[n].count = (realcount + size) | VIA_FLAG;
+
+			count -= size;
+			userbuf += size;
+			continue;
+		}
+
+		/* case 3: if there are buffers left, use one
+		 * XXX needs more review for possible races */
+
+		else if (STATE_STARTED && !via_chan_full (chan)) {
+			DPRINTK ("case 3\n");
+			DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d\n",
+				 atomic_read (state),
+				 BUF_IN_USE,
+				 nextbuf,
+				 prevbuf,
+				 nextbuf /* n */);
+
+			n = nextbuf;
+
+			if (copy_from_user ((void*)chan->sgbuf[n].cpuaddr,
+					     userbuf, size))
+				return -EFAULT;
+
+			if (n == (VIA_DMA_BUFFERS - 1))
+				sgtable[n].count = size | VIA_EOL;
+			else
+				sgtable[n].count = size | VIA_FLAG;
+
+			/* if SGD stopped during data copy or SG table update,
+			 * then loop back to the beginning without updating
+			 * any pointers.
+			 * ie. slack way to prevent race */
+			if (!STATE_STARTED)
+				continue;
+
+			atomic_inc (&chan->next_buf);
+
+			count -= size;
+			userbuf += size;
+			continue;
+		}
+
+		/* case 4, final SGT active case: no free buffers, wait for one */
+
+		else {
+			DPRINTK ("case 4\n");
+			DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d\n",
+				 atomic_read (state),
+				 BUF_IN_USE,
+				 nextbuf,
+				 prevbuf);
+
+			/* if playback stopped, no need to sleep */
+			if (!STATE_STARTED)
+				continue;
+			
+			/* if buffer free, no need to sleep */
+			if (!via_chan_full (chan))
+				continue;
+			
+			if (non_blocking) {
+				rc = userbuf - orig_userbuf;
+				if (rc == 0)
+					rc = -EAGAIN;
+				return rc;
+			}
+
+			DPRINTK ("sleeping\n");
+			interruptible_sleep_on (&chan->wait);
+			if (signal_pending (current))
+				return -ERESTARTSYS;
+		}
+	}
+
+#if 0
+	{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+	}
+#endif
+
+	DPRINTK ("EXIT, returning %d\n",
+		 userbuf - orig_userbuf);
+	return userbuf - orig_userbuf;
+}
+#undef sgcount
+#undef NEXTBUF
+#undef BUF_IN_USE
+#undef STATE_STOPPED
+#undef STATE_STARTED
+
+
+static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct via_info *card;
+	ssize_t rc;
+
+	DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",
+		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
 	
+	assert (file != NULL);
+	assert (buffer != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (ppos != &file->f_pos) {
+		DPRINTK ("EXIT, returning -ESPIPE\n");
+		return -ESPIPE;
+	}
+
+	rc = via_dsp_do_write (card, buffer, count, (file->f_flags & O_NONBLOCK));
+
+	DPRINTK("EXIT, returning %ld\n",(long) rc);
+	return rc;
+}
+
+
+static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct via_info *card;
+	unsigned int mask = 0;
+
 	DPRINTK ("ENTER\n");
 
-	if (!pci_present ()) {
-		printk (KERN_DEBUG PFX "PCI not present, exiting\n");
-		DPRINTK ("EXIT, returning -ENODEV\n");
-		return -ENODEV;
+	assert (file != NULL);
+	assert (wait != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if ((file->f_mode & FMODE_WRITE) &&
+	    (atomic_read (&card->ch_out.state) != sgd_stopped)) {
+		poll_wait(file, &card->ch_out.wait, wait);
+		
+		/* XXX is this correct */
+		if (atomic_read (&card->ch_out.buf_in_use) <
+		    atomic_read (&card->ch_out.next_buf))
+			mask |= POLLOUT | POLLWRNORM;
 	}
 
-	if (probe_via82cxxx() != 0) {
-		printk(KERN_ERR PFX "probe failed, aborting\n");
-		/* XXX unload cards registered so far, if any */
-		DPRINTK ("EXIT, returning -ENODEV\n");
-		return -ENODEV;
+	DPRINTK("EXIT, returning %u\n", mask);
+	return mask;
+}
+
+
+static int via_dsp_drain_dac (struct via_info *card, int non_block)
+{
+	DPRINTK ("ENTER, non_block = %d\n", non_block);
+
+	while (!via_chan_empty (&card->ch_out)) {
+		if (non_block) {
+			DPRINTK ("EXIT, returning -EBUSY\n");
+			return -EBUSY;
+		}
+		if (signal_pending (current)) {
+			DPRINTK ("EXIT, returning -ERESTARTSYS\n");
+			return -ERESTARTSYS;
+		}
+
+#ifndef NDEBUG
+	{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+
+		spin_lock_irq (&card->lock);
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+		spin_unlock_irq (&card->lock);
+		
 	}
+#endif
 
-	pci_read_config_byte (cards[0].pdev, PCI_REVISION_ID, &tmp);
-	for (i = 0; i < arraysize(via_chip_revs); i++)
-		if (via_chip_revs[i].revision == tmp) {
-			rev = via_chip_revs[i].rev_name;
-			break;
-		}
-	printk (KERN_INFO PFX VIA_CARD_NAME " loaded\n");
-	printk (KERN_INFO PFX "Chip rev %s.  Features: SBPro compat%s%s\n",
-		rev,
-		cards[0].opl3_data.io_base == 0 ? "" : ", MPU-401 MIDI",
-		cards[0].have_ac97 == -1 ? "" : ", AC97 mixer");
-	
-	if (via_init_proc () != 0) {
-		printk (KERN_WARNING PFX
-			"Unable to init experimental /proc, ignoring\n");
+		DPRINTK ("sleeping\n");		
+		interruptible_sleep_on (&card->ch_out.wait);
 	}
+	
+	DPRINTK ("EXIT\n");
+	return 0;
+}
 
-	/*
-	 *	Binds us to the sound subsystem	
+
+static int via_dsp_ioctl_space (struct via_info *card,
+				struct via_channel *chan,
+				void *arg)
+{
+	audio_buf_info info;
+	int n;
+
+	info.fragstotal = VIA_DMA_BUFFERS;
+	info.fragsize = VIA_DMA_BUF_SIZE;
+
+	/* number of full fragments we can read without blocking */
+	n = atomic_read (&chan->next_buf) - atomic_read (&chan->buf_in_use);
+	info.fragments = VIA_DMA_BUFFERS - n;
+
+	/* number of bytes that can be read or written immediately
+	 * without blocking.  FIXME: we are lazy and ignore partially-full
+	 * buffers.
 	 */
-	SOUND_LOCK;
+	info.bytes = info.fragments * VIA_DMA_BUF_SIZE;
+
+	DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
+		info.fragstotal,
+		info.fragsize,
+		info.fragments,
+		info.bytes);
+
+	return copy_to_user (arg, &info, sizeof (info));
+}
+
+
+
+static int via_dsp_ioctl (struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	int rc = -EINVAL, rd=0, wr=0, val=0;
+	struct via_info *card;
+
+	DPRINTK ("ENTER, cmd = 0x%08X\n", cmd);
+
+	assert (file != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (file->f_mode & FMODE_WRITE)
+		wr = 1;
+	if (file->f_mode & FMODE_READ)
+		rd = 1;
+	
+	switch (cmd) {
+
+	/* OSS API version.  XXX unverified */		
+	case OSS_GETVERSION:
+		DPRINTK("EXIT, returning SOUND_VERSION\n");
+		return put_user (SOUND_VERSION, (int *)arg);
+
+	/* list of supported PCM data formats */
+	case SNDCTL_DSP_GETFMTS:
+		DPRINTK("EXIT, returning AFMT U8|S16_LE\n");
+                return put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
+
+	/* query or set current channel's PCM data format */
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			rc = 0;
+
+			spin_lock_irq (&card->lock);
+			if (rc == 0 && rd)
+				rc = via_chan_set_fmt (card, &card->ch_in, val);
+			if (rc == 0 && wr)
+				rc = via_chan_set_fmt (card, &card->ch_out, val);
+			spin_unlock_irq (&card->lock);
+
+			if (rc <= 0)
+				return rc ? rc : -EINVAL;
+			val = rc;
+		} else {
+			spin_lock_irq (&card->lock);
+			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
+			    (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT)))
+				val = AFMT_S16_LE;
+			else
+				val = AFMT_U8;
+			spin_unlock_irq (&card->lock);
+		}
+		DPRINTK("SETFMT EXIT, returning %d\n", val);
+                return put_user (val, (int *)arg);
+
+	/* query or set number of channels (1=mono, 2=stereo) */
+        case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			rc = 0;
+			spin_lock_irq (&card->lock);
+			if (rc == 0 && rd)
+				rc = via_chan_set_stereo (card, &card->ch_in, val);
+			if (rc == 0 && wr)
+				rc = via_chan_set_stereo (card, &card->ch_out, val);
+			spin_unlock_irq (&card->lock);
+			if (rc <= 0)
+				return rc ? rc : -EINVAL;
+			val = rc;
+		} else {
+			spin_lock_irq (&card->lock);
+			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
+			    (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO)))
+				val = 2;
+			else
+				val = 1;
+			spin_unlock_irq (&card->lock);
+		}
+		DPRINTK("CHANNELS EXIT, returning %d\n", val);
+                return put_user (val, (int *)arg);
+	
+	/* enable (val is not zero) or disable (val == 0) stereo */
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		rc = 0;
+		spin_lock_irq (&card->lock);
+		if (rc == 0 && rd)
+			rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
+		if (rc == 0 && wr)
+			rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
+		spin_unlock_irq (&card->lock);
+		if (rc <= 0)
+			return rc ? rc : -EINVAL;
+		DPRINTK("STEREO EXIT, returning %d\n", val);
+                return 0;
+	
+	/* query or set sampling rate */
+        case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val < 0)
+			return -EINVAL;
+		if (val > 0) {
+			rc = 0;
+			spin_lock_irq (&card->lock);
+			if (rc == 0 && rd)
+				rc = via_chan_set_speed (card, &card->ch_in, val);
+			if (rc == 0 && wr)
+				rc = via_chan_set_speed (card, &card->ch_out, val);
+			spin_unlock_irq (&card->lock);
+			if (rc <= 0)
+				return rc ? rc : -EINVAL;
+			val = rc;
+		} else {
+			spin_lock_irq (&card->lock);
+			if (rd)
+				val = card->ch_in.rate;
+			else if (wr)
+				val = card->ch_out.rate;
+			else
+				val = 0;
+			spin_unlock_irq (&card->lock);
+		}
+		DPRINTK("SPEED EXIT, returning %d\n", val);
+                return put_user (val, (int *)arg);
+	
+	/* wait until all buffers have been played, and then stop device */
+	case SNDCTL_DSP_SYNC:
+		if (wr) {
+			DPRINTK("SYNC EXIT (after calling via_dsp_drain_dac)\n");
+			return via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK);
+		}
+		break;
+
+	/* stop recording/playback immediately */
+        case SNDCTL_DSP_RESET:
+		spin_lock_irq (&card->lock);
+		if (rd) {
+			via_chan_clear (&card->ch_in);
+			via_chan_pcm_fmt (card, &card->ch_in, 1);
+		}
+		if (wr) {
+			via_chan_clear (&card->ch_out);
+			via_chan_pcm_fmt (card, &card->ch_out, 1);
+		}
+		spin_unlock_irq (&card->lock);
+		DPRINTK("RESET EXIT, returning 0\n");
+		return 0;
+
+	/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+	case SNDCTL_DSP_GETCAPS:
+		DPRINTK("GETCAPS EXIT\n");
+		return put_user(DSP_CAP_REVISION, (int *)arg);
+		
+	/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+	case SNDCTL_DSP_GETBLKSIZE:
+		DPRINTK("GETBLKSIZE EXIT\n");
+		return put_user(VIA_DMA_BUF_SIZE, (int *)arg);
+		
+	/* obtain information about input buffering */
+	case SNDCTL_DSP_GETISPACE:
+		DPRINTK("GETISPACE EXIT\n");
+		return via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
+	
+	/* obtain information about output buffering */
+	case SNDCTL_DSP_GETOSPACE:
+		DPRINTK("GETOSPACE EXIT\n");
+		return via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
+
+	/* return number of bytes remaining to be played by DMA engine */
+	case SNDCTL_DSP_GETODELAY:
+		{
+		int n;
+		
+		n = atomic_read (&card->ch_out.next_buf) -
+		    atomic_read (&card->ch_out.buf_in_use);
+		assert (n >= 0);
+
+		if (n == 0)
+			val = 0;
+		else {
+			val = (n - 1) * VIA_DMA_BUF_SIZE;
+			val += inl (card->ch_out.iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT);
+		}
+		
+		DPRINTK("GETODELAY EXIT, val = %d bytes\n", val);
+                return put_user (val, (int *)arg);
+		}
+
+	/* set fragment size.  implemented as a successful no-op for now */
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
+			 val & 0xFFFF,
+			 val & 0xFFFF,
+			 (val >> 16) & 0xFFFF,
+			 (val >> 16) & 0xFFFF);
+
+		/* just to shut up some programs */
+		return 0;
+
+	/* inform device of an upcoming pause in input (or output).  not implemented */
+	case SNDCTL_DSP_POST:
+		DPRINTK("POST EXIT (null ioctl, returning -EINVAL)\n");
+                return -EINVAL;
+
+	/* not implemented */
+	default:
+		DPRINTK ("unhandled ioctl\n");
+		break;
+	}
+		
+	DPRINTK("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static int via_dsp_open (struct inode *inode, struct file *file)
+{
+	int open_mode, rc = -EINVAL, minor = MINOR(inode->i_rdev);
+	int got_read_chan = 0, is_busy;
+	struct via_info *card;
+	unsigned long flags;
+
+	DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode);
+	
+	if (file->f_mode & FMODE_READ) /* no input ATM */
+		goto err_out;
+
+	card = NULL;
+	assert (via_driver_data != NULL);
+			
+	card = via_driver_data;	
+	DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n",
+		 card->dev_dsp, minor,
+		 (card->dev_dsp ^ minor) & ~0xf);
+
+	if (((card->dev_dsp ^ minor) & ~0xf) == 0)
+		goto match;
+	
+	DPRINTK ("no matching %s found\n", card ? "minor" : "driver");
+	rc = -ENODEV;
+	goto err_out;
+
+match:
+	file->private_data = card;
+
+	/* wait for device to become free */
+	spin_lock_irqsave (&card->lock, flags);
+	open_mode = card->open_mode;
+	if (open_mode & file->f_mode)
+		is_busy = 1;
+	else {
+		is_busy = 0;
+		card->open_mode |= file->f_mode;
+		open_mode = card->open_mode;
+	}
+	spin_unlock_irqrestore (&card->lock, flags);
+	if (is_busy) {
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	DPRINTK("open_mode now 0x%x\n", open_mode);
+
+	/* handle input from analog source */
+	if (file->f_mode & FMODE_READ) {
+		rc = via_chan_init (card, &card->ch_in, 0x10);
+		if (rc)
+			goto err_out_clear_mode;
+			
+		got_read_chan = 1;
+		
+		/* why is this forced to 16-bit stereo in all drivers? */
+		card->ch_in.pcm_fmt =
+			VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+
+		spin_lock_irqsave (&card->lock, flags);
+		via_chan_pcm_fmt (card, &card->ch_out, 0);
+		spin_unlock_irqrestore (&card->lock, flags);
+
+		via_set_adc_rate (card, 8000);
+	}
+
+	/* handle output to analog source */
+	if (file->f_mode & FMODE_WRITE) {
+		rc = via_chan_init (card, &card->ch_out, 0x00);
+		if (rc)
+			goto err_out_clear_mode;
+		
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			card->ch_out.pcm_fmt |= VIA_PCM_FMT_16BIT;
+
+		spin_lock_irqsave (&card->lock, flags);
+		via_chan_pcm_fmt (card, &card->ch_out, 0);
+		spin_unlock_irqrestore (&card->lock, flags);
+
+		via_set_dac_rate (card, 8000);
+	}
+
+	MOD_INC_USE_COUNT;
 	DPRINTK ("EXIT, returning 0\n");
 	return 0;
+
+err_out_clear_mode:
+	if (got_read_chan)
+		via_chan_free (card, &card->ch_in);
+	spin_lock_irqsave (&card->lock, flags);
+	card->open_mode &= ~file->f_mode;
+	spin_unlock_irqrestore (&card->lock, flags);
+err_out:
+	DPRINTK("ERROR EXIT, returning %d\n", rc);
+	return rc;
 }
 
-int init_module(void) {
-	return init_via82cxxx_module();
+
+static int via_dsp_release(struct inode *inode, struct file *file)
+{
+	struct via_info *card;
+	unsigned long flags;
+
+	DPRINTK ("ENTER\n");
+	
+	assert (file != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+	
+	lock_kernel();
+	if (file->f_mode & FMODE_READ)
+		via_chan_free (card, &card->ch_in);
+
+	if (file->f_mode & FMODE_WRITE) {
+		via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK);
+		via_chan_free (card, &card->ch_out);
+	}
+			
+	spin_lock_irqsave (&card->lock, flags);
+	card->open_mode &= ~(file->f_mode);
+	spin_unlock_irqrestore (&card->lock, flags);
+
+ 	wake_up (&card->open_wait);
+	unlock_kernel();
+
+	MOD_DEC_USE_COUNT;
+	DPRINTK("EXIT, returning 0\n");
+	return 0;
 }
 
-/*
- *	This is called when it is removed. It will only be removed 
- *	when its use count is 0. For sound the SOUND_LOCK/SOUND_UNLOCK
- *	macros hide the entire work for this.
+
+/****************************************************************
+ *
+ * Chip setup and kernel registration
+ *
+ *
  */
- 
-static void cleanup_via82cxxx_module(void)
+
+static int __init via_init_one (struct pci_dev *pdev)
 {
-	DPRINTK("ENTER\n");
+	int rc;
+	struct via_info *card;
+	u8 tmp;
+	static int printed_version = 0;
+	
+	DPRINTK ("ENTER\n");
 	
-	if (cards[0].opl3_data.io_base)
-		unload_uart401 (&cards[0].opl3_data);
+	if (printed_version++ == 0)
+		printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n");
+
+	if (check_region (pci_resource_start (pdev, 0),
+	    		     pci_resource_len (pdev, 0))) {
+		printk (KERN_ERR PFX "unable to obtain I/O resources, aborting\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+	request_region (pci_resource_start (pdev, 0),
+	    		pci_resource_len (pdev, 0), VIA_MODULE_NAME);
 
-	via_unload_sb (&cards[0].sb_data);
+	if (pci_enable_device (pdev)) {
+		rc = -EIO;
+		goto err_out_none;
+	}
 	
-	via_unload_ac97 (&cards[0]);
+	card = kmalloc (sizeof (*card), GFP_KERNEL);
+	if (!card) {
+		printk (KERN_ERR PFX "out of memory, aborting\n");
+		rc = -ENOMEM;
+		goto err_out_none;
+	}
+
+	via_driver_data = card;
+
+	memset (card, 0, sizeof (*card));
+	card->pdev = pdev;
+	card->baseaddr = pci_resource_start (pdev, 0);
+	card->card_num = via_num_cards++;
+	spin_lock_init (&card->lock);
+	init_waitqueue_head(&card->open_wait);
+	
+	/* if BAR 2 is present, chip is Rev H or later,
+	 * which means it has a few extra features */
+	if (pci_resource_start (pdev, 2) > 0)
+		card->rev_h = 1;
+
+	if (pdev->irq < 1) {
+		printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq);
+		rc = -ENODEV;
+		goto err_out_kfree;
+	}
+
+	/* 
+	 * init AC97 mixer and codec
+	 */
+	rc = via_ac97_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "AC97 init failed, aborting\n");
+		goto err_out_kfree;
+	}
 
-	via_cleanup_proc ();
+	/*
+	 * init DSP device
+	 */
+	rc = via_dsp_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "DSP device init failed, aborting\n");
+		goto err_out_have_mixer;
+	}
 	
 	/*
-	 *	Final clean up with the sound layer
+	 * init and turn on interrupts, as the last thing we do
 	 */
-	SOUND_LOCK_END;
+	rc = via_interrupt_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "interrupt init failed, aborting\n");
+		goto err_out_have_dsp;
+	}
+	
+	pci_read_config_byte (pdev, 0x3C, &tmp);
+	if ((tmp & 0x0F) != pdev->irq) {
+		printk (KERN_WARNING PFX "IRQ fixup, 0x3C==0x%02X\n", tmp);
+		tmp &= 0xF0;
+		tmp |= pdev->irq;
+		pci_write_config_byte (pdev, 0x3C, tmp);
+		DPRINTK("new 0x3c==0x%02x\n", tmp);
+	} else {
+		DPRINTK("IRQ reg 0x3c==0x%02x, irq==%d\n",
+			tmp, tmp & 0x0F);
+	}
 
-	DPRINTK("EXIT\n");
+	printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n",
+		card->card_num + 1, card->baseaddr, pdev->irq);
+	
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out_have_dsp:
+	via_dsp_cleanup (card);
+
+err_out_have_mixer:
+	via_ac97_cleanup (card);
+
+err_out_kfree:
+#ifndef VIA_NDEBUG
+	memset (card, 0xAB, sizeof (*card)); /* poison memory */
+#endif
+	kfree (card);
+
+err_out_none:
+	release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
+err_out:
+	via_driver_data = NULL;
+	DPRINTK ("EXIT - returning %d\n", rc);
+	return rc;
+}
+
+
+static void via_remove_one (struct pci_dev *pdev)
+{
+	struct via_info *card;
+	
+	DPRINTK ("ENTER\n");
+	
+	assert (pdev != NULL);
+	card = via_driver_data;
+	assert (card != NULL);
+	
+	via_interrupt_cleanup (card);
+	via_dsp_cleanup (card);
+	via_ac97_cleanup (card);
+
+	release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
+
+#ifndef VIA_NDEBUG
+	memset (card, 0xAB, sizeof (*card)); /* poison memory */
+#endif
+	kfree (card);
+
+	via_driver_data = NULL;
+	
+	pci_set_power_state (pdev, 3); /* ...zzzzzz */
+
+	DPRINTK ("EXIT\n");
+	return;
+}
+
+
+/****************************************************************
+ *
+ * Driver initialization and cleanup
+ *
+ *
+ */
+
+static int __init init_via82cxxx_audio(void)
+{
+	DPRINTK ("ENTER\n");
+	
+	via_pdev = pci_find_device (PCI_VENDOR_ID_VIA,
+				    PCI_DEVICE_ID_VIA_82C686_5, NULL);
+	if (!via_pdev) return -ENODEV;
+
+	if (via_init_one (via_pdev)) {
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		return -ENODEV;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
 }
 
-void cleanup_module(void) {
-	cleanup_via82cxxx_module();
+ 
+static void cleanup_via82cxxx_audio(void)
+{
+	DPRINTK("ENTER\n");
+	
+	via_remove_one (via_pdev);
+	
+	via_pdev = via_driver_data = NULL;
+
+	DPRINTK("EXIT\n");
 }
 
+
+module_init(init_via82cxxx_audio);
+module_exit(cleanup_via82cxxx_audio);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc.c linux.18p17/drivers/sound/vidc.c
--- linux.vanilla/drivers/sound/vidc.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/vidc.c	Mon Oct  2 10:04:46 2000
@@ -1,118 +1,536 @@
 /*
- *	drivers/sound/vidc.c
+ * drivers/sound/vidc.c
  *
- *	Detection routine for the VIDC.
+ * VIDC20 audio driver.
  *
- *	Copyright (C) 1997 by Russell King <rmk@arm.uk.linux.org>
+ * Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
+ *
+ * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
+ * engine.  The DMA transfers fixed-format (16-bit little-endian linear)
+ * samples to the VIDC20, which then transfers this data serially to the
+ * DACs.  The samplerate is controlled by the VIDC.
+ *
+ * We currently support a mixer device, but it is currently non-functional.
  */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
 
-#include <asm/io.h>
+#include <asm/hardware.h>
 #include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/iomd.h>
+#include <asm/system.h>
+
 #include "sound_config.h"
 #include "soundmodule.h"
 #include "vidc.h"
 
-int vidc_busy;
+#ifndef _SIOC_TYPE
+#define _SIOC_TYPE(x)	_IOC_TYPE(x)
+#endif
+#ifndef _SIOC_NR
+#define _SIOC_NR(x)	_IOC_NR(x)
+#endif
+
+#define VIDC_SOUND_CLOCK	(250000)
+
+/*
+ * When using SERIAL SOUND mode (external DAC), the number of physical
+ * channels is fixed at 2.
+ */
+static int		vidc_busy;
+static int		vidc_adev;
+static int		vidc_audio_rate;
+static char		vidc_audio_format;
+static char		vidc_audio_channels;
+
+static unsigned char	vidc_level_l[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned char	vidc_level_r[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned int	vidc_audio_volume_l;	/* left PCM vol, 0 - 65536 */
+static unsigned int	vidc_audio_volume_r;	/* right PCM vol, 0 - 65536 */
+
+static void	(*old_mksound)(unsigned int hz, unsigned int ticks);
+extern void	(*kd_mksound)(unsigned int hz, unsigned int ticks);
+extern void	vidc_update_filler(int bits, int channels);
 
-void vidc_update_filler(int format, int channels)
+static void
+vidc_mksound(unsigned int hz, unsigned int ticks)
+{
+
+}
+
+static void
+vidc_mixer_set(int mdev, unsigned int level)
+{
+	unsigned int lev_l = level & 0x007f;
+	unsigned int lev_r = (level & 0x7f00) >> 8;
+	unsigned int mlev_l, mlev_r;
+
+	if (lev_l > 100)
+		lev_l = 100;
+	if (lev_r > 100)
+		lev_r = 100;
+
+#define SCALE(lev,master)	((lev) * (master) * 65536 / 10000)
+
+	mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
+	mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
+
+	switch (mdev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_PCM:
+		vidc_level_l[mdev] = lev_l;
+		vidc_level_r[mdev] = lev_r;
+
+		vidc_audio_volume_l = SCALE(lev_l, mlev_l);
+		vidc_audio_volume_r = SCALE(lev_r, mlev_r);
+
+		break;
+	}
+#undef SCALE
+}
+
+static int vidc_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	unsigned int val;
+	unsigned int mdev;
+
+	if (_SIOC_TYPE(cmd) != 'M')
+		return -EINVAL;
+
+	mdev = _SIOC_NR(cmd);
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (get_user(val, (unsigned int *)arg))
+			return -EFAULT;
+
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			vidc_mixer_set(mdev, val);
+		else
+			return -EINVAL;
+	}
+
+	/*
+	 * Return parameters
+	 */
+	switch (mdev) {
+	case SOUND_MIXER_RECSRC:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_CAPS:
+		val = 0;
+		break;
+
+	default:
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
+		else
+			return -EINVAL;
+	}
+
+	return put_user(val, (unsigned int *)arg) ? -EFAULT : 0;
+}
+
+static struct mixer_operations vidc_mixer_operations = {
+	"VIDC",
+	"VIDCsound",
+	vidc_mixer_ioctl	/* ioctl                */
+};
+
+static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
+{
+	switch (fmt) {
+	default:
+		fmt = AFMT_S16_LE;
+	case AFMT_U8:
+	case AFMT_S8:
+	case AFMT_S16_LE:
+		vidc_audio_format = fmt;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case AFMT_QUERY:
+		break;
+	}
+	return vidc_audio_format;
+}
+
+static int vidc_audio_set_speed(int dev, int rate)
+{
+	if (rate) {
+		unsigned int hwctrl, hwrate;
+		unsigned int newsize, new2size;
+
+		/*
+		 * If we have selected 44.1kHz, use the DAC clock.
+		 */
+		if (0 && rate == 44100) {
+			hwctrl = 0x00000002;
+			hwrate = 3;
+		} else {
+			hwctrl = 0x00000003;
+
+			hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
+			if (hwrate < 3)
+				hwrate = 3;
+			if (hwrate > 255)
+				hwrate = 255;
+
+			rate = VIDC_SOUND_CLOCK / hwrate;
+		}
+
+		outl(0xb0000000 | (hwrate - 2), IO_VIDC_BASE);
+		outl(0xb1000000 | hwctrl, IO_VIDC_BASE);
+
+		newsize = (10000 / hwrate) & ~3;
+		if (newsize < 208)
+			newsize = 208;
+		if (newsize > 4096)
+			newsize = 4096;
+		for (new2size = 128; new2size < newsize; new2size <<= 1);
+		if (new2size - newsize > newsize - (new2size >> 1))
+			new2size >>= 1;
+		if (new2size > 4096) {
+			printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
+				newsize, new2size);
+			new2size = 4096;
+		}
+		dma_bufsize = new2size;
+		vidc_audio_rate = rate;
+	}
+	return vidc_audio_rate;
+}
+
+static short vidc_audio_set_channels(int dev, short channels)
+{
+	switch (channels) {
+	default:
+		channels = 2;
+	case 1:
+	case 2:
+		vidc_audio_channels = channels;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case 0:
+		break;
+	}
+	return vidc_audio_channels;
+}
+
+/*
+ * Open the device
+ */
+static int vidc_audio_open(int dev, int mode)
+{
+	/* This audio device does not have recording capability */
+	if (mode == OPEN_READ)
+		return -EPERM;
+
+	if (vidc_busy)
+		return -EBUSY;
+
+	vidc_busy = 1;
+	return 0;
+}
+
+/*
+ * Close the device
+ */
+static void vidc_audio_close(int dev)
+{
+	vidc_busy = 0;
+}
+
+/*
+ * Output a block via DMA to sound device.
+ *
+ * We just set the DMA start and count; the DMA interrupt routine
+ * will take care of formatting the samples (via the appropriate
+ * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
+ * more data is required.
+ */
+static void
+vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
+{
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	unsigned long flags;
+
+	save_flags_cli(flags);
+	dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
+	dma_count = total_count;
+	restore_flags(flags);
+}
+
+static void
+vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+}
+
+static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	return -EINVAL;
+}
+
+static void vidc_audio_dma_interrupt(void)
+{
+	DMAbuf_outputintr(vidc_adev, 1);
+}
+
+/*
+ * Prepare for outputting samples.
+ *
+ * Each buffer that will be passed will be `bsize' bytes long,
+ * with a total of `bcount' buffers.
+ */
+static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	dma_interrupt = NULL;
+	adev->dmap_out->flags |= DMA_NODMA;
+
+	return 0;
+}
+
+/*
+ * Stop our current operation.
+ */
+static void vidc_audio_reset(int dev)
+{
+	dma_interrupt = NULL;
+}
+
+static int vidc_audio_local_qlen(int dev)
+{
+	return /*dma_count !=*/ 0;
+}
+
+static void vidc_audio_trigger(int dev, int enable_bits)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (enable_bits & PCM_ENABLE_OUTPUT) {
+		if (!(adev->flags & DMA_ACTIVE)) {
+			unsigned long flags;
+
+			save_flags_cli(flags);
+
+			/* prevent recusion */
+			adev->flags |= DMA_ACTIVE;
+
+			dma_interrupt = vidc_audio_dma_interrupt;
+			vidc_sound_dma_irq(0, NULL, NULL);
+			outb(DMA_CR_E | 0x10, IOMD_SD0CR);
+
+			restore_flags(flags);
+		}
+	}
+}
+
+static struct audio_driver vidc_audio_driver =
 {
-	int fillertype;
+	vidc_audio_open,		/* open 		*/
+	vidc_audio_close,		/* close		*/
+	vidc_audio_output_block,	/* output_block 	*/
+	vidc_audio_start_input, 	/* start_input		*/
+	NULL,				/* ioctl		*/
+	vidc_audio_prepare_for_input,	/* prepare_for_input	*/
+	vidc_audio_prepare_for_output,	/* prepare_for_output	*/
+	vidc_audio_reset,		/* halt_io		*/
+	vidc_audio_local_qlen,		/* local_qlen		*/
+	NULL,				/* copy_user		*/
+	NULL,				/* halt_input		*/
+	NULL,				/* halt_output		*/
+	vidc_audio_trigger,		/* trigger		*/
+	vidc_audio_set_speed,		/* set_speed		*/
+	vidc_audio_set_format,		/* set_bits		*/
+	vidc_audio_set_channels,	/* set_channels 	*/
+	NULL,				/* postprocess_write	*/
+	NULL,				/* postprocess_read	*/
+	NULL				/* mmap			*/
+};
 
+void vidc_update_filler(int format, int channels)
+{
 #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
 
-	fillertype = TYPE(format, channels);
-printk("filler type: %X\n", fillertype);
-	switch (fillertype)
-	{
-		default:
-		case TYPE(AFMT_U8, 1):
-			vidc_filler = vidc_fill_1x8_u;
-			break;
-
-		case TYPE(AFMT_U8, 2):
-			vidc_filler = vidc_fill_2x8_u;
-			break;
-
-		case TYPE(AFMT_S8, 1):
-			vidc_filler = vidc_fill_1x8_s;
-			break;
-
-		case TYPE(AFMT_S8, 2):
-			vidc_filler = vidc_fill_2x8_s;
-			break;
-
-		case TYPE(AFMT_S16_LE, 1):
-			vidc_filler = vidc_fill_1x16_s;
-			break;
-
-		case TYPE(AFMT_S16_LE, 2):
-			vidc_filler = vidc_fill_2x16_s;
-			break;
+	switch (TYPE(format, channels)) {
+	default:
+	case TYPE(AFMT_U8, 1):
+		vidc_filler = vidc_fill_1x8_u;
+		break;
+
+	case TYPE(AFMT_U8, 2):
+		vidc_filler = vidc_fill_2x8_u;
+		break;
+
+	case TYPE(AFMT_S8, 1):
+		vidc_filler = vidc_fill_1x8_s;
+		break;
+
+	case TYPE(AFMT_S8, 2):
+		vidc_filler = vidc_fill_2x8_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 1):
+		vidc_filler = vidc_fill_1x16_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 2):
+		vidc_filler = vidc_fill_2x16_s;
+		break;
 	}
 }
 
 void attach_vidc(struct address_info *hw_config)
 {
 	char name[32];
-	int i;
+	int i, adev;
 
 	sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
 	conf_printf(name, hw_config);
+	memset(dma_buf, 0, sizeof(dma_buf));
 
-	for (i = 0; i < 2; i++)
-	{
+	adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
+			&vidc_audio_driver, sizeof(vidc_audio_driver),
+			DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
+			NULL, hw_config->dma, hw_config->dma2);
+
+	if (adev < 0)
+		goto audio_failed;
+
+	/*
+	 * 1024 bytes => 64 buffers
+	 */
+	audio_devs[adev]->min_fragment = 10;
+	audio_devs[adev]->mixer_dev = num_mixers;
+
+	audio_devs[adev]->mixer_dev =
+		sound_install_mixer(MIXER_DRIVER_VERSION,
+				name, &vidc_mixer_operations,
+				sizeof(vidc_mixer_operations), NULL);
+
+	if (audio_devs[adev]->mixer_dev < 0)
+		goto mixer_failed;
+
+	for (i = 0; i < 2; i++) {
 		dma_buf[i] = get_free_page(GFP_KERNEL);
+		if (!dma_buf[i]) {
+			printk(KERN_ERR "%s: can't allocate required buffers\n",
+				name);
+			goto mem_failed;
+		}
 		dma_pbuf[i] = virt_to_phys(dma_buf[i]);
 	}
 
-	if (sound_alloc_dma(hw_config->dma, "VIDCsound"))
-	{
-		printk(KERN_ERR "VIDCsound: can't allocate virtual DMA channel\n");
-		return;
-	}
-	if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, "VIDCsound", &dma_start))
-	{
-		printk(KERN_ERR "VIDCsound: can't allocate DMA interrupt\n");
-		return;
-	}
-//	vidc_synth_init(hw_config);
-	vidc_audio_init(hw_config);
-	vidc_mixer_init(hw_config);
+	if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
+		printk(KERN_ERR "%s: DMA %d is in  use\n", name, hw_config->dma);
+		goto dma_failed;
+	}
+
+	if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
+			hw_config->name, &dma_start)) {
+		printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
+		goto irq_failed;
+	}
+	old_mksound = kd_mksound;
+	kd_mksound = vidc_mksound;
+	vidc_adev = adev;
+	vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
+	return;
+
+irq_failed:
+	sound_free_dma(hw_config->dma);
+dma_failed:
+mem_failed:
+	for (i = 0; i < 2; i++)
+		free_page(dma_buf[i]);
+	sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+mixer_failed:
+	sound_unload_audiodev(adev);
+audio_failed:
+	return;
 }
 
 int probe_vidc(struct address_info *hw_config)
 {
-	hw_config->irq = IRQ_DMAS0;
-	hw_config->dma = DMA_VIRTUAL_SOUND;
-	hw_config->dma2 = -1;
-	hw_config->card_subtype = 16;
+	hw_config->irq		= IRQ_DMAS0;
+	hw_config->dma		= DMA_VIRTUAL_SOUND;
+	hw_config->dma2		= -1;
+	hw_config->card_subtype	= 16;
+	hw_config->name		= "VIDC20";
 	return 1;
 }
 
 void unload_vidc(struct address_info *hw_config)
 {
-	int i;
+	int i, adev = vidc_adev;
+
+	vidc_adev = -1;
+
+	if (old_mksound)
+		kd_mksound = old_mksound;
 
-	free_irq(hw_config->irq, NULL);
+	free_irq(hw_config->irq, &dma_start);
 	sound_free_dma(hw_config->dma);
 
-	for (i = 0; i < 2; i++)
-		free_page(dma_buf[i]);
+	if (adev >= 0) {
+		sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+		sound_unload_audiodev(adev);
+		for (i = 0; i < 2; i++)
+			free_page(dma_buf[i]);
+	}
 }
 
 #ifdef MODULE
 static struct address_info config;
+/*
+ * Note! Module use count is handled by SOUNDLOCK/SOUND_LOCK_END
+ */
 
 int init_module(void)
 {
 	if (probe_vidc(&config) == 0)
 		return -ENODEV;
-	printk("VIDC 16-bit serial sound\n");
+
 	SOUND_LOCK;
 	attach_vidc(&config);
+
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc.h linux.18p17/drivers/sound/vidc.h
--- linux.vanilla/drivers/sound/vidc.h	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/vidc.h	Mon Oct  2 10:04:46 2000
@@ -55,18 +55,9 @@
 extern unsigned long dma_start, dma_count, dma_bufsize;
 extern unsigned long dma_buf[2], dma_pbuf[2];
 
-/* vidc_audio.c */
-
-extern void     vidc_audio_init(struct address_info *hw_config);
-extern int      vidc_audio_get_volume(void);
-extern int      vidc_audio_set_volume(int vol);
-
-/* vidc_mixer.c */
-
-extern void     vidc_mixer_init(struct address_info *hw_config);
-
 /* vidc_synth.c */
 
 extern void     vidc_synth_init(struct address_info *hw_config);
+extern void	vidc_synth_exit(struct address_info *hw_config);
 extern int      vidc_synth_get_volume(void);
 extern int      vidc_synth_set_volume(int vol);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc_audio.c linux.18p17/drivers/sound/vidc_audio.c
--- linux.vanilla/drivers/sound/vidc_audio.c	Sun Jun 11 21:44:18 2000
+++ linux.18p17/drivers/sound/vidc_audio.c	Thu Jan  1 01:00:00 1970
@@ -1,313 +0,0 @@
-/*
- * drivers/sound/vidc_audio.c
- *
- * Audio routines for the VIDC
- *
- * Copyright (C) 1997 Russell King <rmk@arm.uk.linux.org>
- */
-
-#include <asm/hardware.h>
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include "sound_config.h"
-#include "vidc.h"
-
-/*
- * VIDC sound
- *
- * When using SERIAL SOUND mode (external DAC), the number of physical
- * channels is fixed at 2.  Therefore, the sample rate = vidc sample rate.
- */
-
-static int      vidc_adev;
-
-static int      vidc_audio_volume;
-static int      vidc_audio_rate;
-static char     vidc_audio_format;
-static char     vidc_audio_channels;
-
-extern void     vidc_update_filler(int bits, int channels);
-
-int  vidc_audio_get_volume(void)
-{
-	return vidc_audio_volume;
-}
-
-int vidc_audio_set_volume(int newvol)
-{
-	vidc_audio_volume = newvol;
-	return vidc_audio_volume;
-}
-
-static int vidc_audio_set_bits(int fmt)
-{
-printk("setting format: %d\n", fmt);
-	switch (fmt)
-	{
-		case AFMT_QUERY:
-			break;
-		case AFMT_U8:
-		case AFMT_S8:
-		case AFMT_S16_LE:
-			vidc_audio_format = fmt;
-			vidc_update_filler(vidc_audio_format, vidc_audio_channels);
-			break;
-		default:
-			vidc_audio_format = AFMT_S16_LE;
-			vidc_update_filler(vidc_audio_format, vidc_audio_channels);
-			break;
-	}
-	return vidc_audio_format;
-}
-
-static int vidc_audio_set_rate(int rate)
-{
-	if (rate)
-	{
-		int newsize, new2size;
-
-		vidc_audio_rate = ((500000 / rate) + 1) >> 1;
-		if (vidc_audio_rate < 3)
-			vidc_audio_rate = 3;
-		if (vidc_audio_rate > 255)
-			vidc_audio_rate = 255;
-		outl((vidc_audio_rate - 2) | 0xb0000000, IO_VIDC_BASE);
-		outl(0xb1000003, IO_VIDC_BASE);
-		newsize = (10000 / vidc_audio_rate) & ~3;
-		if (newsize < 208)
-			newsize = 208;
-		if (newsize > 4096)
-			newsize = 4096;
-		for (new2size = 128; new2size < newsize; new2size <<= 1);
-		if (new2size - newsize > newsize - (new2size >> 1))
-			new2size >>= 1;
-		dma_bufsize = new2size;
-	}
-	return 250000 / vidc_audio_rate;
-}
-
-static int vidc_audio_set_channels(int channels)
-{
-	switch (channels)
-	{
-		case 0:
-			break;
-		case 1:
-		case 2:
-			vidc_audio_channels = channels;
-			vidc_update_filler(vidc_audio_format, vidc_audio_channels);
-			break;
-		default:
-			vidc_audio_channels = 2;
-			vidc_update_filler(vidc_audio_format, vidc_audio_channels);
-			break;
-	}
-	return vidc_audio_channels;
-}
-
-/*
- * Open the device
- *
- * dev  - device
- * mode - mode to open device (logical OR of OPEN_READ and OPEN_WRITE)
- *
- * Called when opening the DMAbuf               (dmabuf.c:259)
- */
-static int vidc_audio_open(int dev, int mode)
-{
-	if (vidc_busy)
-		return -EBUSY;
-
-	if ((mode & OPEN_READ) && (!mode & OPEN_WRITE))
-	{
-		/* This audio device doesn't have recording capability */
-		return -EIO;
-	}
-	vidc_busy = 1;
-	return 0;
-}
-
-/*
- * Close the device
- *
- * dev  - device
- *
- * Called when closing the DMAbuf               (dmabuf.c:477)
- *      after halt_xfer
- */
-static void vidc_audio_close(int dev)
-{
-	vidc_busy = 0;
-}
-
-static int vidc_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
-	int ret;
-
-	switch (cmd)
-	{
-		case SOUND_PCM_WRITE_RATE:
-			if (get_user(ret, (int *) arg))
-				return -EFAULT;
-			ret = vidc_audio_set_rate(ret);
-			break;
-
-		case SOUND_PCM_READ_RATE:
-			ret = vidc_audio_set_rate(0);
-			break;
-
-		case SNDCTL_DSP_STEREO:
-			if (get_user(ret, (int *) arg))
-				return -EFAULT;
-			ret = vidc_audio_set_channels(ret + 1) - 1;
-			break;
-
-		case SOUND_PCM_WRITE_CHANNELS:
-			if (get_user(ret, (int *) arg))
-				return -EFAULT;
-			ret = vidc_audio_set_channels(ret);
-			break;
-
-		case SOUND_PCM_READ_CHANNELS:
-			ret = vidc_audio_set_channels(0);
-			break;
-
-		case SNDCTL_DSP_SETFMT:
-			if (get_user(ret, (int *) arg))
-				return -EFAULT;
-			ret = vidc_audio_set_bits(ret);
-			break;
-
-		case SOUND_PCM_READ_BITS:
-			ret = vidc_audio_set_bits(0);
-			break;
-
-		case SOUND_PCM_WRITE_FILTER:
-		case SOUND_PCM_READ_FILTER:
-			return -EINVAL;
-
-		default:
-			return -EINVAL;
-	}
-	return put_user(ret, (int *) arg);
-}
-
-/*
- * Output a block via DMA to sound device
- *
- * dev          - device number
- * buf          - physical address of buffer
- * total_count  - total byte count in buffer
- * intrflag     - set if this has been called from an interrupt (via DMAbuf_outputintr)
- * restart_dma  - set if DMA needs to be re-initialised
- *
- * Called when:
- *  1. Starting output                                  (dmabuf.c:1327)
- *  2.                                                  (dmabuf.c:1504)
- *  3. A new buffer needs to be sent to the device      (dmabuf.c:1579)
- */
-static void vidc_audio_output_block(int dev, unsigned long buf, int total_count,
-			int intrflag)
-{
-	struct audio_operations *adev = audio_devs[dev];
-	struct dma_buffparms *dmap = adev->dmap_out;
-
-	dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
-	dma_count = total_count;
-
-	if (!(adev->flags & DMA_ACTIVE))
-	{
-		unsigned long flags;
-printk("kicking output: %lX+%lX [%lX]\n", dma_start, dma_count, *(unsigned long *)dma_start);
-		save_flags_cli(flags);
-		vidc_sound_dma_irq(0, NULL, NULL);
-		outb(DMA_CR_E | 0x10, IOMD_SD0CR);
-		restore_flags(flags);
-	}
-}
-
-static void vidc_audio_start_input(int dev, unsigned long buf, int count,
-		       int intrflag)
-{
-}
-
-static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
-	return -EINVAL;
-}
-
-static void vidc_audio_dma_interrupt(void)
-{
-	DMAbuf_outputintr(vidc_adev, 1);
-}
-
-/*
- * Prepare for outputting samples to `dev'
- *
- * Each buffer that will be passed will be `bsize' bytes long,
- * with a total of `bcount' buffers.
- *
- * Called when:
- *  1. A trigger enables audio output                   (dmabuf.c:978)
- *  2. We get a write buffer without dma_mode setup     (dmabuf.c:1152)
- *  3. We restart a transfer                            (dmabuf.c:1324)
- */
-static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
-	audio_devs[dev]->dmap_out->flags |= DMA_NODMA;
-	dma_interrupt = vidc_audio_dma_interrupt;
-	return 0;
-}
-
-/*
- * Stop our current operation.
- */
-static void vidc_audio_reset(int dev)
-{
-	/* stop interrupts.  Our real interrupt routine
-	 * will close DMA down for us
-	 */
-	dma_interrupt = NULL;
-}
-
-static int vidc_audio_local_qlen(int dev)
-{
-	return /*dma_count !=*/ 0;
-}
-
-static struct audio_driver vidc_audio_driver =
-{
-	vidc_audio_open,		/* open                 */
-	vidc_audio_close,		/* close                */
-	vidc_audio_output_block,	/* output_block         */
-	vidc_audio_start_input,		/* start_input          */
-	vidc_audio_ioctl,		/* ioctl                */
-	vidc_audio_prepare_for_input,	/* prepare_for_input    */
-	vidc_audio_prepare_for_output,	/* prepare_for_output   */
-	vidc_audio_reset,		/* reset                */
-	vidc_audio_local_qlen,		/*+local_qlen           */
-	NULL,				/*+copy_from_user       */
-	NULL,				/*+halt_input           */
-	NULL,				/* halt_output          */
-	NULL,				/*+trigger              */
-	NULL,				/*+set_speed            */
-	NULL,				/*+set_bits             */
-	NULL,				/*+set_channels         */
-};
-
-void vidc_audio_init(struct address_info *hw_config)
-{
-	vidc_audio_volume = 100 | (100 << 8);
-
-	if ((vidc_adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
-				"VIDCsound", &vidc_audio_driver,
-				sizeof(struct audio_driver),
-				DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
-				NULL, hw_config->dma, hw_config->dma2)) >= 0)
-	{
-		audio_devs[vidc_adev]->min_fragment = 10;	/* 1024 bytes => 64 buffers */
-		audio_devs[vidc_adev]->mixer_dev = num_mixers;
-	}
-	else printk(KERN_ERR "VIDCsound: Too many PCM devices available\n");
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc_fill.S linux.18p17/drivers/sound/vidc_fill.S
--- linux.vanilla/drivers/sound/vidc_fill.S	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/vidc_fill.S	Mon Oct  2 10:04:46 2000
@@ -9,6 +9,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/hardware.h>
+#include <asm/iomd.h>
 
 		.text
 
@@ -190,24 +191,24 @@
 		.data
 		.globl	SYMBOL_NAME(dma_interrupt)
 SYMBOL_NAME(dma_interrupt):
-		.long	0
+		.long	0				@ r3
 		.globl	SYMBOL_NAME(dma_pbuf)
 SYMBOL_NAME(dma_pbuf):
-		.long	0
-		.long	0
+		.long	0				@ r4
+		.long	0				@ r5
 		.globl	SYMBOL_NAME(dma_start)
 SYMBOL_NAME(dma_start):
-		.long	0
+		.long	0				@ r0
 		.globl	SYMBOL_NAME(dma_count)
 SYMBOL_NAME(dma_count):
-		.long	0
+		.long	0				@ r1
 		.globl	SYMBOL_NAME(dma_buf)
 SYMBOL_NAME(dma_buf):
-		.long	0
-		.long	0
+		.long	0				@ r2
+		.long	0				@ r3
 		.globl	SYMBOL_NAME(vidc_filler)
 SYMBOL_NAME(vidc_filler):
-		.long	SYMBOL_NAME(vidc_fill_noaudio)
+		.long	SYMBOL_NAME(vidc_fill_noaudio)	@ r4
 		.globl	SYMBOL_NAME(dma_bufsize)
 SYMBOL_NAME(dma_bufsize):
-		.long	0x1000
+		.long	0x1000				@ r5
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc_mixer.c linux.18p17/drivers/sound/vidc_mixer.c
--- linux.vanilla/drivers/sound/vidc_mixer.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/vidc_mixer.c	Thu Jan  1 01:00:00 1970
@@ -1,151 +0,0 @@
-/*
- * drivers/sound/vidc_mixer.c
- *
- * Mixer routines for VIDC
- *
- * Copyright (C) 1997 Russell King <rmk@arm.uk.linux.org>
- */
-
-#include "sound_config.h"
-#include "vidc.h"
-
-int vidc_volume;
-
-static int vidc_get_volume(void)
-{
-	return vidc_volume;
-}
-
-static int vidc_set_volume(int newvol)
-{
-	vidc_volume = newvol;
-/*  printk ("vidc_set_volume: %X\n", newvol); */
-	return newvol;
-}
-
-static int vidc_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
-	int ret;
-
-	switch (cmd)
-	{
-		case SOUND_MIXER_READ_VOLUME:
-			ret = vidc_get_volume();
-			break;
-
-		case SOUND_MIXER_WRITE_VOLUME:
-			if (get_user(ret, (int *) arg))
-				return -EINVAL;
-			ret = vidc_set_volume(ret);
-			break;
-
-		case SOUND_MIXER_READ_BASS:
-		case SOUND_MIXER_WRITE_BASS:
-		case SOUND_MIXER_READ_TREBLE:
-		case SOUND_MIXER_WRITE_TREBLE:
-			ret = 50;
-			break;
-
-		case SOUND_MIXER_READ_SYNTH:
-//			ret = vidc_synth_get_volume();
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_WRITE_SYNTH:
-			if (get_user(ret, (int *) arg))
-				return -EINVAL;
-//			ret = vidc_synth_set_volume(ret);
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_READ_PCM:
-			ret = vidc_audio_get_volume();
-			break;
-
-		case SOUND_MIXER_WRITE_PCM:
-			if (get_user(ret, (int *) arg))
-				return -EINVAL;
-			ret = vidc_audio_set_volume(ret);
-			break;
-
-		case SOUND_MIXER_READ_SPEAKER:
-			ret = 100;
-			break;
-
-		case SOUND_MIXER_WRITE_SPEAKER:
-			ret = 100;
-			break;
-
-		case SOUND_MIXER_READ_LINE:
-		case SOUND_MIXER_WRITE_LINE:
-		case SOUND_MIXER_READ_MIC:
-		case SOUND_MIXER_WRITE_MIC:
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_READ_CD:
-		case SOUND_MIXER_WRITE_CD:
-			ret = 100 | (100 << 8);
-			break;
-
-		case SOUND_MIXER_READ_IMIX:
-		case SOUND_MIXER_WRITE_IMIX:
-		case SOUND_MIXER_READ_ALTPCM:
-		case SOUND_MIXER_WRITE_ALTPCM:
-		case SOUND_MIXER_READ_LINE1:
-		case SOUND_MIXER_WRITE_LINE1:
-		case SOUND_MIXER_READ_LINE2:
-		case SOUND_MIXER_WRITE_LINE2:
-		case SOUND_MIXER_READ_LINE3:
-		case SOUND_MIXER_WRITE_LINE3:
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_READ_RECSRC:
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_WRITE_RECSRC:
-			return -EINVAL;
-			break;
-
-		case SOUND_MIXER_READ_DEVMASK:
-			ret = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
-			break;
-
-		case SOUND_MIXER_READ_RECMASK:
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_READ_STEREODEVS:
-			ret = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
-			break;
-
-		case SOUND_MIXER_READ_CAPS:
-			ret = 0;
-			break;
-
-		case SOUND_MIXER_READ_MUTE:
-			return -EINVAL;
-			break;
-
-		default:
-			return -EINVAL;
-			break;
-	}
-	return put_user(ret, (int *) arg);
-}
-
-static struct mixer_operations vidc_mixer_operations = {
-	"VIDC",
-	"VIDCsound",
-	vidc_default_mixer_ioctl	/* ioctl                */
-};
-
-void vidc_mixer_init(struct address_info *hw_config)
-{
-	int vidc_mixer = sound_alloc_mixerdev();
-	vidc_volume = 100 | (100 << 8);
-	if (num_mixers < MAX_MIXER_DEV)
-		mixer_devs[vidc_mixer] = &vidc_mixer_operations;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/vidc_synth.c linux.18p17/drivers/sound/vidc_synth.c
--- linux.vanilla/drivers/sound/vidc_synth.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/vidc_synth.c	Mon Oct  2 10:04:46 2000
@@ -88,4 +88,8 @@
 	else
 		printk(KERN_ERR "VIDCsound: Too many synthesizers\n");
 }
+
+void vidc_synth_exit(struct address_info *hw_config)
+{
+}
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/waveartist.c linux.18p17/drivers/sound/waveartist.c
--- linux.vanilla/drivers/sound/waveartist.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/waveartist.c	Fri Sep 15 23:31:12 2000
@@ -2,13 +2,13 @@
  * drivers/sound/waveartist.c
  *
  * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Corel Computer NetWinder.
+ * codec chip used in the Rebel.com NetWinder.
  *
  * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
  */
 
 /*
- * Copyright (C) by Corel Computer 1998
+ * Copyright (C) by Rebel.com 1998-1999
  *
  * RWA010 specs received under NDA from Rockwell
  *
@@ -40,12 +40,20 @@
 #include <linux/delay.h>
 #include <linux/smp.h>
 
+#include <asm/dec21285.h>
 #include <asm/hardware.h>
 
 #include "soundmodule.h"
 #include "sound_config.h"
 #include "waveartist.h"
 
+#ifndef _ISA_DMA
+#define _ISA_DMA(x) (x)
+#endif
+#ifndef _ISA_IRQ
+#define _ISA_IRQ(x) (x)
+#endif
+
 #define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec
 
 #define	MIXER_PRIVATE3_RESET	0x53570000
@@ -141,8 +149,288 @@
 
 static int		 nr_waveartist_devs;
 static wavnc_info	 adev_info[MAX_AUDIO_DEV];
+
+static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
 static struct timer_list vnc_timer;
 
+static void
+vnc_mute(wavnc_info *devc, int mute)
+{
+	extern spinlock_t gpio_lock;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	devc->mute_state = mute;
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+	static signed int old_slider_volume;
+	unsigned long flags;
+	signed int volume = 255;
+
+	*CSR_TIMER1_LOAD = 0x00ffffff;
+
+	save_flags(flags);
+	cli();
+
+	outb(0xFF, 0x201);
+	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+	while (volume && (inb(0x201) & 0x01))
+		volume--;
+
+	*CSR_TIMER1_CNTL = 0;
+
+	restore_flags(flags);
+	
+	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+	volume = 150 - (volume >> 5);
+#else
+	volume = (volume >> 6) - 25;
+#endif
+
+	if (volume < 0)
+		volume = 0;
+
+	if (volume > 100)
+		volume = 100;
+
+	/*
+	 * slider quite often reads +-8, so debounce this random noise
+	 */
+	if ((volume - old_slider_volume) > 7 ||
+	    (old_slider_volume - volume) > 7) {
+		old_slider_volume = volume;
+
+		DEB(printk("Slider volume: %d.\n", old_slider_volume));
+	}
+
+	return old_slider_volume;
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+	signed int slider_volume;
+	unsigned int temp;
+
+	/*
+	 * read the "buttons" state.
+	 *  Bit 4 = handset present,
+	 *  Bit 5 = offhook
+	 */
+	// the state should be "querable" via a private IOCTL call
+	temp = inb(0x201) & 0x30;
+
+	if (!devc->handset_mute_sw &&
+	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
+		devc->handset_state = temp;
+		devc->handset_mute_sw = 0;
+
+		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
+	}
+
+	slider_volume = vnc_volume_slider(devc);
+
+	/*
+	 * If we're using software controlled volume, and
+	 * the slider moves by more than 20%, then we
+	 * switch back to slider controlled volume.
+	 */
+	if (devc->slider_vol > slider_volume) {
+		if (devc->slider_vol - slider_volume > 20)
+			devc->use_slider = 1;
+	} else {
+		if (slider_volume - devc->slider_vol > 20)
+			devc->use_slider = 1;
+	}
+
+	/*
+	 * use only left channel
+	 */
+	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+	if (slider_volume != temp && devc->use_slider) {
+		devc->slider_vol = slider_volume;
+
+		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+			slider_volume | slider_volume << 8);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+	int next_timeout;
+
+	if (vnc_slider(adev_info + data))
+		next_timeout = 5;	// mixer reported change
+	else
+		next_timeout = VNC_TIMER_PERIOD;
+
+	mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int val, temp;
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		/*
+		 * Use this call to override the automatic handset
+		 * behaviour - ignore handset.
+		 *  bit 7 = total control over handset - do not
+		 *          to plug/unplug
+		 *  bit 4 = internal mic
+		 *  bit 0 = mute internal speaker
+		 */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		devc->handset_mute_sw = val & 0x80;
+
+		temp = val & VNC_INTERNAL_SPKR;
+		if (devc->mute_state != temp)
+			vnc_mute(devc, temp);
+
+		devc->handset_state = val & VNC_INTERNAL_MIC;
+		waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+		return 0;
+	} else if (cmd == SOUND_MIXER_PRIVATE2) {
+		/*
+		 * Use this call to get private settings
+		 */
+
+		val = 0;
+		if (devc->handset_mute_sw)
+		    val |= 0x80;	
+
+		if (devc->mute_state)
+		    val |= VNC_INTERNAL_SPKR;
+		    
+		if (devc->handset_state)
+		    val |= VNC_INTERNAL_MIC;
+
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+
+		return 0;
+	}
+#if 0
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+		int             val;
+
+		val = *(int *) arg;
+
+		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
+
+		if (val == VNC_SOUND_PAUSE) {
+			wa_sendcmd(0x16);    //PAUSE the ADC
+		} else if (val == VNC_SOUND_RESUME) {
+			wa_sendcmd(0x18);    //RESUME the ADC
+		} else {
+			return -EINVAL;      //invalid parameters...
+		}
+		return 0;
+	}
+
+	if (cmd == SOUND_MIXER_PRIVATE3) {
+		long unsigned   flags;
+		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
+
+		int             val;
+		int             i;
+
+		val = *(int *) arg;
+
+		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
+			return (-EFAULT);
+
+		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
+
+		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
+			wavnc_mixer_reset(devc);
+			return (0);
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
+//			printk("WaveArtist Mixer: Private write command.\n");
+
+			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
+			wa_sendcmd(mixer_reg[0]);
+			wa_sendcmd(mixer_reg[4]);
+
+			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
+			wa_sendcmd(mixer_reg[1]);
+			wa_sendcmd(mixer_reg[5]);
+
+			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
+			wa_sendcmd(mixer_reg[2]);
+			wa_sendcmd(mixer_reg[6]);
+
+			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
+			wa_sendcmd(mixer_reg[3]);
+			wa_sendcmd(mixer_reg[7]);
+
+			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
+			wa_sendcmd(mixer_reg[8]);
+			wa_sendcmd(mixer_reg[9]);
+
+			wa_sendcmd(0x0031);		//set left and right PCM
+			wa_sendcmd(mixer_reg[0x0A]);
+			wa_sendcmd(mixer_reg[0x0B]);
+
+			wa_sendcmd(0x0131);		//set left and right FM
+			wa_sendcmd(mixer_reg[0x0C]);
+			wa_sendcmd(mixer_reg[0x0D]);
+
+			return 0;
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
+//			printk("WaveArtist Mixer: Private read command.\n");
+
+			//first read all current values...
+			save_flags(flags);
+			cli();
+
+			for (i = 0; i < 14; i++) {
+				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
+
+				while (!(inb(STATR) & CMD_RF)) {
+				};	//wait for response ready...
+
+				mixer_reg[i] = inw(CMDR);
+			}
+			restore_flags(flags);
+
+			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
+				return (-EFAULT);
+
+			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
+			return 0;
+		} else
+			return -EINVAL;
+	}
+#endif
+	return -EINVAL;
+}
 
 static inline void
 waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
@@ -368,10 +656,12 @@
 	udelay(20);
 	inw(hw->io_base + CMDR);	// discard second word == 0
 
-	rev[0] = temp >> 8;
-	rev[1] = temp & 255;
-	rev[2] = '\0';
-
+	if (rev)
+	{
+	    rev[0] = temp >> 8;
+	    rev[1] = temp & 255;
+	    rev[2] = '\0';
+	}
 	return temp;
 }
 
@@ -1368,138 +1658,12 @@
 waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
 	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
-#if 0
-	//use this call to override the automatic handset behaviour - ignore handset
-	//bit 0x80 = total control over handset - do not react to plug/unplug
-	//bit 0x10 = 1 == internal mic, otherwise handset mic
-	//bit 0x01 = 1 == mute internal speaker, otherwise unmute
-
-	if (cmd == SOUND_MIXER_PRIVATE1) {
-		int             val, temp;
-
-		val = *(int *) arg;
-
-//		printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val);
-		return -EINVAL;		//check if parameter is logical...
-
-		devc->soft_mute_flag = val;
-
-		temp = val & VNC_INTERNAL_SPKR;
-		if (temp != devc->mute_state) {
-//			printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp);
-			vnc_mute(devc, temp);
-		}
-
-//		temp = devc->handset_state;
-
-		// do not check if it is not already in
-		// the right setting, since we are
-		// laying about the current state...
-
-//		if ((val & VNC_INTERNAL_MIC) != temp) {
-			devc->handset_state = val & VNC_INTERNAL_MIC;
-//			printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state);
-			wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
-//			devc->handset_state = temp;
-		}
-		return 0;
-	}
-#if 0
-	if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
-#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
-		int             val;
-
-		val = *(int *) arg;
-
-		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
-		if (val == VNC_SOUND_PAUSE) {
-			wa_sendcmd(0x16);    //PAUSE the ADC
-		} else if (val == VNC_SOUND_RESUME) {
-			wa_sendcmd(0x18);    //RESUME the ADC
-		} else {
-			return -EINVAL;      //invalid parameters...
-		}
-		return 0;
-	}
-#endif
-
-	if (cmd == SOUND_MIXER_PRIVATE3) {
-		long unsigned   flags;
-		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
-
-		int             val;
-		int             i;
-
-		val = *(int *) arg;
 
-		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
-			return (-EFAULT);
-
-		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
-		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
-			wavnc_mixer_reset(devc);
-			return (0);
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
-//			printk("WaveArtist Mixer: Private write command.\n");
-
-			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
-			wa_sendcmd(mixer_reg[0]);
-			wa_sendcmd(mixer_reg[4]);
+	if (cmd == SOUND_MIXER_PRIVATE1 ||
+	    cmd == SOUND_MIXER_PRIVATE2 ||
+	    cmd == SOUND_MIXER_PRIVATE3)
+		return vnc_private_ioctl(dev, cmd, arg);
 
-			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
-			wa_sendcmd(mixer_reg[1]);
-			wa_sendcmd(mixer_reg[5]);
-
-			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
-			wa_sendcmd(mixer_reg[2]);
-			wa_sendcmd(mixer_reg[6]);
-
-			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
-			wa_sendcmd(mixer_reg[3]);
-			wa_sendcmd(mixer_reg[7]);
-
-			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
-			wa_sendcmd(mixer_reg[8]);
-			wa_sendcmd(mixer_reg[9]);
-
-			wa_sendcmd(0x0031);		//set left and right PCM
-			wa_sendcmd(mixer_reg[0x0A]);
-			wa_sendcmd(mixer_reg[0x0B]);
-
-			wa_sendcmd(0x0131);		//set left and right FM
-			wa_sendcmd(mixer_reg[0x0C]);
-			wa_sendcmd(mixer_reg[0x0D]);
-
-			return 0;
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
-//			printk("WaveArtist Mixer: Private read command.\n");
-
-			//first read all current values...
-			save_flags(flags);
-			cli();
-
-			for (i = 0; i < 14; i++) {
-				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
-
-				while (!(inb(STATR) & CMD_RF)) {
-				};	//wait for response ready...
-
-				mixer_reg[i] = inw(CMDR);
-			}
-			restore_flags(flags);
-
-			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
-				return (-EFAULT);
-
-			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
-			return 0;
-		} else
-			return -EINVAL;
-	}
-#endif
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 			int val;
@@ -1660,143 +1824,11 @@
 	return -1;
 }
 
-/*
- * Corel Netwinder specifics...
- */
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
-	extern spinlock_t gpio_lock;
-	unsigned long flags;
-
-	spin_lock_irqsave(&gpio_lock, flags);
-	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
-	spin_unlock_irqrestore(&gpio_lock, flags);
-
-	devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
-	static signed int old_slider_volume;
-	unsigned long flags;
-	signed int volume = 255;
-
-	*CSR_TIMER1_LOAD = 0x00ffffff;
-
-	save_flags(flags);
-	cli();
-
-	outb(0xFF, 0x201);
-	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
-	while (volume && (inb(0x201) & 0x01))
-		volume--;
-
-	*CSR_TIMER1_CNTL = 0;
-
-	restore_flags(flags);
-	
-	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
-	volume = 150 - (volume >> 5);
-#else
-	volume = (volume >> 6) - 25;
-#endif
-
-	if (volume < 0)
-		volume = 0;
-
-	if (volume > 100)
-		volume = 100;
-
-	/*
-	 * slider quite often reads +-8, so debounce this random noise
-	 */
-	if ((volume - old_slider_volume) > 7 ||
-	    (old_slider_volume - volume) > 7) {
-		old_slider_volume = volume;
-
-		DEB(printk("Slider volume: %d.\n", old_slider_volume));
-	}
-
-	return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
-	signed int slider_volume;
-	unsigned int temp;
-
-	/*
-	 * read the "buttons" state.
-	 *  Bit 4 = handset present,
-	 *  Bit 5 = offhook
-	 */
-	// the state should be "querable" via a private IOCTL call
-	temp = inb(0x201) & 0x30;
-
-	if (!devc->handset_mute_sw &&
-	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
-		devc->handset_state = temp;
-		devc->handset_mute_sw = 0;
-
-		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
-	}
-
-	slider_volume = vnc_volume_slider(devc);
-
-	/*
-	 * If we're using software controlled volume, and
-	 * the slider moves by more than 20%, then we
-	 * switch back to slider controlled volume.
-	 */
-	if (devc->slider_vol > slider_volume) {
-		if (devc->slider_vol - slider_volume > 20)
-			devc->use_slider = 1;
-	} else {
-		if (slider_volume - devc->slider_vol > 20)
-			devc->use_slider = 1;
-	}
-
-	/*
-	 * use only left channel
-	 */
-	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
-	if (slider_volume != temp && devc->use_slider) {
-		devc->slider_vol = slider_volume;
-
-		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
-			slider_volume | slider_volume << 8);
-
-		return 1;
-	}
-
-	return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
-	int next_timeout;
-
-	if (vnc_slider(adev_info + data))
-		next_timeout = 5;	// mixer reported change
-	else
-		next_timeout = VNC_TIMER_PERIOD;
-
-	mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
 int
 probe_waveartist(struct address_info *hw_config)
 {
 	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+	int temp;
 
 	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
 		printk("waveartist: too many audio devices\n");
@@ -1808,14 +1840,39 @@
 		return 0;
 	}
 
-	if (hw_config->irq > 31 || hw_config->irq < 16) {
+	if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
 		printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
 		return 0;
 	}
 
-	if (hw_config->dma != 3) {
+	if (hw_config->dma != _ISA_DMA(3)) {
 		printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
 		return 0;
+	}
+
+	/*
+	 * check if hardware is there... Get version, expect something
+	 * like 'B1' = 0x4231 first check if SB regs are present - if
+	 * so - WA is there as well.  Try to write to the SB CD volume
+	 * mixer reg (and read it back) if OK - the hardware is there.
+	 * If bad rev the first time, wait and retry...
+	 */
+	outb(0x28, 0x224);
+	outb(0x55, 0x225);
+	if (inb(0x225) != 0x55) {
+		printk(KERN_INFO "WaveArtist: probe failed at port 0x%X.\n",
+			hw_config->io_base);
+		return 0;
+	}
+
+	temp = waveartist_getrev(hw_config, 0);
+	if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		mdelay(200);
+		temp = waveartist_getrev(hw_config, 0);
+		if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		    	printk(KERN_DEBUG "WaveArtist probe: error reading "
+				"device rev.(0x%04X).\n", temp);
+		}
 	}
 
 	hw_config->name = "WaveArtist";
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/waveartist.h linux.18p17/drivers/sound/waveartist.h
--- linux.vanilla/drivers/sound/waveartist.h	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/waveartist.h	Fri Sep 15 23:31:12 2000
@@ -1,17 +1,12 @@
 
-// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder
+// def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
 
 //registers
-#define	WA_BASE	0
-//x250
-
-#define CMDR	WA_BASE+0
-#define DATR	WA_BASE+2
-
-#define CTLR	WA_BASE+4
-#define	STATR	WA_BASE+5
-
-#define	IRQSTAT	WA_BASE+12
+#define CMDR	0
+#define DATR	2
+#define CTLR	4
+#define	STATR	5
+#define	IRQSTAT	12
 
 //bit defs
 //reg STATR
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/wavfront.c linux.18p17/drivers/sound/wavfront.c
--- linux.vanilla/drivers/sound/wavfront.c	Fri Apr 21 12:46:36 2000
+++ linux.18p17/drivers/sound/wavfront.c	Mon Oct  2 10:19:25 2000
@@ -87,14 +87,14 @@
  
 #if defined(__alpha__)
 #ifdef __SMP__
-#define LOOPS_PER_SEC cpu_data[smp_processor_id()].loops_per_sec
+#define LOOPS_PER_TICK (cpu_data[smp_processor_id()].loops_per_jiffy)
 #else
-#define LOOPS_PER_SEC	loops_per_sec
+#define LOOPS_PER_TICK	(loops_per_jiffy)
 #endif
 #endif
 
 #if defined(__i386__)
-#define LOOPS_PER_SEC current_cpu_data.loops_per_sec
+#define LOOPS_PER_TICK current_cpu_data.loops_per_jiffy
 #endif
  
 #define _MIDI_SYNTH_C_
@@ -459,7 +459,7 @@
 
 	if (short_loop_cnt == 0) {
 		short_loop_cnt = wait_usecs *
-			(LOOPS_PER_SEC / 1000000);
+			(LOOPS_PER_TICK / (1000000 / HZ));
 	}
 
 	/* Spin for a short period of time, because >99% of all
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.c linux.18p17/drivers/sound/ymfpci.c
--- linux.vanilla/drivers/sound/ymfpci.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/sound/ymfpci.c	Mon Oct  2 10:00:45 2000
@@ -0,0 +1,2394 @@
+/*
+ *  Copyright 1999 Jaroslav Kysela <perex@suse.cz>
+ *  Copyright 2000 Alan Cox <alan@redhat.com>
+ *
+ *  Yamaha YMF7xx driver.
+ *
+ *  This code is a result of high-speed collision
+ *  between ymfpci.c of ALSA and cs46xx.c of Linux.
+ *  -- Pete Zaitcev <zaitcev@metabyte.com>; 2000/09/18
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO:
+ *  - Use P44Slot for 44.1 playback.
+ *  - Capture and duplex
+ *  - 96KHz playback for DVD - use pitch of 2.0.
+ *  - uLaw for Sun apps.
+ *  - Retain DMA buffer on close, do not wait the end of frame.
+ *  - Cleanup
+ *      ? merge ymf_pcm and state
+ *      ? pcm interrupt no pointer
+ *      ? underused structure members
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/poll.h>
+#include <linux/ac97_codec.h>
+#include <linux/sound.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#include "ymfpci.h"
+
+#define snd_magic_cast(t, p, err)	((t *)(p))
+
+/* Channels, such as play and record. I do only play a.t.m. XXX */
+#define NR_HW_CH	1
+
+static void ymfpci_free(ymfpci_t *codec);
+static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd);
+static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
+    int pair, ymfpci_voice_t **rvoice);
+static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
+static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state);
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance);
+
+static ymfpci_t *ymf_devs = NULL;
+
+/*
+ *  constants
+ */
+
+static struct ymf_devid {
+	int id;
+	char *name;
+} ymf_devv[] = {
+	{ PCI_DEVICE_ID_YAMAHA_724,  "YMF724" },
+	{ PCI_DEVICE_ID_YAMAHA_724F, "YMF724F" },
+	{ PCI_DEVICE_ID_YAMAHA_740,  "YMF740" },
+	{ PCI_DEVICE_ID_YAMAHA_740C, "YMF740C" },
+	{ PCI_DEVICE_ID_YAMAHA_744,  "YMF744" },
+	{ PCI_DEVICE_ID_YAMAHA_754,  "YMF754" },
+};
+
+/*
+ * Mindlessly copied from cs46xx XXX
+ */
+extern __inline__ unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/*
+ *  common I/O routines
+ */
+
+static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset)
+{
+	return readb(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val)
+{
+	writeb(val, codec->reg_area_virt + offset);
+}
+
+static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset)
+{
+	return readw(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val)
+{
+	writew(val, codec->reg_area_virt + offset);
+}
+
+static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset)
+{
+	return readl(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val)
+{
+	writel(val, codec->reg_area_virt + offset);
+}
+
+static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched)
+{
+	signed long end_time;
+	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
+	
+	end_time = jiffies + 3 * (HZ / 4);
+	do {
+		if ((ymfpci_readw(codec, reg) & 0x8000) == 0)
+			return 0;
+		if (sched) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		}
+	} while (end_time - (signed long)jiffies >= 0);
+	printk("ymfpci_codec_ready: codec %i is not ready [0x%x]\n",
+	    secondary, ymfpci_readw(codec, reg));
+	return -EBUSY;
+}
+
+static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val)
+{
+	ymfpci_t *codec = dev->private_data;
+	u32 cmd;
+
+	/* XXX Do make use of dev->id */
+	ymfpci_codec_ready(codec, 0, 0);
+	cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
+	ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd);
+}
+
+static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg)
+{
+	ymfpci_t *codec = dev->private_data;
+
+	if (ymfpci_codec_ready(codec, 0, 0))
+		return ~0;
+	ymfpci_writew(codec, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
+	if (ymfpci_codec_ready(codec, 0, 0))
+		return ~0;
+	if (codec->device_id == PCI_DEVICE_ID_YAMAHA_744 && codec->rev < 2) {
+		int i;
+		for (i = 0; i < 600; i++)
+			ymfpci_readw(codec, YDSXGR_PRISTATUSDATA);
+	}
+	return ymfpci_readw(codec, YDSXGR_PRISTATUSDATA);
+}
+
+/*
+ *  Misc routines
+ */
+
+/*
+ * Calculate the actual sampling rate relatetively to the base clock (48kHz).
+ */
+static u32 ymfpci_calc_delta(u32 rate)
+{
+	switch (rate) {
+	case 8000:	return 0x02aaab00;
+	case 11025:	return 0x03accd00;
+	case 16000:	return 0x05555500;
+	case 22050:	return 0x07599a00;
+	case 32000:	return 0x0aaaab00;
+	case 44100:	return 0x0eb33300;
+	default:	return ((rate << 16) / 48000) << 12;
+	}
+}
+
+static u32 def_rate[8] = {
+	100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
+};
+
+static u32 ymfpci_calc_lpfK(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
+		0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x40000000;	/* FIXME: What's the right value? */
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+static u32 ymfpci_calc_lpfQ(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x35280000, 0x34A70000, 0x32020000, 0x31770000,
+		0x31390000, 0x31C90000, 0x33D00000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x370A0000;
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+static u32 ymf_calc_lend(u32 rate)
+{
+	return (rate * YMF_SAMPF) / 48000;
+}
+
+/*
+ * XXX Find if this function exists in the OSS framework.
+ * XXX Make sure we do no panic when ADPCM is selected.
+ */
+static int ymf_pcm_format_width(int format)
+{
+	static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE;
+
+	if ((format & (format-1)) != 0) {
+		printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format);
+		return 8;
+	}
+
+	if (format == AFMT_IMA_ADPCM) return 4;
+	if ((format & mask16) != 0) return 16;
+	return 8;
+}
+
+static void ymf_pcm_update_shift(struct ymf_pcm_format *f)
+{
+	f->shift = 0;
+	if (f->voices == 2)
+		f->shift++;
+	if (ymf_pcm_format_width(f->format) == 16)
+		f->shift++;
+}
+
+/*
+ * Whole OSS-style DMA machinery is taken from cs46xx.
+ */
+
+/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */
+#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
+static int alloc_dmabuf(struct ymf_state *state)
+{
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	void *rawbuf = NULL;
+	int order;
+	unsigned long map, mapend;
+
+	/* alloc as big a chunk as we can */
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
+			break;
+
+	if (!rawbuf)
+		return -ENOMEM;
+
+#if 0
+	printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, rawbuf);
+#endif
+
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->rawbuf = rawbuf;
+	dmabuf->buforder = order;
+
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1);
+	for (map = MAP_NR(rawbuf); map <= mapend; map++)
+		set_bit(PG_reserved, &mem_map[map].flags);
+
+	return 0;
+}
+
+/* free DMA buffer */
+static void dealloc_dmabuf(struct ymf_state *state)
+{
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	unsigned long map, mapend;
+
+	if (dmabuf->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = MAP_NR(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+		for (map = MAP_NR(dmabuf->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);
+		free_pages((unsigned long)dmabuf->rawbuf,dmabuf->buforder);
+	}
+	dmabuf->rawbuf = NULL;
+	dmabuf->mapped = dmabuf->ready = 0;
+}
+
+static int prog_dmabuf(struct ymf_state *state, unsigned rec)
+{
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	int w_16;
+	unsigned bytepersec;
+	unsigned bufsize;
+	unsigned long flags;
+	int redzone;
+	int ret;
+
+	w_16 = ymf_pcm_format_width(state->format.format) == 16;
+
+	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	dmabuf->hwptr = dmabuf->swptr = 0;
+	dmabuf->total_bytes = 0;
+	dmabuf->count = dmabuf->error = 0;
+	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+	/* allocate DMA buffer if not allocated yet */
+	if (!dmabuf->rawbuf)
+		if ((ret = alloc_dmabuf(state)))
+			return ret;
+
+	bytepersec = state->format.rate << state->format.shift;
+
+	/*
+	 * Create fake fragment sizes and numbers for OSS ioctls.
+	 */
+	bufsize = PAGE_SIZE << dmabuf->buforder;
+	if (dmabuf->ossfragshift) {
+		if ((1000 << dmabuf->ossfragshift) < bytepersec)
+			dmabuf->fragshift = ld2(bytepersec/1000);
+		else
+			dmabuf->fragshift = dmabuf->ossfragshift;
+	} else {
+		/* lets hand out reasonable big ass buffers by default */
+		dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
+	}
+	dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+	while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
+		dmabuf->fragshift--;
+		dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+	}
+	dmabuf->fragsize = 1 << dmabuf->fragshift;
+	dmabuf->fragsamples = dmabuf->fragsize >> state->format.shift;
+	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+	/*
+	 * Import what Doom might have set with SNDCTL_DSD_SETFRAGMENT.
+	 */
+	if (dmabuf->ossmaxfrags >= 2 && dmabuf->ossmaxfrags < dmabuf->numfrag) {
+		dmabuf->numfrag = dmabuf->ossmaxfrags;
+		dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= (state->format.shift + 1);
+		if (dmabuf->dmasize < redzone*3) {
+			/*
+			 * The driver works correctly with minimum dmasize
+			 * of redzone*2, but it produces stoppage and clicks.
+			 * So, make it little larger for smoother sound.
+			 * XXX Make dmasize a wholy divisible by fragsize.
+			 */
+//			printk(KERN_ERR "ymfpci: dmasize=%d < redzone=%d * 3\n",
+//			    dmabuf->dmasize, redzone);
+			dmabuf->dmasize = redzone*3;
+		}
+	}
+
+	memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize);
+
+	/*
+	 *	Now set up the ring 
+	 */
+
+	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	if (rec) {
+		/* ymf_rec_setup(state); */
+	} else {
+		if ((ret = ymf_playback_prepare(state->unit, state)) != 0) {
+			return ret;
+		}
+	}
+	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+	/* set the ready flag for the dma buffer (this comment is not stupid) */
+	dmabuf->ready = 1;
+
+#if 0
+	printk("prog_dmabuf: rate %d format 0x%x,"
+	    " numfrag %d fragsize %d dmasize %d\n",
+	       state->format.rate, state->format.format, dmabuf->numfrag,
+	       dmabuf->fragsize, dmabuf->dmasize);
+#endif
+
+	return 0;
+}
+
+static void ymf_start_dac(struct ymf_state *state)
+{
+	ymf_playback_trigger(state->unit, &state->ypcm, 1);
+}
+
+/*
+ * Wait until output is drained.
+ * This does not kill the hardware for the sake of ioctls.
+ */
+static void ymf_wait_dac(struct ymf_state *state)
+{
+	struct ymf_unit *unit = state->unit;
+	ymfpci_pcm_t *ypcm = &state->ypcm;
+	struct wait_queue waita = { current, NULL };
+	unsigned long flags;
+
+	add_wait_queue(&state->dmabuf.wait, &waita);
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	if (state->dmabuf.count != 0 && !state->ypcm.running) {
+		ymf_playback_trigger(unit, ypcm, 1);
+	}
+
+#if 0
+	if (file->f_flags & O_NONBLOCK) {
+		/*
+		 * XXX Our  mistake is to attach DMA buffer to state
+		 * rather than to some per-device structure.
+		 * Cannot skip waiting, can only make it shorter.
+		 */
+	}
+#endif
+
+	while (ypcm->running) {
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+		spin_lock_irqsave(&unit->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&state->dmabuf.wait, &waita);
+
+	/*
+	 * This function may take up to 4 seconds to reach this point
+	 * (32K circular buffer, 8000 Hz). User notices.
+	 */
+}
+
+/*
+ *  Hardware start management
+ */
+
+static void ymfpci_hw_start(ymfpci_t *codec)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (codec->start_count++ == 0) {
+		ymfpci_writel(codec, YDSXGR_MODE, 3);
+		codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1;
+	}
+      	spin_unlock_irqrestore(&codec->reg_lock, flags);
+}
+
+static void ymfpci_hw_stop(ymfpci_t *codec)
+{
+	unsigned long flags;
+	long timeout = 1000;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (--codec->start_count == 0) {
+		ymfpci_writel(codec, YDSXGR_MODE, 0);
+		while (timeout-- > 0) {
+			if ((ymfpci_readl(codec, YDSXGR_STATUS) & 2) == 0)
+				break;
+		}
+	}
+      	spin_unlock_irqrestore(&codec->reg_lock, flags);
+}
+
+/*
+ *  Playback voice management
+ */
+
+static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+	ymfpci_voice_t *voice, *voice2;
+	int idx;
+	
+	*rvoice = NULL;
+	for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
+		voice = &codec->voices[idx];
+		voice2 = pair ? &codec->voices[idx+1] : NULL;
+		if (voice->use || (voice2 && voice2->use))
+			continue;
+		voice->use = 1;
+		if (voice2)
+			voice2->use = 1;
+		switch (type) {
+		case YMFPCI_PCM:
+			voice->pcm = 1;
+			if (voice2)
+				voice2->pcm = 1;
+			break;
+		case YMFPCI_SYNTH:
+			voice->synth = 1;
+			break;
+		case YMFPCI_MIDI:
+			voice->midi = 1;
+			break;
+		}
+		ymfpci_hw_start(codec);
+		if (voice2)
+			ymfpci_hw_start(codec);
+		*rvoice = voice;
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,
+    int pair, ymfpci_voice_t **rvoice)
+{
+	unsigned long flags;
+	int result;
+
+	spin_lock_irqsave(&codec->voice_lock, flags);
+	for (;;) {
+		result = voice_alloc(codec, type, pair, rvoice);
+		if (result == 0 || type != YMFPCI_PCM)
+			break;
+		/* TODO: synth/midi voice deallocation */
+		break;
+	}
+	spin_unlock_irqrestore(&codec->voice_lock, flags);	
+	return result;		
+}
+
+static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice)
+{
+	unsigned long flags;
+	
+	ymfpci_hw_stop(codec);
+	spin_lock_irqsave(&codec->voice_lock, flags);
+	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+	pvoice->ypcm = NULL;
+	pvoice->interrupt = NULL;
+	spin_unlock_irqrestore(&codec->voice_lock, flags);
+	return 0;
+}
+
+/*
+ *  PCM part
+ */
+
+static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
+{
+	ymfpci_pcm_t *ypcm;
+	int redzone;
+	int pos, delta, swptr;
+	int played, distance;
+	struct ymf_state *state;
+	struct ymf_dmabuf *dmabuf;
+	char silence;
+
+	if ((ypcm = voice->ypcm) == NULL) {
+/* P3 */ printk("ymf_pcm_interrupt: voice %d: no ypcm\n", voice->number);
+		return;
+	}
+	if ((state = ypcm->state) == NULL) {
+/* P3 */ printk("ymf_pcm_interrupt: voice %d: no state\n", voice->number);
+		ypcm->running = 0;	// lock it
+		return;
+	}
+	dmabuf = &state->dmabuf;
+	spin_lock(&codec->reg_lock);
+	if (ypcm->running) {
+/* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
+  voice->number, codec->active_bank, dmabuf->count,
+  voice->bank[0].start, voice->bank[1].start); **/
+		silence = (ymf_pcm_format_width(state->format.format) == 16) ?
+		    0 : 0x80;
+		/* We need actual left-hand-side redzone size here. */
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= (state->format.shift + 1);
+		swptr = dmabuf->swptr;
+
+		pos = voice->bank[codec->active_bank].start;
+		pos <<= state->format.shift;
+		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
+			printk(KERN_ERR
+			    "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",
+			    codec->inst, voice->number,
+			    dmabuf->hwptr, dmabuf->dmasize);
+			pos = 0;
+		}
+		if (pos < dmabuf->hwptr) {
+			delta = dmabuf->dmasize - dmabuf->hwptr;
+			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);
+			delta += pos;
+			memset(dmabuf->rawbuf, silence, pos);
+		} else {
+			delta = pos - dmabuf->hwptr;
+			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);
+		}
+		dmabuf->hwptr = pos;
+
+		if (dmabuf->count == 0) {
+			printk("ymfpci%d: %d: strain: hwptr %d\n",
+			    codec->inst, voice->number, dmabuf->hwptr);
+			ymf_playback_trigger(codec, ypcm, 0);
+		}
+
+		if (swptr <= pos) {
+			distance = pos - swptr;
+		} else {
+			distance = dmabuf->dmasize - (swptr - pos);
+		}
+		if (distance < redzone) {
+			/*
+			 * hwptr inside redzone => DMA ran out of samples.
+			 */
+			if (delta < dmabuf->count) {
+				/*
+				 * Lost interrupt or other screwage.
+				 */
+				printk("ymfpci%d: %d: lost: delta %d"
+				    " hwptr %d swptr %d distance %d count %d\n",
+				    codec->inst, voice->number, delta,
+				    dmabuf->hwptr, swptr, distance, dmabuf->count);
+			} else {
+				/*
+				 * Normal end of DMA.
+				 */
+//				printk("ymfpci%d: %d: done: delta %d"
+//				    " hwptr %d swptr %d distance %d count %d\n",
+//				    codec->inst, voice->number, delta,
+//				    dmabuf->hwptr, swptr, distance, dmabuf->count);
+			}
+			played = dmabuf->count;
+			if (ypcm->running) {
+				ymf_playback_trigger(codec, ypcm, 0);
+			}
+		} else {
+			/*
+			 * hwptr is chipping away towards a remote swptr.
+			 * Calculate other distance and apply it to count.
+			 */
+			if (swptr >= pos) {
+				distance = swptr - pos;
+			} else {
+				distance = dmabuf->dmasize - (pos - swptr);
+			}
+			if (distance < dmabuf->count) {
+				played = dmabuf->count - distance;
+			} else {
+				played = 0;
+			}
+		}
+
+		dmabuf->total_bytes += played;
+		dmabuf->count -= played;
+		if (dmabuf->count < dmabuf->dmasize / 2) {
+			wake_up(&dmabuf->wait);
+		}
+	}
+	spin_unlock(&codec->reg_lock);
+}
+
+#if HAVE_RECORD
+static void ymfpci_pcm_capture_interrupt(snd_pcm_subchn_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, );
+	ymfpci_t *codec = ypcm->codec;
+	u32 pos, delta;
+	
+	spin_lock(&codec->reg_lock);
+	if (ypcm->running) {
+		pos = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
+		if (pos < ypcm->last_pos)  // <--  dmabuf->hwptr
+			delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+		else
+			delta = pos - ypcm->last_pos;
+		ypcm->frag_pos += delta;
+		ypcm->last_pos = pos;
+		while (ypcm->frag_pos >= ypcm->frag_size) {
+			ypcm->frag_pos -= ypcm->frag_size;
+			// printk("done - active_bank = 0x%x, start = 0x%x\n", codec->active_bank, voice->bank[codec->active_bank].start);
+			spin_unlock(&codec->reg_lock);
+			snd_pcm_transfer_done(substream);
+			spin_lock(&codec->reg_lock);
+		}
+	}
+	spin_unlock(&codec->reg_lock);
+}
+#endif
+
+static int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd)
+{
+
+	if (ypcm->voices[0] == NULL) {
+/* P3 */ printk("ymfpci: trigger %d no voice\n", cmd);
+		return -EINVAL;
+	}
+	if (cmd != 0) {
+		codec->ctrl_playback[ypcm->voices[0]->number + 1] = virt_to_bus(ypcm->voices[0]->bank);
+		if (ypcm->voices[1] != NULL)
+			codec->ctrl_playback[ypcm->voices[1]->number + 1] = virt_to_bus(ypcm->voices[1]->bank);
+		ypcm->running = 1;
+	} else {
+		codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
+		if (ypcm->voices[1] != NULL)
+			codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
+		ypcm->running = 0;
+	}
+	return 0;
+}
+
+#if HAVE_RECORD
+static int ymfpci_capture_trigger(void *private_data,	
+				      snd_pcm_subchn_t * substream,
+				      int cmd)
+{
+	unsigned long flags;
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, -ENXIO);
+	int result = 0;
+	u32 tmp;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (cmd == SND_PCM_TRIGGER_GO) {
+		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
+		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 1;
+	} else if (cmd == SND_PCM_TRIGGER_STOP) {
+		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
+		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 0;
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	return result;
+}
+#endif
+
+static int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+{
+	int err;
+
+	if (ypcm->voices[1] != NULL && voices < 2) {
+		ymfpci_voice_free(ypcm->codec, ypcm->voices[1]);
+		ypcm->voices[1] = NULL;
+	}
+	if (voices == 1 && ypcm->voices[0] != NULL)
+		return 0;		/* already allocated */
+	if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
+		return 0;		/* already allocated */
+	if (voices > 1) {
+		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
+			ymfpci_voice_free(ypcm->codec, ypcm->voices[0]);
+			ypcm->voices[0] = NULL;
+		}		
+	}
+	err = ymfpci_voice_alloc(ypcm->codec, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
+	if (err < 0)
+		return err;
+	ypcm->voices[0]->ypcm = ypcm;
+	ypcm->voices[0]->interrupt = ymf_pcm_interrupt;
+	if (voices > 1) {
+		ypcm->voices[1] = &ypcm->codec->voices[ypcm->voices[0]->number + 1];
+		ypcm->voices[1]->ypcm = ypcm;
+	}
+	return 0;
+}
+
+static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
+    int rate, int w_16, unsigned long addr, unsigned int end, int spdif)
+{
+	u32 format;
+	u32 delta = ymfpci_calc_delta(rate);
+	u32 lpfQ = ymfpci_calc_lpfQ(rate);
+	u32 lpfK = ymfpci_calc_lpfK(rate);
+	ymfpci_playback_bank_t *bank;
+	int nbank;
+
+	format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);
+	if (stereo)
+		end >>= 1;
+	if (w_16)
+		end >>= 1;
+/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",
+//  voice->number, rate, format, delta, end);
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = &voice->bank[nbank];
+		bank->format = format;
+		bank->loop_default = 0;	/* 0-loops forever, otherwise count */
+		bank->base = addr;
+		bank->loop_start = 0;
+		bank->loop_end = end;
+		bank->loop_frac = 0;
+		bank->eg_gain_end = 0x40000000;
+		bank->lpfQ = lpfQ;
+		bank->status = 0;
+		bank->num_of_frames = 0;
+		bank->loop_count = 0;
+		bank->start = 0;
+		bank->start_frac = 0;
+		bank->delta =
+		bank->delta_end = delta;
+		bank->lpfK =
+		bank->lpfK_end = lpfK;
+		bank->eg_gain = 0x40000000;
+		bank->lpfD1 =
+		bank->lpfD2 = 0;
+
+		bank->left_gain = 
+		bank->right_gain =
+		bank->left_gain_end =
+		bank->right_gain_end =
+		bank->eff1_gain =
+		bank->eff2_gain =
+		bank->eff3_gain =
+		bank->eff1_gain_end =
+		bank->eff2_gain_end =
+		bank->eff3_gain_end = 0;
+
+		if (!stereo) {
+			if (!spdif) {
+				bank->left_gain = 
+				bank->right_gain =
+				bank->left_gain_end =
+				bank->right_gain_end = 0x40000000;
+			} else {
+				bank->eff2_gain =
+				bank->eff2_gain_end =
+				bank->eff3_gain =
+				bank->eff3_gain_end = 0x40000000;
+			}
+		} else {
+			if (!spdif) {
+				if ((voice->number & 1) == 0) {
+					bank->format |= 1;
+					bank->left_gain =
+					bank->left_gain_end = 0x40000000;
+				} else {
+					bank->right_gain =
+					bank->right_gain_end = 0x40000000;
+				}
+			} else {
+				if ((voice->number & 1) == 0) {
+					bank->format |= 1;
+					bank->eff2_gain =
+					bank->eff2_gain_end = 0x40000000;
+				} else {
+					bank->eff3_gain =
+					bank->eff3_gain_end = 0x40000000;
+				}
+			}
+		}
+	}
+}
+
+/*
+ * XXX Use new cache coherent PCI DMA routines instead of virt_to_bus.
+ */
+static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state)
+{
+	ymfpci_pcm_t *ypcm = &state->ypcm;
+	int err, nvoice;
+
+	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
+		/* Cannot be unless we leak voices in ymf_release! */
+		printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",
+		    codec->inst);
+		return err;
+	}
+
+	for (nvoice = 0; nvoice < state->format.voices; nvoice++) {
+		ymf_pcm_init_voice(ypcm->voices[nvoice],
+		    state->format.voices == 2, state->format.rate,
+		    ymf_pcm_format_width(state->format.format) == 16,
+		    virt_to_bus(state->dmabuf.rawbuf), state->dmabuf.dmasize,
+		    ypcm->spdif);
+	}
+	return 0;
+}
+
+#if 0  /* old */
+static int ymfpci_capture_prepare(void *private_data,
+				      snd_pcm_subchn_t * substream)
+{
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+	ymfpci_capture_bank_t * bank;
+	int nbank;
+	u32 rate, format;
+
+	ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
+	ypcm->buffer_size = snd_pcm_lib_transfer_size(substream);
+	ypcm->frag_pos = 0;
+	ypcm->last_pos = 0;
+	ypcm->shift_offset = 0;
+	rate = ((48000 * 4096) / runtime->format.rate) - 1;
+	format = 0;
+	if (runtime->format.voices == 2)
+		format |= 2;
+	if (snd_pcm_format_width(runtime->format.format) == 8)
+		format |= 1;
+	switch (ypcm->capture_bank_number) {
+	case 0:
+		ymfpci_writel(codec, YDSXGR_RECFORMAT, format);
+		ymfpci_writel(codec, YDSXGR_RECSLOTSR, rate);
+		break;
+	case 1:
+		ymfpci_writel(codec, YDSXGR_ADCFORMAT, format);
+		ymfpci_writel(codec, YDSXGR_ADCSLOTSR, rate);
+		break;
+	}
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = codec->bank_capture[ypcm->capture_bank_number][nbank];
+		bank->base = virt_to_bus(runtime->dma_area->buf);
+		bank->loop_end = ypcm->buffer_size;
+		bank->start = 0;
+		bank->num_of_loops = 0;
+	}
+	if (runtime->digital.dig_valid)
+		/*runtime->digital.type == SND_PCM_DIG_AES_IEC958*/
+		ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, runtime->digital.dig_status[0] |
+								(runtime->digital.dig_status[1] << 8));
+	return 0;
+}
+
+static unsigned int ymfpci_playback_pointer(void *private_data,
+						snd_pcm_subchn_t * substream)
+{
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+	ymfpci_voice_t *voice = ypcm->voices[0];
+	unsigned long flags;
+	unsigned int result;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (ypcm->running && voice)
+		result = voice->bank[codec->active_bank].start << ypcm->shift_offset;
+	else
+		result = 0;
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	return result;
+}
+
+static unsigned int ymfpci_capture_pointer(void *private_data,
+					       snd_pcm_subchn_t * substream)
+{
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+	unsigned long flags;
+	unsigned int result;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (ypcm->running)
+		result = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;
+	else
+		result = 0;
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	return result;
+}
+#endif /* old */
+
+void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ymfpci_t *codec = dev_id;
+	u32 status, nvoice, mode;
+	ymfpci_voice_t *voice;
+
+	status = ymfpci_readl(codec, YDSXGR_STATUS);
+	if (status & 0x80000000) {
+		spin_lock(&codec->reg_lock);
+		ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000);
+		mode = ymfpci_readl(codec, YDSXGR_MODE) | 2;
+		ymfpci_writel(codec, YDSXGR_MODE, mode);
+		spin_unlock(&codec->reg_lock);
+		codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1;
+		spin_lock(&codec->voice_lock);
+		for (nvoice = 0; nvoice < 64; nvoice++) {
+			voice = &codec->voices[nvoice];
+			if (voice->interrupt)
+				voice->interrupt(codec, voice);
+		}
+		spin_unlock(&codec->voice_lock);
+	}
+
+	status = ymfpci_readl(codec, YDSXGR_INTFLAG);
+	if (status & 1) {
+		/* timer handler */
+		ymfpci_writel(codec, YDSXGR_INTFLAG, ~0);
+	}
+}
+
+static void ymf_pcm_free_substream(ymfpci_pcm_t *ypcm)
+{
+	ymfpci_t *codec;
+
+	if (ypcm) {
+		codec = ypcm->codec;
+		if (ypcm->voices[1])
+			ymfpci_voice_free(codec, ypcm->voices[1]);
+		if (ypcm->voices[0])
+			ymfpci_voice_free(codec, ypcm->voices[0]);
+	}
+}
+
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance)
+{
+	ymfpci_pcm_t *ypcm;
+	struct ymf_state *state;
+
+	if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) {
+		goto out0;
+	}
+	memset(state, 0, sizeof(struct ymf_state));
+
+	ypcm = &state->ypcm;
+	ypcm->state = state;
+	ypcm->codec = unit;
+	ypcm->type = PLAYBACK_VOICE;
+
+	state->unit = unit;
+	state->virt = nvirt;
+
+	state->format.format = AFMT_U8;
+	state->format.rate = 8000;
+	state->format.voices = 1;
+	ymf_pcm_update_shift(&state->format);
+
+	unit->states[nvirt] = state;
+	return 0;
+
+out0:
+	return -ENOMEM;
+}
+
+#if HAVE_RECORD
+
+static int ymfpci_capture_open(void *private_data,
+				   snd_pcm_subchn_t * substream,
+				   u32 capture_bank_number)
+{
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+	int err;
+
+	if ((err = snd_pcm_dma_alloc(substream, !capture_bank_number ? codec->dma2ptr : codec->dma3ptr, "YMFPCI - ADC")) < 0)
+		return err;
+	ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL);
+	if (ypcm == NULL) {
+		snd_pcm_dma_free(substream);
+		return -ENOMEM;
+	}
+	ypcm->codec = codec;
+	ypcm->type = capture_bank_number + CAPTURE_REC;
+	ypcm->substream = substream;	
+	ypcm->capture_bank_number = capture_bank_number;
+	codec->capture_substream[capture_bank_number] = substream;
+	runtime->hw = &ymfpci_capture;
+	snd_pcm_set_mixer(substream, codec->mixer->device, codec->ac97->me_capture);
+	runtime->private_data = ypcm;
+	runtime->private_free = ymfpci_pcm_free_substream;
+	ymfpci_hw_start(codec);
+	return 0;
+}
+
+#endif  /* old */
+
+/* AES/IEC958 channel status bits */
+#define SND_PCM_AES0_PROFESSIONAL	(1<<0)	/* 0 = consumer, 1 = professional */
+#define SND_PCM_AES0_NONAUDIO		(1<<1)	/* 0 = audio, 1 = non-audio */
+#define SND_PCM_AES0_PRO_EMPHASIS	(7<<2)	/* mask - emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_NOTID	(0<<2)	/* emphasis not indicated */
+#define SND_PCM_AES0_PRO_EMPHASIS_NONE	(1<<2)	/* none emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_5015	(3<<2)	/* 50/15us emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_CCITT	(7<<2)	/* CCITT J.17 emphasis */
+#define SND_PCM_AES0_PRO_FREQ_UNLOCKED	(1<<5)	/* source sample frequency: 0 = locked, 1 = unlocked */
+#define SND_PCM_AES0_PRO_FS		(3<<6)	/* mask - sample frequency */
+#define SND_PCM_AES0_PRO_FS_NOTID	(0<<6)	/* fs not indicated */
+#define SND_PCM_AES0_PRO_FS_44100	(1<<6)	/* 44.1kHz */
+#define SND_PCM_AES0_PRO_FS_48000	(2<<6)	/* 48kHz */
+#define SND_PCM_AES0_PRO_FS_32000	(3<<6)	/* 32kHz */
+#define SND_PCM_AES0_CON_NOT_COPYRIGHT	(1<<2)	/* 0 = copyright, 1 = not copyright */
+#define SND_PCM_AES0_CON_EMPHASIS	(7<<3)	/* mask - emphasis */
+#define SND_PCM_AES0_CON_EMPHASIS_NONE	(0<<3)	/* none emphasis */
+#define SND_PCM_AES0_CON_EMPHASIS_5015	(1<<3)	/* 50/15us emphasis */
+#define SND_PCM_AES0_CON_MODE		(3<<6)	/* mask - mode */
+#define SND_PCM_AES1_PRO_MODE		(15<<0)	/* mask - channel mode */
+#define SND_PCM_AES1_PRO_MODE_NOTID	(0<<0)	/* not indicated */
+#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */
+#define SND_PCM_AES1_PRO_MODE_SINGLE	(4<<0)	/* single channel */
+#define SND_PCM_AES1_PRO_MODE_TWO	(8<<0)	/* two channels */
+#define SND_PCM_AES1_PRO_MODE_PRIMARY	(12<<0)	/* primary/secondary */
+#define SND_PCM_AES1_PRO_MODE_BYTE3	(15<<0)	/* vector to byte 3 */
+#define SND_PCM_AES1_PRO_USERBITS	(15<<4)	/* mask - user bits */
+#define SND_PCM_AES1_PRO_USERBITS_NOTID	(0<<4)	/* not indicated */
+#define SND_PCM_AES1_PRO_USERBITS_192	(8<<4)	/* 192-bit structure */
+#define SND_PCM_AES1_PRO_USERBITS_UDEF	(12<<4)	/* user defined application */
+#define SND_PCM_AES1_CON_CATEGORY	0x7f
+#define SND_PCM_AES1_CON_GENERAL	0x00
+#define SND_PCM_AES1_CON_EXPERIMENTAL	0x40
+#define SND_PCM_AES1_CON_SOLIDMEM_MASK	0x0f
+#define SND_PCM_AES1_CON_SOLIDMEM_ID	0x08
+#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07
+#define SND_PCM_AES1_CON_BROADCAST1_ID	0x04
+#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07
+#define SND_PCM_AES1_CON_DIGDIGCONV_ID	0x02
+#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
+#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06
+#define SND_PCM_AES1_CON_ADC_MASK	0x1f
+#define SND_PCM_AES1_CON_ADC_ID		0x16
+#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f
+#define SND_PCM_AES1_CON_BROADCAST2_ID	0x0e
+#define SND_PCM_AES1_CON_LASEROPT_MASK	0x07
+#define SND_PCM_AES1_CON_LASEROPT_ID	0x01
+#define SND_PCM_AES1_CON_MUSICAL_MASK	0x07
+#define SND_PCM_AES1_CON_MUSICAL_ID	0x05
+#define SND_PCM_AES1_CON_MAGNETIC_MASK	0x07
+#define SND_PCM_AES1_CON_MAGNETIC_ID	0x03
+#define SND_PCM_AES1_CON_IEC908_CD	(SND_PCM_AES1_CON_LASEROPT_ID|0x00)
+#define SND_PCM_AES1_CON_NON_IEC908_CD	(SND_PCM_AES1_CON_LASEROPT_ID|0x08)
+#define SND_PCM_AES1_CON_PCM_CODER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00)
+#define SND_PCM_AES1_CON_SAMPLER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20)
+#define SND_PCM_AES1_CON_MIXER		(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10)
+#define SND_PCM_AES1_CON_RATE_CONVERTER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18)
+#define SND_PCM_AES1_CON_SYNTHESIZER	(SND_PCM_AES1_CON_MUSICAL_ID|0x00)
+#define SND_PCM_AES1_CON_MICROPHONE	(SND_PCM_AES1_CON_MUSICAL_ID|0x08)
+#define SND_PCM_AES1_CON_DAT		(SND_PCM_AES1_CON_MAGNETIC_ID|0x00)
+#define SND_PCM_AES1_CON_VCR		(SND_PCM_AES1_CON_MAGNETIC_ID|0x08)
+#define SND_PCM_AES1_CON_ORIGINAL	(1<<7)	/* this bits depends on the category code */
+#define SND_PCM_AES2_PRO_SBITS		(7<<0)	/* mask - sample bits */
+#define SND_PCM_AES2_PRO_SBITS_20	(2<<0)	/* 20-bit - coordination */
+#define SND_PCM_AES2_PRO_SBITS_24	(4<<0)	/* 24-bit - main audio */
+#define SND_PCM_AES2_PRO_SBITS_UDEF	(6<<0)	/* user defined application */
+#define SND_PCM_AES2_PRO_WORDLEN	(7<<3)	/* mask - source word length */
+#define SND_PCM_AES2_PRO_WORDLEN_NOTID	(0<<3)	/* not indicated */
+#define SND_PCM_AES2_PRO_WORDLEN_22_18	(2<<3)	/* 22-bit or 18-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_23_19	(4<<3)	/* 23-bit or 19-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_24_20	(5<<3)	/* 24-bit or 20-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_20_16	(6<<3)	/* 20-bit or 16-bit */
+#define SND_PCM_AES2_CON_SOURCE		(15<<0)	/* mask - source number */
+#define SND_PCM_AES2_CON_SOURCE_UNSPEC	(0<<0)	/* unspecified */
+#define SND_PCM_AES2_CON_CHANNEL	(15<<4)	/* mask - channel number */
+#define SND_PCM_AES2_CON_CHANNEL_UNSPEC	(0<<4)	/* unspecified */
+#define SND_PCM_AES3_CON_FS		(15<<0)	/* mask - sample frequency */
+#define SND_PCM_AES3_CON_FS_44100	(0<<0)	/* 44.1kHz */
+#define SND_PCM_AES3_CON_FS_48000	(2<<0)	/* 48kHz */
+#define SND_PCM_AES3_CON_FS_32000	(3<<0)	/* 32kHz */
+#define SND_PCM_AES3_CON_CLOCK		(3<<4)	/* mask - clock accuracy */
+#define SND_PCM_AES3_CON_CLOCK_1000PPM	(0<<4)	/* 1000 ppm */
+#define SND_PCM_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
+#define SND_PCM_AES3_CON_CLOCK_VARIABLE	(2<<4)	/* variable pitch */
+
+#if HAVE_RECORD  /* old */
+
+static int ymfpci_capture_close(void *private_data,
+				    snd_pcm_subchn_t * substream)
+{
+	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, -ENXIO);
+
+	if (ypcm != NULL) {
+		codec->capture_substream[ypcm->capture_bank_number] = NULL;
+		ymfpci_hw_stop(codec);
+	}
+	snd_pcm_dma_free(substream);
+	return 0;
+}
+#endif
+
+/*
+ * User interface
+ */
+
+static loff_t ymf_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
+   the user's buffer.  it is filled by the dma machine and drained by this loop. */
+static ssize_t ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+#if HAVE_RECORD
+	struct cs_state *state = (struct cs_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+#ifdef DEBUG
+	printk("cs461x: cs_read called, count = %d\n", count);
+#endif
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+	while (count > 0) {
+		spin_lock_irqsave(&state->card->lock, flags);
+		if (dmabuf->count > (signed) dmabuf->dmasize) {
+			/* buffer overrun, we are recovering from sleep_on_timeout,
+			   resync hwptr and swptr, make process flush the buffer */
+			dmabuf->count = dmabuf->dmasize;
+			dmabuf->swptr = dmabuf->hwptr;
+		}
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count < cnt)
+			cnt = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			/* buffer is empty, start the dma machine and wait for data to be
+			   recorded */
+			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 */
+			}
+			if (signal_pending(current)) {
+				ret = ret ? ret : -ERESTARTSYS;
+				return ret;
+			}
+			continue;
+		}
+
+		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
+			if (!ret) ret = -EFAULT;
+			return ret;
+		}
+
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		dmabuf->swptr = swptr;
+		dmabuf->count -= cnt;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(state);
+	}
+	return ret;
+#else
+	return -EINVAL;
+#endif
+}
+
+static ssize_t ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	struct wait_queue waita = { current, NULL };
+	ssize_t ret;
+	unsigned long flags;
+	unsigned int swptr;
+	int cnt;			/* This many to go in this revolution */
+	int redzone;
+	int delay;
+
+/* P3 */ /* printk("ymf_write: count %d\n", count); */
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+	/*
+	 * Alan's cs46xx works without a red zone - marvel of ingenuity.
+	 * We are not so brilliant... Red zone does two things:
+	 *  1. allows for safe start after a pause as we have no way
+	 *     to know what the actual, relentlessly advancing, hwptr is.
+	 *  2. makes computations in ymf_pcm_interrupt simpler.
+	 */
+	redzone = ymf_calc_lend(state->format.rate) << state->format.shift;
+	redzone *= 3;	/* 2 redzone + 1 possible uncertainty reserve. */
+
+	add_wait_queue(&dmabuf->wait, &waita);
+	while (count > 0) {
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		if (dmabuf->count < 0) {
+			printk(KERN_ERR
+			   "ymf_write: count %d, was legal in cs46xx\n",
+			    dmabuf->count);
+			dmabuf->count = 0;
+		}
+		if (dmabuf->count == 0) {
+			swptr = dmabuf->hwptr;
+			if (state->ypcm.running) {
+				/*
+				 * Add uncertainty reserve.
+				 */
+				cnt = ymf_calc_lend(state->format.rate);
+				cnt <<= state->format.shift;
+				if ((swptr += cnt) >= dmabuf->dmasize) {
+					swptr -= dmabuf->dmasize;
+				}
+			}
+			dmabuf->swptr = swptr;
+		} else {
+			/*
+			 * XXX This is not right if dmabuf->count is small -
+			 * about 2*x frame size or less. We cannot count on
+			 * on appending and not causing an artefact.
+			 * Should use a variation of the count==0 case above.
+			 */
+			swptr = dmabuf->swptr;
+		}
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count + cnt > dmabuf->dmasize - redzone)
+			cnt = (dmabuf->dmasize - redzone) - dmabuf->count;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+/* P3 */ /* printk("ymf_write: full, count %d swptr %d\n",
+  dmabuf->count, dmabuf->swptr); */
+			/*
+			 * buffer is full, start the dma machine and
+			 * wait for data to be played
+			 */
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (!state->ypcm.running) {
+				ymf_playback_trigger(state->unit, &state->ypcm, 1);
+			}
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+			if (!ret) ret = -EFAULT;
+			break;
+		}
+
+		if ((swptr += cnt) >= dmabuf->dmasize) {
+			swptr -= dmabuf->dmasize;
+		}
+
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		dmabuf->swptr = swptr;
+		dmabuf->count += cnt;
+
+		/*
+		 * Start here is a bad idea - may cause startup click
+		 * in /bin/play when dmabuf is not full yet.
+		 * However, some broken applications do not make
+		 * any use of SNDCTL_DSP_SYNC (Doom is the worst).
+		 * One frame is about 5.3ms, Doom write size is 46ms.
+		 */
+		delay = state->format.rate / 20;	/* 50ms */
+		delay <<= state->format.shift;
+		if (dmabuf->count >= delay && !state->ypcm.running) {
+			ymf_playback_trigger(state->unit, &state->ypcm, 1);
+		}
+
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dmabuf->wait, &waita);
+
+/* P3 */ /* printk("ymf_write: dmabuf.count %d\n", dmabuf->count); */
+	return ret;
+}
+
+static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if (file->f_mode & (FMODE_WRITE | FMODE_READ))
+		poll_wait(file, &dmabuf->wait, wait);
+
+	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (dmabuf->count >= (signed)dmabuf->fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (dmabuf->mapped) {
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+	return mask;
+}
+
+static int ymf_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	int ret;
+	unsigned long size;
+	
+
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(state, 0)) != 0)
+			return ret;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(state, 1)) != 0)
+			return ret;
+	} else 
+		return -EINVAL;
+
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << dmabuf->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+			     size, vma->vm_page_prot))
+		return -EAGAIN;
+	dmabuf->mapped = 1;
+
+	return 0;
+}
+
+static int ymf_ioctl(struct inode *inode, struct file *file,
+    unsigned int cmd, unsigned long arg)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val;
+
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_RESET:
+		/* FIXME: spin_lock ? */
+		if (file->f_mode & FMODE_WRITE) {
+			ymf_wait_dac(state);
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->count = dmabuf->total_bytes = 0;
+		}
+#if HAVE_RECORD
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(state);
+			synchronize_irq();
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->count = dmabuf->total_bytes = 0;
+		}
+#endif
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE) {
+			if (file->f_flags & O_NONBLOCK) {
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				if (dmabuf->count != 0 && !state->ypcm.running) {
+					ymf_start_dac(state);
+				}
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			} else {
+				ymf_wait_dac(state);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_SPEED: /* set smaple rate */
+		get_user_ret(val, (int *)arg, -EFAULT);
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SPEED %d\n", val); */
+		if (val >= 8000 && val <= 48000) {
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+			}
+#if HAVE_RECORD
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+			}
+#endif
+			dmabuf->ready = 0;
+			state->format.rate = val;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		return put_user(state->format.rate, (int *)arg);
+
+	/*
+	 * OSS manual does not mention SNDCTL_DSP_STEREO at all.
+	 * All channels are mono and if you want stereo, you
+	 * play into two channels with SNDCTL_DSP_CHANNELS.
+	 * However, mpg123 uses it. I wonder, why Michael Hipp uses it.
+	 */
+	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+		get_user_ret(val, (int *)arg, -EFAULT);
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_STEREO %d\n", val); */
+		if (file->f_mode & FMODE_WRITE) {
+			ymf_wait_dac(state); 
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			dmabuf->ready = 0;
+			state->format.voices = val ? 2 : 1;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+#if HAVE_RECORD
+		if (file->f_mode & FMODE_READ) {
+			/* stop_adc(state); */
+			dmabuf->ready = 0;
+			if(val)
+				dmabuf->fmt |= CS_FMT_STEREO;
+			else
+				dmabuf->fmt &= ~CS_FMT_STEREO;
+		}
+#endif
+		return 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETBLKSIZE\n"); */
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(state, 0)))
+				return val;
+			return put_user(dmabuf->fragsize, (int *)arg);
+		}
+		if (file->f_mode & FMODE_READ) {
+			if ((val = prog_dmabuf(state, 1)))
+				return val;
+			return put_user(dmabuf->fragsize, (int *)arg);
+		}
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETFMTS\n"); */
+		return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Select sample format */
+		get_user_ret(val, (int *)arg, -EFAULT);
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETFMT 0x%x\n", val); */
+		if (val == AFMT_S16_LE || val == AFMT_U8) {
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+			}
+#if HAVE_RECORD
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+			}
+#endif
+			dmabuf->ready = 0;
+			state->format.format = val;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		return put_user(state->format.format, (int *)arg);
+
+	case SNDCTL_DSP_CHANNELS:
+		get_user_ret(val, (int *)arg, -EFAULT);
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */
+		if (val != 0) {
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+				if (val == 1 || val == 2) {
+					dmabuf->ready = 0;
+					state->format.voices = val;
+					ymf_pcm_update_shift(&state->format);
+				}
+			}
+#if HAVE_RECORD
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+				dmabuf->ready = 0;
+			}
+#endif
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		return put_user(state->format.voices, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+		/*
+		 * Quoting OSS PG:
+		 *    The ioctl SNDCTL_DSP_POST is a lightweight version of
+		 *    SNDCTL_DSP_SYNC. It just tells to the driver that there
+		 *    is likely to be a pause in the output. This makes it
+		 *    possible for the device to handle the pause more
+		 *    intelligently. This ioctl doesn't block the application.
+		 *
+		 * The paragraph above is a clumsy way to say "flush ioctl".
+		 * This ioctl is used by mpg123.
+		 */
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		if (dmabuf->count != 0 && !state->ypcm.running) {
+			ymf_start_dac(state);
+		}
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return 0;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if (dmabuf->subdivision)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2)
+			return -EINVAL;
+		dmabuf->subdivision = val;
+		return 0;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		get_user_ret(val, (int *)arg, -EFAULT);
+	/* P3: these frags are for Doom. Amasingly, it sets [2,2**11]. */
+	/* P3 */ // printk("ymfpci: ioctl SNDCTL_DSP_SETFRAGMENT 0x%x\n", val);
+
+		dmabuf->ossfragshift = val & 0xffff;
+		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+		switch (dmabuf->ossmaxfrags) {
+		case 1:
+			dmabuf->ossfragshift = 12;
+			return 0;
+		default:
+			/* Fragments must be 2K long */
+			dmabuf->ossfragshift = 11;
+			dmabuf->ossmaxfrags = 2;
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOSPACE\n"); */
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		/* cs_update_ptr(state); */  /* XXX Always up to date? */
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+#if HAVE_RECORD
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		cs_update_ptr(state);
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->count;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+#endif
+
+	case SNDCTL_DSP_NONBLOCK:
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_NONBLOCK\n"); */
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETCAPS\n"); */
+		/* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
+			    (int *)arg); */
+		return put_user(0, (int *)arg);
+
+#if 0 /* old */
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && dmabuf->enable)
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && dmabuf->enable)
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+					return ret;
+				start_adc(state);
+			} else
+				stop_adc(state);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+					return ret;
+				start_dac(state);  // sure?
+			} else
+				stop_dac(state);
+		}
+		return 0;
+
+#endif
+
+#if HAVE_RECORD
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		cs_update_ptr(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped)
+			dmabuf->count &= dmabuf->fragsize-1;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+#endif
+
+	case SNDCTL_DSP_GETOPTR:
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOPTR\n"); */
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		/* cs_update_ptr(state); */  /* Always up to date */
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped)
+			dmabuf->count &= dmabuf->fragsize-1;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */
+	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETDUPLEX\n"); */
+		return -EINVAL;
+
+#if 0 /* old */
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		cs_update_ptr(state);
+		val = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		return put_user(val, (int *)arg);
+#endif
+
+	case SOUND_PCM_READ_RATE:
+		return put_user(state->format.rate, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		return put_user(state->format.voices,	(int *)arg);
+
+	case SOUND_PCM_READ_BITS:
+		return put_user(AFMT_S16_LE, (int *)arg);
+
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+		return -ENOTTY;
+
+	default:
+	/* P3 */ printk("ymfpci: ioctl cmd 0x%x\n", cmd);
+		/*
+		 * Some programs mix up audio devices and ioctls
+		 * or perhaps they expect "universal" ioctls,
+		 * for instance we get SNDCTL_TMR_CONTINUE here.
+		 * XXX Is there sound_generic_ioctl() around?
+		 */
+	}
+	return -ENOTTY;
+}
+
+static int ymf_open(struct inode *inode, struct file *file)
+{
+	ymfpci_t *unit;
+	int minor, instance;
+	struct ymf_state *state;
+	int nvirt;
+	int err;
+
+	/*
+	 * This is how we do it currently: only one channel is used
+	 * in every board, so that we could use several boards in one machine.
+	 * We waste 63 out of 64 playback slots, but so what.
+	 * OSS model is constructed for devices with single playback channel.
+	 */
+	minor = MINOR(inode->i_rdev);
+	if ((minor & 0x0F) == 3) {	/* /dev/dspN */
+		;
+	} else {
+		return -ENXIO;
+	}
+	instance = (minor >> 4) & 0x0F;
+	nvirt = 0;			/* Such is the partitioning of minor */
+
+	/* XXX Semaphore here! */
+	for (unit = ymf_devs; unit != NULL; unit = unit->next) {
+		if (unit->inst == instance) break;
+	}
+	if (unit == NULL) return -ENODEV;
+
+	if (unit->states[nvirt] != NULL) {
+		/* P3 */ printk("ymfpci%d: busy\n", unit->inst);
+		return -EBUSY;
+	}
+
+	if ((err = ymf_state_alloc(unit, nvirt, instance)) != 0) {
+		return err;
+	}
+	state = unit->states[nvirt];
+
+	file->private_data = state;
+
+	/*
+	 * XXX This ymf_playback_prepare is totally unneeded here.
+	 * The question is if we want to allow write to fail if
+	 * prog_dmabuf fails... Say, no memory in DMA zone?
+	 */
+	if ((err = ymf_playback_prepare(unit, state)) != 0) {
+		/* XXX This recovery is ugly as hell. */
+
+		ymf_pcm_free_substream(&state->ypcm);
+
+		unit->states[state->virt] = NULL;
+		kfree(state);
+
+		return err;
+	}
+
+#if 0 /* test if interrupts work */
+	ymfpci_writew(codec, YDSXGR_TIMERCOUNT, 0xfffe);	/* ~ 680ms */
+	ymfpci_writeb(codec, YDSXGR_TIMERCTRL,
+	    (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
+#endif
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ymf_release(struct inode *inode, struct file *file)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	ymfpci_t *codec = state->unit;
+
+#if 0 /* test if interrupts work */
+	ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);
+#endif
+
+	/* XXX Use the semaphore to unrace us with opens */
+
+	if (state != codec->states[state->virt]) {
+		printk(KERN_ERR "ymfpci%d.%d: state mismatch\n",
+		    state->unit->inst, state->virt);
+		return -EIO;
+	}
+
+	/*
+	 * XXX Solve the case of O_NONBLOCK close - don't deallocate here.
+	 * Deallocate when unloading the driver and we can wait.
+	 */
+	ymf_wait_dac(state);
+	dealloc_dmabuf(state);
+	ymf_pcm_free_substream(&state->ypcm);
+
+	codec->states[state->virt] = NULL;
+	kfree(state);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Mixer operations are based on cs46xx.
+ */
+static int ymf_open_mixdev(struct inode *inode, struct file *file)
+{
+	int i;
+	int minor = MINOR(inode->i_rdev);
+	ymfpci_t *codec;
+
+	for (codec = ymf_devs; codec != NULL; codec = codec->next) {
+		for (i = 0; i < NR_AC97; i++) {
+			if (codec->ac97_codec[i] != NULL &&
+			    codec->ac97_codec[i]->dev_mixer == minor)
+				goto match;
+		}
+	}
+	if (!codec)
+		return -ENODEV;
+
+ match:
+	file->private_data = codec->ac97_codec[i];
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ymf_ioctl_mixdev(struct inode *inode, struct file *file,
+    unsigned int cmd, unsigned long arg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int ymf_release_mixdev(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations ymf_fops = {
+	llseek:		ymf_llseek,
+	read:		ymf_read,
+	write:		ymf_write,
+	poll:		ymf_poll,
+	ioctl:		ymf_ioctl,
+	mmap:		ymf_mmap,
+	open:		ymf_open,
+	release:	ymf_release,
+};
+
+static /*const*/ struct file_operations ymf_mixer_fops = {
+	llseek:		ymf_llseek,
+	ioctl:		ymf_ioctl_mixdev,
+	open:		ymf_open_mixdev,
+	release:	ymf_release_mixdev,
+};
+
+/*
+ *  initialization routines
+ */
+
+static void ymfpci_aclink_reset(struct pci_dev * pci)
+{
+	u8 cmd;
+
+	pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd);
+	if (cmd & 0x03) {
+		pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc);
+		pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03);
+		pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc);
+	}
+}
+
+static void ymfpci_enable_dsp(ymfpci_t *codec)
+{
+	ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001);
+}
+
+static void ymfpci_disable_dsp(ymfpci_t *codec)
+{
+	u32 val;
+	int timeout = 1000;
+
+	val = ymfpci_readl(codec, YDSXGR_CONFIG);
+	if (val)
+		ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000);
+	while (timeout-- > 0) {
+		val = ymfpci_readl(codec, YDSXGR_CONFIG);
+		if ((val & 0x00000002) == 0)
+			break;
+	}
+}
+
+#include "ymfpci_image.h"
+
+static void ymfpci_download_image(ymfpci_t *codec)
+{
+	int i, ver_1e;
+	u16 ctrl;
+
+	ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
+	ymfpci_disable_dsp(codec);
+	ymfpci_writel(codec, YDSXGR_MODE, 0x00010000);
+	ymfpci_writel(codec, YDSXGR_MODE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000);
+	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
+	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+	/* setup DSP instruction code */
+	for (i = 0; i < YDSXG_DSPLENGTH; i++)
+		ymfpci_writel(codec, YDSXGR_DSPINSTRAM + i, DspInst[i >> 2]);
+
+	switch (codec->device_id) {
+	case PCI_DEVICE_ID_YAMAHA_724F:
+	case PCI_DEVICE_ID_YAMAHA_740C:
+	case PCI_DEVICE_ID_YAMAHA_744:
+	case PCI_DEVICE_ID_YAMAHA_754:
+		ver_1e = 1;
+		break;
+	default:
+		ver_1e = 0;
+	}
+
+	if (ver_1e) {
+		/* setup control instruction code */
+		for (i = 0; i < YDSXG_CTRLLENGTH; i++)
+			ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + i, CntrlInst1E[i >> 2]);
+	} else {
+		for (i = 0; i < YDSXG_CTRLLENGTH; i++)
+			ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + i, CntrlInst[i >> 2]);
+	}
+
+	ymfpci_enable_dsp(codec);
+}
+
+static int ymfpci_memalloc(ymfpci_t *codec)
+{
+	long size, playback_ctrl_size;
+	int voice, bank;
+	u8 *ptr;
+
+	playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
+	codec->bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2;
+	codec->bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2;
+	codec->bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2;
+	codec->work_size = YDSXG_DEFAULT_WORK_SIZE;
+
+	size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) +
+	    ((codec->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) +
+	    ((codec->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) +
+	    ((codec->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) +
+	    codec->work_size;
+
+	ptr = (u8 *)kmalloc(size + 0x00ff, GFP_KERNEL);
+	if (ptr == NULL)
+		return -ENOMEM;
+
+	codec->work_ptr = ptr;
+	ptr += 0x00ff;
+	(long)ptr &= ~0x00ff;
+
+	codec->bank_base_playback = ptr;
+	codec->ctrl_playback = (u32 *)ptr;
+	codec->ctrl_playback[0] = YDSXG_PLAYBACK_VOICES;
+	ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
+		for (bank = 0; bank < 2; bank++) {
+			codec->bank_playback[voice][bank] = (ymfpci_playback_bank_t *)ptr;
+			ptr += codec->bank_size_playback;
+		}
+		codec->voices[voice].number = voice;
+		codec->voices[voice].bank = codec->bank_playback[voice][0];
+	}
+	ptr += (codec->bank_size_playback + 0x00ff) & ~0x00ff;
+	codec->bank_base_capture = ptr;
+	for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			codec->bank_capture[voice][bank] = (ymfpci_capture_bank_t *)ptr;
+			ptr += codec->bank_size_capture;
+		}
+	ptr += (codec->bank_size_capture + 0x00ff) & ~0x00ff;
+	codec->bank_base_effect = ptr;
+	for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			codec->bank_effect[voice][bank] = (ymfpci_effect_bank_t *)ptr;
+			ptr += codec->bank_size_effect;
+		}
+	ptr += (codec->bank_size_effect + 0x00ff) & ~0x00ff;
+	codec->work_base = ptr;
+
+	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, virt_to_bus(codec->bank_base_playback));
+	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, virt_to_bus(codec->bank_base_capture));
+	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, virt_to_bus(codec->bank_base_effect));
+	ymfpci_writel(codec, YDSXGR_WORKBASE, virt_to_bus(codec->work_base));
+	ymfpci_writel(codec, YDSXGR_WORKSIZE, codec->work_size >> 2);
+
+	/* S/PDIF output initialization */
+	ymfpci_writew(codec, YDSXGR_SPDIFOUTCTRL, 0);
+	ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS,
+		SND_PCM_AES0_CON_EMPHASIS_NONE |
+		(SND_PCM_AES1_CON_ORIGINAL << 8) |
+		(SND_PCM_AES1_CON_PCM_CODER << 8));
+
+	/* S/PDIF input initialization */
+	ymfpci_writew(codec, YDSXGR_SPDIFINCTRL, 0);
+
+	/* move this volume setup to mixer */
+	ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	ymfpci_writel(codec, YDSXGR_BUF441OUTVOL, 0x3fff3fff);
+	ymfpci_writel(codec, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+	ymfpci_writel(codec, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+
+	return 0;
+}
+
+static int ymf_ac97_init(ymfpci_t *card, int num_ac97)
+{
+	struct ac97_codec *codec;
+	u16 eid;
+
+	if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memset(codec, 0, sizeof(struct ac97_codec));
+
+	/* initialize some basic codec information, other fields will be filled
+	   in ac97_probe_codec */
+	codec->private_data = card;
+	codec->id = num_ac97;
+
+	codec->codec_read = ymfpci_codec_read;
+	codec->codec_write = ymfpci_codec_write;
+
+	if (ac97_probe_codec(codec) == 0) {
+		/* Alan does not have this printout. P3 */
+		printk("ymfpci: ac97_probe_codec failed\n");
+		return -ENODEV;
+	}
+
+	eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID);
+
+	if (eid==0xFFFFFF)
+	{
+		printk(KERN_WARNING "ymfpci: no codec attached ?\n");
+		kfree(codec);
+		return -ENODEV;
+	}
+
+	card->ac97_features = eid;
+
+	if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
+		printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
+		kfree(codec);
+		return -ENODEV;
+	}
+
+	card->ac97_codec[num_ac97] = codec;
+
+	return 0;
+}
+
+static int /* __init */
+ymf_install(struct pci_dev *pcidev, int instance, int devx)
+{
+	ymfpci_t *codec;
+
+	int err;
+
+	if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "ymfpci: no core\n");
+		return -ENOMEM;
+	}
+	memset(codec, 0, sizeof(*codec));
+
+	spin_lock_init(&codec->reg_lock);
+	spin_lock_init(&codec->voice_lock);
+	codec->pci = pcidev;
+	codec->inst = instance;
+	codec->irq = pcidev->irq;
+	codec->device_id = pcidev->device;
+	pci_read_config_byte(pcidev, PCI_REVISION_ID, (u8 *)&codec->rev);
+	codec->reg_area_phys = pcidev->base_address[0]&PCI_BASE_ADDRESS_MEM_MASK;
+	codec->reg_area_virt = (unsigned long)ioremap(codec->reg_area_phys, 0x8000);
+	pci_set_master(pcidev);
+
+	/* XXX KERN_INFO */
+	printk("ymfpci%d: %s at 0x%lx IRQ %d\n", instance,
+	    ymf_devv[devx].name, codec->reg_area_phys, codec->irq);
+
+	ymfpci_aclink_reset(pcidev);
+	if (ymfpci_codec_ready(codec, 0, 1) < 0) {
+		ymfpci_free(codec);
+		return -ENODEV;
+	}
+
+	ymfpci_download_image(codec);
+
+	udelay(100); /* seems we need some delay after downloading image.. */
+
+	if (ymfpci_memalloc(codec) < 0) {
+		ymfpci_free(codec);
+		return -ENODEV;
+	}
+
+	/* ymfpci_proc_init(card, codec); */
+
+	if (request_irq(codec->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
+		printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n",
+		    codec->inst, codec->irq);
+		ymfpci_free(codec);
+		return -ENODEV;
+	}
+
+	/* register /dev/dsp */
+	if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
+		printk(KERN_ERR "ymfpci%d: unable to register dsp\n", codec->inst);
+		ymfpci_free(codec);
+		return -ENODEV;
+	}
+
+	/*
+	 * Poke just the primary for the moment.
+	 */
+	if ((err = ymf_ac97_init(codec, 0)) != 0) {
+		unregister_sound_dsp(codec->dev_audio);
+		ymfpci_free(codec);
+		return err;
+	}
+
+	codec->next = ymf_devs;
+	ymf_devs = codec;
+
+	return 0;
+}
+
+static void
+ymfpci_free(ymfpci_t *codec)
+{
+	u16 ctrl;
+
+	/* ymfpci_proc_done(codec); */
+
+	unregister_sound_dsp(codec->dev_audio);
+
+	ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0);
+	ymfpci_writel(codec, YDSXGR_BUF441OUTVOL, 0);
+	ymfpci_writel(codec, YDSXGR_STATUS, ~0);
+	ymfpci_disable_dsp(codec);
+	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_WORKBASE, 0);
+	ymfpci_writel(codec, YDSXGR_WORKSIZE, 0);
+	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
+	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+	if (codec->reg_area_virt)
+		iounmap((void *)codec->reg_area_virt);
+	if (codec->work_ptr)
+		kfree(codec->work_ptr);
+	kfree(codec);
+}
+
+MODULE_AUTHOR("Jaroslav Kysela");
+MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio");
+
+int /* __init */
+ymf_probe(void)
+{
+	struct pci_dev *pcidev;
+	int foundone;
+	int i;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	/*
+	 * Print our proud ego-nursing "Hello, World!" message as in MS-DOS.
+	 */
+	/* printk(KERN_INFO "ymfpci: Yamaha YMF7xx\n"); */
+
+	/*
+	 * Not very efficient, but Alan did it so in cs46xx.c.
+	 */
+	foundone = 0;
+	pcidev = NULL;
+	for (i = 0; i < sizeof(ymf_devv)/sizeof(ymf_devv[0]); i++) {
+		while ((pcidev = pci_find_device(PCI_VENDOR_ID_YAMAHA,
+		    ymf_devv[i].id, pcidev)) != NULL) {
+			if (ymf_install(pcidev, foundone, i) == 0) {
+				foundone++;
+			}
+		}
+	}
+
+	return foundone;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	if (ymf_probe()==0)
+		printk(KERN_ERR "ymfpci: No devices found.\n");
+	return 0;
+}
+
+void cleanup_module (void)
+{
+	ymfpci_t *next;
+
+	while (ymf_devs){
+		next = ymf_devs->next;
+		ymfpci_free(ymf_devs);
+		ymf_devs = next;
+	}
+}
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.h linux.18p17/drivers/sound/ymfpci.h
--- linux.vanilla/drivers/sound/ymfpci.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/sound/ymfpci.h	Tue Sep 26 18:15:44 2000
@@ -0,0 +1,351 @@
+#ifndef __YMFPCI_H
+#define __YMFPCI_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for Yahama YMF724/740/744/754 chips
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *  Direct registers
+ */
+
+/* #define YMFREG(codec, reg)		(codec->port + YDSXGR_##reg) */
+
+#define	YDSXGR_INTFLAG			0x0004
+#define	YDSXGR_ACTIVITY			0x0006
+#define	YDSXGR_GLOBALCTRL		0x0008
+#define	YDSXGR_ZVCTRL			0x000A
+#define	YDSXGR_TIMERCTRL		0x0010
+#define	YDSXGR_TIMERCTRL_TEN		 0x0001
+#define	YDSXGR_TIMERCTRL_TIEN		 0x0002
+#define	YDSXGR_TIMERCOUNT		0x0012
+#define	YDSXGR_SPDIFOUTCTRL		0x0018
+#define	YDSXGR_SPDIFOUTSTATUS		0x001C
+#define	YDSXGR_EEPROMCTRL		0x0020
+#define	YDSXGR_SPDIFINCTRL		0x0034
+#define	YDSXGR_SPDIFINSTATUS		0x0038
+#define	YDSXGR_DSPPROGRAMDL		0x0048
+#define	YDSXGR_DLCNTRL			0x004C
+#define	YDSXGR_GPIOININTFLAG		0x0050
+#define	YDSXGR_GPIOININTENABLE		0x0052
+#define	YDSXGR_GPIOINSTATUS		0x0054
+#define	YDSXGR_GPIOOUTCTRL		0x0056
+#define	YDSXGR_GPIOFUNCENABLE		0x0058
+#define	YDSXGR_GPIOTYPECONFIG		0x005A
+#define	YDSXGR_AC97CMDDATA		0x0060
+#define	YDSXGR_AC97CMDADR		0x0062
+#define	YDSXGR_PRISTATUSDATA		0x0064
+#define	YDSXGR_PRISTATUSADR		0x0066
+#define	YDSXGR_SECSTATUSDATA		0x0068
+#define	YDSXGR_SECSTATUSADR		0x006A
+#define	YDSXGR_SECCONFIG		0x0070
+#define	YDSXGR_LEGACYOUTVOL		0x0080
+#define	YDSXGR_LEGACYOUTVOLL		0x0080
+#define	YDSXGR_LEGACYOUTVOLR		0x0082
+#define	YDSXGR_NATIVEDACOUTVOL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLR		0x0086
+#define	YDSXGR_SPDIFOUTVOL		0x0088
+#define	YDSXGR_SPDIFOUTVOLL		0x0088
+#define	YDSXGR_SPDIFOUTVOLR		0x008A
+#define	YDSXGR_AC3OUTVOL		0x008C
+#define	YDSXGR_AC3OUTVOLL		0x008C
+#define	YDSXGR_AC3OUTVOLR		0x008E
+#define	YDSXGR_PRIADCOUTVOL		0x0090
+#define	YDSXGR_PRIADCOUTVOLL		0x0090
+#define	YDSXGR_PRIADCOUTVOLR		0x0092
+#define	YDSXGR_LEGACYLOOPVOL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLR		0x0096
+#define	YDSXGR_NATIVEDACLOOPVOL		0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLL	0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLR	0x009A
+#define	YDSXGR_SPDIFLOOPVOL		0x009C
+#define	YDSXGR_SPDIFLOOPVOLL		0x009E
+#define	YDSXGR_SPDIFLOOPVOLR		0x009E
+#define	YDSXGR_AC3LOOPVOL		0x00A0
+#define	YDSXGR_AC3LOOPVOLL		0x00A0
+#define	YDSXGR_AC3LOOPVOLR		0x00A2
+#define	YDSXGR_PRIADCLOOPVOL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLR		0x00A6
+#define	YDSXGR_NATIVEADCINVOL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLR		0x00AA
+#define	YDSXGR_NATIVEDACINVOL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLR		0x00AE
+#define	YDSXGR_BUF441OUTVOL		0x00B0
+#define	YDSXGR_BUF441OUTVOLL		0x00B0
+#define	YDSXGR_BUF441OUTVOLR		0x00B2
+#define	YDSXGR_BUF441LOOPVOL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLR		0x00B6
+#define	YDSXGR_SPDIFOUTVOL2		0x00B8
+#define	YDSXGR_SPDIFOUTVOL2L		0x00B8
+#define	YDSXGR_SPDIFOUTVOL2R		0x00BA
+#define	YDSXGR_SPDIFLOOPVOL2		0x00BC
+#define	YDSXGR_SPDIFLOOPVOL2L		0x00BC
+#define	YDSXGR_SPDIFLOOPVOL2R		0x00BE
+#define	YDSXGR_ADCSLOTSR		0x00C0
+#define	YDSXGR_RECSLOTSR		0x00C4
+#define	YDSXGR_ADCFORMAT		0x00C8
+#define	YDSXGR_RECFORMAT		0x00CC
+#define	YDSXGR_P44SLOTSR		0x00D0
+#define	YDSXGR_STATUS			0x0100
+#define	YDSXGR_CTRLSELECT		0x0104
+#define	YDSXGR_MODE			0x0108
+#define	YDSXGR_SAMPLECOUNT		0x010C
+#define	YDSXGR_NUMOFSAMPLES		0x0110
+#define	YDSXGR_CONFIG			0x0114
+#define	YDSXGR_PLAYCTRLSIZE		0x0140
+#define	YDSXGR_RECCTRLSIZE		0x0144
+#define	YDSXGR_EFFCTRLSIZE		0x0148
+#define	YDSXGR_WORKSIZE			0x014C
+#define	YDSXGR_MAPOFREC			0x0150
+#define	YDSXGR_MAPOFEFFECT		0x0154
+#define	YDSXGR_PLAYCTRLBASE		0x0158
+#define	YDSXGR_RECCTRLBASE		0x015C
+#define	YDSXGR_EFFCTRLBASE		0x0160
+#define	YDSXGR_WORKBASE			0x0164
+#define	YDSXGR_DSPINSTRAM		0x1000
+#define	YDSXGR_CTRLINSTRAM		0x4000
+
+#define YDSXG_AC97READCMD		0x8000
+#define YDSXG_AC97WRITECMD		0x0000
+
+#define PCIR_DSXGCTRL			0x48
+
+#define YDSXG_DSPLENGTH			0x0080
+#define YDSXG_CTRLLENGTH		0x3000
+
+#define YDSXG_DEFAULT_WORK_SIZE		0x0400
+
+#define YDSXG_PLAYBACK_VOICES		64
+#define YDSXG_CAPTURE_VOICES		2
+#define YDSXG_EFFECT_VOICES		5
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define NR_AC97		2
+
+#define YMF_SAMPF			256	/* Samples per frame @48000 */
+
+/*
+ * The slot/voice control bank (2 of these per voice)
+ */
+
+typedef struct stru_ymfpci_playback_bank {
+	u32 format;
+	u32 loop_default;
+	u32 base;			/* 32-bit address */
+	u32 loop_start;			/* 32-bit offset */
+	u32 loop_end;			/* 32-bit offset */
+	u32 loop_frac;			/* 8-bit fraction - loop_start */
+	u32 delta_end;			/* pitch delta end */
+	u32 lpfK_end;
+	u32 eg_gain_end;
+	u32 left_gain_end;
+	u32 right_gain_end;
+	u32 eff1_gain_end;
+	u32 eff2_gain_end;
+	u32 eff3_gain_end;
+	u32 lpfQ;
+	u32 status;		/* P3: Always 0 for some reason. */
+	u32 num_of_frames;
+	u32 loop_count;
+	u32 start;		/* P3: J. reads this to know where chip is. */
+	u32 start_frac;
+	u32 delta;
+	u32 lpfK;
+	u32 eg_gain;
+	u32 left_gain;
+	u32 right_gain;
+	u32 eff1_gain;
+	u32 eff2_gain;
+	u32 eff3_gain;
+	u32 lpfD1;
+	u32 lpfD2;
+} ymfpci_playback_bank_t;
+
+typedef struct stru_ymfpci_capture_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 num_of_loops;		/* counter */
+} ymfpci_capture_bank_t;
+
+typedef struct stru_ymfpci_effect_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 temp;
+} ymfpci_effect_bank_t;
+
+typedef struct stru_ymfpci_voice ymfpci_voice_t;
+typedef struct ymf_pcm ymfpci_pcm_t;
+/*
+ * Throughout the code Yaroslav names YMF unit pointer "codec"
+ * even though it does not correspond to any codec. Must be historic.
+ * We replace it with "unit" over time.
+ * AC97 parts use "codec" to denote a codec, naturally.
+ */
+typedef struct ymf_unit ymfpci_t;
+
+typedef enum {
+	YMFPCI_PCM,
+	YMFPCI_SYNTH,
+	YMFPCI_MIDI
+} ymfpci_voice_type_t;
+
+struct stru_ymfpci_voice {
+	ymfpci_t *codec;
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth: 1,
+	    midi: 1;
+	ymfpci_playback_bank_t *bank;
+	void (*interrupt)(ymfpci_t *codec, ymfpci_voice_t *voice);
+	ymfpci_pcm_t *ypcm;
+};
+
+typedef enum {
+	PLAYBACK_VOICE,
+	CAPTURE_REC,
+	CAPTURE_AC97,
+	EFFECT_DRY_LEFT,
+	EFFECT_DRY_RIGHT,
+	EFFECT_EFF1,
+	EFFECT_EFF2,
+	EFFECT_EFF3
+} ymfpci_pcm_type_t;
+
+struct ymf_pcm {
+	ymfpci_t *codec;
+	ymfpci_pcm_type_t type;
+	struct ymf_state *state;
+	ymfpci_voice_t *voices[2];	/* playback only */
+	int running;			// +
+	int spdif;
+};
+
+struct ymf_unit {
+	unsigned int device_id;	/* PCI device ID */
+	unsigned int rev;	/* PCI revision */
+	unsigned long reg_area_phys;
+	unsigned long reg_area_virt;
+
+	void *work_ptr;				// +
+
+	unsigned int bank_size_playback;
+	unsigned int bank_size_capture;
+	unsigned int bank_size_effect;
+	unsigned int work_size;
+
+	void *bank_base_playback;		// +
+	void *bank_base_capture;		// +
+	void *bank_base_effect;			// +
+	void *work_base;			// +
+
+	u32 *ctrl_playback;
+	ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
+	ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2];
+	ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2];
+
+	int start_count;
+
+	u32 active_bank;
+	ymfpci_voice_t voices[64];
+
+	struct ac97_codec *ac97_codec[NR_AC97];
+	u16 ac97_features;
+
+	struct pci_dev *pci;
+	int irq;
+	int inst;		/* Unit number (instance) */
+
+	spinlock_t reg_lock;
+	spinlock_t voice_lock;
+
+	/* soundcore stuff */
+	int dev_audio;
+
+	ymfpci_t *next;					// *
+	struct ymf_state *states[1];			// *
+	/* ypcm may be the same thing as state, but not for record, effects. */
+};
+
+/*
+ * "Software" or virtual channel, an instance of opened /dev/dsp.
+ */
+
+struct ymf_dmabuf {
+
+	/* OSS buffer management stuff */
+	void *rawbuf;
+	unsigned buforder;
+	unsigned numfrag;
+	unsigned fragshift;
+
+	/* our buffer acts like a circular ring */
+	unsigned hwptr;		/* where dma last started */
+	unsigned swptr;		/* where driver last clear/filled */
+	int count;		/* fill count */
+	unsigned total_bytes;	/* total bytes dmaed by hardware */
+
+	unsigned error;		/* number of over/underruns */
+	struct wait_queue *wait;	/* put process on wait queue when no more space in buffer */
+
+	/* redundant, but makes calculations easier */
+	unsigned fragsize;
+	unsigned dmasize;	/* Total rawbuf[] size */
+	unsigned fragsamples;
+
+	/* OSS stuff */
+	unsigned mapped:1;
+	unsigned ready:1;
+	unsigned ossfragshift;
+	int ossmaxfrags;
+	unsigned subdivision;
+};
+
+struct ymf_pcm_format {
+	int format;			/* OSS format */
+	int rate;			/* rate in Hz */
+	int voices;			/* number of voices */
+	int shift;			/* redundant, computed from the above */
+};
+
+struct ymf_state {
+	struct ymf_unit *unit;			/* backpointer */
+
+	/* single open lock mechanism, only used for recording */
+	struct semaphore open_sem;
+	struct wait_queue *open_wait;
+
+	/* virtual channel number */
+	int virt;				// * unused a.t.m.
+
+	struct ymf_pcm ypcm;			// *
+	struct ymf_dmabuf dmabuf;		// *
+	struct ymf_pcm_format format;		// *
+};
+
+#endif				/* __YMFPCI_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci_image.h linux.18p17/drivers/sound/ymfpci_image.h
--- linux.vanilla/drivers/sound/ymfpci_image.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/sound/ymfpci_image.h	Tue Sep 26 17:08:54 2000
@@ -0,0 +1,1565 @@
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static unsigned long int	DspInst[] = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static unsigned long int	CntrlInst[] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x001A82, 0x032D0D, 0x000810, 0x10043A,
+	0x02D38D, 0x000810, 0x18043A, 0x00010D,
+	0x020015, 0x0000FD, 0x000020, 0x038860,
+	0x039060, 0x038060, 0x038040, 0x038040,
+	0x038040, 0x018040, 0x000A7D, 0x038040,
+	0x038040, 0x018040, 0x200402, 0x000882,
+	0x08001A, 0x000904, 0x015986, 0x000007,
+	0x260007, 0x000007, 0x000007, 0x018A06,
+	0x000007, 0x030C8D, 0x000810, 0x18043A,
+	0x260007, 0x00087D, 0x018042, 0x00160A,
+	0x04A206, 0x000007, 0x00218D, 0x000810,
+	0x08043A, 0x21C206, 0x000007, 0x0007FD,
+	0x018042, 0x08000A, 0x000904, 0x029386,
+	0x000195, 0x090D04, 0x000007, 0x000820,
+	0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+	0x032206, 0x018040, 0x000A7D, 0x038042,
+	0x13804A, 0x18000A, 0x001820, 0x059060,
+	0x058860, 0x018040, 0x0000FD, 0x018042,
+	0x70000A, 0x000115, 0x071144, 0x032386,
+	0x030000, 0x007020, 0x034A06, 0x018040,
+	0x00348D, 0x000810, 0x08043A, 0x21EA06,
+	0x000007, 0x02D38D, 0x000810, 0x18043A,
+	0x018206, 0x000007, 0x240007, 0x000F8D,
+	0x000810, 0x00163A, 0x002402, 0x005C02,
+	0x0028FD, 0x000020, 0x018040, 0x08000D,
+	0x000815, 0x510984, 0x000007, 0x00004D,
+	0x000E5D, 0x000E02, 0x00418D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+	0x000924, 0x000F02, 0x00458D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x018386, 0x000007, 0x01AA06, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x218086, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x055A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x034986, 0x000007, 0x002104, 0x034986,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x06C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00688D, 0x000810, 0x08043A, 0x288A06,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x060206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x215886, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x212086, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x07DA86, 0x00057D, 0x002820,
+	0x03B060, 0x07F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x07FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x008D8D, 0x000810,
+	0x08043A, 0x288A06, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x095186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+	0x000007, 0x001A7D, 0x001375, 0x018042,
+	0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+	0x000007, 0x000820, 0x019060, 0x001104,
+	0x212086, 0x010040, 0x0017FD, 0x018042,
+	0x08000A, 0x000904, 0x212286, 0x000007,
+	0x00197D, 0x038042, 0x09804A, 0x10000A,
+	0x000924, 0x001664, 0x0011FD, 0x038042,
+	0x2B804A, 0x19804A, 0x00008D, 0x218944,
+	0x000007, 0x002244, 0x0AE186, 0x000007,
+	0x001A64, 0x002A24, 0x00197D, 0x080102,
+	0x100122, 0x000820, 0x039060, 0x018040,
+	0x003DFD, 0x00008D, 0x000820, 0x018040,
+	0x001375, 0x001A7D, 0x010042, 0x09804A,
+	0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+	0x309144, 0x000007, 0x00060D, 0x000A15,
+	0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+	0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+	0x000464, 0x000464, 0x000464, 0x00040D,
+	0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+	0x030040, 0x00197D, 0x038042, 0x09804A,
+	0x000A24, 0x08000A, 0x080E64, 0x000007,
+	0x100122, 0x000820, 0x031060, 0x010040,
+	0x0064AC, 0x00027D, 0x000020, 0x018040,
+	0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+	0x09804A, 0x20000A, 0x000095, 0x1A1144,
+	0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+	0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+	0x09804A, 0x28000A, 0x000095, 0x010924,
+	0x002A64, 0x0D1186, 0x000007, 0x002904,
+	0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+	0x00008D, 0x00387D, 0x000820, 0x018040,
+	0x00127D, 0x018042, 0x10000A, 0x003904,
+	0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+	0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+	0x000015, 0x00082D, 0x02C78D, 0x000820,
+	0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+	0x0E7186, 0x400025, 0x00008D, 0x110944,
+	0x000007, 0x00018D, 0x109504, 0x000007,
+	0x009164, 0x000424, 0x000424, 0x000424,
+	0x100102, 0x280002, 0x02C68D, 0x000820,
+	0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+	0x109504, 0x000007, 0x00020D, 0x109184,
+	0x000007, 0x02C70D, 0x000820, 0x00008D,
+	0x0038FD, 0x018040, 0x003BFD, 0x001020,
+	0x03A860, 0x000815, 0x313184, 0x212184,
+	0x000007, 0x03B060, 0x03A060, 0x018040,
+	0x0022FD, 0x000095, 0x010924, 0x000424,
+	0x000424, 0x001264, 0x100102, 0x000820,
+	0x039060, 0x018040, 0x001924, 0x00FB8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x000424,
+	0x000424, 0x00117D, 0x018042, 0x08000A,
+	0x000A24, 0x280502, 0x280C02, 0x09800D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x0022FD, 0x018042, 0x08000A, 0x000095,
+	0x280DC4, 0x011924, 0x00197D, 0x018042,
+	0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+	0x113144, 0x0A8D04, 0x000007, 0x080A44,
+	0x129504, 0x000007, 0x0023FD, 0x001020,
+	0x038040, 0x101244, 0x000007, 0x000820,
+	0x039060, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x10FA86, 0x000007,
+	0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+	0x13804A, 0x090984, 0x000007, 0x000095,
+	0x013D04, 0x118086, 0x10000A, 0x100002,
+	0x090984, 0x000007, 0x038042, 0x11804A,
+	0x090D04, 0x000007, 0x10000A, 0x090D84,
+	0x000007, 0x00257D, 0x000820, 0x018040,
+	0x00010D, 0x000810, 0x28143A, 0x00127D,
+	0x018042, 0x20000A, 0x00197D, 0x018042,
+	0x00117D, 0x31804A, 0x10000A, 0x003124,
+	0x01280D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x300102, 0x003124, 0x000424, 0x000424,
+	0x001224, 0x280502, 0x001A4C, 0x130186,
+	0x700002, 0x00002D, 0x030000, 0x00387D,
+	0x018042, 0x10000A, 0x132A06, 0x002124,
+	0x0000AD, 0x100002, 0x00010D, 0x000924,
+	0x006B24, 0x01368D, 0x00397D, 0x000820,
+	0x058040, 0x038042, 0x09844A, 0x000606,
+	0x08040A, 0x003264, 0x00008D, 0x000A24,
+	0x001020, 0x00227D, 0x018040, 0x013C0D,
+	0x000810, 0x08043A, 0x29D206, 0x000007,
+	0x002820, 0x00207D, 0x018040, 0x00117D,
+	0x038042, 0x13804A, 0x33800A, 0x00387D,
+	0x018042, 0x08000A, 0x000904, 0x163A86,
+	0x000007, 0x00008D, 0x030964, 0x01478D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x380102,
+	0x000424, 0x000424, 0x001224, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x14A286,
+	0x000007, 0x280502, 0x001A4C, 0x163986,
+	0x000007, 0x032164, 0x00632C, 0x003DFD,
+	0x018042, 0x08000A, 0x000095, 0x090904,
+	0x000007, 0x000820, 0x001A4C, 0x156186,
+	0x018040, 0x030000, 0x157A06, 0x002124,
+	0x00010D, 0x000924, 0x006B24, 0x015B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x003A64,
+	0x000095, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x01628D, 0x000810, 0x08043A, 0x29D206,
+	0x000007, 0x14D206, 0x000007, 0x007020,
+	0x08010A, 0x10012A, 0x0020FD, 0x038860,
+	0x039060, 0x018040, 0x00227D, 0x018042,
+	0x003DFD, 0x08000A, 0x31844A, 0x000904,
+	0x16D886, 0x18008B, 0x00008D, 0x189904,
+	0x00312C, 0x17AA06, 0x000007, 0x00324C,
+	0x173386, 0x000007, 0x001904, 0x173086,
+	0x000007, 0x000095, 0x199144, 0x00222C,
+	0x003124, 0x00636C, 0x000E3D, 0x001375,
+	0x000BFD, 0x010042, 0x09804A, 0x10000A,
+	0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+	0x000007, 0x00008D, 0x189904, 0x00226C,
+	0x00322C, 0x30050A, 0x301DAB, 0x002083,
+	0x0018FD, 0x018042, 0x08000A, 0x018924,
+	0x300502, 0x001083, 0x001875, 0x010042,
+	0x10000A, 0x00008D, 0x010924, 0x001375,
+	0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+	0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+	0x305C8B, 0x006083, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x187A86, 0x000007,
+	0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+	0x028924, 0x280502, 0x00060D, 0x000810,
+	0x280C3A, 0x00008D, 0x000810, 0x28143A,
+	0x0A808D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001275, 0x030042, 0x21004A,
+	0x00008D, 0x1A0944, 0x000007, 0x01980D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+	0x089144, 0x000007, 0x000820, 0x010040,
+	0x0025F5, 0x0A3144, 0x000007, 0x000820,
+	0x032860, 0x030040, 0x00217D, 0x038042,
+	0x0B804A, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00008D, 0x000124, 0x00012C,
+	0x000E64, 0x001A64, 0x00636C, 0x08010A,
+	0x10012A, 0x000820, 0x031060, 0x030040,
+	0x0020FD, 0x018042, 0x08000A, 0x00227D,
+	0x018042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00197D, 0x018042, 0x08000A,
+	0x0022FD, 0x038042, 0x10000A, 0x000820,
+	0x031060, 0x030040, 0x090D04, 0x000007,
+	0x000820, 0x030040, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x038042, 0x13804A, 0x19804A, 0x110D04,
+	0x198D04, 0x000007, 0x08000A, 0x001020,
+	0x031860, 0x030860, 0x030040, 0x00008D,
+	0x0B0944, 0x000007, 0x000820, 0x010040,
+	0x0005F5, 0x030042, 0x08000A, 0x000820,
+	0x010040, 0x0000F5, 0x010042, 0x08000A,
+	0x000904, 0x1C6086, 0x001E75, 0x030042,
+	0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+	0x000402, 0x000C02, 0x00177D, 0x001AF5,
+	0x018042, 0x03144A, 0x031C4A, 0x03244A,
+	0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+	0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+	0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+	0x20000A, 0x089144, 0x19A144, 0x0389E4,
+	0x0399EC, 0x005502, 0x005D0A, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x006502, 0x006D0A, 0x030042, 0x0B004A,
+	0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+	0x30000A, 0x089144, 0x19A144, 0x2AB144,
+	0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+	0x03A9E4, 0x000702, 0x00107D, 0x000415,
+	0x018042, 0x08000A, 0x0109E4, 0x000F02,
+	0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+	0x10000A, 0x000934, 0x001674, 0x0029F5,
+	0x010042, 0x10000A, 0x00917C, 0x002075,
+	0x010042, 0x08000A, 0x000904, 0x1ED286,
+	0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+	0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+	0x000BFD, 0x010042, 0x51804A, 0x48000A,
+	0x160007, 0x001075, 0x010042, 0x282C0A,
+	0x281D12, 0x282512, 0x001F32, 0x1E0007,
+	0x0E0007, 0x001975, 0x010042, 0x002DF5,
+	0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+	0x010042, 0x28340A, 0x000E5D, 0x00008D,
+	0x000375, 0x000820, 0x010040, 0x05D2F4,
+	0x54D104, 0x00735C, 0x205386, 0x000007,
+	0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+	0x000810, 0x08043A, 0x332206, 0x000007,
+	0x205A06, 0x000007, 0x080007, 0x002275,
+	0x010042, 0x20000A, 0x002104, 0x212086,
+	0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x209286, 0x000007, 0x002010,
+	0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+	0x028924, 0x280502, 0x280C02, 0x0A810D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x0004FD, 0x018042, 0x70000A, 0x030000,
+	0x007020, 0x06FA06, 0x018040, 0x02180D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x218A86, 0x000007, 0x01F206, 0x000007,
+	0x000875, 0x0009FD, 0x00010D, 0x220A06,
+	0x000295, 0x000B75, 0x00097D, 0x00000D,
+	0x000515, 0x010042, 0x18000A, 0x001904,
+	0x287886, 0x0006F5, 0x001020, 0x010040,
+	0x0004F5, 0x000820, 0x010040, 0x000775,
+	0x010042, 0x09804A, 0x10000A, 0x001124,
+	0x000904, 0x22BA86, 0x000815, 0x080102,
+	0x101204, 0x22DA06, 0x000575, 0x081204,
+	0x000007, 0x100102, 0x000575, 0x000425,
+	0x021124, 0x100102, 0x000820, 0x031060,
+	0x010040, 0x001924, 0x287886, 0x00008D,
+	0x000464, 0x009D04, 0x278886, 0x180102,
+	0x000575, 0x010042, 0x28040A, 0x00018D,
+	0x000924, 0x280D02, 0x00000D, 0x000924,
+	0x281502, 0x10000D, 0x000820, 0x0002F5,
+	0x010040, 0x200007, 0x001175, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x23C286,
+	0x000007, 0x000100, 0x080B20, 0x130B60,
+	0x1B0B60, 0x030A60, 0x010040, 0x050042,
+	0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+	0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x24CA86, 0x004015, 0x000095, 0x010D04,
+	0x24B886, 0x100022, 0x10002A, 0x24E206,
+	0x000007, 0x333104, 0x2AA904, 0x000007,
+	0x032124, 0x280502, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x00292C, 0x00636C,
+	0x25F386, 0x000007, 0x02B164, 0x000464,
+	0x000464, 0x00008D, 0x000A64, 0x280D02,
+	0x10008D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x00008D, 0x38B904, 0x000007,
+	0x03296C, 0x30010A, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x25BA86, 0x000007,
+	0x02312C, 0x28050A, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x267A86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x26C086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x26CA86, 0x000007, 0x003124, 0x300502,
+	0x003924, 0x300583, 0x000883, 0x0005F5,
+	0x010042, 0x28040A, 0x00008D, 0x008124,
+	0x280D02, 0x00008D, 0x008124, 0x281502,
+	0x10018D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001025, 0x000575, 0x030042,
+	0x09004A, 0x10000A, 0x0A0904, 0x121104,
+	0x000007, 0x001020, 0x050860, 0x050040,
+	0x0006FD, 0x018042, 0x09004A, 0x10000A,
+	0x0000A5, 0x0A0904, 0x121104, 0x000007,
+	0x000820, 0x019060, 0x010040, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x284286,
+	0x000007, 0x230A06, 0x000007, 0x000606,
+	0x000007, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x289286, 0x000007, 0x000100,
+	0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+	0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+	0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+	0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+	0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+	0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+	0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+	0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+	0x000606, 0x018040, 0x00008D, 0x000A64,
+	0x280D02, 0x000A24, 0x00027D, 0x018042,
+	0x10000A, 0x001224, 0x0003FD, 0x018042,
+	0x08000A, 0x000904, 0x2A8286, 0x000007,
+	0x00018D, 0x000A24, 0x000464, 0x000464,
+	0x080102, 0x000924, 0x000424, 0x000424,
+	0x100102, 0x02000D, 0x009144, 0x2AD986,
+	0x000007, 0x0001FD, 0x018042, 0x08000A,
+	0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x00027D, 0x001020, 0x000606, 0x018040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x2B2A86, 0x000007, 0x00037D, 0x018042,
+	0x08000A, 0x000904, 0x2B5A86, 0x000007,
+	0x000075, 0x002E7D, 0x010042, 0x0B804A,
+	0x000020, 0x000904, 0x000686, 0x010040,
+	0x31844A, 0x30048B, 0x000883, 0x00008D,
+	0x000810, 0x28143A, 0x00008D, 0x000810,
+	0x280C3A, 0x000675, 0x010042, 0x08000A,
+	0x003815, 0x010924, 0x280502, 0x0B000D,
+	0x000820, 0x0002F5, 0x010040, 0x000606,
+	0x220007, 0x000464, 0x000464, 0x000606,
+	0x000007, 0x000134, 0x007F8D, 0x00093C,
+	0x281D12, 0x282512, 0x001F32, 0x0E0007,
+	0x00010D, 0x00037D, 0x000820, 0x018040,
+	0x05D2F4, 0x000007, 0x080007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2D0286,
+	0x000007, 0x000606, 0x000007, 0x000007,
+	0x000012, 0x100007, 0x320007, 0x600007,
+	0x100080, 0x48001A, 0x004904, 0x2D6186,
+	0x000007, 0x001210, 0x58003A, 0x000145,
+	0x5C5D04, 0x000007, 0x000080, 0x48001A,
+	0x004904, 0x2DB186, 0x000007, 0x001210,
+	0x50003A, 0x005904, 0x2E0886, 0x000045,
+	0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+	0x004224, 0x500102, 0x200502, 0x000082,
+	0x40001A, 0x004104, 0x2E3986, 0x000007,
+	0x003865, 0x40001A, 0x004020, 0x00104D,
+	0x04C184, 0x301B86, 0x000040, 0x040007,
+	0x000165, 0x000145, 0x004020, 0x000040,
+	0x000765, 0x080080, 0x40001A, 0x004104,
+	0x2EC986, 0x000007, 0x001210, 0x40003A,
+	0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+	0x004810, 0x20043A, 0x000882, 0x40001A,
+	0x004104, 0x2F3186, 0x000007, 0x004820,
+	0x005904, 0x300886, 0x000040, 0x0007E5,
+	0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+	0x4216E0, 0x021260, 0x000040, 0x000032,
+	0x400075, 0x00007D, 0x07D574, 0x200512,
+	0x000082, 0x40001A, 0x004104, 0x2FE186,
+	0x000007, 0x037206, 0x640007, 0x060007,
+	0x0000E5, 0x000020, 0x000040, 0x000A65,
+	0x000020, 0x020040, 0x020040, 0x000040,
+	0x000165, 0x000042, 0x70000A, 0x007104,
+	0x30A286, 0x000007, 0x018206, 0x640007,
+	0x050000, 0x007020, 0x000040, 0x037206,
+	0x640007, 0x000007, 0x00306D, 0x028860,
+	0x029060, 0x08000A, 0x028860, 0x008040,
+	0x100012, 0x00100D, 0x009184, 0x314186,
+	0x000E0D, 0x009184, 0x325186, 0x000007,
+	0x300007, 0x001020, 0x003B6D, 0x008040,
+	0x000080, 0x08001A, 0x000904, 0x316186,
+	0x000007, 0x001220, 0x000DED, 0x008040,
+	0x008042, 0x10000A, 0x40000D, 0x109544,
+	0x000007, 0x001020, 0x000DED, 0x008040,
+	0x008042, 0x20040A, 0x000082, 0x08001A,
+	0x000904, 0x31F186, 0x000007, 0x003B6D,
+	0x008042, 0x08000A, 0x000E15, 0x010984,
+	0x329B86, 0x600007, 0x08001A, 0x000C15,
+	0x010984, 0x328386, 0x000020, 0x1A0007,
+	0x0002ED, 0x008040, 0x620007, 0x00306D,
+	0x028042, 0x0A804A, 0x000820, 0x0A804A,
+	0x000606, 0x10804A, 0x000007, 0x282512,
+	0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+	0x000786, 0x000007, 0x0C0007, 0x0A0007,
+	0x1C0007, 0x003465, 0x020040, 0x004820,
+	0x025060, 0x40000A, 0x024060, 0x000040,
+	0x454944, 0x000007, 0x004020, 0x003AE5,
+	0x000040, 0x0028E5, 0x000042, 0x48000A,
+	0x004904, 0x386886, 0x002C65, 0x000042,
+	0x40000A, 0x0000D5, 0x454104, 0x000007,
+	0x000655, 0x054504, 0x34F286, 0x0001D5,
+	0x054504, 0x34F086, 0x002B65, 0x000042,
+	0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+	0x000007, 0x454504, 0x000007, 0x0000CD,
+	0x444944, 0x000007, 0x454504, 0x000007,
+	0x00014D, 0x554944, 0x000007, 0x045144,
+	0x34E986, 0x002C65, 0x000042, 0x48000A,
+	0x4CD104, 0x000007, 0x04C144, 0x34F386,
+	0x000007, 0x160007, 0x002CE5, 0x040042,
+	0x40000A, 0x004020, 0x000040, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x356086,
+	0x000007, 0x002402, 0x36A206, 0x005C02,
+	0x0025E5, 0x000042, 0x40000A, 0x004274,
+	0x002AE5, 0x000042, 0x40000A, 0x004274,
+	0x500112, 0x0029E5, 0x000042, 0x40000A,
+	0x004234, 0x454104, 0x000007, 0x004020,
+	0x000040, 0x003EE5, 0x000020, 0x000040,
+	0x002DE5, 0x400152, 0x50000A, 0x045144,
+	0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+	0x000040, 0x002BE5, 0x000042, 0x40000A,
+	0x404254, 0x000007, 0x002AE5, 0x004020,
+	0x000040, 0x500132, 0x040134, 0x005674,
+	0x0029E5, 0x020042, 0x42000A, 0x000042,
+	0x50000A, 0x05417C, 0x0028E5, 0x000042,
+	0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+	0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+	0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+	0x004820, 0x000040, 0x281D12, 0x282512,
+	0x001F72, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x37AA86, 0x0E0007, 0x160007,
+	0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+	0x004104, 0x37E886, 0x002D65, 0x000042,
+	0x28340A, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+	0x54D104, 0x00735C, 0x385186, 0x000007,
+	0x000606, 0x080007, 0x0C0007, 0x080007,
+	0x0A0007, 0x0001E5, 0x020045, 0x004020,
+	0x000060, 0x000365, 0x000040, 0x002E65,
+	0x001A20, 0x0A1A60, 0x000040, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x000606, 0x50004A, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09  creat
+// 04/12  stop nise fix
+// 06/21  WorkingOff timming
+static unsigned long int	CntrlInst1E[] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif	//_HWMCODE_
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/telephony/ixj.c linux.18p17/drivers/telephony/ixj.c
--- linux.vanilla/drivers/telephony/ixj.c	Sat Sep  9 18:42:45 2000
+++ linux.18p17/drivers/telephony/ixj.c	Thu Aug 31 15:24:51 2000
@@ -4473,14 +4473,21 @@
 	if (lcp == NULL)
 		return -ENOMEM;
 	if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_CADENCE)))
+	{
+		kfree(lcp);
 		return -EFAULT;
+	}
 	lcep = kmalloc(sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used, GFP_KERNEL);
 	if (lcep == NULL) {
 		kfree(lcp);
 		return -ENOMEM;
 	}
 	if (copy_from_user(lcep, lcp->ce, sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used))
+	{
+		kfree(lcep);
+		kfree(lcp);
 		return -EFAULT;
+	}
 	if (j->cadence_t) {
 		kfree(j->cadence_t->ce);
 		kfree(j->cadence_t);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/CREDITS linux.18p17/drivers/usb/CREDITS
--- linux.vanilla/drivers/usb/CREDITS	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/CREDITS	Thu Jan  1 01:00:00 1970
@@ -1,154 +0,0 @@
-Credits for the Simple Linux USB Driver:
-
-The following people have contributed to this code (in alphabetical
-order by last name).  I'm sure this list should be longer, its
-difficult to maintain, add yourself with a patch if desired.
-
-  Alan Cox <alan@lxorguk.ukuu.org.uk>
-  Johannes Erdfelt <jerdfelt@sventech.com>
-  ham <ham@unsuave.com>
-  Bradley M Keryan <keryan@andrew.cmu.edu>
-  Vojtech Pavlik <vojtech@twilight.ucw.cz>
-  Gregory P. Smith <greg@electricrain.com>
-  Linus Torvalds <torvalds@transmeta.com>
-  Roman Weissgaerber <weissg@vienna.at>
-  <Kazuki.Yasumatsu@fujixerox.co.jp>
-
-Special thanks to:
-
-  Inaky Perez Gonzalez <inaky@peloncho.fis.ucm.es> for starting the
-  Linux USB driver effort and writing much of the larger uusbd driver.
-  Much has been learned from that effort.
-
-  The NetBSD & FreeBSD USB developers.  For being on the Linux USB list
-  and offering suggestions and sharing implementation experiences.
-
-Additional thanks to the following companies and people for donations
-of hardware, support, time and development (this is from the original
-THANKS file in Inaky's driver):
-
-        The following corporations have helped us in the development
-of Linux USB / UUSBD:
-
-        - USAR Systems provided us with one of their excellent USB
-          Evaluation Kits. It allows us to test the Linux-USB driver
-          for compilance with the latest USB specification. USAR
-          Systems recognized the importance of an up-to-date open
-          Operating System and supports this project with
-          Hardware. Thanks!.
-
-        - Thanks to Intel Corporation for their precious help.
-
-        - We teamed up with Cherry to make Linux the first OS with
-          built-in USB support. Cherry is one of the biggest keyboard
-          makers in the world.
-
-        - CMD Technology, Inc. sponsored us kindly donating a CSA-6700
-          PCI-to-USB Controller Board to test the OHCI implementation.
-
-        - Due to their support to us, Keytronic can be sure that they
-          will sell keyboards to some of the 3 million (at least)
-          Linux users.
-
-        - Many thanks to ing büro h doran [http://www.ibhdoran.com]!
-          It was almost imposible to get a PC backplate USB connector
-          for the motherboard here at Europe (mine, home-made, was
-          quite lowsy :). Now I know where to adquire nice USB stuff!
-
-        - Genius Germany donated a USB mouse to test the mouse boot
-          protocol. They've also donated a F-23 digital joystick and a
-          NetMouse Pro. Thanks! 
-
-        - AVM GmbH Berlin is supporting the development of the Linux
-          USB driver for the AVM ISDN Controller B1 USB. AVM is a
-          leading manufacturer for active and passive ISDN Controllers
-          and CAPI 2.0-based software. The active design of the AVM B1
-          is open for all OS platforms, including Linux.
-
-        - Thanks to Y-E Data, Inc. for donating their FlashBuster-U
-          USB Floppy Disk Drive, so we could test the bulk transfer
-          code.
-
-        - Many thanks to Logitech for contributing a three axis USB
-          mouse. 
-
-          Logitech designs, manufactures and markets
-          Human Interface Devices, having a long history and
-          experience in making devices such as keyboards, mice,
-          trackballs, cameras, loudspeakers and control devices for
-          gaming and professional use.
-
-          Being a recognized vendor and seller for all these devices,
-          they have donated USB mice, a joystick and a scanner, as a
-          way to acknowledge the importance of Linux and to allow
-          Logitech customers to enjoy support in their favorite
-          operating systems and all Linux users to use Logitech and
-          other USB hardware.
-
-          Logitech is official sponsor of the Linux Conference on
-          Feb. 11th 1999 in Vienna, where we'll will present the
-          current state of the Linux USB effort.
-
-        - CATC has provided means to uncover dark corners of the UHCI
-          inner workings with a USB Inspector.
-
-        - Thanks to Entrega for providing PCI to USB cards, hubs and
-          converter products for development. 
-
-
-        And thanks go to (hey! in no particular order :)
-
-        - Oren Tirosh <orenti@hishome.net>, for standing so patiently
-          all my doubts'bout USB and giving lots of cool ideas.
-
-        - Jochen Karrer <karrer@wpfd25.physik.uni-wuerzburg.de>, for
-          pointing out mortal bugs and giving advice.
-
-        - Edmund Humemberger <ed@atnet.at>, for it's great work on
-          public relationships and general management stuff for the
-          Linux-USB effort.
-
-        - Alberto Menegazzi <flash@flash.iol.it> is starting the
-          documentation for the UUSBD. Go for it!
-
-        - Ric Klaren <ia_ric@cs.utwente.nl> for doing nice
-          introductory documents (compiting with Alberto's :).
-
-        - Christian Groessler <cpg@aladdin.de>, for it's help on those
-          itchy bits ... :)
-
-        - Paul MacKerras for polishing OHCI and pushing me harder for
-          the iMac support, giving improvements and enhancements.
-
-        - Fernando Herrera <fherrera@eurielec.etsit.upm.es> has taken
-          charge of composing, maintaining and feeding the
-          long-awaited, unique and marvelous UUSBD FAQ! Tadaaaa!!!
-
-        - Rasca Gmelch <thron@gmx.de> has revived the raw driver and
-          pointed bugs, as well as started the uusbd-utils package.
-
-        - Peter Dettori <dettori@ozy.dec.com> is unconvering bugs like
-          crazy, as well as making cool suggestions, great :)
-
-        - All the Free Software and Linux community, the FSF & the GNU
-          project, the MIT X consortium, the TeX people ... everyone!
-          You know who you are!
-
-        - Big thanks to Richard Stallman for creating Emacs!
-
-        - The people at the linux-usb mailing list, for reading so
-          many messages :) Ok, no more kidding; for all your advices!
-
-        - All the people at the USB Implementors Forum for their
-          help and assistance.
-
-        - Nathan Myers <ncm@cantrip.org>, for his advice! (hope you
-          liked Cibeles' party).
-
-        - Linus Torvalds, for starting, developing and managing Linux.
-
-        - Mike Smith, Craig Keithley, Thierry Giron and Janet Schank
-          for convincing me USB Standard hubs are not that standard
-          and that's good to allow for vendor specific quirks on the
-          standard hub driver.
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Config.in linux.18p17/drivers/usb/Config.in
--- linux.vanilla/drivers/usb/Config.in	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/Config.in	Sat Oct 14 00:51:40 2000
@@ -1,30 +1,99 @@
 #
 # USB device configuration
 #
-# NOTE NOTE NOTE! This is still considered extremely experimental.
-# Right now hubs, mice and keyboards work - at least with UHCI.
-# But that may be more a lucky coincidence than anything else..
-#
-# This was all developed modularly, but I've been lazy in cleaning
-# it up, so right now they are all bools. 
-#
 mainmenu_option next_comment
-comment 'USB drivers - not for the faint of heart'
+comment 'USB support'
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB
-  if [ ! "$CONFIG_USB" = "n" ]; then
-    bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI
-    bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI
-    bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD
-    if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then
-       bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB
-    fi
+tristate 'Support for USB' CONFIG_USB
+if [ ! "$CONFIG_USB" = "n" ]; then
+   bool '  USB verbose debug messages' CONFIG_USB_DEBUG
 
-    bool 'USB mouse support' CONFIG_USB_MOUSE
-    bool 'USB keyboard support' CONFIG_USB_KBD
-    bool 'USB audio parsing support' CONFIG_USB_AUDIO
-  fi
+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
+      define_bool CONFIG_USB_BANDWIDTH n
+   fi
+
+comment 'USB Controllers'
+   if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
+      dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
+   fi
+   if [ "$CONFIG_USB_UHCI" != "y" ]; then
+      dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
+   fi
+   dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
+
+comment 'USB Devices'
+   dep_tristate '  USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
+   dep_tristate '  USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
+   dep_tristate '  USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
+   dep_tristate '  USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB
+   dep_tristate '  USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB
+   if [ "$CONFIG_USB_SERIAL" != "n" ]; then
+      bool '    USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC $CONFIG_USB_SERIAL
+      dep_tristate '    USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL
+      if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+         dep_tristate '    USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL
+         dep_tristate '    USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL
+         dep_tristate '    USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL
+         dep_tristate '    USB Keyspan USA-xxx Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN $CONFIG_USB_SERIAL
+         if [ "$CONFIG_USB_SERIAL_KEYSPAN" != "n" ]; then
+            bool '      USB Keyspan USA-28 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28
+            bool '      USB Keyspan USA-28X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28X
+            bool '      USB Keyspan USA-19 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19
+            bool '      USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X
+            bool '      USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W
+         fi
+         dep_tristate '    USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL
+         dep_tristate '    USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL
+      fi
+      bool '    USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG $CONFIG_USB_SERIAL
+   fi
+   dep_tristate '  USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	   dep_tristate '  USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB
+      dep_tristate '  USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
+      if [ "$CONFIG_USB_STORAGE" != "n" ]; then
+         bool '    USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
+      fi
+   fi
+#   dep_tristate '  USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
+   dep_tristate '  DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      dep_tristate '  PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET
+      dep_tristate '  USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET
+      dep_tristate '  USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB
+      dep_tristate '  D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV
+#      dep_tristate '  Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI
+      dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB
+   fi
+   if [ "$CONFIG_NET" = "y" ]; then
+      dep_tristate '  Kawasaki USB-ethernet controller' CONFIG_USB_KAWETH $CONFIG_USB
+   fi
+
+comment 'USB HID'
+   dep_tristate '  USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
+   if [ "$CONFIG_USB_HID" != "y" ]; then
+      dep_tristate '  USB HIDBP Keyboard support' CONFIG_USB_KBD $CONFIG_USB
+      dep_tristate '  USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB
+   fi
+   dep_tristate '  Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB
+   dep_tristate '  Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB
+   if [ "$CONFIG_VT" = "y" ]; then
+      dep_tristate '  Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB 
+   fi
+   dep_tristate '  Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB
+   if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
+      int '   Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
+      int '   Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
+   fi
+   dep_tristate '  Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_USB
+   dep_tristate '  Event interface support' CONFIG_INPUT_EVDEV $CONFIG_USB
 fi
 
 endmenu
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Makefile linux.18p17/drivers/usb/Makefile
--- linux.vanilla/drivers/usb/Makefile	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/Makefile	Tue Sep 12 12:52:02 2000
@@ -1,88 +1,123 @@
 #
-# Makefile for the kernel usb device drivers.
+# Makefile for the kernel USB device drivers.
 #
-# 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 inherited from the
-# parent makes..
-#
-# This isn't actually supported yet. Don't try to use it.
 
-SUB_DIRS     :=
-MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
-
-L_TARGET := usb.a
-M_OBJS   :=
-L_OBJS   :=
-LX_OBJS  :=
-USBX_OBJS := usb.o hub.o usb-debug.o
+# Subdirs.
 
-ifeq ($(CONFIG_USB_MOUSE),y)
-  USBX_OBJS += mouse.o
-endif
+SUB_DIRS	:=
+MOD_SUB_DIRS	:= $(SUB_DIRS)
+MOD_IN_SUB_DIRS	:= $(SUB_DIRS)
+ALL_SUB_DIRS	:= $(SUB_DIRS) serial
 
-ifeq ($(CONFIG_USB_KBD),y)
-  USBX_OBJS += keyboard.o keymap.o
-endif
+# The target object and module list name.
 
-ifeq ($(CONFIG_USB_AUDIO),y)
-  USBX_OBJS += audio.o
-endif
+O_TARGET	:= usbdrv.o
+M_OBJS		:=
+O_OBJS		:=
+MOD_LIST_NAME	:= USB_MODULES
 
-ifeq ($(CONFIG_USB), y)
-  L_OBJS += $(USBX_OBJS)
-endif
+# Objects that export symbols.
 
-ifeq ($(CONFIG_USB_UHCI),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += uhci.o uhci-debug.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      M_OBJS += usb-uhci.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
-endif
+export-objs		:= usb.o input.o
 
-ifeq ($(CONFIG_USB_OHCI),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += ohci.o ohci-debug.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      USBO_OBJS += ohci.o ohci-debug.o
-      M_OBJS += usb-ohci.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
+# Multipart objects.
+
+list-multi		:= usbcore.o
+usbcore-objs		:= usb.o usb-debug.o usb-core.o hub.o
+
+# Optional parts of multipart objects.
+
+ifeq ($(CONFIG_USB_DEVICEFS),y)
+	usbcore-objs	+= devio.o inode.o drivers.o devices.o
 endif
 
-ifeq ($(CONFIG_USB_OHCI_HCD),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += ohci-hcd.o ohci-root-hub.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      USBO_OBJS += ohci-hcd.o ohci-root-hub.o
-      M_OBJS += usb-ohci-hcd.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
+# Object file lists.
+
+obj-y	:=
+obj-m	:=
+obj-n	:=
+obj-	:=
+
+# Object files in subdirectories
+
+ifeq ($(CONFIG_USB_SERIAL),y)
+	SUB_DIRS += serial
+	obj-y += serial/usb-serial.o
+else
+	ifeq ($(CONFIG_USB_SERIAL),m)
+		MOD_IN_SUB_DIRS += serial
+	endif
 endif
-include $(TOPDIR)/Rules.make
 
-keymap.o: keymap.c
 
-keymap.c: maps/serial.map maps/usb.map maps/fixup.map
-	./mkmap > $@
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_USB)		+= usbcore.o
+obj-$(CONFIG_USB_UHCI)		+= usb-uhci.o
+obj-$(CONFIG_USB_UHCI_ALT)	+= uhci.o
+obj-$(CONFIG_USB_OHCI)		+= usb-ohci.o
+
+obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o input.o
+obj-$(CONFIG_USB_HID)		+= hid.o input.o
+obj-$(CONFIG_USB_KBD)		+= usbkbd.o input.o
+obj-$(CONFIG_USB_WACOM)		+= wacom.o input.o
+obj-$(CONFIG_USB_WMFORCE)	+= wmforce.o input.o
+obj-$(CONFIG_INPUT_KEYBDEV)	+= keybdev.o input.o
+obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o input.o
+obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o input.o
+obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o input.o
+
+obj-$(CONFIG_USB_SCANNER)	+= scanner.o
+obj-$(CONFIG_USB_ACM)		+= acm.o
+obj-$(CONFIG_USB_PRINTER)	+= printer.o
+obj-$(CONFIG_USB_AUDIO)		+= audio.o
+obj-$(CONFIG_USB_IBMCAM)	+= ibmcam.o
+obj-$(CONFIG_USB_DC2XX)		+= dc2xx.o
+obj-$(CONFIG_USB_MDC800)	+= mdc800.o
+obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
+obj-$(CONFIG_USB_USS720)	+= uss720.o
+obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
+obj-$(CONFIG_USB_PLUSB)		+= plusb.o
+obj-$(CONFIG_USB_OV511)		+= ov511.o
+obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
+obj-$(CONFIG_USB_RIO500)	+= rio500.o
+obj-$(CONFIG_USB_DSBR)		+= dsbr100.o
+obj-$(CONFIG_USB_MICROTEK)	+= microtek.o
+obj-$(CONFIG_USB_KAWETH)	+= kaweth.o
+obj-$(CONFIG_USB_BLUETOOTH)	+= bluetooth.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+int-m		:= $(filter-out $(int-y), $(int-m))
+
+# Take multi-part drivers out of obj-y and put components in.
+
+obj-y		:= $(filter-out $(list-multi), $(obj-y)) $(int-y)
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+MI_OBJS		:= $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS	:= $(sort $(filter     $(export-objs), $(int-m)))
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
 
-usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@  uhci.o uhci-debug.o $(USBX_OBJS)
+# Link rules for multi-part drivers.
 
-usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS)
+usbcore.o: $(usbcore-objs)
+	$(LD) -r -o $@ $(usbcore-objs)
 
-usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
-       
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/README.kbd linux.18p17/drivers/usb/README.kbd
--- linux.vanilla/drivers/usb/README.kbd	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/README.kbd	Thu Jan  1 01:00:00 1970
@@ -1,65 +0,0 @@
-This is a simple USB keyboard driver written from Linus'
-USB driver (started with Greg's usb-0.03b.tar.gz source
-tree)
-
-It works fine with my BTC keyboard but I'm still investigating
-trouble with my MS keyboard (trouble starts with an inability
-to set into boot protocol mode, though, this very well could
-be all due to crappy hardware).
-
-Anyway, I would appreciate you taking a look if you have
-any USB keyboards lying around.  Oh also, I'm doing this on
-UHCI so sorry if it breaks with OHCI.
-
--ham
-
-
-
-Keyboard patch
---------------
-
-Instead of using the multiple keyboard patch and then running into all
-of the kernel version problems that the current Linux-USB project has
-had, I'm just mapping the USB keycodes to the standard AT-101 keycodes
-and sending them directly to "handle_scancode".
-
-This may or may not be considered a hack.  Anyway it is effective, and
-I think safe, and allows USB keyboards to coexist with a serial
-keyboard (oh yeah, one side effect is that you can for example hold
-down the control key on the serial keyboard and press "a" on the USB
-keyboard and you get Control-a like with Windows USB) and works
-fine for console and X.
-
-You do need to make a *tiny* patch the kernel source tree so that the
-function "handle_scancode" is exported from keyboard.c though.
-
-  $ cd /usr/src/linux
-  $ patch -p0 < kbd.patch
-
-And, of course, then, you need to rebuild and install the kernel.
-
-** [Vojtech]: Alternately, just 'insmod kbd-stub', if you don't want
-to use the keyboard and are too lazy to patch the kernel.
-
-Keyboard map
-------------
-
-I'm including a stupid utility "mkmap" which generates the USB->serial
-keymap.  It takes in maps/serial.map (the current serial keymap,
-generated by "dumpkeys"), maps/usb.map (the USB keymap), and
-maps/fixup.map (fixes for e0 keys and misc.) and spits out keymap.c
-Anyway, it is not beautiful but should serve its purpose for the
-moment.
-
-Other changes
--------------
-uhci.c:
-  * added a context value to the irq callback function
-    (this is exactly like the "dev_id" field to request_irq)
-  * played with uhci_reset_port to get better hot-plug results
-    (eg. do a wait_ms(200) before calling uhci_reset_port)
-usb.c:
-  * disconnect all devices after uhci-control thread is killed
-  * skip over the HID descriptor
-  * disconnect the high-level driver in usb_disconnect
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/README.ohci linux.18p17/drivers/usb/README.ohci
--- linux.vanilla/drivers/usb/README.ohci	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/README.ohci	Thu Jan  1 01:00:00 1970
@@ -1,26 +0,0 @@
-May 09, 1999 16:25:58
-
-Cool, things are working "well" now.  (I'm not getting oops's from the
-OHCI code anyways.. ;).  I can attach a usb hub and mouse in any
-possible arrangement of the two and they get configured properly.
-
-You can see that the mouse Interrupt transfers are occuring and being
-acknowledged because /proc/interrupts usb-ohci goes up accordingly with
-mouse movements/events.  That means the TD at least returns some data
-and requeues itself.
-
-Device attach/detach from the root hub is not working well.  Currently
-every interrupt checks for root hub status changes and frame number
-overflow interrupts are enabled.  This means you shouldn't have to
-wait more than 32-33 seconds for the change to occur, less if there is
-other activity.  (due to checking in the WDH caused interrupts)
-My OHCI controller [SiS 5598 motherboard] doesn't seem to play well
-with the RHSC interrupt so it has been disabled.  The ohci_timer
-should be polling but it not currently working, I haven't had time to
-look into that problem.
-
-However, when I tried telling X to use /dev/psaux for the mouse my
-machine locked up...
-
-- greg@electricrain.com
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/README.ohci_hcd linux.18p17/drivers/usb/README.ohci_hcd
--- linux.vanilla/drivers/usb/README.ohci_hcd	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/README.ohci_hcd	Thu Jan  1 01:00:00 1970
@@ -1,112 +0,0 @@
-
-The OHCI HCD layer is a simple but nearly complete implementation of what the
-USB people would call a HCD  for the OHCI. 
- (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
-It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree).
-The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. 
-
-- Roman Weissgaerber <weissg@vienna.at>
-
-
- * v2.1 1999/05/09 ep_addr correction, code cleanup
- * v0.2.0 1999/05/04 
- * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers)
- * virtual root hub is now an option, 
- * memory allocation based on kmalloc and kfree now, simple Bus error handling, 
- * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
- * 
- * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff
- * from Greg Smith (ohci.c): better reset ohci-controller handling, hub
- * 
- * v0.1.0 1999/04/27 initial release
- 
-to remove the module try:
-killall root-hub
-:
-rmmod usb-ohci-hcd
-
-Features:
-- virtual root hub, all basic hub descriptors and commands (state: complete) 
-  this is an option now (v0.2.0)
-  #define  CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB)
-  default is without. 
-  (at the moment: the Virtual Root Hub option is not recommended)
-  
-  files: ohci-root-hub.c, ohci-root-hub.h 
- 
-
-- Endpoint Descriptor (ED) handling more static approach 
- (EDs should be allocated in parallel to the SET CONFIGURATION command and they live
- as long as the function (device) is alive or another configuration is choosen.
- In the HCD layer the EDs has to be allocated manually either by calling a subroutine
- or by sending a USB root hub vendor specific command to the virtual root hub.
- At the alternate linux usb stack EDs will be added (allocated) at their first use.
- 
- files: ohci-hcd.c ohci-hcd.h
- routines:   (do not use for drivers, use the top layer alternate usb commands instead)
- 
- int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1, 
-            int interval, int load, f_handler handler, int ep_size, int speed)
-       adds an endpoint, (if the endpoint already exists some parameters will be updated)
-       
- int usb_ohci_rm_ep(struct  usb_ohci_ed *ed, struct ohci * ohci) 
-       removes an endpoint and all pending TDs of that EP
-       
-  usb_ohci_rm_function( struct ohci * ohci, union ep_addr_ ep_addr)
-       removes all Endpoints of a function (device)
-
-- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers
-  The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has
-  to take care of buffer allocation.
-  files: ohci-hcd.c ohci-hcd.h 
-
-  There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL):
-  
-  int ohci_trans_req(struct ohci * ohci, int ep_addr, int ctrl_len, void  *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1)
-  
-  CTRL: ctrl, ctrl_len ... cmd buffer 
-        data, data_len ... data buffer (in or out)
-  INT, BULK:  ctrl = NULL, ctrl_len=0,
-  		data, data_len ... data buffer (in or out)
-  ISO: tbd
-
-  There is no buffer reinsertion done by the internal HCD function. 
-  (The interface layer does this for a INT-pipe on request.)
-  If you want a transfer then you have to 
-  provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED
-  you can send as many as you like. They should come back by the callback f_handler in
-  the same order (for each endpoint, not globally) If an error occurs all
-  queued transfers of an endpoint will return unsent. They will be marked with an error status.
-  
-  e.g double-buffering for int transfers:
-
-   	ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
-  	ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
-  	
-  and when a data0 packet returns by the callback f_handler requeue it:
-  	ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
-  and when a data1 packet returns by the callback f_handler requeue it:
-  	ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
-  	
-  lw0, lw1 are private fields for upper layers for ids or fine grained handlers.
-  The alternate usb uses them for dev_id and usb_device_irq handler.
-
-
-- Done list handling: returns the requests (callback f_handler in ED) and does 
-  some error handling, root-hub request dequeuing
-  (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0))
-
-ep_addr union or int  is for addressing devices&endpoints: 
-__u8 ep_addr.bep.ep ... bit 3..0  endpoint address
-						bit 4     0
-						bit 6,5  type: eg. 10 CTRL, 11 BULK, 01 INT, 00 ISO
-						bit 7	direction 1 IN, 0 OUT
-
-__u8 ep_addr.bep.fa ... bit 6..0  function address
-						bit 7	  0
-
-(__u8 ep_addr.bep.hc ... host controller nr) not used
-(__u8 ep_addr.bep.host ... host nr) not used
-
-
-  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/acm.c linux.18p17/drivers/usb/acm.c
--- linux.vanilla/drivers/usb/acm.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/acm.c	Sat Sep 23 13:25:02 2000
@@ -0,0 +1,695 @@
+/*
+ * acm.c  Version 0.16
+ *
+ * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
+ * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
+ * Copyright (c) 1999 Johannes Erdfelt	<jerdfelt@valinux.com>
+ * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
+ *
+ * USB Abstract Control Model driver for USB modems and ISDN adapters
+ *
+ * Sponsored by SuSE
+ *
+ * ChangeLog:
+ *	v0.9  - thorough cleaning, URBification, almost a rewrite
+ *	v0.10 - some more cleanups
+ *	v0.11 - fixed flow control, read error doesn't stop reads
+ *	v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
+ *	v0.13 - added termios, added hangup
+ *	v0.14 - sized down struct acm
+ *	v0.15 - fixed flow control again - characters could be lost
+ *	v0.16 - added code for modems with swapped data and control interfaces
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+//#define DEBUG
+#include <linux/usb.h>
+#include <linux/devfs_fs_kernel.h>
+
+/*
+ * CMSPAR, some architectures can't have space and mark parity.
+ */
+
+#ifndef CMSPAR
+#define CMSPAR			0
+#endif
+
+/*
+ * Major and minor numbers.
+ */
+
+#define ACM_TTY_MAJOR		166
+#define ACM_TTY_MINORS		32
+
+/*
+ * Requests.
+ */
+
+#define USB_RT_ACM		(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+#define ACM_REQ_COMMAND		0x00
+#define ACM_REQ_RESPONSE	0x01
+#define ACM_REQ_SET_FEATURE	0x02
+#define ACM_REQ_GET_FEATURE	0x03
+#define ACM_REQ_CLEAR_FEATURE	0x04
+
+#define ACM_REQ_SET_LINE	0x20
+#define ACM_REQ_GET_LINE	0x21
+#define ACM_REQ_SET_CONTROL	0x22
+#define ACM_REQ_SEND_BREAK	0x23
+
+/*
+ * IRQs.
+ */
+
+#define ACM_IRQ_NETWORK		0x00
+#define ACM_IRQ_LINE_STATE	0x20
+
+/*
+ * Output control lines.
+ */
+
+#define ACM_CTRL_DTR		0x01
+#define ACM_CTRL_RTS		0x02
+
+/*
+ * Input control lines and line errors.
+ */
+
+#define ACM_CTRL_DCD		0x01
+#define ACM_CTRL_DSR		0x02
+#define ACM_CTRL_BRK		0x04
+#define ACM_CTRL_RI		0x08
+
+#define ACM_CTRL_FRAMING	0x10
+#define ACM_CTRL_PARITY		0x20
+#define ACM_CTRL_OVERRUN	0x40
+
+/*
+ * Line speed and caracter encoding.
+ */
+
+struct acm_line {
+	__u32 speed;
+	__u8 stopbits;
+	__u8 parity;
+	__u8 databits;
+} __attribute__ ((packed));
+
+/*
+ * Internal driver structures.
+ */
+
+struct acm {
+	struct usb_device *dev;				/* the coresponding usb device */
+	struct usb_interface *iface;			/* the interfaces - +0 control +1 data */
+	struct tty_struct *tty;				/* the coresponding tty */
+	struct urb ctrlurb, readurb, writeurb;		/* urbs */
+	struct acm_line line;				/* line coding (bits, stop, parity) */
+	struct tq_struct tqueue;			/* task queue for line discipline waking up */
+	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
+	unsigned int ctrlout;				/* output control lines (DTR, RTS) */
+	unsigned int writesize;				/* max packet size for the output bulk endpoint */
+	unsigned int used;				/* someone has this acm's device open */
+	unsigned int minor;				/* acm minor number */
+	unsigned char throttle;				/* throttled by tty layer */
+	unsigned char clocal;				/* termios CLOCAL */
+};
+
+static struct usb_driver acm_driver;
+static struct tty_driver acm_tty_driver;
+static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, /* .... */ };
+
+#define ACM_READY(acm)	(acm && acm->dev && acm->used)
+
+/*
+ * Functions for ACM control messages.
+ */
+
+static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
+{
+	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+		request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ * 5);
+	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
+	return retval < 0 ? retval : 0;
+}
+
+#define acm_set_control(acm, control)	acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0)
+#define acm_set_line(acm, line)		acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line))
+#define acm_send_break(acm, ms)		acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
+
+/*
+ * Interrupt handler for various ACM control events
+ */
+
+static void acm_ctrl_irq(struct urb *urb)
+{
+	struct acm *acm = urb->context;
+	devrequest *dr = urb->transfer_buffer;
+	unsigned char *data = (unsigned char *)(dr + 1);
+	int newctrl;
+
+	if (!ACM_READY(acm)) return;
+
+	if (urb->status < 0) {
+		dbg("nonzero ctrl irq status received: %d", urb->status);
+		return;
+	}
+
+	switch (dr->request) {
+
+		case ACM_IRQ_NETWORK:
+	
+			dbg("%s network", data[0] ? "connected to" : "disconnected from");
+			return;
+
+		case ACM_IRQ_LINE_STATE:
+
+			newctrl = le16_to_cpup((__u16 *) data);
+
+#if 0
+			/* Please someone tell me how to do this properly to kill pppd and not kill minicom */
+			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
+				dbg("calling hangup");
+				tty_hangup(acm->tty);
+			}
+#endif
+
+			acm->ctrlin = newctrl;
+
+			dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+				acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',	acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
+				acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',	acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
+				acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',	acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
+				acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
+
+			return;
+
+		default:
+			dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+				dr->request, dr->index, dr->length, data[0], data[1]);
+			return;
+	}
+}
+
+static void acm_read_bulk(struct urb *urb)
+{
+	struct acm *acm = urb->context;
+	struct tty_struct *tty = acm->tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i = 0;
+
+	if (!ACM_READY(acm)) return;
+
+	if (urb->status)
+		dbg("nonzero read bulk status received: %d", urb->status);
+
+	if (!urb->status & !acm->throttle)  {
+		for (i = 0; i < urb->actual_length && !acm->throttle; i++)
+			tty_insert_flip_char(tty, data[i], 0);
+		tty_flip_buffer_push(tty);
+	}
+
+	if (acm->throttle) {
+		memmove(data, data + i, urb->actual_length - i);
+		urb->actual_length -= i;
+		return;
+	}
+
+	urb->actual_length = 0;
+
+	if (usb_submit_urb(urb))
+		dbg("failed resubmitting read urb");
+}
+
+static void acm_write_bulk(struct urb *urb)
+{
+	struct acm *acm = (struct acm *)urb->context;
+
+	if (!ACM_READY(acm)) return;
+
+	if (urb->status)
+		dbg("nonzero write bulk status received: %d", urb->status);
+
+	queue_task(&acm->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void acm_softint(void *private)
+{
+	struct acm *acm = private;
+	struct tty_struct *tty = acm->tty;
+
+ 	if (!ACM_READY(acm)) return;
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+
+	wake_up_interruptible(&tty->write_wait);
+}
+
+/*
+ * TTY handlers
+ */
+
+static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	struct acm *acm = acm_table[MINOR(tty->device)];
+
+	if (!acm || !acm->dev) return -EINVAL;
+
+	tty->driver_data = acm;
+	acm->tty = tty;
+
+	MOD_INC_USE_COUNT;
+
+	if (acm->used++) return 0;
+
+	if (usb_submit_urb(&acm->ctrlurb))
+		dbg("usb_submit_urb(ctrl irq) failed");
+
+	if (usb_submit_urb(&acm->readurb))
+		dbg("usb_submit_urb(read bulk) failed");
+
+	acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
+
+	return 0;
+}
+
+static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	struct acm *acm = tty->driver_data;
+
+	if (!acm || !acm->used) return;
+
+	if (!--acm->used) {
+		if (acm->dev) {
+			acm_set_control(acm, acm->ctrlout = 0);
+			usb_unlink_urb(&acm->ctrlurb);
+			usb_unlink_urb(&acm->writeurb);
+			usb_unlink_urb(&acm->readurb);
+		} else {
+			tty_unregister_devfs(&acm_tty_driver, acm->minor);
+			acm_table[acm->minor] = NULL;
+			kfree(acm);
+		}
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	struct acm *acm = tty->driver_data;
+
+	if (!ACM_READY(acm)) return -EINVAL;
+	if (acm->writeurb.status == -EINPROGRESS) return 0;
+	if (!count) return 0;
+
+	count = (count > acm->writesize) ? acm->writesize : count;
+
+	if (from_user)
+		copy_from_user(acm->writeurb.transfer_buffer, buf, count);
+	else
+		memcpy(acm->writeurb.transfer_buffer, buf, count);
+
+	acm->writeurb.transfer_buffer_length = count;
+
+	if (usb_submit_urb(&acm->writeurb))
+		dbg("usb_submit_urb(write bulk) failed");
+
+	return count;
+}
+
+static int acm_tty_write_room(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	if (!ACM_READY(acm)) return -EINVAL;
+	return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize;
+}
+
+static int acm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	if (!ACM_READY(acm)) return -EINVAL;
+	return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0;
+}
+
+static void acm_tty_throttle(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	if (!ACM_READY(acm)) return;
+	acm->throttle = 1;
+}
+
+static void acm_tty_unthrottle(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	if (!ACM_READY(acm)) return;
+	acm->throttle = 0;
+	if (acm->readurb.status != -EINPROGRESS)
+		acm_read_bulk(&acm->readurb);
+}
+
+static void acm_tty_break_ctl(struct tty_struct *tty, int state)
+{
+	struct acm *acm = tty->driver_data;
+	if (!ACM_READY(acm)) return;
+	if (acm_send_break(acm, state ? 0xffff : 0))
+		dbg("send break failed");
+}
+
+static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct acm *acm = tty->driver_data;
+	unsigned int retval, mask, newctrl;
+
+	if (!ACM_READY(acm)) return -EINVAL;
+
+	switch (cmd) {
+
+		case TIOCMGET:
+
+			return put_user((acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
+				(acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
+				(acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
+				(acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
+				(acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
+				 TIOCM_CTS, (unsigned long *) arg);
+
+		case TIOCMSET:
+		case TIOCMBIS:
+		case TIOCMBIC:
+
+			if ((retval = get_user(mask, (unsigned long *) arg))) return retval;
+
+			newctrl = acm->ctrlout;
+			mask = (mask & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (mask & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+
+			switch (cmd) {
+				case TIOCMSET: newctrl  =  mask; break;
+				case TIOCMBIS: newctrl |=  mask; break;
+				case TIOCMBIC: newctrl &= ~mask; break;
+			}
+	
+			if (acm->ctrlout == newctrl) return 0; 
+			return acm_set_control(acm, acm->ctrlout = newctrl);
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static __u32 acm_tty_speed[] = { 
+	0, 50, 75, 110, 134, 150, 200, 300, 600,
+	1200, 1800, 2400, 4800, 9600, 19200, 38400,
+	57600, 115200, 230400, 460800, 500000, 576000,
+	921600, 1000000, 1152000, 1500000, 2000000,
+	2500000, 3000000, 3500000, 4000000
+};
+
+static __u8 acm_tty_size[] = {
+	5, 6, 7, 8
+};
+
+static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old)
+{
+	struct acm *acm = tty->driver_data;
+	struct termios *termios = tty->termios;
+	struct acm_line newline;
+	int newctrl = acm->ctrlout;
+
+	if (!ACM_READY(acm)) return;
+
+	newline.speed = cpu_to_le32p(acm_tty_speed +
+		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
+	newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0;
+	newline.parity = termios->c_cflag & PARENB ?
+		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
+	newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+
+	acm->clocal = termios->c_cflag & CLOCAL;
+	
+	if (!newline.speed) {
+		newline.speed = acm->line.speed;
+		newctrl &= ~ACM_CTRL_DTR;
+	} else  newctrl |=  ACM_CTRL_DTR;
+
+	if (newctrl != acm->ctrlout)
+		acm_set_control(acm, acm->ctrlout = newctrl);
+
+	if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) {
+		memcpy(&acm->line, &newline, sizeof(struct acm_line));
+		dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits);
+		acm_set_line(acm, &acm->line);
+	}
+}
+
+/*
+ * USB probe and disconnect routines.
+ */
+
+static void *acm_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct acm *acm;
+	struct usb_config_descriptor *cfacm;
+	struct usb_interface_descriptor *ifcom, *ifdata;
+	struct usb_endpoint_descriptor *epctrl, *epread, *epwrite;
+	int readsize, ctrlsize, minor, i;
+	unsigned char *buf;
+
+	if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0
+		|| dev->descriptor.bDeviceProtocol != 0) return NULL;
+
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+
+		cfacm = dev->config + i;
+
+		dbg("probing config %d", cfacm->bConfigurationValue);
+
+		if (cfacm->bNumInterfaces != 2 || 
+		    usb_interface_claimed(cfacm->interface + 0) ||
+		    usb_interface_claimed(cfacm->interface + 1))
+			continue;
+
+		ifcom = cfacm->interface[0].altsetting + 0;
+		ifdata = cfacm->interface[1].altsetting + 0;
+
+		if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) {
+			ifcom = cfacm->interface[1].altsetting + 0;
+			ifdata = cfacm->interface[0].altsetting + 0;
+			if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2)
+				continue;
+		}
+
+		if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 ||
+		    ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1)
+			continue;
+
+		epctrl = ifcom->endpoint + 0;
+		epread = ifdata->endpoint + 0;
+		epwrite = ifdata->endpoint + 1;
+
+		if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 ||
+		   (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 ||
+		   ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80)
+			continue;
+
+		if ((epread->bEndpointAddress & 0x80) != 0x80) {
+			epread = ifdata->endpoint + 1;
+			epwrite = ifdata->endpoint + 0;
+		}
+
+		usb_set_configuration(dev, cfacm->bConfigurationValue);
+
+		for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
+		if (acm_table[minor]) {
+			err("no more free acm devices");
+			return NULL;
+		}
+
+		if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
+			err("out of memory");
+			return NULL;
+		}
+		memset(acm, 0, sizeof(struct acm));
+
+		ctrlsize = epctrl->wMaxPacketSize;
+		readsize = epread->wMaxPacketSize;
+		acm->writesize = epwrite->wMaxPacketSize;
+		acm->iface = cfacm->interface;
+		acm->minor = minor;
+		acm->dev = dev;
+
+		acm->tqueue.routine = acm_softint;
+		acm->tqueue.data = acm;
+
+		if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
+			err("out of memory");
+			kfree(acm);
+			return NULL;
+		}
+
+		FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
+			buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
+
+		FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+			buf += ctrlsize, readsize, acm_read_bulk, acm);
+
+		FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+			buf += readsize, acm->writesize, acm_write_bulk, acm);
+	
+		printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
+
+		acm_set_control(acm, acm->ctrlout);
+
+		acm->line.speed = cpu_to_le32(9600);
+		acm->line.databits = 8;
+		acm_set_line(acm, &acm->line);
+
+		usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm);
+		usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm);
+
+		tty_register_devfs(&acm_tty_driver, 0, minor);
+		return acm_table[minor] = acm;
+	}
+
+	return NULL;
+}
+
+static void acm_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct acm *acm = ptr;
+
+	if (!acm || !acm->dev) {
+		dbg("disconnect on nonexisting interface");
+		return;
+	}
+
+	acm->dev = NULL;
+
+	usb_unlink_urb(&acm->ctrlurb);
+	usb_unlink_urb(&acm->readurb);
+	usb_unlink_urb(&acm->writeurb);
+
+	kfree(acm->ctrlurb.transfer_buffer);
+
+	usb_driver_release_interface(&acm_driver, acm->iface + 0);
+	usb_driver_release_interface(&acm_driver, acm->iface + 1);
+
+	if (!acm->used) {
+		tty_unregister_devfs(&acm_tty_driver, acm->minor);
+		acm_table[acm->minor] = NULL;
+		kfree(acm);
+		return;
+	}
+
+	if (acm->tty)
+		tty_hangup(acm->tty);
+}
+
+/*
+ * USB driver structure.
+ */
+
+static struct usb_driver acm_driver = {
+	name:		"acm",
+	probe:		acm_probe,
+	disconnect:	acm_disconnect
+};
+
+/*
+ * TTY driver structures.
+ */
+
+static int acm_tty_refcount;
+
+static struct tty_struct *acm_tty_table[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS];
+
+static struct tty_driver acm_tty_driver = {
+	magic:			TTY_DRIVER_MAGIC,
+	driver_name:		"acm",
+	name:			"usb/acm/%d",
+	major:			ACM_TTY_MAJOR,
+	minor_start:		0,
+	num:			ACM_TTY_MINORS,
+	type:			TTY_DRIVER_TYPE_SERIAL,
+	subtype:		SERIAL_TYPE_NORMAL,
+	flags:			TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
+
+	refcount:		&acm_tty_refcount,
+
+	table:			acm_tty_table,
+	termios:		acm_tty_termios,
+	termios_locked:		acm_tty_termios_locked,
+
+	open:			acm_tty_open,
+	close:			acm_tty_close,
+	write:			acm_tty_write,
+	write_room:		acm_tty_write_room,
+	ioctl:			acm_tty_ioctl,
+	throttle:		acm_tty_throttle,
+	unthrottle:		acm_tty_unthrottle,
+	chars_in_buffer:	acm_tty_chars_in_buffer,
+	break_ctl:		acm_tty_break_ctl,
+	set_termios:		acm_tty_set_termios
+};
+
+/*
+ * Init / exit.
+ */
+
+static int __init acm_init(void)
+{
+	acm_tty_driver.init_termios =		tty_std_termios;
+	acm_tty_driver.init_termios.c_cflag =	B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+
+	if (tty_register_driver(&acm_tty_driver))
+		return -1;
+
+	if (usb_register(&acm_driver) < 0) {
+		tty_unregister_driver(&acm_tty_driver);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void __exit acm_exit(void)
+{
+	usb_deregister(&acm_driver);
+	tty_unregister_driver(&acm_tty_driver);
+}
+
+module_init(acm_init);
+module_exit(acm_exit);
+
+MODULE_AUTHOR("Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik");
+MODULE_DESCRIPTION("USB Abstract Control Model driver for USB modems and ISDN adapters");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/audio.c linux.18p17/drivers/usb/audio.c
--- linux.vanilla/drivers/usb/audio.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/audio.c	Thu Oct 19 01:14:53 2000
@@ -1,126 +1,3733 @@
+/*****************************************************************************/
+
+/*
+ *	audio.c  --  USB Audio Class driver
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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-09-07:  Alan Cox
+ *		Parsing Audio descriptor patch
+ * 1999-09-08:  Thomas Sailer
+ *		Added OSS compatible data io functions; both parts of the
+ *		driver remain to be glued together
+ * 1999-09-10:  Thomas Sailer
+ *		Beautified the driver. Added sample format conversions.
+ *		Still not properly glued with the parsing code.
+ *		The parsing code seems to have its problems btw,
+ *		Since it parses all available configs but doesn't
+ *		store which iface/altsetting belongs to which config.
+ * 1999-09-20:  Thomas Sailer
+ *		Threw out Alan's parsing code and implemented my own one.
+ *		You cannot reasonnably linearly parse audio descriptors,
+ *		especially the AudioClass descriptors have to be considered
+ *		pointer lists. Mixer parsing untested, due to lack of device.
+ *		First stab at synch pipe implementation, the Dallas USB DAC
+ *		wants to use an Asynch out pipe. usb_audio_state now basically
+ *		only contains lists of mixer and wave devices. We can therefore
+ *		now have multiple mixer/wave devices per USB device.
+ * 1999-10-28:  Thomas Sailer
+ *		Converted to URB API. Fixed a taskstate/wakeup semantics mistake
+ *		that made the driver consume all available CPU cycles.
+ *		Now runs stable on UHCI-Acher/Fliegl/Sailer.
+ * 1999-10-31:  Thomas Sailer
+ *		Audio can now be unloaded if it is not in use by any mixer
+ *		or dsp client (formerly you had to disconnect the audio devices
+ *		from the USB port)
+ *		Finally, about three months after ordering, my "Maxxtro SPK222"
+ *		speakers arrived, isn't disdata a great mail order company 8-)
+ *		Parse class specific endpoint descriptor of the audiostreaming
+ *		interfaces and take the endpoint attributes from there.
+ *		Unbelievably, the Philips USB DAC has a sampling rate range
+ *		of over a decade, yet does not support the sampling rate control!
+ *		No wonder it sounds so bad, has very audible sampling rate
+ *		conversion distortion. Don't try to listen to it using
+ *		decent headphones!
+ *		"Let's make things better" -> but please Philips start with your
+ *		own stuff!!!!
+ * 1999-11-02:  Thomas Sailer
+ *		It takes the Philips boxes several seconds to acquire synchronisation
+ *		that means they won't play short sounds. Should probably maintain
+ *		the ISO datastream even if there's nothing to play.
+ *		Fix counting the total_bytes counter, RealPlayer G2 depends on it.
+ * 1999-12-20:  Thomas Sailer
+ *		Fix bad bug in conversion to per interface probing.
+ *		disconnect was called multiple times for the audio device,
+ *		leading to a premature freeing of the audio structures
+ * 2000-05-13:  Thomas Sailer
+ *		I don't remember who changed the find_format routine,
+ *              but the change was completely broken for the Dallas
+ *              chip. Anyway taking sampling rate into account in find_format
+ *              is bad and should not be done unless there are devices with
+ *              completely broken audio descriptors. Unless someone shows
+ *              me such a descriptor, I will not allow find_format to
+ *              take the sampling rate into account.
+ *              Also, the former find_format made:
+ *              - mpg123 play mono instead of stereo
+ *              - sox completely fail for wav's with sample rates < 44.1kHz
+ *                  for the Dallas chip.
+ *              Also fix a rather long standing problem with applications that
+ *              use "small" writes producing no sound at all.
+ * 2000-05-15:  Thomas Sailer
+ *		My fears came true, the Philips camera indeed has pretty stupid
+ *              audio descriptors.
+ * 2000-05-17:  Thomas Sailer
+ *		Nemsoft spotted my stupid last minute change, thanks
+ * 2000-05-19:  Thomas Sailer
+ *		Fixed FEATURE_UNIT thinkos found thanks to the KC Technology
+ *              Xtend device. Basically the driver treated FEATURE_UNIT's sourced
+ *              by mono terminals as stereo.
+ * 2000-05-20:  Thomas Sailer
+ *		SELECTOR support (and thus selecting record channels from the mixer).
+ *              Somewhat peculiar due to OSS interface limitations. Only works
+ *              for channels where a "slider" is already in front of it (i.e.
+ *              a MIXER unit or a FEATURE unit with volume capability).
+ *
+ */
+
+/*
+ * Strategy:
+ *
+ * Alan Cox and Thomas Sailer are starting to dig at opposite ends and
+ * are hoping to meet in the middle, just like tunnel diggers :)
+ * Alan tackles the descriptor parsing, Thomas the actual data IO and the
+ * OSS compatible interface.
+ *
+ * Data IO implementation issues
+ *
+ * A mmap'able ring buffer per direction is implemented, because
+ * almost every OSS app expects it. It is however impractical to
+ * transmit/receive USB data directly into and out of the ring buffer,
+ * due to alignment and synchronisation issues. Instead, the ring buffer
+ * feeds a constant time delay line that handles the USB issues.
+ *
+ * Now we first try to find an alternate setting that exactly matches
+ * the sample format requested by the user. If we find one, we do not
+ * need to perform any sample rate conversions. If there is no matching
+ * altsetting, we choose the closest one and perform sample format
+ * conversions. We never do sample rate conversion; these are too
+ * expensive to be performed in the kernel.
+ *
+ * Current status:
+ * - Pretty stable on UHCI-Acher/Fliegl/Sailer
+ * - Does not work on OHCI due to lack of OHCI driver supporting URB's
+ *
+ * Generally: Due to the brokenness of the Audio Class spec
+ * it seems generally impossible to write a generic Audio Class driver,
+ * so a reasonable driver should implement the features that are actually
+ * used.
+ *
+ * Parsing implementation issues
+ *
+ * One cannot reasonably parse the AudioClass descriptors linearly.
+ * Therefore the current implementation features routines to look
+ * for a specific descriptor in the descriptor list.
+ *
+ * How does the parsing work? First, all interfaces are searched
+ * for an AudioControl class interface. If found, the config descriptor
+ * that belongs to the current configuration is fetched from the device.
+ * Then the HEADER descriptor is fetched. It contains a list of
+ * all AudioStreaming and MIDIStreaming devices. This list is then walked,
+ * and all AudioStreaming interfaces are classified into input and output
+ * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming
+ * is currently not supported). The input & output list is then used
+ * to group inputs and outputs together and issued pairwise to the
+ * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors
+ * are walked and issued to the mixer construction routine.
+ *
+ * The AudioStreaming parser simply enumerates all altsettings belonging
+ * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE
+ * class specific descriptors to extract the sample format/sample rate
+ * data. Only sample format types PCM and PCM8 are supported right now, and
+ * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to
+ * be the first endpoint of the interface, and the optional synchronisation
+ * isochronous endpoint the second one.
+ *
+ * Mixer construction works as follows: The various TERMINAL and UNIT
+ * descriptors span a tree from the root (OUTPUT_TERMINAL) through the
+ * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk
+ * that tree in a depth first manner. FEATURE_UNITs may contribute volume,
+ * bass and treble sliders to the mixer, MIXER_UNITs volume sliders.
+ * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic
+ * to determine "meaningful" OSS slider numbers, however we will see
+ * how well this works in practice. Other features are not used at the
+ * moment, they seem less often used. Also, it seems difficult at least
+ * to construct recording source switches from SELECTOR_UNITs, but
+ * since there are not many USB ADC's available, we leave that for later.
+ */
+
+/*****************************************************************************/
+
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
-#include "usb.h"
+#include <linux/module.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/wrapper.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/usb.h>
 
-static int usb_audio_probe(struct usb_device *dev);
-static void usb_audio_disconnect(struct usb_device *dev);
-static LIST_HEAD(usb_audio_list);
+#include "audio.h"
 
-struct usb_audio
-{
-	struct usb_device *dev;
+#define AUDIO_DEBUG 1
+
+#define SND_DEV_DSP16   5 
+
+#define dprintk(x)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Linked list of all audio devices...
+ */
+static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs);
+static DECLARE_MUTEX(open_sem);
+
+/*
+ * wait queue for processes wanting to open an USB audio device
+ */
+static DECLARE_WAIT_QUEUE_HEAD(open_wait);
+
+
+#define MAXFORMATS        MAX_ALT
+#define DMABUFSHIFT       17  /* 128k worth of DMA buffer */
+#define NRSGBUF           (1U<<(DMABUFSHIFT-PAGE_SHIFT))
+
+/*
+ * This influences:
+ * - Latency
+ * - Interrupt rate
+ * - Synchronisation behaviour
+ * Don't touch this if you don't understand all of the above.
+ */
+#define DESCFRAMES  5
+#define SYNCFRAMES  DESCFRAMES
+
+#define MIXFLG_STEREOIN   1
+#define MIXFLG_STEREOOUT  2
+
+struct mixerchannel {
+	__u16 value;
+	__u16 osschannel;  /* number of the OSS channel */
+	__s16 minval, maxval;
+	__u16 slctunitid;
+	__u8 unitid;
+	__u8 selector;
+	__u8 chnum;
+	__u8 flags;
+};
+
+struct audioformat {
+	unsigned int format;
+	unsigned int sratelo;
+	unsigned int sratehi;
+	unsigned char altsetting;
+	unsigned char attributes;
+};
+
+struct dmabuf {
+	/* buffer data format */
+	unsigned int format;
+	unsigned int srate;
+	/* physical buffer */
+	unsigned char *sgbuf[NRSGBUF];
+	unsigned bufsize;
+	unsigned numfrag;
+	unsigned fragshift;
+	unsigned wrptr, rdptr;
+	unsigned total_bytes;
+	int count;
+	unsigned error; /* over/underrun */
+	wait_queue_head_t wait;
+	/* redundant, but makes calculations easier */
+	unsigned fragsize;
+	unsigned dmasize;
+	/* OSS stuff */
+	unsigned mapped:1;
+	unsigned ready:1;
+	unsigned ossfragshift;
+	int ossmaxfrags;
+	unsigned subdivision;
+};
+
+struct usb_audio_state;
+
+#define FLG_URB0RUNNING   1
+#define FLG_URB1RUNNING   2
+#define FLG_SYNC0RUNNING  4
+#define FLG_SYNC1RUNNING  8
+#define FLG_RUNNING      16
+#define FLG_CONNECTED    32
+
+struct my_data_urb {
+	urb_t urb;
+	iso_packet_descriptor_t isoframe[DESCFRAMES];
+};
+
+struct my_sync_urb {
+	urb_t urb;
+	iso_packet_descriptor_t isoframe[SYNCFRAMES];
+};
+
+
+struct usb_audiodev {
+	struct list_head list;
+	struct usb_audio_state *state;
+	
+	/* soundcore stuff */
+	int dev_audio;
+
+	/* wave stuff */
+	mode_t open_mode;
+	spinlock_t lock;         /* DMA buffer access spinlock */
+
+	struct usbin {
+		int interface;           /* Interface number, -1 means not used */
+		unsigned int format;     /* USB data format */
+		unsigned int datapipe;   /* the data input pipe */
+		unsigned int syncpipe;   /* the synchronisation pipe - 0 for anything but adaptive IN mode */
+		unsigned int syncinterval;  /* P for adaptive IN mode, 0 otherwise */
+		unsigned int freqn;      /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */
+		unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+		unsigned int phase;      /* phase accumulator */
+		unsigned int flags;      /* see FLG_ defines */
+		
+		struct my_data_urb durb[2];  /* ISO descriptors for the data endpoint */
+		struct my_sync_urb surb[2];  /* ISO sync pipe descriptor if needed */
+		
+		struct dmabuf dma;
+	} usbin;
+
+	struct usbout {
+		int interface;           /* Interface number, -1 means not used */
+		unsigned int format;     /* USB data format */
+		unsigned int datapipe;   /* the data input pipe */
+		unsigned int syncpipe;   /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */
+		unsigned int syncinterval;  /* P for asynchronous OUT mode, 0 otherwise */
+		unsigned int freqn;      /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */
+		unsigned int freqm;      /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */
+		unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+		unsigned int phase;      /* phase accumulator */
+		unsigned int flags;      /* see FLG_ defines */
+
+		struct my_data_urb durb[2];  /* ISO descriptors for the data endpoint */
+		struct my_sync_urb surb[2];  /* ISO sync pipe descriptor if needed */
+		
+		struct dmabuf dma;
+	} usbout;
+
+
+	unsigned int numfmtin, numfmtout;
+	struct audioformat fmtin[MAXFORMATS];
+	struct audioformat fmtout[MAXFORMATS];
+};  
+
+struct usb_mixerdev {
 	struct list_head list;
+	struct usb_audio_state *state;
+
+	/* soundcore stuff */
+	int dev_mixer;
+
+	unsigned char iface;  /* interface number of the AudioControl interface */
+
+	/* USB format descriptions */
+	unsigned int numch, modcnt;
+
+	/* mixch is last and gets allocated dynamically */
+	struct mixerchannel ch[0];
 };
 
-static struct usb_driver usb_audio_driver =
-{
-	"audio",
-	usb_audio_probe,
-	usb_audio_disconnect,
-	{NULL, NULL}
+struct usb_audio_state {
+	struct list_head audiodev;
+
+	/* USB device */
+	struct usb_device *usbdev;
+
+	struct list_head audiolist;
+	struct list_head mixerlist;
+
+	unsigned count;  /* usage counter; NOTE: the usb stack is also considered a user */
 };
 
+/* private audio format extensions */
+#define AFMT_STEREO        0x80000000
+#define AFMT_ISSTEREO(x)   ((x) & AFMT_STEREO)
+#define AFMT_IS16BIT(x)    ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE))
+#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE))
+#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0))
+#define AFMT_BYTES(x)      (1<<AFMT_BYTESSHFIT(x))
+
+/* --------------------------------------------------------------------- */
 
-static int usb_audio_irq(int state, void *buffer, void *dev_id)
+extern inline unsigned ld2(unsigned int x)
 {
-	struct usb_audio *aud = (struct usb_audio*) dev_id;
-	return 1;
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * OSS compatible ring buffer management. The ring buffer may be mmap'ed into
+ * an application address space.
+ *
+ * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so
+ * we now use an array of pointers to a single page each. This saves us the
+ * kernel page table manipulations, but we have to do a page table alike mechanism
+ * (though only one indirection) in software.
+ */
+
+static void dmabuf_release(struct dmabuf *db)
+{
+	unsigned int nr;
+	void *p;
+
+	for(nr = 0; nr < NRSGBUF; nr++) {
+		if (!(p = db->sgbuf[nr]))
+			continue;
+		mem_map_unreserve(MAP_NR(p));
+		free_page((unsigned long)p);
+		db->sgbuf[nr] = NULL;
+	}
+	db->mapped = db->ready = 0;
 }
 
-static int usb_audio_probe(struct usb_device *dev)
+static int dmabuf_init(struct dmabuf *db)
 {
-	struct usb_interface_descriptor *interface;
-	struct usb_endpoint_descriptor *endpoint;
-	struct usb_audio *aud;
+	unsigned int nr, bytepersec, bufs;
+	void *p;
 
-	int i;
-	int na=0;
-	
-	interface = &dev->config[0].interface[0];
+	/* initialize some fields */
+	db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0;
+	/* calculate required buffer size */
+	bytepersec = db->srate << AFMT_BYTESSHIFT(db->format);
+	bufs = 1U << DMABUFSHIFT;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->dmasize = db->numfrag << db->fragshift;
+	for(nr = 0; nr < NRSGBUF; nr++) {
+		if (!db->sgbuf[nr]) {
+			p = (void *)get_free_page(GFP_KERNEL);
+			if (!p)
+				return -ENOMEM;
+			db->sgbuf[nr] = p;
+			mem_map_reserve(MAP_NR(p));
+		}
+		memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE);
+		if ((nr << PAGE_SHIFT) >= db->dmasize)
+			break;
+	}
+	db->bufsize = nr << PAGE_SHIFT;
+	db->ready = 1;
+	dprintk((KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d "
+	       "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n",
+	       bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize,
+	       db->numfrag, db->dmasize, db->bufsize, db->format));
+	return 0;
+}
+
+static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot)
+{
+	unsigned int nr;
+
+	if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize)
+		return -EINVAL;
+	size >>= PAGE_SHIFT;
+	for(nr = 0; nr < size; nr++)
+		if (!db->sgbuf[nr])
+			return -EINVAL;
+	db->mapped = 1;
+	for(nr = 0; nr < size; nr++) {
+		if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), PAGE_SIZE, prot))
+			return -EAGAIN;
+		start += PAGE_SIZE;
+	}
+	return 0;
+}
+
+static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size)
+{
+	unsigned int pgrem, rem;
+
+	db->total_bytes += size;
+	for (;;) {
+		if (size <= 0)
+			return;
+		pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1;
+		if (pgrem > size)
+			pgrem = size;
+		rem = db->dmasize - db->wrptr;
+		if (pgrem > rem)
+			pgrem = rem;
+		memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem);
+		size -= pgrem;
+		(char *)buffer += pgrem;
+		db->wrptr += pgrem;
+		if (db->wrptr >= db->dmasize)
+			db->wrptr = 0;
+	}
+}
+
+static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size)
+{
+	unsigned int pgrem, rem;
+
+	db->total_bytes += size;
+	for (;;) {
+		if (size <= 0)
+			return;
+		pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1;
+		if (pgrem > size)
+			pgrem = size;
+		rem = db->dmasize - db->rdptr;
+		if (pgrem > rem)
+			pgrem = rem;
+		memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem);
+		size -= pgrem;
+		(char *)buffer += pgrem;
+		db->rdptr += pgrem;
+		if (db->rdptr >= db->dmasize)
+			db->rdptr = 0;
+	}
+}
+
+static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void *buffer, unsigned int size)
+{
+	unsigned int pgrem, rem;
+
+	if (!db->ready || db->mapped)
+		return -EINVAL;
+	for (;;) {
+		if (size <= 0)
+			return 0;
+		pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1;
+		if (pgrem > size)
+			pgrem = size;
+		rem = db->dmasize - ptr;
+		if (pgrem > rem)
+			pgrem = rem;
+		copy_from_user_ret((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem, -EFAULT);
+		size -= pgrem;
+		(char *)buffer += pgrem;
+		ptr += pgrem;
+		if (ptr >= db->dmasize)
+			ptr = 0;
+	}
+}
+
+static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void *buffer, unsigned int size)
+{
+	unsigned int pgrem, rem;
+
+	if (!db->ready || db->mapped)
+		return -EINVAL;
+	for (;;) {
+		if (size <= 0)
+			return 0;
+		pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1;
+		if (pgrem > size)
+			pgrem = size;
+		rem = db->dmasize - ptr;
+		if (pgrem > rem)
+			pgrem = rem;
+		copy_to_user_ret(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem, -EFAULT);
+		size -= pgrem;
+		(char *)buffer += pgrem;
+		ptr += pgrem;
+		if (ptr >= db->dmasize)
+			ptr = 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * USB I/O code. We do sample format conversion if necessary
+ */
+
+static void usbin_stop(struct usb_audiodev *as)
+{
+	struct usbin *u = &as->usbin;
+	unsigned long flags;
+	unsigned int i, notkilled = 1;
+
+	spin_lock_irqsave(&as->lock, flags);
+	u->flags &= ~FLG_RUNNING;
+	i = u->flags;
+	spin_unlock_irqrestore(&as->lock, flags);
+	while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
+		set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		spin_lock_irqsave(&as->lock, flags);
+		i = u->flags;
+		spin_unlock_irqrestore(&as->lock, flags);
+		if (notkilled && signal_pending(current)) {
+			if (i & FLG_URB0RUNNING)
+				usb_unlink_urb(&u->durb[0].urb);
+			if (i & FLG_URB1RUNNING)
+				usb_unlink_urb(&u->durb[1].urb);
+			if (i & FLG_SYNC0RUNNING)
+				usb_unlink_urb(&u->surb[0].urb);
+			if (i & FLG_SYNC1RUNNING)
+				usb_unlink_urb(&u->surb[1].urb);
+			notkilled = 0;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	if (u->durb[0].urb.transfer_buffer)
+		kfree(u->durb[0].urb.transfer_buffer);
+	if (u->durb[1].urb.transfer_buffer)
+		kfree(u->durb[1].urb.transfer_buffer);
+	if (u->surb[0].urb.transfer_buffer)
+		kfree(u->surb[0].urb.transfer_buffer);
+	if (u->surb[1].urb.transfer_buffer)
+		kfree(u->surb[1].urb.transfer_buffer);
+	u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = 
+		u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL;
+}
+
+static inline void usbin_release(struct usb_audiodev *as)
+{
+	usbin_stop(as);
+}
+
+static void usbin_disc(struct usb_audiodev *as)
+{
+	struct usbin *u = &as->usbin;
+
+	unsigned long flags;
+
+	spin_lock_irqsave(&as->lock, flags);
+	u->flags &= ~(FLG_RUNNING | FLG_CONNECTED);
+	spin_unlock_irqrestore(&as->lock, flags);
+	usbin_stop(as);
+}
+
+static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt)
+{
+	unsigned int cnt, i;
+	__s16 *sp, *sp2, s;
+	unsigned char *bp;
 
-	for(i=0;i<dev->config[0].bNumInterfaces;i++)
-	{
-		int x;
+	cnt = scnt;
+	if (AFMT_ISSTEREO(ifmt))
+		cnt <<= 1;
+	sp = ((__s16 *)tmp) + cnt;
+	switch (ifmt & ~AFMT_STEREO) {
+	case AFMT_U8:
+		for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
+			bp--;
+			sp--;
+			*sp = (*bp ^ 0x80) << 8;
+		}
+		break;
+			
+	case AFMT_S8:
+		for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
+			bp--;
+			sp--;
+			*sp = *bp << 8;
+		}
+		break;
 		
-		endpoint = &interface->endpoint[i];
+	case AFMT_U16_LE:
+		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+			bp -= 2;
+			sp--;
+			*sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
+		}
+		break;
+
+	case AFMT_U16_BE:
+		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+			bp -= 2;
+			sp--;
+			*sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
+		}
+		break;
+
+	case AFMT_S16_LE:
+		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+			bp -= 2;
+			sp--;
+			*sp = bp[0] | (bp[1] << 8);
+		}
+		break;
+
+	case AFMT_S16_BE:
+		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+			bp -= 2;
+			sp--;
+			*sp = bp[1] | (bp[0] << 8);
+		}
+		break;
+	}
+	if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) {
+		/* expand from mono to stereo */
+		for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) {
+			sp--;
+			sp2 -= 2;
+			sp2[0] = sp2[1] = sp[0];
+		}
+	}
+	if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) {
+		/* contract from stereo to mono */
+		for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2)
+			sp[0] = (sp2[0] + sp2[1]) >> 1;
+	}
+	cnt = scnt;
+	if (AFMT_ISSTEREO(ofmt))
+		cnt <<= 1;
+	sp = ((__s16 *)tmp);
+	bp = ((unsigned char *)obuf);
+	switch (ofmt & ~AFMT_STEREO) {
+	case AFMT_U8:
+		for (i = 0; i < cnt; i++, sp++, bp++)
+			*bp = (*sp >> 8) ^ 0x80;
+		break;
+
+	case AFMT_S8:
+		for (i = 0; i < cnt; i++, sp++, bp++)
+			*bp = *sp >> 8;
+		break;
+
+	case AFMT_U16_LE:
+		for (i = 0; i < cnt; i++, sp++, bp += 2) {
+			s = *sp;
+			bp[0] = s;
+			bp[1] = (s >> 8) ^ 0x80;
+		}
+		break;
+
+	case AFMT_U16_BE:
+		for (i = 0; i < cnt; i++, sp++, bp += 2) {
+			s = *sp;
+			bp[1] = s;
+			bp[0] = (s >> 8) ^ 0x80;
+		}
+		break;
 
-		if(interface->bInterfaceClass != 1) 
-	        	continue;
+	case AFMT_S16_LE:
+		for (i = 0; i < cnt; i++, sp++, bp += 2) {
+			s = *sp;
+			bp[0] = s;
+			bp[1] = s >> 8;
+		}
+		break;
 
-		printk(KERN_INFO "USB audio device detected.\n");
+	case AFMT_S16_BE:
+		for (i = 0; i < cnt; i++, sp++, bp += 2) {
+			s = *sp;
+			bp[1] = s;
+			bp[0] = s >> 8;
+		}
+		break;
+	}
 	
-		switch(interface->bInterfaceSubClass)
-		{
-			case 0x01:
-				printk(KERN_INFO "audio: Control device.\n");
-				break;
-			case 0x02:
-				printk(KERN_INFO "audio: streaming.\n");
-				break;
-			case 0x03:
-				printk(KERN_INFO "audio: nonstreaming.\n");
-				break;
+}
+
+static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
+{
+	union {
+		__s16 s[64];
+		unsigned char b[0];
+	} tmp;
+	unsigned int scnt, maxs, ufmtsh, dfmtsh;
+
+	ufmtsh = AFMT_BYTESSHIFT(u->format);
+	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
+	while (samples > 0) {
+		scnt = samples;
+		if (scnt > maxs)
+			scnt = maxs;
+		conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt);
+		dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh);
+		buffer += scnt << ufmtsh;
+		samples -= scnt;
+	}
+}		
+
+static int usbin_prepare_desc(struct usbin *u, purb_t urb)
+{
+	unsigned int i, maxsize, offs;
+
+	maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+	//printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format);
+	for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) {
+		urb->iso_frame_desc[i].length = maxsize;
+		urb->iso_frame_desc[i].offset = offs;
+	}
+	return 0;
+}
+
+/*
+ * return value: 0 if descriptor should be restarted, -1 otherwise
+ * convert sample format on the fly if necessary
+ */
+static int usbin_retire_desc(struct usbin *u, purb_t urb)
+{
+	unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree;
+	unsigned char *cp;
+
+	ufmtsh = AFMT_BYTESSHIFT(u->format);
+	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	for (i = 0; i < DESCFRAMES; i++) {
+		cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset;
+		if (urb->iso_frame_desc[i].status) {
+			dprintk((KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
+			continue;
+		}
+		scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh;
+		if (!scnt)
+			continue;
+		cnt = scnt << dfmtsh;
+		if (!u->dma.mapped) {
+			dmafree = u->dma.dmasize - u->dma.count;
+			if (cnt > dmafree) {
+				scnt = dmafree >> dfmtsh;
+				cnt = scnt << dfmtsh;
+				err++;
+			}
 		}
-		na++;
+		u->dma.count += cnt;
+		if (u->format == u->dma.format) {
+			/* we do not need format conversion */
+			dmabuf_copyin(&u->dma, cp, cnt);
+		} else {
+			/* we need sampling format conversion */
+			usbin_convert(u, cp, scnt);
+		}
+	}
+	if (err)
+		u->dma.error++;
+	if (u->dma.count >= (signed)u->dma.fragsize)
+		wake_up(&u->dma.wait);
+	return err ? -1 : 0;
+}
+
+static void usbin_completed(struct urb *urb)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
+	struct usbin *u = &as->usbin;
+	unsigned long flags;
+	unsigned int mask;
+	int suret = USB_ST_NOERROR;
+
+#if 0
+	printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
+#endif
+	if (urb == &u->durb[0].urb)
+		mask = FLG_URB0RUNNING;
+	else if (urb == &u->durb[1].urb)
+		mask = FLG_URB1RUNNING;
+	else {
+		mask = 0;
+		printk(KERN_ERR "usbin_completed: panic: unknown URB\n");
 	}
+	urb->dev = as->state->usbdev;
+	spin_lock_irqsave(&as->lock, flags);
+	if (!usbin_retire_desc(u, urb) &&
+	    u->flags & FLG_RUNNING &&
+	    !usbin_prepare_desc(u, urb) && 
+	    (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) {
+		u->flags |= mask;
+	} else {
+		u->flags &= ~(mask | FLG_RUNNING);
+		wake_up(&u->dma.wait);
+		printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret);
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+}
+
+/*
+ * we output sync data
+ */
+static int usbin_sync_prepare_desc(struct usbin *u, purb_t urb)
+{
+	unsigned char *cp = urb->transfer_buffer;
+	unsigned int i, offs;
 	
-	if(na==0)
-		return -1;
+	for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) {
+		urb->iso_frame_desc[i].length = 3;
+		urb->iso_frame_desc[i].offset = offs;
+		cp[0] = u->freqn;
+		cp[1] = u->freqn >> 8;
+		cp[2] = u->freqn >> 16;
+	}
+	return 0;
+}
+
+/*
+ * return value: 0 if descriptor should be restarted, -1 otherwise
+ */
+static int usbin_sync_retire_desc(struct usbin *u, purb_t urb)
+{
+	unsigned int i;
+	
+	for (i = 0; i < SYNCFRAMES; i++)
+		if (urb->iso_frame_desc[0].status)
+			dprintk((KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
+	return 0;
+}
 
-	aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL);
-	if(aud)
-	{
-        	memset(aud, 0, sizeof(*aud));
-        	aud->dev = dev;
-        	dev->private = aud;
+static void usbin_sync_completed(struct urb *urb)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
+	struct usbin *u = &as->usbin;
+	unsigned long flags;
+	unsigned int mask;
+	int suret = USB_ST_NOERROR;
 
-		endpoint = &interface->endpoint[0];
+#if 0
+	printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
+#endif
+	if (urb == &u->surb[0].urb)
+		mask = FLG_SYNC0RUNNING;
+	else if (urb == &u->surb[1].urb)
+		mask = FLG_SYNC1RUNNING;
+	else {
+		mask = 0;
+		printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n");
+	}
+	urb->dev = as->state->usbdev;
+	spin_lock_irqsave(&as->lock, flags);
+	if (!usbin_sync_retire_desc(u, urb) &&
+	    u->flags & FLG_RUNNING &&
+	    !usbin_sync_prepare_desc(u, urb) && 
+	    (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) {
+		u->flags |= mask;
+	} else {
+		u->flags &= ~(mask | FLG_RUNNING);
+		wake_up(&u->dma.wait);
+		dprintk((KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+}
 
-//        	usb_set_configuration(dev, dev->config[0].bConfigurationValue);
-//        	usb_set_protocol(dev, 0);
-//        	usb_set_idle(dev, 0, 0);
-        
-        	usb_request_irq(dev,
-                        usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
-                        usb_audio_irq,
-                        endpoint->bInterval,
-                        aud);
+static int usbin_start(struct usb_audiodev *as)
+{
+	struct usb_device *dev = as->state->usbdev;
+	struct usbin *u = &as->usbin;
+	purb_t urb;
+	unsigned long flags;
+	unsigned int maxsze, bufsz;
 
-		list_add(&aud->list, &usb_audio_list);
+#if 0
+	printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n",
+	       dev->devnum, u->format, u->dma.format, u->dma.srate);
+#endif
+	/* allocate USB storage if not already done */
+	spin_lock_irqsave(&as->lock, flags);
+	if (!(u->flags & FLG_CONNECTED)) {
+		spin_unlock_irqrestore(&as->lock, flags);
+		return -EIO;
+	}
+	if (!(u->flags & FLG_RUNNING)) {
+		spin_unlock_irqrestore(&as->lock, flags);
+		u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
+		u->freqmax = u->freqn + (u->freqn >> 2);
+		u->phase = 0;
+		maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+		bufsz = DESCFRAMES * maxsze;
+		if (u->durb[0].urb.transfer_buffer)
+			kfree(u->durb[0].urb.transfer_buffer);
+		u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
+		u->durb[0].urb.transfer_buffer_length = bufsz;
+		if (u->durb[1].urb.transfer_buffer)
+			kfree(u->durb[1].urb.transfer_buffer);
+		u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
+		u->durb[1].urb.transfer_buffer_length = bufsz;
+		if (u->syncpipe) {
+			if (u->surb[0].urb.transfer_buffer)
+				kfree(u->surb[0].urb.transfer_buffer);
+			u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
+			u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES;
+			if (u->surb[1].urb.transfer_buffer)
+				kfree(u->surb[1].urb.transfer_buffer);
+			u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
+			u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES;
+		}
+		if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || 
+		    (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) {
+			printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum);
+			return 0;
+		}
+		spin_lock_irqsave(&as->lock, flags);
+	}
+	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;
+		urb->dev = dev;
+		urb->pipe = u->datapipe;
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->number_of_packets = DESCFRAMES;
+		urb->context = as;
+		urb->complete = usbin_completed;
+		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb))
+			u->flags |= FLG_URB0RUNNING;
+		else
+			u->flags &= ~FLG_RUNNING;
+	}
+	if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) {
+		urb = &u->durb[1].urb;
+		urb->dev = dev;
+		urb->pipe = u->datapipe;
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->number_of_packets = DESCFRAMES;
+		urb->context = as;
+		urb->complete = usbin_completed;
+		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb))
+			u->flags |= FLG_URB1RUNNING;
+		else
+			u->flags &= ~FLG_RUNNING;
 	}
+	if (u->syncpipe) {
+		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) {
+			urb = &u->surb[0].urb;
+			urb->dev = dev;
+			urb->pipe = u->syncpipe;
+			urb->transfer_flags = USB_ISO_ASAP;
+			urb->number_of_packets = SYNCFRAMES;
+			urb->context = as;
+			urb->complete = usbin_sync_completed;
+			/* stride: u->syncinterval */
+			if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb))
+				u->flags |= FLG_SYNC0RUNNING;
+			else
+				u->flags &= ~FLG_RUNNING;
+		}
+		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) {
+			urb = &u->surb[1].urb;
+			urb->dev = dev;
+			urb->pipe = u->syncpipe;
+			urb->transfer_flags = USB_ISO_ASAP;
+			urb->number_of_packets = SYNCFRAMES;
+			urb->context = as;
+			urb->complete = usbin_sync_completed;
+			/* stride: u->syncinterval */
+			if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb))
+				u->flags |= FLG_SYNC1RUNNING;
+			else
+				u->flags &= ~FLG_RUNNING;
+		}
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
 	return 0;
 }
 
-static void usb_audio_disconnect(struct usb_device *dev)
+static void usbout_stop(struct usb_audiodev *as)
 {
-	struct usb_audio *aud = (struct usb_audio*) dev->private;
-	if(aud)
-	{
-        	dev->private = NULL;
-        	list_del(&aud->list);
-        	kfree(aud);
+	struct usbout *u = &as->usbout;
+	unsigned long flags;
+	unsigned int i, notkilled = 1;
+
+	spin_lock_irqsave(&as->lock, flags);
+	u->flags &= ~FLG_RUNNING;
+	i = u->flags;
+	spin_unlock_irqrestore(&as->lock, flags);
+	while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
+		set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		spin_lock_irqsave(&as->lock, flags);
+		i = u->flags;
+		spin_unlock_irqrestore(&as->lock, flags);
+		if (notkilled && signal_pending(current)) {
+			if (i & FLG_URB0RUNNING)
+				usb_unlink_urb(&u->durb[0].urb);
+			if (i & FLG_URB1RUNNING)
+				usb_unlink_urb(&u->durb[1].urb);
+			if (i & FLG_SYNC0RUNNING)
+				usb_unlink_urb(&u->surb[0].urb);
+			if (i & FLG_SYNC1RUNNING)
+				usb_unlink_urb(&u->surb[1].urb);
+			notkilled = 0;
+		}
 	}
-	printk(KERN_INFO "USB audio driver removed.\n");
+	set_current_state(TASK_RUNNING);
+	if (u->durb[0].urb.transfer_buffer)
+		kfree(u->durb[0].urb.transfer_buffer);
+	if (u->durb[1].urb.transfer_buffer)
+		kfree(u->durb[1].urb.transfer_buffer);
+	if (u->surb[0].urb.transfer_buffer)
+		kfree(u->surb[0].urb.transfer_buffer);
+	if (u->surb[1].urb.transfer_buffer)
+		kfree(u->surb[1].urb.transfer_buffer);
+	u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = 
+		u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL;
 }
 
-int usb_audio_init(void)
+static inline void usbout_release(struct usb_audiodev *as)
 {
-	usb_register(&usb_audio_driver);
+	usbout_stop(as);
+}
+
+static void usbout_disc(struct usb_audiodev *as)
+{
+	struct usbout *u = &as->usbout;
+	unsigned long flags;
+
+	spin_lock_irqsave(&as->lock, flags);
+	u->flags &= ~(FLG_RUNNING | FLG_CONNECTED);
+	spin_unlock_irqrestore(&as->lock, flags);
+	usbout_stop(as);
+}
+
+static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples)
+{
+	union {
+		__s16 s[64];
+		unsigned char b[0];
+	} tmp;
+	unsigned int scnt, maxs, ufmtsh, dfmtsh;
+
+	ufmtsh = AFMT_BYTESSHIFT(u->format);
+	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
+	while (samples > 0) {
+		scnt = samples;
+		if (scnt > maxs)
+			scnt = maxs;
+		dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh);
+		conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt);
+		buffer += scnt << ufmtsh;
+		samples -= scnt;
+	}
+}		
+
+static int usbout_prepare_desc(struct usbout *u, purb_t urb)
+{
+	unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs;
+	unsigned char *cp = urb->transfer_buffer;
+
+	ufmtsh = AFMT_BYTESSHIFT(u->format);
+	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	for (i = offs = 0; i < DESCFRAMES; i++) {
+		urb->iso_frame_desc[i].offset = offs;
+		u->phase = (u->phase & 0x3fff) + u->freqm;
+		scnt = u->phase >> 14;
+		if (!scnt) {
+			urb->iso_frame_desc[i].length = 0;
+			continue;
+		}
+		cnt = scnt << dfmtsh;
+		if (!u->dma.mapped) {
+			if (cnt > u->dma.count) {
+				scnt = u->dma.count >> dfmtsh;
+				cnt = scnt << dfmtsh;
+				err++;
+			}
+			u->dma.count -= cnt;
+		} else
+			u->dma.count += cnt;
+		if (u->format == u->dma.format) {
+			/* we do not need format conversion */
+			dmabuf_copyout(&u->dma, cp, cnt);
+		} else {
+			/* we need sampling format conversion */
+			usbout_convert(u, cp, scnt);
+		}
+		cnt = scnt << ufmtsh;
+		urb->iso_frame_desc[i].length = cnt;
+		offs += cnt;
+		cp += cnt;
+	}
+	if (err)
+		u->dma.error++;
+	if (u->dma.mapped) {
+		if (u->dma.count >= (signed)u->dma.fragsize)
+			wake_up(&u->dma.wait);
+	} else {
+		if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize)
+			wake_up(&u->dma.wait);
+	}
+	return err ? -1 : 0;
+}
+
+/*
+ * return value: 0 if descriptor should be restarted, -1 otherwise
+ */
+static int usbout_retire_desc(struct usbout *u, purb_t urb)
+{
+	unsigned int i;
+
+	for (i = 0; i < DESCFRAMES; i++) {
+		if (urb->iso_frame_desc[i].status) {
+			dprintk((KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
+			continue;
+		}
+	}
+	return 0;
+}
+
+static void usbout_completed(struct urb *urb)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
+	struct usbout *u = &as->usbout;
+	unsigned long flags;
+	unsigned int mask;
+	int suret = USB_ST_NOERROR;
+
+#if 0
+	printk(KERN_DEBUG "usbout_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
+#endif
+	if (urb == &u->durb[0].urb)
+		mask = FLG_URB0RUNNING;
+	else if (urb == &u->durb[1].urb)
+		mask = FLG_URB1RUNNING;
+	else {
+		mask = 0;
+		printk(KERN_ERR "usbout_completed: panic: unknown URB\n");
+	}
+	urb->dev = as->state->usbdev;
+	spin_lock_irqsave(&as->lock, flags);
+	if (!usbout_retire_desc(u, urb) &&
+	    u->flags & FLG_RUNNING &&
+	    !usbout_prepare_desc(u, urb) && 
+	    (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) {
+		u->flags |= mask;
+	} else {
+		u->flags &= ~(mask | FLG_RUNNING);
+		wake_up(&u->dma.wait);
+		dprintk((KERN_DEBUG "usbout_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+}
+
+static int usbout_sync_prepare_desc(struct usbout *u, purb_t urb)
+{
+	unsigned int i, offs;
+
+	for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) {
+		urb->iso_frame_desc[i].length = 3;
+		urb->iso_frame_desc[i].offset = offs;
+	}
 	return 0;
 }
 
 /*
- *	Support functions for parsing
+ * return value: 0 if descriptor should be restarted, -1 otherwise
  */
- 
-void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data)
+static int usbout_sync_retire_desc(struct usbout *u, purb_t urb)
+{
+	unsigned char *cp = urb->transfer_buffer;
+	unsigned int f, i;
+
+	for (i = 0; i < SYNCFRAMES; i++, cp += 3) {
+		if (urb->iso_frame_desc[i].status) {
+			dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
+			continue;
+		}
+		if (urb->iso_frame_desc[i].actual_length < 3) {
+			dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length));
+			continue;
+		}
+		f = cp[0] | (cp[1] << 8) | (cp[2] << 16);
+		if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) {
+			printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn);
+			continue;
+		}
+		u->freqm = f;
+	}
+	return 0;
+}
+
+static void usbout_sync_completed(struct urb *urb)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
+	struct usbout *u = &as->usbout;
+	unsigned long flags;
+	unsigned int mask;
+	int suret = USB_ST_NOERROR;
+
+#if 0
+	printk(KERN_DEBUG "usbout_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
+#endif
+	if (urb == &u->surb[0].urb)
+		mask = FLG_SYNC0RUNNING;
+	else if (urb == &u->surb[1].urb)
+		mask = FLG_SYNC1RUNNING;
+	else {
+		mask = 0;
+		printk(KERN_ERR "usbout_sync_completed: panic: unknown URB\n");
+	}
+	urb->dev = as->state->usbdev;
+	spin_lock_irqsave(&as->lock, flags);
+	if (!usbout_sync_retire_desc(u, urb) &&
+	    u->flags & FLG_RUNNING &&
+	    !usbout_sync_prepare_desc(u, urb) && 
+	    (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) {
+		u->flags |= mask;
+	} else {
+		u->flags &= ~(mask | FLG_RUNNING);
+		wake_up(&u->dma.wait);
+		dprintk((KERN_DEBUG "usbout_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+}
+
+static int usbout_start(struct usb_audiodev *as)
+{
+	struct usb_device *dev = as->state->usbdev;
+	struct usbout *u = &as->usbout;
+	purb_t urb;
+	unsigned long flags;
+	unsigned int maxsze, bufsz;
+
+#if 0
+	printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n",
+	       dev->devnum, u->format, u->dma.format, u->dma.srate);
+#endif
+	/* allocate USB storage if not already done */
+	spin_lock_irqsave(&as->lock, flags);
+	if (!(u->flags & FLG_CONNECTED)) {
+		spin_unlock_irqrestore(&as->lock, flags);
+		return -EIO;
+	}
+	if (!(u->flags & FLG_RUNNING)) {
+		spin_unlock_irqrestore(&as->lock, flags);
+		u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
+		u->freqmax = u->freqn + (u->freqn >> 2);
+		u->phase = 0;
+		maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+		bufsz = DESCFRAMES * maxsze;
+		if (u->durb[0].urb.transfer_buffer)
+			kfree(u->durb[0].urb.transfer_buffer);
+		u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
+		u->durb[0].urb.transfer_buffer_length = bufsz;
+		if (u->durb[1].urb.transfer_buffer)
+			kfree(u->durb[1].urb.transfer_buffer);
+		u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
+		u->durb[1].urb.transfer_buffer_length = bufsz;
+		if (u->syncpipe) {
+			if (u->surb[0].urb.transfer_buffer)
+				kfree(u->surb[0].urb.transfer_buffer);
+			u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
+			u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES;
+			if (u->surb[1].urb.transfer_buffer)
+				kfree(u->surb[1].urb.transfer_buffer);
+			u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
+			u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES;
+		}
+		if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || 
+		    (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) {
+			printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum);
+			return 0;
+		}
+		spin_lock_irqsave(&as->lock, flags);
+	}
+	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;
+		urb->dev = dev;
+		urb->pipe = u->datapipe;
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->number_of_packets = DESCFRAMES;
+		urb->context = as;
+		urb->complete = usbout_completed;
+		if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb))
+			u->flags |= FLG_URB0RUNNING;
+		else
+			u->flags &= ~FLG_RUNNING;
+	}
+	if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) {
+		urb = &u->durb[1].urb;
+		urb->dev = dev;
+		urb->pipe = u->datapipe;
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->number_of_packets = DESCFRAMES;
+		urb->context = as;
+		urb->complete = usbout_completed;
+		if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb))
+			u->flags |= FLG_URB1RUNNING;
+		else
+			u->flags &= ~FLG_RUNNING;
+	}
+	if (u->syncpipe) {
+		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) {
+			urb = &u->surb[0].urb;
+			urb->dev = dev;
+			urb->pipe = u->syncpipe;
+			urb->transfer_flags = USB_ISO_ASAP;
+			urb->number_of_packets = SYNCFRAMES;
+			urb->context = as;
+			urb->complete = usbout_sync_completed;
+			/* stride: u->syncinterval */
+			if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb))
+				u->flags |= FLG_SYNC0RUNNING;
+			else
+				u->flags &= ~FLG_RUNNING;
+		}
+		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) {
+			urb = &u->surb[1].urb;
+			urb->dev = dev;
+			urb->pipe = u->syncpipe;
+			urb->transfer_flags = USB_ISO_ASAP;
+			urb->number_of_packets = SYNCFRAMES;
+			urb->context = as;
+			urb->complete = usbout_sync_completed;
+			/* stride: u->syncinterval */
+			if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb))
+				u->flags |= FLG_SYNC1RUNNING;
+			else
+				u->flags &= ~FLG_RUNNING;
+		}
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate)
+{
+	unsigned int g = 0;
+
+	if (srate < afp->sratelo)
+		g += afp->sratelo - srate;
+	if (srate > afp->sratehi)
+		g += srate - afp->sratehi;
+	if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt))
+		g += 0x100000;
+	if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt))
+		g += 0x400000;
+	if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt))
+		g += 0x100000;
+	if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt))
+		g += 0x400000;
+	return g;
+}
+
+static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate)
+{
+	unsigned int i, g, gb = ~0;
+	int j = -1; /* default to failure */
+
+	/* find "best" format (according to format_goodness) */
+	for (i = 0; i < nr; i++) {
+		g = format_goodness(&afp[i], fmt, srate);
+		if (g >= gb) 
+			continue;
+		j = i;
+		gb = g;
+	}
+       	return j;
+}
+
+static int set_format_in(struct usb_audiodev *as)
+{
+	struct usb_device *dev = as->state->usbdev;
+	struct usb_config_descriptor *config = dev->actconfig;
+	struct usb_interface_descriptor *alts;
+	struct usb_interface *iface;
+	struct usbin *u = &as->usbin;
+	struct dmabuf *d = &u->dma;
+	struct audioformat *fmt;
+	unsigned int ep;
+	unsigned char data[3];
+	int fmtnr, ret;
+
+	if (u->interface < 0 || u->interface >= config->bNumInterfaces)
+		return 0;
+	iface = &config->interface[u->interface];
+
+	fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate);
+	if (fmtnr < 0) {
+		printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n");
+		return -1;
+	}
+
+	fmt = as->fmtin + fmtnr;
+	alts = &iface->altsetting[fmt->altsetting];
+	u->format = fmt->format;
+	u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf);
+	u->syncpipe = u->syncinterval = 0;
+	if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x08) {
+		if (alts->bNumEndpoints < 2 ||
+		    alts->endpoint[1].bmAttributes != 0x01 ||
+		    alts->endpoint[1].bSynchAddress != 0 ||
+		    alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress & 0x7f)) {
+			printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n",
+			       dev->devnum, u->interface, fmt->altsetting);
+			return -1;
+		}
+		u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf);
+		u->syncinterval = alts->endpoint[1].bRefresh;
+	}
+	if (d->srate < fmt->sratelo)
+		d->srate = fmt->sratelo;
+	if (d->srate > fmt->sratehi)
+		d->srate = fmt->sratehi;
+	dprintk((KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
+	
+	if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) {
+		printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
+		       dev->devnum, u->interface, fmt->altsetting);
+		return -1;
+	}
+	if (fmt->sratelo == fmt->sratehi)
+		return 0;
+	ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN);
+	/* if endpoint has pitch control, enable it */
+	if (fmt->attributes & 0x02) {
+		data[0] = 1;
+		if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n",
+			       ret, dev->devnum, u->interface, ep, d->srate);
+			return -1;
+		}
+	}
+	/* if endpoint has sampling rate control, set it */
+	if (fmt->attributes & 0x01) {
+		data[0] = d->srate;
+		data[1] = d->srate >> 8;
+		data[2] = d->srate >> 16;
+		if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to set input sampling frequency device %d interface %u endpoint 0x%x to %u\n",
+			       ret, dev->devnum, u->interface, ep, d->srate);
+			return -1;
+		}
+		if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to get input sampling frequency device %d interface %u endpoint 0x%x\n",
+			       ret, dev->devnum, u->interface, ep);
+			return -1;
+		}
+		dprintk((KERN_DEBUG "usbaudio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n",
+		       dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)));
+		d->srate = data[0] | (data[1] << 8) | (data[2] << 16);
+	}
+	return 0;
+}
+
+static int set_format_out(struct usb_audiodev *as)
+{
+	struct usb_device *dev = as->state->usbdev;
+	struct usb_config_descriptor *config = dev->actconfig;
+	struct usb_interface_descriptor *alts;
+	struct usb_interface *iface;	
+	struct usbout *u = &as->usbout;
+	struct dmabuf *d = &u->dma;
+	struct audioformat *fmt;
+	unsigned int ep;
+	unsigned char data[3];
+	int fmtnr, ret;
+
+	if (u->interface < 0 || u->interface >= config->bNumInterfaces)
+		return 0;
+	iface = &config->interface[u->interface];
+
+	fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate);
+	if (fmtnr < 0) {
+		printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n");
+		return -1;
+	}
+
+	fmt = as->fmtout + fmtnr;
+	u->format = fmt->format;
+	alts = &iface->altsetting[fmt->altsetting];
+	u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf);
+	u->syncpipe = u->syncinterval = 0;
+	if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x04) {
+#if 0
+		printk(KERN_DEBUG "bNumEndpoints 0x%02x endpoint[1].bmAttributes 0x%02x\n"
+		       KERN_DEBUG "endpoint[1].bSynchAddress 0x%02x endpoint[1].bEndpointAddress 0x%02x\n"
+		       KERN_DEBUG "endpoint[0].bSynchAddress 0x%02x\n", alts->bNumEndpoints,
+		       alts->endpoint[1].bmAttributes, alts->endpoint[1].bSynchAddress,
+		       alts->endpoint[1].bEndpointAddress, alts->endpoint[0].bSynchAddress);
+#endif
+		if (alts->bNumEndpoints < 2 ||
+		    alts->endpoint[1].bmAttributes != 0x01 ||
+		    alts->endpoint[1].bSynchAddress != 0 ||
+		    alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress | 0x80)) {
+			printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n",
+			       dev->devnum, u->interface, fmt->altsetting);
+			return -1;
+		}
+		u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf);
+		u->syncinterval = alts->endpoint[1].bRefresh;
+	}
+	if (d->srate < fmt->sratelo)
+		d->srate = fmt->sratelo;
+	if (d->srate > fmt->sratehi)
+		d->srate = fmt->sratehi;
+	
+	dprintk((KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting));
+
+	if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) {
+		printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n",
+		       dev->devnum, u->interface, fmt->altsetting);
+		return -1;
+	}
+	if (fmt->sratelo == fmt->sratehi)
+		return 0;
+	ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN);
+	/* if endpoint has pitch control, enable it */
+	if (fmt->attributes & 0x02) {
+		data[0] = 1;
+		if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n",
+			       ret, dev->devnum, u->interface, ep, d->srate);
+			return -1;
+		}
+	}
+	/* if endpoint has sampling rate control, set it */
+	if (fmt->attributes & 0x01) {
+		data[0] = d->srate;
+		data[1] = d->srate >> 8;
+		data[2] = d->srate >> 16;
+		if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to set output sampling frequency device %d interface %u endpoint 0x%x to %u\n",
+			       ret, dev->devnum, u->interface, ep, d->srate);
+			return -1;
+		}
+		if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			printk(KERN_ERR "usbaudio: failure (error %d) to get output sampling frequency device %d interface %u endpoint 0x%x\n",
+			       ret, dev->devnum, u->interface, ep);
+			return -1;
+		}
+		dprintk((KERN_DEBUG "usbaudio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n",
+		       dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)));
+		d->srate = data[0] | (data[1] << 8) | (data[2] << 16);
+	}
+	return 0;
+}
+
+static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate)
+{
+	int ret1 = 0, ret2 = 0;
+
+	if (!(fmode & (FMODE_READ|FMODE_WRITE)))
+		return -EINVAL;
+	if (fmode & FMODE_READ) {
+		usbin_stop(s);
+		s->usbin.dma.ready = 0;
+		if (fmt == AFMT_QUERY)
+			fmt = s->usbin.dma.format;
+		else
+			s->usbin.dma.format = fmt;
+		if (!srate)
+			srate = s->usbin.dma.srate;
+		else
+			s->usbin.dma.srate = srate;
+	}
+	if (fmode & FMODE_WRITE) {
+		usbout_stop(s);
+		s->usbout.dma.ready = 0;
+		if (fmt == AFMT_QUERY)
+			fmt = s->usbout.dma.format;
+		else
+			s->usbout.dma.format = fmt;
+		if (!srate)
+			srate = s->usbout.dma.srate;
+		else
+			s->usbout.dma.srate = srate;
+	}
+	if (fmode & FMODE_READ)
+		ret1 = set_format_in(s);
+	if (fmode & FMODE_WRITE)
+		ret2 = set_format_out(s);
+	return ret1 ? ret1 : ret2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value)
+{
+	struct usb_device *dev = ms->state->usbdev;
+	unsigned char data[2];
+	struct mixerchannel *ch;
+	int v1, v2, v3;
+
+	if (mixch >= ms->numch)
+		return -1;
+	ch = &ms->ch[mixch];
+	v3 = ch->maxval - ch->minval;
+	v1 = value & 0xff;
+	v2 = (value >> 8) & 0xff;
+	if (v1 > 100)
+		v1 = 100;
+	if (v2 > 100)
+		v2 = 100;
+	if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
+		v2 = v1;
+	ch->value = v1 | (v2 << 8);
+	v1 = (v1 * v3) / 100 + ch->minval;
+	v2 = (v2 * v3) / 100 + ch->minval;
+	switch (ch->selector) {
+	case 0:  /* mixer unit request */
+		data[0] = v1;
+		data[1] = v1 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0)
+			goto err;
+		if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
+			return 0;
+		data[0] = v2;
+		data[1] = v2 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)),
+				    ms->iface | (ch->unitid << 8), data, 2, HZ) < 0)
+			goto err;
+		return 0;
+
+		/* various feature unit controls */
+	case VOLUME_CONTROL:
+		data[0] = v1;
+		data[1] = v1 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0)
+			goto err;
+		if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
+			return 0;
+		data[0] = v2;
+		data[1] = v2 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0)
+			goto err;
+		return 0;
+                
+	case BASS_CONTROL:
+	case MID_CONTROL:
+	case TREBLE_CONTROL:
+		data[0] = v1 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0)
+			goto err;
+		if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
+			return 0;
+		data[0] = v2 >> 8;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0)
+			goto err;
+		return 0;
+
+	default:
+		return -1;
+	}
+	return 0;
+
+ err:
+	printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", 
+		dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector);
+	return -1;
+}
+
+static int get_rec_src(struct usb_mixerdev *ms)
+{
+	struct usb_device *dev = ms->state->usbdev;
+	unsigned int mask = 0, retmask = 0;
+	unsigned int i, j;
+	unsigned char buf;
+	int err = 0;
+
+	for (i = 0; i < ms->numch; i++) {
+		if (!ms->ch[i].slctunitid || (mask & (1 << i)))
+			continue;
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) {
+			err = -EIO;
+			printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", 
+			       dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff);
+			continue;
+		}
+		for (j = i; j < ms->numch; j++) {
+			if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
+				continue;
+			mask |= 1 << j;
+			if (buf == (ms->ch[j].slctunitid >> 8))
+				retmask |= 1 << ms->ch[j].osschannel;
+		}
+	}
+	if (err)
+		return -EIO;
+	return retmask;
+}
+
+static int set_rec_src(struct usb_mixerdev *ms, int srcmask)
+{
+	struct usb_device *dev = ms->state->usbdev;
+	unsigned int mask = 0, smask, bmask;
+	unsigned int i, j;
+	unsigned char buf;
+	int err = 0;
+
+	for (i = 0; i < ms->numch; i++) {
+		if (!ms->ch[i].slctunitid || (mask & (1 << i)))
+			continue;
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) {
+			err = -EIO;
+			printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", 
+			       dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff);
+			continue;
+		}
+		/* first generate smask */
+		smask = bmask = 0;
+		for (j = i; j < ms->numch; j++) {
+			if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
+				continue;
+			smask |= 1 << ms->ch[j].osschannel;
+			if (buf == (ms->ch[j].slctunitid >> 8))
+				bmask |= 1 << ms->ch[j].osschannel;
+			mask |= 1 << j;
+		}
+		/* check for multiple set sources */
+		j = hweight32(srcmask & smask);
+		if (j == 0)
+			continue;
+		if (j > 1)
+			srcmask &= ~bmask;
+		for (j = i; j < ms->numch; j++) {
+			if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff)
+				continue;
+			if (!(srcmask & (1 << ms->ch[j].osschannel)))
+				continue;
+			buf = ms->ch[j].slctunitid >> 8;
+			if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, HZ) < 0) {
+				err = -EIO;
+				printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n", 
+				       dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff);
+				continue;
+			}
+		}
+	}
+	return err ? -EIO : 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * should be called with open_sem hold, so that no new processes
+ * look at the audio device to be destroyed
+ */
+
+static void release(struct usb_audio_state *s)
+{
+	struct usb_audiodev *as;
+	struct usb_mixerdev *ms;
+
+	s->count--;
+	if (s->count) {
+		up(&open_sem);
+		return;
+	}
+	up(&open_sem);
+	wake_up(&open_wait);
+	while (!list_empty(&s->audiolist)) {
+		as = list_entry(s->audiolist.next, struct usb_audiodev, list);
+		list_del(&as->list);
+		usbin_release(as);
+		usbout_release(as);
+		dmabuf_release(&as->usbin.dma);
+		dmabuf_release(&as->usbout.dma);
+		kfree(as);
+	}
+	while (!list_empty(&s->mixerlist)) {
+		ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list);
+		list_del(&ms->list);
+		kfree(ms);
+	}
+	kfree(s);
+}
+
+extern inline int prog_dmabuf_in(struct usb_audiodev *as)
+{
+	usbin_stop(as);
+	return dmabuf_init(&as->usbin.dma);
+}
+
+extern inline int prog_dmabuf_out(struct usb_audiodev *as)
+{
+	usbout_stop(as);
+	return dmabuf_init(&as->usbout.dma);
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int usb_audio_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct list_head *devs, *mdevs;
+	struct usb_mixerdev *ms;
+	struct usb_audio_state *s;
+
+	down(&open_sem);
+	for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) {
+		s = list_entry(devs, struct usb_audio_state, audiodev);
+		for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) {
+			ms = list_entry(mdevs, struct usb_mixerdev, list);
+			if (ms->dev_mixer == minor)
+				goto mixer_found;
+		}
+	}
+	up(&open_sem);
+	return -ENODEV;
+
+ mixer_found:
+	if (!s->usbdev) {
+		up(&open_sem);
+		return -EIO;
+	}
+	file->private_data = ms;
+	s->count++;
+
+	up(&open_sem);
+	return 0;
+}
+
+static int usb_audio_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data;
+	struct usb_audio_state *s = ms->state;
+
+	down(&open_sem);
+	release(s);
+	return 0;
+}
+
+static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data;
+	int i, j, val;
+
+	if (!ms->state->usbdev)
+		return -ENODEV;
+  
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "USB_AUDIO", sizeof(info.id));
+		strncpy(info.name, "USB Audio Class Driver", sizeof(info.name));
+		info.modify_counter = ms->modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "USB_AUDIO", sizeof(info.id));
+		strncpy(info.name, "USB Audio Class Driver", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+		return -EINVAL;
+	if (_IOC_DIR(cmd) == _IOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			val = get_rec_src(ms);
+			if (val < 0)
+				return val;
+			return put_user(val, (int *)arg);
+
+		case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < ms->numch; i++)
+				val |= 1 << ms->ch[i].osschannel;
+			return put_user(val, (int *)arg);
+
+		case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < ms->numch; i++)
+				if (ms->ch[i].slctunitid)
+					val |= 1 << ms->ch[i].osschannel;
+			return put_user(val, (int *)arg);
+
+		case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < ms->numch; i++)
+				if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))
+					val |= 1 << ms->ch[i].osschannel;
+			return put_user(val, (int *)arg);
+			
+		case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+			if (i >= SOUND_MIXER_NRDEVICES)
+				return -EINVAL;
+			for (j = 0; j < ms->numch; j++) {
+				if (ms->ch[j].osschannel == i) {
+					return put_user(ms->ch[j].value, (int *)arg);
+				}
+			}
+			return -EINVAL;
+		}
+	}
+	if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	ms->modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		return set_rec_src(ms, val);
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES)
+			return -EINVAL;
+		for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++);
+		if (j >= ms->numch)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (wrmixer(ms, j, val))
+			return -EIO;
+		return put_user(ms->ch[j].value, (int *)arg);
+	}
+}
+
+static /*const*/ struct file_operations usb_mixer_fops = {
+	llseek:		usb_audio_llseek,
+	ioctl:		usb_audio_ioctl_mixdev,
+	open:		usb_audio_open_mixdev,
+	release:	usb_audio_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_out(struct usb_audiodev *as, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+	
+	if (as->usbout.dma.mapped || !as->usbout.dma.ready)
+		return 0;
+	usbout_start(as);
+	add_wait_queue(&as->usbout.dma.wait, &wait);
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&as->lock, flags);
+		count = as->usbout.dma.count;
+		spin_unlock_irqrestore(&as->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+			break;
+		if (nonblock) {
+			remove_wait_queue(&as->usbout.dma.wait, &wait);
+			set_current_state(TASK_RUNNING);
+			return -EBUSY;
+		}
+		tmo = 3 * HZ * count / as->usbout.dma.srate;
+		tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format);
+		if (!schedule_timeout(tmo + 1)) {
+			printk(KERN_DEBUG "usbaudio: dma timed out??\n");
+			break;
+		}
+	}
+	remove_wait_queue(&as->usbout.dma.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t usb_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned int ptr;
+	int cnt, err;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (as->usbin.dma.mapped)
+		return -ENXIO;
+	if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	add_wait_queue(&as->usbin.dma.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&as->lock, flags);
+		ptr = as->usbin.dma.rdptr;
+		cnt = as->usbin.dma.count;
+		/* set task state early to avoid wakeup races */
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&as->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (usbin_start(as)) {
+				if (!ret)
+					ret = -ENODEV;
+				break;
+			}
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) {
+			if (!ret)
+				ret = err;
+			break;
+		}
+		ptr += cnt;
+		if (ptr >= as->usbin.dma.dmasize)
+			ptr -= as->usbin.dma.dmasize;
+		spin_lock_irqsave(&as->lock, flags);
+		as->usbin.dma.rdptr = ptr;
+		as->usbin.dma.count -= cnt;
+		spin_unlock_irqrestore(&as->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&as->usbin.dma.wait, &wait);
+	return ret;
+}
+
+static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned int ptr;
+	unsigned int start_thr;
+	int cnt, err;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (as->usbout.dma.mapped)
+		return -ENXIO;
+	if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES));
+	add_wait_queue(&as->usbout.dma.wait, &wait);
+	while (count > 0) {
+#if 0
+		printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n",
+		       count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize,
+		       as->usbout.flags, current->state);
+#endif
+		spin_lock_irqsave(&as->lock, flags);
+		if (as->usbout.dma.count < 0) {
+			as->usbout.dma.count = 0;
+			as->usbout.dma.rdptr = as->usbout.dma.wrptr;
+		}
+		ptr = as->usbout.dma.wrptr;
+		cnt = as->usbout.dma.dmasize - as->usbout.dma.count;
+		/* set task state early to avoid wakeup races */
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&as->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (usbout_start(as)) {
+				if (!ret)
+					ret = -ENODEV;
+				break;
+			}
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) {
+			if (!ret)
+				ret = err;
+			break;
+		}
+		ptr += cnt;
+		if (ptr >= as->usbout.dma.dmasize)
+			ptr -= as->usbout.dma.dmasize;
+		spin_lock_irqsave(&as->lock, flags);
+		as->usbout.dma.wrptr = ptr;
+		as->usbout.dma.count += cnt;
+		spin_unlock_irqrestore(&as->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (as->usbout.dma.count >= start_thr && usbout_start(as)) {
+			if (!ret)
+				ret = -ENODEV;
+			break;
+		}
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&as->usbout.dma.wait, &wait);
+	return ret;
+}
+
+/* Called without the kernel lock - fine */
+static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (!as->usbout.dma.ready)
+			prog_dmabuf_out(as);
+		poll_wait(file, &as->usbout.dma.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!as->usbin.dma.ready)
+			prog_dmabuf_in(as);
+		poll_wait(file, &as->usbin.dma.wait, wait);
+	}
+	spin_lock_irqsave(&as->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (as->usbout.dma.mapped) {
+			if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&as->lock, flags);
+	return mask;
+}
+
+static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	struct dmabuf *db;
+	int ret;
+
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_out(as)) != 0)
+			return ret;
+		db = &as->usbout.dma;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_in(as)) != 0)
+			return ret;
+		db = &as->usbin.dma;
+	} else
+		return -EINVAL;
+
+	return dmabuf_mmap(db,  vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	struct usb_audio_state *s = as->state;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val, val2, mapped, ret;
+
+	if (!s->usbdev)
+		return -EIO;
+	mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) ||
+		((file->f_mode & FMODE_READ) && as->usbin.dma.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | 
+				DSP_CAP_MMAP | DSP_CAP_BATCH, (int *)arg);
+
+	case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			usbout_stop(as);
+			as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			usbin_stop(as);
+			as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			if (val < 4000)
+				val = 4000;
+			if (val > 100000)
+				val = 100000;
+			if (set_format(as, file->f_mode, AFMT_QUERY, val))
+				return -EIO;
+		}
+		return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg);
+
+	case SNDCTL_DSP_STEREO:
+		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		if (set_format(as, file->f_mode, val2 | AFMT_STEREO, 0))
+			return -EIO;
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+			if (val == 1)
+				val2 &= ~AFMT_STEREO;
+			else
+				val2 |= AFMT_STEREO;
+			if (set_format(as, file->f_mode, val2, 0))
+				return -EIO;
+		}
+		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg);
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+		return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
+				AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			if (hweight32(val) != 1)
+				return -EINVAL;
+			if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
+				     AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE)))
+				return -EINVAL;
+			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+			val |= val2 & AFMT_STEREO;
+			if (set_format(as, file->f_mode, val, 0))
+				return -EIO;
+		}
+		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		return put_user(val2 & ~AFMT_STEREO, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+		return 0;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as)))
+					return ret;
+				if (usbin_start(as))
+					return -ENODEV;
+			} else
+				usbin_stop(as);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as)))
+					return ret;
+				if (usbout_start(as))
+					return -ENODEV;
+			} else
+				usbout_stop(as);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0)
+			return val;
+		spin_lock_irqsave(&as->lock, flags);
+		abinfo.fragsize = as->usbout.dma.fragsize;
+		abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count;
+		abinfo.fragstotal = as->usbout.dma.numfrag;
+		abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift;      
+		spin_unlock_irqrestore(&as->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0)
+			return val;
+		spin_lock_irqsave(&as->lock, flags);
+		abinfo.fragsize = as->usbin.dma.fragsize;
+		abinfo.bytes = as->usbin.dma.count;
+		abinfo.fragstotal = as->usbin.dma.numfrag;
+		abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift;      
+		spin_unlock_irqrestore(&as->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&as->lock, flags);
+		val = as->usbout.dma.count;
+		spin_unlock_irqrestore(&as->lock, flags);
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&as->lock, flags);
+		cinfo.bytes = as->usbin.dma.total_bytes;
+		cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift;
+		cinfo.ptr = as->usbin.dma.wrptr;
+		if (as->usbin.dma.mapped)
+			as->usbin.dma.count &= as->usbin.dma.fragsize-1;
+		spin_unlock_irqrestore(&as->lock, flags);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&as->lock, flags);
+		cinfo.bytes = as->usbout.dma.total_bytes;
+		cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift;
+		cinfo.ptr = as->usbout.dma.rdptr;
+		if (as->usbout.dma.mapped)
+			as->usbout.dma.count &= as->usbout.dma.fragsize-1;
+		spin_unlock_irqrestore(&as->lock, flags);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+       case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_out(as)))
+				return val;
+			return put_user(as->usbout.dma.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_in(as)))
+			return val;
+		return put_user(as->usbin.dma.fragsize, (int *)arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			as->usbin.dma.ossfragshift = val & 0xffff;
+			as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff;
+			if (as->usbin.dma.ossfragshift < 4)
+				as->usbin.dma.ossfragshift = 4;
+			if (as->usbin.dma.ossfragshift > 15)
+				as->usbin.dma.ossfragshift = 15;
+			if (as->usbin.dma.ossmaxfrags < 4)
+				as->usbin.dma.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			as->usbout.dma.ossfragshift = val & 0xffff;
+			as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff;
+			if (as->usbout.dma.ossfragshift < 4)
+				as->usbout.dma.ossfragshift = 4;
+			if (as->usbout.dma.ossfragshift > 15)
+				as->usbout.dma.ossfragshift = 15;
+			if (as->usbout.dma.ossmaxfrags < 4)
+				as->usbout.dma.ossmaxfrags = 4;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision))
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			as->usbin.dma.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			as->usbout.dma.subdivision = val;
+		return 0;
+
+	case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg);
+
+	case SOUND_PCM_READ_BITS:
+		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		return put_user(AFMT_IS16BIT(val2) ? 16 : 8, (int *)arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_READ_FILTER:
+		return -EINVAL;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int usb_audio_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	struct list_head *devs, *adevs;
+	struct usb_audiodev *as;
+	struct usb_audio_state *s;
+
+	for (;;) {
+		down(&open_sem);
+		for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) {
+			s = list_entry(devs, struct usb_audio_state, audiodev);
+			for (adevs = s->audiolist.next; adevs != &s->audiolist; adevs = adevs->next) {
+				as = list_entry(adevs, struct usb_audiodev, list);
+				if (!((as->dev_audio ^ minor) & ~0xf))
+					goto device_found;
+			}
+		}
+		up(&open_sem);
+		return -ENODEV;
+
+	device_found:
+		if (!s->usbdev) {
+			up(&open_sem);
+			return -EIO;
+		}
+		/* wait for device to become free */
+		if (!(as->open_mode & file->f_mode))
+			break;
+		if (file->f_flags & O_NONBLOCK) {
+			up(&open_sem);
+			return -EBUSY;
+		}
+		__set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&open_wait, &wait);
+		up(&open_sem);
+		schedule();
+		__set_current_state(TASK_RUNNING);
+		remove_wait_queue(&open_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+	if (file->f_mode & FMODE_READ)
+		as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0;
+	if (file->f_mode & FMODE_WRITE)
+		as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0;
+	if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) {
+		up(&open_sem);
+		return -EIO;
+	}
+	file->private_data = as;
+	as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	s->count++;
+	up(&open_sem);
+	return 0;
+}
+
+static int usb_audio_release(struct inode *inode, struct file *file)
+{
+	struct usb_audiodev *as = (struct usb_audiodev *)file->private_data;
+	struct usb_audio_state *s = as->state;
+	struct usb_device *dev = s->usbdev;
+	struct usb_interface *iface;
+
+	if (file->f_mode & FMODE_WRITE)
+		drain_out(as, file->f_flags & O_NONBLOCK);
+	down(&open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		usbout_stop(as);
+		if (dev && as->usbout.interface >= 0) {
+			iface = &dev->actconfig->interface[as->usbout.interface];
+			usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0);
+		}
+		dmabuf_release(&as->usbout.dma);
+		usbout_release(as);
+	}
+	if (file->f_mode & FMODE_READ) {
+		usbin_stop(as);
+		if (dev && as->usbin.interface >= 0) {
+			iface = &dev->actconfig->interface[as->usbin.interface];
+			usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0);
+		}
+		dmabuf_release(&as->usbin.dma);
+		usbin_release(as);
+	}
+	as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	release(s);
+	wake_up(&open_wait);
+	return 0;
+}
+
+static /*const*/ struct file_operations usb_audio_fops = {
+	llseek:		usb_audio_llseek,
+	read:		usb_audio_read,
+	write:		usb_audio_write,
+	poll:		usb_audio_poll,
+	ioctl:		usb_audio_ioctl,
+	mmap:		usb_audio_mmap,
+	open:		usb_audio_open,
+	release:	usb_audio_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver usb_audio_driver = {
+	"audio",
+	usb_audio_probe,
+	usb_audio_disconnect,
+	LIST_HEAD_INIT(usb_audio_driver.driver_list), 
+	NULL,
+	0
+};
+
+static void *find_descriptor(void *descstart, unsigned int desclen, void *after, 
+			     u8 dtype, int iface, int altsetting)
+{
+	u8 *p, *end, *next;
+	int ifc = -1, as = -1;
+
+	p = descstart;
+	end = p + desclen;
+	for (; p < end;) {
+		if (p[0] < 2)
+			return NULL;
+		next = p + p[0];
+		if (next > end)
+			return NULL;
+		if (p[1] == USB_DT_INTERFACE) {
+			/* minimum length of interface descriptor */
+			if (p[0] < 9)
+				return NULL;
+			ifc = p[2];
+			as = p[3];
+		}
+		if (p[1] == dtype && (!after || (void *)p > after) &&
+		    (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) {
+			return p;
+		}
+		p = next;
+	}
+	return NULL;
+}
+
+static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting)
+{
+	unsigned char *p;
+
+	p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting);
+	while (p) {
+		if (p[0] >= 3 && p[2] == dsubtype)
+			return p;
+		p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting);
+	}
+	return NULL;
+}
+
+static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface)
+{
+	unsigned char *p;
+
+	p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1);
+	while (p) {
+		if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit)
+			return p;
+		p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1);
+	}
+	return NULL;
+}
+
+static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout)
+{
+	struct usb_device *dev = s->usbdev;
+	struct usb_audiodev *as;
+	struct usb_config_descriptor *config = dev->actconfig;
+	struct usb_interface_descriptor *alts;
+	struct usb_interface *iface;
+	struct audioformat *fp;
+	unsigned char *fmt, *csep;
+	unsigned int i, j, k, format;
+
+	if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL)))
+		return;
+	memset(as, 0, sizeof(struct usb_audiodev));
+	init_waitqueue_head(&as->usbin.dma.wait);
+	init_waitqueue_head(&as->usbout.dma.wait);
+	spin_lock_init(&as->lock);
+	spin_lock_init(&as->usbin.durb[0].urb.lock);
+	spin_lock_init(&as->usbin.durb[1].urb.lock);
+	spin_lock_init(&as->usbin.surb[0].urb.lock);
+	spin_lock_init(&as->usbin.surb[1].urb.lock);
+	spin_lock_init(&as->usbout.durb[0].urb.lock);
+	spin_lock_init(&as->usbout.durb[1].urb.lock);
+	spin_lock_init(&as->usbout.surb[0].urb.lock);
+	spin_lock_init(&as->usbout.surb[1].urb.lock);
+	as->state = s;
+	as->usbin.interface = asifin;
+	as->usbout.interface = asifout;
+	/* search for input formats */
+	if (asifin >= 0) {
+		as->usbin.flags = FLG_CONNECTED;
+		iface = &config->interface[asifin];
+		for (i = 0; i < iface->num_altsetting; i++) {
+			alts = &iface->altsetting[i];
+			if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2)
+				continue;
+			if (alts->bNumEndpoints < 1) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 ||
+			    !(alts->endpoint[0].bEndpointAddress & 0x80)) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous in\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i);
+			if (!fmt) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
+			fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i);
+			if (!fmt) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", 
+				       dev->devnum, asifin, i, fmt[4], fmt[5]);
+				continue;
+			}
+			csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifin, i);
+			if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", 
+				       dev->devnum, asifin, i);
+				continue;
+			}
+			if (as->numfmtin >= MAXFORMATS)
+				continue;
+			fp = &as->fmtin[as->numfmtin++];
+			if (fmt[5] == 2)
+				format &= (AFMT_U16_LE | AFMT_S16_LE);
+			else
+				format &= (AFMT_U8 | AFMT_S8);
+			if (fmt[4] == 2)
+				format |= AFMT_STEREO;
+			fp->format = format;
+			fp->altsetting = i;
+			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
+			for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
+				k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
+				if (k > fp->sratehi)
+					fp->sratehi = k;
+				if (k < fp->sratelo)
+					fp->sratelo = k;
+			}
+			fp->attributes = csep[3];
+			printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", 
+			       dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes);
+		}
+	}
+	/* search for output formats */
+	if (asifout >= 0) {
+		as->usbout.flags = FLG_CONNECTED;
+		iface = &config->interface[asifout];
+		for (i = 0; i < iface->num_altsetting; i++) {
+			alts = &iface->altsetting[i];
+			if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2)
+				continue;
+			if (alts->bNumEndpoints < 1) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 ||
+			    (alts->endpoint[0].bEndpointAddress & 0x80)) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous out\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i);
+			if (!fmt) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
+			fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i);
+			if (!fmt) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", 
+				       dev->devnum, asifout, i, fmt[4], fmt[5]);
+				continue;
+			}
+			csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifout, i);
+			if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
+				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", 
+				       dev->devnum, asifout, i);
+				continue;
+			}
+			if (as->numfmtout >= MAXFORMATS)
+				continue;
+			fp = &as->fmtout[as->numfmtout++];
+			if (fmt[5] == 2)
+				format &= (AFMT_U16_LE | AFMT_S16_LE);
+			else
+				format &= (AFMT_U8 | AFMT_S8);
+			if (fmt[4] == 2)
+				format |= AFMT_STEREO;
+			fp->format = format;
+			fp->altsetting = i;
+			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
+			for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) {
+				k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16);
+				if (k > fp->sratehi)
+					fp->sratehi = k;
+				if (k < fp->sratelo)
+					fp->sratelo = k;
+			}
+			fp->attributes = csep[3];
+			printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", 
+			       dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes);
+		}
+	}
+	if (as->numfmtin == 0 && as->numfmtout == 0) {
+		kfree(as);
+		return;
+	}
+	if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) {
+		printk(KERN_ERR "usbaudio: cannot register dsp\n");
+		kfree(as);
+		return;
+	}
+	/* everything successful */
+	list_add_tail(&as->list, &s->audiolist);
+}
+
+struct consmixstate {
+	struct usb_audio_state *s;
+	unsigned char *buffer;
+	unsigned int buflen;
+	unsigned int ctrlif;
+	struct mixerchannel mixch[SOUND_MIXER_NRDEVICES];
+	unsigned int nrmixch;
+	unsigned int mixchmask;
+	unsigned long unitbitmap[32/sizeof(unsigned long)];
+	/* return values */
+	unsigned int nrchannels;
+	unsigned int termtype;
+	unsigned int chconfig;
+};
+
+static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr)
+{
+	struct mixerchannel *c;
+
+	if (nr >= SOUND_MIXER_NRDEVICES) {
+		printk(KERN_ERR "usbaudio: invalid OSS mixer channel %u\n", nr);
+		return NULL;
+	}
+	if (!(state->mixchmask & (1 << nr))) {
+		printk(KERN_WARNING "usbaudio: OSS mixer channel %u already in use\n", nr);
+		return NULL;
+	}
+	c = &state->mixch[state->nrmixch++];
+	c->osschannel = nr;
+	state->mixchmask &= ~(1 << nr);
+	return c;
+}
+
+static unsigned int getvolchannel(struct consmixstate *state)
+{
+	unsigned int u;
+
+	if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME))
+		return SOUND_MIXER_VOLUME;
+	if ((state->termtype & 0xff00) == 0x0100) {
+		if (state->mixchmask & SOUND_MASK_PCM)
+			return SOUND_MIXER_PCM;
+		if (state->mixchmask & SOUND_MASK_ALTPCM)
+			return SOUND_MIXER_ALTPCM;
+	}
+	if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC))
+		return SOUND_MIXER_MIC;
+	if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER))
+		return SOUND_MIXER_SPEAKER;
+	if ((state->termtype & 0xff00) == 0x0500) {
+		if (state->mixchmask & SOUND_MASK_PHONEIN)
+			return SOUND_MIXER_PHONEIN;
+		if (state->mixchmask & SOUND_MASK_PHONEOUT)
+			return SOUND_MIXER_PHONEOUT;
+	}
+	if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO))
+		return SOUND_MIXER_RADIO;
+	if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO))
+		return SOUND_MIXER_VIDEO;
+	u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 |
+				    SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3));
+	return u-1;
+}
+
+static void prepmixch(struct consmixstate *state)
+{
+	struct usb_device *dev = state->s->usbdev;
+	struct mixerchannel *ch;
+	unsigned char buf[2];
+	__s16 v1;
+	unsigned int v2, v3;
+
+	if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES)
+		return;
+	ch = &state->mixch[state->nrmixch-1];
+	switch (ch->selector) {
+	case 0:  /* mixer unit request */
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		ch->minval = buf[0] | (buf[1] << 8);
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		ch->maxval = buf[0] | (buf[1] << 8);
+		v2 = ch->maxval - ch->minval;
+		if (!v2)
+			v2 = 1;
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		v1 = buf[0] | (buf[1] << 8);
+		v3 = v1 - ch->minval;
+		v3 = 100 * v3 / v2;
+		if (v3 > 100)
+			v3 = 100;
+		ch->value = v3;
+		if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
+			if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+					    ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)),
+					    state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+			v1 = buf[0] | (buf[1] << 8);
+			v3 = v1 - ch->minval;
+			v3 = 100 * v3 / v2;
+			if (v3 > 100)
+				v3 = 100;
+		}
+		ch->value |= v3 << 8;
+		break;
+
+		/* various feature unit controls */
+	case VOLUME_CONTROL:
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		ch->minval = buf[0] | (buf[1] << 8);
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		ch->maxval = buf[0] | (buf[1] << 8);
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+			goto err;
+		v1 = buf[0] | (buf[1] << 8);
+		v2 = ch->maxval - ch->minval;
+		v3 = v1 - ch->minval;
+		if (!v2)
+			v2 = 1;
+		v3 = 100 * v3 / v2;
+		if (v3 > 100)
+			v3 = 100;
+		ch->value = v3;
+		if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
+			if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+					    (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0)
+				goto err;
+			v1 = buf[0] | (buf[1] << 8);
+			v3 = v1 - ch->minval;
+			v3 = 100 * v3 / v2;
+			if (v3 > 100)
+				v3 = 100;
+		}
+		ch->value |= v3 << 8;
+		break;
+		
+	case BASS_CONTROL:
+	case MID_CONTROL:
+	case TREBLE_CONTROL:
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0)
+			goto err;
+		ch->minval = buf[0] << 8;
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0)
+			goto err;
+		ch->maxval = buf[0] << 8;
+		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				    (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0)
+			goto err;
+		v1 = buf[0] << 8;
+		v2 = ch->maxval - ch->minval;
+		v3 = v1 - ch->minval;
+		if (!v2)
+			v2 = 1;
+		v3 = 100 * v3 / v2;
+		if (v3 > 100)
+			v3 = 100;
+		ch->value = v3;
+		if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) {
+			if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+					    (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0)
+				goto err;
+			v1 = buf[0] << 8;
+			v3 = v1 - ch->minval;
+			v3 = 100 * v3 / v2;
+			if (v3 > 100)
+				v3 = 100;
+		}
+		ch->value |= v3 << 8;
+		break;
+		
+	default:
+		goto err;
+	}
+	return;
+
+ err:
+	printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", 
+	       dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector);
+	if (state->nrmixch)
+		state->nrmixch--;
+}
+
+
+static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid);
+
+extern inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch)
+{
+	unsigned int idx;
+
+	idx = inidx*numoch;
+	if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7))))
+		return 0;
+	if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)))
+		return 1;
+	idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT);
+	if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7))))
+		return 0;
+	return 1;
+}
+
+static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer)
+{
+	unsigned int nroutch = mixer[5+mixer[4]];
+	unsigned int chidx[SOUND_MIXER_NRDEVICES+1];
+	unsigned int termt[SOUND_MIXER_NRDEVICES];
+	unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0;
+	unsigned char *bmap = &mixer[9+mixer[4]];
+	unsigned int bmapsize;
+	struct mixerchannel *ch;
+	unsigned int i;
+
+	if (!mixer[4]) {
+		printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]);
+		return;
+	}
+	if (mixer[4] > SOUND_MIXER_NRDEVICES) {
+		printk(KERN_ERR "usbaudio: mixer unit %u: too many input pins\n", mixer[3]);
+		return;
+	}
+	chidx[0] = 0;
+	for (i = 0; i < mixer[4]; i++) {
+		usb_audio_recurseunit(state, mixer[5+i]);
+		chidx[i+1] = chidx[i] + state->nrchannels;
+		termt[i] = state->termtype;
+	}
+	state->termtype = 0;
+	state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8);
+	bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3;
+	bmap += bmapsize - 1;
+	if (mixer[0] < 10+mixer[4]+bmapsize) {
+		printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]);
+		return;
+	}
+	for (i = 0; i < mixer[4]; i++) {
+		state->termtype = termt[i];
+		if (chidx[i+1]-chidx[i] >= 2) {
+			flg |= MIXFLG_STEREOIN;
+			if (checkmixbmap(bmap, flg, chidx[i], nroutch)) {
+				ch = getmixchannel(state, getvolchannel(state));
+				if (ch) {
+					ch->unitid = mixer[3];
+					ch->selector = 0;
+					ch->chnum = chidx[i]+1;
+					ch->flags = flg;
+					prepmixch(state);
+				}
+				continue;
+			}
+		}
+		flg &= ~MIXFLG_STEREOIN;
+		if (checkmixbmap(bmap, flg, chidx[i], nroutch)) {
+			ch = getmixchannel(state, getvolchannel(state));
+			if (ch) {
+				ch->unitid = mixer[3];
+				ch->selector = 0;
+				ch->chnum = chidx[i]+1;
+				ch->flags = flg;
+				prepmixch(state);
+			}
+		}
+	}	
+	state->termtype = 0;
+}
+
+static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid)
+{
+	unsigned int i;
+	
+	for (i = 0; i < state->nrmixch; i++)
+		if (state->mixch[i].unitid == unitid)
+			return &state->mixch[i];
+	return NULL;
+}
+
+static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector)
+{
+	unsigned int chnum, i, mixch;
+	struct mixerchannel *mch;
+
+	if (!selector[4]) {
+		printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]);
+		return;
+	}
+	mixch = state->nrmixch;
+	usb_audio_recurseunit(state, selector[5]);
+	if (state->nrmixch != mixch) {
+		mch = &state->mixch[state->nrmixch-1];
+		mch->slctunitid = selector[3] | (1 << 8);
+	} else if ((mch = slctsrc_findunit(state, selector[5]))) {
+		mch->slctunitid = selector[3] | (1 << 8);
+	} else {
+		printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]);
+	}
+	chnum = state->nrchannels;
+	for (i = 1; i < selector[4]; i++) {
+		mixch = state->nrmixch;
+		usb_audio_recurseunit(state, selector[5+i]);
+		if (chnum != state->nrchannels) {
+			printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]);
+			state->termtype = 0;
+			state->chconfig = 0;
+			state->nrchannels = 0;
+			return;
+		}
+		if (state->nrmixch != mixch) {
+			mch = &state->mixch[state->nrmixch-1];
+			mch->slctunitid = selector[3] | ((i + 1) << 8);
+		} else if ((mch = slctsrc_findunit(state, selector[5+i]))) {
+			mch->slctunitid = selector[3] | ((i + 1) << 8);
+		} else {
+			printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1);
+		}
+	}
+	state->termtype = 0;
+	state->chconfig = 0;
+}
+
+/* in the future we might try to handle 3D etc. effect units */
+
+static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc)
+{
+	unsigned int i;
+
+	for (i = 0; i < proc[6]; i++)
+		usb_audio_recurseunit(state, proc[7+i]);
+	state->nrchannels = proc[7+proc[6]];
+	state->termtype = 0;
+	state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8);
+}
+
+static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr)
+{
+	struct mixerchannel *ch;
+	unsigned short chftr, mchftr;
+#if 0
+	struct usb_device *dev = state->s->usbdev;
+	unsigned char data[1];
+#endif
+
+	usb_audio_recurseunit(state, ftr[4]);
+	if (state->nrchannels == 0) {
+		printk(KERN_ERR "usbaudio: feature unit %u source has no channels\n", ftr[3]);
+		return;
+	}
+	if (state->nrchannels > 2)
+		printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]);
+	if (state->nrchannels == 1 && ftr[0] == 7+ftr[5]) {
+		printk(KERN_WARNING "usbaudio: workaround for broken Philips Camera Microphone descriptor enabled\n");
+		mchftr = ftr[6];
+		chftr = 0;
+	} else {
+		if (ftr[0] < 7+ftr[5]*(1+state->nrchannels)) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", ftr[3]);
+			return;
+		}
+		mchftr = ftr[6];
+		chftr = ftr[6+ftr[5]];
+		if (state->nrchannels > 1)
+			chftr &= ftr[6+2*ftr[5]];
+	}
+	/* volume control */
+	if (chftr & 2) {
+		ch = getmixchannel(state, getvolchannel(state));
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = VOLUME_CONTROL;
+			ch->chnum = 1;
+			ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
+			prepmixch(state);
+		}
+	} else if (mchftr & 2) {
+		ch = getmixchannel(state, getvolchannel(state));
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = VOLUME_CONTROL;
+			ch->chnum = 0;
+			ch->flags = 0;
+			prepmixch(state);
+		}
+	}
+	/* bass control */
+	if (chftr & 4) {
+		ch = getmixchannel(state, SOUND_MIXER_BASS);
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = BASS_CONTROL;
+			ch->chnum = 1;
+			ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
+			prepmixch(state);
+		}
+	} else if (mchftr & 4) {
+		ch = getmixchannel(state, SOUND_MIXER_BASS);
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = BASS_CONTROL;
+			ch->chnum = 0;
+			ch->flags = 0;
+			prepmixch(state);
+		}
+	}
+	/* treble control */
+	if (chftr & 16) {
+		ch = getmixchannel(state, SOUND_MIXER_TREBLE);
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = TREBLE_CONTROL;
+			ch->chnum = 1;
+			ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0;
+			prepmixch(state);
+		}
+	} else if (mchftr & 16) {
+		ch = getmixchannel(state, SOUND_MIXER_TREBLE);
+		if (ch) {
+			ch->unitid = ftr[3];
+			ch->selector = TREBLE_CONTROL;
+			ch->chnum = 0;
+			ch->flags = 0;
+			prepmixch(state);
+		}
+	}
+#if 0
+	/* if there are mute controls, unmute them */
+	/* does not seem to be necessary, and the Dallas chip does not seem to support the "all" channel (255) */
+	if ((chftr & 1) || (mchftr & 1)) {
+		printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif);
+		data[0] = 0;
+		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				    (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, HZ) < 0)
+			printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif);
+ 	}
+#endif
+}
+
+static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid)
+{
+	unsigned char *p1;
+	unsigned int i, j;
+
+	if (test_and_set_bit(unitid, &state->unitbitmap)) {
+		printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid);
+		return;
+	}
+	p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif);
+	if (!p1) {
+		printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid);
+		return;
+	}
+	state->nrchannels = 0;
+	state->termtype = 0;
+	state->chconfig = 0;
+	switch (p1[2]) {
+	case INPUT_TERMINAL:
+		if (p1[0] < 12) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid);
+			return;
+		}
+		state->nrchannels = p1[7];
+		state->termtype = p1[4] | (p1[5] << 8);
+		state->chconfig = p1[8] | (p1[9] << 8);
+		return;
+
+	case MIXER_UNIT:
+		if (p1[0] < 10 || p1[0] < 10+p1[4]) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid MIXER_UNIT descriptor\n", unitid);
+			return;
+		}
+		usb_audio_mixerunit(state, p1);
+		return;
+
+	case SELECTOR_UNIT:
+		if (p1[0] < 6 || p1[0] < 6+p1[4]) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid);
+			return;
+		}
+		usb_audio_selectorunit(state, p1);
+		return;
+
+	case FEATURE_UNIT:
+		if (p1[0] < 7 || p1[0] < 7+p1[5]) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid);
+			return;
+		}
+		usb_audio_featureunit(state, p1);
+		return;		
+
+	case PROCESSING_UNIT:
+		if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid);
+			return;
+		}
+		usb_audio_processingunit(state, p1);
+		return;		
+
+	case EXTENSION_UNIT:
+		if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) {
+			printk(KERN_ERR "usbaudio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid);
+			return;
+		}
+		for (j = i = 0; i < p1[6]; i++) {
+			usb_audio_recurseunit(state, p1[7+i]);
+			if (!i)
+				j = state->termtype;
+			else if (j != state->termtype)
+				j = 0;
+		}
+		state->nrchannels = p1[7+p1[6]];
+		state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8);
+		state->termtype = j;
+		return;
+
+	default:
+		printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+		return;
+	}
+}
+
+static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm)
+{
+	struct usb_mixerdev *ms;
+	struct consmixstate state;
+
+	memset(&state, 0, sizeof(state));
+	state.s = s;
+	state.nrmixch = 0;
+	state.mixchmask = ~0;
+	state.buffer = buffer;
+	state.buflen = buflen;
+	state.ctrlif = ctrlif;
+	set_bit(oterm[3], &state.unitbitmap);  /* mark terminal ID as visited */
+	printk(KERN_INFO "usbaudio: constructing mixer for Terminal %u type 0x%04x\n",
+	       oterm[3], oterm[4] | (oterm[5] << 8));
+	usb_audio_recurseunit(&state, oterm[7]);
+	if (!state.nrmixch) {
+		printk(KERN_INFO "usbaudio: no mixer controls found for Terminal %u\n", oterm[3]);
+		return;
+	}
+	if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL)))
+		return;
+	memset(ms, 0, sizeof(struct usb_mixerdev));
+	memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel));
+	ms->state = s;
+	ms->iface = ctrlif;
+	ms->numch = state.nrmixch;
+	if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) {
+		printk(KERN_ERR "usbaudio: cannot register mixer\n");
+		kfree(ms);
+		return;
+	}
+	list_add_tail(&ms->list, &s->mixerlist);
+}
+
+static void *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif)
+{
+	struct usb_audio_state *s;
+	struct usb_config_descriptor *config = dev->actconfig;
+	struct usb_interface *iface;
+	unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES];
+	unsigned char *p1;
+	unsigned int i, j, k, numifin = 0, numifout = 0;
+	
+	if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL)))
+		return NULL;
+	memset(s, 0, sizeof(struct usb_audio_state));
+	INIT_LIST_HEAD(&s->audiolist);
+	INIT_LIST_HEAD(&s->mixerlist);
+	s->usbdev = dev;
+	s->count = 1;
+
+	/* find audiocontrol interface */
+	if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) {
+		printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u no HEADER found\n",
+		       dev->devnum, ctrlif);
+		goto ret;
+	}
+	if (p1[0] < 8 + p1[7]) {
+		printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u HEADER error\n",
+		       dev->devnum, ctrlif);
+		goto ret;
+	}
+	if (!p1[7])
+		printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n",
+		       dev->devnum, ctrlif);
+	for (i = 0; i < p1[7]; i++) {
+		j = p1[8+i];
+		if (j >= config->bNumInterfaces) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n",
+			       dev->devnum, ctrlif, j);
+			continue;
+		}
+		iface = &config->interface[j];
+		if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n",
+			       dev->devnum, ctrlif, j);
+			continue;
+		}
+		if (iface->altsetting[0].bInterfaceSubClass == 3) {
+			printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n",
+			       dev->devnum, ctrlif, j);
+			continue;
+		}
+		if (iface->altsetting[0].bInterfaceSubClass != 2) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n",
+			       dev->devnum, ctrlif, j);
+			continue;
+		}
+		if (iface->num_altsetting == 0) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has no working interface.\n", dev->devnum, ctrlif);
+			continue;
+		}
+		if (iface->num_altsetting == 1) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif);
+			continue;
+		}
+		if (iface->altsetting[0].bNumEndpoints > 0) {
+			/* Check all endpoints; should they all have a bandwidth of 0 ? */
+			for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) {
+				if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 0) {
+					printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k);
+					break;
+				}
+			}
+			if (k < iface->altsetting[0].bNumEndpoints)
+				continue;
+		}
+		if (iface->altsetting[1].bNumEndpoints < 1) {
+			printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n",
+			       dev->devnum, ctrlif, j);
+			continue;
+		}
+		/* note: this requires the data endpoint to be ep0 and the optional sync
+		   ep to be ep1, which seems to be the case */
+		if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) {
+			if (numifin < USB_MAXINTERFACES) {
+				ifin[numifin++] = j;
+				usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
+			}
+		} else {
+			if (numifout < USB_MAXINTERFACES) {
+				ifout[numifout++] = j;
+				usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
+			}
+		}
+	}
+	printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n",
+	       dev->devnum, ctrlif, numifin, numifout);
+	for (i = 0; i < numifin && i < numifout; i++)
+		usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]);
+	for (j = i; j < numifin; j++)
+		usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1);
+	for (j = i; j < numifout; j++)
+		usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]);
+	/* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */
+	p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1);
+	while (p1) {
+		if (p1[0] >= 9)
+			usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1);
+		p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1);
+	}
+
+ret:
+	if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) {
+		kfree(s);
+		return NULL;
+	}
+	/* everything successful */
+	down(&open_sem);
+	list_add_tail(&s->audiodev, &audiodevs);
+	up(&open_sem);
+	printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s);
+	return s;
+}
+
+/* we only care for the currently active configuration */
+
+static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_config_descriptor *config = dev->actconfig;	
+	unsigned char *buffer;
+	unsigned char buf[8];
+	unsigned int i, buflen;
+	int ret;
+
+#if 0
+	printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum,
+	       config->interface[ifnum].altsetting[0].bInterfaceClass,
+	       config->interface[ifnum].altsetting[0].bInterfaceSubClass);
+#endif
+	if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO ||
+	    config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) {
+#if 0
+		printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n",
+		       dev->descriptor.idVendor, dev->descriptor.idProduct);
+#endif
+		return NULL;
+	}
+	/*
+	 * audiocontrol interface found
+	 * find which configuration number is active
+	 */
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+		if (dev->config+i == config)
+			goto configfound;
+	printk(KERN_ERR "usbaudio: cannot find active configuration number of device %d\n", dev->devnum);
+	return NULL;
+
+ configfound:
+	if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
+		printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue);
+		return NULL;
+	}
+	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8);
+	if (ret<0) {
+		printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum);
+		return NULL;
+	}
+	if (buf[1] != USB_DT_CONFIG || buf[0] < 9) {
+		printk(KERN_ERR "usbaudio: invalid config descriptor %d of device %d\n", i, dev->devnum);
+		return NULL;
+	}
+	buflen = buf[2] | (buf[3] << 8);
+	if (!(buffer = kmalloc(buflen, GFP_KERNEL)))
+		return NULL;
+	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen);
+	if (ret < 0) {
+		kfree(buffer);
+		printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum);
+		return NULL;
+	}
+	return usb_audio_parsecontrol(dev, buffer, buflen, ifnum);
+}
+
+
+/* a revoke facility would make things simpler */
+
+static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_audio_state *s = (struct usb_audio_state *)ptr;
+	struct list_head *list;
+	struct usb_audiodev *as;
+	struct usb_mixerdev *ms;
+
+	/* we get called with -1 for every audiostreaming interface registered */
+	if (s == (struct usb_audio_state *)-1) {
+		dprintk((KERN_DEBUG "usb_audio_disconnect: called with -1\n"));
+		return;
+	}
+	if (!s->usbdev) {
+		dprintk((KERN_DEBUG "usb_audio_disconnect: already called for %p!\n", s));
+		return;
+	}
+	down(&open_sem);
+	list_del(&s->audiodev);
+	INIT_LIST_HEAD(&s->audiodev);
+	s->usbdev = NULL;
+	/* deregister all audio and mixer devices, so no new processes can open this device */
+	for(list = s->audiolist.next; list != &s->audiolist; list = list->next) {
+		as = list_entry(list, struct usb_audiodev, list);
+		usbin_disc(as);
+		usbout_disc(as);
+		wake_up(&as->usbin.dma.wait);
+		wake_up(&as->usbout.dma.wait);
+		if (as->dev_audio >= 0)
+			unregister_sound_dsp(as->dev_audio);
+		as->dev_audio = -1;
+	}
+	for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) {
+		ms = list_entry(list, struct usb_mixerdev, list);
+		if (ms->dev_mixer >= 0)
+			unregister_sound_mixer(ms->dev_mixer);
+		ms->dev_mixer = -1;
+	}
+	release(s);
+	wake_up(&open_wait);
+}
+
+int usb_audio_init(void)
+{
+	usb_register(&usb_audio_driver);
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
 {
+	return usb_audio_init();
 }
 
-void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data)
+void cleanup_module(void)
 {
+	usb_deregister(&usb_audio_driver);
 }
 
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/audio.h linux.18p17/drivers/usb/audio.h
--- linux.vanilla/drivers/usb/audio.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/audio.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,116 @@
+#define USB_DT_CS_DEVICE                0x21
+#define USB_DT_CS_CONFIG                0x22
+#define USB_DT_CS_STRING                0x23
+#define USB_DT_CS_INTERFACE             0x24
+#define USB_DT_CS_ENDPOINT              0x25
+
+#define CS_AUDIO_UNDEFINED		0x20
+#define CS_AUDIO_DEVICE			0x21
+#define CS_AUDIO_CONFIGURATION		0x22
+#define CS_AUDIO_STRING			0x23
+#define CS_AUDIO_INTERFACE		0x24
+#define CS_AUDIO_ENDPOINT		0x25
+
+#define HEADER				0x01
+#define INPUT_TERMINAL			0x02
+#define OUTPUT_TERMINAL			0x03
+#define MIXER_UNIT			0x04
+#define SELECTOR_UNIT			0x05
+#define FEATURE_UNIT			0x06
+#define PROCESSING_UNIT			0x07
+#define EXTENSION_UNIT			0x08
+
+#define AS_GENERAL			0x01
+#define FORMAT_TYPE			0x02
+#define FORMAT_SPECIFIC			0x03
+
+#define EP_GENERAL			0x01
+
+#define MAX_CHAN			9
+#define MAX_FREQ			16
+#define MAX_IFACE			8
+#define MAX_FORMAT			8
+#define MAX_ALT				32 	/* Sorry, we need quite a few for the Philips webcams */
+
+struct usb_audio_terminal
+{	
+	u8	flags;
+	u8	assoc;
+	u16	type;			/* Mic etc */
+	u8	channels;
+	u8	source;
+	u16	chancfg;
+};
+
+struct usb_audio_format
+{
+	u8	type;
+	u8	channels;
+	u8	num_freq;
+	u8	sfz;
+	u8	bits;
+	u16	freq[MAX_FREQ];
+};
+
+struct usb_audio_interface
+{
+	u8	terminal;
+	u8	delay;
+	u16	num_formats;
+	u16	format_type;
+	u8	flags;
+	u8	idleconf;	/* Idle config */
+#define AU_IFACE_FOUND	1
+	struct  usb_audio_format format[MAX_FORMAT];
+};
+
+struct usb_audio_device
+{
+	struct list_head list;
+	u8	mixer;
+	u8	selector;
+	void	*irq_handle;
+	u8	num_channels;
+	u8	num_dsp_iface;
+	u8	channel_map[MAX_CHAN];
+	struct usb_audio_terminal terminal[MAX_CHAN];
+	struct usb_audio_interface interface[MAX_IFACE][MAX_ALT];
+};
+
+
+
+/* Audio Class specific Request Codes */
+
+#define SET_CUR    0x01
+#define GET_CUR    0x81
+#define SET_MIN    0x02
+#define GET_MIN    0x82
+#define SET_MAX    0x03
+#define GET_MAX    0x83
+#define SET_RES    0x04
+#define GET_RES    0x84
+#define SET_MEM    0x05
+#define GET_MEM    0x85
+#define GET_STAT   0xff
+
+/* Terminal Control Selectors */
+
+#define COPY_PROTECT_CONTROL       0x01
+
+/* Feature Unit Control Selectors */
+
+#define MUTE_CONTROL               0x01
+#define VOLUME_CONTROL             0x02
+#define BASS_CONTROL               0x03
+#define MID_CONTROL                0x04
+#define TREBLE_CONTROL             0x05
+#define GRAPHIC_EQUALIZER_CONTROL  0x06
+#define AUTOMATIC_GAIN_CONTROL     0x07
+#define DELAY_CONTROL              0x08
+#define BASS_BOOST_CONTROL         0x09
+#define LOUDNESS_CONTROL           0x0a
+
+/* Endpoint Control Selectors */
+
+#define SAMPLING_FREQ_CONTROL      0x01
+#define PITCH_CONTROL              0x02
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/bluetooth.c linux.18p17/drivers/usb/bluetooth.c
--- linux.vanilla/drivers/usb/bluetooth.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/bluetooth.c	Sat Oct 14 00:31:47 2000
@@ -0,0 +1,1273 @@
+/*
+ * 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.
+ *
+ * (07/11/2000) Version 0.4 gkh
+ *	Fixed bug in disconnect for when we call tty_hangup
+ *	Fixed bug in bluetooth_ctrl_msg where the bluetooth struct was not
+ *	getting attached to the control urb properly.
+ *	Fixed bug in bluetooth_write where we pay attention to the result
+ *	of bluetooth_ctrl_msg.
+ *
+ * (08/03/2000) Version 0.3 gkh mdc
+ *	Merged in Mark's changes to make the driver play nice with the Axis
+ *	stack.
+ *	Made the write bulk use an urb pool to enable larger transfers with
+ *	fewer calls to the driver.
+ *	Fixed off by one bug in acl pkt receive
+ *	Made packet counters specific to each bluetooth device 
+ *	Added checks for zero length callbacks
+ *	Added buffers for int and bulk packets.  Had to do this otherwise 
+ *	packet types could intermingle.
+ *	Made a control urb pool for the control messages.
+ *
+ * (07/11/2000) Version 0.2 gkh
+ *	Fixed a small bug found by Nils Faerber in the usb_bluetooth_probe 
+ *	function.
+ *
+ * (07/09/2000) Version 0.1 gkh
+ *	Initial release. Has support for sending ACL data (which is really just
+ *	a HCI frame.) Raw HCI commands and HCI events are not supported.
+ *	A ioctl will probably be needed for the HCI commands and events in the
+ *	future. All isoch endpoints are ignored at this time also.
+ *	This driver should work for all currently shipping USB Bluetooth 
+ *	devices at this time :)
+ * 
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/devfs_fs_kernel.h>
+
+#define DEBUG
+#include <linux/usb.h>
+
+/* Module information */
+MODULE_AUTHOR("Greg Kroah-Hartman, Mark Douglas Corner");
+MODULE_DESCRIPTION("USB Bluetooth driver");
+
+/* define this if you have hardware that is not good */
+/*#define	BTBUGGYHARDWARE */
+
+/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+#define WIRELESS_CLASS_CODE			0xe0
+#define RF_SUBCLASS_CODE			0x01
+#define BLUETOOTH_PROGRAMMING_PROTOCOL_CODE	0x01
+
+
+#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 */
+
+#define BLUETOOTH_CONTROL_REQUEST_TYPE	0x20
+
+/* Bluetooth packet types */
+#define CMD_PKT			0x01
+#define ACL_PKT			0x02
+#define SCO_PKT			0x03
+#define EVENT_PKT		0x04
+#define ERROR_PKT		0x05
+#define NEG_PKT			0x06
+
+/* Message sizes */
+#define MAX_EVENT_SIZE		0xFF
+#define EVENT_HDR_SIZE		3	/* 2 for the header + 1 for the type indicator */
+#define EVENT_BUFFER_SIZE	(MAX_EVENT_SIZE + EVENT_HDR_SIZE)
+
+#define MAX_ACL_SIZE		0xFFFF
+#define ACL_HDR_SIZE		5	/* 4 for the header + 1 for the type indicator */
+#define ACL_BUFFER_SIZE		(MAX_ACL_SIZE + ACL_HDR_SIZE)
+
+/* parity check flag */
+#define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+#define CHAR2INT16(c1,c0)	(((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define MIN(a,b)		(((a)<(b))?(a):(b))
+
+#define NUM_BULK_URBS		24
+#define NUM_CONTROL_URBS	16
+
+struct usb_bluetooth {
+	int			magic;
+	struct usb_device *	dev;
+	struct tty_driver *	tty_driver;	/* the tty_driver for this device */
+	struct tty_struct *	tty;		/* the coresponding tty for this port */
+
+	unsigned char		minor;		/* the starting minor number for this device */
+	char			active;		/* someone has this device open */
+	int			throttle;	/* throttled by tty layer */
+	
+	__u8			control_out_bInterfaceNum;
+	struct urb *		control_urb_pool[NUM_CONTROL_URBS];
+	devrequest		dr[NUM_CONTROL_URBS];
+
+	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_buffer_size;
+	struct urb *		write_urb_pool[NUM_BULK_URBS];
+	__u8			bulk_out_endpointAddress;
+
+	wait_queue_head_t	write_wait;
+
+	struct tq_struct	tqueue;		/* task queue for line discipline waking up */
+	
+	unsigned int		int_packet_pos;
+	unsigned char		int_buffer[EVENT_BUFFER_SIZE];
+	unsigned int		bulk_packet_pos;
+	unsigned char		bulk_buffer[ACL_BUFFER_SIZE];	/* 64k preallocated, fix? */
+};
+
+
+/* local function prototypes */
+static int  bluetooth_open		(struct tty_struct *tty, struct file *filp);
+static void bluetooth_close		(struct tty_struct *tty, struct file *filp);
+static int  bluetooth_write		(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static int  bluetooth_write_room	(struct tty_struct *tty);
+static int  bluetooth_chars_in_buffer	(struct tty_struct *tty);
+static void bluetooth_throttle		(struct tty_struct *tty);
+static void bluetooth_unthrottle	(struct tty_struct *tty);
+static int  bluetooth_ioctl		(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void bluetooth_set_termios	(struct tty_struct *tty, struct termios *old);
+
+static void bluetooth_int_callback		(struct urb *urb);
+static void bluetooth_ctrl_callback		(struct urb *urb);
+static void bluetooth_read_bulk_callback	(struct urb *urb);
+static void bluetooth_write_bulk_callback	(struct urb *urb);
+
+static void * usb_bluetooth_probe	(struct usb_device *dev, unsigned int ifnum);
+static void usb_bluetooth_disconnect	(struct usb_device *dev, void *ptr);
+
+
+static struct usb_driver usb_bluetooth_driver = {
+	name:		"bluetooth",
+	probe:		usb_bluetooth_probe,
+	disconnect:	usb_bluetooth_disconnect,
+};
+
+static int			bluetooth_refcount;
+static struct tty_driver	bluetooth_tty_driver;
+static struct tty_struct *	bluetooth_tty[BLUETOOTH_TTY_MINORS];
+static struct termios *		bluetooth_termios[BLUETOOTH_TTY_MINORS];
+static struct termios *		bluetooth_termios_locked[BLUETOOTH_TTY_MINORS];
+static struct usb_bluetooth	*bluetooth_table[BLUETOOTH_TTY_MINORS] = {NULL, };
+
+
+
+static inline int bluetooth_paranoia_check (struct usb_bluetooth *bluetooth, const char *function)
+{
+	if (!bluetooth) {
+		dbg("%s - bluetooth == NULL", function);
+		return -1;
+	}
+	if (bluetooth->magic != USB_BLUETOOTH_MAGIC) {
+		dbg("%s - bad magic number for bluetooth", function);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static inline struct usb_bluetooth* get_usb_bluetooth (struct usb_bluetooth *bluetooth, const char *function)
+{
+	if (!bluetooth || 
+	    bluetooth_paranoia_check (bluetooth, function)) { 
+		/* then say that we dont have a valid usb_bluetooth thing, which will
+		 * end up generating -ENODEV return values */
+		return NULL;
+	}
+
+	return bluetooth;
+}
+
+
+static inline struct usb_bluetooth *get_bluetooth_by_minor (int minor)
+{
+	return bluetooth_table[minor];
+}
+
+
+static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int value, void *buf, int len)
+{
+	struct urb *urb = NULL;
+	devrequest *dr = NULL;
+	int i;
+	int status;
+
+	dbg (__FUNCTION__);
+
+	/* try to find a free urb in our list */
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+		if (bluetooth->control_urb_pool[i]->status != -EINPROGRESS) {
+			urb = bluetooth->control_urb_pool[i];
+			dr = &bluetooth->dr[i];
+			break;
+		}
+	}
+	if (urb == NULL) {
+		dbg (__FUNCTION__ " - no free urbs");
+		return -ENOMEM;
+	}
+
+	/* free up the last buffer that this urb used */
+	if (urb->transfer_buffer != NULL) {
+		kfree(urb->transfer_buffer);
+		urb->transfer_buffer = NULL;
+	}
+
+	dr->requesttype = BLUETOOTH_CONTROL_REQUEST_TYPE;
+	dr->request = request;
+	dr->value = cpu_to_le16p(&value);
+	dr->index = cpu_to_le16p(&bluetooth->control_out_bInterfaceNum);
+	dr->length = cpu_to_le16p(&len);
+	
+	FILL_CONTROL_URB (urb, bluetooth->dev, usb_sndctrlpipe(bluetooth->dev, 0),
+			  (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, bluetooth);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb);
+	if (status)
+		dbg(__FUNCTION__ " - usb_submit_urb(control) failed with status = %d", status);
+	
+	return 0;
+}
+
+
+
+
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+static int bluetooth_open (struct tty_struct *tty, struct file * filp)
+{
+	struct usb_bluetooth *bluetooth;
+	int result;
+
+	dbg(__FUNCTION__);
+
+	/* initialize the pointer incase something fails */
+	tty->driver_data = NULL;
+
+	/* get the bluetooth object associated with this tty pointer */
+	bluetooth = get_bluetooth_by_minor (MINOR(tty->device));
+
+	if (bluetooth_paranoia_check (bluetooth, __FUNCTION__)) {
+		return -ENODEV;
+	}
+
+	if (bluetooth->active) {
+		dbg (__FUNCTION__ " - device already open");
+		return -EINVAL;
+	}
+
+	/* set up our structure making the tty driver remember our object, and us it */
+	tty->driver_data = bluetooth;
+	bluetooth->tty = tty;
+
+	bluetooth->active = 1;
+
+	/* Reset the packet position counters */
+	bluetooth->int_packet_pos = 0;
+	bluetooth->bulk_packet_pos = 0;
+
+#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);
+
+	return 0;
+}
+
+
+static void bluetooth_close (struct tty_struct *tty, struct file * filp)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	int i;
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not opened");
+		return;
+	}
+
+	/* shutdown any bulk reads and writes that might be going on */
+	for (i = 0; i < NUM_BULK_URBS; ++i)
+		usb_unlink_urb (bluetooth->write_urb_pool[i]);
+	usb_unlink_urb (bluetooth->read_urb);
+
+	bluetooth->active = 0;
+}
+
+
+static int bluetooth_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	struct urb *urb = NULL;
+	unsigned char *new_buffer;
+	const unsigned char *current_position;
+	int status;
+	int bytes_sent;
+	int buffer_size;
+	int i;
+
+	if (!bluetooth) {
+		return -ENODEV;
+	}
+
+	dbg(__FUNCTION__ " - %d byte(s)", count);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not opened");
+		return -EINVAL;
+	}
+
+	if (count == 0) {
+		dbg(__FUNCTION__ " - write request of 0 bytes");
+		return 0;
+	}
+	if (count == 1) {
+		dbg(__FUNCTION__ " - write request only included type %d", buf[0]);
+		return 1;
+	}
+
+#ifdef DEBUG
+	printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count);
+	for (i = 0; i < count; ++i) {
+		printk ("%.2x ", buf[i]);
+	}
+	printk ("\n");
+#endif
+
+	switch (*buf) {
+		/* First byte indicates the type of packet */
+		case CMD_PKT:
+			/* dbg(__FUNCTION__ "- Send cmd_pkt len:%d", count);*/
+
+			if (in_interrupt()){
+				printk("cmd_pkt from interrupt!\n");
+				return count;
+			}
+
+			new_buffer = kmalloc (count-1, GFP_KERNEL);
+
+			if (!new_buffer) {
+				err (__FUNCTION__ "- out of memory.");
+				return -ENOMEM;
+			}
+
+			if (from_user)
+				copy_from_user (new_buffer, buf+1, count-1);
+			else
+				memcpy (new_buffer, buf+1, count-1);
+
+			if (bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1) != 0) {
+				kfree (new_buffer);
+				return 0;
+			}
+
+			/* need to free new_buffer somehow... FIXME */
+			return count;
+
+		case ACL_PKT:
+			current_position = buf;
+			++current_position;
+			--count;
+			bytes_sent = 0;
+
+			while (count > 0) {
+				urb = NULL;
+
+				/* try to find a free urb in our list */
+				for (i = 0; i < NUM_BULK_URBS; ++i) {
+					if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+						urb = bluetooth->write_urb_pool[i];
+						break;
+					}
+				}
+				if (urb == NULL) {
+					dbg (__FUNCTION__ " - no free urbs");
+					return bytes_sent;
+				}
+				
+				/* free up the last buffer that this urb used */
+				if (urb->transfer_buffer != NULL) {
+					kfree(urb->transfer_buffer);
+					urb->transfer_buffer = NULL;
+				}
+
+				buffer_size = MIN (count, bluetooth->bulk_out_buffer_size);
+				
+				new_buffer = kmalloc (buffer_size, GFP_KERNEL);
+				if (new_buffer == NULL) {
+					err(__FUNCTION__" no more kernel memory...");
+					return bytes_sent;
+				}
+
+				if (from_user)
+					copy_from_user(new_buffer, current_position, buffer_size);
+				else
+					memcpy (new_buffer, current_position, buffer_size);
+
+				/* build up our urb */
+				FILL_BULK_URB (urb, bluetooth->dev, usb_sndbulkpipe(bluetooth->dev, bluetooth->bulk_out_endpointAddress),
+						new_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth);
+				urb->transfer_flags |= USB_QUEUE_BULK;
+
+				/* send it down the pipe */
+				status = usb_submit_urb(urb);
+				if (status)
+					dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status);
+#ifdef BTBUGGYHARDWARE
+				/* A workaround for the stalled data bug */
+				/* May or may not be needed...*/
+				if (count != 0) {
+					udelay(500);
+				}
+#endif
+				current_position += buffer_size;
+				bytes_sent += buffer_size;
+				count -= buffer_size;
+			}
+
+			return bytes_sent + 1;
+		
+		default :
+			dbg(__FUNCTION__" - unsupported (at this time) write type");
+	}
+
+	return 0;
+} 
+
+
+static int bluetooth_write_room (struct tty_struct *tty) 
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	int room = 0;
+	int i;
+
+	if (!bluetooth) {
+		return -ENODEV;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+			room += bluetooth->bulk_out_buffer_size;
+		}
+	}
+
+	dbg(__FUNCTION__ " - returns %d", room);
+	return room;
+}
+
+
+static int bluetooth_chars_in_buffer (struct tty_struct *tty) 
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	int chars = 0;
+	int i;
+
+	if (!bluetooth) {
+		return -ENODEV;
+	}
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		if (bluetooth->write_urb_pool[i]->status == -EINPROGRESS) {
+			chars += bluetooth->write_urb_pool[i]->transfer_buffer_length;
+		}
+	}
+
+	dbg (__FUNCTION__ " - returns %d", chars);
+	return chars;
+}
+
+
+static void bluetooth_throttle (struct tty_struct * tty)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+	
+	dbg(__FUNCTION__ " unsupported (at this time)");
+
+	return;
+}
+
+
+static void bluetooth_unthrottle (struct tty_struct * tty)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	dbg(__FUNCTION__ " unsupported (at this time)");
+}
+
+
+static int bluetooth_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return -ENODEV;
+	}
+
+	dbg(__FUNCTION__ " - cmd 0x%.4x", cmd);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return -ENODEV;
+	}
+
+	/* FIXME!!! */
+	return -ENOIOCTLCMD;
+}
+
+
+static void bluetooth_set_termios (struct tty_struct *tty, struct termios * old)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	/* FIXME!!! */
+
+	return;
+}
+
+
+#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;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	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){
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length))
+		usb_unlink_urb(bluetooth->read_urb);
+}
+#endif
+
+
+/*****************************************************************************
+ * urb callback functions
+ *****************************************************************************/
+
+
+static void bluetooth_int_callback (struct urb *urb)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int i;
+	unsigned int count = urb->actual_length;
+	unsigned int packet_size;
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero int status received: %d", urb->status);
+		return;
+	}
+
+	if (!count) {
+		dbg(__FUNCTION__ " - zero length int");
+		return;
+	}
+
+
+#ifdef DEBUG
+	if (count) {
+		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+		for (i = 0; i < count; ++i) {
+			printk ("%.2x ", data[i]);
+		}
+		printk ("\n");
+	}
+#endif
+
+#ifdef BTBUGGYHARDWARE
+	if ((count >= 2) && (data[0] == 0xFF) && (data[1] == 0x00)) {
+		data += 2;
+		count -= 2;
+	}
+	if (count == 0) {
+		urb->actual_length = 0;
+		return;
+	}
+#endif
+	/* We add  a packet type identifier to the beginning of each
+	   HCI frame.  This makes the data in the tty look like a
+	   serial USB devices.  Each HCI frame can be broken across
+	   multiple URBs so we buffer them until we have a full hci
+	   packet */
+
+	if (!bluetooth->int_packet_pos) {
+		bluetooth->int_buffer[0] = EVENT_PKT;
+		bluetooth->int_packet_pos++;
+	}
+	
+	if (bluetooth->int_packet_pos + count > EVENT_BUFFER_SIZE) {
+		err(__FUNCTION__ " - exceeded EVENT_BUFFER_SIZE");
+		bluetooth->int_packet_pos = 0;
+		return;
+	}
+
+	memcpy (&bluetooth->int_buffer[bluetooth->int_packet_pos],
+		urb->transfer_buffer, count);
+	bluetooth->int_packet_pos += count;
+	urb->actual_length = 0;
+
+	if (bluetooth->int_packet_pos >= EVENT_HDR_SIZE)
+		packet_size = bluetooth->int_buffer[2];
+	else
+		return;
+
+	if (packet_size + EVENT_HDR_SIZE < bluetooth->int_packet_pos) {
+		err(__FUNCTION__ " - packet was too long");
+		bluetooth->int_packet_pos = 0;
+		return;
+	}
+
+	if (packet_size + EVENT_HDR_SIZE == bluetooth->int_packet_pos){
+		for (i = 0; i < bluetooth->int_packet_pos; ++i)
+			tty_insert_flip_char(bluetooth->tty, bluetooth->int_buffer[i], 0);
+		tty_flip_buffer_push(bluetooth->tty);
+
+		bluetooth->int_packet_pos = 0;
+	}
+}
+
+
+static void bluetooth_ctrl_callback (struct urb *urb)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+}
+
+
+static void bluetooth_read_bulk_callback (struct urb *urb)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+	unsigned char *data = urb->transfer_buffer;
+	unsigned int count = urb->actual_length;
+	unsigned int i;
+	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;
+		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;
+	}
+#endif
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+		goto exit;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		goto exit;
+	}
+
+	if (!count) {
+		dbg(__FUNCTION__ " - zero length read bulk");
+		goto exit;
+	}
+
+#ifdef DEBUG
+	if (count) {
+		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+		for (i = 0; i < count; ++i) {
+			printk ("%.2x ", data[i]);
+		}
+		printk ("\n");
+	}
+#endif
+	/* We add  a packet type identifier to the beginning of each
+	   HCI frame.  This makes the data in the tty look like a
+	   serial USB devices.  Each HCI frame can be broken across
+	   multiple URBs so we buffer them until we have a full hci
+	   packet */
+	
+	if (!bluetooth->bulk_packet_pos) {
+		bluetooth->bulk_buffer[0] = ACL_PKT;
+		bluetooth->bulk_packet_pos++;
+	}
+
+	if (bluetooth->bulk_packet_pos + count > ACL_BUFFER_SIZE) {
+		err(__FUNCTION__ " - exceeded ACL_BUFFER_SIZE");
+		bluetooth->bulk_packet_pos = 0;
+		goto exit;
+	}
+
+	memcpy (&bluetooth->bulk_buffer[bluetooth->bulk_packet_pos],
+		urb->transfer_buffer, count);
+	bluetooth->bulk_packet_pos += count;
+	urb->actual_length = 0;
+
+	if (bluetooth->bulk_packet_pos >= ACL_HDR_SIZE) {
+		packet_size = CHAR2INT16(bluetooth->bulk_buffer[4],bluetooth->bulk_buffer[3]);
+	} else {
+		goto exit;
+	}
+
+	if (packet_size + ACL_HDR_SIZE < bluetooth->bulk_packet_pos) {
+		err(__FUNCTION__ " - packet was too long");
+		bluetooth->bulk_packet_pos = 0;
+		goto exit;
+	}
+
+	if (packet_size + ACL_HDR_SIZE == bluetooth->bulk_packet_pos) {
+		for (i = 0; i < bluetooth->bulk_packet_pos; ++i)
+			tty_insert_flip_char(bluetooth->tty, bluetooth->bulk_buffer[i], 0);
+		tty_flip_buffer_push(bluetooth->tty);
+		bluetooth->bulk_packet_pos = 0;
+	}	
+
+exit:
+	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;
+}
+
+
+static void bluetooth_write_bulk_callback (struct urb *urb)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	/* wake up our little function to let the tty layer know that something happened */
+	queue_task(&bluetooth->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	return;
+}
+
+
+static void bluetooth_softint(void *private)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)private, __FUNCTION__);
+	struct tty_struct *tty;
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	tty = bluetooth->tty;
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+		dbg(__FUNCTION__ " - write wakeup call.");
+		(tty->ldisc.write_wakeup)(tty);
+	}
+
+	wake_up_interruptible(&tty->write_wait);
+}
+
+
+static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_bluetooth *bluetooth = NULL;
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint[8];
+	struct usb_endpoint_descriptor *bulk_in_endpoint[8];
+	struct usb_endpoint_descriptor *bulk_out_endpoint[8];
+	int control_out_endpoint;
+
+	int minor;
+	int buffer_size;
+	int i;
+	int num_interrupt_in = 0;
+	int num_bulk_in = 0;
+	int num_bulk_out = 0;
+
+	/* see if this device has the proper class signature */
+	if ((dev->descriptor.bDeviceClass != WIRELESS_CLASS_CODE) || 
+	    (dev->descriptor.bDeviceSubClass != RF_SUBCLASS_CODE) ||
+	    (dev->descriptor.bDeviceProtocol != BLUETOOTH_PROGRAMMING_PROTOCOL_CODE)) {
+		dbg (__FUNCTION__ " - class signature %d, %d, %d did not match", 
+		     dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass,
+		     dev->descriptor.bDeviceProtocol);
+		return NULL;
+	}
+
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	control_out_endpoint = interface->bInterfaceNumber;
+
+	/* find the endpoints that we need */
+	for (i = 0; i < interface->bNumEndpoints; ++i) {
+		endpoint = &interface->endpoint[i];
+
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk in endpoint */
+			dbg("found bulk in");
+			bulk_in_endpoint[num_bulk_in] = endpoint;
+			++num_bulk_in;
+		}
+
+		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk out endpoint */
+			dbg("found bulk out");
+			bulk_out_endpoint[num_bulk_out] = endpoint;
+			++num_bulk_out;
+		}
+
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x03)) {
+			/* we found a interrupt in endpoint */
+			dbg("found interrupt in");
+			interrupt_in_endpoint[num_interrupt_in] = endpoint;
+			++num_interrupt_in;
+		}
+	}
+
+	/* according to the spec, we can only have 1 bulk_in, 1 bulk_out, and 1 interrupt_in endpoints */
+	if ((num_bulk_in != 1) ||
+	    (num_bulk_out != 1) ||
+	    (num_interrupt_in != 1)) {
+		dbg (__FUNCTION__ " - improper number of endpoints. Bluetooth driver not bound.");
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+	info("USB Bluetooth converter detected");
+
+	for (minor = 0; minor < BLUETOOTH_TTY_MINORS && bluetooth_table[minor]; ++minor)
+		;
+	if (bluetooth_table[minor]) {
+		err("No more free Bluetooth devices");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+
+	if (!(bluetooth = kmalloc(sizeof(struct usb_bluetooth), GFP_KERNEL))) {
+		err("Out of memory");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+
+	memset(bluetooth, 0, sizeof(struct usb_bluetooth));
+
+	bluetooth->magic = USB_BLUETOOTH_MAGIC;
+	bluetooth->dev = dev;
+	bluetooth->minor = minor;
+	bluetooth->tqueue.routine = bluetooth_softint;
+	bluetooth->tqueue.data = bluetooth;
+
+	/* record the interface number for the control out */
+	bluetooth->control_out_bInterfaceNum = control_out_endpoint;
+	
+	/* create our control out urb pool */ 
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+		struct urb  *urb = usb_alloc_urb(0);
+		if (urb == NULL) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		urb->transfer_buffer = NULL;
+		bluetooth->control_urb_pool[i] = urb;
+	}
+
+	/* set up the endpoint information */
+	endpoint = bulk_in_endpoint[0];
+	bluetooth->read_urb = usb_alloc_urb (0);
+	if (!bluetooth->read_urb) {
+		err("No free urbs available");
+		goto probe_error;
+	}
+	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");
+		goto probe_error;
+	}
+	FILL_BULK_URB(bluetooth->read_urb, dev, usb_rcvbulkpipe(dev, endpoint->bEndpointAddress),
+		      bluetooth->bulk_in_buffer, buffer_size, bluetooth_read_bulk_callback, bluetooth);
+
+	endpoint = bulk_out_endpoint[0];
+	bluetooth->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+	
+	/* create our write urb pool */ 
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		struct urb  *urb = usb_alloc_urb(0);
+		if (urb == NULL) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		urb->transfer_buffer = NULL;
+		bluetooth->write_urb_pool[i] = urb;
+	}
+	
+	bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2;
+
+	endpoint = interrupt_in_endpoint[0];
+	bluetooth->interrupt_in_urb = usb_alloc_urb(0);
+	if (!bluetooth->interrupt_in_urb) {
+		err("No free urbs available");
+		goto probe_error;
+	}
+	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");
+		goto probe_error;
+	}
+	FILL_INT_URB(bluetooth->interrupt_in_urb, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+		     bluetooth->interrupt_in_buffer, buffer_size, bluetooth_int_callback,
+		     bluetooth, endpoint->bInterval);
+
+	/* 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 ttyUB%d (or usb/ttub/%d for devfs)", minor, minor);
+
+	bluetooth_table[minor] = bluetooth;
+
+	return bluetooth; /* success */
+
+probe_error:
+	if (bluetooth->read_urb)
+		usb_free_urb (bluetooth->read_urb);
+	if (bluetooth->bulk_in_buffer)
+		kfree (bluetooth->bulk_in_buffer);
+	if (bluetooth->interrupt_in_urb)
+		usb_free_urb (bluetooth->interrupt_in_urb);
+	if (bluetooth->interrupt_in_buffer)
+		kfree (bluetooth->interrupt_in_buffer);
+	for (i = 0; i < NUM_BULK_URBS; ++i)
+		if (bluetooth->write_urb_pool[i])
+			usb_free_urb (bluetooth->write_urb_pool[i]);
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) 
+		if (bluetooth->control_urb_pool[i])
+			usb_free_urb (bluetooth->control_urb_pool[i]);
+
+	bluetooth_table[minor] = NULL;
+
+	/* free up any memory that we allocated */
+	kfree (bluetooth);
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+
+static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_bluetooth *bluetooth = (struct usb_bluetooth *) ptr;
+	int i;
+
+	if (bluetooth) {
+		if ((bluetooth->active) && (bluetooth->tty))
+			tty_hangup(bluetooth->tty);
+
+		bluetooth->active = 0;
+
+		if (bluetooth->read_urb) {
+			usb_unlink_urb (bluetooth->read_urb);
+			usb_free_urb (bluetooth->read_urb);
+		}
+		if (bluetooth->bulk_in_buffer)
+			kfree (bluetooth->bulk_in_buffer);
+
+		if (bluetooth->interrupt_in_urb) {
+			usb_unlink_urb (bluetooth->interrupt_in_urb);
+			usb_free_urb (bluetooth->interrupt_in_urb);
+		}
+		if (bluetooth->interrupt_in_buffer)
+			kfree (bluetooth->interrupt_in_buffer);
+
+		tty_unregister_devfs (&bluetooth_tty_driver, bluetooth->minor);
+
+		for (i = 0; i < NUM_BULK_URBS; ++i) {
+			if (bluetooth->write_urb_pool[i]) {
+				usb_unlink_urb (bluetooth->write_urb_pool[i]);
+				if (bluetooth->write_urb_pool[i]->transfer_buffer)
+					kfree (bluetooth->write_urb_pool[i]->transfer_buffer);
+				usb_free_urb (bluetooth->write_urb_pool[i]);
+			}
+		}
+		for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+			if (bluetooth->control_urb_pool[i]) {
+				usb_unlink_urb (bluetooth->control_urb_pool[i]);
+				if (bluetooth->control_urb_pool[i]->transfer_buffer)
+					kfree (bluetooth->control_urb_pool[i]->transfer_buffer);
+				usb_free_urb (bluetooth->control_urb_pool[i]);
+			}
+		}
+		
+		info("Bluetooth converter now disconnected from ttyUB%d", bluetooth->minor);
+
+		bluetooth_table[bluetooth->minor] = NULL;
+
+		/* free up any memory that we allocated */
+		kfree (bluetooth);
+
+	} else {
+		info("device disconnected");
+	}
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct tty_driver bluetooth_tty_driver = {
+	magic:			TTY_DRIVER_MAGIC,
+	driver_name:		"usb-bluetooth",
+	name:			"usb/ttub/%d",
+	major:			BLUETOOTH_TTY_MAJOR,
+	minor_start:		0,
+	num:			BLUETOOTH_TTY_MINORS,
+	type:			TTY_DRIVER_TYPE_SERIAL,
+	subtype:		SERIAL_TYPE_NORMAL,
+	flags:			TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
+
+	refcount:		&bluetooth_refcount,
+	table:			bluetooth_tty,
+	termios:		bluetooth_termios,
+	termios_locked:		bluetooth_termios_locked,
+
+	open:			bluetooth_open,
+	close:			bluetooth_close,
+	write:			bluetooth_write,
+	write_room:		bluetooth_write_room,
+	ioctl:			bluetooth_ioctl,
+	set_termios:		bluetooth_set_termios,
+	throttle:		bluetooth_throttle,
+	unthrottle:		bluetooth_unthrottle,
+	chars_in_buffer:	bluetooth_chars_in_buffer,
+};
+
+
+int usb_bluetooth_init(void)
+{
+	int i;
+	int result;
+
+	/* Initalize our global data */
+	for (i = 0; i < BLUETOOTH_TTY_MINORS; ++i) {
+		bluetooth_table[i] = NULL;
+	}
+
+	info ("USB Bluetooth support registered");
+
+	/* register the tty driver */
+	bluetooth_tty_driver.init_termios          = tty_std_termios;
+	bluetooth_tty_driver.init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	if (tty_register_driver (&bluetooth_tty_driver)) {
+		err(__FUNCTION__ " - failed to register tty driver");
+		return -1;
+	}
+
+	/* register the USB driver */
+	result = usb_register(&usb_bluetooth_driver);
+	if (result < 0) {
+		tty_unregister_driver(&bluetooth_tty_driver);
+		err("usb_register failed for the USB bluetooth driver. Error number %d", result);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void usb_bluetooth_exit(void)
+{
+	usb_deregister(&usb_bluetooth_driver);
+	tty_unregister_driver(&bluetooth_tty_driver);
+}
+
+
+module_init(usb_bluetooth_init);
+module_exit(usb_bluetooth_exit);
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dabfirmware.h linux.18p17/drivers/usb/dabfirmware.h
--- linux.vanilla/drivers/usb/dabfirmware.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/dabfirmware.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,1408 @@
+/*
+ * dabdata.h - dab usb firmware and bitstream data
+ */
+
+static INTEL_HEX_RECORD firmware[] = {
+
+{  2, 0x0000, 0, {0x21,0x57} },
+{  3, 0x0003, 0, {0x02,0x01,0x66} },
+{  3, 0x000b, 0, {0x02,0x01,0x66} },
+{  3, 0x0013, 0, {0x02,0x01,0x66} },
+{  3, 0x001b, 0, {0x02,0x01,0x66} },
+{  3, 0x0023, 0, {0x02,0x01,0x66} },
+{  3, 0x002b, 0, {0x02,0x01,0x66} },
+{  3, 0x0033, 0, {0x02,0x03,0x0f} },
+{  3, 0x003b, 0, {0x02,0x01,0x66} },
+{  3, 0x0043, 0, {0x02,0x01,0x00} },
+{  3, 0x004b, 0, {0x02,0x01,0x66} },
+{  3, 0x0053, 0, {0x02,0x01,0x66} },
+{  3, 0x005b, 0, {0x02,0x04,0xbd} },
+{  3, 0x0063, 0, {0x02,0x01,0x67} },
+{  3, 0x0100, 0, {0x02,0x0c,0x5a} },
+{  3, 0x0104, 0, {0x02,0x01,0xed} },
+{  3, 0x0108, 0, {0x02,0x02,0x51} },
+{  3, 0x010c, 0, {0x02,0x02,0x7c} },
+{  3, 0x0110, 0, {0x02,0x02,0xe4} },
+{  1, 0x0114, 0, {0x32} },
+{  1, 0x0118, 0, {0x32} },
+{  3, 0x011c, 0, {0x02,0x05,0xfd} },
+{  3, 0x0120, 0, {0x02,0x00,0x00} },
+{  3, 0x0124, 0, {0x02,0x00,0x00} },
+{  3, 0x0128, 0, {0x02,0x04,0x3c} },
+{  3, 0x012c, 0, {0x02,0x04,0x6a} },
+{  3, 0x0130, 0, {0x02,0x00,0x00} },
+{  3, 0x0134, 0, {0x02,0x00,0x00} },
+{  3, 0x0138, 0, {0x02,0x00,0x00} },
+{  3, 0x013c, 0, {0x02,0x00,0x00} },
+{  3, 0x0140, 0, {0x02,0x00,0x00} },
+{  3, 0x0144, 0, {0x02,0x00,0x00} },
+{  3, 0x0148, 0, {0x02,0x00,0x00} },
+{  3, 0x014c, 0, {0x02,0x00,0x00} },
+{  3, 0x0150, 0, {0x02,0x00,0x00} },
+{  3, 0x0154, 0, {0x02,0x00,0x00} },
+{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} },
+{  5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} },
+{  1, 0x0166, 0, {0x32} },
+{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} },
+{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} },
+{  3, 0x019f, 0, {0x55,0x7f,0xf0} },
+{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} },
+{  3, 0x01da, 0, {0xd0,0xd0,0x32} },
+{  8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} },
+{  7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} },
+{  1, 0x01ec, 0, {0x22} },
+{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} },
+{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} },
+{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} },
+{  9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} },
+{  4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} },
+{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} },
+{  2, 0x023c, 0, {0x01,0x0b} },
+{  2, 0x023e, 0, {0x80,0xe3} },
+{  2, 0x0240, 0, {0xd0,0x86} },
+{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} },
+{  1, 0x0250, 0, {0x32} },
+{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{  4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} },
+{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} },
+{  6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} },
+{  6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} },
+{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{  4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} },
+{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} },
+{  7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} },
+{  6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} },
+{  5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} },
+{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} },
+{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} },
+{  1, 0x0398, 0, {0xf0} },
+{  1, 0x0399, 0, {0x22} },
+{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} },
+{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} },
+{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{  1, 0x03ca, 0, {0x22} },
+{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} },
+{  9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} },
+{  2, 0x03de, 0, {0x03,0x72} },
+{  1, 0x03e0, 0, {0x22} },
+{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} },
+{  9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} },
+{  9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} },
+{  9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} },
+{  8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} },
+{  1, 0x0410, 0,{0x22} },
+{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{  4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} },
+{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} },
+{  7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} },
+{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{  7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} },
+{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} },
+{  1, 0x04ee, 0, {0x22} },
+{  7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} },
+{  1, 0x04f6, 0, {0x22} },
+{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} },
+{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} },
+{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} },
+{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} },
+{  2, 0x052c, 0, {0x7a,0x00} },
+{  7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} },
+{  2, 0x0535, 0, {0x7b,0x00} },
+{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} },
+{  7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} },
+{  9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} },
+{  4, 0x0552, 0, {0x70,0x47,0x75,0x71} },
+{  8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} },
+{  5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} },
+{  1, 0x0563, 0, {0x22} },
+{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} },
+{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} },
+{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} },
+{  5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} },
+{  3, 0x05a1, 0, {0x02,0x05,0xf9} },
+{  6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} },
+{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} },
+{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} },
+{  2, 0x05c6, 0, {0x80,0x31} },
+{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} },
+{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} },
+{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} },
+{  7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} },
+{  3, 0x05f9, 0, {0x75,0x38,0x01} },
+{  1, 0x05fc, 0, {0x22} },
+{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} },
+{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} },
+{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} },
+{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} },
+{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} },
+{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} },
+{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} },
+{  3, 0x06a2, 0, {0xbf,0x81,0x0a} },
+{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} },
+{  8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} },
+{  3, 0x06b7, 0, {0xba,0x01,0x0c} },
+{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} },
+{  8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} },
+{  3, 0x06d9, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} },
+{  8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} },
+{  3, 0x06fc, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} },
+{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} },
+{  5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} },
+{  1, 0x071d, 0, {0x22} },
+{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} },
+{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{  9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{  9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{  7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} },
+{  3, 0x074d, 0, {0xba,0xff,0x00} },
+{  2, 0x0750, 0, {0x50,0x0a} },
+{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} },
+{  8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} },
+{  1, 0x076e, 0, {0x22} },
+{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} },
+{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} },
+{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} },
+{  9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} },
+{  5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} },
+{  7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} },
+{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} },
+{  4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} },
+{  9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} },
+{  7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} },
+{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} },
+{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} },
+{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} },
+{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} },
+{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} },
+{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} },
+{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} },
+{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} },
+{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} },
+{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} },
+{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} },
+{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} },
+{  9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} },
+{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} },
+{  9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{  2, 0x089d, 0, {0x80,0xe0} },
+{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} },
+{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} },
+{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{  2, 0x08d0, 0, {0x80,0xd9} },
+{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} },
+{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} },
+{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} },
+{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} },
+{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} },
+{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} },
+{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} },
+{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} },
+{  3, 0x0937, 0, {0x02,0x0a,0x3f} },
+{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} },
+{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} },
+{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} },
+{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} },
+{  2, 0x097a, 0, {0x60,0x0c} },
+{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} },
+{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} },
+{  6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} },
+{  3, 0x09a7, 0, {0xba,0xa0,0x00} },
+{  2, 0x09aa, 0, {0x50,0x24} },
+{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} },
+{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} },
+{  9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{  5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} },
+{  6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} },
+{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} },
+{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} },
+{  6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} },
+{  5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} },
+{  4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{  3, 0x0a1a, 0, {0x05,0xd0,0x04} },
+{  5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} },
+{  4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{  3, 0x0a3c, 0, {0x05,0xd0,0x04} },
+{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} },
+{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} },
+{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} },
+{  8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} },
+{  8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} },
+{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} },
+{  5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} },
+{  5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} },
+{  5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} },
+{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} },
+{  1, 0x0a9f, 0, {0x00} },
+{  7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} },
+{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{  9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} },
+{  3, 0x0ac8, 0, {0x02,0x0b,0x36} },
+{  5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} },
+{  3, 0x0ad0, 0, {0xbb,0x64,0x00} },
+{  2, 0x0ad3, 0, {0x50,0x1c} },
+{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} },
+{  1, 0x0af0, 0, {0xdf} },
+{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} },
+{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} },
+{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} },
+{  9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} },
+{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} },
+{  3, 0x0b36, 0, {0xba,0x2a,0x3b} },
+{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} },
+{  2, 0x0b54, 0, {0x7b,0x00} },
+{  3, 0x0b56, 0, {0xbb,0x0a,0x00} },
+{  5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{  8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} },
+{  3, 0x0b74, 0, {0xba,0x2b,0x1a} },
+{  8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} },
+{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} },
+{  4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} },
+{  3, 0x0b91, 0, {0xba,0x10,0x1f} },
+{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} },
+{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{  3, 0x0bb0, 0, {0x02,0x0c,0x2d} },
+{  3, 0x0bb3, 0, {0xba,0x11,0x12} },
+{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} },
+{  4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} },
+{  3, 0x0bc8, 0, {0xba,0x12,0x12} },
+{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} },
+{  4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} },
+{  3, 0x0bdd, 0, {0xba,0x13,0x0b} },
+{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} },
+{  3, 0x0beb, 0, {0xba,0x14,0x11} },
+{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} },
+{  3, 0x0bfc, 0, {0x02,0x80,0x2e} },
+{  3, 0x0bff, 0, {0xba,0x15,0x1d} },
+{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} },
+{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} },
+{  3, 0x0c1c, 0, {0x02,0x80,0x0e} },
+{  3, 0x0c1f, 0, {0xba,0x16,0x0b} },
+{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} },
+{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} },
+{  1, 0x0c38, 0, {0x22} },
+{  7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} },
+{  7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} },
+{  6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} },
+{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} },
+{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} },
+{  6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} },
+{  3, 0x0c97, 0, {0x02,0x0d,0x38} },
+{  3, 0x0c9a, 0, {0xba,0x80,0x14} },
+{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} },
+{  6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{  5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} },
+{  3, 0x0cb6, 0, {0x02,0x0d,0x1d} },
+{  8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} },
+{  2, 0x0cc1, 0, {0x40,0x21} },
+{  6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} },
+{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} },
+{  3, 0x0ce4, 0, {0xbc,0x81,0x00} },
+{  2, 0x0ce7, 0, {0x40,0x21} },
+{  6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} },
+{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} },
+{  5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} },
+{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{  5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} },
+{  3, 0x0d22, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} },
+{  5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} },
+{  3, 0x0d38, 0, {0xbb,0x01,0x2d} },
+{  6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{  3, 0x0d41, 0, {0xba,0x02,0x11} },
+{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{  4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} },
+{  5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} },
+{  3, 0x0d5a, 0, {0x02,0x0e,0xcd} },
+{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} },
+{  3, 0x0d68, 0, {0xbb,0x03,0x1f} },
+{  6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{  5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} },
+{  3, 0x0d76, 0, {0x02,0x0e,0xcd} },
+{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{  4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} },
+{  3, 0x0d8a, 0, {0xbb,0x06,0x54} },
+{  5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} },
+{  3, 0x0d92, 0, {0x02,0x0e,0xc5} },
+{  8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} },
+{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{  9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} },
+{  3, 0x0dbc, 0, {0x02,0x0e,0xc5} },
+{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} },
+{  3, 0x0dc9, 0, {0x02,0x0e,0xc5} },
+{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{  9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{  6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} },
+{  3, 0x0de7, 0, {0xbb,0x08,0x10} },
+{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{  3, 0x0df7, 0, {0x02,0x0e,0xcd} },
+{  3, 0x0dfa, 0, {0xbb,0x09,0x31} },
+{  5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} },
+{  3, 0x0e02, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} },
+{  8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} },
+{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} },
+{  9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} },
+{  3, 0x0e2e, 0, {0xbb,0x0a,0x27} },
+{  5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} },
+{  3, 0x0e36, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} },
+{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{  3, 0x0e55, 0, {0x02,0x0e,0xcd} },
+{  5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} },
+{  3, 0x0e5d, 0, {0x02,0x0e,0xa9} },
+{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} },
+{  8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} },
+{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} },
+{  4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} },
+{  8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} },
+{  8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} },
+{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} },
+{  5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} },
+{  5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} },
+{  5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} },
+{  5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} },
+{  5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} },
+{  3, 0x0ebd, 0, {0xbb,0x84,0x05} },
+{  5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} },
+{  8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} },
+{  6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} },
+{  2, 0x0ed3, 0, {0xd0,0x86} },
+{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} },
+{  7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} },
+{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} },
+{  9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} },
+{  7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} },
+{  7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} },
+{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} },
+{  7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} },
+{  1, 0x0f3a, 0, {0x22} },
+{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} },
+{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} },
+{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} },
+{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} },
+{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} },
+{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} },
+{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} },
+{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} },
+{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} },
+{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} },
+{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} },
+{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} },
+{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} },
+{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} },
+{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} },
+{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} },
+{  4, 0x1017, 0, {0x00,0x03,0x01,0x00} },
+{  2, 0x101b, 0, {0x7a,0x00} },
+{  3, 0x101d, 0, {0xba,0x05,0x00} },
+{  2, 0x1020, 0, {0x50,0x17} },
+{  8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} },
+{  5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} },
+{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} },
+{  3, 0x1039, 0, {0x90,0x00,0x01} },
+{  1, 0x103c, 0, {0x22} },
+{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} },
+{  4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} },
+{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} },
+{  4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} },
+{  8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} },
+{  1, 0x1069, 0, {0x22} },
+{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} },
+{  4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} },
+{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} },
+{  4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} },
+{  1, 0x108e, 0, {0x22} },
+{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} },
+{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} },
+{  5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} },
+{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} },
+{  6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} },
+{  6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{  1, 0x10c9, 0, {0x22} },
+{  8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} },
+{  3, 0x10d2, 0, {0xba,0xff,0x00} },
+{  2, 0x10d5, 0, {0x50,0x0a} },
+{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{  1, 0x10e1, 0, {0x22} },
+{  5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} },
+{  8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} },
+{  6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} },
+{  4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} },
+{  5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} },
+{  2, 0x1109, 0, {0x80,0xee} },
+{  1, 0x110b, 0, {0x22} },
+{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} },
+{  3, 0x1116, 0, {0xba,0x28,0x00} },
+{  2, 0x1119, 0, {0x50,0x03} },
+{  3, 0x111b, 0, {0x0a,0x80,0xf8} },
+{  5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} },
+{  8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} },
+{  5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} },
+{  9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} },
+{  2, 0x1139, 0, {0x7a,0x00} },
+{  3, 0x113b, 0, {0xba,0x28,0x00} },
+{  2, 0x113e, 0, {0x50,0x03} },
+{  3, 0x1140, 0, {0x0a,0x80,0xf8} },
+{  1, 0x1143, 0, {0x22} },
+{  4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} },
+{  2, 0x1148, 0, {0x80,0x07} },
+{  7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} },
+{  5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} },
+{  9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} },
+{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} },
+{  8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} },
+{  3, 0x1171, 0, {0x0a,0x80,0xf5} },
+{  1, 0x1174, 0, {0x22} },
+{  6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} },
+{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} },
+{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} },
+{  9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{  2, 0x119d, 0, {0x80,0xdc} },
+{  1, 0x119f, 0, {0x22} },
+{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} },
+{  2, 0x11ad, 0, {0x50,0x06} },
+{  6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{  4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} },
+{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} },
+{  8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} },
+{  6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{  4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} },
+{  5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} },
+{  1, 0x11dc, 0, {0x22} },
+{  7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} },
+{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} },
+{  3, 0x11f0, 0, {0x12,0x11,0x44} },
+{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} },
+{  5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} },
+{  1, 0x1204, 0, {0x22} },
+{  2, 0x1205, 0, {0x7a,0x04} },
+{  3, 0x1207, 0, {0xba,0x40,0x00} },
+{  2, 0x120a, 0, {0x50,0x36} },
+{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} },
+{  3, 0x121a, 0, {0xbc,0x08,0x00} },
+{  2, 0x121d, 0, {0x50,0x20} },
+{  6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} },
+{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} },
+{  8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} },
+{  7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} },
+{  3, 0x123f, 0, {0x0a,0x80,0xc5} },
+{  1, 0x1242, 0, {0x22} },
+{  4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} },
+{  3, 0x1247, 0, {0xba,0x10,0x00} },
+{  2, 0x124a, 0, {0x50,0x20} },
+{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} },
+{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} },
+{  4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} },
+{  1, 0x126c, 0, {0x22} },
+{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{  1, 0x12b3, 0, {0x22} },
+{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} },
+{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} },
+{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} },
+{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} },
+{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} },
+{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} },
+{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} },
+{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} },
+{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} },
+{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} },
+{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} },
+{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} },
+{  3, 0x135c, 0, {0xbd,0x06,0x00} },
+{  2, 0x135f, 0, {0x50,0x12} },
+{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{  7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} },
+{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} },
+{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} },
+{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} },
+{  6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} },
+{  1, 0x13a2, 0, {0x22} },
+{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} },
+{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} },
+{  3, 0x13be, 0, {0xfa,0x7b,0x00} },
+{  3, 0x13c1, 0, {0xbb,0x10,0x00} },
+{  2, 0x13c4, 0, {0x50,0x24} },
+{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} },
+{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} },
+{  8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} },
+{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} },
+{  5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} },
+{  1, 0x13fd, 0, {0x22} },
+{  4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} },
+{  2, 0x1402, 0, {0x80,0x16} },
+{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} },
+{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} },
+{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} },
+{  6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} },
+{  6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} },
+{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} },
+{  1, 0x143b, 0, {0xe4} },
+{  3, 0x143c, 0, {0xfa,0x70,0x0f} },
+{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} },
+{  3, 0x144b, 0, {0x02,0x80,0x0a} },
+{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} },
+{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} },
+{  4, 0x1464, 0, {0x02,0xea,0x70,0x1a} },
+{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} },
+{  9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} },
+{  4, 0x147e, 0, {0x11,0x75,0x80,0x36} },
+{  2, 0x1482, 0, {0x7a,0x00} },
+{  3, 0x1484, 0, {0xba,0x10,0x00} },
+{  2, 0x1487, 0, {0x50,0x2f} },
+{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} },
+{  4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} },
+{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} },
+{  9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} },
+{  4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} },
+{  3, 0x14b5, 0, {0x0a,0x80,0xcc} },
+{  1, 0x14b8, 0, {0x22} },
+{  6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} },
+{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} },
+{  7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} },
+{  7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} },
+{  5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} },
+{  4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} },
+{  5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} },
+{  4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} },
+{  1, 0x14ed, 0, {0x22} },
+{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} },
+{  6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} },
+{  5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} },
+{  3, 0x1507, 0, {0xe0,0x80,0x07} },
+{  4, 0x150a, 0, {0xe4,0x93,0x80,0x03} },
+{  3, 0x150e, 0, {0xa8,0x82,0xe2} },
+{  4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} },
+{  1, 0x1515, 0, {0x22} },
+{  0, 0x0000, 1, {0} }
+
+};
+
+static unsigned char bitstream[] = {
+
+0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D,
+0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B,
+0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39,
+0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34,
+0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B,
+0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F,
+0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF,
+0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE,
+0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF,
+0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF,
+0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF,
+0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD,
+0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,
+0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D,
+0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE,
+0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF,
+0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F,
+0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F,
+0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF,
+0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F,
+0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F,
+0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2,
+0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE,
+0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF,
+0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF,
+0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B,
+0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF,
+0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7,
+0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF,
+0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF,
+0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5,
+0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD,
+0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF,
+0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF,
+0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B,
+0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF,
+0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED,
+0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF,
+0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB,
+0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF,
+0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE,
+0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7,
+0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE,
+0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD,
+0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD,
+0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF,
+0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD,
+0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A,
+0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF,
+0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF,
+0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA,
+0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF,
+0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD,
+0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF,
+0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF,
+0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E,
+0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF,
+0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF,
+0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF,
+0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3,
+0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F,
+0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF,
+0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF,
+0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7,
+0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF,
+0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD,
+0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE,
+0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF,
+0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF,
+0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3,
+0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A,
+0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF,
+0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB,
+0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF,
+0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD,
+0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF,
+0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,
+0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F,
+0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED,
+0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7,
+0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7,
+0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF,
+0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F,
+0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE,
+0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA,
+0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F,
+0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA,
+0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB,
+0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB,
+0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF,
+0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF,
+0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD,
+0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C,
+0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF,
+0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD,
+0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF,
+0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3,
+0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB,
+0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F,
+0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3,
+0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF,
+0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7,
+0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7,
+0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF,
+0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE,
+0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF,
+0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB,
+0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66,
+0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE,
+0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6,
+0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED,
+0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD,
+0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7,
+0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF,
+0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB,
+0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF,
+0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F,
+0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7,
+0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9,
+0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF,
+0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF,
+0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE,
+0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF,
+0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF,
+0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD,
+0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1,
+0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF,
+0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF,
+0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB,
+0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF,
+0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF,
+0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3,
+0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE,
+0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD,
+0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE,
+0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF,
+0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB,
+0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF,
+0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF,
+0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF,
+0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F,
+0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C,
+0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD,
+0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF,
+0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F,
+0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE,
+0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD,
+0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54,
+0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2,
+0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD,
+0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF,
+0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF,
+0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7,
+0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA,
+0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF,
+0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C,
+0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF,
+0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5,
+0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB,
+0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF,
+0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF,
+0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F,
+0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F,
+0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF,
+0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF,
+0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5,
+0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF,
+0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF,
+0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A,
+0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36,
+0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF,
+0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F,
+0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF,
+0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF,
+0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF,
+0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF,
+0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE,
+0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,
+0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF,
+0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F,
+0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B,
+0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB,
+0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF,
+0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF,
+0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA,
+0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37,
+0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD,
+0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B,
+0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF,
+0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B,
+0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B,
+0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD,
+0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF,
+0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF,
+0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF,
+0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA,
+0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E,
+0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF,
+0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6,
+0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD,
+0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F,
+0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7,
+0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
+0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB,
+0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD,
+0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E,
+0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE,
+0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF,
+0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF,
+0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE,
+0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2,
+0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF,
+0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC,
+0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF,
+0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7,
+0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7,
+0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3,
+0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9,
+0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF,
+0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,
+0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F,
+0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B,
+0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE,
+0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79,
+0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F,
+0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7,
+0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC,
+0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7,
+0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF,
+0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7,
+0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F,
+0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE,
+0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF,
+0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F,
+0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7,
+0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7,
+0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF,
+0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD,
+0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF,
+0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB,
+0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE,
+0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3,
+0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF,
+0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE,
+0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6,
+0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF,
+0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF,
+0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F,
+0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE,
+0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6,
+0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77,
+0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF,
+0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A,
+0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F,
+0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F,
+0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF,
+0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB,
+0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F,
+0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67,
+0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF,
+0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D,
+0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3,
+0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D,
+0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB,
+0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE,
+0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5,
+0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D,
+0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F,
+0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB,
+0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB,
+0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF,
+0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF,
+0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77,
+0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F,
+0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE,
+0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF,
+0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD,
+0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F,
+0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A,
+0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA,
+0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF,
+0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF,
+0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF,
+0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF,
+0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9,
+0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F,
+0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7,
+0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4,
+0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF,
+0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7,
+0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF,
+0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF,
+0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF,
+0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5,
+0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF,
+0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD,
+0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77,
+0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE,
+0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF,
+0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF,
+0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC,
+0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB,
+0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F,
+0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6,
+0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD,
+0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF,
+0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC,
+0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,
+0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E,
+0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F,
+0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB,
+0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF,
+0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7,
+0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F,
+0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2,
+0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA,
+0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF,
+0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8,
+0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB,
+0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF,
+0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB,
+0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF,
+0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF,
+0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7,
+0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3,
+0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F,
+0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF,
+0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF,
+0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F,
+0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF,
+0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F,
+0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF,
+0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7,
+0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9,
+0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71,
+0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF,
+0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF,
+0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F,
+0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7,
+0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC,
+0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE,
+0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7,
+0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF,
+0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB,
+0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B,
+0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF,
+0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE,
+0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17,
+0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F,
+0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE,
+0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,
+0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F,
+0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF,
+0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF,
+0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F,
+0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF,
+0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5,
+0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA,
+0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE,
+0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF,
+0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA,
+0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3,
+0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF,
+0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7,
+0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF,
+0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB,
+0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9,
+0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF,
+0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD,
+0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE,
+0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE,
+0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB,
+0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB,
+0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB,
+0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF,
+0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF,
+0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77,
+0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE,
+0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8,
+0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E,
+0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3,
+0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,
+0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7,
+0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB,
+0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF,
+0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF,
+0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF,
+0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3,
+0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F,
+0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F,
+0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,
+0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF,
+0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF,
+0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD,
+0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE,
+0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1,
+0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF,
+0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC,
+0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF,
+0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB,
+0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9,
+0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E,
+0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF,
+0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,
+0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF,
+0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7,
+0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF,
+0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F,
+0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF,
+0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF,
+0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3,
+0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC,
+0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF,
+0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF,
+0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF,
+0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF,
+0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF,
+0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF,
+0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF,
+0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF,
+0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB,
+0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F,
+0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF,
+0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD,
+0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD,
+0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE,
+0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF,
+0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5,
+0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F,
+0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF,
+0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE,
+0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF,
+0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF,
+0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF,
+0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB,
+0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7,
+0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF,
+0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F,
+0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF,
+0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB,
+0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF,
+0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF,
+0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF,
+0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,
+0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,
+0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE,
+0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE,
+0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4,
+0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC,
+0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF,
+0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7,
+0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65,
+0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3,
+0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB,
+0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF,
+0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B,
+0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF,
+0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF,
+0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF,
+0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7,
+0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF,
+0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9,
+0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,
+0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF,
+0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A,
+0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7,
+0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF,
+0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD,
+0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC,
+0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA,
+0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D,
+0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF,
+0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6,
+0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF,
+0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF,
+0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF,
+0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF,
+0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB,
+0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E,
+0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF,
+0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF,
+0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF,
+0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16,
+0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF,
+0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7,
+0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9,
+0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF,
+0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F,
+0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A,
+0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE,
+0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB,
+0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB,
+0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3,
+0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF,
+0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE,
+0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB,
+0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF,
+0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE,
+0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5,
+0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF,
+0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD,
+0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F,
+0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7,
+0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA,
+0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD,
+0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA,
+0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33,
+0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9,
+0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF,
+0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F,
+0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF,
+0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7,
+0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB,
+0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF,
+0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD,
+0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE,
+0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF,
+0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F,
+0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B,
+0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9,
+0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD,
+0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF,
+0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB,
+0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF,
+0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF,
+0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF,
+0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9,
+0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF,
+0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD,
+0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF,
+0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF,
+0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE,
+0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE,
+0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF,
+0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF,
+0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF,
+0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF,
+0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF,
+0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD,
+0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF,
+0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F,
+0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF,
+0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE,
+0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB,
+0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF,
+0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF,
+0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5,
+0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF,
+0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB,
+0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7,
+0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF,
+0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE,
+0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB,
+0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB,
+0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF,
+0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7,
+0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F,
+0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF,
+0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF,
+0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF,
+0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF,
+0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7,
+0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,
+0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC,
+0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC,
+0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF,
+0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF,
+0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB,
+0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD,
+0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF
+
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dabusb.c linux.18p17/drivers/usb/dabusb.c
--- linux.vanilla/drivers/usb/dabusb.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/dabusb.c	Fri Sep 22 14:55:31 2000
@@ -0,0 +1,847 @@
+/*****************************************************************************/
+
+/*
+ *      dabusb.c  --  dab usb driver.
+ *
+ *      Copyright (C) 1999  Deti Fliegl (deti@fliegl.de)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ *  $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+#include "dabusb.h"
+#include "dabfirmware.h"
+
+/* --------------------------------------------------------------------- */
+
+#define NRDABUSB 4
+
+/*-------------------------------------------------------------------*/
+
+static dabusb_t dabusb[NRDABUSB];
+static int buffers = 256;
+
+/*-------------------------------------------------------------------*/
+
+static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src)
+{
+	unsigned long flags;
+	struct list_head *tmp;
+	int ret = 0;
+
+	spin_lock_irqsave (&s->lock, flags);
+
+	if (list_empty (src)) {
+		// no elements in source buffer
+		ret = -1;
+		goto err;
+	}
+	tmp = src->next;
+	list_del (tmp);
+	list_add_tail (tmp, dst);
+
+  err:	spin_unlock_irqrestore (&s->lock, flags);
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+#ifdef DEBUG 
+static void dump_urb (purb_t purb)
+{
+	dbg("urb                   :%p", purb);
+	dbg("next                  :%p", purb->next);
+	dbg("dev                   :%p", purb->dev);
+	dbg("pipe                  :%08X", purb->pipe);
+	dbg("status                :%d", purb->status);
+	dbg("transfer_flags        :%08X", purb->transfer_flags);
+	dbg("transfer_buffer       :%p", purb->transfer_buffer);
+	dbg("transfer_buffer_length:%d", purb->transfer_buffer_length);
+	dbg("actual_length         :%d", purb->actual_length);
+	dbg("setup_packet          :%p", purb->setup_packet);
+	dbg("start_frame           :%d", purb->start_frame);
+	dbg("number_of_packets     :%d", purb->number_of_packets);
+	dbg("interval              :%d", purb->interval);
+	dbg("error_count           :%d", purb->error_count);
+	dbg("context               :%p", purb->context);
+	dbg("complete              :%p", purb->complete);
+}
+#endif
+/*-------------------------------------------------------------------*/
+static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
+{
+	unsigned long flags;
+	struct list_head *p;
+	pbuff_t b;
+
+	dbg("dabusb_cancel_queue");
+
+	spin_lock_irqsave (&s->lock, flags);
+
+	for (p = q->next; p != q; p = p->next) {
+		b = list_entry (p, buff_t, buff_list);
+
+#ifdef DEBUG
+		dump_urb(b->purb);
+#endif
+		usb_unlink_urb (b->purb);
+	}
+	spin_unlock_irqrestore (&s->lock, flags);
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_queue (struct list_head *q)
+{
+	struct list_head *tmp;
+	struct list_head *p;
+	pbuff_t b;
+
+	dbg("dabusb_free_queue");
+	for (p = q->next; p != q;) {
+		b = list_entry (p, buff_t, buff_list);
+
+#ifdef DEBUG 
+		dump_urb(b->purb);
+#endif
+		if (b->purb->transfer_buffer)
+			kfree (b->purb->transfer_buffer);
+		usb_free_urb(b->purb);
+		tmp = p->next;
+		list_del (p);
+		kfree (b);
+		p = tmp;
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_buffers (pdabusb_t s)
+{
+	unsigned long flags;
+	dbg("dabusb_free_buffers");
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	dabusb_free_queue (&s->free_buff_list);
+	dabusb_free_queue (&s->rec_buff_list);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	s->got_mem = 0;
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+static void dabusb_iso_complete (purb_t purb)
+{
+	pbuff_t b = purb->context;
+	pdabusb_t s = b->s;
+	int i;
+	int len;
+	int dst = 0;
+	void *buf = purb->transfer_buffer;
+
+	dbg("dabusb_iso_complete");
+
+	// process if URB was not killed
+	if (purb->status != -ENOENT) {
+		unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE);
+		int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+		for (i = 0; i < purb->number_of_packets; i++)
+			if (!purb->iso_frame_desc[i].status) {
+				len = purb->iso_frame_desc[i].actual_length;
+				if (len <= pipesize) {
+					memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len);
+					dst += len;
+				}
+				else
+					err("dabusb_iso_complete: invalid len %d", len);
+			}
+			else
+				warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status);
+		if (dst != purb->actual_length)
+			err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length);
+	}
+
+	if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) {
+		s->overruns++;
+		err("overrun (%d)", s->overruns);
+	}
+	wake_up (&s->wait);
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_alloc_buffers (pdabusb_t s)
+{
+	int buffers = 0;
+	pbuff_t b;
+	unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE);
+	int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe));
+	int packets = _ISOPIPESIZE / pipesize;
+	int transfer_buffer_length = packets * pipesize;
+	int i;
+
+	dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d",
+		 pipesize, packets, transfer_buffer_length);
+
+	while (buffers < (s->total_buffer_size << 10)) {
+		b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL);
+		if (!b) {
+			err("kmalloc(sizeof(buff_t))==NULL");
+			goto err;
+		}
+		memset (b, sizeof (buff_t), 0);
+		b->s = s;
+		b->purb = usb_alloc_urb(packets);
+		if (!b->purb) {
+			err("usb_alloc_urb == NULL");
+			kfree (b);
+			goto err;
+		}
+
+		b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL);
+		if (!b->purb->transfer_buffer) {
+			kfree (b->purb);
+			kfree (b);
+			err("kmalloc(%d)==NULL", transfer_buffer_length);
+			goto err;
+		}
+
+		b->purb->transfer_buffer_length = transfer_buffer_length;
+		b->purb->number_of_packets = packets;
+		b->purb->complete = dabusb_iso_complete;
+		b->purb->context = b;
+		b->purb->dev = s->usbdev;
+		b->purb->pipe = pipe;
+		b->purb->transfer_flags = USB_ISO_ASAP;
+
+		for (i = 0; i < packets; i++) {
+			b->purb->iso_frame_desc[i].offset = i * pipesize;
+			b->purb->iso_frame_desc[i].length = pipesize;
+		}
+
+		buffers += transfer_buffer_length;
+		list_add_tail (&b->buff_list, &s->free_buff_list);
+	}
+	s->got_mem = buffers;
+
+	return 0;
+
+	err:
+	dabusb_free_buffers (s);
+	return -ENOMEM;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
+{
+	int ret;
+	unsigned int pipe;
+	int actual_length;
+
+	dbg("dabusb_bulk");
+
+	if (!pb->pipe)
+		pipe = usb_rcvbulkpipe (s->usbdev, 2);
+	else
+		pipe = usb_sndbulkpipe (s->usbdev, 2);
+
+	ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100);
+	if(ret<0) {
+		err("dabusb: usb_bulk_msg failed(%d)",ret);
+
+		if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+			err("set_interface failed");
+			return -EINVAL;
+		}
+
+	}
+	
+	if( ret == -EPIPE ) {
+		warn("CLEAR_FEATURE request to remove STALL condition.");
+		if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+			err("request failed");
+	}
+
+	pb->size = actual_length;
+	return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len)
+{
+	int ret;
+	unsigned char *transfer_buffer =  kmalloc (len, GFP_KERNEL);
+
+	if (!transfer_buffer) {
+		err("dabusb_writemem: kmalloc(%d) failed.", len);
+		return -ENOMEM;
+	}
+
+	memcpy (transfer_buffer, data, len);
+
+	ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300);
+
+	kfree (transfer_buffer);
+	return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit)
+{
+	dbg("dabusb_8051_reset: %d",reset_bit);
+	return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_loadmem (pdabusb_t s, const char *fname)
+{
+	int ret;
+	PINTEL_HEX_RECORD ptr = firmware;
+
+	dbg("Enter dabusb_loadmem (internal)");
+	
+	ret = dabusb_8051_reset (s, 1);
+	while (ptr->Type == 0) {
+
+		dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length);
+
+		ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length);
+		if (ret < 0) {
+			err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length);
+			break;
+		}
+		ptr++;
+	}
+	ret = dabusb_8051_reset (s, 0);
+
+	dbg("dabusb_loadmem: exit");
+
+	return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b)
+{
+	b->size = 4;
+	b->data[0] = 0x2a;
+	b->data[1] = 0;
+	b->data[2] = 0;
+	b->data[3] = 0;
+
+	dbg("dabusb_fpga_clear");
+
+	return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b)
+{
+	b->size = 4;
+	b->data[0] = 0x2c;
+	b->data[1] = 0;
+	b->data[2] = 0;
+	b->data[3] = 0;
+
+	dbg("dabusb_fpga_init");
+
+	return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_download (pdabusb_t s, const char *fname)
+{
+	pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+	unsigned int blen, n;
+	int ret;
+	unsigned char *buf = bitstream;
+
+	dbg("Enter dabusb_fpga_download (internal)");
+
+	if (!b) {
+		err("kmalloc(sizeof(bulk_transfer_t))==NULL");
+		return -ENOMEM;
+	}
+
+	b->pipe = 1;
+	ret = dabusb_fpga_clear (s, b);
+	mdelay (10);
+	blen = buf[73] + (buf[72] << 8);
+
+	dbg("Bitstream len: %i", blen);
+
+	b->data[0] = 0x2b;
+	b->data[1] = 0;
+	b->data[2] = 0;
+	b->data[3] = 60;
+
+	for (n = 0; n <= blen + 60; n += 60) {
+		// some cclks for startup
+		b->size = 64;
+		memcpy (b->data + 4, buf + 74 + n, 60);
+		ret = dabusb_bulk (s, b);
+		if (ret < 0) {
+			err("dabusb_bulk failed.");
+			break;
+		}
+		mdelay (1);
+	}
+
+	ret = dabusb_fpga_init (s, b);
+	kfree (b);
+
+	dbg("exit dabusb_fpga_download");
+
+	return ret;
+}
+
+static loff_t dabusb_llseek (struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static int dabusb_stop (pdabusb_t s)
+{
+	dbg("dabusb_stop");
+
+	s->state = _stopped;
+	dabusb_cancel_queue (s, &s->rec_buff_list);
+
+	dbg("pending_io: %d", s->pending_io.counter);
+
+	s->pending_io.counter = 0;
+	return 0;
+}
+
+static int dabusb_startrek (pdabusb_t s)
+{
+	if (!s->got_mem && s->state != _started) {
+
+		dbg("dabusb_startrek");
+
+		if (dabusb_alloc_buffers (s) < 0)
+			return -ENOMEM;
+		dabusb_stop (s);
+		s->state = _started;
+		s->readptr = 0;
+	}
+
+	if (!list_empty (&s->free_buff_list)) {
+		pbuff_t end;
+		int ret;
+		
+	while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) {
+
+			dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list);
+
+			end = list_entry (s->rec_buff_list.prev, buff_t, buff_list);
+
+			ret = usb_submit_urb (end->purb);
+			if (ret) {
+				err("usb_submit_urb returned:%d", ret);
+				if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+					err("startrek: dabusb_add_buf_tail failed");
+				break;
+			}
+			else
+				atomic_inc (&s->pending_io);
+		}
+		dbg("pending_io: %d",s->pending_io.counter);
+	}
+
+	return 0;
+}
+
+static ssize_t dabusb_read (struct file *file, char *buf, size_t count, loff_t * ppos)
+{
+	pdabusb_t s = (pdabusb_t) file->private_data;
+	unsigned long flags;
+	unsigned ret = 0;
+	int rem;
+	int cnt;
+	pbuff_t b;
+	purb_t purb = NULL;
+
+	dbg("dabusb_read");
+
+	if (*ppos)
+		return -ESPIPE;
+
+	if (s->remove_pending)
+		return -EIO;
+
+
+	if (!s->usbdev)
+		return -EIO;
+
+	while (count > 0) {
+		dabusb_startrek (s);
+
+		spin_lock_irqsave (&s->lock, flags);
+
+		if (list_empty (&s->rec_buff_list)) {
+
+			spin_unlock_irqrestore(&s->lock, flags);
+
+			err("error: rec_buf_list is empty");
+			goto err;
+		}
+		
+		b = list_entry (s->rec_buff_list.next, buff_t, buff_list);
+		purb = b->purb;
+
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		if (purb->status == -EINPROGRESS) {
+			if (file->f_flags & O_NONBLOCK)		// return nonblocking
+			 {
+				if (!ret)
+					ret = -EAGAIN;
+				goto err;
+			}
+
+			interruptible_sleep_on (&s->wait);
+
+			if (signal_pending (current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				goto err;
+			}
+
+			spin_lock_irqsave (&s->lock, flags);
+
+			if (list_empty (&s->rec_buff_list)) {
+				spin_unlock_irqrestore(&s->lock, flags);
+				err("error: still no buffer available.");
+				goto err;
+			}
+			spin_unlock_irqrestore(&s->lock, flags);
+			s->readptr = 0;
+		}
+		if (s->remove_pending) {
+			ret = -EIO;
+			goto err;
+		}
+
+		rem = purb->actual_length - s->readptr;		// set remaining bytes to copy
+
+		if (count >= rem)
+			cnt = rem;
+		else
+			cnt = count;
+
+		dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt);
+
+		if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) {
+			err("read: copy_to_user failed");
+			if (!ret)
+				ret = -EFAULT;
+			goto err;
+		}
+
+		s->readptr += cnt;
+		count -= cnt;
+		buf += cnt;
+		ret += cnt;
+
+		if (s->readptr == purb->actual_length) {
+			// finished, take next buffer
+			if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+				err("read: dabusb_add_buf_tail failed");
+			s->readptr = 0;
+		}
+	}
+      err:			//up(&s->mutex);
+	return ret;
+}
+
+static int dabusb_open (struct inode *inode, struct file *file)
+{
+	int devnum = MINOR (inode->i_rdev);
+	pdabusb_t s;
+
+	if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB))
+		return -EIO;
+
+	s = &dabusb[devnum - DABUSB_MINOR];
+
+	dbg("dabusb_open");
+	down (&s->mutex);
+
+	while (!s->usbdev || s->opened) {
+		up (&s->mutex);
+
+		if (file->f_flags & O_NONBLOCK) {
+			return -EBUSY;
+		}
+		schedule_timeout (HZ / 2);
+
+		if (signal_pending (current)) {
+			return -EAGAIN;
+		}
+		down (&s->mutex);
+	}
+	if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+		err("set_interface failed");
+		return -EINVAL;
+	}
+	s->opened = 1;
+	up (&s->mutex);
+
+	file->f_pos = 0;
+	file->private_data = s;
+
+	return 0;
+}
+
+static int dabusb_release (struct inode *inode, struct file *file)
+{
+	pdabusb_t s = (pdabusb_t) file->private_data;
+
+	dbg("dabusb_release");
+
+	lock_kernel();
+	down (&s->mutex);
+	dabusb_stop (s);
+	dabusb_free_buffers (s);
+	up (&s->mutex);
+
+	if (!s->remove_pending) {
+		if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
+			err("set_interface failed");
+	}
+	else
+		wake_up (&s->remove_ok);
+
+	s->opened = 0;
+	unlock_kernel();
+	return 0;
+}
+
+static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	pdabusb_t s = (pdabusb_t) file->private_data;
+	pbulk_transfer_t pbulk;
+	int ret = 0;
+	int version = DABUSB_VERSION;
+
+	dbg("dabusb_ioctl");
+
+	if (s->remove_pending)
+		return -EIO;
+
+	down (&s->mutex);
+
+	if (!s->usbdev) {
+		up (&s->mutex);
+		return -EIO;
+	}
+
+	switch (cmd) {
+
+	case IOCTL_DAB_BULK:
+		pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+
+		if (!pbulk) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user (pbulk, (void *) arg, sizeof (bulk_transfer_t))) {
+			ret = -EFAULT;
+			kfree (pbulk);
+			break;
+		}
+
+		ret=dabusb_bulk (s, pbulk);
+		if(ret==0)
+			ret = copy_to_user ((void *) arg, pbulk, sizeof (bulk_transfer_t));
+		kfree (pbulk);
+		break;
+
+	case IOCTL_DAB_OVERRUNS:
+		ret = put_user (s->overruns, (unsigned int *) arg);
+		break;
+
+	case IOCTL_DAB_VERSION:
+		ret = put_user (version, (unsigned int *) arg);
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	up (&s->mutex);
+	return ret;
+}
+
+static struct file_operations dabusb_fops =
+{
+	llseek:		dabusb_llseek,
+	read:		dabusb_read,
+	ioctl:		dabusb_ioctl,
+	open:		dabusb_open,
+	release:	dabusb_release,
+};
+
+static int dabusb_find_struct (void)
+{
+	int u;
+
+	for (u = 0; u < NRDABUSB; u++) {
+		pdabusb_t s = &dabusb[u];
+		if (!s->usbdev)
+			return u;
+	}
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum)
+{
+	int devnum;
+	pdabusb_t s;
+
+	dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
+	  usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
+
+	/* the 1234:5678 is just a self assigned test ID */
+	if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) &&
+	    (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999))
+		return NULL;
+
+	/* We don't handle multiple configurations */
+	if (usbdev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999)
+		return NULL;
+
+	devnum = dabusb_find_struct ();
+	if (devnum == -1)
+		return NULL;
+
+	s = &dabusb[devnum];
+
+	down (&s->mutex);
+	s->remove_pending = 0;
+	s->usbdev = usbdev;
+
+	if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) {
+		err("set_configuration failed");
+		goto reject;
+	}
+	if (usbdev->descriptor.idProduct == 0x2131) {
+		dabusb_loadmem (s, NULL);
+		goto reject;
+	}
+	else {
+		dabusb_fpga_download (s, NULL);
+
+		if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) {
+			err("set_interface failed");
+			goto reject;
+		}
+	}
+	dbg("bound to interface: %d", ifnum);
+	up (&s->mutex);
+	MOD_INC_USE_COUNT;
+	return s;
+
+      reject:
+	up (&s->mutex);
+	s->usbdev = NULL;
+	return NULL;
+}
+
+static void dabusb_disconnect (struct usb_device *usbdev, void *ptr)
+{
+	pdabusb_t s = (pdabusb_t) ptr;
+
+	dbg("dabusb_disconnect");
+
+	s->remove_pending = 1;
+	wake_up (&s->wait);
+	if (s->state == _started)
+		sleep_on (&s->remove_ok);
+	s->usbdev = NULL;
+	s->overruns = 0;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver dabusb_driver =
+{
+	"dabusb",
+	dabusb_probe,
+	dabusb_disconnect,
+	{NULL, NULL},
+	&dabusb_fops,
+	DABUSB_MINOR
+};
+
+/* --------------------------------------------------------------------- */
+
+static int __init dabusb_init (void)
+{
+	unsigned u;
+
+	/* initialize struct */
+	for (u = 0; u < NRDABUSB; u++) {
+		pdabusb_t s = &dabusb[u];
+		memset (s, 0, sizeof (dabusb_t));
+		init_MUTEX (&s->mutex);
+		s->usbdev = NULL;
+		s->total_buffer_size = buffers;
+		init_waitqueue_head (&s->wait);
+		init_waitqueue_head (&s->remove_ok);
+		spin_lock_init (&s->lock);
+		INIT_LIST_HEAD (&s->free_buff_list);
+		INIT_LIST_HEAD (&s->rec_buff_list);
+	}
+
+	/* register misc device */
+	if (usb_register(&dabusb_driver))
+		return -1;
+
+	dbg("dabusb_init: driver registered");
+	return 0;
+}
+
+static void __exit dabusb_cleanup (void)
+{
+	dbg("dabusb_cleanup");
+
+	usb_deregister (&dabusb_driver);
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");
+MODULE_DESCRIPTION ("DAB-USB Interface Driver for Linux (c)1999");
+MODULE_PARM (buffers, "i");
+MODULE_PARM_DESC (buffers, "Number of buffers (default=256)");
+
+module_init (dabusb_init);
+module_exit (dabusb_cleanup);
+
+/* --------------------------------------------------------------------- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dabusb.h linux.18p17/drivers/usb/dabusb.h
--- linux.vanilla/drivers/usb/dabusb.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/dabusb.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,84 @@
+#define _BULK_DATA_LEN 64
+typedef struct
+{
+	unsigned char data[_BULK_DATA_LEN];
+	unsigned int size;
+	unsigned int pipe;
+}bulk_transfer_t,*pbulk_transfer_t;
+
+#define DABUSB_MINOR 240		/* some unassigned USB minor */
+#define DABUSB_VERSION 0x1000
+#define IOCTL_DAB_BULK              _IOWR('d', 0x30, bulk_transfer_t)
+#define IOCTL_DAB_OVERRUNS	    _IOR('d',  0x15, int)
+#define IOCTL_DAB_VERSION           _IOR('d', 0x3f, int) 
+
+#ifdef __KERNEL__
+
+typedef enum { _stopped=0, _started } driver_state_t;
+
+typedef struct
+{
+	struct semaphore mutex;
+	struct usb_device *usbdev;
+	wait_queue_head_t wait;
+	wait_queue_head_t remove_ok;
+	spinlock_t lock;
+	volatile atomic_t pending_io;
+	driver_state_t state;
+	int remove_pending;
+	int got_mem;
+	int total_buffer_size;
+	unsigned int overruns;
+	int readptr;
+	int opened;
+	struct list_head free_buff_list;
+	struct list_head rec_buff_list;
+} dabusb_t,*pdabusb_t;
+
+typedef struct 
+{
+	pdabusb_t s;
+	purb_t purb;
+	struct list_head buff_list;
+} buff_t,*pbuff_t;
+
+typedef struct
+{
+	wait_queue_head_t wait;
+} bulk_completion_context_t, *pbulk_completion_context_t;
+
+
+#define _DABUSB_IF 2
+#define _DABUSB_ISOPIPE 0x09
+#define _ISOPIPESIZE	16384
+
+#define _BULK_DATA_LEN 64
+// Vendor specific request code for Anchor Upload/Download
+// This one is implemented in the core
+#define ANCHOR_LOAD_INTERNAL  0xA0
+
+// EZ-USB Control and Status Register.  Bit 0 controls 8051 reset
+#define CPUCS_REG    0x7F92
+#define _TOTAL_BUFFERS 384
+
+#define MAX_INTEL_HEX_RECORD_LENGTH 16
+
+#ifndef _BYTE_DEFINED
+#define _BYTE_DEFINED
+typedef unsigned char BYTE;
+#endif // !_BYTE_DEFINED
+
+#ifndef _WORD_DEFINED
+#define _WORD_DEFINED
+typedef unsigned short WORD;
+#endif // !_WORD_DEFINED
+
+typedef struct _INTEL_HEX_RECORD
+{
+   BYTE  Length;
+   WORD  Address;
+   BYTE  Type;
+   BYTE  Data[MAX_INTEL_HEX_RECORD_LENGTH];
+} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dc2xx.c linux.18p17/drivers/usb/dc2xx.c
--- linux.vanilla/drivers/usb/dc2xx.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/dc2xx.c	Fri Sep 15 22:03:43 2000
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 1999-2000 by David Brownell <david-b@pacbell.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ 
+ 
+/*
+ * USB driver for Kodak DC-2XX series digital still cameras
+ *
+ * The protocol here is the same as the one going over a serial line, but
+ * it uses USB for speed.  Set up /dev/kodak, get gphoto (www.gphoto.org),
+ * and have fun!
+ *
+ * This should also work for a number of other digital (non-Kodak) cameras,
+ * by adding the vendor and product IDs to the table below.  They'll need
+ * to be the sort using USB just as a fast bulk data channel.
+ */
+
+/*
+ * HISTORY
+ *
+ * 26 August, 1999 -- first release (0.1), works with my DC-240.
+ * 	The DC-280 (2Mpixel) should also work, but isn't tested.
+ *	If you use gphoto, make sure you have the USB updates.
+ *	Lives in a 2.3.14 or so Linux kernel, in drivers/usb.
+ * 31 August, 1999 -- minor update to recognize DC-260 and handle
+ *	its endpoints being in a different order.  Note that as
+ *	of gPhoto 0.36pre, the USB updates are integrated.
+ * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0);
+ *	added timeouts to bulk_msg calls.  Minor updates, docs.
+ * 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
+ * 08 Jan, 2000 .. multiple camera support
+ * 12 Aug, 2000 .. add some real locking, remove an Oops
+ *
+ * Thanks to:  the folk who've provided USB product IDs, sent in
+ * patches, and shared their sucesses!
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#undef DEBUG
+#include <linux/usb.h>
+
+
+
+/* current USB framework handles max of 16 USB devices per driver */
+#define	MAX_CAMERAS		8
+
+/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */
+#define	USB_CAMERA_MINOR_BASE	80
+
+
+// XXX remove packet size limit, now that bulk transfers seem fixed
+
+/* Application protocol limit is 0x8002; USB has disliked that limit! */
+#define	MAX_PACKET_SIZE		0x2000		/* e.g. image downloading */
+
+#define	MAX_READ_RETRY		5		/* times to retry reads */
+#define	MAX_WRITE_RETRY		5		/* times to retry writes */
+#define	RETRY_TIMEOUT		(HZ)		/* sleep between retries */
+
+
+/* table of cameras that work through this driver */
+static const struct camera {
+	unsigned short	idVendor;
+	unsigned short	idProduct;
+	/* plus hooks for camera-specific info if needed */
+} cameras [] = {
+	/* These have the same application level protocol */  
+    { 0x040a, 0x0120 },		// Kodak DC-240
+    { 0x040a, 0x0130 },		// Kodak DC-280
+
+	/* These have a different application level protocol which
+	 * is part of the Flashpoint "DigitaOS".  That supports some
+	 * non-camera devices, and some non-Kodak cameras.
+	 */  
+    { 0x040a, 0x0100 },		// Kodak DC-220
+    { 0x040a, 0x0110 },		// Kodak DC-260
+    { 0x040a, 0x0111 },		// Kodak DC-265
+    { 0x040a, 0x0112 },		// Kodak DC-290
+    { 0xf003, 0x6002 },		// HP PhotoSmart C500
+
+	/* Other USB devices may well work here too, so long as they
+	 * just stick to half duplex bulk packet exchanges.  That
+	 * means, among other things, no iso or interrupt endpoints.
+	 */
+};
+
+
+struct camera_state {
+	struct usb_device	*dev;		/* USB device handle */
+	int			inEP;		/* read endpoint */
+	int			outEP;		/* write endpoint */
+	const struct camera	*info;		/* DC-240, etc */
+	int			subminor;	/* which minor dev #? */
+	struct semaphore	sem;		/* locks this struct */
+
+	/* this is non-null iff the device is open */
+	char			*buf;		/* buffer for I/O */
+
+	/* always valid */
+	wait_queue_head_t	wait;		/* for timed waits */
+};
+
+
+/* Support multiple cameras, possibly of different types.  */
+static struct camera_state *minor_data [MAX_CAMERAS];
+
+/* make this an rwlock if contention becomes an issue */
+static DECLARE_MUTEX (state_table_mutex);
+
+static ssize_t camera_read (struct file *file,
+	char *buf, size_t len, loff_t *ppos)
+{
+	struct camera_state	*camera;
+	int			retries;
+	int			retval = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	camera = (struct camera_state *) file->private_data;
+	down (&camera->sem);
+	if (!camera->dev) {
+		up (&camera->sem);
+		return -ENODEV;
+	}
+
+	/* Big reads are common, for image downloading.  Smaller ones
+	 * are also common (even "directory listing" commands don't
+	 * send very much data).  We preserve packet boundaries here,
+	 * they matter in the application protocol.
+	 */
+	for (retries = 0; retries < MAX_READ_RETRY; retries++) {
+		int			count;
+
+		if (signal_pending (current)) {
+			retval = -EINTR;
+			break;
+		}
+
+		retval = usb_bulk_msg (camera->dev,
+			  usb_rcvbulkpipe (camera->dev, camera->inEP),
+			  camera->buf, len, &count, HZ*10);
+
+		dbg ("read (%d) - 0x%x %d", len, retval, count);
+
+		if (!retval) {
+			if (copy_to_user (buf, camera->buf, count))
+				retval = -EFAULT;
+			else
+				retval = count;
+			break;
+		}
+		if (retval != USB_ST_TIMEOUT)
+			break;
+		interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
+
+		dbg ("read (%d) - retry", len);
+	}
+	up (&camera->sem);
+	return retval;
+}
+
+static ssize_t camera_write (struct file *file,
+	const char *buf, size_t len, loff_t *ppos)
+{
+	struct camera_state	*camera;
+	ssize_t			bytes_written = 0;
+
+	if (len > MAX_PACKET_SIZE)
+		return -EINVAL;
+
+	camera = (struct camera_state *) file->private_data;
+	down (&camera->sem);
+	if (!camera->dev) {
+		up (&camera->sem);
+		return -ENODEV;
+	}
+	
+	/* most writes will be small: simple commands, sometimes with
+	 * parameters.  putting images (like borders) into the camera
+	 * would be the main use of big writes.
+	 */
+	while (len > 0) {
+		char		*obuf = camera->buf;
+		int		maxretry = MAX_WRITE_RETRY;
+		unsigned long	copy_size, thistime;
+
+		/* it's not clear that retrying can do any good ... or that
+		 * fragmenting application packets into N writes is correct.
+		 */
+		thistime = copy_size = len;
+		if (copy_from_user (obuf, buf, copy_size)) {
+			bytes_written = -EFAULT;
+			break;
+		}
+		while (thistime) {
+			int		result;
+			int		count;
+
+			if (signal_pending (current)) {
+				if (!bytes_written)
+					bytes_written = -EINTR;
+				goto done;
+			}
+
+			result = usb_bulk_msg (camera->dev,
+				 usb_sndbulkpipe (camera->dev, camera->outEP),
+				 obuf, thistime, &count, HZ*10);
+
+			if (result)
+				dbg ("write USB err - %d", result);
+
+			if (count) {
+				obuf += count;
+				thistime -= count;
+				maxretry = MAX_WRITE_RETRY;
+				continue;
+			} else if (!result)
+				break;
+				
+			if (result == USB_ST_TIMEOUT) {	/* NAK - delay a bit */
+				if (!maxretry--) {
+					if (!bytes_written)
+						bytes_written = -ETIME;
+					goto done;
+				}
+                                interruptible_sleep_on_timeout (&camera->wait,
+					RETRY_TIMEOUT);
+				continue;
+			} 
+			if (!bytes_written)
+				bytes_written = -EIO;
+			goto done;
+		}
+		bytes_written += copy_size;
+		len -= copy_size;
+		buf += copy_size;
+	}
+done:
+	up (&camera->sem);
+	dbg ("wrote %d", bytes_written); 
+	return bytes_written;
+}
+
+static int camera_open (struct inode *inode, struct file *file)
+{
+	struct camera_state	*camera = NULL;
+	int			subminor;
+	int			value = 0;
+
+	down (&state_table_mutex);
+	subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
+	if (subminor < 0 || subminor >= MAX_CAMERAS
+			|| !(camera = minor_data [subminor])) {
+		up (&state_table_mutex);
+		return -ENODEV;
+	}
+	down (&camera->sem);
+	up (&state_table_mutex);
+
+	if (camera->buf) {
+		value = -EBUSY;
+		goto done;
+	}
+
+	if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
+		value = -ENOMEM;
+		goto done;
+	}
+
+	dbg ("open #%d", subminor); 
+
+	file->private_data = camera;
+done:
+	up (&camera->sem);
+	return value;
+}
+
+static int camera_release (struct inode *inode, struct file *file)
+{
+	struct camera_state	*camera;
+	int			subminor;
+
+	camera = (struct camera_state *) file->private_data;
+	down (&state_table_mutex);
+	down (&camera->sem);
+
+	if (camera->buf) {
+		kfree (camera->buf);
+		camera->buf = 0;
+	}
+	subminor = camera->subminor;
+
+	/* If camera was unplugged with open file ... */
+	if (!camera->dev) {
+		minor_data [subminor] = NULL;
+		kfree (camera);
+	}
+	up (&camera->sem);
+	up (&state_table_mutex);
+
+	dbg ("close #%d", subminor); 
+
+	return 0;
+}
+
+	/* XXX should define some ioctls to expose camera type
+	 * to applications ... what USB exposes should suffice.
+	 * apps should be able to see the camera type.
+	 */
+static /* const */ struct file_operations usb_camera_fops = {
+	    /* Uses GCC initializer extension; simpler to maintain */
+	read:		camera_read,
+	write:		camera_write,
+	open:		camera_open,
+	release:	camera_release,
+};
+
+
+
+static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	int				i;
+	const struct camera		*camera_info = NULL;
+	struct usb_interface_descriptor	*interface;
+	struct usb_endpoint_descriptor	*endpoint;
+	int				direction, ep;
+	struct camera_state		*camera = NULL;
+
+	/* Is it a supported camera? */
+	for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) {
+		if (cameras [i].idVendor != dev->descriptor.idVendor)
+			continue;
+		if (cameras [i].idProduct != dev->descriptor.idProduct)
+			continue;
+		camera_info = &cameras [i];
+		break;
+	}
+	if (camera_info == NULL)
+		return NULL;
+
+	/* these have one config, one interface */
+	if (dev->descriptor.bNumConfigurations != 1
+			|| dev->config[0].bNumInterfaces != 1) {
+		dbg ("Bogus camera config info");
+		return NULL;
+	}
+
+	/* models differ in how they report themselves */
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE
+		&& interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+			|| interface->bInterfaceSubClass != 0
+			|| interface->bInterfaceProtocol != 0
+			|| interface->bNumEndpoints != 2
+			) {
+		dbg ("Bogus camera interface info");
+		return NULL;
+	}
+
+
+	/* select "subminor" number (part of a minor number) */
+	down (&state_table_mutex);
+	for (i = 0; i < MAX_CAMERAS; i++) {
+		if (!minor_data [i])
+			break;
+	}
+	if (i >= MAX_CAMERAS) {
+		info ("Ignoring additional USB Camera");
+		up (&state_table_mutex);
+		goto bye;
+	}
+
+	/* allocate & init camera state */
+	camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
+	if (!camera) {
+		err ("no memory!");
+		up (&state_table_mutex);
+		goto bye;
+	}
+
+	init_MUTEX (&camera->sem);
+	camera->info = camera_info;
+	camera->subminor = i;
+	camera->buf = NULL;
+	init_waitqueue_head (&camera->wait);
+
+
+	/* get input and output endpoints (either order) */
+	endpoint = interface->endpoint;
+	camera->outEP = camera->inEP =  -1;
+
+	ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+	if (direction == USB_DIR_IN)
+		camera->inEP = ep;
+	else
+		camera->outEP = ep;
+
+	ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+	if (direction == USB_DIR_IN)
+		camera->inEP = ep;
+	else
+		camera->outEP = ep;
+
+	if (camera->outEP == -1 || camera->inEP == -1
+			|| endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK
+			|| endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK
+			) {
+		dbg ("Bogus endpoints");
+		goto error;
+	}
+
+	info ("USB Camera #%d connected", camera->subminor);
+
+	camera->dev = dev;
+	usb_inc_dev_use (dev);
+	goto bye;
+
+error:
+	minor_data [camera->subminor] = NULL;
+	kfree (camera);
+	camera = NULL;
+bye:
+	up (&state_table_mutex);
+	return camera;
+}
+
+static void camera_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct camera_state	*camera = (struct camera_state *) ptr;
+	int			subminor = camera->subminor;
+
+	down (&state_table_mutex);
+	down (&camera->sem);
+
+	/* If camera's not opened, we can clean up right away.
+	 * Else apps see a disconnect on next I/O; the release cleans.
+	 */
+	if (!camera->buf) {
+		minor_data [subminor] = NULL;
+		kfree (camera);
+	} else
+		camera->dev = NULL;
+
+	info ("USB Camera #%d disconnected", subminor);
+	usb_dec_dev_use (dev);
+
+	up (&camera->sem);
+	up (&state_table_mutex);
+}
+
+static /* const */ struct usb_driver camera_driver = {
+	"dc2xx",
+	camera_probe,
+	camera_disconnect,
+	{ NULL, NULL },
+	&usb_camera_fops,
+	USB_CAMERA_MINOR_BASE
+};
+
+
+int __init usb_dc2xx_init(void)
+{
+ 	if (usb_register (&camera_driver) < 0)
+ 		return -1;
+	return 0;
+}
+
+void __exit usb_dc2xx_cleanup(void)
+{
+	usb_deregister (&camera_driver);
+}
+
+
+MODULE_AUTHOR("David Brownell, david-b@pacbell.net");
+MODULE_DESCRIPTION("USB Camera Driver for Kodak DC-2xx series cameras");
+
+module_init (usb_dc2xx_init);
+module_exit (usb_dc2xx_cleanup);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devices.c linux.18p17/drivers/usb/devices.c
--- linux.vanilla/drivers/usb/devices.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/devices.c	Sat Sep 23 13:19:43 2000
@@ -0,0 +1,560 @@
+/*
+ * devices.c
+ * (C) Copyright 1999 Randy Dunlap.
+ * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
+ * (C) Copyright 1999 Deti Fliegl (new USB architecture)
+ *
+ * $id$
+ *
+ * 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
+ *
+ *************************************************************
+ *
+ * <mountpoint>/devices contains USB topology, device, config, class,
+ * interface, & endpoint data.
+ *
+ * I considered using /proc/bus/usb/devices/device# for each device
+ * as it is attached or detached, but I didn't like this for some
+ * reason -- maybe it's just too deep of a directory structure.
+ * I also don't like looking in multiple places to gather and view
+ * the data.  Having only one file for ./devices also prevents race
+ * conditions that could arise if a program was reading device info
+ * for devices that are being removed (unplugged).  (That is, the
+ * program may find a directory for devnum_12 then try to open it,
+ * but it was just unplugged, so the directory is now deleted.
+ * But programs would just have to be prepared for situations like
+ * this in any plug-and-play environment.)
+ *
+ * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Converted the whole proc stuff to real
+ *   read methods. Now not the whole device list needs to fit
+ *   into one page, only the device list for one bus.
+ *   Added a poll method to /proc/bus/usb/devices, to wake
+ *   up an eventual usbd
+ * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Turned into its own filesystem
+ * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
+ *   Converted file reading routine to dump to buffer once
+ *   per device, not per bus
+ *
+ * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+#define MAX_TOPO_LEVEL		6
+
+/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
+#define ALLOW_SERIAL_NUMBER
+
+static char *format_topo =
+/* T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
+  "T:  Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
+
+static char *format_string_manufacturer =
+/* S:  Manufacturer=xxxx */
+  "S:  Manufacturer=%.100s\n";
+
+static char *format_string_product =
+/* S:  Product=xxxx */
+  "S:  Product=%.100s\n";
+
+#ifdef ALLOW_SERIAL_NUMBER
+static char *format_string_serialnumber =
+/* S:  SerialNumber=xxxx */
+  "S:  SerialNumber=%.100s\n";
+#endif
+
+static char *format_bandwidth =
+/* B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
+  "B:  Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
+  
+static char *format_device1 =
+/* D:  Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
+  "D:  Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
+
+static char *format_device2 =
+/* P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx */
+  "P:  Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
+
+static char *format_config =
+/* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
+  "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
+  
+static char *format_iface =
+/* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
+  "I:  If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
+
+static char *format_endpt =
+/* E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */
+  "E:  Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n";
+
+
+/*
+ * Need access to the driver and USB bus lists.
+ * extern struct list_head usb_driver_list;
+ * extern struct list_head usb_bus_list;
+ * However, these will come from functions that return ptrs to each of them.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
+static unsigned int conndiscevcnt = 0;
+
+/* this struct stores the poll state for <mountpoint>/devices pollers */
+struct usb_device_status {
+	unsigned int lastev;
+};
+
+struct class_info {
+	int class;
+	char *class_name;
+};
+
+static const struct class_info clas_info[] =
+{					/* max. 5 chars. per name string */
+	{USB_CLASS_PER_INTERFACE,	">ifc"},
+	{USB_CLASS_AUDIO,		"audio"},
+	{USB_CLASS_COMM,		"comm."},
+	{USB_CLASS_HID,			"HID"},
+	{USB_CLASS_HUB,			"hub"},
+	{USB_CLASS_PHYSICAL,		"PID"},
+	{USB_CLASS_PRINTER,		"print"},
+	{USB_CLASS_MASS_STORAGE,	"stor."},
+	{USB_CLASS_DATA,		"data"},
+	{USB_CLASS_APP_SPEC,		"app."},
+	{USB_CLASS_VENDOR_SPEC,		"vend."},
+	{-1,				"unk."}		/* leave as last */
+};
+
+/*****************************************************************/
+
+void usbdevfs_conn_disc_event(void)
+{
+	wake_up(&deviceconndiscwq);
+	conndiscevcnt++;
+}
+
+static const char *class_decode(const int class)
+{
+	int ix;
+
+	for (ix = 0; clas_info[ix].class != -1; ix++)
+		if (clas_info[ix].class == class)
+			break;
+	return (clas_info[ix].class_name);
+}
+
+static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc)
+{
+	char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."};
+
+	if (start > end)
+		return start;
+	start += sprintf(start, format_endpt, desc->bEndpointAddress,
+			 (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL
+			 	? 'B' :			/* bidirectional */
+			 (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O',
+			 desc->bmAttributes, EndpointType[desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK],
+			 desc->wMaxPacketSize, desc->bInterval);
+	return start;
+}
+
+static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint)
+{
+	return usb_dump_endpoint_descriptor(start, end, endpoint);
+}
+
+static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno)
+{
+	struct usb_interface_descriptor *desc = &iface->altsetting[setno];
+
+	if (start > end)
+		return start;
+	start += sprintf(start, format_iface,
+			 desc->bInterfaceNumber,
+			 desc->bAlternateSetting,
+			 desc->bNumEndpoints,
+			 desc->bInterfaceClass,
+			 class_decode(desc->bInterfaceClass),
+			 desc->bInterfaceSubClass,
+			 desc->bInterfaceProtocol,
+			 iface->driver ? iface->driver->name : "(none)");
+	return start;
+}
+
+static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno)
+{
+	struct usb_interface_descriptor *desc = &iface->altsetting[setno];
+	int i;
+
+	start = usb_dump_interface_descriptor(start, end, iface, setno);
+	for (i = 0; i < desc->bNumEndpoints; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_endpoint(start, end, desc->endpoint + i);
+	}
+	return start;
+}
+
+/* TBD:
+ * 0. TBDs
+ * 1. marking active config and ifaces (code lists all, but should mark
+ *    which ones are active, if any)
+ * 2. add <halted> status to each endpoint line
+ */
+
+static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, format_config,
+			 active ? '*' : ' ',	/* mark active/actual/current cfg. */
+			 desc->bNumInterfaces,
+			 desc->bConfigurationValue,
+			 desc->bmAttributes,
+			 desc->MaxPower * 2);
+	return start;
+}
+
+static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, int active)
+{
+	int i, j;
+	struct usb_interface *interface;
+
+	if (start > end)
+		return start;
+	if (!config)		/* getting these some in 2.3.7; none in 2.3.6 */
+		return start + sprintf(start, "(null Cfg. desc.)\n");
+	start = usb_dump_config_descriptor(start, end, config, active);
+	for (i = 0; i < config->bNumInterfaces; i++) {
+		interface = config->interface + i;
+		if (!interface)
+			break;
+		for (j = 0; j < interface->num_altsetting; j++) {
+			if (start > end)
+				return start;
+			start = usb_dump_interface(start, end, interface, j);
+		}
+	}
+	return start;
+}
+
+/*
+ * Dump the different USB descriptors.
+ */
+static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc)
+{
+	if (start > end)
+		return start;
+	start += sprintf (start, format_device1,
+			  desc->bcdUSB >> 8, desc->bcdUSB & 0xff,
+			  desc->bDeviceClass,
+			  class_decode (desc->bDeviceClass),
+			  desc->bDeviceSubClass,
+			  desc->bDeviceProtocol,
+			  desc->bMaxPacketSize0,
+			  desc->bNumConfigurations);
+	if (start > end)
+		return start;
+	start += sprintf(start, format_device2,
+			 desc->idVendor, desc->idProduct,
+			 desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
+	return start;
+}
+
+/*
+ * Dump the different strings that this device holds.
+ */
+static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev)
+{
+	char *buf;
+
+	if (start > end)
+		return start;
+	buf = kmalloc(128, GFP_KERNEL);
+	if (!buf)
+		return start;
+	if (dev->descriptor.iManufacturer) {
+		if (usb_string(dev, dev->descriptor.iManufacturer, buf, 128) > 0)
+			start += sprintf(start, format_string_manufacturer, buf);
+	}				
+	if (start > end)
+		goto out;
+	if (dev->descriptor.iProduct) {
+		if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0)
+			start += sprintf(start, format_string_product, buf);
+	}
+	if (start > end)
+		goto out;
+#ifdef ALLOW_SERIAL_NUMBER
+	if (dev->descriptor.iSerialNumber) {
+		if (usb_string(dev, dev->descriptor.iSerialNumber, buf, 128) > 0)
+			start += sprintf(start, format_string_serialnumber, buf);
+	}
+#endif
+ out:
+	kfree(buf);
+	return start;
+}
+
+static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
+{
+	int i;
+
+	if (start > end)
+		return start;
+		
+	start = usb_dump_device_descriptor(start, end, &dev->descriptor);
+
+	if (start > end)
+		return start;
+	
+	start = usb_dump_device_strings (start, end, dev);
+	
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_config(start, end, dev->config + i,
+					(dev->config + i) == dev->actconfig); /* active ? */
+	}
+	return start;
+}
+
+
+#ifdef PROC_EXTRA /* TBD: may want to add this code later */
+
+static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc)
+{
+	int leng = USB_DT_HUB_NONVAR_SIZE;
+	unsigned char *ptr = (unsigned char *)desc;
+
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	while (leng && start <= end) {
+		start += sprintf(start, " %02x", *ptr);
+		ptr++; leng--;
+	}
+	*start++ = '\n';
+	return start;
+}
+
+static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+		start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]);
+	return start;
+}
+
+#endif /* PROC_EXTRA */
+
+/*****************************************************************/
+
+/* This is a recursive function. Parameters:
+ * buffer - the user-space buffer to write data into
+ * nbytes - the maximum number of bytes to write
+ * skip_bytes - the number of bytes to skip before writing anything
+ * file_offset - the offset into the devices file on completion
+ */
+static ssize_t usb_device_dump(char **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
+				struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
+{
+	int chix;
+	int ret, cnt = 0;
+	int parent_devnum = 0;
+	char *pages_start, *data_end;
+	unsigned int length;
+	ssize_t total_written = 0;
+	
+	/* don't bother with anything else if we're not writing any data */
+	if (*nbytes <= 0)
+		return 0;
+	
+	if (level > MAX_TOPO_LEVEL)
+		return total_written;
+	/* allocate 2^1 pages = 8K (on i386); should be more than enough for one device */
+        if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1)))
+                return -ENOMEM;
+		
+	if (usbdev->parent && usbdev->parent->devnum != -1)
+		parent_devnum = usbdev->parent->devnum;
+	/*
+	 * So the root hub's parent is 0 and any device that is
+	 * plugged into the root hub has a parent of 0.
+	 */
+	data_end = pages_start + sprintf(pages_start, format_topo, bus->busnum, level, parent_devnum, index, count,
+					usbdev->devnum, usbdev->slow ? "1.5" : "12 ", usbdev->maxchild);
+	/*
+	 * level = topology-tier level;
+	 * parent_devnum = parent device number;
+	 * index = parent's connector number;
+	 * count = device count at this level
+	 */
+	/* If this is the root hub, display the bandwidth information */
+	if (level == 0)
+		data_end += sprintf(data_end, format_bandwidth, bus->bandwidth_allocated,
+				FRAME_TIME_MAX_USECS_ALLOC,
+				(100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC,
+			         bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs);
+	
+	data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev);
+	
+	if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))
+		data_end += sprintf(data_end, "(truncated)\n");
+	
+	length = data_end - pages_start;
+	/* if we can start copying some data to the user */
+	if (length > *skip_bytes) {
+		length -= *skip_bytes;
+		if (length > *nbytes)
+			length = *nbytes;
+		if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
+			free_pages((unsigned long)pages_start, 1);
+			
+			if (total_written == 0)
+				return -EFAULT;
+			return total_written;
+		}
+		*nbytes -= length;
+		*file_offset += length;
+		total_written += length;
+		*buffer += length;
+		*skip_bytes = 0;
+	} else
+		*skip_bytes -= length;
+	
+	free_pages((unsigned long)pages_start, 1);
+	
+	/* Now look at all of this device's children. */
+	for (chix = 0; chix < usbdev->maxchild; chix++) {
+		if (usbdev->children[chix]) {
+			ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, usbdev->children[chix],
+					bus, level + 1, chix, ++cnt);
+			if (ret == -EFAULT)
+				return total_written;
+			total_written += ret;
+		}
+	}
+	return total_written;
+}
+
+static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
+{
+	struct list_head *buslist;
+	struct usb_bus *bus;
+	ssize_t ret, total_written = 0;
+	loff_t skip_bytes = *ppos;
+
+	if (*ppos < 0)
+		return -EINVAL;
+	if (nbytes <= 0)
+		return 0;
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+
+	/* enumerate busses */
+	for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) {
+		/* print devices for this bus */
+		bus = list_entry(buslist, struct usb_bus, bus_list);
+		/* recurse through all children of the root hub */
+		ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
+		if (ret < 0)
+			return ret;
+		total_written += ret;
+	}
+	return total_written;
+}
+
+/* Kernel lock for "lastev" protection */
+static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+	unsigned int mask = 0;
+
+	lock_kernel();
+	if (!st) {
+		st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
+		if (!st)
+			return POLLIN;
+		/*
+		 * need to prevent the module from being unloaded, since
+		 * proc_unregister does not call the release method and
+		 * we would have a memory leak
+		 */
+		st->lastev = conndiscevcnt;
+		file->private_data = st;
+		mask = POLLIN;
+	}
+	if (file->f_mode & FMODE_READ)
+                poll_wait(file, &deviceconndiscwq, wait);
+	if (st->lastev != conndiscevcnt)
+		mask |= POLLIN;
+	st->lastev = conndiscevcnt;
+	unlock_kernel();
+	return mask;
+}
+
+static int usb_device_open(struct inode *inode, struct file *file)
+{
+        file->private_data = NULL;
+        return 0;
+}
+
+static int usb_device_release(struct inode *inode, struct file *file)
+{
+	if (file->private_data) {
+		kfree(file->private_data);
+		file->private_data = NULL;
+	}
+
+        return 0;
+}
+
+static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+struct file_operations usbdevfs_devices_fops = {
+	llseek:		usb_device_lseek,
+	read:		usb_device_read,
+	poll:		usb_device_poll,
+	open:		usb_device_open,
+	release:	usb_device_release,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devio.c linux.18p17/drivers/usb/devio.c
--- linux.vanilla/drivers/usb/devio.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/devio.c	Fri Sep 15 12:49:48 2000
@@ -0,0 +1,1254 @@
+/*****************************************************************************/
+
+/*
+ *      devio.c  --  User space communication with USB devices.
+ *
+ *      Copyright (C) 1999-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
+ *
+ *  This file implements the usbdevfs/x/y files, where
+ *  x is the bus number and y the device number.
+ *
+ *  It allows user space programs/"drivers" to communicate directly
+ *  with USB devices without intervening kernel driver.
+ *
+ *  Revision history
+ *    22.12.1999   0.1   Initial release (split from proc_usb.c)
+ *    04.01.2000   0.2   Turned into its own filesystem
+ */
+
+/*****************************************************************************/
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+
+struct async {
+        struct list_head asynclist;
+        struct dev_state *ps;
+	struct task_struct *task;
+	unsigned int signr;
+	void *userbuffer;
+        void *userurb;
+        urb_t urb;
+};
+
+/*
+ * my own sync control and bulk methods. Here to experiment
+ * and because the kernel ones set the process to TASK_UNINTERRUPTIBLE.
+ */
+
+struct sync {
+	wait_queue_head_t wait;
+};
+
+static void sync_completed(purb_t urb)
+{
+        struct sync *s = (struct sync *)urb->context;
+ 
+        wake_up(&s->wait);	
+}
+
+static int do_sync(purb_t urb, int timeout)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	unsigned long tm;
+	signed long tmdiff;
+	struct sync s;
+	int ret;
+
+	tm = jiffies+timeout;
+	init_waitqueue_head(&s.wait);
+	add_wait_queue(&s.wait, &wait);
+	urb->context = &s;
+	urb->complete = sync_completed;
+	set_current_state(TASK_INTERRUPTIBLE);
+	if ((ret = usb_submit_urb(urb)))
+		goto out;
+	while (urb->status == -EINPROGRESS) {
+		tmdiff = tm - jiffies;
+		if (tmdiff <= 0) {
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			goto out;
+		}
+		schedule_timeout(tmdiff);
+	}
+	ret = urb->status;
+ out:
+	set_current_state(TASK_RUNNING);
+	usb_unlink_urb(urb);
+	remove_wait_queue(&s.wait, &wait);
+	return ret;
+}
+
+static int my_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+			      __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+	urb_t *urb;
+	int ret;
+
+	if (!(urb = usb_alloc_urb(0)))
+		return -ENOMEM;
+	if (!(urb->setup_packet = kmalloc(8, GFP_KERNEL))) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	urb->setup_packet[0] = requesttype;
+	urb->setup_packet[1] = request;
+	urb->setup_packet[2] = value;
+	urb->setup_packet[3] = value >> 8;
+	urb->setup_packet[4] = index;
+	urb->setup_packet[5] = index >> 8;
+	urb->setup_packet[6] = size;
+	urb->setup_packet[7] = size >> 8;
+	urb->dev = dev;
+        urb->pipe = pipe;
+        urb->transfer_buffer = data;
+        urb->transfer_buffer_length = size;
+	ret = do_sync(urb, timeout);
+	//if (ret >= 0)
+	//	ret = urb->status;
+	if (ret >= 0)
+		ret = urb->actual_length;
+	kfree(urb->setup_packet);
+	usb_free_urb(urb);
+	return ret;
+}
+
+static int my_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, 
+			   void *data, int len, int *actual_length, int timeout)
+{
+	urb_t *urb;
+	int ret;
+
+	if (!(urb = usb_alloc_urb(0)))
+		return -ENOMEM;
+        urb->dev = dev;
+        urb->pipe = pipe;
+        urb->transfer_buffer = data;
+        urb->transfer_buffer_length = len;
+	ret = do_sync(urb, timeout);
+	//if (ret >= 0)
+	//	ret = urb->status;
+	if (ret >= 0 && actual_length != NULL)
+		*actual_length = urb->actual_length;
+	usb_free_urb(urb);
+	return ret;
+}
+
+static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t *ppos)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	ssize_t ret = 0;
+	unsigned len;
+	loff_t pos;
+	int i;
+
+	pos = *ppos;
+	down_read(&ps->devsem);
+	if (!ps->dev) {
+		ret = -ENODEV;
+		goto err;
+	} else if (pos < 0) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (pos < sizeof(struct usb_device_descriptor)) {
+		len = sizeof(struct usb_device_descriptor) - pos;
+		if (len > nbytes)
+			len = nbytes;
+		if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) {
+			ret = -EFAULT;
+			goto err;
+		}
+
+		*ppos += len;
+		buf += len;
+		nbytes -= len;
+		ret += len;
+	}
+
+	pos = sizeof(struct usb_device_descriptor);
+	for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) {
+		struct usb_config_descriptor *config =
+			(struct usb_config_descriptor *)ps->dev->rawdescriptors[i];
+		unsigned int length = le16_to_cpu(config->wTotalLength);
+
+		if (*ppos < pos + length) {
+			len = length - (*ppos - pos);
+			if (len > nbytes)
+				len = nbytes;
+
+			if (copy_to_user(buf,
+			    ps->dev->rawdescriptors[i] + (*ppos - pos), len)) {
+				ret = -EFAULT;
+				goto err;
+			}
+
+			*ppos += len;
+			buf += len;
+			nbytes -= len;
+			ret += len;
+		}
+
+		pos += length;
+	}
+
+err:
+	up_read(&ps->devsem);
+	return ret;
+}
+
+extern inline unsigned int ld2(unsigned int x)
+{
+        unsigned int r = 0;
+        
+        if (x >= 0x10000) {
+                x >>= 16;
+                r += 16;
+        }
+        if (x >= 0x100) {
+                x >>= 8;
+                r += 8;
+        }
+        if (x >= 0x10) {
+                x >>= 4;
+                r += 4;
+        }
+        if (x >= 4) {
+                x >>= 2;
+                r += 2;
+        }
+        if (x >= 2)
+                r++;
+        return r;
+}
+
+/*
+ * async list handling
+ */
+
+static struct async *alloc_async(unsigned int numisoframes)
+{
+        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t);
+        struct async *as = kmalloc(assize, GFP_KERNEL);
+        if (!as)
+                return NULL;
+        memset(as, 0, assize);
+        as->urb.number_of_packets = numisoframes;
+        return as;
+}
+
+static void free_async(struct async *as)
+{
+        if (as->urb.transfer_buffer)
+                kfree(as->urb.transfer_buffer);
+        kfree(as);
+}
+
+extern __inline__ void async_newpending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_add_tail(&as->asynclist, &ps->async_pending);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+extern __inline__ void async_removepending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_del(&as->asynclist);
+        INIT_LIST_HEAD(&as->asynclist);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+extern __inline__ struct async *async_getcompleted(struct dev_state *ps)
+{
+        unsigned long flags;
+        struct async *as = NULL;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        if (!list_empty(&ps->async_completed)) {
+                as = list_entry(ps->async_completed.next, struct async, asynclist);
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return as;
+}
+
+extern __inline__ struct async *async_getpending(struct dev_state *ps, void *userurb)
+{
+        unsigned long flags;
+        struct async *as;
+        struct list_head *p;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        for (p = ps->async_pending.next; p != &ps->async_pending; ) {
+                as = list_entry(p, struct async, asynclist);
+                p = p->next;
+                if (as->userurb != userurb)
+                        continue;
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+                spin_unlock_irqrestore(&ps->lock, flags);
+                return as;
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return NULL;
+}
+
+static void async_completed(purb_t urb)
+{
+        struct async *as = (struct async *)urb->context;
+        struct dev_state *ps = as->ps;
+	struct siginfo sinfo;
+
+#if 1
+	printk(KERN_DEBUG "usbdevfs: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", 
+	       urb->status, urb->error_count, urb->actual_length, urb->pipe);
+#endif
+        spin_lock(&ps->lock);
+        list_del(&as->asynclist);
+        list_add_tail(&as->asynclist, &ps->async_completed);
+        spin_unlock(&ps->lock);
+        wake_up(&ps->wait);
+	if (as->signr) {
+		sinfo.si_signo = as->signr;
+		sinfo.si_errno = as->urb.status;
+		sinfo.si_code = SI_ASYNCIO;
+		sinfo.si_addr = as->userurb;
+		send_sig_info(as->signr, &sinfo, as->task);
+	}
+}
+
+static void destroy_all_async(struct dev_state *ps)
+{
+        struct async *as;
+        unsigned long flags;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        while (!list_empty(&ps->async_pending)) {
+                as = list_entry(ps->async_pending.next, struct async, asynclist);
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+                spin_unlock_irqrestore(&ps->lock, flags);
+                /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */
+                usb_unlink_urb(&as->urb);
+                spin_lock_irqsave(&ps->lock, flags);
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        while ((as = async_getcompleted(ps)))
+                free_async(as);
+}
+
+/*
+ * interface claiming
+ */
+
+static void *driver_probe(struct usb_device *dev, unsigned int intf)
+{
+	return NULL;
+}
+
+static void driver_disconnect(struct usb_device *dev, void *context)
+{
+	struct dev_state *ps = (struct dev_state *)context;
+
+	ps->ifclaimed = 0;
+}
+
+struct usb_driver usbdevfs_driver = {
+	name:		"usbdevfs",
+	probe:		driver_probe,
+	disconnect:	driver_disconnect,
+};
+
+static int claimintf(struct dev_state *ps, unsigned int intf)
+{
+	struct usb_device *dev = ps->dev;
+	struct usb_interface *iface;
+	int err;
+
+	if (intf >= 8*sizeof(ps->ifclaimed) || !dev || intf >= dev->actconfig->bNumInterfaces)
+		return -EINVAL;
+	/* already claimed */
+	if (test_bit(intf, &ps->ifclaimed))
+		return 0;
+	iface = &dev->actconfig->interface[intf];
+	err = -EBUSY;
+	lock_kernel();
+	if (!usb_interface_claimed(iface)) {
+		usb_driver_claim_interface(&usbdevfs_driver, iface, ps);
+		set_bit(intf, &ps->ifclaimed);
+		err = 0;
+	}
+	unlock_kernel();
+	return err;
+}
+
+static int releaseintf(struct dev_state *ps, unsigned int intf)
+{
+	struct usb_device *dev;
+	struct usb_interface *iface;
+	int err;
+
+	if (intf >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	err = -EINVAL;
+	lock_kernel();
+	dev = ps->dev;
+	if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) {
+		iface = &dev->actconfig->interface[intf];
+		usb_driver_release_interface(&usbdevfs_driver, iface);
+		err = 0;
+	}
+	unlock_kernel();
+	return err;
+}
+
+static int checkintf(struct dev_state *ps, unsigned int intf)
+{
+	if (intf >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	if (test_bit(intf, &ps->ifclaimed))
+		return 0;
+	/* if not yet claimed, claim it for the driver */
+	printk(KERN_WARNING "usbdevfs: process %d (%s) did not claim interface %u before use\n",
+	       current->pid, current->comm, intf);
+	return claimintf(ps, intf);
+}
+
+static int findintfep(struct usb_device *dev, unsigned int ep)
+{
+	unsigned int i, j, e;
+        struct usb_interface *iface;
+	struct usb_interface_descriptor *alts;
+	struct usb_endpoint_descriptor *endpt;
+
+	if (ep & ~(USB_DIR_IN|0xf))
+		return -EINVAL;
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		iface = &dev->actconfig->interface[i];
+		for (j = 0; j < iface->num_altsetting; j++) {
+                        alts = &iface->altsetting[j];
+			for (e = 0; e < alts->bNumEndpoints; e++) {
+				endpt = &alts->endpoint[e];
+				if (endpt->bEndpointAddress == ep)
+					return i;
+			}
+		}
+	}
+	return -ENOENT; 
+}
+
+static int findintfif(struct usb_device *dev, unsigned int ifn)
+{
+	unsigned int i, j;
+        struct usb_interface *iface;
+	struct usb_interface_descriptor *alts;
+
+	if (ifn & ~0xff)
+		return -EINVAL;
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		iface = &dev->actconfig->interface[i];
+		for (j = 0; j < iface->num_altsetting; j++) {
+                        alts = &iface->altsetting[j];
+			if (alts->bInterfaceNumber == ifn)
+				return i;
+		}
+	}
+	return -ENOENT; 
+}
+
+extern struct list_head usb_driver_list;
+
+#if 0
+static int finddriver(struct usb_driver **driver, char *name)
+{
+	struct list_head *tmp;
+
+	tmp = usb_driver_list.next;
+	while (tmp != &usb_driver_list) {
+		struct usb_driver *d = list_entry(tmp, struct usb_driver,
+							driver_list);
+
+		if (!strcmp(d->name, name)) {
+			*driver = d;
+			return 0;
+		}
+
+		tmp = tmp->next;
+	}
+
+	return -EINVAL;
+}
+#endif
+
+/*
+ * file operations
+ */
+static int usbdev_open(struct inode *inode, struct file *file)
+{
+	struct usb_device *dev;
+	struct dev_state *ps;
+	int ret;
+
+	/* 
+	 * no locking necessary here, as both sys_open (actually filp_open)
+	 * and the hub thread have the kernel lock
+	 * (still acquire the kernel lock for safety)
+	 */
+	lock_kernel();
+	ret = -ENOENT;
+	if (ITYPE(inode->i_ino) != IDEVICE)
+		goto out;
+	dev = inode->u.usbdev_i.p.dev;
+	if (!dev)
+		goto out;
+	ret = -ENOMEM;
+	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
+		goto out;
+	ret = 0;
+	ps->dev = dev;
+	ps->file = file;
+	spin_lock_init(&ps->lock);
+	INIT_LIST_HEAD(&ps->async_pending);
+	INIT_LIST_HEAD(&ps->async_completed);
+	init_waitqueue_head(&ps->wait);
+	init_rwsem(&ps->devsem);
+	ps->discsignr = 0;
+	ps->disctask = current;
+	ps->disccontext = NULL;
+	ps->ifclaimed = 0;
+	wmb();
+	list_add_tail(&ps->list, &dev->filelist);
+	file->private_data = ps;
+ out:
+	unlock_kernel();
+        return ret;
+}
+
+static int usbdev_release(struct inode *inode, struct file *file)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	unsigned int i;
+
+	lock_kernel();
+	list_del(&ps->list);
+	INIT_LIST_HEAD(&ps->list);
+	if (ps->dev) {
+		for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++)
+			if (test_bit(i, &ps->ifclaimed))
+				releaseintf(ps, i);
+	}
+	unlock_kernel();
+	destroy_all_async(ps);
+	kfree(ps);
+        return 0;
+}
+
+static int proc_control(struct dev_state *ps, void *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_ctrltransfer ctrl;
+	unsigned int tmo;
+	unsigned char *tbuf;
+	int i, ret;
+
+	if (copy_from_user(&ctrl, (void *)arg, sizeof(ctrl)))
+		return -EFAULT;
+	switch (ctrl.requesttype & 0x1f) {
+	case USB_RECIP_ENDPOINT:
+		if ((ret = findintfep(ps->dev, ctrl.index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+
+	case USB_RECIP_INTERFACE:
+		if ((ret = findintfif(ps->dev, ctrl.index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+	}
+	if (ctrl.length > PAGE_SIZE)
+		return -EINVAL;
+	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = (ctrl.timeout * HZ + 999) / 1000;
+	if (ctrl.requesttype & 0x80) {
+		if (ctrl.length && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.length)) {
+			free_page((unsigned long)tbuf);
+			return -EINVAL;
+		}
+		i = my_usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+				       ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
+		if ((i > 0) && ctrl.length) {
+			if (copy_to_user(ctrl.data, tbuf, ctrl.length))
+				return -EFAULT;
+		}
+	} else {
+		if (ctrl.length) {
+			if (copy_from_user(tbuf, ctrl.data, ctrl.length))
+				return -EFAULT;
+		}
+		i = my_usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+				       ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
+	}
+	free_page((unsigned long)tbuf);
+	if (i<0) {
+		printk(KERN_DEBUG "usbdevfs: USBDEVFS_CONTROL failed dev %d rqt %u rq %u len %u ret %d\n", 
+		       dev->devnum, ctrl.requesttype, ctrl.request, ctrl.length, i);
+	}
+	return i;
+}
+
+static int proc_bulk(struct dev_state *ps, void *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_bulktransfer bulk;
+	unsigned int tmo, len1, pipe;
+	int len2;
+	unsigned char *tbuf;
+	int i, ret;
+
+	if (copy_from_user(&bulk, (void *)arg, sizeof(bulk)))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, bulk.ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (bulk.ep & USB_DIR_IN)
+		pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
+	else
+		pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
+	if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
+		return -EINVAL;
+	len1 = bulk.len;
+	if (len1 > PAGE_SIZE)
+		len1 = PAGE_SIZE;
+	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = (bulk.timeout * HZ + 999) / 1000;
+	if (bulk.ep & 0x80) {
+		if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
+			free_page((unsigned long)tbuf);
+			return -EINVAL;
+		}
+		i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		if (!i && len2) {
+			if (copy_to_user(bulk.data, tbuf, len2))
+				return -EFAULT;
+		}
+	} else {
+		if (len1) {
+			if (copy_from_user(tbuf, bulk.data, len1))
+				return -EFAULT;
+		}
+		i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+	}
+	free_page((unsigned long)tbuf);
+	if (i < 0) {
+		printk(KERN_WARNING "usbdevfs: USBDEVFS_BULK failed dev %d ep 0x%x len %u ret %d\n", 
+		       dev->devnum, bulk.ep, bulk.len, i);
+		return i;
+	}
+	return len2;
+}
+
+static int proc_resetep(struct dev_state *ps, void *arg)
+{
+	unsigned int ep;
+	int ret;
+
+	if (get_user(ep, (unsigned int *)arg))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
+	return 0;
+}
+
+static int proc_clearhalt(struct dev_state *ps, void *arg)
+{
+	unsigned int ep;
+	int pipe;
+	int ret;
+
+	if (get_user(ep, (unsigned int *)arg))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (ep & USB_DIR_IN)
+		pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
+	else
+		pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f);
+
+	return usb_clear_halt(ps->dev, pipe);
+}
+
+static int proc_getdriver(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_getdriver gd;
+	struct usb_interface *interface;
+	int ret;
+
+	if (copy_from_user(&gd, arg, sizeof(gd)))
+		return -EFAULT;
+	if ((ret = findintfif(ps->dev, gd.interface)) < 0)
+		return ret;
+	interface = usb_ifnum_to_if(ps->dev, gd.interface);
+	if (!interface)
+		return -EINVAL;
+	if (!interface->driver)
+		return -ENODATA;
+	strcpy(gd.driver, interface->driver->name);
+	if (copy_to_user(arg, &gd, sizeof(gd)))
+		return -EFAULT;
+	return 0;
+}
+
+static int proc_connectinfo(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_connectinfo ci;
+
+	ci.devnum = ps->dev->devnum;
+	ci.slow = ps->dev->slow;
+	if (copy_to_user(arg, &ci, sizeof(ci)))
+		return -EFAULT;
+	return 0;
+}
+
+static int proc_resetdevice(struct dev_state *ps)
+{
+	int i, ret;
+
+	ret = usb_reset_device(ps->dev);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *intf = &ps->dev->actconfig->interface[i];
+
+		/* Don't simulate interfaces we've claimed */
+		if (test_bit(i, &ps->ifclaimed))
+			continue;
+
+		if (intf->driver) {
+			down(&intf->driver->serialize);
+			intf->driver->disconnect(ps->dev, intf->private_data);
+			intf->driver->probe(ps->dev, i);
+			up(&intf->driver->serialize);
+		}
+	}
+
+	return 0;
+}
+
+static int proc_setintf(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_setinterface setintf;
+	struct usb_interface *interface;
+	int ret;
+
+	if (copy_from_user(&setintf, arg, sizeof(setintf)))
+		return -EFAULT;
+	if ((ret = findintfif(ps->dev, setintf.interface)) < 0)
+		return ret;
+	interface = usb_ifnum_to_if(ps->dev, setintf.interface);
+	if (!interface)
+		return -EINVAL;
+	if (interface->driver) {
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+	}
+	if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting))
+		return -EINVAL;
+	return 0;
+}
+
+static int proc_setconfig(struct dev_state *ps, void *arg)
+{
+	unsigned int u;
+
+	if (get_user(u, (unsigned int *)arg))
+		return -EFAULT;
+	if (usb_set_configuration(ps->dev, u) < 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int proc_submiturb(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_urb uurb;
+	struct usbdevfs_iso_packet_desc *isopkt = NULL;
+	struct async *as;
+	unsigned int u, totlen, isofrmlen;
+	int ret;
+
+	if (copy_from_user(&uurb, arg, sizeof(uurb)))
+		return -EFAULT;
+	if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD))
+		return -EINVAL;
+	if (!uurb.buffer)
+		return -EINVAL;
+	if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX))
+		return -EINVAL;
+	if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	switch(uurb.type) {
+	case USBDEVFS_URB_TYPE_BULK:
+		uurb.number_of_packets = 0;
+		if (uurb.buffer_length > 16384)
+			return -EINVAL;
+		if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length))
+			return -EFAULT;
+		break;
+
+	case USBDEVFS_URB_TYPE_ISO:
+		/* arbitrary limit */
+		if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128)
+			return -EINVAL;
+		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets;
+		if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(isopkt, &((struct usbdevfs_urb *)arg)->iso_frame_desc, isofrmlen)) {
+			kfree(isopkt);
+			return -EFAULT;
+		}
+		for (totlen = u = 0; u < uurb.number_of_packets; u++) {
+			if (isopkt[u].length > 1023) {
+				kfree(isopkt);
+				return -EINVAL;
+			}
+			totlen += isopkt[u].length;
+		}
+		if (totlen > 32768) {
+			kfree(isopkt);
+			return -ENOMEM;
+		}
+		uurb.buffer_length = totlen;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (!(as = alloc_async(uurb.number_of_packets))) {
+		if (isopkt)
+			kfree(isopkt);
+		return -ENOMEM;
+	}
+	if (!(as->urb.transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) {
+		if (isopkt)
+			kfree(isopkt);
+		free_async(as);
+		return -ENOMEM;
+	}
+        as->urb.next = NULL;
+        as->urb.dev = ps->dev;
+        as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN);
+        as->urb.transfer_flags = uurb.flags;
+	as->urb.transfer_buffer_length = uurb.buffer_length;
+	as->urb.start_frame = uurb.start_frame;
+	as->urb.number_of_packets = uurb.number_of_packets;
+        as->urb.context = as;
+        as->urb.complete = async_completed;
+	for (totlen = u = 0; u < uurb.number_of_packets; u++) {
+		as->urb.iso_frame_desc[u].offset = totlen;
+		as->urb.iso_frame_desc[u].length = isopkt[u].length;
+		totlen += isopkt[u].length;
+	}
+	if (isopkt)
+		kfree(isopkt);
+	as->ps = ps;
+        as->userurb = arg;
+	if (uurb.endpoint & USB_DIR_IN)
+		as->userbuffer = uurb.buffer;
+	else
+		as->userbuffer = NULL;
+	as->signr = uurb.signr;
+	as->task = current;
+	if (!(uurb.endpoint & USB_DIR_IN)) {
+		if (copy_from_user(as->urb.transfer_buffer, uurb.buffer, as->urb.transfer_buffer_length)) {
+			free_async(as);
+			return -EFAULT;
+		}
+	}
+        async_newpending(as);
+        if ((ret = usb_submit_urb(&as->urb))) {
+		printk(KERN_DEBUG "usbdevfs: usb_submit_urb returned %d\n", ret);
+                async_removepending(as);
+                free_async(as);
+                return ret;
+        }
+        return 0;
+}
+
+static int proc_unlinkurb(struct dev_state *ps, void *arg)
+{
+	struct async *as;
+
+	as = async_getpending(ps, arg);
+	if (!as)
+		return -EINVAL;
+	usb_unlink_urb(&as->urb);
+	return 0;
+}
+
+static int processcompl(struct async *as)
+{
+	unsigned int i;
+
+	if (as->userbuffer)
+		if (copy_to_user(as->userbuffer, as->urb.transfer_buffer, as->urb.transfer_buffer_length))
+			return -EFAULT;
+	if (put_user(as->urb.status,
+		     &((struct usbdevfs_urb *)as->userurb)->status))
+		return -EFAULT;
+	if (put_user(as->urb.actual_length,
+		     &((struct usbdevfs_urb *)as->userurb)->actual_length))
+		return -EFAULT;
+	if (put_user(as->urb.error_count,
+		     &((struct usbdevfs_urb *)as->userurb)->error_count))
+		return -EFAULT;
+	if (!(usb_pipeisoc(as->urb.pipe)))
+		return 0;
+	for (i = 0; i < as->urb.number_of_packets; i++) {
+		if (put_user(as->urb.iso_frame_desc[i].actual_length, 
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length))
+			return -EFAULT;
+		if (put_user(as->urb.iso_frame_desc[i].status, 
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static int proc_reapurb(struct dev_state *ps, void *arg)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	struct async *as = NULL;
+	void *addr;
+	int ret;
+
+	add_wait_queue(&ps->wait, &wait);
+	while (ps->dev) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if ((as = async_getcompleted(ps)))
+			break;
+		if (signal_pending(current))
+			break;
+		up_read(&ps->devsem);
+		schedule();
+		down_read(&ps->devsem);
+	}
+	remove_wait_queue(&ps->wait, &wait);
+	set_current_state(TASK_RUNNING);
+	if (as) {
+		ret = processcompl(as);
+		addr = as->userurb;
+		free_async(as);
+		if (ret)
+			return ret;
+		if (put_user(addr, (void **)arg))
+			return -EFAULT;
+		return 0;
+	}
+	if (signal_pending(current))
+		return -EINTR;
+	return -EIO;
+}
+
+static int proc_reapurbnonblock(struct dev_state *ps, void *arg)
+{
+	struct async *as;
+	void *addr;
+	int ret;
+
+	if (!(as = async_getcompleted(ps)))
+		return -EAGAIN;
+	ret = processcompl(as);
+	addr = as->userurb;
+	free_async(as);
+	if (ret)
+		return ret;
+	if (put_user(addr, (void **)arg))
+		return -EFAULT;
+	return 0;
+}
+
+static int proc_disconnectsignal(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_disconnectsignal ds;
+
+	if (copy_from_user(&ds, arg, sizeof(ds)))
+		return -EFAULT;
+	if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
+		return -EINVAL;
+	ps->discsignr = ds.signr;
+	ps->disccontext = ds.context;
+	return 0;
+}
+
+static int proc_claiminterface(struct dev_state *ps, void *arg)
+{
+	unsigned int intf;
+	int ret;
+
+	if (get_user(intf, (unsigned int *)arg))
+		return -EFAULT;
+	if ((ret = findintfif(ps->dev, intf)) < 0)
+		return ret;
+	return claimintf(ps, ret);
+}
+
+static int proc_releaseinterface(struct dev_state *ps, void *arg)
+{
+	unsigned int intf;
+	int ret;
+
+	if (get_user(intf, (unsigned int *)arg))
+		return -EFAULT;
+	if ((ret = findintfif(ps->dev, intf)) < 0)
+		return ret;
+	return releaseintf(ps, intf);
+}
+
+static int proc_ioctl (struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_ioctl	ctrl;
+	int			size;
+	void			*buf = 0;
+	int			retval = 0;
+
+	/* get input parameters and alloc buffer */
+	if (copy_from_user (&ctrl, (void *) arg, sizeof (ctrl)))
+		return -EFAULT;
+	if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
+		if ((buf = kmalloc (size, GFP_KERNEL)) == 0)
+			return -ENOMEM;
+		if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0
+				&& copy_from_user (buf, ctrl.data, size) != 0) {
+			kfree (buf);
+			return -EFAULT;
+		} else
+			memset (arg, 0, size);
+	}
+
+	/* ioctl to device */
+	if (ctrl.ifno < 0) {
+		switch (ctrl.ioctl_code) {
+		/* access/release token for issuing control messages
+		 * ask a particular driver to bind/unbind, ... etc
+		 */
+		}
+		retval = -ENOSYS;
+
+	/* ioctl to the driver which has claimed a given interface */
+	} else {
+		struct usb_interface	*ifp = 0;
+		if (!ps->dev)
+			retval = -ENODEV;
+		else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces)
+			retval = -EINVAL;
+		else {
+			if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
+				retval = -EINVAL;
+			else if (ifp->driver == 0 || ifp->driver->ioctl == 0)
+				retval = -ENOSYS;
+		}
+		if (retval == 0)
+			/* ifno might usefully be passed ... */
+			retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf);
+			/* size = min (size, retval)? */
+	}
+
+	/* cleanup and return */
+	if (retval >= 0
+			&& (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
+			&& size > 0
+			&& copy_to_user (ctrl.data, buf, size) != 0)
+		retval = -EFAULT;
+	if (buf != 0)
+		kfree (buf);
+	return retval;
+}
+
+static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	int ret = -ENOIOCTLCMD;
+
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EPERM;
+	down_read(&ps->devsem);
+	if (!ps->dev) {
+		up_read(&ps->devsem);
+		return -ENODEV;
+	}
+	switch (cmd) {
+	case USBDEVFS_CONTROL:
+		ret = proc_control(ps, (void *)arg);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_BULK:
+		ret = proc_bulk(ps, (void *)arg);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_RESETEP:
+		ret = proc_resetep(ps, (void *)arg);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_RESET:
+		ret = proc_resetdevice(ps);
+		break;
+
+	case USBDEVFS_CLEAR_HALT:
+		ret = proc_clearhalt(ps, (void *)arg);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_GETDRIVER:
+		ret = proc_getdriver(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_CONNECTINFO:
+		ret = proc_connectinfo(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SETINTERFACE:
+		ret = proc_setintf(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SETCONFIGURATION:
+		ret = proc_setconfig(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SUBMITURB:
+		ret = proc_submiturb(ps, (void *)arg);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_DISCARDURB:
+		ret = proc_unlinkurb(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_REAPURB:
+		ret = proc_reapurb(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_REAPURBNDELAY:
+		ret = proc_reapurbnonblock(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_DISCSIGNAL:
+		ret = proc_disconnectsignal(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_CLAIMINTERFACE:
+		ret = proc_claiminterface(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_RELEASEINTERFACE:
+		ret = proc_releaseinterface(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_IOCTL:
+		ret = proc_ioctl(ps, (void *) arg);
+		break;
+	}
+	up_read(&ps->devsem);
+	if (ret >= 0)
+		inode->i_atime = CURRENT_TIME;
+	return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+        unsigned int mask = 0;
+
+	poll_wait(file, &ps->wait, wait);
+	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
+		mask |= POLLOUT | POLLWRNORM;
+	if (!ps->dev)
+		mask |= POLLERR | POLLHUP;
+	return mask;
+}
+
+struct file_operations usbdevfs_device_file_operations = {
+	llseek:		usbdev_lseek,
+	read:		usbdev_read,
+	poll:		usbdev_poll,
+	ioctl:		usbdev_ioctl,
+	open:		usbdev_open,
+	release:	usbdev_release,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/drivers.c linux.18p17/drivers/usb/drivers.c
--- linux.vanilla/drivers/usb/drivers.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/drivers.c	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,119 @@
+/*
+ * drivers.c
+ * (C) Copyright 1999 Randy Dunlap.
+ * (C) Copyright 1999, 2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
+ * (C) Copyright 1999 Deti Fliegl (new USB architecture)
+ *
+ * $id$
+ *
+ * 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
+ *
+ *************************************************************
+ *
+ * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Converted the whole proc stuff to real
+ *   read methods. Now not the whole device list needs to fit
+ *   into one page, only the device list for one bus.
+ *   Added a poll method to /proc/bus/usb/devices, to wake
+ *   up an eventual usbd
+ * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Turned into its own filesystem
+ *
+ * $Id: drivers.c,v 1.3 2000/01/11 13:58:24 tom Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+/*****************************************************************/
+
+/*
+ * Dump usb_driver_list.
+ *
+ * We now walk the list of registered USB drivers.
+ */
+static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
+{
+	struct list_head *tmp = usb_driver_list.next;
+	char *page, *start, *end;
+	ssize_t ret = 0;
+	unsigned int pos, len;
+
+	if (*ppos < 0)
+		return -EINVAL;
+	if (nbytes <= 0)
+		return 0;
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+                return -ENOMEM;
+	start = page;
+	end = page + (PAGE_SIZE - 100);
+	pos = *ppos;
+	for (; tmp != &usb_driver_list; tmp = tmp->next) {
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+		int minor = driver->fops ? driver->minor : -1;
+		if (minor == -1)
+			start += sprintf (start, "         %s\n", driver->name);
+		else
+			start += sprintf (start, "%3d-%3d: %s\n", minor, minor + 15, driver->name);
+		if (start > end) {
+			start += sprintf(start, "(truncated)\n");
+			break;
+		}
+	}
+	if (start == page)
+		start += sprintf(start, "(none)\n");
+	len = start - page;
+	if (len > pos) {
+		len -= pos;
+		if (len > nbytes)
+			len = nbytes;
+		ret = len;
+		if (copy_to_user(buf, page + pos, len))
+			ret = -EFAULT;
+		else
+			*ppos += len;
+	}
+	free_page((unsigned long)page);
+	return ret;
+}
+
+static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+struct file_operations usbdevfs_drivers_fops = {
+	llseek:		usb_driver_lseek,
+	read:		usb_driver_read,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dsbr100.c linux.18p17/drivers/usb/dsbr100.c
--- linux.vanilla/drivers/usb/dsbr100.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/dsbr100.c	Sat Sep 23 13:23:42 2000
@@ -0,0 +1,359 @@
+/* A driver for the D-Link DSB-R100 USB radio.  The R100 plugs
+ into both the USB and an analog audio input, so this thing
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well.  So, scanning will only find
+ stereo stations.  Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning.  My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner <msdemlei@tucana.harvard.edu>
+
+ 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
+
+ History:
+
+ Version 0.24:
+ 	Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
+	right.  Some minor cleanup, improved standalone compilation
+
+ Version 0.23:
+ 	Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
+
+ Version 0.22:
+ 	Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, 
+	thanks to Mike Cox for pointing the problem out.
+
+ Version 0.21:
+ 	Markus: Minor cleanup, warnings if something goes wrong, lame attempt
+	to adhere to Documentation/CodingStyle
+
+ Version 0.2: 
+ 	Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+	Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+#define TB_LEN 16
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
+	void *arg);
+static int usb_dsbr100_open(struct video_device *dev, int flags);
+static void usb_dsbr100_close(struct video_device *dev);
+
+
+typedef struct
+{	struct urb readurb,writeurb;
+	struct usb_device *dev;
+	unsigned char transfer_buffer[TB_LEN];
+	int curfreq;
+	int stereo;
+	int ifnum;
+} usb_dsbr100;
+
+
+static struct video_device usb_dsbr100_radio=
+{
+	"D-Link DSB R-100 USB radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_AZTECH,
+	usb_dsbr100_open,
+	usb_dsbr100_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	NULL,	/* No poll */
+	usb_dsbr100_ioctl,
+	NULL,
+	NULL
+};
+
+static int users = 0;
+
+static struct usb_driver usb_dsbr100_driver = {
+	name:		"dsbr100",
+	probe:		usb_dsbr100_probe,
+	disconnect:	usb_dsbr100_disconnect,
+	driver_list: {NULL,NULL},
+	fops: NULL,
+	minor: 0
+};
+
+
+static int dsbr100_start(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+		return -1;
+	return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_stop(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+		return -1;
+	return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
+{
+	freq = (freq/16*80)/1000+856;
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x01, 0xC0, (freq>>8)&0x00ff, freq&0xff, 
+		radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+		radio->stereo = -1;
+		return -1;
+	}
+	radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+	return (radio->transfer_buffer)[0];
+}
+
+static void dsbr100_getstat(usb_dsbr100 *radio)
+{
+	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+		0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+		radio->stereo = -1;
+	else
+		radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	usb_dsbr100 *radio;
+
+	if (dev->descriptor.idVendor!=DSB100_VENDOR ||
+			dev->descriptor.idProduct!=DSB100_PRODUCT)
+		return NULL;
+	if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL)))
+		return NULL;
+	usb_dsbr100_radio.priv = radio;
+	radio->dev = dev;
+	radio->ifnum = ifnum;
+	radio->curfreq = 1454000;
+	return (void*)radio;
+}
+
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr)
+{
+	usb_dsbr100 *radio=ptr;
+
+	if (users)
+		return;
+	kfree(radio);
+	usb_dsbr100_radio.priv = NULL;
+}
+
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
+	void *arg)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (!radio)
+		return -EINVAL;
+
+	switch(cmd)
+	{
+		case VIDIOCGCAP: {
+			struct video_capability v;
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			strcpy(v.name, "D-Link R-100 USB Radio");
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER: {
+			struct video_tuner v;
+			dsbr100_getstat(radio);
+			if(copy_from_user(&v, arg,sizeof(v))!=0) 
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v.rangelow = 87*16000;
+			v.rangehigh = 108*16000;
+			v.flags = VIDEO_TUNER_LOW;
+			v.mode = VIDEO_MODE_AUTO;
+			v.signal = radio->stereo*0x7000;
+				/* Don't know how to get signal strength */
+			v.flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
+			strcpy(v.name, "DSB R-100");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER: {
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ: 
+			if (radio->curfreq==-1)
+				return -EINVAL;
+			if(copy_to_user(arg, &(radio->curfreq), 
+				sizeof(radio->curfreq)))
+				return -EFAULT;
+			return 0;
+
+		case VIDIOCSFREQ:
+			if(copy_from_user(&(radio->curfreq), arg,
+				sizeof(radio->curfreq)))
+				return -EFAULT;
+			if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+				warn("set frequency failed");
+			return 0;
+
+		case VIDIOCGAUDIO: {
+			struct video_audio v;
+			memset(&v,0, sizeof(v));
+			v.flags|=VIDEO_AUDIO_MUTABLE;
+			v.mode=VIDEO_SOUND_STEREO;
+			v.volume=1;
+			v.step=1;
+			strcpy(v.name, "Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO: {
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v))) 
+				return -EFAULT;	
+			if(v.audio) 
+				return -EINVAL;
+
+			if(v.flags&VIDEO_AUDIO_MUTE) {
+				if (dsbr100_stop(radio)==-1)
+					warn("radio did not respond properly");
+			}
+			else
+				if (dsbr100_start(radio)==-1)
+					warn("radio did not respond properly");
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+
+static int usb_dsbr100_open(struct video_device *dev, int flags)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (! radio) {
+		warn("radio not initialised");
+		return -EAGAIN;
+	}
+	if(users)
+	{
+		warn("radio in use");
+		return -EBUSY;
+	}
+	users++;
+	MOD_INC_USE_COUNT;
+	if (dsbr100_start(radio)<0)
+		warn("radio did not start up properly");
+	dsbr100_setfreq(radio,radio->curfreq);
+	return 0;
+}
+
+static void usb_dsbr100_close(struct video_device *dev)
+{
+	usb_dsbr100 *radio=dev->priv;
+
+	if (!radio)
+		return;
+	users--;
+	dsbr100_stop(radio);
+	MOD_DEC_USE_COUNT;
+}
+
+static int __init dsbr100_init(void)
+{
+	usb_dsbr100_radio.priv = NULL;
+	usb_register(&usb_dsbr100_driver);
+	if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {	
+		warn("couldn't register video device");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void __exit dsbr100_exit(void)
+{
+	usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+	if (radio)
+		dsbr100_stop(radio);
+	video_unregister_device(&usb_dsbr100_radio);
+	usb_deregister(&usb_dsbr100_driver);
+}
+
+module_init (dsbr100_init);
+module_exit (dsbr100_exit);
+
+MODULE_AUTHOR("Markus Demleitner <msdemlei@tucana.harvard.edu>");
+MODULE_DESCRIPTION("D-Link DSB-R100 USB radio driver");
+
+/*
+vi: ts=8
+Sigh.  Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/evdev.c linux.18p17/drivers/usb/evdev.c
--- linux.vanilla/drivers/usb/evdev.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/evdev.c	Fri Sep 22 14:56:09 2000
@@ -0,0 +1,356 @@
+/*
+ * $Id: evdev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  Event char devices, giving access to raw input device events.
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#define EVDEV_MINOR_BASE	64
+#define EVDEV_MINORS		32
+#define EVDEV_BUFFER_SIZE	64
+
+#include <linux/poll.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/smp_lock.h>
+#include <linux/kcomp.h>
+
+struct evdev {
+	int exist;
+	int open;
+	int minor;
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	devfs_handle_t devfs;
+	struct evdev_list *list;
+};
+
+struct evdev_list {
+	struct input_event buffer[EVDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	struct fasync_struct *fasync;
+	struct evdev *evdev;
+	struct evdev_list *next;
+};
+
+static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ };
+
+static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct evdev *evdev = handle->private;
+	struct evdev_list *list = evdev->list;
+
+	while (list) {
+
+		get_fast_time(&list->buffer[list->head].time);
+		list->buffer[list->head].type = type;
+		list->buffer[list->head].code = code;
+		list->buffer[list->head].value = value;
+		list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+		
+		kill_fasync(list->fasync, SIGIO);
+
+		list = list->next;
+	}
+
+	wake_up_interruptible(&evdev->wait);
+}
+
+static int evdev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct evdev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int evdev_release(struct inode * inode, struct file * file)
+{
+	struct evdev_list *list = file->private_data;
+	struct evdev_list **listptr;
+
+	lock_kernel();
+	listptr = &list->evdev->list;
+	evdev_fasync(-1, file, 0);
+
+	while (*listptr && (*listptr != list))
+		listptr = &((*listptr)->next);
+	*listptr = (*listptr)->next;
+
+	if (!--list->evdev->open) {
+		if (list->evdev->exist) {
+			input_close_device(&list->evdev->handle);	
+		} else {
+			input_unregister_minor(list->evdev->devfs);
+			evdev_table[list->evdev->minor] = NULL;
+			kfree(list->evdev);
+		}
+	}
+
+	kfree(list);
+	unlock_kernel();
+
+	return 0;
+}
+
+static int evdev_open(struct inode * inode, struct file * file)
+{
+	struct evdev_list *list;
+	int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE;
+
+	if (i > EVDEV_MINORS || !evdev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct evdev_list));
+
+	list->evdev = evdev_table[i];
+	list->next = evdev_table[i]->list;
+	evdev_table[i]->list = list;
+
+	file->private_data = list;
+
+	if (!list->evdev->open++)
+		if (list->evdev->exist)
+			input_open_device(&list->evdev->handle);	
+
+	return 0;
+}
+
+static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct evdev_list *list = file->private_data;
+	int retval = 0;
+
+	if (list->head == list->tail) {
+
+		add_wait_queue(&list->evdev->wait, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+
+		while (list->head == list->tail) {
+
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+
+			schedule();
+		}
+
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&list->evdev->wait, &wait);
+	}
+
+	if (retval)
+		return retval;
+
+	while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
+		if (copy_to_user(buffer + retval, list->buffer + list->tail,
+			 sizeof(struct input_event))) return -EFAULT;
+		list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+		retval += sizeof(struct input_event);
+	}
+
+	return retval;	
+}
+
+/* No kernel lock - fine */
+static unsigned int evdev_poll(struct file *file, poll_table *wait)
+{
+	struct evdev_list *list = file->private_data;
+	poll_wait(file, &list->evdev->wait, wait);
+	if (list->head != list->tail)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct evdev_list *list = file->private_data;
+	struct evdev *evdev = list->evdev;
+	struct input_dev *dev = evdev->handle.dev;
+	int retval;
+
+	switch (cmd) {
+
+		case EVIOCGVERSION:
+			return put_user(EV_VERSION, (int *) arg);
+
+		case EVIOCGID:
+			if ((retval = put_user(dev->idbus,     ((short *) arg) + 0))) return retval;
+			if ((retval = put_user(dev->idvendor,  ((short *) arg) + 1))) return retval;
+			if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval;
+			if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval;
+			return 0;
+
+		default:
+
+			if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+				return -EINVAL;
+
+			if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+				long *bits;
+				int len;
+
+				switch (_IOC_NR(cmd) & EV_MAX) {
+					case      0: bits = dev->evbit;  len = EV_MAX;  break;
+					case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+					case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+					case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+					case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+					case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+					default: return -EINVAL;
+				}
+				len = NBITS(len) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) {
+					printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n",
+						len, _IOC_SIZE(cmd));
+					len = _IOC_SIZE(cmd);
+				}
+				return copy_to_user((char *) arg, bits, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
+				int len;
+				if (!dev->name) return 0;
+				len = strlen(dev->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len;
+			}
+
+			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+				int t = _IOC_NR(cmd) & ABS_MAX;
+
+				if ((retval = put_user(dev->abs[t],     ((int *) arg) + 0))) return retval;
+				if ((retval = put_user(dev->absmin[t],  ((int *) arg) + 1))) return retval;
+				if ((retval = put_user(dev->absmax[t],  ((int *) arg) + 2))) return retval;
+				if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval;
+				if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval;
+
+				return 0;
+			}
+	}
+	return -EINVAL;
+}
+
+static struct file_operations evdev_fops = {
+	read:		evdev_read,
+	write:		evdev_write,
+	poll:		evdev_poll,
+	open:		evdev_open,
+	release:	evdev_release,
+	ioctl:		evdev_ioctl,
+	fasync:		evdev_fasync,
+};
+
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+	struct evdev *evdev;
+	int minor;
+
+	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+	if (evdev_table[minor]) {
+		printk(KERN_ERR "evdev: no more free evdev devices\n");
+		return NULL;
+	}
+
+	if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+		return NULL;
+	memset(evdev, 0, sizeof(struct evdev));
+
+	init_waitqueue_head(&evdev->wait);
+
+	evdev->minor = minor;
+	evdev_table[minor] = evdev;
+
+	evdev->handle.dev = dev;
+	evdev->handle.handler = handler;
+	evdev->handle.private = evdev;
+
+	evdev->exist = 1;
+
+	evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE);
+
+	printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number);
+
+	return &evdev->handle;
+}
+
+static void evdev_disconnect(struct input_handle *handle)
+{
+	struct evdev *evdev = handle->private;
+
+	evdev->exist = 0;
+
+	if (evdev->open) {
+		input_close_device(handle);
+	} else {
+		input_unregister_minor(evdev->devfs);
+		evdev_table[evdev->minor] = NULL;
+		kfree(evdev);
+	}
+}
+	
+static struct input_handler evdev_handler = {
+	event:		evdev_event,
+	connect:	evdev_connect,
+	disconnect:	evdev_disconnect,
+	fops:		&evdev_fops,
+	minor:		EVDEV_MINOR_BASE,
+};
+
+static int __init evdev_init(void)
+{
+	input_register_handler(&evdev_handler);
+	return 0;
+}
+
+static void __exit evdev_exit(void)
+{
+	input_unregister_handler(&evdev_handler);
+}
+
+module_init(evdev_init);
+module_exit(evdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Event character device driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hid-debug.h linux.18p17/drivers/usb/hid-debug.h
--- linux.vanilla/drivers/usb/hid-debug.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/hid-debug.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,235 @@
+/*
+ * $Id: hid-debug.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $
+ *
+ *  (c) 1999 Andreas Gal	<gal@cs.uni-magdeburg.de>
+ *  (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
+ *
+ *  Some debug stuff for the HID parser.
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+struct hid_usage_entry {
+	unsigned  page;
+	unsigned  usage;
+	char     *description;
+};
+
+static struct hid_usage_entry hid_usage_table[] = {
+  {  1,      0, "GenericDesktop" },
+    {0, 0x01, "Pointer"},
+    {0, 0x02, "Mouse"},
+    {0, 0x04, "Joystick"},
+    {0, 0x05, "GamePad"},
+    {0, 0x06, "Keyboard"},
+    {0, 0x07, "Keypad"},
+    {0, 0x08, "MultiAxis"},
+      {0, 0x30, "X"},
+      {0, 0x31, "Y"},
+      {0, 0x32, "Z"},
+      {0, 0x33, "Rx"},
+      {0, 0x34, "Ry"},
+      {0, 0x35, "Rz"},
+      {0, 0x36, "Slider"},
+      {0, 0x37, "Dial"},
+      {0, 0x38, "Wheel"},
+      {0, 0x39, "HatSwitch"},
+    {0, 0x3a, "CountedBuffer"},
+      {0, 0x3b, "ByteCount"},
+      {0, 0x3c, "MotionWakeup"},
+      {0, 0x3d, "Start"},
+      {0, 0x3e, "Select"},
+      {0, 0x40, "Vx"},
+      {0, 0x41, "Vy"},
+      {0, 0x42, "Vz"},
+      {0, 0x43, "Vbrx"},
+      {0, 0x44, "Vbry"},
+      {0, 0x45, "Vbrz"},
+      {0, 0x46, "Vno"},
+    {0, 0x80, "SystemControl"}, 
+      {0, 0x81, "SystemPowerDown"},
+      {0, 0x82, "SystemSleep"},
+      {0, 0x83, "SystemWakeUp"},
+      {0, 0x84, "SystemContextMenu"},
+      {0, 0x85, "SystemMainMenu"},
+      {0, 0x86, "SystemAppMenu"},
+      {0, 0x87, "SystemMenuHelp"},
+      {0, 0x88, "SystemMenuExit"},
+      {0, 0x89, "SystemMenuSelect"},
+      {0, 0x8a, "SystemMenuRight"},
+      {0, 0x8b, "SystemMenuLeft"},
+      {0, 0x8c, "SystemMenuUp"},
+      {0, 0x8d, "SystemMenuDown"},
+    {0, 0x90, "D-padUp"},
+    {0, 0x91, "D-padDown"},
+    {0, 0x92, "D-padRight"},
+    {0, 0x93, "D-padLeft"},
+  {  7, 0, "Keyboard" },
+  {  8, 0, "LED" },
+  {  9, 0, "Button" },
+  { 12, 0, "Hotkey" },
+  { 13, 0, "Digitizers" },
+    {0, 0x01, "Digitizer"},
+    {0, 0x02, "Pen"},
+    {0, 0x03, "LightPen"},
+    {0, 0x04, "TouchScreen"},
+    {0, 0x05, "TouchPad"},
+    {0, 0x20, "Stylus"},
+    {0, 0x21, "Puck"},
+    {0, 0x22, "Finger"},
+    {0, 0x30, "TipPressure"},
+    {0, 0x31, "BarrelPressure"},
+    {0, 0x32, "InRange"},
+    {0, 0x33, "Touch"},
+    {0, 0x34, "UnTouch"},
+    {0, 0x35, "Tap"},
+    {0, 0x39, "TabletFunctionKey"},
+    {0, 0x3a, "ProgramChangeKey"},
+    {0, 0x3c, "Invert"},
+    {0, 0x42, "TipSwitch"},
+    {0, 0x43, "SecondaryTipSwitch"},
+    {0, 0x44, "BarrelSwitch"},
+    {0, 0x45, "Eraser"},
+    {0, 0x46, "TabletPick"},
+  { 15, 0, "PhysicalInterfaceDevice" },
+  { 0, 0, NULL }
+};
+
+static void resolv_usage_page(unsigned page) {
+	struct hid_usage_entry *p;
+
+	for (p = hid_usage_table; p->description; p++)
+		if (p->page == page) {
+			printk("%s", p->description);
+			return;
+		}
+	printk("%04x", page);
+}
+
+static void resolv_usage(unsigned usage) {
+	struct hid_usage_entry *p;
+
+	resolv_usage_page(usage >> 16);
+	printk(".");
+	for (p = hid_usage_table; p->description; p++)
+		if (p->page == (usage >> 16)) {
+			for(++p; p->description && p->page == 0; p++)
+				if (p->usage == (usage & 0xffff)) {
+					printk("%s", p->description);
+					return;
+				}
+			break;
+		}
+	printk("%04x", usage & 0xffff);
+}
+
+__inline__ static void tab(int n) {
+	while (n--) printk(" ");
+}
+
+static void hid_dump_field(struct hid_field *field, int n) {
+	int j;
+	
+	if (field->physical) {
+		tab(n);
+		printk("Physical(");
+		resolv_usage(field->physical); printk(")\n");
+	}
+	if (field->logical) {
+		tab(n);
+		printk("Logical(");
+		resolv_usage(field->logical); printk(")\n");
+	}
+	tab(n); printk("Usage(%d)\n", field->maxusage);
+	for (j = 0; j < field->maxusage; j++) {
+		tab(n+2);resolv_usage(field->usage[j].hid); printk("\n");
+	}
+	if (field->logical_minimum != field->logical_maximum) {
+		tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
+		tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
+	}
+	if (field->physical_minimum != field->physical_maximum) {
+		tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
+		tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
+	}
+	if (field->unit_exponent) {
+		tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
+	}
+	if (field->unit) {
+		tab(n); printk("Unit(%u)\n", field->unit);
+	}
+	tab(n); printk("Report Size(%u)\n", field->report_size);
+	tab(n); printk("Report Count(%u)\n", field->report_count);
+	tab(n); printk("Report Offset(%u)\n", field->report_offset);
+
+	tab(n); printk("Flags( ");
+	j = field->flags;
+	printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
+	printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
+	printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
+	printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
+	printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
+	printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : "");
+	printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
+	printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
+	printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
+	printk(")\n");
+}
+
+void hid_dump_device(struct hid_device *device) {
+	struct hid_report_enum *report_enum;
+	struct hid_report *report;
+	struct list_head *list;
+	unsigned i,k;
+	static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
+	
+	printk("Application(");
+	resolv_usage(device->application);
+	printk(")\n");
+
+	for (i = 0; i < HID_REPORT_TYPES; i++) {
+		report_enum = device->report_enum + i;
+		list = report_enum->report_list.next;
+		while (list != &report_enum->report_list) {
+			report = (struct hid_report *) list;
+			tab(2);
+			printk("%s", table[i]);
+			if (report->id)
+				printk("(%d)", report->id);
+			printk("[%s]", table[report->type]);
+			printk("\n");
+			for (k = 0; k < report->maxfield; k++) {
+				tab(4);
+				printk("Field(%d)\n", k);
+				hid_dump_field(report->field[k], 6);
+			}
+			list = list->next;
+		}
+	}
+}
+
+void hid_dump_input(struct hid_usage *usage, __s32 value) {
+	printk("hidd: input ");
+	resolv_usage(usage->hid);
+	printk(" = %d\n", value);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hid.c linux.18p17/drivers/usb/hid.c
--- linux.vanilla/drivers/usb/hid.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/hid.c	Sat Sep 23 13:21:35 2000
@@ -0,0 +1,1548 @@
+/*
+ * $Id: hid.c,v 1.16 2000/09/18 21:38:55 vojtech Exp $
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000 Vojtech Pavlik
+ *
+ *  USB HID support for the Linux input drivers
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#undef DEBUG
+#undef DEBUG_DATA
+#include <linux/usb.h>
+
+#include <asm/unaligned.h>
+
+#include "hid.h"
+
+#ifdef DEBUG
+#include "hid-debug.h"
+#else
+#define hid_dump_input(a,b)	do { } while (0)
+#define hid_dump_device(c)	do { } while (0)
+#endif
+
+#define unk	KEY_UNKNOWN
+
+static unsigned char hid_keyboard[256] = {
+	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+	 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
+	 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+	 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+	 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
+	120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
+	115,114,unk,unk,unk,124,unk,181,182,183,184,185,186,187,188,189,
+	190,191,192,193,194,195,196,197,198,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+	150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
+};
+
+static struct {
+	__s32 x;
+	__s32 y;
+}  hid_hat_to_axis[] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0, 0}};
+
+static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
+				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+
+/*
+ * Register a new report for a device.
+ */
+
+static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
+{
+	struct hid_report_enum *report_enum = device->report_enum + type;
+	struct hid_report *report;
+	 
+	if (report_enum->report_id_hash[id])
+		return report_enum->report_id_hash[id];
+
+	if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
+		return NULL;
+	memset(report, 0, sizeof(struct hid_report));
+
+	if (id != 0) report_enum->numbered = 1;
+
+	report->id = id;
+	report->type = type;
+	report->size = 0;
+	report->device = device;
+	report_enum->report_id_hash[id] = report;
+
+	list_add_tail(&report->list, &report_enum->report_list);
+
+	return report;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
+{
+	if (report->maxfield < HID_MAX_FIELDS) {
+		struct hid_field *field;
+
+		if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+				+ values * sizeof(unsigned), GFP_KERNEL)))
+			return NULL;
+		memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+				+ values * sizeof(unsigned));
+
+		report->field[report->maxfield++] = field;
+		field->usage = (struct hid_usage *)(field + 1);
+		field->value = (unsigned *)(field->usage + usages);
+		field->report = report;
+
+		return field;
+	}
+
+	dbg("too many fields in report");
+	return NULL;
+}
+
+/*
+ * Open a collection. The type/usage is pushed on the stack.
+ */
+
+static int open_collection(struct hid_parser *parser, unsigned type)
+{
+	unsigned usage;
+
+	usage = parser->local.usage[0];
+
+	if (type == HID_COLLECTION_APPLICATION && !parser->device->application)
+		parser->device->application = usage;
+
+	if (parser->collection_stack_ptr < HID_COLLECTION_STACK_SIZE) { /* PUSH on stack */
+		struct hid_collection *collection = parser->collection_stack + parser->collection_stack_ptr++;
+		collection->type = type;
+		collection->usage = usage;
+		return 0;
+	}
+
+	dbg("collection stack overflow");
+	return -1;
+}
+
+/*
+ * Close a collection.
+ */
+
+static int close_collection(struct hid_parser *parser)
+{
+	if (parser->collection_stack_ptr > 0) {	/* POP from stack */
+		parser->collection_stack_ptr--;
+		return 0;
+	}
+	dbg("collection stack underflow");
+	return -1;
+}
+
+/*
+ * Climb up the stack, search for the specified collection type
+ * and return the usage.
+ */
+
+static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
+{
+	int n;
+	for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
+		if (parser->collection_stack[n].type == type)
+			return parser->collection_stack[n].usage;
+	return 0; /* we know nothing about this usage type */
+}
+
+/*
+ * Add a usage to the temporary parser table.
+ */
+
+static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+{
+	if (parser->local.usage_index >= HID_MAX_USAGES) {
+		dbg("usage index exceeded");
+		return -1;
+	}
+	parser->local.usage[parser->local.usage_index++] = usage;
+	return 0;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags)
+{
+	struct hid_report *report;
+	struct hid_field *field;
+	int usages;
+	unsigned offset;
+	int i;
+
+	if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
+    		dbg("hid_register_report failed");
+		return -1;
+	}
+
+	if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */
+		if (parser->global.logical_maximum <= parser->global.logical_minimum) {
+			dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
+			return -1;
+		}
+		usages = parser->local.usage_index;
+		/* Hint: we can assume usages < MAX_USAGE here */
+	} else { /* VARIABLE */
+		usages = parser->global.report_count;
+	}
+	offset = report->size;
+	report->size += parser->global.report_size *
+			parser->global.report_count;
+	if (usages == 0)
+		return 0; /* ignore padding fields */
+	if ((field = hid_register_field(report, usages,
+			     parser->global.report_count)) == NULL)
+		return 0;
+	field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
+	field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
+	for (i = 0; i < usages; i++) field->usage[i].hid = parser->local.usage[i];
+	field->maxusage = usages;
+	field->flags = flags;
+	field->report_offset = offset;
+	field->report_type = report_type;
+	field->report_size = parser->global.report_size;
+	field->report_count = parser->global.report_count;
+	field->logical_minimum = parser->global.logical_minimum;
+	field->logical_maximum = parser->global.logical_maximum;
+	field->physical_minimum = parser->global.physical_minimum;
+	field->physical_maximum = parser->global.physical_maximum;
+	field->unit_exponent = parser->global.unit_exponent;
+	field->unit = parser->global.unit;
+	return 0;
+}
+
+/*
+ * Read data value from item.
+ */
+
+static __inline__ __u32 item_udata(struct hid_item *item)
+{
+	switch (item->size) {
+		case 1: return item->data.u8;
+		case 2: return item->data.u16;
+		case 4: return item->data.u32;
+	}
+	return 0;
+}
+
+static __inline__ __s32 item_sdata(struct hid_item *item)
+{
+	switch (item->size) {
+		case 1: return item->data.s8;
+		case 2: return item->data.s16;
+		case 4: return item->data.s32;
+	}
+	return 0;
+}
+
+/*
+ * Process a global item.
+ */
+
+static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
+{
+	switch (item->tag) {
+
+		case HID_GLOBAL_ITEM_TAG_PUSH:
+
+			if (parser->global_stack_ptr < HID_GLOBAL_STACK_SIZE) {
+				memcpy(parser->global_stack + parser->global_stack_ptr++,
+					&parser->global, sizeof(struct hid_global));
+				return 0;
+			}
+			dbg("global enviroment stack overflow");
+			return -1;
+
+		case HID_GLOBAL_ITEM_TAG_POP:
+
+			if (parser->global_stack_ptr > 0) {
+				memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
+					sizeof(struct hid_global));
+				return 0;
+			}
+			dbg("global enviroment stack underflow");
+			return -1;
+
+		case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+			parser->global.usage_page = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+			parser->global.logical_minimum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+			parser->global.logical_maximum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+			parser->global.physical_minimum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+			parser->global.physical_maximum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+			parser->global.unit_exponent = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_UNIT:
+			parser->global.unit = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+			if ((parser->global.report_size = item_udata(item)) > 32) {
+				dbg("invalid report_size %d", parser->global.report_size);
+				return -1;
+			}
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+			if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
+				dbg("invalid report_count %d", parser->global.report_count);
+				return -1;
+			}
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+			if ((parser->global.report_id = item_udata(item)) == 0) {
+				dbg("report_id 0 is invalid");
+				return -1;
+			}
+			return 0;
+
+		default:
+			dbg("unknown global tag 0x%x", item->tag);
+			return -1;
+	}
+}
+
+/*
+ * Process a local item.
+ */
+
+static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
+{
+	__u32 data;
+
+	if (item->size == 0) {
+		dbg("item data expected for local item");
+		return -1;
+	}
+
+	data = item_udata(item);
+
+	switch (item->tag) {
+
+		case HID_LOCAL_ITEM_TAG_DELIMITER:
+
+			if (data) {
+				/*
+				 * We treat items before the first delimiter
+				 * as global to all usage sets (branch 0).
+				 * In the moment we process only these global
+				 * items and the first delimiter set.
+				 */
+				if (parser->local.delimiter_depth != 0) {
+					dbg("nested delimiters");
+					return -1;
+				}
+				parser->local.delimiter_depth++;
+				parser->local.delimiter_branch++;
+			} else {
+				if (parser->local.delimiter_depth < 1) {
+					dbg("bogus close delimiter");
+					return -1;
+				}
+				parser->local.delimiter_depth--;
+			}
+			return 1;
+
+		case HID_LOCAL_ITEM_TAG_USAGE:
+
+			if (parser->local.delimiter_branch < 2) {
+				if (item->size <= 2)
+					data = (parser->global.usage_page << 16) + data;
+				return hid_add_usage(parser, data);
+			}
+			dbg("alternative usage ignored");
+			return 0;
+
+		case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+
+			if (parser->local.delimiter_branch < 2) {
+				if (item->size <= 2)
+					data = (parser->global.usage_page << 16) + data;
+				parser->local.usage_minimum = data;
+				return 0;
+			}
+			dbg("alternative usage ignored");
+			return 0;
+
+		case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+
+			if (parser->local.delimiter_branch < 2) {
+				unsigned n;
+				if (item->size <= 2)
+					data = (parser->global.usage_page << 16) + data;
+				for (n = parser->local.usage_minimum; n <= data; n++)
+					if (hid_add_usage(parser, n)) {
+						dbg("hid_add_usage failed\n");
+						return -1;
+					}
+				return 0;
+			}
+			dbg("alternative usage ignored");
+			return 0;
+
+		default:
+
+			dbg("unknown local item tag 0x%x", item->tag);
+			return 0;
+	}
+}
+
+/*
+ * Process a main item.
+ */
+
+static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
+{
+	__u32 data;
+	int ret;
+
+	data = item_udata(item);
+	
+	switch (item->tag) {
+		case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+			ret = open_collection(parser, data & 3);
+			break;
+		case HID_MAIN_ITEM_TAG_END_COLLECTION:
+			ret = close_collection(parser);
+			break;
+		case HID_MAIN_ITEM_TAG_INPUT:
+			ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+			break;
+		case HID_MAIN_ITEM_TAG_OUTPUT:
+			ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+			break;
+		case HID_MAIN_ITEM_TAG_FEATURE:
+			ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+			break;
+		default:
+			dbg("unknown main item tag 0x%x", item->tag);
+			ret = 0;
+	}
+
+	memset(&parser->local, 0, sizeof(parser->local));	/* Reset the local parser environment */
+
+	return ret;
+}
+
+/*
+ * Process a reserved item.
+ */
+
+static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
+{
+	dbg("reserved item type, tag 0x%x", item->tag);
+	return 0;
+}
+
+/*
+ * Free a report and all registered fields. The field->usage and
+ * field->value table's are allocated behind the field, so we need
+ * only to free(field) itself.
+ */
+
+static void hid_free_report(struct hid_report *report)
+{
+	unsigned n;
+
+	for (n = 0; n < report->maxfield; n++)
+		kfree(report->field[n]);
+	kfree(report);
+}
+
+/*
+ * Free a device structure, all reports, and all fields.
+ */
+
+static void hid_free_device(struct hid_device *device)
+{
+	unsigned i,j;
+
+	for (i = 0; i < HID_REPORT_TYPES; i++) {
+		struct hid_report_enum *report_enum = device->report_enum + i;
+
+		for (j = 0; j < 256; j++) {
+			struct hid_report *report = report_enum->report_id_hash[j];
+			if (report) hid_free_report(report);
+		}
+	}
+
+	if (device->rdesc) kfree(device->rdesc);
+}
+
+/*
+ * Fetch a report description item from the data stream. We support long
+ * items, though they are not used yet.
+ */
+
+static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
+{
+	if ((end - start) > 0) {
+
+		__u8 b = *start++;
+		item->type = (b >> 2) & 3;
+		item->tag  = (b >> 4) & 15;
+
+		if (item->tag == HID_ITEM_TAG_LONG) {
+
+			item->format = HID_ITEM_FORMAT_LONG;
+
+			if ((end - start) >= 2) {
+
+				item->size = *start++;
+				item->tag  = *start++;
+
+				if ((end - start) >= item->size) {
+					item->data.longdata = start;
+					start += item->size;
+					return start;
+				}
+			}
+		} else {
+
+			item->format = HID_ITEM_FORMAT_SHORT;
+			item->size = b & 3;
+			switch (item->size) {
+
+				case 0:
+					return start;
+
+				case 1: 
+					if ((end - start) >= 1) {
+						item->data.u8 = *start++;
+						return start;
+					}
+					break;
+
+				case 2: 
+					if ((end - start) >= 2) {
+						item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++));
+						return start;
+					}
+
+				case 3: 
+					item->size++;
+					if ((end - start) >= 4) {
+						item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++));
+						return start;
+					}
+			}
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Parse a report description into a hid_device structure. Reports are
+ * enumerated, fields are attached to these reports.
+ */
+
+static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+{
+	struct hid_device *device;
+	struct hid_parser *parser;
+	struct hid_item    item;
+	__u8 *end;
+	unsigned i;
+	static int (*dispatch_type[])(struct hid_parser *parser,
+				      struct hid_item *item) = {
+		hid_parser_main,
+		hid_parser_global,
+		hid_parser_local,
+		hid_parser_reserved
+	};
+
+	if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
+		return NULL;
+	memset(device, 0, sizeof(struct hid_device));
+
+	for (i = 0; i < HID_REPORT_TYPES; i++)
+		INIT_LIST_HEAD(&device->report_enum[i].report_list);
+
+	if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
+		kfree(device);
+		return NULL;
+	}
+	memcpy(device->rdesc, start, size);
+
+	if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
+		kfree(device->rdesc);
+		kfree(device);
+		return NULL;
+	}
+	memset(parser, 0, sizeof(struct hid_parser));
+	parser->device = device;
+	
+	end = start + size;
+	while ((start = fetch_item(start, end, &item)) != 0) {
+		if (item.format != HID_ITEM_FORMAT_SHORT) {
+			dbg("unexpected long global item");
+			hid_free_device(device);
+			kfree(parser);
+			return NULL;
+		}
+		if (dispatch_type[item.type](parser, &item)) {
+			dbg("item %u %u %u %u parsing failed\n",
+				item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
+			hid_free_device(device);
+			kfree(parser);
+			return NULL;
+		}
+
+		if (start == end) {
+			if (parser->collection_stack_ptr) {
+				dbg("unbalanced collection at end of report description");
+				hid_free_device(device);
+				kfree(parser);
+				return NULL;
+			}
+			if (parser->local.delimiter_depth) {
+				dbg("unbalanced delimiter at end of report description");
+				hid_free_device(device);
+				kfree(parser);
+				return NULL;
+			}
+			kfree(parser);
+			return device;
+		}
+	}
+
+	dbg("item fetching failed at offset %d\n", (int)(end - start));
+	hid_free_device(device);
+	kfree(parser);
+	return NULL;
+}
+
+/*
+ * Convert a signed n-bit integer to signed 32-bit integer. Common
+ * cases are done through the compiler, the screwed things has to be
+ * done by hand.
+ */
+
+static __inline__ __s32 snto32(__u32 value, unsigned n)
+{
+	switch (n) {
+		case 8:  return ((__s8)value);
+		case 16: return ((__s16)value);
+		case 32: return ((__s32)value);
+	}
+	return value & (1 << (n - 1)) ? value | (-1 << n) : value;
+}
+
+/*
+ * Convert a signed 32-bit integer to a signed n-bit integer. 
+ */
+
+static __inline__ __u32 s32ton(__s32 value, unsigned n)
+{
+	__s32 a = value >> (n - 1);
+	if (a && a != -1) return value > 0 ? 1 << (n - 1) : (1 << n) - 1;
+	return value & ((1 << n) - 1);
+}
+
+/*
+ * Extract/implement a data field from/to a report. We use 64-bit unsigned,
+ * 32-bit aligned, so that we can possibly have alignment problems on some
+ * odd architectures.
+ */
+
+static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
+{
+	report += (offset >> 5) << 2; offset &= 31;
+	return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1);
+}
+
+static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
+{
+	report += (offset >> 5) << 2; offset &= 31;
+	*(__u64*)report &= cpu_to_le64(~((((__u64) 1 << n) - 1) << offset));
+	*(__u64*)report |= cpu_to_le64((__u64)value << offset);
+}
+
+static void hid_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage)
+{
+	struct input_dev *input = &device->input;
+	int max;
+	unsigned long *bit;
+
+	switch (usage->hid & HID_USAGE_PAGE) {
+
+		case HID_UP_KEYBOARD:
+
+			set_bit(EV_REP, input->evbit);
+			usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+
+			if ((usage->hid & HID_USAGE) < 256) {
+				if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE]))
+					return;
+				clear_bit(usage->code, bit);
+			} else
+				usage->code = KEY_UNKNOWN;
+
+			break; 
+
+		case HID_UP_BUTTON:
+
+			usage->code = ((usage->hid - 1) & 0xf) + 0x100;
+			usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+			
+			switch (device->application) {
+				case HID_GD_GAMEPAD:  usage->code += 0x10;
+				case HID_GD_JOYSTICK: usage->code += 0x10;
+				case HID_GD_MOUSE:    usage->code += 0x10; break;
+				default:
+					if (field->physical == HID_GD_POINTER)
+						usage->code += 0x10;
+					break;
+			}
+			break;
+
+		case HID_UP_GENDESK:
+
+			if ((usage->hid & 0xf0) == 0x80) {	/* SystemControl */
+				switch (usage->hid & 0xf) {
+					case 0x1: usage->code = KEY_POWER;  break;
+					case 0x2: usage->code = KEY_SLEEP;  break;
+					case 0x3: usage->code = KEY_WAKEUP; break;
+					default: usage->code = KEY_UNKNOWN; break;
+				}
+				usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+				break;
+			}
+
+			usage->code = usage->hid & 0xf;
+
+			if (field->report_size == 1) {
+				usage->code = BTN_MISC;
+				usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+				break;
+			}
+
+			if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+				usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
+				break;
+			} 
+
+			usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+
+			if (usage->hid == HID_GD_HATSWITCH) {
+				usage->code = ABS_HAT0X;
+				usage->hat = 1 + (field->logical_maximum == 4);
+			}
+			break;
+
+		case HID_UP_LED:
+
+			usage->code = (usage->hid - 1) & 0xf;
+			usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; 
+			break;
+
+		case HID_UP_DIGITIZER:
+
+			switch (usage->hid & 0xff) {
+
+				case 0x30: /* TipPressure */
+
+					if (!test_bit(BTN_TOUCH, input->keybit)) {
+						device->quirks |= HID_QUIRK_NOTOUCH;
+						set_bit(EV_KEY, input->evbit);
+						set_bit(BTN_TOUCH, input->keybit);
+					}
+					usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; 
+					usage->code = ABS_PRESSURE;
+					clear_bit(usage->code, bit);
+					break;
+
+				case 0x32: /* InRange */
+
+					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+					switch (field->physical & 0xff) {	
+						case 0x21: usage->code = BTN_TOOL_MOUSE; break;
+						case 0x22: usage->code = BTN_TOOL_FINGER; break;
+						default: usage->code = BTN_TOOL_PEN; break;
+					}
+					break;
+
+				case 0x3c: /* Invert */
+
+					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+					usage->code = BTN_TOOL_RUBBER;
+					clear_bit(usage->code, bit);
+					break;
+
+				case 0x33: /* Touch */
+				case 0x42: /* TipSwitch */
+				case 0x43: /* TipSwitch2 */
+
+					device->quirks &= ~HID_QUIRK_NOTOUCH;
+					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+					usage->code = BTN_TOUCH;
+					clear_bit(usage->code, bit);
+					break;
+
+				case 0x44: /* BarrelSwitch */
+
+					usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+					usage->code = BTN_STYLUS;
+					clear_bit(usage->code, bit);
+					break;
+
+				default:  goto unknown;
+			}
+			break;
+
+		case HID_UP_CONSUMER:	/* USB HUT v1.1, pages 56-62 */
+			
+			switch (usage->hid & HID_USAGE) {
+
+				case 0x034: usage->code = KEY_SLEEP;		break;
+				case 0x036: usage->code = BTN_MISC;		break;
+				case 0x08a: usage->code = KEY_WWW;		break;
+				case 0x095: usage->code = KEY_HELP;		break;
+
+				case 0x0b4: usage->code = KEY_REWIND;		break;
+				case 0x0b5: usage->code = KEY_NEXTSONG;		break;
+				case 0x0b6: usage->code = KEY_PREVIOUSSONG;	break;
+				case 0x0b7: usage->code = KEY_STOPCD;		break;
+				case 0x0b8: usage->code = KEY_EJECTCD;		break;
+				case 0x0cd: usage->code = KEY_PLAYPAUSE;	break;
+
+				case 0x0e2: usage->code = KEY_MUTE;		break;
+				case 0x0e9: usage->code = KEY_VOLUMEUP;		break;
+				case 0x0ea: usage->code = KEY_VOLUMEDOWN;	break;
+
+				case 0x183: usage->code = KEY_CONFIG;		break;
+				case 0x18a: usage->code = KEY_MAIL;		break;
+				case 0x192: usage->code = KEY_CALC;		break;
+				case 0x194: usage->code = KEY_FILE;		break;
+
+				case 0x21a: usage->code = KEY_UNDO;		break;
+				case 0x21b: usage->code = KEY_COPY;		break;
+				case 0x21c: usage->code = KEY_CUT;		break;
+				case 0x21d: usage->code = KEY_PASTE;		break;
+
+				case 0x221: usage->code = KEY_FIND;		break;
+				case 0x223: usage->code = KEY_HOMEPAGE;		break;
+				case 0x224: usage->code = KEY_BACK;		break;
+				case 0x225: usage->code = KEY_FORWARD;		break;
+				case 0x226: usage->code = KEY_STOP;		break;
+				case 0x227: usage->code = KEY_REFRESH;		break;
+				case 0x22a: usage->code = KEY_BOOKMARKS;	break;
+
+				default:    usage->code = KEY_UNKNOWN;		break;
+
+			}
+		
+			usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+			break;
+
+		default:
+		unknown:
+
+			if (field->report_size == 1) {
+				usage->code = BTN_MISC;
+				usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+				break;
+			}
+
+			if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+				usage->code = REL_MISC;
+				usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
+				break;
+			}
+
+			usage->code = ABS_MISC;
+			usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+			break;
+	}
+
+	set_bit(usage->type, input->evbit);
+
+	while (usage->code <= max && test_and_set_bit(usage->code, bit)) {
+		usage->code = find_next_zero_bit(bit, max + 1, usage->code);
+	}
+
+	if (usage->code > max) return;
+
+	if (usage->type == EV_ABS) {
+		int a = field->logical_minimum;
+		int b = field->logical_maximum;
+
+		input->absmin[usage->code] = a; 
+		input->absmax[usage->code] = b;
+		input->absfuzz[usage->code] = (b - a) >> 8;
+		input->absflat[usage->code] = (b - a) >> 4;
+	}
+
+	if (usage->hat) {
+		int i;
+		for (i = usage->code; i < usage->code + 2 && i <= max; i++) {
+			input->absmax[i] = 1;
+			input->absmin[i] = -1;
+			input->absfuzz[i] = 0;
+			input->absflat[i] = 0;
+		}
+		set_bit(usage->code + 1, input->absbit);
+	}
+}
+
+static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+	hid_dump_input(usage, value);
+
+	if (usage->hat) {
+		if (usage->hat == 2) value = value * 2;
+		if (value > 8) value = 8;
+		input_event(input, usage->type, usage->code    , hid_hat_to_axis[value].x);
+		input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
+		*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
+		if (value) {
+			input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
+			return;
+		}
+		input_event(input, usage->type, usage->code, 0);
+		input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
+		int a = field->logical_minimum;
+		int b = field->logical_maximum;
+		input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
+	}
+
+	input_event(input, usage->type, usage->code, value);
+
+	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+		input_event(input, usage->type, usage->code, 0);
+}
+
+/*
+ * Search an array for a value.
+ */
+
+static __inline__ int search(__s32 *array, __s32 value, unsigned n)
+{
+	while (n--) if (*array++ == value) return 0;
+	return -1;
+}
+
+/*
+ * Analyse a received field, and fetch the data from it. The field
+ * content is stored for next report processing (we do differential
+ * reporting to the layer).
+ */
+
+static void hid_input_field(struct hid_device *dev, struct hid_field *field, __u8 *data)
+{
+	unsigned n;
+	unsigned count = field->report_count;
+	unsigned offset = field->report_offset;
+	unsigned size = field->report_size;
+	__s32 min = field->logical_minimum;
+	__s32 max = field->logical_maximum;
+	__s32 value[count]; /* WARNING: gcc specific */
+   
+	for (n = 0; n < count; n++)
+			value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) : 
+						    extract(data, offset + n * size, size);
+
+	for (n = 0; n < count; n++) {
+
+		if (HID_MAIN_ITEM_VARIABLE & field->flags) {
+
+			if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+				if (!value[n]) continue;
+			} else {
+				if (value[n] == field->value[n]) continue;
+			}
+			hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]);
+
+		} else {
+
+			if (field->value[n] >= min && field->value[n] <= max			/* non-NULL value */
+				&& field->usage[field->value[n] - min].hid			/* nonzero usage */
+				&& search(value, field->value[n], count))
+					hid_process_event(&dev->input, &dev->quirks, field,
+						&field->usage[field->value[n] - min], 0);
+
+			if (value[n] >= min && value[n] <= max					/* non-NULL value */
+				&& field->usage[value[n] - min].hid				/* nonzero usage */
+				&& search(field->value, value[n], count))
+					hid_process_event(&dev->input, &dev->quirks,
+						field, &field->usage[value[n] - min], 1);
+		}
+	}
+
+	memcpy(field->value, value, count * sizeof(__s32));
+}
+
+/*
+ * Interrupt input handler - analyse a received report.
+ */
+
+static void hid_irq(struct urb *urb)
+{
+	struct hid_device *device = urb->context;
+	struct hid_report_enum *report_enum = device->report_enum + HID_INPUT_REPORT;
+	struct hid_report *report;
+	__u8 *data = urb->transfer_buffer;
+	int len = urb->actual_length;
+	int n;
+
+	if (urb->status) {
+		dbg("nonzero status in irq %d", urb->status);
+		return;
+	}
+
+	if (!len) {
+		dbg("empty report");
+		return;
+	}
+
+#ifdef DEBUG_DATA
+	printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered) = ", len, report_enum->numbered ? "" : "un");
+	for (n = 0; n < len; n++)
+		printk(" %02x", data[n]);
+	printk("\n");
+#endif
+
+	n = 0;				/* Normally report number is 0 */
+
+	if (report_enum->numbered) {	/* Device uses numbered reports, data[0] is report number */
+		n = *data++;
+		len--;
+	} 
+
+	if (!(report = report_enum->report_id_hash[n])) {
+		dbg("undefined report_id %d received", n);
+#ifdef DEBUG
+			printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len);
+			for (n = 0; n < len; n++)
+				printk(" %02x", data[n]);
+			printk("\n");
+#endif
+	
+		return;
+	}
+
+	if (len < ((report->size - 1) >> 3) + 1) {
+		dbg("report %d is too short, (%d < %d)", report->id, len, ((report->size - 1) >> 3) + 1);
+		return;
+	}
+
+	for (n = 0; n < report->maxfield; n++)
+		hid_input_field(device, report->field[n], data);
+
+	return;
+}
+
+/*
+ * hid_read_report() s intended to read the hid devices values even
+ * before the input device is registered, so that the userland interface
+ * modules start with real values. This is especially important for joydev.c
+ * automagic calibration. Doesn't work yet, though. Don't know why, the control
+ * request just times out on most devices I have and returns nonsense on others.
+ */
+
+static void hid_read_report(struct hid_device *hid, struct hid_report *report)
+{
+#if 0
+	int rlen = ((report->size - 1) >> 3) + 1;
+	char rdata[rlen];
+	struct urb urb;
+	int read, j;
+
+	memset(&urb, 0, sizeof(struct urb));
+	memset(rdata, 0, rlen);
+
+	urb.transfer_buffer = rdata;
+	urb.actual_length = rlen;
+	urb.context = hid;
+
+	dbg("getting report type %d id %d len %d", report->type + 1, report->id, rlen);
+
+	if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, rdata, rlen)) != rlen) {
+		dbg("reading report failed rlen %d read %d", rlen, read);
+#ifdef DEBUG
+		printk(KERN_DEBUG __FILE__ ": report = ");
+		for (j = 0; j < rlen; j++) printk(" %02x", rdata[j]);
+		printk("\n");
+#endif
+		return;
+	}
+
+	hid_irq(&urb);
+#endif
+}
+
+/*
+ * Output the field into the report.
+ */
+
+static void hid_output_field(struct hid_field *field, __u8 *data)
+{
+	unsigned count = field->report_count;
+	unsigned offset = field->report_offset;
+	unsigned size = field->report_size;
+	unsigned n;
+   
+	for (n = 0; n < count; n++) {
+		if (field->logical_minimum < 0)	/* signed values */
+			implement(data, offset + n * size, size, s32ton(field->value[n], size));
+		 else				/* unsigned values */
+			implement(data, offset + n * size, size, field->value[n]);
+       	}
+}
+
+/*
+ * Create a report.
+ */
+
+void hid_output_report(struct hid_report *report, __u8 *data)
+{
+	unsigned n;
+	for (n = 0; n < report->maxfield; n++)
+		hid_output_field(report->field[n], data);
+};
+
+/*
+ * Set a field value. The report this field belongs to has to be
+ * created and transfered to the device, to set this value in the
+ * device.
+ */
+
+int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
+{
+	unsigned size = field->report_size;
+
+	hid_dump_input(field->usage + offset, value);
+	
+	if (offset >= field->report_count) {
+		dbg("offset exceeds report_count");
+		return -1;
+	}
+	if (field->logical_minimum < 0) {
+		if (value != snto32(s32ton(value, size), size)) {
+			dbg("value %d is out of range", value);
+			return -1;
+		}
+	}
+	if (   (value > field->logical_maximum)
+	    || (value < field->logical_minimum)) {
+		dbg("value %d is invalid", value);
+		return -1;
+	}
+	field->value[offset] = value;
+	return 0;
+}
+
+static int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
+{
+	struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT;
+	struct list_head *list = report_enum->report_list.next;
+	int i, j;
+
+	while (list != &report_enum->report_list) {
+		struct hid_report *report = (struct hid_report *) list;
+		list = list->next;
+		for (i = 0; i < report->maxfield; i++) {
+			*field = report->field[i];
+			for (j = 0; j < (*field)->maxusage; j++)
+				if ((*field)->usage[j].type == type && (*field)->usage[j].code == code)
+					return j;
+		}
+	}
+	return -1;
+}
+
+static int hid_submit_out(struct hid_device *hid)
+{
+	hid->urbout.transfer_buffer_length = hid->out[hid->outtail].dr.length;
+	hid->urbout.transfer_buffer = hid->out[hid->outtail].buffer;
+	hid->urbout.setup_packet = (void *) &(hid->out[hid->outtail].dr);
+
+	if (usb_submit_urb(&hid->urbout)) {
+		err("usb_submit_urb(out) failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void hid_ctrl(struct urb *urb)
+{
+	struct hid_device *hid = urb->context;
+
+        if (urb->status)
+		warn("ctrl urb status %d received", urb->status);
+	
+	hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+	if (hid->outhead != hid->outtail)
+		hid_submit_out(hid);
+}       
+
+static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct hid_device *hid = dev->private;
+	struct hid_field *field = NULL;
+	int offset;
+
+	if ((offset = hid_find_field(hid, type, code, &field)) == -1) {
+		warn("event field not found");
+		return -1;
+	}
+
+	hid_set_field(field, offset, value);
+	hid_output_report(field->report, hid->out[hid->outhead].buffer);
+
+	hid->out[hid->outhead].dr.value = 0x200 | field->report->id;
+	hid->out[hid->outhead].dr.length = ((field->report->size - 1) >> 3) + 1;
+
+	hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+	if (hid->outhead == hid->outtail)
+		hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+	if (hid->urbout.status != -EINPROGRESS)
+		hid_submit_out(hid);
+
+	return 0;
+}
+
+static int hid_open(struct input_dev *dev)
+{
+	struct hid_device *hid = dev->private;
+
+	if (hid->open++)
+		return 0;
+
+	 if (usb_submit_urb(&hid->urb))
+		return -EIO;
+
+	return 0;
+}
+
+static void hid_close(struct input_dev *dev)
+{
+	struct hid_device *hid = dev->private;
+
+	if (!--hid->open)
+		usb_unlink_urb(&hid->urb);
+}
+
+/*
+ * Configure the input layer interface
+ * Read all reports and initalize the absoulte field values.
+ */
+
+static void hid_init_input(struct hid_device *hid)
+{
+	struct hid_report_enum *report_enum;
+	struct list_head *list;
+	int i, j, k;
+
+	hid->input.private = hid;
+	hid->input.event = hid_event;
+	hid->input.open = hid_open;
+	hid->input.close = hid_close;
+
+	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+
+		report_enum = hid->report_enum + k;
+		list = report_enum->report_list.next;
+
+		while (list != &report_enum->report_list) {
+
+			struct hid_report *report = (struct hid_report *) list;
+
+			list = list->next;
+
+			for (i = 0; i < report->maxfield; i++)
+				for (j = 0; j < report->field[i]->maxusage; j++)
+					hid_configure_usage(hid, report->field[i], report->field[i]->usage + j);
+
+			if (k == HID_INPUT_REPORT)  {
+				usb_set_idle(hid->dev, hid->ifnum, 0, report->id);
+				hid_read_report(hid, report);
+			}
+		}
+	}
+}
+
+#define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE	0x0010
+#define USB_DEVICE_ID_WACOM_INTUOS	0x0020
+
+struct hid_blacklist {
+	__u16 idVendor;
+	__u16 idProduct;
+} hid_blacklist[] = {
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3},
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4},
+	{ 0, 0 }
+};
+
+static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, char *name)
+{
+	struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0;
+	struct hid_descriptor *hdesc;
+	struct hid_device *hid;
+	unsigned rsize = 0;
+	int n;
+
+	for (n = 0; hid_blacklist[n].idVendor; n++)
+		if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) &&
+			(hid_blacklist[n].idProduct == dev->descriptor.idProduct)) return NULL;
+
+	if (interface->bInterfaceClass != USB_INTERFACE_CLASS_HID)
+		return NULL;
+
+	if (usb_get_extra_descriptor(interface, USB_DT_HID, &hdesc)
+		&& usb_get_extra_descriptor(&interface->endpoint[0], USB_DT_HID, &hdesc)) {
+			dbg("class descriptor not present\n");
+			return NULL;
+	}
+
+	for (n = 0; n < hdesc->bNumDescriptors; n++)
+		if (hdesc->desc[n].bDescriptorType == USB_DT_REPORT)
+			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
+
+	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+		dbg("weird size of report descriptor (%u)", rsize);
+		return NULL;
+	}
+
+	{
+		__u8 rdesc[rsize];
+
+		if ((n = usb_get_class_descriptor(dev, interface->bInterfaceNumber, USB_DT_REPORT, 0, rdesc, rsize)) < 0) {
+			dbg("reading report descriptor failed");
+			return NULL;
+		}
+
+#ifdef DEBUG_DATA
+		printk(KERN_DEBUG __FILE__ ": report (size %u, read %d) = ", rsize, n);
+		for (n = 0; n < rsize; n++)
+			printk(" %02x", (unsigned) rdesc[n]);
+		printk("\n");
+#endif
+
+		if (!(hid = hid_parse_report(rdesc, rsize))) {
+			dbg("parsing report descriptor failed");
+			return NULL;
+		}
+	}
+
+	for (n = 0; n < interface->bNumEndpoints; n++) {
+
+		struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n];
+		int pipe, maxp;
+
+		if ((endpoint->bmAttributes & 3) != 3)		/* Not an interrupt endpoint */
+			continue;
+
+		if (!(endpoint->bEndpointAddress & 0x80)) 	/* Not an input endpoint */
+			continue;
+
+		pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+		maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+		FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval);
+	
+		break;
+	}
+
+	if (n == interface->bNumEndpoints) {
+		dbg("couldn't find an input interrupt endpoint");
+		hid_free_device(hid);
+		return NULL;
+	}
+
+	hid->version = hdesc->bcdHID;
+	hid->country = hdesc->bCountryCode;
+	hid->dev = dev;
+	hid->ifnum = interface->bInterfaceNumber;
+
+	for (n = 0; n < HID_CONTROL_FIFO_SIZE; n++) {
+		hid->out[n].dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+		hid->out[n].dr.request = USB_REQ_SET_REPORT;
+		hid->out[n].dr.index = hid->ifnum;
+	}
+
+	hid->input.name = hid->name;
+	hid->input.idbus = BUS_USB;
+	hid->input.idvendor = dev->descriptor.idVendor;
+	hid->input.idproduct = dev->descriptor.idProduct;
+	hid->input.idversion = dev->descriptor.bcdDevice;
+
+	if (strlen(name)) 
+		strcpy(hid->name, name);
+	else
+		sprintf(hid->name, "USB HID %s %04x:%04x",
+			((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ?
+			hid_types[hid->application & 0xffff] : "Device",
+			hid->input.idvendor, hid->input.idproduct);
+
+	FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0),
+		(void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid);
+
+	if (interface->bInterfaceSubClass == 1)
+        	usb_set_protocol(dev, hid->ifnum, 1);
+
+	return hid;
+}
+
+static void* hid_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct hid_device *hid;
+	char name[128];
+	char *buf;
+
+	dbg("HID probe called for ifnum %d", ifnum);
+
+	name[0] = 0;
+
+	if (!(buf = kmalloc(63, GFP_KERNEL)))
+		return NULL;
+
+	if (dev->descriptor.iManufacturer &&
+		usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+			strcat(name, buf);
+	if (dev->descriptor.iProduct &&
+		usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+			sprintf(name, "%s %s", name, buf);
+
+	kfree(buf);
+
+	if (!(hid = usb_hid_configure(dev, ifnum, name)))
+		return NULL;
+
+	hid_dump_device(hid);
+
+	hid_init_input(hid);
+	input_register_device(&hid->input);
+
+	printk(KERN_INFO "input%d: USB HID v%x.%02x %s",
+		hid->input.number,
+		hid->version >> 8, hid->version & 0xff,
+		((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ?
+		hid_types[hid->application & 0xffff] : "Device");
+
+	if (strlen(name))
+		printk(" [%s]", name);
+	else
+		printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct);
+	
+	printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum);
+
+	return hid;
+}
+
+static void hid_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct hid_device *hid = ptr;
+
+	dbg("cleanup called");
+	usb_unlink_urb(&hid->urb);
+	input_unregister_device(&hid->input);
+	hid_free_device(hid);
+}
+
+static struct usb_driver hid_driver = {
+	name:		"hid",
+	probe:		hid_probe,
+	disconnect:	hid_disconnect
+};
+
+static int __init hid_init(void)
+{
+	usb_register(&hid_driver);
+	return 0;
+}
+
+static void __exit hid_exit(void)
+{
+	usb_deregister(&hid_driver);
+}
+
+module_init(hid_init);
+module_exit(hid_exit);
+
+MODULE_AUTHOR("Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB HID support drivers");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hid.h linux.18p17/drivers/usb/hid.h
--- linux.vanilla/drivers/usb/hid.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/hid.h	Sun Oct 15 23:40:53 2000
@@ -0,0 +1,341 @@
+#ifndef __HID_H
+#define __HID_H
+
+/*
+ * $Id: hid.h,v 1.7 2000/05/31 22:57:48 vojtech Exp $
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/list.h>
+
+/*
+ * USB HID (Human Interface Device) interface class code
+ */
+
+#define USB_INTERFACE_CLASS_HID		3
+
+/*
+ * We parse each description item into this structure. Short items data
+ * values are expanded to 32-bit signed int, long items contain a pointer
+ * into the data area.
+ */
+
+struct hid_item {
+	unsigned  format;
+	__u8      size;
+	__u8      type;
+	__u8      tag;
+	union {
+	    __u8   u8;
+	    __s8   s8;
+	    __u16  u16;
+	    __s16  s16;
+	    __u32  u32;
+	    __s32  s32;
+	    __u8  *longdata;
+	} data;
+};
+
+/*
+ * HID report item format
+ */
+
+#define HID_ITEM_FORMAT_SHORT	0
+#define HID_ITEM_FORMAT_LONG	1
+
+/*
+ * Special tag indicating long items
+ */
+
+#define HID_ITEM_TAG_LONG	15
+
+/*
+ * HID report descriptor item type (prefix bit 2,3)
+ */
+
+#define HID_ITEM_TYPE_MAIN		0
+#define HID_ITEM_TYPE_GLOBAL		1
+#define HID_ITEM_TYPE_LOCAL		2
+#define HID_ITEM_TYPE_RESERVED		3
+
+/*
+ * HID report descriptor main item tags
+ */
+
+#define HID_MAIN_ITEM_TAG_INPUT			8
+#define HID_MAIN_ITEM_TAG_OUTPUT		9
+#define HID_MAIN_ITEM_TAG_FEATURE		11
+#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION	10
+#define HID_MAIN_ITEM_TAG_END_COLLECTION	12
+
+/*
+ * HID report descriptor main item contents
+ */
+
+#define HID_MAIN_ITEM_CONSTANT		0x001
+#define HID_MAIN_ITEM_VARIABLE		0x002
+#define HID_MAIN_ITEM_RELATIVE		0x004
+#define HID_MAIN_ITEM_WRAP		0x008	
+#define HID_MAIN_ITEM_NONLINEAR		0x010
+#define HID_MAIN_ITEM_NO_PREFERRED	0x020
+#define HID_MAIN_ITEM_NULL_STATE	0x040
+#define HID_MAIN_ITEM_VOLATILE		0x080
+#define HID_MAIN_ITEM_BUFFERED_BYTE	0x100
+
+/*
+ * HID report descriptor collection item types
+ */
+
+#define HID_COLLECTION_PHYSICAL		0
+#define HID_COLLECTION_APPLICATION	1
+#define HID_COLLECTION_LOGICAL		2
+
+/*
+ * HID report descriptor global item tags
+ */
+
+#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE		0
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM	1
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM	2
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM	3
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM	4
+#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT	5
+#define HID_GLOBAL_ITEM_TAG_UNIT		6
+#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE		7
+#define HID_GLOBAL_ITEM_TAG_REPORT_ID		8
+#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT	9
+#define HID_GLOBAL_ITEM_TAG_PUSH		10
+#define HID_GLOBAL_ITEM_TAG_POP			11
+
+/*
+ * HID report descriptor local item tags
+ */
+
+#define HID_LOCAL_ITEM_TAG_USAGE		0
+#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM	1
+#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM	2
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX	3
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM	4
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM	5
+#define HID_LOCAL_ITEM_TAG_STRING_INDEX		7
+#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM	8
+#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM	9
+#define HID_LOCAL_ITEM_TAG_DELIMITER		10
+
+/*
+ * HID usage tables
+ */
+
+#define HID_USAGE_PAGE		0xffff0000
+
+#define HID_UP_GENDESK 		0x00010000
+#define HID_UP_KEYBOARD 	0x00070000
+#define HID_UP_LED 		0x00080000
+#define HID_UP_BUTTON 		0x00090000
+#define HID_UP_CONSUMER		0x000c0000
+#define HID_UP_DIGITIZER 	0x000d0000
+#define HID_UP_PID 		0x000f0000
+
+#define HID_USAGE		0x0000ffff
+
+#define HID_GD_POINTER		0x00010001
+#define HID_GD_MOUSE		0x00010002
+#define HID_GD_JOYSTICK		0x00010004
+#define HID_GD_GAMEPAD		0x00010005
+#define HID_GD_HATSWITCH	0x00010039
+
+/*
+ * HID report types --- Ouch! HID spec says 1 2 3!
+ */
+
+#define HID_INPUT_REPORT	0
+#define HID_OUTPUT_REPORT	1
+#define HID_FEATURE_REPORT	2
+
+/*
+ * HID device quirks.
+ */
+
+#define HID_QUIRK_INVERT	0x01
+#define HID_QUIRK_NOTOUCH	0x02
+
+/*
+ * This is the global enviroment of the parser. This information is
+ * persistent for main-items. The global enviroment can be saved and
+ * restored with PUSH/POP statements.
+ */
+
+struct hid_global {
+	unsigned usage_page;
+	__s32    logical_minimum;
+	__s32    logical_maximum;
+	__s32    physical_minimum;
+	__s32    physical_maximum;
+	unsigned unit_exponent;
+	unsigned unit;
+	unsigned report_id;
+	unsigned report_size;
+	unsigned report_count;
+};
+
+/*
+ * This is the local enviroment. It is resistent up the the next main-item.
+ */
+
+#define HID_MAX_DESCRIPTOR_SIZE		4096
+#define HID_MAX_USAGES			1024
+
+struct hid_local {
+	unsigned usage[HID_MAX_USAGES]; /* usage array */
+	unsigned usage_index;
+	unsigned usage_minimum;
+	unsigned delimiter_depth;
+	unsigned delimiter_branch;
+};
+
+/*
+ * This is the collection stack. We climb up the stack to determine
+ * application and function of each field.
+ */
+
+struct hid_collection {
+	unsigned type;
+	unsigned usage;
+};
+
+struct hid_usage {
+	unsigned  hid;			/* hid usage code */
+	__u16     code;			/* input driver code */
+	__u8      type;			/* input driver type */
+	__u8	  hat;			/* hat switch fun */
+};
+
+struct hid_field {
+	unsigned  physical;		/* physical usage for this field */
+	unsigned  logical;		/* logical usage for this field */
+	struct hid_usage *usage;	/* usage table for this function */
+	unsigned  maxusage;		/* maximum usage index */
+	unsigned  flags;		/* main-item flags (i.e. volatile,array,constant) */
+	unsigned  report_offset;	/* bit offset in the report */
+	unsigned  report_size;		/* size of this field in the report */
+	unsigned  report_count;		/* number of this field in the report */
+	unsigned  report_type;		/* (input,output,feature) */
+	__s32    *value;		/* last known value(s) */
+	__s32     logical_minimum;
+	__s32     logical_maximum;
+	__s32     physical_minimum;
+	__s32     physical_maximum;
+	unsigned  unit_exponent;
+	unsigned  unit;
+	struct hid_report *report;	/* associated report */
+};
+
+#define HID_MAX_FIELDS 64
+
+struct hid_report {
+	struct list_head list;
+	unsigned id;					/* id of this report */
+	unsigned type;					/* report type */
+	struct hid_field *field[HID_MAX_FIELDS];	/* fields of the report */
+	unsigned maxfield;				/* maximum valid field index */
+	unsigned size;					/* size of the report (bits) */
+	struct hid_device *device;			/* associated device */
+};
+
+struct hid_report_enum {
+	unsigned numbered;
+	struct list_head report_list;
+	struct hid_report *report_id_hash[256];
+};
+
+#define HID_REPORT_TYPES 3
+
+#define HID_BUFFER_SIZE		32
+#define HID_CONTROL_FIFO_SIZE	8
+
+struct hid_control_fifo {
+	devrequest dr;
+	char buffer[HID_BUFFER_SIZE];
+};
+
+struct hid_device {							/* device report descriptor */
+	 __u8 *rdesc;
+	unsigned rsize;
+	unsigned application;						/* HID application, i.e. Digitizer */
+	unsigned version;						/* HID version */
+	unsigned country;						/* HID country */
+	struct hid_report_enum report_enum[HID_REPORT_TYPES];
+
+	struct usb_device *dev;						/* USB device */
+	int ifnum;							/* USB interface number */
+
+	struct urb urb;							/* USB URB structure */
+	char buffer[HID_BUFFER_SIZE];					/* Rx buffer */
+
+	struct urb urbout;						/* Output URB */
+	struct hid_control_fifo out[HID_CONTROL_FIFO_SIZE];		/* Transmit buffer */
+	unsigned char outhead, outtail;					/* Tx buffer head & tail */
+
+	struct input_dev input;						/* input device structure */
+	int open;							/* is the device open by input? */
+	int quirks;							/* Various nasty tricks the device can pull on us */
+	char name[128];							/* Device name */
+};
+
+#define HID_GLOBAL_STACK_SIZE 4
+#define HID_COLLECTION_STACK_SIZE 4
+
+struct hid_parser {
+	struct hid_global     global;
+	struct hid_global     global_stack[HID_GLOBAL_STACK_SIZE];
+	unsigned              global_stack_ptr;
+	struct hid_local      local;
+	struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE];
+	unsigned              collection_stack_ptr;
+	struct hid_device    *device;
+};
+
+struct hid_class_descriptor {
+	__u8  bDescriptorType;
+	__u16 wDescriptorLength;
+} __attribute__ ((packed));
+
+struct hid_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdHID;
+	__u8  bCountryCode;
+	__u8  bNumDescriptors;
+
+	struct hid_class_descriptor desc[1];
+} __attribute__ ((packed));
+
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.c linux.18p17/drivers/usb/hub.c
--- linux.vanilla/drivers/usb/hub.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/hub.c	Sat Sep 23 13:21:38 2000
@@ -1,220 +1,317 @@
 /*
  * USB hub driver.
  *
- * This is horrible, it knows about the UHCI driver
- * internals, but it's just meant as a rough example,
- * let's do the virtualization later when this works.
- *
  * (C) Copyright 1999 Linus Torvalds
  * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Gregory P. Smith
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/list.h>
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
 
-#include <asm/spinlock.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
 
-#include "usb.h"
-#include "uhci.h"
 #include "hub.h"
 
-extern struct usb_operations uhci_device_operations;
-
 /* Wakes up khubd */
-static struct wait_queue *usb_hub_wait = NULL;
 static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(usb_address0_sem);
+
+static LIST_HEAD(hub_event_list);	/* List of hubs needing servicing */
+static LIST_HEAD(hub_list);		/* List containing all of the hubs (for cleanup) */
+
+static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
+static int khubd_pid = 0;			/* PID of khubd */
+static int khubd_running = 0;
+
+static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+		USB_DT_HUB << 8, 0, data, size, HZ);
+}
 
-/* List of hubs needing servicing */
-static struct list_head hub_event_list;
+static int usb_clear_hub_feature(struct usb_device *dev, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ);
+}
 
-/* PID of khubd */
-static int khubd_pid = 0;
+static int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
+}
+
+static int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
+}
+
+static int usb_get_hub_status(struct usb_device *dev, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
+		data, sizeof(struct usb_hub_status), HZ);
+}
+
+static int usb_get_port_status(struct usb_device *dev, int port, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
+		data, sizeof(struct usb_hub_status), HZ);
+}
 
 /*
  * A irq handler returns non-zero to indicate to
  * the low-level driver that it wants to be re-activated,
  * or zero to say "I'm done".
  */
-static int hub_irq(int status, void *__buffer, void *dev_id)
+static void hub_irq(struct urb *urb)
 {
-	struct usb_hub *hub = dev_id;
+	struct usb_hub *hub = (struct usb_hub *)urb->context;
 	unsigned long flags;
 
-	if (waitqueue_active(&usb_hub_wait)) {
+	if (urb->status) {
+		if (urb->status != -ENOENT)
+			dbg("nonzero status in irq %d", urb->status);
+
+		return;
+	}
+
+	/* Something happened, let khubd figure it out */
+	if (waitqueue_active(&khubd_wait)) {
 		/* Add the hub to the event queue */
 		spin_lock_irqsave(&hub_event_lock, flags);
 		if (hub->event_list.next == &hub->event_list) {
 			list_add(&hub->event_list, &hub_event_list);
 			/* Wake up khubd */
-			wake_up(&usb_hub_wait);
+			wake_up(&khubd_wait);
 		}
 		spin_unlock_irqrestore(&hub_event_lock, flags);
 	}
+}
+
+static void usb_hub_power_on(struct usb_hub *hub)
+{
+	int i;
 
-	return 1;
+	/* Enable power to the ports */
+	dbg("enabling power on all ports");
+	for (i = 0; i < hub->nports; i++)
+		usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER);
 }
 
-static void usb_hub_configure(struct usb_hub *hub)
+static int usb_hub_configure(struct usb_hub *hub)
 {
 	struct usb_device *dev = hub->dev;
-	unsigned char hubdescriptor[8], buf[4];
-	int charac, i;
+	unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap;
+	struct usb_hub_descriptor *descriptor;
+	struct usb_descriptor_header *header;
+	struct usb_hub_status *hubsts;
+	int i, ret;
+
+	/* Request the entire hub descriptor. */
+	header = (struct usb_descriptor_header *)buffer;
+	ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer));
+		/* <buffer> is large enough for a hub with 127 ports;
+		 * the hub can/will return fewer bytes here. */
+	if (ret < 0) {
+		err("Unable to get hub descriptor (err = %d)", ret);
+		return -1;
+	}
 
-	usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+	bitmap = kmalloc(header->bLength, GFP_KERNEL);
+	if (!bitmap) {
+		err("Unable to kmalloc %d bytes for bitmap", header->bLength);
+		return -1;
+	}
 
-	if (usb_get_hub_descriptor(dev, hubdescriptor, 8))
-		return;
+	memcpy (bitmap, buffer, header->bLength);
+	descriptor = (struct usb_hub_descriptor *)bitmap;
 
-	hub->nports = dev->maxchild = hubdescriptor[2];
-	printk("hub: %d-port%s detected\n", hub->nports,
-		(hub->nports == 1) ? "" : "s");
+	hub->nports = dev->maxchild = descriptor->bNbrPorts;
+	info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s");
 
-	charac = (hubdescriptor[4] << 8) + hubdescriptor[3];
-	switch (charac & HUB_CHAR_LPSM) {
+	switch (descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
 		case 0x00:
-			printk("hub: ganged power switching\n");
+			dbg("ganged power switching");
 			break;
 		case 0x01:
-			printk("hub: individual port power switching\n");
+			dbg("individual port power switching");
 			break;
 		case 0x02:
 		case 0x03:
-			printk("hub: unknown reserved power switching mode\n");
+			dbg("unknown reserved power switching mode");
 			break;
 	}
 
-	if (charac & HUB_CHAR_COMPOUND)
-		printk("hub: part of a compound device\n");
+	if (descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND)
+		dbg("part of a compound device");
 	else
-		printk("hub: standalone hub\n");
+		dbg("standalone hub");
 
-	switch (charac & HUB_CHAR_OCPM) {
+	switch (descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
 		case 0x00:
-			printk("hub: global over current protection\n");
+			dbg("global over-current protection");
 			break;
 		case 0x08:
-			printk("hub: individual port over current protection\n");
+			dbg("individual port over-current protection");
 			break;
 		case 0x10:
 		case 0x18:
-			printk("hub: no over current protection\n");
+			dbg("no over-current protection");
                         break;
 	}
 
-	printk("hub: power on to power good time: %dms\n",
-		hubdescriptor[5] * 2);
-
-	printk("hub: hub controller current requirement: %dmA\n",
-		hubdescriptor[6]);
+	dbg("power on to power good time: %dms", descriptor->bPwrOn2PwrGood * 2);
+	dbg("hub controller current requirement: %dmA", descriptor->bHubContrCurrent);
 
 	for (i = 0; i < dev->maxchild; i++)
-		printk("hub:  port %d is%s removable\n", i + 1,
-			hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8))
+		dbg("port %d is%s removable", i + 1,
+			bitmap[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8))
 			? " not" : "");
 
-	if (usb_get_hub_status(dev, buf))
-		return;
+	kfree(bitmap);
 
-	printk("hub: local power source is %s\n",
-		(buf[0] & 1) ? "lost (inactive)" : "good");
+	ret = usb_get_hub_status(dev, buffer);
+	if (ret < 0) {
+		err("Unable to get hub status (err = %d)", ret);
+		return -1;
+	}
 
-	printk("hub: %sover current condition exists\n",
-		(buf[0] & 2) ? "" : "no ");
+	hubsts = (struct usb_hub_status *)buffer;
+	dbg("local power source is %s",
+		(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
 
-#if 0
-	for (i = 0; i < hub->nports; i++) {
-		int portstat, portchange;
-		unsigned char portstatus[4];
+	dbg("%sover-current condition exists",
+		(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
 
-		if (usb_get_port_status(dev, i + 1, portstatus))
-			return;
-		portstat = (portstatus[1] << 8) + portstatus[0];
-		portchange = (portstatus[3] << 8) + portstatus[2];
+	usb_hub_power_on(hub);
 
-		printk("hub: port %d status\n", i + 1);
-		printk("hub:  %sdevice present\n", (portstat & 1) ? "" : "no ");
-		printk("hub:  %s\n", (portstat & 2) ? "enabled" : "disabled");
-		printk("hub:  %ssuspended\n", (portstat & 4) ? "" : "not ");
-		printk("hub:  %sover current\n", (portstat & 8) ? "" : "not ");
-		printk("hub:  has %spower\n", (portstat & 0x100) ? "" : "no ");
-		printk("hub:  %s speed\n", (portstat & 0x200) ? "low" : "full");
-	}
-#endif
-
-	/* Enable power to the ports */
-	printk("enabling power on all ports\n");
-	for (i = 0; i < hub->nports; i++)
-		usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+	return 0;
 }
 
-static int hub_probe(struct usb_device *dev)
+static void *hub_probe(struct usb_device *dev, unsigned int i)
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_endpoint_descriptor *endpoint;
 	struct usb_hub *hub;
+	unsigned long flags;
+	unsigned int pipe;
+	int maxp, ret;
 
-	/* We don't handle multi-config hubs */
-	if (dev->descriptor.bNumConfigurations != 1)
-		return -1;
-
-	/* We don't handle multi-interface hubs */
-	if (dev->config[0].bNumInterfaces != 1)
-		return -1;
-
-	interface = &dev->config[0].interface[0];
+	interface = &dev->actconfig->interface[i].altsetting[0];
 
 	/* Is it a hub? */
-	if (interface->bInterfaceClass != 9)
-		return -1;
+	if (interface->bInterfaceClass != USB_CLASS_HUB)
+		return NULL;
+
+	/* Some hubs have a subclass of 1, which AFAICT according to the */
+	/*  specs is not defined, but it works */
 	if ((interface->bInterfaceSubClass != 0) &&
 	    (interface->bInterfaceSubClass != 1))
-		return -1;
+		return NULL;
 
 	/* Multiple endpoints? What kind of mutant ninja-hub is this? */
 	if (interface->bNumEndpoints != 1)
-		return -1;
+		return NULL;
 
 	endpoint = &interface->endpoint[0];
 
 	/* Output endpoint? Curiousier and curiousier.. */
-	if (!(endpoint->bEndpointAddress & 0x80))
-		return -1;
+	if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {
+		err("Device is hub class, but has output endpoint?");
+		return NULL;
+	}
 
 	/* If it's not an interrupt endpoint, we'd better punt! */
-	if ((endpoint->bmAttributes & 3) != 3)
-		return -1;
+	if ((endpoint->bmAttributes & 3) != 3) {
+		err("Device is hub class, but has endpoint other than interrupt?");
+		return NULL;
+	}
 
 	/* We found a hub */
-	printk("USB hub found\n");
+	info("USB hub found");
 
-	if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) {
-		printk("couldn't kmalloc hub struct\n");
-		return -1;
+	hub = kmalloc(sizeof(*hub), GFP_KERNEL);
+	if (!hub) {
+		err("couldn't kmalloc hub struct");
+		return NULL;
 	}
 
 	memset(hub, 0, sizeof(*hub));
 
-	dev->private = hub;
-
 	INIT_LIST_HEAD(&hub->event_list);
 	hub->dev = dev;
 
-	usb_hub_configure(hub);
+	/* Record the new hub's existence */
+	spin_lock_irqsave(&hub_event_lock, flags);
+	INIT_LIST_HEAD(&hub->hub_list);
+	list_add(&hub->hub_list, &hub_list);
+	spin_unlock_irqrestore(&hub_event_lock, flags);
 
-	usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub);
+	if (usb_hub_configure(hub) >= 0) {
+		pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+		maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+		if (maxp > sizeof(hub->buffer))
+			maxp = sizeof(hub->buffer);
+
+		hub->urb = usb_alloc_urb(0);
+		if (!hub->urb) {
+			err("couldn't allocate interrupt urb");
+			goto fail;
+		}
 
-	/* Wake up khubd */
-	wake_up(&usb_hub_wait);
+		FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq,
+			hub, endpoint->bInterval);
+		ret = usb_submit_urb(hub->urb);
+		if (ret) {
+			err("usb_submit_urb failed (%d)", ret);
+			goto fail;
+		}
 
-	return 0;
+		/* Wake up khubd */
+		wake_up(&khubd_wait);
+	}
+
+	return hub;
+
+fail:
+	/* free hub, but first clean up its list. */
+	spin_lock_irqsave(&hub_event_lock, flags);
+
+	/* Delete it and then reset it */
+	list_del(&hub->event_list);
+	INIT_LIST_HEAD(&hub->event_list);
+	list_del(&hub->hub_list);
+	INIT_LIST_HEAD(&hub->hub_list);
+
+	spin_unlock_irqrestore(&hub_event_lock, flags);
+
+	kfree(hub);
+
+	return NULL;
 }
 
-static void hub_disconnect(struct usb_device *dev)
+static void hub_disconnect(struct usb_device *dev, void *ptr)
 {
-	struct usb_hub *hub = dev->private;
+	struct usb_hub *hub = (struct usb_hub *)ptr;
 	unsigned long flags;
 
 	spin_lock_irqsave(&hub_event_lock, flags);
@@ -222,201 +319,481 @@
 	/* Delete it and then reset it */
 	list_del(&hub->event_list);
 	INIT_LIST_HEAD(&hub->event_list);
+	list_del(&hub->hub_list);
+	INIT_LIST_HEAD(&hub->hub_list);
 
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 
+	if (hub->urb) {
+		usb_unlink_urb(hub->urb);
+		usb_free_urb(hub->urb);
+		hub->urb = NULL;
+	}
+
 	/* Free the memory */
 	kfree(hub);
 }
 
+static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data)
+{
+	/* assert ifno == 0 (part of hub spec) */
+	switch (code) {
+	case USBDEVFS_HUB_PORTINFO: {
+		struct usbdevfs_hub_portinfo *info = user_data;
+		unsigned long flags;
+		int i;
+
+		spin_lock_irqsave (&hub_event_lock, flags);
+		if (hub->devnum <= 0)
+			info->nports = 0;
+		else {
+			info->nports = hub->maxchild;
+			for (i = 0; i < info->nports; i++) {
+				if (hub->children [i] == NULL)
+					info->port [i] = 0;
+				else
+					info->port [i] = hub->children [i]->devnum;
+			}
+		}
+		spin_unlock_irqrestore (&hub_event_lock, flags);
+
+		return info->nports + 1;
+		}
+
+	default:
+		return -ENOSYS;
+	}
+}
+
 static void usb_hub_port_connect_change(struct usb_device *hub, int port)
 {
 	struct usb_device *usb;
-	unsigned char buf[4];
+	struct usb_port_status portsts;
 	unsigned short portstatus, portchange;
+	int ret, tries;
 
-	usb_disconnect(&hub->children[port]);
+	wait_ms(100);
 
-	usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
-
-	wait_ms(50);	/* FIXME: This is from the *BSD stack, thanks! :) */
-
-	if (usb_get_port_status(hub, port + 1, buf)) {
-		printk("get_port_status failed\n");
+	ret = usb_get_port_status(hub, port + 1, &portsts);
+	if (ret < 0) {
+		err("get_port_status(%d) failed (err = %d)", port + 1, ret);
 		return;
 	}
 
-	portstatus = *((unsigned short *)buf + 0);
-	portchange = *((unsigned short *)buf + 1);
+	portstatus = le16_to_cpu(portsts.wPortStatus);
+	portchange = le16_to_cpu(portsts.wPortChange);
+	dbg("portstatus %x, change %x, %s", portstatus, portchange,
+		portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s");
+
+	/* Clear the connection change status */
+	usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+	/* Disconnect any existing devices under this port */
+	if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+	     (!(portstatus & USB_PORT_STAT_ENABLE)))|| (hub->children[port])) {
+		usb_disconnect(&hub->children[port]);
+		/* Return now if nothing is connected */
+		if (!(portstatus & USB_PORT_STAT_CONNECTION))
+			return;
+	}
+	wait_ms(400);
+
+	down(&usb_address0_sem);
 
-	if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
-		(!(portstatus & USB_PORT_STAT_ENABLE))) {
-		/* We're done now, we already disconnected the device */
-		/* printk("not connected/enabled\n"); */
-		return;
+#define MAX_TRIES 5
+	/* Reset the port */
+	for (tries = 0; tries < MAX_TRIES ; tries++) {
+		usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+		wait_ms(200);
+
+		ret = usb_get_port_status(hub, port + 1, &portsts);
+		if (ret < 0) {
+			err("get_port_status(%d) failed (err = %d)", port + 1, ret);
+			goto out;
+		}
+
+		portstatus = le16_to_cpu(portsts.wPortStatus);
+		portchange = le16_to_cpu(portsts.wPortChange);
+		dbg("portstatus %x, change %x, %s", portstatus ,portchange,
+			portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s");
+
+		if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
+		    !(portstatus & USB_PORT_STAT_CONNECTION))
+			goto out;
+
+		if (portstatus & USB_PORT_STAT_ENABLE)
+			break;
+
+		wait_ms(200);
 	}
 
-	usb = hub->bus->op->allocate(hub);
-	if (!usb) {
-		printk("couldn't allocate usb_device\n");
-		return;
+	if (tries >= MAX_TRIES) {
+		err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES);
+		err("Maybe the USB cable is bad?");
+		goto out;
 	}
 
-	usb_connect(usb);
+	usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
+
+	/* Allocate a new device struct for it */
+	usb = usb_alloc_dev(hub, hub->bus);
+	if (!usb) {
+		err("couldn't allocate usb_device");
+		goto out;
+	}
 
 	usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
 
 	hub->children[port] = usb;
 
-	usb_new_device(usb);
+	/* Find a new device ID for it */
+	usb_connect(usb);
+
+	/* Run it through the hoops (find a driver, etc) */
+	ret = usb_new_device(usb);
+	if (ret) {
+		/* Try resetting the device. Windows does this and it */
+		/*  gets some devices working correctly */
+		usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+		ret = usb_new_device(usb);
+		if (ret) {
+			usb_disconnect(&hub->children[port]);
+
+			/* Woops, disable the port */
+			dbg("hub: disabling port %d", port + 1);
+			usb_clear_port_feature(hub, port + 1,
+				USB_PORT_FEAT_ENABLE);
+		}
+	}
+
+out:
+	up(&usb_address0_sem);
 }
 
 static void usb_hub_events(void)
 {
 	unsigned long flags;
-	unsigned char buf[4];
-	unsigned short portstatus, portchange;
 	int i;
-	struct list_head *next, *tmp, *head = &hub_event_list;
+	struct list_head *tmp;
 	struct usb_device *dev;
 	struct usb_hub *hub;
+	struct usb_hub_status hubsts;
+	unsigned short hubstatus, hubchange;
 
-	spin_lock_irqsave(&hub_event_lock, flags);
+	/*
+	 *  We restart the list everytime to avoid a deadlock with
+	 * deleting hubs downstream from this one. This should be
+	 * safe since we delete the hub from the event list.
+	 * Not the most efficient, but avoids deadlocks.
+	 */
+	while (1) {
+		spin_lock_irqsave(&hub_event_lock, flags);
+
+		if (list_empty(&hub_event_list))
+			goto he_unlock;
+
+		/* Grab the next entry from the beginning of the list */
+		tmp = hub_event_list.next;
 
-	tmp = head->next;
-	while (tmp != head) {
 		hub = list_entry(tmp, struct usb_hub, event_list);
 		dev = hub->dev;
 
-		next = tmp->next;
-
 		list_del(tmp);
 		INIT_LIST_HEAD(tmp);
 
+		spin_unlock_irqrestore(&hub_event_lock, flags);
+
 		for (i = 0; i < hub->nports; i++) {
-			if (usb_get_port_status(dev, i + 1, buf)) {
-				printk("get_port_status failed\n");
+			struct usb_port_status portsts;
+			unsigned short portstatus, portchange;
+
+			if (usb_get_port_status(dev, i + 1, &portsts) < 0) {
+				err("get_port_status failed");
 				continue;
 			}
 
-			portstatus = *((unsigned short *)buf + 0);
-			portchange = *((unsigned short *)buf + 1);
+			portstatus = le16_to_cpu(portsts.wPortStatus);
+			portchange = le16_to_cpu(portsts.wPortChange);
 
 			if (portchange & USB_PORT_STAT_C_CONNECTION) {
-				printk("hub: port %d connection change\n", i + 1);
-
-				usb_clear_port_feature(dev, i + 1,
-					USB_PORT_FEAT_C_CONNECTION);
+				dbg("port %d connection change", i + 1);
 
 				usb_hub_port_connect_change(dev, i);
 			}
 
 			if (portchange & USB_PORT_STAT_C_ENABLE) {
-				printk("hub: port %d enable change\n", i + 1);
-				usb_clear_port_feature(dev, i + 1,
-					USB_PORT_FEAT_C_ENABLE);
-			}
+				dbg("port %d enable change, status %x", i + 1, portstatus);
+				usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE);
 
-			if (portchange & USB_PORT_STAT_C_SUSPEND)
-				printk("hub: port %d suspend change\n", i + 1);
+				// EM interference sometimes causes bad shielded USB devices to 
+				// be shutdown by the hub, this hack enables them again.
+				// Works at least with mouse driver. 
+				if (!(portstatus & USB_PORT_STAT_ENABLE) && 
+				    (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) {
+					err("already running port %i disabled by hub (EMI?), re-enabling...",
+						i + 1);
+					usb_hub_port_connect_change(dev, i);
+				}
+			}
 
-			if (portchange & USB_PORT_STAT_C_OVERCURRENT)
-				printk("hub: port %d over-current change\n", i + 1);
+			if (portstatus & USB_PORT_STAT_SUSPEND) {
+				dbg("port %d suspend change", i + 1);
+				usb_clear_port_feature(dev, i + 1,  USB_PORT_FEAT_SUSPEND);
+			}
+			
+			if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+				err("port %d over-current change", i + 1);
+				usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+				usb_hub_power_on(hub);
+			}
 
 			if (portchange & USB_PORT_STAT_C_RESET) {
-				printk("hub: port %d reset change\n", i + 1);
-				usb_clear_port_feature(dev, i + 1,
-					USB_PORT_FEAT_C_RESET);
+				dbg("port %d reset change", i + 1);
+				usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET);
 			}
+		} /* end for i */
 
-#if 0
-			if (!portchange)
-				continue;
-
-			if (usb_get_port_status(dev, i + 1, buf))
-				return;
-
-			portstatus = (buf[1] << 8) + buf[0];
-			portchange = (buf[3] << 8) + buf[2];
-
-			printk("hub: port %d status\n", i + 1);
-			printk("hub:  %sdevice present\n", (portstatus & 1) ? "" : "no ");
-			printk("hub:  %s\n", (portstatus & 2) ? "enabled" : "disabled");
-			printk("hub:  %ssuspended\n", (portstatus & 4) ? "" : "not ");
-			printk("hub:  %sover current\n", (portstatus & 8) ? "" : "not ");
-			printk("hub:  has %spower\n", (portstatus & 0x100) ? "" : "no ");
-			printk("hub:  %s speed\n", (portstatus & 0x200) ? "low" : "full");
-#endif
+		/* deal with hub status changes */
+		if (usb_get_hub_status(dev, &hubsts) < 0) {
+			err("get_hub_status failed");
+		} else {
+			hubstatus = le16_to_cpup(&hubsts.wHubStatus);
+			hubchange = le16_to_cpup(&hubsts.wHubChange);
+			if (hubchange & HUB_CHANGE_LOCAL_POWER) {
+				dbg("hub power change");
+				usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER);
+			}
+			if (hubchange & HUB_CHANGE_OVERCURRENT) {
+				dbg("hub overcurrent change");
+				wait_ms(500); //Cool down
+				usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
+                        	usb_hub_power_on(hub);
+			}
 		}
-		tmp = next;
-#if 0
-		wait_ms(1000);
-#endif
-        }
+        } /* end while (1) */
 
+he_unlock:
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 }
 
 static int usb_hub_thread(void *__hub)
 {
+	khubd_running = 1;
+
 	lock_kernel();
 
 	/*
 	 * This thread doesn't need any user-level access,
 	 * so get rid of all our resources
 	 */
-	printk("usb_hub_thread at %p\n", &usb_hub_thread);
-	exit_mm(current);
-	exit_files(current);
-	exit_fs(current);
+	exit_files(current);  /* daemonize doesn't do exit_files */
+	current->files = init_task.files;
+	atomic_inc(&current->files->count);
+	daemonize();
 
 	/* Setup a nice name */
 	strcpy(current->comm, "khubd");
 
 	/* Send me a signal to get me die (for debugging) */
 	do {
-		interruptible_sleep_on(&usb_hub_wait);
 		usb_hub_events();
+		interruptible_sleep_on(&khubd_wait);
 	} while (!signal_pending(current));
 
-	printk("usb_hub_thread exiting\n");
+	dbg("usb_hub_thread exiting");
+	khubd_running = 0;
 
 	return 0;
 }
 
 static struct usb_driver hub_driver = {
-	"hub",
-	hub_probe,
-	hub_disconnect,
-	{ NULL, NULL }
+	name:		"hub",
+	probe:		hub_probe,
+	ioctl:		hub_ioctl,
+	disconnect:	hub_disconnect
 };
 
 /*
  * This should be a separate module.
  */
-int hub_init(void)
+int usb_hub_init(void)
 {
 	int pid;
 
-	INIT_LIST_HEAD(&hub_event_list);
+	if (usb_register(&hub_driver) < 0) {
+		err("Unable to register USB hub driver");
+		return -1;
+	}
 
-	usb_register(&hub_driver);
-	pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	pid = kernel_thread(usb_hub_thread, NULL,
+		CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	if (pid >= 0) {
 		khubd_pid = pid;
+
 		return 0;
 	}
 
 	/* Fall through if kernel_thread failed */
 	usb_deregister(&hub_driver);
 
-	return 0;
+	return -1;
 }
 
-void hub_cleanup(void)
+void usb_hub_cleanup(void)
 {
-	if (khubd_pid >= 0)
-		kill_proc(khubd_pid, SIGINT, 1);
+	int ret;
+
+	/* Kill the thread */
+	ret = kill_proc(khubd_pid, SIGTERM, 1);
+	if (!ret) {
+		/* Wait 10 seconds */
+		int count = 10 * 100;
+
+		while (khubd_running && --count) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+
+		if (!count)
+			err("giving up on killing khubd");
+	}
 
+	/*
+	 * Hub resources are freed for us by usb_deregister.  It
+	 * usb_driver_purge on every device which in turn calls that
+	 * devices disconnect function if it is using this driver.
+	 * The hub_disconnect function takes care of releasing the
+	 * individual hub resources. -greg
+	 */
 	usb_deregister(&hub_driver);
+} /* usb_hub_cleanup() */
+
+/*
+ * WARNING - If a driver calls usb_reset_device, you should simulate a
+ * disconnect() and probe() for other interfaces you doesn't claim. This
+ * is left up to the driver writer right now. This insures other drivers
+ * have a chance to re-setup their interface.
+ *
+ * Take a look at proc_resetdevice in devio.c for some sample code to
+ * do this.
+ */
+int usb_reset_device(struct usb_device *dev)
+{
+	struct usb_device *parent = dev->parent;
+	struct usb_device_descriptor descriptor;
+	int i, ret, port = -1;
+
+	if (!parent) {
+		err("attempting to reset root hub!");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < parent->maxchild; i++)
+		if (parent->children[i] == dev) {
+			port = i;
+			break;
+		}
+
+	if (port < 0)
+		return -ENOENT;
+
+	down(&usb_address0_sem);
+
+	/* Send a reset to the device */
+	usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET);
+
+	wait_ms(200);
+
+	usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET);
+
+	/* Reprogram the Address */
+	ret = usb_set_address(dev);
+	if (ret < 0) {
+		err("USB device not accepting new address (error=%d)", ret);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		up(&usb_address0_sem);
+		return ret;
+	}
+
+	wait_ms(10);	/* Let the SET_ADDRESS settle */
+
+	up(&usb_address0_sem);
+
+	/*
+	 * Now we fetch the configuration descriptors for the device and
+	 * see if anything has changed. If it has, we dump the current
+	 * parsed descriptors and reparse from scratch. Then we leave
+	 * the device alone for the caller to finish setting up.
+	 *
+	 * If nothing changed, we reprogram the configuration and then
+	 * the alternate settings.
+	 */
+	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor,
+			sizeof(descriptor));
+	if (ret < 0)
+		return ret;
+
+	le16_to_cpus(&descriptor.bcdUSB);
+	le16_to_cpus(&descriptor.idVendor);
+	le16_to_cpus(&descriptor.idProduct);
+	le16_to_cpus(&descriptor.bcdDevice);
+
+	if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) {
+		usb_destroy_configuration(dev);
+
+		ret = usb_get_device_descriptor(dev);
+		if (ret < sizeof(dev->descriptor)) {
+			if (ret < 0)
+				err("unable to get device descriptor (error=%d)", ret);
+			else
+				err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret);
+        
+			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+			dev->devnum = -1;
+			return -EIO;
+		}
+
+		ret = usb_get_configuration(dev);
+		if (ret < 0) {
+			err("unable to get configuration (error=%d)", ret);
+			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);
+
+		return 1;
+	} else {
+		ret = usb_set_configuration(dev,
+			dev->actconfig->bConfigurationValue);
+		if (ret < 0) {
+			err("failed to set active configuration (error=%d)",
+				ret);
+			return ret;
+		}
+
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+			struct usb_interface *intf =
+				&dev->actconfig->interface[i];
+			struct usb_interface_descriptor *as =
+				&intf->altsetting[intf->act_altsetting];
+
+			ret = usb_set_interface(dev, as->bInterfaceNumber,
+				as->bAlternateSetting);
+			if (ret < 0) {
+				err("failed to set active alternate setting for interface %d (error=%d)", i, ret);
+				return ret;
+			}
+		}
+
+		return 0;
+	}
+
+	return 0;
 }
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.h linux.18p17/drivers/usb/hub.h
--- linux.vanilla/drivers/usb/hub.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/hub.h	Fri Sep  8 17:20:01 2000
@@ -4,7 +4,14 @@
 #include <linux/list.h>
 
 /*
- * Hub feature numbers
+ * Hub request types
+ */
+
+#define USB_RT_HUB	(USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT	(USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+/*
+ * Hub Class feature numbers
  */
 #define C_HUB_LOCAL_POWER	0
 #define C_HUB_OVER_CURRENT	1
@@ -12,6 +19,7 @@
 /*
  * Port feature numbers
  */
+#define USB_PORT_FEAT_CONNECTION	0
 #define USB_PORT_FEAT_ENABLE		1
 #define USB_PORT_FEAT_SUSPEND		2
 #define USB_PORT_FEAT_OVER_CURRENT	3
@@ -24,7 +32,12 @@
 #define USB_PORT_FEAT_C_OVER_CURRENT	19
 #define USB_PORT_FEAT_C_RESET		20
 
-/* wPortStatus */
+struct usb_port_status {
+	__u16 wPortStatus;
+	__u16 wPortChange;	
+} __attribute__ ((packed));
+
+/* wPortStatus bits */
 #define USB_PORT_STAT_CONNECTION	0x0001
 #define USB_PORT_STAT_ENABLE		0x0002
 #define USB_PORT_STAT_SUSPEND		0x0004
@@ -33,18 +46,46 @@
 #define USB_PORT_STAT_POWER		0x0100
 #define USB_PORT_STAT_LOW_SPEED		0x0200
 
-/* wPortChange */
+/* wPortChange bits */
 #define USB_PORT_STAT_C_CONNECTION	0x0001
 #define USB_PORT_STAT_C_ENABLE		0x0002
 #define USB_PORT_STAT_C_SUSPEND		0x0004
 #define USB_PORT_STAT_C_OVERCURRENT	0x0008
 #define USB_PORT_STAT_C_RESET		0x0010
 
-/* Characteristics */
+/* wHubCharacteristics (masks) */
 #define HUB_CHAR_LPSM		0x0003
 #define HUB_CHAR_COMPOUND	0x0004
 #define HUB_CHAR_OCPM		0x0018
 
+struct usb_hub_status {
+	__u16 wHubStatus;
+	__u16 wHubChange;
+} __attribute__ ((packed));
+
+/*
+ *Hub Status & Hub Change bit masks
+ */
+#define HUB_STATUS_LOCAL_POWER	0x0001
+#define HUB_STATUS_OVERCURRENT	0x0002
+
+#define HUB_CHANGE_LOCAL_POWER	0x0001
+#define HUB_CHANGE_OVERCURRENT	0x0002
+
+#define HUB_DESCRIPTOR_MAX_SIZE	39	/* enough for 127 ports on a hub */
+
+/* Hub descriptor */
+struct usb_hub_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bNbrPorts;
+	__u16 wHubCharacteristics;
+	__u8  bPwrOn2PwrGood;
+	__u8  bHubContrCurrent;
+	/* DeviceRemovable and PortPwrCtrlMask want to be variable-length 
+	   bitmaps that hold max 256 entries, but for now they're ignored */
+} __attribute__ ((packed));
+
 struct usb_device;
 
 typedef enum {
@@ -67,6 +108,14 @@
 	/* Device structure */
 	struct usb_device *dev;
 
+	/* Interrupt polling pipe */
+	struct urb *urb;
+
+	char buffer[USB_MAXCHILDREN / 8];
+
+	/* List of hubs */
+	struct list_head hub_list;
+
 	/* Temporary event list */
 	struct list_head event_list;
 
@@ -77,4 +126,3 @@
 };
 
 #endif
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ibmcam.c linux.18p17/drivers/usb/ibmcam.c
--- linux.vanilla/drivers/usb/ibmcam.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/ibmcam.c	Sat Sep 23 13:22:07 2000
@@ -0,0 +1,3162 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports IBM C-It Video Camera.
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * 5/24/00 Removed optional (and unnecessary) locking of the driver while
+ * the device remains plugged in. Corrected race conditions in ibmcam_open
+ * and ibmcam_probe() routines using this as a guideline:
+ *
+ * (2) The big kernel lock is automatically released when a process sleeps
+ *   in the kernel and is automatically reacquired on reschedule if the
+ *   process had the lock originally.  Any code that can be compiled as
+ *   a module and is entered with the big kernel lock held *MUST*
+ *   increment the use count to activate the indirect module protection
+ *   before doing anything that might sleep.
+ *
+ *   In practice, this means that all routines that live in modules and
+ *   are invoked under the big kernel lock should do MOD_INC_USE_COUNT
+ *   as their very first action.  And all failure paths from that
+ *   routine must do MOD_DEC_USE_COUNT before returning.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/wrapper.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#include <asm/io.h>
+
+#include "ibmcam.h"
+
+#define	ENABLE_HEXDUMP	0	/* Enable if you need it */
+static int debug = 0;
+
+/* Completion states of the data parser */
+typedef enum {
+	scan_Continue,		/* Just parse next item */
+	scan_NextFrame,		/* Frame done, send it to V4L */
+	scan_Out,		/* Not enough data for frame */
+	scan_EndParse		/* End parsing */
+} scan_state_t;
+
+/* Bit flags (options) */
+#define FLAGS_RETRY_VIDIOCSYNC		(1 << 0)
+#define	FLAGS_MONOCHROME		(1 << 1)
+#define FLAGS_DISPLAY_HINTS		(1 << 2)
+#define FLAGS_OVERLAY_STATS		(1 << 3)
+#define FLAGS_FORCE_TESTPATTERN		(1 << 4)
+#define FLAGS_SEPARATE_FRAMES		(1 << 5)
+#define FLAGS_CLEAN_FRAMES		(1 << 6)
+
+static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+
+/* This is the size of V4L frame that we provide */
+static const int imgwidth = V4L_FRAME_WIDTH_USED;
+static const int imgheight = V4L_FRAME_HEIGHT;
+static const int min_imgwidth  = 8;
+static const int min_imgheight = 4;
+
+static int lighting = 1; /* Medium */
+
+#define SHARPNESS_MIN	0
+#define SHARPNESS_MAX	6
+static int sharpness = 4; /* Low noise, good details */
+
+#define FRAMERATE_MIN	0
+#define FRAMERATE_MAX	6
+static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */
+
+enum {
+	VIDEOSIZE_128x96 = 0,
+	VIDEOSIZE_176x144,
+	VIDEOSIZE_352x288,
+	VIDEOSIZE_320x240,
+	VIDEOSIZE_352x240,
+};
+
+static int videosize = VIDEOSIZE_352x288;
+
+/*
+ * The value of 'scratchbufsize' affects quality of the picture
+ * in many ways. Shorter buffers may cause loss of data when client
+ * is too slow. Larger buffers are memory-consuming and take longer
+ * to work with. This setting can be adjusted, but the default value
+ * should be OK for most desktop users.
+ */
+#define DEFAULT_SCRATCH_BUF_SIZE	(0x10000)	/* 64 KB */
+static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE;
+
+/*
+ * Here we define several initialization variables. They may
+ * be used to automatically set color, hue, brightness and
+ * contrast to desired values. This is particularly useful in
+ * case of webcams (which have no controls and no on-screen
+ * output) and also when a client V4L software is used that
+ * does not have some of those controls. In any case it's
+ * good to have startup values as options.
+ *
+ * These values are all in [0..255] range. This simplifies
+ * operation. Note that actual values of V4L variables may
+ * be scaled up (as much as << 8). User can see that only
+ * on overlay output, however, or through a V4L client.
+ */
+static int init_brightness = 128;
+static int init_contrast = 192;
+static int init_color = 128;
+static int init_hue = 128;
+static int hue_correction = 128;
+
+/* Settings for camera model 2 */
+static int init_model2_rg = -1;
+static int init_model2_rg2 = -1;
+static int init_model2_sat = -1;
+static int init_model2_yb = -1;
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+MODULE_PARM(flags, "i");
+MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=seperate frames, 6=clean frames");
+MODULE_PARM(framerate, "i");
+MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
+MODULE_PARM(lighting, "i");
+MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light");
+MODULE_PARM(sharpness, "i");
+MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)");
+MODULE_PARM(videosize, "i");
+MODULE_PARM_DESC(videosize, "Image size: 0=128x96, 1=176x144, 2=352x288, 3=320x240, 4=352x240 (default=1)");
+MODULE_PARM(init_brightness, "i");
+MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
+MODULE_PARM(init_contrast, "i");
+MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
+MODULE_PARM(init_color, "i");
+MODULE_PARM_DESC(init_color, "Dolor preconfiguration: 0-255 (default=128)");
+MODULE_PARM(init_hue, "i");
+MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
+MODULE_PARM(hue_correction, "i");
+MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
+
+MODULE_PARM(init_model2_rg, "i");
+MODULE_PARM_DESC(init_model2_rg, "Model2 preconfiguration: 0-255 (default=112)");
+MODULE_PARM(init_model2_rg2, "i");
+MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)");
+MODULE_PARM(init_model2_sat, "i");
+MODULE_PARM_DESC(init_model2_sat, "Model2 preconfiguration: 0-255 (default=52)");
+MODULE_PARM(init_model2_yb, "i");
+MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)");
+
+MODULE_AUTHOR ("module author");
+MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000");
+
+/* Still mysterious i2c commands */
+static const unsigned short unknown_88 = 0x0088;
+static const unsigned short unknown_89 = 0x0089;
+static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 };
+static const unsigned short contrast_14 = 0x0014;
+static const unsigned short light_27 = 0x0027;
+static const unsigned short sharp_13 = 0x0013;
+
+/* i2c commands for Model 2 cameras */
+static const unsigned short mod2_brightness = 0x001a;		/* $5b .. $ee; default=$5a */
+static const unsigned short mod2_set_framerate = 0x001c;	/* 0 (fast).. $1F (slow) */
+static const unsigned short mod2_color_balance_rg2 = 0x001e;	/* 0 (red) .. $7F (green) */
+static const unsigned short mod2_saturation = 0x0020;		/* 0 (b/w) - $7F (full color) */
+static const unsigned short mod2_color_balance_yb = 0x0022;	/* 0..$7F, $50 is about right */
+static const unsigned short mod2_color_balance_rg = 0x0024;	/* 0..$7F, $70 is about right */
+static const unsigned short mod2_sensitivity = 0x0028;		/* 0 (min) .. $1F (max) */
+
+#define MAX_IBMCAM	4
+
+struct usb_ibmcam cams[MAX_IBMCAM];
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+#define MDEBUG(x)	do { } while(0)		/* Debug memory management */
+
+static struct usb_driver ibmcam_driver;
+static void usb_ibmcam_release(struct usb_ibmcam *ibmcam);
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none(*pgd)) {
+		pmd = pmd_offset(pgd, adr);
+		if (!pmd_none(*pmd)) {
+			ptep = pte_offset(pmd, adr);
+			pte = *ptep;
+			if (pte_present(pte)) {
+				ret = (unsigned long) page_address(pte_page(pte));
+				ret |= (adr & (PAGE_SIZE - 1));
+			}
+		}
+	}
+	MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));
+	return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+	unsigned long kva, ret;
+
+	kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+	ret = virt_to_bus((void *)kva);
+	MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
+	return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = virt_to_bus((void *)kva);
+	MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
+	return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+	void *mem;
+	unsigned long adr, page;
+
+	/* Round it off to PAGE_SIZE */
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	mem = vmalloc_32(size);
+	if (!mem)
+		return NULL;
+
+	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+		mem_map_reserve(MAP_NR(__va(page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+	unsigned long adr, page;
+
+	if (!mem)
+		return;
+
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	adr=(unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+		mem_map_unreserve(MAP_NR(__va(page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	vfree(mem);
+}
+
+#if ENABLE_HEXDUMP
+static void ibmcam_hexdump(const unsigned char *data, int len)
+{
+	char tmp[80];
+	int i, k;
+
+	for (i=k=0; len > 0; i++, len--) {
+		if (i > 0 && (i%16 == 0)) {
+			printk("%s\n", tmp);
+			k=0;
+		}
+		k += sprintf(&tmp[k], "%02x ", data[i]);
+	}
+	if (k > 0)
+		printk("%s\n", tmp);
+}
+#endif
+
+/*
+ * usb_ibmcam_overlaychar()
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+void usb_ibmcam_overlaychar(
+	struct usb_ibmcam *ibmcam,
+	struct ibmcam_frame *frame,
+	int x, int y, int ch)
+{
+	static const unsigned short digits[16] = {
+		0xF6DE, /* 0 */
+		0x2492, /* 1 */
+		0xE7CE, /* 2 */
+		0xE79E, /* 3 */
+		0xB792, /* 4 */
+		0xF39E, /* 5 */
+		0xF3DE, /* 6 */
+		0xF492, /* 7 */
+		0xF7DE, /* 8 */
+		0xF79E, /* 9 */
+		0x77DA, /* a */
+		0xD75C, /* b */
+		0xF24E, /* c */
+		0xD6DC, /* d */
+		0xF34E, /* e */
+		0xF348  /* f */
+	};
+	unsigned short digit;
+	int ix, iy;
+
+	if ((ibmcam == NULL) || (frame == NULL))
+		return;
+
+	if (ch >= '0' && ch <= '9')
+		ch -= '0';
+	else if (ch >= 'A' && ch <= 'F')
+		ch = 10 + (ch - 'A');
+	else if (ch >= 'a' && ch <= 'f')
+		ch = 10 + (ch - 'a');
+	else
+		return;
+	digit = digits[ch];
+
+	for (iy=0; iy < 5; iy++) {
+		for (ix=0; ix < 3; ix++) {
+			if (digit & 0x8000) {
+				IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF);
+			}
+			digit = digit << 1;
+		}
+	}
+}
+
+/*
+ * usb_ibmcam_overlaystring()
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+void usb_ibmcam_overlaystring(
+	struct usb_ibmcam *ibmcam,
+	struct ibmcam_frame *frame,
+	int x, int y, const char *str)
+{
+	while (*str) {
+		usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str);
+		str++;
+		x += 4; /* 3 pixels character + 1 space */
+	}
+}
+
+/*
+ * usb_ibmcam_overlaystats()
+ *
+ * Overlays important debugging information.
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame)
+{
+	const int y_diff = 8;
+	char tmp[16];
+	int x = 10;
+	int y = 10;
+
+	sprintf(tmp, "%8x", ibmcam->frame_num);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->urb_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->urb_length);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->data_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->header_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->iso_skip_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8lx", ibmcam->iso_err_count);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8x", ibmcam->vpic.colour);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8x", ibmcam->vpic.hue);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+
+	sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8);
+	usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp);
+	y += y_diff;
+}
+
+/*
+ * usb_ibmcam_testpattern()
+ *
+ * Procedure forms a test pattern (yellow grid on blue background).
+ *
+ * Parameters:
+ * fullframe:   if TRUE then entire frame is filled, otherwise the procedure
+ *	      continues from the current scanline.
+ * pmode	0: fill the frame with solid blue color (like on VCR or TV)
+ *	      1: Draw a colored grid
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode)
+{
+	static const char proc[] = "usb_ibmcam_testpattern";
+	struct ibmcam_frame *frame;
+	unsigned char *f;
+	int num_cell = 0;
+	int scan_length = 0;
+	static int num_pass = 0;
+
+	if (ibmcam == NULL) {
+		printk(KERN_ERR "%s: ibmcam == NULL\n", proc);
+		return;
+	}
+	if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) {
+		printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe);
+		return;
+	}
+
+	/* Grab the current frame */
+	frame = &ibmcam->frame[ibmcam->curframe];
+
+	/* Optionally start at the beginning */
+	if (fullframe) {
+		frame->curline = 0;
+		frame->scanlength = 0;
+	}
+
+	/* Form every scan line */
+	for (; frame->curline < imgheight; frame->curline++) {
+		int i;
+
+		f = frame->data + (imgwidth * 3 * frame->curline);
+		for (i=0; i < imgwidth; i++) {
+			unsigned char cb=0x80;
+			unsigned char cg = 0;
+			unsigned char cr = 0;
+
+			if (pmode == 1) {
+				if (frame->curline % 32 == 0)
+					cb = 0, cg = cr = 0xFF;
+				else if (i % 32 == 0) {
+					if (frame->curline % 32 == 1)
+						num_cell++;
+					cb = 0, cg = cr = 0xFF;
+				} else {
+					cb = ((num_cell*7) + num_pass) & 0xFF;
+					cg = ((num_cell*5) + num_pass*2) & 0xFF;
+					cr = ((num_cell*3) + num_pass*3) & 0xFF;
+				}
+			} else {
+				/* Just the blue screen */
+			}
+				
+			*f++ = cb;
+			*f++ = cg;
+			*f++ = cr;
+			scan_length += 3;
+		}
+	}
+
+	frame->grabstate = FRAME_DONE;
+	frame->scanlength += scan_length;
+	++num_pass;
+
+	/* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */
+	usb_ibmcam_overlaystats(ibmcam, frame);
+}
+
+static unsigned char *ibmcam_model1_find_header(unsigned char hdr_sig, unsigned char *data, int len)
+{
+	while (len >= 4)
+	{
+		if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00))
+		{
+#if 0
+			/* This code helps to detect new frame markers */
+			printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]);
+#endif
+			if (data[3] == hdr_sig) {
+				if (debug > 2)
+					printk(KERN_DEBUG "Header found.\n");
+				return data+4;
+			}
+		}
+		++data;
+		--len;
+	}
+	return NULL;
+}
+
+static unsigned char *ibmcam_model2_find_header(unsigned char hdr_sig, unsigned char *data, int len)
+{
+	int marker_len = 0;
+
+	switch (videosize) {
+	case VIDEOSIZE_176x144:
+		marker_len = 10;
+		break;
+	default:
+		marker_len = 2;
+		break;
+	}
+	while (len >= marker_len)
+	{
+		if ((data[0] == 0x00) && (data[1] == 0xFF))
+		{
+#if 0
+			/* This code helps to detect new frame markers */
+			static int pass = 0;
+			if (pass++ == 0)
+				ibmcam_hexdump(data, (len > 16) ? 16 : len);
+#endif
+			if (debug > 2)
+				printk(KERN_DEBUG "Header found.\n");
+			return data+marker_len;
+		}
+		++data;
+		--len;
+	}
+	return NULL;
+}
+
+/* How much data is left in the scratch buf? */
+#define scratch_left(x)	(ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch))
+
+/* Grab the remaining */
+static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data)
+{
+	unsigned long left;
+
+	left = scratch_left(data);
+	memmove(ibmcam->scratch, data, left);
+	ibmcam->scratchlen = left;
+}
+
+/*
+ * usb_ibmcam_find_header()
+ *
+ * Locate one of supported header markers in the scratch buffer.
+ * Once found, remove all preceding bytes AND the marker (4 bytes)
+ * from the scratch buffer. Whatever follows must be video lines.
+ *
+ * History:
+ * 1/21/00  Created.
+ */
+static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam)
+{
+	struct ibmcam_frame *frame;
+	unsigned char *data, *tmp;
+
+	data = ibmcam->scratch;
+	frame = &ibmcam->frame[ibmcam->curframe];
+
+	if (ibmcam->camera_model == IBMCAM_MODEL_1)
+		tmp = ibmcam_model1_find_header(frame->hdr_sig, data, scratch_left(data));
+	else if (ibmcam->camera_model == IBMCAM_MODEL_2)
+		tmp = ibmcam_model2_find_header(frame->hdr_sig, data, scratch_left(data));
+	else
+		tmp = NULL;
+
+	if (tmp == NULL) {
+		/* No header - entire scratch buffer is useless! */
+		if (debug > 2)
+			printk(KERN_DEBUG "Skipping frame, no header\n");
+		ibmcam->scratchlen = 0;
+		return scan_EndParse;
+	}
+	/* Header found */
+	data = tmp;
+
+	ibmcam->has_hdr = 1;
+	ibmcam->header_count++;
+	frame->scanstate = STATE_LINES;
+	frame->curline = 0;
+
+	if (flags & FLAGS_FORCE_TESTPATTERN) {
+		usb_ibmcam_testpattern(ibmcam, 1, 1);
+		return scan_NextFrame;
+	}
+	usb_ibmcam_align_scratch(ibmcam, data);
+	return scan_Continue;
+}
+
+/*
+ * usb_ibmcam_parse_lines()
+ *
+ * Parse one line (TODO: more than one!) from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ * History:
+ * 1/21/00  Created.
+ */
+static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen)
+{
+	struct ibmcam_frame *frame;
+	unsigned char *data, *f, *chromaLine;
+	unsigned int len;
+	const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL;	/* V4L line offset */
+	const int hue_corr  = (ibmcam->vpic.hue - 0x8000) >> 10;	/* -32..+31 */
+	const int hue2_corr = (hue_correction - 128) / 4;		/* -32..+31 */
+	const int ccm = 128; /* Color correction median - see below */
+	int y, u, v, i, frame_done=0, mono_plane, color_corr;
+
+	color_corr = (ibmcam->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+	RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1);
+	data = ibmcam->scratch;
+	frame = &ibmcam->frame[ibmcam->curframe];
+
+	len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */
+	/*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/
+
+	mono_plane = ((frame->curline & 1) == 0);
+
+	/*
+	 * Lines are organized this way (or are they?)
+	 *
+	 * I420:
+	 * ~~~~
+	 * ___________________________________
+	 * |-----Y-----|---UVUVUV...UVUV-----| \
+	 * |-----------+---------------------|  \
+	 * |<-- 176 -->|<------ 176*2 ------>|  Total 72. pairs of lines
+	 * |...	   ...	     ...|  /
+	 * |___________|_____________________| /
+	 *  - odd line- ------- even line ---
+	 *
+	 * another format:
+	 * ~~~~~~~~~~~~~~
+	 * ___________________________________
+	 * |-----Y-----|---UVUVUV...UVUV-----| \
+	 * |-----------+---------------------|  \
+	 * |<-- 352 -->|<------ 352*2 ------>|  Total 144. pairs of lines
+	 * |...	   ...	     ...|  /
+	 * |___________|_____________________| /
+	 *  - odd line- ------- even line ---
+	 */
+
+	/* Make sure there's enough data for the entire line */
+	if (scratch_left(data) < (len+1024)) {
+		/*printk(KERN_DEBUG "out of data, need %u.\n", len);*/
+		return scan_Out;
+	}
+
+#if 0
+	{	/* This code prints beginning of the source frame */
+		static int pass = 0;
+		if ((pass++ % 3000) == 0)
+			ibmcam_hexdump(data, 16);
+	}
+#endif
+
+#if 0
+	if (frame->curline == 10 || frame->curline == 11) {
+		/* This code prints beginning of 10th (mono), 11th (chroma) line */
+		static int pass = 0;
+		if ((pass % 100) == 0)
+			ibmcam_hexdump(data, 16);
+		if (frame->curline == 11)
+			pass++;
+	}
+#endif
+	/*
+	 * Make sure that our writing into output buffer
+	 * will not exceed the buffer. Mind that we may write
+	 * not into current output scanline but in several after
+	 * it as well (if we enlarge image vertically.)
+	 */
+	if ((frame->curline + 1) >= V4L_FRAME_HEIGHT)
+		return scan_NextFrame;
+
+	/*
+	 * Now we are sure that entire line (representing all 'frame->frmwidth'
+	 * pixels from the camera) is available in the scratch buffer. We
+	 * start copying the line left-aligned to the V4L buffer (which
+	 * might be larger - not smaller, hopefully). If the camera
+	 * line is shorter then we should pad the V4L buffer with something
+	 * (black in this case) to complete the line.
+	 */
+	f = frame->data + (v4l_linesize * frame->curline);
+
+	/*
+	 * chromaLine points to 1st pixel of the line with chrominance.
+	 * If current line is monochrome then chromaLine points to next
+	 * line after monochrome one. If current line has chrominance
+	 * then chromaLine points to this very line. Such method allows
+	 * to access chrominance data uniformly.
+	 *
+	 * To obtain chrominance data from the 'chromaLine' use this:
+	 *   v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]...
+	 *   u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]...
+	 *
+	 * Indices must be calculated this way:
+	 * v_index = (i >> 1) << 2;
+	 * u_index = (i >> 1) << 2 + 2;
+	 *
+	 * where 'i' is the column number [0..frame->frmwidth-1]
+	 */
+	chromaLine = data;
+	if (mono_plane)
+		chromaLine += frame->frmwidth;
+
+	for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2))
+	{
+		unsigned char rv, gv, bv;	/* RGB components */
+
+		/*
+		 * Search for potential Start-Of-Frame marker. It should
+		 * not be here, of course, but if your formats don't match
+		 * you might exceed the frame. We must match the marker to
+		 * each byte of multi-byte data element if it is multi-byte.
+		 */
+#if 1
+		if ((ibmcam->camera_model == IBMCAM_MODEL_1) && (scratch_left(data) >= (4+2))) {
+			unsigned char *dp;
+			int j;
+
+			for (j=0, dp=data; j < 2; j++, dp++) {
+				if ((dp[0] == 0x00) && (dp[1] == 0xFF) &&
+				    (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) {
+					ibmcam->has_hdr = 2;
+					frame_done++;
+					break;
+				}
+			}
+		}
+#endif
+
+		/* Check for various visual debugging hints (colorized pixels) */
+		if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) {
+			/*
+			 * This is bad and should not happen. This means that
+			 * we somehow overshoot the line and encountered new
+			 * frame! Obviously our camera/V4L frame size is out
+			 * of whack. This cyan dot will help you to figure
+			 * out where exactly the new frame arrived.
+			 */
+			if (ibmcam->has_hdr == 1) {
+				bv = 0; /* Yellow marker */
+				gv = 0xFF;
+				rv = 0xFF;
+			} else {
+				bv = 0xFF; /* Cyan marker */
+				gv = 0xFF;
+				rv = 0;
+			}
+			ibmcam->has_hdr = 0;
+			goto make_pixel;
+		}
+
+		if (mono_plane || frame->order_yc)
+			y = data[0];
+		else
+			y = data[1];
+
+		if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
+			rv = gv = bv = y;
+		else {
+			int off_0, off_2;
+
+			off_0 = (i >> 1) << 2;
+			off_2 = off_0 + 2;
+
+			if (frame->order_yc) {
+				off_0++;
+				off_2++;
+			}
+			if (!frame->order_uv) {
+				off_0 += 2;
+				off_2 -= 2;
+			}
+			u = chromaLine[off_0] + hue_corr;
+			v = chromaLine[off_2] + hue2_corr;
+
+			/* Apply color correction */
+			if (color_corr != 0) {
+				/* Magnify up to 2 times, reduce down to zero saturation */
+				u = 128 + ((ccm + color_corr) * (u - 128)) / ccm;
+				v = 128 + ((ccm + color_corr) * (v - 128)) / ccm;
+			}
+			YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv);
+		}
+
+	make_pixel:
+		/*
+		 * The purpose of creating the pixel here, in one,
+		 * dedicated place is that we may need to make the
+		 * pixel wider and taller than it actually is. This
+		 * may be used if camera generates small frames for
+		 * sake of frame rate (or any other reason.)
+		 *
+		 * The output data consists of B, G, R bytes
+		 * (in this order).
+		 */
+#if USES_IBMCAM_PUTPIXEL
+		IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+#else
+		*f++ = bv;
+		*f++ = gv;
+		*f++ = rv;
+#endif
+		/*
+		 * Typically we do not decide within a legitimate frame
+		 * that we want to end the frame. However debugging code
+		 * may detect marker of new frame within the data. Then
+		 * this condition activates. The 'data' pointer is already
+		 * pointing at the new marker, so we'd better leave it as is.
+		 */
+		if (frame_done)
+			break;	/* End scanning of lines */
+	}
+	/*
+	 * Account for number of bytes that we wrote into output V4L frame.
+	 * We do it here, after we are done with the scanline, because we
+	 * may fill more than one output scanline if we do vertical
+	 * enlargement.
+	 */
+	frame->curline++;
+	*pcopylen += v4l_linesize;
+	usb_ibmcam_align_scratch(ibmcam, data);
+
+	if (frame_done || (frame->curline >= frame->frmheight))
+		return scan_NextFrame;
+	else
+		return scan_Continue;
+}
+
+/*
+ * usb_ibmcam_model2_parse_lines()
+ *
+ * This procedure deals with a weird RGB format that is produced by IBM
+ * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175,
+ * depending on horizontal size of the picture:
+ *
+ * <--- 160 or 176 pairs of RA,RB bytes ----->
+ * *-----------------------------------------* \
+ * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx |  \
+ * |-----+-----+-----+-----+ ... +-----+-----|   *- This is pair of horizontal lines,
+ * | B0  | G0  | B1  | G1  | ... | Bx  | Gx  |  /   total 240 or 288 lines (120 or 144
+ * |=====+=====+=====+=====+ ... +=====+=====| /    such pairs).
+ *
+ * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1
+ * defines ONE pixel. Therefore this format yields 176x144 "decoded"
+ * resolution at best. I do not know why camera sends such format - the
+ * previous model just used I420 and everyone was happy.
+ *
+ * I do not know what is the difference between RAi and RBi bytes. Both
+ * seemingly represent R component, but slightly vary in value (so that
+ * the picture looks a bit colored if one or another is used). I use
+ * them both as R component in attempt to at least partially recover the
+ * lost resolution.
+ */
+static scan_state_t usb_ibmcam_model2_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen)
+{
+	struct ibmcam_frame *frame;
+	unsigned char *data, *f, *la, *lb;
+	unsigned int len;
+	const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL;	/* V4L line offset */
+	int i, j, frame_done=0, color_corr;
+
+	color_corr = (ibmcam->vpic.colour) >> 8; /* 0..+255 */
+
+	data = ibmcam->scratch;
+	frame = &ibmcam->frame[ibmcam->curframe];
+
+	/* Here we deal with pairs of horizontal lines */
+
+	len = frame->frmwidth * 2; /* 2 lines */
+	/*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/
+
+	/* Make sure there's enough data for the entire line */
+	if (scratch_left(data) < (len+32)) {
+		/*printk(KERN_DEBUG "out of data, need %u.\n", len);*/
+		return scan_Out;
+	}
+
+	/*
+	 * Make sure that our writing into output buffer
+	 * will not exceed the buffer. Mind that we may write
+	 * not into current output scanline but in several after
+	 * it as well (if we enlarge image vertically.)
+	 */
+	if ((frame->curline + 1) >= V4L_FRAME_HEIGHT)
+		return scan_NextFrame;
+
+	if ((frame->curline & 1) == 0) {
+		la = data;
+		lb = data + frame->frmwidth;
+	} else {
+		la = data + frame->frmwidth;
+		lb = data;
+	}
+
+	/*
+	 * Now we are sure that entire line (representing all 'frame->frmwidth'
+	 * pixels from the camera) is available in the scratch buffer. We
+	 * start copying the line left-aligned to the V4L buffer (which
+	 * might be larger - not smaller, hopefully). If the camera
+	 * line is shorter then we should pad the V4L buffer with something
+	 * (black in this case) to complete the line.
+	 */
+	f = frame->data + (v4l_linesize * frame->curline);
+
+	/* Fill the 2-line strip */
+	for (i = 0; i < frame->frmwidth; i++) {
+		int y, rv, gv, bv;	/* RGB components */
+
+		j = i & (~1);
+
+		/* Check for various visual debugging hints (colorized pixels) */
+		if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) {
+			if (ibmcam->has_hdr == 1) {
+				bv = 0; /* Yellow marker */
+				gv = 0xFF;
+				rv = 0xFF;
+			} else {
+				bv = 0xFF; /* Cyan marker */
+				gv = 0xFF;
+				rv = 0;
+			}
+			ibmcam->has_hdr = 0;
+			goto make_pixel;
+		}
+
+		/*
+		 * Here I use RA and RB components, one per physical pixel.
+		 * This causes fine vertical grid on the picture but may improve
+		 * horizontal resolution. If you prefer replicating, use this:
+		 *   rv = la[j + 0];   ... or ... rv = la[j + 1];
+		 * then the pixel will be replicated.
+		 */
+		rv = la[i];
+		gv = lb[j + 1];
+		bv = lb[j + 0];
+
+		y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */
+
+		if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
+			rv = gv = bv = y;
+		else if (color_corr != 128) {
+
+			/* Calculate difference between color and brightness */
+			rv -= y;
+			gv -= y;
+			bv -= y;
+
+			/* Scale differences */
+			rv = (rv * color_corr) / 128;
+			gv = (gv * color_corr) / 128;
+			bv = (bv * color_corr) / 128;
+
+			/* Reapply brightness */
+			rv += y;
+			gv += y;
+			bv += y;
+
+			/* Watch for overflows */
+			RESTRICT_TO_RANGE(rv, 0, 255);
+			RESTRICT_TO_RANGE(gv, 0, 255);
+			RESTRICT_TO_RANGE(bv, 0, 255);
+		}
+
+	make_pixel:
+		IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+		IBMCAM_PUTPIXEL(frame, i, frame->curline+1, rv, gv, bv);
+	}
+	/*
+	 * Account for number of bytes that we wrote into output V4L frame.
+	 * We do it here, after we are done with the scanline, because we
+	 * may fill more than one output scanline if we do vertical
+	 * enlargement.
+	 */
+	frame->curline += 2;
+	*pcopylen += v4l_linesize * 2;
+	data += frame->frmwidth * 2;
+	usb_ibmcam_align_scratch(ibmcam, data);
+
+	if (frame_done || (frame->curline >= frame->frmheight))
+		return scan_NextFrame;
+	else
+		return scan_Continue;
+}
+
+/*
+ * ibmcam_parse_data()
+ *
+ * Generic routine to parse the scratch buffer. It employs either
+ * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most
+ * of work.
+ *
+ * History:
+ * 1/21/00  Created.
+ */
+static void ibmcam_parse_data(struct usb_ibmcam *ibmcam)
+{
+	struct ibmcam_frame *frame;
+	unsigned char *data = ibmcam->scratch;
+	scan_state_t newstate;
+	long copylen = 0;
+
+	/* Grab the current frame and the previous frame */
+	frame = &ibmcam->frame[ibmcam->curframe];
+
+	/* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */
+
+	while (1) {
+
+		newstate = scan_Out;
+		if (scratch_left(data)) {
+			if (frame->scanstate == STATE_SCANNING)
+				newstate = usb_ibmcam_find_header(ibmcam);
+			else if (frame->scanstate == STATE_LINES) {
+				if ((ibmcam->camera_model == IBMCAM_MODEL_2) &&
+				    (videosize >= VIDEOSIZE_352x288)) {
+					newstate = usb_ibmcam_model2_parse_lines(ibmcam, &copylen);
+				}
+				else {
+					newstate = usb_ibmcam_parse_lines(ibmcam, &copylen);
+				}
+			}
+		}
+		if (newstate == scan_Continue)
+			continue;
+		else if ((newstate == scan_NextFrame) || (newstate == scan_Out))
+			break;
+		else
+			return; /* scan_EndParse */
+	}
+
+	if (newstate == scan_NextFrame) {
+		frame->grabstate = FRAME_DONE;
+		ibmcam->curframe = -1;
+		ibmcam->frame_num++;
+
+		/* Optionally display statistics on the screen */
+		if (flags & FLAGS_OVERLAY_STATS)
+			usb_ibmcam_overlaystats(ibmcam, frame);
+
+		/* This will cause the process to request another frame. */
+		if (waitqueue_active(&frame->wq))
+			wake_up_interruptible(&frame->wq);
+	}
+
+	/* Update the frame's uncompressed length. */
+	frame->scanlength += copylen;
+}
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb)
+{
+	unsigned char *cdata, *data, *data0;
+	int i, totlen = 0;
+
+	data = data0 = ibmcam->scratch + ibmcam->scratchlen;
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+		
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		/* Detect and ignore errored packets */
+		if (st < 0) {
+			if (debug >= 1) {
+				printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n",
+				       i, n, st);
+			}
+			ibmcam->iso_err_count++;
+			continue;
+		}
+
+		/* Detect and ignore empty packets */
+		if (n <= 0) {
+			ibmcam->iso_skip_count++;
+			continue;
+		}
+
+		/*
+		 * If camera continues to feed us with data but there is no
+		 * consumption (if, for example, V4L client fell asleep) we
+		 * may overflow the buffer. We have to move old data over to
+		 * free room for new data. This is bad for old data. If we
+		 * just drop new data then it's bad for new data... choose
+		 * your favorite evil here.
+		 */
+		if ((ibmcam->scratchlen + n) > scratchbufsize) {
+#if 0
+			ibmcam->scratch_ovf_count++;
+			if (debug >= 3)
+				printk(KERN_ERR "ibmcam: scratch buf overflow! "
+				       "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
+			return totlen;
+#else
+			int mv;
+
+			ibmcam->scratch_ovf_count++;
+			if (debug >= 3) {
+				printk(KERN_ERR "ibmcam: scratch buf overflow! "
+				       "scr_len: %d, n: %d\n", ibmcam->scratchlen, n );
+			}
+			mv  = (ibmcam->scratchlen + n) - scratchbufsize;
+			if (ibmcam->scratchlen >= mv) {
+				int newslen = ibmcam->scratchlen - mv;
+				memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen);
+				ibmcam->scratchlen = newslen;
+				data = data0 = ibmcam->scratch + ibmcam->scratchlen;
+			} else {
+				printk(KERN_ERR "ibmcam: scratch buf too small\n");
+				return totlen;
+			}
+#endif
+		}
+
+		/* Now we know that there is enough room in scratch buffer */
+		memmove(data, cdata, n);
+		data += n;
+		totlen += n;
+		ibmcam->scratchlen += n;
+	}
+#if 0
+	if (totlen > 0) {
+		static int foo=0;
+		if (foo < 1) {
+			printk(KERN_DEBUG "+%d.\n", totlen);
+			ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen);
+			++foo;
+		}
+	}
+#endif
+	return totlen;
+}
+
+static void ibmcam_isoc_irq(struct urb *urb)
+{
+	int len;
+	struct usb_ibmcam *ibmcam = urb->context;
+	struct ibmcam_sbuf *sbuf;
+	int i;
+
+	/* We don't want to do anything if we are about to be removed! */
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return;
+
+#if 0
+	if (urb->actual_length > 0) {
+		printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, "
+		       " errcount = %d, length = %d\n", urb, urb->status,
+		       urb->error_count, urb->actual_length);
+	} else {
+		static int c = 0;
+		if (c++ % 100 == 0)
+			printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n");
+	}
+#endif
+
+	if (!ibmcam->streaming) {
+		if (debug >= 1)
+			printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n");
+		return;
+	}
+	
+	sbuf = &ibmcam->sbuf[ibmcam->cursbuf];
+
+	/* Copy the data received into our scratch buffer */
+	len = ibmcam_compress_isochronous(ibmcam, urb);
+
+	ibmcam->urb_count++;
+	ibmcam->urb_length = len;
+	ibmcam->data_count += len;
+
+#if 0   /* This code prints few initial bytes of ISO data: used to decode markers */
+	if (ibmcam->urb_count % 64 == 1) {
+		if (ibmcam->urb_count == 1) {
+			ibmcam_hexdump(ibmcam->scratch,
+				       (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen);
+		}
+	}
+#endif
+
+	/* If we collected enough data let's parse! */
+	if (ibmcam->scratchlen) {
+		/* If we don't have a frame we're current working on, complain */
+		if (ibmcam->curframe >= 0)
+			ibmcam_parse_data(ibmcam);
+		else {
+			if (debug >= 1)
+				printk(KERN_DEBUG "ibmcam: received data, but no frame available\n");
+		}
+	}
+
+	for (i = 0; i < FRAMES_PER_DESC; i++) {
+		sbuf->urb->iso_frame_desc[i].status = 0;
+		sbuf->urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	/* Move to the next sbuf */
+	ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF;
+
+	return;
+}
+
+/*
+ * usb_ibmcam_veio()
+ *
+ * History:
+ * 1/27/00  Added check for dev == NULL; this happens if camera is unplugged.
+ */
+static int usb_ibmcam_veio(
+	struct usb_ibmcam *ibmcam,
+	unsigned char req,
+	unsigned short value,
+	unsigned short index)
+{
+	static const char proc[] = "usb_ibmcam_veio";
+	unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
+	int i;
+
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return 0;
+
+	if (req == 1) {
+		i = usb_control_msg(
+			ibmcam->dev,
+			usb_rcvctrlpipe(ibmcam->dev, 0),
+			req,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+			value,
+			index,
+			cp,
+			sizeof(cp),
+			HZ);
+#if 0
+		printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+		       "(req=$%02x val=$%04x ind=$%04x)\n",
+		       cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+		       req, value, index);
+#endif
+	} else {
+		i = usb_control_msg(
+			ibmcam->dev,
+			usb_sndctrlpipe(ibmcam->dev, 0),
+			req,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+			value,
+			index,
+			NULL,
+			0,
+			HZ);
+	}
+	if (i < 0) {
+		printk(KERN_ERR "%s: ERROR=%d. Camera stopped - "
+		       "reconnect or reload driver.\n", proc, i);
+		ibmcam->last_error = i;
+	}
+	return i;
+}
+
+/*
+ * usb_ibmcam_calculate_fps()
+ *
+ * This procedure roughly calculates the real frame rate based
+ * on FPS code (framerate=NNN option). Actual FPS differs
+ * slightly depending on lighting conditions, so that actual frame
+ * rate is determined by the camera. Since I don't know how to ask
+ * the camera what FPS is now I have to use the FPS code instead.
+ *
+ * The FPS code is in range [0..6], 0 is slowest, 6 is fastest.
+ * Corresponding real FPS should be in range [3..30] frames per second.
+ * The conversion formula is obvious:
+ *
+ * real_fps = 3 + (fps_code * 4.5)
+ *
+ * History:
+ * 1/18/00  Created.
+ */
+static int usb_ibmcam_calculate_fps(void)
+{
+	return 3 + framerate*4 + framerate/2;
+}
+
+/*
+ * usb_ibmcam_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+static void usb_ibmcam_send_FF_04_02(struct usb_ibmcam *ibmcam)
+{
+	usb_ibmcam_veio(ibmcam, 0, 0x00FF, 0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+}
+
+static void usb_ibmcam_send_00_04_06(struct usb_ibmcam *ibmcam)
+{
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0006, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_send_x_00(ibmcam, x);
+	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+}
+
+static void usb_ibmcam_send_x_01_00_05(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02_01(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
+}
+
+static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_ibmcam *ibmcam, unsigned short x)
+{
+	usb_ibmcam_veio(ibmcam, 0, x,      0x0127);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124);
+}
+
+static void usb_ibmcam_Packet_Format1(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val)
+{
+	usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
+	usb_ibmcam_send_x_00_05		(ibmcam, fkey);
+	usb_ibmcam_send_x_00_05_02_08_01(ibmcam, val);
+	usb_ibmcam_send_x_00_05		(ibmcam, unknown_88);
+	usb_ibmcam_send_x_00_05_02_01	(ibmcam, fkey);
+	usb_ibmcam_send_x_00_05		(ibmcam, unknown_89);
+	usb_ibmcam_send_x_00		(ibmcam, fkey);
+	usb_ibmcam_send_00_04_06	(ibmcam);
+	usb_ibmcam_veio			(ibmcam, 1, 0x0000, 0x0126);
+	usb_ibmcam_send_FF_04_02	(ibmcam);
+}
+
+static void usb_ibmcam_PacketFormat2(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val)
+{
+	usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
+	usb_ibmcam_send_x_00_05		(ibmcam, fkey);
+	usb_ibmcam_send_x_00_05_02	(ibmcam, val);
+}
+
+static void usb_ibmcam_model2_Packet2(struct usb_ibmcam *ibmcam)
+{
+	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012d);
+	usb_ibmcam_veio(ibmcam, 0, 0xfea3, 0x0124);
+}
+
+static void usb_ibmcam_model2_Packet1(struct usb_ibmcam *ibmcam, unsigned short v1, unsigned short v2)
+{
+	usb_ibmcam_veio(ibmcam, 0, 0x00aa, 0x012d);
+	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012e);
+	usb_ibmcam_veio(ibmcam, 0, v1, 	   0x012f);
+	usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x0130);
+	usb_ibmcam_veio(ibmcam, 0, 0xc719, 0x0124);
+	usb_ibmcam_veio(ibmcam, 0, v2,     0x0127);
+
+	usb_ibmcam_model2_Packet2(ibmcam);
+}
+
+/*
+ * usb_ibmcam_adjust_contrast()
+ *
+ * The contrast value changes from 0 (high contrast) to 15 (low contrast).
+ * This is in reverse to usual order of things (such as TV controls), so
+ * we reverse it again here.
+ *
+ * TODO: we probably don't need to send the setup 5 times...
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam)
+{
+	unsigned char new_contrast = ibmcam->vpic.contrast >> 12;
+	const int ntries = 5;
+
+	if (new_contrast >= 16)
+		new_contrast = 15;
+	new_contrast = 15 - new_contrast;
+	if (new_contrast != ibmcam->vpic_old.contrast) {
+		ibmcam->vpic_old.contrast = new_contrast;
+		if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+			int i;
+			for (i=0; i < ntries; i++) {
+				usb_ibmcam_Packet_Format1(ibmcam, contrast_14, new_contrast);
+				usb_ibmcam_send_FF_04_02(ibmcam);
+			}
+		} else {
+			/* Camera model 2 does not have this control; implemented in software. */
+		}
+	}
+}
+
+/*
+ * usb_ibmcam_change_lighting_conditions()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS. Lighting is set as a module parameter.
+ *
+ * History:
+ * 1/5/00   Created.
+ * 2/20/00  Added support for Model 2 cameras.
+ */
+static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam)
+{
+	static const char proc[] = "usb_ibmcam_change_lighting_conditions";
+
+	if (debug > 0)
+		printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting);
+
+	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+		const int ntries = 5;
+		int i;
+		for (i=0; i < ntries; i++)
+			usb_ibmcam_Packet_Format1(ibmcam, light_27, (unsigned short) lighting);
+	} else {
+		/*
+		 * This command apparently requires camera to be stopped. My
+		 * experiments showed that it -is- possible to alter the lighting
+		 * conditions setting "on the fly", but why bother? This setting does
+		 * not work reliably in all cases, so I decided simply to leave the
+		 * setting where Xirlink put it - in the camera setup phase. This code
+		 * is commented out because it does not work at -any- moment, so its
+		 * presence makes no sense. You may use it for experiments.
+		 */
+#if 0
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c);	/* Stop camera */
+		usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting);
+		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);	/* Start camera */
+#endif
+	}
+}
+
+/*
+ * usb_ibmcam_set_sharpness()
+ *
+ * Cameras model 1 have internal smoothing feature. It is controlled by value in
+ * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
+ * Recommended value is 4. Cameras model 2 do not have this feature at all.
+ */
+static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam)
+{
+	static const char proc[] = "usb_ibmcam_set_sharpness";
+
+	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+		static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+		unsigned short i, sv;
+
+		RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+		if (debug > 0)
+			printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness);
+
+		sv = sa[sharpness - SHARPNESS_MIN];
+		for (i=0; i < 2; i++) {
+			usb_ibmcam_send_x_01_00_05	(ibmcam, unknown_88);
+			usb_ibmcam_send_x_00_05		(ibmcam, sharp_13);
+			usb_ibmcam_send_x_00_05_02	(ibmcam, sv);
+		}
+	} else {
+		/* Camera model 2 does not have this control */
+	}
+}
+
+/*
+ * usb_ibmcam_set_brightness()
+ *
+ * This procedure changes brightness of the picture.
+ */
+static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam)
+{
+	static const char proc[] = "usb_ibmcam_set_brightness";
+	static const unsigned short n = 1;
+	unsigned short i, j, bv[3];
+
+	bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10;
+	if (bv[0] == (ibmcam->vpic_old.brightness >> 10))
+		return;
+	ibmcam->vpic_old.brightness = ibmcam->vpic.brightness;
+
+	if (debug > 0)
+		printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n",
+		       proc, bv[0], bv[1], bv[2]);
+
+	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+		for (j=0; j < 3; j++)
+			for (i=0; i < n; i++)
+				usb_ibmcam_Packet_Format1(ibmcam, bright_3x[j], bv[j]);
+	} else {
+		i = ibmcam->vpic.brightness >> 12;	/* 0 .. 15 */
+		j = 0x60 + i * ((0xee - 0x60) / 16);	/* 0x60 .. 0xee or so */
+		usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, j);
+	}
+}
+
+static void usb_ibmcam_model2_set_hue(struct usb_ibmcam *ibmcam)
+{
+	unsigned short hue = ibmcam->vpic.hue >> 9; /* 0 .. 7F */
+
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, hue);
+	/* usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, sat); */
+}
+
+/*
+ * usb_ibmcam_adjust_picture()
+ *
+ * This procedure gets called from V4L interface to update picture settings.
+ * Here we change brightness and contrast.
+ */
+static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam)
+{
+	usb_ibmcam_adjust_contrast(ibmcam);
+	usb_ibmcam_set_brightness(ibmcam);
+	if (ibmcam->camera_model == IBMCAM_MODEL_2) {
+		usb_ibmcam_model2_set_hue(ibmcam);
+	}
+}
+
+static int usb_ibmcam_model1_setup(struct usb_ibmcam *ibmcam)
+{
+	const int ntries = 5;
+	int i;
+
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0128);
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
+	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
+	usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100);	/* LED Off */
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
+	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
+	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0108);
+
+	usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0112);
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115);
+	usb_ibmcam_veio(ibmcam, 0, 0x06, 0x0115);
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116);
+	usb_ibmcam_veio(ibmcam, 0, 0x44, 0x0116);
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116);
+	usb_ibmcam_veio(ibmcam, 0, 0x40, 0x0116);
+	usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115);
+	usb_ibmcam_veio(ibmcam, 0, 0x0e, 0x0115);
+	usb_ibmcam_veio(ibmcam, 0, 0x19, 0x012c);
+
+	usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x1e);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x0d);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x09);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x3b, 0x00);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x28, 0x22);
+	usb_ibmcam_Packet_Format1(ibmcam, light_27, 0);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08);
+
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x00);
+
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x14);
+
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x01, 0xe1);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x02, 0xcd);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x03, 0xcd);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x04, 0xfa);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
+
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0a, 0x37);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0b, 0xb8);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0c, 0xf3);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0d, 0xe3);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0e, 0x0d);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x0f, 0xf2);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x10, 0xd5);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x11, 0xba);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x12, 0x53);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
+
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x16, 0x00);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x17, 0x28);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x18, 0x7d);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x19, 0xbe);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff);
+	usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00);
+
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x18);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x13, 0x18);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x14, 0x06);
+
+	/* This is default brightness */
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0x37);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0x46);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0x55);
+
+	usb_ibmcam_Packet_Format1(ibmcam, 0x2e, 0x04);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x2d, 0x04);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x29, 0x80);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x01);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x17);
+	usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x34, 0x00);
+
+	usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0101);
+	usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010a);
+
+	switch (videosize) {
+	case VIDEOSIZE_128x96:
+		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0103);
+		usb_ibmcam_veio(ibmcam, 0, 0x60, 0x0105);
+		usb_ibmcam_veio(ibmcam, 0, 0x0c, 0x010b);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x0b, 0x011d);
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129);
+		break;
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103);
+		usb_ibmcam_veio(ibmcam, 0, 0x8f, 0x0105);
+		usb_ibmcam_veio(ibmcam, 0, 0x06, 0x010b);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x0d, 0x011d);
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0129);
+		break;
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103);
+		usb_ibmcam_veio(ibmcam, 0, 0x90, 0x0105);
+		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x010b);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x05, 0x011d);
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129);
+		break;
+	}
+
+	usb_ibmcam_veio(ibmcam, 0, 0xff, 0x012b);
+
+	/* This is another brightness - don't know why */
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0xc3);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0xd2);
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0xe1);
+
+	/* Default contrast */
+	for (i=0; i < ntries; i++)
+		usb_ibmcam_Packet_Format1(ibmcam, contrast_14, 0x0a);
+
+	/* Default sharpness */
+	for (i=0; i < 2; i++)
+		usb_ibmcam_PacketFormat2(ibmcam, sharp_13, 0x1a);	/* Level 4 FIXME */
+
+	/* Default lighting conditions */
+	usb_ibmcam_Packet_Format1(ibmcam, light_27, lighting); /* 0=Bright 2=Low */
+
+	/* Assorted init */
+
+	switch (videosize) {
+	case VIDEOSIZE_128x96:
+		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e);
+		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x36, 0x0102);
+		usb_ibmcam_veio(ibmcam, 0, 0x1a, 0x0104);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c);
+		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
+#if 0
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0106);
+		usb_ibmcam_veio(ibmcam, 0, 0x38, 0x0107);
+#else
+		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0106);
+		usb_ibmcam_veio(ibmcam, 0, 0x2a, 0x0107);
+#endif
+		break;
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e);
+		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x0102);
+		usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0104);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c);
+		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0106);
+		usb_ibmcam_veio(ibmcam, 0, 0xca, 0x0107);
+		break;
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f);
+		usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x08, 0x0102);
+		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0104);
+		usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x2f, 0x011c);
+		usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a);	/* Same everywhere */
+		usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0106);
+		usb_ibmcam_veio(ibmcam, 0, 0xf6, 0x0107);
+		break;
+	}
+	return IBMCAM_IS_OPERATIONAL(ibmcam);
+}
+
+static int usb_ibmcam_model2_setup(struct usb_ibmcam *ibmcam)
+{
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100);	/* LED on */
+	usb_ibmcam_veio(ibmcam, 1, 0x0000, 0x0116);
+	usb_ibmcam_veio(ibmcam, 0, 0x0060, 0x0116);
+	usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0112);
+	usb_ibmcam_veio(ibmcam, 0, 0x00bc, 0x012c);
+	usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x012b);
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0108);
+	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0133);
+	usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0102);
+	switch (videosize) {
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		usb_ibmcam_veio(ibmcam, 0, 0x00b9, 0x010a);	/* Unique to this mode */
+		usb_ibmcam_veio(ibmcam, 0, 0x0038, 0x0119);	/* Unique to this mode */
+		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x0090, 0x0107);	/* Unique to every mode*/
+		break;
+	case VIDEOSIZE_320x240:
+		usb_ibmcam_veio(ibmcam, 0, 0x0028, 0x0103);	/* Unique to this mode */
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x0098, 0x0107);	/* Unique to every mode*/
+		break;
+	case VIDEOSIZE_352x240:
+		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x00da, 0x0107);	/* Unique to every mode*/
+		break;
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103);	/* All except 320x240 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119);	/* All except 176x144 */
+		usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106);	/* Same */
+		usb_ibmcam_veio(ibmcam, 0, 0x00fe, 0x0107);	/* Unique to every mode*/
+		break;
+	}
+	return IBMCAM_IS_OPERATIONAL(ibmcam);
+}
+
+/*
+ * usb_ibmcam_model1_setup_after_video_if()
+ *
+ * This code adds finishing touches to the video data interface.
+ * Here we configure the frame rate and turn on the LED.
+ */
+static void usb_ibmcam_model1_setup_after_video_if(struct usb_ibmcam *ibmcam)
+{
+	unsigned short internal_frame_rate;
+
+	RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
+	internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */
+	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100);	/* LED On  */
+	usb_ibmcam_veio(ibmcam, 0, internal_frame_rate, 0x0111);
+	usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114);
+	usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c);
+}
+
+static void usb_ibmcam_model2_setup_after_video_if(struct usb_ibmcam *ibmcam)
+{
+	unsigned short setup_model2_rg, setup_model2_rg2, setup_model2_sat, setup_model2_yb;
+
+	usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100);	/* LED on */
+
+	switch (videosize) {
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_veio(ibmcam, 0, 0x0050, 0x0111);
+		usb_ibmcam_veio(ibmcam, 0, 0x00d0, 0x0111);
+		break;
+	case VIDEOSIZE_320x240:
+	case VIDEOSIZE_352x240:
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_veio(ibmcam, 0, 0x0040, 0x0111);
+		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x0111);
+		break;
+	}
+	usb_ibmcam_veio(ibmcam, 0, 0x009b, 0x010f);
+	usb_ibmcam_veio(ibmcam, 0, 0x00bb, 0x010f);
+
+	/*
+	 * Hardware settings, may affect CMOS sensor; not user controls!
+	 * -------------------------------------------------------------
+	 * 0x0004: no effect
+	 * 0x0006: hardware effect
+	 * 0x0008: no effect
+	 * 0x000a: stops video stream, probably important h/w setting
+	 * 0x000c: changes color in hardware manner (not user setting)
+	 * 0x0012: changes number of colors (does not affect speed)
+	 * 0x002a: no effect
+	 * 0x002c: hardware setting (related to scan lines)
+	 * 0x002e: stops video stream, probably important h/w setting
+	 */
+	usb_ibmcam_model2_Packet1(ibmcam, 0x000a, 0x005c);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0004, 0x0000);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0006, 0x00fb);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0008, 0x0000);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x000c, 0x0009);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0012, 0x000a);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x002a, 0x0000);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x002c, 0x0000);
+	usb_ibmcam_model2_Packet1(ibmcam, 0x002e, 0x0008);
+
+	/*
+	 * Function 0x0030 pops up all over the place. Apparently
+	 * it is a hardware control register, with every bit assigned to
+	 * do something.
+	 */
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0000);
+
+	/*
+	 * Magic control of CMOS sensor. Only lower values like
+	 * 0-3 work, and picture shifts left or right. Don't change.
+	 */
+	switch (videosize) {
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0002);
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */
+		break;
+	case VIDEOSIZE_320x240:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009);
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0005); /* Horizontal shift */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Another hardware setting */
+		break;
+	case VIDEOSIZE_352x240:
+		/* This mode doesn't work as Windows programs it; changed to work */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); /* Windows sets this to 8 */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0003); /* Horizontal shift */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+		break;
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0003);
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */
+		break;
+	}
+
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, 0x005a);
+
+	/*
+	 * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest).
+	 * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the
+	 * slowest setting. However for all practical reasons high settings make no
+	 * sense because USB is not fast enough to support high FPS. Be aware that
+	 * the picture datastream will be severely disrupted if you ask for
+	 * frame rate faster than allowed for the video size - see below:
+	 *
+	 * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+	 * -----------------------------------------------------------------
+	 * 176x144: [6..31]
+	 * 320x240: [8..31]
+	 * 352x240: [10..31]
+	 * 352x288: [16..31] I have to raise lower threshold for stability...
+	 *
+	 * As usual, slower FPS provides better sensitivity.
+	 */
+	{
+		short hw_fps=31, i_framerate;
+
+		RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
+		i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN;
+		switch (videosize) {
+		case VIDEOSIZE_176x144:
+			hw_fps = 6 + i_framerate*4;
+			break;
+		case VIDEOSIZE_320x240:
+			hw_fps = 8 + i_framerate*3;
+			break;
+		case VIDEOSIZE_352x240:
+			hw_fps = 10 + i_framerate*2;
+			break;
+		case VIDEOSIZE_352x288:
+			hw_fps = 28 + i_framerate/2;
+			break;
+		}
+		if (debug > 0)
+			printk(KERN_DEBUG "Framerate (hardware): %hd.\n", hw_fps);
+		RESTRICT_TO_RANGE(hw_fps, 0, 31);
+		usb_ibmcam_model2_Packet1(ibmcam, mod2_set_framerate, hw_fps);
+	}
+
+	/*
+	 * This setting does not visibly affect pictures; left it here
+	 * because it was present in Windows USB data stream. This function
+	 * does not allow arbitrary values and apparently is a bit mask, to
+	 * be activated only at appropriate time. Don't change it randomly!
+	 */
+	switch (videosize) {
+	case VIDEOSIZE_176x144:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x00c2);
+		break;
+	case VIDEOSIZE_320x240:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0044);
+		break;
+	case VIDEOSIZE_352x240:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0046);
+		break;
+	case VIDEOSIZE_352x288:
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0048);
+		break;
+	}
+
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting);
+
+	if (init_model2_rg >= 0) {
+		RESTRICT_TO_RANGE(init_model2_rg, 0, 255);
+		setup_model2_rg = init_model2_rg;
+	} else
+		setup_model2_rg = 0x0070;
+
+	if (init_model2_rg2 >= 0) {
+		RESTRICT_TO_RANGE(init_model2_rg2, 0, 255);
+		setup_model2_rg2 = init_model2_rg2;
+	} else
+		setup_model2_rg2 = 0x002f;
+
+	if (init_model2_sat >= 0) {
+		RESTRICT_TO_RANGE(init_model2_sat, 0, 255);
+		setup_model2_sat = init_model2_sat;
+	} else
+		setup_model2_sat = 0x0034;
+
+	if (init_model2_yb >= 0) {
+		RESTRICT_TO_RANGE(init_model2_yb, 0, 255);
+		setup_model2_yb = init_model2_yb;
+	} else
+		setup_model2_yb = 0x00a0;
+
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg2, setup_model2_rg2);
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, setup_model2_sat);
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_yb, setup_model2_yb);
+	usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, setup_model2_rg);
+
+	/* Hardware control command */
+	usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004);
+
+	usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);	/* Go camera, go! */
+	usb_clear_halt(ibmcam->dev, ibmcam->video_endp);
+}
+
+/*
+ * usb_ibmcam_setup_video_stop()
+ *
+ * This code tells camera to stop streaming. The interface remains
+ * configured and bandwidth - claimed.
+ */
+static void usb_ibmcam_setup_video_stop(struct usb_ibmcam *ibmcam)
+{
+	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
+		usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114);
+		usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c);
+		usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c);
+		usb_ibmcam_send_FF_04_02(ibmcam);
+		usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100);
+		usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100);	/* LED Off */
+	} else if (ibmcam->camera_model == IBMCAM_MODEL_2) {
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c);	/* Stop the camera */
+
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004);
+
+		usb_ibmcam_veio(ibmcam, 0, 0x0080, 0x0100);	/* LED Off */
+		usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111);
+		usb_ibmcam_veio(ibmcam, 0, 0x00a0, 0x0111);
+
+		usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0002);
+
+		usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111);
+		usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0112);
+	}
+}
+
+/*
+ * usb_ibmcam_reinit_iso()
+ *
+ * This procedure sends couple of commands to the camera and then
+ * resets the video pipe. This sequence was observed to reinit the
+ * camera or, at least, to initiate ISO data stream.
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop)
+{
+	if (ibmcam->camera_model == IBMCAM_MODEL_1) {
+		if (do_stop)
+			usb_ibmcam_setup_video_stop(ibmcam);
+		usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0114);
+		usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c);
+		usb_clear_halt(ibmcam->dev, ibmcam->video_endp);
+		usb_ibmcam_model1_setup_after_video_if(ibmcam);
+	} else if (ibmcam->camera_model == IBMCAM_MODEL_2) {
+		usb_ibmcam_model2_setup_after_video_if(ibmcam);
+	}
+}
+
+/*
+ * ibmcam_init_isoc()
+ *
+ * History:
+ * 1/27/00  Used ibmcam->iface, ibmcam->ifaceAltActive instead of hardcoded values.
+ *          Simplified by using for loop, allowed any number of URBs.
+ */
+static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam)
+{
+	struct usb_device *dev = ibmcam->dev;
+	int i, err;
+
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return -EFAULT;
+
+	ibmcam->compress = 0;
+	ibmcam->curframe = -1;
+	ibmcam->cursbuf = 0;
+	ibmcam->scratchlen = 0;
+
+	/* Alternate interface 1 is is the biggest frame size */
+	i = usb_set_interface(dev, ibmcam->iface, ibmcam->ifaceAltActive);
+	if (i < 0) {
+		printk(KERN_ERR "usb_set_interface error\n");
+		ibmcam->last_error = i;
+		return -EBUSY;
+	}
+	usb_ibmcam_change_lighting_conditions(ibmcam);
+	usb_ibmcam_set_sharpness(ibmcam);
+	usb_ibmcam_reinit_iso(ibmcam, 0);
+
+	/* We double buffer the Iso lists */
+
+	for (i=0; i < IBMCAM_NUMSBUF; i++) {
+		int j, k;
+		urb_t *urb;
+
+		urb = usb_alloc_urb(FRAMES_PER_DESC);
+		if (urb == NULL) {
+			printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc() failed.\n");
+			return -ENOMEM;
+		}
+		ibmcam->sbuf[i].urb = urb;
+		urb->dev = dev;
+		urb->context = ibmcam;
+		urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp);
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->transfer_buffer = ibmcam->sbuf[i].data;
+		urb->complete = ibmcam_isoc_irq;
+		urb->number_of_packets = FRAMES_PER_DESC;
+		urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC;
+		for (j=k=0; j < FRAMES_PER_DESC; j++, k += ibmcam->iso_packet_len) {
+			urb->iso_frame_desc[j].offset = k;
+			urb->iso_frame_desc[j].length = ibmcam->iso_packet_len;
+		}
+	}
+
+	/* Link URBs into a ring so that they invoke each other infinitely */
+	for (i=0; i < IBMCAM_NUMSBUF; i++) {
+		if ((i+1) < IBMCAM_NUMSBUF)
+			ibmcam->sbuf[i].urb->next = ibmcam->sbuf[i+1].urb;
+		else
+			ibmcam->sbuf[i].urb->next = ibmcam->sbuf[0].urb;
+	}
+
+	/* Submit all URBs */
+	for (i=0; i < IBMCAM_NUMSBUF; i++) {
+		err = usb_submit_urb(ibmcam->sbuf[i].urb);
+		if (err)
+			printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(%d) ret %d\n",
+			       i, err);
+	}
+
+	ibmcam->streaming = 1;
+	/* printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp); */
+	return 0;
+}
+
+/*
+ * ibmcam_stop_isoc()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ * History:
+ * 1/22/00  Corrected order of actions to work after surprise removal.
+ * 1/27/00  Used ibmcam->iface, ibmcam->ifaceAltInactive instead of hardcoded values.
+ */
+static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam)
+{
+	static const char proc[] = "ibmcam_stop_isoc";
+	int i, j;
+
+	if (!ibmcam->streaming || (ibmcam->dev == NULL))
+		return;
+
+	/* Unschedule all of the iso td's */
+	for (i=0; i < IBMCAM_NUMSBUF; i++) {
+		j = usb_unlink_urb(ibmcam->sbuf[i].urb);
+		if (j < 0)
+			printk(KERN_ERR "%s: usb_unlink_urb() error %d.\n", proc, j);
+	}
+	/* printk(KERN_DEBUG "streaming=0\n"); */
+	ibmcam->streaming = 0;
+
+	/* Delete them all */
+	for (i=0; i < IBMCAM_NUMSBUF; i++)
+		usb_free_urb(ibmcam->sbuf[i].urb);
+
+	if (!ibmcam->remove_pending) {
+		usb_ibmcam_setup_video_stop(ibmcam);
+
+		/* Set packet size to 0 */
+		j = usb_set_interface(ibmcam->dev, ibmcam->iface, ibmcam->ifaceAltInactive);
+		if (j < 0) {
+			printk(KERN_ERR "%s: usb_set_interface() error %d.\n", proc, j);
+			ibmcam->last_error = j;
+		}
+	}
+}
+
+/*
+ * ibmcam_new_frame()
+ *
+ * History:
+ * 29-Mar-00 Added copying of previous frame into the current one.
+ */
+static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum)
+{
+	struct ibmcam_frame *frame;
+	int n, width, height;
+
+	/* If we're not grabbing a frame right now and the other frame is */
+	/*  ready to be grabbed into, then use it instead */
+	if (ibmcam->curframe != -1)
+		return 0;
+
+	n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES;
+	if (ibmcam->frame[n].grabstate == FRAME_READY)
+		framenum = n;
+
+	frame = &ibmcam->frame[framenum];
+
+	frame->grabstate = FRAME_GRABBING;
+	frame->scanstate = STATE_SCANNING;
+	frame->scanlength = 0;		/* Accumulated in ibmcam_parse_data() */
+	ibmcam->curframe = framenum;
+
+	/*
+	 * Normally we would want to copy previous frame into the current one
+	 * before we even start filling it with data; this allows us to stop
+	 * filling at any moment; top portion of the frame will be new and
+	 * bottom portion will stay as it was in previous frame. If we don't
+	 * do that then missing chunks of video stream will result in flickering
+	 * portions of old data whatever it was before.
+	 *
+	 * If we choose not to copy previous frame (to, for example, save few
+	 * bus cycles - the frame can be pretty large!) then we have an option
+	 * to clear the frame before using. If we experience losses in this
+	 * mode then missing picture will be black (no flickering).
+	 *
+	 * Finally, if user chooses not to clean the current frame before
+	 * filling it with data then the old data will be visible if we fail
+	 * to refill entire frame with new data.
+	 */
+	if (!(flags & FLAGS_SEPARATE_FRAMES)) {
+		/* This copies previous frame into this one to mask losses */
+		memmove(frame->data, ibmcam->frame[1-framenum].data,  MAX_FRAME_SIZE);
+	} else {
+		if (flags & FLAGS_CLEAN_FRAMES) {
+			/* This provides a "clean" frame but slows things down */
+			memset(frame->data, 0, MAX_FRAME_SIZE);
+		}
+	}
+	switch (videosize) {
+	case VIDEOSIZE_128x96:
+		frame->frmwidth = 128;
+		frame->frmheight = 96;
+		frame->order_uv = 1;	/* U Y V Y ... */
+		frame->hdr_sig = 0x06;	/* 00 FF 00 06 */
+		break;
+	case VIDEOSIZE_176x144:
+		frame->frmwidth = 176;
+		frame->frmheight = 144;
+		frame->order_uv = 1;	/* U Y V Y ... */
+		frame->hdr_sig = 0x0E;	/* 00 FF 00 0E */
+		break;
+	case VIDEOSIZE_320x240:		/* For model 2 only */
+		frame->frmwidth = 320;
+		frame->frmheight = 240;
+		break;
+	case VIDEOSIZE_352x240:		/* For model 2 only */
+		frame->frmwidth = 352;
+		frame->frmheight = 240;
+		break;
+	case VIDEOSIZE_352x288:
+		frame->frmwidth = 352;
+		frame->frmheight = 288;
+		frame->order_uv = 0;	/* V Y U Y ... */
+		frame->hdr_sig = 0x00;	/* 00 FF 00 00 */
+		break;
+	}
+	frame->order_yc = (ibmcam->camera_model == IBMCAM_MODEL_2);
+
+	width = frame->width;
+	RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth);
+	width &= ~7;		/* Multiple of 8 */
+
+	height = frame->height;
+	RESTRICT_TO_RANGE(height, min_imgheight, imgheight);
+	height &= ~3;		/* Multiple of 4 */
+
+	return 0;
+}
+
+/*
+ * ibmcam_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'ibmcam->user'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ * History:
+ * 1/22/00  Rewrote, moved scratch buffer allocation here. Now the
+ *          camera is also initialized here (once per connect), at
+ *          expense of V4L client (it waits on open() call).
+ * 1/27/00  Used IBMCAM_NUMSBUF as number of URB buffers.
+ * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ */
+static int ibmcam_open(struct video_device *dev, int flags)
+{
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+	const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len;
+	int i, err = 0;
+
+	MOD_INC_USE_COUNT;
+	down(&ibmcam->lock);
+
+	if (ibmcam->user)
+		err = -EBUSY;
+	else {
+		/* Clean pointers so we know if we allocated something */
+		for (i=0; i < IBMCAM_NUMSBUF; i++)
+			ibmcam->sbuf[i].data = NULL;
+
+		/* Allocate memory for the frame buffers */
+		ibmcam->fbuf_size = IBMCAM_NUMFRAMES * MAX_FRAME_SIZE;
+		ibmcam->fbuf = rvmalloc(ibmcam->fbuf_size);
+		ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL);
+		ibmcam->scratchlen = 0;
+		if ((ibmcam->fbuf == NULL) || (ibmcam->scratch == NULL))
+			err = -ENOMEM;
+		else {
+			/* Allocate all buffers */
+			for (i=0; i < IBMCAM_NUMFRAMES; i++) {
+				ibmcam->frame[i].grabstate = FRAME_UNUSED;
+				ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE;
+				/*
+				 * Set default sizes in case IOCTL (VIDIOCMCAPTURE)
+				 * is not used (using read() instead).
+				 */
+				ibmcam->frame[i].width = imgwidth;
+				ibmcam->frame[i].height = imgheight;
+				ibmcam->frame[i].bytes_read = 0;
+			}
+			for (i=0; i < IBMCAM_NUMSBUF; i++) {
+				ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL);
+				if (ibmcam->sbuf[i].data == NULL) {
+					err = -ENOMEM;
+					break;
+				}
+			}
+		}
+		if (err) {
+			/* Have to free all that memory */
+			if (ibmcam->fbuf != NULL) {
+				rvfree(ibmcam->fbuf, ibmcam->fbuf_size);
+				ibmcam->fbuf = NULL;
+			}
+			if (ibmcam->scratch != NULL) {
+				kfree(ibmcam->scratch);
+				ibmcam->scratch = NULL;
+			}
+			for (i=0; i < IBMCAM_NUMSBUF; i++) {
+				if (ibmcam->sbuf[i].data != NULL) {
+					kfree (ibmcam->sbuf[i].data);
+					ibmcam->sbuf[i].data = NULL;
+				}
+			}
+		}
+	}
+
+	/* If so far no errors then we shall start the camera */
+	if (!err) {
+		err = ibmcam_init_isoc(ibmcam);
+		if (!err) {
+			/* Send init sequence only once, it's large! */
+			if (!ibmcam->initialized) {
+				int setup_ok = 0;
+				if (ibmcam->camera_model == IBMCAM_MODEL_1)
+					setup_ok = usb_ibmcam_model1_setup(ibmcam);
+				else if (ibmcam->camera_model == IBMCAM_MODEL_2)
+					setup_ok = usb_ibmcam_model2_setup(ibmcam);
+				if (setup_ok)
+					ibmcam->initialized = 1;
+				else
+					err = -EBUSY;
+			}
+			if (!err)
+				ibmcam->user++;
+		}
+	}
+	up(&ibmcam->lock);
+	if (err)
+		MOD_DEC_USE_COUNT;
+	return err;
+}
+
+/*
+ * ibmcam_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in ibmcam_open().
+ *
+ * History:
+ * 1/22/00  Moved scratch buffer deallocation here.
+ * 1/27/00  Used IBMCAM_NUMSBUF as number of URB buffers.
+ * 5/24/00  Moved MOD_DEC_USE_COUNT outside of code that can sleep.
+ */
+static void ibmcam_close(struct video_device *dev)
+{
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+	int i;
+
+	down(&ibmcam->lock);	
+
+	ibmcam_stop_isoc(ibmcam);
+
+	rvfree(ibmcam->fbuf, ibmcam->fbuf_size);
+	kfree(ibmcam->scratch);
+	for (i=0; i < IBMCAM_NUMSBUF; i++)
+		kfree(ibmcam->sbuf[i].data);
+
+	ibmcam->user--;
+
+	if (ibmcam->remove_pending) {
+		printk(KERN_INFO "ibmcam_close: Final disconnect.\n");
+		usb_ibmcam_release(ibmcam);
+	}
+	up(&ibmcam->lock);
+	MOD_DEC_USE_COUNT;
+}
+
+static int ibmcam_init_done(struct video_device *dev)
+{
+	return 0;
+}
+
+static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+/*
+ * ibmcam_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ * History:
+ * 1/22/00  Corrected VIDIOCSPICT to reject unsupported settings.
+ */
+static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return -EFAULT;
+
+	switch (cmd) {
+		case VIDIOCGCAP:
+		{
+			if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGCHAN:
+		{
+			if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSCHAN:
+		{
+			int v;
+
+			if (copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */
+				return -EINVAL;
+			if (v != ibmcam->vchan.channel) {
+				ibmcam->vchan.channel = v;
+				usb_ibmcam_change_lighting_conditions(ibmcam);
+			}
+			return 0;
+		}
+		case VIDIOCGPICT:
+		{
+			if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSPICT:
+		{
+			struct video_picture tmp;
+			/*
+			 * Use temporary 'video_picture' structure to preserve our
+			 * own settings (such as color depth, palette) that we
+			 * aren't allowing everyone (V4L client) to change.
+			 */
+			if (copy_from_user(&tmp, arg, sizeof(tmp)))
+				return -EFAULT;
+			ibmcam->vpic.brightness = tmp.brightness;
+			ibmcam->vpic.hue = tmp.hue;
+			ibmcam->vpic.colour = tmp.colour;
+			ibmcam->vpic.contrast = tmp.contrast;
+			usb_ibmcam_adjust_picture(ibmcam);
+			return 0;
+		}
+		case VIDIOCSWIN:
+		{
+			struct video_window vw;
+
+			if (copy_from_user(&vw, arg, sizeof(vw)))
+				return -EFAULT;
+			if (vw.flags)
+				return -EINVAL;
+			if (vw.clipcount)
+				return -EINVAL;
+			if (vw.height != imgheight)
+				return -EINVAL;
+			if (vw.width != imgwidth)
+				return -EINVAL;
+
+			ibmcam->compress = 0;
+
+			return 0;
+		}
+		case VIDIOCGWIN:
+		{
+			struct video_window vw;
+
+			vw.x = 0;
+			vw.y = 0;
+			vw.width = imgwidth;
+			vw.height = imgheight;
+			vw.chromakey = 0;
+			vw.flags = usb_ibmcam_calculate_fps();
+
+			if (copy_to_user(arg, &vw, sizeof(vw)))
+				return -EFAULT;
+
+			return 0;
+		}
+		case VIDIOCGMBUF:
+		{
+			struct video_mbuf vm;
+
+			memset(&vm, 0, sizeof(vm));
+			vm.size = MAX_FRAME_SIZE * 2;
+			vm.frames = 2;
+			vm.offsets[0] = 0;
+			vm.offsets[1] = MAX_FRAME_SIZE;
+
+			if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+				return -EFAULT;
+
+			return 0;
+		}
+		case VIDIOCMCAPTURE:
+		{
+			struct video_mmap vm;
+
+			if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+				return -EFAULT;
+
+			if (debug >= 1)
+				printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n",
+					vm.frame, vm.width, vm.height, vm.format);
+
+			if (vm.format != VIDEO_PALETTE_RGB24)
+				return -EINVAL;
+
+			if ((vm.frame != 0) && (vm.frame != 1))
+				return -EINVAL;
+
+			if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING)
+				return -EBUSY;
+
+			/* Don't compress if the size changed */
+			if ((ibmcam->frame[vm.frame].width != vm.width) ||
+			    (ibmcam->frame[vm.frame].height != vm.height))
+				ibmcam->compress = 0;
+
+			ibmcam->frame[vm.frame].width = vm.width;
+			ibmcam->frame[vm.frame].height = vm.height;
+
+			/* Mark it as ready */
+			ibmcam->frame[vm.frame].grabstate = FRAME_READY;
+
+			return ibmcam_new_frame(ibmcam, vm.frame);
+		}
+		case VIDIOCSYNC:
+		{
+			int frame;
+
+			if (copy_from_user((void *)&frame, arg, sizeof(int)))
+				return -EFAULT;
+
+			if (debug >= 1)
+				printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame);
+
+			switch (ibmcam->frame[frame].grabstate) {
+			case FRAME_UNUSED:
+				return -EINVAL;
+			case FRAME_READY:
+			case FRAME_GRABBING:
+			case FRAME_ERROR:
+			{
+				int ntries;
+		redo:
+				if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+					return -EIO;
+				ntries = 0; 
+				do {
+					interruptible_sleep_on(&ibmcam->frame[frame].wq);
+					if (signal_pending(current)) {
+						if (flags & FLAGS_RETRY_VIDIOCSYNC) {
+							/* Polling apps will destroy frames with that! */
+							ibmcam_new_frame(ibmcam, frame);
+							usb_ibmcam_testpattern(ibmcam, 1, 0);
+							ibmcam->curframe = -1;
+							ibmcam->frame_num++;
+
+							/* This will request another frame. */
+							if (waitqueue_active(&ibmcam->frame[frame].wq))
+								wake_up_interruptible(&ibmcam->frame[frame].wq);
+							return 0;
+						} else {
+							/* Standard answer: not ready yet! */
+							return -EINTR;
+						}
+					}
+				} while (ibmcam->frame[frame].grabstate == FRAME_GRABBING);
+
+				if (ibmcam->frame[frame].grabstate == FRAME_ERROR) {
+					int ret = ibmcam_new_frame(ibmcam, frame);
+					if (ret < 0)
+						return ret;
+					goto redo;
+				}
+			}
+			case FRAME_DONE:
+				ibmcam->frame[frame].grabstate = FRAME_UNUSED;
+				break;
+			}
+
+			ibmcam->frame[frame].grabstate = FRAME_UNUSED;
+
+			return 0;
+		}
+		case VIDIOCGFBUF:
+		{
+			struct video_buffer vb;
+
+			memset(&vb, 0, sizeof(vb));
+			vb.base = NULL;	/* frame buffer not supported, not used */
+
+			if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+				return -EFAULT;
+
+ 			return 0;
+ 		}
+		case VIDIOCKEY:
+			return 0;
+
+		case VIDIOCCAPTURE:
+			return -EINVAL;
+
+		case VIDIOCSFBUF:
+
+		case VIDIOCGTUNER:
+		case VIDIOCSTUNER:
+
+		case VIDIOCGFREQ:
+		case VIDIOCSFREQ:
+
+		case VIDIOCGAUDIO:
+		case VIDIOCSAUDIO:
+			return -EINVAL;
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+	int frmx = -1;
+	volatile struct ibmcam_frame *frame;
+
+	if (debug >= 1)
+		printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock);
+
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam) || (buf == NULL))
+		return -EFAULT;
+
+	/* See if a frame is completed, then use it. */
+	if (ibmcam->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
+		frmx = 0;
+	else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
+		frmx = 1;
+
+	if (noblock && (frmx == -1))
+		return -EAGAIN;
+
+	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
+	/* See if a frame is in process (grabbing), then use it. */
+	if (frmx == -1) {
+		if (ibmcam->frame[0].grabstate == FRAME_GRABBING)
+			frmx = 0;
+		else if (ibmcam->frame[1].grabstate == FRAME_GRABBING)
+			frmx = 1;
+	}
+
+	/* If no frame is active, start one. */
+	if (frmx == -1)
+		ibmcam_new_frame(ibmcam, frmx = 0);
+
+	frame = &ibmcam->frame[frmx];
+
+restart:
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return -EIO;
+	while (frame->grabstate == FRAME_GRABBING) {
+		interruptible_sleep_on((void *)&frame->wq);
+		if (signal_pending(current))
+			return -EINTR;
+	}
+
+	if (frame->grabstate == FRAME_ERROR) {
+		frame->bytes_read = 0;
+		if (ibmcam_new_frame(ibmcam, frmx))
+			printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n");
+		goto restart;
+	}
+
+	if (debug >= 1)
+		printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
+			frmx, frame->bytes_read, frame->scanlength);
+
+	/* copy bytes to user space; we allow for partials reads */
+	if ((count + frame->bytes_read) > frame->scanlength)
+		count = frame->scanlength - frame->bytes_read;
+
+	if (copy_to_user(buf, frame->data + frame->bytes_read, count))
+		return -EFAULT;
+
+	frame->bytes_read += count;
+	if (debug >= 1)
+		printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n",
+			count, frame->bytes_read);
+
+	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+		frame->bytes_read = 0;
+
+		/* Mark it as available to be used again. */
+		ibmcam->frame[frmx].grabstate = FRAME_UNUSED;
+		if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1))
+			printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n");
+	}
+
+	return count;
+}
+
+static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev;
+	unsigned long start = (unsigned long)adr;
+	unsigned long page, pos;
+
+	if (!IBMCAM_IS_OPERATIONAL(ibmcam))
+		return -EFAULT;
+
+	if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+		return -EINVAL;
+
+	pos = (unsigned long)ibmcam->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa(pos);
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+			return -EAGAIN;
+
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	return 0;
+}
+
+static struct video_device ibmcam_template = {
+	"CPiA USB Camera",
+	VID_TYPE_CAPTURE,
+	VID_HARDWARE_CPIA,
+	ibmcam_open,
+	ibmcam_close,
+	ibmcam_read,
+	ibmcam_write,
+	NULL,
+	ibmcam_ioctl,
+	ibmcam_mmap,
+	ibmcam_init_done,
+	NULL,
+	0,
+	0
+};
+
+static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam)
+{
+	if (ibmcam == NULL)
+		return;
+
+	RESTRICT_TO_RANGE(init_brightness, 0, 255);
+	RESTRICT_TO_RANGE(init_contrast, 0, 255);
+	RESTRICT_TO_RANGE(init_color, 0, 255);
+	RESTRICT_TO_RANGE(init_hue, 0, 255);
+	RESTRICT_TO_RANGE(hue_correction, 0, 255);
+
+	memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic));
+	memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old));
+
+	ibmcam->vpic.colour = init_color << 8;
+	ibmcam->vpic.hue = init_hue << 8;
+	ibmcam->vpic.brightness = init_brightness << 8;
+	ibmcam->vpic.contrast = init_contrast << 8;
+	ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */
+	ibmcam->vpic.depth = 24;
+	ibmcam->vpic.palette = VIDEO_PALETTE_RGB24;
+
+	memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap));
+	strcpy(ibmcam->vcap.name, "IBM USB Camera");
+	ibmcam->vcap.type = VID_TYPE_CAPTURE;
+	ibmcam->vcap.channels = 1;
+	ibmcam->vcap.audios = 0;
+	ibmcam->vcap.maxwidth = imgwidth;
+	ibmcam->vcap.maxheight = imgheight;
+	ibmcam->vcap.minwidth = min_imgwidth;
+	ibmcam->vcap.minheight = min_imgheight;
+
+	memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan));
+	ibmcam->vchan.flags = 0;
+	ibmcam->vchan.tuners = 0;
+	ibmcam->vchan.channel = 0;
+	ibmcam->vchan.type = VIDEO_TYPE_CAMERA;
+	strcpy(ibmcam->vchan.name, "Camera");
+}
+
+/*
+ * ibmcam_find_struct()
+ *
+ * This code searches the array of preallocated (static) structures
+ * and returns index of the first one that isn't in use. Returns -1
+ * if there are no free structures.
+ *
+ * History:
+ * 1/27/00  Created.
+ */
+static int ibmcam_find_struct(void)
+{
+	int i, u;
+
+	for (u = 0; u < MAX_IBMCAM; u++) {
+		struct usb_ibmcam *ibmcam = &cams[u];
+		if (!ibmcam->ibmcam_used) /* This one is free */
+		{
+			ibmcam->ibmcam_used = 1;	/* In use now */
+			for (i=0; i < IBMCAM_NUMFRAMES; i++)
+				init_waitqueue_head(&ibmcam->frame[i].wq);
+			init_MUTEX(&ibmcam->lock);	/* to 1 == available */
+			ibmcam->dev = NULL;
+			memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template));
+			return u;
+		}
+	}
+	return -1;
+}
+
+/*
+ * usb_ibmcam_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like IBM C-it camera.
+ *
+ * History:
+ * 1/22/00  Moved camera init code to ibmcam_open()
+ * 1/27/00  Changed to use static structures, added locking.
+ * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ * 7/3/00   Fixed endianness bug.
+ */
+static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_ibmcam *ibmcam = NULL;
+	const struct usb_interface_descriptor *interface;
+	const struct usb_endpoint_descriptor *endpoint;
+	int devnum, model=0;
+
+	if (debug >= 1)
+		printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum);
+
+	/* We don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	/* Is it an IBM camera? */
+	if ((dev->descriptor.idVendor != 0x0545) ||
+	    (dev->descriptor.idProduct != 0x8080))
+		return NULL;
+
+	/* Check the version/revision */
+	switch (dev->descriptor.bcdDevice) {
+	case 0x0002:
+		if (ifnum != 2)
+			return NULL;
+		printk(KERN_INFO "IBM USB camera found (model 1, rev. 0x%04x).\n",
+			dev->descriptor.bcdDevice);
+		model = IBMCAM_MODEL_1;
+		break;
+	case 0x030A:
+		if (ifnum != 0)
+			return NULL;
+		printk(KERN_INFO "IBM USB camera found (model 2, rev. 0x%04x).\n",
+			dev->descriptor.bcdDevice);
+		model = IBMCAM_MODEL_2;
+		break;
+	default:
+		printk(KERN_ERR "IBM camera with revision 0x%04x is not supported.\n",
+			dev->descriptor.bcdDevice);
+		return NULL;
+	}
+
+	/* Validate found interface: must have one ISO endpoint */
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	if (interface->bNumEndpoints != 1) {
+		printk(KERN_ERR "IBM camera: interface %d. has %u. endpoints!\n",
+		       ifnum, (unsigned)(interface->bNumEndpoints));
+		return NULL;
+	}
+	endpoint = &interface->endpoint[0];
+	if ((endpoint->bmAttributes & 0x03) != 0x01) {
+		printk(KERN_ERR "IBM camera: interface %d. has non-ISO endpoint!\n", ifnum);
+		return NULL;
+	}
+	if ((endpoint->bEndpointAddress & 0x80) == 0) {
+		printk(KERN_ERR "IBM camera: interface %d. has ISO OUT endpoint!\n", ifnum);
+		return NULL;
+	}
+
+	/* Validate options */
+	if (model == IBMCAM_MODEL_1) {
+		RESTRICT_TO_RANGE(lighting, 0, 2);
+		RESTRICT_TO_RANGE(videosize, VIDEOSIZE_128x96, VIDEOSIZE_352x288);
+	} else {
+		RESTRICT_TO_RANGE(lighting, 0, 15);
+		RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240);
+	}
+
+	/* Code below may sleep, need to lock module while we are here */
+	MOD_INC_USE_COUNT;
+
+	devnum = ibmcam_find_struct();
+	if (devnum == -1) {
+		printk(KERN_INFO "IBM USB camera driver: Too many devices!\n");
+		ibmcam = NULL; /* Do not free, it's preallocated */
+		goto probe_done;
+	}
+	ibmcam = &cams[devnum];
+
+	down(&ibmcam->lock);
+	ibmcam->camera_model = model;
+	ibmcam->remove_pending = 0;
+	ibmcam->last_error = 0;
+	ibmcam->dev = dev;
+	ibmcam->iface = ifnum;
+	ibmcam->ifaceAltInactive = 0;
+	ibmcam->ifaceAltActive = 1;
+	ibmcam->video_endp = endpoint->bEndpointAddress;
+	ibmcam->iso_packet_len = 1014;
+	ibmcam->compress = 0;
+	ibmcam->user=0; 
+
+	usb_ibmcam_configure_video(ibmcam);
+	up (&ibmcam->lock);
+
+	if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) {
+		printk(KERN_ERR "video_register_device failed\n");
+		ibmcam = NULL; /* Do not free, it's preallocated */
+	}
+	if (debug > 1)
+		printk(KERN_DEBUG "video_register_device() successful\n");
+probe_done:
+	MOD_DEC_USE_COUNT;
+	return ibmcam;
+}
+
+/*
+ * usb_ibmcam_release()
+ *
+ * This code does final release of struct usb_ibmcam. This happens
+ * after the device is disconnected -and- all clients closed their files.
+ *
+ * History:
+ * 1/27/00  Created.
+ */
+static void usb_ibmcam_release(struct usb_ibmcam *ibmcam)
+{
+	video_unregister_device(&ibmcam->vdev);
+	if (debug > 0)
+		printk(KERN_DEBUG "usb_ibmcam_release: Video unregistered.\n");
+	ibmcam->ibmcam_used = 0;
+	ibmcam->initialized = 0;
+}
+
+/*
+ * usb_ibmcam_disconnect()
+ *
+ * This procedure stops all driver activity, deallocates interface-private
+ * structure (pointed by 'ptr') and after that driver should be removable
+ * with no ill consequences.
+ *
+ * This code handles surprise removal. The ibmcam->user is a counter which
+ * increments on open() and decrements on close(). If we see here that
+ * this counter is not 0 then we have a client who still has us opened.
+ * We set ibmcam->remove_pending flag as early as possible, and after that
+ * all access to the camera will gracefully fail. These failures should
+ * prompt client to (eventually) close the video device, and then - in
+ * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter.
+ *
+ * History:
+ * 1/22/00  Added polling of MOD_IN_USE to delay removal until all users gone.
+ * 1/27/00  Reworked to allow pending disconnects; see ibmcam_close()
+ * 5/24/00  Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ */
+static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr)
+{
+	static const char proc[] = "usb_ibmcam_disconnect";
+	struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr;
+
+	MOD_INC_USE_COUNT;
+
+	if (debug > 0)
+		printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr);
+
+	down(&ibmcam->lock);
+	ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */
+
+	/* At this time we ask to cancel outstanding URBs */
+	ibmcam_stop_isoc(ibmcam);
+
+	ibmcam->dev = NULL;    	    /* USB device is no more */
+
+	if (ibmcam->user)
+		printk(KERN_INFO "%s: In use, disconnect pending.\n", proc);
+	else
+		usb_ibmcam_release(ibmcam);
+	up(&ibmcam->lock);
+	printk(KERN_INFO "IBM USB camera disconnected.\n");
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver ibmcam_driver = {
+	"ibmcam",
+	usb_ibmcam_probe,
+	usb_ibmcam_disconnect,
+	{ NULL, NULL }
+};
+
+/*
+ * usb_ibmcam_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ * History:
+ * 1/27/00  Reworked to use statically allocated usb_ibmcam structures.
+ */
+static int __init usb_ibmcam_init(void)
+{
+	unsigned u;
+
+	/* Initialize struct */
+	for (u = 0; u < MAX_IBMCAM; u++) {
+		struct usb_ibmcam *ibmcam = &cams[u];
+		memset (ibmcam, 0, sizeof(struct usb_ibmcam));
+	}
+	return usb_register(&ibmcam_driver);
+}
+
+static void __exit usb_ibmcam_cleanup(void)
+{
+	usb_deregister(&ibmcam_driver);
+}
+
+module_init(usb_ibmcam_init);
+module_exit(usb_ibmcam_cleanup);
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ibmcam.h linux.18p17/drivers/usb/ibmcam.h
--- linux.vanilla/drivers/usb/ibmcam.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/ibmcam.h	Fri Sep  8 17:19:39 2000
@@ -0,0 +1,240 @@
+/*
+ * Header file for USB IBM C-It Video Camera driver.
+ *
+ * Supports IBM C-It Video Camera.
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ */
+
+#ifndef __LINUX_IBMCAM_H
+#define __LINUX_IBMCAM_H
+
+#include <linux/list.h>
+
+#define USES_IBMCAM_PUTPIXEL    0       /* 0=Fast/oops 1=Slow/secure */
+
+/* Video Size 384 x 288 x 3 bytes for RGB */
+/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */
+#define V4L_FRAME_WIDTH         384
+#define V4L_FRAME_WIDTH_USED	352
+#define V4L_FRAME_HEIGHT        288
+#define V4L_BYTES_PER_PIXEL     3
+#define MAX_FRAME_SIZE          (V4L_FRAME_WIDTH * V4L_FRAME_HEIGHT * V4L_BYTES_PER_PIXEL)
+
+/* Camera capabilities (maximum) */
+#define CAMERA_IMAGE_WIDTH      352
+#define CAMERA_IMAGE_HEIGHT     288
+#define CAMERA_IMAGE_LINE_SZ    ((CAMERA_IMAGE_WIDTH * 3) / 2) /* Bytes */
+#define CAMERA_URB_FRAMES       32
+#define CAMERA_MAX_ISO_PACKET   1023 /* 1022 actually sent by camera */
+
+#define IBMCAM_NUMFRAMES	2
+#define IBMCAM_NUMSBUF		2
+
+#define FRAMES_PER_DESC		(CAMERA_URB_FRAMES)
+#define FRAME_SIZE_PER_DESC	(CAMERA_MAX_ISO_PACKET)
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+/*
+ * This macro performs bounds checking - use it when working with
+ * new formats, or else you may get oopses all over the place.
+ * If pixel falls out of bounds then it gets shoved back (as close
+ * to place of offence as possible) and is painted bright red.
+ */
+#define IBMCAM_PUTPIXEL(fr, ix, iy, vr, vg, vb) { \
+	register unsigned char *pf; \
+	int limiter = 0, mx, my; \
+	mx = ix; \
+	my = iy; \
+	if (mx < 0) { \
+		mx=0; \
+		limiter++; \
+	} else if (mx >= 352) { \
+		mx=351; \
+		limiter++; \
+	} \
+	if (my < 0) { \
+		my = 0; \
+		limiter++; \
+	} else if (my >= V4L_FRAME_HEIGHT) { \
+		my = V4L_FRAME_HEIGHT - 1; \
+		limiter++; \
+	} \
+	pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*352 + (ix)); \
+	if (limiter) { \
+		*pf++ = 0; \
+		*pf++ = 0; \
+		*pf++ = 0xFF; \
+	} else { \
+		*pf++ = (vb); \
+		*pf++ = (vg); \
+		*pf++ = (vr); \
+	} \
+}
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16)		    + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16)		  + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) -  53281*(U-128) -  25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
+    int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+    mm_y = (my) - 16;  \
+    mm_u = (mu) - 128; \
+    mm_v = (mv) - 128; \
+    mm_yc= mm_y * 76284; \
+    mm_b = (mm_yc		+ 132252*mm_v	) >> 16; \
+    mm_g = (mm_yc -  53281*mm_u -  25625*mm_v	) >> 16; \
+    mm_r = (mm_yc + 104595*mm_u			) >> 16; \
+    mb = LIMIT_RGB(mm_b); \
+    mg = LIMIT_RGB(mm_g); \
+    mr = LIMIT_RGB(mm_r); \
+}
+
+/* Debugging aid */
+#define IBMCAM_SAY_AND_WAIT(what) { \
+	wait_queue_head_t wq; \
+	init_waitqueue_head(&wq); \
+	printk(KERN_INFO "Say: %s\n", what); \
+	interruptible_sleep_on_timeout (&wq, HZ*3); \
+}
+
+/*
+ * This macro checks if ibmcam is still operational. The 'ibmcam'
+ * pointer must be valid, ibmcam->dev must be valid, we are not
+ * removing the device and the device has not erred on us.
+ */
+#define IBMCAM_IS_OPERATIONAL(ibm_cam) (\
+	(ibm_cam != NULL) && \
+	((ibm_cam)->dev != NULL) && \
+	((ibm_cam)->last_error == 0) && \
+	(!(ibm_cam)->remove_pending))
+
+enum {
+	STATE_SCANNING,		/* Scanning for header */
+	STATE_LINES,		/* Parsing lines */
+};
+
+enum {
+	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
+	FRAME_READY,		/* Ready to start grabbing */
+	FRAME_GRABBING,		/* In the process of being grabbed into */
+	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
+	FRAME_ERROR,		/* Something bad happened while processing */
+};
+
+struct usb_device;
+
+struct ibmcam_sbuf {
+	char *data;
+	urb_t *urb;
+};
+
+struct ibmcam_frame {
+	char *data;		/* Frame buffer */
+	int order_uv;		/* True=UV False=VU */
+	int order_yc;		/* True=Yc False=cY ('c'=either U or V) */
+	unsigned char hdr_sig;	/* "00 FF 00 ??" where 'hdr_sig' is '??' */
+
+	int width;		/* Width application is expecting */
+	int height;		/* Height */
+
+	int frmwidth;		/* Width the frame actually is */
+	int frmheight;		/* Height */
+
+	volatile int grabstate;	/* State of grabbing */
+	int scanstate;		/* State of scanning */
+
+	int curline;		/* Line of frame we're working on */
+
+	long scanlength;	/* uncompressed, raw data length of frame */
+	long bytes_read;	/* amount of scanlength that has been read from *data */
+
+	wait_queue_head_t wq;	/* Processes waiting */
+};
+
+#define	IBMCAM_MODEL_1	1	/* XVP-501, 3 interfaces, rev. 0.02 */
+#define IBMCAM_MODEL_2	2	/* KSX-X9903, 2 interfaces, rev. 3.0a */
+
+struct usb_ibmcam {
+	struct video_device vdev;
+
+	/* Device structure */
+	struct usb_device *dev;
+
+	unsigned char iface;                            /* Video interface number */
+	unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */
+
+	struct semaphore lock;
+	int user;		/* user count for exclusive use */
+
+	int ibmcam_used;        /* Is this structure in use? */
+	int initialized;	/* Had we already sent init sequence? */
+	int camera_model;	/* What type of IBM camera we got? */
+	int streaming;		/* Are we streaming Isochronous? */
+	int grabbing;		/* Are we grabbing? */
+	int last_error;		/* What calamity struck us? */
+
+	int compress;		/* Should the next frame be compressed? */
+
+	char *fbuf;		/* Videodev buffer area */
+	int fbuf_size;		/* Videodev buffer size */
+
+	int curframe;
+	struct ibmcam_frame frame[IBMCAM_NUMFRAMES];	/* Double buffering */
+
+	int cursbuf;		/* Current receiving sbuf */
+	struct ibmcam_sbuf sbuf[IBMCAM_NUMSBUF];	/* Double buffering */
+	volatile int remove_pending;	/* If set then about to exit */
+
+        /*
+	 * Scratch space from the Isochronous pipe.
+	 * Scratch buffer should contain at least one pair of lines
+	 * (CAMERA_IMAGE_LINE_SZ). We set it to two pairs here.
+	 * This will be approximately 2 KB. HOWEVER in reality this
+	 * buffer must be as large as hundred of KB because otherwise
+	 * you'll get lots of overflows because V4L client may request
+	 * frames not as uniformly as USB sources them.
+	 */
+	unsigned char *scratch;
+	int scratchlen;
+
+	struct video_picture vpic, vpic_old;	/* Picture settings */
+	struct video_capability vcap;		/* Video capabilities */
+	struct video_channel vchan;	/* May be used for tuner support */
+	unsigned char video_endp;	/* 0x82 for IBM camera */
+        int has_hdr;
+        int frame_num;
+	int iso_packet_len;		/* Videomode-dependent, saves bus bandwidth */
+
+	/* Statistics that can be overlayed on screen */
+        unsigned long urb_count;        /* How many URBs we received so far */
+        unsigned long urb_length;       /* Length of last URB */
+        unsigned long data_count;       /* How many bytes we received */
+        unsigned long header_count;     /* How many frame headers we found */
+	unsigned long scratch_ovf_count;/* How many times we overflowed scratch */
+	unsigned long iso_skip_count;	/* How many empty ISO packets received */
+	unsigned long iso_err_count;	/* How many bad ISO packets received */
+};
+
+#endif /* __LINUX_IBMCAM_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/inits.h linux.18p17/drivers/usb/inits.h
--- linux.vanilla/drivers/usb/inits.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/inits.h	Thu Jan  1 01:00:00 1970
@@ -1,6 +0,0 @@
-int bp_mouse_init(void);
-int usb_kbd_init(void);
-int usb_audio_init(void);
-int hub_init(void);
-void hub_cleanup(void);
-void usb_mouse_cleanup(void);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/inode.c linux.18p17/drivers/usb/inode.c
--- linux.vanilla/drivers/usb/inode.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/inode.c	Thu Sep 14 15:35:56 2000
@@ -0,0 +1,730 @@
+/*****************************************************************************/
+
+/*
+ *	inode.c  --  Inode/Dentry functions for the USB device file system.
+ *
+ *	Copyright (C) 2000
+ *          Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  $Id: inode.c,v 1.3 2000/01/11 13:58:25 tom Exp $
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ */
+
+/*****************************************************************************/
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(superlist);
+
+struct special {
+	const char *name;
+	struct inode_operations *iops;
+	struct inode *inode;
+	struct list_head inodes;
+};
+
+static struct inode_operations usbdevfs_devices_inode_operations = {
+	default_file_ops: &usbdevfs_devices_fops,
+};
+
+static struct inode_operations usbdevfs_drivers_inode_operations = {
+	default_file_ops: &usbdevfs_drivers_fops,
+};
+
+static struct inode_operations usbdevfs_device_inode_operations = {
+	default_file_ops: &usbdevfs_device_file_operations,
+};
+
+static struct special special[] = { 
+	{ "devices", &usbdevfs_devices_inode_operations,  },
+	{ "drivers", &usbdevfs_drivers_inode_operations,  }
+};
+
+#define NRSPECIAL (sizeof(special)/sizeof(special[0]))
+
+/* --------------------------------------------------------------------- */
+
+static int dnumber(struct dentry *dentry)
+{
+	const char *name;
+	unsigned int s;
+
+	if (dentry->d_name.len != 3)
+		return -1;
+	name = dentry->d_name.name;
+	if (name[0] < '0' || name[0] > '9' ||
+	    name[1] < '0' || name[1] > '9' ||
+	    name[2] < '0' || name[2] > '9')
+		return -1;
+	s = name[0] - '0';
+	s = s * 10 + name[1] - '0';
+	s = s * 10 + name[2] - '0';
+	return s;
+}
+
+/*
+ * utility functions; should be called with the kernel lock held
+ * to protect against busses/devices appearing/disappearing
+ */
+
+static void new_dev_inode(struct usb_device *dev, struct super_block *sb)
+{
+	struct inode *inode;
+	unsigned int devnum = dev->devnum;
+	unsigned int busnum = dev->bus->busnum;
+
+	if (devnum < 1 || devnum > 127 || busnum > 255)
+		return;
+	inode = iget(sb, IDEVICE | (busnum << 8) | devnum);
+	if (!inode) {
+		printk(KERN_ERR "usbdevfs: cannot create inode for bus %u device %u\n", busnum, devnum);
+		return;
+	}
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_uid = sb->u.usbdevfs_sb.devuid;
+	inode->i_gid = sb->u.usbdevfs_sb.devgid;
+	inode->i_mode = sb->u.usbdevfs_sb.devmode | S_IFREG;
+	inode->i_op = &usbdevfs_device_inode_operations;
+	inode->i_size = sizeof(struct usb_device_descriptor);
+	inode->u.usbdev_i.p.dev = dev;
+	list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
+	list_add_tail(&inode->u.usbdev_i.dlist, &dev->inodes);
+}
+
+static void recurse_new_dev_inode(struct usb_device *dev, struct super_block *sb)
+{
+	unsigned int i;
+
+	if (!dev)
+		return;
+	new_dev_inode(dev, sb);
+	for (i = 0; i < dev->maxchild; i++) {
+                if (!dev->children[i])
+                        continue;
+		recurse_new_dev_inode(dev->children[i], sb);
+	}
+}
+
+static void new_bus_inode(struct usb_bus *bus, struct super_block *sb)
+{
+	struct inode *inode;
+	unsigned int busnum = bus->busnum;
+
+	if (busnum > 255)
+		return;
+	inode = iget(sb, IBUS | (busnum << 8));
+	if (!inode) {
+		printk(KERN_ERR "usbdevfs: cannot create inode for bus %u\n", busnum);
+		return;
+	}
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_uid = sb->u.usbdevfs_sb.busuid;
+	inode->i_gid = sb->u.usbdevfs_sb.busgid;
+	inode->i_mode = sb->u.usbdevfs_sb.busmode | S_IFDIR;
+	inode->i_op = &usbdevfs_bus_inode_operations;
+	inode->u.usbdev_i.p.bus = bus;
+	list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
+	list_add_tail(&inode->u.usbdev_i.dlist, &bus->inodes);
+}
+
+static void free_inode(struct inode *inode)
+{
+	inode->u.usbdev_i.p.bus = NULL;
+	inode->u.usbdev_i.p.dev = NULL;
+	inode->i_mode &= ~S_IRWXUGO;
+	inode->i_uid = inode->i_gid = 0;
+	inode->i_size = 0;
+	list_del(&inode->u.usbdev_i.slist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
+	list_del(&inode->u.usbdev_i.dlist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
+	iput(inode);
+}
+
+static struct usb_bus *usbdevfs_findbus(int busnr)
+{
+        struct list_head *list;
+        struct usb_bus *bus;
+
+        for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
+                bus = list_entry(list, struct usb_bus, bus_list);
+                if (bus->busnum == busnr)
+                        return bus;
+        }
+        return NULL;
+}
+
+#if 0
+static struct usb_device *finddev(struct usb_device *dev, int devnr)
+{
+        unsigned int i;
+        struct usb_device *d2;
+
+        if (!dev)
+                return NULL;
+        if (dev->devnum == devnr)
+                return dev;
+        for (i = 0; i < dev->maxchild; i++) {
+                if (!dev->children[i])
+                        continue;
+                if ((d2 = finddev(dev->children[i], devnr)))
+                        return d2;
+        }
+        return NULL;
+}
+
+static struct usb_device *usbdevfs_finddevice(struct usb_bus *bus, int devnr)
+{
+        return finddev(bus->root_hub, devnr);
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static int usbdevfs_revalidate(struct dentry *dentry, int flags)
+{
+	struct inode *inode = dentry->d_inode;
+
+        if (!inode)
+                return 0;
+	if (ITYPE(inode->i_ino) == IBUS && !inode->u.usbdev_i.p.bus)
+		return 0;
+	if (ITYPE(inode->i_ino) == IDEVICE && !inode->u.usbdev_i.p.dev)
+		return 0;
+	return 1;
+}
+
+static struct dentry_operations usbdevfs_dentry_operations = {
+	d_revalidate:	usbdevfs_revalidate,
+};
+
+static struct dentry *usbdevfs_root_lookup(struct inode *dir, struct dentry *dentry)
+{
+	int busnr;
+	unsigned long ino = 0;
+	unsigned int i;
+	struct inode *inode;
+
+	/* sanity check */
+	if (dir->i_ino != IROOT)
+		return ERR_PTR(-EINVAL);
+	dentry->d_op = &usbdevfs_dentry_operations;
+	busnr = dnumber(dentry);
+	if (busnr >= 0 && busnr <= 255)
+		ino = IBUS | (busnr << 8);
+	if (!ino) {
+		for (i = 0; i < NRSPECIAL; i++) {
+			if (strlen(special[i].name) == dentry->d_name.len && 
+			    !strncmp(special[i].name, dentry->d_name.name, dentry->d_name.len)) {
+				ino = ISPECIAL | (i + IROOT + 1);
+				break;
+			}
+		}
+	}
+	if (!ino)
+		return ERR_PTR(-ENOENT);
+	inode = iget(dir->i_sb, ino);
+	if (!inode)
+		return ERR_PTR(-EINVAL);
+	if (inode && ITYPE(ino) == IBUS && inode->u.usbdev_i.p.bus == NULL) {
+		iput(inode);
+		inode = NULL;
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct dentry *usbdevfs_bus_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode;
+	int devnr;
+
+	/* sanity check */
+	if (ITYPE(dir->i_ino) != IBUS)
+		return ERR_PTR(-EINVAL);
+	dentry->d_op = &usbdevfs_dentry_operations;
+	devnr = dnumber(dentry);
+	if (devnr < 1 || devnr > 127)
+		return ERR_PTR(-ENOENT);
+	inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff << 8)) | devnr);
+	if (!inode)
+		return ERR_PTR(-EINVAL);
+	if (inode && inode->u.usbdev_i.p.dev == NULL) {
+		iput(inode);
+		inode = NULL;
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	unsigned long ino = inode->i_ino;
+	struct special *spec;
+	struct list_head *list;
+	struct usb_bus *bus;
+	char numbuf[8];
+	unsigned int i;
+
+	/* sanity check */
+	if (ino != IROOT)
+		return -EINVAL;
+	i = filp->f_pos;
+	switch (i) {
+	case 0:
+		if (filldir(dirent, ".", 1, i, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		i++;
+		/* fall through */
+
+	case 1:
+		if (filldir(dirent, "..", 2, i, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		i++;
+		/* fall through */
+
+	default:
+		
+		while (i >= 2 && i < 2+NRSPECIAL) {
+			spec = &special[filp->f_pos-2];
+			if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) < 0)
+				return 0;
+			filp->f_pos++;
+			i++;
+		}
+		if (i < 2+NRSPECIAL)
+			return 0;
+		i -= 2+NRSPECIAL;
+		lock_kernel();
+		for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
+			if (i > 0) {
+				i--;
+				continue;
+			}
+			bus = list_entry(list, struct usb_bus, bus_list);
+			sprintf(numbuf, "%03d", bus->busnum);
+			if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8)) < 0)
+				break;
+			filp->f_pos++;
+		}
+		unlock_kernel();
+		return 0;
+	}
+}
+
+static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struct file *filp, void *dirent, filldir_t filldir)
+{
+	char numbuf[8];
+	unsigned int i;
+
+	if (!dev)
+		return pos;
+	sprintf(numbuf, "%03d", dev->devnum);
+	if (pos > 0)
+		pos--;
+	else {
+		if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff)) < 0)
+			return -1;
+		filp->f_pos++;
+	}
+	for (i = 0; i < dev->maxchild; i++) {
+		if (!dev->children[i])
+			continue;
+		pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir);
+		if (pos < 0)
+			return -1;
+	}
+	return pos;
+}
+
+static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	unsigned long ino = inode->i_ino;
+	struct usb_bus *bus;
+
+	/* sanity check */
+	if (ITYPE(ino) != IBUS)
+		return -EINVAL;
+	switch ((unsigned int)filp->f_pos) {
+	case 0:
+		if (filldir(dirent, ".", 1, filp->f_pos, ino) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+
+	case 1:
+		if (filldir(dirent, "..", 2, filp->f_pos, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+
+	default:
+		lock_kernel();
+		bus = usbdevfs_findbus(IBUSNR(ino));
+		bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) << 8), filp->f_pos-2, filp, dirent, filldir);
+		unlock_kernel();
+		return 0;
+	}
+}
+
+static struct file_operations usbdevfs_root_file_operations = {
+	readdir: usbdevfs_root_readdir,
+};
+
+static struct inode_operations usbdevfs_root_inode_operations = {
+	default_file_ops: &usbdevfs_root_file_operations,
+	lookup: usbdevfs_root_lookup,
+};
+
+static struct file_operations usbdevfs_bus_file_operations = {
+	readdir: usbdevfs_bus_readdir,
+};
+
+static struct inode_operations usbdevfs_bus_inode_operations = {
+	default_file_ops: &usbdevfs_bus_file_operations,
+	lookup: usbdevfs_bus_lookup,
+};
+
+static void usbdevfs_read_inode(struct inode *inode)
+{
+	struct special *spec;
+
+	inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME;
+	inode->i_mode = S_IFREG;
+	inode->i_gid = inode->i_uid = 0;
+	INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
+	inode->u.usbdev_i.p.dev = NULL;
+	inode->u.usbdev_i.p.bus = NULL;
+	switch (ITYPE(inode->i_ino)) {
+	case ISPECIAL:
+		if (inode->i_ino == IROOT) {
+			inode->i_op = &usbdevfs_root_inode_operations;
+			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+			return;
+		}
+		if (inode->i_ino <= IROOT || inode->i_ino > IROOT+NRSPECIAL)
+			return;
+		spec = &special[inode->i_ino-(IROOT+1)];
+		inode->i_op = spec->iops;
+		return;
+
+	case IDEVICE:
+		return;
+
+	case IBUS:
+		return;
+
+	default:
+		return;
+        }
+}
+
+static void usbdevfs_put_super(struct super_block *sb)
+{
+	list_del(&sb->u.usbdevfs_sb.slist);
+	INIT_LIST_HEAD(&sb->u.usbdevfs_sb.slist);
+	while (!list_empty(&sb->u.usbdevfs_sb.ilist))
+		free_inode(list_entry(sb->u.usbdevfs_sb.ilist.next, struct inode, u.usbdev_i.slist));
+	MOD_DEC_USE_COUNT;
+}
+
+static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int x)
+{
+        buf->f_type = USBDEVICE_SUPER_MAGIC;
+        buf->f_bsize = PAGE_SIZE/sizeof(long);   /* ??? */
+        buf->f_bfree = 0;
+        buf->f_bavail = 0;
+        buf->f_ffree = 0;
+        buf->f_namelen = NAME_MAX;
+        return 0;
+}
+
+static struct super_operations usbdevfs_sops = { 
+	read_inode:	usbdevfs_read_inode,
+	put_super:	usbdevfs_put_super,
+	statfs:		usbdevfs_statfs,
+};
+
+struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int silent)
+{
+        struct inode *root_inode, *inode;
+	struct list_head *blist;
+	struct usb_bus *bus;
+	unsigned int i;
+	uid_t devuid = 0, busuid = 0, listuid = 0;
+	gid_t devgid = 0, busgid = 0, listgid = 0;
+	umode_t devmode = S_IWUSR | S_IRUGO, busmode = S_IXUGO | S_IRUGO, listmode = S_IRUGO;
+	char *curopt = NULL, *value;
+
+	MOD_INC_USE_COUNT;
+	/* parse options */
+	if (data)
+		curopt = strtok(data, ",");
+	for (; curopt; curopt = strtok(NULL, ",")) {
+		if ((value = strchr(curopt, '=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(curopt, "devuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			devuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "devgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			devgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "devmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			devmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			busuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			busgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			busmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			listuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			listgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			listmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+	}
+	/* fill superblock */
+        s->s_blocksize = 1024;
+        s->s_blocksize_bits = 10;
+        s->s_magic = USBDEVICE_SUPER_MAGIC;
+        s->s_op = &usbdevfs_sops;
+	INIT_LIST_HEAD(&s->u.usbdevfs_sb.slist);
+	INIT_LIST_HEAD(&s->u.usbdevfs_sb.ilist);
+	s->u.usbdevfs_sb.devuid = devuid;
+	s->u.usbdevfs_sb.devgid = devgid;
+	s->u.usbdevfs_sb.devmode = devmode;
+	s->u.usbdevfs_sb.busuid = busuid;
+	s->u.usbdevfs_sb.busgid = busgid;
+	s->u.usbdevfs_sb.busmode = busmode;
+	root_inode = iget(s, IROOT);
+        if (!root_inode)
+                goto out_no_root;
+        s->s_root = d_alloc_root(root_inode, NULL);
+        if (!s->s_root)
+                goto out_no_root;
+	list_add_tail(&s->u.usbdevfs_sb.slist, &superlist);
+	for (i = 0; i < NRSPECIAL; i++) {
+		if (!(inode = iget(s, IROOT+1+i)))
+			continue;
+		inode->i_uid = listuid;
+		inode->i_gid = listgid;
+		inode->i_mode = listmode | S_IFREG;
+		special[i].inode = inode;
+		list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist);
+		list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes);
+	}
+	lock_kernel();
+	for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) {
+		bus = list_entry(blist, struct usb_bus, bus_list);
+		new_bus_inode(bus, s);
+		recurse_new_dev_inode(bus->root_hub, s);
+	}
+	unlock_kernel();
+        return s;
+
+ out_no_root:
+        printk("usbdevfs_read_super: get root inode failed\n");
+        iput(root_inode);
+	MOD_DEC_USE_COUNT;
+        return NULL;
+
+ opterr:
+        printk(KERN_WARNING "usbdevfs: mount parameter error\n");
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, 0);
+
+/* --------------------------------------------------------------------- */
+
+static void update_special_inodes (void)
+{
+	int i;
+	for (i = 0; i < NRSPECIAL; i++) {
+		struct inode *inode = special[i].inode;
+		if (inode)
+			inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	}
+}
+
+
+void usbdevfs_add_bus(struct usb_bus *bus)
+{
+	struct list_head *slist;
+
+	lock_kernel();
+	for (slist = superlist.next; slist != &superlist; slist = slist->next)
+		new_bus_inode(bus, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_remove_bus(struct usb_bus *bus)
+{
+	lock_kernel();
+	while (!list_empty(&bus->inodes))
+		free_inode(list_entry(bus->inodes.next, struct inode, u.usbdev_i.dlist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_add_device(struct usb_device *dev)
+{
+	struct list_head *slist;
+
+	lock_kernel();
+	for (slist = superlist.next; slist != &superlist; slist = slist->next)
+		new_dev_inode(dev, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_remove_device(struct usb_device *dev)
+{
+	struct dev_state *ds;
+	struct siginfo sinfo;
+
+	lock_kernel();
+	while (!list_empty(&dev->inodes))
+		free_inode(list_entry(dev->inodes.next, struct inode, u.usbdev_i.dlist));
+	while (!list_empty(&dev->filelist)) {
+		ds = list_entry(dev->filelist.next, struct dev_state, list);
+		list_del(&ds->list);
+		INIT_LIST_HEAD(&ds->list);
+		down_write(&ds->devsem);
+		ds->dev = NULL;
+		up_write(&ds->devsem);
+		if (ds->discsignr) {
+			sinfo.si_signo = SIGPIPE;
+			sinfo.si_errno = EPIPE;
+			sinfo.si_code = SI_ASYNCIO;
+			sinfo.si_addr = ds->disccontext;
+			send_sig_info(ds->discsignr, &sinfo, ds->disctask);
+		}
+	}
+	
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_PROC_FS		
+static struct proc_dir_entry *usbdir = NULL;
+#endif	
+
+int __init usbdevfs_init(void)
+{
+	int ret;
+
+	for (ret = 0; ret < NRSPECIAL; ret++) {
+		INIT_LIST_HEAD(&special[ret].inodes);
+	}
+	if ((ret = usb_register(&usbdevfs_driver)))
+		return ret;
+	if ((ret = register_filesystem(&usbdevice_fs_type)))
+		usb_deregister(&usbdevfs_driver);
+#ifdef CONFIG_PROC_FS		
+	/* create mount point for usbdevfs */
+	usbdir = proc_mkdir("usb", proc_bus);
+#endif	
+	return ret;
+}
+
+void __exit usbdevfs_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS	
+        if (usbdir)
+                remove_proc_entry("usb", proc_bus);
+#endif
+	usb_deregister(&usbdevfs_driver);
+	unregister_filesystem(&usbdevice_fs_type);
+}
+
+#if 0
+module_init(usbdevfs_init);
+module_exit(usbdevfs_cleanup);
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/input.c linux.18p17/drivers/usb/input.c
--- linux.vanilla/drivers/usb/input.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/input.c	Sat Sep 30 17:28:18 2000
@@ -0,0 +1,409 @@
+/*
+ * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  The input layer module itself
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/kcomp.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input layer module");
+
+EXPORT_SYMBOL(input_register_device);
+EXPORT_SYMBOL(input_unregister_device);
+EXPORT_SYMBOL(input_register_handler);
+EXPORT_SYMBOL(input_unregister_handler);
+EXPORT_SYMBOL(input_register_minor);
+EXPORT_SYMBOL(input_unregister_minor);
+EXPORT_SYMBOL(input_open_device);
+EXPORT_SYMBOL(input_close_device);
+EXPORT_SYMBOL(input_event);
+
+#define INPUT_MAJOR	13
+#define INPUT_DEVICES	256
+
+static struct input_dev *input_dev = NULL;
+static struct input_handler *input_handler = NULL;
+static struct input_handler *input_table[8] = { NULL, /* ... */ };
+static devfs_handle_t input_devfs_handle = NULL;
+static int input_number = 0;
+static long input_devices[NBITS(INPUT_DEVICES)] = { 0, /* ... */ };
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct input_handle *handle = dev->handle;
+
+/*
+ * Filter non-events, and bad input values out.
+ */
+
+	if (type > EV_MAX || !test_bit(type, dev->evbit))
+		return;
+
+	switch (type) {
+
+		case EV_KEY:
+
+			if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+				return;
+
+			if (value == 2) break;
+
+			change_bit(code, dev->key);
+
+			if (test_bit(EV_REP, dev->evbit) && dev->timer.function) {
+				if (value) {
+					mod_timer(&dev->timer, jiffies + dev->rep[REP_DELAY]);
+					dev->repeat_key = code;
+					break;
+				}
+				if (dev->repeat_key == code)
+					del_timer(&dev->timer);
+			}
+
+			break;
+		
+		case EV_ABS:
+
+			if (code > ABS_MAX || !test_bit(code, dev->absbit))
+				return;
+
+			if (dev->absfuzz[code]) {
+				if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+				    (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+					return;
+
+				if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+				    (value < dev->abs[code] + dev->absfuzz[code]))
+					value = (dev->abs[code] * 3 + value) >> 2;
+
+				if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+				    (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+					value = (dev->abs[code] + value) >> 1;
+			}
+
+			if (dev->abs[code] == value)
+				return;
+
+			dev->abs[code] = value;
+			break;
+
+		case EV_REL:
+
+			if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+				return;
+
+			break;
+
+		case EV_LED:
+	
+			if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+				return;
+
+			change_bit(code, dev->led);
+			if (dev->event) dev->event(dev, type, code, value);	
+	
+			break;
+
+		case EV_SND:
+	
+			if (code > SND_MAX || !test_bit(code, dev->sndbit) || !!test_bit(code, dev->snd) == value)
+				return;
+
+			change_bit(code, dev->snd);
+			if (dev->event) dev->event(dev, type, code, value);	
+	
+			break;
+
+		case EV_REP:
+
+			if (code > REP_MAX || dev->rep[code] == value) return;
+
+			dev->rep[code] = value;
+			if (dev->event) dev->event(dev, type, code, value);
+
+			break;
+	}
+/*
+ * Add randomness.
+ */
+
+#if 0 /* BUG */
+	add_input_randomness(((unsigned long) dev) ^ (type << 24) ^ (code << 16) ^ value);
+#endif
+
+/*
+ * Distribute the event to handler modules.
+ */
+
+	while (handle) {
+		if (handle->open)
+			handle->handler->event(handle, type, code, value);
+		handle = handle->dnext;
+	}
+}
+
+static void input_repeat_key(unsigned long data)
+{
+	struct input_dev *dev = (void *) data;
+	input_event(dev, EV_KEY, dev->repeat_key, 2);
+	mod_timer(&dev->timer, jiffies + dev->rep[REP_PERIOD]);
+}
+
+int input_open_device(struct input_handle *handle)
+{
+	handle->open++;
+	if (handle->dev->open)
+		return handle->dev->open(handle->dev);
+	return 0;
+}
+
+void input_close_device(struct input_handle *handle)
+{
+	if (handle->dev->close)
+		handle->dev->close(handle->dev);
+	handle->open--;
+}
+
+static void input_link_handle(struct input_handle *handle)
+{
+	handle->dnext = handle->dev->handle;
+	handle->hnext = handle->handler->handle;
+	handle->dev->handle = handle;
+	handle->handler->handle = handle;
+}
+
+static void input_unlink_handle(struct input_handle *handle)
+{
+	struct input_handle **handleptr;
+
+	handleptr = &handle->dev->handle;
+	while (*handleptr && (*handleptr != handle))
+		handleptr = &((*handleptr)->dnext);
+	*handleptr = (*handleptr)->dnext;
+
+	handleptr = &handle->handler->handle;
+	while (*handleptr && (*handleptr != handle))
+		handleptr = &((*handleptr)->hnext);
+	*handleptr = (*handleptr)->hnext;
+}
+
+void input_register_device(struct input_dev *dev)
+{
+	struct input_handler *handler = input_handler;
+	struct input_handle *handle;
+
+/*
+ * Initialize repeat timer to default values.
+ */
+
+	init_timer(&dev->timer);
+	dev->timer.data = (long) dev;
+	dev->timer.function = input_repeat_key;
+	dev->rep[REP_DELAY] = HZ/4;
+	dev->rep[REP_PERIOD] = HZ/33;
+
+/*
+ * Add the device.
+ */
+
+	if (input_number >= INPUT_DEVICES) {
+		printk(KERN_WARNING "input: ran out of input device numbers!\n");
+		dev->number = input_number;
+	} else {
+		dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES);
+		set_bit(dev->number, input_devices);
+	}
+		
+	dev->next = input_dev;	
+	input_dev = dev;
+	input_number++;
+
+/*
+ * Notify handlers.
+ */
+
+	while (handler) {
+		if ((handle = handler->connect(handler, dev)))
+			input_link_handle(handle);
+		handler = handler->next;
+	}
+}
+
+void input_unregister_device(struct input_dev *dev)
+{
+	struct input_handle *handle = dev->handle;
+	struct input_dev **devptr = &input_dev;
+	struct input_handle *dnext;
+
+/*
+ * Kill any pending repeat timers.
+ */
+
+	del_timer(&dev->timer);
+
+/*
+ * Notify handlers.
+ */
+
+	while (handle) {
+		dnext = handle->dnext;
+		input_unlink_handle(handle);
+		handle->handler->disconnect(handle);
+		handle = dnext;
+	}
+
+/*
+ * Remove the device.
+ */
+
+	while (*devptr && (*devptr != dev))
+		devptr = &((*devptr)->next);
+	*devptr = (*devptr)->next;
+
+	input_number--;
+
+	if (dev->number < INPUT_DEVICES)
+		clear_bit(dev->number, input_devices);
+}
+
+void input_register_handler(struct input_handler *handler)
+{
+	struct input_dev *dev = input_dev;
+	struct input_handle *handle;
+
+/*
+ * Add minors if needed.
+ */
+
+	if (handler->fops != NULL)
+		input_table[handler->minor >> 5] = handler;
+
+/*
+ * Add the handler.
+ */
+
+	handler->next = input_handler;	
+	input_handler = handler;
+	
+/*
+ * Notify it about all existing devices.
+ */
+
+	while (dev) {
+		if ((handle = handler->connect(handler, dev)))
+			input_link_handle(handle);
+		dev = dev->next;
+	}
+}
+
+void input_unregister_handler(struct input_handler *handler)
+{
+	struct input_handler **handlerptr = &input_handler;
+	struct input_handle *handle = handler->handle;
+	struct input_handle *hnext;
+
+/*
+ * Tell the handler to disconnect from all devices it keeps open.
+ */
+
+	while (handle) {
+		hnext = handle->hnext;
+		input_unlink_handle(handle);
+		handler->disconnect(handle);
+		handle = hnext;
+	}
+
+/*
+ * Remove it.
+ */
+
+	while (*handlerptr && (*handlerptr != handler))
+		handlerptr = &((*handlerptr)->next);
+
+	*handlerptr = (*handlerptr)->next;
+
+/*
+ * Remove minors.
+ */
+
+	if (handler->fops != NULL)
+		input_table[handler->minor >> 5] = NULL;
+}
+
+static int input_open_file(struct inode *inode, struct file *file)
+{
+	struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5];
+
+	if (!handler || !handler->fops || !handler->fops->open)
+		return -ENODEV;
+
+	file->f_op = handler->fops;
+
+	return handler->fops->open(inode, file);
+}
+
+static struct file_operations input_fops = {
+	open: input_open_file,
+};
+
+devfs_handle_t input_register_minor(char *name, int minor, int minor_base)
+{
+	char devfs_name[16];
+	sprintf(devfs_name, name, minor);
+	return devfs_register(input_devfs_handle, devfs_name, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base,
+		S_IFCHR | S_IRUGO | S_IWUSR, &input_fops, NULL);
+}
+
+void input_unregister_minor(devfs_handle_t handle)
+{
+	devfs_unregister(handle);
+}
+
+static int __init input_init(void)
+{
+	if (devfs_register_chrdev(INPUT_MAJOR, "input", &input_fops)) {
+		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+		return -EBUSY;
+	}
+	input_devfs_handle = devfs_mk_dir(NULL, "input", 5, NULL);
+	return 0;
+}
+
+static void __exit input_exit(void)
+{
+	devfs_unregister(input_devfs_handle);
+        if (devfs_unregister_chrdev(INPUT_MAJOR, "input"))
+                printk(KERN_ERR "input: can't unregister char major %d", INPUT_MAJOR);
+}
+
+module_init(input_init);
+module_exit(input_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/joydev.c linux.18p17/drivers/usb/joydev.c
--- linux.vanilla/drivers/usb/joydev.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/joydev.c	Fri Sep 22 14:56:09 2000
@@ -0,0 +1,496 @@
+/*
+ * $Id: joydev.c,v 1.13 2000/08/14 21:05:26 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik 
+ *  Copyright (c) 1999 Colin Van Dyke 
+ *
+ *  Joystick device driver for the input driver suite.
+ *
+ *  Sponsored by SuSE and Intel
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+
+#define JOYDEV_MINOR_BASE	0
+#define JOYDEV_MINORS		32
+#define JOYDEV_BUFFER_SIZE	64
+
+struct joydev {
+	int exist;
+	int open;
+	int minor;
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	devfs_handle_t devfs;
+	struct joydev *next;
+	struct joydev_list *list;
+	struct js_corr corr[ABS_MAX];
+	struct JS_DATA_SAVE_TYPE glue;
+	int nabs;
+	int nkey;
+	__u16 keymap[KEY_MAX - BTN_MISC];
+	__u16 keypam[KEY_MAX - BTN_MISC];
+	__u8 absmap[ABS_MAX];
+	__u8 abspam[ABS_MAX];
+	__s16 abs[ABS_MAX];
+};
+
+struct joydev_list {
+	struct js_event buffer[JOYDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	int startup;
+	struct fasync_struct *fasync;
+	struct joydev *joydev;
+	struct joydev_list *next;
+};
+
+static struct joydev *joydev_table[JOYDEV_MINORS];
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Joystick device driver");
+MODULE_SUPPORTED_DEVICE("input/js");
+
+static int joydev_correct(int value, struct js_corr *corr)
+{
+	switch (corr->type) {
+		case JS_CORR_NONE:
+			break;
+		case JS_CORR_BROKEN:
+			value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+				((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+				((corr->coef[2] * (value - corr->coef[0])) >> 14);
+			break;
+		default:
+			return 0;
+	}
+
+	if (value < -32767) return -32767;
+	if (value >  32767) return  32767;
+
+	return value;
+}
+
+static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct joydev *joydev = handle->private;
+	struct joydev_list *list = joydev->list;
+	struct js_event event;
+
+	switch (type) {
+
+		case EV_KEY:
+			if (code < BTN_MISC || value == 2) return;
+			event.type = JS_EVENT_BUTTON;
+			event.number = joydev->keymap[code - BTN_MISC];
+			event.value = value;
+			break;
+
+		case EV_ABS:
+			event.type = JS_EVENT_AXIS;
+			event.number = joydev->absmap[code];
+			event.value = joydev_correct(value, joydev->corr + event.number);
+			if (event.value == joydev->abs[event.number]) return;
+			joydev->abs[event.number] = event.value;
+			break;
+
+		default:
+			return;
+	}  
+
+	event.time = jiffies * (1000 / HZ);
+
+	while (list) {
+
+		memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
+
+		if (list->startup == joydev->nabs + joydev->nkey)
+			if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+				list->startup = 0;
+
+		kill_fasync(list->fasync, SIGIO);
+
+		list = list->next;
+	}
+
+	wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct joydev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int joydev_release(struct inode * inode, struct file * file)
+{
+	struct joydev_list *list = file->private_data;
+	struct joydev_list **listptr;
+
+	lock_kernel();
+	listptr = &list->joydev->list;
+	joydev_fasync(-1, file, 0);
+
+	while (*listptr && (*listptr != list))
+		listptr = &((*listptr)->next);
+	*listptr = (*listptr)->next;
+
+	if (!--list->joydev->open) {
+		if (list->joydev->exist) {
+			input_close_device(&list->joydev->handle);
+		} else {
+			input_unregister_minor(list->joydev->devfs);
+			joydev_table[list->joydev->minor] = NULL;
+			kfree(list->joydev);
+		}
+	}
+
+	kfree(list);
+	unlock_kernel();
+
+	return 0;
+}
+
+static int joydev_open(struct inode *inode, struct file *file)
+{
+	struct joydev_list *list;
+	int i = MINOR(inode->i_rdev) - JOYDEV_MINOR_BASE;
+
+	if (i > JOYDEV_MINORS || !joydev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct joydev_list));
+
+	list->joydev = joydev_table[i];
+	list->next = joydev_table[i]->list;
+	joydev_table[i]->list = list;	
+
+	file->private_data = list;
+
+	if (!list->joydev->open++)
+		if (list->joydev->exist)
+			input_open_device(&list->joydev->handle);
+
+	return 0;
+}
+
+static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct joydev_list *list = file->private_data;
+	struct joydev *joydev = list->joydev;
+	struct input_dev *input = joydev->handle.dev;
+	int retval = 0;
+
+	if (count < sizeof(struct js_event))
+		return -EINVAL;
+
+	if (count == sizeof(struct JS_DATA_TYPE)) {
+
+		struct JS_DATA_TYPE data;
+
+		data.buttons =  ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) |
+				((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0);
+		data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+		data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+
+		if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+			return -EFAULT;
+
+		list->startup = 0;
+		list->tail = list->head;
+
+		return sizeof(struct JS_DATA_TYPE);
+	}
+
+	if (list->head == list->tail && list->startup == joydev->nabs + joydev->nkey) {
+
+		add_wait_queue(&list->joydev->wait, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+
+		while (list->head == list->tail) {
+
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+
+			schedule();
+		}
+
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&list->joydev->wait, &wait);
+	}
+
+	if (retval)
+		return retval;
+
+	while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+
+		struct js_event event;
+
+		event.time = jiffies * (1000/HZ);
+
+		if (list->startup < joydev->nkey) {
+			event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+			event.number = list->startup;
+			event.value = !!test_bit(joydev->keypam[event.number], input->key);
+		} else {
+			event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+			event.number = list->startup - joydev->nkey;
+			event.value = joydev->abs[event.number];
+		}
+
+		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+			return -EFAULT;
+
+		list->startup++;
+		retval += sizeof(struct js_event);
+	}
+
+	while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
+
+		if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
+			return -EFAULT;
+
+		list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+		retval += sizeof(struct js_event);
+	}
+
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int joydev_poll(struct file *file, poll_table *wait)
+{
+	struct joydev_list *list = file->private_data;
+	poll_wait(file, &list->joydev->wait, wait);
+	if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct joydev_list *list = file->private_data;
+	struct joydev *joydev = list->joydev;
+	struct input_dev *dev = joydev->handle.dev;
+
+
+	switch (cmd) {
+
+		case JS_SET_CAL:
+			return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg,
+				sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+		case JS_GET_CAL:
+			return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR,
+				sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+		case JS_SET_TIMEOUT:
+			return get_user(joydev->glue.JS_TIMEOUT, (int *) arg);
+		case JS_GET_TIMEOUT:
+			return put_user(joydev->glue.JS_TIMEOUT, (int *) arg);
+		case JS_SET_TIMELIMIT:
+			return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg);
+		case JS_GET_TIMELIMIT:
+			return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg);
+		case JS_SET_ALL:
+			return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg,
+						sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+		case JS_GET_ALL:
+			return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue,
+						sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+		case JSIOCGVERSION:
+			return put_user(JS_VERSION, (__u32 *) arg);
+		case JSIOCGAXES:
+			return put_user(joydev->nabs, (__u8 *) arg);
+		case JSIOCGBUTTONS:
+			return put_user(joydev->nkey, (__u8 *) arg);
+		case JSIOCSCORR:
+			return copy_from_user(joydev->corr, (struct js_corr *) arg,
+						sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+		case JSIOCGCORR:
+			return copy_to_user((struct js_corr *) arg, joydev->corr,
+						sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+		default:
+			if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+				int len;
+				if (!dev->name) return 0;
+				len = strlen(dev->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT;
+				return len;
+			}
+	}
+	return -EINVAL;
+}
+
+static struct file_operations joydev_fops = {
+	read:		joydev_read,
+	write:		joydev_write,
+	poll:		joydev_poll,
+	open:		joydev_open,
+	release:	joydev_release,
+	ioctl:		joydev_ioctl,
+	fasync:		joydev_fasync,
+};
+
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+	struct joydev *joydev;
+	int i, j, minor;
+
+	if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_ABS, dev->evbit) &&
+	     (test_bit(ABS_X, dev->absbit) || test_bit(ABS_Y, dev->absbit)) &&
+	     (test_bit(BTN_TRIGGER, dev->keybit) || test_bit(BTN_A, dev->keybit)
+		|| test_bit(BTN_1, dev->keybit)))) return NULL; 
+
+	for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
+	if (joydev_table[minor]) {
+		printk(KERN_ERR "joydev: no more free joydev devices\n");
+		return NULL;
+	}
+
+	if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
+		return NULL;
+	memset(joydev, 0, sizeof(struct joydev));
+
+	init_waitqueue_head(&joydev->wait);
+
+	joydev->minor = minor;
+	joydev_table[minor] = joydev;
+
+	joydev->handle.dev = dev;
+	joydev->handle.handler = handler;
+	joydev->handle.private = joydev;
+
+	joydev->exist = 1;
+
+	for (i = 0; i < ABS_MAX; i++)
+		if (test_bit(i, dev->absbit)) {
+			joydev->absmap[i] = joydev->nabs;
+			joydev->abspam[joydev->nabs] = i;
+			joydev->nabs++;
+		}
+
+	for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < joydev->nabs; i++) {
+		j = joydev->abspam[i];
+		if (dev->absmax[j] == dev->absmin[j]) {
+			joydev->corr[i].type = JS_CORR_NONE;
+			continue;
+		}
+		joydev->corr[i].type = JS_CORR_BROKEN;
+		joydev->corr[i].prec = dev->absfuzz[j];
+		joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+		joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+		joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]);
+		joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]);
+
+		joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+	}
+
+	joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE);
+
+	printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number);
+
+	return &joydev->handle;
+}
+
+static void joydev_disconnect(struct input_handle *handle)
+{
+	struct joydev *joydev = handle->private;
+
+	joydev->exist = 0;
+
+	if (joydev->open) {
+		input_close_device(handle);	
+	} else {
+		input_unregister_minor(joydev->devfs);
+		joydev_table[joydev->minor] = NULL;
+		kfree(joydev);
+	}
+}
+
+static struct input_handler joydev_handler = {
+	event:		joydev_event,
+	connect:	joydev_connect,
+	disconnect:	joydev_disconnect,
+	fops:		&joydev_fops,
+	minor:		JOYDEV_MINOR_BASE,
+};
+
+static int __init joydev_init(void)
+{
+	input_register_handler(&joydev_handler);
+	return 0;
+}
+
+static void __exit joydev_exit(void)
+{
+	input_unregister_handler(&joydev_handler);
+}
+
+module_init(joydev_init);
+module_exit(joydev_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/kaweth.c linux.18p17/drivers/usb/kaweth.c
--- linux.vanilla/drivers/usb/kaweth.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/kaweth.c	Fri Sep  1 14:07:21 2000
@@ -0,0 +1,1030 @@
+/****************************************************************
+ *
+ *     kawasaki.c - driver for KL5KUSB101 based USB->Ethernet
+ *
+ *     (c) 2000 Interlan Communications
+ *
+ *     Original author: The Zapman <zapman@interlan.net>
+ *     	Inspired by, and much credit goes to Michael Rothwell <rothwell@interlan.net>
+ *     		for the test equipment, help, and patience
+ *			Based off of (and with thanks to) Petko Manolov's pegaus.c driver.
+ *			Also many thanks to Joel Silverman at Kawasaki for providing the firmware
+ *				and driver resources.
+ *
+ *		This program is free software; you can redistribute it and/or modify it
+ *		under the terms of the GNU General Public License as published by the
+ *		Free Software Foundation; either version 2, or (at your option) any
+ *		later version.
+ *
+ *		This program is distributed in the hope that it will be useful,
+ *		but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *		GNU General Public License for more details.
+ *		
+ *		You should have received a copy of the GNU General Public License
+ *		along with this program; if not, write to the Free Software Foundation,
+ *		Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ *
+ ****************************************************************/
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include <linux/types.h>
+#include <asm/semaphore.h>
+
+#include "kawethfw.h"
+
+#define KAWETH_MTU								1514
+#define KAWETH_BUF_SIZE						1664
+#define KAWETH_TX_TIMEOUT					(1 * HZ)
+#define KAWETH_FIRMWARE_BUF_SIZE	4096
+#define KAWETH_CONTROL_TIMEOUT		(1 * HZ)
+
+#define KAWETH_STATUS_BROKEN			0x0000001
+#define KAWETH_STATUS_CLOSING			0x0000002
+
+#define KAWETH_PACKET_FILTER_PROMISCUOUS		0x01
+#define KAWETH_PACKET_FILTER_ALL_MULTICAST	0x02
+#define KAWETH_PACKET_FILTER_DIRECTED				0x04
+#define KAWETH_PACKET_FILTER_BROADCAST			0x08
+#define KAWETH_PACKET_FILTER_MULTICAST			0x10
+
+#define KAWETH_COMMAND_GET_ETHERNET_DESC		0x00
+#define KAWETH_COMMAND_SET_PACKET_FILTER		0x02
+#define KAWETH_COMMAND_GET_MAC							0x06
+#define KAWETH_COMMAND_SET_URB_SIZE					0x08
+#define KAWETH_COMMAND_SET_SOFS_WAIT				0x09
+#define KAWETH_COMMAND_SCAN									0xFF
+
+#define KAWETH_SOFS_TO_WAIT									0x05
+
+
+MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>");
+MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
+
+static void *kaweth_probe(struct usb_device *dev, unsigned int ifnum);
+static void kaweth_disconnect(struct usb_device *dev, void *ptr);
+
+/****************************************************************
+ *     kaweth_driver
+ ****************************************************************/
+static struct usb_driver kaweth_driver = {
+	name:		"kaweth",
+	probe:		kaweth_probe,
+	disconnect:	kaweth_disconnect,
+};
+//  __attribute__ ((packed))
+typedef __u8 eth_addr_t[6];
+
+/****************************************************************
+ *     usb_eth_dev
+ ****************************************************************/
+struct usb_eth_dev {
+	char *name;
+	__u16 vendor;
+	__u16 device;
+	void *pdata;
+};
+
+/****************************************************************
+ *     kaweth_ethernet_configuration
+ ****************************************************************/
+struct kaweth_ethernet_configuration
+{
+	__u8 size;
+	__u8 reserved1;
+	__u8 reserved2;
+	eth_addr_t hw_addr;
+	__u32 statistics_mask;
+	__u16 segment_size;
+	__u16 max_multicast_filters;
+	__u8 reserved3;
+} __attribute__ ((packed));
+
+/****************************************************************
+ *     kaweth_device
+ ****************************************************************/
+struct kaweth_device
+{
+	spinlock_t device_lock;
+
+	__u32 status;
+
+	struct usb_device *dev;
+	struct net_device *net;
+	wait_queue_head_t control_wait;
+
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	
+	__u8 firmware_buf[KAWETH_FIRMWARE_BUF_SIZE];
+	__u8 tx_buf[KAWETH_BUF_SIZE];
+	__u8 rx_buf[KAWETH_BUF_SIZE];
+
+	struct kaweth_ethernet_configuration configuration;
+
+	struct net_device_stats stats;
+} __attribute__ ((packed));
+
+/****************************************************************
+ *     usb_dev_id
+ ****************************************************************/
+static struct usb_eth_dev usb_dev_id[] = {
+	{ "KLSI KL5KUSB101B", 0x05e9, 0x0008, NULL },
+	{ "NetGear EA-101", 0x0846, 0x1001, NULL },
+	{ "Peracom Enet", 0x0565, 0x0002, NULL },
+	{ "Peracom Enet2", 0x0565, 0x0005, NULL },
+	{ "3Com 3C19250", 0x0506, 0x03e8, NULL },
+	{ "Linksys USB10T", 0x066b, 0x2202, NULL },
+	{ "D-Link DSB-650C", 0x2001, 0x4000, NULL },
+	{ "D-Link DSB-650", 0x0557, 0x2002, NULL },
+	{ "AOX USB Ethernet", 0x03e8, 0x0008, NULL },
+	{ "ADS USB-10BT", 0x06e1, 0x0008, NULL },
+	{ "Entrega E45", 0x1645, 0x0005, NULL},
+	{ "SMC 2202USB", 0x0707, 0x0100, NULL},
+	{ "Correga K.K.", 0x07aa, 0x0001, NULL},
+	{ NULL, 0, 0, NULL },
+};
+
+/****************************************************************
+ *     kaweth_check_device_ids
+ ****************************************************************/
+static int kaweth_check_device_ids(__u16 vendor, __u16 product)
+{
+	int i=0;
+	
+	while (usb_dev_id[i].name)
+	{
+		if((usb_dev_id[i].vendor == vendor) && 
+			 (usb_dev_id[i].device == product))
+			{
+				printk("%s connected...\n", usb_dev_id[i].name);
+				
+				return 0;
+			}
+		i++;
+	}
+	return	1;
+}
+
+/****************************************************************
+ *     kaweth_control_complete
+ ****************************************************************/
+static void kaweth_control_complete(struct urb *urb)
+{
+	struct kaweth_device *kaweth = (struct kaweth_device *)urb->context;
+
+	if(waitqueue_active(&kaweth->control_wait))
+	{
+		wake_up(&kaweth->control_wait);
+	}
+}
+
+/****************************************************************
+ *     kaweth_control
+ ****************************************************************/
+static int kaweth_control(struct kaweth_device *kaweth,
+													unsigned int pipe, 
+													__u8 request, 
+													__u8 requesttype, 
+													__u16 value, 
+													__u16 index,
+													void *data, 
+													__u16 size, 
+													int timeout)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	purb_t urb;
+	devrequest *dr;
+	int status;
+
+	if(in_interrupt())
+	{
+		return -EBUSY;
+	}
+
+	urb = usb_alloc_urb(0);
+	dr = kmalloc(sizeof(devrequest), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+
+	if(!urb || !dr)
+	{
+		if(urb) kfree(urb);
+
+		return -ENOMEM;
+	}
+
+	spin_lock(&kaweth->device_lock);
+
+	urb->dev = kaweth->dev;
+	urb->pipe = pipe;
+	urb->setup_packet = (void *)dr;
+	urb->transfer_flags = USB_DISABLE_SPD;
+	urb->transfer_buffer = (void *)data;
+	urb->transfer_buffer_length = size;
+	urb->actual_length = size;
+	urb->complete = kaweth_control_complete;
+	urb->context = kaweth;
+
+	dr->requesttype = requesttype;
+	dr->request = request;
+	dr->value = value;
+	dr->index = index;
+	dr->length = size;
+
+	printk("Request type: %x  Request: %x  Value: %x Index: %x Length: %x\n",
+				 (int)dr->requesttype,
+				 (int)dr->request,
+				 (int)dr->value,
+				 (int)dr->index,
+				 (int)dr->length); 
+
+	init_waitqueue_head(&kaweth->control_wait);
+
+	current->state = TASK_INTERRUPTIBLE;
+	
+	add_wait_queue(&kaweth->control_wait, &wait);
+
+	status = usb_submit_urb(urb);
+
+//	printk("pipe: %x\n", pipe);
+//	printk("maxpacket: %x\n", usb_maxpacket(kaweth->dev, pipe, usb_pipeout(pipe)));
+
+	if(status)
+	{		
+		usb_free_urb(urb);
+		remove_wait_queue(&kaweth->control_wait, &wait);
+		spin_unlock(&kaweth->device_lock);
+		return status;
+	}
+
+	if(urb->status == -EINPROGRESS)
+	{
+		while(timeout && urb->status == -EINPROGRESS)
+		{
+			status = timeout = schedule_timeout(timeout);
+		}
+	}
+	else
+	{
+		status = 1;
+	}
+
+	remove_wait_queue(&kaweth->control_wait, &wait);
+
+	if(!status)
+	{
+		usb_unlink_urb(urb);
+	
+		printk("kaweth timeout\n");
+
+		status = -ETIMEDOUT;
+	}
+	else
+	{
+		status = urb->status;
+
+		if(urb->status)
+		{
+			printk("kaweth control message failed (urb addr: %x)\n", (int)urb);
+			
+			usb_clear_halt(kaweth->dev, usb_rcvctrlpipe(kaweth->dev, 0));
+			usb_clear_halt(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0));
+
+			usb_unlink_urb(urb);
+		}
+	}
+
+	spin_unlock(&kaweth->device_lock);
+
+	printk("Actual length: %d, length %d\n", urb->actual_length, urb->transfer_buffer_length);
+
+//	printk("pipe: %x\n", pipe);
+//	printk("maxpacket: %x\n", usb_maxpacket(kaweth->dev, pipe, usb_pipeout(pipe)));
+
+	usb_free_urb(urb);
+	kfree(dr);
+	
+	return status;	
+}
+
+/****************************************************************
+ *     kaweth_read_configuration
+ ****************************************************************/
+static int kaweth_read_configuration(struct kaweth_device *kaweth)
+{
+	int retval;
+
+	printk("Reading kaweth configuration\n");
+
+	retval = kaweth_control(kaweth,
+									usb_rcvctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_GET_ETHERNET_DESC,
+									USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+									0,
+									0,
+									(void *)&kaweth->configuration,
+									sizeof(kaweth->configuration),
+									KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_urb_size
+ ****************************************************************/
+static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)
+{
+	int retval;
+
+	printk("Setting URB size to %d\n", (unsigned)urb_size);
+
+	retval = kaweth_control(kaweth,
+									usb_sndctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_SET_URB_SIZE,
+									USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+									urb_size,
+									0,
+									(void *)&kaweth->firmware_buf,
+									0,
+									KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_sofs_wait
+ ****************************************************************/
+static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)
+{
+	int retval;
+
+	printk("Set SOFS wait to %d\n", (unsigned)sofs_wait);
+
+	retval = kaweth_control(kaweth,
+									usb_sndctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_SET_SOFS_WAIT,
+									USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+									sofs_wait,
+									0,
+									(void *)&kaweth->firmware_buf,
+									0,
+									KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_recieve_filter
+ ****************************************************************/
+static int kaweth_set_recieve_filter(struct kaweth_device *kaweth, __u16 recieve_filter)
+{
+	int retval;
+
+	printk("Set recieve filter to %d\n", (unsigned)recieve_filter);
+
+	retval = kaweth_control(kaweth,
+									usb_sndctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_SET_PACKET_FILTER,
+									USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+									recieve_filter,
+									0,
+									(void *)&kaweth->firmware_buf,
+									0,
+									KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_download_firmware
+ ****************************************************************/
+static int kaweth_download_firmware(struct kaweth_device *kaweth, 
+																		__u8 *data, 
+																		__u16 data_len,
+																		__u8 interrupt,
+																		__u8 type)
+{	
+	if(data_len > KAWETH_FIRMWARE_BUF_SIZE)
+	{
+		printk("Firmware too big: %d\n", data_len);
+		
+		return -ENOSPC;
+	}
+	
+	memcpy(kaweth->firmware_buf, data, data_len);
+	
+	kaweth->firmware_buf[2] = (data_len & 0xFF) - 7;
+	kaweth->firmware_buf[3] = data_len >> 8;
+	kaweth->firmware_buf[4] = type;
+	kaweth->firmware_buf[5] = interrupt;
+
+	printk("Downloading firmware at %x to kaweth device at %x...\n", (int)data, (int)kaweth);
+	printk("Firmware length: %d\n", data_len);
+
+	return kaweth_control(kaweth,
+									usb_sndctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_SCAN,
+									USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+									0,
+									0,
+									(void *)&kaweth->firmware_buf,
+									data_len,
+									KAWETH_CONTROL_TIMEOUT);
+}
+
+/****************************************************************
+ *     kaweth_trigger_firmware
+ ****************************************************************/
+static int kaweth_trigger_firmware(struct kaweth_device *kaweth,
+																	 __u8 interrupt)
+{
+	kaweth->firmware_buf[0] = 0xB6;
+	kaweth->firmware_buf[1] = 0xC3;
+	kaweth->firmware_buf[2] = 1;
+	kaweth->firmware_buf[3] = 0;
+	kaweth->firmware_buf[4] = 6;
+	kaweth->firmware_buf[5] = 100;
+	kaweth->firmware_buf[6] = 0;
+	kaweth->firmware_buf[7] = 0;
+	
+	printk("Triggering firmware\n");
+
+	return kaweth_control(kaweth,
+									usb_sndctrlpipe(kaweth->dev, 0),
+									KAWETH_COMMAND_SCAN,
+									USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+									0,
+									0,
+									(void *)&kaweth->firmware_buf,
+									8,
+									KAWETH_CONTROL_TIMEOUT);
+}
+
+/****************************************************************
+ *     kaweth_reset
+ ****************************************************************/
+static int kaweth_reset(struct kaweth_device *kaweth)
+{
+	int result;
+
+	result = usb_set_configuration(kaweth->dev, 1);
+	
+	udelay(10000);
+
+	return result;
+}
+
+static void kaweth_usb_recieve(struct urb *);
+
+/****************************************************************
+ *     kaweth_resubmit_rx_urb
+ ****************************************************************/
+static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth)
+{
+	int result;
+	
+	memset(kaweth->rx_urb, 0, sizeof(*kaweth->rx_urb));
+
+	spin_lock_init(&kaweth->rx_urb->lock);
+
+	FILL_BULK_URB(kaweth->rx_urb,
+								kaweth->dev,
+								usb_rcvbulkpipe(kaweth->dev, 1),
+								kaweth->rx_buf,
+								KAWETH_BUF_SIZE,
+								kaweth_usb_recieve,
+								kaweth);
+
+//	kaweth->rx_urb->transfer_flags = USB_DISABLE_SPD | USB_URB_EARLY_COMPLETE;
+
+	if((result = usb_submit_urb(kaweth->rx_urb)))
+	{
+		printk("kaweth: resubmitting rx_urb %d failed\n", result);
+	}
+}
+
+/****************************************************************
+ *     kaweth_usb_recieve
+ ****************************************************************/
+static void kaweth_usb_recieve(struct urb *urb)
+{
+	struct kaweth_device *kaweth = urb->context;
+	struct net_device *net = kaweth->net;
+	
+	int count = urb->actual_length,
+			count2 = urb->transfer_buffer_length;
+			
+	__u16 pkt_len = *(__u16 *)kaweth->rx_buf;
+
+	struct sk_buff *skb;
+
+	if(kaweth->status & KAWETH_STATUS_CLOSING)
+	{
+		return;
+	}
+	
+#define RX_FAILED		kaweth_resubmit_rx_urb(kaweth);			\
+										return
+
+	if(urb->status && urb->status != -EREMOTEIO && count != 1) 
+	{
+		printk("%s: RX status: %d count: %d packet_len: %d\n", 
+					 net->name, urb->status, count, (int)pkt_len);
+
+		RX_FAILED;
+	}
+
+	if(kaweth->net && (count > 2))
+	{
+		if(pkt_len > (count - 2))
+		{
+			printk("kaweth error: packet length too long for USB frame (pkt_len: %x, count: %x)\n",
+						 pkt_len, count);
+
+			printk("Packet len & 2047: %x\n", pkt_len & 2047);
+			printk("Count 2: %x\n", count2);
+
+			RX_FAILED;
+		}
+		
+		if(!(skb = dev_alloc_skb(pkt_len+2)))
+		{
+			RX_FAILED;
+		}
+
+		skb->dev = net;
+
+		eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
+		
+		skb_put(skb, pkt_len);
+
+		skb->protocol = eth_type_trans(skb, net);
+		
+		netif_rx(skb);
+		
+		kaweth->stats.rx_packets++;
+		kaweth->stats.rx_bytes += pkt_len;
+	}
+
+	kaweth_resubmit_rx_urb(kaweth);
+}
+
+/****************************************************************
+ *     kaweth_open
+ ****************************************************************/
+static int kaweth_open(struct net_device *net)
+{
+	struct kaweth_device *kaweth = (struct kaweth_device *)net->priv;
+
+	printk("Dev usage: %d\n", kaweth->dev->refcnt.counter);
+
+	printk("Opening network device...\n");
+
+	kaweth_resubmit_rx_urb(kaweth);
+
+	netif_start_queue(net);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+/****************************************************************
+ *     kaweth_close
+ ****************************************************************/
+static int kaweth_close(struct net_device *net)
+{
+	struct kaweth_device *kaweth = net->priv;
+
+	netif_stop_queue(net);
+
+	kaweth->status |= KAWETH_STATUS_CLOSING;
+
+	usb_unlink_urb(kaweth->rx_urb);
+
+	kaweth->status &= ~KAWETH_STATUS_CLOSING;
+
+	MOD_DEC_USE_COUNT;
+
+	printk("Dev usage: %d\n", kaweth->dev->refcnt.counter);
+
+	return 0;
+}
+
+/****************************************************************
+ *     kaweth_ioctl
+ ****************************************************************/
+static int kaweth_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+//	__u16 *data = (__u16 *)&rq->ifr_data;
+//	struct kaweth_device *kaweth = net->priv;
+/*
+	switch(cmd) {
+		default:
+			return -EOPNOTSUPP;
+	}
+*/
+	return -EOPNOTSUPP;
+}
+
+/****************************************************************
+ *     kaweth_usb_transmit_complete
+ ****************************************************************/
+static void kaweth_usb_transmit_complete(struct urb *urb)
+{
+	struct kaweth_device *kaweth = urb->context;
+
+	spin_lock(&kaweth->device_lock);
+
+	if (urb->status)
+		printk("%s: TX status %d", kaweth->net->name, urb->status);
+
+	netif_wake_queue(kaweth->net);
+
+	spin_unlock(&kaweth->device_lock);
+}
+
+/****************************************************************
+ *     kaweth_start_xmit
+ ****************************************************************/
+static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	struct kaweth_device *kaweth = net->priv;
+//	int count = ((skb->len) & 0x3f) ? skb->len+2 : skb->len+3;
+	int count = skb->len;
+	
+	int res;
+
+	spin_lock(&kaweth->device_lock);
+
+	netif_stop_queue(net);
+
+	*((__u16 *)kaweth->tx_buf) = skb->len;
+
+	memcpy(kaweth->tx_buf + 2, skb->data, skb->len);
+
+	memset(kaweth->tx_urb, 0, sizeof(*kaweth->tx_urb));
+	spin_lock_init(&kaweth->tx_urb->lock);
+
+	FILL_BULK_URB(kaweth->tx_urb,
+							kaweth->dev,
+							usb_sndbulkpipe(kaweth->dev, 2),
+							kaweth->tx_buf,
+							count + 2,
+							kaweth_usb_transmit_complete,
+							kaweth);
+
+	if((res = usb_submit_urb(kaweth->tx_urb)))
+	{
+		warn("kaweth failed tx_urb %d", res);
+		kaweth->stats.tx_errors++;
+		
+		netif_start_queue(net);
+	} 
+	else 
+	{
+		kaweth->stats.tx_packets++;
+		kaweth->stats.tx_bytes += skb->len;
+		net->trans_start = jiffies;
+	}
+
+	dev_kfree_skb(skb);
+
+	spin_unlock(&kaweth->device_lock);
+
+	return 0;
+}
+
+/****************************************************************
+ *     kaweth_set_rx_mode
+ ****************************************************************/
+static void kaweth_set_rx_mode(struct net_device *net)
+{
+	struct kaweth_device *kaweth = net->priv;
+	int result;
+	
+	__u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED |
+															 KAWETH_PACKET_FILTER_BROADCAST |
+															 KAWETH_PACKET_FILTER_MULTICAST;
+
+	printk("Setting Rx mode\n");
+	
+	netif_stop_queue(net);
+
+	if (net->flags & IFF_PROMISC) 
+	{
+		packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS;
+	} 
+	else if ((net->mc_count) || //  > multicast_filter_limit) ||
+					 (net->flags & IFF_ALLMULTI)) 
+	{
+		packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST;
+	}
+
+	result = kaweth_control(kaweth,
+								 usb_sndctrlpipe(kaweth->dev, 0),
+								 KAWETH_COMMAND_SET_PACKET_FILTER,
+								 USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+								 packet_filter_bitmap,
+								 0,
+								 (void *)&kaweth->firmware_buf,
+								 0,
+								 KAWETH_CONTROL_TIMEOUT);
+
+	if(result < 0)
+	{
+		printk("Failed to set Rx mode: %d\n", result);
+	}
+	else
+	{
+		printk("%s: set Rx mode to %d\n", net->name, packet_filter_bitmap);
+	}
+		
+	netif_wake_queue(net);
+}
+
+/****************************************************************
+ *     kaweth_netdev_stats
+ ****************************************************************/
+static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev)
+{
+	return &((struct kaweth_device *)dev->priv)->stats;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 0)
+/****************************************************************
+ *     kaweth_tx_timeout
+ ****************************************************************/
+static void kaweth_tx_timeout(struct net_device *net)
+{
+	struct kaweth_device *kaweth = net->priv;
+
+	printk("%s: Tx timed out. Reseting...\n", net->name);
+	kaweth->stats.tx_errors++;
+	net->trans_start = jiffies;
+
+	usb_unlink_urb(kaweth->tx_urb);
+
+	netif_wake_queue(net);
+}
+#endif
+
+/****************************************************************
+ *     kaweth_probe
+ ****************************************************************/
+static void *kaweth_probe(struct usb_device *dev, unsigned int ifnum)
+{
+//	struct net_device *net;
+//	eth_addr_t mac_addr;
+	struct kaweth_device *kaweth;
+	const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	int result = 0;
+	int i;
+	
+	printk("Kawasaki Device Probe (Device number:%x): 0x%4.4x:0x%4.4x\n",
+				 dev->devnum, 
+				 (int)dev->descriptor.idVendor, 
+				 (int)dev->descriptor.idProduct);
+
+	printk("Device at %p\n", dev);
+
+	printk("Descriptor length: %x type: %x\n", 
+				 (int)dev->descriptor.bLength,
+				 (int)dev->descriptor.bDescriptorType);
+
+	if (kaweth_check_device_ids(dev->descriptor.idVendor, 
+															dev->descriptor.idProduct)) 
+	{
+		return NULL;
+	}
+
+	if(!(kaweth = kmalloc(sizeof(struct kaweth_device), GFP_KERNEL)))
+	{
+		printk("out of memory allocating device structure\n");
+		return NULL;
+	}
+
+	memset(kaweth, 0, sizeof(*kaweth));
+
+	kaweth->dev = dev;
+	kaweth->status = 0;
+	kaweth->net = NULL;
+	kaweth->device_lock = SPIN_LOCK_UNLOCKED;
+
+	if((result = kaweth_read_configuration(kaweth)) < 0)
+	{
+//		printk("Resetting (jiffies: %x)...\n", jiffies);
+		printk("Resetting...\n");
+
+		kaweth_reset(kaweth);
+
+//		printk("Dowloading firmware (jiffies: %x)...\n", jiffies);
+												 
+		if((result = kaweth_download_firmware(kaweth, kaweth_new_code, len_kaweth_new_code, 100, 2)) < 0)
+		{
+			printk("Error downloading firmware (%d), no net device created\n", result);
+
+			kfree(kaweth);
+			
+			return NULL;
+		}
+
+//		printk("Dowloading firmware fix (jiffies: %x)...\n", jiffies);
+
+		if((result = kaweth_download_firmware(kaweth, kaweth_new_code_fix, len_kaweth_new_code_fix, 100, 3)) < 0)
+		{
+			printk("Error downloading firmware fix (%d), no net device created\n", result);
+
+			kfree(kaweth);
+
+			return NULL;
+		}
+
+//		printk("Dowloading firmware trigger (jiffies: %x)...\n", jiffies);
+
+		if((result = kaweth_trigger_firmware(kaweth, 100)) < 0)
+		{
+			printk("Error triggering firmware (%d), no net device created\n", result);
+
+			kfree(kaweth);
+
+			return NULL;
+		}
+
+		udelay(1000);
+
+		printk("Resetting device (jiffies: %lx)...\n", jiffies);
+
+		if(kaweth_reset(kaweth))
+		{
+			printk("Error resetting device\n");
+
+			kfree(kaweth);
+
+			return NULL;
+		}
+
+		printk("Reset device (jiffies: %lx)...\n", jiffies);
+	}
+
+	result = kaweth_read_configuration(kaweth);
+
+	printk("Statitstics collection: %x\n", kaweth->configuration.statistics_mask);
+	printk("Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1));
+	printk("MTU: %x\n", kaweth->configuration.segment_size);
+	printk("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+				 (int)kaweth->configuration.hw_addr[0],
+				 (int)kaweth->configuration.hw_addr[1],
+				 (int)kaweth->configuration.hw_addr[2],
+				 (int)kaweth->configuration.hw_addr[3],
+				 (int)kaweth->configuration.hw_addr[4],
+				 (int)kaweth->configuration.hw_addr[5]);
+
+	if(!memcmp(&kaweth->configuration.hw_addr, &bcast_addr, sizeof(bcast_addr)))
+	{
+		printk("Firmware not functioning properly, no net device created\n");
+
+		kfree(kaweth);
+
+		return NULL;
+	}
+
+	for(i = 0; i < sizeof(kaweth->configuration); i++)
+	{
+		printk(":%2.2x:", (unsigned int)((unsigned char *)&kaweth->configuration)[i]);
+	}
+
+	printk("\n");
+
+	if(result < 0)
+	{
+		printk("Error reading configuration (%d), no net device created\n", result);
+
+		return kaweth;
+	}
+
+	result = kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE);
+
+	if(result < 0)
+	{
+		printk("Error setting URB size\n");
+
+		return kaweth;
+	}
+	
+	result = kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT);
+
+	if(result < 0)
+	{
+		printk("Error setting SOFS wait\n");
+
+		return kaweth;
+	}
+
+	result = kaweth_set_recieve_filter(kaweth, KAWETH_PACKET_FILTER_DIRECTED |
+															 KAWETH_PACKET_FILTER_BROADCAST |
+															 KAWETH_PACKET_FILTER_MULTICAST);
+
+	if(result < 0)
+	{
+		printk("Error setting recieve filter\n");
+
+		return kaweth;
+	}
+	
+	printk("Initializing net device...\n");
+
+	kaweth->tx_urb = usb_alloc_urb(0);
+	kaweth->rx_urb = usb_alloc_urb(0);
+
+	kaweth->net = init_etherdev(0, 0);
+
+	memcpy(kaweth->net->broadcast, &bcast_addr, sizeof(bcast_addr));
+	memcpy(kaweth->net->dev_addr, &kaweth->configuration.hw_addr, sizeof(kaweth->configuration.hw_addr));
+	 
+	kaweth->net->priv = kaweth;
+	kaweth->net->open = kaweth_open;
+	kaweth->net->stop = kaweth_close;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 0)
+	kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT;
+	kaweth->net->tx_timeout = kaweth_tx_timeout;
+#endif
+	
+	kaweth->net->do_ioctl = kaweth_ioctl;
+	kaweth->net->hard_start_xmit = kaweth_start_xmit;
+	kaweth->net->set_multicast_list = kaweth_set_rx_mode;
+	kaweth->net->get_stats = kaweth_netdev_stats;
+	kaweth->net->mtu = kaweth->configuration.segment_size;
+
+	memset(&kaweth->stats, 0, sizeof(kaweth->stats));
+
+	printk("kaweth interface created at %s\n", kaweth->net->name);
+								
+	printk("Kaweth probe returning...\n");
+
+	return kaweth;
+}
+
+/****************************************************************
+ *     kaweth_disconnect
+ ****************************************************************/
+static void kaweth_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct kaweth_device *kaweth = ptr;
+
+	printk("Unregistering kaweth\n");
+
+	if (!kaweth) {
+		warn("unregistering non-existant device");
+		return;
+	}
+
+	if(kaweth->net)
+	{
+		if(kaweth->net->flags & IFF_UP)
+		{
+			printk("Closing net device\n");
+			
+			dev_close(kaweth->net);
+		}
+
+		printk("Unregistering net device\n");
+		
+		unregister_netdev(kaweth->net);
+	}
+
+	usb_free_urb(kaweth->rx_urb);
+	usb_free_urb(kaweth->tx_urb);
+
+	kfree(kaweth);
+}
+
+
+/****************************************************************
+ *     kaweth_init
+ ****************************************************************/
+int __init kaweth_init(void)
+{
+	printk("Kawasaki USB->Ethernet Driver loading...\n");
+
+	return usb_register(&kaweth_driver);
+}
+
+/****************************************************************
+ *     kaweth_exit
+ ****************************************************************/
+void __exit kaweth_exit(void)
+{
+	usb_deregister(&kaweth_driver);
+}
+
+module_init(kaweth_init);
+module_exit(kaweth_exit);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/kawethfw.h linux.18p17/drivers/usb/kawethfw.h
--- linux.vanilla/drivers/usb/kawethfw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/kawethfw.h	Fri Sep  1 14:07:21 2000
@@ -0,0 +1,639 @@
+/******************************************/
+/* NOTE: B6/C3 is data header signature   */
+/*       0xAA/0xBB is data length = total */
+/*       bytes - 7, 0xCC is type, 0xDD is */
+/*       interrupt to use.                */
+/******************************************/
+
+/****************************************************************
+ *     kaweth_trigger_code
+ ****************************************************************/
+static __u8 kaweth_trigger_code[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0xc8, 0x07, 0xa0, 0x00, 0xf0, 0x07, 0x5e, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0x0a, 0x00, 0x08, 0x00,
+    0xf0, 0x09, 0x00, 0x00, 0x02, 0x00, 0xe7, 0x07,
+    0x36, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00,
+    0x04, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x10, 0xc0,
+    0xf0, 0x09, 0x0e, 0xc0, 0x00, 0x00, 0xe7, 0x87,
+    0x01, 0x00, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0x17, 0x02, 0xc8, 0x07, 0xa0, 0x00,
+    0xe7, 0x17, 0x50, 0xc3, 0x10, 0xc0, 0x30, 0xd8,
+    0x04, 0x00, 0x30, 0x5c, 0x08, 0x00, 0x04, 0x00,
+    0xb0, 0xc0, 0x06, 0x00, 0xc8, 0x05, 0xe7, 0x05,
+    0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0x49, 0xaf,
+    0xc0, 0x07, 0x00, 0x00, 0x60, 0xaf, 0x4a, 0xaf,
+    0x00, 0x0c, 0x0c, 0x00, 0x40, 0xd2, 0x00, 0x1c,
+    0x0c, 0x00, 0x40, 0xd2, 0x30, 0x00, 0x08, 0x00,
+    0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0xf0, 0x07,
+    0x86, 0x00, 0x06, 0x00, 0x67, 0xcf, 0x27, 0x0c,
+    0x02, 0x00, 0x00, 0x00, 0x27, 0x0c, 0x00, 0x00,
+    0x0e, 0xc0, 0x49, 0xaf, 0x64, 0xaf, 0xc0, 0x07,
+    0x00, 0x00, 0x4b, 0xaf, 0x4a, 0xaf, 0x5a, 0xcf,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x94, 0x00, 0x05, 0x00,
+    0,    0
+};
+
+/****************************************************************
+ *     kaweth_trigger_code_fix
+ ****************************************************************/
+static __u8 kaweth_trigger_code_fix[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x02, 0x00, 0x06, 0x00, 0x18, 0x00, 0x3e, 0x00,
+    0x80, 0x00, 0x98, 0x00, 0xaa, 0x00,
+    0,    0
+};
+
+/****************************************************************
+ *     kaweth_new_code
+ ****************************************************************/
+static __u8 kaweth_new_code[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00,
+    0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+    0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+    0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+    0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09,
+    0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08,
+    0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0,
+    0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08,
+    0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1,
+    0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+    0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf,
+    0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09,
+    0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0,
+    0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08,
+    0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08,
+    0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57,
+    0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08,
+    0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00,
+    0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09,
+    0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+    0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00,
+    0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1,
+    0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03,
+    0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1,
+    0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08,
+    0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04,
+    0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09,
+    0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00,
+    0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57,
+    0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08,
+    0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08,
+    0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00,
+    0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00,
+    0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf,
+    0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17,
+    0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07,
+    0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08,
+    0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+    0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08,
+    0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0,
+    0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37,
+    0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60,
+    0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9,
+    0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17,
+    0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00,
+    0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00,
+    0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04,
+    0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf,
+    0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf,
+    0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02,
+    0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf,
+    0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57,
+    0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57,
+    0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09,
+    0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02,
+    0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01,
+    0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57,
+    0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02,
+    0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1,
+    0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77,
+    0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02,
+    0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77,
+    0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07,
+    0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00,
+    0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77,
+    0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda,
+    0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0,
+    0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf,
+    0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8,
+    0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77,
+    0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50,
+    0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00,
+    0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02,
+    0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0,
+    0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00,
+    0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e,
+    0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0,
+    0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+    0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07,
+    0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e,
+    0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08,
+    0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf,
+    0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07,
+    0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+    0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08,
+    0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09,
+    0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07,
+    0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00,
+    0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf,
+    0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08,
+    0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05,
+    0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08,
+    0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67,
+    0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0,
+    0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00,
+    0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08,
+    0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80,
+    0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08,
+    0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+    0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf,
+    0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0,
+    0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00,
+    0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00,
+    0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17,
+    0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07,
+    0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+    0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03,
+    0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0,
+    0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07,
+    0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00,
+    0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08,
+    0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08,
+    0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+    0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02,
+    0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08,
+    0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+    0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00,
+    0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+    0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+    0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52,
+    0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08,
+    0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+    0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05,
+    0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04,
+    0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62,
+    0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02,
+    0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+    0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17,
+    0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77,
+    0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9,
+    0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1,
+    0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27,
+    0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27,
+    0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00,
+    0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+    0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17,
+    0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07,
+    0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1,
+    0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59,
+    0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf,
+    0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08,
+    0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf,
+    0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07,
+    0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77,
+    0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00,
+    0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf,
+    0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00,
+    0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00,
+    0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0,
+    0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77,
+    0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08,
+    0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf,
+    0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1,
+    0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1,
+    0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf,
+    0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
+    0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09,
+    0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53,
+    0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05,
+    0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8,
+    0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf,
+    0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30,
+    0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf,
+    0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30,
+    0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf,
+    0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09,
+    0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09,
+    0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0,
+    0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01,
+    0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07,
+    0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+    0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37,
+    0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08,
+    0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+    0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+    0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+    0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08,
+    0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08,
+    0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0,
+    0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00,
+    0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02,
+    0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00,
+    0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00,
+    0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0,
+    0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+    0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04,
+    0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09,
+    0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00,
+    0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02,
+    0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05,
+    0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57,
+    0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf,
+    0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0,
+    0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+    0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09,
+    0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+    0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+    0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+    0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+    0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00,
+    0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00,
+    0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57,
+    0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf,
+    0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17,
+    0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57,
+    0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57,
+    0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00,
+    0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77,
+    0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+    0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00,
+    0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+    0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1,
+    0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02,
+    0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77,
+    0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+    0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00,
+    0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf,
+    0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57,
+    0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57,
+    0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8,
+    0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+    0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08,
+    0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09,
+    0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77,
+    0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57,
+    0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19,
+    0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+    0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf,
+    0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57,
+    0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b,
+    0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05,
+    0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09,
+    0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04,
+    0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1,
+    0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf,
+    0x68, 0x04, 0x97, 0xcf, 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, 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, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc,
+    0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80,
+    0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e,
+    0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00,
+    0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09,
+    0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00,
+    0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+    0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07,
+    0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13,
+    0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08,
+    0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09,
+    0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09,
+    0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b,
+    0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00,
+    0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00,
+    0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf,
+    0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90,
+    0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00,
+    0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b,
+    0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00,
+    0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07,
+    0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00,
+    0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00,
+    0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf,
+    0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0,
+    0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+    0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf,
+    0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08,
+    0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf,
+    0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94,
+    0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09,
+    0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf,
+    0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00,
+    0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00,
+    0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00,
+    0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00,
+    0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00,
+    0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0,
+    0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0,
+    0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00,
+    0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a,
+    0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a,
+    0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b,
+    0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02,
+    0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+    0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57,
+    0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37,
+    0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2,
+    0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00,
+    0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57,
+    0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b,
+    0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09,
+    0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07,
+    0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08,
+    0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08,
+    0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0,
+    0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00,
+    0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08,
+    0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf,
+    0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09,
+    0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08,
+    0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c,
+    0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf,
+    0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2,
+    0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08,
+    0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07,
+    0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb,
+    0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf,
+    0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57,
+    0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07,
+    0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00,
+    0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08,
+    0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07,
+    0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf,
+    0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00,
+    0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf,
+    0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1,
+    0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf,
+    0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf,
+    0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+    0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+    0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+    0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02,
+    0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08,
+    0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80,
+    0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+    0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+    0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf,
+    0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08,
+    0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+    0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+    0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf,
+    0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07,
+    0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1,
+    0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00,
+    0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde,
+    0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2,
+    0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08,
+    0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf,
+    0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08,
+    0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde,
+    0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08,
+    0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0,
+    0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf,
+    0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57,
+    0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf,
+    0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0,
+    0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67,
+    0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00,
+    0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0,
+    0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+    0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf,
+    0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08,
+    0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf,
+    0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0,
+    0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00,
+    0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08,
+    0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00,
+    0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07,
+    0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00,
+    0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09,
+    0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07,
+    0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1,
+    0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+    0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07,
+    0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08,
+    0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00,
+    0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00,
+    0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09,
+    0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02,
+    0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda,
+    0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08,
+    0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08,
+    0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02,
+    0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+    0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+    0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0,
+    0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08,
+    0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08,
+    0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08,
+    0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08,
+    0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07,
+    0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+    0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+    0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+    0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+    0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+    0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+    0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+    0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+    0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07,
+    0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+    0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09,
+    0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00,
+    0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+    0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00,
+    0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+    0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+    0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+    0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00,
+    0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d,
+    0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1,
+    0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87,
+    0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08,
+    0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0,
+    0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0,
+    0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87,
+    0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7,
+    0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0,
+    0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0,
+    0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09,
+    0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2,
+    0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf,
+    0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02,
+    0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08,
+    0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77,
+    0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf,
+    0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e,
+    0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0,
+    0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77,
+    0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57,
+    0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb,
+    0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00,
+    0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb,
+    0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09,
+    0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00,
+    0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08,
+    0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09,
+    0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02,
+    0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05,
+    0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1,
+    0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02,
+    0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+    0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04,
+    0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00,
+    0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf,
+    0,    0
+};
+
+/****************************************************************
+ *     kaweth_new_code_fix
+ ****************************************************************/
+static __u8 kaweth_new_code_fix[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00,
+    0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00,
+    0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00,
+    0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00,
+    0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00,
+    0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00,
+    0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00,
+    0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00,
+    0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00,
+    0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00,
+    0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01,
+    0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01,
+    0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01,
+    0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01,
+    0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01,
+    0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01,
+    0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02,
+    0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02,
+    0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02,
+    0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02,
+    0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03,
+    0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03,
+    0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03,
+    0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03,
+    0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03,
+    0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03,
+    0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03,
+    0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04,
+    0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04,
+    0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04,
+    0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04,
+    0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04,
+    0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05,
+    0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05,
+    0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05,
+    0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05,
+    0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06,
+    0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06,
+    0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06,
+    0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06,
+    0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06,
+    0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06,
+    0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06,
+    0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07,
+    0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07,
+    0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08,
+    0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08,
+    0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08,
+    0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08,
+    0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09,
+    0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09,
+    0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09,
+    0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09,
+    0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09,
+    0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09,
+    0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09,
+    0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09,
+    0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a,
+    0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a,
+    0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a,
+    0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a,
+    0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+    0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+    0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a,
+    0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a,
+    0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a,
+    0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a,
+    0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b,
+    0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b,
+    0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b,
+    0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b,
+    0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b,
+    0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b,
+    0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c,
+    0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c,
+    0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c,
+    0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c,
+    0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c,
+    0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c,
+    0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c,
+    0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c,
+    0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d,
+    0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d,
+    0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d,
+    0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d,
+    0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d,
+    0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e,
+    0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e,
+    0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e,
+    0,    0
+};
+
+const int len_kaweth_trigger_code = sizeof(kaweth_trigger_code);
+const int len_kaweth_trigger_code_fix = sizeof(kaweth_trigger_code_fix);
+const int len_kaweth_new_code = sizeof(kaweth_new_code);
+const int len_kaweth_new_code_fix = sizeof(kaweth_new_code_fix);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/keybdev.c linux.18p17/drivers/usb/keybdev.c
--- linux.vanilla/drivers/usb/keybdev.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/keybdev.c	Fri Sep 22 14:56:09 2000
@@ -0,0 +1,202 @@
+/*
+ * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  Input driver to keyboard driver binding.
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/config.h>
+#include <linux/kbd_ll.h>
+#include <linux/input.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kbd_kern.h>
+#include <linux/kcomp.h>
+
+#if defined(__i386__) || defined(__alpha__) || defined(__mips__)
+
+static int x86_sysrq_alt = 0;
+
+static unsigned short x86_keycodes[256] = {
+	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+	 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+	 80, 81, 82, 83, 43, 85, 86, 87, 88,115,119,120,121,375,123, 90,
+	284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339,
+	367,294,293,286,350, 92,334,512,116,377,109,111,259,347,348,349,
+	360, 93, 94, 95, 98,376,100,101,357,316,354,304,289,102,351,355,
+	103,104,105,275,281,272,306,106,274,107,288,364,358,363,362,361,
+	291,108,381,290,287,292,279,305,280, 99,112,257,258,113,270,114,
+	118,117,125,374,379,125,260,261,262,263,264,265,266,267,268,269,
+	271,273,276,277,278,282,283,295,296,297,299,300,301,302,303,307,
+	308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,330,
+	332,340,341,342,343,344,345,346,356,359,365,368,369,370,371,372
+};
+
+static int emulate_raw(unsigned int keycode, int down)
+{
+	if (keycode > 255 || !x86_keycodes[keycode])
+		return -1; 
+
+	if (keycode == KEY_PAUSE) {
+		handle_scancode(0xe1, 1);
+		handle_scancode(0x1d, down);
+		handle_scancode(0x45, down);
+		return 0;
+	} 
+
+	if (keycode == KEY_SYSRQ && x86_sysrq_alt) {
+		handle_scancode(0x54, down);
+		return 0;
+	}
+
+	if (x86_keycodes[keycode] & 0x100)
+		handle_scancode(0xe0, 1);
+
+	handle_scancode(x86_keycodes[keycode] & 0x7f, down);
+
+	if (keycode == KEY_SYSRQ) {
+		handle_scancode(0xe0, 1);
+		handle_scancode(0x37, down);
+	}
+
+	if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
+		x86_sysrq_alt = down;
+
+	return 0;
+}
+
+#elif defined(CONFIG_MAC_KEYBOARD)
+
+static unsigned char mac_keycodes[128] =
+	{ 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+	 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128,  1,
+	  2,  3,  5,  4, 38, 40, 37, 41, 39, 50, 56, 42,  6,  7,  8,  9,
+	 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+	 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+	 84, 85, 82, 65, 42,  0, 10,103,111,  0,  0,  0,  0,  0,  0,  0,
+	 76,125, 75,105,124,  0,115, 62,116, 59, 60,119, 61,121,114,117,
+	  0,  0,  0,  0,127, 81,  0,113,  0,  0,  0,  0,  0, 55, 55 };
+
+static int emulate_raw(unsigned int keycode, int down)
+{
+	if (keycode > 127 || !mac_keycodes[keycode])
+		return -1;
+
+	handle_scancode(mac_keycodes[keycode] & 0x7f, down);
+
+	return 0;
+}
+
+#endif
+
+static struct input_handler keybdev_handler;
+
+void keybdev_ledfunc(unsigned int led)
+{
+	struct input_handle *handle;	
+
+	for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+
+		input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+		input_event(handle->dev, EV_LED, LED_NUML,    !!(led & 0x02));
+		input_event(handle->dev, EV_LED, LED_CAPSL,   !!(led & 0x04));
+
+	}
+}
+
+void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int down)
+{
+	if (type != EV_KEY) return;
+
+	if (emulate_raw(code, down))
+		printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", code);
+
+	mark_bh(KEYBOARD_BH);
+}
+
+static struct input_handle *keybdev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+	struct input_handle *handle;
+	int i;
+
+	if (!test_bit(EV_KEY, dev->evbit))
+		return NULL;
+
+	for (i = KEY_RESERVED; i < BTN_MISC; i++)
+		if (test_bit(i, dev->keybit)) break;
+
+	if (i == BTN_MISC)
+ 		return NULL;
+
+	if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+		return NULL;
+	memset(handle, 0, sizeof(struct input_handle));
+
+	handle->dev = dev;
+	handle->handler = handler;
+
+	input_open_device(handle);
+
+	printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+
+	return handle;
+}
+
+static void keybdev_disconnect(struct input_handle *handle)
+{
+	printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number);
+	input_close_device(handle);
+	kfree(handle);
+}
+	
+static struct input_handler keybdev_handler = {
+	event:		keybdev_event,
+	connect:	keybdev_connect,
+	disconnect:	keybdev_disconnect,
+};
+
+static int __init keybdev_init(void)
+{
+	input_register_handler(&keybdev_handler);
+	kbd_ledfunc = keybdev_ledfunc;
+	return 0;
+}
+
+static void __exit keybdev_exit(void)
+{
+	kbd_ledfunc = NULL;
+	input_unregister_handler(&keybdev_handler);
+}
+
+module_init(keybdev_init);
+module_exit(keybdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input driver to keyboard driver binding");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/keyboard.c linux.18p17/drivers/usb/keyboard.c
--- linux.vanilla/drivers/usb/keyboard.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/keyboard.c	Thu Jan  1 01:00:00 1970
@@ -1,226 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/kbd_ll.h>
-#include "usb.h"
-
-#define PCKBD_PRESSED  0x00
-#define PCKBD_RELEASED 0x80
-#define PCKBD_NEEDS_E0 0x80
-
-#define USBKBD_MODIFIER_BASE  120
-#define USBKBD_KEYCODE_OFFSET 2
-#define USBKBD_KEYCODE_COUNT  6
-
-#define USBKBD_VALID_KEYCODE(key) ((unsigned char)(key) > 3)
-#define USBKBD_FIND_KEYCODE(down, key, count) \
-    ((unsigned char*) memscan((down), (key), (count)) < ((down) + (count)))
-
-#define USBKBD_REPEAT_DELAY (HZ / 4)
-#define USBKBD_REPEAT_RATE (HZ / 20)
-
-struct usb_keyboard
-{
-    struct usb_device *dev;
-    unsigned long down[2];
-    unsigned char repeat_key;
-    struct timer_list repeat_timer;
-    struct list_head list;
-};
-
-extern unsigned char usb_kbd_map[];
-
-static int usb_kbd_probe(struct usb_device *dev);
-static void usb_kbd_disconnect(struct usb_device *dev);
-static void usb_kbd_repeat(unsigned long dummy);
-
-static LIST_HEAD(usb_kbd_list);
-
-static struct usb_driver usb_kbd_driver =
-{
-    "keyboard",
-    usb_kbd_probe,
-    usb_kbd_disconnect,
-    {NULL, NULL}
-};
-
-
-static void
-usb_kbd_handle_key(unsigned char key, int down)
-{
-    int scancode = (int) usb_kbd_map[key];
-    if(scancode)
-    {
-        if(scancode & PCKBD_NEEDS_E0)
-        {
-            handle_scancode(0xe0, 1);
-        }
-        handle_scancode((scancode & ~PCKBD_NEEDS_E0), down);
-    }
-}
-
-static void
-usb_kbd_repeat(unsigned long dev_id)
-{
-    struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
-
-    unsigned long flags;
-    save_flags(flags);
-    cli();
-
-    if(kbd->repeat_key)
-    {
-        usb_kbd_handle_key(kbd->repeat_key, 1);
-
-        /* reset repeat timer */
-        kbd->repeat_timer.function = usb_kbd_repeat;
-        kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE;
-        kbd->repeat_timer.data = (unsigned long) kbd;
-        kbd->repeat_timer.prev = NULL;
-        kbd->repeat_timer.next = NULL;
-        add_timer(&kbd->repeat_timer);
-    }
-
-    restore_flags(flags);
-}
-
-static int
-usb_kbd_irq(int state, void *buffer, void *dev_id)
-{
-    struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
-    unsigned long *down = (unsigned long*) buffer;
-
-    if(kbd->down[0] != down[0] || kbd->down[1] != down[1])
-    {
-        unsigned char *olddown, *newdown;
-        unsigned char modsdelta, key;
-        int i;
-
-        /* handle modifier change */
-        modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down);
-        if(modsdelta)
-        {
-            for(i = 0; i < 8; i++)
-            {
-                if(modsdelta & 0x01)
-                {
-                    int pressed = (*(unsigned char*) down >> i) & 0x01;
-                    usb_kbd_handle_key(
-                        i + USBKBD_MODIFIER_BASE,
-                        pressed);
-                }
-                modsdelta >>= 1;
-            }
-        }
-
-        olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET;
-        newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET;
-
-        /* handle released keys */
-        for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
-        {
-            key = olddown[i];
-            if(USBKBD_VALID_KEYCODE(key)
-               && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT))
-            {
-                usb_kbd_handle_key(key, 0);
-            }
-        }
-
-        /* handle pressed keys */
-        kbd->repeat_key = 0;
-        for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
-        {
-            key = newdown[i];
-            if(USBKBD_VALID_KEYCODE(key)
-               && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT))
-            {
-                usb_kbd_handle_key(key, 1);
-                kbd->repeat_key = key;
-            }
-        }
-
-        /* set repeat timer if any keys were pressed */
-        if(kbd->repeat_key)
-        {
-            del_timer(&kbd->repeat_timer);
-            kbd->repeat_timer.function = usb_kbd_repeat;
-            kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY;
-            kbd->repeat_timer.data = (unsigned long) kbd;
-            kbd->repeat_timer.prev = NULL;
-            kbd->repeat_timer.next = NULL;
-            add_timer(&kbd->repeat_timer);
-        }
-
-        kbd->down[0] = down[0];
-        kbd->down[1] = down[1];
-    }
-
-    return 1;
-}
-
-static int
-usb_kbd_probe(struct usb_device *dev)
-{
-    struct usb_interface_descriptor *interface;
-    struct usb_endpoint_descriptor *endpoint;
-    struct usb_keyboard *kbd;
-
-    interface = &dev->config[0].interface[0];
-    endpoint = &interface->endpoint[0];
-
-    if(interface->bInterfaceClass != 3
-       || interface->bInterfaceSubClass != 1
-       || interface->bInterfaceProtocol != 1)
-    {
-        return -1;
-    }
-
-    printk(KERN_INFO "USB HID boot protocol keyboard detected.\n");
-
-    kbd = kmalloc(sizeof(struct usb_keyboard), GFP_KERNEL);
-    if(kbd)
-    {
-        memset(kbd, 0, sizeof(*kbd));
-        kbd->dev = dev;
-        dev->private = kbd;
-
-        usb_set_configuration(dev, dev->config[0].bConfigurationValue);
-        usb_set_protocol(dev, 0);
-        usb_set_idle(dev, 0, 0);
-        
-        usb_request_irq(dev,
-                        usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
-                        usb_kbd_irq,
-                        endpoint->bInterval,
-                        kbd);
-
-        list_add(&kbd->list, &usb_kbd_list);
-    }
-
-    return 0;
-}
-
-static void
-usb_kbd_disconnect(struct usb_device *dev)
-{
-    struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private;
-    if(kbd)
-    {
-        dev->private = NULL;
-        list_del(&kbd->list);
-        del_timer(&kbd->repeat_timer);
-        kfree(kbd);
-    }
-
-    printk(KERN_INFO "USB HID boot protocol keyboard removed.\n");
-}
-
-int
-usb_kbd_init(void)
-{
-    usb_register(&usb_kbd_driver);
-    return 0;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/keymap.c linux.18p17/drivers/usb/keymap.c
--- linux.vanilla/drivers/usb/keymap.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/keymap.c	Thu Jan  1 01:00:00 1970
@@ -1,50 +0,0 @@
-unsigned char usb_kbd_map[256] = 
-{
-    0x00,  0x00,  0x00,  0x00,  0x1e,  0x30,  0x2e,  0x20,
-    0x12,  0x21,  0x22,  0x23,  0x17,  0x24,  0x25,  0x26,
-
-    0x32,  0x31,  0x18,  0x19,  0x10,  0x13,  0x1f,  0x14,
-    0x16,  0x2f,  0x11,  0x2d,  0x15,  0x2c,  0x02,  0x03,
-
-    0x04,  0x05,  0x06,  0x07,  0x08,  0x09,  0x0a,  0x0b,
-    0x1c,  0x01,  0xd3,  0x0f,  0x39,  0x0c,  0x0d,  0x1a,
-
-    0x1b,  0x2b,  0x00,  0x27,  0x28,  0x29,  0x33,  0x34,
-    0x35,  0x3a,  0x3b,  0x3c,  0x3d,  0x3e,  0x3f,  0x40,
-
-    0x41,  0x42,  0x43,  0x44,  0x57,  0x58,  0xb7,  0x46,
-    0x00,  0xd2,  0xc7,  0xc9,  0x63,  0xcf,  0xd1,  0xcd,
-
-    0xcb,  0xd0,  0xc8,  0x45,  0xb5,  0x37,  0x4a,  0x4e,
-    0x9c,  0x4f,  0x50,  0x51,  0x4b,  0x4c,  0x4d,  0x47,
-
-    0x48,  0x49,  0x52,  0x53,  0x00,  0x6d,  0x00,  0x00,
-    0xbd,  0xbe,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-
-    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-    0x1d,  0x2a,  0x38,  0xdb,  0x9d,  0x36,  0xb8,  0xdc,
-
-    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,  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,
-    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,  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,
-
-    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/maps/fixup.map linux.18p17/drivers/usb/maps/fixup.map
--- linux.vanilla/drivers/usb/maps/fixup.map	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/maps/fixup.map	Thu Jan  1 01:00:00 1970
@@ -1,31 +0,0 @@
-# misc fixes
-keycode   0 = Pause
-keycode  29 = Control
-keycode  99 = Remove
-keycode  42 = Shift
-keycode  54 = Shift_R
-keycode 109 = Application
-
-# E0 keys (or'ed with 0x80)
-keycode 156 = KP_Enter
-keycode 157 = Control_R
-keycode 181 = KP_Divide
-keycode 183 = Print_Screen
-keycode 184 = Alt_R
-keycode 189 = F13
-keycode 190 = F14
-keycode 193 = F17
-keycode 198 = Break
-keycode 199 = Home
-keycode 200 = Up
-keycode 201 = Prior
-keycode 203 = Left
-keycode 205 = Right
-keycode 207 = End
-keycode 208 = Down
-keycode 209 = Next
-keycode 210 = Insert
-keycode 211 = Delete
-keycode 219 = Window
-keycode 220 = Window_R
-keycode 221 = Menu
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/maps/serial.map linux.18p17/drivers/usb/maps/serial.map
--- linux.vanilla/drivers/usb/maps/serial.map	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/maps/serial.map	Thu Jan  1 01:00:00 1970
@@ -1,370 +0,0 @@
-keymaps 0-2,4-6,8-9,12
-keycode   1 = Escape          
-	alt	keycode   1 = Meta_Escape     
-	shift	alt	keycode   1 = Meta_Escape     
-	control	alt	keycode   1 = Meta_Escape     
-keycode   2 = one              exclam          
-	alt	keycode   2 = Meta_one        
-	shift	alt	keycode   2 = Meta_exclam     
-keycode   3 = two              at               at               nul              nul             
-	alt	keycode   3 = Meta_two        
-	shift	alt	keycode   3 = Meta_at         
-	control	alt	keycode   3 = Meta_nul        
-keycode   4 = three            numbersign      
-	control	keycode   4 = Escape          
-	alt	keycode   4 = Meta_three      
-	shift	alt	keycode   4 = Meta_numbersign 
-keycode   5 = four             dollar           dollar           Control_backslash
-	alt	keycode   5 = Meta_four       
-	shift	alt	keycode   5 = Meta_dollar     
-	control	alt	keycode   5 = Meta_Control_backslash
-keycode   6 = five             percent         
-	control	keycode   6 = Control_bracketright
-	alt	keycode   6 = Meta_five       
-	shift	alt	keycode   6 = Meta_percent    
-keycode   7 = six              asciicircum     
-	control	keycode   7 = Control_asciicircum
-	alt	keycode   7 = Meta_six        
-	shift	alt	keycode   7 = Meta_asciicircum
-keycode   8 = seven            ampersand        braceleft        Control_underscore
-	alt	keycode   8 = Meta_seven      
-	shift	alt	keycode   8 = Meta_ampersand  
-	control	alt	keycode   8 = Meta_Control_underscore
-keycode   9 = eight            asterisk         bracketleft      Delete          
-	alt	keycode   9 = Meta_eight      
-	shift	alt	keycode   9 = Meta_asterisk   
-	control	alt	keycode   9 = Meta_Delete     
-keycode  10 = nine             parenleft        bracketright    
-	alt	keycode  10 = Meta_nine       
-	shift	alt	keycode  10 = Meta_parenleft  
-keycode  11 = zero             parenright       braceright      
-	alt	keycode  11 = Meta_zero       
-	shift	alt	keycode  11 = Meta_parenright 
-keycode  12 = minus            underscore       backslash        Control_underscore Control_underscore
-	alt	keycode  12 = Meta_minus      
-	shift	alt	keycode  12 = Meta_underscore 
-	control	alt	keycode  12 = Meta_Control_underscore
-keycode  13 = equal            plus            
-	alt	keycode  13 = Meta_equal      
-	shift	alt	keycode  13 = Meta_plus       
-keycode  14 = Delete          
-	alt	keycode  14 = Meta_Delete     
-	shift	alt	keycode  14 = Meta_Delete     
-	control	alt	keycode  14 = Meta_Delete     
-keycode  15 = Tab             
-	alt	keycode  15 = Meta_Tab        
-	shift	alt	keycode  15 = Meta_Tab        
-	control	alt	keycode  15 = Meta_Tab        
-keycode  16 = q               
-keycode  17 = w               
-keycode  18 = e               
-keycode  19 = r               
-keycode  20 = t               
-keycode  21 = y               
-keycode  22 = u               
-keycode  23 = i               
-keycode  24 = o               
-keycode  25 = p               
-keycode  26 = bracketleft      braceleft       
-	control	keycode  26 = Escape          
-	alt	keycode  26 = Meta_bracketleft
-	shift	alt	keycode  26 = Meta_braceleft  
-keycode  27 = bracketright     braceright       asciitilde       Control_bracketright
-	alt	keycode  27 = Meta_bracketright
-	shift	alt	keycode  27 = Meta_braceright 
-	control	alt	keycode  27 = Meta_Control_bracketright
-keycode  28 = Return          
-	alt	keycode  28 = Meta_Control_m  
-keycode  29 = Control         
-keycode  30 = a               
-keycode  31 = s               
-keycode  32 = d               
-keycode  33 = f               
-keycode  34 = g               
-keycode  35 = h               
-keycode  36 = j               
-keycode  37 = k               
-keycode  38 = l               
-keycode  39 = semicolon        colon           
-	alt	keycode  39 = Meta_semicolon  
-	shift	alt	keycode  39 = Meta_colon      
-keycode  40 = apostrophe       quotedbl        
-	control	keycode  40 = Control_g       
-	alt	keycode  40 = Meta_apostrophe 
-	shift	alt	keycode  40 = Meta_quotedbl   
-keycode  41 = grave            asciitilde      
-	control	keycode  41 = nul             
-	alt	keycode  41 = Meta_grave      
-	shift	alt	keycode  41 = Meta_asciitilde 
-keycode  42 = Shift           
-keycode  43 = backslash        bar             
-	control	keycode  43 = Control_backslash
-	alt	keycode  43 = Meta_backslash  
-	shift	alt	keycode  43 = Meta_bar        
-keycode  44 = z               
-keycode  45 = x               
-keycode  46 = c               
-keycode  47 = v               
-keycode  48 = b               
-keycode  49 = n               
-keycode  50 = m               
-keycode  51 = comma            less            
-	alt	keycode  51 = Meta_comma      
-	shift	alt	keycode  51 = Meta_less       
-keycode  52 = period           greater         
-	alt	keycode  52 = Meta_period     
-	shift	alt	keycode  52 = Meta_greater    
-keycode  53 = slash            question        
-	control	keycode  53 = Delete          
-	alt	keycode  53 = Meta_slash      
-	shift	alt	keycode  53 = Meta_question   
-keycode  54 = Shift           
-keycode  55 = KP_Multiply     
-	altgr	keycode  55 = Hex_C           
-keycode  56 = Alt             
-keycode  57 = space           
-	control	keycode  57 = nul             
-	alt	keycode  57 = Meta_space      
-	shift	alt	keycode  57 = Meta_space      
-	control	alt	keycode  57 = Meta_nul        
-keycode  58 = Caps_Lock       
-keycode  59 = F1               F13              Console_13       F25             
-	alt	keycode  59 = Console_1       
-	control	alt	keycode  59 = Console_1       
-keycode  60 = F2               F14              Console_14       F26             
-	alt	keycode  60 = Console_2       
-	control	alt	keycode  60 = Console_2       
-keycode  61 = F3               F15              Console_15       F27             
-	alt	keycode  61 = Console_3       
-	control	alt	keycode  61 = Console_3       
-keycode  62 = F4               F16              Console_16       F28             
-	alt	keycode  62 = Console_4       
-	control	alt	keycode  62 = Console_4       
-keycode  63 = F5               F17              Console_17       F29             
-	alt	keycode  63 = Console_5       
-	control	alt	keycode  63 = Console_5       
-keycode  64 = F6               F18              Console_18       F30             
-	alt	keycode  64 = Console_6       
-	control	alt	keycode  64 = Console_6       
-keycode  65 = F7               F19              Console_19       F31             
-	alt	keycode  65 = Console_7       
-	control	alt	keycode  65 = Console_7       
-keycode  66 = F8               F20              Console_20       F32             
-	alt	keycode  66 = Console_8       
-	control	alt	keycode  66 = Console_8       
-keycode  67 = F9               F21              Console_21       F33             
-	alt	keycode  67 = Console_9       
-	control	alt	keycode  67 = Console_9       
-keycode  68 = F10              F22              Console_22       F34             
-	alt	keycode  68 = Console_10      
-	control	alt	keycode  68 = Console_10      
-keycode  69 = Num_Lock        
-	altgr	keycode  69 = Hex_E           
-keycode  70 = Scroll_Lock      Show_Memory      Show_Registers   Show_State      
-	alt	keycode  70 = Scroll_Lock     
-keycode  71 = KP_7            
-	altgr	keycode  71 = Hex_7           
-	alt	keycode  71 = Ascii_7         
-keycode  72 = KP_8            
-	altgr	keycode  72 = Hex_8           
-	alt	keycode  72 = Ascii_8         
-keycode  73 = KP_9            
-	altgr	keycode  73 = Hex_9           
-	alt	keycode  73 = Ascii_9         
-keycode  74 = KP_Subtract     
-keycode  75 = KP_4            
-	altgr	keycode  75 = Hex_4           
-	alt	keycode  75 = Ascii_4         
-keycode  76 = KP_5            
-	altgr	keycode  76 = Hex_5           
-	alt	keycode  76 = Ascii_5         
-keycode  77 = KP_6            
-	altgr	keycode  77 = Hex_6           
-	alt	keycode  77 = Ascii_6         
-keycode  78 = KP_Add          
-keycode  79 = KP_1            
-	altgr	keycode  79 = Hex_1           
-	alt	keycode  79 = Ascii_1         
-keycode  80 = KP_2            
-	altgr	keycode  80 = Hex_2           
-	alt	keycode  80 = Ascii_2         
-keycode  81 = KP_3            
-	altgr	keycode  81 = Hex_3           
-	alt	keycode  81 = Ascii_3         
-keycode  82 = KP_0            
-	altgr	keycode  82 = Hex_0           
-	alt	keycode  82 = Ascii_0         
-keycode  83 = KP_Period       
-	altgr	control	keycode  83 = Boot            
-	control	alt	keycode  83 = Boot            
-keycode  84 = Last_Console    
-keycode  85 =
-keycode  86 = less             greater          bar             
-	alt	keycode  86 = Meta_less       
-	shift	alt	keycode  86 = Meta_greater    
-keycode  87 = F11              F23              Console_23       F35             
-	alt	keycode  87 = Console_11      
-	control	alt	keycode  87 = Console_11      
-keycode  88 = F12              F24              Console_24       F36             
-	alt	keycode  88 = Console_12      
-	control	alt	keycode  88 = Console_12      
-keycode  89 =
-keycode  90 =
-keycode  91 =
-keycode  92 =
-keycode  93 =
-keycode  94 =
-keycode  95 =
-keycode  96 = KP_Enter        
-keycode  97 = Control         
-keycode  98 = KP_Divide       
-	altgr	keycode  98 = Hex_B           
-keycode  99 = Control_backslash
-	alt	keycode  99 = Meta_Control_backslash
-	shift	alt	keycode  99 = Meta_Control_backslash
-	control	alt	keycode  99 = Meta_Control_backslash
-keycode 100 = AltGr           
-keycode 101 = Break           
-keycode 102 = Find            
-keycode 103 = Up              
-	alt	keycode 103 = KeyboardSignal  
-keycode 104 = Prior           
-	shift	keycode 104 = Scroll_Backward 
-keycode 105 = Left            
-	alt	keycode 105 = Decr_Console    
-keycode 106 = Right           
-	alt	keycode 106 = Incr_Console    
-keycode 107 = Select          
-keycode 108 = Down            
-keycode 109 = Next            
-	shift	keycode 109 = Scroll_Forward  
-keycode 110 = Insert          
-keycode 111 = Remove          
-	altgr	control	keycode 111 = Boot            
-	control	alt	keycode 111 = Boot            
-keycode 112 = Macro           
-	altgr	control	keycode 112 = VoidSymbol      
-	shift	alt	keycode 112 = VoidSymbol      
-keycode 113 = F13             
-	altgr	control	keycode 113 = VoidSymbol      
-	shift	alt	keycode 113 = VoidSymbol      
-keycode 114 = F14             
-	altgr	control	keycode 114 = VoidSymbol      
-	shift	alt	keycode 114 = VoidSymbol      
-keycode 115 = Help            
-	altgr	control	keycode 115 = VoidSymbol      
-	shift	alt	keycode 115 = VoidSymbol      
-keycode 116 = Do              
-	altgr	control	keycode 116 = VoidSymbol      
-	shift	alt	keycode 116 = VoidSymbol      
-keycode 117 = F17             
-	altgr	control	keycode 117 = VoidSymbol      
-	shift	alt	keycode 117 = VoidSymbol      
-keycode 118 = KP_MinPlus      
-	altgr	control	keycode 118 = VoidSymbol      
-	shift	alt	keycode 118 = VoidSymbol      
-keycode 119 = Pause           
-keycode 120 =
-keycode 121 =
-keycode 122 =
-keycode 123 =
-keycode 124 =
-keycode 125 =
-keycode 126 =
-keycode 127 =
-string F1 = "\033[[A"
-string F2 = "\033[[B"
-string F3 = "\033[[C"
-string F4 = "\033[[D"
-string F5 = "\033[[E"
-string F6 = "\033[17~"
-string F7 = "\033[18~"
-string F8 = "\033[19~"
-string F9 = "\033[20~"
-string F10 = "\033[21~"
-string F11 = "\033[23~"
-string F12 = "\033[24~"
-string F13 = "\033[25~"
-string F14 = "\033[26~"
-string F15 = "\033[28~"
-string F16 = "\033[29~"
-string F17 = "\033[31~"
-string F18 = "\033[32~"
-string F19 = "\033[33~"
-string F20 = "\033[34~"
-string Find = "\033[1~"
-string Insert = "\033[2~"
-string Remove = "\033[3~"
-string Select = "\033[4~"
-string Prior = "\033[5~"
-string Next = "\033[6~"
-string Macro = "\033[M"
-string Pause = "\033[P"
-compose '`' 'A' to 'Ŕ'
-compose '`' 'a' to 'ŕ'
-compose '\'' 'A' to 'Á'
-compose '\'' 'a' to 'á'
-compose '^' 'A' to 'Â'
-compose '^' 'a' to 'â'
-compose '~' 'A' to 'Ă'
-compose '~' 'a' to 'ă'
-compose '"' 'A' to 'Ä'
-compose '"' 'a' to 'ä'
-compose 'O' 'A' to 'Ĺ'
-compose 'o' 'a' to 'ĺ'
-compose '0' 'A' to 'Ĺ'
-compose '0' 'a' to 'ĺ'
-compose 'A' 'A' to 'Ĺ'
-compose 'a' 'a' to 'ĺ'
-compose 'A' 'E' to 'Ć'
-compose 'a' 'e' to 'ć'
-compose ',' 'C' to 'Ç'
-compose ',' 'c' to 'ç'
-compose '`' 'E' to 'Č'
-compose '`' 'e' to 'č'
-compose '\'' 'E' to 'É'
-compose '\'' 'e' to 'é'
-compose '^' 'E' to 'Ę'
-compose '^' 'e' to 'ę'
-compose '"' 'E' to 'Ë'
-compose '"' 'e' to 'ë'
-compose '`' 'I' to 'Ě'
-compose '`' 'i' to 'ě'
-compose '\'' 'I' to 'Í'
-compose '\'' 'i' to 'í'
-compose '^' 'I' to 'Î'
-compose '^' 'i' to 'î'
-compose '"' 'I' to 'Ď'
-compose '"' 'i' to 'ď'
-compose '-' 'D' to 'Đ'
-compose '-' 'd' to 'đ'
-compose '~' 'N' to 'Ń'
-compose '~' 'n' to 'ń'
-compose '`' 'O' to 'Ň'
-compose '`' 'o' to 'ň'
-compose '\'' 'O' to 'Ó'
-compose '\'' 'o' to 'ó'
-compose '^' 'O' to 'Ô'
-compose '^' 'o' to 'ô'
-compose '~' 'O' to 'Ő'
-compose '~' 'o' to 'ő'
-compose '"' 'O' to 'Ö'
-compose '"' 'o' to 'ö'
-compose '/' 'O' to 'Ř'
-compose '/' 'o' to 'ř'
-compose '`' 'U' to 'Ů'
-compose '`' 'u' to 'ů'
-compose '\'' 'U' to 'Ú'
-compose '\'' 'u' to 'ú'
-compose '^' 'U' to 'Ű'
-compose '^' 'u' to 'ű'
-compose '"' 'U' to 'Ü'
-compose '"' 'u' to 'ü'
-compose '\'' 'Y' to 'Ý'
-compose '\'' 'y' to 'ý'
-compose 'T' 'H' to 'Ţ'
-compose 't' 'h' to 'ţ'
-compose 's' 's' to 'ß'
-compose '"' 'y' to '˙'
-compose 's' 'z' to 'ß'
-compose 'i' 'j' to '˙'
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/maps/usb.map linux.18p17/drivers/usb/maps/usb.map
--- linux.vanilla/drivers/usb/maps/usb.map	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/maps/usb.map	Thu Jan  1 01:00:00 1970
@@ -1,233 +0,0 @@
-# USB kernel keymap.
-keymaps 0-2,4-5,8,12
-
-keycode   4 = a
-	altgr   keycode  30 = Hex_A
-keycode   5 = b
-	altgr   keycode  48 = Hex_B
-keycode   6 = c
-	altgr   keycode  46 = Hex_C   
-keycode   7 = d
-	altgr   keycode  32 = Hex_D   
-keycode   8 = e
-	altgr   keycode  18 = Hex_E   
-keycode   9 = f
-	altgr   keycode  33 = Hex_F               
-keycode  10 = g               
-keycode  11 = h               
-keycode  12 = i               
-keycode  13 = j               
-keycode  14 = k               
-keycode  15 = l               
-keycode  16 = m               
-keycode  17 = n               
-keycode  18 = o               
-keycode  19 = p               
-keycode  20 = q               
-keycode  21 = r               
-keycode  22 = s               
-keycode  23 = t               
-keycode  24 = u               
-keycode  25 = v               
-keycode  26 = w               
-keycode  27 = x               
-keycode  28 = y               
-keycode  29 = z               
-keycode  30 = one              exclam          
-	alt     keycode   2 = Meta_one        
-keycode  31 = two              at
-	control	keycode   3 = nul             
-	shift	control	keycode   3 = nul             
-	alt	keycode   3 = Meta_two        
-keycode  32 = three            numbersign      
-	control keycode   4 = Escape          
-	alt     keycode   4 = Meta_three      
-keycode  33 = four             dollar
-	control keycode   5 = Control_backslash
-	alt     keycode   5 = Meta_four       
-keycode  34 = five             percent         
-	control keycode   6 = Control_bracketright
-	alt     keycode   6 = Meta_five       
-keycode  35 = six              asciicircum     
-	control keycode   7 = Control_asciicircum
-	alt     keycode   7 = Meta_six        
-keycode  36 = seven            ampersand
-	control keycode   8 = Control_underscore
-	alt     keycode   8 = Meta_seven      
-keycode  37 = eight            asterisk
-	control keycode   9 = Delete          
-	alt     keycode   9 = Meta_eight      
-keycode  38 = nine             parenleft
-	alt     keycode  10 = Meta_nine       
-keycode  39 = zero             parenright
-	alt     keycode  11 = Meta_zero       
-keycode  40 = Return          
-	alt     keycode  28 = Meta_Control_m  
-keycode  41 = Escape           Escape          
-	alt     keycode   1 = Meta_Escape     
-keycode  42 = Delete           Delete          
-	control keycode  14 = BackSpace
-	alt     keycode  14 = Meta_Delete     
-keycode  43 = Tab              Tab             
-	alt     keycode  15 = Meta_Tab        
-keycode  44 = space            space           
-	control keycode  57 = nul             
-	alt     keycode  57 = Meta_space      
-keycode  45 = minus            underscore       backslash       
-	control	keycode  12 = Control_underscore
-	shift	control	keycode  12 = Control_underscore
-	alt	keycode  12 = Meta_minus      
-keycode  46 = equal            plus            
-	alt     keycode  13 = Meta_equal      
-keycode  47 = bracketleft      braceleft       
-	control keycode  26 = Escape          
-	alt     keycode  26 = Meta_bracketleft
-keycode  48 = bracketright     braceright       asciitilde      
-	control keycode  27 = Control_bracketright
-	alt     keycode  27 = Meta_bracketright
-keycode  49 = backslash        bar             
-	control keycode  43 = Control_backslash
-	alt     keycode  43 = Meta_backslash  
-keycode 50 =
-keycode  51 = semicolon        colon           
-	alt     keycode  39 = Meta_semicolon  
-keycode  52 = apostrophe       quotedbl        
-	control keycode  40 = Control_g       
-	alt     keycode  40 = Meta_apostrophe 
-keycode  53 = grave            asciitilde      
-	control keycode  41 = nul             
-	alt     keycode  41 = Meta_grave      
-keycode  54 = comma            less            
-	alt     keycode  51 = Meta_comma      
-keycode  55 = period           greater         
-	control keycode  52 = Compose         
-	alt     keycode  52 = Meta_period     
-keycode  56 = slash            question        
-	control keycode  53 = Delete          
-	alt     keycode  53 = Meta_slash      
-keycode  57 = Caps_Lock       
-keycode  58 = F1               F11              Console_13      
-	control keycode  59 = F1              
-	alt     keycode  59 = Console_1       
-	control alt     keycode  59 = Console_1       
-keycode  59 = F2               F12              Console_14      
-	control keycode  60 = F2              
-	alt     keycode  60 = Console_2       
-	control alt     keycode  60 = Console_2       
-keycode  60 = F3               F13              Console_15      
-	control keycode  61 = F3              
-	alt     keycode  61 = Console_3       
-	control alt     keycode  61 = Console_3       
-keycode  61 = F4               F14              Console_16      
-	control keycode  62 = F4              
-	alt     keycode  62 = Console_4       
-	control alt     keycode  62 = Console_4       
-keycode  62 = F5               F15              Console_17      
-	control keycode  63 = F5              
-	alt     keycode  63 = Console_5       
-	control alt     keycode  63 = Console_5       
-keycode  63 = F6               F16              Console_18      
-	control keycode  64 = F6              
-	alt     keycode  64 = Console_6       
-	control alt     keycode  64 = Console_6       
-keycode  64 = F7               F17              Console_19      
-	control keycode  65 = F7              
-	alt     keycode  65 = Console_7       
-	control alt     keycode  65 = Console_7       
-keycode  65 = F8               F18              Console_20      
-	control keycode  66 = F8              
-	alt     keycode  66 = Console_8       
-	control alt     keycode  66 = Console_8       
-keycode  66 = F9               F19              Console_21      
-	control keycode  67 = F9              
-	alt     keycode  67 = Console_9       
-	control alt     keycode  67 = Console_9       
-keycode  67 = F10              F20              Console_22      
-	control keycode  68 = F10             
-	alt     keycode  68 = Console_10      
-	control alt     keycode  68 = Console_10      
-keycode  68 = F11              F11              Console_23      
-	control keycode  87 = F11             
-	alt     keycode  87 = Console_11      
-	control alt     keycode  87 = Console_11      
-keycode  69 = F12              F12              Console_24      
-	control keycode  88 = F12             
-	alt     keycode  88 = Console_12      
-	control alt     keycode  88 = Console_12      
-keycode  70 = Print_Screen
-keycode  71 = Scroll_Lock      Show_Memory      Show_Registers  
-	control keycode  70 = Show_State      
-	alt     keycode  70 = Scroll_Lock     
-keycode  72 = Pause           
-keycode  73 = Insert          
-keycode  74 = Home
-keycode  75 = Prior           
-	shift   keycode 104 = Scroll_Backward 
-keycode  76 = Remove          
-#	altgr   control keycode 111 = Boot            
-	control alt     keycode 111 = Boot            
-keycode  77 = End
-keycode  78 = Next            
-	shift   keycode 109 = Scroll_Forward  
-keycode  79 = Right           
-	alt     keycode 106 = Incr_Console
-keycode  80 = Left            
-	alt     keycode 105 = Decr_Console
-keycode  81 = Down            
-keycode  82 = Up              
-keycode  83 = Num_Lock
-	shift   keycode  69 = Bare_Num_Lock
-keycode  84 = KP_Divide       
-keycode  85 = KP_Multiply     
-keycode  86 = KP_Subtract     
-keycode  87 = KP_Add          
-keycode  88 = KP_Enter        
-keycode  89 = KP_1            
-	alt     keycode  79 = Ascii_1         
-	altgr   keycode  79 = Hex_1         
-keycode  90 = KP_2            
-	alt     keycode  80 = Ascii_2         
-	altgr   keycode  80 = Hex_2         
-keycode  91 = KP_3            
-	alt     keycode  81 = Ascii_3         
-	altgr   keycode  81 = Hex_3         
-keycode  92 = KP_4            
-	alt     keycode  75 = Ascii_4         
-	altgr   keycode  75 = Hex_4         
-keycode  93 = KP_5            
-	alt     keycode  76 = Ascii_5         
-	altgr   keycode  76 = Hex_5         
-keycode  94 = KP_6            
-	alt     keycode  77 = Ascii_6         
-	altgr   keycode  77 = Hex_6         
-keycode  95 = KP_7            
-	alt     keycode  71 = Ascii_7         
-	altgr   keycode  71 = Hex_7         
-keycode  96 = KP_8            
-	alt     keycode  72 = Ascii_8         
-	altgr   keycode  72 = Hex_8         
-keycode  97 = KP_9            
-	alt     keycode  73 = Ascii_9         
-	altgr   keycode  73 = Hex_9         
-keycode  98 = KP_0            
-	alt     keycode  82 = Ascii_0         
-	altgr   keycode  82 = Hex_0         
-keycode  99 = KP_Period       
-#	altgr   control keycode  83 = Boot            
-	control alt     keycode  83 = Boot            
-keycode 100 =
-keycode 101 = Application
-keycode 102 =
-keycode 103 =
-keycode 104 = F13             
-keycode 105 = F14             
-
-# modifiers
-keycode 120 = Control
-keycode 121 = Shift
-keycode 122 = Alt
-keycode 123 = Window
-keycode 124 = Control_R
-keycode 125 = Shift_R
-keycode 126 = Alt_R
-keycode 127 = Window_R
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/mdc800.c linux.18p17/drivers/usb/mdc800.c
--- linux.vanilla/drivers/usb/mdc800.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/mdc800.c	Fri Sep 15 22:04:11 2000
@@ -0,0 +1,938 @@
+/*
+ * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ *	USB-Kernel Driver for the Mustek MDC800 Digital Camera
+ *	(c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
+ *
+ *
+ *	The driver brings the USB functions of the MDC800 to Linux.
+ * To use the Camera you must support the USB Protocoll of the camera
+ * to the Kernel Node.
+ * The Driver uses a misc device Node. Create it with :
+ * mknod /dev/mustek c 180 32
+ *
+ * The driver supports only one camera.
+ *
+ * version 0.7.3
+ * bugfix : The mdc800->state field gets set to READY after the
+ * the diconnect function sets it to NOT_CONNECTED. This makes the
+ * driver running like the camera is connected and causes some
+ * hang ups.
+ *
+ * version 0.7.1
+ * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload
+ * problems when compiled as Module.
+ * (04/04/2000)
+ *
+ * The mdc800 driver gets assigned the USB Minor 32-47. The Registration
+ * was updated to use these values.
+ * (26/03/2000)
+ *
+ * The Init und Exit Module Function are updated.
+ * (01/03/2000)
+ *
+ * version 0.7.0
+ * Rewrite of the driver : The driver now uses URB's. The old stuff
+ * has been removed.
+ *
+ * version 0.6.0
+ * Rewrite of this driver: The Emulation of the rs232 protocoll
+ * has been removed from the driver. A special executeCommand function
+ * for this driver is included to gphoto.
+ * The driver supports two kind of communication to bulk endpoints.
+ * Either with the dev->bus->ops->bulk... or with callback function.
+ * (09/11/1999)
+ *
+ * version 0.5.0:
+ *	first Version that gets a version number. Most of the needed
+ * functions work.
+ * (20/10/1999)
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+
+#include <linux/usb.h>
+
+#define VERSION 	"0.7.3"
+#define RELEASE_DATE 	"(24/04/2000)"
+
+/* Vendor and Product Information */
+#define MDC800_VENDOR_ID 	0x055f
+#define MDC800_PRODUCT_ID	0xa800
+
+/* Timeouts (msec) */
+#define TO_DOWNLOAD_GET_READY		1500
+#define TO_DOWNLOAD_GET_BUSY		1500
+#define TO_WRITE_GET_READY		1000
+#define TO_DEFAULT_COMMAND		5000
+#define TO_READ_FROM_IRQ 		TO_DEFAULT_COMMAND
+#define TO_GET_READY			TO_DEFAULT_COMMAND
+
+/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
+#define MDC800_DEVICE_MINOR_BASE 32
+
+
+/**************************************************************************
+	Data and structs
+***************************************************************************/
+
+
+typedef enum {
+	NOT_CONNECTED, READY, WORKING, DOWNLOAD
+} mdc800_state;
+
+
+/* Data for the driver */
+struct mdc800_data
+{
+	struct usb_device *	dev;			// Device Data
+	mdc800_state 		state;
+
+	unsigned int		endpoint [4];
+
+	purb_t			irq_urb;
+	wait_queue_head_t	irq_wait;
+	char*			irq_urb_buffer;
+
+	int			camera_busy;          // is camera busy ?
+	int 			camera_request_ready; // Status to synchronize with irq
+	char 			camera_response [8];	 // last Bytes send after busy
+
+	purb_t   		write_urb;
+	char*			write_urb_buffer;
+	wait_queue_head_t	write_wait;
+
+
+	purb_t	   		download_urb;
+	char*			download_urb_buffer;
+	wait_queue_head_t	download_wait;
+	int			download_left;		// Bytes left to download ?
+
+
+	/* Device Data */
+	char			out [64];	// Answer Buffer
+	int 			out_ptr;		// Index to the first not readen byte
+	int			out_count;	// Bytes in the buffer
+
+	int			open;			// Camera device open ?
+	int			rw_lock;		// Block read <-> write
+
+	char 			in [8];			// Command Input Buffer
+	int  			in_count;
+
+	int			pic_index;		// Cache for the Imagesize (-1 for nothing cached )
+	int			pic_len;
+};
+
+
+/* Specification of the Endpoints */
+static struct usb_endpoint_descriptor mdc800_ed [4] =
+{
+	{ 0,0, 0x01, 0x02,  8, 0,0,0 },
+	{ 0,0, 0x82, 0x03,  8, 0,0,0 },
+	{ 0,0, 0x03, 0x02, 64, 0,0,0 },
+	{ 0,0, 0x84, 0x02, 64, 0,0,0 }
+};
+
+
+/* The Variable used by the driver */
+static struct mdc800_data* mdc800=0;
+
+
+/***************************************************************************
+	The USB Part of the driver
+****************************************************************************/
+
+static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b)
+{
+	return (
+		   ( a->bEndpointAddress == b->bEndpointAddress )
+		&& ( a->bmAttributes     == b->bmAttributes     )
+		&& ( a->wMaxPacketSize   == b->wMaxPacketSize   )
+	);
+}
+
+
+/*
+ * Checks wether the camera responds busy
+ */
+static int mdc800_isBusy (char* ch)
+{
+	int i=0;
+	while (i<8)
+	{
+		if (ch [i] != (char)0x99)
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+
+/*
+ * Checks wether the Camera is ready
+ */
+static int mdc800_isReady (char *ch)
+{
+	int i=0;
+	while (i<8)
+	{
+		if (ch [i] != (char)0xbb)
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+
+
+/*
+ * USB IRQ Handler for InputLine
+ */
+static void mdc800_usb_irq (struct urb *urb)
+{
+	int data_received=0, wake_up;
+	unsigned char* b=urb->transfer_buffer;
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status >= 0)
+	{
+
+		//dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+		if (mdc800_isBusy (b))
+		{
+			if (!mdc800->camera_busy)
+			{
+				mdc800->camera_busy=1;
+				dbg ("gets busy");
+			}
+		}
+		else
+		{
+			if (mdc800->camera_busy && mdc800_isReady (b))
+			{
+				mdc800->camera_busy=0;
+				dbg ("gets ready");
+			}
+		}
+		if (!(mdc800_isBusy (b) || mdc800_isReady (b)))
+		{
+			/* Store Data in camera_answer field */
+			dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+			memcpy (mdc800->camera_response,b,8);
+			data_received=1;
+		}
+	}
+	wake_up= ( mdc800->camera_request_ready > 0 )
+				&&
+				(
+					((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
+				||
+					((mdc800->camera_request_ready == 2) && data_received)
+				||
+					((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
+				||
+					(urb->status < 0)
+				);
+
+	if (wake_up)
+	{
+		mdc800->camera_request_ready=0;
+		wake_up_interruptible (&mdc800->irq_wait);
+	}
+}
+
+
+/*
+ * Waits a while until the irq responds that camera is ready
+ *
+ *  mode : 0: Wait for camera gets ready
+ *         1: Wait for receiving data
+ *         2: Wait for camera gets busy
+ *
+ * msec: Time to wait
+ */
+static int mdc800_usb_waitForIRQ (int mode, int msec)
+{
+	mdc800->camera_request_ready=1+mode;
+
+	interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000);
+
+	if (mdc800->camera_request_ready>0)
+	{
+		mdc800->camera_request_ready=0;
+		err ("timeout waiting for camera.");
+		return -1;
+	}
+	
+	if (mdc800->state == NOT_CONNECTED)
+	{
+		warn ("Camera gets disconnected during waiting for irq.");
+		mdc800->camera_request_ready=0;
+		return -2;
+	}
+	
+	return 0;
+}
+
+
+/*
+ * The write_urb callback function
+ */
+static void mdc800_usb_write_notify (struct urb *urb)
+{
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status != 0)
+	{
+		err ("writing command fails (status=%i)", urb->status);
+	}
+	else
+	{	
+		mdc800->state=READY;
+	}
+	wake_up_interruptible (&mdc800->write_wait);
+}
+
+
+/*
+ * The download_urb callback function
+ */
+static void mdc800_usb_download_notify (struct urb *urb)
+{
+	struct mdc800_data* mdc800=urb->context;
+
+	if (urb->status == 0)
+	{
+		/* Fill output buffer with these data */
+		memcpy (mdc800->out,  urb->transfer_buffer, 64);
+		mdc800->out_count=64;
+		mdc800->out_ptr=0;
+		mdc800->download_left-=64;
+		if (mdc800->download_left == 0)
+		{
+			mdc800->state=READY;
+		}
+	}
+	else
+	{
+		err ("request bytes fails (status:%i)", urb->status);
+	}
+	wake_up_interruptible (&mdc800->download_wait);
+}
+
+
+/***************************************************************************
+	Probing for the Camera
+ ***************************************************************************/
+
+static struct usb_driver mdc800_usb_driver;
+
+/*
+ * Callback to search the Mustek MDC800 on the USB Bus
+ */
+static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum )
+{
+	int 										i,j;
+	struct usb_interface_descriptor	*intf_desc;
+	int irq_interval=0;
+
+	dbg ("(mdc800_usb_probe) called.");
+
+
+	if (dev->descriptor.idVendor != MDC800_VENDOR_ID)
+		return 0;
+	if (dev->descriptor.idProduct != MDC800_PRODUCT_ID)
+		return 0;
+
+	if (mdc800->dev != 0)
+	{
+		warn ("only one Mustek MDC800 is supported.");
+		return 0;
+	}
+
+	if (dev->descriptor.bNumConfigurations != 1)
+	{
+		err ("probe fails -> wrong Number of Configuration");
+		return 0;
+	}
+	intf_desc=&dev->actconfig->interface[ifnum].altsetting[0];
+
+	if (
+			( intf_desc->bInterfaceClass != 0xff )
+		||	( intf_desc->bInterfaceSubClass != 0 )
+		|| ( intf_desc->bInterfaceProtocol != 0 )
+		|| ( intf_desc->bNumEndpoints != 4)
+	)
+	{
+		err ("probe fails -> wrong Interface");
+		return 0;
+	}
+
+	/* Check the Endpoints */
+	for (i=0; i<4; i++)
+	{
+		mdc800->endpoint[i]=-1;
+		for (j=0; j<4; j++)
+		{
+			if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i]))
+			{
+				mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ;
+				if (i==1)
+				{
+					irq_interval=intf_desc->endpoint [j].bInterval;
+				}
+
+				continue;
+			}
+		}
+		if (mdc800->endpoint[i] == -1)
+		{
+			err ("probe fails -> Wrong Endpoints.");
+			return 0;
+		}
+	}
+
+
+	usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800);
+	if (usb_set_interface (dev, ifnum, 0) < 0)
+	{
+		err ("MDC800 Configuration fails.");
+		return 0;
+	}
+
+	info ("Found Mustek MDC800 on USB.");
+
+	mdc800->dev=dev;
+	mdc800->state=READY;
+	mdc800->open=0;
+	mdc800->rw_lock=0;
+
+	/* Setup URB Structs */
+	FILL_INT_URB (
+		mdc800->irq_urb,
+		mdc800->dev,
+		usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]),
+		mdc800->irq_urb_buffer,
+		8,
+		mdc800_usb_irq,
+		mdc800,
+		irq_interval
+	);
+
+	FILL_BULK_URB (
+		mdc800->write_urb,
+		mdc800->dev,
+		usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]),
+		mdc800->write_urb_buffer,
+		8,
+		mdc800_usb_write_notify,
+		mdc800
+	);
+
+	FILL_BULK_URB (
+		mdc800->download_urb,
+		mdc800->dev,
+		usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]),
+		mdc800->download_urb_buffer,
+		64,
+		mdc800_usb_download_notify,
+		mdc800
+	);
+
+	return mdc800;
+}
+
+
+/*
+ * Disconnect USB device (maybe the MDC800)
+ */
+static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
+{
+	struct mdc800_data* mdc800=(struct mdc800_data*) ptr;
+
+	dbg ("(mdc800_usb_disconnect) called");
+
+	if (mdc800->state == NOT_CONNECTED)
+		return;
+	
+	mdc800->state=NOT_CONNECTED;
+
+	usb_unlink_urb (mdc800->irq_urb);
+	usb_unlink_urb (mdc800->write_urb);
+	usb_unlink_urb (mdc800->download_urb);
+
+	usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]);
+
+	mdc800->dev=0;
+	info ("Mustek MDC800 disconnected from USB.");
+}
+
+
+/***************************************************************************
+	The Misc device Part (file_operations)
+****************************************************************************/
+
+/*
+ * This Function calc the Answersize for a command.
+ */
+static int mdc800_getAnswerSize (char command)
+{
+	switch ((unsigned char) command)
+	{
+		case 0x2a:
+		case 0x49:
+		case 0x51:
+		case 0x0d:
+		case 0x20:
+		case 0x07:
+		case 0x01:
+		case 0x25:
+		case 0x00:
+			return 8;
+
+		case 0x05:
+		case 0x3e:
+			return mdc800->pic_len;
+
+		case 0x09:
+			return 4096;
+
+		default:
+			return 0;
+	}
+}
+
+
+/*
+ * Init the device: (1) alloc mem (2) Increase MOD Count ..
+ */
+static int mdc800_device_open (struct inode* inode, struct file *file)
+{
+	int retval=0;
+	
+	if (mdc800->state == NOT_CONNECTED)
+	{
+		return -EBUSY;
+	}
+
+	if (mdc800->open)
+	{
+		return -EBUSY;
+	}
+
+	mdc800->rw_lock=0;
+	mdc800->in_count=0;
+	mdc800->out_count=0;
+	mdc800->out_ptr=0;
+	mdc800->pic_index=0;
+	mdc800->pic_len=-1;
+	mdc800->download_left=0;
+
+	mdc800->camera_busy=0;
+	mdc800->camera_request_ready=0;
+
+	retval=0;
+	if (usb_submit_urb (mdc800->irq_urb))
+	{
+		err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
+		return -EIO;
+	}
+
+	mdc800->open=1;
+
+	dbg ("Mustek MDC800 device opened.");
+	return 0;
+}
+
+
+/*
+ * Close the Camera and release Memory
+ */
+static int mdc800_device_release (struct inode* inode, struct file *file)
+{
+	int retval=0;
+	dbg ("Mustek MDC800 device closed.");
+
+	lock_kernel();
+	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
+	{
+		mdc800->open=0;
+		usb_unlink_urb (mdc800->irq_urb);
+		usb_unlink_urb (mdc800->write_urb);
+		usb_unlink_urb (mdc800->download_urb);
+	}
+	else
+	{
+		retval=-EIO;
+	}
+	unlock_kernel();
+
+	return retval;
+}
+
+
+/*
+ * The Device read callback Function
+ */
+static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos)
+{
+	int   left=len, sts=len; /* single transfer size */
+	char* ptr=buf;
+
+	if (mdc800->state == NOT_CONNECTED)
+		return -EBUSY;
+
+	if (mdc800->state == WORKING)
+	{
+		warn ("Illegal State \"working\" reached during read ?!");
+		return -EBUSY;
+	}
+
+	if (!mdc800->open || mdc800->rw_lock)
+		return -EBUSY;
+	mdc800->rw_lock=1;
+
+	while (left)
+	{
+		if (signal_pending (current)) {
+			mdc800->rw_lock=0;
+			return -EINTR;
+		}
+
+		sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
+
+		if (sts <= 0)
+		{
+			/* Too less Data in buffer */
+			if (mdc800->state == DOWNLOAD)
+			{
+				mdc800->out_count=0;
+				mdc800->out_ptr=0;
+
+				/* Download -> Request new bytes */
+				if (usb_submit_urb (mdc800->download_urb))
+				{
+					err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
+					mdc800->rw_lock=0;
+					return len-left;
+				}
+				interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
+				if (mdc800->download_urb->status != 0)
+				{
+					err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
+					mdc800->rw_lock=0;
+					return len-left;
+				}
+			}
+			else
+			{
+				/* No more bytes -> that's an error*/
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+		}
+		else
+		{
+			/* memcpy Bytes */
+			memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts);
+			ptr+=sts;
+			left-=sts;
+			mdc800->out_ptr+=sts;
+		}
+	}
+
+	mdc800->rw_lock=0;
+	return len-left;
+}
+
+
+/*
+ * The Device write callback Function
+ * If a 8Byte Command is received, it will be send to the camera.
+ * After this the driver initiates the request for the answer or
+ * just waits until the camera becomes ready.
+ */
+static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
+{
+	int i=0;
+
+	if (mdc800->state != READY)
+		return -EBUSY;
+
+	if (!mdc800->open || mdc800->rw_lock)
+		return -EBUSY;
+	mdc800->rw_lock=1;
+
+	while (i<len)
+	{
+		if (signal_pending (current)) {
+			mdc800->rw_lock=0;
+			return -EINTR;
+		}
+
+		/* check for command start */
+		if (buf [i] == (char) 0x55)
+		{
+			mdc800->in_count=0;
+			mdc800->out_count=0;
+			mdc800->out_ptr=0;
+			mdc800->download_left=0;
+		}
+
+		/* save command byte */
+		if (mdc800->in_count < 8)
+		{
+			mdc800->in[mdc800->in_count]=buf[i];
+			mdc800->in_count++;
+		}
+		else
+		{
+			err ("Command is to long !\n");
+			mdc800->rw_lock=0;
+			return -EIO;
+		}
+
+		/* Command Buffer full ? -> send it to camera */
+		if (mdc800->in_count == 8)
+		{
+			int answersize;
+
+			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
+			{
+				err ("Camera didn't get ready.\n");
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+
+			answersize=mdc800_getAnswerSize (mdc800->in[1]);
+
+			mdc800->state=WORKING;
+			memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
+			if (usb_submit_urb (mdc800->write_urb))
+			{
+				err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+			interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000);
+			if (mdc800->state == WORKING)
+			{
+				usb_unlink_urb (mdc800->write_urb);
+				mdc800->rw_lock=0;
+				return -EIO;
+			}
+
+			switch ((unsigned char) mdc800->in[1])
+			{
+				case 0x05: /* Download Image */
+				case 0x3e: /* Take shot in Fine Mode (WCam Mode) */
+					if (mdc800->pic_len < 0)
+					{
+						err ("call 0x07 before 0x05,0x3e");
+						mdc800->state=READY;
+						mdc800->rw_lock=0;
+						return -EIO;
+					}
+					mdc800->pic_len=-1;
+
+				case 0x09: /* Download Thumbnail */
+					mdc800->download_left=answersize+64;
+					mdc800->state=DOWNLOAD;
+					mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY);
+					break;
+
+
+				default:
+					if (answersize)
+					{
+
+						if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
+						{
+							err ("requesting answer from irq fails");
+							mdc800->rw_lock=0;
+							return -EIO;
+						}
+
+						/* Write dummy data, (this is ugly but part of the USB Protokoll */
+						/* if you use endpoint 1 as bulk and not as irq */
+						memcpy (mdc800->out, mdc800->camera_response,8);
+
+						/* This is the interpreted answer */
+						memcpy (&mdc800->out[8], mdc800->camera_response,8);
+
+						mdc800->out_ptr=0;
+						mdc800->out_count=16;
+
+						/* Cache the Imagesize, if command was getImageSize */
+						if (mdc800->in [1] == (char) 0x07)
+						{
+							mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2];
+
+							dbg ("cached imagesize = %i",mdc800->pic_len);
+						}
+
+					}
+					else
+					{
+						if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
+						{
+							err ("Command Timeout.");
+							mdc800->rw_lock=0;
+							return -EIO;
+						}
+					}
+					mdc800->state=READY;
+					break;
+			}
+		}
+		i++;
+	}
+	mdc800->rw_lock=0;
+	return i;
+}
+
+
+/***************************************************************************
+	Init and Cleanup this driver (Structs and types)
+****************************************************************************/
+
+/* File Operations of this drivers */
+static struct file_operations mdc800_device_ops =
+{
+	read:		mdc800_device_read,
+	write:		mdc800_device_write,
+	open:		mdc800_device_open,
+	release:	mdc800_device_release,
+};
+
+
+
+/*
+ * USB Driver Struct for this device
+ */
+static struct usb_driver mdc800_usb_driver =
+{
+	"mdc800",
+	mdc800_usb_probe,
+	mdc800_usb_disconnect,
+	{ 0,0 },
+	&mdc800_device_ops,
+	MDC800_DEVICE_MINOR_BASE
+};
+
+
+
+/************************************************************************
+	Init and Cleanup this driver (Main Functions)
+*************************************************************************/
+
+#define try(A)           if ((A) == 0) goto cleanup_on_fail;
+#define try_free_mem(A)  if (A != 0) { kfree (A); A=0; }
+#define try_free_urb(A)  if (A != 0) { usb_free_urb (A); A=0; }
+
+int __init usb_mdc800_init (void)
+{
+	/* Allocate Memory */
+	try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
+
+	mdc800->dev=0;
+	mdc800->open=0;
+	mdc800->state=NOT_CONNECTED;
+	memset(mdc800, 0, sizeof(struct mdc800_data));
+
+	init_waitqueue_head (&mdc800->irq_wait);
+	init_waitqueue_head (&mdc800->write_wait);
+	init_waitqueue_head (&mdc800->download_wait);
+
+	try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
+	try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
+	try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
+
+	try (mdc800->irq_urb=usb_alloc_urb (0));
+	try (mdc800->download_urb=usb_alloc_urb (0));
+	try (mdc800->write_urb=usb_alloc_urb (0));
+
+	/* Register the driver */
+	if (usb_register (&mdc800_usb_driver) < 0)
+		goto cleanup_on_fail;
+
+	info ("Mustek Digital Camera Driver " VERSION " (MDC800)");
+	info (RELEASE_DATE " Henning Zabel <henning@uni-paderborn.de>");
+
+	return 0;
+
+	/* Clean driver up, when something fails */
+
+cleanup_on_fail:
+
+	if (mdc800 != 0)
+	{
+		err ("can't alloc memory!");
+
+		try_free_mem (mdc800->download_urb_buffer);
+		try_free_mem (mdc800->write_urb_buffer);
+		try_free_mem (mdc800->irq_urb_buffer);
+
+		try_free_urb (mdc800->write_urb);
+		try_free_urb (mdc800->download_urb);
+		try_free_urb (mdc800->irq_urb);
+
+		kfree (mdc800);
+	}
+	mdc800=0;
+	return -1;
+}
+
+
+void __exit usb_mdc800_cleanup (void)
+{
+	usb_deregister (&mdc800_usb_driver);
+
+	usb_free_urb (mdc800->irq_urb);
+	usb_free_urb (mdc800->download_urb);
+	usb_free_urb (mdc800->write_urb);
+
+	kfree (mdc800->irq_urb_buffer);
+	kfree (mdc800->write_urb_buffer);
+	kfree (mdc800->download_urb_buffer);
+
+	kfree (mdc800);
+	mdc800=0;
+}
+
+
+MODULE_AUTHOR ("Henning Zabel <henning@uni-paderborn.de>");
+MODULE_DESCRIPTION ("USB Driver for Mustek MDC800 Digital Camera");
+
+module_init (usb_mdc800_init);
+module_exit (usb_mdc800_cleanup);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/microtek.c linux.18p17/drivers/usb/microtek.c
--- linux.vanilla/drivers/usb/microtek.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/microtek.c	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,1055 @@
+/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
+ *
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * Parts shamelessly stolen from usb-storage and copyright by their
+ * authors. Thanks to Matt Dharm for giving us permission!
+ *
+ * This driver implements a SCSI host controller driver and a USB
+ * device driver. To avoid confusion, all the USB related stuff is
+ * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
+ * 
+ * Microtek (www.microtek.com) did not release the specifications for
+ * their USB protocol to us, so we had to reverse engineer them. We
+ * don't know for which models they are valid.
+ *
+ * The X6 USB has three bulk endpoints, one output (0x1) down which
+ * commands and outgoing data are sent, and two input: 0x82 from which
+ * normal data is read from the scanner (in packets of maximum 32
+ * bytes) and from which the status byte is read, and 0x83 from which
+ * the results of a scan (or preview) are read in up to 64 * 1024 byte
+ * chunks by the Windows driver. We don't know how much it is possible
+ * to read at a time from 0x83.
+ *
+ * It seems possible to read (with URB transfers) everything from 0x82
+ * in one go, without bothering to read in 32 byte chunks.
+ *
+ * There seems to be an optimisation of a further READ implicit if
+ * you simply read from 0x83.
+ *
+ * Guessed protocol:
+ *
+ *	Send raw SCSI command to EP 0x1
+ *
+ *	If there is data to receive:
+ *		If the command was READ datatype=image:
+ *			Read a lot of data from EP 0x83
+ *		Else:
+ *			Read data from EP 0x82
+ *	Else:
+ *		If there is data to transmit:
+ *			Write it to EP 0x1
+ *
+ *	Read status byte from EP 0x82
+ *
+ * References:
+ *
+ * The SCSI command set for the scanner is available from
+ *	ftp://ftp.microtek.com/microtek/devpack/
+ *
+ * Microtek NV sent us a more up to date version of the document. If
+ * you want it, just send mail.
+ *
+ * Status:
+ *
+ *	This driver does not work properly yet.
+ *	Untested with multiple scanners.
+ *	Untested on SMP.
+ *	Untested on UHCI.
+ *
+ * History:
+ *
+ *	20000417 starting history
+ *	20000417 fixed load oops
+ *	20000417 fixed unload oops
+ *	20000419 fixed READ IMAGE detection
+ *	20000424 started conversion to use URBs
+ *	20000502 handled short transfers as errors
+ *	20000513 rename and organisation of functions (john)
+ *	20000513 added IDs for all products supported by Windows driver (john)
+ *	20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
+ *	20000514 Version 0.0.8j
+ *      20000514 Fix reporting of non-existant devices to SCSI layer (john)
+ *	20000514 Added MTS_DEBUG_INT (john)
+ *	20000514 Changed "usb-microtek" to "microtek" for consistency (john)
+ *	20000514 Stupid bug fixes (john)
+ *	20000514 Version 0.0.9j
+ *	20000515 Put transfer context and URB in mts_desc (john)
+ *	20000515 Added prelim turn off debugging support (john)
+ *	20000515 Version 0.0.10j
+ *      20000515 Fixed up URB allocation (clear URB on alloc) (john)
+ *      20000515 Version 0.0.11j
+ *	20000516 Removed unnecessary spinlock in mts_transfer_context (john)
+ *	20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
+ *	20000516 Implemented (badly) scsi_abort (john)
+ *	20000516 Version 0.0.12j
+ *      20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
+ *      20000517 Added mts_debug_dump to print ll USB info (john)
+ *	20000518 Tweaks and documentation updates (john)
+ *	20000518 Version 0.0.13j
+ *	20000518 Cleaned up abort handling (john)
+ *	20000523 Removed scsi_command and various scsi_..._resets (john)
+ *	20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
+ *	20000523 Fixed last tiresome compile warning (john)
+ *	20000523 Version 0.0.14j (though version 0.1 has come out?)
+ *	20000602 Added primitive reset
+ *	20000602 Version 0.2.0
+ *	20000603 various cosmetic changes
+ *	20000603 Version 0.2.1
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/proc_fs.h>
+
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "microtek.h"
+
+/* Constants */
+
+#define MTS_ABORT_TIMEOUT HZ /*jiffies*/
+
+
+/* Should we do debugging? */
+
+#define MTS_DO_DEBUG
+
+
+/* USB layer driver interface */
+
+static void *mts_usb_probe(struct usb_device *dev, unsigned int interface);
+static void mts_usb_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver mts_usb_driver = {
+	"microtek",
+	mts_usb_probe,
+	mts_usb_disconnect,
+	{ NULL, NULL } /* we need no fops. let gcc fill it */
+};
+
+
+/* Internal driver stuff */
+
+#define MTS_VERSION	"0.2.1"
+#define MTS_NAME	"microtek usb (rev " MTS_VERSION "): "
+
+#define MTS_WARNING(x...) \
+	printk( KERN_WARNING MTS_NAME ## x )
+#define MTS_ERROR(x...) \
+	printk( KERN_ERR MTS_NAME ## x )
+#define MTS_INT_ERROR(x...) \
+	MTS_ERROR( ## x )
+#define MTS_MESSAGE(x...) \
+	printk( KERN_INFO MTS_NAME ## x )
+
+#if defined MTS_DO_DEBUG
+
+#define MTS_DEBUG(x...) \
+	printk( KERN_DEBUG MTS_NAME ## x )
+
+#define MTS_DEBUG_GOT_HERE() \
+	MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
+#define MTS_DEBUG_INT() \
+	do { MTS_DEBUG_GOT_HERE(); \
+	     MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \
+	     MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
+             mts_debug_dump(context->instance);\
+	   } while(0)
+#else
+
+#define MTS_NUL_STATEMENT do { } while(0)
+
+#define MTS_DEBUG(x...)	MTS_NUL_STATEMENT
+#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
+#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
+
+#endif
+	
+
+
+#define MTS_INT_INIT()\
+	do {\
+	context = (struct mts_transfer_context*)transfer->context; \
+	if (atomic_read(&context->do_abort)) {\
+		mts_transfer_cleanup(transfer);\
+		return;\
+	}\
+	MTS_DEBUG_INT();\
+	} while (0)
+
+static inline void mts_debug_dump(struct mts_desc* desc) {
+	MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n",
+		  (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
+		  (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
+		);
+	MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
+		  usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
+		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
+		);
+}
+
+
+static inline void mts_show_command(Scsi_Cmnd *srb)
+{
+	char *what = NULL;
+
+	switch (srb->cmnd[0]) {
+	case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+	case REZERO_UNIT: what = "REZERO_UNIT"; break;
+	case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+	case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+	case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+	case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+	case READ_6: what = "READ_6"; break;
+	case WRITE_6: what = "WRITE_6"; break;
+	case SEEK_6: what = "SEEK_6"; break;
+	case READ_REVERSE: what = "READ_REVERSE"; break;
+	case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+	case SPACE: what = "SPACE"; break;
+	case INQUIRY: what = "INQUIRY"; break;
+	case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+	case MODE_SELECT: what = "MODE_SELECT"; break;
+	case RESERVE: what = "RESERVE"; break;
+	case RELEASE: what = "RELEASE"; break;
+	case COPY: what = "COPY"; break;
+	case ERASE: what = "ERASE"; break;
+	case MODE_SENSE: what = "MODE_SENSE"; break;
+	case START_STOP: what = "START_STOP"; break;
+	case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+	case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+	case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+	case SET_WINDOW: what = "SET_WINDOW"; break;
+	case READ_CAPACITY: what = "READ_CAPACITY"; break;
+	case READ_10: what = "READ_10"; break;
+	case WRITE_10: what = "WRITE_10"; break;
+	case SEEK_10: what = "SEEK_10"; break;
+	case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+	case VERIFY: what = "VERIFY"; break;
+	case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+	case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+	case SEARCH_LOW: what = "SEARCH_LOW"; break;
+	case SET_LIMITS: what = "SET_LIMITS"; break;
+	case READ_POSITION: what = "READ_POSITION"; break;
+	case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+	case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+	case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+	case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+	case COMPARE: what = "COMPARE"; break;
+	case COPY_VERIFY: what = "COPY_VERIFY"; break;
+	case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+	case READ_BUFFER: what = "READ_BUFFER"; break;
+	case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+	case READ_LONG: what = "READ_LONG"; break;
+	case WRITE_LONG: what = "WRITE_LONG"; break;
+	case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+	case WRITE_SAME: what = "WRITE_SAME"; break;
+	case READ_TOC: what = "READ_TOC"; break;
+	case LOG_SELECT: what = "LOG_SELECT"; break;
+	case LOG_SENSE: what = "LOG_SENSE"; break;
+	case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+	case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+	case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+	case READ_12: what = "READ_12"; break;
+	case WRITE_12: what = "WRITE_12"; break;
+	case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+	case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+	case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+	case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+	case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+	case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+	case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+	default:
+		MTS_DEBUG("can't decode command\n");
+		goto out;
+		break;
+	}
+	MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
+
+ out:
+	MTS_DEBUG( "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	       srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], 
+	       srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
+
+static inline int mts_is_aborting(struct mts_desc* desc) {
+	return (atomic_read(&desc->context.do_abort));
+}
+
+static inline void mts_request_abort(struct mts_desc* desc) {
+	MTS_DEBUG_GOT_HERE();
+	mts_debug_dump(desc);
+	atomic_set(&desc->context.do_abort,1);
+}
+
+static inline void mts_urb_abort(struct mts_desc* desc) {
+	MTS_DEBUG_GOT_HERE();
+	mts_debug_dump(desc);
+	if ( desc->urb.status == USB_ST_URB_PENDING ) {
+		usb_unlink_urb( &desc->urb );
+	}
+}
+
+static inline void mts_wait_abort(struct mts_desc* desc)
+{
+	mts_request_abort(desc);
+
+	while( !atomic_read(&desc->lock.count) ) {
+/* Is there a function to check if the semaphore is locked? */
+		schedule_timeout( MTS_ABORT_TIMEOUT ); 
+		MTS_DEBUG_GOT_HERE();
+		mts_urb_abort(desc);
+	}
+
+}
+
+
+static struct mts_desc * mts_list; /* list of active scanners */
+struct semaphore mts_list_semaphore;
+
+/* Internal list operations */
+
+static
+void mts_remove_nolock( struct mts_desc* to_remove )
+{
+	MTS_DEBUG( "removing 0x%x from list\n",
+		   (int)to_remove );
+
+	mts_wait_abort(to_remove);
+	
+	down( &to_remove->lock );
+
+	MTS_DEBUG_GOT_HERE();
+	
+	if ( to_remove != mts_list ) {
+		MTS_DEBUG_GOT_HERE();
+		if (to_remove->prev && to_remove->next)
+			to_remove->prev->next = to_remove->next;
+	} else {
+		MTS_DEBUG_GOT_HERE();
+		mts_list = to_remove->next;
+		if (mts_list) {
+			MTS_DEBUG_GOT_HERE();
+			mts_list->prev = 0;
+		}
+	}
+		
+	if ( to_remove->next ) {
+		MTS_DEBUG_GOT_HERE();
+		to_remove->next->prev = to_remove->prev;
+	}
+
+	MTS_DEBUG_GOT_HERE();
+	scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl));
+	
+	kfree( to_remove );
+}
+
+static
+void mts_add_nolock( struct mts_desc* to_add )
+{
+	MTS_DEBUG( "adding 0x%x to list\n", (int)to_add );
+	
+	to_add->prev = 0;
+	to_add->next = mts_list;
+	if ( mts_list ) {
+		mts_list->prev = to_add;
+	}
+			
+	mts_list = to_add;
+}
+		
+
+
+
+/* SCSI driver interface */
+
+/* scsi related functions - dummies for now mostly */
+
+static int mts_scsi_release(struct Scsi_Host *psh)
+{
+	MTS_DEBUG_GOT_HERE();
+	
+	return 0;
+}
+
+static int mts_scsi_abort (Scsi_Cmnd *srb)
+/* interrupt context (!) */
+{
+	struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+	MTS_DEBUG_GOT_HERE();
+	
+	mts_request_abort(desc);
+	mts_urb_abort(desc);
+	
+	return SCSI_ABORT_PENDING;
+}
+
+static int mts_scsi_host_reset (Scsi_Cmnd *srb)
+{
+	struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+	MTS_DEBUG_GOT_HERE();
+	mts_debug_dump(desc);
+
+	usb_reset_device(desc->usb_dev);
+	return 0;  /* RANT why here 0 and not SUCCESS */
+}
+
+/* the core of the scsi part */
+
+/* faking a detection - which can't fail :-) */
+
+static int mts_scsi_detect (struct SHT * sht)
+{
+	/* Whole function stolen from usb-storage */
+	
+	struct mts_desc * desc = (struct mts_desc *)sht->proc_dir;
+	/* What a hideous hack! */
+	  
+	char local_name[48];
+
+	MTS_DEBUG_GOT_HERE();
+
+	/* set up the name of our subdirectory under /proc/scsi/ */
+	sprintf(local_name, "microtek-%d", desc->host_number);
+	sht->name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+	/* FIXME: where is this freed ? */
+
+	if (!sht->name) {
+		MTS_ERROR( "unable to allocate memory for proc interface!!\n" );
+		return 0;
+	}
+	
+	strcpy((char *)sht->name, local_name);
+
+ 	sht->proc_dir = NULL;
+
+	/* In host->hostdata we store a pointer to desc */
+	desc->host = scsi_register(sht, sizeof(desc));
+	desc->host->hostdata[0] = (unsigned long)desc;
+/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ 
+
+	return 1;
+}
+
+
+
+/* Main entrypoint: SCSI commands are dispatched to here */
+
+
+
+static
+int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
+
+static void mts_transfer_cleanup( struct urb *transfer );
+
+
+inline static
+void mts_int_submit_urb (struct urb* transfer,
+			int pipe,
+			void* data,
+			unsigned length,
+			mts_usb_urb_callback callback )
+/* Interrupt context! */
+
+/* Holding transfer->context->lock! */
+{
+	int res;
+	struct mts_transfer_context* context;
+	
+	MTS_INT_INIT();
+	
+	FILL_BULK_URB(transfer,
+		      context->instance->usb_dev,
+		      pipe,
+		      data,
+		      length,
+		      callback,
+		      context
+		);
+
+/*	transfer->transfer_flags = USB_DISABLE_SPD;*/
+	transfer->transfer_flags = USB_ASYNC_UNLINK;
+	transfer->status = 0;
+	transfer->timeout = 100;
+
+	res = usb_submit_urb( transfer );
+	if ( res ) {
+		MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
+		context->srb->result = DID_ERROR << 16;
+		context->state = mts_con_error;
+		mts_transfer_cleanup(transfer);
+	}
+	return;
+}
+
+
+static void mts_transfer_cleanup( struct urb *transfer )
+/* Interrupt context! */
+{
+	struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context;
+	
+	up( &context->instance->lock );
+	if ( context->final_callback )
+		context->final_callback(context->srb);
+
+}
+
+static void mts_transfer_done( struct urb *transfer )
+{
+	struct mts_transfer_context* context;
+
+	MTS_INT_INIT();
+
+	context->srb->result &= MTS_MAX_CHUNK_MASK;
+	context->srb->result |= (unsigned)context->status<<1;
+
+	mts_transfer_cleanup(transfer);
+
+	return;
+}
+
+
+static void mts_get_status( struct urb *transfer )
+/* Interrupt context! */
+{
+	struct mts_transfer_context* context;
+
+	MTS_INT_INIT();
+
+	context->state = mts_con_status;
+	
+	mts_int_submit_urb(transfer,
+			   usb_rcvbulkpipe(context->instance->usb_dev,
+					   context->instance->ep_response),
+			   &context->status,
+			   1,
+			   mts_transfer_done );
+	
+	
+	return;
+}
+
+static void mts_data_done( struct urb* transfer )
+/* Interrupt context! */
+{
+	struct mts_transfer_context* context;
+
+	MTS_INT_INIT();
+
+	if ( context->data_length != transfer->actual_length ) {
+#if 0
+		context->srb->resid = context->data_length - transfer->actual_length;
+#else
+		printk(KERN_WARNING "microtek.c: Don't know how to handle resid on 2.2 kernels\n");
+#endif
+	} else if ( transfer->status ) {
+		context->srb->result = DID_ERROR<<16;
+	}
+
+	mts_get_status(transfer);
+
+	return;
+}
+
+
+static void mts_command_done( struct urb *transfer )
+/* Interrupt context! */
+{
+	struct mts_transfer_context* context;
+
+	MTS_INT_INIT();
+
+	if ( transfer->status ) {
+		context->srb->result = DID_ERROR<<16;
+		mts_transfer_cleanup(transfer);
+		
+		return;
+	}
+	
+	if ( context->data ) {
+		context->state = mts_con_data;
+		mts_int_submit_urb(transfer,
+				   context->data_pipe,
+				   context->data,
+				   context->data_length,
+				   mts_data_done);
+	} else mts_get_status(transfer);
+	
+	return;
+}
+
+
+
+	static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
+	static const u8 mts_read_image_sig_len = 4;
+	static const unsigned char mts_direction[256/8] = {
+		0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, 
+		0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+
+#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
+
+static void
+mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
+{
+
+	int pipe;
+
+
+	MTS_DEBUG_GOT_HERE();
+
+	desc->context.instance = desc;
+	desc->context.srb = srb;
+	desc->context.state = mts_con_command;
+	atomic_set(&desc->context.do_abort,0);
+	
+	if ( !srb->bufflen ){
+		desc->context.data = 0;
+		desc->context.data_length = 0;
+		return;
+	} else {
+		desc->context.data = srb->buffer;
+		desc->context.data_length = srb->bufflen;
+	}
+	
+	/* can't rely on srb->sc_data_direction */
+
+	/* Brutally ripped from usb-storage */
+
+	if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
+) { 		pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
+		MTS_DEBUG( "transfering from desc->ep_image == %d\n",
+			   (int)desc->ep_image );
+	} else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
+			pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
+			MTS_DEBUG( "transfering from desc->ep_response == %d\n",
+				   (int)desc->ep_response);
+	} else {
+		MTS_DEBUG("transfering to desc->ep_out == %d\n",
+			  (int)desc->ep_out);
+		pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
+	}
+	desc->context.data_pipe = pipe;
+}
+
+
+static
+int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
+{
+	struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+	int err = 0;
+	int res;
+
+	MTS_DEBUG_GOT_HERE();
+	mts_show_command(srb);
+	mts_debug_dump(desc);
+	
+	if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+
+		MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
+		
+		MTS_DEBUG("this device doesn't exist\n");
+
+		srb->result = DID_BAD_TARGET << 16;
+
+		if(callback)
+			callback(srb);
+
+		goto out;
+	}	
+
+	down(&desc->lock);
+
+	MTS_DEBUG_GOT_HERE();
+	mts_show_command(srb);
+	
+
+	FILL_BULK_URB(&desc->urb,
+		      desc->usb_dev,
+		      usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+		      srb->cmnd,
+		      srb->cmd_len,
+		      mts_command_done,
+		      &desc->context
+		      );
+	
+		
+	mts_build_transfer_context( srb, desc );
+	desc->context.final_callback = callback;
+	desc->urb.timeout = 100;
+	desc->urb.transfer_flags = USB_ASYNC_UNLINK;
+	
+/*	desc->urb.transfer_flags = USB_DISABLE_SPD;*/
+
+	res=usb_submit_urb(&desc->urb);
+	
+	if(res){
+		MTS_ERROR("error %d submitting URB\n",(int)res);
+		srb->result = DID_ERROR << 16;
+
+		if(callback)
+			callback(srb);
+
+		goto out;
+	}	
+	
+	MTS_DEBUG_GOT_HERE();
+
+ out:
+	return err;
+}
+/*
+ * this defines our 'host'
+ */
+
+/* NOTE: This is taken from usb-storage, should be right. */
+
+
+static Scsi_Host_Template mts_scsi_host_template = {
+	name:           "microtek",
+	detect:		mts_scsi_detect,
+	release:	mts_scsi_release,
+	command:	0,
+	queuecommand:	mts_scsi_queuecommand,
+
+	eh_abort_handler:	mts_scsi_abort,
+	eh_device_reset_handler:0,
+	eh_bus_reset_handler:	0,
+	eh_host_reset_handler:	mts_scsi_host_reset,
+
+	can_queue:		1,
+	this_id:		-1,
+	cmd_per_lun:		1,
+	present:		0,
+	unchecked_isa_dma:	FALSE,
+	use_clustering:		FALSE,
+	use_new_eh_code:	TRUE,
+	emulated:		TRUE
+};
+
+
+/* USB layer driver interface implementation */
+
+static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+	struct mts_desc* to_remove = (struct mts_desc*)ptr;
+	
+	MTS_DEBUG_GOT_HERE();
+
+	/* leave the list - lock it */
+	down(&mts_list_semaphore);
+
+	mts_remove_nolock(to_remove);
+
+	up(&mts_list_semaphore);
+}
+
+struct vendor_product
+{
+	u16 idVendor;
+	u16 idProduct;
+	char* name;
+	enum
+	{
+		mts_sup_unknown=0,
+		mts_sup_alpha,
+		mts_sup_full
+	}
+	support_status;
+} ;
+
+
+/* These are taken from the msmUSB.inf file on the Windows driver CD */
+const static struct vendor_product mts_supported_products[] =
+{
+	{
+		0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown
+	},
+	{
+		0x5da, 0x94,"Phantom 336CX",mts_sup_unknown
+	},
+	{
+		0x5da, 0x99,"Scanmaker X6",mts_sup_alpha
+	},
+	{
+		0x5da, 0x9a,"Phantom C6",mts_sup_unknown
+	},
+	{
+		0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown
+	},
+	{
+		0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown
+	},
+	{
+		0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown
+	},
+	{
+		0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown
+	}
+}
+;
+const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ];
+		/* Must never be derefed, points to one after last entry */
+
+
+static void * mts_usb_probe (struct usb_device *dev, unsigned int interface)
+{
+	int i;
+	int result;
+	int ep_out = -1;
+	int ep_in_set[3]; /* this will break if we have more than three endpoints
+			   which is why we check */
+	int *ep_in_current = ep_in_set;
+	
+	struct mts_desc * new_desc;
+	struct vendor_product const* p;
+
+	/* the altsettting 0 on the interface we're probing */
+	struct usb_interface_descriptor *altsetting;
+
+	MTS_DEBUG_GOT_HERE();
+	MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
+	
+	MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
+		   (int)dev->descriptor.idProduct,
+		   (int)dev->descriptor.idVendor );
+
+	MTS_DEBUG_GOT_HERE();
+	
+	/* checking IDs */
+	for( p = mts_supported_products; p != mts_last_product; p++ )
+		if ( dev->descriptor.idVendor == p->idVendor &&
+		     dev->descriptor.idProduct == p->idProduct )
+			goto is_supported;
+		else
+			MTS_DEBUG( "doesn't appear to be model %s\n", p->name );
+
+	MTS_DEBUG( "returning NULL: unsupported\n" );
+	
+	return NULL;
+	
+ is_supported:
+
+	MTS_DEBUG_GOT_HERE();
+
+	MTS_DEBUG( "found model %s\n", p->name );
+	if ( p->support_status != mts_sup_full )
+		MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
+			     p->name );
+	
+	/* the altsettting 0 on the interface we're probing */
+	altsetting =
+		&(dev->actconfig->interface[interface].altsetting[0]);	
+
+	
+	/* Check if the config is sane */
+
+	if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
+		MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
+			     (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints );
+		return NULL;
+	}
+
+	for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
+		if ((altsetting->endpoint[i].bmAttributes & 
+		     USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
+			
+			MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
+			     (int)altsetting->endpoint[i].bEndpointAddress );
+		} else {
+			if (altsetting->endpoint[i].bEndpointAddress & 
+			    USB_DIR_IN)
+				*ep_in_current++
+					= altsetting->endpoint[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			else {
+				if ( ep_out != -1 ) {
+					MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
+					return NULL;
+				}
+		
+				ep_out = altsetting->endpoint[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			}
+		}
+		
+	}
+	
+
+	if ( ep_out == -1 ) {
+		MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
+		return NULL;
+	}
+	
+	
+	/* I don't understand the following fully (it's from usb-storage) -- John */
+
+	/* set the interface -- STALL is an acceptable response here */
+	result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+	
+	MTS_DEBUG("usb_set_interface returned %d.\n",result);
+	switch( result )
+	{
+	case 0: /* no error */
+		break;
+		
+	case -EPIPE:
+		usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+		MTS_DEBUG( "clearing clearing stall on control interface\n" );
+		break;
+		
+	default:
+		MTS_DEBUG( "unknown error %d from usb_set_interface\n",
+			(int)result );
+ 		return NULL;
+	}
+	
+	
+	/* allocating a new descriptor */
+	new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
+	if (new_desc == NULL)
+	{
+		MTS_ERROR("couldn't allocate scanner desc, bailing out!\n");
+		return NULL;
+	}
+
+	/* As done by usb_alloc_urb */
+	memset( new_desc, 0, sizeof(*new_desc) );
+	spin_lock_init(&new_desc->urb.lock);
+	
+		
+	/* initialising that descriptor */
+	new_desc->usb_dev = dev;
+	new_desc->interface = interface;
+
+	init_MUTEX(&new_desc->lock);
+
+	if(mts_list){
+		new_desc->host_number = mts_list->host_number+1;
+	} else {
+		new_desc->host_number = 0;
+	}
+	
+	/* endpoints */
+	
+	new_desc->ep_out = ep_out;
+	new_desc->ep_response = ep_in_set[0];
+	new_desc->ep_image = ep_in_set[1];
+	
+
+	if ( new_desc->ep_out != MTS_EP_OUT )
+		MTS_WARNING( "will this work? Command EP is not usually %d\n",
+			     (int)new_desc->ep_out );
+
+	if ( new_desc->ep_response != MTS_EP_RESPONSE )
+		MTS_WARNING( "will this work? Response EP is not usually %d\n",
+			     (int)new_desc->ep_response );
+
+	if ( new_desc->ep_image != MTS_EP_IMAGE )
+		MTS_WARNING( "will this work? Image data EP is not usually %d\n",
+			     (int)new_desc->ep_image );
+
+	
+	/* Initialize the host template based on the default one */
+	memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template)); 	
+	/* HACK from usb-storage - this is needed for scsi detection */
+	(struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
+	
+	MTS_DEBUG("registering SCSI module\n");
+	
+	new_desc->ctempl.module = THIS_MODULE;
+	result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl));	
+	/* Will get hit back in microtek_detect by this func */
+	if ( result )
+	{
+		MTS_ERROR( "error %d from scsi_register_module! Help!\n",
+			   (int)result );
+
+		/* FIXME: need more cleanup? */
+		kfree( new_desc );
+		return NULL;
+	}
+	MTS_DEBUG_GOT_HERE();
+
+	/* FIXME: the bomb is armed, must the host be registered under lock ? */
+	/* join the list - lock it */
+	down(&mts_list_semaphore);
+
+	mts_add_nolock( new_desc );
+	
+	up(&mts_list_semaphore);
+	
+	
+	MTS_DEBUG("completed probe and exiting happily\n");
+	
+	return (void *)new_desc;
+}
+
+
+
+/* get us noticed by the rest of the kernel */
+
+int __init microtek_drv_init(void)
+{
+	int result;
+	
+	MTS_DEBUG_GOT_HERE();
+	init_MUTEX(&mts_list_semaphore);
+
+	if ((result = usb_register(&mts_usb_driver)) < 0) {
+		MTS_DEBUG("usb_register returned %d\n", result );
+		return -1;
+	} else {
+		MTS_DEBUG("driver registered.\n");
+	}
+	
+	return 0;
+}
+
+void __exit microtek_drv_exit(void)
+{
+	struct mts_desc* next;
+	
+	MTS_DEBUG_GOT_HERE();
+
+	usb_deregister(&mts_usb_driver);
+
+	down(&mts_list_semaphore);
+
+	while (mts_list) {
+		/* keep track of where the next one is */
+		next = mts_list->next;
+
+		mts_remove_nolock( mts_list );
+
+		/* advance the list pointer */
+		mts_list = next;
+	}
+	
+	up(&mts_list_semaphore);
+}
+
+module_init(microtek_drv_init);
+module_exit(microtek_drv_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/microtek.h linux.18p17/drivers/usb/microtek.h
--- linux.vanilla/drivers/usb/microtek.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/microtek.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,73 @@
+ /*
+ * Driver for Microtek Scanmaker X6 USB scanner and possibly others.
+ * 
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * See microtek.c for history
+ *
+ */
+
+typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
+typedef void (*mts_usb_urb_callback) (struct urb *);
+
+
+struct mts_transfer_context
+{
+	struct mts_desc* instance;
+	mts_scsi_cmnd_callback final_callback;
+	Scsi_Cmnd *srb;
+	
+	void* data;
+	unsigned data_length;
+	int data_pipe;
+
+	enum {
+		mts_con_none,
+		mts_con_command,
+		mts_con_data,
+		mts_con_status,
+		mts_con_error,
+		mts_con_done
+	}
+	state;
+
+	atomic_t do_abort; /* when != 0 URB completion routines will
+			      return straightaway */
+	
+	u8 status; /* status returned from ep_response after command completion */
+};
+
+
+struct mts_desc {
+	struct mts_desc *next;
+	struct mts_desc *prev;
+
+	struct usb_device *usb_dev;
+	
+	int interface;
+
+	/* Endpoint addresses */
+	u8 ep_out;
+	u8 ep_response;
+	u8 ep_image;
+	
+	struct Scsi_Host * host;
+	Scsi_Host_Template ctempl;
+	int host_number;
+	
+	struct semaphore lock;
+
+	struct urb urb;
+	struct mts_transfer_context context;
+};
+
+
+#define MTS_EP_OUT	0x1
+#define MTS_EP_RESPONSE	0x2
+#define MTS_EP_IMAGE	0x3
+#define MTS_EP_TOTAL	0x3
+
+#define MTS_MAX_CHUNK_MASK ~0x3fu 
+/*maximum amount the scanner will transmit at once */ 
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/mkmap linux.18p17/drivers/usb/mkmap
--- linux.vanilla/drivers/usb/mkmap	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/mkmap	Thu Jan  1 01:00:00 1970
@@ -1,83 +0,0 @@
-#!/usr/bin/perl
-
-($ME = $0) =~ s|.*/||;
-
-$file = "maps/serial.map";
-$line = 1;
-open(PC, $file) || die("$!");
-while(<PC>)
-{
-    if(/^\s*keycode\s+(\d+)\s*=\s*(\S+)/)
-    {
-	my($idx) = int($1);
-	my($sym) = $2;
-	if(defined($map{uc($sym)}))
-	{
-	    # print STDERR "$file:$line: warning: `$sym' redefined\n";
-	}
-	$map{uc($sym)} = $idx;
-    }
-    $line++;
-}
-close(PC);
-
-$file = "maps/fixup.map";
-$line = 1;
-open(FIXUP, $file) || die("$!");
-while(<FIXUP>)
-{
-    if(/^\s*keycode\s+(\d+)\s*=\s*/)
-    {
-	my($idx) = int($1);
-	for $sym (split(/\s+/, $'))
-        {
-	    $map{uc($sym)} = $idx;
-	}
-    }
-    $line++;
-}
-close(FIXUP);
-
-$file = "maps/usb.map";
-$line = 1;
-open(USB, $file) || die("$!");
-while(<USB>)
-{
-    if(/^\s*keycode\s+(\d+)\s*=\s*/)
-    {
-	my($idx) = int($1);
-	for $sym (split(/\s+/, $'))
-        {
-	    my($val) = $map{uc($sym)};
-	    $map[$idx] = $val;
-	    if(!defined($val))
-	    {
-		print STDERR "$file:$line: warning: `$sym' undefined\n";
-	    }
-	    else
-	    {
-		last;
-	    }
-	}
-    }
-    $line++;
-}
-close(USB);
-
-print "unsigned char usb_kbd_map[256] = \n{\n";
-for($x = 0; $x < 32; $x++)
-{
-    if($x && !($x % 2))
-    {
-	print "\n";
-    }
-    print "  ";
-    for($y = 0; $y < 8; $y++)
-    {
-	my($idx) = $x * 8 + $y;
-	print sprintf("  0x%02x,",
-		      int(defined($map[$idx]) ? $map[$idx]:0));
-    }
-    print "\n";
-}
-print "};\n";
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/mouse.c linux.18p17/drivers/usb/mouse.c
--- linux.vanilla/drivers/usb/mouse.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/mouse.c	Thu Jan  1 01:00:00 1970
@@ -1,293 +0,0 @@
-/*
- * USB HID boot protocol mouse support based on MS BusMouse driver, psaux 
- * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus.
- *
- * Brad Keryan 4/3/1999
- *
- * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it
- * correctly. Events are added together, not queued, to keep the rodent sober.
- *
- * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events.
- * This is wrong: when an application (like X or gpm) reads the mouse device,
- * it wants to find out the mouse's current position, not its recent history.
- * The button thing turned out to be UHCI not flipping data toggle, so half the
- * packets were thrown out.
- *
- * version 0.01: Switched over to busmouse protocol, and changed the minor
- * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now, 
- * but it still doesn't generate button events unless you move the mouse.
- *
- * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I 
- * know that's not very nice). Moving in the X and Y axes works. Buttons don't
- * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease 
- * combos when you hold down a button and drag the mouse around. Probably has 
- * some additional bugs on an SMP machine.
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/malloc.h>
-
-#include <asm/spinlock.h>
-
-#include "usb.h"
-
-#define USB_MOUSE_MINOR 32
-
-struct mouse_state {
-	unsigned char buttons; /* current button state */
-	long dx; /* dx, dy, dz are change since last read */
-	long dy; 
-	long dz;
-	int present; /* this mouse is plugged in */
-	int active; /* someone is has this mouse's device open */
-	int ready; /* the mouse has changed state since the last read */
-	struct wait_queue *wait; /* for polling */
-	struct fasync_struct *fasync;
-	/* later, add a list here to support multiple mice */
-	/* but we will also need a list of file pointers to identify it */
-};
-
-static struct mouse_state static_mouse_state;
-
-spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;
-
-static int mouse_irq(int state, void *__buffer, void *dev_id)
-{
-	signed char *data = __buffer;
-	/* finding the mouse is easy when there's only one */
-	struct mouse_state *mouse = &static_mouse_state; 
-
-	/* if a mouse moves with no one listening, do we care? no */
-	if(!mouse->active)
-		return 1;
-
-	/* if the USB mouse sends an interrupt, then something noteworthy
-	   must have happened */
-	mouse->buttons = data[0] & 0x07;
-	mouse->dx += data[1]; /* data[] is signed, so this works */
-	mouse->dy -= data[2]; /* y-axis is reversed */
-	mouse->dz += data[3];
-	mouse->ready = 1;
-
-	add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + 
-				     (mouse->dy << 8) + mouse->dx);
-
-	wake_up_interruptible(&mouse->wait);
-	if (mouse->fasync)
-		kill_fasync(mouse->fasync, SIGIO);
-
-	return 1;
-}
-
-static int fasync_mouse(int fd, struct file *filp, int on)
-{
-	int retval;
-	struct mouse_state *mouse = &static_mouse_state;
-
-	retval = fasync_helper(fd, filp, on, &mouse->fasync);
-	if (retval < 0)
-		return retval;
-	return 0;
-}
-
-static int release_mouse(struct inode * inode, struct file * file)
-{
-	struct mouse_state *mouse = &static_mouse_state;
-
-	fasync_mouse(-1, file, 0);
-	if (--mouse->active)
-		return 0;
-	return 0;
-}
-
-static int open_mouse(struct inode * inode, struct file * file)
-{
-	struct mouse_state *mouse = &static_mouse_state;
-
-	if (!mouse->present)
-		return -EINVAL;
-	if (mouse->active++)
-		return 0;
-	/* flush state */
-	mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0;
-	return 0;
-}
-
-static ssize_t write_mouse(struct file * file,
-       const char * buffer, size_t count, loff_t *ppos)
-{
-	return -EINVAL;
-}
-
-/*
- * Look like a PS/2 mouse, please..
- *
- * The PS/2 protocol is fairly strange, but
- * oh, well, it's at least common..
- */
-static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos)
-{
-	int retval = 0;
-	static int state = 0;
-	struct mouse_state *mouse = &static_mouse_state;
-
-	if (count) {
-		mouse->ready = 0;
-		switch (state) {
-		case 0: { /* buttons and sign */
-			int buttons = mouse->buttons;
-			mouse->buttons = 0;
-			if (mouse->dx < 0)
-				buttons |= 0x10;
-			if (mouse->dy < 0)
-				buttons |= 0x20;
-			put_user(buttons, buffer);
-			buffer++;
-			retval++;
-			state = 1;
-			if (!--count)
-				break;
-		}
-		case 1: { /* dx */
-			int dx = mouse->dx;
-			mouse->dx = 0;
-			put_user(dx, buffer);
-			buffer++;
-			retval++;
-			state = 2;
-			if (!--count)
-				break;
-		}
-		case 2:	{ /* dy */
-			int dy = mouse->dy;
-			mouse->dy = 0;
-			put_user(dy, buffer);
-			buffer++;
-			retval++;
-			state = 0;
-		}
-		break;
-		}
-	}
-	return retval;
-}
-
-static unsigned int mouse_poll(struct file *file, poll_table * wait)
-{
-	struct mouse_state *mouse = &static_mouse_state;
-
-	poll_wait(file, &mouse->wait, wait);
-	if (mouse->ready)
-		return POLLIN | POLLRDNORM;
-	return 0;
-}
-
-struct file_operations usb_mouse_fops = {
-	NULL,		/* mouse_seek */
-	read_mouse,
-	write_mouse,
-	NULL, 		/* mouse_readdir */
-	mouse_poll, 	/* mouse_poll */
-	NULL, 		/* mouse_ioctl */
-	NULL,		/* mouse_mmap */
-	open_mouse,
-	NULL,		/* flush */
-	release_mouse,
-	NULL,
-	fasync_mouse,
-};
-
-static struct miscdevice usb_mouse = {
-	USB_MOUSE_MINOR, "USB mouse", &usb_mouse_fops
-};
-
-static int mouse_probe(struct usb_device *dev)
-{
-	struct usb_interface_descriptor *interface;
-	struct usb_endpoint_descriptor *endpoint;
-	struct mouse_state *mouse = &static_mouse_state;
-
-	/* We don't handle multi-config mice */
-	if (dev->descriptor.bNumConfigurations != 1)
-		return -1;
-
-	/* We don't handle multi-interface mice */
-	if (dev->config[0].bNumInterfaces != 1)
-		return -1;
-
-	/* Is it a mouse interface? */
-	interface = &dev->config[0].interface[0];
-	if (interface->bInterfaceClass != 3)
-		return -1;
-	if (interface->bInterfaceSubClass != 1)
-		return -1;
-	if (interface->bInterfaceProtocol != 2)
-		return -1;
-
-	/* Multiple endpoints? What kind of mutant ninja-mouse is this? */
-	if (interface->bNumEndpoints != 1)
-		return -1;
-
-	endpoint = &interface->endpoint[0];
-
-	/* Output endpoint? Curiousier and curiousier.. */
-	if (!(endpoint->bEndpointAddress & 0x80))
-		return -1;
-
-	/* If it's not an interrupt endpoint, we'd better punt! */
-	if ((endpoint->bmAttributes & 3) != 3)
-		return -1;
-
-	printk("USB mouse found\n");
-
-	usb_set_configuration(dev, dev->config[0].bConfigurationValue);
-
-	usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL);
-
-	mouse->present = 1;
-	return 0;
-}
-
-static void mouse_disconnect(struct usb_device *dev)
-{
-	struct mouse_state *mouse = &static_mouse_state;
-
-	/* this might need work */
-	mouse->present = 0;
-}
-
-static struct usb_driver mouse_driver = {
-	"mouse",
-	mouse_probe,
-	mouse_disconnect,
-	{ NULL, NULL }
-};
-
-int usb_mouse_init(void)
-{
-	struct mouse_state *mouse = &static_mouse_state;
-
-	misc_register(&usb_mouse);
-
-	mouse->present = mouse->active = 0;
-	mouse->wait = NULL;
-	mouse->fasync = NULL;
-
-	usb_register(&mouse_driver);
-	printk(KERN_INFO "USB HID boot protocol mouse registered.\n");
-	return 0;
-}
-
-void usb_mouse_cleanup(void)
-{
-	/* this, too, probably needs work */
-	usb_deregister(&mouse_driver);
-	misc_deregister(&usb_mouse);
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/mousedev.c linux.18p17/drivers/usb/mousedev.c
--- linux.vanilla/drivers/usb/mousedev.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/mousedev.c	Fri Sep 22 14:56:09 2000
@@ -0,0 +1,489 @@
+/*
+ * $Id: mousedev.c,v 1.15 2000/08/14 21:05:26 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  Input driver to PS/2 or ImPS/2 device driver module.
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#define MOUSEDEV_MINOR_BASE 	32
+#define MOUSEDEV_MINORS		32
+#define MOUSEDEV_MIX		31
+
+#include <linux/malloc.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+#include <linux/kcomp.h>
+
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
+#endif
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
+#endif
+
+struct mousedev {
+	int exist;
+	int open;
+	int minor;
+	wait_queue_head_t wait;
+	struct mousedev_list *list;
+	struct input_handle handle;
+	devfs_handle_t devfs;
+};
+
+struct mousedev_list {
+	struct fasync_struct *fasync;
+	struct mousedev *mousedev;
+	struct mousedev_list *next;
+	int dx, dy, dz, oldx, oldy;
+	signed char ps2[6];
+	unsigned long buttons;
+	unsigned char ready, buffer, bufsiz;
+	unsigned char mode, genseq, impseq;
+};
+
+#define MOUSEDEV_GENIUS_LEN	5
+#define MOUSEDEV_IMPS_LEN	6
+
+static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 };
+static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
+
+static struct input_handler mousedev_handler;
+
+static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
+static struct mousedev mousedev_mix;
+
+static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL };
+	struct mousedev **mousedev = mousedevs;
+	struct mousedev_list *list;
+	int index, size;
+
+	while (*mousedev) {
+		list = (*mousedev)->list;
+		while (list) {
+			switch (type) {
+				case EV_ABS:
+					if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+						break;
+					switch (code) {
+						case ABS_X:	
+							size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X];
+							list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size;
+							list->oldx += list->dx * size;
+							break;
+						case ABS_Y:
+							size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
+							list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size;
+							list->oldy -= list->dy * size;
+							break;
+					}
+					break;
+
+				case EV_REL:
+					switch (code) {
+						case REL_X:	list->dx += value; break;
+						case REL_Y:	list->dy -= value; break;
+						case REL_WHEEL:	if (list->mode) list->dz -= value; break;
+					}
+					break;
+
+				case EV_KEY:
+					switch (code) {
+						case BTN_0:
+						case BTN_TOUCH:
+						case BTN_LEFT:   index = 0; break;
+						case BTN_4:
+						case BTN_EXTRA:  if (list->mode > 1) { index = 4; break; }
+						case BTN_STYLUS:
+						case BTN_1:
+						case BTN_RIGHT:  index = 1; break;
+						case BTN_3:
+						case BTN_SIDE:   if (list->mode > 1) { index = 3; break; }
+						case BTN_2:
+						case BTN_STYLUS2:
+						case BTN_MIDDLE: index = 2; break;	
+						default: return;
+					}
+					switch (value) {
+						case 0: clear_bit(index, &list->buttons); break;
+						case 1: set_bit(index, &list->buttons); break;
+						case 2: return;
+					}
+					break;
+			}
+					
+			list->ready = 1;
+
+			kill_fasync(list->fasync, SIGIO);
+
+			list = list->next;
+		}
+
+		wake_up_interruptible(&((*mousedev)->wait));
+		mousedev++;
+	}
+}
+
+static int mousedev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct mousedev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int mousedev_release(struct inode * inode, struct file * file)
+{
+	struct mousedev_list *list = file->private_data;
+	struct mousedev_list **listptr;
+
+	lock_kernel();
+	listptr = &list->mousedev->list;
+	mousedev_fasync(-1, file, 0);
+
+	while (*listptr && (*listptr != list))
+		listptr = &((*listptr)->next);
+	*listptr = (*listptr)->next;
+
+	if (!--list->mousedev->open) {
+		if (list->mousedev->minor == MOUSEDEV_MIX) {
+			struct input_handle *handle = mousedev_handler.handle;
+			while (handle) {
+				struct mousedev *mousedev = handle->private;
+				if (!mousedev->open) {
+					if (mousedev->exist) {
+						input_close_device(&mousedev->handle);
+					} else {
+						input_unregister_minor(mousedev->devfs);
+						mousedev_table[mousedev->minor] = NULL;
+						kfree(mousedev);
+					}
+				}
+				handle = handle->hnext;
+			}
+		} else {
+			if (!mousedev_mix.open) {
+				if (list->mousedev->exist) {
+					input_close_device(&list->mousedev->handle);
+				} else {
+					input_unregister_minor(list->mousedev->devfs);
+					mousedev_table[list->mousedev->minor] = NULL;
+					kfree(list->mousedev);
+				}
+			}
+		}
+	}
+	
+	kfree(list);
+	unlock_kernel();
+
+	return 0;
+}
+
+static int mousedev_open(struct inode * inode, struct file * file)
+{
+	struct mousedev_list *list;
+	int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE;
+
+	if (i > MOUSEDEV_MINORS || !mousedev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct mousedev_list));
+
+	list->mousedev = mousedev_table[i];
+	list->next = mousedev_table[i]->list;
+	mousedev_table[i]->list = list;
+	file->private_data = list;
+
+	if (!list->mousedev->open++) {
+		if (list->mousedev->minor == MOUSEDEV_MIX) {
+			struct input_handle *handle = mousedev_handler.handle;
+			while (handle) {
+				struct mousedev *mousedev = handle->private;
+				if (!mousedev->open)
+					if (mousedev->exist)	
+						input_open_device(handle);
+				handle = handle->hnext;
+			}
+		} else {
+			if (!mousedev_mix.open)
+				if (list->mousedev->exist)	
+					input_open_device(&list->mousedev->handle);
+		}
+	}
+
+	return 0;
+}
+
+static void mousedev_packet(struct mousedev_list *list, unsigned char off)
+{
+	list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07);
+	list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx));
+	list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
+	list->dx -= list->ps2[off + 1];
+	list->dy -= list->ps2[off + 2];
+	list->bufsiz = off + 3;
+
+	if (list->mode > 1)
+		list->ps2[off] |= ((list->buttons & 0x18) << 3);
+	
+	if (list->mode) {
+		list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz));
+		list->bufsiz++;
+		list->dz -= list->ps2[off + 3];
+	}
+	if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0;
+	list->buffer = list->bufsiz;
+}
+
+
+static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+	struct mousedev_list *list = file->private_data;
+	unsigned char c;
+	int i;
+
+	for (i = 0; i < count; i++) {
+
+		c = buffer[i];
+
+		if (c == mousedev_genius_seq[list->genseq]) {
+			if (++list->genseq == MOUSEDEV_GENIUS_LEN) {
+				list->genseq = 0;
+				list->ready = 1;
+				list->mode = 2;
+			}
+		} else list->genseq = 0;
+
+		if (c == mousedev_imps_seq[list->impseq]) {
+			if (++list->impseq == MOUSEDEV_IMPS_LEN) {
+				list->impseq = 0;
+				list->ready = 1;
+				list->mode = 1;
+			}
+		} else list->impseq = 0;
+
+		list->ps2[0] = 0xfa;
+		list->bufsiz = 1;
+
+		switch (c) {
+
+			case 0xeb: /* Poll */
+				mousedev_packet(list, 1);
+				break;
+
+			case 0xf2: /* Get ID */
+				list->ps2[1] = (list->mode == 1) ? 3 : 0;
+				list->bufsiz = 2;
+				break;
+
+			case 0xe9: /* Get info */
+				if (list->mode == 2) {
+					list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55;
+				} else {
+					list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
+				}
+				list->bufsiz = 4;
+				break;
+		}
+
+		list->buffer = list->bufsiz;
+	}
+
+	kill_fasync(list->fasync, SIGIO);
+
+	wake_up_interruptible(&list->mousedev->wait);
+		
+	return count;
+}
+
+static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct mousedev_list *list = file->private_data;
+	int retval = 0;
+
+	if (!list->ready && !list->buffer) {
+
+		add_wait_queue(&list->mousedev->wait, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+
+		while (!list->ready) {
+
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+
+			schedule();
+		}
+
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&list->mousedev->wait, &wait);
+	}
+
+	if (retval)
+		return retval;
+
+	if (!list->buffer)
+		mousedev_packet(list, 0);
+
+	if (count > list->buffer)
+		count = list->buffer;
+
+	if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count))
+		return -EFAULT;
+	
+	list->buffer -= count;
+
+	return count;	
+}
+
+/* No kernel lock - fine */
+static unsigned int mousedev_poll(struct file *file, poll_table *wait)
+{
+	struct mousedev_list *list = file->private_data;
+	poll_wait(file, &list->mousedev->wait, wait);
+	if (list->ready || list->buffer)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+struct file_operations mousedev_fops = {
+	read:		mousedev_read,
+	write:		mousedev_write,
+	poll:		mousedev_poll,
+	open:		mousedev_open,
+	release:	mousedev_release,
+	fasync:		mousedev_fasync,
+};
+
+static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev)
+{
+	struct mousedev *mousedev;
+	int minor = 0;
+
+	if (!test_bit(EV_KEY, dev->evbit) ||
+	   (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit)))
+		return NULL;
+
+	if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) &&
+	    (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit)))
+		return NULL;
+
+	for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
+	if (mousedev_table[minor]) {
+		printk(KERN_ERR "mousedev: no more free mousedev devices\n");
+		return NULL;
+	}
+
+	if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
+		return NULL;
+	memset(mousedev, 0, sizeof(struct mousedev));
+	init_waitqueue_head(&mousedev->wait);
+
+	mousedev->exist = 1;
+	mousedev->minor = minor;
+	mousedev_table[minor] = mousedev;
+
+	mousedev->handle.dev = dev;
+	mousedev->handle.handler = handler;
+	mousedev->handle.private = mousedev;
+
+	mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE);
+
+	if (mousedev_mix.open)
+		input_open_device(&mousedev->handle);
+
+	printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number);
+
+	return &mousedev->handle;
+}
+
+static void mousedev_disconnect(struct input_handle *handle)
+{
+	struct mousedev *mousedev = handle->private;
+
+	mousedev->exist = 0;
+
+	if (mousedev->open) {
+		input_close_device(handle);
+	} else {
+		if (mousedev_mix.open)
+			input_close_device(handle);
+		input_unregister_minor(mousedev->devfs);
+		mousedev_table[mousedev->minor] = NULL;
+		kfree(mousedev);
+	}
+}
+	
+static struct input_handler mousedev_handler = {
+	event:		mousedev_event,
+	connect:	mousedev_connect,
+	disconnect:	mousedev_disconnect,
+	fops:		&mousedev_fops,
+	minor:		MOUSEDEV_MINOR_BASE,
+};
+
+static int __init mousedev_init(void)
+{
+	input_register_handler(&mousedev_handler);
+
+	memset(&mousedev_mix, 0, sizeof(struct mousedev));
+	init_waitqueue_head(&mousedev_mix.wait);
+	mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
+	mousedev_mix.exist = 1;
+	mousedev_mix.minor = MOUSEDEV_MIX;
+	mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE);
+
+	printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
+
+	return 0;
+}
+
+static void __exit mousedev_exit(void)
+{
+	input_unregister_minor(mousedev_mix.devfs);
+	input_unregister_handler(&mousedev_handler);
+}
+
+module_init(mousedev_init);
+module_exit(mousedev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci-debug.c linux.18p17/drivers/usb/ohci-debug.c
--- linux.vanilla/drivers/usb/ohci-debug.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci-debug.c	Thu Jan  1 01:00:00 1970
@@ -1,178 +0,0 @@
-/*
- * OHCI debugging code.  It's gross.
- *
- * (C) Copyright 1999 Gregory P. Smith
- */
-
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#include "ohci.h"
-
-void show_ohci_status(struct ohci *ohci)
-{
-	struct ohci_regs regs;
-	int i;
-
-	regs.revision = readl(&ohci->regs->revision);
-	regs.control = readl(&ohci->regs->control);
-	regs.cmdstatus = readl(&ohci->regs->cmdstatus);
-	regs.intrstatus = readl(&ohci->regs->intrstatus);
-	regs.intrenable = readl(&ohci->regs->intrenable);
-	regs.hcca = readl(&ohci->regs->hcca);
-	regs.ed_periodcurrent = readl(&ohci->regs->ed_periodcurrent);
-	regs.ed_controlhead = readl(&ohci->regs->ed_controlhead);
-	regs.ed_controlcurrent = readl(&ohci->regs->ed_controlcurrent);
-	regs.ed_bulkhead = readl(&ohci->regs->ed_bulkhead);
-	regs.ed_bulkcurrent = readl(&ohci->regs->ed_bulkcurrent);
-	regs.current_donehead = readl(&ohci->regs->current_donehead);
-	regs.fminterval = readl(&ohci->regs->fminterval);
-	regs.fmremaining = readl(&ohci->regs->fmremaining);
-	regs.fmnumber = readl(&ohci->regs->fmnumber);
-	regs.periodicstart = readl(&ohci->regs->periodicstart);
-	regs.lsthresh = readl(&ohci->regs->lsthresh);
-	regs.roothub.a = readl(&ohci->regs->roothub.a);
-	regs.roothub.b = readl(&ohci->regs->roothub.b);
-	regs.roothub.status = readl(&ohci->regs->roothub.status);
-	for (i=0; i<MAX_ROOT_PORTS; ++i)
-		regs.roothub.portstatus[i] = readl(&ohci->regs->roothub.portstatus[i]);
-
-	printk(KERN_DEBUG "  ohci revision    =  %x\n", regs.revision);
-	printk(KERN_DEBUG "  ohci control     =  %x\n", regs.control);
-	printk(KERN_DEBUG "  ohci cmdstatus   =  %x\n", regs.cmdstatus);
-	printk(KERN_DEBUG "  ohci intrstatus  =  %x\n", regs.intrstatus);
-	printk(KERN_DEBUG "  ohci intrenable  =  %x\n", regs.intrenable);
-
-	printk(KERN_DEBUG "  ohci hcca        =  %x\n", regs.hcca);
-	printk(KERN_DEBUG "  ohci ed_pdcur    =  %x\n", regs.ed_periodcurrent);
-	printk(KERN_DEBUG "  ohci ed_ctrlhead =  %x\n", regs.ed_controlhead);
-	printk(KERN_DEBUG "  ohci ed_ctrlcur  =  %x\n", regs.ed_controlcurrent);
-	printk(KERN_DEBUG "  ohci ed_bulkhead =  %x\n", regs.ed_bulkhead);
-	printk(KERN_DEBUG "  ohci ed_bulkcur  =  %x\n", regs.ed_bulkcurrent);
-	printk(KERN_DEBUG "  ohci curdonehead =  %x\n", regs.current_donehead);
-
-	printk(KERN_DEBUG "  ohci fminterval  =  %x\n", regs.fminterval);
-	printk(KERN_DEBUG "  ohci fmremaining =  %x\n", regs.fmremaining);
-	printk(KERN_DEBUG "  ohci fmnumber    =  %x\n", regs.fmnumber);
-	printk(KERN_DEBUG "  ohci pdstart     =  %x\n", regs.periodicstart);
-	printk(KERN_DEBUG "  ohci lsthresh    =  %x\n", regs.lsthresh);
-
-	printk(KERN_DEBUG "  ohci roothub.a   =  %x\n", regs.roothub.a);
-	printk(KERN_DEBUG "  ohci roothub.b   =  %x\n", regs.roothub.b);
-	printk(KERN_DEBUG "  ohci root status =  %x\n", regs.roothub.status);
-	printk(KERN_DEBUG "    roothub.port0  =  %x\n", regs.roothub.portstatus[0]);
-	printk(KERN_DEBUG "    roothub.port1  =  %x\n", regs.roothub.portstatus[1]);
-} /* show_ohci_status() */
-
-
-void show_ohci_ed(struct ohci_ed *ed)
-{
-	int stat = ed->status;
-	int skip = (stat & OHCI_ED_SKIP);
-	int mps = (stat & OHCI_ED_MPS) >> 16;
-	int isoc = (stat & OHCI_ED_F_ISOC);
-	int low_speed = (stat & OHCI_ED_S_LOW);
-	int dir = (stat & OHCI_ED_D);
-	int endnum = (stat & OHCI_ED_EN) >> 7;
-	int funcaddr = (stat & OHCI_ED_FA);
-	int halted = (ed->_head_td & 1);
-	int toggle = (ed->_head_td & 2) >> 1;
-
-	printk(KERN_DEBUG "   ohci ED:\n");
-	printk(KERN_DEBUG "     status     =  0x%x\n", stat);
-	printk(KERN_DEBUG "       %sMPS %d%s%s%s%s tc%d e%d fa%d\n",
-			skip ? "Skip " : "",
-			mps,
-			isoc ? "Isoc. " : "",
-			low_speed ? " LowSpd" : "",
-			(dir == OHCI_ED_D_IN) ? " Input" :
-			(dir == OHCI_ED_D_OUT) ? " Output" : "",
-			halted ? " Halted" : "",
-			toggle,
-			endnum,
-			funcaddr);
-	printk(KERN_DEBUG "     tail_td    =  0x%x\n", ed->tail_td);
-	printk(KERN_DEBUG "     head_td    =  0x%x\n", ed_head_td(ed));
-	printk(KERN_DEBUG "     next_ed    =  0x%x\n", ed->next_ed);
-} /* show_ohci_ed() */
-
-
-void show_ohci_td(struct ohci_td *td)
-{
-	int td_round = td->info & OHCI_TD_ROUND;
-	int td_dir = td->info & OHCI_TD_D;
-	int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21;
-	int td_toggle = (td->info & OHCI_TD_DT) >> 24;
-	int td_errcnt = td_errorcount(*td);
-	int td_cc = OHCI_TD_CC_GET(td->info);
-
-	printk(KERN_DEBUG "   ohci TD hardware fields:\n");
-	printk(KERN_DEBUG "      info     =  0x%x\n", td->info);
-	printk(KERN_DEBUG "        %s%s%s%d %s\n",
-		td_round ? "Rounding " : "",
-		(td_dir == OHCI_TD_D_IN) ? "Input " :
-		(td_dir == OHCI_TD_D_OUT) ? "Output " :
-		(td_dir == OHCI_TD_D_SETUP) ? "Setup " : "",
-		"IntDelay ", td_int_delay,
-		(td_toggle < 2) ? " " :
-		(td_toggle & 1) ? "Data1 " : "Data0 ");
-	printk(KERN_DEBUG "        %s%d %s0x%x, %sAccessed, %sActive\n",
-		"ErrorCnt ", td_errcnt,
-		"ComplCode ", td_cc,
-		td_cc_accessed(*td) ? "" : "Not ",
-		td_active(*td) ? "" : "Not ");
-
-	printk(KERN_DEBUG "      cur_buf  =  0x%x\n", td->cur_buf);
-	printk(KERN_DEBUG "      next_td  =  0x%x\n", td->next_td);
-	printk(KERN_DEBUG "      buf_end  =  0x%x\n", td->buf_end);
-	printk(KERN_DEBUG "   ohci TD driver fields:\n");
-	printk(KERN_DEBUG "      data     =  %p\n", td->data);
-	printk(KERN_DEBUG "      dev_id   =  %p\n", td->dev_id);
-	printk(KERN_DEBUG "      ed       =  %p\n", td->ed);
-	if (td->data != NULL) {
-	unsigned char *d = td->data;
-	printk(KERN_DEBUG "   DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n",
-			d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] );
-	}
-} /* show_ohci_td() */
-
-
-void show_ohci_device(struct ohci_device *dev)
-{
-	int idx;
-	printk(KERN_DEBUG "  ohci_device usb       =  %p\n", dev->usb);
-	printk(KERN_DEBUG "  ohci_device ohci      =  %p\n", dev->ohci);
-	printk(KERN_DEBUG "  ohci_device ohci_hcca =  %p\n", dev->hcca);
-	for (idx=0; idx<3 /*NUM_EDS*/; ++idx) {
-		printk(KERN_DEBUG "   [ed num %d] ", idx);
-		show_ohci_ed(&dev->ed[idx]);
-	}
-	for (idx=0; idx<3 /*NUM_TDS*/; ++idx) {
-		printk(KERN_DEBUG "   [td num %d] ", idx);
-		show_ohci_td(&dev->td[idx]);
-	}
-	printk(KERN_DEBUG "  ohci_device data\n    ");
-	for (idx=0; idx<4; ++idx) {
-		printk(KERN_DEBUG " %08lx", dev->data[idx]);
-	}
-	printk(KERN_DEBUG "\n");
-} /* show_ohci_device() */
-
-
-void show_ohci_hcca(struct ohci_hcca *hcca)
-{
-	int idx;
-
-	printk(KERN_DEBUG "  ohci_hcca\n");
-
-	for (idx=0; idx<NUM_INTS; idx++) {
-		printk(KERN_DEBUG "    int_table[%2d]  == %p\n", idx, hcca->int_table +idx);
-	}
-
-	printk(KERN_DEBUG "    frame_no          == %d\n", hcca->frame_no);
-	printk(KERN_DEBUG "    donehead          == 0x%08x\n", hcca->donehead);
-} /* show_ohci_hcca() */
-
-
-/* vim:sw=8
- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci-hcd.c linux.18p17/drivers/usb/ohci-hcd.c
--- linux.vanilla/drivers/usb/ohci-hcd.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci-hcd.c	Thu Jan  1 01:00:00 1970
@@ -1,1489 +0,0 @@
-/*
- * OHCI HCD (Host Controller Driver) for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- *
- * The OHCI HCD layer is a simple but nearly complete implementation of what the
- * USB people would call a HCD  for the OHCI. 
- * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
- * The layer on top of it, is for interfacing to the alternate-usb device-drivers.
- * 
- * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ]
- * [ Open Host Controller Interface driver for USB. ]
- * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ]
- * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ]
- * [ $Log: ohci.c,v $ ]
- * [ Revision 1.1  1999/04/05 08:32:30  greg ]
- * 
- * 
- * v2.1 1999/05/09 ep_addr correction, code clean up
- * v2.0 1999/05/04 
- * virtual root hub is now an option, 
- * memory allocation based on kmalloc and kfree now, Bus error handling, 
- * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
- * 
- * from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff)
- * from Greg Smith (ohci.c) (reset controller handling, hub)
- * 
- * v1.0 1999/04/27 initial release
- * ohci-hcd.c
- */
-
-/* #define OHCI_DBG  */  /* printk some debug information */
-
- 
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-
-#include <asm/spinlock.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "usb.h"
-#include "ohci-hcd.h"
-#include "inits.h" 
-
- 
-
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-static int handle_apm_event(apm_event_t event);
-static int apm_resume = 0;
-#endif
-
- 
-
-static struct wait_queue *control_wakeup;         
-static struct wait_queue *root_hub = NULL;
- 
-static __u8 cc_to_status[16] = { /* mapping of the OHCI CC to the UHCI status codes; first guess */
-/* Activ, Stalled, Data Buffer Err, Babble Detected : NAK recvd, CRC/Timeout, Bitstuff, reservd */
-/* No  Error  */		0x00,
-/* CRC Error  */		0x04,
-/* Bit Stuff  */		0x02,
-/* Data Togg  */ 		0x40,
-/* Stall      */		0x40,
-/* DevNotResp */		0x04,
-/* PIDCheck   */		0x04,
-/* UnExpPID   */		0x40,
-/* DataOver   */		0x20,
-/* DataUnder  */ 		0x20,
-/* reservd    */		0x40,
-/* reservd    */		0x40,
-/* BufferOver */		0x20,
-/* BuffUnder  */		0x20,
-/* Not Access */		0x80,
-/* Not Access */		0x80
- };
- 
- 
-/********
- **** Interface functions
- ***********************************************/
- 
-static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1)
-{
-
-	struct ohci * ohci = ohci_in; 
-	usb_device_irq handler=(void *) lw0;
-	void *dev_id = (void *) lw1;
-	int ret;
-	
-	OHCI_DEBUG({ int i; printk("USB HC IRQ <<<: %x: data(%d):", ep_addr, data_len);)
-	OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
-	OHCI_DEBUG( printk(" ret_status: %x\n", status); })
-	
- 	ret = handler(cc_to_status[status & 0xf], data, dev_id);
-	if(ret == 0) return 0; /* 0 .. do not requeue  */
-	if(status > 0) return -1; /* error occured do not requeue ? */
-	ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */
-	return 0;
-}
-
-static int sohci_ctrl_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw)
-{  
-	*(int * )lw0 = status;
-	wake_up(&control_wakeup);
-
-	OHCI_DEBUG( { int i; printk("USB HC CTRL<<<: %x: ctrl(%d):", ep_addr, ctrl_len);)
-	OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) ctrl)[i]);)
-	OHCI_DEBUG( printk(" data(%d):", data_len);) 
-	OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
-	OHCI_DEBUG( printk(" ret_status: %x\n", status); })
-    return 0;                       
-} 
-                                                             
-static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
-{
-	struct ohci * ohci = usb_dev->bus->hcpriv;
-	union ep_addr_ ep_addr;
-	
-	ep_addr.iep = 0;
-	ep_addr.bep.ep = ((pipe >> 15) & 0x0f) 		/* endpoint address */
-				| (pipe  & 0x80)  	 			/* direction */
-				| (1 << 5);						/* type = int*/
-	ep_addr.bep.fa = ((pipe >> 8) & 0x7f);			/* device address */
-
-	OHCI_DEBUG( printk("USB HC IRQ >>>: %x: every %d ms\n", ep_addr.iep, period);) 
-	
-	usb_ohci_add_ep(ohci, ep_addr.iep, period, 1, sohci_int_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
-	
-   	ohci_trans_req(ohci, ep_addr.iep, 0, NULL, ((struct ohci_device *) usb_dev->hcpriv)->data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id);
-	return 0;
-}
-
-
-static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
-{
-	struct wait_queue wait = { current, NULL }; 
-	struct ohci * ohci = usb_dev->bus->hcpriv;
-	int status;
-	union ep_addr_ ep_addr;
-
-    ep_addr.iep = 0;
-	ep_addr.bep.ep = ((pipe >> 15) & 0x0f) 		/* endpoint address */		
-				| (pipe  & 0x80)  				/* direction */
-				|  (1 << 6);					/* type = ctrl*/
-	ep_addr.bep.fa	= ((pipe >> 8) & 0x7f);				/* device address */
-	
-	status = 0xf; /* CC not Accessed */
-	OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: %x: ctrl(%d):", ep_addr.iep, 8);)
-	OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);)
-	OHCI_DEBUG( printk(" data(%d):", len);) 
-	OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
-	OHCI_DEBUG( printk("\n"); })
-	
-	usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_ctrl_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
-	
-	current->state = TASK_UNINTERRUPTIBLE;
-    add_wait_queue(&control_wakeup, &wait);   
- 
-	ohci_trans_req(ohci, ep_addr.iep, 8, cmd, data, len, (__OHCI_BAG) &status, 0);
-
-	schedule_timeout(HZ/10);
-
-    remove_wait_queue(&control_wakeup, &wait); 
-     
-    OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);)
-     
-	return cc_to_status[status & 0x0f];
-}
-
-
-static int sohci_usb_deallocate(struct usb_device *usb_dev) {
-    struct ohci_device *dev = usb_to_ohci(usb_dev); 
-	union ep_addr_ ep_addr;
-
-	ep_addr.iep = 0;
-	
-	OHCI_DEBUG(printk("USB HC dealloc %x\n", usb_dev->devnum);)
-
-	/* wait_ms(20); */
-
-	if(usb_dev->devnum >=0) {
-		ep_addr.bep.fa = usb_dev->devnum;
-    	usb_ohci_rm_function(((struct ohci_device *)usb_dev->hcpriv)->ohci, ep_addr.iep);
-	}
-	
-    USB_FREE(dev);
-	USB_FREE(usb_dev);
-
-	return 0;
-}
-
-static struct usb_device *sohci_usb_allocate(struct usb_device *parent) {
- 
-	struct usb_device *usb_dev;
-	struct ohci_device *dev;
-	
-	
-	USB_ALLOC(usb_dev, sizeof(*usb_dev));
-	if (!usb_dev)
-		return NULL;
-		
-	memset(usb_dev, 0, sizeof(*usb_dev));
-
-	USB_ALLOC(dev, sizeof(*dev));
-	if (!dev) {
-		USB_FREE(usb_dev);
-		return NULL;
-	}
-
-	/* Initialize "dev" */
-	memset(dev, 0, sizeof(*dev));
-
-	usb_dev->hcpriv = dev;
-	dev->usb = usb_dev;
-
-	usb_dev->parent = parent;
-
-	if (parent) {
-		usb_dev->bus = parent->bus;
-		dev->ohci = usb_to_ohci(parent)->ohci;
-	}
-	return usb_dev;
-}
-
-struct usb_operations sohci_device_operations = {
-	sohci_usb_allocate,
-	sohci_usb_deallocate,
-	sohci_control_msg,
-	sohci_request_irq,
-};
-
- 
-/******
- *** ED handling functions
- ************************************/
-
-
-
-/* 
- * search for the right place to insert an interrupt ed into the int tree 
- * do some load ballancing
- * */
-
-static int usb_ohci_int_ballance(struct ohci * ohci, int interval, int load) {
-
-  int i,j;
-  
-  j = 0;   /* search for the least loaded interrupt endpoint branch of all 32 branches */
-  for(i=0; i< 32; i++) if(ohci->ohci_int_load[j] > ohci->ohci_int_load[i]) j=i; 
-
-  if(interval < 1) interval = 1;
-  if(interval > 32) interval = 32;
-  for(i= 0; ((interval >> i) > 1 ); interval &= (0xfffe << i++ )); /* interval = 2^int(ld(interval)) */
-  
-
-  for(i=j%interval; i< 32; i+=interval) ohci->ohci_int_load[i] += load;
-  j = interval + (j % interval); 
-  
-  OHCI_DEBUG(printk("USB HC new int ed on pos : %x \n",j);)
-  
-  return j;
-}
-
-/* get the ed from the endpoint / device adress */
-
-struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in) {
-
-union ep_addr_ ep_addr;
-struct usb_ohci_ed *tmp;
-unsigned int mask;
-
-mask = 0;
-ep_addr.iep = ep_addr_in;
-
-#ifdef VROOTHUB
- if(ep_addr.bep.fa == ohci->root_hub_funct_addr) {
- 	if((ep_addr.bep.ep & 0x0f) == 0)
- 		return &ohci->ed_rh_ep0; /* root hub ep0 */
- 	else
- 		return &ohci->ed_rh_epi; /* root hub int ep */
- }
-#endif
-
- tmp = ohci->ed_func_ep0[ep_addr.bep.fa];
- mask = ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); 
- ep_addr.bep.ep &= mask; /* mask out direction of ctrl ep */
- 
-  while (tmp != NULL) {
-    if (tmp->ep_addr.iep == ep_addr.iep) 
-      return tmp;
-    tmp = tmp->ed_list;
-  }
-  return NULL;
-}
- 
-spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
-/* add a new endpoint ep_addr */
-struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr_in, int interval, int load, f_handler handler, int ep_size, int speed) {
-   
-  struct usb_ohci_ed * ed; 
-  struct usb_ohci_td * td;
-  union ep_addr_ ep_addr;
- 
-   
-  int int_junk;
- 
-  struct usb_ohci_ed *tmp;
-
-  ep_addr.iep = ep_addr_in ;
-  ep_addr.bep.ep &= ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); /* mask out direction of ctrl ep */
-
-  spin_lock(&usb_ed_lock);
-  
-  tmp =  ohci_find_ep(ohci, ep_addr.iep);
-  if (tmp != NULL) { 
-  
-#ifdef VROOTHUB
-  	if(ep_addr.bep.fa == ohci->root_hub_funct_addr) {
- 		if((ep_addr.bep.ep & 0x0f) != 0) { /* root hub int ep */
-			ohci->ed_rh_epi.handler = handler;
- 			ohci_init_rh_int_timer(ohci, interval);
- 		}
- 		else { /* root hub ep0 */
- 			 ohci->ed_rh_ep0.handler = handler;
- 		}			 	
- 	}
-
- 	else 
-#endif 
-
- 		{
- 		tmp->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7)
-  		
-    	| (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0)
-    	| (speed << 13)
-    	| ep_size <<16;
- 
-  		tmp->handler = handler; 
-  	}
- 	spin_unlock(&usb_ed_lock);
-    return tmp;    /* ed  already in use */
-  }
-  
-  
-  OHCI_ALLOC(td, sizeof(td)); /* dummy td; end of td list for ed */
-  OHCI_ALLOC(ed, sizeof(ed));
-  td->prev_td = NULL;
-  
-  ed->hw.tail_td = virt_to_bus(&td->hw);
-  ed->hw.head_td = ed->hw.tail_td;
-  ed->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7)
-  /*  | ((ep_addr.bep.port & 0x80)? 0x1000 : 0x0800 ) */
-    | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0)
-    | (speed << 13)
-    | ep_size <<16;
-  
-  ed->handler = handler;
-
-  switch((ep_addr.bep.ep >> 5) & 0x03) {
-  case CTRL:
-    ed->hw.next_ed = 0;
-    if(ohci->ed_controltail == NULL) {
-      writel(virt_to_bus(&ed->hw), &ohci->regs->ed_controlhead);
-    }
-    else {
-      ohci->ed_controltail->hw.next_ed = virt_to_bus(&ed->hw);
-    }
-    ed->ed_prev = ohci->ed_controltail;
-    ohci->ed_controltail = ed;	  
-    break;
-  case BULK:  
-    ed->hw.next_ed = 0;
-    if(ohci->ed_bulktail == NULL) {
-      writel(virt_to_bus(&ed->hw), &ohci->regs->ed_bulkhead);
-    }
-    else {
-      ohci->ed_bulktail->hw.next_ed = virt_to_bus(&ed->hw);
-    }
-    ed->ed_prev = ohci->ed_bulktail;
-    ohci->ed_bulktail = ed;	  
-    break;
-  case INT:
-    int_junk = usb_ohci_int_ballance(ohci, interval, load);
-    ed->hw.next_ed = ohci->hc_area->ed[int_junk].next_ed; 
-    ohci->hc_area->ed[int_junk].next_ed = virt_to_bus(&ed->hw);
-    ed->ed_prev = (struct usb_ohci_ed *) &ohci->hc_area->ed[int_junk];
-    break;
-  case ISO:
-    ed->hw.next_ed = 0;
-    ohci->ed_isotail->hw.next_ed = virt_to_bus(&ed->hw);
-    ed->ed_prev = ohci->ed_isotail;
-    ohci->ed_isotail = ed;	  
-    break;
-  }
-  ed->ep_addr  = ep_addr;
-  
-  /* Add it to the "hash"-table of known endpoint descriptors */      
-  
-  ed->ed_list = ohci->ed_func_ep0[ed->ep_addr.bep.fa];
-  ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed; 
-
-  spin_unlock(&usb_ed_lock);
-  
-  OHCI_DEBUG(printk("USB HC new ed %x: %x :", ep_addr.iep, (unsigned int ) ed); )
-  OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; )
-  return 0;
-}
-
-/*****
- * Request the removal of an endpoint
- * 
- * put the ep on the rm_list and request a stop of the bulk or ctrl list 
- * real removal is done at the next start of frame hardware interrupt 
- */
-int usb_ohci_rm_ep(struct ohci * ohci, struct  usb_ohci_ed *ed)
-{    
-  unsigned int flags;
-  struct usb_ohci_ed *tmp;
-  
-  OHCI_DEBUG(printk("USB HC remove ed %x: %x :\n", ed->ep_addr.iep, (unsigned int ) ed); )
-
-  spin_lock_irqsave(&usb_ed_lock, flags);
-
-  tmp = ohci->ed_func_ep0[ed->ep_addr.bep.fa];
-  if (tmp == NULL) {
-  	spin_unlock_irqrestore(&usb_ed_lock, flags);
-    return 0;
-  }
-
-  if(tmp == ed) {
-     ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed->ed_list;
-  }
-  else {
-    while (tmp->ed_list != ed) {
-      if (tmp->ed_list == NULL) {
-        spin_unlock_irqrestore(&usb_ed_lock, flags);
-		return 0;
-	  }
-      tmp = tmp->ed_list;
-    }
-    tmp->ed_list = ed->ed_list;
-  }
-  ed->ed_list = ohci->ed_rm_list;
-  ohci->ed_rm_list = ed;
-  ed->hw.info  |=  OHCI_ED_SKIP;
-
-  switch((ed->ep_addr.bep.ep >> 5) & 0x03) {
-  	case CTRL:
-    	writel_mask(~(0x01<<4), &ohci->regs->control); /* stop CTRL list */
-    	break;
-  	case BULK:
-    	writel_mask(~(0x01<<5), &ohci->regs->control); /* stop BULK list */
-    	break;
-  }
-
-
-  writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
-
-  spin_unlock_irqrestore(&usb_ed_lock, flags);
-
-  return 1;
-}
-
-/* we have requested to stop the bulk or CTRL list,
- * now we can remove the eds on the rm_list */
- 
-static int ohci_rm_eds(struct ohci * ohci) {
-
-  unsigned int flags;
-  struct usb_ohci_ed *ed;
-  struct usb_ohci_ed *ed_tmp;
-  struct usb_ohci_td *td;
-  __u32 td_hw_tmp;
-  __u32 td_hw;
-  
-  spin_lock_irqsave(&usb_ed_lock, flags);
-
-  ed = ohci->ed_rm_list;
-
-  while (ed != NULL) {
-    
-    switch((ed->ep_addr.bep.ep >> 5) & 0x03) {
-    case CTRL: 
-      if(ed->ed_prev == NULL) {
-		writel(ed->hw.next_ed, &ohci->regs->ed_controlhead);
-      }
-      else {
-		ed->ed_prev->hw.next_ed = ed->hw.next_ed;
-      }
-      if(ohci->ed_controltail == ed) {
-		ohci->ed_controltail = ed->ed_prev;
-      }
-      break;
-    case BULK: 
-      if(ed->ed_prev == NULL) {
-		writel(ed->hw.next_ed, &ohci->regs->ed_bulkhead);
-      }
-      else {
-		ed->ed_prev->hw.next_ed = ed->hw.next_ed;
-      }
-      if(ohci->ed_bulktail == ed) {
-		ohci->ed_bulktail = ed->ed_prev;
-      }
-      break;
-    case INT: 
-      	ed->ed_prev->hw.next_ed = ed->hw.next_ed;
-      break;
-    case ISO:
-      ed->ed_prev->hw.next_ed = ed->hw.next_ed;
-      if(ohci->ed_isotail == ed) {
-		ohci->ed_isotail = ed->ed_prev;
-      }
-      break;
-    }
-    
-    if(ed->hw.next_ed != 0) ((struct usb_ohci_ed *) bus_to_virt(ed->hw.next_ed))->ed_prev = ed->ed_prev;
-    
-
-/* tds directly connected to ed */
- 
-	td_hw = ed->hw.head_td & 0xfffffff0;
-    while(td_hw != 0) {
-    	td = bus_to_virt(td_hw);
-    	td_hw_tmp = td_hw;
-    	td_hw = td->hw.next_td;
-    	OHCI_FREE(td); /* free pending tds */ 
-    	if(td_hw_tmp == ed->hw.tail_td) break;
-    	
-    }
-     
-    /* mark TDs on the hc done list (if there are any) */      
-	td_hw = readl(&ohci->regs->donehead) & 0xfffffff0;
-    while(td_hw != 0) {
-    	td = bus_to_virt(td_hw);
-    	td_hw = td->hw.next_td;
-    	if(td->ep == ed) td->ep = 0;
-   	}
- 
-    /* mark TDs on the hcca done list (if there are any) */      
-	td_hw = ohci->hc_area->hcca.done_head & 0xfffffff0 ;
-	
-    while(td_hw != 0) { 
-    	td = bus_to_virt(td_hw); 
-    	td_hw = td->hw.next_td;
-    	if(td->ep == ed) td->ep = 0;
-   	}
-
-    ed_tmp = ed;
-    ed = ed->ed_list;
-    OHCI_FREE(ed_tmp);  /* free ed */ 
-  }
-  writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
-  writel(0, &ohci->regs->ed_bulkcurrent);    /* reset BULK list */
-  writel_set((0x01<<4), &ohci->regs->control);   /* start CTRL u. (BULK list) */ 
-
-  spin_unlock_irqrestore(&usb_ed_lock, flags);
-  
-  ohci->ed_rm_list = NULL;
-  OHCI_DEBUG(printk("USB HC after rm ed control: %4x  intrstat: %4x intrenable: %4x\n", readl(&ohci->regs->control),readl(&ohci->regs->intrstatus),readl(&ohci->regs->intrenable));)
- 
-
-  return 0;
-}
-
-/* remove all endpoints of a function (device) */
-int usb_ohci_rm_function( struct ohci * ohci, unsigned int ep_addr_in)
-{    
-  struct usb_ohci_ed *ed;
-  struct usb_ohci_ed *tmp;
-  union ep_addr_ ep_addr;
-
- 
-  
-  ep_addr.iep = ep_addr_in;
-
-  for(ed = ohci->ed_func_ep0[ep_addr.bep.fa]; ed != NULL;) {
-	tmp = ed;
-    ed = ed->ed_list;
-    usb_ohci_rm_ep(ohci, tmp);
-  }
- 
-
-  
-  return 1;
-}
-
-
-
-
-
-/******
- *** TD handling functions
- ************************************/
-
-
-#define FILL_TD(TD_PT, HANDLER, INFO, DATA, LEN, LW0, LW1)  \
-    td_pt = (TD_PT); \
-    td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); \
-    td_pt1->ep = usb_ep; \
-    td_pt1->handler = (HANDLER); \
-    td_pt1->buffer_start = (DATA); \
-    td_pt1->lw0 = (LW0); \
-    td_pt1->lw1 = (LW1); \
-    td_pt1->hw.info = (INFO); \
-    td_pt1->hw.cur_buf = virt_to_bus(DATA); \
-    td_pt1->hw.buf_end = td_pt1->hw.cur_buf + (LEN) - 1; \
-    td_pt1->hw.next_td = virt_to_bus(td_pt); \
-    usb_ep->hw.tail_td = virt_to_bus(td_pt); \
-    td_pt->prev_td = td_pt1; \
-    td_pt->hw.next_td = 0
-
-spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED;
-
-int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int ctrl_len, void  *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) {
-
-  int ed_type;
-  unsigned int flags;
-  struct usb_ohci_td *td_pt;
-  struct usb_ohci_td *td_pt1;
-  struct usb_ohci_td *td_pt_a1, *td_pt_a2, *td_pt_a3;
-  struct usb_ohci_ed *usb_ep;
-  f_handler handler;
-
-  
- td_pt_a1 =NULL;
- td_pt_a2 =NULL;
- td_pt_a3 =NULL;
-  
-  usb_ep = ohci_find_ep(ohci, ep_addr);
-  if(usb_ep == NULL ) return -1; /* not known ep */
-  
-  handler = usb_ep->handler;
- 
-#ifdef VROOTHUB 
-  if(usb_ep == &ohci->ed_rh_ep0) { /* root hub ep 0 control endpoint */  
-    root_hub_control_msg(ohci, 8, ctrl, data, data_len, lw0, lw1, handler);
-    return 0;
-  }
-
-  if(usb_ep == &ohci->ed_rh_epi) { /* root hub interrupt endpoint */
-  
-    root_hub_int_req(ohci, 8, ctrl, data, data_len, lw0, lw1, handler);  
-	return 0;
-  }
-#endif
-  /*  struct usb_ohci_ed * usb_ep = usb_ohci_add_ep(pipe, ohci, interval, 1); */
- 
-   ed_type = ((((union ep_addr_)ep_addr).bep.ep >> 5) & 0x07);
-  
-  switch(ed_type) {
-  case BULK_IN:
-  case BULK_OUT:   
-  case INT_IN:
-  case INT_OUT:    
-    OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1));
-    break;
-
-  case CTRL_IN:
-  case CTRL_OUT:
-    OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1));
-    OHCI_ALLOC(td_pt_a3, sizeof(td_pt_a3));  
-    if(data_len > 0) {
-      OHCI_ALLOC(td_pt_a2, sizeof(td_pt_a2));
-    }
-    break;
-
-  case ISO_IN:
-  case ISO_OUT:
-
-  }
-    
-  spin_lock_irqsave(&usb_req_lock, flags);
-  
-  switch(ed_type) {
-  case BULK_IN:
-    FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 );
-    writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
-    break;
-
-  case BULK_OUT: 
-    FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 );
-    writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
-    break;
-
-  case INT_IN: 
-    FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 );  
-    break;
-
-  case INT_OUT: 
-    FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 );
-    break;
-
-  case CTRL_IN: 
-    FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 );  
-    if(data_len > 0) { 
-      FILL_TD( td_pt_a2, NULL, TD_CC | TD_R |  TD_DP_IN | TD_T_DATA1, data, data_len, 0, 0 );  
-    } 
-    FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_OUT | TD_T_DATA1, NULL, 0, lw0, lw1 );
-    writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
-    break;
-
-  case CTRL_OUT:     
-    FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 );  
-    if(data_len > 0) { 
-      FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1, data, data_len, 0, 0 );  
-    } 
-    FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_IN | TD_T_DATA1, NULL, 0, lw0, lw1 );
-    writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
-    break;
-
-  case ISO_IN:
-  case ISO_OUT:
-    break;
-  }
-
-
-  
-
- td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); 
-
- 
-  if(td_pt_a3 != NULL) td_pt_a3->prev_td = NULL;
-  	else if (td_pt_a2 != NULL) td_pt_a2->prev_td = NULL;
-  	  	else if (td_pt_a1 != NULL) td_pt_a1->prev_td = NULL;
-  	  
-  spin_unlock_irqrestore(&usb_req_lock, flags);
-  return 0;
-}
-
-
-/******
- *** Done List handling functions
- ************************************/
-
-/* replies to the request have to be on a FIFO basis so
- * we reverse the reversed done-list */
- 
-static struct usb_ohci_td * ohci_reverse_done_list(struct ohci * ohci) {
-
-  __u32 td_list_hc;
-  struct usb_ohci_td      * td_list = NULL;
-  struct usb_ohci_td      * td_rev = NULL;
-  	
-	td_list_hc = ohci->hc_area->hcca.done_head & 0xfffffff0;
-    ohci->hc_area->hcca.done_head = 0;
-    
- 	while(td_list_hc) {
-		
-		td_list = (struct  usb_ohci_td *) bus_to_virt(td_list_hc);
-		td_list->next_dl_td = td_rev;
-			
-		td_rev = td_list;
-		td_list_hc = td_list->hw.next_td & 0xfffffff0;
-	}
- return td_list;
-}
-
-/* all done requests are replied here */
-static int usb_ohci_done_list(struct ohci *  ohci) {
-
-  struct usb_ohci_td      * td = NULL;
-  struct usb_ohci_td      * td_list; 
-  struct usb_ohci_td      * td_list_next = NULL;
-  struct usb_ohci_td      * td_err = NULL;
-	__u32 td_hw;
- 
-	
-  td_list = ohci_reverse_done_list(ohci);
-
-  while(td_list) {
-   	td_list_next = td_list->next_dl_td;
-    td = td_list;
-
-    if(td->ep == NULL) { /* removed ep */
-   		OHCI_FREE(td_list);
-   		break;
-   	}
-   	
-   	/* the HC halts an ED if an error occurs; put all pendings TDs of an halted ED on the
-   	 * done list; they are marked with an 0xf CC_error code 
-   	 */
-   	 
- 	if(TD_CC_GET(td_list->hw.info) != TD_CC_NOERROR) { /* on error move all pending tds of an ed into the done list */
- 		printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hw.info), td_list->ep->ep_addr.iep);
-    td_err= td_list;
-	td_hw = td_list->ep->hw.head_td & 0xfffffff0;
-    while(td_hw != 0) { 
-    	if(td_hw == td_list->ep->hw.tail_td) break;
-    	td = bus_to_virt(td_hw);
-    	td_err->next_dl_td = td;
-    	td_err= td;
-    	td_hw = td->hw.next_td;
-    }
-    td_list->ep->hw.head_td = td_list->ep->hw.tail_td;
-    td->next_dl_td = td_list_next;
-    td_list_next = td_list->next_dl_td;
- 	
-    }
-  /*  send the reply  */
- 	if(td_list->handler != NULL)  {
-    	if(td_list->prev_td == NULL) {
-			td_list->handler((void *) ohci,
-						td_list->ep->ep_addr.iep,
-						0, 
-						NULL, 
-						td_list->buffer_start, 
-						td_list->hw.buf_end-virt_to_bus(td_list->buffer_start)+1, 
-						TD_CC_GET(td_list->hw.info),
-						td_list->lw0,
-						td_list->lw1);
-			OHCI_FREE(td_list);			
-   	  	}
-      	else {
-      		if(td_list->prev_td->prev_td == NULL) { /* cntrl 2 Transactions dataless */
-	  		td_list->handler((void *) ohci,
-	  		            td_list->ep->ep_addr.iep,
-	  					td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1,
-	  					td_list->prev_td->buffer_start, 
-	  					NULL,
-	  					0, 
-	  					(TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info),
-						td_list->lw0,
-						td_list->lw1);
-			OHCI_FREE(td_list->prev_td);
-			OHCI_FREE(td_list);
-			}
-			else { /* cntrl 3 Transactions */
-	  			td_list->handler((void *) ohci,
-	  					td_list->ep->ep_addr.iep, 
-	  					td_list->prev_td->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->prev_td->buffer_start)+1, 
-	  					td_list->prev_td->prev_td->buffer_start, 
-	  					td_list->prev_td->buffer_start, 
-	  					td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1, 
-	  					(TD_CC_GET(td_list->prev_td->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->prev_td->hw.info)
-	  						: (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info),
-						td_list->lw0,
-						td_list->lw1); 
-				OHCI_FREE(td_list->prev_td->prev_td);			
-				OHCI_FREE(td_list->prev_td);
-				OHCI_FREE(td_list);
-					
-			}
-      	} 
-    	
-    }
-    td_list = td_list_next;
-  }  
-  return 0; 
-}
-
-
-
-/******
- *** HC functions
- ************************************/
- 
-
- 
-void reset_hc(struct ohci *ohci) {
-	int retries = 5;
-	int timeout = 30;
-	int fminterval;
-	
-	if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */
-		writel(0x08,  &ohci->regs->cmdstatus); /* request ownership */
-		printk("USB HC TakeOver from SMM\n");
-		do {
-			wait_ms(100);
-			if(--retries) { 
-				printk("USB HC TakeOver timed out!\n");
-				break;
-			}
-		}
-		while(readl(&ohci->regs->control) & 0x100); 
-	}	
-		
-	writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */
-	OHCI_DEBUG(printk("USB HC reset_hc: %x ; retries: %d\n", readl(&ohci->regs->control), 5-retries);)
-	fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
-	writel(1,  &ohci->regs->cmdstatus);	   /* HC Reset */
-	while ((readl(&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */
-		if (--timeout == 0) {
-			printk("USB HC reset timed out!\n");
-			return;
-		}	
-		udelay(1);
-	}
-	/* set the timing */
-	fminterval |= (((fminterval -210) * 6)/7)<<16; 
-	writel(fminterval, &ohci->regs->fminterval);
-	writel(((fminterval&0x3fff)*9)/10, &ohci->regs->periodicstart);
-}
-
-
-/*
- * Reset and start an OHCI controller
- */
-void start_hc(struct ohci *ohci)
-{
-   /*  int fminterval; */
-    unsigned int mask;
-  /*  fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
-	reset_hc(ohci); */
- 
-
-	writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */
- 
-	/* Choose the interrupts we care about now, others later on demand */
-	mask = OHCI_INTR_MIE | OHCI_INTR_WDH;
-	/* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF|  
-		OHCI_INTR_FNO */
-		
-
-	 
-	writel((0x00), &ohci->regs->control); /* USB Reset BUS */
-	wait_ms(10);
-	  
-	writel((0x97), &ohci->regs->control); /* USB Operational  */
-	
- 	writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
-	wait_ms(50); 
-		
-	OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); )
- 	OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); )
- 	OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); )
- 	OHCI_DEBUG(printk("USB HC roothubstatu: %x \n", readl( &(ohci->regs->roothub.status) )); )
- 	OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); )
-   	OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); )
-  
-	/* control_wakeup = NULL; */
-	writel(mask, &ohci->regs->intrenable);
-	writel(mask, &ohci->regs->intrstatus);
- 
-#ifdef VROOTHUB
-	{
-
-	struct usb_device * usb_dev;
-	struct ohci_device *dev;
-
-	usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
-	dev = usb_dev->hcpriv; 
-
-	dev->ohci = ohci; 
-
-	usb_connect(usb_dev);
-
-	ohci->root_hub->usb->children[0] = usb_dev;
-
-	usb_new_device(usb_dev);
-	}
-#endif
-
- 
-	
-}
-
-
-
-
-static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r)
-{
-  struct ohci *ohci = __ohci;
-  struct ohci_regs *regs = ohci->regs;
- 
-  int ints; 
-
- 
- if((ohci->hc_area->hcca.done_head != 0) && !(ohci->hc_area->hcca.done_head & 0x01)) {
-    ints =  OHCI_INTR_WDH;
-  }
-  else { 
-    if((ints = (readl(&regs->intrstatus) & readl(&regs->intrenable))) == 0)
-      return;
-   } 
-   
-  ohci->intrstatus |= ints;
-  OHCI_DEBUG(printk("USB HC interrupt: %x (%x) \n", ints, readl(&ohci->regs->intrstatus));)
-
-  /* ints &= ~(OHCI_INTR_WDH);  WH Bit will be set by done list subroutine */
- /*  if(ints & OHCI_INTR_FNO) {
-   	writel(OHCI_INTR_FNO,  &regs->intrstatus);
-   	if (waitqueue_active(&ohci_tasks)) wake_up(&ohci_tasks);
-  } */
-  
-  if(ints & OHCI_INTR_WDH) {
-   	writel(OHCI_INTR_WDH,  &regs->intrdisable);	
-   	ohci->intrstatus &= (~OHCI_INTR_WDH);
-	usb_ohci_done_list(ohci); /* prepare out channel list */
-	writel(OHCI_INTR_WDH, &ohci->regs->intrstatus);
-	writel(OHCI_INTR_WDH, &ohci->regs->intrenable);
-   	 
-  }
-  
-  if(ints & OHCI_INTR_SF) {
-  	writel(OHCI_INTR_SF,  &regs->intrdisable);	
-  	writel(OHCI_INTR_SF, &ohci->regs->intrstatus);
-	ohci->intrstatus &= (~OHCI_INTR_SF);
-  	if(ohci->ed_rm_list != NULL) {
-			ohci_rm_eds(ohci);
- 	 }
-  }
-#ifndef VROOTHUB 
-  if(ints & OHCI_INTR_RHSC) {  
-  	writel(OHCI_INTR_RHSC, &regs->intrdisable);
-  	writel(OHCI_INTR_RHSC, &ohci->regs->intrstatus);
-	wake_up(&root_hub);
-
-  }
- #endif
-
-  writel(OHCI_INTR_MIE, &regs->intrenable);
-	
-}
- 
-#ifndef VROOTHUB
-/*
- * This gets called if the connect status on the root
- * hub (and the root hub only) changes.
- */
-static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr)
-{
-	struct usb_device *usb_dev;
-    struct ohci_device *dev;
-	OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );)
-
-	/*
-	 * Even if the status says we're connected,
-	 * the fact that the status bits changed may
-	 * that we got disconnected and then reconnected.
-	 *
-	 * So start off by getting rid of any old devices..
-	 */
-	usb_disconnect(&ohci->root_hub->usb->children[port_nr]);
-
-	 if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) {
-	 	writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]);
-		return; /* nothing connected */
-	}
-	/*
-	 * Ok, we got a new connection. Allocate a device to it,
-	 * and find out what it wants to do..
-	 */
-	usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
-	dev = usb_dev->hcpriv; 
-	dev->ohci = ohci; 
-	usb_connect(dev->usb);
-	ohci->root_hub->usb->children[port_nr] = usb_dev;
-	wait_ms(200); /* wait for powerup */
-    /* reset port/device */
- 	writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */
- 	while(!(readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_PRSC)) wait_ms(10); /* reset active ? */
- 	writel(RH_PS_PES, &ohci->regs->roothub.portstatus[port_nr]); /* enable port */
- 	wait_ms(10);
-	/* Get speed information */
-	usb_dev->slow = (readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_LSDA) ? 1 : 0;
-
-	/*
-	 * Ok, all the stuff specific to the root hub has been done.
-	 * The rest is generic for any new USB attach, regardless of
-	 * hub type.
-	 */
-	usb_new_device(usb_dev);
-}
-#endif
- 
-
-
-/*
- * Allocate the resources required for running an OHCI controller.
- * Host controller interrupts must not be running while calling this
- * function.
- *
- * The mem_base parameter must be the usable -virtual- address of the
- * host controller's memory mapped I/O registers.
- *
- * This is where OHCI triumphs over UHCI, because good is dumb.
- * Note how much simpler this function is than in uhci.c.
- *
- * OHCI hardware takes care of most of the scheduling of different
- * transfer types with the correct prioritization for us.
- */
-
-
-static struct ohci *alloc_ohci(void* mem_base)
-{
-	int i,j;
-	struct ohci *ohci;
-	struct ohci_hc_area *hc_area;
-	struct usb_bus *bus;
-	struct ohci_device *dev;
-	struct usb_device *usb;
-
-	/*
-	 * Here we allocate some dummy  EDs as well as the
-	 * OHCI host controller communications area.  The HCCA is just
-	 * a nice pool of memory with pointers to endpoint descriptors
-	 * for the different interrupts.
-	 *
-	 * The first page of memory  contains the HCCA and ohci structure
-	 */
-	hc_area = (struct  ohci_hc_area *) __get_free_pages(GFP_KERNEL, 1);
-	if (!hc_area)
-		return NULL;
-	memset(hc_area, 0, sizeof(*hc_area));
-    ohci = &hc_area->ohci;
-	ohci->irq = -1;
-	ohci->regs = mem_base;
-    
-	ohci->hc_area = hc_area;
-	/* Tell the controller where the HCCA is */
-	writel(virt_to_bus(&hc_area->hcca), &ohci->regs->hcca);
-	
- 
-	/*
-	 * Initialize the ED polling "tree",  full tree;
-         * dummy eds ed[i] (hc should skip them) 
-         * i == 0 is the end of the iso list;
-		 * 1 is the   1ms node, 
-         * 2,3        2ms nodes,
-         * 4,5,6,7    4ms nodes,
-         * 8  ... 15  8ms nodes,
-         * 16 ... 31  16ms nodes,
-         * 32 ... 63  32ms nodes
-         * Sequenzes:
-		 * 32-16- 8-4-2-1-0
-         * 33-17- 9-5-3-1-0
-         * 34-18-10-6-2-1-0
-         * 35-19-11-7-3-1-0
-         * 36-20-12-4-2-1-0
-         * 37-21-13-5-3-1-0
-         * 38-22-14-6-2-1-0
-         * 39-23-15-7-3-1-0
-         * 40-24- 8-4-2-1-0
-         * 41-25- 9-5-3-1-0
-         * 42-26-10-6-2-1-0
-         *     :      :
-         * 63-31-15-7-3-1-0
-	 */
-        hc_area->ed[ED_ISO].info |=  OHCI_ED_SKIP; /* place holder, so skip it */
-        hc_area->ed[ED_ISO].next_ed =  0x0000;       /* end of iso list */
-
-        hc_area->ed[1].next_ed = virt_to_bus(&(hc_area->ed[ED_ISO]));
-        hc_area->ed[1].info |=  OHCI_ED_SKIP; /* place holder, skip it */
-	     
-        j=1;
-        for (i = 2; i < (NUM_INTS * 2); i++) {
-          	if (i >= NUM_INTS) 
-	   		 	hc_area->hcca.int_table[i - NUM_INTS] = virt_to_bus(&(hc_area->ed[i]));
-           
-          	if( i == j*4) j *= 2;
-	    	hc_area->ed[i].next_ed = virt_to_bus(&(hc_area->ed[j+ i%j]));
-            hc_area->ed[i].info |=  OHCI_ED_SKIP; /* place holder, skip it */
-        }
-
-
-	/*
-	 * for load ballancing of the interrupt branches 
-	 */
-	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
-
-	/*
-	 * Store the end of control and bulk list eds. So, we know where we can add
-	 * elements to these lists.
-	 */
-	ohci->ed_isotail     = (struct usb_ohci_ed *) &(hc_area->ed[ED_ISO]);
-        ohci->ed_controltail = NULL;
-        ohci->ed_bulktail    = NULL;
-	
-	/*
-	 * Tell the controller where the control and bulk lists are
-	 * The lists are empty now.
-	 */	
-	writel(0, &ohci->regs->ed_controlhead);
-	writel(0, &ohci->regs->ed_bulkhead);
-
-	
-	USB_ALLOC(bus, sizeof(*bus));
-	if (!bus)
-		return NULL;
-
-	memset(bus, 0, sizeof(*bus));
-
-	ohci->bus = bus;
-	bus->hcpriv = (void *) ohci;
-	bus->op = &sohci_device_operations;
-
-	 
-	usb = sohci_usb_allocate(NULL);
-	if (!usb)
-		return NULL;
-
-	dev = ohci->root_hub = usb_to_ohci(usb);
-
-	usb->bus = bus;
-	/* bus->root_hub = ohci_to_usb(ohci->root_hub); */
-	dev->ohci = ohci;
-	
-	/* Initialize the root hub */
-	 
-	usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
-	usb_init_root_hub(usb);
-
-	return ohci;
-} 
-
-
-/*
- * De-allocate all resources..
- */
-
-static void release_ohci(struct ohci *ohci)
-{
-	int i;
-	union ep_addr_ ep_addr;
-	ep_addr.iep = 0;
-	
-    OHCI_DEBUG(printk("USB HC release ohci \n");)
-    
-	if (ohci->irq >= 0) {
-		free_irq(ohci->irq, ohci);
-		ohci->irq = -1;
-	}
-
-    /* stop hc */
-	writel(OHCI_USB_SUSPEND, &ohci->regs->control);
-		
-	/* deallocate all EDs and TDs */
-	for(i=0; i < 128; i ++) {
-		ep_addr.bep.fa = i;
-		usb_ohci_rm_function(ohci, ep_addr.iep);
-	}
-	ohci_rm_eds(ohci); /* remove eds */
-	
-    /* disconnect all devices */    
-	if(ohci->root_hub)
-		for(i = 0; i < ohci->root_hub->usb->maxchild; i++)
-			  usb_disconnect(ohci->root_hub->usb->children + i);
-	    
-    USB_FREE(ohci->root_hub->usb);
-    USB_FREE(ohci->root_hub);
-    USB_FREE(ohci->bus);
-    
-	/* unmap the IO address space */
-	iounmap(ohci->regs);
-       
-	
-	free_pages((unsigned int) ohci->hc_area, 1);
-	
-}
-
- 
-void cleanup_drivers(void);
-
-static int ohci_roothub_thread(void * __ohci)
-{
-        struct ohci *ohci = (struct ohci *)__ohci;
-        lock_kernel(); 
-
-        /*
-         * This thread doesn't need any user-level access,
-         * so get rid of all our resources..
-         */
-        printk("ohci_roothub_thread at %p\n", &ohci_roothub_thread);
-        exit_mm(current);
-        exit_files(current);
-        exit_fs(current);
-
-
-        strcpy(current->comm, "root-hub");
-
-         
-		start_hc(ohci);
-		writel( 0x10000, &ohci->regs->roothub.status);
-		wait_ms(50); /* root hub power on */
-         do {
-#ifdef CONFIG_APM
-		if (apm_resume) {
-			apm_resume = 0;
-			start_hc(ohci);
-			continue;
-		}
-#endif
- 	 
-         OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); )
-#ifndef VROOTHUB
-		/*	if (ohci->intrstatus & OHCI_INTR_RHSC)  */
-			{
-				int port_nr;
-	     		for(port_nr=0; port_nr< ohci->root_hub->usb->maxchild; port_nr++)
-	     			if(readl(&ohci->regs->roothub.portstatus[port_nr]) & (RH_PS_CSC | RH_PS_PRSC)) {		
-	     		 		ohci_connect_change(ohci, port_nr);
-	     		 		writel(0xffff0000, &ohci->regs->roothub.portstatus[port_nr]);
-	     		 	}
-	     		 ohci->intrstatus &= ~(OHCI_INTR_RHSC);
-	     		 writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
-	    	}
-#endif
- 
-         interruptible_sleep_on(&root_hub);
- 
-      	} while (!signal_pending(current)); 
-             	
-#ifdef VROOTHUB
-		ohci_del_rh_int_timer(ohci);
-#endif
- 
-
-		cleanup_drivers();
-      /*  reset_hc(ohci); */
-	 
-        release_ohci(ohci);
-        MOD_DEC_USE_COUNT;
-
-	    printk("ohci_control_thread exiting\n");
-
-        return 0;
-}
-
-
- 
-
-/*
- * Increment the module usage count, start the control thread and
- * return success.
- */
-static int found_ohci(int irq, void* mem_base)
-{
-	int retval;
-	struct ohci *ohci;
-    OHCI_DEBUG(printk("USB HC found  ohci: irq= %d membase= %x \n", irq, (int)mem_base);)
-	/* Allocate the running OHCI structures */
-	ohci = alloc_ohci(mem_base);
-	if (!ohci) {
-	  return -ENOMEM;
-	}
-
-	reset_hc(ohci);
-
-	retval = -EBUSY;
-	if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) {
-		int pid;
-
-		MOD_INC_USE_COUNT; 
-		ohci->irq = irq;
-		 
-		pid = kernel_thread(ohci_roothub_thread, ohci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-		if (pid >= 0) 
-		 	return 0;
-		
-
-		MOD_DEC_USE_COUNT;
-		retval = pid;
-	}
-	
-	release_ohci(ohci);
-	return retval;
-}
- 
-static int start_ohci(struct pci_dev *dev)
-{
-	unsigned int mem_base = dev->base_address[0];
-
-	/* If its OHCI, its memory */
-	if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)
-		return -ENODEV;
-
-	/* Get the memory address and map it for IO */
-	mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
-
-	/* 
-	 * FIXME ioremap_nocache isn't implemented on all CPUs (such
-	 * as the Alpha) [?]  What should I use instead...
-	 *
-	 * The iounmap() is done on in release_ohci.
-	 */
-	mem_base = (unsigned int) ioremap_nocache(mem_base, 4096);
-
-	if (!mem_base) {
-		printk("Error mapping OHCI memory\n");
-		return -EFAULT;
-	}
-
-	return found_ohci(dev->irq, (void *) mem_base);
-} 
-
-
-
-#ifdef CONFIG_APM
-static int handle_apm_event(apm_event_t event)
-{
-	static int down = 0;
-
-	switch (event) {
-	case APM_SYS_SUSPEND:
-	case APM_USER_SUSPEND:
-		if (down) {
-			printk(KERN_DEBUG "ohci: received extra suspend event\n");
-			break;
-		}
-		down = 1;
-		break;
-	case APM_NORMAL_RESUME:
-	case APM_CRITICAL_RESUME:
-		if (!down) {
-			printk(KERN_DEBUG "ohci: received bogus resume event\n");
-			break;
-		}
-		down = 0;
-		if (waitqueue_active(&root_hub)) {
-			apm_resume = 1;
-			wake_up(&root_hub);
-		}
-		break;
-	}
-	return 0;
-}
-#endif
-
-
- int usb_mouse_init(void); 
-#ifdef MODULE
-
-void cleanup_module(void)
-{
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-}
-
-#define ohci_hcd_init init_module
-
-#endif
-
-#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
-#define PCI_CLASS_SERIAL_USB_OHCI_PG 0x10
- 
-
-int ohci_hcd_init(void)
-{
-  int retval;
-  struct pci_dev *dev = NULL;
- 
-  retval = -ENODEV;
-   
-  dev = NULL;
-  while((dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev))) { /* OHCI */
-    retval = start_ohci(dev);
-    if (retval < 0) break;
-
-    
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
-#ifdef CONFIG_APM
-		apm_register_callback(&handle_apm_event);
-#endif
-  
-    return 0;
-  }
-  return retval;
-}
-
-void cleanup_drivers(void)
-{
-	 hub_cleanup(); 
-#ifdef CONFIG_USB_MOUSE
-	 usb_mouse_cleanup();
-#endif
-}
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci-hcd.h linux.18p17/drivers/usb/ohci-hcd.h
--- linux.vanilla/drivers/usb/ohci-hcd.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci-hcd.h	Thu Jan  1 01:00:00 1970
@@ -1,404 +0,0 @@
- /*
- * OHCI HCD (Host Controller Driver) for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- *
- * The OHCI HCD layer is a simple but nearly complete implementation of what the
- * USB people would call a HCD  for the OHCI. 
- * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
- * The layer on top of it, is for interfacing to the alternate-usb device-drivers.
- * 
- * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ]
- * [ Open Host Controller Interface driver for USB. ]
- * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ]
- * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ]
- * [ $Log: ohci.c,v $ ]
- * [ Revision 1.1  1999/04/05 08:32:30  greg ]
- * 
- * 
- * v2.1 1999/05/09 ep_addr correction, code clean up
- * v2.0 1999/05/04 
- * v1.0 1999/04/27
- * ohci-hcd.h
- */
-
-#include <linux/config.h>
-
-#ifdef CONFIG_USB_OHCI_VROOTHUB
-#define VROOTHUB  
-#endif
-/* enables virtual root hub 
- * (root hub will be managed by the hub controller 
- *  hub.c of the alternate usb driver)
- *  last time I did more testing without virtual root hub 
- *  -> the virtual root hub could be more unstable now */
- 
- 
-  
-#ifdef OHCI_DBG
-#define OHCI_DEBUG(X) X
-#else 
-#define OHCI_DEBUG(X)
-#endif 
-
-/* for readl writel functions */
-#include <linux/list.h>
-#include <asm/io.h>
-
-/* for ED and TD structures */
-
-typedef void * __OHCI_BAG;
-typedef int (*f_handler )(void * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void *data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1);
-
-
-
-struct ep_address {
-  __u8 ep;  /* bit 7: IN/-OUT, 6,5: type 10..CTRL 00..ISO  11..BULK 10..INT, 3..0: ep nr */ 
-  __u8 fa;    /* function address */
-  __u8 hc;
-  __u8 host;
-};
-
-union ep_addr_ {
-  unsigned int iep;
-  struct ep_address bep;
-};
-
-/*
- * ED and TD descriptors has to be 16-byte aligned
- */
-struct ohci_hw_ed {
-  __u32 info;       
-  __u32 tail_td;	/* TD Queue tail pointer */
-  __u32 head_td;	/* TD Queue head pointer */
-  __u32 next_ed;	/* Next ED */
-} __attribute((aligned(16)));
-
-
-struct usb_ohci_ed {
-  struct ohci_hw_ed hw;
-  /*  struct ohci * ohci; */
-  f_handler handler;
-  union ep_addr_ ep_addr;
-  struct usb_ohci_ed *ed_list;
-  struct usb_ohci_ed *ed_prev;
-} __attribute((aligned(32)));
-
- /* OHCI Hardware fields */
-struct ohci_hw_td {     
-  __u32 info;
-  __u32 cur_buf;		/* Current Buffer Pointer */
-  __u32 next_td;		/* Next TD Pointer */
-  __u32 buf_end;		/* Memory Buffer End Pointer */
-} __attribute((aligned(16)));
-
-/* TD info field */
-#define TD_CC       0xf0000000
-#define TD_CC_GET(td_p) ((td_p >>28) & 0x04)
-#define TD_EC       0x0C000000
-#define TD_T        0x03000000
-#define TD_T_DATA0  0x02000000
-#define TD_T_DATA1  0x03000000
-#define TD_T_TOGGLE 0x00000000
-#define TD_R        0x00040000
-#define TD_DI       0x00E00000
-#define TD_DI_SET(X) (((X) & 0x07)<< 21)
-#define TD_DP       0x00180000
-#define TD_DP_SETUP 0x00000000
-#define TD_DP_IN    0x00100000
-#define TD_DP_OUT   0x00080000
-
-/* CC Codes */
-#define TD_CC_NOERROR      0x00
-#define TD_CC_CRC          0x01
-#define TD_CC_BITSTUFFING  0x02
-#define TD_CC_DATATOGGLEM  0x03
-#define TD_CC_STALL        0x04
-#define TD_DEVNOTRESP      0x05
-#define TD_PIDCHECKFAIL    0x06
-#define TD_UNEXPECTEDPID   0x07
-#define TD_DATAOVERRUN     0x08
-#define TD_DATAUNDERRUN    0x09
-#define TD_BUFFEROVERRUN   0x0C
-#define TD_BUFFERUNDERRUN  0x0D
-#define TD_NOTACCESSED     0x0F
-
-
-
-struct usb_ohci_td {
-  struct ohci_hw_td hw;
-  void *  buffer_start;
-  f_handler handler; 
-  struct usb_ohci_td *prev_td;
-  struct usb_ohci_ed *ep;
-  struct usb_ohci_td *next_dl_td;
-  __OHCI_BAG lw0;
-  __OHCI_BAG lw1;
-} __attribute((aligned(32)));
-
-
-
-/* TD types */
-#define BULK		0x03
-#define INT			0x01
-#define CTRL		0x02
-#define ISO			0x00
-/* TD types with direction */
-#define BULK_IN		0x07
-#define BULK_OUT	0x03
-#define INT_IN		0x05
-#define INT_OUT		0x01
-#define CTRL_IN		0x06
-#define CTRL_OUT	0x02
-#define ISO_IN		0x04
-#define ISO_OUT		0x00
-
-struct ohci_rep_td {
-  int cmd_len;
-  void * cmd;
-  void * data;
-  int data_len;
-  f_handler handler;
-  struct ohci_rep_td *next_td;
-  int ep_addr;
-  __OHCI_BAG lw0;
-  __OHCI_BAG lw1;
-  __u32 status;
-} __attribute((aligned(32))); 
-
-#define OHCI_ED_SKIP	(1 << 14)
-#define OHCI_ED_MPS	(0x7ff << 16)
-#define OHCI_ED_F_NORM	(0)
-#define OHCI_ED_F_ISOC	(1 << 15)
-#define OHCI_ED_S_LOW	(1 << 13)
-#define OHCI_ED_S_HIGH	(0)
-#define OHCI_ED_D	(3 << 11)
-#define OHCI_ED_D_IN	(2 << 11)
-#define OHCI_ED_D_OUT	(1 << 11)
-#define OHCI_ED_EN	(0xf << 7)
-#define OHCI_ED_FA	(0x7f)
-
- 
-/*
- * The HCCA (Host Controller Communications Area) is a 256 byte
- * structure defined in the OHCI spec. that the host controller is
- * told the base address of.  It must be 256-byte aligned.
- */
-#define NUM_INTS 32	/* part of the OHCI standard */
-struct ohci_hcca {
-    __u32	int_table[NUM_INTS];	/* Interrupt ED table */
-	__u16	frame_no;		/* current frame number */
-	__u16	pad1;			/* set to 0 on each frame_no change */
-	__u32	done_head;		/* info returned for an interrupt */
-	u8		reserved_for_hc[116];
-} __attribute((aligned(256)));
-
-  
-
-#define ED_INT_1	1
-#define ED_INT_2	2
-#define ED_INT_4	4
-#define ED_INT_8	8
-#define ED_INT_16	16
-#define ED_INT_32	32
-#define ED_CONTROL	64
-#define ED_BULK		65
-#define ED_ISO		0	/* same as 1ms interrupt queue */
- 
-
-/*
- * This is the maximum number of root hub ports.  I don't think we'll
- * ever see more than two as that's the space available on an ATX
- * motherboard's case, but it could happen.  The OHCI spec allows for
- * up to 15... (which is insane!)
- * 
- * Although I suppose several "ports" could be connected directly to
- * internal laptop devices such as a keyboard, mouse, camera and
- * serial/parallel ports.  hmm...  That'd be neat.
- */
-#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
-
-/*
- * This is the structure of the OHCI controller's memory mapped I/O
- * region.  This is Memory Mapped I/O.  You must use the readl() and
- * writel() macros defined in asm/io.h to access these!!
- */
-struct ohci_regs {
-	/* control and status registers */
-	__u32	revision;
-	__u32	control;
-	__u32	cmdstatus;
-	__u32	intrstatus;
-	__u32	intrenable;
-	__u32	intrdisable;
-	/* memory pointers */
-	__u32	hcca;
-	__u32	ed_periodcurrent;
-	__u32	ed_controlhead;
-	__u32	ed_controlcurrent;
-	__u32	ed_bulkhead;
-	__u32	ed_bulkcurrent;
-	__u32	donehead;
-	/* frame counters */
-	__u32	fminterval;
-	__u32	fmremaining;
-	__u32	fmnumber;
-	__u32	periodicstart;
-	__u32	lsthresh;
-	/* Root hub ports */
-	struct	ohci_roothub_regs {
-		__u32	a;
-		__u32	b;
-		__u32	status;
-		__u32	portstatus[MAX_ROOT_PORTS];
-	} roothub;
-} __attribute((aligned(32)));
-
-
-/* 
- * Read a MMIO register and re-write it after ANDing with (m)
- */
-#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
-
-/*
- * Read a MMIO register and re-write it after ORing with (b)
- */
-#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
-
-/*
- * cmdstatus register */
-#define OHCI_CLF  0x02
-#define OHCI_BLF  0x04
-
-/*
- * Interrupt register masks
- */
-#define OHCI_INTR_SO	(1)
-#define OHCI_INTR_WDH	(1 << 1)
-#define OHCI_INTR_SF	(1 << 2)
-#define OHCI_INTR_RD	(1 << 3)
-#define OHCI_INTR_UE	(1 << 4)
-#define OHCI_INTR_FNO	(1 << 5)
-#define OHCI_INTR_RHSC	(1 << 6)
-#define OHCI_INTR_OC	(1 << 30)
-#define OHCI_INTR_MIE	(1 << 31)
-
-/*
- * Control register masks
- */
-#define OHCI_USB_OPER		(2 << 6)
-#define OHCI_USB_SUSPEND	(3 << 6)
-
-/*
- * This is the full ohci controller description
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs. (Linus)
- */
-
-
-struct ohci {
-    	int irq;
-	    struct ohci_regs *regs;					/* OHCI controller's memory */	
-	    struct ohci_hc_area *hc_area;			/* hcca, int ed-tree, ohci itself .. */                
-        int root_hub_funct_addr;                /* Address of Root Hub endpoint */       
-        int ohci_int_load[32];                       /* load of the 32 Interrupt Chains (for load ballancing)*/     
-        struct usb_ohci_ed * ed_rm_list;        /* list of all endpoints to be removed */
-        struct usb_ohci_ed * ed_bulktail;       /* last endpoint of bulk list */
-        struct usb_ohci_ed * ed_controltail;    /* last endpoint of control list */
-        struct usb_ohci_ed * ed_isotail;        /* last endpoint of iso list */
-        struct ohci_device * root_hub;
-        struct usb_ohci_ed ed_rh_ep0;
-        struct usb_ohci_ed ed_rh_epi;
-        struct ohci_rep_td *td_rh_epi;
-        int intrstatus;
-        struct usb_ohci_ed *ed_func_ep0[128];   /* "hash"-table for ep to ed mapping */
-        struct ohci_rep_td *repl_queue;			/* for internal requests */
-        int rh_int_interval;
-        int rh_int_timer;   
-        struct usb_bus *bus;
-       
-       
-};
-
-/*
- *  Warning: This constant must not be so large as to cause the
- *  ohci_device structure to exceed one 4096 byte page.  Or "weird
- *  things will happen" as the alloc_ohci() function assumes that
- *  its less than one page at the moment.  (FIXME)
- */
-#define NUM_TDS	4		/* num of preallocated transfer descriptors */
-#define NUM_EDS 80		/* num of preallocated endpoint descriptors */
-
-struct ohci_hc_area {
-
-	struct ohci_hcca 	hcca;		/* OHCI mem. mapped IO area 256 Bytes*/
-
-	struct ohci_hw_ed		ed[NUM_EDS];	/* Endpoint Descriptors 80 * 16  : 1280 Bytes */
-	struct ohci_hw_td		td[NUM_TDS];	/* Transfer Descriptors 2 * 32   : 64 Bytes */
-        struct ohci             ohci;
-        
-};
-struct ohci_device {
-	struct usb_device	*usb;
-	struct ohci			*ohci;
-	unsigned long		data[16];
-};
-
-#define ohci_to_usb(uhci)	((ohci)->usb)
-#define usb_to_ohci(usb)	((struct ohci_device *)(usb)->hcpriv)
-
-/* Debugging code */
-/*void show_ed(struct ohci_ed *ed);
-void show_td(struct ohci_td *td);
-void show_status(struct ohci *ohci); */
-
-/* hcd */
-int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int cmd_len, void  *cmd, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1); 
-struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr, int interval, int load, f_handler handler, int ep_size, int speed);
-int usb_ohci_rm_function(struct ohci * ohci, unsigned int ep_addr); 
-int usb_ohci_rm_ep(struct ohci * ohci, struct  usb_ohci_ed *ed);
-struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in);
-
-/* roothub */
-int ohci_del_rh_int_timer(struct ohci * ohci);
-int	ohci_init_rh_int_timer(struct ohci * ohci, int interval);  	
-int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void *  data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
-int root_hub_send_irq(struct ohci * ohci, void * data, int data_len );
-int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
-int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int  len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
-int send_replies(struct ohci * ohci);
-
-  
- 
-
-/* Root-Hub Register info */
-
-#define RH_PS_CCS            0x00000001   
-#define RH_PS_PES            0x00000002   
-#define RH_PS_PSS            0x00000004   
-#define RH_PS_POCI           0x00000008   
-#define RH_PS_PRS            0x00000010  
-#define RH_PS_PPS            0x00000100   
-#define RH_PS_LSDA           0x00000200    
-#define RH_PS_CSC            0x00010000 
-#define RH_PS_PESC           0x00020000   
-#define RH_PS_PSSC           0x00040000    
-#define RH_PS_OCIC           0x00080000    
-#define RH_PS_PRSC           0x00100000   
-
-
-#ifdef OHCI_DBG
-#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d\n", -- __ohci_free_cnt)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("OHCI ALLO: %d\n", ++ __ohci_free_cnt)
-#define USB_FREE(x) kfree(x); printk("USB FREE: %d\n", -- __ohci_free1_cnt)
-#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("USB ALLO: %d\n", ++ __ohci_free1_cnt)
-static int __ohci_free_cnt = 0;
-static int __ohci_free1_cnt = 0;
-#else
-#define OHCI_FREE(x) kfree(x) 
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) 
-#define USB_FREE(x) kfree(x) 
-#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) 
-#endif
- 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci-root-hub.c linux.18p17/drivers/usb/ohci-root-hub.c
--- linux.vanilla/drivers/usb/ohci-root-hub.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci-root-hub.c	Thu Jan  1 01:00:00 1970
@@ -1,604 +0,0 @@
-/*
- * HCD (OHCI) Virtual Root Hub for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at)
- *
- * The Root Hub is build into the HC (UHCI or OHCI) hardware. 
- * This piece of code lets it look like it resides on the usb
- * like the other hubs.
- * (for anyone who wants to do a control operation on the root hub)
- *  
- * v2.1 1999/05/09 
- * v2.0 1999/05/04
- * v1.0 1999/04/27
- * ohci-root-hub.c
- *  
- */
- 
-
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-
-#include "usb.h"
-#include "ohci-hcd.h" 
-
-#ifdef VROOTHUB 
-
-#include "ohci-root-hub.h"
-
-
-static __u8 root_hub_dev_des[] =
-{
-        0x12,       /*  __u8  bLength; */
-		0x01,       /*  __u8  bDescriptorType; Device */
-		0x00,	    /*  __u16 bcdUSB; v1.0 */
-        0x01,
-		0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
-		0x00,	    /*  __u8  bDeviceSubClass; */
-		0x00,       /*  __u8  bDeviceProtocol; */
-		0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
-		0x00,       /*  __u16 idVendor; */
-        0x00,
-		0x00,       /*  __u16 idProduct; */
-        0x00,
-		0x00,       /*  __u16 bcdDevice; */
-        0x00,
-		0x00,       /*  __u8  iManufacturer; */
-		0x00,       /*  __u8  iProduct; */
-		0x00,       /*  __u8  iSerialNumber; */
-        0x01        /*  __u8  bNumConfigurations; */
-};
-
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
-		0x09,       /*  __u8  bLength; */
-		0x02,       /*  __u8  bDescriptorType; Configuration */
-		0x19,       /*  __u16 wTotalLength; */
-        0x00,
-		0x01,       /*  __u8  bNumInterfaces; */
-		0x01,       /*  __u8  bConfigurationValue; */
-		0x00,       /*  __u8  iConfiguration; */
-		0x40,       /*  __u8  bmAttributes; 
-                 Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
-		0x00,       /*  __u8  MaxPower; */
-      
-     /* interface */	  
-        0x09,       /*  __u8  if_bLength; */
-        0x04,       /*  __u8  if_bDescriptorType; Interface */
-        0x00,       /*  __u8  if_bInterfaceNumber; */
-        0x00,       /*  __u8  if_bAlternateSetting; */
-        0x01,       /*  __u8  if_bNumEndpoints; */
-        0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
-        0x00,       /*  __u8  if_bInterfaceSubClass; */
-        0x00,       /*  __u8  if_bInterfaceProtocol; */
-        0x00,       /*  __u8  if_iInterface; */
-     
-     /* endpoint */
-        0x07,       /*  __u8  ep_bLength; */
-        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
-        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-        0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-        0x40,       /*  __u16 ep_wMaxPacketSize; 64 Bytes */
-        0x00,
-        0xff        /*  __u8  ep_bInterval; 255 ms */
-};
-
-/* 
-For OHCI we need just the  2nd Byte, so we 
-don't need this constant byte-array 
-
-static __u8 root_hub_hub_des[] =
-{ 
-        0x00,       *  __u8  bLength; *
-		0x29,       *  __u8  bDescriptorType; Hub-descriptor *
-        0x02,       *  __u8  bNbrPorts; *
-        0x00,       * __u16  wHubCharacteristics; *
-        0x00,
-        0x01,       *  __u8  bPwrOn2pwrGood; 2ms * 
-        0x00,       *  __u8  bHubContrCurrent; 0 mA *
-        0x00,       *  __u8  DeviceRemovable; *** 8 Ports max *** *
-        0xff        *  __u8  PortPwrCtrlMask; *** 8 ports max *** *
-};
-*/
-
-
-int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int leni, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler)
-{ 
-
-  __u32 stat;
-  __u32 rep_handler;
-  int req_reply=0;
-  union ep_addr_ ep_addr;
-  union ep_addr_ ep_addr_ret;
-        __u8 * cmd = rh_cmd;
-	__u8 * data = rh_data;
-	int i;
-	int len =leni;
-
-        __u8  bmRequestType = cmd[0];
-        __u8  bRequest      = cmd[1];
-        __u16 wValue        = cmd[3] << 8 | cmd [2];
-        __u16 wIndex        = cmd[5] << 8 | cmd [4];
-        __u16 wLength       = cmd[7] << 8 | cmd [6];
-printk("USB root hub: adr: %8x cmd(%8x): ", ohci->root_hub_funct_addr, 8);
-for(i=0;i<8;i++)
-	printk("%2x", ((char *)rh_cmd)[i]);
- 
-printk(" ; \n");
-
-	ep_addr_ret.iep = 0;
-	ep_addr_ret.bep.fa = ohci->root_hub_funct_addr;
-	ep_addr_ret.bep.ep =  (bmRequestType & 0x80) | 0x40;
-
-  switch (bmRequestType | bRequest << 8) {
-   /* Request Destination:
-      without flags: Device, 
-      RH_INTERFACE: interface, 
-      RH_ENDPOINT: endpoint,
-      RH_CLASS means HUB here, 
-      RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
-   */
-  
-   case RH_GET_STATUS:
-     len = 2; 
-     data[0] = 0x01;
-     data[1] = 0x00;
-     req_reply = RH_ACK; 
-     break;
-   case RH_GET_STATUS | RH_INTERFACE:
-     len = 2; 
-     data[0] = 0x00;
-     data[1] = 0x00;     
-     req_reply = RH_ACK;
-     break;
-   case RH_GET_STATUS | RH_ENDPOINT:
-     len = 2; 
-     data[0] = 0x00;
-     data[1] = 0x00;     
-     req_reply = RH_ACK;
-     break;
-   case RH_GET_STATUS | RH_CLASS: /* HUB_STATUS */
-     stat = readl(&ohci->regs->roothub.status) & 0x7fff7fff; /* bit 31 u. 15 has other meaning */ 
-     data[0] = stat & 0xff;
-     data[1] = (stat >> 8) & 0xff;
-     data[2] = (stat >> 16) & 0xff;
-     data[3] = (stat >> 24) & 0xff;
-     len = 4;
-     req_reply = RH_ACK; 
-     break;
-   case RH_GET_STATUS | RH_OTHER | RH_CLASS: /* PORT_STATUS */
-     stat = readl(&ohci->regs->roothub.portstatus[wIndex-1]);
-     data[0] = stat & 0xff;
-     data[1] = (stat >> 8) & 0xff;
-     data[2] = (stat >> 16) & 0xff;
-     data[3] = (stat >> 24) & 0xff;
-     len = 4;
-     req_reply = RH_ACK; 
-     printk("rh: stat %4x wIndex %4x;\n", stat , wIndex);
-     break;
-
-   case RH_CLEAR_FEATURE:
-     switch (wValue) {
-       case (RH_DEVICE_REMOTE_WAKEUP):
-       default:
-     }
-     break;
-
-   case RH_CLEAR_FEATURE | RH_ENDPOINT:  
-     switch (wValue) {
-       case (RH_ENDPOINT_STALL): 
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       default:
-     }
-     break;
-
-   case RH_CLEAR_FEATURE | RH_CLASS:
-     switch (wValue) {
-       /*     case (RH_C_HUB_LOCAL_POWER):  OHCI says: no switching of this one */
-       case (RH_C_HUB_OVER_CURRENT):
-         writel(RH_PS_OCIC, &ohci->regs->roothub.status);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       default:
-     }
-     break;
-   case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
-     switch (wValue) {
-       case (RH_PORT_ENABLE):
-	     writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_PORT_SUSPEND):
-         writel(RH_PS_POCI, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_PORT_POWER):
-         writel(RH_PS_LSDA, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_C_PORT_CONNECTION):
-         writel(RH_PS_CSC, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_C_PORT_ENABLE):
-         writel(RH_PS_PESC, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_C_PORT_SUSPEND):
-         writel(RH_PS_PSSC, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_C_PORT_OVER_CURRENT):
-         writel(RH_PS_OCIC, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_C_PORT_RESET):
-         writel(RH_PS_PRSC, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       /*
-       case (RH_PORT_CONNECTION):
-       case (RH_PORT_OVER_CURRENT):
-       case (RH_PORT_RESET):
-       case (RH_PORT_LOW_SPEED):
-       */
-       default:
-     }
-     break;
-   case RH_SET_FEATURE:  
-     switch (wValue) {
-       case (RH_DEVICE_REMOTE_WAKEUP):
-       default:
-     }
-     break;
-
-   case RH_SET_FEATURE | RH_ENDPOINT:
-     switch (wValue) {
-       case (RH_ENDPOINT_STALL):
-       default:
-     }
-     break;
-
-   case RH_SET_FEATURE | RH_CLASS: 
-     switch (wValue) {
-       /* case (RH_C_HUB_LOCAL_POWER): Root Hub has no local Power 
-       case (RH_C_HUB_OVER_CURRENT): */
-      default:
-     }
-     break;
-   case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
-     switch (wValue) {
-       case (RH_PORT_SUSPEND):
-         writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_PORT_RESET):
-         if((readl(&ohci->regs->roothub.portstatus[wIndex-1]) &1) != 0)  /* BUG IN HUP CODE *********/
-         writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_PORT_POWER):
-         writel(RH_PS_PPS, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       case (RH_PORT_ENABLE):
-         writel(RH_PS_PES, &ohci->regs->roothub.portstatus[wIndex-1]);
-         len=0;
-         req_reply = RH_ACK;
-         break;
-       /*
-       case (RH_PORT_CONNECTION):
-       case (RH_PORT_OVER_CURRENT):
-       case (RH_PORT_LOW_SPEED):
-       case (RH_C_PORT_CONNECTION):
-       case (RH_C_PORT_ENABLE):
-       case (RH_C_PORT_SUSPEND):
-       case (RH_C_PORT_OVER_CURRENT):
-       case (RH_C_PORT_RESET):
-       */
-       default:  
-     }
-     break;
-
-  case RH_SET_ADDRESS:
-     ohci->root_hub_funct_addr = wValue; 
-	/* ohci->ed_func_ep0[wValue] = &ohci->ed_rh_ep0;
-	 ohci->ed_func_ep0[wValue]->ep_addr.bep.fa = wValue; 
-	 ohci->ed_func_ep0[wValue]->ed_list = &ohci->ed_rh_epi; */
-	 ohci->ed_rh_epi.ed_list = NULL;
-	 ohci->ed_rh_epi.ep_addr.bep.fa = wValue;
-	 ohci->ed_rh_epi.ep_addr.bep.ep = 0xa1; /* Int in port 1 */
-     ohci->ed_func_ep0[0]= NULL;
-     len = 0;
-     req_reply = RH_ACK;
-     break;
-
-   case RH_GET_DESCRIPTOR:
-     switch ((wValue & 0xff00) >> 8) {
-     case (0x01): /* device descriptor */
-       len = min(sizeof(root_hub_dev_des), wLength);
-       memcpy(data, root_hub_dev_des, len);
-       req_reply = RH_ACK;
-       break;
-     case (0x02): /* configuration descriptor */
-       len = min(sizeof(root_hub_config_des), wLength);
-       memcpy(data, root_hub_config_des, len);
-       req_reply = RH_ACK;
-       break;
-     case (0x03): /* string descriptors */
-     default:
-     }
-     break;
-  case RH_GET_DESCRIPTOR | RH_CLASS:
-     data[1] = 0x29;
-     stat = readl(&ohci->regs->roothub.a);  
-     data[2] = stat & 0xff;        /* number of ports */
-     data[0] = (data[2] / 8) * 2 + 9; /* length of descriptor */
-     if(data[0] > wLength) {
-       req_reply =  RH_REQ_ERR;
-       break;
-     }
-     data[3] = (stat >> 8) & 0xff;
-     data[4] = (stat >> 16) & 0xff;
-     data[5] = (stat >> 24) & 0xff;
-     data[6] = 0; /* Root Hub needs no current from bus */
-     stat = readl(&ohci->regs->roothub.b); 
-     if(data[2] <= 8) { /* less than 8 Ports */
-       data[7] = stat & 0xff;
-       data[8] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1 ?, stat >> 16 for USB Rev. 1.0 */
-     }
-     else {
-       data[7] = stat & 0xff;
-       data[8] = (stat >> 8) & 0xff;
-       data[9] = (stat >> 16) & 0xff;  /* 0xff for USB Rev. 1.1?, stat >> 16 for USB Rev. 1.0 */
-       data[10] = (stat >> 24) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 24 for USB Rev. 1.0 */
-     }
-     len = data[0];
-     req_reply = RH_ACK;
-     break;
-
-   case RH_SET_DESCRIPTOR:
-     break;
-
-   case RH_GET_CONFIGURATION:
-     len = 1;
-     data[0] = 0x01;
-     req_reply = RH_ACK;
-     break;
-
-   case RH_SET_CONFIGURATION: /* start it up */
-     writel( 0x10000, &ohci->regs->roothub.status);
-     /*writel( OHCI_INTR_RHSC, &ohci->regs->intrenable);*/
-     len = 0;
-     req_reply = RH_ACK;
-     break;
-   /*  Optional or meaningless requests 
-     case RH_GET_STATE | RH_OTHER | RH_CLASS:
-     case RH_GET_INTERFACE | RH_INTERFACE:
-     case RH_SET_INTERFACE | RH_INTERFACE:
-     case RH_SYNC_FRAME | RH_ENDPOINT: 
-   */
-
-  /* Vendor Requests, we are the vendor!
-	Will the USB-Consortium give us a Vendor Id
-	for a virtual hub-device :-) ?
-     We could use these requests for configuration purposes on the HCD Driver, not used in the altenate usb !*/ 
-
-  case RH_SET_FEATURE | RH_VENDOR: /* remove all endpoints of device wIndex = Dev << 8  */
-    switch(wValue) {
-    case RH_REMOVE_EP:
-      ep_addr.iep = 0;
-      ep_addr.bep.ep = wIndex & 0xff;
-      ep_addr.bep.fa = (wIndex << 8) & 0xff00;
-      usb_ohci_rm_function(ohci, ep_addr.iep);
-      len=0;
-      req_reply = RH_ACK;
-      break;
-    }
-    break;
-  case RH_SET_FEATURE | RH_ENDPOINT | RH_VENDOR: /* remove endpoint wIndex = Dev << 8 | EP */
-    switch(wValue) {
-    case RH_REMOVE_EP: 
-      ep_addr.iep = 0;
-      ep_addr.bep.ep = wIndex & 0xff;
-      ep_addr.bep.fa = (wIndex << 8) & 0xff00;
-      usb_ohci_rm_ep(ohci, ohci_find_ep(ohci, ep_addr.iep));
-      len=0;
-      req_reply = RH_ACK;
-      break;
-    }
-    break;
-  case RH_SET_EP | RH_ENDPOINT | RH_VENDOR:   
-    ep_addr.bep.ep = data[0];
-    ep_addr.bep.fa = data[1];
-    ep_addr.bep.hc = data[2];
-    ep_addr.bep.host = data[3];  
-    rep_handler  = data[7] << 24 |data[6] << 16 | data[5] << 8 | data[4];
-    /*  struct usb_ohci_ed *usb_ohci_add_ep(union ep_addr_ ep_addr,
-	struct ohci * ohci, int interval, int load, int (*handler)(int, void*), int ep_size, int speed) */
-    usb_ohci_add_ep(ohci, ep_addr.iep, data[8], data[9], (f_handler) rep_handler, data[11] << 8 | data[10] , data[12]);
-    len=0;
-    req_reply = RH_ACK;
-    break;
-
-  default: 
-  }
- printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) ));
-  printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) ));
-
-  /* if (req_reply == RH_ACK) len; */
-  queue_reply(ohci, ep_addr_ret.iep, 8, rh_cmd, data, len, lw0, lw1, handler);
-  return 0;
-}
-
-int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void *  data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler){
-
-	struct ohci_rep_td *td;
- 	struct ohci_rep_td *tmp;
- 	union ep_addr_ ep_addr;
- 	
- 	td = kmalloc(sizeof(td),GFP_KERNEL);
- 	tmp = ohci->td_rh_epi;
- 	td->next_td = NULL;
- 	if(tmp == NULL) { /* queue td */
- 		ohci->td_rh_epi = td;	
- 	}
- 	else {
- 		while(tmp->next_td != NULL) tmp = tmp->next_td;
- 		tmp->next_td = td;
- 	}
- 	ep_addr.iep = 0;
- 	ep_addr.bep.fa = ohci->root_hub_funct_addr;
- 	ep_addr.bep.ep = 0xA1; /* INT IN EP endpoint 1 */
-  	td->cmd_len = 0;
-  	td->cmd = NULL;
-  	td->data = data;
-  	td->data_len = data_len;
-  	td->handler = handler; 
-  	td->next_td = NULL;
-  	td->ep_addr = ep_addr.iep;
-	td->lw0 = lw0;
-	td->lw1 = lw1;
-	ohci_init_rh_int_timer(ohci, 255);
-	return 0;
-}
-
-
-/* prepare Interrupt pipe transaction data; HUP INTERRUPT ENDPOINT */ 
-int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) {
-
-  int num_ports;
-  int i;
-  int ret;
-  int len;
-
-  __u8 * data = rh_data;
-
-  num_ports = readl(&ohci->regs->roothub.a) & 0xff; 
-  data[0] = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0;
-  ret = data[0];
-
-  for(i=0; i < num_ports; i++) {
-    data[i/8] |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8);
-    ret += data[i/8];
-  }
-  len = i/8 + 1;
-  
-  if (ret > 0) return len;
-
-  return  RH_NACK;
-}
-
-
-
-
- 
-static struct timer_list rh_int_timer;
-
-/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-static void rh_int_timer_do(unsigned long ptr) {
-	int len; 
-	int interval;
-	struct ohci * ohci = (struct ohci *) ptr;
-    struct ohci_rep_td *td = ohci->td_rh_epi;
-
-	if(td != NULL) { /* if ther is a TD handle the INT request */
-		
-		len = root_hub_send_irq(ohci, td->data, td->data_len );
-		if(len > 0) {
-			ohci->td_rh_epi = td->next_td;
-			td->next_td = ohci->repl_queue; 
-			ohci->repl_queue = td;
-			send_replies(ohci);
-		}
-	}	
-	interval = ohci->rh_int_interval;
-	init_timer(& rh_int_timer);
-	rh_int_timer.function = rh_int_timer_do;
-	rh_int_timer.data = (unsigned long) ohci;
-	rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
-	add_timer(&rh_int_timer);
-}
-
-/* Root Hub INTs are polled by this timer */
-int ohci_init_rh_int_timer(struct ohci * ohci, int interval) {
-
-	if(!(ohci->rh_int_timer)) { 
-		ohci->rh_int_timer = 1;
-		ohci->rh_int_interval = interval;
-		init_timer(& rh_int_timer);
-		rh_int_timer.function = rh_int_timer_do;
-		rh_int_timer.data = (unsigned long) ohci;
-		rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
-		add_timer(&rh_int_timer);
-	}
-	return 0;
-}
-
-int ohci_del_rh_int_timer(struct ohci * ohci) {
-	del_timer(&rh_int_timer);
-	return 0;
-}
-/* for root hub replies, queue the reply, (it will be sent immediately now) */
-
-int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int  len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) {
-
- struct ohci_rep_td *td;
- int status = 0;
- 
-printk("queue_reply ep: %x len: %x\n", ep_addr, len);
-td = kmalloc(sizeof(td), GFP_KERNEL);
-
-  if (len < 0) { status = len; len = 0;}
-  td->cmd_len = cmd_len;
-  td->cmd = cmd;
-  td->data = data;
-  td->data_len = len;
-  td->handler = handler; 
-  td->next_td = ohci->repl_queue; ohci->repl_queue = td;
-  td->ep_addr = ep_addr;
-  td->lw0 = lw0;
-  td->lw1 = lw1;
-  td->status = status;
-  send_replies(ohci);
-return 0;
-}
-
-/* for root hub replies; send the reply */
-int send_replies(struct ohci * ohci) { 
-	struct ohci_rep_td *td;
-	struct ohci_rep_td *tmp;
-
-	td = ohci->repl_queue; ohci->repl_queue = NULL;
-	while ( td != NULL) {
-		td->handler((void *) ohci, td->ep_addr,td->cmd_len,td->cmd, td->data, td->data_len, td->status, td->lw0, td->lw1); 
-		tmp = td;
-		td = td->next_td;
-	
-		kfree(tmp);
-	}
-	return 0;
-}
- 
-#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci-root-hub.h linux.18p17/drivers/usb/ohci-root-hub.h
--- linux.vanilla/drivers/usb/ohci-root-hub.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci-root-hub.h	Thu Jan  1 01:00:00 1970
@@ -1,71 +0,0 @@
-/*
- * HCD (OHCI) Virtual Root Hub Protocol for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at)
- *
- * The Root Hub is build into the HC (UHCI or OHCI) hardware. 
- * This piece of code lets it look like it resides on the bus 
- * like the other hubs.
- * (for anyone who wants to do a control operation on the root hub)
- *  
- * v1.0 1999/04/27
- * ohci-root-hub.h
- *  
- */
- 
-/* destination of request */
-#define RH_INTERFACE               0x01
-#define RH_ENDPOINT                0x02
-#define RH_OTHER                   0x03
-
-#define RH_CLASS                   0x20
-#define RH_VENDOR                  0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS           0x0080
-#define RH_CLEAR_FEATURE        0x0100
-#define RH_SET_FEATURE          0x0300
-#define RH_SET_ADDRESS			0x0500
-#define RH_GET_DESCRIPTOR		0x0680
-#define RH_SET_DESCRIPTOR       0x0700
-#define RH_GET_CONFIGURATION	0x0880
-#define RH_SET_CONFIGURATION	0x0900
-#define RH_GET_STATE            0x0280
-#define RH_GET_INTERFACE        0x0A80
-#define RH_SET_INTERFACE        0x0B00
-#define RH_SYNC_FRAME           0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP               0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION         0x00
-#define RH_PORT_ENABLE             0x01
-#define RH_PORT_SUSPEND            0x02
-#define RH_PORT_OVER_CURRENT       0x03
-#define RH_PORT_RESET              0x04
-#define RH_PORT_POWER              0x08
-#define RH_PORT_LOW_SPEED          0x09
-#define RH_C_PORT_CONNECTION       0x10
-#define RH_C_PORT_ENABLE           0x11
-#define RH_C_PORT_SUSPEND          0x12
-#define RH_C_PORT_OVER_CURRENT     0x13
-#define RH_C_PORT_RESET            0x14  
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER       0x00
-#define RH_C_HUB_OVER_CURRENT      0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP    0x00
-#define RH_ENDPOINT_STALL          0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP               0x00
-
-
-#define RH_ACK                     0x01
-#define RH_REQ_ERR                 -1
-#define RH_NACK                    0x00
- 
-#define min(a,b) (((a)<(b))?(a):(b))  
- 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci.c linux.18p17/drivers/usb/ohci.c
--- linux.vanilla/drivers/usb/ohci.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci.c	Thu Jan  1 01:00:00 1970
@@ -1,1669 +0,0 @@
-/*
- * Open Host Controller Interface driver for USB.
- *
- * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
- *
- * This is the "other" host controller interface for USB.  You will
- * find this on many non-Intel based motherboards, and of course the
- * Mac.  As Linus hacked his UHCI driver together first, I modeled
- * this after his.. (it should be obvious)
- *
- * From the programming standpoint the OHCI interface seems a little
- * prettier and potentially less CPU intensive.  This remains to be
- * proven.  In reality, I don't believe it'll make one darn bit of
- * difference.  USB v1.1 is a slow bus by today's standards.
- *
- * OHCI hardware takes care of most of the scheduling of different
- * transfer types with the correct prioritization for us.
- *
- * To get started in USB, I used the "Universal Serial Bus System
- * Architecture" book by Mindshare, Inc.  It was a reasonable introduction
- * and overview of USB and the two dominant host controller interfaces
- * however you're better off just reading the real specs available
- * from www.usb.org as you'll need them to get enough detailt to
- * actually implement a HCD.  The book has many typos and omissions
- * Beware, the specs are the victim of a committee.
- *
- * This code was written with Guinness on the brain, xsnow on the desktop
- * and Orbital, Orb, Enya & Massive Attack on the CD player.  What a life!  ;) 
- *
- * No filesystems were harmed in the development of this code.
- *
- * $Id: ohci.c,v 1.26 1999/05/11 07:34:47 greg Exp $
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-
-#include <asm/spinlock.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "ohci.h"
-#include "inits.h"
-
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-static int handle_apm_event(apm_event_t event);
-static int apm_resume = 0;
-#endif
-
-static struct wait_queue *ohci_configure = NULL;
-
-#ifdef OHCI_TIMER
-static struct timer_list ohci_timer;	/* timer for root hub polling */
-#endif
-
-
-static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td)
-{
-	unsigned int status;
-
-	status = td->info & OHCI_TD_CC;
-
-	/* TODO Debugging code for TD failures goes here */
-
-	return status;
-} /* ohci_td_result() */
-
-
-static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED;
-
-/*
- * Add a TD to the end of the TD list on a given ED.  If td->next_td
- * points to any more TDs, they will be added as well (naturally).
- * Otherwise td->next_td must be 0.
- * 
- * The SKIP flag will be cleared after this function.
- *
- * Important!  This function needs locking and atomicity as it works
- * in parallel with the HC's DMA.  Locking ohci_edtd_lock while using
- * the function is a must.
- *
- * This function can be called by the interrupt handler.
- */
-static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed)
-{
-	/* don't let the HC pull anything from underneath us */
-	ed->status |= OHCI_ED_SKIP;
-
-	if (ed_head_td(ed) == 0) {	/* empty list, put it on the head */
-		set_ed_head_td(ed, virt_to_bus(td));
-		ed->tail_td = 0;
-	} else {
-		struct ohci_td *tail, *head;
-		head = (ed_head_td(ed) == 0) ? NULL : bus_to_virt(ed_head_td(ed));
-		tail = (ed->tail_td == 0) ? NULL : bus_to_virt(ed->tail_td);
-		if (!tail) {	/* no tail, single element list */
-			td->next_td = head->next_td;
-			head->next_td = virt_to_bus(td);
-			ed->tail_td = virt_to_bus(td);
-		} else {	/* append to the list */
-			td->next_td = tail->next_td;
-			tail->next_td = virt_to_bus(td);
-			ed->tail_td = virt_to_bus(td);
-		}
-	}
-
-	/* save the ED link in each of the TDs added */
-	td->ed = ed;
-	while (td->next_td != 0) {
-		td = bus_to_virt(td->next_td);
-		td->ed = ed;
-	}
-
-	/* turn off the SKIP flag */
-	ed->status &= ~OHCI_ED_SKIP;
-} /* ohci_add_td_to_ed() */
-
-
-inline void ohci_start_control(struct ohci *ohci)
-{
-	/* tell the HC to start processing the control list */
-	writel(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus);
-}
-
-inline void ohci_start_bulk(struct ohci *ohci)
-{
-	/* tell the HC to start processing the bulk list */
-	writel(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus);
-}
-
-inline void ohci_start_periodic(struct ohci *ohci)
-{
-	/* enable processing periodc transfers starting next frame */
-	writel_set(OHCI_USB_PLE, &ohci->regs->control);
-}
-
-inline void ohci_start_isoc(struct ohci *ohci)
-{
-	/* enable processing isoc. transfers starting next frame */
-	writel_set(OHCI_USB_IE, &ohci->regs->control);
-}
-
-/*
- * Add an ED to the hardware register ED list pointed to by hw_listhead_p
- */
-static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p)
-{
-	__u32 listhead;
-	unsigned long flags;
-
-	spin_lock_irqsave(&ohci_edtd_lock, flags);
-	
-	listhead = readl(hw_listhead_p);
-
-	/* if the list is not empty, insert this ED at the front */
-	/* XXX should they go on the end? */
-	if (listhead) {
-		ed->next_ed = listhead;
-	}
-
-	/* update the hardware listhead pointer */
-	writel(virt_to_bus(ed), hw_listhead_p);
-
-	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-} /* ohci_add_ed() */
-
-
-/*
- *  Put another control ED on the controller's list
- */
-void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed)
-{
-	ohci_add_ed_to_hw(ed, &ohci->regs->ed_controlhead);
-	ohci_start_control(ohci);
-} /* ohci_add_control_ed() */
-
-
-#if 0
-/*
- *  Put another control ED on the controller's list
- */
-void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period)
-{
-	ohci_add_ed_to_hw(ed, /* XXX */);
-	ohci_start_periodic(ohci);
-} /* ohci_add_control_ed() */
-#endif
-
-
-/*
- *  Remove an ED from the HC list whos bus headpointer is pointed to
- *  by hw_listhead_p
- *  
- *  Note that the SKIP bit is left on in the removed ED.
- */
-void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p)
-{
-	unsigned long flags;
-	struct ohci_ed *cur;
-	__u32 bus_ed = virt_to_bus(ed);
-	__u32 bus_cur;
-
-	if (ed == NULL || !bus_ed)
-		return;
-
-	/* tell the controller this skip ED */
-	ed->status |= OHCI_ED_SKIP;
-
-	bus_cur = readl(hw_listhead_p);
-
-	if (bus_cur == 0)
-		return;   /* the list is already empty */
-
-	cur = bus_to_virt(bus_cur);
-
-	spin_lock_irqsave(&ohci_edtd_lock, flags);
-
-	/* if its the head ED, move the head */
-	if (bus_cur == bus_ed) {
-		writel(cur->next_ed, hw_listhead_p);
-	} else if (cur->next_ed != 0) {
-		struct ohci_ed *prev;
-
-		/* walk the list and unlink the ED if found */
-		for (;;) {
-			prev = cur;
-			cur = bus_to_virt(cur->next_ed);
-
-			if (virt_to_bus(cur) == bus_ed) {
-				/* unlink from the list */
-				prev->next_ed = cur->next_ed;
-				break;
-			}
-
-			if (cur->next_ed == 0)
-				break;
-		}
-	}
-
-	/* clear any links from the ED for safety */
-	ed->next_ed = 0;
-
-	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-} /* ohci_remove_ed_from_hw() */
-
-/*
- *  Remove an ED from the controller's control list.  Note that the SKIP bit
- *  is left on in the removed ED.
- */
-inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed)
-{
-	ohci_remove_ed_from_hw(ed, &ohci->regs->ed_controlhead);
-}
-
-/*
- *  Remove an ED from the controller's bulk list.  Note that the SKIP bit
- *  is left on in the removed ED.
- */
-inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed)
-{
-	ohci_remove_ed_from_hw(ed, &ohci->regs->ed_bulkhead);
-}
-
-
-/*
- *  Remove a TD from the given EDs TD list.
- */
-static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed)
-{
-	unsigned long flags;
-	struct ohci_td *head_td;
-
-	if ((td == NULL) || (ed == NULL))
-		return;
-
-	spin_lock_irqsave(&ohci_edtd_lock, flags);
-
-	if (ed_head_td(ed) == 0)
-		return;
-
-	/* set the "skip me bit" in this ED */
-	ed->status |= OHCI_ED_SKIP;
-
-	/* XXX Assuming this list will never be circular */
-
-	head_td = bus_to_virt(ed_head_td(ed));
-	if (virt_to_bus(td) == ed_head_td(ed)) {
-		/* It's the first TD, remove it. */
-		set_ed_head_td(ed, head_td->next_td);
-	} else {
-		struct ohci_td *prev_td, *cur_td;
-
-		/* FIXME: collapse this into a nice simple loop :) */
-		if (head_td->next_td != 0) {
-			prev_td = head_td;
-			cur_td = bus_to_virt(head_td->next_td);
-			for (;;) {
-				if (td == cur_td) {
-					/* remove it */
-					prev_td->next_td = cur_td->next_td;
-					break;
-				}
-				if (cur_td->next_td == 0)
-					break;
-				prev_td = cur_td;
-				cur_td = bus_to_virt(cur_td->next_td);
-			}
-		}
-	}
-
-	td->next_td = 0;  /* remove the TDs links */
-	td->ed = NULL;
-
-	/* TODO return this TD to the pool of free TDs */
-
-	/* unset the "skip me bit" in this ED */
-	ed->status &= ~OHCI_ED_SKIP;
-
-	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-} /* ohci_remove_td_from_ed() */
-
-
-/*
- * Get a pointer (virtual) to an available TD from the given device's
- * pool.
- *
- * Return NULL if none are left.
- */
-static struct ohci_td *ohci_get_free_td(struct ohci_device *dev)
-{
-	int idx;
-
-	for (idx=0; idx < NUM_TDS; idx++) {
-		if (!td_allocated(dev->td[idx])) {
-			struct ohci_td *new_td = &dev->td[idx];
-			/* zero out the TD */
-			memset(new_td, 0, sizeof(*new_td));
-			/* mark the new TDs as unaccessed */
-			new_td->info = OHCI_TD_CC_NEW;
-			/* mark it as allocated */
-			allocate_td(new_td);
-			return new_td;
-		}
-	}
-
-	printk("usb-ohci error: unable to allocate a TD\n");
-	return NULL;
-} /* ohci_get_free_td() */
-
-
-/*
- *  Initialize a TD
- *
- * 	dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP
- * 	toggle = TOGGLE_AUTO, TOGGLE_DATA0, TOGGLE_DATA1
- */
-inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed)
-{
-	/* hardware fields */
-	td->info = OHCI_TD_CC_NEW |
-		(dir & OHCI_TD_D) |
-		(toggle & OHCI_TD_DT) |
-		flags;
-	td->cur_buf = (data == NULL) ? 0 : virt_to_bus(data);
-	td->buf_end = (len == 0) ? 0 : td->cur_buf + len - 1;
-
-	/* driver fields */
-	td->data = data;
-	td->dev_id = dev_id;
-	td->completed = completed;
-
-	return td;
-} /* ohci_fill_new_td() */
-
-
-/**********************************
- * OHCI interrupt list operations *
- **********************************/
-
-/*
- * Request an interrupt handler for one "pipe" of a USB device.
- * (this function is pretty minimal right now)
- *
- * At the moment this is only good for input interrupts. (ie: for a
- * mouse or keyboard)
- *
- * Period is desired polling interval in ms.  The closest, shorter
- * match will be used.  Powers of two from 1-32 are supported by OHCI.
- */
-static int ohci_request_irq(struct usb_device *usb, unsigned int pipe,
-	usb_device_irq handler, int period, void *dev_id)
-{
-	struct ohci_device *dev = usb_to_ohci(usb);
-	struct ohci_td *td;
-	struct ohci_ed *interrupt_ed;	/* endpoint descriptor for this irq */
-
-	/*
-	 * Pick a good frequency endpoint based on the requested period
-	 */
-	interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)];
-
-	/*
-	 * Set the max packet size, device speed, endpoint number, usb
-	 * device number (function address), and type of TD.
-	 *
-	 * FIXME: Isochronous transfers need a pool of special 32 byte
-	 * TDs (32 byte aligned) in order to be supported.
-	 */
-	interrupt_ed->status = \
-		ed_set_maxpacket(usb_maxpacket(pipe)) |
-		ed_set_speed(usb_pipeslow(pipe)) |
-		usb_pipe_endpdev(pipe) |
-		OHCI_ED_F_NORM;
-
-	td = ohci_get_free_td(dev);
-	/* FIXME: check for NULL */
-
-	/* Fill in the TD */
-	ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)),
-			TOGGLE_AUTO,
-			OHCI_TD_ROUND,
-			dev->data, DATA_BUF_LEN,
-			dev_id, handler);
-	/*
-	 * TODO: be aware that OHCI won't advance out of the 4kb
-	 * page cur_buf started in.  It'll wrap around to the start
-	 * of the page...  annoying or useful? you decide.
-	 *
-	 * We should make sure dev->data doesn't cross a page...
-	 */
-
-	/* FIXME: this just guarantees that its the end of the list */
-	td->next_td = 0;
-
-	/* Linus did this. see asm/system.h; scary concept... I don't
-	 * know if its needed here or not but it won't hurt. */
-	wmb();
-
-	/*
-	 *  Put the TD onto our ED
-	 */
-	{
-		unsigned long flags;
-		spin_lock_irqsave(&ohci_edtd_lock, flags);
-		ohci_add_td_to_ed(td, interrupt_ed);
-		spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-	}
-
-#if 0
-	/* Assimilate the new ED into the collective */
-	/*
-	 *  When dynamic ED allocation is done, this call will be
-	 *  useful.  For now, the correct ED already on the
-	 *  controller's proper periodic ED lists was chosen above.
-	 */
-	ohci_add_periodic_ed(dev->ohci, interrupt_ed, period);
-#else
-	/* enable periodic (interrupt) transfers on the HC */
-	ohci_start_periodic(dev->ohci);
-#endif
-
-	return 0;
-} /* ohci_request_irq() */
-
-
-/*
- * Control thread operations:
- */
-static struct wait_queue *control_wakeup;
-
-/*
- *  This is the handler that gets called when a control transaction
- *  completes.
- *
- *  This function is called from the interrupt handler.
- */
-static int ohci_control_completed(int stats, void *buffer, void *dev_id)
-{
-	wake_up(&control_wakeup);
-	return 0;
-} /* ohci_control_completed() */
-
-
-/*
- * Send or receive a control message on a "pipe"
- *
- * The cmd parameter is a pointer to the 8 byte setup command to be
- * sent.  FIXME:  This is a devrequest in usb.h.  The function
- * should be updated to accept a devrequest* instead of void*..
- *
- * A control message contains:
- *   - The command itself
- *   - An optional data phase (if len > 0)
- *   - Status complete phase
- */
-static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len)
-{
-	struct ohci_device *dev = usb_to_ohci(usb);
-	/*
-	 * ideally dev->ed should be linked into the root hub's
-	 * control_ed list and used instead of just using it directly.
-	 * This could present a problem as is with more than one
-	 * device.  (but who wants to use a keyboard AND a mouse
-	 * anyways? ;)
-	 */
-	struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL];
-	struct ohci_td *setup_td, *data_td, *status_td;
-	struct wait_queue wait = { current, NULL };
-
-#if 0
-	printk(KERN_DEBUG "entering ohci_control_msg %p (ohci_dev: %p) pipe 0x%x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
-#endif
-
-	/*
-	 * Set the max packet size, device speed, endpoint number, usb
-	 * device number (function address), and type of TD.
-	 *
-	 */
-	control_ed->status = \
-		ed_set_maxpacket(usb_maxpacket(pipe)) |
-		ed_set_speed(usb_pipeslow(pipe)) |
-		usb_pipe_endpdev(pipe) |
-		OHCI_ED_F_NORM;
-
-	/*
-	 * Build the control TD
-	 */
-
-	/* get a TD to send this control message with */
-	setup_td = ohci_get_free_td(dev);
-	/* TODO check for NULL */
-
-	/*
-	 * Set the not accessed condition code, allow odd sized data,
-	 * and set the data transfer type to SETUP.  Setup DATA always
-	 * uses a DATA0 packet.
-	 *
-	 * The setup packet contains a devrequest (usb.h) which
-	 * will always be 8 bytes long.  FIXME: the cmd parameter
-	 * should be a pointer to one of these instead of a void* !!!
-	 */
-	ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0,
-			OHCI_TD_IOC_OFF,
-			cmd, 8,		/* cmd is always 8 bytes long */
-			NULL, NULL);
-
-	/* allocate the next TD */
-	data_td = ohci_get_free_td(dev);  /* TODO check for NULL */
-
-	/* link to the next TD */
-	setup_td->next_td = virt_to_bus(data_td);
-
-	if (len > 0) {
-
-		/* build the Control DATA TD, it starts with a DATA1. */
-		ohci_fill_new_td(data_td, td_set_dir_out(usb_pipeout(pipe)),
-				TOGGLE_DATA1,
-				OHCI_TD_ROUND | OHCI_TD_IOC_OFF,
-				data, len,
-				NULL, NULL);
-
-		/*
-		 * XXX we should check that the data buffer doesn't
-		 * cross a 4096 byte boundary.  If so, it needs to be
-		 * copied into a single 4096 byte aligned area for the
-		 * OHCI's TD logic to see it all, or multiple TDs need
-		 * to be made for each page.
-		 *
-		 * It's not likely a control transfer will run into
-		 * this problem.. (famous last words)
-		 */
-
-		status_td = ohci_get_free_td(dev);  /* TODO check for NULL */
-		data_td->next_td = virt_to_bus(status_td);
-	} else {
-		status_td = data_td; /* no data_td, use it for status */
-	}
-
-	/* The control status packet always uses a DATA1 */
-	ohci_fill_new_td(status_td,
-			td_set_dir_in(usb_pipeout(pipe) | (len == 0)),
-			TOGGLE_DATA1,
-			0,
-			NULL, 0,
-			NULL, ohci_control_completed);
-	status_td->next_td = 0; /* end of TDs */
-
-	/*
-	 * Start the control transaction..
-	 */
-	current->state = TASK_UNINTERRUPTIBLE;
-	add_wait_queue(&control_wakeup, &wait);
-
-	/*
-	 * Add the chain of 2-3 control TDs to the control ED's TD list
-	 */
-	{
-		unsigned long flags;
-		spin_lock_irqsave(&ohci_edtd_lock, flags);
-		ohci_add_td_to_ed(setup_td, control_ed);
-		spin_unlock_irqrestore(&ohci_edtd_lock, flags);
-	}
-
-#if 0
-	/* complete transaction debugging output (before) */
-	printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed));
-	show_ohci_ed(control_ed);
-	printk(KERN_DEBUG " Setup TD %lx:\n", virt_to_bus(setup_td));
-	show_ohci_td(setup_td);
-	if (data_td != status_td) {
-		printk(KERN_DEBUG " Data TD %lx:\n", virt_to_bus(data_td));
-		show_ohci_td(data_td);
-	}
-	printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td));
-	show_ohci_td(status_td);
-#endif
-
-	/* Give the ED to the HC */
-	ohci_add_control_ed(dev->ohci, control_ed);
-
-	/* FIXME:
-	 * this should really check to see that the transaction completed.
-	 */
-	schedule_timeout(HZ/10);
-
-	remove_wait_queue(&control_wakeup, &wait);
-
-#if 0
-	/* complete transaction debugging output (after) */
-	printk(KERN_DEBUG " (after) Control ED:\n");
-	show_ohci_ed(control_ed);
-	printk(KERN_DEBUG " (after) Setup TD:\n");
-	show_ohci_td(setup_td);
-	if (data_td != status_td) {
-		printk(KERN_DEBUG " (after) Data TD:\n");
-		show_ohci_td(data_td);
-	}
-	printk(KERN_DEBUG " (after) Status TD:\n");
-	show_ohci_td(status_td);
-#endif
-
-	/* clean up incase it failed */
-	/* XXX only do this if their ed pointer still points to control_ed
-	 * incase they've been reclaimed and used by something else
-	 * already. -greg */
-	ohci_remove_td_from_ed(setup_td, control_ed);
-	ohci_remove_td_from_ed(data_td, control_ed);
-	ohci_remove_td_from_ed(status_td, control_ed);
-
-	/* remove the control ED */
-	ohci_remove_control_ed(dev->ohci, control_ed);
-
-#if 0
-	printk(KERN_DEBUG "leaving ohci_control_msg\n");
-#endif
-
-	return ohci_td_result(dev, status_td);
-} /* ohci_control_msg() */
-
-
-/*
- * Allocate a new USB device to be attached to an OHCI controller
- */
-static struct usb_device *ohci_usb_allocate(struct usb_device *parent)
-{
-	struct usb_device *usb_dev;
-	struct ohci_device *dev;
-
-	/*
-	 * Allocate the generic USB device
-	 */
-	usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
-	if (!usb_dev)
-		return NULL;
-
-	memset(usb_dev, 0, sizeof(*usb_dev));
-
-	/*
-	 * Allocate an OHCI device (EDs and TDs for this device)
-	 */
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev) {
-		kfree(usb_dev);
-		return NULL;
-	}
-
-	memset(dev, 0, sizeof(*dev));
-
-	/*
-	 * Link them together
-	 */
-	usb_dev->hcpriv = dev;
-	dev->usb = usb_dev;
-
-	/*
-	 * Link the device to its parent (hub, etc..) if any.
-	 */
-	usb_dev->parent = parent;
-
-	if (parent) {
-		usb_dev->bus = parent->bus;
-		dev->ohci = usb_to_ohci(parent)->ohci;
-	}
-
-	return usb_dev;
-} /* ohci_usb_allocate() */
-
-
-/*
- * Free a usb device.
- *
- * TODO This function needs to take better care of the EDs and TDs, etc.
- */
-static int ohci_usb_deallocate(struct usb_device *usb_dev)
-{
-	kfree(usb_to_ohci(usb_dev));
-	kfree(usb_dev);
-	return 0;
-}
-
-
-/*
- * functions for the generic USB driver
- */
-struct usb_operations ohci_device_operations = {
-	ohci_usb_allocate,
-	ohci_usb_deallocate,
-	ohci_control_msg,
-	ohci_request_irq,
-};
-
-
-/*
- * Reset an OHCI controller.  Returns >= 0 on success.
- *
- * Afterwards the HC will be in the "suspend" state which prevents you
- * from writing to some registers.  Bring it to the operational state
- * ASAP.
- */
-static int reset_hc(struct ohci *ohci)
-{
-	int timeout = 1000;  /* prevent an infinite loop */
-
-#if 0
-	printk(KERN_DEBUG "usb-ohci: resetting HC %p\n", ohci);
-#endif
-
-	writel(~0x0, &ohci->regs->intrdisable);    /* Disable HC interrupts */
-	writel(1, &ohci->regs->cmdstatus);	   /* HC Reset */
-	writel_mask(0x3f, &ohci->regs->control);   /* move to UsbReset state */
-
-	while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) {
-		if (!--timeout) {
-			printk("usb-ohci: USB HC reset timed out!\n");
-			return -1;
-		}
-		udelay(1);
-	}
-
-	printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci);
-
-	return 0;
-} /* reset_hc() */
-
-
-/*
- * Reset and start an OHCI controller.  Returns >= 0 on success.
- */
-static int start_hc(struct ohci *ohci)
-{
-	int ret = 0;
-	int fminterval;
-
-	fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
-#if 0
-	printk(KERN_DEBUG "entering start_hc %p\n", ohci);
-#endif
-
-	if (reset_hc(ohci) < 0)
-		return -1;
-
-	/* restore registers cleared by the reset */
-	writel(virt_to_bus(ohci->root_hub->hcca), &ohci->regs->hcca);
-
-	/*
-	 * XXX Should fminterval also be set here?
-	 * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant)
-	 */
-	fminterval |= (0x2edf << 16);
-	writel(fminterval, &ohci->regs->fminterval);
-	/* Start periodic transfers at 90% of fminterval (fmremaining
-	 * counts down; this will put them in the first 10% of the
-	 * frame). */
-	writel((0x2edf*9)/10, &ohci->regs->periodicstart);
-
-	/*
-	 * FNO (frame number overflow) could be enabled...  they
-	 * occur every 32768 frames (every 32-33 seconds).  This is
-	 * useful for debugging and as a bus heartbeat. -greg
-	 */
-	/* Choose the interrupts we care about */
-	writel( OHCI_INTR_MIE | /* OHCI_INTR_RHSC | */
-		OHCI_INTR_WDH | OHCI_INTR_FNO,
-		&ohci->regs->intrenable);
-
-	/* Enter the USB Operational state & start the frames a flowing.. */
-	writel_set(OHCI_USB_OPER, &ohci->regs->control);
-	
-	/* Enable control lists */
-	writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control);
-
-	/* Turn on power to the root hub ports (thanks Roman!) */
-	writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status );
-
-	printk("usb-ohci: host controller operational\n");
-
-	return ret;
-} /* start_hc() */
-
-
-/*
- * Reset a root hub port
- */
-static void ohci_reset_port(struct ohci *ohci, unsigned int port)
-{
-	int status;
-
-	/* Don't allow overflows. */
-	if (port >= MAX_ROOT_PORTS) {
-		printk("usb-ohci: bad port #%d in ohci_reset_port\n", port);
-		port = MAX_ROOT_PORTS-1;
-	}
-
-	writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);  /* Reset */
-
-	/*
-	 * Wait for the reset to complete.
-	 */
-	wait_ms(10);
-
-	/* check port status to see that the reset completed */
-	status = readl(&ohci->regs->roothub.portstatus[port]);
-	if (status & PORT_PRS) {
-		/* reset failed, try harder? */
-		printk("usb-ohci: port %d reset failed, retrying\n", port);
-		writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);
-		wait_ms(50);
-	}
-
-	/* TODO we might need to re-enable the port here or is that
-	 * done elsewhere? */
-
-} /* ohci_reset_port */
-
-
-/*
- * This gets called if the connect status on the root hub changes.
- */
-static void ohci_connect_change(struct ohci * ohci, int port)
-{
-	struct usb_device *usb_dev;
-	struct ohci_device *dev;
-	/* memory I/O address of the port status register */
-	void *portaddr = &ohci->regs->roothub.portstatus[port];
-	int portstatus;	
-
-	printk(KERN_DEBUG "ohci_connect_change(%p, %d)\n", ohci, port);
-
-	/*
-	 * Because of the status change we have to forget
-	 * everything we think we know about the device
-	 * on this root hub port.  It may have changed.
-	 */
-	usb_disconnect(ohci->root_hub->usb->children + port);
-
-	portstatus = readl(portaddr);
-
-	/* disable the port if nothing is connected */
-	if (!(portstatus & PORT_CCS)) {
-		writel(PORT_CCS, portaddr);
-		return;
-	}
-
-	/*
-	 * Allocate a device for the new thingy that's been attached
-	 */
-	usb_dev = ohci_usb_allocate(ohci->root_hub->usb);
-	dev = usb_dev->hcpriv;
-
-	dev->ohci = ohci;
-
-	usb_connect(dev->usb);
-
-	/* link it into the bus's device tree */
-	ohci->root_hub->usb->children[port] = usb_dev;
-
-	wait_ms(200); /* wait for powerup; XXX is this needed? */
-	ohci_reset_port(ohci, port);
-
-	/* Get information on speed by using LSD */
-	usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0;
-
-	/*
-	 * Do generic USB device tree processing on the new device.
-	 */
-	usb_new_device(usb_dev);
-
-} /* ohci_connect_change() */
-
-
-/*
- * This gets called when the root hub configuration
- * has changed.  Just go through each port, seeing if
- * there is something interesting happening.
- */
-static void ohci_check_configuration(struct ohci *ohci)
-{
-	struct ohci_regs *regs = ohci->regs;
-	int num = 0;
-	int maxport = readl(&ohci->regs->roothub) & 0xff;
-
-#if 1
-	printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci);
-#endif
-
-	do {
-		if (readl(&regs->roothub.portstatus[num]) & PORT_CSC) {
-			/* reset the connect status change bit */
-			writel(PORT_CSC, &regs->roothub.portstatus[num]);
-			/* check the port for a nifty device */
-			ohci_connect_change(ohci, num);
-		}
-	} while (++num < maxport);
-
-#if 0
-	printk(KERN_DEBUG "leaving ohci_check_configuration %p\n", ohci);
-#endif
-} /* ohci_check_configuration() */
-
-
-
-/*
- * Check root hub port status and wake the control thread up if
- * anything has changed.
- *
- * This function is called from the interrupt handler.
- */
-static void ohci_root_hub_events(struct ohci *ohci)
-{
-	if (waitqueue_active(&ohci_configure)) {
-		int num = 0;
-		int maxport = ohci->root_hub->usb->maxchild;
-
-		do {
-			if (readl(&ohci->regs->roothub.portstatus[num]) &
-					PORT_CSC) {
-				if (waitqueue_active(&ohci_configure))
-					wake_up(&ohci_configure);
-				return;
-			}
-		} while (++num < maxport);
-	}
-} /* ohci_root_hub_events() */
-
-
-/*
- * The done list is in reverse order; we need to process TDs in the
- * order they were finished (FIFO).  This function builds the FIFO
- * list using the next_dl_td pointer.
- *
- * This function originally by Roman Weissgaerber (weissg@vienna.at)
- *
- * This function is called from the interrupt handler.
- */
-static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci)
-{
-	__u32 td_list_hc;
-	struct ohci_hcca *hcca = ohci->root_hub->hcca;
-	struct ohci_td *td_list = NULL;
-	struct ohci_td *td_rev = NULL;
-  	
-	td_list_hc = hcca->donehead & 0xfffffff0;
-	hcca->donehead = 0;
-
- 	while(td_list_hc) {
-		td_list = (struct ohci_td *) bus_to_virt(td_list_hc);
-		td_list->next_dl_td = td_rev;
-			
-		td_rev = td_list;
-		td_list_hc = td_list->next_td & 0xfffffff0;
-	}
-
-	return td_list;
-} /* ohci_reverse_donelist() */
-
-
-/*
- * Collect this interrupt's goodies off of the list of finished TDs
- * that the OHCI controller is kind enough to setup for us.
- *
- * This function is called from the interrupt handler.
- */
-static void ohci_reap_donelist(struct ohci *ohci)
-{
-	struct ohci_td *td;		/* used for walking the list */
-
-	spin_lock(&ohci_edtd_lock);
-
-	/* create the FIFO ordered donelist */
-	td = ohci_reverse_donelist(ohci);
-
-	while (td != NULL) {
-		struct ohci_td *next_td = td->next_dl_td;
-
-		/* FIXME: munge td->info into a future standard status format */
-		/* Check if TD should be re-queued */
-		if ((td->completed != NULL) &&
-		    (td->completed(OHCI_TD_CC_GET(td->info), td->data, td->dev_id)))
-		{
-			/* Mark the TD as active again:
-			 * Set the not accessed condition code
-			 * FIXME: should this reset OHCI_TD_ERRCNT?
-			 */
-			td->info |= OHCI_TD_CC_NEW;
-
-			/* point it back to the start of the data buffer */
-			td->cur_buf = virt_to_bus(td->data);
-
-			/* XXX disabled for debugging reasons right now.. */
-			/* insert it back on its ED */
-			ohci_add_td_to_ed(td, td->ed);
-		} else {
-			/* return it to the pool of free TDs */
-			ohci_free_td(td);
-		}
-
-		td = next_td;
-	}
-
-	spin_unlock(&ohci_edtd_lock);
-} /* ohci_reap_donelist() */
-
-
-#if 0
-static int in_int = 0;
-#endif
-/*
- * Get annoyed at the controller for bothering us.
- * This pretty much follows the OHCI v1.0a spec, section 5.3.
- */
-static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r)
-{
-	struct ohci *ohci = __ohci;
-	struct ohci_regs *regs = ohci->regs;
-	struct ohci_hcca *hcca = ohci->root_hub->hcca;
-	__u32 status, context;
-
-#if 0
-	/* for debugging to keep IRQs from running away. */
-	if (in_int >= 2)
-		return;
-	++in_int;
-	return;
-#endif
-
-	/* Save the status of the interrupts that are enabled */
-	status = readl(&regs->intrstatus);
-	status &= readl(&regs->intrenable);
-
-
-	/* make context = the interrupt status bits that we care about */
-	if (hcca->donehead != 0) {
-		context = OHCI_INTR_WDH;   /* hcca donehead needs processing */
-		if (hcca->donehead & 1) {
-			context |= status;  /* other status change to check */
-		}
-	} else {
-		context = status;
-		if (!context) {
-			/* TODO increment a useless interrupt counter here */
-			return;
-		}
-	}
-
-	/* Disable HC interrupts */
-	writel(OHCI_INTR_MIE, &regs->intrdisable);
-
-	/* Process the done list */
-	if (context & OHCI_INTR_WDH) {
-		/* See which TD's completed.. */
-		ohci_reap_donelist(ohci);
-
-		/* reset the done queue and tell the controller */
-		hcca->donehead = 0;
-		writel(OHCI_INTR_WDH, &regs->intrstatus);
-
-		context &= ~OHCI_INTR_WDH;  /* mark this as checked */
-	}
-
-	/* Process any root hub status changes */
-	if (context & OHCI_INTR_RHSC) {
-		/* Wake the thread to process root hub events */
-		if (waitqueue_active(&ohci_configure))
-			wake_up(&ohci_configure);
-
-		writel(OHCI_INTR_RHSC, &regs->intrstatus);
-		/* 
-		 * Don't unset RHSC in context; it should be disabled.
-		 * The control thread will re-enable it after it has
-		 * checked the root hub status.
-		 */
-	} else {
-		/* check the root hub status anyways. Some controllers
-		 * might not generate the interrupt properly. (?) */
-		ohci_root_hub_events(ohci);
-	}
-
-	/* Check those "other" pesky bits */
-	if (context & (OHCI_INTR_FNO)) {
-		writel(OHCI_INTR_FNO, &regs->intrstatus);
-		context &= ~OHCI_INTR_FNO;  /* mark this as checked */
-	}
-	if (context & OHCI_INTR_SO) {
-		writel(OHCI_INTR_SO, &regs->intrstatus);
-		context &= ~OHCI_INTR_SO;  /* mark this as checked */
-	}
-	if (context & OHCI_INTR_RD) {
-		writel(OHCI_INTR_RD, &regs->intrstatus);
-		context &= ~OHCI_INTR_RD;  /* mark this as checked */
-	}
-	if (context & OHCI_INTR_UE) {
-		/* FIXME: need to have the control thread reset the
-		 * controller now and keep a count of unrecoverable
-		 * errors.  If there are too many, it should just shut
-		 * the broken controller down entirely. */
-		writel(OHCI_INTR_UE, &regs->intrstatus);
-		context &= ~OHCI_INTR_UE;  /* mark this as checked */
-	}
-	if (context & OHCI_INTR_OC) {
-		writel(OHCI_INTR_OC, &regs->intrstatus);
-		context &= ~OHCI_INTR_OC;  /* mark this as checked */
-	}
-
-	/* Mask out any remaining unprocessed interrupts so we don't
-	 * get any more of them. */
-	if (context & ~OHCI_INTR_MIE) {
-		writel(context, &regs->intrdisable);
-	}
-
-	/* Re-enable HC interrupts */
-	writel(OHCI_INTR_MIE, &regs->intrenable);
-
-} /* ohci_interrupt() */
-
-
-/*
- * Allocate the resources required for running an OHCI controller.
- * Host controller interrupts must not be running while calling this
- * function or the penguins will get angry.
- *
- * The mem_base parameter must be the usable -virtual- address of the
- * host controller's memory mapped I/O registers.
- */
-static struct ohci *alloc_ohci(void* mem_base)
-{
-	int i;
-	struct ohci *ohci;
-	struct usb_bus *bus;
-	struct ohci_device *dev;
-	struct usb_device *usb;
-
-#if 0
-	printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base);
-#endif
-
-	ohci = kmalloc(sizeof(*ohci), GFP_KERNEL);
-	if (!ohci)
-		return NULL;
-
-	memset(ohci, 0, sizeof(*ohci));
-
-	ohci->irq = -1;
-	ohci->regs = mem_base;
-	INIT_LIST_HEAD(&ohci->interrupt_list);
-
-	bus = kmalloc(sizeof(*bus), GFP_KERNEL);
-	if (!bus)
-		return NULL;
-
-	memset(bus, 0, sizeof(*bus));
-
-	ohci->bus = bus;
-	bus->hcpriv = ohci;
-	bus->op = &ohci_device_operations;
-
-	/*
-	 * Allocate the USB device structure and root hub.
-	 *
-	 * Here we allocate our own root hub and TDs as well as the
-	 * OHCI host controller communications area.  The HCCA is just
-	 * a nice pool of memory with pointers to endpoint descriptors
-	 * for the different interrupts.
-	 */
-	usb = ohci_usb_allocate(NULL);
-	if (!usb)
-		return NULL;
-
-	dev = ohci->root_hub = usb_to_ohci(usb);
-
-	usb->bus = bus;
-
-	/* Initialize the root hub */
-	dev->ohci = ohci;    /* link back to the controller */
-
-	/*
-	 * Allocate the Host Controller Communications Area on a 256
-	 * byte boundary.  XXX take the easy way out and just grab a
-	 * page as that's guaranteed to have a nice boundary.
-	 */
-	dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL);
-
-	/* Tell the controller where the HCCA is */
-	writel(virt_to_bus(dev->hcca), &ohci->regs->hcca);
-
-#if 0
-	printk(KERN_DEBUG "usb-ohci: HCCA allocated at %p (bus %p)\n", dev->hcca, (void*)virt_to_bus(dev->hcca));
-#endif
-
-	/* Get the number of ports on the root hub */
-	usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
-	if (usb->maxchild > MAX_ROOT_PORTS) {
-		printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);
-		usb->maxchild = MAX_ROOT_PORTS;
-	}
-	if (usb->maxchild < 1) {
-		printk("usb-ohci: Less than one root hub port? Impossible!\n");
-		usb->maxchild = 1;
-	}
-	printk("usb-ohci: %d root hub ports found\n", usb->maxchild);
-
-	/*
-	 * Initialize the ED polling "tree" (for simplicity's sake in
-	 * this driver many nodes in the tree will be identical)
-	 */
-	dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]);
-	dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]);
-	dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]);
-	dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]);
-	dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]);
-
-	/*
-	 * Initialize the polling table to call interrupts at the
-	 * intended intervals.
-	 */
-	dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_32]);
-	for (i = 1; i < NUM_INTS; i++) {
-		if (i & 1)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_16]);
-		else if (i & 2)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_8]);
-		else if (i & 4)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_4]);
-		else if (i & 8)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_2]);
-		else if (i & 16)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_1]);
-	}
-
-	/*
-	 * Tell the controller where the control and bulk lists are
-	 * The lists start out empty.
-	 */
-	writel(0, &ohci->regs->ed_controlhead);
-	writel(0, &ohci->regs->ed_bulkhead);
-	/*
-	writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead);
-	writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead);
-	*/
-
-#if 0
-	printk(KERN_DEBUG "alloc_ohci(): controller\n");
-	show_ohci_status(ohci);
-#endif
-
-#if 0
-	printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci);
-#endif
-
-	return ohci;
-} /* alloc_ohci() */
-
-
-/*
- * De-allocate all resoueces..
- */
-static void release_ohci(struct ohci *ohci)
-{
-	printk(KERN_DEBUG "entering release_ohci %p\n", ohci);
-
-#ifdef OHCI_TIMER
-	/* stop our timer */
-	del_timer(&ohci_timer);
-#endif
-	if (ohci->irq >= 0) {
-		free_irq(ohci->irq, ohci);
-		ohci->irq = -1;
-	}
-
-	/* stop all OHCI interrupts */
-	writel(~0x0, &ohci->regs->intrdisable);
-
-	if (ohci->root_hub) {
-		/* ensure that HC is stopped before releasing the HCCA */
-		writel(OHCI_USB_SUSPEND, &ohci->regs->control);
-		free_page((unsigned long) ohci->root_hub->hcca);
-		kfree(ohci->root_hub);
-		ohci->root_hub->hcca = NULL;
-		ohci->root_hub = NULL;
-	}
-
-	/* unmap the IO address space */
-	iounmap(ohci->regs);
-
-	kfree(ohci);
-
-	MOD_DEC_USE_COUNT;
-
-	/* If the ohci itself were dynamic we'd free it here */
-
-	printk(KERN_DEBUG "usb-ohci: HC resources released.\n");
-} /* release_ohci() */
-
-
-/*
- * USB OHCI control thread
- */
-static int ohci_control_thread(void * __ohci)
-{
-	struct ohci *ohci = (struct ohci *)__ohci;
-
-	/*
-	 * I'm unfamiliar with the SMP kernel locking.. where should
-	 * this be released and what does it do?  -greg
-	 */
-	lock_kernel();
-
-	/*
-	 * This thread doesn't need any user-level access,
-	 * so get rid of all of our resources..
-	 */
-	printk("ohci_control_thread code at %p\n", &ohci_control_thread);
-	exit_mm(current);
-	exit_files(current);
-	exit_fs(current);
-
-	strcpy(current->comm, "ohci-control");
-
-	/*
-	 * Damn the torpedoes, full speed ahead
-	 */
-	if (start_hc(ohci) < 0) {
-		printk("usb-ohci: failed to start the controller\n");
-		release_ohci(ohci);
-		printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
-		return 0;
-	}
-
-	for(;;) {
-		siginfo_t info;
-		int unsigned long signr;
-
-		wait_ms(200);
-
-		/* check the root hub configuration for changes. */
-		ohci_check_configuration(ohci);
-
-		/* re-enable root hub status change interrupts. */
-#if 0
-		writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
-#endif
-
-		printk(KERN_DEBUG "ohci-control thread sleeping\n");
-		interruptible_sleep_on(&ohci_configure);
-#ifdef CONFIG_APM
-		if (apm_resume) {
-			apm_resume = 0;
-			if (start_hc(ohci) < 0)
-				break;
-			continue;
-		}
-#endif
-
-		/*
-		 * If we were woken up by a signal, see if its useful,
-		 * otherwise exit.
-		 */
-		if (signal_pending(current)) {
-			/* sending SIGUSR1 makes us print out some info */
-			spin_lock_irq(&current->sigmask_lock);
-			signr = dequeue_signal(&current->blocked, &info);
-			spin_unlock_irq(&current->sigmask_lock);
-
-			if(signr == SIGUSR1) {
-				/* FIXME: have it do a full ed/td queue dump */
-				printk(KERN_DEBUG "OHCI status dump:\n");
-				show_ohci_status(ohci);
-			} else {
-				/* unknown signal, exit the thread */
-				break;
-			}
-		}
-	} /* for (;;) */
-
-	reset_hc(ohci);
-	release_ohci(ohci);
-
-	printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
-
-	return 0;
-} /* ohci_control_thread() */
-
-
-#ifdef CONFIG_APM
-static int handle_apm_event(apm_event_t event)
-{
-	static int down = 0;
-
-	switch (event) {
-	case APM_SYS_SUSPEND:
-	case APM_USER_SUSPEND:
-		if (down) {
-			printk(KERN_DEBUG "usb-ohci: received extra suspend event\n");
-			break;
-		}
-		down = 1;
-		break;
-	case APM_NORMAL_RESUME:
-	case APM_CRITICAL_RESUME:
-		if (!down) {
-			printk(KERN_DEBUG "usb-ohci: received bogus resume event\n");
-			break;
-		}
-		down = 0;
-		if (waitqueue_active(&ohci_configure)) {
-			apm_resume = 1;
-			wake_up(&ohci_configure);
-		}
-		break;
-	}
-	return 0;
-} /* handle_apm_event() */
-#endif
-
-
-#ifdef OHCI_TIMER
-/*
- * Inspired by Ińaky's driver.  This function is a timer routine that
- * is called OHCI_TIMER_FREQ times per second.  It polls the root hub
- * for status changes as on my system things are acting a bit odd at
- * the moment..
- */
-static void ohci_timer_func (unsigned long ohci_ptr)
-{
-	struct ohci *ohci = (struct ohci*)ohci_ptr;
-
-	ohci_root_hub_events(ohci);
-
-	/* press the snooze button... */
-	mod_timer(&ohci_timer, jiffies + (OHCI_TIMER_FREQ*HZ));
-} /* ohci_timer_func() */
-#endif
-
-
-/*
- * Increment the module usage count, start the control thread and
- * return success if the controller is good.
- */
-static int found_ohci(int irq, void* mem_base)
-{
-	int retval;
-	struct ohci *ohci;
-
-#if 0
-	printk(KERN_DEBUG "entering found_ohci %d %p\n", irq, mem_base);
-#endif
-
-	/* Allocate the running OHCI structures */
-	ohci = alloc_ohci(mem_base);
-	if (!ohci) {
-		return -ENOMEM;
-	}
-
-#ifdef OHCI_TIMER
-	init_timer(&ohci_timer);
-	ohci_timer.expires = jiffies + (OHCI_TIMER_FREQ*HZ);
-	ohci_timer.data = (unsigned long)ohci;
-	ohci_timer.function = ohci_timer_func;
-#endif
-
-	retval = -EBUSY;
-	if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) {
-		int pid;
-
-		ohci->irq = irq;
-
-#if 0
-		printk(KERN_DEBUG "usb-ohci: starting ohci-control thread\n");
-#endif
-
-		/* fork off the handler */
-		pid = kernel_thread(ohci_control_thread, ohci,
-				CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-		if (pid >= 0) {
-			return 0;
-		}
-
-		retval = pid;
-	} else {
-		printk("usb-ohci: Couldn't allocate interrupt %d\n", irq);
-	}
-	release_ohci(ohci);
-
-#if 0
-	printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base);
-#endif
-
-	return retval;
-} /* found_ohci() */
-
-
-/*
- * If this controller is for real, map the IO memory and proceed
- */
-static int init_ohci(struct pci_dev *dev)
-{
-	unsigned long mem_base = dev->base_address[0];
-	
-	/* If its OHCI, its memory */
-	if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)
-		return -ENODEV;
-
-	/* Get the memory address and map it for IO */
-	mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
-
-	/* no interrupt won't work... */
-	if (dev->irq == 0) {
-		printk("usb-ohci: no irq assigned? check your BIOS settings.\n");
-		return -ENODEV;
-	}
-
-	/* 
-	 * FIXME ioremap_nocache isn't implemented on all CPUs (such
-	 * as the Alpha) [?]  What should I use instead...
-	 *
-	 * The iounmap() is done on in release_ohci.
-	 */
-	mem_base = (unsigned long) ioremap_nocache(mem_base, 4096);
-
-	if (!mem_base) {
-		printk("Error mapping OHCI memory\n");
-		return -EFAULT;
-	}
-        MOD_INC_USE_COUNT;
-
-	if (found_ohci(dev->irq, (void *) mem_base) < 0) {
-		MOD_DEC_USE_COUNT;
-		return -1;
-	}
-
-	return 0;
-} /* init_ohci() */
-
-#ifdef MODULE
-/*
- *  Clean up when unloading the module
- */
-void cleanup_module(void)
-{
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_cleanup();
-#endif
-	printk("usb-ohci: module unloaded\n");
-}
-
-#define ohci_init init_module
-
-#endif
-
-
-/* TODO this should be named following Linux convention and go in pci.h */
-#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)
-
-/*
- * Search the PCI bus for an OHCI USB controller and set it up
- *
- * If anyone wants multiple controllers this will need to be
- * updated..  Right now, it just picks the first one it finds.
- */
-int ohci_init(void)
-{
-	int retval;
-	struct pci_dev *dev = NULL;
-	/*u8 type;*/
-
-	if (sizeof(struct ohci_device) > 4096) {
-		printk("usb-ohci: struct ohci_device to large\n");
-		return -ENODEV;
-	}
-
-	printk("OHCI USB Driver loading\n");
-
-	retval = -ENODEV;
-	for (;;) {
-		/* Find an OHCI USB controller */
-		dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev);
-		if (!dev)
-			break;
-
-		/* Verify that its OpenHCI by checking for MMIO */
-		/* pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
-		if (!type)
-			continue; */
-
-		/* Ok, set it up */
-		retval = init_ohci(dev);
-		if (retval < 0)
-			continue;
-
-		/* TODO check module params here to determine what to load */
-
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
-#ifdef CONFIG_APM
-		apm_register_callback(&handle_apm_event);
-#endif
-
-		return 0; /* no error */
-	}
-	return retval;
-} /* ohci_init */
-
-/* vim:sw=8
- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ohci.h linux.18p17/drivers/usb/ohci.h
--- linux.vanilla/drivers/usb/ohci.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/ohci.h	Thu Jan  1 01:00:00 1970
@@ -1,349 +0,0 @@
-#ifndef __LINUX_OHCI_H
-#define __LINUX_OHCI_H
-
-/*
- * Open Host Controller Interface data structures and defines.
- *
- * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
- *
- * $Id: ohci.h,v 1.15 1999/05/09 23:25:49 greg Exp $
- */
-
-#include <linux/list.h>
-#include <asm/io.h>
-
-#include "usb.h"
-
-struct ohci_ed;
-
-/*
- * Each TD must be aligned on a 16-byte boundary.  From the OHCI v1.0 spec
- * it does not state that TDs must be contiguious in memory (due to the
- * use of the next_td field).  This gives us extra room at the end of a
- * TD for our own driver specific data.
- *
- * This structure's size must be a multiple of 16 bytes. ?? no way, I
- * don't see why.  Alignment should be all that matters.
- */
-struct ohci_td {
-    	/* OHCI Hardware fields */
-    	__u32 info;		/* TD status & type flags */
-	__u32 cur_buf;		/* Current Buffer Pointer (bus address) */
-	__u32 next_td;		/* Next TD Pointer (bus address) */
-	__u32 buf_end;		/* Memory Buffer End Pointer (bus address) */
-
-	/* Driver specific fields */
-	struct ohci_ed *ed;		/* address of the ED this TD is on */
-	struct ohci_td *next_dl_td;	/* used during donelist processing */
-	void *data;			/* virt. address of the the buffer */
-	usb_device_irq completed;	/* Completion handler routine */
-	int allocated;			/* boolean: is this TD allocated? */
-
-	/* User or Device class driver specific fields */
-	void *dev_id;	/* user defined pointer passed to irq handler */
-} __attribute((aligned(16)));
-
-#define OHCI_TD_ROUND	(1 << 18)	/* buffer rounding bit */
-#define OHCI_TD_D	(3 << 19)	/* direction of xfer: */
-#define OHCI_TD_D_IN	(2 << 19)
-#define OHCI_TD_D_OUT	(1 << 19)
-#define OHCI_TD_D_SETUP (0 << 19)
-#define td_set_dir_in(d)	((d) ? OHCI_TD_D_IN : OHCI_TD_D_OUT )
-#define td_set_dir_out(d)	((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN )
-#define OHCI_TD_IOC_DELAY (7 << 21)	/* frame delay allowed before int. */
-#define OHCI_TD_IOC_OFF	(OHCI_TD_IOC_DELAY)	/* no interrupt on complete */
-#define OHCI_TD_DT	(3 << 24)	/* data toggle bits */
-#define TOGGLE_AUTO	(0 << 24)	/* automatic (from the ED) */
-#define TOGGLE_DATA0	(2 << 24)	/* force Data0 */
-#define TOGGLE_DATA1	(3 << 24)	/* force Data1 */
-#define td_force_toggle(b)	(((b) | 2) << 24)
-#define OHCI_TD_ERRCNT	(3 << 26)	/* error count */
-#define td_errorcount(td)	(((td).info >> 26) & 3)
-#define OHCI_TD_CC	(0xf << 28)	/* condition code */
-#define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf)
-#define OHCI_TD_CC_NEW	(OHCI_TD_CC)	/* set this on all unaccessed TDs! */
-#define td_cc_notaccessed(td)	(((td).info >> 29) == 7)
-#define td_cc_accessed(td)	(((td).info >> 29) != 7)
-#define td_cc_noerror(td)	((((td).info) & OHCI_TD_CC) == 0)
-#define td_active(td)	(!td_cc_noerror((td)) && (td_errorcount((td)) < 3))
-#define td_done(td)	(td_cc_noerror((td)) || (td_errorcount((td)) == 3))
-
-#define td_allocated(td)	((td).allocated)
-#define allocate_td(td)		((td)->allocated = 1)
-#define ohci_free_td(td)	((td)->allocated = 0)
-
-
-/*
- * The endpoint descriptors also requires 16-byte alignment
- */
-struct ohci_ed {
-	/* OHCI hardware fields */
-	__u32 status;
-	__u32 tail_td;	/* TD Queue tail pointer */
-	__u32 _head_td;	/* TD Queue head pointer, toggle carry & halted bits */
-	__u32 next_ed;	/* Next ED */
-} __attribute((aligned(16)));
-
-/* get the head_td */
-#define ed_head_td(ed)	((ed)->_head_td & 0xfffffff0)
-
-/* save the carry flag while setting the head_td */
-#define set_ed_head_td(ed, td)	((ed)->_head_td = (td) | ((ed)->_head_td & 3))
-
-#define OHCI_ED_SKIP	(1 << 14)
-#define OHCI_ED_MPS	(0x7ff << 16)
-/* FIXME: should cap at the USB max packet size [0x4ff] */
-#define ed_set_maxpacket(s)	(((s) << 16) & OHCI_ED_MPS)
-#define OHCI_ED_F_NORM	(0)
-#define OHCI_ED_F_ISOC	(1 << 15)
-#define ed_set_type_isoc(i)	((i) ? OHCI_ED_F_ISOC : OHCI_ED_F_NORM)
-#define OHCI_ED_S_LOW	(1 << 13)
-#define OHCI_ED_S_HIGH	(0)
-#define ed_set_speed(s)		((s) ? OHCI_ED_S_LOW : OHCI_ED_S_HIGH)
-#define OHCI_ED_D	(3 << 11)
-#define OHCI_ED_D_IN	(2 << 11)
-#define OHCI_ED_D_OUT	(1 << 11)
-#define ed_set_dir_in(d)	((d) ? OHCI_ED_D_IN : OHCI_ED_D_OUT)
-#define ed_set_dir_out(d)	((d) ? OHCI_ED_D_OUT : OHCI_ED_D_IN)
-#define OHCI_ED_EN	(0xf << 7)
-#define OHCI_ED_FA	(0x7f)
-
-
-/* NOTE: bits 27-31 of the status dword are reserved for the driver */
-/*
- * We'll use this status flag for to mark if an ED is in use by the
- * driver or not.  If the bit is set, it is used.
- *
- * FIXME: implement this!
- */
-#define ED_USED	(1 << 31)
-
-/*
- * The HCCA (Host Controller Communications Area) is a 256 byte
- * structure defined in the OHCI spec. that the host controller is
- * told the base address of.  It must be 256-byte aligned.
- */
-#define NUM_INTS 32	/* part of the OHCI standard */
-struct ohci_hcca {
-    	__u32	int_table[NUM_INTS];	/* Interrupt ED table */
-	__u16	frame_no;		/* current frame number */
-	__u16	pad1;			/* set to 0 on each frame_no change */
-	__u32	donehead;		/* info returned for an interrupt */
-	u8	reserved_for_hc[116];
-} __attribute((aligned(256)));
-
-/*
- * The TD entries here are pre-allocated as Linus did with his simple
- * UHCI implementation.  With the current state of this driver that
- * shouldn't be a problem.  However if someone ever connects 127
- * supported devices to this driver and tries to use them all at once:
- * 	a)  They're insane!
- * 	b)  They should code in dynamic allocation
- */
-struct ohci;
-
-/*
- *  Warning: These constants must not be so large as to cause the
- *  ohci_device structure to exceed one 4096 byte page.  Or "weird
- *  things will happen" as the alloc_ohci() function assumes that
- *  its less than one page.  (FIXME)
- */
-#define NUM_TDS	32		/* num of preallocated transfer descriptors */
-#define DATA_BUF_LEN 16		/* num of unsigned long's for the data buf */
-
-/*
- * For this "simple" driver we only support a single ED for each
- * polling rate.
- *
- * Later on this driver should be extended to use a full tree of EDs
- * so that there can be 32 different 32ms polling frames, etc.
- * Individual devices shouldn't need as many as the root hub in that
- * case; complicating how things are done at the moment.
- *
- * Bulk and Control transfers hang off of their own ED lists.
- */
-#define NUM_EDS 16		/* num of preallocated endpoint descriptors */
-
-#define ohci_to_usb(ohci)	((ohci)->usb)
-#define usb_to_ohci(usb)	((struct ohci_device *)(usb)->hcpriv)
-
-/* The usb_device must be first! */
-struct ohci_device {
-    	struct usb_device	*usb;
-
-	struct ohci		*ohci;
-	struct ohci_hcca 	*hcca;		/* OHCI mem. mapped IO area */
-
-	struct ohci_ed		ed[NUM_EDS];	/* Endpoint Descriptors */
-	struct ohci_td		td[NUM_TDS];	/* Transfer Descriptors */
-
-	unsigned long		data[DATA_BUF_LEN];
-};
-
-/* .... */
-
-#define ED_INT_1	0
-#define ED_INT_2	1
-#define ED_INT_4	2
-#define ED_INT_8	3
-#define ED_INT_16	4
-#define ED_INT_32	5
-#define ED_CONTROL	6
-#define ED_BULK		7
-#define ED_ISO		ED_INT_1	/* same as 1ms interrupt queue */
-#define ED_FIRST_AVAIL  8		/* first non-reserved ED */
-
-/*
- * Given a period p in ms, convert it to the closest endpoint
- * interrupt frequency; rounding down.  This is a gross macro.
- * Feel free to toss it for actual code. (gasp!)
- */
-#define ms_to_ed_int(p) \
-	((p >= 32) ? ED_INT_32 : \
-	 ((p & 16) ? ED_INT_16 : \
-	  ((p & 8) ? ED_INT_8 : \
-	   ((p & 4) ? ED_INT_4 : \
-	    ((p & 2) ? ED_INT_2 : \
-	     ED_INT_1)))))  /* hmm.. scheme or lisp anyone? */
-
-/*
- * This is the maximum number of root hub ports.  I don't think we'll
- * ever see more than two as that's the space available on an ATX
- * motherboard's case, but it could happen.  The OHCI spec allows for
- * up to 15... (which is insane!)
- * 
- * Although I suppose several "ports" could be connected directly to
- * internal laptop devices such as a keyboard, mouse, camera and
- * serial/parallel ports.  hmm...  That'd be neat.
- */
-#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
-
-/*
- * This is the structure of the OHCI controller's memory mapped I/O
- * region.  This is Memory Mapped I/O.  You must use the readl() and
- * writel() macros defined in asm/io.h to access these!!
- */
-struct ohci_regs {
-	/* control and status registers */
-	__u32	revision;
-	__u32	control;
-	__u32	cmdstatus;
-	__u32	intrstatus;
-	__u32	intrenable;
-	__u32	intrdisable;
-	/* memory pointers */
-	__u32	hcca;
-	__u32	ed_periodcurrent;
-	__u32	ed_controlhead;
-	__u32	ed_controlcurrent;
-	__u32	ed_bulkhead;
-	__u32	ed_bulkcurrent;
-	__u32	current_donehead; /* The driver should get this from the HCCA */
-	/* frame counters */
-	__u32	fminterval;
-	__u32	fmremaining;
-	__u32	fmnumber;
-	__u32	periodicstart;
-	__u32	lsthresh;
-	/* Root hub ports */
-	struct	ohci_roothub_regs {
-		__u32	a;
-		__u32	b;
-		__u32	status;
-		__u32	portstatus[MAX_ROOT_PORTS];
-	} roothub;
-} __attribute((aligned(32)));
-
-/* 
- * Read a MMIO register and re-write it after ANDing with (m)
- */
-#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
-
-/*
- * Read a MMIO register and re-write it after ORing with (b)
- */
-#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
-
-
-#define PORT_CCS	(1)		/* port current connect status */
-#define PORT_PES	(1 << 1)	/* port enable status */
-#define PORT_PSS	(1 << 2)	/* port suspend status */
-#define PORT_POCI	(1 << 3)	/* port overcurrent indicator */
-#define PORT_PRS	(1 << 4)	/* port reset status */
-#define PORT_PPS	(1 << 8)	/* port power status */
-#define PORT_LSDA	(1 << 9)	/* port low speed dev. attached */
-#define PORT_CSC	(1 << 16)	/* port connect status change */
-#define PORT_PESC	(1 << 17)	/* port enable status change */
-#define PORT_PSSC	(1 << 18)	/* port suspend status change */
-#define PORT_OCIC	(1 << 19)	/* port over current indicator chg */
-#define PORT_PRSC	(1 << 20)	/* port reset status change */
-
-/*
- * Root Hub status register masks
- */
-#define OHCI_ROOT_LPS	(1)		/* turn off root hub ports power */
-#define OHCI_ROOT_OCI	(1 << 1)	/* Overcurrent Indicator */
-#define OHCI_ROOT_DRWE	(1 << 15)	/* Device remote wakeup enable */
-#define OHCI_ROOT_LPSC	(1 << 16)	/* turn on root hub ports power */
-#define OHCI_ROOT_OCIC	(1 << 17)	/* Overcurrent indicator change */
-#define OHCI_ROOT_CRWE	(1 << 31)	/* Clear RemoteWakeupEnable */
-
-/*
- * Interrupt register masks
- */
-#define OHCI_INTR_SO	(1)
-#define OHCI_INTR_WDH	(1 << 1)
-#define OHCI_INTR_SF	(1 << 2)
-#define OHCI_INTR_RD	(1 << 3)
-#define OHCI_INTR_UE	(1 << 4)
-#define OHCI_INTR_FNO	(1 << 5)
-#define OHCI_INTR_RHSC	(1 << 6)
-#define OHCI_INTR_OC	(1 << 30)
-#define OHCI_INTR_MIE	(1 << 31)
-
-/*
- * Control register masks
- */
-#define OHCI_USB_OPER		(2 << 6)
-#define OHCI_USB_SUSPEND	(3 << 6)
-#define OHCI_USB_PLE		(1 << 2)  /* Periodic (interrupt) list enable */
-#define OHCI_USB_IE		(1 << 3)  /* Isochronous list enable */
-#define OHCI_USB_CLE		(1 << 4)  /* Control list enable */
-#define OHCI_USB_BLE		(1 << 5)  /* Bulk list enable */
-
-/*
- * Command status register masks
- */
-#define OHCI_CMDSTAT_HCR	(1)
-#define OHCI_CMDSTAT_CLF	(1 << 1)
-#define OHCI_CMDSTAT_BLF	(1 << 2)
-#define OHCI_CMDSTAT_OCR	(1 << 3)
-#define OHCI_CMDSTAT_SOC	(3 << 16)
-
-/*
- * This is the full ohci controller description
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs. (Linus)
- */
-struct ohci {
-    	int irq;
-	struct ohci_regs *regs;			/* OHCI controller's memory */
-	struct usb_bus *bus;
-	struct ohci_device *root_hub;		/* Root hub & controller */
-	struct list_head interrupt_list;	/* List of interrupt active TDs for this OHCI */
-};
-
-#define OHCI_TIMER
-#define OHCI_TIMER_FREQ	(1)		/* frequency of OHCI status checks */
-
-/* Debugging code */
-void show_ohci_ed(struct ohci_ed *ed);
-void show_ohci_td(struct ohci_td *td);
-void show_ohci_status(struct ohci *ohci);
-void show_ohci_device(struct ohci_device *dev);
-void show_ohci_hcca(struct ohci_hcca *hcca);
-
-#endif
-/* vim:sw=8
- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ov511.c linux.18p17/drivers/usb/ov511.c
--- linux.vanilla/drivers/usb/ov511.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/ov511.c	Sun Oct  1 11:20:09 2000
@@ -0,0 +1,3365 @@
+/*
+ * OmniVision OV511 Camera-to-USB Bridge Driver
+ *
+ * Copyright (c) 1999-2000 Mark W. McClelland
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * Snapshot code by Kevin Moore
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ * 
+ * Based on the Linux CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt.
+ * 
+ * Please see the file: linux/Documentation/usb/ov511.txt 
+ * and the website at:  http://alpha.dyndns.org/ov511
+ * for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+static const char version[] = "1.25";
+
+#define __NO_VERSION__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/usb.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/wrapper.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "ov511.h"
+
+#undef OV511_GBR422		/* Experimental -- sets the 7610 to GBR422 */
+
+#define OV511_I2C_RETRIES 3
+
+/* Video Size 640 x 480 x 3 bytes for RGB */
+#define MAX_FRAME_SIZE (640 * 480 * 3)
+#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
+
+#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \
+		     ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24))
+
+/* PARAMETER VARIABLES: */
+static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
+
+/* 0=no debug messages
+ * 1=init/detection/unload and other significant messages,
+ * 2=some warning messages
+ * 3=config/control function calls
+ * 4=most function calls and data parsing messages
+ * 5=highly repetitive mesgs
+ * NOTE: This should be changed to 0, 1, or 2 for production kernels
+ */
+static int debug = 3;
+
+/* Fix vertical misalignment of red and blue at 640x480 */
+static int fix_rgb_offset = 0;
+
+/* Snapshot mode enabled flag */
+static int snapshot = 0;
+
+/* Sensor detection override (global for all attached cameras) */
+static int sensor = 0;
+
+/* Increase this if you are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 5;
+
+/* For legal values, see the OV7610/7620 specs under register Common F,
+ * upper nybble  (set to 0-F) */
+static int aperture = -1;
+
+/* Force image to be read in RGB instead of BGR. This option allow
+ * programs that expect RGB data (e.g. gqcam) to work with this driver. */
+static int force_rgb = 0;
+
+/* Number of seconds before inactive buffers are deallocated */
+static int buf_timeout = 5;
+
+/* Number of cameras to stream from simultaneously */
+static int cams = 1;
+
+/* Prevent apps from timing out if frame is not done in time */
+static int retry_sync = 0;
+
+/* Enable compression. This is for experimentation only; compressed images
+ * still cannot be decoded yet. */
+static int compress = 0;
+
+/* Display test pattern - doesn't work yet either */
+static int testpat = 0;
+
+MODULE_PARM(autoadjust, "i");
+MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max");
+MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM_DESC(fix_rgb_offset, "Fix vertical misalignment of red and blue at 640x480");
+MODULE_PARM(snapshot, "i");
+MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
+MODULE_PARM(sensor, "i");
+MODULE_PARM_DESC(sensor, "Override sensor detection");
+MODULE_PARM(i2c_detect_tries, "i");
+MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor");
+MODULE_PARM(aperture, "i");
+MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs");
+MODULE_PARM(force_rgb, "i");
+MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
+MODULE_PARM(buf_timeout, "i");
+MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
+MODULE_PARM(cams, "i");
+MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
+MODULE_PARM(retry_sync, "i");
+MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
+MODULE_PARM(compress, "i");
+MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM(testpat, "i");
+MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
+
+MODULE_AUTHOR("Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>");
+MODULE_DESCRIPTION("OV511 USB Camera Driver");
+
+static char kernel_version[] = UTS_RELEASE;
+
+static struct usb_driver ov511_driver;
+
+/* I know, I know, global variables suck. This is only a temporary hack */
+int output_offset;
+
+/**********************************************************************
+ * List of known OV511-based cameras
+ **********************************************************************/
+
+static struct cam_list clist[] = {
+	{   0, "generic model (no ID)" },
+	{   3, "D-Link DSB-C300" },
+	{   4, "generic OV511/OV7610" },
+	{   5, "Puretek PT-6007" },
+	{  21, "Creative Labs WebCam 3" },
+	{  36, "Koala-Cam" },
+	{  38, "Lifeview USB Life TV" },	/* No support yet! */
+	{ 100, "Lifeview RoboCam" },
+	{ 102, "AverMedia InterCam Elite" },
+	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
+	{  -1, NULL }
+};
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+static struct palette_list plist[] = {
+	{ VIDEO_PALETTE_GREY,	"GREY" },
+	{ VIDEO_PALETTE_HI240,  "HI240" },
+	{ VIDEO_PALETTE_RGB565, "RGB565" },
+	{ VIDEO_PALETTE_RGB24,	"RGB24" },
+	{ VIDEO_PALETTE_RGB32,	"RGB32" },
+	{ VIDEO_PALETTE_RGB555, "RGB555" },
+	{ VIDEO_PALETTE_YUV422, "YUV422" },
+	{ VIDEO_PALETTE_YUYV,   "YUYV" },
+	{ VIDEO_PALETTE_UYVY,   "UYVY" },
+	{ VIDEO_PALETTE_YUV420, "YUV420" },
+	{ VIDEO_PALETTE_YUV411, "YUV411" },
+	{ VIDEO_PALETTE_RAW,    "RAW" },
+	{ VIDEO_PALETTE_YUV422P,"YUV422P" },
+	{ VIDEO_PALETTE_YUV411P,"YUV411P" },
+	{ VIDEO_PALETTE_YUV420P,"YUV420P" },
+	{ VIDEO_PALETTE_YUV410P,"YUV410P" },
+	{ -1, NULL }
+};
+#endif
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the OV511 driver -claudio
+ **********************************************************************/
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none(*pgd)) {
+		pmd = pmd_offset(pgd, adr);
+		if (!pmd_none(*pmd)) {
+			ptep = pte_offset(pmd, adr);
+			pte = *ptep;
+			if (pte_present(pte))
+				ret = page_address(pte_page(pte)) |
+				      (adr & (PAGE_SIZE-1));
+		}
+	}
+
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+	void *mem;
+	unsigned long adr, page;
+
+	/* Round it off to PAGE_SIZE */
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	mem = vmalloc_32(size);
+	if (!mem)
+		return NULL;
+
+	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+		mem_map_reserve(MAP_NR(__va(page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+	unsigned long adr, page;
+
+	if (!mem)
+		return;
+
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	adr=(unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+		mem_map_unreserve(MAP_NR(__va(page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	vfree(mem);
+}
+
+/**********************************************************************
+ * /proc interface
+ * Based on the CPiA driver version 0.7.4 -claudio
+ **********************************************************************/
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
+static struct proc_dir_entry *ov511_proc_entry = NULL;
+extern struct proc_dir_entry *video_proc_entry;
+
+#define YES_NO(x) ((x) ? "yes" : "no")
+
+static int ov511_read_proc(char *page, char **start, off_t off,
+                          int count, int *eof, void *data)
+{
+	char *out = page;
+	int i, j, len;
+	struct usb_ov511 *ov511 = data;
+
+	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
+	 *            or we need to get more sophisticated. */
+
+	out += sprintf (out, "driver_version  : %s\n", version);
+	out += sprintf (out, "custom_id       : %d\n", ov511->customid);
+	out += sprintf (out, "model           : %s\n", ov511->desc ?
+		clist[ov511->desc].description : "unknown");
+	out += sprintf (out, "streaming       : %s\n", YES_NO (ov511->streaming));
+	out += sprintf (out, "grabbing        : %s\n", YES_NO (ov511->grabbing));
+	out += sprintf (out, "compress        : %s\n", YES_NO (ov511->compress));
+	out += sprintf (out, "subcapture      : %s\n", YES_NO (ov511->sub_flag));
+	out += sprintf (out, "sub_size        : %d %d %d %d\n",
+		ov511->subx, ov511->suby, ov511->subw, ov511->subh);
+	out += sprintf (out, "data_format     : %s\n", force_rgb ? "RGB" : "BGR");
+	out += sprintf (out, "brightness      : %d\n", ov511->brightness >> 8);
+	out += sprintf (out, "colour          : %d\n", ov511->colour >> 8);
+	out += sprintf (out, "contrast        : %d\n", ov511->contrast >> 8);
+	out += sprintf (out, "num_frames      : %d\n", OV511_NUMFRAMES);
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		out += sprintf (out, "frame           : %d\n", i);
+		out += sprintf (out, "  depth         : %d\n",
+			ov511->frame[i].depth);
+		out += sprintf (out, "  size          : %d %d\n",
+			ov511->frame[i].width, ov511->frame[i].height);
+		out += sprintf (out, "  format        : ");
+		for (j = 0; plist[j].num >= 0; j++) {
+			if (plist[j].num == ov511->frame[i].format) {
+				out += sprintf (out, "%s\n", plist[j].name);
+				break;
+			}
+		}
+		if (plist[j].num < 0)
+			out += sprintf (out, "unknown\n");
+		out += sprintf (out, "  segsize       : %d\n",
+			ov511->frame[i].segsize);
+		out += sprintf (out, "  data_buffer   : 0x%p\n",
+			ov511->frame[i].data);
+	}
+	out += sprintf (out, "snap_enabled    : %s\n", YES_NO (ov511->snap_enabled));
+	out += sprintf (out, "bridge          : %s\n",
+		ov511->bridge == BRG_OV511 ? "OV511" :
+		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+		"unknown");
+	out += sprintf (out, "sensor          : %s\n",
+		ov511->sensor == SEN_OV7610 ? "OV7610" :
+		ov511->sensor == SEN_OV7620 ? "OV7620" :
+		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
+		"unknown");
+	out += sprintf (out, "packet_size     : %d\n", ov511->packet_size);
+	out += sprintf (out, "framebuffer     : 0x%p\n", ov511->fbuf);
+	
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0) return 0;
+	} else
+		len = count;
+
+	*start = page + off;
+
+	return len;
+}
+
+static int ov511_write_proc(struct file *file, const char *buffer,
+                           unsigned long count, void *data)
+{
+	return -EINVAL;
+}
+
+static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+{
+	char name[7];
+	struct proc_dir_entry *ent;
+	
+	if (!ov511_proc_entry || !ov511)
+		return;
+
+	sprintf(name, "video%d", ov511->vdev.minor);
+	PDEBUG (4, "creating /proc/video/ov511/%s", name);
+	
+	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
+
+	if (!ent)
+		return;
+
+	ent->data = ov511;
+	ent->read_proc = ov511_read_proc;
+	ent->write_proc = ov511_write_proc;
+	ov511->proc_entry = ent;
+}
+
+static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+{
+	char name[7];
+	
+	if (!ov511 || !ov511->proc_entry)
+		return;
+	
+	sprintf(name, "video%d", ov511->vdev.minor);
+	PDEBUG (4, "destroying %s", name);
+	remove_proc_entry(name, ov511_proc_entry);
+	ov511->proc_entry = NULL;
+}
+
+static void proc_ov511_create(void)
+{
+	/* No current standard here. Alan prefers /proc/video/ as it keeps
+	 * /proc "less cluttered than /proc/randomcardifoundintheshed/"
+	 * -claudio
+	 */
+	if (video_proc_entry == NULL) {
+		err("Unable to initialise /proc/video/ov511");
+		return;
+	}
+
+	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry);
+
+	if (ov511_proc_entry)
+		ov511_proc_entry->owner = THIS_MODULE;
+	else
+		err("Unable to initialise /proc/ov511");
+}
+
+static void proc_ov511_destroy(void)
+{
+	PDEBUG (3, "removing /proc/video/ov511");
+
+	if (ov511_proc_entry == NULL)
+		return;
+
+	remove_proc_entry("ov511", video_proc_entry);
+}
+#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
+
+
+/**********************************************************************
+ *
+ * Camera interface
+ *
+ **********************************************************************/
+
+static int ov511_reg_write(struct usb_device *dev,
+			   unsigned char reg,
+			   unsigned char value)
+{
+	int rc;
+
+	rc = usb_control_msg(dev,
+		usb_sndctrlpipe(dev, 0),
+		2 /* REG_IO */,
+		USB_TYPE_CLASS | USB_RECIP_DEVICE,
+		0, (__u16)reg, &value, 1, HZ);	
+
+	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+
+	if (rc < 0)
+		err("reg write: error %d", rc);
+
+	return rc;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
+{
+	int rc;
+	unsigned char buffer[1];
+
+	rc = usb_control_msg(dev,
+		usb_rcvctrlpipe(dev, 0),
+		2 /* REG_IO */,
+		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
+		0, (__u16)reg, buffer, 1, HZ);
+                               
+	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
+	
+	if (rc < 0) {
+		err("reg read: error %d", rc);
+		return rc;
+	} else {
+		return buffer[0];	
+	}
+}
+
+static int ov511_i2c_write(struct usb_device *dev,
+			   unsigned char reg,
+			   unsigned char value)
+{
+	int rc, retries;
+
+	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+
+	/* Three byte write cycle */
+	for (retries = OV511_I2C_RETRIES; ; ) {
+		/* Select camera register */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+		if (rc < 0) goto error;
+
+		/* Write "value" to I2C data port of OV511 */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);	
+		if (rc < 0) goto error;
+
+		/* Initiate 3-byte write cycle */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x01);
+		if (rc < 0) goto error;
+
+		do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
+		while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+		if (rc < 0) goto error;
+
+		if ((rc&2) == 0) /* Ack? */
+			break;
+#if 0
+		/* I2C abort */	
+		ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
+#endif
+		if (--retries < 0) {
+			err("i2c write retries exhausted");
+			rc = -1;
+			goto error;
+		}
+	}
+
+	return 0;
+
+error:
+	err("i2c write: error %d", rc);
+	return rc;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+{
+	int rc, value, retries;
+
+	/* Two byte write cycle */
+	for (retries = OV511_I2C_RETRIES; ; ) {
+		/* Select camera register */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+		if (rc < 0) goto error;
+
+		/* Initiate 2-byte write cycle */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03);
+		if (rc < 0) goto error;
+
+		do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
+		while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+		if (rc < 0) goto error;
+
+		if ((rc&2) == 0) /* Ack? */
+			break;
+
+		/* I2C abort */	
+		ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
+
+		if (--retries < 0) {
+			err("i2c write retries exhausted");
+			rc = -1;
+			goto error;
+		}
+	}
+
+	/* Two byte read cycle */
+	for (retries = OV511_I2C_RETRIES; ; ) {
+		/* Initiate 2-byte read cycle */
+		rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
+		if (rc < 0) goto error;
+
+		do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
+		while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+		if (rc < 0) goto error;
+
+		if ((rc&2) == 0) /* Ack? */
+			break;
+
+		/* I2C abort */	
+		rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
+		if (rc < 0) goto error;
+
+		if (--retries < 0) {
+			err("i2c read retries exhausted");
+			rc = -1;
+			goto error;
+		}
+	}
+
+	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
+
+	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
+		
+	/* This is needed to make ov511_i2c_write() work */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
+	if (rc < 0)
+		goto error;
+	
+	return value;
+
+error:
+	err("i2c read: error %d", rc);
+	return rc;
+}
+
+static int ov511_write_regvals(struct usb_device *dev,
+			       struct ov511_regvals * pRegvals)
+{
+	int rc;
+
+	while (pRegvals->bus != OV511_DONE_BUS) {
+		if (pRegvals->bus == OV511_REG_BUS) {
+			if ((rc = ov511_reg_write(dev, pRegvals->reg,
+			                           pRegvals->val)) < 0)
+				goto error;
+		} else if (pRegvals->bus == OV511_I2C_BUS) {
+			if ((rc = ov511_i2c_write(dev, pRegvals->reg, 
+			                           pRegvals->val)) < 0)
+				goto error;
+		} else {
+			err("Bad regval array");
+			rc = -1;
+			goto error;
+		}
+		pRegvals++;
+	}
+	return 0;
+
+error:
+	err("write regvals: error %d", rc);
+	return rc;
+}
+
+#ifdef OV511_DEBUG 
+static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn)
+{
+	int i;
+	int rc;
+	for(i = reg1; i <= regn; i++) {
+		rc = ov511_i2c_read(dev, i);
+		PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+	}
+}
+
+static void ov511_dump_i2c_regs(struct usb_device *dev)
+{
+	PDEBUG(3, "I2C REGS");
+	ov511_dump_i2c_range(dev, 0x00, 0x7C);
+}
+
+#if 0
+static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
+{
+	int i;
+	int rc;
+	for(i = reg1; i <= regn; i++) {
+	  rc = ov511_reg_read(dev, i);
+	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
+	}
+}
+
+static void ov511_dump_regs(struct usb_device *dev)
+{
+	PDEBUG(1, "CAMERA INTERFACE REGS");
+	ov511_dump_reg_range(dev, 0x10, 0x1f);
+	PDEBUG(1, "DRAM INTERFACE REGS");
+	ov511_dump_reg_range(dev, 0x20, 0x23);
+	PDEBUG(1, "ISO FIFO REGS");
+	ov511_dump_reg_range(dev, 0x30, 0x31);
+	PDEBUG(1, "PIO REGS");
+	ov511_dump_reg_range(dev, 0x38, 0x39);
+	ov511_dump_reg_range(dev, 0x3e, 0x3e);
+	PDEBUG(1, "I2C REGS");
+	ov511_dump_reg_range(dev, 0x40, 0x49);
+	PDEBUG(1, "SYSTEM CONTROL REGS");
+	ov511_dump_reg_range(dev, 0x50, 0x55);
+	ov511_dump_reg_range(dev, 0x5e, 0x5f);
+	PDEBUG(1, "OmniCE REGS");
+	ov511_dump_reg_range(dev, 0x70, 0x79);
+	ov511_dump_reg_range(dev, 0x80, 0x9f);
+	ov511_dump_reg_range(dev, 0xa0, 0xbf);
+
+}
+#endif
+#endif
+
+static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+{
+	int rc;
+	
+	PDEBUG(3, "Reset: type=0x%X", reset_type);
+	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
+	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
+
+	if (rc < 0)
+		err("reset: command failed");
+
+	return rc;
+}
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline int ov511_stop(struct usb_device *dev)
+{
+	PDEBUG(4, "stopping");
+	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called */
+static inline int ov511_restart(struct usb_device *dev)
+{
+	PDEBUG(4, "restarting");
+	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+}
+
+static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+{
+	int alt, mult;
+
+	if (ov511_stop(ov511->dev) < 0)
+		return -EIO;
+
+	mult = size >> 5;
+
+	if (ov511->bridge == BRG_OV511) {
+		if (size == 0) alt = OV511_ALT_SIZE_0;
+		else if (size == 257) alt = OV511_ALT_SIZE_257;
+		else if (size == 513) alt = OV511_ALT_SIZE_513;
+		else if (size == 769) alt = OV511_ALT_SIZE_769;
+		else if (size == 993) alt = OV511_ALT_SIZE_993;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (size == 0) alt = OV511PLUS_ALT_SIZE_0;
+		else if (size == 33) alt = OV511PLUS_ALT_SIZE_33;
+		else if (size == 129) alt = OV511PLUS_ALT_SIZE_129;
+		else if (size == 257) alt = OV511PLUS_ALT_SIZE_257;
+		else if (size == 385) alt = OV511PLUS_ALT_SIZE_385;
+		else if (size == 513) alt = OV511PLUS_ALT_SIZE_513;
+		else if (size == 769) alt = OV511PLUS_ALT_SIZE_769;
+		else if (size == 961) alt = OV511PLUS_ALT_SIZE_961;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
+	} else {
+		err("Set packet size: Invalid bridge type");
+		return -EINVAL;
+	}
+
+	PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
+
+	if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
+		return -ENOMEM;
+	
+	if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
+		err("Set packet size: set interface error");
+		return -EBUSY;
+	}
+
+	// FIXME - Should we only reset the FIFO?
+	if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
+		return -ENOMEM;
+
+	ov511->packet_size = size;
+
+	if (ov511_restart(ov511->dev) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+
+static inline int
+ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+	int ret;
+	struct usb_device *dev = ov511->dev;
+
+	PDEBUG(4, "ov511_set_picture");
+
+	if (ov511_stop(dev) < 0)
+		return -EIO;
+
+	ov511->contrast = p->contrast;
+	ov511->brightness = p->brightness;
+	ov511->colour = p->colour;
+	ov511->hue = p->hue;
+	ov511->whiteness = p->whiteness;
+
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
+		return -EIO;
+#if 0
+	/* disable auto adjust mode */
+	if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
+		return -EIO;
+#endif
+	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE
+		|| ov511->sensor == SEN_OV6620)
+		if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
+			return -EIO;
+
+	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
+		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+			return -EIO;
+
+		if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
+			return -EIO;
+
+		if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
+			return -EIO;
+
+		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
+			return -EIO;
+	} else if ((ov511->sensor == SEN_OV7620) 
+	         || (ov511->sensor == SEN_OV7620AE)) {
+#if 0
+		cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
+		cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
+	        // DEBUG_CODE
+	        PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
+	         ov511_i2c_read(dev, OV7610_REG_BRT)); 
+
+		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+			return -EIO;
+#endif
+	}
+
+	if (ov511_restart(dev) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static inline int
+ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+	int ret;
+	struct usb_device *dev = ov511->dev;
+
+	PDEBUG(4, "ov511_get_picture");
+
+	if (ov511_stop(dev) < 0)
+		return -EIO;
+
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
+	p->colour = ret << 8;
+
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
+	p->contrast = ret << 8;
+
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
+	p->brightness = ret << 8;
+
+	/* This may not be the best way to do it */
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO;
+	p->hue = ret << 8;
+
+	p->whiteness = 105 << 8;
+
+	/* Can we get these from frame[0]? -claudio? */
+	p->depth = ov511->frame[0].depth;
+	p->palette = ov511->frame[0].format;
+
+	if (ov511_restart(dev) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
+static struct mode_list mlist[] = {
+	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
+	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
+	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
+	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 0, 0 }
+};
+
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov511,
+		     int width, int height, int mode, int sub_flag)
+{
+	int i;
+	struct usb_device *dev = ov511->dev;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hwscale = 0, vwscale = 0;
+
+	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+	       width, height, mode, sub_flag);
+
+	if (ov511_stop(ov511->dev) < 0)
+		return -EIO;
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		ov511_reg_write(dev, 0x16, 0x00);
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* these aren't valid on the OV6620/OV7620 */
+			ov511_i2c_write(dev, 0x0e, 0x44);
+		}
+		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x00);
+		ov511_reg_write(dev, 0x1f, 0x01);
+	} else {
+		ov511_reg_write(dev, 0x16, 0x01);
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* not valid on the OV6620/OV7620 */
+			ov511_i2c_write(dev, 0x0e, 0x04);
+		}
+		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x01);
+		ov511_reg_write(dev, 0x1f, 0x03);
+	}
+
+	/* The different sensor ICs handle setting up of window differently */
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+		hwsbase = 0x38;
+		hwebase = 0x39;
+		vwsbase = 0x03;
+		vwebase = 0x04;
+		break;
+	case SEN_OV7620:
+		hwsbase = 0x2c;
+		hwebase = 0x2d;
+		vwsbase = vwebase = 0x05;
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	/* Bit 5 of COM C register varies with sensor */ 
+	if (ov511->sensor == SEN_OV6620) {
+		if (width > 176 && height > 144) {  /* CIF */
+			ov511_i2c_write(dev, 0x14, 0x04);
+			hwscale = 1;
+			vwscale = 1;  /* The datasheet says 0; it's wrong */
+			hwsize = 352;
+			vwsize = 288;
+		} else {			    /* QCIF */
+			ov511_i2c_write(dev, 0x14, 0x24);
+			hwsize = 176;
+			vwsize = 144;
+		}
+	}
+	else {
+		if (width > 320 && height > 240) {  /* VGA */
+			ov511_i2c_write(dev, 0x14, 0x04);
+			hwscale = 2;
+			vwscale = 1;
+			hwsize = 640;
+			vwsize = 480;
+		} else {			    /* QVGA */
+			ov511_i2c_write(dev, 0x14, 0x24);
+			hwscale = 1;
+			hwsize = 320;
+			vwsize = 240;
+		}	
+	}
+
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+	if (sub_flag) {
+		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
+		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale));
+		ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale));
+	} else {
+		ov511_i2c_write(dev, 0x17, hwsbase);
+		ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase);
+		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
+	}
+
+	for (i = 0; mlist[i].width; i++) {
+		int lncnt, pxcnt, clock;
+
+		if (width != mlist[i].width || height != mlist[i].height)
+			continue;
+
+		if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
+			continue;
+
+		/* Here I'm assuming that snapshot size == image size.
+		 * I hope that's always true. --claudio
+		 */
+		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+
+		ov511_reg_write(dev, 0x12, pxcnt);
+		ov511_reg_write(dev, 0x13, lncnt);
+		ov511_reg_write(dev, 0x14, mlist[i].pxdv);
+		ov511_reg_write(dev, 0x15, mlist[i].lndv);
+		ov511_reg_write(dev, 0x18, mlist[i].m420);
+
+		/* Snapshot additions */
+		ov511_reg_write(dev, 0x1a, pxcnt);
+		ov511_reg_write(dev, 0x1b, lncnt);
+                ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
+                ov511_reg_write(dev, 0x1d, mlist[i].lndv);
+
+		/* Calculate and set the clock divisor */
+		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+			* (mlist[i].color ? 3 : 2) / 2) / 66000;
+		ov511_i2c_write(dev, 0x11, clock);
+
+#ifdef OV511_GBR422
+		ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
+#else
+		ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+#endif
+
+		/* 7620/6620 don't have register 0x35, so play it safe */
+		if (ov511->sensor == SEN_OV7610 ||
+		    ov511->sensor == SEN_OV7620AE)
+			ov511_i2c_write(dev, 0x35, mlist[i].common_L);
+
+		break;
+	}
+
+	if (compress) {
+		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
+		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+	}
+
+	if (ov511_restart(ov511->dev) < 0)
+		return -EIO;
+
+	if (mlist[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
+		return -EINVAL;
+	}
+
+#ifdef OV511_DEBUG
+	if (debug >= 5)
+		ov511_dump_i2c_regs(dev);
+#endif
+
+	return 0;
+}
+
+/**********************************************************************
+ *
+ * Color correction functions
+ *
+ **********************************************************************/
+
+/*
+ * Turn a YUV4:2:0 block into an RGB block
+ *
+ * Video4Linux seems to use the blue, green, red channel
+ * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+ *
+ * Color space conversion coefficients taken from the excellent
+ * http://www.inforamp.net/~poynton/ColorFAQ.html
+ * In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+ * Y values are given for all 4 pixels, but the U (Pb)
+ * and V (Pr) are assumed constant over the 2x2 block.
+ *
+ * To avoid floating point arithmetic, the color conversion
+ * coefficients are scaled into 16.16 fixed-point integers.
+ * They were determined as follows:
+ *
+ *	double brightness = 1.0;  (0->black; 1->full scale) 
+ *	double saturation = 1.0;  (0->greyscale; 1->full color)
+ *	double fixScale = brightness * 256 * 256;
+ *	int rvScale = (int)(1.402 * saturation * fixScale);
+ *	int guScale = (int)(-0.344136 * saturation * fixScale);
+ *	int gvScale = (int)(-0.714136 * saturation * fixScale);
+ *	int buScale = (int)(1.772 * saturation * fixScale);
+ *	int yScale = (int)(fixScale);	
+ */
+
+/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+
+static inline void
+ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
+	int rowPixels, unsigned char * rgb)
+{
+	const int rvScale = 91881;
+	const int guScale = -22553;
+	const int gvScale = -46801;
+	const int buScale = 116129;
+	const int yScale  = 65536;
+	int r, g, b;
+
+	g = guScale * u + gvScale * v;
+	if (force_rgb) {
+		r = buScale * u;
+		b = rvScale * v;
+	} else {
+		r = rvScale * v;
+		b = buScale * u;
+	}
+
+	yTL *= yScale; yTR *= yScale;
+	yBL *= yScale; yBR *= yScale;
+
+	/* Write out top two pixels */
+	rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
+	rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
+
+	/* Skip down to next line to write out bottom two pixels */
+	rgb += 3 * rowPixels;
+	rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
+	rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+}
+
+/*
+ * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
+ * The first 64 bytes of each segment are U, the next 64 are V.  The U and
+ * V are arranged as follows:
+ *
+ *      0  1 ...  7
+ *      8  9 ... 15
+ *           ...   
+ *     56 57 ... 63
+ *
+ * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+ *
+ * The next 256 bytes are full resolution Y data and represent 4 squares
+ * of 8x8 pixels as follows:
+ *
+ *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
+ *      8  9 ... 15    72  73 ...  79        200 201 ... 207
+ *           ...              ...                    ...
+ *     56 57 ... 63   120 121 ... 127   ...  248 249 ... 255
+ *
+ * Note that the U and V data in one segment represents a 16 x 16 pixel
+ * area, but the Y data represents a 32 x 8 pixel area.
+ *
+ * If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments,
+ * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
+ * this puts the data on the standard output and can be analyzed with the
+ * parseppm.c utility I wrote.  That's a much faster way for figuring out how
+ * this data is scrambled.
+ */
+
+#define HDIV 8
+#define WDIV (256/HDIV)
+
+#undef OV511_DUMPPIX
+
+/* #define this and OV511_DUMPPIX to disable parsing of UV data */
+#undef OV511_FORCE_MONO
+
+#ifdef OV511_GBR422
+static void
+ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
+		       int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
+		
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+				*(pOut1 + iWidth*3 + 3) = *pIn++;
+			pOut1 += 6;
+		}
+		pOut += iWidth*3*2;
+	}
+
+	pIn = pIn0 + 64;
+	pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
+				*(pOut1 + iWidth*3 + 3) = *pIn++;
+			pOut1 += 6;
+		}
+		pOut += iWidth*3*2;
+	}
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY + 1;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1 = *pIn++;
+				pOut1 += 3;
+			}
+			pOut1 += (iWidth - 8) * 3;
+		}
+		pOut += 8 * 3;
+	}
+}
+
+#else
+
+static void
+ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
+		       int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+#ifndef OV511_DUMPPIX
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+
+	/* Just copy the Y's if in the first stripe */
+	if (!iHalf) {
+		pIn = pIn0 + 128;
+		pOut = pOut0 + iOutY;
+		for (k = 0; k < 4; k++) {
+			pOut1 = pOut;
+			for (l = 0; l < 8; l++) {
+				for (m = 0; m < 8; m++) {
+					*pOut1 = *pIn++;
+					pOut1 += 3;
+				}
+				pOut1 += (iWidth - 8) * 3;
+			}
+			pOut += 8 * 3;
+		}
+	}
+
+	/* Use the first half of VUs to calculate value */
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV;
+	for (l = 0; l < 4; l++) {
+		for (m=0; m<8; m++) {
+			int y00 = *(pOut);
+			int y01 = *(pOut+3);
+			int y10 = *(pOut+iWidth*3);
+			int y11 = *(pOut+iWidth*3+3);
+			int v   = *(pIn+64) - 128;
+			int u   = *pIn++ - 128;
+			ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
+				pOut);
+			pOut += 6;
+		}
+		pOut += (iWidth*2 - 16) * 3;
+	}
+
+	/* Just copy the other UV rows */
+	for (l = 0; l < 4; l++) {
+		for (m = 0; m < 8; m++) {
+			*pOut++ = *(pIn + 64);
+			*pOut = *pIn++;
+			pOut += 5;
+		}
+		pOut += (iWidth*2 - 16) * 3;
+	}
+
+	/* Calculate values if it's the second half */
+	if (iHalf) {
+		pIn = pIn0 + 128;
+		pOut = pOut0 + iOutY;
+		for (k = 0; k < 4; k++) {
+			pOut1 = pOut;
+			for (l=0; l<4; l++) {
+				for (m=0; m<4; m++) {
+					int y10 = *(pIn+8);
+					int y00 = *pIn++;
+					int y11 = *(pIn+8);
+					int y01 = *pIn++;
+					int v   = *pOut1 - 128;
+					int u   = *(pOut1+1) - 128;
+					ov511_move_420_block(y00, y01, y10,
+						y11, u, v, iWidth, pOut1);
+					pOut1 += 6;
+				}
+				pOut1 += (iWidth*2 - 8) * 3;
+				pIn += 8;
+			}
+			pOut += 8 * 3;
+		}
+	}
+#else
+
+#ifndef OV511_FORCE_MONO
+	/* Just dump pix data straight out for debug */
+	int i, j;
+
+	pOut0 += iOutY;
+	for (i = 0; i < HDIV; i++) {
+		for (j = 0; j < WDIV; j++) {
+			*pOut0++ = *pIn0++;
+			*pOut0++ = *pIn0++;
+			*pOut0++ = *pIn0++;
+		}
+		pOut0 += (iWidth - WDIV) * 3;
+	}
+#else
+
+#if 1
+	/* This converts the Y data to "black-and-white" RGB data */
+	/* Useful for experimenting with compression */
+	int k, l, m;
+	unsigned char *pIn, *pOut, *pOut1;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1++ = *pIn;
+				*pOut1++ = *pIn;
+				*pOut1++ = *pIn++;
+			}
+			pOut1 += (iWidth - 8) * 3;
+		}
+		pOut += 8 * 3;
+	}
+#else
+	/* This will dump the Y channel data stream as-is */ 
+	int count;
+	unsigned char *pIn, *pOut;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + output_offset;
+	for (count = 0; count < 256; count++) {
+		*pOut++ = *pIn;
+		*pOut++ = *pIn;
+		*pOut++ = *pIn++;
+		output_offset += 3;
+	}	
+#endif
+
+#endif
+
+#endif
+}
+#endif
+
+/* This converts YUV420 segments to YUYV */
+static void
+ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
+		        int iOutY, int iOutUV, int iWidth)
+{
+	int k, l, m;
+	unsigned char *pIn, *pOut, *pOut1;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1 = (*pIn++);
+				pOut1 += 2;
+			}
+			pOut1 += (iWidth - 8) * 2;
+		}
+		pOut += 8 * 2;
+	}
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + 1;
+	for (l = 0; l < 8; l++) {
+		for (m=0; m<8; m++) {
+			int v   = *(pIn+64);
+			int u   = *pIn++;
+			
+			*pOut = u;
+			*(pOut+2) = v;
+			*(pOut+iWidth) = u;
+			*(pOut+iWidth+2) = v;
+			pOut += 4;
+		}
+		pOut += (iWidth*4 - 32);
+	}
+}
+
+static void
+ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0,
+		        int iOutY, int iOutUV, int iWidth, int iHeight)
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+	unsigned a = iWidth * iHeight;
+	unsigned w = iWidth / 2;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + a;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+		pOut += w;
+	}
+
+	pIn = pIn0 + 64;
+	pOut = pOut0 + iOutUV + a + a/4;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+		pOut += w;
+	}
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++)
+				*pOut1++ =*pIn++;
+			pOut1 += iWidth - 8;
+		}
+		pOut += 8;
+	}
+}
+
+static void
+ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
+		       int iOutY, int iOutUV, int iWidth, int iHeight)
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+	unsigned a = iWidth * iHeight;
+	unsigned w = iWidth / 2;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + a;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + w) = *pIn++;
+			pOut1++;
+		}
+		pOut += iWidth;
+	}
+
+	pIn = pIn0 + 64;
+	pOut = pOut0 + iOutUV + a + a/2;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + w) = *pIn++;
+			pOut1++;
+		}
+		pOut += iWidth;
+	}
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++)
+				*pOut1++ =*pIn++;
+			pOut1 += iWidth - 8;
+		}
+		pOut += 8;
+	}
+}
+
+/*
+ * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
+ *      8  9 ... 15    72  73 ...  79        200 201 ... 207
+ *           ...              ...                    ...
+ *     56 57 ... 63   120 121 ... 127        248 249 ... 255
+ *
+ */ 
+static void
+ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0,
+		      int iOutY, int iWidth)		    
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1++ = *pIn++;
+			}
+			pOut1 += iWidth - 8;
+		}
+		pOut += 8;
+	}
+}
+
+/*
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ */
+static void fixFrameRGBoffset(struct ov511_frame *frame)
+{
+	int x, y;
+	int rowBytes = frame->width*3, w = frame->width;
+	unsigned char *rgb = frame->data;
+	const int shift = 1;  /* Distance to shift pixels by, vertically */
+
+	/* Don't bother with little images */
+	if (frame->width < 400) 
+		return;
+
+	/* Shift red channel up */
+	for (y = shift; y < frame->height; y++)	{
+		int lp = (y-shift)*rowBytes;     /* Previous line offset */
+		int lc = y*rowBytes;             /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	}
+
+	/* Shift blue channel down */
+	for (y = frame->height-shift-1; y >= 0; y--) {
+		int ln = (y + shift) * rowBytes;  /* Next line offset */
+		int lc = y * rowBytes;            /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	}
+}
+
+/**********************************************************************
+ *
+ * OV511 data transfer, IRQ handler
+ *
+ **********************************************************************/
+
+static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+{
+	unsigned char *cdata;
+	int i, totlen = 0;
+	int aPackNum[10];
+	struct ov511_frame *frame;
+	unsigned char *pData;
+	int iPix;
+
+	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = 0;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1;
+
+		if (!n || ov511->curframe == -1)
+			continue;
+
+		if (st)
+			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
+
+		frame = &ov511->frame[ov511->curframe];
+		
+		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+		 * byte non-zero. The EOF packet has image width/height in the
+		 * 10th and 11th packets. The 9th bit is given as follows:
+		 *
+		 * bit 7: EOF
+		 *     6: compression enabled
+		 *     5: 422/420/400 modes
+		 *     4: 422/420/400 modes
+		 *     3: 1
+		 *     2: snapshot bottom on
+		 *     1: snapshot frame
+		 *     0: even/odd field
+		 */
+
+		/* Check for SOF/EOF packet */
+		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
+		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
+		     (~cdata[8] & 0x08))
+			goto check_middle;
+
+		/* Frame end */
+		if (cdata[8] & 0x80) {
+			struct timeval *ts;
+
+			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
+			do_gettimeofday (ts);
+
+		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
+				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
+				(int)(cdata[9]), (int)(cdata[10]));
+
+			if (frame->scanstate == STATE_LINES) {
+		    		int iFrameNext;
+
+				if (fix_rgb_offset)
+					fixFrameRGBoffset(frame);
+				frame->grabstate = FRAME_DONE;
+
+				if (waitqueue_active(&frame->wq)) {
+					frame->grabstate = FRAME_DONE;
+					wake_up_interruptible(&frame->wq);
+				}
+
+				/* If next frame is ready or grabbing,
+                                 * point to it */
+				iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
+				if (ov511->frame[iFrameNext].grabstate == FRAME_READY
+				    || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) {
+					ov511->curframe = iFrameNext;
+					ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
+				} else {
+					if (frame->grabstate == FRAME_DONE) {
+						PDEBUG(4, "Frame done! congratulations");
+					} else {
+						PDEBUG(4, "Frame not ready? state = %d",
+							ov511->frame[iFrameNext].grabstate);
+					}
+
+					ov511->curframe = -1;
+				}
+			}
+			/* Image corruption caused by misplaced frame->segment = 0
+			 * fixed by carlosf@conectiva.com.br
+			 */
+		} else {
+			/* Frame start */
+			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
+
+			output_offset = 0;
+
+			/* Check to see if it's a snapshot frame */
+			/* FIXME?? Should the snapshot reset go here? Performance? */
+			if (cdata[8] & 0x02) {
+				frame->snapshot = 1;
+				PDEBUG(3, "snapshot detected");
+			}
+
+			frame->scanstate = STATE_LINES;
+			frame->segment = 0;
+		}
+
+check_middle:
+		/* Are we in a frame? */
+		if (frame->scanstate != STATE_LINES)
+			continue;
+
+		/* Deal with leftover from last segment, if any */
+		if (frame->segment) {
+			pData = ov511->scratch;
+			iPix = -ov511->scratchlen;
+			memmove(pData + ov511->scratchlen, cdata,
+				iPix+frame->segsize);
+		} else {
+			pData = &cdata[iPix = 9];
+	 	}
+
+		/* Parse the segments */
+		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
+		    frame->segment < frame->width * frame->height / 256) {
+			int iSegY, iSegUV;
+			int iY, jY, iUV, jUV;
+			int iOutY, iOutYP, iOutUV, iOutUVP;
+			unsigned char *pOut;
+
+			iSegY = iSegUV = frame->segment;
+			pOut = frame->data;
+			frame->segment++;
+			iPix += frame->segsize;
+
+			/* Handle subwindow */
+			if (frame->sub_flag) {
+				int iSeg1;
+
+				iSeg1 = iSegY / (ov511->subw / 32);
+				iSeg1 *= frame->width / 32;
+				iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
+				if (iSegY >= frame->width * ov511->subh / 256)
+					break;
+
+				iSeg1 = iSegUV / (ov511->subw / 16);
+				iSeg1 *= frame->width / 16;
+				iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16));
+
+				pOut += (ov511->subx + ov511->suby * frame->width) *
+					(frame->depth >> 3);
+			}
+
+			/* 
+			 * i counts segment lines
+			 * j counts segment columns
+			 * iOut is the offset (in bytes) of the upper left corner
+			 */
+			iY = iSegY / (frame->width / WDIV);
+			jY = iSegY - iY * (frame->width / WDIV);
+			iOutYP = iY*HDIV*frame->width + jY*WDIV;
+			iOutY = iOutYP * (frame->depth >> 3);
+			iUV = iSegUV / (frame->width / WDIV * 2);
+			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
+			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
+			iOutUV = iOutUVP * (frame->depth >> 3);
+
+			switch (frame->format) {
+			case VIDEO_PALETTE_GREY:
+				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
+				break;
+			case VIDEO_PALETTE_RGB24:
+				ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
+					iY & 1, frame->width);
+				break;
+			case VIDEO_PALETTE_YUV422:
+			case VIDEO_PALETTE_YUYV:
+				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
+				break;
+			case VIDEO_PALETTE_YUV420:
+				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
+					frame->width, frame->height);
+				break;
+			case VIDEO_PALETTE_YUV422P:
+				ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
+					frame->width, frame->height);
+				break;
+			default:
+				err("Unsupported format: %d", frame->format);
+			}
+
+			pData = &cdata[iPix];
+		}
+
+		/* Save extra data for next time */
+		if (frame->segment < frame->width * frame->height / 256) {
+			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
+			if (ov511->scratchlen < frame->segsize)
+				memmove(ov511->scratch, pData, ov511->scratchlen);
+			else
+				ov511->scratchlen = 0;
+		}
+	}
+
+	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
+	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
+	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+
+	return totlen;
+}
+
+static void ov511_isoc_irq(struct urb *urb)
+{
+	int len;
+	struct usb_ov511 *ov511 = urb->context;
+	struct ov511_sbuf *sbuf;
+
+	if (!ov511->dev)
+		return;
+
+	if (!ov511->streaming) {
+		PDEBUG(4, "hmmm... not streaming, but got interrupt");
+		return;
+	}
+	
+	sbuf = &ov511->sbuf[ov511->cursbuf];
+
+	/* Copy the data received into our scratch buffer */
+	if (ov511->curframe >= 0) {
+		len = ov511_move_data(ov511, urb);
+	} else if (waitqueue_active(&ov511->wq)) {
+		wake_up_interruptible(&ov511->wq);
+	}
+
+	/* Move to the next sbuf */
+	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
+
+	return;
+}
+
+static int ov511_init_isoc(struct usb_ov511 *ov511)
+{
+	urb_t *urb;
+	int fx, err, n, size;
+
+	PDEBUG(3, "*** Initializing capture ***");
+
+	ov511->compress = 0;
+	ov511->curframe = -1;
+	ov511->cursbuf = 0;
+	ov511->scratchlen = 0;
+
+	if (ov511->bridge == BRG_OV511)
+		if (cams == 1)			 size = 993;
+		else if (cams == 2)		 size = 513;
+		else if (cams == 3 || cams == 4) size = 257;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	else if (ov511->bridge == BRG_OV511PLUS)
+		if (cams == 1)                      size = 961;
+		else if (cams == 2)                 size = 513;
+		else if (cams == 3 || cams == 4) size = 257;
+		else if (cams >= 5 && cams <= 8)    size = 129;
+		else if (cams >= 9 && cams <= 31)   size = 33;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	else {
+		err("invalid bridge type");
+		return -1;
+	}
+
+	ov511_set_packet_size(ov511, size);
+
+	for (n = 0; n < OV511_NUMSBUF; n++) {
+		urb = usb_alloc_urb(FRAMES_PER_DESC);
+	
+		if (!urb) {
+			err("init isoc: usb_alloc_urb ret. NULL");
+			return -ENOMEM;
+		}
+		ov511->sbuf[n].urb = urb;
+		urb->dev = ov511->dev;
+		urb->context = ov511;
+		urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->transfer_buffer = ov511->sbuf[n].data;
+		urb->complete = ov511_isoc_irq;
+		urb->number_of_packets = FRAMES_PER_DESC;
+		urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+			urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+			urb->iso_frame_desc[fx].length = ov511->packet_size;
+		}
+	}
+
+	ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb;
+	for (n = 0; n < OV511_NUMSBUF - 1; n++)
+		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
+
+	for (n = 0; n < OV511_NUMSBUF; n++) {
+		err = usb_submit_urb(ov511->sbuf[n].urb);
+		if (err)
+			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
+	}
+
+	ov511->streaming = 1;
+
+	return 0;
+}
+
+static void ov511_stop_isoc(struct usb_ov511 *ov511)
+{
+	int n;
+
+	if (!ov511->streaming || !ov511->dev)
+		return;
+
+	PDEBUG (3, "*** Stopping capture ***");
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->streaming = 0;
+
+	/* Unschedule all of the iso td's */
+	for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+		if (ov511->sbuf[n].urb) {
+			ov511->sbuf[n].urb->next = NULL;
+			usb_unlink_urb(ov511->sbuf[n].urb);
+			usb_free_urb(ov511->sbuf[n].urb);
+			ov511->sbuf[n].urb = NULL;
+		}
+	}
+}
+
+static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
+{
+	struct ov511_frame *frame;
+
+	PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
+		framenum);
+	if (!ov511->dev)
+		return -1;
+
+	/* If we're not grabbing a frame right now and the other frame is */
+	/* ready to be grabbed into, then use it instead */
+	if (ov511->curframe == -1) {
+		if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
+			framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+	} else
+		return 0;
+
+	frame = &ov511->frame[framenum];
+
+	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, 
+		frame->width, frame->height);
+
+	frame->grabstate = FRAME_GRABBING;
+	frame->scanstate = STATE_SCANNING;
+	frame->scanlength = 0;		/* accumulated in ov511_parse_data() */
+	frame->snapshot = 0;
+
+	ov511->curframe = framenum;
+
+	/* Make sure it's not too big */
+	if (frame->width > ov511->maxwidth)
+		frame->width = ov511->maxwidth;
+
+	frame->width &= ~7L;		/* Multiple of 8 */
+
+	if (frame->height > ov511->maxheight)
+		frame->height = ov511->maxheight;
+
+	frame->height &= ~3L;		/* Multiple of 4 */
+
+	return 0;
+}
+
+/****************************************************************************
+ *
+ * Buffer management
+ *
+ ***************************************************************************/
+static int ov511_alloc(struct usb_ov511 *ov511)
+{
+	int i;
+
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC) {
+		ov511->buf_state = BUF_ALLOCATED;
+		del_timer(&ov511->buf_timer);
+	}
+
+	if (ov511->buf_state == BUF_ALLOCATED)
+		goto out;
+
+	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
+	if (!ov511->fbuf)
+		goto error;
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].grabstate = FRAME_UNUSED;
+		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
+		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+
+		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
+			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
+		if (!ov511->sbuf[i].data) {
+			while (--i) {
+				kfree(ov511->sbuf[i].data);
+				ov511->sbuf[i].data = NULL;
+			}
+			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+			ov511->fbuf = NULL;
+			goto error;
+		}
+		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
+	}
+	ov511->buf_state = BUF_ALLOCATED;
+out:
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+	return 0;
+error:
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+	up(&ov511->buf_lock);
+	PDEBUG(4, "errored");
+	return -ENOMEM;
+}
+
+/* 
+ * - You must acquire buf_lock before entering this function.
+ * - Because this code will free any non-null pointer, you must be sure to null
+ *   them if you explicitly free them somewhere else!
+ */
+static void ov511_do_dealloc(struct usb_ov511 *ov511)
+{
+	int i;
+	PDEBUG(4, "entered");
+
+	if (ov511->fbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		ov511->fbuf = NULL;
+	}
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		if (ov511->sbuf[i].data) {
+			kfree(ov511->sbuf[i].data);
+			ov511->sbuf[i].data = NULL;
+		}
+	}
+
+	PDEBUG(4, "buffer memory deallocated");
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+	PDEBUG(4, "leaving");
+}
+
+static void ov511_buf_callback(unsigned long data)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)data;
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC)
+		ov511_do_dealloc(ov511);
+
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+}
+
+static void ov511_dealloc(struct usb_ov511 *ov511, int now)
+{
+	struct timer_list *bt = &(ov511->buf_timer);
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	PDEBUG(4, "deallocating buffer memory %s", now ? "now" : "later");
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC) {
+		ov511->buf_state = BUF_ALLOCATED;
+		del_timer(bt);
+	}
+
+	if (now)
+		ov511_do_dealloc(ov511);
+	else {
+		ov511->buf_state = BUF_PEND_DEALLOC;
+		init_timer(bt);
+		bt->function = ov511_buf_callback;
+		bt->data = (unsigned long)ov511;
+		bt->expires = jiffies + buf_timeout * HZ;
+		add_timer(bt);
+	}
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+}
+
+/****************************************************************************
+ *
+ * V4L API
+ *
+ ***************************************************************************/
+
+static int ov511_open(struct video_device *dev, int flags)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	int err = 0;
+
+	MOD_INC_USE_COUNT;
+	PDEBUG(4, "opening");
+	down(&ov511->lock);
+
+	err = -EBUSY;
+	if (ov511->user) 
+		goto out;
+
+	err = -ENOMEM;
+	if (ov511_alloc(ov511))
+		goto out;
+
+	ov511->sub_flag = 0;
+
+	err = ov511_init_isoc(ov511);
+	if (err) {
+		ov511_dealloc(ov511, 0);
+		goto out;
+	}
+
+	ov511->user++;
+
+out:
+	up(&ov511->lock);
+
+	if (err)
+		MOD_DEC_USE_COUNT;
+
+	return err;
+}
+
+static void ov511_close(struct video_device *dev)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+
+	PDEBUG(4, "ov511_close");
+	
+	down(&ov511->lock);
+
+	ov511->user--;
+	ov511_stop_isoc(ov511);
+
+	if (ov511->dev)
+		ov511_dealloc(ov511, 0);
+
+	up(&ov511->lock);
+
+	if (!ov511->dev) {
+		ov511_dealloc(ov511, 1);
+		video_unregister_device(&ov511->vdev);
+		kfree(ov511);
+		ov511 = NULL;
+	}
+
+	MOD_DEC_USE_COUNT;
+}
+
+static int ov511_init_done(struct video_device *dev)
+{
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	create_proc_ov511_cam((struct usb_ov511 *)dev);
+#endif
+
+	return 0;
+}
+
+static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
+
+	PDEBUG(4, "IOCtl: 0x%X", cmd);
+
+	if (!ov511->dev)
+		return -EIO;	
+
+	switch (cmd) {
+	case VIDIOCGCAP:
+	{
+		struct video_capability b;
+
+		PDEBUG (4, "VIDIOCGCAP");
+
+		strcpy(b.name, "OV511 USB Camera");
+		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+		b.channels = 1;
+		b.audios = 0;
+		b.maxwidth = ov511->maxwidth;
+		b.maxheight = ov511->maxheight;
+		b.minwidth = 32;
+		b.minheight = 16;
+
+		if (copy_to_user(arg, &b, sizeof(b)))
+			return -EFAULT;
+				
+		return 0;
+	}
+	case VIDIOCGCHAN:
+	{
+		struct video_channel v;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+		if (v.channel != 0)
+			return -EINVAL;
+
+		v.flags = 0;
+		v.tuners = 0;
+		v.type = VIDEO_TYPE_CAMERA;
+		strcpy(v.name, "Camera");
+
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+				
+		return 0;
+	}
+	case VIDIOCSCHAN:
+	{
+		int v;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		if (v != 0)
+			return -EINVAL;
+
+		return 0;
+	}
+	case VIDIOCGPICT:
+	{
+		struct video_picture p;
+
+		PDEBUG (4, "VIDIOCGPICT");
+
+		if (ov7610_get_picture(ov511, &p))
+			return -EIO;
+							
+		if (copy_to_user(arg, &p, sizeof(p)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case VIDIOCSPICT:
+	{
+		struct video_picture p;
+		int i;
+
+		PDEBUG (4, "VIDIOCSPICT");
+
+		if (copy_from_user(&p, arg, sizeof(p)))
+			return -EFAULT;
+
+		if (p.palette != VIDEO_PALETTE_GREY &&
+		    p.palette != VIDEO_PALETTE_RGB24 &&
+		    p.palette != VIDEO_PALETTE_YUV422 &&
+		    p.palette != VIDEO_PALETTE_YUYV &&
+		    p.palette != VIDEO_PALETTE_YUV420 &&
+		    p.palette != VIDEO_PALETTE_YUV422P)
+			return -EINVAL;
+			
+		if (ov7610_set_picture(ov511, &p))
+			return -EIO;
+
+		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
+		for (i = 0; i < OV511_NUMFRAMES; i++) {
+			ov511->frame[i].depth = p.depth;
+			ov511->frame[i].format = p.palette;
+			ov511->frame[i].segsize = GET_SEGSIZE(p.palette);
+		}
+
+		return 0;
+	}
+	case VIDIOCGCAPTURE:
+	{
+		int vf;
+
+		PDEBUG (4, "VIDIOCGCAPTURE");
+
+		if (copy_from_user(&vf, arg, sizeof(vf)))
+			return -EFAULT;
+		ov511->sub_flag = vf;
+		return 0;
+	}
+	case VIDIOCSCAPTURE:
+	{
+		struct video_capture vc;
+
+		if (copy_from_user(&vc, arg, sizeof(vc)))
+			return -EFAULT;
+		if (vc.flags)
+			return -EINVAL;
+		if (vc.decimation)
+			return -EINVAL;
+
+		vc.x &= ~3L;
+		vc.y &= ~1L;
+		vc.y &= ~31L;
+
+		if (vc.width == 0)
+			vc.width = 32;
+
+		vc.height /= 16;
+		vc.height *= 16;
+		if (vc.height == 0)
+			vc.height = 16;
+
+		ov511->subx = vc.x;
+		ov511->suby = vc.y;
+		ov511->subw = vc.width;
+		ov511->subh = vc.height;
+
+		return 0;
+	}
+	case VIDIOCSWIN:
+	{
+		struct video_window vw;
+		int i, result;
+
+		if (copy_from_user(&vw, arg, sizeof(vw)))
+			return -EFAULT;
+
+		PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d",
+			vw.width, vw.height);
+
+#if 0
+		if (vw.flags)
+			return -EINVAL;
+		if (vw.clipcount)
+			return -EINVAL;
+		if (vw.height != ov511->maxheight)
+			return -EINVAL;
+		if (vw.width != ov511->maxwidth)
+			return -EINVAL;
+#endif
+
+		/* If we're collecting previous frame wait
+		   before changing modes */
+		interruptible_sleep_on(&ov511->wq);
+		if (signal_pending(current)) return -EINTR;
+
+		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+			ov511->frame[0].format, ov511->sub_flag);
+		if (result < 0)
+			return result;
+
+		for (i = 0; i < OV511_NUMFRAMES; i++) {
+			ov511->frame[i].width = vw.width;
+			ov511->frame[i].height = vw.height;
+		}
+
+		return 0;
+	}
+	case VIDIOCGWIN:
+	{
+		struct video_window vw;
+
+		vw.x = 0;		/* FIXME */
+		vw.y = 0;
+		vw.width = ov511->frame[0].width;
+		vw.height = ov511->frame[0].height;
+		vw.chromakey = 0;
+		vw.flags = 30;
+
+		PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
+
+		if (copy_to_user(arg, &vw, sizeof(vw)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case VIDIOCGMBUF:
+	{
+		struct video_mbuf vm;
+
+		memset(&vm, 0, sizeof(vm));
+		vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE;
+		vm.frames = OV511_NUMFRAMES;
+		vm.offsets[0] = 0;
+		vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
+
+		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case VIDIOCMCAPTURE:
+	{
+		struct video_mmap vm;
+		int ret;
+
+		if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+			return -EFAULT;
+
+		PDEBUG(4, "CMCAPTURE");
+		PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
+			vm.frame, vm.width, vm.height, vm.format);
+
+		if (vm.format != VIDEO_PALETTE_RGB24 &&
+		    vm.format != VIDEO_PALETTE_YUV422 &&
+		    vm.format != VIDEO_PALETTE_YUYV &&
+		    vm.format != VIDEO_PALETTE_YUV420 &&
+		    vm.format != VIDEO_PALETTE_YUV422P &&
+		    vm.format != VIDEO_PALETTE_GREY)
+			return -EINVAL;
+
+		if ((vm.frame != 0) && (vm.frame != 1))
+			return -EINVAL;
+				
+		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight)
+			return -EINVAL;
+
+		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
+			return -EBUSY;
+
+		/* Don't compress if the size changed */
+		if ((ov511->frame[vm.frame].width != vm.width) ||
+		    (ov511->frame[vm.frame].height != vm.height) ||
+		    (ov511->frame[vm.frame].format != vm.format) ||
+		    (ov511->frame[vm.frame].sub_flag !=
+		     ov511->sub_flag)) {
+			/* If we're collecting previous frame wait
+			   before changing modes */
+			interruptible_sleep_on(&ov511->wq);
+			if (signal_pending(current)) return -EINTR;
+			ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
+				vm.format, ov511->sub_flag);
+#if 0
+			if (ret < 0)
+				return ret;
+#endif
+		}
+
+		ov511->frame[vm.frame].width = vm.width;
+		ov511->frame[vm.frame].height = vm.height;
+		ov511->frame[vm.frame].format = vm.format;
+		ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
+		ov511->frame[vm.frame].depth = GET_DEPTH(vm.format);
+
+		/* Mark it as ready */
+		ov511->frame[vm.frame].grabstate = FRAME_READY;
+
+		return ov511_new_frame(ov511, vm.frame);
+	}
+	case VIDIOCSYNC:
+	{
+		int frame;
+
+		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+			return -EFAULT;
+
+		PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
+		       ov511->frame[frame].grabstate);
+
+		switch (ov511->frame[frame].grabstate) {
+		case FRAME_UNUSED:
+			return -EINVAL;
+		case FRAME_READY:
+		case FRAME_GRABBING:
+		case FRAME_ERROR:
+redo:
+			if (!ov511->dev)
+				return -EIO;
+
+			do {
+#if 0
+				init_waitqueue_head(&ov511->frame[frame].wq);
+#endif
+				interruptible_sleep_on(&ov511->frame[frame].wq);
+				if (signal_pending(current)) {
+					if (retry_sync) {
+						PDEBUG(3, "***retry sync***");
+
+						/* Polling apps will destroy frames with that! */
+						ov511_new_frame(ov511, frame);
+						ov511->curframe = -1;
+
+						/* This will request another frame. */
+						if (waitqueue_active(&ov511->frame[frame].wq))
+							wake_up_interruptible(&ov511->frame[frame].wq);
+
+						return 0;
+ 					} else {
+						return -EINTR;
+					}
+				}
+			} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
+
+			if (ov511->frame[frame].grabstate == FRAME_ERROR) {
+				int ret;
+
+				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+					return ret;
+				goto redo;
+			}			
+		case FRAME_DONE:
+			ov511->frame[frame].grabstate = FRAME_UNUSED;
+
+			/* Reset the hardware snapshot button */
+			/* FIXME - Is this the best place for this? */
+			if ((ov511->snap_enabled) &&
+			    (ov511->frame[frame].snapshot)) {
+				ov511->frame[frame].snapshot = 0;
+				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+			}
+			break;
+		} /* end switch */
+
+		return 0;
+	}
+	case VIDIOCGFBUF:
+	{
+		struct video_buffer vb;
+
+		memset(&vb, 0, sizeof(vb));
+		vb.base = NULL;	/* frame buffer not supported, not used */
+
+		if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case VIDIOCKEY:
+		return 0;
+	case VIDIOCCAPTURE:
+		return -EINVAL;
+	case VIDIOCSFBUF:
+		return -EINVAL;
+	case VIDIOCGTUNER:
+	case VIDIOCSTUNER:
+		return -EINVAL;
+	case VIDIOCGFREQ:
+	case VIDIOCSFREQ:
+		return -EINVAL;
+	case VIDIOCGAUDIO:
+	case VIDIOCSAUDIO:
+		return -EINVAL;
+	default:
+		return -ENOIOCTLCMD;
+	} /* end switch */
+
+	return 0;
+}
+
+static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	int i;
+	int frmx = -1;
+	volatile struct ov511_frame *frame;
+
+	PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
+
+	if (!dev || !buf)
+		return -EFAULT;
+
+	if (!ov511->dev)
+		return -EIO;
+
+	/* See if a frame is completed, then use it. */
+	if (ov511->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
+		frmx = 0;
+	else if (ov511->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
+		frmx = 1;
+
+	/* If nonblocking we return immediately */
+	if (noblock && (frmx == -1))
+		return -EAGAIN;
+
+	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
+	/* See if a frame is in process (grabbing), then use it. */
+	if (frmx == -1) {
+		if (ov511->frame[0].grabstate == FRAME_GRABBING)
+			frmx = 0;
+		else if (ov511->frame[1].grabstate == FRAME_GRABBING)
+			frmx = 1;
+	}
+
+	/* If no frame is active, start one. */
+	if (frmx == -1)
+		ov511_new_frame(ov511, frmx = 0);
+
+	frame = &ov511->frame[frmx];
+
+restart:
+	if (!ov511->dev)
+		return -EIO;
+
+	/* Wait while we're grabbing the image */
+	PDEBUG(4, "Waiting image grabbing");
+	while (frame->grabstate == FRAME_GRABBING) {
+		interruptible_sleep_on(&ov511->frame[frmx].wq);
+		if (signal_pending(current))
+			return -EINTR;
+	}
+	PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+
+	if (frame->grabstate == FRAME_ERROR) {
+		frame->bytes_read = 0;
+		err("** ick! ** Errored frame %d", ov511->curframe);
+		if (ov511_new_frame(ov511, frmx))
+			err("read: ov511_new_frame error");
+		goto restart;
+	}
+
+
+	/* Repeat until we get a snapshot frame */
+	if (ov511->snap_enabled)
+		PDEBUG (4, "Waiting snapshot frame");
+	if (ov511->snap_enabled && !frame->snapshot) {
+		frame->bytes_read = 0;
+		if (ov511_new_frame(ov511, frmx))
+			err("ov511_new_frame error");
+		goto restart;
+	}
+
+	/* Clear the snapshot */
+	if (ov511->snap_enabled && frame->snapshot) {
+		frame->snapshot = 0;
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	}
+
+	PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+		frame->bytes_read, frame->scanlength);
+
+	/* copy bytes to user space; we allow for partials reads */
+//	if ((count + frame->bytes_read) > frame->scanlength)
+//		count = frame->scanlength - frame->bytes_read;
+
+	/* FIXME - count hardwired to be one frame... */
+	count = frame->width * frame->height * (frame->depth >> 3);
+
+	PDEBUG(4, "Copy to user space: %ld bytes", count);
+	if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
+		PDEBUG(4, "Copy failed! %d bytes not copied", i);
+		return -EFAULT;
+	}
+
+	frame->bytes_read += count;
+	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
+		count, frame->bytes_read);
+
+	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+		frame->bytes_read = 0;
+
+		/* Mark it as available to be used again. */
+		ov511->frame[frmx].grabstate = FRAME_UNUSED;
+		if (ov511_new_frame(ov511, !frmx))
+			err("ov511_new_frame returned error");
+	}
+
+	PDEBUG(4, "read finished, returning %ld (sweet)", count);
+
+	return count;
+}
+
+static int ov511_mmap(struct video_device *dev, const char *adr,
+	unsigned long size)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	unsigned long start = (unsigned long)adr;
+	unsigned long page, pos;
+
+	if (ov511->dev == NULL)
+		return -EIO;
+
+	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
+
+	if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+		return -EINVAL;
+
+	pos = (unsigned long)ov511->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa(pos);
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+			return -EAGAIN;
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+
+	return 0;
+}
+
+static struct video_device ov511_template = {
+	name:		"OV511 USB Camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_OV511,
+	open:		ov511_open,
+	close:		ov511_close,
+	read:		ov511_read,
+	write:		ov511_write,
+	ioctl:		ov511_ioctl,
+	mmap:		ov511_mmap,
+	initialize:	ov511_init_done,
+};
+
+/****************************************************************************
+ *
+ * OV511/OV7610 configuration
+ *
+ ***************************************************************************/
+
+static int ov76xx_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i, success;
+	int rc;
+
+	/* Lawrence Glaister <lg@jfm.bc.ca> reports:
+	 *
+	 * Register 0x0f in the 7610 has the following effects:
+	 *
+	 * 0x85 (AEC method 1): Best overall, good contrast range
+	 * 0x45 (AEC method 2): Very overexposed
+	 * 0xa5 (spec sheet default): Ok, but the black level is
+	 *	shifted resulting in loss of contrast
+	 * 0x05 (old driver setting): very overexposed, too much
+	 *	contrast
+	 */
+	static struct ov511_regvals aRegvalsNorm7610[] = {
+		{ OV511_I2C_BUS, 0x10, 0xff },
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },
+		{ OV511_I2C_BUS, 0x2b, 0xac },
+		{ OV511_I2C_BUS, 0x12, 0x00 },
+		{ OV511_I2C_BUS, 0x38, 0x81 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },	/* 0c */
+		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
+		{ OV511_I2C_BUS, 0x15, 0x01 },
+		{ OV511_I2C_BUS, 0x20, 0x1c },
+		{ OV511_I2C_BUS, 0x23, 0x2a },
+		{ OV511_I2C_BUS, 0x24, 0x10 },
+		{ OV511_I2C_BUS, 0x25, 0x8a },
+		{ OV511_I2C_BUS, 0x27, 0xc2 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 },
+		{ OV511_I2C_BUS, 0x2c, 0xfe },
+		{ OV511_I2C_BUS, 0x30, 0x71 },
+		{ OV511_I2C_BUS, 0x31, 0x60 },
+		{ OV511_I2C_BUS, 0x32, 0x26 },
+		{ OV511_I2C_BUS, 0x33, 0x20 },
+		{ OV511_I2C_BUS, 0x34, 0x48 },
+		{ OV511_I2C_BUS, 0x12, 0x24 },
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	static struct ov511_regvals aRegvalsNorm7620[] = {
+		{ OV511_I2C_BUS, 0x10, 0xff },
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },
+		{ OV511_I2C_BUS, 0x2b, 0xac },
+		{ OV511_I2C_BUS, 0x12, 0x00 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },
+		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
+		{ OV511_I2C_BUS, 0x15, 0x01 },
+		{ OV511_I2C_BUS, 0x23, 0x00 },
+		{ OV511_I2C_BUS, 0x24, 0x10 },
+		{ OV511_I2C_BUS, 0x25, 0x8a },
+		{ OV511_I2C_BUS, 0x27, 0xe2 },
+		{ OV511_I2C_BUS, 0x2a, 0x00 },
+		{ OV511_I2C_BUS, 0x2c, 0xfe },
+		{ OV511_I2C_BUS, 0x30, 0x71 },
+		{ OV511_I2C_BUS, 0x31, 0x60 },
+		{ OV511_I2C_BUS, 0x32, 0x26 },
+		{ OV511_I2C_BUS, 0x33, 0x20 },
+		{ OV511_I2C_BUS, 0x34, 0x48 },
+		{ OV511_I2C_BUS, 0x12, 0x24 },
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	PDEBUG (4, "starting configuration");
+
+	/* This looks redundant, but is necessary for WebCam 3 */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+	                    OV7610_I2C_WRITE_ID) < 0)
+		return -1;
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+	                    OV7610_I2C_READ_ID) < 0)
+		return -1;
+
+	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+		return -1;
+
+	/* Reset the 76xx */ 
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the 76xx */ 
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		/* Wait for it to initialize */ 
+		schedule_timeout (1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	}
+
+	if (success) {
+		PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i);
+	} else {
+		/* Reset the 76xx */
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+
+		/* Wait for it to initialize */
+		schedule_timeout (1 + 150 * HZ / 1000);
+
+		i = 0;
+		success = 0;
+		while (i <= i2c_detect_tries) {
+			if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+			    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+				success = 1;
+				break;
+			} else {
+				i++;
+			}
+		}
+
+		if ((i == i2c_detect_tries) && (success == 0)) {
+			err("Failed to read sensor ID. You might not have an OV7610/20,");
+			err("or it may be not responding. Report this to");
+			err("mwm@i.am");
+			return -1;
+		} else {
+			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+		}
+	}
+
+	/* Detect sensor if user didn't use override param */
+	if (sensor == 0) {
+		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+
+		if (rc < 0) {
+			err("Error detecting sensor type");
+			return -1;
+		} else if((rc & 3) == 3) {
+			info("Sensor is an OV7610");
+			ov511->sensor = SEN_OV7610;
+		} else if((rc & 3) == 1) {
+			info("Sensor is an OV7620AE");
+			ov511->sensor = SEN_OV7620AE;
+		} else if((rc & 3) == 0) {
+			info("Sensor is an OV7620");
+			ov511->sensor = SEN_OV7620;
+		} else {
+			err("Unknown image sensor version: %d", rc & 3);
+			return -1;
+		}
+	} else {	/* sensor != 0; user overrode detection */
+		ov511->sensor = sensor;
+		info("Sensor set to type %d", ov511->sensor);
+	}
+
+	if (ov511->sensor == SEN_OV7620) {
+		PDEBUG(4, "Writing 7620 registers");
+		if (ov511_write_regvals(dev, aRegvalsNorm7620))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 7610 registers");
+		if (ov511_write_regvals(dev, aRegvalsNorm7610))
+			return -1;
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+
+	if (aperture < 0) {          /* go with the default */
+		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
+	} else if (aperture <= 0xf) {  /* user overrode default */
+		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
+			return -1;
+	} else {
+		err("Invalid setting for aperture; legal value: 0 - 15");
+		return -1;
+	}
+
+	if (autoadjust) {
+		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	} else {
+		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
+		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+	}
+
+	return 0;
+}
+
+static int ov6xx0_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i, success, rc;
+
+	static struct ov511_regvals aRegvalsNorm6x20[] = {
+		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x03, 0xd0 },
+		{ OV511_I2C_BUS, 0x05, 0x7f },
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
+		{ OV511_I2C_BUS, 0x14, 0x04 },
+		{ OV511_I2C_BUS, 0x16, 0x06 },  /* ? */
+		{ OV511_I2C_BUS, 0x19, 0x04 },
+		{ OV511_I2C_BUS, 0x1a, 0x93 },
+		{ OV511_I2C_BUS, 0x20, 0x28 },
+		{ OV511_I2C_BUS, 0x27, 0xa2 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 },  /* 84? */
+		{ OV511_I2C_BUS, 0x2b, 0xac },  /* a8? */
+		{ OV511_I2C_BUS, 0x2d, 0x95 },
+		{ OV511_I2C_BUS, 0x33, 0x28 },
+		{ OV511_I2C_BUS, 0x34, 0xc7 },
+		{ OV511_I2C_BUS, 0x38, 0x8b },
+		{ OV511_I2C_BUS, 0x3c, 0x5c },
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+		{ OV511_I2C_BUS, 0x3f, 0x00 },
+		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
+		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
+		{ OV511_I2C_BUS, 0x4d, 0xd2 },
+		{ OV511_I2C_BUS, 0x4e, 0xc1 },
+		{ OV511_I2C_BUS, 0x4f, 0x04 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	PDEBUG (4, "starting sensor configuration");
+	
+	/* Reset the 6xx0 */ 
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the 6xx0 */ 
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		/* Wait for it to initialize */ 
+		schedule_timeout (1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	}
+
+	if (success) {
+		PDEBUG(1, "I2C synced in %d attempt(s)", i);
+	} else {
+		err("Failed to read sensor ID. You might not have an OV6xx0,");
+		err("or it may be not responding. Report this to");
+		err("mwm@i.am");
+		return -1;
+	}
+
+	/* Detect sensor if user didn't use override param */
+	if (sensor == 0) {
+		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+
+		if (rc < 0) {
+			err("Error detecting sensor type");
+			return -1;
+		} else {
+			info("Sensor is an OV6xx0 (version %d)", rc & 3);
+			ov511->sensor = SEN_OV6620;
+		}
+	} else {	/* sensor != 0; user overrode detection */
+		ov511->sensor = sensor;
+		info("Sensor set to type %d", ov511->sensor);
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 352;
+	ov511->maxheight = 288;
+
+	PDEBUG(4, "Writing 6x20 registers");
+	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
+		return -1;
+
+	if (aperture < 0) {          /* go with the default */
+		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
+	} else if (aperture <= 0xf) {  /* user overrode default */
+		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
+			return -1;
+	} else {
+		err("Invalid setting for aperture; legal value: 0 - 15");
+		return -1;
+	}
+
+	if (autoadjust) {
+		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	} else {
+		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
+		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+	}
+
+	return 0;
+}
+
+
+static int ov511_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i;
+
+	static struct ov511_regvals aRegvalsInit[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+		{ OV511_DONE_BUS, 0x0, 0x00},
+	};
+
+	static struct ov511_regvals aRegvalsNorm511[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+
+	for (i = 0; i < OV511_NUMFRAMES; i++)
+		init_waitqueue_head(&ov511->frame[i].wq);
+
+	init_waitqueue_head(&ov511->wq);
+
+	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) {
+		err("video_register_device failed");
+		return -EBUSY;
+	}
+
+	if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
+	if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->snap_enabled = snapshot;	
+
+	/* Test for 76xx */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+	                   OV7610_I2C_WRITE_ID) < 0)
+		goto error;
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+	                   OV7610_I2C_READ_ID) < 0)
+		goto error;
+
+	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+		goto error;
+
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+		                    OV6xx0_I2C_WRITE_ID) < 0)
+			goto error;
+
+		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+		                    OV6xx0_I2C_READ_ID) < 0)
+			goto error;
+
+		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+			goto error;
+
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+			err("Can't determine sensor slave IDs");
+			goto error;
+		}
+		
+		if(ov6xx0_configure(ov511) < 0) {
+			err("failed to configure OV6xx0");
+ 			goto error;
+		}
+	} else {
+		if(ov76xx_configure(ov511) < 0) {
+			err("failed to configure OV76xx");
+	 		goto error;
+		}
+	}
+	
+	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+	 * (using read() instead). */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].width = ov511->maxwidth;
+		ov511->frame[i].height = ov511->maxheight;
+		ov511->frame[i].depth = 24;
+		ov511->frame[i].bytes_read = 0;
+		ov511->frame[i].segment = 0;
+		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
+		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
+	}
+
+	/* Initialize to max width/height, RGB24 */
+	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
+				 VIDEO_PALETTE_RGB24, 0) < 0)
+		goto error;
+
+	return 0;
+	
+error:
+	video_unregister_device(&ov511->vdev);
+	usb_driver_release_interface(&ov511_driver,
+		&dev->actconfig->interface[ov511->iface]);
+
+	return -EBUSY;	
+}
+
+
+/****************************************************************************
+ *
+ *  USB routines
+ *
+ ***************************************************************************/
+
+static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_ov511 *ov511;
+	int i;
+
+	PDEBUG(1, "probing for device...");
+
+	/* We don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1)
+		return NULL;
+
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+
+	/* Is it an OV511/OV511+? */
+	if (dev->descriptor.idVendor != 0x05a9 
+	 && dev->descriptor.idVendor != 0x0813)
+		return NULL;
+	if (dev->descriptor.idProduct != 0x0511
+	 && dev->descriptor.idProduct != 0xA511
+	 && dev->descriptor.idProduct != 0x0002)
+		return NULL;
+
+	/* Checking vendor/product should be enough, but what the hell */
+	if (interface->bInterfaceClass != 0xFF) 
+		return NULL;
+	if (interface->bInterfaceSubClass != 0x00)
+		return NULL;
+
+	/* Since code below may sleep, we use this as a lock */
+	MOD_INC_USE_COUNT;
+
+	if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
+		err("couldn't kmalloc ov511 struct");
+		goto error;
+	}
+
+	memset(ov511, 0, sizeof(*ov511));
+
+	ov511->dev = dev;
+	ov511->iface = interface->bInterfaceNumber;
+
+	switch (dev->descriptor.idProduct) {
+	case 0x0511:
+		info("USB OV511 camera found");
+		ov511->bridge = BRG_OV511;
+		break;
+	case 0xA511:
+		info("USB OV511+ camera found");
+		ov511->bridge = BRG_OV511PLUS;
+		break;
+	case 0x0002:
+		if (dev->descriptor.idVendor != 0x0813)
+			goto error;
+		info("Intel Play Me2Cam (OV511+) found");
+		ov511->bridge = BRG_OV511PLUS;
+		break;
+	default:
+		err("Unknown product ID");
+		goto error;
+	}
+
+	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+	if (ov511->customid < 0) {
+		err("Unable to read camera bridge registers");
+		goto error;
+	}
+
+	ov511->desc = -1;
+	PDEBUG (4, "CustomID = %d", ov511->customid);
+	for (i = 0; clist[i].id >= 0; i++) {
+		if (ov511->customid == clist[i].id) {
+			info("camera: %s", clist[i].description);
+			ov511->desc = i;
+			break;
+		}
+	}
+
+	/* Lifeview USB Life TV not supported */
+	if (clist[i].id == 38) {
+		err("This device is not supported yet.");
+		goto error;
+	}
+
+	if (clist[i].id == -1) {
+		err("Camera type (%d) not recognized", ov511->customid);
+		err("Please contact mwm@i.am to request");
+		err("support for your camera.");
+	}
+
+	/* Workaround for some applications that want data in RGB
+	 * instead of BGR */
+	if (force_rgb)
+		info("data format set to RGB");
+
+	if (!ov511_configure(ov511)) {
+		ov511->user = 0;
+		init_MUTEX(&ov511->lock);	/* to 1 == available */
+		init_MUTEX(&ov511->buf_lock);
+		ov511->buf_state = BUF_NOT_ALLOCATED;
+	} else {
+		err("Failed to configure camera");
+		goto error;
+	}
+
+	MOD_DEC_USE_COUNT;
+     	return ov511;
+
+error:
+	if (ov511) {
+		kfree(ov511);
+		ov511 = NULL;
+	}
+
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+
+static void ov511_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
+	int n;
+
+	MOD_INC_USE_COUNT;
+
+	PDEBUG(3, "");
+
+	/* We don't want people trying to open up the device */
+	if (!ov511->user)
+		video_unregister_device(&ov511->vdev);
+	else
+		PDEBUG(3, "Device open...deferring video_unregister_device");
+
+	for (n = 0; n < OV511_NUMFRAMES; n++)
+		ov511->frame[n].grabstate = FRAME_ERROR;
+
+	ov511->curframe = -1;
+
+	/* This will cause the process to request another frame */
+	for (n = 0; n < OV511_NUMFRAMES; n++)
+		if (waitqueue_active(&ov511->frame[n].wq))
+			wake_up_interruptible(&ov511->frame[n].wq);
+	if (waitqueue_active(&ov511->wq))
+		wake_up_interruptible(&ov511->wq);
+
+	ov511->streaming = 0;
+
+	/* Unschedule all of the iso td's */
+	for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+		if (ov511->sbuf[n].urb) {
+			ov511->sbuf[n].urb->next = NULL;
+			usb_unlink_urb(ov511->sbuf[n].urb);
+			usb_free_urb(ov511->sbuf[n].urb);
+			ov511->sbuf[n].urb = NULL;
+		}
+	}
+
+	usb_driver_release_interface(&ov511_driver,
+		&ov511->dev->actconfig->interface[ov511->iface]);
+	ov511->dev = NULL;
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+        destroy_proc_ov511_cam(ov511);
+#endif
+
+	/* Free the memory */
+	if (ov511 && !ov511->user) {
+		ov511_dealloc(ov511, 1);
+		kfree(ov511);
+		ov511 = NULL;
+	}
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver ov511_driver = {
+	"ov511",
+	ov511_probe,
+	ov511_disconnect,
+	{ NULL, NULL }
+};
+
+
+/****************************************************************************
+ *
+ *  Module routines
+ *
+ ***************************************************************************/
+
+static int __init usb_ov511_init(void)
+{
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+        proc_ov511_create();
+#endif
+
+	if (usb_register(&ov511_driver) < 0)
+		return -1;
+
+	info("ov511 driver version %s registered", version);
+
+	return 0;
+}
+
+static void __exit usb_ov511_exit(void)
+{
+	usb_deregister(&ov511_driver);
+	info("driver deregistered");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+        proc_ov511_destroy();
+#endif 
+}
+
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ov511.h linux.18p17/drivers/usb/ov511.h
--- linux.vanilla/drivers/usb/ov511.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/ov511.h	Thu Oct 19 01:49:52 2000
@@ -0,0 +1,371 @@
+
+#ifndef __LINUX_OV511_H
+#define __LINUX_OV511_H
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+
+#define OV511_DEBUG	/* Turn on debug messages */
+
+#ifdef OV511_DEBUG
+#  define PDEBUG(level, fmt, args...) \
+if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+#else
+#  define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* Camera interface register numbers */
+#define OV511_REG_CAMERA_DELAY_MODE		0x10
+#define OV511_REG_CAMERA_EDGE_MODE		0x11
+#define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM	0x12
+#define OV511_REG_CAMERA_CLAMPED_LINE_NUM	0x13
+#define OV511_REG_CAMERA_PIXEL_DIVISOR		0x14
+#define OV511_REG_CAMERA_LINE_DIVISOR		0x15
+#define OV511_REG_CAMERA_DATA_INPUT_SELECT	0x16
+#define OV511_REG_CAMERA_RESERVED_LINE_MODE	0x17
+#define OV511_REG_CAMERA_BITMASK		0x18
+
+/* Snapshot mode camera interface register numbers */
+#define OV511_REG_SNAP_CAPTURED_FRAME		0x19
+#define OV511_REG_SNAP_CLAMPED_PIXEL_NUM	0x1A
+#define OV511_REG_SNAP_CLAMPED_LINE_NUM		0x1B
+#define OV511_REG_SNAP_PIXEL_DIVISOR		0x1C
+#define OV511_REG_SNAP_LINE_DIVISOR		0x1D
+#define OV511_REG_SNAP_DATA_INPUT_SELECT	0x1E
+#define OV511_REG_SNAP_BITMASK			0x1F
+
+/* DRAM register numbers */
+#define OV511_REG_DRAM_ENABLE_FLOW_CONTROL	0x20
+#define OV511_REG_DRAM_READ_CYCLE_PREDICT	0x21
+#define OV511_REG_DRAM_MANUAL_READ_CYCLE	0x22
+#define OV511_REG_DRAM_REFRESH_COUNTER		0x23
+
+/* ISO FIFO register numbers */
+#define OV511_REG_FIFO_PACKET_SIZE		0x30
+#define OV511_REG_FIFO_BITMASK			0x31
+
+/* PIO register numbers */
+#define OV511_REG_PIO_BITMASK			0x38
+#define OV511_REG_PIO_DATA_PORT			0x39
+#define OV511_REG_PIO_BIST			0x3E
+
+/* I2C register numbers */
+#define OV511_REG_I2C_CONTROL			0x40
+#define OV511_REG_I2C_SLAVE_ID_WRITE		0x41
+#define OV511_REG_I2C_SUB_ADDRESS_3_BYTE	0x42
+#define OV511_REG_I2C_SUB_ADDRESS_2_BYTE	0x43
+#define OV511_REG_I2C_SLAVE_ID_READ		0x44
+#define OV511_REG_I2C_DATA_PORT			0x45
+#define OV511_REG_I2C_CLOCK_PRESCALER		0x46
+#define OV511_REG_I2C_TIME_OUT_COUNTER		0x47
+
+/* I2C snapshot register numbers */
+#define OV511_REG_I2C_SNAP_SUB_ADDRESS		0x48
+#define OV511_REG_I2C_SNAP_DATA_PORT		0x49
+
+/* System control register numbers */
+#define OV511_REG_SYSTEM_RESET			0x50
+#define 	OV511_RESET_UDC			0x01
+#define 	OV511_RESET_I2C			0x02
+#define 	OV511_RESET_FIFO		0x04
+#define 	OV511_RESET_OMNICE		0x08
+#define 	OV511_RESET_DRAM_INTF		0x10
+#define 	OV511_RESET_CAMERA_INTF		0x20
+#define 	OV511_RESET_OV511		0x40
+#define 	OV511_RESET_NOREGS		0x3F /* All but OV511 & regs */
+#define 	OV511_RESET_ALL			0x7F
+#define OV511_REG_SYSTEM_CLOCK_DIVISOR		0x51
+#define OV511_REG_SYSTEM_SNAPSHOT		0x52
+#define OV511_REG_SYSTEM_INIT         		0x53
+#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+ only */
+#define OV511_REG_SYSTEM_LED_CTL		0x55	/* OV511+ only */
+#define OV511_REG_SYSTEM_USER_DEFINED		0x5E
+#define OV511_REG_SYSTEM_CUSTOM_ID		0x5F
+
+/* OmniCE register numbers */
+#define OV511_OMNICE_PREDICTION_HORIZ_Y		0x70
+#define OV511_OMNICE_PREDICTION_HORIZ_UV	0x71
+#define OV511_OMNICE_PREDICTION_VERT_Y		0x72
+#define OV511_OMNICE_PREDICTION_VERT_UV		0x73
+#define OV511_OMNICE_QUANTIZATION_HORIZ_Y	0x74
+#define OV511_OMNICE_QUANTIZATION_HORIZ_UV	0x75
+#define OV511_OMNICE_QUANTIZATION_VERT_Y	0x76
+#define OV511_OMNICE_QUANTIZATION_VERT_UV	0x77
+#define OV511_OMNICE_ENABLE			0x78
+#define OV511_OMNICE_LUT_ENABLE			0x79		
+#define OV511_OMNICE_Y_LUT_BEGIN		0x80
+#define OV511_OMNICE_Y_LUT_END			0x9F
+#define OV511_OMNICE_UV_LUT_BEGIN		0xA0
+#define OV511_OMNICE_UV_LUT_END			0xBF
+
+/* Alternate numbers for various max packet sizes (OV511 only) */
+#define OV511_ALT_SIZE_992	0
+#define OV511_ALT_SIZE_993	1
+#define OV511_ALT_SIZE_768	2
+#define OV511_ALT_SIZE_769	3
+#define OV511_ALT_SIZE_512	4
+#define OV511_ALT_SIZE_513	5
+#define OV511_ALT_SIZE_257	6
+#define OV511_ALT_SIZE_0	7
+
+/* Alternate numbers for various max packet sizes (OV511+ only) */
+#define OV511PLUS_ALT_SIZE_0	0
+#define OV511PLUS_ALT_SIZE_33	1
+#define OV511PLUS_ALT_SIZE_129	2
+#define OV511PLUS_ALT_SIZE_257	3
+#define OV511PLUS_ALT_SIZE_385	4
+#define OV511PLUS_ALT_SIZE_513	5
+#define OV511PLUS_ALT_SIZE_769	6
+#define OV511PLUS_ALT_SIZE_961	7
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN          0x00	/* gain setting (5:0) */
+#define OV7610_REG_BLUE          0x01	/* blue channel balance */
+#define OV7610_REG_RED           0x02	/* red channel balance */
+#define OV7610_REG_SAT           0x03	/* saturation */
+					/* 04 reserved */
+#define OV7610_REG_CNT           0x05	/* Y contrast */
+#define OV7610_REG_BRT           0x06	/* Y brightness */
+					/* 08-0b reserved */
+#define OV7610_REG_BLUE_BIAS     0x0C	/* blue channel bias (5:0) */
+#define OV7610_REG_RED_BIAS      0x0D	/* read channel bias (5:0) */
+#define OV7610_REG_GAMMA_COEFF   0x0E	/* gamma settings */
+#define OV7610_REG_WB_RANGE      0x0F	/* AEC/ALC/S-AWB settings */
+#define OV7610_REG_EXP           0x10	/* manual exposure setting */
+#define OV7610_REG_CLOCK         0x11	/* polarity/clock prescaler */
+#define OV7610_REG_COM_A         0x12	/* misc common regs */
+#define OV7610_REG_COM_B         0x13	/* misc common regs */
+#define OV7610_REG_COM_C         0x14	/* misc common regs */
+#define OV7610_REG_COM_D         0x15	/* misc common regs */
+#define OV7610_REG_FIELD_DIVIDE  0x16	/* field interval/mode settings */
+#define OV7610_REG_HWIN_START    0x17	/* horizontal window start */
+#define OV7610_REG_HWIN_END      0x18	/* horizontal window end */
+#define OV7610_REG_VWIN_START    0x19	/* vertical window start */
+#define OV7610_REG_VWIN_END      0x1A	/* vertical window end */
+#define OV7610_REG_PIXEL_SHIFT   0x1B	/* pixel shift */
+#define OV7610_REG_ID_HIGH       0x1C	/* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW        0x1D	/* manufacturer ID LSB */
+					/* 0e-0f reserved */
+#define OV7610_REG_COM_E         0x20	/* misc common regs */
+#define OV7610_REG_YOFFSET       0x21	/* Y channel offset */
+#define OV7610_REG_UOFFSET       0x22	/* U channel offset */
+					/* 23 reserved */
+#define OV7610_REG_ECW           0x24	/* Exposure white level for AEC */
+#define OV7610_REG_ECB           0x25	/* Exposure black level for AEC */
+#define OV7610_REG_COM_F         0x26	/* misc settings */
+#define OV7610_REG_COM_G         0x27	/* misc settings */
+#define OV7610_REG_COM_H         0x28	/* misc settings */
+#define OV7610_REG_COM_I         0x29	/* misc settings */
+#define OV7610_REG_FRAMERATE_H   0x2A	/* frame rate MSB + misc */
+#define OV7610_REG_FRAMERATE_L   0x2B	/* frame rate LSB */
+#define OV7610_REG_ALC           0x2C	/* Auto Level Control settings */
+#define OV7610_REG_COM_J         0x2D	/* misc settings */
+#define OV7610_REG_VOFFSET       0x2E	/* V channel offset adjustment */
+#define OV7610_REG_ARRAY_BIAS	 0x2F	/* Array bias -- don't change */
+					/* 30-32 reserved */
+#define OV7610_REG_YGAMMA        0x33	/* misc gamma settings (7:6) */
+#define OV7610_REG_BIAS_ADJUST   0x34	/* misc bias settings */
+#define OV7610_REG_COM_L         0x35	/* misc settings */
+					/* 36-37 reserved */
+#define OV7610_REG_COM_K         0x38	/* misc registers */
+
+
+#define SCRATCH_BUF_SIZE 512
+
+#define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
+#define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
+#define MAX_FRAME_SIZE_PER_DESC	993	/* For statically allocated stuff */
+
+#define OV511_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
+
+// CAMERA SPECIFIC
+// FIXME - these can vary between specific models
+#define OV7610_I2C_WRITE_ID 0x42
+#define OV7610_I2C_READ_ID  0x43
+#define OV6xx0_I2C_WRITE_ID 0xC0
+#define OV6xx0_I2C_READ_ID  0xC1
+
+#define OV511_I2C_CLOCK_PRESCALER 0x03
+
+/* Prototypes */
+int usb_ov511_reg_read(struct usb_device *dev, unsigned char reg);
+int usb_ov511_reg_write(struct usb_device *dev,
+                        unsigned char reg,
+                        unsigned char value);
+
+/* Bridge types */
+enum {
+	BRG_OV511,
+	BRG_OV511PLUS,
+};
+
+/* Sensor types */
+enum {
+	SEN_UNKNOWN,
+	SEN_OV7610,
+	SEN_OV7620,
+	SEN_OV7620AE,
+	SEN_OV6620,
+};
+
+enum {
+	STATE_SCANNING,		/* Scanning for start */
+	STATE_HEADER,		/* Parsing header */
+	STATE_LINES,		/* Parsing lines */
+};
+
+/* Buffer states */
+enum {
+	BUF_NOT_ALLOCATED,
+	BUF_ALLOCATED,
+	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
+};
+
+struct usb_device;
+
+struct ov511_sbuf {
+	char *data;
+	urb_t *urb;
+};
+
+enum {
+	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
+	FRAME_READY,		/* Ready to start grabbing */
+	FRAME_GRABBING,		/* In the process of being grabbed into */
+	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
+	FRAME_ERROR,		/* Something bad happened while processing */
+};
+
+struct ov511_regvals {
+	enum {
+		OV511_DONE_BUS,
+		OV511_REG_BUS,
+		OV511_I2C_BUS,
+	} bus;
+	unsigned char reg;
+	unsigned char val;
+};
+
+struct ov511_frame {
+	char *data;		/* Frame buffer */
+
+	int depth;		/* Bytes per pixel */
+	int width;		/* Width application is expecting */
+	int height;		/* Height */
+
+	int hdrwidth;		/* Width the frame actually is */
+	int hdrheight;		/* Height */
+
+	int sub_flag;		/* Sub-capture mode for this frame? */
+	unsigned int format;	/* Format for this frame */
+	int segsize;		/* How big is each segment from the camera? */
+
+	volatile int grabstate;	/* State of grabbing */
+	int scanstate;		/* State of scanning */
+
+	int curline;		/* Line of frame we're working on */
+	int curpix;
+	int segment;		/* Segment from the incoming data */
+
+	long scanlength;	/* uncompressed, raw data length of frame */
+	long bytes_read;	/* amount of scanlength that has been read from *data */
+
+	wait_queue_head_t wq;	/* Processes waiting */
+
+	int snapshot;		/* True if frame was a snapshot */
+};
+
+#define OV511_NUMFRAMES	2
+#define OV511_NUMSBUF	2
+
+struct usb_ov511 {
+	struct video_device vdev;
+
+	/* Device structure */
+	struct usb_device *dev;
+
+	int customid;
+	int desc;
+	unsigned char iface;
+
+	/* Determined by sensor type */
+	int maxwidth;
+	int maxheight;
+
+	int brightness;
+	int colour;
+	int contrast;
+	int hue;
+	int whiteness;
+
+	struct semaphore lock;
+	int user;		/* user count for exclusive use */
+
+	int streaming;		/* Are we streaming Isochronous? */
+	int grabbing;		/* Are we grabbing? */
+
+	int compress;		/* Should the next frame be compressed? */
+
+	char *fbuf;		/* Videodev buffer area */
+
+	int sub_flag;		/* Pix Array subcapture on flag */
+	int subx;		/* Pix Array subcapture x offset */
+	int suby;		/* Pix Array subcapture y offset */
+	int subw;		/* Pix Array subcapture width */
+	int subh;		/* Pix Array subcapture height */
+
+	int curframe;		/* Current receiving sbuf */
+	struct ov511_frame frame[OV511_NUMFRAMES];	
+
+	int cursbuf;		/* Current receiving sbuf */
+	struct ov511_sbuf sbuf[OV511_NUMSBUF];
+
+	/* Scratch space from the Isochronous pipe */
+	unsigned char scratch[SCRATCH_BUF_SIZE];
+	int scratchlen;
+
+	wait_queue_head_t wq;	/* Processes waiting */
+
+	int snap_enabled;	/* Snapshot mode enabled */
+	
+	int bridge;		/* Type of bridge (OV511 or OV511+) */
+	int sensor;		/* Type of image sensor chip */
+
+	int packet_size;	/* Frame size per isoc desc */
+
+				/* proc interface */
+	struct semaphore param_lock;	/* params lock for this camera */
+	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
+	
+	/* Framebuffer/sbuf management */
+	int buf_state;
+	struct semaphore buf_lock;
+	struct timer_list buf_timer;
+};
+
+struct cam_list {
+	int id;
+	char *description;
+};
+
+struct palette_list {
+	int num;
+	char *name;
+};
+
+struct mode_list {
+	int width;
+	int height;
+	int color;		/* 0=grayscale, 1=color */
+	u8 pxcnt;		/* pixel counter */
+	u8 lncnt;		/* line counter */
+	u8 pxdv;		/* pixel divisor */
+	u8 lndv;		/* line divisor */
+	u8 m420;
+	u8 common_A;
+	u8 common_L;
+};
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/pegasus.c linux.18p17/drivers/usb/pegasus.c
--- linux.vanilla/drivers/usb/pegasus.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/pegasus.c	Sat Oct 14 00:49:13 2000
@@ -0,0 +1,1008 @@
+/*
+**	Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
+**
+**	Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg)
+**	
+**
+**	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>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+
+
+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	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,
+};
+
+
+typedef struct pegasus {
+	struct usb_device	*usb;
+	struct net_device	*net;
+	struct net_device_stats	stats;
+	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;
+	__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@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, 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}
+};
+
+
+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 get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+{
+	int	ret;
+
+	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);
+
+	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 set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+{
+	int	ret;
+
+	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 );
+
+	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 set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
+{
+	int	ret;
+
+	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" );
+
+	return 1;
+}
+
+
+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 ( read_eprom_word( pegasus, i, (__u16 *)&id[i*2]) )
+			return 1;
+	return 0;
+}
+
+
+static inline int reset_mac( pegasus_t *pegasus )
+{
+	__u8	data = 0x8;
+	int	i;
+
+	set_register(pegasus, EthCtrl1, data);
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, EthCtrl1, 1, &data);
+		if (~data & 0x08) {
+			if (loopback & 1) 
+				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;
+		}
+	}
+	if ( i == REG_TIMEOUT )
+		return 1;
+	return	0;
+}
+
+
+static int enable_net_traffic( struct net_device *dev, struct usb_device *usb )
+{
+	__u16	linkpart, bmsr;
+	__u8	node_id[6];
+	__u8	data[4];
+	pegasus_t *pegasus = dev->priv;
+
+	if ( get_node_id(pegasus, node_id) ) 
+		return 1;
+
+	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 ( !(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 ( !(linkpart & 1) )
+		warn( "link partner stat %x", linkpart );
+
+	data[0] = 0xc9;
+	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;
+
+	*(unsigned *)pegasus->eth_regs = *(unsigned *)data;
+
+	set_registers( pegasus, EthCtrl0, 3, data );
+
+	return 0;
+}
+
+
+static void read_bulk_callback( struct urb *urb )
+{
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net;
+	int count = urb->actual_length, res;
+	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 ( 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++;
+
+		goto goon;
+	}
+
+	pkt_len = (rx_status & 0xfff) - 8;
+
+	if ( !(skb = dev_alloc_skb(pkt_len+2)) )
+		goto goon;
+
+	skb->dev = net;
+	skb_reserve(skb, 2);
+	eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+	skb_put(skb, pkt_len);
+
+	skb->protocol = eth_type_trans(skb, net);
+	netif_rx(skb);
+	pegasus->stats.rx_packets++;
+	pegasus->stats.rx_bytes += pkt_len;
+
+goon:
+	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 write_bulk_callback( struct urb *urb )
+{
+	pegasus_t *pegasus = urb->context;
+
+	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+		return;
+
+	if ( !netif_device_present(pegasus->net) )
+		return;
+		
+	if ( urb->status )
+		info("%s: TX status %d", pegasus->net->name, urb->status);
+
+	netif_wake_queue( pegasus->net );
+}
+
+
+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);
+	}
+}
+
+
+static void pegasus_tx_timeout( struct net_device *net )
+{
+	pegasus_t *pegasus = net->priv;
+
+	if ( !pegasus )
+		return;
+	
+	usb_unlink_urb( &pegasus->tx_urb );
+	warn("%s: Tx timed out.", net->name);
+	pegasus->stats.tx_errors++;
+	net->trans_start = jiffies;
+
+	netif_wake_queue( net );
+}
+
+
+static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *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);
+	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 );
+	} else {
+		pegasus->stats.tx_packets++;
+		pegasus->stats.tx_bytes += skb->len;
+		net->trans_start = jiffies;
+	}
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+
+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 )
+{
+	int 	tmp=0;
+
+	set_registers( pegasus, EthCtrl0, 2, &tmp );
+}
+
+
+static int pegasus_open(struct net_device *net)
+{
+	pegasus_t *pegasus = (pegasus_t *)net->priv;
+	int	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( __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;
+
+	return 0;
+}
+
+
+static int pegasus_close( struct net_device *net )
+{
+	pegasus_t	*pegasus = net->priv;
+
+	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;
+
+	return 0;
+}
+
+
+static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
+{
+	__u16 *data = (__u16 *)&rq->ifr_data;
+	pegasus_t	*pegasus = net->priv;
+
+	switch(cmd) {
+		case SIOCDEVPRIVATE:
+			data[0] = pegasus->phy;
+		case SIOCDEVPRIVATE+1:
+			read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]);
+			return 0;
+		case SIOCDEVPRIVATE+2:
+			if ( !capable(CAP_NET_ADMIN) )
+				return -EPERM;
+			write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
+			return 0;
+		default:
+			return -EOPNOTSUPP;
+	}
+}
+
+
+static void pegasus_set_multicast( struct net_device *net )
+{
+	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);
+	} else if ((net->mc_count > multicast_filter_limit) ||
+			(net->flags & IFF_ALLMULTI)) {
+		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);
+}
+
+
+static int check_device_ids( __u16 vendor, __u16 product )
+{
+	int i=0;
+	
+	while ( usb_dev_id[i].name ) {
+		if ( (usb_dev_id[i].vendor == vendor) && 
+			(usb_dev_id[i].device == product) )
+			return i;
+		i++;
+	}
+	return	-1;
+}
+
+
+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;
+	pegasus_t *pegasus;
+	int	dev_indx;
+
+	if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) {
+		return NULL;
+	}
+
+	if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+		err("usb_set_configuration() failed");
+		return NULL;
+	}
+
+	if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+		err("out of memory allocating device structure");
+		return NULL;
+	}
+	memset(pegasus, 0, sizeof(struct pegasus));
+
+	net = init_etherdev(0, 0);
+	net->priv = pegasus;
+	net->open = pegasus_open;
+	net->stop = pegasus_close;
+#if 0
+	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+	net->tx_timeout = pegasus_tx_timeout;
+#endif
+	net->do_ioctl = pegasus_ioctl;
+	net->hard_start_xmit = pegasus_start_xmit;
+	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;
+
+	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;
+	}
+
+	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 )
+{
+	struct pegasus *pegasus = ptr;
+
+	if ( !pegasus ) {
+		warn("unregistering non-existant device");
+		return;
+	}
+
+	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;
+}
+
+
+static struct usb_driver pegasus_driver = {
+	name:		"pegasus",
+	probe:		pegasus_probe,
+	disconnect:	pegasus_disconnect,
+};
+
+int __init pegasus_init(void)
+{
+	info( "%s", version );
+	return usb_register( &pegasus_driver );
+}
+
+void __exit pegasus_exit(void)
+{
+	usb_deregister( &pegasus_driver );
+}
+
+module_init( pegasus_init );
+module_exit( pegasus_exit );
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/plusb.c linux.18p17/drivers/usb/plusb.c
--- linux.vanilla/drivers/usb/plusb.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/plusb.c	Sat Oct 14 00:50:03 2000
@@ -0,0 +1,676 @@
+/*****************************************************************************/
+
+/*
+ *      plusb.c  --  prolific pl-2302 driver.
+ *
+ *      Copyright (C) 2000  Deti Fliegl (deti@fliegl.de)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ *  $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#define DEBUG
+#include <linux/usb.h>
+
+#include "plusb.h"
+
+/* --------------------------------------------------------------------- */
+
+#define NRPLUSB 4
+
+/*-------------------------------------------------------------------*/
+
+static plusb_t plusb[NRPLUSB];
+
+/* --------------------------------------------------------------------- */
+static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src)
+{
+	unsigned long flags;
+	struct list_head *tmp;
+	int ret = 0;
+
+	spin_lock_irqsave (&s->lock, flags);
+
+	if (list_empty (src)) {
+		// no elements in source buffer
+		ret = -1;
+		goto err;
+	}
+	tmp = src->next;
+	list_del (tmp);
+	list_add_tail (tmp, dst);
+
+  err:	spin_unlock_irqrestore (&s->lock, flags);
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+
+static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length)
+{
+	int ret;
+
+	dbg("plusb_my_bulk: len:%d",size);
+
+	ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500);
+	if(ret<0) {
+		err("plusb: usb_bulk_msg failed(%d)",ret);
+	}
+	
+	if( ret == -EPIPE ) {
+		warn("CLEAR_FEATURE request to remove STALL condition.");
+		if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+			err("request failed");
+		}
+
+	dbg("plusb_my_bulk: finished act: %d", *actual_length);		
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_bh(void *context)
+{
+	plusb_t *s=context;
+	struct net_device_stats *stats=&s->net_stats;
+	int ret=0;
+	int actual_length;
+	skb_list_t *skb_list;
+	struct sk_buff *skb;
+
+	dbg("plusb_bh: i:%d",in_interrupt());
+
+	while(!list_empty(&s->tx_skb_list)) {
+
+		if(!(s->status&_PLUSB_TXOK))
+			break; 
+		
+		skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list);
+		if(!skb_list->state) {
+			dbg("plusb_bh: not yet ready");
+			schedule();
+			continue;
+		}
+
+		skb=skb_list->skb;
+		ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE),
+		                  skb->data, skb->len, &actual_length);
+	
+		if(ret || skb->len != actual_length ||!(skb->len%64)) {
+			plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE),
+		              NULL, 0, &actual_length);
+		}
+
+		if(!ret) {
+			stats->tx_packets++;
+			stats->tx_bytes+=skb->len;
+                }
+		else {
+			stats->tx_errors++;
+			stats->tx_aborted_errors++;
+		}
+
+		dbg("plusb_bh: dev_kfree_skb");
+
+		dev_kfree_skb(skb);
+		skb_list->state=0;
+		plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list);	
+	}
+
+	dbg("plusb_bh: finished");
+	s->in_bh=0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	skb_list_t *skb_list;
+	int ret=NET_XMIT_SUCCESS;
+
+	dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt());
+
+        if(!s->connected || list_empty(&s->free_skb_list)) {
+		ret=NET_XMIT_CN;
+		goto lab;
+	}	
+
+	plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list);
+	skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list);
+	skb_list->skb=skb;
+	skb_list->state=1;
+
+lab:
+	if(s->in_bh)
+		return ret;
+
+	dbg("plusb_net_xmit: queue_task");
+
+	s->in_bh=1;
+	queue_task(&s->bh, &tq_scheduler);
+
+	dbg("plusb_net_xmit: finished");
+	return ret;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_bulk_complete(urb_t *purb)
+{
+	plusb_t *s=purb->context;
+
+	dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length);
+	if(!s->connected)
+		return;
+
+	if( !purb->status) {
+		struct sk_buff *skb;
+		unsigned char *dst;
+		int len=purb->transfer_buffer_length;
+		struct net_device_stats *stats=&s->net_stats;
+
+		skb=dev_alloc_skb(len);
+
+		if(!skb) {
+			err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len);
+			stats->rx_dropped++;
+			return;
+		}
+
+		dst=(char *)skb_put(skb, len);
+		memcpy( dst, purb->transfer_buffer, len);
+
+		skb->dev=&s->net_dev;
+		skb->protocol=eth_type_trans(skb, skb->dev);
+		stats->rx_packets++;
+		stats->rx_bytes+=len;
+		netif_rx(skb);
+	}
+	else
+		purb->status=0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_int_complete(urb_t *purb)
+{
+	plusb_t *s=purb->context;
+	s->status=((unsigned char*)purb->transfer_buffer)[0]&255;
+#if 0
+	if((s->status&0x3f)!=0x20) {
+		warn("invalid device status %02X", s->status);
+		return;
+	}
+#endif	
+	if(!s->connected)
+		return;
+
+	if(s->status&_PLUSB_RXD) {
+		int ret;
+		
+		if(s->bulkurb->status) {
+			err("plusb_int_complete: URB still in use");
+			return;
+		}
+		
+		ret=usb_submit_urb(s->bulkurb);
+		if(ret && ret!=-EBUSY) {
+			err("plusb_int_complete: usb_submit_urb failed");
+		}
+	}
+		
+	if(purb->status || s->status!=160)
+		dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_free_all(plusb_t *s)
+{
+	struct list_head *skb;
+	skb_list_t *skb_list;
+	
+	dbg("plusb_free_all");
+	run_task_queue(&tq_immediate);	
+
+	if(s->inturb) {
+		dbg("unlink inturb");
+		usb_unlink_urb(s->inturb);
+	}
+
+	if(s->inturb && s->inturb->transfer_buffer) {
+		dbg("kfree inturb->transfer_buffer");
+		kfree(s->inturb->transfer_buffer);
+		s->inturb->transfer_buffer=NULL;
+	}
+	
+	if(s->inturb) {
+		dbg("free_urb inturb");
+		usb_free_urb(s->inturb);
+		s->inturb=NULL;
+	}
+
+	if(s->bulkurb) {
+		dbg("unlink bulkurb");
+		usb_unlink_urb(s->bulkurb);
+	}
+	
+	if(s->bulkurb && s->bulkurb->transfer_buffer) {
+		dbg("kfree bulkurb->transfer_buffer");
+		kfree(s->bulkurb->transfer_buffer);
+		s->bulkurb->transfer_buffer=NULL;
+	}
+	if(s->bulkurb) {
+		dbg("free_urb bulkurb");
+		usb_free_urb(s->bulkurb);
+		s->bulkurb=NULL;
+	}
+	
+	while(!list_empty(&s->free_skb_list)) {
+		skb=s->free_skb_list.next;
+		list_del(skb);
+		skb_list = list_entry (skb, skb_list_t, skb_list);
+		kfree(skb_list);
+	}
+
+	while(!list_empty(&s->tx_skb_list)) {
+		skb=s->tx_skb_list.next;
+		list_del(skb);
+		skb_list = list_entry (skb, skb_list_t, skb_list);
+		kfree(skb_list);	
+	}
+	dbg("plusb_free_all: finished");	
+}
+
+/*-------------------------------------------------------------------*/
+
+static int plusb_alloc(plusb_t *s)
+{
+	int i;
+	skb_list_t *skb;
+
+	dbg("plusb_alloc");
+	
+	for(i=0 ; i < _SKB_NUM ; i++) {
+		skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL);
+		if(!skb) {
+			err("kmalloc for skb_list failed");
+			goto reject;
+		}
+		memset(skb, 0, sizeof(skb_list_t));
+		list_add(&skb->skb_list, &s->free_skb_list);
+	}
+
+	dbg("inturb allocation:");
+	s->inturb=usb_alloc_urb(0);
+	if(!s->inturb) {
+		err("alloc_urb failed");
+		goto reject;
+	}
+
+	dbg("bulkurb allocation:");	
+	s->bulkurb=usb_alloc_urb(0);
+	if(!s->bulkurb) {
+		err("alloc_urb failed");
+		goto reject;
+	}
+	
+	dbg("bulkurb/inturb init:");
+	s->inturb->dev=s->usbdev;
+	s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE);
+	s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL);
+	if(!s->inturb->transfer_buffer) {
+		err("kmalloc failed");
+		goto reject;
+	}
+	
+	s->inturb->transfer_buffer_length=1;
+	s->inturb->complete=plusb_int_complete;
+	s->inturb->context=s;
+	s->inturb->interval=10;
+
+	dbg("inturb submission:");
+	if(usb_submit_urb(s->inturb)<0) {
+		err("usb_submit_urb failed");
+		goto reject;
+	}
+
+	dbg("bulkurb init:");
+	s->bulkurb->dev=s->usbdev;
+	s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE);
+	s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL);
+	if(!s->bulkurb->transfer_buffer) {
+		err("kmalloc failed");
+		goto reject;
+	}
+	
+	s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN;
+	s->bulkurb->complete=plusb_bulk_complete;
+	s->bulkurb->context=s;
+	
+	dbg("plusb_alloc: finished");
+	
+	return 0;
+
+  reject:
+  	dbg("plusb_alloc: failed");
+	
+	plusb_free_all(s);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------*/
+
+static int plusb_net_open(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("plusb_net_open");
+	
+	if(plusb_alloc(s))
+		return -ENOMEM;
+
+	s->opened=1;
+	MOD_INC_USE_COUNT;
+	
+	dbg("plusb_net_open: success");
+	
+	return 0;
+	
+}
+
+/* --------------------------------------------------------------------- */
+
+static int plusb_net_stop(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("plusb_net_stop");	
+	
+	plusb_free_all(s);
+	s->opened=0;
+	MOD_DEC_USE_COUNT;
+	dbg("plusb_net_stop:finished");
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *plusb_net_get_stats(struct net_device *dev)
+{
+	plusb_t *s=dev->priv;
+	
+	dbg("net_device_stats");
+	
+	return &s->net_stats;
+}
+
+/* --------------------------------------------------------------------- */
+
+static plusb_t *plusb_find_struct (void)
+{
+	int u;
+
+	for (u = 0; u < NRPLUSB; u++) {
+		plusb_t *s = &plusb[u];
+		if (!s->connected)
+			return s;
+	}
+	return NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void plusb_disconnect (struct usb_device *usbdev, void *ptr)
+{
+	plusb_t *s = ptr;
+
+	printk ("plusb_net_disconnect: Starting\n");
+	
+	dbg("plusb_disconnect");
+	
+	s->connected = 0;
+	
+	plusb_free_all(s);
+
+	if(!s->opened && s->net_dev.name) {
+		dbg("unregistering netdev: %s",s->net_dev.name);
+		unregister_netdev(&s->net_dev);
+		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;
+}
+
+/* --------------------------------------------------------------------- */
+
+int plusb_net_init(struct net_device *dev)
+{
+	dbg("plusb_net_init");
+	
+	dev->open=plusb_net_open;
+	dev->stop=plusb_net_stop;
+	dev->hard_start_xmit=plusb_net_xmit;
+	dev->get_stats	= plusb_net_get_stats;
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->flags = IFF_POINTOPOINT|IFF_NOARP;
+
+	
+	dbg("plusb_net_init: finished");
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum)
+{
+	plusb_t *s;
+
+	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;
+
+	/* We don't handle multiple configurations */
+	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;
+		s->net_dev.priv=s;
+		if(!register_netdev(&s->net_dev))
+			info("registered: %s", s->net_dev.name);
+		else {
+			err("register_netdev failed");
+			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");
+		plusb_alloc(s);
+	}
+
+	info("bound to interface: %d dev: %p", ifnum, usbdev);
+	MOD_INC_USE_COUNT;
+	return s;
+}
+/* --------------------------------------------------------------------- */
+
+static struct usb_driver plusb_driver =
+{
+	name: "plusb",
+	probe: plusb_probe,
+	disconnect: plusb_disconnect,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int __init plusb_init (void)
+{
+	unsigned u;
+	
+	dbg("plusb_init");
+	
+	/* initialize struct */
+	for (u = 0; u < NRPLUSB; 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);
+
+	dbg("plusb_init: driver registered");
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void __exit plusb_cleanup (void)
+{
+	unsigned u;
+
+	dbg("plusb_cleanup");
+	for (u = 0; u < NRPLUSB; u++) {
+		plusb_t *s = &plusb[u];
+		if(s->net_dev.name[0]) {
+			dbg("unregistering netdev: %s",s->net_dev.name);
+			unregister_netdev(&s->net_dev);
+		}
+	}
+	usb_deregister (&plusb_driver);
+	dbg("plusb_cleanup: finished");
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");
+MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000");
+
+
+module_init (plusb_init);
+module_exit (plusb_cleanup);
+
+/* --------------------------------------------------------------------- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/plusb.h linux.18p17/drivers/usb/plusb.h
--- linux.vanilla/drivers/usb/plusb.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/plusb.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,48 @@
+#define _PLUSB_INTPIPE		0x1
+#define _PLUSB_BULKOUTPIPE	0x2
+#define _PLUSB_BULKINPIPE	0x3
+
+#define _SKB_NUM		1000
+//  7  6  5  4  3  2  1  0
+//  tx rx 1  0
+// 1110 0000 rxdata
+// 1010 0000 idle
+// 0010 0000 tx over
+// 0110      tx over + rxd
+
+#define _PLUSB_RXD		0x40
+#define _PLUSB_TXOK		0x80
+
+#ifdef __KERNEL__
+#define _BULK_DATA_LEN		16384
+
+typedef struct
+{
+	struct list_head skb_list;
+	struct sk_buff *skb;
+	int state;
+} skb_list_t,*pskb_list_t;
+
+typedef struct
+{
+	struct usb_device *usbdev;
+
+	int status;
+	int connected;
+	int in_bh;
+	int opened;
+
+	spinlock_t lock;
+
+	urb_t *inturb;
+	urb_t *bulkurb;	
+
+	struct list_head tx_skb_list;
+	struct list_head free_skb_list;
+	struct tq_struct bh;
+
+	struct net_device net_dev;
+	struct net_device_stats net_stats;
+} plusb_t,*pplusb_t;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/printer.c linux.18p17/drivers/usb/printer.c
--- linux.vanilla/drivers/usb/printer.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/printer.c	Fri Sep 15 22:02:39 2000
@@ -0,0 +1,525 @@
+/*
+ * printer.c  Version 0.6
+ *
+ * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
+ * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
+ * Copyright (c) 2000 Randy Dunlap	<randy.dunlap@intel.com>
+ * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
+ *
+ * USB Printer Device Class driver for USB printers and printer cables
+ *
+ * Sponsored by SuSE
+ *
+ * ChangeLog:
+ *	v0.1 - thorough cleaning, URBification, almost a rewrite
+ *	v0.2 - some more cleanups
+ *	v0.3 - cleaner again, waitqueue fixes
+ *	v0.4 - fixes in unidirectional mode
+ *	v0.5 - add DEVICE_ID string support
+ *	v0.6 - never time out
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/lp.h>
+#undef DEBUG
+#include <linux/usb.h>
+
+#define USBLP_BUF_SIZE		8192
+#define DEVICE_ID_SIZE		1024
+
+#define IOCNR_GET_DEVICE_ID	1
+#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)	/* get device_id string */
+
+/*
+ * A DEVICE_ID string may include the printer's serial number.
+ * It should end with a semi-colon (';').
+ * An example from an HP 970C DeskJet printer is (this is one long string,
+ * with the serial number changed):
+MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ:                    ;
+ */
+
+/*
+ * USB Printer Requests
+ */
+
+#define USBLP_REQ_GET_ID	0x00
+#define USBLP_REQ_GET_STATUS	0x01
+#define USBLP_REQ_RESET		0x02
+
+#define USBLP_MINORS		16
+#define USBLP_MINOR_BASE	0
+
+#define USBLP_WRITE_TIMEOUT	(5*HZ)			/* 5 seconds */
+
+struct usblp {
+	struct usb_device 	*dev;			/* USB device */
+	struct urb		readurb, writeurb;	/* The urbs */
+	wait_queue_head_t	wait;			/* Zzzzz ... */
+	int			readcount;		/* Counter for reads */
+	int			ifnum;			/* Interface number */
+	int			minor;			/* minor number of device */
+	unsigned char		used;			/* True if open */
+	unsigned char		bidir;			/* interface is bidirectional */
+	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
+							/* first 2 bytes are (big-endian) length */
+};
+
+static struct usblp *usblp_table[USBLP_MINORS];
+
+/*
+ * Functions for usblp control messages.
+ */
+
+static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, int value, void *buf, int len)
+{
+	int retval = usb_control_msg(usblp->dev,
+		dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
+		request, USB_TYPE_CLASS | dir | recip, value, usblp->ifnum, buf, len, HZ * 5);
+	dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d",
+		request, !!dir, recip, value, len, retval);
+	return retval < 0 ? retval : 0;
+}
+
+#define usblp_read_status(usblp, status)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1)
+#define usblp_get_id(usblp, config, id, maxlen)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)
+#define usblp_reset(usblp)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)
+
+/*
+ * URB callback.
+ */
+
+static void usblp_bulk(struct urb *urb)
+{
+	struct usblp *usblp = urb->context;
+
+	if (!usblp || !usblp->dev || !usblp->used)
+		return;
+
+	if (urb->status)
+		warn("usblp%d: nonzero read/write bulk status received: %d",
+			usblp->minor, urb->status);
+
+	wake_up_interruptible(&usblp->wait);
+}
+
+/*
+ * Get and print printer errors.
+ */
+
+static char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };
+
+static int usblp_check_status(struct usblp *usblp, int err)
+{
+	unsigned char status, newerr = 0;
+
+	if (usblp_read_status(usblp, &status)) {
+		err("usblp%d: failed reading printer status", usblp->minor);
+		return 0;
+	}
+
+	if (~status & LP_PERRORP) {
+		newerr = 3;
+		if (status & LP_POUTPA) newerr = 1;
+		if (~status & LP_PSELECD) newerr = 2;
+	}
+
+	if (newerr != err)
+		info("usblp%d: %s", usblp->minor, usblp_messages[newerr]);
+
+	return newerr;
+}
+
+/*
+ * File op functions.
+ */
+
+static int usblp_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev) - USBLP_MINOR_BASE;
+	struct usblp *usblp;
+	int retval;
+
+	if (minor < 0 || minor >= USBLP_MINORS)
+		return -ENODEV;
+
+	lock_kernel();
+	usblp  = usblp_table[minor];
+
+	retval = -ENODEV;
+	if (!usblp || !usblp->dev)
+		goto out;
+
+	retval = -EBUSY;
+	if (usblp->used)
+		goto out;
+
+	if ((retval = usblp_check_status(usblp, 0))) {
+		retval = retval > 1 ? -EIO : -ENOSPC;
+		goto out;
+	}
+
+	usblp->used = 1;
+	file->private_data = usblp;
+
+	usblp->writeurb.transfer_buffer_length = 0;
+	usblp->writeurb.status = 0;
+
+	if (usblp->bidir) {
+		usblp->readcount = 0;
+		usb_submit_urb(&usblp->readurb);
+	}
+out:
+	unlock_kernel();
+	return retval;
+}
+
+static int usblp_release(struct inode *inode, struct file *file)
+{
+	struct usblp *usblp = file->private_data;
+
+	usblp->used = 0;
+
+	if (usblp->dev) {
+		if (usblp->bidir)
+			usb_unlink_urb(&usblp->readurb);
+		usb_unlink_urb(&usblp->writeurb);
+		return 0;
+	}
+
+	usblp_table[usblp->minor] = NULL;
+	kfree(usblp->device_id_string);
+	kfree(usblp);
+
+	return 0;
+}
+
+/* No kernel lock - fine */
+static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct usblp *usblp = file->private_data;
+	poll_wait(file, &usblp->wait, wait);
+	return ((usblp->bidir || usblp->readurb.status  == -EINPROGRESS) ? 0 : POLLIN  | POLLRDNORM)
+	     		      | (usblp->writeurb.status == -EINPROGRESS  ? 0 : POLLOUT | POLLWRNORM);
+}
+
+static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usblp *usblp = file->private_data;
+	int length;
+
+	if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ))
+		return -EINVAL;
+
+	switch (_IOC_NR(cmd)) {
+
+		case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+
+			length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+
+			dbg ("usblp_ioctl GET_DEVICE_ID actlen: %d, size: %d, string: '%s'",
+				length, _IOC_SIZE(cmd), &usblp->device_id_string[2]);
+
+			if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */
+
+			if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length))
+				return -EFAULT;
+
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct usblp *usblp = file->private_data;
+	int timeout, err = 0, writecount = 0;
+
+	while (writecount < count) {
+
+		if (usblp->writeurb.status == -EINPROGRESS) {
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			timeout = USBLP_WRITE_TIMEOUT;
+			while (timeout && usblp->writeurb.status == -EINPROGRESS) {
+
+				if (signal_pending(current))
+					return writecount ? writecount : -EINTR;
+
+				timeout = interruptible_sleep_on_timeout(&usblp->wait, timeout);
+			}
+		}
+
+		if (!usblp->dev)
+			return -ENODEV;
+
+		if (usblp->writeurb.status) {
+			err = usblp_check_status(usblp, err);
+			continue;
+		}
+
+		writecount += usblp->writeurb.transfer_buffer_length;
+		usblp->writeurb.transfer_buffer_length = 0;
+
+		if (writecount == count)
+			continue;
+
+		usblp->writeurb.transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ?
+							 (count - writecount) : USBLP_BUF_SIZE;
+
+		if (copy_from_user(usblp->writeurb.transfer_buffer, buffer + writecount,
+				usblp->writeurb.transfer_buffer_length)) return -EFAULT;
+
+		usb_submit_urb(&usblp->writeurb);
+	}
+
+	return count;
+}
+
+static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct usblp *usblp = file->private_data;
+
+	if (!usblp->bidir)
+		return -EINVAL;
+
+	if (usblp->readurb.status == -EINPROGRESS) {
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		while (usblp->readurb.status == -EINPROGRESS) {
+			if (signal_pending(current))
+				return -EINTR;
+			interruptible_sleep_on(&usblp->wait);
+		}
+	}
+
+	if (!usblp->dev)
+		return -ENODEV;
+
+	if (usblp->readurb.status) {
+		err("usblp%d: error %d reading from printer",
+			usblp->minor, usblp->readurb.status);
+		usb_submit_urb(&usblp->readurb);
+		return -EIO;
+	}
+
+	count = count < usblp->readurb.actual_length - usblp->readcount ?
+		count :	usblp->readurb.actual_length - usblp->readcount;
+
+	if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount, count))
+		return -EFAULT;
+
+	if ((usblp->readcount += count) == usblp->readurb.actual_length)
+		usb_submit_urb(&usblp->readurb);
+
+	return count;
+}
+
+static void *usblp_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *epread, *epwrite;
+	struct usblp *usblp;
+	int minor, i, alts = -1, bidir = 0;
+	int length, err;
+	char *buf;
+
+	for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) {
+
+		interface = &dev->actconfig->interface[ifnum].altsetting[i];
+
+		if (interface->bInterfaceClass != 7 || interface->bInterfaceSubClass != 1 ||
+		    interface->bInterfaceProtocol < 1 || interface->bInterfaceProtocol > 3 ||
+		   (interface->bInterfaceProtocol > 1 && interface->bNumEndpoints < 2))
+			continue;
+
+		if (alts == -1)
+			alts = i;
+
+		if (!bidir && interface->bInterfaceProtocol > 1) {
+			bidir = 1;
+			alts = i;
+		}
+	}
+
+	if (alts == -1)
+		return NULL;
+
+	interface = &dev->actconfig->interface[ifnum].altsetting[alts];
+	if (usb_set_interface(dev, ifnum, alts))
+		err("can't set desired altsetting %d on interface %d", alts, ifnum);
+
+	epwrite = interface->endpoint + 0;
+	epread = bidir ? interface->endpoint + 1 : NULL;
+
+	if ((epwrite->bEndpointAddress & 0x80) == 0x80) {
+		if (interface->bNumEndpoints == 1)
+			return NULL;
+		epwrite = interface->endpoint + 1;
+		epread = bidir ? interface->endpoint + 0 : NULL;
+	}
+
+	if ((epwrite->bEndpointAddress & 0x80) == 0x80)
+		return NULL;
+
+	if (bidir && (epread->bEndpointAddress & 0x80) != 0x80)
+		return NULL;
+
+	for (minor = 0; minor < USBLP_MINORS && usblp_table[minor]; minor++);
+	if (usblp_table[minor]) {
+		err("no more free usblp devices");
+		return NULL;
+	}
+
+	if (!(usblp = kmalloc(sizeof(struct usblp), GFP_KERNEL))) {
+		err("out of memory");
+		return NULL;
+	}
+	memset(usblp, 0, sizeof(struct usblp));
+
+	usblp->dev = dev;
+	usblp->ifnum = ifnum;
+	usblp->minor = minor;
+	usblp->bidir = bidir;
+
+	init_waitqueue_head(&usblp->wait);
+
+	if (!(buf = kmalloc(USBLP_BUF_SIZE * (bidir ? 2 : 1), GFP_KERNEL))) {
+		err("out of memory");
+		kfree(usblp);
+		return NULL;
+	}
+
+	if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) {
+		err("out of memory");
+		kfree(usblp);
+		kfree(buf);
+		return NULL;
+	}
+
+	FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+		buf, 0, usblp_bulk, usblp);
+
+	if (bidir)
+		FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+			buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp);
+
+	/* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */
+	err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1);
+	if (err >= 0) {
+		length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */
+		if (length < DEVICE_ID_SIZE)
+			usblp->device_id_string[length] = '\0';
+		else
+			usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0';
+		dbg ("usblp%d Device ID string [%d]=%s",
+			minor, length, &usblp->device_id_string[2]);
+	}
+	else {
+		err ("usblp%d: error = %d reading IEEE-1284 Device ID string",
+			minor, err);
+		usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';
+	}
+
+#ifdef DEBUG
+	usblp_check_status(usblp);
+#endif
+
+	info("usblp%d: USB %sdirectional printer dev %d if %d alt %d",
+		minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts);
+
+	return usblp_table[minor] = usblp;
+}
+
+static void usblp_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usblp *usblp = ptr;
+
+	if (!usblp || !usblp->dev) {
+		err("disconnect on nonexisting interface");
+		return;
+	}
+
+	usblp->dev = NULL;
+
+	usb_unlink_urb(&usblp->writeurb);
+	if (usblp->bidir)
+		usb_unlink_urb(&usblp->readurb);
+
+	kfree(usblp->writeurb.transfer_buffer);
+
+	if (usblp->used) return;
+
+	kfree(usblp->device_id_string);
+
+	usblp_table[usblp->minor] = NULL;
+	kfree(usblp);
+}
+
+static struct file_operations usblp_fops = {
+	read:		usblp_read,
+	write:		usblp_write,
+	poll:		usblp_poll,
+	ioctl:		usblp_ioctl,
+	open:		usblp_open,
+	release:	usblp_release,
+};
+
+static struct usb_driver usblp_driver = {
+	name:		"usblp",
+	probe:		usblp_probe,
+	disconnect:	usblp_disconnect,
+	fops:		&usblp_fops,
+	minor:		USBLP_MINOR_BASE
+};
+
+static int __init usblp_init(void)
+{
+	if (usb_register(&usblp_driver))
+		return -1;
+	return 0;
+}
+
+static void __exit usblp_exit(void)
+{
+	usb_deregister(&usblp_driver);
+}
+
+module_init(usblp_init);
+module_exit(usblp_exit);
+
+MODULE_AUTHOR("Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap");
+MODULE_DESCRIPTION("USB Printer Device Class driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/restart linux.18p17/drivers/usb/restart
--- linux.vanilla/drivers/usb/restart	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/restart	Thu Jan  1 01:00:00 1970
@@ -1,38 +0,0 @@
-#!/bin/sh
-
-ME=`basename $0`
-
-#UMOD=`lsmod | grep '^bp-mouse' | grep -v grep`
-#if test "$UMOD"; then
-#  echo "$ME: removing bp-mouse.o"
-#  if ! rmmod bp-mouse; then
-#    echo "$ME: cannot remove bp-mouse.o"
-#    exit 1
-#  fi
-#fi
-
-UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'`
-if test "$UPID"; then
-  echo "$ME: killing $UPID"
-  kill $UPID
-fi
-
-UMOD=`lsmod | grep '^usb-ohci' | grep -v grep`
-if test "$UMOD"; then
-  echo "$ME: removing usb-ohci.o"
-  sleep 1
-  if ! rmmod usb-ohci; then
-    echo "$ME: cannot remove usb-ohci.o"
-    exit 1
-  fi
-fi
-
-dmesg -c > /dev/null
-
-echo "$ME: starting usb-ohci.o"
-insmod -m usb-ohci.o > usb-ohci.map
-
-sleep 1
-UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'`
-if test "$UPID"; then echo "$ME: ohci-control is pid $UPID" ; fi
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/rio500.c linux.18p17/drivers/usb/rio500.c
--- linux.vanilla/drivers/usb/rio500.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/rio500.c	Fri Sep 15 22:03:35 2000
@@ -0,0 +1,450 @@
+/* -*- linux-c -*- */
+
+/* 
+ * Driver for USB Rio 500
+ *
+ * Cesar Miquel (miquel@df.uba.ar)
+ * 
+ * based on hp_scanner.c by David E. Nelson (dnelson@jump.net)
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#include "rio500_usb.h"
+
+#define RIO_MINOR   64
+
+/* stall/wait timeout for rio */
+#define NAK_TIMEOUT (HZ)
+
+#define IBUF_SIZE 0x1000
+
+/* Size of the rio buffer */
+#define OBUF_SIZE 0x10000
+
+struct rio_usb_data {
+        struct usb_device *rio_dev;     /* init: probe_rio */
+        unsigned int ifnum;             /* Interface number of the USB device */
+        int isopen;                     /* nz if open */
+        int present;                    /* Device is present on the bus */
+        char *obuf, *ibuf;              /* transfer buffers */
+        char bulk_in_ep, bulk_out_ep;   /* Endpoint assignments */
+        wait_queue_head_t wait_q;       /* for timeouts */
+};
+
+static struct rio_usb_data rio_instance;
+
+static int open_rio(struct inode *inode, struct file *file)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	if (rio->isopen || !rio->present) {
+		return -EBUSY;
+	}
+	rio->isopen = 1;
+
+	init_waitqueue_head(&rio->wait_q);
+
+	MOD_INC_USE_COUNT;
+
+	info("Rio opened.");
+
+	return 0;
+}
+
+static int close_rio(struct inode *inode, struct file *file)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	rio->isopen = 0;
+
+	MOD_DEC_USE_COUNT;
+
+	info("Rio closed.");
+	return 0;
+}
+
+static int
+ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
+	  unsigned long arg)
+{
+	struct RioCommand rio_cmd;
+	struct rio_usb_data *rio = &rio_instance;
+	void *data;
+	unsigned char *buffer;
+	int result, requesttype;
+	int retries;
+
+        /* Sanity check to make sure rio is connected, powered, etc */
+        if ( rio == NULL ||
+             rio->present == 0 ||
+             rio->rio_dev == NULL )
+          return -1;
+
+	switch (cmd) {
+	case RIO_RECV_COMMAND:
+		data = (void *) arg;
+		if (data == NULL)
+			break;
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand)))
+			return -EFAULT;
+		if (rio_cmd.length > PAGE_SIZE)
+			return -EINVAL;
+		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+		if (buffer == NULL)
+			return -ENOMEM;
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length))
+			return -EFAULT;
+
+		requesttype = rio_cmd.requesttype | USB_DIR_IN |
+		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		dbg
+		    ("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
+		     requesttype, rio_cmd.request, rio_cmd.value,
+		     rio_cmd.index, rio_cmd.length);
+		/* Send rio control message */
+		retries = 3;
+		while (retries) {
+			result = usb_control_msg(rio->rio_dev,
+						 usb_rcvctrlpipe(rio-> rio_dev, 0),
+						 rio_cmd.request,
+						 requesttype,
+						 rio_cmd.value,
+						 rio_cmd.index, buffer,
+						 rio_cmd.length,
+						 rio_cmd.timeout);
+			if (result == -ETIMEDOUT)
+				retries--;
+			else if (result < 0) {
+				err("Error executing ioctrl. code = %d",
+				     le32_to_cpu(result));
+				retries = 0;
+			} else {
+				dbg("Executed ioctl. Result = %d (data=%04x)",
+				     le32_to_cpu(result),
+				     le32_to_cpu(*((long *) buffer)));
+				if (copy_to_user(rio_cmd.buffer, buffer,
+						 rio_cmd.length))
+					return -EFAULT;
+				retries = 0;
+			}
+
+			/* rio_cmd.buffer contains a raw stream of single byte
+			   data which has been returned from rio.  Data is
+			   interpreted at application level.  For data that
+			   will be cast to data types longer than 1 byte, data
+			   will be little_endian and will potentially need to
+			   be swapped at the app level */
+
+		}
+		free_page((unsigned long) buffer);
+		break;
+
+	case RIO_SEND_COMMAND:
+		data = (void *) arg;
+		if (data == NULL)
+			break;
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand)))
+			return -EFAULT;
+		if (rio_cmd.length > PAGE_SIZE)
+			return -EINVAL;
+		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+		if (buffer == NULL)
+			return -ENOMEM;
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length))
+			return -EFAULT;
+
+		requesttype = rio_cmd.requesttype | USB_DIR_OUT |
+		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		dbg("sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
+		     requesttype, rio_cmd.request, rio_cmd.value,
+		     rio_cmd.index, rio_cmd.length);
+		/* Send rio control message */
+		retries = 3;
+		while (retries) {
+			result = usb_control_msg(rio->rio_dev,
+						 usb_sndctrlpipe(rio-> rio_dev, 0),
+						 rio_cmd.request,
+						 requesttype,
+						 rio_cmd.value,
+						 rio_cmd.index, buffer,
+						 rio_cmd.length,
+						 rio_cmd.timeout);
+			if (result == -ETIMEDOUT)
+				retries--;
+			else if (result < 0) {
+				err("Error executing ioctrl. code = %d",
+				     le32_to_cpu(result));
+				retries = 0;
+			} else {
+				dbg("Executed ioctl. Result = %d",
+				       le32_to_cpu(result));
+				retries = 0;
+
+			}
+
+		}
+		free_page((unsigned long) buffer);
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+		break;
+	}
+
+	return 0;
+}
+
+static ssize_t
+write_rio(struct file *file, const char *buffer,
+	  size_t count, loff_t * ppos)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	unsigned long copy_size;
+	unsigned long bytes_written = 0;
+	unsigned int partial;
+
+	int result = 0;
+	int maxretry;
+
+        /* Sanity check to make sure rio is connected, powered, etc */
+        if ( rio == NULL ||
+             rio->present == 0 ||
+             rio->rio_dev == NULL )
+          return -1;
+
+	do {
+		unsigned long thistime;
+		char *obuf = rio->obuf;
+
+		thistime = copy_size =
+		    (count >= OBUF_SIZE) ? OBUF_SIZE : count;
+		if (copy_from_user(rio->obuf, buffer, copy_size))
+			return -EFAULT;
+		maxretry = 5;
+		while (thistime) {
+			if (!rio->rio_dev)
+				return -ENODEV;
+			if (signal_pending(current)) {
+				return bytes_written ? bytes_written : -EINTR;
+			}
+
+			result = usb_bulk_msg(rio->rio_dev,
+					 usb_sndbulkpipe(rio->rio_dev, 2),
+					 obuf, thistime, &partial, 5 * HZ);
+
+			dbg("write stats: result:%d thistime:%lu partial:%u",
+			     result, thistime, partial);
+
+			if (result == USB_ST_TIMEOUT) {	/* NAK - so hold for a while */
+				if (!maxretry--) {
+					return -ETIME;
+				}
+				interruptible_sleep_on_timeout(&rio-> wait_q, NAK_TIMEOUT);
+				continue;
+			} else if (!result & partial) {
+				obuf += partial;
+				thistime -= partial;
+			} else
+				break;
+		};
+		if (result) {
+			err("Write Whoops - %x", result);
+			return -EIO;
+		}
+		bytes_written += copy_size;
+		count -= copy_size;
+		buffer += copy_size;
+	} while (count > 0);
+
+	return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t
+read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos)
+{
+	struct rio_usb_data *rio = &rio_instance;
+	ssize_t read_count;
+	unsigned int partial;
+	int this_read;
+	int result;
+	int maxretry = 10;
+	char *ibuf = rio->ibuf;
+
+        /* Sanity check to make sure rio is connected, powered, etc */
+        if ( rio == NULL ||
+             rio->present == 0 ||
+             rio->rio_dev == NULL )
+          return -1;
+
+	read_count = 0;
+
+	while (count > 0) {
+		if (signal_pending(current)) {
+			return read_count ? read_count : -EINTR;
+		}
+		if (!rio->rio_dev)
+			return -ENODEV;
+		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+
+		result = usb_bulk_msg(rio->rio_dev,
+				      usb_rcvbulkpipe(rio->rio_dev, 1),
+				      ibuf, this_read, &partial,
+				      (int) (HZ * 8));
+
+		dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u",
+		       result, this_read, partial);
+
+		if (partial) {
+			count = this_read = partial;
+		} else if (result == USB_ST_TIMEOUT || result == 15) {	/* FIXME: 15 ??? */
+			if (!maxretry--) {
+				err("read_rio: maxretry timeout");
+				return -ETIME;
+			}
+			interruptible_sleep_on_timeout(&rio->wait_q,
+						       NAK_TIMEOUT);
+			continue;
+		} else if (result != USB_ST_DATAUNDERRUN) {
+			err("Read Whoops - result:%u partial:%u this_read:%u",
+			     result, partial, this_read);
+			return -EIO;
+		} else {
+			return (0);
+		}
+
+		if (this_read) {
+			if (copy_to_user(buffer, ibuf, this_read))
+				return -EFAULT;
+			count -= this_read;
+			read_count += this_read;
+			buffer += this_read;
+		}
+	}
+	return read_count;
+}
+
+static void *probe_rio(struct usb_device *dev, unsigned int ifnum)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	if (dev->descriptor.idVendor != 0x841) {
+		return NULL;
+	}
+
+	if (dev->descriptor.idProduct != 0x1 /* RIO 500 */ ) {
+		warn(KERN_INFO "Rio player model not supported/tested.");
+		return NULL;
+	}
+
+	info("USB Rio found at address %d", dev->devnum);
+
+	rio->present = 1;
+	rio->rio_dev = dev;
+
+	if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+		err("probe_rio: Not enough memory for the output buffer");
+		return NULL;
+	}
+	dbg("probe_rio: obuf address:%p", rio->obuf);
+
+	if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+		err("probe_rio: Not enough memory for the input buffer");
+		kfree(rio->obuf);
+		return NULL;
+	}
+	dbg("probe_rio: ibuf address:%p", rio->ibuf);
+
+	return rio;
+}
+
+static void disconnect_rio(struct usb_device *dev, void *ptr)
+{
+	struct rio_usb_data *rio = (struct rio_usb_data *) ptr;
+
+	if (rio->isopen) {
+		rio->isopen = 0;
+		/* better let it finish - the release will do whats needed */
+		rio->rio_dev = NULL;
+		return;
+	}
+	kfree(rio->ibuf);
+	kfree(rio->obuf);
+
+	info("USB Rio disconnected.");
+
+	rio->present = 0;
+}
+
+static struct
+file_operations usb_rio_fops = {
+	read:		read_rio,
+	write:		write_rio,
+	ioctl:		ioctl_rio,
+	open:		open_rio,
+	release:	close_rio,
+};
+
+static struct
+usb_driver rio_driver = {
+	"rio500",
+	probe_rio,
+	disconnect_rio,
+	{NULL, NULL},
+	&usb_rio_fops,
+	RIO_MINOR
+};
+
+int usb_rio_init(void)
+{
+	if (usb_register(&rio_driver) < 0)
+		return -1;
+
+	info("USB Rio support registered.");
+	return 0;
+}
+
+
+void usb_rio_cleanup(void)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	rio->present = 0;
+	usb_deregister(&rio_driver);
+
+
+}
+
+module_init(usb_rio_init);
+module_exit(usb_rio_cleanup);
+
+MODULE_AUTHOR("Cesar Miquel <miquel@df.uba.ar>");
+MODULE_DESCRIPTION("USB Rio 500 driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/rio500_usb.h linux.18p17/drivers/usb/rio500_usb.h
--- linux.vanilla/drivers/usb/rio500_usb.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/rio500_usb.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,37 @@
+/*  ----------------------------------------------------------------------
+
+    Copyright (C) 2000  Cesar Miquel  (miquel@df.uba.ar)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    ---------------------------------------------------------------------- */
+
+
+
+#define RIO_SEND_COMMAND			0x1
+#define RIO_RECV_COMMAND			0x2
+
+#define RIO_DIR_OUT               	        0x0
+#define RIO_DIR_IN				0x1
+
+struct RioCommand {
+	short length;
+	int request;
+	int requesttype;
+	int value;
+	int index;
+	void *buffer;
+	int timeout;
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/scanner.c linux.18p17/drivers/usb/scanner.c
--- linux.vanilla/drivers/usb/scanner.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/scanner.c	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,876 @@
+/* -*- linux-c -*- */
+
+/* 
+ * Driver for USB Scanners (linux-2.4.0test1-ac7)
+ *
+ * Copyright (C) 1999, 2000 David E. Nelson
+ *
+ * Portions may be copyright Brad Keryan and Michael Gee.
+ *
+ * David E. Nelson (dnelson@jump.net)
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Originally based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * History
+ *
+ *  0.1  8/31/1999
+ *
+ *    Developed/tested using linux-2.3.15 with minor ohci.c changes to
+ *    support short packes during bulk xfer mode.  Some testing was
+ *    done with ohci-hcd but the performace was low.  Very limited
+ *    testing was performed with uhci but I was unable to get it to
+ *    work.  Initial relase to the linux-usb development effort.
+ *
+ *
+ *  0.2  10/16/1999
+ *
+ *    - Device can't be opened unless a scanner is plugged into the USB.
+ *    - Finally settled on a reasonable value for the I/O buffer's.
+ *    - Cleaned up write_scanner()
+ *    - Disabled read/write stats
+ *    - A little more code cleanup
+ *
+ *
+ *  0.3  10/18/1999
+ *
+ *    - Device registration changed to reflect new device
+ *      allocation/registration for linux-2.3.22+.
+ *    - Adopted David Brownell's <david-b@pacbell.net> technique for 
+ *      assigning bulk endpoints.
+ *    - Removed unnessesary #include's
+ *    - Scanner model now reported via syslog INFO after being detected 
+ *      *and* configured.
+ *    - Added user specified vendor:product USB ID's which can be passed 
+ *      as module parameters.
+ *
+ *
+ *  0.3.1
+ *
+ *    - Applied patches for linux-2.3.25.
+ *    - Error number reporting changed to reflect negative return codes.
+ *
+ *
+ *  0.3.2
+ *
+ *    - Applied patches for linux-2.3.26 to scanner_init().
+ *    - Debug read/write stats now report values as signed decimal.
+ *
+ *
+ *  0.3.3
+ *
+ *    - Updated the bulk_msg() calls to usb usb_bulk_msg().
+ *    - Added a small delay in the write_scanner() method to aid in
+ *      avoiding NULL data reads on HP scanners.  We'll see how this works.
+ *    - Return values from usb_bulk_msg() now ignore positive values for
+ *      use with the ohci driver.
+ *    - Added conditional debugging instead of commenting/uncommenting
+ *      all over the place.
+ *    - kfree()'d the pointer after using usb_string() as documented in
+ *      linux-usb-api.txt.
+ *    - Added usb_set_configuration().  It got lost in version 0.3 -- ack!
+ *    - Added the HP 5200C USB Vendor/Product ID's.
+ *
+ *
+ *  0.3.4  1/23/2000
+ *
+ *    - Added Greg K-H's <greg@kroah.com> patch for better handling of 
+ *      Product/Vendor detection.
+ *    - The driver now autoconfigures its endpoints including interrupt
+ *      endpoints if one is detected.  The concept was originally based
+ *      upon David Brownell's method.
+ *    - Added some Seiko/Epson ID's. Thanks to Karl Heinz 
+ *      Kremer <khk@khk.net>.
+ *    - Added some preliminary ioctl() calls for the PV8630 which is used
+ *      by the HP4200. The ioctl()'s still have to be registered. Thanks 
+ *      to Adrian Perez Jorge <adrianpj@easynews.com>.
+ *    - Moved/migrated stuff to scanner.h
+ *    - Removed the usb_set_configuration() since this is handled by
+ *      the usb_new_device() routine in usb.c.
+ *    - Added the HP 3300C.  Thanks to Bruce Tenison.
+ *    - Changed user specified vendor/product id so that root hub doesn't
+ *      get falsely attached to. Thanks to Greg K-H.
+ *    - Added some Mustek ID's. Thanks to Gernot Hoyler 
+ *      <Dr.Hoyler@t-online.de>.
+ *    - Modified the usb_string() reporting.  See kfree() comment above.
+ *    - Added Umax Astra 2000U. Thanks to Doug Alcorn <doug@lathi.net>.
+ *    - Updated the printk()'s to use the info/warn/dbg macros.
+ *    - Updated usb_bulk_msg() argument types to fix gcc warnings.
+ *
+ *
+ *  0.4  2/4/2000
+ *
+ *    - Removed usb_string() from probe_scanner since the core now does a
+ *      good job of reporting what was connnected.  
+ *    - Finally, simultaneous multiple device attachment!
+ *    - Fixed some potential memory freeing issues should memory allocation
+ *      fail in probe_scanner();
+ *    - Some fixes to disconnect_scanner().
+ *    - Added interrupt endpoint support.
+ *    - Added Agfa SnapScan Touch. Thanks to Jan Van den Bergh
+ *      <jan.vandenbergh@cs.kuleuven.ac.be>.
+ *    - Added Umax 1220U ID's. Thanks to Maciek Klimkowski
+ *      <mac@nexus.carleton.ca>.
+ *    - Fixed bug in write_scanner(). The buffer was not being properly
+ *      updated for writes larger than OBUF_SIZE. Thanks to Henrik 
+ *      Johansson <henrikjo@post.utfors.se> for identifying it.
+ *    - Added Microtek X6 ID's. Thanks to Oliver Neukum
+ *      <Oliver.Neukum@lrz.uni-muenchen.de>.
+ *
+ * 
+ *  0.4.1  2/15/2000
+ *  
+ *    - Fixed 'count' bug in read_scanner(). Thanks to Henrik
+ *      Johansson <henrikjo@post.utfors.se> for identifying it.  Amazing
+ *      it has worked this long.
+ *    - Fixed '>=' bug in both read/write_scanner methods.
+ *    - Cleaned up both read/write_scanner() methods so that they are 
+ *      a little more readable.
+ *    - Added a lot of Microtek ID's.  Thanks to Adrian Perez Jorge.
+ *    - Adopted the __initcall().
+ *    - Added #include <linux/init.h> to scanner.h for __initcall().
+ *    - Added one liner in irq_scanner() to keep gcc from complaining 
+ *      about an unused variable (data) if debugging was disabled
+ *      in scanner.c.
+ *    - Increased the timeout parameter in read_scanner() to 120 Secs.
+ *
+ *
+ *  0.4.2  3/23/2000
+ *
+ *    - Added Umax 1236U ID.  Thanks to Philipp Baer <ph_baer@npw.net>.
+ *    - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's.
+ *      Thanks to Adrian Perez Jorge <adrianpj@easynews.com>.
+ *    - Fixed error number reported for non-existant devices.  Thanks to
+ *      Spyridon Papadimitriou <Spyridon_Papadimitriou@gs91.sp.cs.cmu.edu>.
+ *    - Added Acer Prisascan 620U ID's.  Thanks to Joao <joey@knoware.nl>.
+ *    - Replaced __initcall() with module_init()/module_exit(). Updates
+ *      from patch-2.3.48.
+ *    - Replaced file_operations structure with new syntax.  Updates
+ *      from patch-2.3.49.
+ *    - Changed #include "usb.h" to #include <linux/usb.h>
+ *    - Added #define SCN_IOCTL to exclude development areas 
+ *      since 2.4.x is about to be released. This mainly affects the 
+ *      ioctl() stuff.  See scanner.h for more details.
+ *    - Changed the return value for signal_pending() from -ERESTARTSYS to
+ *      -EINTR.
+ *
+ *
+ * 0.4.3  4/30/2000
+ *
+ *    - Added Umax Astra 2200 ID.  Thanks to Flynn Marquardt 
+ *      <flynn@isr.uni-stuttgart.de>.
+ *    - Added iVina 1200U ID. Thanks to Dyson Lin <dyson@avision.com.tw>.
+ *    - Added access time update for the device file courtesy of Paul
+ *      Mackerras <paulus@linuxcare.com>.  This allows a user space daemon
+ *      to turn the lamp off for a Umax 1220U scanner after a prescribed
+ *      time.
+ *    - Fixed HP S20 ID's.  Thanks to Ruud Linders <rlinders@xs4all.nl>.
+ *    - Added Acer ScanPrisa 620U ID. Thanks to Oliver
+ *      Schwartz <Oliver.Schwartz@gmx.de> via sane-devel mail list.
+ *    - Fixed bug in read_scanner for copy_to_user() function.  The returned
+ *      value should be 'partial' not 'this_read'.
+ *    - Fixed bug in read_scanner. 'count' should be decremented 
+ *      by 'this_read' and not by 'partial'.  This resulted in twice as many
+ *      calls to read_scanner() for small amounts of data and possibly
+ *      unexpected returns of '0'.  Thanks to Karl Heinz 
+ *      Kremer <khk@khk.net> and Alain Knaff <Alain.Knaff@ltnb.lu>
+ *      for discovering this.
+ *    - Integrated Randy Dunlap's <randy.dunlap@intel.com> patch for a
+ *      scanner lookup/ident table. Thanks Randy.
+ *    - Documentation updates.
+ *    - Added wait queues to read_scanner().
+ *
+ *
+ * 0.4.3.1
+ *
+ *    - Fixed HP S20 ID's...again..sigh.  Thanks to Ruud
+ *      Linders <rlinders@xs4all.nl>.
+ *
+ *
+ *  TODO
+ *
+ *    - Performance
+ *    - Select/poll methods
+ *    - More testing
+ *    - Proper registry/assignment for LM9830 ioctl's
+ *
+ *
+ *  Thanks to:
+ *
+ *    - All the folks on the linux-usb list who put up with me. :)  This 
+ *      has been a great learning experience for me.
+ *    - To Linus Torvalds for this great OS.
+ *    - The GNU folks.
+ *    - The folks that forwarded Vendor:Product ID's to me.
+ *    - Johannes Erdfelt for the loaning of a USB analyzer for tracking an
+ *      issue with HP-4100 and uhci.
+ *    - Adolfo Montero for his assistance.
+ *    - All the folks who chimed in with reports and suggestions.
+ *    - All the developers that are working on USB SANE backends or other
+ *      applications to use USB scanners.
+ *
+ *  Performance:
+ *
+ *    System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
+ *            300 dpi scan of the entire bed
+ *      24 Bit Color ~ 70 secs - 3.6 Mbit/sec
+ *       8 Bit Gray  ~ 17 secs - 4.2 Mbit/sec
+ */
+
+/* 
+ * Scanner definitions, macros, module info, 
+ * debug/ioctl/data_dump enable, and other constants.
+ */ 
+#include "scanner.h"
+
+static void
+irq_scanner(struct urb *urb)
+{
+
+/*
+ * For the meantime, this is just a placeholder until I figure out what
+ * all I want to do with it -- or somebody else for that matter.
+ */
+
+	struct scn_usb_data *scn = urb->context;
+	unsigned char *data = &scn->button;
+	data += 0;		/* Keep gcc from complaining about unused var */
+
+	if (urb->status) {
+		return;
+	}
+
+	dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data);
+	return;
+}
+
+
+static int
+open_scanner(struct inode * inode, struct file * file)
+{
+	struct scn_usb_data *scn;
+	struct usb_device *dev;
+
+	kdev_t scn_minor;
+
+	scn_minor = USB_SCN_MINOR(inode);
+
+	dbg("open_scanner: scn_minor:%d", scn_minor);
+
+	if (!p_scn_table[scn_minor]) {
+		err("open_scanner(%d): Unable to access minor data", scn_minor);
+		return -ENODEV;
+	}
+
+	scn = p_scn_table[scn_minor];
+
+	dev = scn->scn_dev;
+
+	if (!dev) {
+		err("open_scanner(%d): Scanner device not present", scn_minor);
+		return -ENODEV;
+	}
+
+	if (!scn->present) {
+		err("open_scanner(%d): Scanner is not present", scn_minor);
+		return -ENODEV;
+	}
+
+	if (scn->isopen) {
+		err("open_scanner(%d): Scanner device is already open", scn_minor);
+		return -EBUSY;
+	}
+
+	init_waitqueue_head(&scn->rd_wait_q);
+
+	scn->isopen = 1;
+
+	file->private_data = scn; /* Used by the read and write metheds */
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int
+close_scanner(struct inode * inode, struct file * file)
+{
+	struct scn_usb_data *scn;
+
+	kdev_t scn_minor;
+
+	scn_minor = USB_SCN_MINOR (inode);
+
+	dbg("close_scanner: scn_minor:%d", scn_minor);
+
+	if (!p_scn_table[scn_minor]) {
+		err("close_scanner(%d): invalid scn_minor", scn_minor);
+		return -ENODEV;
+	}
+
+	scn = p_scn_table[scn_minor];
+
+	scn->isopen = 0;
+
+	file->private_data = NULL;
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static ssize_t
+write_scanner(struct file * file, const char * buffer,
+              size_t count, loff_t *ppos)
+{
+	struct scn_usb_data *scn;
+	struct usb_device *dev;
+	
+	ssize_t bytes_written = 0; /* Overall count of bytes written */
+	ssize_t ret = 0;
+
+	kdev_t scn_minor;
+
+	int this_write;		/* Number of bytes to write */
+	int partial;		/* Number of bytes successfully written */
+	int result = 0;
+	
+	char *obuf;
+
+	scn = file->private_data;
+
+	scn_minor = scn->scn_minor;
+
+	obuf = scn->obuf;
+
+	dev = scn->scn_dev;
+
+	file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+
+	while (count > 0) {
+
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+
+		this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
+		
+		if (copy_from_user(scn->obuf, buffer, this_write)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
+		dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial);
+
+		if (result == USB_ST_TIMEOUT) {	/* NAK -- shouldn't happen */
+			warn("write_scanner: NAK recieved.");
+			ret = -ETIME;
+			break;
+		} else if (result < 0) { /* We should not get any I/O errors */
+			warn("write_scanner(%d): funky result: %d. Please notify the maintainer.", scn_minor, result);
+			ret = -EIO;
+			break;
+		} 
+
+#ifdef WR_DATA_DUMP
+		if (partial) {
+			unsigned char cnt, cnt_max;
+			cnt_max = (partial > 24) ? 24 : partial;
+			printk(KERN_DEBUG "dump(%d): ", scn_minor);
+			for (cnt=0; cnt < cnt_max; cnt++) {
+				printk("%X ", obuf[cnt]);
+			}
+			printk("\n");
+		}
+#endif
+		if (partial != this_write) { /* Unable to write all contents of obuf */
+			ret = -EIO;
+			break;
+		}
+
+		if (partial) { /* Data written */
+			buffer += partial;
+			count -= partial;
+			bytes_written += partial;
+		} else { /* No data written */
+			ret = 0;
+			break;
+		}
+	}
+	mdelay(5);		/* This seems to help with SANE queries */
+	return ret ? ret : bytes_written;
+}
+
+static ssize_t
+read_scanner(struct file * file, char * buffer,
+             size_t count, loff_t *ppos)
+{
+	struct scn_usb_data *scn;
+	struct usb_device *dev;
+
+	ssize_t bytes_read;	/* Overall count of bytes_read */
+	ssize_t ret;
+
+	kdev_t scn_minor;
+
+	int partial;		/* Number of bytes successfully read */
+	int this_read;		/* Max number of bytes to read */
+	int result;
+	int rd_expire = RD_EXPIRE;
+
+	char *ibuf;
+
+	scn = file->private_data;
+
+	scn_minor = scn->scn_minor;
+
+	ibuf = scn->ibuf;
+
+	dev = scn->scn_dev;
+
+	bytes_read = 0;
+	ret = 0;
+
+	file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* Update the
+                                                            atime of
+                                                            the device
+                                                            node */
+
+	while (count > 0) {
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+
+		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+		
+		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, RD_NAK_TIMEOUT);
+		dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count);
+
+/* 
+ * Scanners are sometimes inheriently slow since they are mechanical
+ * in nature.  USB bulk reads tend to timeout while the scanner is
+ * positioning, resetting, warming up the lamp, etc if the timeout is
+ * set too low.  A very long timeout parameter for bulk reads was used
+ * to overcome this limitation, but this sometimes resulted in folks
+ * having to wait for the timeout to expire after pressing Ctrl-C from
+ * an application. The user was sometimes left with the impression
+ * that something had hung or crashed when in fact the USB read was
+ * just waiting on data.  So, the below code retains the same long
+ * timeout period, but splits it up into smaller parts so that
+ * Ctrl-C's are acted upon in a reasonable amount of time. 
+ */
+
+		if (result == USB_ST_TIMEOUT && !partial) { /* Timeout
+                                                               and no
+                                                               data */
+			if (--rd_expire <= 0) {
+				warn("read_scanner(%d): excessive NAK's received", scn_minor);
+				ret = -ETIME;
+				break;
+			} else {
+				interruptible_sleep_on_timeout(&scn->rd_wait_q, RD_NAK_TIMEOUT);
+				continue;
+			}
+		} else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
+			warn("read_scanner(%d): funky result:%d. Please notify the maintainer.", scn_minor, (int)result);
+			ret = -EIO;
+			break;
+		}
+
+#ifdef RD_DATA_DUMP
+		if (partial) {
+			unsigned char cnt, cnt_max;
+			cnt_max = (partial > 24) ? 24 : partial;
+			printk(KERN_DEBUG "dump(%d): ", scn_minor);
+			for (cnt=0; cnt < cnt_max; cnt++) {
+				printk("%X ", ibuf[cnt]);
+			}
+			printk("\n");
+		}
+#endif
+
+		if (partial) { /* Data returned */
+			if (copy_to_user(buffer, ibuf, partial)) {
+				ret = -EFAULT;
+				break;
+			}
+			count -= this_read; /* Compensate for short reads */
+			bytes_read += partial; /* Keep tally of what actually was read */
+			buffer += partial;
+		} else {
+			ret = 0;
+			break;
+		}
+	}
+	
+	return ret ? ret : bytes_read;
+}
+
+static void *
+probe_scanner(struct usb_device *dev, unsigned int ifnum)
+{
+	struct scn_usb_data *scn;
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	
+	int ep_cnt;
+	int ix;
+
+	kdev_t scn_minor;
+
+	char valid_device = 0;
+	char have_bulk_in, have_bulk_out, have_intr;
+
+	if (vendor != -1 && product != -1) {
+		info("probe_scanner: User specified USB scanner -- Vendor:Product - %x:%x", vendor, product);
+	}
+
+	dbg("probe_scanner: USB dev address:%p", dev);
+	dbg("probe_scanner: ifnum:%u", ifnum);
+
+/*
+ * 1. Check Vendor/Product
+ * 2. Determine/Assign Bulk Endpoints
+ * 3. Determine/Assign Intr Endpoint
+ */
+
+/* 
+ * There doesn't seem to be an imaging class defined in the USB
+ * Spec. (yet).  If there is, HP isn't following it and it doesn't
+ * look like anybody else is either.  Therefore, we have to test the
+ * Vendor and Product ID's to see what we have.  Also, other scanners
+ * may be able to use this driver by specifying both vendor and
+ * product ID's as options to the scanner module in conf.modules.
+ *
+ * NOTE: Just because a product is supported here does not mean that
+ * applications exist that support the product.  It's in the hopes
+ * that this will allow developers a means to produce applications
+ * that will support USB products.
+ *
+ * Until we detect a device which is pleasing, we silently punt.
+ */
+
+	for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct scanner_device); ix++) {
+		if ((dev->descriptor.idVendor == scanner_device_ids [ix].idVendor) &&
+		    (dev->descriptor.idProduct == scanner_device_ids [ix].idProduct)) {
+			valid_device = 1;
+			break;
+                }
+	}
+	if (dev->descriptor.idVendor == vendor &&   /* User specified */
+	    dev->descriptor.idProduct == product) { /* User specified */
+		valid_device = 1;
+	}
+	
+        if (!valid_device)
+                return NULL;    /* We didn't find anything pleasing */
+
+/*
+ * After this point we can be a little noisy about what we are trying to
+ *  configure.
+ */
+
+	if (dev->descriptor.bNumConfigurations != 1) {
+		info("probe_scanner: Only one device configuration is supported.");
+		return NULL;
+	}
+
+	if (dev->config[0].bNumInterfaces != 1) {
+		info("probe_scanner: Only one device interface is supported.");
+		return NULL;
+	}
+
+	interface = dev->config[0].interface[ifnum].altsetting;
+	endpoint = interface[ifnum].endpoint;
+
+/* 
+ * Start checking for two bulk endpoints OR two bulk endpoints *and* one
+ * interrupt endpoint. If we have an interrupt endpoint go ahead and
+ * setup the handler. FIXME: This is a future enhancement...
+ */
+
+	dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints);
+
+	if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) {
+		info("probe_scanner: Only two or three endpoints supported.");
+		return NULL;
+	}
+
+	ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0;
+
+	while (ep_cnt < interface->bNumEndpoints) {
+
+		if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) {
+			ep_cnt++;
+			have_bulk_in = ep_cnt;
+			dbg("probe_scanner: bulk_in_ep:%d", have_bulk_in);
+			continue;
+		}
+		
+		if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
+			ep_cnt++;
+			have_bulk_out = ep_cnt;
+			dbg("probe_scanner: bulk_out_ep:%d", have_bulk_out);
+			continue;
+		}
+
+		if (!have_intr && IS_EP_INTR(endpoint[ep_cnt])) {
+			ep_cnt++;
+			have_intr = ep_cnt;
+			dbg("probe_scanner: intr_ep:%d", have_intr);
+			continue;
+		}
+		info("probe_scanner: Undetected endpoint. Notify the maintainer.");
+		return NULL;	/* Shouldn't ever get here unless we have something weird */
+	}
+
+
+/*
+ * Perform a quick check to make sure that everything worked as it
+ * should have.
+ */
+
+	switch(interface->bNumEndpoints) {
+	case 2:
+		if (!have_bulk_in || !have_bulk_out) {
+			info("probe_scanner: Two bulk endpoints required.");
+			return NULL;
+		}
+		break;
+	case 3:
+		if (!have_bulk_in || !have_bulk_out || !have_intr) {
+			info("probe_scanner: Two bulk endpoints and one interrupt endpoint required.");
+			return NULL;
+		}
+		break;
+	default:
+		info("probe_scanner: Endpoint determination failed.  Notify the maintainer.");
+		return NULL;
+	}
+
+
+/* 
+ * Determine a minor number and initialize the structure associated
+ * with it.  The problem with this is that we are counting on the fact
+ * that the user will sequentially add device nodes for the scanner
+ * devices.  */
+
+	for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
+		if (!p_scn_table[scn_minor])
+			break;
+	}
+
+/* Check to make sure that the last slot isn't already taken */
+	if (p_scn_table[scn_minor]) {
+		err("probe_scanner: No more minor devices remaining.");
+		return NULL;
+	}
+
+	dbg("probe_scanner: Allocated minor:%d", scn_minor);
+
+	if (!(scn = kmalloc (sizeof (struct scn_usb_data), GFP_KERNEL))) {
+		err("probe_scanner: Out of memory.");
+		return NULL;
+	}
+	memset (scn, 0, sizeof(struct scn_usb_data));
+	dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn);
+
+
+/* Ok, if we detected an interrupt EP, setup a handler for it */
+	if (have_intr) {
+		dbg("probe_scanner(%d): Configuring IRQ handler for intr EP:%d", scn_minor, have_intr);
+		FILL_INT_URB(&scn->scn_irq, dev, 
+			     usb_rcvintpipe(dev, have_intr),
+			     &scn->button, 1, irq_scanner, scn,
+			     // endpoint[(int)have_intr].bInterval);
+			     250);
+
+	        if (usb_submit_urb(&scn->scn_irq)) {
+			err("probe_scanner(%d): Unable to allocate INT URB.", scn_minor);
+                	kfree(scn);
+                	return NULL;
+        	}
+	}
+
+
+/* Ok, now initialize all the relevant values */
+	if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+		err("probe_scanner(%d): Not enough memory for the output buffer.", scn_minor);
+		kfree(scn);
+		return NULL;
+	}
+	dbg("probe_scanner(%d): obuf address:%p", scn_minor, scn->obuf);
+
+	if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+		err("probe_scanner(%d): Not enough memory for the input buffer.", scn_minor);
+		kfree(scn->obuf);
+		kfree(scn);
+		return NULL;
+	}
+	dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf);
+
+	scn->bulk_in_ep = have_bulk_in;
+	scn->bulk_out_ep = have_bulk_out;
+	scn->intr_ep = have_intr;
+	scn->present = 1;
+	scn->scn_dev = dev;
+	scn->scn_minor = scn_minor;
+	scn->isopen = 0;
+
+	return p_scn_table[scn_minor] = scn;
+}
+
+static void
+disconnect_scanner(struct usb_device *dev, void *ptr)
+{
+	struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
+	
+	if(scn->intr_ep) {
+		dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor);
+		usb_unlink_urb(&scn->scn_irq);
+	}
+        usb_driver_release_interface(&scanner_driver,
+                &scn->scn_dev->actconfig->interface[scn->ifnum]);
+
+	kfree(scn->ibuf);
+	kfree(scn->obuf);
+
+	dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
+	p_scn_table[scn->scn_minor] = NULL;
+	kfree (scn);
+}
+
+#ifdef SCN_IOCTL
+static int
+ioctl_scanner(struct inode *inode, struct file *file,
+	      unsigned int cmd, unsigned long arg)
+{
+	struct usb_device *dev;
+	
+	int result;
+
+	kdev_t scn_minor;
+	
+	scn_minor = USB_SCN_MINOR(inode);
+
+	if (!p_scn_table[scn_minor]) {
+		err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
+		return -ENODEV;
+	}
+
+	dev = p_scn_table[scn_minor]->scn_dev;
+	
+	switch (cmd)
+	{
+	case PV8630_IOCTL_INREQUEST :
+	{
+		struct {
+			__u8  data;
+			__u8  request;
+			__u16 value;
+			__u16 index;
+		} args;
+		
+		if (copy_from_user(&args, (void *)arg, sizeof(args)))
+			return -EFAULT;
+		
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+					 args.request, USB_TYPE_VENDOR|
+					 USB_RECIP_DEVICE|USB_DIR_IN,
+					 args.value, args.index, &args.data,
+					 1, HZ*5);
+
+		dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index, args.request);
+
+		if (copy_to_user((void *)arg, &args, sizeof(args)))
+			return -EFAULT;
+
+		dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result);
+		
+		return result;
+	}
+	case PV8630_IOCTL_OUTREQUEST :
+	{
+		struct {
+			__u8  request;
+			__u16 value;
+			__u16 index;
+		} args;
+		
+		if (copy_from_user(&args, (void *)arg, sizeof(args)))
+			return -EFAULT;
+		
+		dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x args.request:%x\n", scn_minor, args.value, args.index, args.request);
+
+		result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+					 args.request, USB_TYPE_VENDOR|
+					 USB_RECIP_DEVICE|USB_DIR_OUT,
+					 args.value, args.index, NULL,
+					 0, HZ*5);
+
+		dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result);
+		
+		return result;
+	}
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+#endif /* SCN_IOCTL */
+
+static struct
+file_operations usb_scanner_fops = {
+	read:		read_scanner,
+	write:		write_scanner,
+#ifdef SCN_IOCTL
+	ioctl:		ioctl_scanner,
+#endif /* SCN_IOCTL */
+	open:		open_scanner,
+	release:	close_scanner,
+};
+
+static struct
+usb_driver scanner_driver = {
+       "usbscanner",
+       probe_scanner,
+       disconnect_scanner,
+       { NULL, NULL },
+       &usb_scanner_fops,
+       SCN_BASE_MNR
+};
+
+void __exit
+usb_scanner_exit(void)
+{
+	usb_deregister(&scanner_driver);
+}
+
+int __init
+usb_scanner_init (void)
+{
+        if (usb_register(&scanner_driver) < 0)
+                return -1;
+
+	info("USB Scanner support registered.");
+	return 0;
+}
+
+module_init(usb_scanner_init);
+module_exit(usb_scanner_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/scanner.h linux.18p17/drivers/usb/scanner.h
--- linux.vanilla/drivers/usb/scanner.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/scanner.h	Thu Oct 19 01:49:58 2000
@@ -0,0 +1,166 @@
+/*
+ * Driver for USB Scanners (linux-2.4.0test1-ac7)
+ *
+ * Copyright (C) 1999, 2000 David E. Nelson
+ *
+ * David E. Nelson (dnelson@jump.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */ 
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+
+// #define DEBUG
+
+#include <linux/usb.h>
+
+static __s32 vendor=-1, product=-1;
+
+MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");
+MODULE_DESCRIPTION("USB Scanner Driver");
+
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+
+/* Enable to activate the ioctl interface.  This is mainly meant for */
+/* development purposes until an ioctl number is officially registered */
+// #define SCN_IOCTL
+
+/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */
+// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
+// #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
+
+#define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
+#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+#define IS_EP_INTR(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_INT ? 1 : 0)
+
+#define USB_SCN_MINOR(X) MINOR((X)->i_rdev) - SCN_BASE_MNR
+
+#ifdef DEBUG
+#define SCN_DEBUG(X) X
+#else
+#define SCN_DEBUG(X)
+#endif
+
+#define IBUF_SIZE 32768
+#define OBUF_SIZE 4096
+
+/* read_scanner timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */
+#define RD_NAK_TIMEOUT (10*HZ)	/* Number of X seconds to wait */
+#define RD_EXPIRE 12		/* Number of attempts to wait X seconds */
+
+
+/* FIXME: These are NOT registered ioctls()'s */
+#define PV8630_IOCTL_INREQUEST 69
+#define PV8630_IOCTL_OUTREQUEST 70
+
+#define SCN_MAX_MNR 16		/* We're allocated 16 minors */
+#define SCN_BASE_MNR 48		/* USB Scanners start at minor 48 */
+
+struct scn_usb_data {
+	struct usb_device *scn_dev;
+	struct urb scn_irq;
+	unsigned int ifnum;	/* Interface number of the USB device */
+	kdev_t scn_minor;	/* Scanner minor - used in disconnect() */
+	unsigned char button;	/* Front panel buffer */
+        char isopen;		/* Not zero if the device is open */
+	char present;		/* Not zero if device is present */
+	char *obuf, *ibuf;	/* transfer buffers */
+	char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
+	wait_queue_head_t rd_wait_q; /* read timeouts */
+};
+
+static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */};
+
+/* table of scanners that may work with this driver */
+static const struct scanner_device {
+	__u16		idVendor;
+	__u16		idProduct;
+} scanner_device_ids [] = {
+	/* Acer */
+		{ 0x04a5, 0x2060 },	/* Prisa Acerscan 620U & 640U (!) */
+		{ 0x04a5, 0x2040 },	/* Prisa AcerScan 620U (!) */
+	/* Agfa */
+		{ 0x06bd, 0x0001 },	/* SnapScan 1212U */
+		{ 0x06bd, 0x2061 },	/* Another SnapScan 1212U (?) */
+		{ 0x06bd, 0x0100 },	/* SnapScan Touch */
+	/* Colorado -- See Primax/Colorado below */
+	/* Epson -- See Seiko/Epson below */
+	/* Genius */
+		{ 0x0458, 0x2001 },	/* ColorPage-Vivid Pro */
+	/* Hewlett Packard */
+		{ 0x03f0, 0x0205 },	/* 3300C */
+		{ 0x03f0, 0x0101 },	/* 4100C */
+		{ 0x03f0, 0x0105 },	/* 4200C */
+		{ 0x03f0, 0x0102 },	/* PhotoSmart S20 */
+		{ 0x03f0, 0x0401 },	/* 5200C */
+		{ 0x03f0, 0x0201 },	/* 6200C */
+		{ 0x03f0, 0x0601 },	/* 6300C */
+	/* iVina */
+		{ 0x0638, 0x0268 },     /* 1200U */
+	/* Microtek */
+		{ 0x05da, 0x0099 },	/* ScanMaker X6 - X6U */
+		{ 0x05da, 0x0094 },	/* Phantom 336CX - C3 */
+		{ 0x05da, 0x00a0 },	/* Phantom 336CX - C3 #2 */
+		{ 0x05da, 0x009a },	/* Phantom C6 */
+		{ 0x05da, 0x00a3 },	/* ScanMaker V6USL */
+		{ 0x05da, 0x80a3 },	/* ScanMaker V6USL #2 */
+		{ 0x05da, 0x80ac },	/* ScanMaker V6UL - SpicyU */
+	/* Mustek */
+		{ 0x055f, 0x0001 },	/* 1200 CU */
+	/* Primax/Colorado */
+		{ 0x0461, 0x0300 },	/* G2-300 #1 */
+		{ 0x0461, 0x0380 },	/* G2-600 #1 */
+		{ 0x0461, 0x0301 },	/* G2E-300 #1 */
+		{ 0x0461, 0x0381 },	/* ReadyScan 636i */
+		{ 0x0461, 0x0302 },	/* G2-300 #2 */
+		{ 0x0461, 0x0382 },	/* G2-600 #2 */
+		{ 0x0461, 0x0303 },	/* G2E-300 #2 */
+		{ 0x0461, 0x0383 },	/* G2E-600 */
+		{ 0x0461, 0x0340 },	/* Colorado USB 9600 */
+		{ 0x0461, 0x0360 },	/* Colorado USB 19200 */
+		{ 0x0461, 0x0341 },	/* Colorado 600u */
+		{ 0x0461, 0x0361 },	/* Colorado 1200u */
+	/* Seiko/Epson Corp. */
+		{ 0x04b8, 0x0101 },	/* Perfection 636U and 636Photo */
+		{ 0x04b8, 0x0103 },	/* Perfection 610 */
+		{ 0x04b8, 0x0104 },	/* Perfection 1200U and 1200Photo */
+	/* Umax */
+		{ 0x1606, 0x0010 },	/* Astra 1220U */
+		{ 0x1606, 0x0002 },	/* Astra 1236U */
+		{ 0x1606, 0x0030 },	/* Astra 2000U */
+		{ 0x1606, 0x0230 },	/* Astra 2200U */
+	/* Visioneer */
+		{ 0x04a7, 0x0221 },	/* OneTouch 5300 */
+		{ 0x04a7, 0x0221 },	/* OneTouch 7600 duplicate ID (!) */
+		{ 0x04a7, 0x0231 },	/* 6100 */
+};
+
+/* Forward declarations */
+static struct usb_driver scanner_driver;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/Makefile linux.18p17/drivers/usb/serial/Makefile
--- linux.vanilla/drivers/usb/serial/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/Makefile	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,37 @@
+#
+# Makefile for the USB serial device drivers.
+#
+
+O_TARGET	:= usb-serial.o
+M_OBJS		:=
+O_OBJS		:=
+MOD_LIST_NAME	:= USB_SERIAL_MODULES
+
+# Object file lists.
+
+obj-y	:=
+obj-m	:=
+obj-n	:=
+obj-	:=
+
+obj-$(CONFIG_USB_SERIAL)			+= usbserial.o
+obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
+obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
+obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN)		+= keyspan.o
+obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
+obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT)	+= digi_acceleport.o
+ 
+# Objects that export symbols.
+export-objs	:= usbserial.o
+
+# Translate to Rules.make lists.
+
+O_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
+
+include $(TOPDIR)/Rules.make
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/Makefile-keyspan_pda_fw linux.18p17/drivers/usb/serial/Makefile-keyspan_pda_fw
--- linux.vanilla/drivers/usb/serial/Makefile-keyspan_pda_fw	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/Makefile-keyspan_pda_fw	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,16 @@
+
+# some rules to handle the quirks of the 'as31' assembler, like
+# insisting upon fixed suffixes for the input and output files,
+# and its lack of preprocessor support
+
+all: keyspan_pda_fw.h
+
+%.asm: %.S
+	gcc -x assembler-with-cpp -P -E -o $@ $<
+
+%.hex: %.asm
+	as31 -l $<
+	mv $*.obj $@
+
+%_fw.h: %.hex ezusb_convert.pl
+	perl ezusb_convert.pl $* < $< > $@
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/digi_acceleport.c linux.18p17/drivers/usb/serial/digi_acceleport.c
--- linux.vanilla/drivers/usb/serial/digi_acceleport.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/digi_acceleport.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,2038 @@
+/*
+*  Digi AccelePort USB-4 Serial Converter
+*
+*  Copyright 2000 by Digi International
+*
+*  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.
+*
+*  Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's
+*  usb-serial driver.
+*
+*  Peter Berger (pberger@brimson.com)
+*  Al Borchers (borchers@steinerpoint.com)
+*
+*  (8/8/2000) pberger and borchers
+*    -- Fixed close so that 
+*       - it can timeout while waiting for transmit idle, if needed;
+*       - it ignores interrupts when flushing the port, turning
+*         of modem signalling, and so on;
+*       - it waits for the flush to really complete before returning.
+*    -- Read_bulk_callback and write_bulk_callback check for a closed
+*       port before using the tty struct or writing to the port.
+*    -- The two changes above fix the oops caused by interrupted closes.
+*    -- Added interruptible args to write_oob_command and set_modem_signals
+*       and added a timeout arg to transmit_idle; needed for fixes to
+*       close.
+*    -- Added code for rx_throttle and rx_unthrottle so that input flow
+*       control works.
+*    -- Added code to set overrun, parity, framing, and break errors
+*       (untested).
+*    -- Set USB_DISABLE_SPD flag for write bulk urbs, so no 0 length
+*       bulk writes are done.  These hung the Digi USB device.  The
+*       0 length bulk writes were a new feature of usb-uhci added in
+*       the 2.4.0-test6 kernels.
+*    -- Fixed mod inc race in open; do mod inc before sleeping to wait
+*       for a close to finish.
+*
+*  (7/31/2000) pberger
+*    -- Fixed bugs with hardware handshaking:
+*       - Added code to set/clear tty->hw_stopped in digi_read_oob_callback()
+*         and digi_set_termios()
+*    -- Added code in digi_set_termios() to
+*       - add conditional in code handling transition from B0 to only
+*         set RTS if RTS/CTS flow control is either not in use or if
+*         the port is not currently throttled.
+*       - handle turning off CRTSCTS.
+*
+*  (7/30/2000) borchers
+*    -- Added support for more than one Digi USB device by moving
+*       globals to a private structure in the pointed to from the
+*       usb_serial structure.
+*    -- Moved the modem change and transmit idle wait queues into
+*       the port private structure, so each port has its own queue
+*       rather than sharing global queues.
+*    -- Added support for break signals.
+*
+*  (7/25/2000) pberger
+*    -- Added USB-2 support.  Note: the USB-2 supports 3 devices: two
+*       serial and a parallel port.  The parallel port is implemented
+*       as a serial-to-parallel converter.  That is, the driver actually
+*       presents all three USB-2 interfaces as serial ports, but the third
+*       one physically connects to a parallel device.  Thus, for example,
+*       one could plug a parallel printer into the USB-2's third port,
+*       but from the kernel's (and userland's) point of view what's
+*       actually out there is a serial device.
+*
+*  (7/15/2000) borchers
+*    -- Fixed race in open when a close is in progress.
+*    -- Keep count of opens and dec the module use count for each
+*       outstanding open when shutdown is called (on disconnect).
+*    -- Fixed sanity checks in read_bulk_callback and write_bulk_callback
+*       so pointers are checked before use.
+*    -- Split read bulk callback into in band and out of band
+*       callbacks, and no longer restart read chains if there is
+*       a status error or a sanity error.  This fixed the seg
+*       faults and other errors we used to get on disconnect.
+*    -- Port->active is once again a flag as usb-serial intended it
+*       to be, not a count.  Since it was only a char it would
+*       have been limited to 256 simultaneous opens.  Now the open
+*       count is kept in the port private structure in dp_open_count.
+*    -- Added code for modularization of the digi_acceleport driver.
+*
+*  (6/27/2000) pberger and borchers
+*    -- Zeroed out sync field in the wakeup_task before first use;
+*       otherwise the uninitialized value might prevent the task from
+*       being scheduled.
+*    -- Initialized ret value to 0 in write_bulk_callback, otherwise
+*       the uninitialized value could cause a spurious debugging message.
+*
+*  (6/22/2000) pberger and borchers
+*    -- Made cond_wait_... inline--apparently on SPARC the flags arg
+*       to spin_lock_irqsave cannot be passed to another function
+*       to call spin_unlock_irqrestore.  Thanks to Pauline Middelink.
+*    -- In digi_set_modem_signals the inner nested spin locks use just
+*       spin_lock() rather than spin_lock_irqsave().  The old code
+*       mistakenly left interrupts off.  Thanks to Pauline Middelink.
+*    -- copy_from_user (which can sleep) is no longer called while a
+*       spinlock is held.  We copy to a local buffer before getting
+*       the spinlock--don't like the extra copy but the code is simpler.
+*    -- Printk and dbg are no longer called while a spin lock is held.
+*
+*  (6/4/2000) pberger and borchers
+*    -- Replaced separate calls to spin_unlock_irqrestore and
+*       interruptible_sleep_on_timeout with a new function
+*       cond_wait_interruptible_timeout_irqrestore.  This eliminates
+*       the race condition where the wake up could happen after
+*       the unlock and before the sleep.
+*    -- Close now waits for output to drain.
+*    -- Open waits until any close in progress is finished.
+*    -- All out of band responses are now processed, not just the
+*       first in a USB packet.
+*    -- Fixed a bug that prevented the driver from working when the
+*       first Digi port was not the first USB serial port--the driver
+*       was mistakenly using the external USB serial port number to
+*       try to index into its internal ports.
+*    -- Fixed an SMP bug -- write_bulk_callback is called directly from
+*       an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are
+*       needed for locks outside write_bulk_callback that are also
+*       acquired by write_bulk_callback to prevent deadlocks.
+*    -- Fixed support for select() by making digi_chars_in_buffer()
+*       return 256 when -EINPROGRESS is set, as the line discipline
+*       code in n_tty.c expects.
+*    -- Fixed an include file ordering problem that prevented debugging
+*       messages from working.
+*    -- Fixed an intermittent timeout problem that caused writes to
+*       sometimes get stuck on some machines on some kernels.  It turns
+*       out in these circumstances write_chan() (in n_tty.c) was
+*       asleep waiting for our wakeup call.  Even though we call
+*       wake_up_interruptible() in digi_write_bulk_callback(), there is
+*       a race condition that could cause the wakeup to fail: if our
+*       wake_up_interruptible() call occurs between the time that our
+*       driver write routine finishes and write_chan() sets current->state
+*       to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state
+*       to TASK_RUNNING will be lost and write_chan's subsequent call to
+*       schedule() will never return (unless it catches a signal).
+*       This race condition occurs because write_bulk_callback() (and thus
+*       the wakeup) are called asynchonously from an interrupt, rather than
+*       from the scheduler.  We can avoid the race by calling the wakeup
+*       from the scheduler queue and that's our fix:  Now, at the end of
+*       write_bulk_callback() we queue up a wakeup call on the scheduler
+*       task queue.  We still also invoke the wakeup directly since that
+*       squeezes a bit more performance out of the driver, and any lost
+*       race conditions will get cleaned up at the next scheduler run.
+*
+*       NOTE:  The problem also goes away if you comment out
+*       the two code lines in write_chan() where current->state
+*       is set to TASK_RUNNING just before calling driver.write() and to
+*       TASK_INTERRUPTIBLE immediately afterwards.  This is why the
+*       problem did not show up with the 2.2 kernels -- they do not
+*       include that code.
+*
+*  (5/16/2000) pberger and borchers
+*    -- Added timeouts to sleeps, to defend against lost wake ups.
+*    -- Handle transition to/from B0 baud rate in digi_set_termios.
+*
+*  (5/13/2000) pberger and borchers
+*    -- All commands now sent on out of band port, using
+*       digi_write_oob_command.
+*    -- Get modem control signals whenever they change, support TIOCMGET/
+*       SET/BIS/BIC ioctls.
+*    -- digi_set_termios now supports parity, word size, stop bits, and
+*       receive enable.
+*    -- Cleaned up open and close, use digi_set_termios and
+*       digi_write_oob_command to set port parameters.
+*    -- Added digi_startup_device to start read chains on all ports.
+*    -- Write buffer is only used when count==1, to be sure put_char can
+*       write a char (unless the buffer is full).
+*
+*  (5/10/2000) pberger and borchers
+*    -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close.
+*    -- Fixed problem where the first incoming character is lost on
+*       port opens after the first close on that port.  Now we keep
+*       the read_urb chain open until shutdown.
+*    -- Added more port conditioning calls in digi_open and digi_close.
+*    -- Convert port->active to a use count so that we can deal with multiple
+*       opens and closes properly.
+*    -- Fixed some problems with the locking code.
+*
+*  (5/3/2000) pberger and borchers
+*    -- First alpha version of the driver--many known limitations and bugs.
+*
+*
+*  Locking and SMP
+*
+*  - Each port, including the out-of-band port, has a lock used to
+*    serialize all access to the port's private structure.
+*  - The port lock is also used to serialize all writes and access to
+*    the port's URB.
+*  - The port lock is also used for the port write_wait condition
+*    variable.  Holding the port lock will prevent a wake up on the
+*    port's write_wait; this can be used with cond_wait_... to be sure
+*    the wake up is not lost in a race when dropping the lock and
+*    sleeping waiting for the wakeup.
+*  - digi_write() does not sleep, since it is sometimes called on
+*    interrupt time.
+*  - digi_write_bulk_callback() and digi_read_bulk_callback() are
+*    called directly from interrupts.  Hence spin_lock_irqsave()
+*    and spin_lock_irqrestore() are used in the rest of the code
+*    for any locks they acquire.
+*  - digi_write_bulk_callback() gets the port lock before waking up
+*    processes sleeping on the port write_wait.  It also schedules
+*    wake ups so they happen from the scheduler, because the tty
+*    system can miss wake ups from interrupts.
+*  - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to
+*    recheck the condition they are sleeping on.  This is defensive,
+*    in case a wake up is lost.
+*  - Following Documentation/DocBook/kernel-locking.pdf no spin locks
+*    are held when calling copy_to/from_user or printk.
+*    
+*  $Id: digi_acceleport.c,v 1.80 2000/08/09 06:36:18 root Exp $
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/tqueue.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "usb-serial.h"
+
+
+/* Defines */
+
+/* port output buffer length -- must be <= transfer buffer length - 2 */
+/* so we can be sure to send the full buffer in one urb */
+#define DIGI_OUT_BUF_SIZE		8
+
+/* port input buffer length -- must be >= transfer buffer length - 3 */
+/* so we can be sure to hold at least one full buffer from one urb */
+#define DIGI_IN_BUF_SIZE		64
+
+/* retry timeout while sleeping */
+#define DIGI_RETRY_TIMEOUT		(HZ/10)
+
+/* timeout while waiting for tty output to drain in close */
+/* this delay is used twice in close, so the total delay could */
+/* be twice this value */
+#define DIGI_CLOSE_TIMEOUT		(5*HZ)
+
+
+/* AccelePort USB Defines */
+
+/* ids */
+#define DIGI_VENDOR_ID			0x05c5
+#define DIGI_2_ID			0x0002	/* USB-2 */
+#define DIGI_4_ID			0x0004	/* USB-4 */
+
+/* commands
+ * "INB": can be used on the in-band endpoint
+ * "OOB": can be used on the out-of-band endpoint
+ */
+#define DIGI_CMD_SET_BAUD_RATE			0	/* INB, OOB */
+#define DIGI_CMD_SET_WORD_SIZE			1	/* INB, OOB */
+#define DIGI_CMD_SET_PARITY			2	/* INB, OOB */
+#define DIGI_CMD_SET_STOP_BITS			3	/* INB, OOB */
+#define DIGI_CMD_SET_INPUT_FLOW_CONTROL		4	/* INB, OOB */
+#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL	5	/* INB, OOB */
+#define DIGI_CMD_SET_DTR_SIGNAL			6	/* INB, OOB */
+#define DIGI_CMD_SET_RTS_SIGNAL			7	/* INB, OOB */
+#define DIGI_CMD_READ_INPUT_SIGNALS		8	/*      OOB */
+#define DIGI_CMD_IFLUSH_FIFO			9	/*      OOB */
+#define DIGI_CMD_RECEIVE_ENABLE			10	/* INB, OOB */
+#define DIGI_CMD_BREAK_CONTROL			11	/* INB, OOB */
+#define DIGI_CMD_LOCAL_LOOPBACK			12	/* INB, OOB */
+#define DIGI_CMD_TRANSMIT_IDLE			13	/* INB, OOB */
+#define DIGI_CMD_READ_UART_REGISTER		14	/*      OOB */
+#define DIGI_CMD_WRITE_UART_REGISTER		15	/* INB, OOB */
+#define DIGI_CMD_AND_UART_REGISTER		16	/* INB, OOB */
+#define DIGI_CMD_OR_UART_REGISTER		17	/* INB, OOB */
+#define DIGI_CMD_SEND_DATA			18	/* INB      */
+#define DIGI_CMD_RECEIVE_DATA			19	/* INB      */
+#define DIGI_CMD_RECEIVE_DISABLE		20	/* INB      */
+#define DIGI_CMD_GET_PORT_TYPE			21	/*      OOB */
+
+/* baud rates */
+#define DIGI_BAUD_50				0
+#define DIGI_BAUD_75				1
+#define DIGI_BAUD_110				2
+#define DIGI_BAUD_150				3
+#define DIGI_BAUD_200				4
+#define DIGI_BAUD_300				5
+#define DIGI_BAUD_600				6
+#define DIGI_BAUD_1200				7
+#define DIGI_BAUD_1800				8
+#define DIGI_BAUD_2400				9
+#define DIGI_BAUD_4800				10
+#define DIGI_BAUD_7200				11
+#define DIGI_BAUD_9600				12
+#define DIGI_BAUD_14400				13
+#define DIGI_BAUD_19200				14
+#define DIGI_BAUD_28800				15
+#define DIGI_BAUD_38400				16
+#define DIGI_BAUD_57600				17
+#define DIGI_BAUD_76800				18
+#define DIGI_BAUD_115200			19
+#define DIGI_BAUD_153600			20
+#define DIGI_BAUD_230400			21
+#define DIGI_BAUD_460800			22
+
+/* arguments */
+#define DIGI_WORD_SIZE_5			0
+#define DIGI_WORD_SIZE_6			1
+#define DIGI_WORD_SIZE_7			2
+#define DIGI_WORD_SIZE_8			3
+
+#define DIGI_PARITY_NONE			0
+#define DIGI_PARITY_ODD				1
+#define DIGI_PARITY_EVEN			2
+#define DIGI_PARITY_MARK			3
+#define DIGI_PARITY_SPACE			4
+
+#define DIGI_STOP_BITS_1			0
+#define DIGI_STOP_BITS_2			1
+
+#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF	1
+#define DIGI_INPUT_FLOW_CONTROL_RTS		2
+#define DIGI_INPUT_FLOW_CONTROL_DTR		4
+
+#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF	1
+#define DIGI_OUTPUT_FLOW_CONTROL_CTS		2
+#define DIGI_OUTPUT_FLOW_CONTROL_DSR		4
+
+#define DIGI_DTR_INACTIVE			0
+#define DIGI_DTR_ACTIVE				1
+#define DIGI_DTR_INPUT_FLOW_CONTROL		2
+
+#define DIGI_RTS_INACTIVE			0
+#define DIGI_RTS_ACTIVE				1
+#define DIGI_RTS_INPUT_FLOW_CONTROL		2
+#define DIGI_RTS_TOGGLE				3
+
+#define DIGI_FLUSH_TX				1
+#define DIGI_FLUSH_RX				2
+#define DIGI_RESUME_TX				4 /* clears xoff condition */
+
+#define DIGI_TRANSMIT_NOT_IDLE			0
+#define DIGI_TRANSMIT_IDLE			1
+
+#define DIGI_DISABLE				0
+#define DIGI_ENABLE				1
+
+#define DIGI_DEASSERT				0
+#define DIGI_ASSERT				1
+
+/* in band status codes */
+#define DIGI_OVERRUN_ERROR			4
+#define DIGI_PARITY_ERROR			8
+#define DIGI_FRAMING_ERROR			16
+#define DIGI_BREAK_ERROR			32
+
+/* out of band status */
+#define DIGI_NO_ERROR				0
+#define DIGI_BAD_FIRST_PARAMETER		1
+#define DIGI_BAD_SECOND_PARAMETER		2
+#define DIGI_INVALID_LINE			3
+#define DIGI_INVALID_OPCODE			4
+
+/* input signals */
+#define DIGI_READ_INPUT_SIGNALS_SLOT		1
+#define DIGI_READ_INPUT_SIGNALS_ERR		2
+#define DIGI_READ_INPUT_SIGNALS_BUSY		4
+#define DIGI_READ_INPUT_SIGNALS_PE		8
+#define DIGI_READ_INPUT_SIGNALS_CTS		16
+#define DIGI_READ_INPUT_SIGNALS_DSR		32
+#define DIGI_READ_INPUT_SIGNALS_RI		64
+#define DIGI_READ_INPUT_SIGNALS_DCD		128
+
+
+/* macros */
+#define MAX(a,b)	(((a)>(b))?(a):(b))
+#define MIN(a,b)	(((a)<(b))?(a):(b))
+
+
+/* Structures */
+
+typedef struct digi_serial {
+	spinlock_t ds_serial_lock;
+	struct usb_serial_port *ds_oob_port;	/* out-of-band port */
+	int ds_oob_port_num;			/* index of out-of-band port */
+	int ds_device_started;
+} digi_serial_t;
+
+typedef struct digi_port {
+	spinlock_t dp_port_lock;
+	int dp_port_num;
+	int dp_out_buf_len;
+	unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE];
+	int dp_in_buf_len;
+	unsigned char dp_in_buf[DIGI_IN_BUF_SIZE];
+	unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE];
+	unsigned int dp_modem_signals;
+	wait_queue_head_t dp_modem_change_wait;
+	int dp_open_count;			/* inc on open, dec on close */
+	int dp_transmit_idle;
+	wait_queue_head_t dp_transmit_idle_wait;
+	int dp_throttled;
+	int dp_throttle_restart;
+	wait_queue_head_t dp_flush_wait;
+	int dp_in_close;			/* close in progress */
+	wait_queue_head_t dp_close_wait;	/* wait queue for close */
+	struct tq_struct dp_wakeup_task;
+} digi_port_t;
+
+
+/* Local Function Declarations */
+
+static void digi_wakeup_write( struct usb_serial_port *port );
+static void digi_wakeup_write_lock( struct usb_serial_port *port );
+static int digi_write_oob_command( struct usb_serial_port *port,
+	unsigned char *buf, int count, int interruptible );
+static int digi_write_inb_command( struct usb_serial_port *port,
+	unsigned char *buf, int count, unsigned long timeout );
+static int digi_set_modem_signals( struct usb_serial_port *port,
+	unsigned int modem_signals, int interruptible );
+static int digi_transmit_idle( struct usb_serial_port *port,
+	unsigned long timeout );
+static void digi_rx_throttle (struct usb_serial_port *port);
+static void digi_rx_unthrottle (struct usb_serial_port *port);
+static void digi_set_termios( struct usb_serial_port *port, 
+	struct termios *old_termios );
+static void digi_break_ctl( struct usb_serial_port *port, int break_state );
+static int digi_ioctl( struct usb_serial_port *port, struct file *file,
+	unsigned int cmd, unsigned long arg );
+static int digi_write( struct usb_serial_port *port, int from_user,
+	const unsigned char *buf, int count );
+static void digi_write_bulk_callback( struct urb *urb );
+static int digi_write_room( struct usb_serial_port *port );
+static int digi_chars_in_buffer( struct usb_serial_port *port );
+static int digi_open( struct usb_serial_port *port, struct file *filp );
+static void digi_close( struct usb_serial_port *port, struct file *filp );
+static int digi_startup_device( struct usb_serial *serial );
+static int digi_startup( struct usb_serial *serial );
+static void digi_shutdown( struct usb_serial *serial );
+static void digi_read_bulk_callback( struct urb *urb );
+static int digi_read_inb_callback( struct urb *urb );
+static int digi_read_oob_callback( struct urb *urb );
+
+
+/* Statics */
+
+/* device info needed for the Digi serial converter */
+static u16 digi_vendor_id = DIGI_VENDOR_ID;
+static u16 digi_product_2_id = DIGI_2_ID;	/* USB 2 */
+static u16 digi_product_4_id = DIGI_4_ID;	/* USB 4 */
+
+static struct usb_serial_device_type digi_acceleport_2_device = {
+	name:				"Digi USB",
+	idVendor:			&digi_vendor_id,
+	idProduct:			&digi_product_2_id,
+	needs_interrupt_in:		DONT_CARE,
+	needs_bulk_in:			MUST_HAVE,
+	needs_bulk_out:			MUST_HAVE,
+	num_interrupt_in:		0,
+	num_bulk_in:			4,
+	num_bulk_out:			4,
+	num_ports:			3,
+	open:				digi_open,
+	close:				digi_close,
+	write:				digi_write,
+	write_room:			digi_write_room,
+	write_bulk_callback: 		digi_write_bulk_callback,
+	read_bulk_callback:		digi_read_bulk_callback,
+	chars_in_buffer:		digi_chars_in_buffer,
+	throttle:			digi_rx_throttle,
+	unthrottle:			digi_rx_unthrottle,
+	ioctl:				digi_ioctl,
+	set_termios:			digi_set_termios,
+	break_ctl:			digi_break_ctl,
+	startup:			digi_startup,
+	shutdown:			digi_shutdown,
+};
+
+static struct usb_serial_device_type digi_acceleport_4_device = {
+	name:				"Digi USB",
+	idVendor:			&digi_vendor_id,
+	idProduct:			&digi_product_4_id,
+	needs_interrupt_in:		DONT_CARE,
+	needs_bulk_in:			MUST_HAVE,
+	needs_bulk_out:			MUST_HAVE,
+	num_interrupt_in:		0,
+	num_bulk_in:			5,
+	num_bulk_out:			5,
+	num_ports:			4,
+	open:				digi_open,
+	close:				digi_close,
+	write:				digi_write,
+	write_room:			digi_write_room,
+	write_bulk_callback: 		digi_write_bulk_callback,
+	read_bulk_callback:		digi_read_bulk_callback,
+	chars_in_buffer:		digi_chars_in_buffer,
+	throttle:			digi_rx_throttle,
+	unthrottle:			digi_rx_unthrottle,
+	ioctl:				digi_ioctl,
+	set_termios:			digi_set_termios,
+	break_ctl:			digi_break_ctl,
+	startup:			digi_startup,
+	shutdown:			digi_shutdown,
+};
+
+
+/* Functions */
+
+/*
+*  Cond Wait Interruptible Timeout Irqrestore
+*
+*  Do spin_unlock_irqrestore and interruptible_sleep_on_timeout
+*  so that wake ups are not lost if they occur between the unlock
+*  and the sleep.  In other words, spin_lock_irqrestore and
+*  interruptible_sleep_on_timeout are "atomic" with respect to
+*  wake ups.  This is used to implement condition variables.
+*/
+
+static inline long cond_wait_interruptible_timeout_irqrestore(
+	wait_queue_head_t *q, long timeout,
+	spinlock_t *lock, unsigned long flags )
+{
+
+	wait_queue_t wait;
+
+
+	init_waitqueue_entry( &wait, current );
+
+	set_current_state( TASK_INTERRUPTIBLE );
+
+	add_wait_queue( q, &wait );
+
+	spin_unlock_irqrestore( lock, flags );
+
+	timeout = schedule_timeout(timeout);
+
+	set_current_state( TASK_RUNNING );
+
+	remove_wait_queue( q, &wait );
+
+	return( timeout );
+
+}
+
+
+/*
+*  Digi Wakeup Write
+*
+*  Wake up port, line discipline, and tty processes sleeping
+*  on writes.
+*/
+
+static void digi_wakeup_write_lock( struct usb_serial_port *port )
+{
+
+	unsigned long flags;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+	digi_wakeup_write( port );
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+}
+
+static void digi_wakeup_write( struct usb_serial_port *port )
+{
+
+	struct tty_struct *tty = port->tty;
+
+
+	/* wake up port processes */
+	wake_up_interruptible( &port->write_wait );
+
+	/* wake up line discipline */
+	if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+	&& tty->ldisc.write_wakeup )
+		(tty->ldisc.write_wakeup)(tty);
+
+	/* wake up other tty processes */
+	wake_up_interruptible( &tty->write_wait );
+	/* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
+
+}
+
+
+/*
+*  Digi Write OOB Command
+*
+*  Write commands on the out of band port.  Commands are 4
+*  bytes each, multiple commands can be sent at once, and
+*  no command will be split across USB packets.  Returns 0
+*  if successful, -EINTR if interrupted while sleeping and
+*  the interruptible flag is true, or a negative error
+*  returned by usb_submit_urb.
+*/
+
+static int digi_write_oob_command( struct usb_serial_port *port,
+	unsigned char *buf, int count, int interruptible )
+{
+
+	int ret = 0;
+	int len;
+	struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port;
+	digi_port_t *oob_priv = (digi_port_t *)oob_port->private;
+	unsigned long flags = 0;
+
+
+dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count );
+
+	spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+
+	while( count > 0 ) {
+
+		while( oob_port->write_urb->status == -EINPROGRESS ) {
+			cond_wait_interruptible_timeout_irqrestore(
+				&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+				&oob_priv->dp_port_lock, flags );
+			if( interruptible && signal_pending(current) ) {
+				return( -EINTR );
+			}
+			spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+		}
+
+		/* len must be a multiple of 4, so commands are not split */
+		len = MIN( count, oob_port->bulk_out_size );
+		if( len > 4 )
+			len &= ~3;
+
+		memcpy( oob_port->write_urb->transfer_buffer, buf, len );
+		oob_port->write_urb->transfer_buffer_length = len;
+
+		if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) {
+			count -= len;
+			buf += len;
+		}
+
+	}
+
+	spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+
+	if( ret ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d",
+			ret );
+	}
+
+	return( ret );
+
+}
+
+
+/*
+*  Digi Write In Band Command
+*
+*  Write commands on the given port.  Commands are 4
+*  bytes each, multiple commands can be sent at once, and
+*  no command will be split across USB packets.  If timeout
+*  is non-zero, write in band command will return after
+*  waiting unsuccessfully for the URB status to clear for
+*  timeout ticks.  Returns 0 if successful, or a negative
+*  error returned by digi_write.
+*/
+
+static int digi_write_inb_command( struct usb_serial_port *port,
+	unsigned char *buf, int count, unsigned long timeout )
+{
+
+	int ret = 0;
+	int len;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned char *data = port->write_urb->transfer_buffer;
+	unsigned long flags = 0;
+
+
+dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num,
+count );
+
+	if( timeout )
+		timeout += jiffies;
+	else
+		timeout = ULONG_MAX;
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	while( count > 0 && ret == 0 ) {
+
+		while( port->write_urb->status == -EINPROGRESS
+		&& jiffies < timeout ) {
+			cond_wait_interruptible_timeout_irqrestore(
+				&port->write_wait, DIGI_RETRY_TIMEOUT,
+				&priv->dp_port_lock, flags );
+			if( signal_pending(current) ) {
+				return( -EINTR );
+			}
+			spin_lock_irqsave( &priv->dp_port_lock, flags );
+		}
+
+		/* len must be a multiple of 4 and small enough to */
+		/* guarantee the write will send buffered data first, */
+		/* so commands are in order with data and not split */
+		len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len );
+		if( len > 4 )
+			len &= ~3;
+
+		/* write any buffered data first */
+		if( priv->dp_out_buf_len > 0 ) {
+			data[0] = DIGI_CMD_SEND_DATA;
+			data[1] = priv->dp_out_buf_len;
+			memcpy( data+2, priv->dp_out_buf,
+				priv->dp_out_buf_len );
+			memcpy( data+2+priv->dp_out_buf_len, buf, len );
+			port->write_urb->transfer_buffer_length
+				= priv->dp_out_buf_len+2+len;
+		} else {
+			memcpy( data, buf, len );
+			port->write_urb->transfer_buffer_length = len;
+		}
+
+		if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
+			priv->dp_out_buf_len = 0;
+			count -= len;
+			buf += len;
+		}
+
+	}
+
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+	if( ret ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+		ret, priv->dp_port_num );
+	}
+
+	return( ret );
+
+}
+
+
+/*
+*  Digi Set Modem Signals
+*
+*  Sets or clears DTR and RTS on the port, according to the
+*  modem_signals argument.  Use TIOCM_DTR and TIOCM_RTS flags
+*  for the modem_signals argument.  Returns 0 if successful,
+*  -EINTR if interrupted while sleeping, or a non-zero error
+*  returned by usb_submit_urb.
+*/
+
+static int digi_set_modem_signals( struct usb_serial_port *port,
+	unsigned int modem_signals, int interruptible )
+{
+
+	int ret;
+	digi_port_t *port_priv = (digi_port_t *)port->private;
+	struct usb_serial_port *oob_port = (struct usb_serial_port *)((digi_serial_t *)port->serial->private)->ds_oob_port;
+	digi_port_t *oob_priv = (digi_port_t *)oob_port->private;
+	unsigned char *data = oob_port->write_urb->transfer_buffer;
+	unsigned long flags = 0;
+
+
+dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
+port_priv->dp_port_num, modem_signals );
+
+	spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+	spin_lock( &port_priv->dp_port_lock );
+
+	while( oob_port->write_urb->status == -EINPROGRESS ) {
+		spin_unlock( &port_priv->dp_port_lock );
+		cond_wait_interruptible_timeout_irqrestore(
+			&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
+			&oob_priv->dp_port_lock, flags );
+		if( interruptible && signal_pending(current) ) {
+			return( -EINTR );
+		}
+		spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
+		spin_lock( &port_priv->dp_port_lock );
+	}
+
+	data[0] = DIGI_CMD_SET_DTR_SIGNAL;
+	data[1] = port_priv->dp_port_num;
+	data[2] = (modem_signals&TIOCM_DTR) ?
+		DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE;
+	data[3] = 0;
+
+	data[4] = DIGI_CMD_SET_RTS_SIGNAL;
+	data[5] = port_priv->dp_port_num;
+	data[6] = (modem_signals&TIOCM_RTS) ?
+		DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE;
+	data[7] = 0;
+
+	oob_port->write_urb->transfer_buffer_length = 8;
+
+	if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) {
+		port_priv->dp_modem_signals =
+			(port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS))
+			| (modem_signals&(TIOCM_DTR|TIOCM_RTS));
+	}
+
+	spin_unlock( &port_priv->dp_port_lock );
+	spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+
+	if( ret ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d",
+		ret );
+	}
+
+	return( ret );
+
+}
+
+
+/*
+*  Digi Transmit Idle
+*
+*  Digi transmit idle waits, up to timeout ticks, for the transmitter
+*  to go idle.  It returns 0 if successful or a negative error.
+*
+*  There are race conditions here if more than one process is calling
+*  digi_transmit_idle on the same port at the same time.  However, this
+*  is only called from close, and only one process can be in close on a
+*  port at a time, so its ok.
+*/
+
+static int digi_transmit_idle( struct usb_serial_port *port,
+	unsigned long timeout )
+{
+
+	int ret;
+	unsigned char buf[2];
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned long flags = 0;
+
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+	priv->dp_transmit_idle = 0;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+	buf[0] = DIGI_CMD_TRANSMIT_IDLE;
+	buf[1] = 0;
+
+	timeout += jiffies;
+
+	if( (ret=digi_write_inb_command( port, buf, 2, timeout-jiffies )) != 0 )
+		return( ret );
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	while( jiffies < timeout && !priv->dp_transmit_idle ) {
+		cond_wait_interruptible_timeout_irqrestore(
+			&priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT,
+			&priv->dp_port_lock, flags );
+		if( signal_pending(current) ) {
+			return( -EINTR );
+		}
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+	}
+
+	priv->dp_transmit_idle = 0;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+	return( 0 );
+
+}
+
+
+static void digi_rx_throttle( struct usb_serial_port *port )
+{
+
+	unsigned long flags;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+
+
+dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num );
+
+	/* stop receiving characters by not resubmitting the read urb */
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+	priv->dp_throttled = 1;
+	priv->dp_throttle_restart = 0;
+	priv->dp_in_buf_len = 0;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+}
+
+
+static void digi_rx_unthrottle( struct usb_serial_port *port )
+{
+
+	int ret = 0;
+	int len;
+	unsigned long flags;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	struct tty_struct *tty = port->tty;
+
+
+dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num );
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	/* send any buffered chars from throttle time on to tty subsystem */
+	len = MIN( priv->dp_in_buf_len, TTY_FLIPBUF_SIZE - tty->flip.count );
+	if( len > 0 ) {
+		memcpy( tty->flip.char_buf_ptr, priv->dp_in_buf, len );
+		memcpy( tty->flip.flag_buf_ptr, priv->dp_in_flag_buf, len );
+		tty->flip.char_buf_ptr += len;
+		tty->flip.flag_buf_ptr += len;
+		tty->flip.count += len;
+		tty_flip_buffer_push( tty );
+	}
+
+	/* restart read chain */
+	if( priv->dp_throttle_restart )
+		ret = usb_submit_urb( port->read_urb );
+
+	/* turn throttle off */
+	priv->dp_throttled = 0;
+	priv->dp_in_buf_len = 0;
+	priv->dp_throttle_restart = 0;
+
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+	if( ret ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+			ret, priv->dp_port_num );
+	}
+
+}
+
+
+static void digi_set_termios( struct usb_serial_port *port, 
+	struct termios *old_termios )
+{
+
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned int iflag = port->tty->termios->c_iflag;
+	unsigned int cflag = port->tty->termios->c_cflag;
+	unsigned int old_iflag = old_termios->c_iflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	unsigned char buf[32];
+	unsigned int modem_signals;
+	int arg,ret;
+	int i = 0;
+
+
+dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag );
+
+	/* set baud rate */
+	if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
+
+		arg = -1;
+
+		/* reassert DTR and (maybe) RTS on transition from B0 */
+		if( (old_cflag&CBAUD) == B0 ) {
+			/* don't set RTS if using hardware flow control */
+			/* and throttling input */
+			modem_signals = TIOCM_DTR;
+			if( !(port->tty->termios->c_cflag & CRTSCTS) ||
+			!test_bit(TTY_THROTTLED, &port->tty->flags) ) {
+				modem_signals |= TIOCM_RTS;
+			}
+			digi_set_modem_signals( port, modem_signals, 1 );
+		}
+
+		switch( (cflag&CBAUD) ) {
+			/* drop DTR and RTS on transition to B0 */
+		case B0: digi_set_modem_signals( port, 0, 1 ); break;
+		case B50: arg = DIGI_BAUD_50; break;
+		case B75: arg = DIGI_BAUD_75; break;
+		case B110: arg = DIGI_BAUD_110; break;
+		case B150: arg = DIGI_BAUD_150; break;
+		case B200: arg = DIGI_BAUD_200; break;
+		case B300: arg = DIGI_BAUD_300; break;
+		case B600: arg = DIGI_BAUD_600; break;
+		case B1200: arg = DIGI_BAUD_1200; break;
+		case B1800: arg = DIGI_BAUD_1800; break;
+		case B2400: arg = DIGI_BAUD_2400; break;
+		case B4800: arg = DIGI_BAUD_4800; break;
+		case B9600: arg = DIGI_BAUD_9600; break;
+		case B19200: arg = DIGI_BAUD_19200; break;
+		case B38400: arg = DIGI_BAUD_38400; break;
+		case B57600: arg = DIGI_BAUD_57600; break;
+		case B115200: arg = DIGI_BAUD_115200; break;
+		case B230400: arg = DIGI_BAUD_230400; break;
+		case B460800: arg = DIGI_BAUD_460800; break;
+		default:
+			dbg( "digi_set_termios: can't handle baud rate 0x%x",
+				(cflag&CBAUD) );
+			break;
+		}
+
+		if( arg != -1 ) {
+			buf[i++] = DIGI_CMD_SET_BAUD_RATE;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = arg;
+			buf[i++] = 0;
+		}
+
+	}
+
+	/* set parity */
+	if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) {
+
+		if( (cflag&PARENB) ) {
+			if( (cflag&PARODD) )
+				arg = DIGI_PARITY_ODD;
+			else
+				arg = DIGI_PARITY_EVEN;
+		} else {
+			arg = DIGI_PARITY_NONE;
+		}
+
+		buf[i++] = DIGI_CMD_SET_PARITY;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	/* set word size */
+	if( (cflag&CSIZE) != (old_cflag&CSIZE) ) {
+
+		arg = -1;
+
+		switch( (cflag&CSIZE) ) {
+		case CS5: arg = DIGI_WORD_SIZE_5; break;
+		case CS6: arg = DIGI_WORD_SIZE_6; break;
+		case CS7: arg = DIGI_WORD_SIZE_7; break;
+		case CS8: arg = DIGI_WORD_SIZE_8; break;
+		default:
+			dbg( "digi_set_termios: can't handle word size %d",
+				(cflag&CSIZE) );
+			break;
+		}
+
+		if( arg != -1 ) {
+			buf[i++] = DIGI_CMD_SET_WORD_SIZE;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = arg;
+			buf[i++] = 0;
+		}
+
+	}
+
+	/* set stop bits */
+	if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) {
+
+		if( (cflag&CSTOPB) )
+			arg = DIGI_STOP_BITS_2;
+		else
+			arg = DIGI_STOP_BITS_1;
+
+		buf[i++] = DIGI_CMD_SET_STOP_BITS;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	/* set input flow control */
+	if( (iflag&IXOFF) != (old_iflag&IXOFF)
+	|| (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) {
+
+		arg = 0;
+
+		if( (iflag&IXOFF) )
+			arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
+		else
+			arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
+
+		if( (cflag&CRTSCTS) ) {
+
+			arg |= DIGI_INPUT_FLOW_CONTROL_RTS;
+
+			/* On USB-4 it is necessary to assert RTS prior */
+			/* to selecting RTS input flow control.  */
+			buf[i++] = DIGI_CMD_SET_RTS_SIGNAL;
+			buf[i++] = priv->dp_port_num;
+			buf[i++] = DIGI_RTS_ACTIVE;
+			buf[i++] = 0;
+
+		} else {
+			arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
+		}
+
+		buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	/* set output flow control */
+	if( (iflag&IXON) != (old_iflag&IXON)
+	|| (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) {
+
+		arg = 0;
+
+		if( (iflag&IXON) )
+			arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
+		else
+			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
+
+		if( (cflag&CRTSCTS) ) {
+			arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS;
+		} else {
+			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
+			port->tty->hw_stopped = 0;
+		}
+
+		buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	/* set receive enable/disable */
+	if( (cflag&CREAD) != (old_cflag&CREAD) ) {
+
+		if( (cflag&CREAD) )
+			arg = DIGI_ENABLE;
+		else
+			arg = DIGI_DISABLE;
+
+		buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
+		buf[i++] = priv->dp_port_num;
+		buf[i++] = arg;
+		buf[i++] = 0;
+
+	}
+
+	if( (ret=digi_write_oob_command( port, buf, i, 1 )) != 0 )
+		dbg( "digi_set_termios: write oob failed, ret=%d", ret );
+
+}
+
+
+static void digi_break_ctl( struct usb_serial_port *port, int break_state )
+{
+
+	unsigned char buf[4];
+
+
+	buf[0] = DIGI_CMD_BREAK_CONTROL;
+	buf[1] = 2;				/* length */
+	buf[2] = break_state ? 1 : 0;
+	buf[3] = 0;				/* pad */
+
+	digi_write_inb_command( port, buf, 4, 0 );
+
+}
+
+
+static int digi_ioctl( struct usb_serial_port *port, struct file *file,
+	unsigned int cmd, unsigned long arg )
+{
+
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned int val;
+	unsigned long flags = 0;
+
+
+dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd );
+
+	switch (cmd) {
+
+	case TIOCMGET:
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+		val = priv->dp_modem_signals;
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) )
+			return( -EFAULT );
+		return( 0 );
+
+	case TIOCMSET:
+	case TIOCMBIS:
+	case TIOCMBIC:
+		if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) )
+			return( -EFAULT );
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+		if( cmd == TIOCMBIS )
+			val = priv->dp_modem_signals | val;
+		else if( cmd == TIOCMBIC )
+			val = priv->dp_modem_signals & ~val;
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return( digi_set_modem_signals( port, val, 1 ) );
+
+	case TIOCMIWAIT:
+		/* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+		/* TODO */
+		return( 0 );
+
+	case TIOCGICOUNT:
+		/* return count of modemline transitions */
+		/* TODO */
+		return 0;
+
+	}
+
+	return( -ENOIOCTLCMD );
+
+}
+
+
+static int digi_write( struct usb_serial_port *port, int from_user,
+	const unsigned char *buf, int count )
+{
+
+	int ret,data_len,new_len;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned char *data = port->write_urb->transfer_buffer;
+	unsigned char user_buf[64];	/* 64 bytes is max USB bulk packet */
+	unsigned long flags = 0;
+
+
+dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d",
+priv->dp_port_num, count, from_user, in_interrupt() );
+
+	/* copy user data (which can sleep) before getting spin lock */
+	count = MIN( 64, MIN( count, port->bulk_out_size-2 ) );
+	if( from_user && copy_from_user( user_buf, buf, count ) ) {
+		return( -EFAULT );
+	}
+
+	/* be sure only one write proceeds at a time */
+	/* there are races on the port private buffer */
+	/* and races to check write_urb->status */
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	/* wait for urb status clear to submit another urb */
+	if( port->write_urb->status == -EINPROGRESS ) {
+
+		/* buffer data if count is 1 (probably put_char) if possible */
+		if( count == 1 ) {
+			new_len = MIN( count,
+				DIGI_OUT_BUF_SIZE-priv->dp_out_buf_len );
+			memcpy( priv->dp_out_buf+priv->dp_out_buf_len, buf,
+				new_len );
+			priv->dp_out_buf_len += new_len;
+		} else {
+			new_len = 0;
+		}
+
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+		return( new_len );
+
+	}
+
+	/* allow space for any buffered data and for new data, up to */
+	/* transfer buffer size - 2 (for command and length bytes) */
+	new_len = MIN( count, port->bulk_out_size-2-priv->dp_out_buf_len );
+	data_len = new_len + priv->dp_out_buf_len;
+
+	if( data_len == 0 ) {
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return( 0 );
+	}
+
+	port->write_urb->transfer_buffer_length = data_len+2;
+
+	*data++ = DIGI_CMD_SEND_DATA;
+	*data++ = data_len;
+
+	/* copy in buffered data first */
+	memcpy( data, priv->dp_out_buf, priv->dp_out_buf_len );
+	data += priv->dp_out_buf_len;
+
+	/* copy in new data */
+	memcpy( data, from_user ? user_buf : buf, new_len );
+
+	if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
+		ret = new_len;
+		priv->dp_out_buf_len = 0;
+	}
+
+	/* return length of new data written, or error */
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+	if( ret < 0 ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+			ret, priv->dp_port_num );
+	}
+
+dbg( "digi_write: returning %d", ret );
+	return( ret );
+
+} 
+
+
+static void digi_write_bulk_callback( struct urb *urb )
+{
+
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+	digi_port_t *priv;
+	int ret = 0;
+
+
+dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status );
+
+	/* port and serial sanity check */
+	if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) {
+		err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+			urb->status );
+		return;
+	}
+	serial = port->serial;
+	if( serial == NULL || serial->private == NULL ) {
+		err( __FUNCTION__ ": serial or serial->private is NULL, status=%d", urb->status );
+		return;
+	}
+
+	/* handle oob callback */
+	if( priv->dp_port_num
+	== ((digi_serial_t *)(serial->private))->ds_oob_port_num ) {
+		dbg( "digi_write_bulk_callback: oob callback" );
+		spin_lock( &priv->dp_port_lock );
+		wake_up_interruptible( &port->write_wait );
+		spin_unlock( &priv->dp_port_lock );
+		return;
+	}
+
+	/* further sanity checks */
+	if( port_paranoia_check( port, __FUNCTION__ )
+	|| serial_paranoia_check( serial, __FUNCTION__ ) )
+		return;
+
+	/* try to send any buffered data on this port, if it is open */
+	spin_lock( &priv->dp_port_lock );
+	if( priv->dp_open_count && port->write_urb->status != -EINPROGRESS
+	&& priv->dp_out_buf_len > 0 ) {
+
+		*((unsigned char *)(port->write_urb->transfer_buffer))
+			= (unsigned char)DIGI_CMD_SEND_DATA;
+		*((unsigned char *)(port->write_urb->transfer_buffer)+1)
+			= (unsigned char)priv->dp_out_buf_len;
+
+		port->write_urb->transfer_buffer_length
+			= priv->dp_out_buf_len+2;
+
+		memcpy( port->write_urb->transfer_buffer+2, priv->dp_out_buf,
+			priv->dp_out_buf_len );
+
+		if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
+			priv->dp_out_buf_len = 0;
+		}
+
+	}
+
+	/* wake up processes sleeping on writes immediately */
+	digi_wakeup_write( port );
+
+	/* also queue up a wakeup at scheduler time, in case we */
+	/* lost the race in write_chan(). */
+	queue_task( &priv->dp_wakeup_task, &tq_scheduler );
+
+	spin_unlock( &priv->dp_port_lock );
+
+	if( ret ) {
+		err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+			ret, priv->dp_port_num );
+	}
+
+}
+
+
+static int digi_write_room( struct usb_serial_port *port )
+{
+
+	int room;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	unsigned long flags = 0;
+
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	if( port->write_urb->status == -EINPROGRESS )
+		room = 0;
+	else
+		room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
+
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room );
+	return( room );
+
+}
+
+
+static int digi_chars_in_buffer( struct usb_serial_port *port )
+{
+
+	digi_port_t *priv = (digi_port_t *)(port->private);
+
+
+	if( port->write_urb->status == -EINPROGRESS ) {
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 );
+		/* return( port->bulk_out_size - 2 ); */
+		return( 256 );
+	} else {
+dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_out_buf_len );
+		return( priv->dp_out_buf_len );
+	}
+
+}
+
+
+static int digi_open( struct usb_serial_port *port, struct file *filp )
+{
+
+	int ret;
+	unsigned char buf[32];
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	struct termios not_termios;
+	unsigned long flags = 0;
+
+
+dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
+
+	/* be sure the device is started up */
+	if( digi_startup_device( port->serial ) != 0 )
+		return( -ENXIO );
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+
+	/* don't wait on a close in progress for non-blocking opens */
+	if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return( -EAGAIN );
+	}
+
+	/* inc module use count before sleeping to wait for closes */
+	++priv->dp_open_count;
+	MOD_INC_USE_COUNT;
+
+	/* wait for a close in progress to finish */
+	while( priv->dp_in_close ) {
+		cond_wait_interruptible_timeout_irqrestore(
+			&priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
+			&priv->dp_port_lock, flags );
+		if( signal_pending(current) ) {
+			--priv->dp_open_count;
+			MOD_DEC_USE_COUNT;
+			return( -EINTR );
+		}
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+	}
+
+	/* if port is already open, just return */
+	/* be sure exactly one open proceeds */
+	if( port->active ) {
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return( 0 );
+	}
+
+	/* first open, mark port as active */
+	port->active = 1;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ 
+	/* read modem signals automatically whenever they change */
+	buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
+	buf[1] = priv->dp_port_num;
+	buf[2] = DIGI_ENABLE;
+	buf[3] = 0;
+
+	/* flush fifos */
+	buf[4] = DIGI_CMD_IFLUSH_FIFO;
+	buf[5] = priv->dp_port_num;
+	buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+	buf[7] = 0;
+
+	if( (ret=digi_write_oob_command( port, buf, 8, 1 )) != 0 )
+		dbg( "digi_open: write oob failed, ret=%d", ret );
+
+	/* set termios settings */
+	not_termios.c_cflag = ~port->tty->termios->c_cflag;
+	not_termios.c_iflag = ~port->tty->termios->c_iflag;
+	digi_set_termios( port, &not_termios );
+
+	/* set DTR and RTS */
+	digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS, 1 );
+
+	return( 0 );
+
+}
+
+
+static void digi_close( struct usb_serial_port *port, struct file *filp )
+{
+
+	int ret;
+	unsigned char buf[32];
+	struct tty_struct *tty = port->tty;
+	digi_port_t *priv = (digi_port_t *)port->private;
+	unsigned long flags = 0;
+
+
+dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
+
+
+	/* do cleanup only after final close on this port */
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+	if( priv->dp_open_count > 1 ) {
+		--priv->dp_open_count;
+		MOD_DEC_USE_COUNT;
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return;
+	} else if( priv->dp_open_count <= 0 ) {
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+		return;
+	}
+	priv->dp_in_close = 1;
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+	/* tell line discipline to process only XON/XOFF */
+        tty->closing = 1;
+
+	/* wait for output to drain */
+	if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+		tty_wait_until_sent( tty, DIGI_CLOSE_TIMEOUT );
+	}
+
+	/* flush driver and line discipline buffers */
+	if( tty->driver.flush_buffer )
+		tty->driver.flush_buffer( tty );
+	if( tty->ldisc.flush_buffer )
+		tty->ldisc.flush_buffer( tty );
+
+	/* wait for transmit idle */
+	if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
+		digi_transmit_idle( port, DIGI_CLOSE_TIMEOUT );
+	}
+
+	/* drop DTR and RTS */
+	digi_set_modem_signals( port, 0, 0 );
+
+	/* disable input flow control */
+	buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
+	buf[1] = priv->dp_port_num;
+	buf[2] = DIGI_DISABLE;
+	buf[3] = 0;
+
+	/* disable output flow control */
+	buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
+	buf[5] = priv->dp_port_num;
+	buf[6] = DIGI_DISABLE;
+	buf[7] = 0;
+
+	/* disable reading modem signals automatically */
+	buf[8] = DIGI_CMD_READ_INPUT_SIGNALS;
+	buf[9] = priv->dp_port_num;
+	buf[10] = DIGI_DISABLE;
+	buf[11] = 0;
+
+	/* disable receive */
+	buf[12] = DIGI_CMD_RECEIVE_ENABLE;
+	buf[13] = priv->dp_port_num;
+	buf[14] = DIGI_DISABLE;
+	buf[15] = 0;
+
+	/* flush fifos */
+	buf[16] = DIGI_CMD_IFLUSH_FIFO;
+	buf[17] = priv->dp_port_num;
+	buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
+	buf[19] = 0;
+
+	if( (ret=digi_write_oob_command( port, buf, 20, 0 )) != 0 )
+		dbg( "digi_close: write oob failed, ret=%d", ret );
+
+	/* wait for final commands on oob port to complete */
+	interruptible_sleep_on_timeout( &priv->dp_flush_wait,
+		DIGI_CLOSE_TIMEOUT );
+
+	/* shutdown any outstanding bulk writes */
+	usb_unlink_urb (port->write_urb);
+
+	tty->closing = 0;
+
+	spin_lock_irqsave( &priv->dp_port_lock, flags );
+	port->active = 0;
+	priv->dp_in_close = 0;
+	--priv->dp_open_count;
+	MOD_DEC_USE_COUNT;
+	wake_up_interruptible( &priv->dp_close_wait );
+	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+
+dbg( "digi_close: done" );
+}
+
+
+/*
+*  Digi Startup Device
+*
+*  Starts reads on all ports.  Must be called AFTER startup, with
+*  urbs initialized.  Returns 0 if successful, non-zero error otherwise.
+*/
+
+static int digi_startup_device( struct usb_serial *serial )
+{
+
+	int i,ret = 0;
+	digi_serial_t *serial_priv = (digi_serial_t *)serial->private;
+	struct usb_serial_port *port;
+
+
+	/* be sure this happens exactly once */
+	spin_lock( &serial_priv->ds_serial_lock );
+	if( serial_priv->ds_device_started ) {
+		spin_unlock( &serial_priv->ds_serial_lock );
+		return( 0 );
+	}
+	serial_priv->ds_device_started = 1;
+	spin_unlock( &serial_priv->ds_serial_lock );
+
+	/* start reading from each bulk in endpoint for the device */
+	/* set USB_DISABLE_SPD flag for write bulk urbs */
+	for( i=0; i<serial->type->num_ports+1; i++ ) {
+
+		port = &serial->port[i];
+
+		port->write_urb->transfer_flags |= USB_DISABLE_SPD;
+
+		if( (ret=usb_submit_urb(port->read_urb)) != 0 ) {
+			err(
+			__FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+			ret, i );
+			break;
+		}
+
+	}
+
+	return( ret );
+
+}
+
+
+static int digi_startup( struct usb_serial *serial )
+{
+
+	int i;
+	digi_port_t *priv;
+	digi_serial_t *serial_priv;
+
+
+dbg( "digi_startup: TOP" );
+
+	/* allocate the private data structures for all ports */
+	/* number of regular ports + 1 for the out-of-band port */
+	for( i=0; i<serial->type->num_ports+1; i++ ) {
+
+		serial->port[i].active = 0;
+
+		/* allocate port private structure */
+		priv = serial->port[i].private =
+			(digi_port_t *)kmalloc( sizeof(digi_port_t),
+			GFP_KERNEL );
+		if( priv == (digi_port_t *)0 ) {
+			while( --i >= 0 )
+				kfree( serial->port[i].private );
+			return( 1 );			/* error */
+		}
+
+		/* initialize port private structure */
+		spin_lock_init( &priv->dp_port_lock );
+		priv->dp_port_num = i;
+		priv->dp_out_buf_len = 0;
+		priv->dp_in_buf_len = 0;
+		priv->dp_modem_signals = 0;
+		init_waitqueue_head( &priv->dp_modem_change_wait );
+		priv->dp_open_count = 0;
+		priv->dp_transmit_idle = 0;
+		init_waitqueue_head( &priv->dp_transmit_idle_wait );
+		priv->dp_throttled = 0;
+		priv->dp_throttle_restart = 0;
+		init_waitqueue_head( &priv->dp_flush_wait );
+		priv->dp_in_close = 0;
+		init_waitqueue_head( &priv->dp_close_wait );
+		priv->dp_wakeup_task.next = NULL;
+		priv->dp_wakeup_task.sync = 0;
+		priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock;
+		priv->dp_wakeup_task.data = (void *)(&serial->port[i]);
+
+		/* initialize write wait queue for this port */
+		init_waitqueue_head( &serial->port[i].write_wait );
+
+	}
+
+	/* allocate serial private structure */
+	serial_priv = serial->private =
+		(digi_serial_t *)kmalloc( sizeof(digi_serial_t),
+		GFP_KERNEL );
+	if( serial_priv == (digi_serial_t *)0 ) {
+		for( i=0; i<serial->type->num_ports+1; i++ )
+			kfree( serial->port[i].private );
+		return( 1 );			/* error */
+	}
+
+	/* initialize serial private structure */
+	spin_lock_init( &serial_priv->ds_serial_lock );
+	serial_priv->ds_oob_port_num = serial->type->num_ports;
+	serial_priv->ds_oob_port = &serial->port[serial_priv->ds_oob_port_num];
+	serial_priv->ds_device_started = 0;
+
+	return( 0 );
+
+}
+
+
+static void digi_shutdown( struct usb_serial *serial )
+{
+
+	int i;
+	digi_port_t *priv;
+	unsigned long flags;
+
+
+dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() );
+
+	/* stop reads and writes on all ports */
+	for( i=0; i<serial->type->num_ports+1; i++ ) {
+		usb_unlink_urb( serial->port[i].read_urb );
+		usb_unlink_urb( serial->port[i].write_urb );
+	}
+
+	/* dec module use count */
+	for( i=0; i<serial->type->num_ports; i++ ) {
+		priv = serial->port[i].private;
+		spin_lock_irqsave( &priv->dp_port_lock, flags );
+		while( priv->dp_open_count > 0 ) {
+			MOD_DEC_USE_COUNT;
+			--priv->dp_open_count;
+		}
+		spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+	}
+
+	/* free the private data structures for all ports */
+	/* number of regular ports + 1 for the out-of-band port */
+	for( i=0; i<serial->type->num_ports+1; i++ )
+		kfree( serial->port[i].private );
+	kfree( serial->private );
+
+}
+
+
+static void digi_read_bulk_callback( struct urb *urb )
+{
+
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	digi_port_t *priv;
+	int ret;
+
+
+dbg( "digi_read_bulk_callback: TOP" );
+
+	/* port sanity check, do not resubmit if port is not valid */
+	if( port == NULL || (priv=(digi_port_t *)(port->private)) == NULL ) {
+		err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+			urb->status );
+		return;
+	}
+	if( port->serial == NULL
+	|| serial_paranoia_check( port->serial, __FUNCTION__ )
+	|| port->serial->private == NULL ) {
+		err( __FUNCTION__ ": serial is bad or serial->private is NULL, status=%d", urb->status );
+		return;
+	}
+
+	/* do not resubmit urb if it has any status error */
+	if( urb->status ) {
+		err( __FUNCTION__ ": nonzero read bulk status: status=%d, port=%d", urb->status, priv->dp_port_num );
+		return;
+	}
+
+	/* handle oob or inb callback, do not resubmit if error */
+	if( priv->dp_port_num
+	== ((digi_serial_t *)(port->serial->private))->ds_oob_port_num ) {
+		if( digi_read_oob_callback( urb ) != 0 )
+			return;
+	} else {
+		if( digi_read_inb_callback( urb ) != 0 )
+			return;
+	}
+
+	/* continue read */
+	if( (ret=usb_submit_urb(urb)) != 0 ) {
+		err( __FUNCTION__ ": failed resubmitting urb, ret=%d, port=%d",
+			ret, priv->dp_port_num );
+	}
+
+}
+
+
+/* 
+*  Digi Read INB Callback
+*
+*  Digi Read INB Callback handles reads on the in band ports, sending
+*  the data on to the tty subsystem.  When called we know port and
+*  port->private are not NULL and port->serial has been validated.
+*  It returns 0 if successful, 1 if successful but the port is
+*  throttled, and -1 if the sanity checks failed.
+*/
+
+static int digi_read_inb_callback( struct urb *urb )
+{
+
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct tty_struct *tty = port->tty;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	int opcode = ((unsigned char *)urb->transfer_buffer)[0];
+	int len = ((unsigned char *)urb->transfer_buffer)[1];
+	int status = ((unsigned char *)urb->transfer_buffer)[2];
+	unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
+	int flag,throttled;
+
+
+	/* sanity check */
+	if( port_paranoia_check( port, __FUNCTION__ ) )
+		return( -1 );
+
+	/* do not process callbacks on closed ports */
+	/* but do continue the read chain */
+	if( priv->dp_open_count == 0 )
+		return( 0 );
+
+	/* short/multiple packet check */
+	if( urb->actual_length != len + 2 ) {
+     		err( __FUNCTION__ ": INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status );
+		return( -1 );
+	}
+
+	spin_lock( &priv->dp_port_lock );
+
+	/* check for throttle; if set, do not resubmit read urb */
+	/* indicate the read chain needs to be restarted on unthrottle */
+	throttled = priv->dp_throttled;
+	if( throttled )
+		priv->dp_throttle_restart = 1;
+
+	/* receive data */
+	if( opcode == DIGI_CMD_RECEIVE_DATA ) {
+
+		/* get flag from status */
+		flag = 0;
+
+		/* overrun is special, not associated with a char */
+		if( status & DIGI_OVERRUN_ERROR ) {
+			tty_insert_flip_char( tty, 0, TTY_OVERRUN );
+		}
+
+		/* break takes precedence over parity, */
+		/* which takes precedence over framing errors */
+		if( status & DIGI_BREAK_ERROR ) {
+			flag = TTY_BREAK;
+		} else if( status & DIGI_PARITY_ERROR ) {
+			flag = TTY_PARITY;
+		} else if( status & DIGI_FRAMING_ERROR ) {
+			flag = TTY_FRAME;
+		}
+
+		/* data length is len-1 (one byte of len is status) */
+		--len;
+
+		if( throttled ) {
+
+			len = MIN( len,
+				DIGI_IN_BUF_SIZE - priv->dp_in_buf_len );
+
+			if( len > 0 ) {
+				memcpy( priv->dp_in_buf + priv->dp_in_buf_len,
+					data, len );
+				memset( priv->dp_in_flag_buf
+					+ priv->dp_in_buf_len, flag, len );
+				priv->dp_in_buf_len += len;
+			}
+
+		} else {
+
+			len = MIN( len, TTY_FLIPBUF_SIZE - tty->flip.count );
+
+			if( len > 0 ) {
+				memcpy( tty->flip.char_buf_ptr, data, len );
+				memset( tty->flip.flag_buf_ptr, flag, len );
+				tty->flip.char_buf_ptr += len;
+				tty->flip.flag_buf_ptr += len;
+				tty->flip.count += len;
+				tty_flip_buffer_push( tty );
+			}
+
+		}
+
+	}
+
+	spin_unlock( &priv->dp_port_lock );
+
+	if( opcode == DIGI_CMD_RECEIVE_DISABLE ) {
+		dbg( __FUNCTION__ ": got RECEIVE_DISABLE" );
+	} else if( opcode != DIGI_CMD_RECEIVE_DATA ) {
+		dbg( __FUNCTION__ ": unknown opcode: %d", opcode );
+	}
+
+	return( throttled ? 1 : 0 );
+
+}
+
+
+/* 
+*  Digi Read OOB Callback
+*
+*  Digi Read OOB Callback handles reads on the out of band port.
+*  When called we know port and port->private are not NULL and
+*  the port->serial is valid.  It returns 0 if successful, and
+*  -1 if the sanity checks failed.
+*/
+
+static int digi_read_oob_callback( struct urb *urb )
+{
+
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+	digi_port_t *priv = (digi_port_t *)(port->private);
+	int opcode, line, status, val;
+	int i;
+
+
+dbg( "digi_read_oob_callback: port=%d, len=%d", priv->dp_port_num,
+urb->actual_length );
+
+	/* handle each oob command */
+	for( i=0; i<urb->actual_length-3; ) {
+
+		opcode = ((unsigned char *)urb->transfer_buffer)[i++];
+		line = ((unsigned char *)urb->transfer_buffer)[i++];
+		status = ((unsigned char *)urb->transfer_buffer)[i++];
+		val = ((unsigned char *)urb->transfer_buffer)[i++];
+
+dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d",
+opcode, line, status, val );
+
+		if( status != 0 || line >= serial->type->num_ports )
+			continue;
+
+		port = &serial->port[line];
+
+		if( port_paranoia_check( port, __FUNCTION__ )
+		|| (priv=port->private) == NULL )
+			return( -1 );
+
+		if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) {
+
+			spin_lock( &priv->dp_port_lock );
+
+			/* convert from digi flags to termiox flags */
+			if( val & DIGI_READ_INPUT_SIGNALS_CTS ) {
+				priv->dp_modem_signals |= TIOCM_CTS;
+				/* port must be open to use tty struct */
+				if( priv->dp_open_count
+				&& port->tty->termios->c_cflag & CRTSCTS ) {
+					port->tty->hw_stopped = 0;
+					digi_wakeup_write( port );
+				}
+			} else {
+				priv->dp_modem_signals &= ~TIOCM_CTS;
+				/* port must be open to use tty struct */
+				if( priv->dp_open_count
+				&& port->tty->termios->c_cflag & CRTSCTS ) {
+					port->tty->hw_stopped = 1;
+				}
+			}
+			if( val & DIGI_READ_INPUT_SIGNALS_DSR )
+				priv->dp_modem_signals |= TIOCM_DSR;
+			else
+				priv->dp_modem_signals &= ~TIOCM_DSR;
+			if( val & DIGI_READ_INPUT_SIGNALS_RI )
+				priv->dp_modem_signals |= TIOCM_RI;
+			else
+				priv->dp_modem_signals &= ~TIOCM_RI;
+			if( val & DIGI_READ_INPUT_SIGNALS_DCD )
+				priv->dp_modem_signals |= TIOCM_CD;
+			else
+				priv->dp_modem_signals &= ~TIOCM_CD;
+
+			wake_up_interruptible( &priv->dp_modem_change_wait );
+			spin_unlock( &priv->dp_port_lock );
+
+		} else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) {
+
+			spin_lock( &priv->dp_port_lock );
+			priv->dp_transmit_idle = 1;
+			wake_up_interruptible( &priv->dp_transmit_idle_wait );
+			spin_unlock( &priv->dp_port_lock );
+
+		} else if( opcode == DIGI_CMD_IFLUSH_FIFO ) {
+
+			wake_up_interruptible( &priv->dp_flush_wait );
+
+		}
+
+	}
+
+	return( 0 );
+
+}
+
+
+static int __init digi_init (void)
+{
+	usb_serial_register (&digi_acceleport_2_device);
+	usb_serial_register (&digi_acceleport_4_device);
+	return 0;
+}
+
+
+static void __exit digi_exit (void)
+{
+	usb_serial_deregister (&digi_acceleport_2_device);
+	usb_serial_deregister (&digi_acceleport_4_device);
+}
+
+
+module_init(digi_init);
+module_exit(digi_exit);
+
+
+MODULE_AUTHOR("Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>");
+MODULE_DESCRIPTION("Digi AccelePort USB-4 Serial Converter driver");
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/ezusb_convert.pl linux.18p17/drivers/usb/serial/ezusb_convert.pl
--- linux.vanilla/drivers/usb/serial/ezusb_convert.pl	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/ezusb_convert.pl	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,50 @@
+#! /usr/bin/perl -w
+
+
+# convert an Intel HEX file into a set of C records usable by the firmware
+# loading code in usb-serial.c (or others)
+
+# accepts the .hex file(s) on stdin, a basename (to name the initialized
+# array) as an argument, and prints the .h file to stdout. Typical usage:
+#  perl ezusb_convert.pl foo <foo.hex >fw_foo.h
+
+
+my $basename = $ARGV[0];
+die "no base name specified" unless $basename;
+
+while (<STDIN>) {
+    # ':' <len> <addr> <type> <len-data> <crc> '\r'
+    #  len, type, crc are 2-char hex, addr is 4-char hex. type is 00 for
+    # normal records, 01 for EOF
+    my($lenstring, $addrstring, $typestring, $reststring, $doscrap) =
+      /^:(\w\w)(\w\w\w\w)(\w\w)(\w+)(\r?)$/;
+    die "malformed line: $_" unless $reststring;
+    last if $typestring eq '01';
+    my($len) = hex($lenstring);
+    my($addr) = hex($addrstring);
+    my(@bytes) = unpack("C*", pack("H".(2*$len), $reststring));
+    #pop(@bytes); # last byte is a CRC
+    push(@records, [$addr, \@bytes]);
+}
+
+@sorted_records = sort { $a->[0] <=> $b->[0] } @records;
+
+print <<"EOF";
+/*
+ * ${basename}_fw.h
+ *
+ * Generated from ${basename}.s by ezusb_convert.pl
+ * This file is presumed to be under the same copyright as the source file
+ * from which it was derived.
+ */
+
+EOF
+
+print "static const struct ezusb_hex_record ${basename}_firmware[] = {\n";
+foreach $r (@sorted_records) {
+    printf("{ 0x%04x,\t%d,\t{", $r->[0], scalar(@{$r->[1]}));
+    print join(", ", map {sprintf('0x%02x', $_);} @{$r->[1]});
+    print "} },\n";
+}
+print "{ 0xffff,\t0,\t{0x00} }\n";
+print "};\n";
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/ftdi_sio.c linux.18p17/drivers/usb/serial/ftdi_sio.c
--- linux.vanilla/drivers/usb/serial/ftdi_sio.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/ftdi_sio.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,719 @@
+/*
+ * USB FTDI SIO driver
+ *
+ * 	Copyright (C) 1999, 2000
+ * 	    Greg Kroah-Hartman (greg@kroah.com)
+ *          Bill Ryder (bryder@sgi.com)
+ *
+ * 	This program is free software; you can redistribute it and/or modify
+ * 	it under the terms of the GNU General Public License as published by
+ * 	the Free Software Foundation; either version 2 of the License, or
+ * 	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (09/11/2000) gkh
+ *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
+ *
+ * (07/19/2000) gkh
+ *	Added module_init and module_exit functions to handle the fact that this
+ *	driver is a loadable module now.
+ *
+ * (04/04/2000) Bill Ryder 
+ *         Fixed bugs in TCGET/TCSET ioctls (by removing them - they are 
+ *             handled elsewhere in the serial driver chain).
+ *
+ * (03/30/2000) Bill Ryder 
+ *         Implemented lots of ioctls
+ * 	Fixed a race condition in write
+ * 	Changed some dbg's to errs
+ *
+ * (03/26/2000) gkh
+ * 	Split driver up into device specific pieces.
+ *
+ */
+
+/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */
+/* Thanx to FTDI for so kindly providing details of the protocol required */
+/*   to talk to the device */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "ftdi_sio.h"
+
+#define FTDI_VENDOR_ID			0x0403
+#define FTDI_SIO_SERIAL_CONVERTER_ID	0x8372
+
+/* function prototypes for a FTDI serial converter */
+static int  ftdi_sio_startup		(struct usb_serial *serial);
+static int  ftdi_sio_open		(struct usb_serial_port *port, struct file *filp);
+static void ftdi_sio_close		(struct usb_serial_port *port, struct file *filp);
+static int  ftdi_sio_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static void ftdi_sio_write_bulk_callback (struct urb *urb);
+static void ftdi_sio_read_bulk_callback	(struct urb *urb);
+static void ftdi_sio_set_termios	(struct usb_serial_port *port, struct termios * old);
+static int  ftdi_sio_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+
+/* All of the device info needed for the FTDI SIO serial converter */
+static __u16	ftdi_vendor_id		= FTDI_VENDOR_ID;
+static __u16	ftdi_sio_product_id	= FTDI_SIO_SERIAL_CONVERTER_ID;
+struct usb_serial_device_type ftdi_sio_device = {
+	name:			"FTDI SIO",
+	idVendor:		&ftdi_vendor_id,	/* the FTDI vendor ID */
+	idProduct:		&ftdi_sio_product_id,	/* the FTDI SIO product id */
+	needs_interrupt_in:	MUST_HAVE_NOT,		/* this device must not have an interrupt in endpoint */
+	needs_bulk_in:		MUST_HAVE,		/* this device must have a bulk in endpoint */
+	needs_bulk_out:		MUST_HAVE,		/* this device must have a bulk out endpoint */
+	num_interrupt_in:	0,
+	num_bulk_in:		1,
+	num_bulk_out:		1,
+	num_ports:		1,
+	open:			ftdi_sio_open,
+	close:			ftdi_sio_close,
+	write:			ftdi_sio_write,
+	read_bulk_callback:	ftdi_sio_read_bulk_callback,
+	write_bulk_callback:	ftdi_sio_write_bulk_callback,
+	ioctl:			ftdi_sio_ioctl,
+	set_termios:		ftdi_sio_set_termios,
+	startup:		ftdi_sio_startup,
+};
+
+
+/*
+ * ***************************************************************************
+ * FTDI SIO Serial Converter specific driver functions
+ * ***************************************************************************
+ *
+ * Bill Ryder bryder@sgi.com of Silicon Graphics, Inc. did the FTDI_SIO code
+ * Thanx to FTDI for so kindly providing details of the protocol required
+ *   to talk to the device - http://www.ftdi.co.uk
+ *
+ * Tested as at this version - other stuff might work
+ * 23 March 2000
+ *     Works:
+ *      Baudrates - 9600, 38400,19200, 57600, 115200  
+ *      TIOCMBIC - TIOCM_DTR / TIOCM_RTS 
+ *      TIOCMBIS - TIOCM_DTR / TIOCM_RTS 
+ *      TIOCMSET - DTR on/RTSon  / DTR off, RTS off 
+ *      no parity:CS8 even parity:CS7 odd parity:CS7 
+ *      CRTSCTS flow control 
+ *     
+ *      Pilot-xfer zillions of times
+ *  
+ *      cu works with dir option 
+ *
+ *   Not Tested (ie might not work): 
+ *      xon/xoff flow control 
+ *      ppp (modem handling in general) 
+ *
+ *   KNOWN BUGS:
+ *    Multiple Opens
+ *    ==============
+ *      Seems to have problem when opening an already open port, 
+ *      Get I/O error on first attempt, then it lets you in. 
+ *      Need to do proper usage counting - keep registered callbacks for first opener.
+ *     
+ *     Reproduce with: 
+ *       cu -l /dev/ttyUSB0 dir 
+ *       whilst cu is running do:
+ *        stty -a < /dev/ttyUSB0  
+ *
+ *     from stty get: 'bash: /dev/ttyUSB0: Invalid argument ' 
+ *     from cu get 
+ *        write: Invalid argument 
+ *    
+ *    Initialisation Problem
+ *    ======================
+ *    Pilot transfer required me to run the serial_loopback program before it would work.
+ *    Still working on this. See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio
+ *
+ */
+
+#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
+
+/* do some startup allocations not currently performed by usb_serial_probe() */
+static int ftdi_sio_startup (struct usb_serial *serial)
+{
+	init_waitqueue_head(&serial->port[0].write_wait);
+	
+	return (0);
+}
+
+static int  ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
+{ /* ftdi_sio_open */
+	struct termios tmp_termios;
+	struct usb_serial *serial = port->serial;
+	char buf[1]; /* Needed for the usb_control_msg I think */
+
+	dbg("ftdi_sio_open port %d", port->number);
+
+        /* FIXME - multiple concurrent opens cause trouble */
+	if (port->active) {
+		err ("port already open");
+		return -EINVAL;
+	}
+	port->active = 1; /* FIXME - For multiple open this should increment */
+
+	/* See ftdi_sio.h for description of what is reset */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
+			FTDI_SIO_RESET_SIO, 
+			0, buf, 0, WDR_TIMEOUT);
+
+	/* Setup termios */
+	port->tty->termios->c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+
+	
+	ftdi_sio_set_termios(port, &tmp_termios);	
+
+	/* Disable flow control */
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+			    0, 0, 
+			    buf, 0, WDR_TIMEOUT) < 0) {
+		err("error from flowcontrol urb");
+		return(-EINVAL);
+	}	    
+
+	/* Turn on RTS and DTR since we are not flow controlling*/
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_DTR_HIGH, 0, 
+			    buf, 0, WDR_TIMEOUT) < 0) {
+		err("Error from DTR HIGH urb");
+	}
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_RTS_HIGH, 0, 
+			    buf, 0, WDR_TIMEOUT) < 0) {
+		err("Error from RTS HIGH urb");
+	}
+	
+	/*Start reading from the device*/
+	if (usb_submit_urb(port->read_urb))
+		err("usb_submit_urb(read bulk) failed");
+
+	return (0);
+} /* ftdi_sio_open */
+
+
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
+{ /* ftdi_sio_close */
+	struct usb_serial *serial = port->serial;
+	unsigned int c_cflag = port->tty->termios->c_cflag;
+	char buf[1];
+
+	dbg("ftdi_sio_close port %d", port->number);
+	
+	if (c_cflag & HUPCL){
+		/* Disable flow control */
+		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("error from flowcontrol urb");
+		}	    
+
+		/* drop DTR */
+ 		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    (unsigned)FTDI_SIO_SET_DTR_LOW, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("Error from DTR LOW urb");
+		}
+		/* drop RTS */
+		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    (unsigned)FTDI_SIO_SET_RTS_LOW, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("Error from RTS LOW urb");
+		}	
+	}
+
+	/* shutdown our bulk reads and writes */
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
+	port->active = 0;
+} /* ftdi_sio_close */
+
+
+  
+/* The ftdi_sio requires the first byte to have:
+ *  B0 1
+ *  B1 0
+ *  B2..7 length of message excluding byte 0
+ */
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user, 
+			   const unsigned char *buf, int count)
+{ /* ftdi_sio_write */
+	struct usb_serial *serial = port->serial;
+	const int data_offset = 1;
+	int rc; 
+	DECLARE_WAITQUEUE(wait, current);
+	
+	dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
+
+	if (count == 0) {
+		err("write request of 0 bytes");
+		return 0;
+	}
+
+	/* only do something if we have a bulk out endpoint */
+	if (serial->num_bulk_out) {
+		unsigned char *first_byte = port->write_urb->transfer_buffer;
+
+		/* Was seeing a race here, got a read callback, then write callback before
+		   hitting interuptible_sleep_on  - so wrapping in add_wait_queue stuff */
+
+		add_wait_queue(&port->write_wait, &wait);
+		set_current_state (TASK_INTERRUPTIBLE);
+		while (port->write_urb->status == -EINPROGRESS) {
+			dbg("ftdi_sio - write in progress - retrying");
+			if (0 /* file->f_flags & O_NONBLOCK */) {
+				rc = -EAGAIN;
+				goto err;
+			}
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&port->write_wait, &wait);
+				rc = -ERESTARTSYS;
+				goto err;
+			}
+			schedule();
+		}		
+		remove_wait_queue(&port->write_wait, &wait);
+		set_current_state(TASK_RUNNING);
+
+		count += data_offset;
+		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+		if (count == 0) {
+			return 0;
+		}
+
+		/* Copy in the data to send */
+		if (from_user) {
+			copy_from_user(port->write_urb->transfer_buffer + data_offset , 
+				       buf, count - data_offset );
+		}
+		else {
+			memcpy(port->write_urb->transfer_buffer + data_offset,
+			       buf, count - data_offset );
+		}  
+
+		/* Write the control byte at the front of the packet*/
+		first_byte = port->write_urb->transfer_buffer;
+		*first_byte = 1 | ((count-data_offset) << 2) ; 
+
+		dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
+		usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
+		
+		/* send the data out the bulk port */
+		port->write_urb->transfer_buffer_length = count;
+
+		if (usb_submit_urb(port->write_urb))
+			err("usb_submit_urb(write bulk) failed");
+
+		dbg("write returning: %d", count - data_offset);
+		return (count - data_offset);
+	}
+	
+	/* no bulk out, so return 0 bytes written */
+	return 0;
+ err: /* error exit */
+	return(rc);
+} /* ftdi_sio_write */
+
+static void ftdi_sio_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty = port->tty;
+
+	dbg("ftdi_sio_write_bulk_callback");
+
+	if (port_paranoia_check (port, "ftdi_sio_write_bulk_callback")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "ftdi_sio_write_bulk_callback")) {
+		return;
+	}
+	
+	if (urb->status) {
+		dbg("nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	wake_up_interruptible(&port->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+
+	wake_up_interruptible(&tty->write_wait);
+	
+	return;
+} /* ftdi_sio_write_bulk_callback */
+
+static void ftdi_sio_read_bulk_callback (struct urb *urb)
+{ /* ftdi_sio_serial_buld_callback */
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty = port->tty ;
+       	unsigned char *data = urb->transfer_buffer;
+
+	const int data_offset = 2;
+	int i;
+
+	dbg("ftdi_sio read callback");
+
+	if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
+		return;
+	}
+	
+	/* TO DO -- check for hung up line and handle appropriately: */
+	/*   send hangup (need to find out how to do this) */
+ 
+
+	if (urb->status) {
+		/* This will happen at close every time so it is a dbg not an err */
+		dbg("nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	if (urb->actual_length > 2) {
+		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	} else {
+                dbg("Just status");
+        }
+
+	if (urb->actual_length > data_offset) {
+		for (i = data_offset ; i < urb->actual_length ; ++i) {
+			tty_insert_flip_char(tty, data[i], 0);
+	  	}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		err("failed resubmitting read urb");
+
+	return;
+} /* ftdi_sio_serial_read_bulk_callback */
+
+/* As I understand this - old_termios contains the original termios settings */
+/*  and tty->termios contains the new setting to be used */
+/* */
+/*   WARNING: set_termios calls this with old_termios in kernel space */
+
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{ /* ftdi_sio_set_termios */
+	struct usb_serial *serial = port->serial;
+	unsigned int cflag = port->tty->termios->c_cflag;
+	__u16 urb_value; /* Will hold the new flags */
+	char buf[1]; /* Perhaps I should dynamically alloc this? */
+	
+	dbg("ftdi_sio_set_termios port %d", port->number);
+
+
+	/* FIXME -For this cut I don't care if the line is really changing or 
+	   not  - so just do the change regardless  - should be able to 
+	   compare old_termios and tty->termios */
+	/* NOTE These routines can get interrupted by 
+	   ftdi_sio_read_bulk_callback  - need to examine what this 
+           means - don't see any problems yet */
+	
+	/* Set number of data bits, parity, stop bits */
+	
+	urb_value = 0;
+	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+		      FTDI_SIO_SET_DATA_STOP_BITS_1);
+	urb_value |= (cflag & PARENB ? 
+		      (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : 
+		       FTDI_SIO_SET_DATA_PARITY_EVEN) :
+		      FTDI_SIO_SET_DATA_PARITY_NONE);
+	if (cflag & CSIZE) {
+		switch (cflag & CSIZE) {
+		case CS5: urb_value |= 5; dbg("Setting CS5"); break;
+		case CS6: urb_value |= 6; dbg("Setting CS6"); break;
+		case CS7: urb_value |= 7; dbg("Setting CS7"); break;
+		case CS8: urb_value |= 8; dbg("Setting CS8"); break;
+		default:
+			err("CSIZE was set but not CS5-CS8");
+		}
+	}
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_DATA_REQUEST, 
+			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			    urb_value , 0,
+			    buf, 0, 100) < 0) {
+		err("FAILED to set databits/stopbits/parity");
+	}	   
+
+	/* Now do the baudrate */
+
+	switch(cflag & CBAUD){
+	case B0: break; /* Handled below */
+	case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break;
+	case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break;
+	case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break;
+	case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break;
+	case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break;
+	case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break;
+	case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break;
+	case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
+	case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
+	case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
+	default: dbg("FTDI_SIO does not support the baudrate requested"); 
+		/* FIXME - how to return an error for this? */ break;
+	}
+	if ((cflag & CBAUD) == B0 ) {
+		/* Disable flow control */
+		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("error from disable flowcontrol urb");
+		}	    
+		/* Drop RTS and DTR */
+		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    (unsigned)FTDI_SIO_SET_DTR_LOW, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("Error from DTR LOW urb");
+		}
+		if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    (unsigned)FTDI_SIO_SET_RTS_LOW, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("Error from RTS LOW urb");
+		}	
+		
+	} else {
+		if (usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_BAUDRATE_REQUEST, 
+				    FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+				    urb_value, 0, 
+				    buf, 0, 100) < 0) {
+			err("urb failed to set baurdrate");
+		}
+	}
+	/* Set flow control */
+	/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
+	if (cflag & CRTSCTS) {
+		dbg("Setting to CRTSCTS flow control");
+		if (usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0 , FTDI_SIO_RTS_CTS_HS,
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("urb failed to set to rts/cts flow control");
+		}		
+		
+	} else { 
+		/* CHECK Assuming XON/XOFF handled by stack - not by device */
+		/* Disable flow control */
+		dbg("Turning off hardware flow control");
+		if (usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+				    0, 0, 
+				    buf, 0, WDR_TIMEOUT) < 0) {
+			err("urb failed to clear flow control");
+		}				
+		
+	}
+	return;
+} /* ftdi_sio_set_termios */
+
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = port->serial;
+	__u16 urb_value=0; /* Will hold the new flags */
+	char buf[1];
+	int  ret, mask;
+	
+	dbg("ftdi_sio_ioctl - cmd 0x%04x", cmd);
+
+	/* Based on code from acm.c and others */
+	switch (cmd) {
+
+	case TIOCMGET:
+		dbg("TIOCMGET");
+		/* Request the status from the device */
+		if ((ret = usb_control_msg(serial->dev, 
+					   usb_rcvctrlpipe(serial->dev, 0),
+					   FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
+					   FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+					   0, 0, 
+					   buf, 1, HZ * 5)) < 0 ) {
+			dbg("Get not get modem status of device");
+			return(ret);
+		}
+
+		return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
+				(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
+				(buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
+				(buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0),
+				(unsigned long *) arg);
+		break;
+
+	case TIOCMSET: /* Turns on and off the lines as specified by the mask */
+		dbg("TIOCMSET");
+		if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+		urb_value = ((mask & TIOCM_DTR) ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW);
+		if ((ret = usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    urb_value , 0,
+				    buf, 0, WDR_TIMEOUT)) < 0){
+			err("Urb to set DTR failed");
+			return(ret);
+		}
+		urb_value = ((mask & TIOCM_RTS) ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW);
+		if ((ret = usb_control_msg(serial->dev, 
+				    usb_sndctrlpipe(serial->dev, 0),
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+				    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+				    urb_value , 0,
+				    buf, 0, WDR_TIMEOUT)) < 0){
+			err("Urb to set RTS failed");
+			return(ret);
+		}
+		break;
+					
+	case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
+		dbg("TIOCMBIS");
+ 	        if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+  	        if (mask & TIOCM_DTR){
+			if ((ret = usb_control_msg(serial->dev, 
+					    usb_sndctrlpipe(serial->dev, 0),
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+					    FTDI_SIO_SET_DTR_HIGH , 0,
+					    buf, 0, WDR_TIMEOUT)) < 0){
+				err("Urb to set DTR failed");
+				return(ret);
+				}
+			}
+			if (mask & TIOCM_RTS) {
+			if ((ret = usb_control_msg(serial->dev, 
+					    usb_sndctrlpipe(serial->dev, 0),
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+					    FTDI_SIO_SET_RTS_HIGH , 0,
+					    buf, 0, WDR_TIMEOUT)) < 0){
+				err("Urb to set RTS failed");
+				return(ret);
+			}
+		}
+					break;
+
+	case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
+		dbg("TIOCMBIC");
+ 	        if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+  	        if (mask & TIOCM_DTR){
+			if ((ret = usb_control_msg(serial->dev, 
+					    usb_sndctrlpipe(serial->dev, 0),
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+					    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+					    FTDI_SIO_SET_DTR_LOW , 0,
+					    buf, 0, WDR_TIMEOUT)) < 0){
+				err("Urb to unset DTR failed");
+				return(ret);
+			}
+		}	
+		if (mask & TIOCM_RTS) {
+			if ((ret = usb_control_msg(serial->dev, 
+					       usb_sndctrlpipe(serial->dev, 0),
+					       FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+					       FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+					    FTDI_SIO_SET_RTS_LOW , 0,
+					    buf, 0, WDR_TIMEOUT)) < 0){
+				err("Urb to unset RTS failed");
+				return(ret);
+			}
+		}
+		break;
+
+		/*
+		 * I had originally implemented TCSET{A,S}{,F,W} and
+		 * TCGET{A,S} here separately, however when testing I
+		 * found that the higher layers actually do the termios
+		 * conversions themselves and pass the call onto
+		 * ftdi_sio_set_termios. 
+		 *
+		 */
+
+	default:
+	  /* This is not an error - turns out the higher layers will do 
+	   *  some ioctls itself (see comment above)
+ 	   */
+		dbg("ftdi_sio ioctl arg not supported - it was 0x%04x",cmd);
+		return(-ENOIOCTLCMD);
+		break;
+	}
+	dbg("ftdi_sio_ioctl returning 0");
+	return 0;
+} /* ftdi_sio_ioctl */
+
+
+static int __init ftdi_sio_init (void)
+{
+	usb_serial_register (&ftdi_sio_device);
+	return 0;
+}
+
+
+static void __exit ftdi_sio_exit (void)
+{
+	usb_serial_deregister (&ftdi_sio_device);
+}
+
+
+module_init(ftdi_sio_init);
+module_exit(ftdi_sio_exit);
+
+MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>");
+MODULE_DESCRIPTION("USB FTDI SIO driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/ftdi_sio.h linux.18p17/drivers/usb/serial/ftdi_sio.h
--- linux.vanilla/drivers/usb/serial/ftdi_sio.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/ftdi_sio.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,413 @@
+/*
+ * Definitions for the FTDI USB Single Port Serial Converter - 
+ * known as FTDI_SIO (Serial Input/Output application of the chipset) 
+ *
+ * The example I have is known as the USC-1000 which is available from
+ * http://www.dse.co.nz - cat no XH4214 It looks similar to this:
+ * http://www.dansdata.com/usbser.htm but I can't be sure There are other
+ * USC-1000s which don't look like my device though so beware!
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, 
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc.- wrote the 
+ * FTDI_SIO implementation.
+ *
+ */
+
+#define FTDI_VID	0x0403	/* Vendor Id */
+#define FTDI_SIO_PID	0x8372	/* Product Id */
+
+/* Vendor Request Interface */
+#define FTDI_SIO_RESET 		0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL 	1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL	2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE	3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA	4 /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_MODEM_STATUS	5 /* Retrieve current value of modern status register */
+#define FTDI_SIO_SET_EVENT_CHAR	6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR	7 /* Set the error character */
+
+/* Port Identifier Table */
+#define PIT_DEFAULT 		0 /* SIOA */
+#define PIT_SIOA		1 /* SIOA */
+/* The device this driver is tested with one has only one port */
+#define PIT_SIOB		2 /* SIOB */
+#define PIT_PARALLEL		3 /* Parallel */
+
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET
+#define FTDI_SIO_RESET_REQUEST_TYPE 0x40
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_RESET
+ * wValue:         Control Value 
+ *                   0 = Reset SIO
+ *                   1 = Purge RX buffer
+ *                   2 = Purge TX buffer
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           None
+ *
+ * The Reset SIO command has this effect:
+ *
+ *    Sets flow control set to 'none'
+ *    Event char = $0D
+ *    Event trigger = disabled
+ *    Purge RX buffer
+ *    Purge TX buffer
+ *    Clear DTR
+ *    Clear RTS
+ *    baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+   */
+
+/* FTDI_SIO_SET_BAUDRATE */
+#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_SET_BAUDRATE
+ * wValue:         BaudRate value - see below
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           None
+ */
+
+typedef enum {
+ ftdi_sio_b300 = 0, 
+ ftdi_sio_b600 = 1, 
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+} FTDI_SIO_baudrate_t ;
+
+#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA
+#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8 )
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11 )
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11 )
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11 )
+
+/* FTDI_SIO_SET_DATA */
+
+/*
+ * BmRequestType:  0100 0000B 
+ * bRequest:       FTDI_SIO_SET_DATA
+ * wValue:         Data characteristics (see below)
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           No
+ *
+ * Data characteristics
+ *
+ *   B0..7   Number of data bits
+ *   B8..10  Parity
+ *           0 = None
+ *           1 = Odd
+ *           2 = Even
+ *           3 = Mark
+ *           4 = Space
+ *   B11..13 Stop Bits
+ *           0 = 1
+ *           1 = 1.5
+ *           2 = 2
+ *   B14..15 Reserved
+ *
+ */
+
+
+
+/* FTDI_SIO_MODEM_CTRL */
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL
+
+/* 
+ * BmRequestType:   0100 0000B
+ * bRequest:        FTDI_SIO_MODEM_CTRL
+ * wValue:          ControlValue (see below)
+ * wIndex:          Port
+ * wLength:         0
+ * Data:            None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ */
+
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH ( 1 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_DTR_LOW  ( 0 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 ))
+#define FTDI_SIO_SET_RTS_LOW ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 ))
+
+/*
+ * ControlValue
+ * B0    DTR state
+ *          0 = reset
+ *          1 = set
+ * B1    RTS state
+ *          0 = reset
+ *          1 = set
+ * B2..7 Reserved
+ * B8    DTR state enable
+ *          0 = ignore
+ *          1 = use DTR state
+ * B9    RTS state enable
+ *          0 = ignore
+ *          1 = use RTS state
+ * B10..15 Reserved
+ */
+
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 
+#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
+#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
+#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
+/*
+ *   BmRequestType:  0100 0000b
+ *   bRequest:       FTDI_SIO_SET_FLOW_CTRL
+ *   wValue:         Xoff/Xon
+ *   wIndex:         Protocol/Port - hIndex is protocl / lIndex is port
+ *   wLength:        0 
+ *   Data:           None
+ *
+ * hIndex protocol is:
+ *   B0 Output handshaking using RTS/CTS
+ *       0 = disabled
+ *       1 = enabled
+ *   B1 Output handshaking using DTR/DSR
+ *       0 = disabled
+ *       1 = enabled
+ *   B2 Xon/Xoff handshaking
+ *       0 = disabled
+ *       1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the XOFF character 
+ * and the lValue field contains the XON character.
+ */  
+ 
+/*
+ * FTDI_SIO_SET_EVENT_CHAR 
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+
+#define  FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR
+#define  FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40
+
+
+/* 
+ *  BmRequestType:   0100 0000b
+ *  bRequest:        FTDI_SIO_SET_EVENT_CHAR
+ *  wValue:          EventChar
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            None
+ *
+ * wValue:
+ *   B0..7   Event Character
+ *   B8      Event Character Processing
+ *             0 = disabled
+ *             1 = enabled
+ *   B9..15  Reserved
+ *
+ */
+          
+/* FTDI_SIO_SET_ERROR_CHAR */
+
+/* Set the parity error replacement character for the specified communications port */
+
+/* 
+ *  BmRequestType:  0100 0000b
+ *  bRequest:       FTDI_SIO_SET_EVENT_CHAR
+ *  wValue:         Error Char
+ *  wIndex:         Port
+ *  wLength:        0
+ *  Data:           None
+ *
+ *Error Char
+ *  B0..7  Error Character
+ *  B8     Error Character Processing
+ *           0 = disabled
+ *           1 = enabled
+ *  B9..15 Reserved
+ *
+ */
+
+/* FTDI_SIO_GET_MODEM_STATUS */
+/* Retreive the current value of the modem status register */
+
+#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0
+#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK  0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+/* 
+ *   BmRequestType:   1100 0000b
+ *   bRequest:        FTDI_SIO_GET_MODEM_STATUS
+ *   wValue:          zero
+ *   wIndex:          Port
+ *   wLength:         1
+ *   Data:            Status
+ * 
+ * One byte of data is returned 
+ * B0..3 0
+ * B4    CTS
+ *         0 = inactive
+ *         1 = active
+ * B5    DSR
+ *         0 = inactive
+ *         1 = active
+ * B6    Ring Indicator (RI)
+ *         0 = inactive
+ *         1 = active
+ * B7    Receive Line Signal Detect (RLSD)
+ *         0 = inactive
+ *         1 = active 
+ */
+
+
+
+/* Descriptors returned by the device 
+ * 
+ *  Device Descriptor
+ * 
+ * Offset	Field		Size	Value	Description
+ * 0	bLength		1	0x12	Size of descriptor in bytes
+ * 1	bDescriptorType	1	0x01	DEVICE Descriptor Type
+ * 2	bcdUSB		2	0x0110	USB Spec Release Number
+ * 4	bDeviceClass	1	0x00	Class Code
+ * 5	bDeviceSubClass	1	0x00	SubClass Code
+ * 6	bDeviceProtocol	1	0x00	Protocol Code
+ * 7	bMaxPacketSize0 1	0x08	Maximum packet size for endpoint 0
+ * 8	idVendor	2	0x0403	Vendor ID
+ * 10	idProduct	2	0x8372	Product ID (FTDI_SIO_PID)
+ * 12	bcdDevice	2	0x0001	Device release number
+ * 14	iManufacturer	1	0x01	Index of man. string desc
+ * 15	iProduct	1	0x02	Index of prod string desc
+ * 16	iSerialNumber	1	0x02	Index of serial nmr string desc
+ * 17	bNumConfigurations 1    0x01	Number of possible configurations
+ * 
+ * Configuration Descriptor
+ * 
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x09	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x02	CONFIGURATION Descriptor Type
+ * 2	wTotalLength		2	0x0020	Total length of data
+ * 4	bNumInterfaces		1	0x01	Number of interfaces supported
+ * 5	bConfigurationValue	1	0x01	Argument for SetCOnfiguration() req
+ * 6	iConfiguration		1	0x02	Index of config string descriptor
+ * 7	bmAttributes		1	0x20	Config characteristics Remote Wakeup
+ * 8	MaxPower		1	0x1E	Max power consumption
+ * 
+ * Interface Descriptor
+ * 
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x09	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x04	INTERFACE Descriptor Type
+ * 2	bInterfaceNumber	1	0x00	Number of interface
+ * 3	bAlternateSetting	1	0x00	Value used to select alternate
+ * 4	bNumEndpoints		1	0x02	Number of endpoints
+ * 5	bInterfaceClass		1	0xFF	Class Code
+ * 6	bInterfaceSubClass	1	0xFF	Subclass Code
+ * 7	bInterfaceProtocol	1	0xFF	Protocol Code
+ * 8	iInterface		1	0x02	Index of interface string description
+ * 
+ * IN Endpoint Descriptor
+ * 
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x07	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x05	ENDPOINT descriptor type
+ * 2	bEndpointAddress	1	0x82	Address of endpoint
+ * 3	bmAttributes		1	0x02	Endpoint attributes - Bulk
+ * 4	bNumEndpoints		2	0x0040	maximum packet size
+ * 5	bInterval		1	0x00	Interval for polling endpoint
+ * 
+ * OUT Endpoint Descriptor
+ * 
+ * Offset	Field			Size	Value
+ * 0	bLength			1	0x07	Size of descriptor in bytes
+ * 1	bDescriptorType		1	0x05	ENDPOINT descriptor type
+ * 2	bEndpointAddress	1	0x02	Address of endpoint
+ * 3	bmAttributes		1	0x02	Endpoint attributes - Bulk
+ * 4	bNumEndpoints		2	0x0040	maximum packet size
+ * 5	bInterval		1	0x00	Interval for polling endpoint
+ *     
+ * DATA FORMAT
+ * 
+ * IN Endpoint
+ * 
+ * The device reserves the first two bytes of data on this endpoint to contain the current
+ * values of the modem and line status registers. In the absence of data, the device 
+ * generates a message consisting of these two status bytes every 40 ms
+ * 
+ * Byte 0: Modem Status
+ * 
+ * Offset	Description
+ * B0	Reserved - must be 1
+ * B1	Reserved - must be 0
+ * B2	Reserved - must be 0
+ * B3	Reserved - must be 0
+ * B4	Clear to Send (CTS)
+ * B5	Data Set Ready (DSR)
+ * B6	Ring Indicator (RI)
+ * B7	Receive Line Signal Detect (RLSD)
+ * 
+ * Byte 1: Line Status
+ * 
+ * Offset	Description
+ * B0	Data Ready (DR)
+ * B1	Overrun Error (OE)
+ * B2	Parity Error (PE)
+ * B3	Framing Error (FE)
+ * B4	Break Interrupt (BI)
+ * B5	Transmitter Holding Register (THRE)
+ * B6	Transmitter Empty (TEMT)
+ * B7	Error in RCVR FIFO
+ * 
+ * OUT Endpoint
+ * 
+ * This device reserves the first bytes of data on this endpoint contain the length
+ * and port identifier of the message. For the FTDI USB Serial converter the port 
+ * identifier is always 1.
+ * 
+ * Byte 0: Line Status
+ * 
+ * Offset	Description
+ * B0	Reserved - must be 1
+ * B1	Reserved - must be 0
+ * B2..7	Length of message - (not including Byte 0)
+ * 
+ */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan.c linux.18p17/drivers/usb/serial/keyspan.c
--- linux.vanilla/drivers/usb/serial/keyspan.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,725 @@
+/*
+  Keyspan USB to Serial Converter driver
+ 
+  (C) Copyright (C) 2000
+      Hugh Blemings <hugh@linuxcare.com>
+   
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  See http://www.linuxcare.com.au/hugh/keyspan.html for more
+  information on this driver.
+  
+  Code in this driver inspired by and in a number of places taken
+  from Brian Warner's original Keyspan-PDA driver.
+
+  This driver has been put together with the support of Innosys, Inc.
+  and Keyspan, Inc the manufacturers of the Keyspan USB-serial products.
+  Thanks Guys :)
+  
+  Tip 'o the hat to Linuxcare for supporting staff in their work on
+  open source projects.
+
+  Wed Jul 19 14:00:42 EST 2000 gkh
+	Added module_init and module_exit functions to handle the fact that this
+	driver is a loadable module now.
+
+  Tue Jul 18 16:14:52 EST 2000 Hugh
+    Basic character input/output for USA-19 now mostly works,
+    fixed at 9600 baud for the moment.
+
+*/
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+#include "keyspan.h"
+
+	/* Per device and per port private data */
+struct keyspan_serial_private {
+	struct urb	*in_urbs[8];
+	struct urb	*out_urbs[8];
+	char		out_buffer[64];
+	char		in_buffer[64];
+};
+
+struct keyspan_port_private {
+ 		/* Keep track of which output endpoint to use */
+	int		out_flip;
+
+		/* Settings for the port */
+	int		baud;
+	int		old_baud;
+	enum		{parity_none, parity_odd, parity_even} parity;
+	enum		{flow_none, flow_cts, flow_xon} flow_control;
+	int		rts_state;
+	int		dtr_state;
+
+};
+
+
+	/* FIXME this will break if multiple physical interfaces used */
+static wait_queue_head_t	out_wait;
+	
+	/* Include Keyspan message headers (not both yet, need some tweaks
+	   to get clean build) */
+/*#include "keyspan_usa26msg.h"*/
+#include "keyspan_usa28msg.h"
+	
+	/* If you don't get debugging output, uncomment the following
+	   two lines to enable cheat. */
+#undef 	dbg
+#define	dbg	printk
+
+	/* Functions - mostly stubs for now */
+static void keyspan_rx_throttle (struct usb_serial_port *port)
+{
+	dbg("keyspan_rx_throttle port %d", port->number);
+}
+
+
+static void keyspan_rx_unthrottle (struct usb_serial_port *port)
+{
+	dbg("keyspan_rx_unthrottle port %d", port->number);
+}
+
+
+static void keyspan_break_ctl (struct usb_serial_port *port, int break_state)
+{
+	dbg("keyspan_break_ctl");
+}
+
+
+static void keyspan_set_termios (struct usb_serial_port *port, 
+				     struct termios *old_termios)
+{
+	dbg("keyspan_set_termios");
+}
+
+static int keyspan_ioctl(struct usb_serial_port *port, struct file *file,
+			     unsigned int cmd, unsigned long arg)
+{
+	unsigned int		value;
+
+	dbg("keyspan_ioctl_info");
+	
+	switch (cmd) {
+	case TIOCMGET:
+		value = TIOCM_DTR | TIOCM_RNG;
+		if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) {
+			return -EFAULT;
+		}
+		else {
+			return 0;
+		}
+		
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int keyspan_write(struct usb_serial_port *port, int from_user, 
+			     const unsigned char *buf, int count)
+{
+	struct usb_serial 		*serial = port->serial;
+	struct keyspan_serial_private 	*s_priv;
+	struct keyspan_port_private 	*p_priv;
+	int				current_urb;
+	int				i;
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+	p_priv = (struct keyspan_port_private *)(port->private);
+	
+	if (p_priv->out_flip == 0) {
+		current_urb = 0;
+		p_priv->out_flip = 1;
+	}
+	else {
+		current_urb = 1;
+		p_priv->out_flip = 0;
+	}
+
+	dbg("keyspan_write called for port %d (%d) chars {", port->number, count);
+	for (i = 0; i < count ; i++) {
+		dbg("%02x ", buf[i]);
+	}
+	dbg("}\n");
+
+	if (count == 0) {
+		dbg("write request of 0 bytes");
+		return (0);
+	}
+
+		/* only send data if we have a bulk out endpoint */
+	if (s_priv->out_urbs[current_urb]) {
+		while (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) {
+			dbg (__FUNCTION__ " INPROGRES\n");
+			interruptible_sleep_on(&out_wait);
+			if (signal_pending(current)) {
+				dbg (__FUNCTION__ " signal\n");
+				return (-ERESTARTSYS);
+			}
+		}
+		/*if (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) {
+			dbg ("already writing");
+			return (-EAGAIN);
+		}*/
+			/* First byte in buffer is "last flag" - unused so
+			   for now so set to zero */
+		memset(s_priv->out_urbs[current_urb]->transfer_buffer, 0, 1);
+
+		if (from_user) {
+			copy_from_user(s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count);
+		}
+		else {
+			memcpy (s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count);
+		}  
+
+		/* send the data out the bulk port */
+		s_priv->out_urbs[current_urb]->transfer_buffer_length = count + 1;
+
+		if (usb_submit_urb(s_priv->out_urbs[current_urb])) {
+			dbg("usb_submit_urb(write bulk) failed");
+		}
+
+		return (count);
+	}
+	
+	/* no bulk out, so return 0 bytes written */
+	return (0);
+}
+
+
+static void keyspan_write_bulk_callback (struct urb *urb)
+{
+	int		endpoint;
+	
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+	dbg("keyspan_write_bulk_callback for endpoint %d\n", endpoint);
+
+		/* Only do wakeup if this callback is from one of the data
+		   endpoints.  */
+	if (endpoint == 2 || endpoint == 3) {
+		wake_up_interruptible(&out_wait);
+	}
+
+}
+
+
+static void keyspan_read_bulk_callback (struct urb *urb)
+{
+	int			i;
+	int			endpoint;
+	struct usb_serial	*serial;
+	struct usb_serial_port	*port;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+
+	endpoint = usb_pipeendpoint(urb->pipe);
+
+
+	if (urb->status) {
+		dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.\n",
+			      		urb->status, endpoint);
+		return;
+	}
+
+	switch (endpoint) {
+
+			/* If this is one of the data endpoints, stuff it's
+		   	   contents into the tty flip_buffer. */
+		case 1:
+		case 2: serial = (struct usb_serial *) urb->context;
+			port = &serial->port[0];
+			tty = port->tty;
+			if (urb->actual_length) {
+				for (i = 0; i < urb->actual_length ; ++i) {
+					 tty_insert_flip_char(tty, data[i], 0);
+				}
+				tty_flip_buffer_push(tty);
+			}
+			break;
+
+			/* INACK endpoint */	
+		case 3: dbg(__FUNCTION__ " callback for INACK endpoint\n");
+			break;
+
+			/* INSTAT endpoint */
+		case 4: dbg(__FUNCTION__ " callback for INSTAT endpoint\n");
+			break;
+		
+		default:
+		       	dbg(__FUNCTION__ " callback for unknown endpoint!\n");
+			break;
+	}
+				
+		/* Resubmit urb so we continue receiving */
+	if (usb_submit_urb(urb)) {
+		dbg(__FUNCTION__ "resubmit read urb failed.\n");
+	}
+	return;
+	
+}
+
+static int keyspan_write_room (struct usb_serial_port *port)
+{
+//	dbg("keyspan_write_room called\n");
+	return (32);
+
+}
+
+
+static int keyspan_chars_in_buffer (struct usb_serial_port *port)
+{
+	return (0);
+}
+
+
+static int keyspan_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct keyspan_port_private 	*p_priv;
+	struct keyspan_serial_private 	*s_priv;
+	struct usb_serial 		*serial = port->serial;
+	int				i;
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+	p_priv = (struct keyspan_port_private *)(port->private);
+	
+	dbg("keyspan_open called.\n");
+	
+	if (port->active) {
+		dbg(__FUNCTION__ "port->active already true!\n");
+		return (-EINVAL);
+	}
+
+	p_priv = (struct keyspan_port_private *)(port->private);
+	
+	p_priv->out_flip = 0;
+	port->active = 1;
+
+		/* Start reading from port */
+	for (i = 0; i < 4; i++) {
+		if (s_priv->in_urbs[i]) {
+			if (usb_submit_urb(s_priv->in_urbs[i])) {
+				dbg(__FUNCTION__ " submit in urb %d failed", i);
+			}
+		}
+
+	}
+	
+	keyspan_usa19_send_setup(serial, port);
+
+	return (0);
+}
+
+
+static void keyspan_close(struct usb_serial_port *port, struct file *filp)
+{
+	int			i;
+	struct usb_serial	*serial = port->serial; /* FIXME should so sanity check */
+	struct keyspan_serial_private 	*s_priv;
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+	
+		/* Stop reading/writing urbs */
+	for (i = 0; i < 4; i++) {
+		if (s_priv->in_urbs[i]) {
+			usb_unlink_urb(s_priv->in_urbs[i]);
+		}
+
+	}
+	for (i = 0; i < 3; i++) {
+		if (s_priv->out_urbs[i]) {
+			usb_unlink_urb(s_priv->out_urbs[i]);
+		}
+
+	}
+	port->active = 0;
+	dbg("keyspan_close called\n");
+}
+
+
+	/* download the firmware to a pre-renumeration device */
+static int keyspan_fake_startup (struct usb_serial *serial)
+{
+	int 				response;
+	const struct ezusb_hex_record 	*record;
+	char				*fw_name;
+
+	dbg("Keyspan startup version %04x product %04x\n", serial->dev->descriptor.bcdDevice,
+		       					serial->dev->descriptor.idProduct); 
+	
+	if ((serial->dev->descriptor.bcdDevice & 0x8000) != 0x8000) {
+		dbg("Firmware already loaded.  Quitting.\n");
+		return(1);
+	}
+
+		/* Select firmware image on the basis of idProduct */
+	switch (serial->dev->descriptor.idProduct) {
+		case 0x0101: record = &keyspan_usa28_firmware[0];
+			     fw_name = "USA28";
+			     break;
+			     
+		case 0x0102: record = &keyspan_usa28x_firmware[0];
+			     fw_name = "USA28X";
+			     break;
+
+		case 0x0103: record = &keyspan_usa19_firmware[0];
+			     fw_name = "USA19";
+			     break;
+			     
+		case 0x0105: record = &keyspan_usa18x_firmware[0];
+			     fw_name = "USA18X";
+			     break;
+			     
+		case 0x0106: record = &keyspan_usa19w_firmware[0];
+			     fw_name = "USA19W";
+			     break;
+		
+		default:     record = NULL;
+			     fw_name = "Unknown";
+			     break;
+	}
+
+	if (record == NULL) {
+		err("Required keyspan firmware image (%s) unavailable.\n", fw_name);
+		return(1);
+	}
+
+	dbg("Uploading Keyspan %s firmware.\n", fw_name);
+
+		/* download the firmware image */
+	response = ezusb_set_reset(serial, 1);
+
+	while(record->address != 0xffff) {
+		response = ezusb_writememory(serial, record->address,
+					     (unsigned char *)record->data,
+					     record->data_size, 0xa0);
+		if (response < 0) {
+			err("ezusb_writememory failed for Keyspan"
+			    "firmware (%d %04X %p %d)",
+			    response, 
+			    record->address, record->data, record->data_size);
+			break;
+		}
+		record++;
+	}
+		/* bring device out of reset. Renumeration will occur in a
+		   moment and the new device will bind to the real driver */
+	response = ezusb_set_reset(serial, 0);
+
+		/* we don't want this device to have a driver assigned to it. */
+	return (1);
+}
+
+	/* USA-19 uses three output endpoints and four input
+	   endpoints.  First two output endpoints are for
+	   data (used in an alternating fashion), the third is
+	   output control.  First two input endpoints are for
+	   data (again alternating), the third is the ACK
+	   endpoint, the fourth is input status. */
+static void keyspan_usa19_setup_urbs(struct usb_serial *serial)
+{
+	struct keyspan_serial_private 	*s_priv;
+	int			i;
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+
+		/* Output urbs first */
+	dbg(__FUNCTION__ "Allocating output urbs.\n");
+	for (i = 0; i < 3; i++) {
+
+		s_priv->out_urbs[i] = usb_alloc_urb (0);	/* No ISO */
+		if (!s_priv->out_urbs[i]) {
+			dbg (__FUNCTION__ "Alloc for %d out urb failed.\n", i);
+			return;
+		}
+
+		FILL_BULK_URB(s_priv->out_urbs[i], serial->dev, 
+			      usb_sndbulkpipe(serial->dev, i + 1),
+			      &s_priv->out_buffer[i], sizeof(s_priv->out_buffer[i]),
+			      keyspan_write_bulk_callback, 
+			      serial);
+	}
+
+		/* Now input urbs */
+	dbg(__FUNCTION__ "Allocating input urbs.\n");
+	for (i = 0; i < 4; i++) {
+
+		s_priv->in_urbs[i] = usb_alloc_urb (0);	/* No ISO */
+		if (!s_priv->in_urbs[i]) {
+			dbg (__FUNCTION__ "Alloc for %d in urb failed.\n", i);
+			return;
+		}
+
+		FILL_BULK_URB(s_priv->in_urbs[i], serial->dev, 
+			      usb_rcvbulkpipe(serial->dev, i + 0x81),
+			      &s_priv->in_buffer[i], sizeof(s_priv->in_buffer[i]),
+			      keyspan_read_bulk_callback, 
+			      serial);
+	}
+	
+}
+
+static int keyspan_usa19_calc_baud(u32 baud_rate, u8 *rate_hi, u8 *rate_low)
+{
+	u32 b16,	/* baud rate times 16 (actual rate used internally) */
+		div,	/* divisor */	
+		cnt;	/* inverse of divisor (programmed into 8051) */
+
+		/* prevent divide by zero...  */
+	if( (b16 = (baud_rate * 16L)) == 0) {
+		return (KEYSPAN_INVALID_BAUD_RATE);
+	}
+
+		/* calculate the divisor and the counter (its inverse) */
+	if( (div = (USA19_BAUDCLK / b16)) == 0) {
+		return (KEYSPAN_INVALID_BAUD_RATE);
+	}
+	else {
+		cnt = 0 - div;
+	}
+
+	if(div > 0xffff) {
+		return (KEYSPAN_INVALID_BAUD_RATE);
+	}
+
+		/* return the counter values */
+	*rate_low = (u8) (cnt & 0xff);
+	*rate_hi = (u8) ((cnt >> 8) & 0xff);
+	
+	dbg(__FUNCTION__ " Baud rate of %d is %02x %02x.\n", baud_rate, *rate_hi, *rate_low);
+
+	return (KEYSPAN_BAUD_RATE_OK);
+}
+
+static int keyspan_usa19_send_setup(struct usb_serial *serial, struct usb_serial_port *port)
+{
+	struct portControlMessage	msg;		
+	struct keyspan_serial_private 	*s_priv;
+	struct keyspan_port_private 	*p_priv;
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+	p_priv = (struct keyspan_port_private *)(port->private);
+
+	//memset(msg, 0, sizeof (struct portControlMessage));
+		
+	msg.setBaudRate = 1;
+	if (keyspan_usa19_calc_baud(9600, &msg.baudHi, &msg.baudLo) ==
+		KEYSPAN_INVALID_BAUD_RATE ) {
+		dbg(__FUNCTION__ "Invalid baud rate requested %d.\n", 9600);
+		msg.baudLo = 0xff;
+		msg.baudHi = 0xb2;	/* Values for 9600 baud */
+	}
+
+		/* If parity is enabled, we must calculate it ourselves. */
+	if (p_priv->parity) {
+		msg.parity = 1;
+	}
+	else {
+		msg.parity = 0;
+	}
+
+	msg.ctsFlowControl = 0;
+	msg.xonFlowControl = 0;
+	msg.rts = 1;
+	msg.dtr = 0;
+	
+	msg.forwardingLength = 1;
+	msg.forwardMs = 10;
+	msg.breakThreshold = 45;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+	
+	msg._txOn = 1;
+	msg._txOff = 0;
+	msg.txFlush = 0;
+	msg.txForceXoff = 0;
+	msg.txBreak = 0;
+	msg.rxOn = 1;
+	msg.rxOff = 0;
+	msg.rxFlush = 0;
+	msg.rxForward = 0;
+	msg.returnStatus = 1;
+	msg.resetDataToggle = 1;
+
+		
+	/* only do something if we have a bulk out endpoint */
+	if (s_priv->out_urbs[2]) {
+		if (s_priv->out_urbs[2]->status == -EINPROGRESS) {
+			dbg (__FUNCTION__ " already writing");
+			return(-1);
+		}
+		memcpy (s_priv->out_urbs[2]->transfer_buffer, &msg, sizeof(msg));
+	
+			/* send the data out the device on control endpoint */
+		s_priv->out_urbs[2]->transfer_buffer_length = sizeof(msg);
+
+		if (usb_submit_urb(s_priv->out_urbs[2])) {
+			dbg(__FUNCTION__ " usb_submit_urb(setup) failed\n");
+		}
+		else {
+			dbg(__FUNCTION__ " usb_submit_urb(setup) OK %d bytes\n", s_priv->out_urbs[2]->transfer_buffer_length);
+		}
+	
+	}
+	return (0);
+}
+
+
+	/* Gets called by the "real" driver (ie once firmware is loaded
+	   and renumeration has taken place. */
+static int keyspan_startup (struct usb_serial *serial)
+{
+	int			i;
+	struct usb_serial_port	*port;
+
+	dbg("keyspan_startup called.\n");
+
+		/* Setup private data for serial driver */
+	serial->private = kmalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
+	if (!serial->private) {
+		dbg(__FUNCTION__ "kmalloc for keyspan_serial_private failed!.\n");
+		return (1);
+	}
+	memset(serial->private, 0, sizeof(struct keyspan_serial_private));
+		
+	init_waitqueue_head(&out_wait);
+	
+		/* Now setup per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = &serial->port[i];
+		port->private = kmalloc(sizeof(struct keyspan_port_private), GFP_KERNEL);
+		if (!port->private) {
+			dbg(__FUNCTION__ "kmalloc for keyspan_port_private (%d) failed!.\n", i);
+			return (1);
+		}
+		memset(port->private, 0, sizeof(struct keyspan_port_private));
+	}
+
+	
+
+	switch (serial->dev->descriptor.idProduct) {
+		case 0x0107:	keyspan_usa19_setup_urbs(serial);
+				//keyspan_send_usa19_setup(serial);
+				break;
+
+		default:	break;
+	}
+
+			
+	return (0);
+}
+
+static void keyspan_shutdown (struct usb_serial *serial)
+{
+	int				i;
+	struct usb_serial_port		*port;
+	struct keyspan_serial_private 	*s_priv;
+
+	dbg("keyspan_shutdown called freeing ");
+
+	s_priv = (struct keyspan_serial_private *)(serial->private);
+
+		/* Stop reading/writing urbs */
+	for (i = 0; i < 4; i++) {
+		if (s_priv->in_urbs[i]) {
+			usb_unlink_urb(s_priv->in_urbs[i]);
+		}
+
+	}
+	for (i = 0; i < 3; i++) {
+		if (s_priv->out_urbs[i]) {
+			usb_unlink_urb(s_priv->out_urbs[i]);
+		}
+
+	}
+		/* Now free them */
+	for (i = 0; i < 7; i ++) {
+		if (s_priv->in_urbs[i] != NULL) {
+			dbg("in%d ", i);
+			usb_free_urb(s_priv->in_urbs[i]);
+		}
+		
+		if (s_priv->out_urbs[i] != NULL) {
+			dbg("out%d ", i);
+			usb_free_urb(s_priv->out_urbs[i]);
+		}
+	}
+	dbg("urbs.\n");
+
+	dbg("Freeing serial->private.\n");	
+	kfree(serial->private);
+
+	dbg("Freeing port->private.\n");	
+		/* Now free per port private data */
+	for (i = 0; i < serial->num_ports; i++) {
+		port = &serial->port[i];
+		kfree(port->private);
+	}
+	
+}
+
+
+static int __init keyspan_init (void)
+{
+	usb_serial_register (&keyspan_usa18x_pre_device);
+	usb_serial_register (&keyspan_usa19_pre_device);
+	usb_serial_register (&keyspan_usa19w_pre_device);
+	usb_serial_register (&keyspan_usa28_pre_device);
+	usb_serial_register (&keyspan_usa28x_pre_device);
+	usb_serial_register (&keyspan_usa18x_device);
+	usb_serial_register (&keyspan_usa19_device);
+	usb_serial_register (&keyspan_usa19w_device);
+	usb_serial_register (&keyspan_usa28_device);
+	usb_serial_register (&keyspan_usa28x_device);
+	return 0;
+}
+
+
+static void __exit keyspan_exit (void)
+{
+	usb_serial_deregister (&keyspan_usa18x_pre_device);
+	usb_serial_deregister (&keyspan_usa19_pre_device);
+	usb_serial_deregister (&keyspan_usa19w_pre_device);
+	usb_serial_deregister (&keyspan_usa28_pre_device);
+	usb_serial_deregister (&keyspan_usa28x_pre_device);
+	usb_serial_deregister (&keyspan_usa18x_device);
+	usb_serial_deregister (&keyspan_usa19_device);
+	usb_serial_deregister (&keyspan_usa19w_device);
+	usb_serial_deregister (&keyspan_usa28_device);
+	usb_serial_deregister (&keyspan_usa28x_device);
+}
+
+
+module_init(keyspan_init);
+module_exit(keyspan_exit);
+
+MODULE_AUTHOR("Hugh Blemings <hugh@linuxcare.com>");
+MODULE_DESCRIPTION("Keyspan USB to Serial Converter driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan.h linux.18p17/drivers/usb/serial/keyspan.h
--- linux.vanilla/drivers/usb/serial/keyspan.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan.h	Fri Sep  8 17:20:11 2000
@@ -0,0 +1,327 @@
+/*
+  Keyspan USB to Serial Converter driver
+ 
+  (C) Copyright (C) 2000
+      Hugh Blemings <hugh@linuxcare.com>
+   
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  See http://www.linuxcare.com.au/hugh/keyspan.html for more
+  information on this driver.
+  
+  Code in this driver inspired by and in a number of places taken
+  from Brian Warner's original Keyspan-PDA driver.
+
+  This driver has been put together with the support of Innosys, Inc.
+  and Keyspan, Inc the manufacturers of the Keyspan USB-serial products.
+  Thanks Guys :)
+  
+  Tip 'o the hat to Linuxcare for supporting staff in their work on
+  open source projects.
+  
+  See keyspan.c for update history.
+
+*/
+
+#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
+#define __LINUX_USB_SERIAL_KEYSPAN_H
+
+#include <linux/config.h>
+
+	/* Function prototypes for Keyspan serial converter */
+static int  keyspan_open		(struct usb_serial_port *port,
+					 struct file *filp);
+static void keyspan_close		(struct usb_serial_port *port,
+					 struct file *filp);
+static int  keyspan_startup		(struct usb_serial *serial);
+static void keyspan_shutdown		(struct usb_serial *serial);
+static void keyspan_rx_throttle		(struct usb_serial_port *port);
+static void keyspan_rx_unthrottle	(struct usb_serial_port *port);
+static int  keyspan_write_room		(struct usb_serial_port *port);
+static int  keyspan_write		(struct usb_serial_port *port,
+					 int from_user,
+					 const unsigned char *buf,
+					 int count);
+static void keyspan_write_bulk_callback (struct urb *urb);
+static void keyspan_read_bulk_callback  (struct urb *urb);
+static int  keyspan_chars_in_buffer 	(struct usb_serial_port *port);
+static int  keyspan_ioctl		(struct usb_serial_port *port,
+					 struct file *file,
+					 unsigned int cmd,
+					 unsigned long arg);
+static void keyspan_set_termios		(struct usb_serial_port *port,
+					 struct termios *old);
+static void keyspan_break_ctl		(struct usb_serial_port *port,
+					 int break_state);
+static int  keyspan_fake_startup	(struct usb_serial *serial);
+
+static int  keyspan_usa19_calc_baud	(u32 baud_rate, u8 *rate_hi, 
+					 u8 *rate_low);
+static void keyspan_usa19_setup_urbs	(struct usb_serial *serial);
+static int  keyspan_usa19_send_setup	(struct usb_serial *serial,
+					 struct usb_serial_port *port);
+
+
+	/* Functions from usbserial.c for ezusb firmware handling */
+extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
+extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
+
+	/* Struct used for firmware */
+struct ezusb_hex_record {
+	__u16 address;
+	__u8 data_size;
+	__u8 data[16];
+};
+	/* Conditionally include firmware images, if they aren't
+	   included create a null pointer instead.  Current 
+	   firmware images aren't optimised to remove duplicate
+	   addresses. */
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA28
+        #include "keyspan_usa28_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa28_firmware = NULL;
+#endif
+
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA28X
+        #include "keyspan_usa28x_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa28x_firmware = NULL;
+#endif
+
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA19
+        #include "keyspan_usa19_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa19_firmware = NULL;
+#endif
+
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA18X
+        #include "keyspan_usa18x_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa18x_firmware = NULL;
+#endif
+
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA19W
+        #include "keyspan_usa19w_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa19w_firmware = NULL;
+#endif
+
+
+	/* Values used for baud rate calculation - device specific */
+#define	KEYSPAN_INVALID_BAUD_RATE		(-1)
+#define	KEYSPAN_BAUD_RATE_OK			(0)
+#define	USA19_BAUDCLK				(12000000L)
+
+	/* Device info for the Keyspan serial converter */
+#define KEYSPAN_VENDOR_ID			(0x06cd)
+static __u16	keyspan_vendor_id		= KEYSPAN_VENDOR_ID;
+
+    /* Product IDs for the five products supported, pre-renumeration */
+static __u16	keyspan_usa18x_pre_product_id	= 0x0105;	
+static __u16	keyspan_usa19_pre_product_id	= 0x0103;	
+static __u16	keyspan_usa19w_pre_product_id	= 0x0106;	
+static __u16	keyspan_usa28_pre_product_id	= 0x0101;	
+static __u16	keyspan_usa28x_pre_product_id	= 0x0102;	
+
+    /* Product IDs post-renumeration */
+static __u16	keyspan_usa18x_product_id	= 0x0112;	
+static __u16	keyspan_usa19_product_id	= 0x0107;	
+static __u16	keyspan_usa19w_product_id	= 0x0108;	
+static __u16	keyspan_usa28_product_id	= 0x010f;	
+static __u16	keyspan_usa28x_product_id	= 0x0110;	
+
+    /* Structs for the devices, pre and post renumeration.
+       These are incomplete at present - HAB 20000708  */
+struct usb_serial_device_type keyspan_usa18x_pre_device = {
+	name:			"Keyspan USA18X - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa18x_pre_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		keyspan_fake_startup	
+};
+
+struct usb_serial_device_type keyspan_usa19_pre_device = {
+	name:			"Keyspan USA19 - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa19_pre_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		keyspan_fake_startup	
+};
+
+
+struct usb_serial_device_type keyspan_usa19w_pre_device = {
+	name:			"Keyspan USA19W - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa19w_pre_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		keyspan_fake_startup	
+};
+
+
+struct usb_serial_device_type keyspan_usa28_pre_device = {
+	name:			"Keyspan USA28 - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa28_pre_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		2,
+	startup:		keyspan_fake_startup	
+};
+
+struct usb_serial_device_type keyspan_usa28x_pre_device = {
+	name:			"Keyspan USA28X - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa28x_pre_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		2,
+	startup:		keyspan_fake_startup	
+};
+
+
+struct usb_serial_device_type keyspan_usa18x_device = {
+	name:			"Keyspan USA18X",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa18x_product_id,
+	needs_interrupt_in:	DONT_CARE,	
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	open:			keyspan_open,
+	close:			keyspan_close,
+	throttle:		keyspan_rx_throttle,
+	unthrottle:		keyspan_rx_unthrottle,
+	set_termios:		keyspan_set_termios,
+};
+
+struct usb_serial_device_type keyspan_usa19_device = {
+	name:			"Keyspan USA19",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa19_product_id,
+	needs_interrupt_in:	DONT_CARE,	
+	needs_bulk_in:		MUST_HAVE,
+	needs_bulk_out:		MUST_HAVE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		3,
+	num_bulk_out:		4,
+	num_ports:		1,
+	open:			keyspan_open,
+	close:			keyspan_close,
+	write:			keyspan_write,
+	write_room:		keyspan_write_room,
+	write_bulk_callback: 	keyspan_write_bulk_callback,
+	read_int_callback:	keyspan_read_bulk_callback,
+	chars_in_buffer:	keyspan_chars_in_buffer,
+	throttle:		keyspan_rx_throttle,
+	unthrottle:		keyspan_rx_unthrottle,
+	ioctl:			keyspan_ioctl,
+	set_termios:		keyspan_set_termios,
+	break_ctl:		keyspan_break_ctl,
+	startup:		keyspan_startup,
+	shutdown:		keyspan_shutdown,
+};
+
+
+struct usb_serial_device_type keyspan_usa19w_device = {
+	name:			"Keyspan USA19W",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa19w_product_id,
+	needs_interrupt_in:	DONT_CARE,	
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	open:			keyspan_open,
+	close:			keyspan_close,
+	throttle:		keyspan_rx_throttle,
+	unthrottle:		keyspan_rx_unthrottle,
+	set_termios:		keyspan_set_termios,
+};
+
+
+struct usb_serial_device_type keyspan_usa28_device = {
+	name:			"Keyspan USA28",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa28_product_id,
+	needs_interrupt_in:	DONT_CARE,	
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		2,
+	open:			keyspan_open,
+	close:			keyspan_close,
+	throttle:		keyspan_rx_throttle,
+	unthrottle:		keyspan_rx_unthrottle,
+	set_termios:		keyspan_set_termios,
+};
+
+
+struct usb_serial_device_type keyspan_usa28x_device = {
+	name:			"Keyspan USA28X",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_usa28x_product_id,
+	needs_interrupt_in:	DONT_CARE,	
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		2,
+	open:			keyspan_open,
+	close:			keyspan_close,
+	write:			keyspan_write,
+	write_room:		keyspan_write_room,
+	write_bulk_callback: 	keyspan_write_bulk_callback,
+	read_int_callback:	keyspan_read_bulk_callback,
+	chars_in_buffer:	keyspan_chars_in_buffer,
+	throttle:		keyspan_rx_throttle,
+	unthrottle:		keyspan_rx_unthrottle,
+	ioctl:			keyspan_ioctl,
+	set_termios:		keyspan_set_termios,
+	break_ctl:		keyspan_break_ctl,
+	startup:		keyspan_startup,
+	shutdown:		keyspan_shutdown,
+
+				
+
+};
+
+
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_pda.S linux.18p17/drivers/usb/serial/keyspan_pda.S
--- linux.vanilla/drivers/usb/serial/keyspan_pda.S	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_pda.S	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,1124 @@
+/*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
+ * 
+ *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
+ *  the EzUSB microcontroller.
+ * 
+ *  (C) Copyright 2000 Brian Warner <warner@lothar.com>
+ * 
+ * 	This program is free software; you can redistribute it and/or modify
+ * 	it under the terms of the GNU General Public License as published by
+ * 	the Free Software Foundation; either version 2 of the License, or
+ * 	(at your option) any later version.
+ * 
+ *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
+ *  company.
+ * 
+ *  This serial adapter is basically an EzUSB chip and an RS-232 line driver
+ *  in a little widget that has a DB-9 on one end and a USB plug on the other.
+ *  It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
+ *  as a baud-rate generator. The wiring is:
+ *   PC0/RxD0 <- rxd (DB9 pin 2)         PC4 <- dsr pin 6
+ *   PC1/TxD0 -> txd pin 3               PC5 <- ri  pin 9
+ *   PC2      -> rts pin 7               PC6 <- dcd pin 1
+ *   PC3      <- cts pin 8               PC7 -> dtr pin 4
+ *   PB1 -> line driver standby
+ *
+ *  The EzUSB register constants below come from their excellent documentation
+ *  and sample code (which used to be available at www.anchorchips.com, but
+ *  that has now been absorbed into Cypress' site and the CD-ROM contents
+ *  don't appear to be available online anymore). If we get multiple
+ *  EzUSB-based drivers into the kernel, it might be useful to pull them out
+ *  into a separate .h file.
+ * 
+ * THEORY OF OPERATION:
+ *
+ *   There are two 256-byte ring buffers, one for tx, one for rx.
+ *
+ *   EP2out is pure tx data. When it appears, the data is copied into the tx
+ *   ring and serial transmission is started if it wasn't already running. The
+ *   "tx buffer empty" interrupt may kick off another character if the ring
+ *   still has data. If the host is tx-blocked because the ring filled up,
+ *   it will request a "tx unthrottle" interrupt. If sending a serial character
+ *   empties the ring below the desired threshold, we set a bit that will send
+ *   up the tx unthrottle message as soon as the rx buffer becomes free.
+ *
+ *   EP2in (interrupt) is used to send both rx chars and rx status messages
+ *   (only "tx unthrottle" at this time) back up to the host. The first byte
+ *   of the rx message indicates data (0) or status msg (1). Status messages
+ *   are sent before any data.
+ *
+ *   Incoming serial characters are put into the rx ring by the serial
+ *   interrupt, and the EP2in buffer sent if it wasn't already in transit.
+ *   When the EP2in buffer returns, the interrupt prompts us to send more
+ *   rx chars (or status messages) if they are pending.
+ *
+ *   Device control happens through "vendor specific" control messages on EP0.
+ *   All messages are destined for the "Interface" (with the index always 0,
+ *   so that if their two-port device might someday use similar firmware, we
+ *   can use index=1 to refer to the second port). The messages defined are:
+ *
+ *    bRequest = 0 : set baud/bits/parity
+ *               1 : unused
+ *               2 : reserved for setting HW flow control (CTSRTS)
+ *               3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
+ *               4 : set break (on/off)
+ *               5 : reserved for requesting interrupts on pin state change
+ *               6 : query buffer room or chars in tx buffer
+ *               7 : request tx unthrottle interrupt
+ *
+ *  The host-side driver is set to recognize the device ID values stashed in
+ *  serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
+ *  start it running. This firmware will use EzUSB's "renumeration" trick by
+ *  simulating a bus disconnect, then reconnect with a different device ID
+ *  (encoded in the desc_device descriptor below). The host driver then
+ *  recognizes the new device ID and glues it to the real serial driver code.
+ *
+ * USEFUL DOCS:
+ *  EzUSB Technical Reference Manual: <http://www.anchorchips.com>
+ *  8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
+ *   basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
+ *   use totally different registers!
+ *  USB 1.1 spec: www.usb.org
+ *
+ * HOW TO BUILD:
+ *  gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
+ *  as31 -l keyspan_pda.asm
+ *  mv keyspan_pda.obj keyspan_pda.hex
+ *  perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
+ * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
+ * a bit to make it build.
+ *
+ * THANKS:
+ *  Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
+ *  AnchorChips, for making such an incredibly useful little microcontroller.
+ *  KeySpan, for making a handy, cheap ($40) widget that was so easy to take
+ *           apart and trace with an ohmmeter.
+ *
+ * TODO:
+ *  lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
+ *  control. Interrupting host upon change in DCD, etc, counting transitions.
+ *  Need to find a safe device id to use (the one used by the Keyspan firmware
+ *  under Windows would be ideal.. can anyone figure out what it is?). Parity.
+ *  More baud rates. Oh, and the string-descriptor-length silicon bug
+ *  workaround should be implemented, but I'm lazy, and the consequence is
+ *  that the device name strings that show up in your kernel log will have
+ *  lots of trailing binary garbage in them (appears as ????). Device strings
+ *  should be made more accurate.
+ *
+ * Questions, bugs, patches to Brian.
+ *
+ *  -Brian Warner <warner@lothar.com>
+ *
+ */
+	
+#define HIGH(x) (((x) & 0xff00) / 256)
+#define LOW(x) ((x) & 0xff)
+
+#define dpl1 0x84
+#define dph1 0x85
+#define dps 0x86
+
+;;; our bit assignments
+#define TX_RUNNING 0
+#define DO_TX_UNTHROTTLE 1
+	
+	;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
+#define STACK #0x60-1
+
+#define EXIF 0x91
+#define EIE 0xe8
+	.flag EUSB, EIE.0
+	.flag ES0, IE.4
+
+#define EP0CS #0x7fb4
+#define EP0STALLbit #0x01
+#define IN0BUF #0x7f00
+#define IN0BC #0x7fb5
+#define OUT0BUF #0x7ec0
+#define OUT0BC #0x7fc5		
+#define IN2BUF #0x7e00
+#define IN2BC #0x7fb9
+#define IN2CS #0x7fb8
+#define OUT2BC #0x7fc9
+#define OUT2CS #0x7fc8
+#define OUT2BUF #0x7dc0
+#define IN4BUF #0x7d00
+#define IN4BC #0x7fbd
+#define IN4CS #0x7fbc
+#define OEB #0x7f9d
+#define OUTB #0x7f97
+#define OEC #0x7f9e
+#define OUTC #0x7f98
+#define PINSC #0x7f9b
+#define PORTCCFG #0x7f95
+#define IN07IRQ #0x7fa9
+#define OUT07IRQ #0x7faa
+#define IN07IEN #0x7fac
+#define OUT07IEN #0x7fad
+#define USBIRQ #0x7fab
+#define USBIEN #0x7fae
+#define USBBAV #0x7faf
+#define USBCS #0x7fd6
+#define SUDPTRH #0x7fd4
+#define SUDPTRL #0x7fd5
+#define SETUPDAT #0x7fe8
+		
+	;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
+
+	.org 0
+	ljmp start
+	;; interrupt vectors
+	.org 23H
+	ljmp serial_int
+	.byte 0
+	
+	.org 43H
+	ljmp USB_Jump_Table
+	.byte 0			; filled in by the USB core
+
+;;; local variables. These are not initialized properly: do it by hand.
+	.org 30H
+rx_ring_in:	.byte 0
+rx_ring_out:	.byte 0
+tx_ring_in:	.byte 0
+tx_ring_out:	.byte 0
+tx_unthrottle_threshold:	.byte 0
+		
+	.org 0x100H		; wants to be on a page boundary
+USB_Jump_Table:
+	ljmp	ISR_Sudav	; Setup Data Available
+	.byte 0
+	ljmp	0		; Start of Frame
+	.byte 0
+	ljmp	0		; Setup Data Loading
+	.byte 0
+	ljmp	0		; Global Suspend
+	.byte 	0
+	ljmp	0		; USB Reset  	
+	.byte	0
+	ljmp	0		; Reserved
+	.byte	0
+	ljmp	0		; End Point 0 In
+	.byte	0
+	ljmp	0		; End Point 0 Out
+	.byte	0
+	ljmp	0		; End Point 1 In
+	.byte	0
+	ljmp	0		; End Point 1 Out
+	.byte	0
+	ljmp	ISR_Ep2in
+	.byte	0
+	ljmp	ISR_Ep2out
+	.byte	0
+
+
+	.org 0x200
+		
+start:	mov SP,STACK-1 ; set stack
+	;; clear local variables
+	clr a
+	mov tx_ring_in, a
+	mov tx_ring_out, a
+	mov rx_ring_in, a
+	mov rx_ring_out, a
+	mov tx_unthrottle_threshold, a
+	clr TX_RUNNING
+	clr DO_TX_UNTHROTTLE
+	
+	;; clear fifo with "fe"
+	mov r1, 0
+	mov a, #0xfe
+	mov dptr, #tx_ring
+clear_tx_ring_loop:
+	movx @dptr, a
+	inc dptr
+	djnz r1, clear_tx_ring_loop
+
+	mov a, #0xfd
+	mov dptr, #rx_ring
+clear_rx_ring_loop:
+	movx @dptr, a
+	inc dptr
+	djnz r1, clear_rx_ring_loop
+
+;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
+	;; set OEB.1
+	mov a, #02H
+	mov dptr,OEB
+	movx @dptr,a
+	;; clear PB1
+	mov a, #00H
+	mov dptr,OUTB
+	movx @dptr,a
+	;; set OEC.[127]
+	mov a, #0x86
+	mov dptr,OEC
+	movx @dptr,a
+	;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
+	mov dptr, PORTCCFG
+	mov a, #0x03
+	movx @dptr, a
+	
+	;; set up interrupts, autovectoring
+	mov dptr, USBBAV
+	movx a,@dptr
+	setb acc.0		; AVEN bit to 0
+	movx @dptr, a
+
+	mov a,#0x01		; enable SUDAV:	setup data available (for ep0)
+	mov dptr, USBIRQ
+	movx @dptr, a		; clear SUDAVI
+	mov dptr, USBIEN
+	movx @dptr, a
+	
+	mov dptr, IN07IEN
+	mov a,#0x04		; enable IN2 int
+	movx @dptr, a
+	
+	mov dptr, OUT07IEN
+	mov a,#0x04		; enable OUT2 int
+	movx @dptr, a
+	mov dptr, OUT2BC
+	movx @dptr, a		; arm OUT2
+
+	mov a, #0x84		; turn on RTS, DTR
+	mov dptr,OUTC
+	movx @dptr, a
+	;; setup the serial port. 9600 8N1.
+	mov a,#01010011		; mode 1, enable rx, clear int
+	mov SCON, a
+	;;  using timer2, in 16-bit baud-rate-generator mode
+	;;   (xtal 12MHz, internal fosc 24MHz)
+	;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
+	;;  57600: 0xFFF2.F, say 0xFFF3
+	;;   9600: 0xFFB1.E, say 0xFFB2
+	;;    300: 0xF63C
+#define BAUD 9600
+#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
+#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
+#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
+		
+	mov T2CON, #030h	; rclk=1,tclk=1,cp=0,tr2=0(enable later)
+	mov r3, #5
+	acall set_baud
+	setb TR2
+	mov SCON, #050h
+	
+#if 0
+	mov r1, #0x40
+	mov a, #0x41
+send:	
+	mov SBUF, a
+	inc a
+	anl a, #0x3F
+	orl a, #0x40
+;	xrl a, #0x02
+wait1:	
+	jnb TI, wait1
+	clr TI
+	djnz r1, send
+;done:	sjmp done
+
+#endif
+	
+	setb EUSB
+	setb EA
+	setb ES0
+	;acall dump_stat
+
+	;; hey, what say we RENUMERATE! (TRM p.62)
+	mov a, #0
+	mov dps, a
+	mov dptr, USBCS
+	mov a, #0x02		; DISCON=0, DISCOE=0, RENUM=1
+	movx @dptr, a
+	;; now presence pin is floating, simulating disconnect. wait 0.5s
+	mov r1, #46
+renum_wait1:
+	mov r2, #0
+renum_wait2:
+	mov r3, #0
+renum_wait3:
+	djnz r3, renum_wait3
+	djnz r2, renum_wait2
+	djnz r1, renum_wait1	; wait about n*(256^2) 6MHz clocks
+	mov a, #0x06		; DISCON=0, DISCOE=1, RENUM=1
+	movx @dptr, a
+	;; we are back online. the host device will now re-query us
+	
+	
+main:	sjmp main
+
+	
+
+ISR_Sudav:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, USBIRQ	; clear USB int
+	mov a,#01h
+	movx @dptr,a
+
+	;; get request type
+	mov dptr, SETUPDAT
+	movx a, @dptr
+	mov r1, a		; r1 = bmRequestType
+	inc dptr
+	movx a, @dptr
+	mov r2, a		; r2 = bRequest
+	inc dptr
+	movx a, @dptr
+	mov r3, a		; r3 = wValueL
+	inc dptr
+	movx a, @dptr
+	mov r4, a		; r4 = wValueH
+
+	;; main switch on bmRequest.type: standard or vendor
+	mov a, r1
+	anl a, #0x60
+	cjne a, #0x00, setup_bmreq_type_not_standard
+	;; standard request: now main switch is on bRequest
+	ljmp setup_bmreq_is_standard
+	
+setup_bmreq_type_not_standard:	
+	;; a still has bmreq&0x60
+	cjne a, #0x40, setup_bmreq_type_not_vendor
+	;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
+	;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
+	cjne r2, #0x00, setup_ctrl_not_00
+	;; 00 is set baud, wValue[0] has baud rate index
+	lcall set_baud		; index in r3, carry set if error
+	jc setup_bmreq_type_not_standard__do_stall
+	ljmp setup_done_ack
+setup_bmreq_type_not_standard__do_stall:
+	ljmp setup_stall
+setup_ctrl_not_00:
+	cjne r2, #0x01, setup_ctrl_not_01
+	;; 01 is reserved for set bits (parity). TODO
+	ljmp setup_stall
+setup_ctrl_not_01:
+	cjne r2, #0x02, setup_ctrl_not_02
+	;; 02 is set HW flow control. TODO
+	ljmp setup_stall
+setup_ctrl_not_02:
+	cjne r2, #0x03, setup_ctrl_not_03
+	;; 03 is control pins (RTS, DTR).
+	ljmp control_pins	; will jump to setup_done_ack,
+				;  or setup_return_one_byte
+setup_ctrl_not_03:
+	cjne r2, #0x04, setup_ctrl_not_04
+	;; 04 is send break (really "turn break on/off"). TODO
+	cjne r3, #0x00, setup_ctrl_do_break_on
+	;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
+	mov dptr, PORTCCFG
+	movx a, @dptr
+	orl a, #0x02
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_ctrl_do_break_on:
+	;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
+	mov dptr, OUTC
+	movx a, @dptr
+	anl a, #0xfd		; ~0x02
+	movx @dptr, a
+	mov dptr, PORTCCFG
+	movx a, @dptr
+	anl a, #0xfd		; ~0x02
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_ctrl_not_04:
+	cjne r2, #0x05, setup_ctrl_not_05
+	;; 05 is set desired interrupt bitmap. TODO
+	ljmp setup_stall
+setup_ctrl_not_05:
+	cjne r2, #0x06, setup_ctrl_not_06
+	;; 06 is query room
+	cjne r3, #0x00, setup_ctrl_06_not_00
+	;; 06, wValue[0]=0 is query write_room
+	mov a, tx_ring_out
+	setb c
+	subb a, tx_ring_in	; out-1-in = 255 - (in-out)
+	ljmp setup_return_one_byte
+setup_ctrl_06_not_00:
+	cjne r3, #0x01, setup_ctrl_06_not_01
+	;; 06, wValue[0]=1 is query chars_in_buffer
+	mov a, tx_ring_in
+	clr c
+	subb a, tx_ring_out	; in-out
+	ljmp setup_return_one_byte
+setup_ctrl_06_not_01:	
+	ljmp setup_stall
+setup_ctrl_not_06:
+	cjne r2, #0x07, setup_ctrl_not_07
+	;; 07 is request tx unthrottle interrupt
+	mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
+	ljmp setup_done_ack
+setup_ctrl_not_07:
+	ljmp setup_stall
+	
+setup_bmreq_type_not_vendor:
+	ljmp setup_stall
+
+
+setup_bmreq_is_standard:	
+	cjne r2, #0x00, setup_breq_not_00
+	;; 00:	Get_Status (sub-switch on bmRequestType: device, ep, int)
+	cjne r1, #0x80, setup_Get_Status_not_device
+	;; Get_Status(device)
+	;;  are we self-powered? no. can we do remote wakeup? no
+	;;   so return two zero bytes. This is reusable
+setup_return_two_zero_bytes:
+	mov dptr, IN0BUF
+	clr a
+	movx @dptr, a
+	inc dptr
+	movx @dptr, a
+	mov dptr, IN0BC
+	mov a, #2
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Status_not_device:
+	cjne r1, #0x82, setup_Get_Status_not_endpoint
+	;; Get_Status(endpoint)
+	;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
+	;; for now: cheat. TODO
+	sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_endpoint:
+	cjne r1, #0x81, setup_Get_Status_not_interface
+	;; Get_Status(interface): return two zeros
+	sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_interface:	
+	ljmp setup_stall
+	
+setup_breq_not_00:
+	cjne r2, #0x01, setup_breq_not_01
+	;; 01:	Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
+	cjne r3, #0x00, setup_Clear_Feature_not_stall
+	;; Clear_Feature(stall). should clear a stall bit. TODO
+	ljmp setup_stall
+setup_Clear_Feature_not_stall:
+	cjne r3, #0x01, setup_Clear_Feature_not_rwake
+	;; Clear_Feature(remote wakeup). ignored.
+	ljmp setup_done_ack
+setup_Clear_Feature_not_rwake:
+	ljmp setup_stall
+	
+setup_breq_not_01:
+	cjne r2, #0x03, setup_breq_not_03
+	;; 03:	Set_Feature (sub-switch on wValueL: stall, remote wakeup)
+	cjne r3, #0x00, setup_Set_Feature_not_stall
+	;; Set_Feature(stall). Should set a stall bit. TODO
+	ljmp setup_stall
+setup_Set_Feature_not_stall:
+	cjne r3, #0x01, setup_Set_Feature_not_rwake
+	;; Set_Feature(remote wakeup). ignored.
+	ljmp setup_done_ack
+setup_Set_Feature_not_rwake:
+	ljmp setup_stall
+	
+setup_breq_not_03:	
+	cjne r2, #0x06, setup_breq_not_06
+	;; 06:	Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
+	cjne r4, #0x01, setup_Get_Descriptor_not_device
+	;; Get_Descriptor(device)
+	mov dptr, SUDPTRH
+	mov a, #HIGH(desc_device)
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, #LOW(desc_device)
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Descriptor_not_device:
+	cjne r4, #0x02, setup_Get_Descriptor_not_config
+	;; Get_Descriptor(config[n])
+	cjne r3, #0x00, setup_stall; only handle n==0
+	;; Get_Descriptor(config[0])
+	mov dptr, SUDPTRH
+	mov a, #HIGH(desc_config1)
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, #LOW(desc_config1)
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_Get_Descriptor_not_config:
+	cjne r4, #0x03, setup_Get_Descriptor_not_string
+	;; Get_Descriptor(string[wValueL])
+	;;  if (wValueL >= maxstrings) stall
+	mov a, #((desc_strings_end-desc_strings)/2)
+	clr c
+	subb a,r3		; a=4, r3 = 0..3 . if a<=0 then stall
+	jc  setup_stall
+	jz  setup_stall
+	mov a, r3
+	add a, r3		; a = 2*wValueL
+	mov dptr, #desc_strings
+	add a, dpl
+	mov dpl, a
+	mov a, #0
+	addc a, dph
+	mov dph, a		; dph = desc_strings[a]. big endian! (handy)
+	;; it looks like my adapter uses a revision of the EZUSB that
+	;; contains "rev D errata number 8", as hinted in the EzUSB example
+	;; code. I cannot find an actual errata description on the Cypress
+	;; web site, but from the example code it looks like this bug causes
+	;; the length of string descriptors to be read incorrectly, possibly
+	;; sending back more characters than the descriptor has. The workaround
+	;; is to manually send out all of the data. The consequence of not
+	;; using the workaround is that the strings gathered by the kernel
+	;; driver are too long and are filled with trailing garbage (including
+	;; leftover strings). Writing this out by hand is a nuisance, so for
+	;; now I will just live with the bug.
+	movx a, @dptr
+	mov r1, a
+	inc dptr
+	movx a, @dptr
+	mov r2, a
+	mov dptr, SUDPTRH
+	mov a, r1
+	movx @dptr, a
+	mov dptr, SUDPTRL
+	mov a, r2
+	movx @dptr, a
+	;; done
+	ljmp setup_done_ack
+	
+setup_Get_Descriptor_not_string:
+	ljmp setup_stall
+	
+setup_breq_not_06:
+	cjne r2, #0x08, setup_breq_not_08
+	;; Get_Configuration. always 1. return one byte.
+	;; this is reusable
+	mov a, #1
+setup_return_one_byte:	
+	mov dptr, IN0BUF
+	movx @dptr, a
+	mov a, #1
+	mov dptr, IN0BC
+	movx @dptr, a
+	ljmp setup_done_ack
+setup_breq_not_08:
+	cjne r2, #0x09, setup_breq_not_09
+	;; 09: Set_Configuration. ignored.
+	ljmp setup_done_ack
+setup_breq_not_09:
+	cjne r2, #0x0a, setup_breq_not_0a
+	;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
+	;;  since we only have one interface, ignore wIndexL, return a 0
+	mov a, #0
+	ljmp setup_return_one_byte
+setup_breq_not_0a:
+	cjne r2, #0x0b, setup_breq_not_0b
+	;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
+	ljmp setup_done_ack
+setup_breq_not_0b:
+	ljmp setup_stall
+
+		
+setup_done_ack:	
+	;; now clear HSNAK
+	mov dptr, EP0CS
+	mov a, #0x02
+	movx @dptr, a
+	sjmp setup_done
+setup_stall:	
+	;; unhandled. STALL
+	;EP0CS |= bmEPSTALL
+	mov dptr, EP0CS
+	movx a, @dptr
+	orl a, EP0STALLbit
+	movx @dptr, a
+	sjmp setup_done
+	
+setup_done:	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+;;; ==============================================================
+	
+set_baud:			; baud index in r3
+	;; verify a < 10
+	mov a, r3
+	jb ACC.7, set_baud__badbaud
+	clr c
+	subb a, #10
+	jnc set_baud__badbaud
+	mov a, r3
+	rl a			; a = index*2
+	add a, #LOW(baud_table)
+	mov dpl, a
+	mov a, #HIGH(baud_table)
+	addc a, #0
+	mov dph, a
+	;; TODO: shut down xmit/receive
+	;; TODO: wait for current xmit char to leave
+	;; TODO: shut down timer to avoid partial-char glitch
+	movx a,@dptr		; BAUD_HIGH
+	mov RCAP2H, a
+	mov TH2, a
+	inc dptr
+	movx a,@dptr		; BAUD_LOW
+	mov RCAP2L, a
+	mov TL2, a
+	;; TODO: restart xmit/receive
+	;; TODO: reenable interrupts, resume tx if pending
+	clr c			; c=0: success
+	ret
+set_baud__badbaud:
+	setb c			; c=1: failure
+	ret
+	
+;;; ==================================================
+control_pins:
+	cjne r1, #0x41, control_pins_in
+control_pins_out:
+	mov a, r3 ; wValue[0] holds new bits:	b7 is new DTR, b2 is new RTS
+	xrl a, #0xff		; 1 means active, 0V, +12V ?
+	anl a, #0x84
+	mov r3, a
+	mov dptr, OUTC
+	movx a, @dptr		; only change bits 7 and 2
+	anl a, #0x7b		; ~0x84
+	orl a, r3
+	movx @dptr, a		; other pins are inputs, bits ignored
+	ljmp setup_done_ack
+control_pins_in:
+	mov dptr, PINSC
+	movx a, @dptr
+	xrl a, #0xff
+	ljmp setup_return_one_byte
+
+;;; ========================================
+	
+ISR_Ep2in:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, IN07IRQ	; clear USB int
+	mov a,#04h
+	movx @dptr,a
+
+	;; do stuff
+	lcall start_in
+	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+ISR_Ep2out:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	mov a,EXIF
+	clr acc.4
+	mov EXIF,a		; clear INT2 first
+	mov dptr, OUT07IRQ	; clear USB int
+	mov a,#04h
+	movx @dptr,a
+
+	;; do stuff
+
+	;; copy data into buffer. for now, assume we will have enough space
+	mov dptr, OUT2BC	; get byte count
+	movx a,@dptr
+	mov r1, a
+	clr a
+	mov dps, a
+	mov dptr, OUT2BUF	; load DPTR0 with source
+	mov dph1, #HIGH(tx_ring)	; load DPTR1 with target
+	mov dpl1, tx_ring_in
+OUT_loop:
+	movx a,@dptr		; read
+	inc dps			; switch to DPTR1: target
+	inc dpl1		; target = tx_ring_in+1
+	movx @dptr,a		; store
+	mov a,dpl1
+	cjne a, tx_ring_out, OUT_no_overflow
+	sjmp OUT_overflow
+OUT_no_overflow:	
+	inc tx_ring_in		; tx_ring_in++
+	inc dps			; switch to DPTR0: source
+	inc dptr
+	djnz r1, OUT_loop
+	sjmp OUT_done
+OUT_overflow:
+	;; signal overflow
+	;; fall through
+OUT_done:	
+	;; ack
+	mov dptr,OUT2BC
+	movx @dptr,a
+
+	;; start tx
+	acall maybe_start_tx
+	;acall dump_stat
+	
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+dump_stat:
+	;; fill in EP4in with a debugging message:
+	;;   tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
+	;;   tx_active
+	;;   tx_ring[0..15]
+	;;   0xfc
+	;;   rx_ring[0..15]
+	clr a
+	mov dps, a
+	
+	mov dptr, IN4CS
+	movx a, @dptr
+	jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
+	mov dptr, IN4BUF
+	
+	mov a, tx_ring_in
+	movx @dptr, a
+	inc dptr
+	mov a, tx_ring_out
+	movx @dptr, a
+	inc dptr
+
+	mov a, rx_ring_in
+	movx @dptr, a
+	inc dptr
+	mov a, rx_ring_out
+	movx @dptr, a
+	inc dptr
+	
+	clr a
+	jnb TX_RUNNING, dump_stat__no_tx_running
+	inc a
+dump_stat__no_tx_running:
+	movx @dptr, a
+	inc dptr
+	;; tx_ring[0..15]
+	inc dps
+	mov dptr, #tx_ring	; DPTR1: source
+	mov r1, #16
+dump_stat__tx_ring_loop:
+	movx a, @dptr
+	inc dptr
+	inc dps
+	movx @dptr, a
+	inc dptr
+	inc dps
+	djnz r1, dump_stat__tx_ring_loop
+	inc dps
+	
+	mov a, #0xfc
+	movx @dptr, a
+	inc dptr
+	
+	;; rx_ring[0..15]
+	inc dps
+	mov dptr, #rx_ring	; DPTR1: source
+	mov r1, #16
+dump_stat__rx_ring_loop:
+	movx a, @dptr
+	inc dptr
+	inc dps
+	movx @dptr, a
+	inc dptr
+	inc dps
+	djnz r1, dump_stat__rx_ring_loop
+	
+	;; now send it
+	clr a
+	mov dps, a
+	mov dptr, IN4BC
+	mov a, #38
+	movx @dptr, a
+dump_stat__done:	
+	ret
+		
+;;; ============================================================
+	
+maybe_start_tx:
+	;; make sure the tx process is running.
+	jb TX_RUNNING, start_tx_done
+start_tx:
+	;; is there work to be done?
+	mov a, tx_ring_in
+	cjne a,tx_ring_out, start_tx__work
+	ret			; no work
+start_tx__work:	
+	;; tx was not running. send the first character, setup the TI int
+	inc tx_ring_out		; [++tx_ring_out]
+	mov dph, #HIGH(tx_ring)
+	mov dpl, tx_ring_out
+	movx a, @dptr
+	mov sbuf, a
+	setb TX_RUNNING
+start_tx_done:
+	;; can we unthrottle the host tx process?
+	;;  step 1: do we care?
+	mov a, #0
+	cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
+	;; nope
+start_tx_really_done:
+	ret
+start_tx__maybe_unthrottle_tx:
+	;;  step 2: is there now room?
+	mov a, tx_ring_out
+	setb c
+	subb a, tx_ring_in
+	;; a is now write_room. If thresh >= a, we can unthrottle
+	clr c
+	subb a, tx_unthrottle_threshold
+	jc start_tx_really_done	; nope
+	;; yes, we can unthrottle. remove the threshold and mark a request
+	mov tx_unthrottle_threshold, #0
+	setb DO_TX_UNTHROTTLE
+	;; prod rx, which will actually send the message when in2 becomes free
+	ljmp start_in
+	
+
+serial_int:
+	push dps
+	push dpl
+	push dph
+	push dpl1
+	push dph1
+	push acc
+	jnb TI, serial_int__not_tx
+	;; tx finished. send another character if we have one
+	clr TI			; clear int
+	clr TX_RUNNING
+	lcall start_tx
+serial_int__not_tx:
+	jnb RI, serial_int__not_rx
+	lcall get_rx_char
+	clr RI			; clear int
+serial_int__not_rx:	
+	;; return
+	pop acc
+	pop dph1
+	pop dpl1
+	pop dph
+	pop dpl
+	pop dps
+	reti
+
+get_rx_char:
+	mov dph, #HIGH(rx_ring)
+	mov dpl, rx_ring_in
+	inc dpl			; target = rx_ring_in+1
+	mov a, sbuf
+	movx @dptr, a
+	;; check for overflow before incrementing rx_ring_in
+	mov a, dpl
+	cjne a, rx_ring_out, get_rx_char__no_overflow
+	;; signal overflow
+	ret
+get_rx_char__no_overflow:	
+	inc rx_ring_in
+	;; kick off USB INpipe
+	acall start_in
+	ret
+
+start_in:
+	;; check if the inpipe is already running.
+	mov dptr, IN2CS
+	movx a, @dptr
+	jb acc.1, start_in__done; int will handle it
+	jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
+	;; see if there is any work to do. a serial interrupt might occur
+	;; during this sequence?
+	mov a, rx_ring_in
+	cjne a, rx_ring_out, start_in__have_work
+	ret			; nope
+start_in__have_work:	
+	;; now copy as much data as possible into the pipe. 63 bytes max.
+	clr a
+	mov dps, a
+	mov dph, #HIGH(rx_ring)	; load DPTR0 with source
+	inc dps
+	mov dptr, IN2BUF	; load DPTR1 with target
+	movx @dptr, a		; in[0] signals that rest of IN is rx data
+	inc dptr
+	inc dps
+	;; loop until we run out of data, or we have copied 64 bytes
+	mov r1, #1		; INbuf size counter
+start_in__loop:
+	mov a, rx_ring_in
+	cjne a, rx_ring_out, start_in__still_copying
+	sjmp start_in__kick
+start_in__still_copying:
+	inc rx_ring_out
+	mov dpl, rx_ring_out
+	movx a, @dptr
+	inc dps
+	movx @dptr, a		; write into IN buffer
+	inc dptr
+	inc dps
+	inc r1
+	cjne r1, #64, start_in__loop; loop
+start_in__kick:
+	;; either we ran out of data, or we copied 64 bytes. r1 has byte count
+	;; kick off IN
+	mov dptr, IN2BC
+	mov a, r1
+	jz start_in__done
+	movx @dptr, a
+	;; done
+start_in__done:
+	;acall dump_stat
+	ret
+start_in__do_tx_unthrottle:
+	;; special sequence: send a tx unthrottle message
+	clr DO_TX_UNTHROTTLE
+	clr a
+	mov dps, a
+	mov dptr, IN2BUF
+	mov a, #1
+	movx @dptr, a
+	inc dptr
+	mov a, #2
+	movx @dptr, a
+	mov dptr, IN2BC
+	movx @dptr, a
+	ret
+	
+putchar:
+	clr TI
+	mov SBUF, a
+putchar_wait:
+	jnb TI, putchar_wait
+	clr TI
+	ret
+
+	
+baud_table:			; baud_high, then baud_low
+	;; baud[0]: 110
+	.byte BAUD_HIGH(110)
+	.byte BAUD_LOW(110)
+	;; baud[1]: 300
+	.byte BAUD_HIGH(300)
+	.byte BAUD_LOW(300)
+	;; baud[2]: 1200
+	.byte BAUD_HIGH(1200)
+	.byte BAUD_LOW(1200)
+	;; baud[3]: 2400
+	.byte BAUD_HIGH(2400)
+	.byte BAUD_LOW(2400)
+	;; baud[4]: 4800
+	.byte BAUD_HIGH(4800)
+	.byte BAUD_LOW(4800)
+	;; baud[5]: 9600
+	.byte BAUD_HIGH(9600)
+	.byte BAUD_LOW(9600)
+	;; baud[6]: 19200
+	.byte BAUD_HIGH(19200)
+	.byte BAUD_LOW(19200)
+	;; baud[7]: 38400
+	.byte BAUD_HIGH(38400)
+	.byte BAUD_LOW(38400)
+	;; baud[8]: 57600
+	.byte BAUD_HIGH(57600)
+	.byte BAUD_LOW(57600)
+	;; baud[9]: 115200
+	.byte BAUD_HIGH(115200)
+	.byte BAUD_LOW(115200)
+
+desc_device:
+	.byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
+	.byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
+;;; The "real" device id, which must match the host driver, is that
+;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
+	
+desc_config1:
+	.byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
+	.byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
+	.byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
+	.byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
+
+desc_strings:
+	.word string_langids, string_mfg, string_product, string_serial
+desc_strings_end:
+
+string_langids:	.byte string_langids_end-string_langids
+	.byte 3
+	.word 0
+string_langids_end:
+
+	;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
+	;; *that* is a pain in the ass to encode. And they are little-endian
+	;; too. Use this perl snippet to get the bytecodes:
+	/* while (<>) {
+	    @c = split(//);
+	    foreach $c (@c) {
+	     printf("0x%02x, 0x00, ", ord($c));
+	    }
+	   }
+	*/
+
+string_mfg:	.byte string_mfg_end-string_mfg
+	.byte 3
+;	.byte "ACME usb widgets"
+	.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
+string_mfg_end:
+	
+string_product:	.byte string_product_end-string_product
+	.byte 3
+;	.byte "ACME USB serial widget"
+	.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
+string_product_end:
+	
+string_serial:	.byte string_serial_end-string_serial
+	.byte 3
+;	.byte "47"
+	.byte 0x34, 0x00, 0x37, 0x00
+string_serial_end:
+		
+;;; ring buffer memory
+	;; tx_ring_in+1 is where the next input byte will go
+	;; [tx_ring_out] has been sent
+	;; if tx_ring_in == tx_ring_out, theres no work to do
+	;; there are (tx_ring_in - tx_ring_out) chars to be written
+	;; dont let _in lap _out
+	;;   cannot inc if tx_ring_in+1 == tx_ring_out
+	;;  write [tx_ring_in+1] then tx_ring_in++
+	;;   if (tx_ring_in+1 == tx_ring_out), overflow
+	;;   else tx_ring_in++
+	;;  read/send [tx_ring_out+1], then tx_ring_out++
+
+	;; rx_ring_in works the same way
+	
+	.org 0x1000
+tx_ring:
+	.skip 0x100		; 256 bytes
+rx_ring:
+	.skip 0x100		; 256 bytes
+	
+	
+	.END
+	
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_pda.c linux.18p17/drivers/usb/serial/keyspan_pda.c
--- linux.vanilla/drivers/usb/serial/keyspan_pda.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_pda.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,801 @@
+/*
+ * USB Keyspan PDA Converter driver
+ *
+ * Copyright (c) 1999, 2000 Greg Kroah-Hartman	<greg@kroah.com>
+ * Copyright (c) 1999, 2000 Brian Warner	<warner@lothar.com>
+ * Copyright (c) 2000 Al Borchers		<borchers@steinerpoint.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ * (08/28/2000) gkh
+ *	Added locks for SMP safeness.
+ *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more 
+ *	than once.
+ * 
+ * (07/20/2000) borchers
+ *	- keyspan_pda_write no longer sleeps if it is called on interrupt time;
+ *	  PPP and the line discipline with stty echo on can call write on
+ *	  interrupt time and this would cause an oops if write slept
+ *	- if keyspan_pda_write is in an interrupt, it will not call
+ *	  usb_control_msg (which sleeps) to query the room in the device
+ *	  buffer, it simply uses the current room value it has
+ *	- if the urb is busy or if it is throttled keyspan_pda_write just
+ *	  returns 0, rather than sleeping to wait for this to change; the
+ *	  write_chan code in n_tty.c will sleep if needed before calling
+ *	  keyspan_pda_write again
+ *	- if the device needs to be unthrottled, write now queues up the
+ *	  call to usb_control_msg (which sleeps) to unthrottle the device
+ *	- the wakeups from keyspan_pda_write_bulk_callback are queued rather
+ *	  than done directly from the callback to avoid the race in write_chan
+ *	- keyspan_pda_chars_in_buffer also indicates its buffer is full if the
+ *	  urb status is -EINPROGRESS, meaning it cannot write at the moment
+ *      
+ * (07/19/2000) gkh
+ *	Added module_init and module_exit functions to handle the fact that this
+ *	driver is a loadable module now.
+ *
+ * (03/26/2000) gkh
+ *	Split driver up into device specific pieces.
+ * 
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/tqueue.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+struct ezusb_hex_record {
+	__u16 address;
+	__u8 data_size;
+	__u8 data[16];
+};
+
+#include "keyspan_pda_fw.h"
+
+#include "usb-serial.h"
+
+struct keyspan_pda_private {
+	int			tx_room;
+	int			tx_throttled;
+	struct tq_struct	wakeup_task;
+	struct tq_struct	unthrottle_task;
+};
+
+#define KEYSPAN_VENDOR_ID		0x06cd
+#define KEYSPAN_PDA_FAKE_ID		0x0103
+#define KEYSPAN_PDA_ID			0x0104 /* no clue */
+
+/* All of the device info needed for the Keyspan PDA serial converter */
+static __u16	keyspan_vendor_id		= KEYSPAN_VENDOR_ID;
+static __u16	keyspan_pda_fake_product_id	= KEYSPAN_PDA_FAKE_ID;
+static __u16	keyspan_pda_product_id		= KEYSPAN_PDA_ID;
+
+
+
+static void keyspan_pda_wakeup_write( struct usb_serial_port *port )
+{
+
+	struct tty_struct *tty = port->tty;
+
+	/* wake up port processes */
+	wake_up_interruptible( &port->write_wait );
+
+	/* wake up line discipline */
+	if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+	&& tty->ldisc.write_wakeup )
+		(tty->ldisc.write_wakeup)(tty);
+
+	/* wake up other tty processes */
+	wake_up_interruptible( &tty->write_wait );
+	/* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
+
+}
+
+static void keyspan_pda_request_unthrottle( struct usb_serial *serial )
+{
+
+	dbg(" request_unthrottle");
+	/* ask the device to tell us when the tx buffer becomes
+	   sufficiently empty */
+	usb_control_msg(serial->dev, 
+			     usb_sndctrlpipe(serial->dev, 0),
+			     7, /* request_unthrottle */
+			     USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+			     | USB_DIR_OUT,
+			     16, /* value: threshold */
+			     0, /* index */
+			     NULL,
+			     0,
+			     2*HZ);
+
+}
+
+
+static void keyspan_pda_rx_interrupt (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+       	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i;
+	struct keyspan_pda_private *priv;
+	priv = (struct keyspan_pda_private *)(port->private);
+
+	/* the urb might have been killed. */
+	if (urb->status)
+		return;
+	
+	if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+	
+ 	/* see if the message is data or a status interrupt */
+	switch (data[0]) {
+	case 0:
+		/* rest of message is rx data */
+		if (urb->actual_length) {
+			tty = serial->port[0].tty;
+			for (i = 1; i < urb->actual_length ; ++i) {
+				tty_insert_flip_char(tty, data[i], 0);
+			}
+			tty_flip_buffer_push(tty);
+		}
+		break;
+	case 1:
+		/* status interrupt */
+		dbg(" rx int, d1=%d, d2=%d", data[1], data[2]);
+		switch (data[1]) {
+		case 1: /* modemline change */
+			break;
+		case 2: /* tx unthrottle interrupt */
+			tty = serial->port[0].tty;
+			priv->tx_throttled = 0;
+			/* queue up a wakeup at scheduler time */
+			queue_task( &priv->wakeup_task, &tq_scheduler );
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* INT urbs are automatically re-submitted */
+}
+
+
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
+{
+	/* stop receiving characters. We just turn off the URB request, and
+	   let chars pile up in the device. If we're doing hardware
+	   flowcontrol, the device will signal the other end when its buffer
+	   fills up. If we're doing XON/XOFF, this would be a good time to
+	   send an XOFF, although it might make sense to foist that off
+	   upon the device too. */
+
+	dbg("keyspan_pda_rx_throttle port %d", port->number);
+	usb_unlink_urb(port->interrupt_in_urb);
+}
+
+
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port)
+{
+	/* just restart the receive interrupt URB */
+	dbg("keyspan_pda_rx_unthrottle port %d", port->number);
+	if (usb_submit_urb(port->interrupt_in_urb))
+		dbg(" usb_submit_urb(read urb) failed");
+	return;
+}
+
+
+static int keyspan_pda_setbaud (struct usb_serial *serial, int baud)
+{
+	int rc;
+	int bindex;
+
+	switch(baud) {
+		case 110: bindex = 0; break;
+		case 300: bindex = 1; break;
+		case 1200: bindex = 2; break;
+		case 2400: bindex = 3; break;
+		case 4800: bindex = 4; break;
+		case 9600: bindex = 5; break;
+		case 19200: bindex = 6; break;
+		case 38400: bindex = 7; break;
+		case 57600: bindex = 8; break;
+		case 115200: bindex = 9; break;
+		default: return -EINVAL;
+	}
+
+	/* rather than figure out how to sleep while waiting for this
+	   to complete, I just use the "legacy" API. */
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     0, /* set baud */
+			     USB_TYPE_VENDOR 
+			     | USB_RECIP_INTERFACE
+			     | USB_DIR_OUT, /* type */
+			     bindex, /* value */
+			     0, /* index */
+			     NULL, /* &data */
+			     0, /* size */
+			     2*HZ); /* timeout */
+	return(rc);
+}
+
+
+static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state)
+{
+	struct usb_serial *serial = port->serial;
+	int value;
+	if (break_state == -1)
+		value = 1; /* start break */
+	else
+		value = 0; /* clear break */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			4, /* set break */
+			USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			value, 0, NULL, 0, 2*HZ);
+	/* there is something funky about this.. the TCSBRK that 'cu' performs
+	   ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
+	   seconds apart, but it feels like the break sent isn't as long as it
+	   is on /dev/ttyS0 */
+}
+
+
+static void keyspan_pda_set_termios (struct usb_serial_port *port, 
+				     struct termios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned int cflag = port->tty->termios->c_cflag;
+
+	/* cflag specifies lots of stuff: number of stop bits, parity, number
+	   of data bits, baud. What can the device actually handle?:
+	   CSTOPB (1 stop bit or 2)
+	   PARENB (parity)
+	   CSIZE (5bit .. 8bit)
+	   There is minimal hw support for parity (a PSW bit seems to hold the
+	   parity of whatever is in the accumulator). The UART either deals
+	   with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
+	   1 special, stop). So, with firmware changes, we could do:
+	   8N1: 10 bit
+	   8N2: 11 bit, extra bit always (mark?)
+	   8[EOMS]1: 11 bit, extra bit is parity
+	   7[EOMS]1: 10 bit, b0/b7 is parity
+	   7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
+
+	   HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS
+	   bit.
+
+	   For now, just do baud. */
+
+	switch (cflag & CBAUD) {
+		/* we could support more values here, just need to calculate
+		   the necessary divisors in the firmware. <asm/termbits.h>
+		   has the Bnnn constants. */
+		case B110: keyspan_pda_setbaud(serial, 110); break;
+		case B300: keyspan_pda_setbaud(serial, 300); break;
+		case B1200: keyspan_pda_setbaud(serial, 1200); break;
+		case B2400: keyspan_pda_setbaud(serial, 2400); break;
+		case B4800: keyspan_pda_setbaud(serial, 4800); break;
+		case B9600: keyspan_pda_setbaud(serial, 9600); break;
+		case B19200: keyspan_pda_setbaud(serial, 19200); break;
+		case B38400: keyspan_pda_setbaud(serial, 38400); break;
+		case B57600: keyspan_pda_setbaud(serial, 57600); break;
+		case B115200: keyspan_pda_setbaud(serial, 115200); break;
+		default: dbg("can't handle requested baud rate"); break;
+	}
+}
+
+
+/* modem control pins: DTR and RTS are outputs and can be controlled.
+   DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
+   read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
+
+static int keyspan_pda_get_modem_info(struct usb_serial *serial,
+				      unsigned char *value)
+{
+	int rc;
+	unsigned char data;
+	rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			     3, /* get pins */
+			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
+			     0, 0, &data, 1, 2*HZ);
+	if (rc > 0)
+		*value = data;
+	return rc;
+}
+
+
+static int keyspan_pda_set_modem_info(struct usb_serial *serial,
+				      unsigned char value)
+{
+	int rc;
+	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     3, /* set pins */
+			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
+			     value, 0, NULL, 0, 2*HZ);
+	return rc;
+}
+
+
+static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file,
+			     unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = port->serial;
+	int rc;
+	unsigned int value;
+	unsigned char status, mask;
+
+	switch (cmd) {
+	case TIOCMGET: /* get modem pins state */
+		rc = keyspan_pda_get_modem_info(serial, &status);
+		if (rc < 0)
+			return rc;
+		value =
+			((status & (1<<7)) ? TIOCM_DTR : 0) |
+			((status & (1<<6)) ? TIOCM_CAR : 0) |
+			((status & (1<<5)) ? TIOCM_RNG : 0) |
+			((status & (1<<4)) ? TIOCM_DSR : 0) |
+			((status & (1<<3)) ? TIOCM_CTS : 0) |
+			((status & (1<<2)) ? TIOCM_RTS : 0);
+		if (copy_to_user((unsigned int *)arg, &value, sizeof(int)))
+			return -EFAULT;
+		return 0;
+	case TIOCMSET: /* set a state as returned by MGET */
+		if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+			return -EFAULT;
+		status =
+			((value & TIOCM_DTR) ? (1<<7) : 0) |
+			((value & TIOCM_CAR) ? (1<<6) : 0) |
+			((value & TIOCM_RNG) ? (1<<5) : 0) |
+			((value & TIOCM_DSR) ? (1<<4) : 0) |
+			((value & TIOCM_CTS) ? (1<<3) : 0) |
+			((value & TIOCM_RTS) ? (1<<2) : 0);
+		rc = keyspan_pda_set_modem_info(serial, status);
+		if (rc < 0)
+			return rc;
+		return 0;
+	case TIOCMBIS: /* set bits in bitmask <arg> */
+	case TIOCMBIC: /* clear bits from bitmask <arg> */
+		if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+			return -EFAULT;
+		rc = keyspan_pda_get_modem_info(serial, &status);
+		if (rc < 0)
+			return rc;
+		mask =
+			((value & TIOCM_RTS) ? (1<<2) : 0) |
+			((value & TIOCM_DTR) ? (1<<7) : 0);
+		if (cmd == TIOCMBIS)
+			status |= mask;
+		else
+			status &= ~mask;
+		rc = keyspan_pda_set_modem_info(serial, status);
+		if (rc < 0)
+			return rc;
+		return 0;
+	case TIOCMIWAIT:
+		/* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+		/* TODO */
+	case TIOCGICOUNT:
+		/* return count of modemline transitions */
+		return 0; /* TODO */
+	}
+	
+	return -ENOIOCTLCMD;
+}
+
+static int keyspan_pda_write(struct usb_serial_port *port, int from_user, 
+			     const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	int request_unthrottle = 0;
+	int rc = 0;
+	struct keyspan_pda_private *priv;
+	unsigned long flags;
+
+	priv = (struct keyspan_pda_private *)(port->private);
+	/* guess how much room is left in the device's ring buffer, and if we
+	   want to send more than that, check first, updating our notion of
+	   what is left. If our write will result in no room left, ask the
+	   device to give us an interrupt when the room available rises above
+	   a threshold, and hold off all writers (eventually, those using
+	   select() or poll() too) until we receive that unthrottle interrupt.
+	   Block if we can't write anything at all, otherwise write as much as
+	   we can. */
+	dbg("keyspan_pda_write(%d)",count);
+	if (count == 0) {
+		dbg(" write request of 0 bytes");
+		return (0);
+	}
+
+	/* we might block because of:
+	   the TX urb is in-flight (wait until it completes)
+	   the device is full (wait until it says there is room)
+	*/
+	if (port->write_urb->status == -EINPROGRESS || priv->tx_throttled ) {
+		return( 0 );
+	}
+
+	/* At this point the URB is in our control, nobody else can submit it
+	   again (the only sudden transition was the one from EINPROGRESS to
+	   finished).  Also, the tx process is not throttled. So we are
+	   ready to write. */
+
+	spin_lock_irqsave (&port->port_lock, flags);
+	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+	/* Check if we might overrun the Tx buffer.   If so, ask the
+	   device how much room it really has.  This is done only on
+	   scheduler time, since usb_control_msg() sleeps. */
+	if (count > priv->tx_room && !in_interrupt()) {
+		unsigned char room;
+		rc = usb_control_msg(serial->dev, 
+				     usb_rcvctrlpipe(serial->dev, 0),
+				     6, /* write_room */
+				     USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+				     | USB_DIR_IN,
+				     0, /* value: 0 means "remaining room" */
+				     0, /* index */
+				     &room,
+				     1,
+				     2*HZ);
+		if (rc < 0) {
+			dbg(" roomquery failed");
+			spin_unlock_irqrestore (&port->port_lock, flags);
+			return rc; /* failed */
+		}
+		if (rc == 0) {
+			dbg(" roomquery returned 0 bytes");
+			spin_unlock_irqrestore (&port->port_lock, flags);
+			return -EIO; /* device didn't return any data */
+		}
+		dbg(" roomquery says %d", room);
+		priv->tx_room = room;
+	}
+	if (count > priv->tx_room) {
+		/* we're about to completely fill the Tx buffer, so
+		   we'll be throttled afterwards. */
+		count = priv->tx_room;
+		request_unthrottle = 1;
+	}
+
+	if (count) {
+		/* now transfer data */
+		if (from_user) {
+			if( copy_from_user(port->write_urb->transfer_buffer,
+			buf, count) ) {
+				spin_unlock_irqrestore (&port->port_lock, flags);
+				return( -EFAULT );
+			}
+		}
+		else {
+			memcpy (port->write_urb->transfer_buffer, buf, count);
+		}  
+		/* send the data out the bulk port */
+		port->write_urb->transfer_buffer_length = count;
+		
+		priv->tx_room -= count;
+
+		if (usb_submit_urb(port->write_urb)) {
+			dbg(" usb_submit_urb(write bulk) failed");
+			spin_unlock_irqrestore (&port->port_lock, flags);
+			return (0);
+		}
+	}
+	else {
+		/* There wasn't any room left, so we are throttled until
+		   the buffer empties a bit */
+		request_unthrottle = 1;
+	}
+
+	if (request_unthrottle) {
+		priv->tx_throttled = 1; /* block writers */
+		queue_task( &priv->unthrottle_task, &tq_scheduler );
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+	return (count);
+}
+
+
+static void keyspan_pda_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial;
+	struct keyspan_pda_private *priv;
+
+	priv = (struct keyspan_pda_private *)(port->private);
+
+	if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+		return;
+	}
+	
+	/* queue up a wakeup at scheduler time */
+	queue_task( &priv->wakeup_task, &tq_scheduler );
+
+}
+
+
+static int keyspan_pda_write_room (struct usb_serial_port *port)
+{
+	struct keyspan_pda_private *priv;
+	priv = (struct keyspan_pda_private *)(port->private);
+
+	/* used by n_tty.c for processing of tabs and such. Giving it our
+	   conservative guess is probably good enough, but needs testing by
+	   running a console through the device. */
+
+	return (priv->tx_room);
+}
+
+
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
+{
+	struct keyspan_pda_private *priv;
+	priv = (struct keyspan_pda_private *)(port->private);
+	
+	/* when throttled, return at least WAKEUP_CHARS to tell select() (via
+	   n_tty.c:normal_poll() ) that we're not writeable. */
+	if( port->write_urb->status == -EINPROGRESS || priv->tx_throttled )
+		return 256;
+	return 0;
+}
+
+
+static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned char room;
+	int rc;
+	struct keyspan_pda_private *priv;
+	unsigned long flags;
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	MOD_INC_USE_COUNT;
+	++port->open_count;
+
+	if (!port->active) {
+		port->active = 1;
+ 
+		/* find out how much room is in the Tx ring */
+		spin_unlock_irqrestore (&port->port_lock, flags);
+		rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				     6, /* write_room */
+				     USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+				     | USB_DIR_IN,
+				     0, /* value */
+				     0, /* index */
+				     &room,
+				     1,
+				     2*HZ);
+		spin_lock_irqsave (&port->port_lock, flags);
+		if (rc < 0) {
+			dbg(__FUNCTION__" - roomquery failed");
+			goto error;
+		}
+		if (rc == 0) {
+			dbg(__FUNCTION__" - roomquery returned 0 bytes");
+			rc = -EIO;
+			goto error;
+		}
+		priv = (struct keyspan_pda_private *)(port->private);
+		priv->tx_room = room;
+		priv->tx_throttled = room ? 0 : 1;
+
+		/* the normal serial device seems to always turn on DTR and RTS here,
+		   so do the same */
+		spin_unlock_irqrestore (&port->port_lock, flags);
+		if (port->tty->termios->c_cflag & CBAUD)
+			keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
+		else
+			keyspan_pda_set_modem_info(serial, 0);
+
+		/*Start reading from the device*/
+		if (usb_submit_urb(port->interrupt_in_urb))
+			dbg(__FUNCTION__" - usb_submit_urb(read int) failed");
+	} else {
+		spin_unlock_irqrestore (&port->port_lock, flags);
+	}
+
+
+	return (0);
+error:
+	--port->open_count;
+	port->active = 0;
+	MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore (&port->port_lock, flags);
+	return rc;
+}
+
+
+static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	--port->open_count;
+	MOD_DEC_USE_COUNT;
+
+	if (port->open_count <= 0) {
+		/* the normal serial device seems to always shut off DTR and RTS now */
+		spin_unlock_irqrestore (&port->port_lock, flags);
+		if (port->tty->termios->c_cflag & HUPCL)
+			keyspan_pda_set_modem_info(serial, 0);
+		spin_lock_irqsave (&port->port_lock, flags);
+
+		/* shutdown our bulk reads and writes */
+		usb_unlink_urb (port->write_urb);
+		usb_unlink_urb (port->interrupt_in_urb);
+		port->active = 0;
+		port->open_count = 0;
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+}
+
+
+/* download the firmware to a "fake" device (pre-renumeration) */
+static int keyspan_pda_fake_startup (struct usb_serial *serial)
+{
+	int response;
+	const struct ezusb_hex_record *record;
+
+	/* download the firmware here ... */
+	response = ezusb_set_reset(serial, 1);
+
+	record = &keyspan_pda_firmware[0];
+	while(record->address != 0xffff) {
+		response = ezusb_writememory(serial, record->address,
+					     (unsigned char *)record->data,
+					     record->data_size, 0xa0);
+		if (response < 0) {
+			err("ezusb_writememory failed for Keyspan PDA "
+			    "firmware (%d %04X %p %d)",
+			    response, 
+			    record->address, record->data, record->data_size);
+			break;
+		}
+		record++;
+	}
+	/* bring device out of reset. Renumeration will occur in a moment
+	   and the new device will bind to the real driver */
+	response = ezusb_set_reset(serial, 0);
+
+	/* we want this device to fail to have a driver assigned to it. */
+	return (1);
+}
+
+static int keyspan_pda_startup (struct usb_serial *serial)
+{
+
+	struct keyspan_pda_private *priv;
+
+	/* allocate the private data structures for all ports. Well, for all
+	   one ports. */
+
+	priv = serial->port[0].private
+		= kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
+	if (!priv)
+		return (1); /* error */
+	init_waitqueue_head(&serial->port[0].write_wait);
+	priv->wakeup_task.next = NULL;
+	priv->wakeup_task.sync = 0;
+	priv->wakeup_task.routine = (void *)keyspan_pda_wakeup_write;
+	priv->wakeup_task.data = (void *)(&serial->port[0]);
+	priv->unthrottle_task.next = NULL;
+	priv->unthrottle_task.sync = 0;
+	priv->unthrottle_task.routine = (void *)keyspan_pda_request_unthrottle;
+	priv->unthrottle_task.data = (void *)(serial);
+	return (0);
+}
+
+static void keyspan_pda_shutdown (struct usb_serial *serial)
+{
+	dbg (__FUNCTION__);
+	
+	while (serial->port[0].open_count > 0) {
+		keyspan_pda_close (&serial->port[0], NULL);
+	}
+	kfree(serial->port[0].private);
+}
+
+struct usb_serial_device_type keyspan_pda_fake_device = {
+	name:			"Keyspan PDA - (prerenumeration)",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_pda_fake_product_id,
+	needs_interrupt_in:	DONT_CARE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		DONT_CARE,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		keyspan_pda_fake_startup,
+};
+
+struct usb_serial_device_type keyspan_pda_device = {
+	name:			"Keyspan PDA",
+	idVendor:		&keyspan_vendor_id,
+	idProduct:		&keyspan_pda_product_id,
+	needs_interrupt_in:	MUST_HAVE,
+	needs_bulk_in:		DONT_CARE,
+	needs_bulk_out:		MUST_HAVE,
+	num_interrupt_in:	1,
+	num_bulk_in:		0,
+	num_bulk_out:		1,
+	num_ports:		1,
+	open:			keyspan_pda_open,
+	close:			keyspan_pda_close,
+	write:			keyspan_pda_write,
+	write_room:		keyspan_pda_write_room,
+	write_bulk_callback: 	keyspan_pda_write_bulk_callback,
+	read_int_callback:	keyspan_pda_rx_interrupt,
+	chars_in_buffer:	keyspan_pda_chars_in_buffer,
+	throttle:		keyspan_pda_rx_throttle,
+	unthrottle:		keyspan_pda_rx_unthrottle,
+	ioctl:			keyspan_pda_ioctl,
+	set_termios:		keyspan_pda_set_termios,
+	break_ctl:		keyspan_pda_break_ctl,
+	startup:		keyspan_pda_startup,
+	shutdown:		keyspan_pda_shutdown,
+};
+
+
+static int __init keyspan_pda_init (void)
+{
+	usb_serial_register (&keyspan_pda_fake_device);
+	usb_serial_register (&keyspan_pda_device);
+	return 0;
+}
+
+
+static void __exit keyspan_pda_exit (void)
+{
+	usb_serial_deregister (&keyspan_pda_fake_device);
+	usb_serial_deregister (&keyspan_pda_device);
+}
+
+
+module_init(keyspan_pda_init);
+module_exit(keyspan_pda_exit);
+
+MODULE_AUTHOR("Brian Warner <warner@lothar.com>");
+MODULE_DESCRIPTION("USB Keyspan PDA Converter driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_pda_fw.h linux.18p17/drivers/usb/serial/keyspan_pda_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_pda_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_pda_fw.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,93 @@
+/*
+ * keyspan_pda_fw.h
+ *
+ * Generated from keyspan_pda.s by ezusb_convert.pl
+ * This file is presumed to be under the same copyright as the source file
+ * from which it was derived.
+ */
+
+static const struct ezusb_hex_record keyspan_pda_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x02, 0x00} },
+{ 0x0023,	4,	{0x02, 0x05, 0x5f, 0x00} },
+{ 0x0043,	4,	{0x02, 0x01, 0x00, 0x00} },
+{ 0x0030,	5,	{0x00, 0x00, 0x00, 0x00, 0x00} },
+{ 0x0100,	16,	{0x02, 0x02, 0x96, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00} },
+{ 0x0110,	16,	{0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00} },
+{ 0x0120,	16,	{0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x04, 0x61, 0x00, 0x02, 0x04, 0x89, 0x00} },
+{ 0x0200,	16,	{0x75, 0x81, 0x5e, 0xe4, 0xf5, 0x32, 0xf5, 0x33, 0xf5, 0x30, 0xf5, 0x31, 0xf5, 0x34, 0xc2, 0x00} },
+{ 0x0210,	16,	{0xc2, 0x01, 0xa9, 0x00, 0x74, 0xfe, 0x90, 0x10, 0x00, 0xf0, 0xa3, 0xd9, 0xfc, 0x74, 0xfd, 0x90} },
+{ 0x0220,	16,	{0x11, 0x00, 0xf0, 0xa3, 0xd9, 0xfc, 0x74, 0x02, 0x90, 0x7f, 0x9d, 0xf0, 0x74, 0x00, 0x90, 0x7f} },
+{ 0x0230,	16,	{0x97, 0xf0, 0x74, 0x86, 0x90, 0x7f, 0x9e, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0x03, 0xf0, 0x90, 0x7f} },
+{ 0x0240,	16,	{0xaf, 0xe0, 0xd2, 0xe0, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xab, 0xf0, 0x90, 0x7f, 0xae, 0xf0, 0x90} },
+{ 0x0250,	16,	{0x7f, 0xac, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xad, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x74} },
+{ 0x0260,	16,	{0x84, 0x90, 0x7f, 0x98, 0xf0, 0x74, 0x00, 0xf5, 0x98, 0x75, 0xc8, 0x30, 0x7b, 0x05, 0x91, 0x20} },
+{ 0x0270,	16,	{0xd2, 0xca, 0x75, 0x98, 0x50, 0xd2, 0xe8, 0xd2, 0xaf, 0xd2, 0xac, 0x74, 0x00, 0xf5, 0x86, 0x90} },
+{ 0x0280,	16,	{0x7f, 0xd6, 0x74, 0x02, 0xf0, 0x79, 0x2e, 0x7a, 0x00, 0x7b, 0x00, 0xdb, 0xfe, 0xda, 0xfa, 0xd9} },
+{ 0x0290,	16,	{0xf6, 0x74, 0x06, 0xf0, 0x80, 0xfe, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85} },
+{ 0x02a0,	16,	{0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0x90, 0x7f} },
+{ 0x02b0,	16,	{0xe8, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb, 0xa3, 0xe0, 0xfc, 0xe9, 0x54, 0x60, 0xb4} },
+{ 0x02c0,	16,	{0x00, 0x03, 0x02, 0x03, 0x39, 0xb4, 0x40, 0x6e, 0xba, 0x00, 0x0b, 0x12, 0x04, 0x20, 0x40, 0x03} },
+{ 0x02d0,	16,	{0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x01, 0x03, 0x02, 0x04, 0x0a, 0xba, 0x02, 0x03, 0x02} },
+{ 0x02e0,	16,	{0x04, 0x0a, 0xba, 0x03, 0x03, 0x02, 0x04, 0x44, 0xba, 0x04, 0x1e, 0xbb, 0x00, 0x0a, 0x90, 0x7f} },
+{ 0x02f0,	16,	{0x95, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfd, 0xf0, 0x90} },
+{ 0x0300,	16,	{0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x02, 0x04, 0x02, 0xba, 0x05, 0x03, 0x02, 0x04, 0x0a, 0xba} },
+{ 0x0310,	16,	{0x06, 0x19, 0xbb, 0x00, 0x08, 0xe5, 0x33, 0xd3, 0x95, 0x32, 0x02, 0x03, 0xde, 0xbb, 0x01, 0x08} },
+{ 0x0320,	16,	{0xe5, 0x32, 0xc3, 0x95, 0x33, 0x02, 0x03, 0xde, 0x02, 0x04, 0x0a, 0xba, 0x07, 0x05, 0x8b, 0x34} },
+{ 0x0330,	16,	{0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0x02, 0x04, 0x0a, 0xba, 0x00, 0x20, 0xb9, 0x80, 0x10, 0x90} },
+{ 0x0340,	16,	{0x7f, 0x00, 0xe4, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x04, 0x02, 0xb9} },
+{ 0x0350,	16,	{0x82, 0x02, 0x80, 0xeb, 0xb9, 0x81, 0x02, 0x80, 0xe6, 0x02, 0x04, 0x0a, 0xba, 0x01, 0x0f, 0xbb} },
+{ 0x0360,	16,	{0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x03} },
+{ 0x0370,	16,	{0x0f, 0xbb, 0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a} },
+{ 0x0380,	16,	{0xba, 0x06, 0x56, 0xbc, 0x01, 0x0f, 0x90, 0x7f, 0xd4, 0x74, 0x06, 0xf0, 0x90, 0x7f, 0xd5, 0x74} },
+{ 0x0390,	16,	{0x12, 0xf0, 0x02, 0x04, 0x02, 0xbc, 0x02, 0x12, 0xbb, 0x00, 0x6f, 0x90, 0x7f, 0xd4, 0x74, 0x06} },
+{ 0x03a0,	16,	{0xf0, 0x90, 0x7f, 0xd5, 0x74, 0x24, 0xf0, 0x02, 0x04, 0x02, 0xbc, 0x03, 0x29, 0x74, 0x04, 0xc3} },
+{ 0x03b0,	16,	{0x9b, 0x40, 0x57, 0x60, 0x55, 0xeb, 0x2b, 0x90, 0x06, 0x44, 0x25, 0x82, 0xf5, 0x82, 0x74, 0x00} },
+{ 0x03c0,	16,	{0x35, 0x83, 0xf5, 0x83, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0x90, 0x7f, 0xd4, 0xe9, 0xf0, 0x90, 0x7f} },
+{ 0x03d0,	16,	{0xd5, 0xea, 0xf0, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x08, 0x0f, 0x74, 0x01, 0x90, 0x7f} },
+{ 0x03e0,	16,	{0x00, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x04, 0x02, 0xba, 0x09, 0x03, 0x02, 0x04} },
+{ 0x03f0,	16,	{0x02, 0xba, 0x0a, 0x05, 0x74, 0x00, 0x02, 0x03, 0xde, 0xba, 0x0b, 0x03, 0x02, 0x04, 0x02, 0x02} },
+{ 0x0400,	16,	{0x04, 0x0a, 0x90, 0x7f, 0xb4, 0x74, 0x02, 0xf0, 0x80, 0x09, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x0410,	16,	{0xf0, 0x80, 0x00, 0xd0, 0xe0, 0xd0, 0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32} },
+{ 0x0420,	16,	{0xeb, 0x20, 0xe7, 0x1e, 0xc3, 0x94, 0x0a, 0x50, 0x19, 0xeb, 0x23, 0x24, 0xfe, 0xf5, 0x82, 0x74} },
+{ 0x0430,	16,	{0x05, 0x34, 0x00, 0xf5, 0x83, 0xe0, 0xf5, 0xcb, 0xf5, 0xcd, 0xa3, 0xe0, 0xf5, 0xca, 0xf5, 0xcc} },
+{ 0x0440,	16,	{0xc3, 0x22, 0xd3, 0x22, 0xb9, 0x41, 0x11, 0xeb, 0x64, 0xff, 0x54, 0x84, 0xfb, 0x90, 0x7f, 0x98} },
+{ 0x0450,	16,	{0xe0, 0x54, 0x7b, 0x4b, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 0x9b, 0xe0, 0x64, 0xff, 0x02, 0x03} },
+{ 0x0460,	16,	{0xde, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2} },
+{ 0x0470,	16,	{0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xa9, 0x74, 0x04, 0xf0, 0x12, 0x05, 0xa0, 0xd0, 0xe0, 0xd0, 0x85} },
+{ 0x0480,	16,	{0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0} },
+{ 0x0490,	16,	{0x84, 0xc0, 0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xaa, 0x74, 0x04} },
+{ 0x04a0,	16,	{0xf0, 0x90, 0x7f, 0xc9, 0xe0, 0xf9, 0xe4, 0xf5, 0x86, 0x90, 0x7d, 0xc0, 0x75, 0x85, 0x10, 0x85} },
+{ 0x04b0,	16,	{0x32, 0x84, 0xe0, 0x05, 0x86, 0x05, 0x84, 0xf0, 0xe5, 0x84, 0xb5, 0x33, 0x02, 0x80, 0x09, 0x05} },
+{ 0x04c0,	16,	{0x32, 0x05, 0x86, 0xa3, 0xd9, 0xec, 0x80, 0x00, 0x90, 0x7f, 0xc9, 0xf0, 0xb1, 0x31, 0xd0, 0xe0} },
+{ 0x04d0,	16,	{0xd0, 0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0xe4, 0xf5, 0x86, 0x90, 0x7f} },
+{ 0x04e0,	16,	{0xbc, 0xe0, 0x20, 0xe1, 0x4b, 0x90, 0x7d, 0x00, 0xe5, 0x32, 0xf0, 0xa3, 0xe5, 0x33, 0xf0, 0xa3} },
+{ 0x04f0,	16,	{0xe5, 0x30, 0xf0, 0xa3, 0xe5, 0x31, 0xf0, 0xa3, 0xe4, 0x30, 0x00, 0x01, 0x04, 0xf0, 0xa3, 0x05} },
+{ 0x0500,	16,	{0x86, 0x90, 0x10, 0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6} },
+{ 0x0510,	16,	{0x05, 0x86, 0x74, 0xfc, 0xf0, 0xa3, 0x05, 0x86, 0x90, 0x11, 0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05} },
+{ 0x0520,	16,	{0x86, 0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6, 0xe4, 0xf5, 0x86, 0x90, 0x7f, 0xbd, 0x74, 0x26, 0xf0} },
+{ 0x0530,	16,	{0x22, 0x20, 0x00, 0x13, 0xe5, 0x32, 0xb5, 0x33, 0x01, 0x22, 0x05, 0x33, 0x75, 0x83, 0x10, 0x85} },
+{ 0x0540,	16,	{0x33, 0x82, 0xe0, 0xf5, 0x99, 0xd2, 0x00, 0x74, 0x00, 0xb5, 0x34, 0x01, 0x22, 0xe5, 0x33, 0xd3} },
+{ 0x0550,	16,	{0x95, 0x32, 0xc3, 0x95, 0x34, 0x40, 0xf5, 0x75, 0x34, 0x00, 0xd2, 0x01, 0x02, 0x05, 0xa0, 0xc0} },
+{ 0x0560,	16,	{0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85, 0xc0, 0xe0, 0x30, 0x99, 0x07, 0xc2, 0x99} },
+{ 0x0570,	16,	{0xc2, 0x00, 0x12, 0x05, 0x34, 0x30, 0x98, 0x05, 0x12, 0x05, 0x8a, 0xc2, 0x98, 0xd0, 0xe0, 0xd0} },
+{ 0x0580,	16,	{0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0x75, 0x83, 0x11, 0x85, 0x30, 0x82} },
+{ 0x0590,	16,	{0x05, 0x82, 0xe5, 0x99, 0xf0, 0xe5, 0x82, 0xb5, 0x31, 0x01, 0x22, 0x05, 0x30, 0xb1, 0xa0, 0x22} },
+{ 0x05a0,	16,	{0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x38, 0x20, 0x01, 0x36, 0xe5, 0x30, 0xb5, 0x31, 0x01, 0x22} },
+{ 0x05b0,	16,	{0xe4, 0xf5, 0x86, 0x75, 0x83, 0x11, 0x05, 0x86, 0x90, 0x7e, 0x00, 0xf0, 0xa3, 0x05, 0x86, 0x79} },
+{ 0x05c0,	16,	{0x01, 0xe5, 0x30, 0xb5, 0x31, 0x02, 0x80, 0x10, 0x05, 0x31, 0x85, 0x31, 0x82, 0xe0, 0x05, 0x86} },
+{ 0x05d0,	16,	{0xf0, 0xa3, 0x05, 0x86, 0x09, 0xb9, 0x40, 0xe9, 0x90, 0x7f, 0xb9, 0xe9, 0x60, 0x01, 0xf0, 0x22} },
+{ 0x05e0,	16,	{0xc2, 0x01, 0xe4, 0xf5, 0x86, 0x90, 0x7e, 0x00, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x02, 0xf0, 0x90} },
+{ 0x05f0,	16,	{0x7f, 0xb9, 0xf0, 0x22, 0xc2, 0x99, 0xf5, 0x99, 0x30, 0x99, 0xfd, 0xc2, 0x99, 0x22, 0xe5, 0x5e} },
+{ 0x0600,	16,	{0xf6, 0x3c, 0xfd, 0x8f, 0xfe, 0xc8, 0xff, 0x64, 0xff, 0xb2, 0xff, 0xd9, 0xff, 0xed, 0xff, 0xf3} },
+{ 0x0610,	16,	{0xff, 0xfa, 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40, 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab} },
+{ 0x0620,	16,	{0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00} },
+{ 0x0630,	16,	{0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01, 0x07, 0x05, 0x02} },
+{ 0x0640,	16,	{0x02, 0x40, 0x00, 0x00, 0x06, 0x4c, 0x06, 0x50, 0x06, 0x72, 0x06, 0xa0, 0x04, 0x03, 0x00, 0x00} },
+{ 0x0650,	16,	{0x22, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00} },
+{ 0x0660,	16,	{0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00} },
+{ 0x0670,	16,	{0x73, 0x00, 0x2e, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00} },
+{ 0x0680,	16,	{0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00} },
+{ 0x0690,	16,	{0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00} },
+{ 0x06a0,	6,	{0x06, 0x03, 0x34, 0x00, 0x37, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa18x_fw.h linux.18p17/drivers/usb/serial/keyspan_usa18x_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa18x_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa18x_fw.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,406 @@
+/* keyspan_usa18x_fw.h
+  
+   Generated from Keyspan firmware image Wed Jul  5 09:18:29 2000 EST
+   This firmware is for the Keyspan USA-18X Serial Adaptor
+
+   "The firmware contained herein as keyspan_usa18x_fw.h is
+   Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated
+   ("Keyspan"), as an unpublished work.  This notice does not imply
+   unrestricted or public access to this firmware which is a trade secret of
+   Keyspan, and which may not be reproduced, used, sold or transferred to any
+   third party without Keyspan's prior written consent.  All Rights Reserved.
+
+   This firmware may not be modified and may only be used with the Keyspan 
+   USA-18X Serial Adapter.  Distribution and/or Modification of the
+   keyspan.c driver which includes this firmware, in whole or in part,
+   requires the inclusion of this statement."
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa18x_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x0e, 0x9b} },
+{ 0x0003,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0013,	16,	{0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90} },
+{ 0x0023,	15,	{0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x07, 0xf0, 0xe4, 0x90, 0x7f, 0x98, 0xf0, 0x22} },
+{ 0x0033,	3,	{0x02, 0x13, 0xfc} },
+{ 0x0036,	12,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0043,	3,	{0x02, 0x14, 0x00} },
+{ 0x0046,	16,	{0x30, 0x08, 0x13, 0x12, 0x13, 0xc0, 0xef, 0xc3, 0x95, 0x3c, 0x50, 0x73, 0x90, 0x7f, 0xbf, 0x74} },
+{ 0x0056,	16,	{0x01, 0xf0, 0xc2, 0x08, 0x80, 0x69, 0x30, 0x02, 0x34, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x5f} },
+{ 0x0066,	16,	{0x12, 0x13, 0xc0, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x56, 0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92, 0x08} },
+{ 0x0076,	16,	{0x90, 0x7f, 0xc7, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7e, 0x7f, 0x41, 0x75, 0x0c, 0x7e, 0x75, 0x0d} },
+{ 0x0086,	16,	{0x41, 0x12, 0x0d, 0x55, 0xc2, 0x02, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x80, 0x32, 0x90, 0x7f, 0xc8} },
+{ 0x0096,	16,	{0xe0, 0x20, 0xe1, 0x2b, 0x12, 0x13, 0xc0, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x22, 0x90, 0x7d, 0xc0} },
+{ 0x00a6,	16,	{0xe0, 0x13, 0x92, 0x08, 0x90, 0x7f, 0xc9, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7d, 0x7f, 0xc1, 0x75} },
+{ 0x00b6,	16,	{0x0c, 0x7d, 0x75, 0x0d, 0xc1, 0x12, 0x0d, 0x55, 0xd2, 0x02, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0x90} },
+{ 0x00c6,	16,	{0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x01, 0x53, 0x53, 0x36, 0x80, 0x12, 0x13, 0xcc, 0xef} },
+{ 0x00d6,	16,	{0x42, 0x36, 0x12, 0x12, 0x49, 0x8f, 0x19, 0xef, 0xc3, 0x95, 0x3a, 0x50, 0x0f, 0x12, 0x13, 0xa8} },
+{ 0x00e6,	16,	{0xef, 0x30, 0xe0, 0x08, 0xe5, 0x36, 0x20, 0xe7, 0x03, 0x30, 0x0a, 0x61, 0xc2, 0x0a, 0xe5, 0x19} },
+{ 0x00f6,	16,	{0x70, 0x04, 0xf5, 0x36, 0x80, 0x57, 0x12, 0x13, 0xcc, 0xef, 0x42, 0x36, 0xe5, 0x36, 0x30, 0xe7} },
+{ 0x0106,	16,	{0x26, 0xe5, 0x19, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x19, 0x20, 0x85, 0x19, 0x08, 0x7e, 0x7e} },
+{ 0x0116,	16,	{0x7f, 0x80, 0x75, 0x0c, 0x7e, 0x75, 0x0d, 0x80, 0xaf, 0x36, 0x12, 0x0f, 0xb7, 0xe5, 0x19, 0x25} },
+{ 0x0126,	16,	{0xe0, 0x90, 0x7f, 0xb7, 0xf0, 0x80, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x3f, 0x40, 0x03, 0x75, 0x19} },
+{ 0x0136,	16,	{0x3f, 0x85, 0x19, 0x08, 0xe4, 0x90, 0x7e, 0x80, 0xf0, 0x7e, 0x7e, 0x7f, 0x81, 0x75, 0x0c, 0x7e} },
+{ 0x0146,	16,	{0x75, 0x0d, 0x81, 0x12, 0x0d, 0x7a, 0xe5, 0x19, 0x04, 0x90, 0x7f, 0xb7, 0xf0, 0x90, 0x7f, 0xce} },
+{ 0x0156,	16,	{0xe0, 0x30, 0xe1, 0x06, 0x20, 0x04, 0x03, 0x02, 0x03, 0xbd, 0xc2, 0x04, 0xe4, 0xf5, 0x18, 0x74} },
+{ 0x0166,	16,	{0x40, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0x7c, 0x00} },
+{ 0x0176,	16,	{0x7b, 0x01, 0x7a, 0x7e, 0x79, 0x00, 0x24, 0x00, 0xf9, 0xec, 0x34, 0x7e, 0xfa, 0xef, 0x12, 0x0c} },
+{ 0x0186,	16,	{0xe5, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x20, 0xd7, 0x90, 0x7e, 0x00, 0xe0, 0x60, 0x68, 0x90, 0x7e} },
+{ 0x0196,	16,	{0x03, 0xe0, 0x60, 0x24, 0x7f, 0x01, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0x7f, 0x03, 0x7d, 0xcd, 0x12} },
+{ 0x01a6,	16,	{0x12, 0x24, 0x43, 0x46, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46} },
+{ 0x01b6,	16,	{0xf0, 0xe4, 0x90, 0x7e, 0x13, 0xf0, 0x80, 0x30, 0x90, 0x7e, 0x01, 0xe0, 0xff, 0x12, 0x10, 0xa8} },
+{ 0x01c6,	16,	{0x90, 0x7e, 0x02, 0xe0, 0xff, 0x12, 0x10, 0xce, 0x7f, 0x01, 0x90, 0x7e, 0x11, 0xe0, 0xfd, 0x12} },
+{ 0x01d6,	16,	{0x12, 0x24, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x24, 0x43, 0x46, 0x80, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x01e6,	16,	{0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5, 0x40} },
+{ 0x01f6,	16,	{0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x03, 0xe0, 0x70, 0x06, 0x90, 0x7e, 0x13, 0xe0} },
+{ 0x0206,	16,	{0x70, 0x08, 0xe4, 0x90, 0x7e, 0x13, 0xf0, 0x75, 0x25, 0xff, 0x90, 0x7e, 0x05, 0xe0, 0x60, 0x12} },
+{ 0x0216,	16,	{0xa3, 0xe0, 0x54, 0x3f, 0xf5, 0x44, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe5} },
+{ 0x0226,	16,	{0x44, 0xf0, 0x90, 0x7e, 0x07, 0xe0, 0x60, 0x2b, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x42, 0x80, 0x80} },
+{ 0x0236,	16,	{0x03, 0x53, 0x42, 0x7f, 0x53, 0x42, 0xfc, 0x90, 0x7e, 0x09, 0xe0, 0x60, 0x11, 0x43, 0x42, 0x02} },
+{ 0x0246,	16,	{0xa3, 0xe0, 0xff, 0x12, 0x11, 0x1a, 0x90, 0x7e, 0x0b, 0xe0, 0xff, 0x12, 0x11, 0x40, 0xaf, 0x42} },
+{ 0x0256,	16,	{0x12, 0x10, 0xf4, 0x90, 0x7e, 0x03, 0xe0, 0x60, 0x08, 0x53, 0x42, 0x7f, 0xaf, 0x42, 0x12, 0x10} },
+{ 0x0266,	16,	{0xf4, 0x90, 0x7e, 0x0c, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x46, 0x02, 0x80, 0x03} },
+{ 0x0276,	16,	{0x53, 0x46, 0xfd, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90} },
+{ 0x0286,	16,	{0x7e, 0x0e, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x46, 0x01, 0x80, 0x03, 0x53, 0x46} },
+{ 0x0296,	16,	{0xfe, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7e, 0x12} },
+{ 0x02a6,	16,	{0xe0, 0xf5, 0x3a, 0xa3, 0xe0, 0x13, 0x92, 0x0c, 0xa3, 0xe0, 0xf5, 0x3c, 0xa3, 0xe0, 0x60, 0x05} },
+{ 0x02b6,	16,	{0x43, 0x46, 0x10, 0x80, 0x03, 0x53, 0x46, 0xef, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0} },
+{ 0x02c6,	16,	{0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7e, 0x16, 0xe0, 0x60, 0x32, 0x53, 0x44, 0xbf, 0x90, 0x7f, 0x98} },
+{ 0x02d6,	16,	{0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x11} },
+{ 0x02e6,	16,	{0xf0, 0x12, 0x13, 0x9c, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0x53, 0x3e, 0xfd, 0xe4, 0xff} },
+{ 0x02f6,	16,	{0xad, 0x3e, 0x12, 0x12, 0x24, 0xe4, 0xf5, 0x2a, 0xf5, 0x29, 0xd2, 0x06, 0x90, 0x7e, 0x17, 0xe0} },
+{ 0x0306,	16,	{0x60, 0x0f, 0x43, 0x3e, 0x02, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0x75, 0x29, 0x01, 0xd2} },
+{ 0x0316,	16,	{0x06, 0x90, 0x7e, 0x18, 0xe0, 0x60, 0x1b, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5, 0x40, 0x44} },
+{ 0x0326,	16,	{0x04, 0x90, 0xc0, 0x00, 0xf0, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0xd2, 0x02} },
+{ 0x0336,	16,	{0xc2, 0x08, 0x90, 0x7e, 0x19, 0xe0, 0x60, 0x11, 0x43, 0x44, 0x40, 0x90, 0x7f, 0x98, 0x74, 0x13} },
+{ 0x0346,	16,	{0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1a, 0xe0, 0x60, 0x0f, 0x53} },
+{ 0x0356,	16,	{0x3e, 0xfe, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0x75, 0x2b, 0x01, 0xd2, 0x06, 0x90, 0x7e} },
+{ 0x0366,	16,	{0x1b, 0xe0, 0x60, 0x0f, 0x43, 0x3e, 0x01, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0xe4, 0xf5} },
+{ 0x0376,	16,	{0x2b, 0xd2, 0x06, 0x90, 0x7e, 0x1c, 0xe0, 0x60, 0x0e, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5} },
+{ 0x0386,	16,	{0x40, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1d, 0xe0, 0x60, 0x02, 0xd2, 0x0a, 0x90} },
+{ 0x0396,	16,	{0x7e, 0x1e, 0xe0, 0x60, 0x08, 0x75, 0x2c, 0x01, 0xe4, 0xf5, 0x38, 0xd2, 0x06, 0x90, 0x7e, 0x1f} },
+{ 0x03a6,	16,	{0xe0, 0x60, 0x0f, 0x90, 0x7f, 0xd7, 0x74, 0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74, 0x15, 0xf0, 0x74} },
+{ 0x03b6,	16,	{0x35, 0xf0, 0xe4, 0x90, 0x7f, 0xcf, 0xf0, 0x30, 0x18, 0x52, 0xe5, 0x38, 0x60, 0x02, 0x15, 0x38} },
+{ 0x03c6,	16,	{0x20, 0x12, 0x49, 0xe5, 0x13, 0xd3, 0x94, 0x00, 0x40, 0x04, 0x15, 0x13, 0x80, 0x3e, 0x75, 0x13} },
+{ 0x03d6,	16,	{0x0a, 0x30, 0x19, 0x02, 0xd2, 0x12, 0x12, 0x13, 0x9c, 0xef, 0x54, 0x01, 0xf5, 0x19, 0x65, 0x2a} },
+{ 0x03e6,	16,	{0x60, 0x05, 0x85, 0x19, 0x2a, 0xd2, 0x06, 0x12, 0x13, 0xd8, 0xef, 0x54, 0x80, 0xf5, 0x19, 0x65} },
+{ 0x03f6,	16,	{0x26, 0x60, 0x05, 0x85, 0x19, 0x26, 0xd2, 0x06, 0x30, 0x0c, 0x11, 0x12, 0x13, 0xd8, 0xef, 0x54} },
+{ 0x0406,	16,	{0x10, 0xf5, 0x19, 0x65, 0x25, 0x60, 0x05, 0x85, 0x19, 0x25, 0xd2, 0x06, 0x20, 0x19, 0x03, 0x02} },
+{ 0x0416,	16,	{0x07, 0xe4, 0x30, 0x09, 0x13, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x95, 0x3d, 0x50, 0x73, 0x90, 0x7f} },
+{ 0x0426,	16,	{0xc1, 0x74, 0x01, 0xf0, 0xc2, 0x09, 0x80, 0x69, 0x30, 0x03, 0x34, 0x90, 0x7f, 0xca, 0xe0, 0x20} },
+{ 0x0436,	16,	{0xe1, 0x5f, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x56, 0x90, 0x7d, 0x40, 0xe0, 0x13} },
+{ 0x0446,	16,	{0x92, 0x09, 0x90, 0x7f, 0xcb, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7d, 0x7f, 0x41, 0x75, 0x0c, 0x7d} },
+{ 0x0456,	16,	{0x75, 0x0d, 0x41, 0x12, 0x0d, 0x9f, 0xc2, 0x03, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0x80, 0x32, 0x90} },
+{ 0x0466,	16,	{0x7f, 0xcc, 0xe0, 0x20, 0xe1, 0x2b, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x22, 0x90} },
+{ 0x0476,	16,	{0x7c, 0xc0, 0xe0, 0x13, 0x92, 0x09, 0x90, 0x7f, 0xcd, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7c, 0x7f} },
+{ 0x0486,	16,	{0xc1, 0x75, 0x0c, 0x7c, 0x75, 0x0d, 0xc1, 0x12, 0x0d, 0x9f, 0xd2, 0x03, 0xe4, 0x90, 0x7f, 0xcd} },
+{ 0x0496,	16,	{0xf0, 0x90, 0x7f, 0xba, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x05, 0x25, 0x53, 0x37, 0x80, 0x12, 0x14} },
+{ 0x04a6,	16,	{0x37, 0xef, 0x42, 0x37, 0x12, 0x12, 0x93, 0x8f, 0x19, 0xef, 0xc3, 0x95, 0x3b, 0x50, 0x0f, 0x12} },
+{ 0x04b6,	16,	{0x14, 0x13, 0xef, 0x30, 0xe0, 0x08, 0xe5, 0x37, 0x20, 0xe7, 0x03, 0x30, 0x0b, 0x61, 0xc2, 0x0b} },
+{ 0x04c6,	16,	{0xe5, 0x19, 0x70, 0x04, 0xf5, 0x37, 0x80, 0x57, 0x12, 0x14, 0x37, 0xef, 0x42, 0x37, 0xe5, 0x37} },
+{ 0x04d6,	16,	{0x30, 0xe7, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x19, 0x20, 0x85, 0x19, 0x08} },
+{ 0x04e6,	16,	{0x7e, 0x7d, 0x7f, 0x80, 0x75, 0x0c, 0x7d, 0x75, 0x0d, 0x80, 0xaf, 0x37, 0x12, 0x0f, 0xf0, 0xe5} },
+{ 0x04f6,	16,	{0x19, 0x25, 0xe0, 0x90, 0x7f, 0xbb, 0xf0, 0x80, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x3f, 0x40, 0x03} },
+{ 0x0506,	16,	{0x75, 0x19, 0x3f, 0x85, 0x19, 0x08, 0xe4, 0x90, 0x7d, 0x80, 0xf0, 0x7e, 0x7d, 0x7f, 0x81, 0x75} },
+{ 0x0516,	16,	{0x0c, 0x7d, 0x75, 0x0d, 0x81, 0x12, 0x0d, 0xc4, 0xe5, 0x19, 0x04, 0x90, 0x7f, 0xbb, 0xf0, 0x90} },
+{ 0x0526,	16,	{0x7f, 0xd0, 0xe0, 0x30, 0xe1, 0x06, 0x20, 0x05, 0x03, 0x02, 0x07, 0x8f, 0xc2, 0x05, 0xe4, 0xf5} },
+{ 0x0536,	16,	{0x18, 0x74, 0xc0, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18} },
+{ 0x0546,	16,	{0x7c, 0x00, 0x7b, 0x01, 0x7a, 0x7e, 0x79, 0x20, 0x24, 0x20, 0xf9, 0xec, 0x34, 0x7e, 0xfa, 0xef} },
+{ 0x0556,	16,	{0x12, 0x0c, 0xe5, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x20, 0xd7, 0x90, 0x7e, 0x20, 0xe0, 0x60, 0x68} },
+{ 0x0566,	16,	{0x90, 0x7e, 0x23, 0xe0, 0x60, 0x24, 0x7f, 0x01, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0x7f, 0x03, 0x7d} },
+{ 0x0576,	16,	{0xcd, 0x12, 0x12, 0x6e, 0x43, 0x47, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00} },
+{ 0x0586,	16,	{0xe5, 0x47, 0xf0, 0xe4, 0x90, 0x7e, 0x33, 0xf0, 0x80, 0x30, 0x90, 0x7e, 0x21, 0xe0, 0xff, 0x12} },
+{ 0x0596,	16,	{0x11, 0x8c, 0x90, 0x7e, 0x22, 0xe0, 0xff, 0x12, 0x11, 0xb2, 0x7f, 0x01, 0x90, 0x7e, 0x31, 0xe0} },
+{ 0x05a6,	16,	{0xfd, 0x12, 0x12, 0x6e, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x6e, 0x43, 0x47, 0x80, 0x90, 0x7f} },
+{ 0x05b6,	16,	{0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0} },
+{ 0x05c6,	16,	{0xe5, 0x41, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x23, 0xe0, 0x70, 0x06, 0x90, 0x7e} },
+{ 0x05d6,	16,	{0x33, 0xe0, 0x70, 0x08, 0xe4, 0x90, 0x7e, 0x33, 0xf0, 0x75, 0x2e, 0xff, 0x90, 0x7e, 0x25, 0xe0} },
+{ 0x05e6,	16,	{0x60, 0x12, 0xa3, 0xe0, 0x54, 0x3f, 0xf5, 0x45, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0} },
+{ 0x05f6,	16,	{0x00, 0xe5, 0x45, 0xf0, 0x90, 0x7e, 0x27, 0xe0, 0x60, 0x2b, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x43} },
+{ 0x0606,	16,	{0x80, 0x80, 0x03, 0x53, 0x43, 0x7f, 0x53, 0x43, 0xfc, 0x90, 0x7e, 0x29, 0xe0, 0x60, 0x11, 0x43} },
+{ 0x0616,	16,	{0x43, 0x02, 0xa3, 0xe0, 0xff, 0x12, 0x11, 0xd8, 0x90, 0x7e, 0x2b, 0xe0, 0xff, 0x12, 0x11, 0xfe} },
+{ 0x0626,	16,	{0xaf, 0x43, 0x12, 0x11, 0x66, 0x90, 0x7e, 0x23, 0xe0, 0x60, 0x08, 0x53, 0x43, 0x7f, 0xaf, 0x43} },
+{ 0x0636,	16,	{0x12, 0x11, 0x66, 0x90, 0x7e, 0x2c, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x47, 0x02} },
+{ 0x0646,	16,	{0x80, 0x03, 0x53, 0x47, 0xfd, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47} },
+{ 0x0656,	16,	{0xf0, 0x90, 0x7e, 0x2e, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x47, 0x01, 0x80, 0x03} },
+{ 0x0666,	16,	{0x53, 0x47, 0xfe, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90} },
+{ 0x0676,	16,	{0x7e, 0x32, 0xe0, 0xf5, 0x3b, 0xa3, 0xe0, 0x13, 0x92, 0x0d, 0xa3, 0xe0, 0xf5, 0x3d, 0xa3, 0xe0} },
+{ 0x0686,	16,	{0x60, 0x05, 0x43, 0x47, 0x10, 0x80, 0x03, 0x53, 0x47, 0xef, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0} },
+{ 0x0696,	16,	{0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90, 0x7e, 0x36, 0xe0, 0x60, 0x32, 0x53, 0x45, 0xbf, 0x90} },
+{ 0x06a6,	16,	{0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98} },
+{ 0x06b6,	16,	{0x74, 0x09, 0xf0, 0x12, 0x13, 0xf0, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0x53, 0x3f, 0xfd} },
+{ 0x06c6,	16,	{0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0xe4, 0xf5, 0x33, 0xf5, 0x32, 0xd2, 0x07, 0x90, 0x7e} },
+{ 0x06d6,	16,	{0x37, 0xe0, 0x60, 0x0f, 0x43, 0x3f, 0x02, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0x75, 0x32} },
+{ 0x06e6,	16,	{0x01, 0xd2, 0x07, 0x90, 0x7e, 0x38, 0xe0, 0x60, 0x1b, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0xe5} },
+{ 0x06f6,	16,	{0x41, 0x44, 0x04, 0x90, 0xc0, 0x00, 0xf0, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0x90, 0x7f, 0xcd, 0xf0} },
+{ 0x0706,	16,	{0xd2, 0x03, 0xc2, 0x09, 0x90, 0x7e, 0x39, 0xe0, 0x60, 0x11, 0x43, 0x45, 0x40, 0x90, 0x7f, 0x98} },
+{ 0x0716,	16,	{0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x3a, 0xe0, 0x60} },
+{ 0x0726,	16,	{0x0f, 0x53, 0x3f, 0xfe, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0x75, 0x34, 0x01, 0xd2, 0x07} },
+{ 0x0736,	16,	{0x90, 0x7e, 0x3b, 0xe0, 0x60, 0x0f, 0x43, 0x3f, 0x01, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e} },
+{ 0x0746,	16,	{0xe4, 0xf5, 0x34, 0xd2, 0x07, 0x90, 0x7e, 0x3c, 0xe0, 0x60, 0x0e, 0x90, 0x7f, 0x98, 0x74, 0x0a} },
+{ 0x0756,	16,	{0xf0, 0xe5, 0x41, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x3d, 0xe0, 0x60, 0x02, 0xd2} },
+{ 0x0766,	16,	{0x0b, 0x90, 0x7e, 0x3e, 0xe0, 0x60, 0x08, 0x75, 0x35, 0x01, 0xe4, 0xf5, 0x39, 0xd2, 0x07, 0x90} },
+{ 0x0776,	16,	{0x7e, 0x3f, 0xe0, 0x60, 0x0f, 0x90, 0x7f, 0xd7, 0x74, 0x13, 0xf0, 0x74, 0x33, 0xf0, 0x74, 0x16} },
+{ 0x0786,	16,	{0xf0, 0x74, 0x36, 0xf0, 0xe4, 0x90, 0x7f, 0xd1, 0xf0, 0x30, 0x18, 0x52, 0xe5, 0x39, 0x60, 0x02} },
+{ 0x0796,	16,	{0x15, 0x39, 0x30, 0x12, 0x49, 0xe5, 0x13, 0xd3, 0x94, 0x00, 0x40, 0x04, 0x15, 0x13, 0x80, 0x3e} },
+{ 0x07a6,	16,	{0x75, 0x13, 0x0a, 0x30, 0x19, 0x02, 0xc2, 0x12, 0x12, 0x13, 0xf0, 0xef, 0x54, 0x01, 0xf5, 0x19} },
+{ 0x07b6,	16,	{0x65, 0x33, 0x60, 0x05, 0x85, 0x19, 0x33, 0xd2, 0x07, 0x12, 0x14, 0x43, 0xef, 0x54, 0x80, 0xf5} },
+{ 0x07c6,	16,	{0x19, 0x65, 0x2f, 0x60, 0x05, 0x85, 0x19, 0x2f, 0xd2, 0x07, 0x30, 0x0d, 0x11, 0x12, 0x14, 0x43} },
+{ 0x07d6,	16,	{0xef, 0x54, 0x10, 0xf5, 0x19, 0x65, 0x2e, 0x60, 0x05, 0x85, 0x19, 0x2e, 0xd2, 0x07, 0x30, 0x18} },
+{ 0x07e6,	16,	{0x2a, 0x90, 0x7f, 0xd2, 0xe0, 0x20, 0xe1, 0x23, 0x90, 0x7b, 0x40, 0xe0, 0x60, 0x09, 0xe0, 0xf5} },
+{ 0x07f6,	16,	{0x15, 0x90, 0x7b, 0x42, 0xe0, 0xf5, 0x16, 0x90, 0x7b, 0x41, 0xe0, 0x60, 0x09, 0x90, 0x7f, 0xd7} },
+{ 0x0806,	16,	{0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0, 0xe4, 0x90, 0x7f, 0xd3, 0xf0, 0x90, 0x7f, 0xc2, 0xe0, 0x30} },
+{ 0x0816,	16,	{0xe1, 0x03, 0x02, 0x09, 0x18, 0xe5, 0x0a, 0x70, 0x40, 0x30, 0x06, 0x39, 0xe5, 0x38, 0x70, 0x35} },
+{ 0x0826,	16,	{0xc2, 0x06, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x24, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00} },
+{ 0x0836,	16,	{0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83} },
+{ 0x0846,	16,	{0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x09, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x09, 0xf0, 0x75} },
+{ 0x0856,	16,	{0x38, 0x10, 0xe4, 0xf5, 0x2c, 0x75, 0x0a, 0x01, 0x22, 0xe5, 0x0a, 0x64, 0x01, 0x70, 0x40, 0x30} },
+{ 0x0866,	16,	{0x07, 0x39, 0xe5, 0x39, 0x70, 0x35, 0xc2, 0x07, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x2d} },
+{ 0x0876,	16,	{0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5} },
+{ 0x0886,	16,	{0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x09, 0xdb, 0x90} },
+{ 0x0896,	16,	{0x7f, 0xc3, 0x74, 0x09, 0xf0, 0x75, 0x39, 0x10, 0xe4, 0xf5, 0x35, 0x75, 0x0a, 0x02, 0x22, 0xe5} },
+{ 0x08a6,	16,	{0x0a, 0x64, 0x02, 0x70, 0x36, 0x30, 0x13, 0x2f, 0xc2, 0x13, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00} },
+{ 0x08b6,	16,	{0x74, 0x0e, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25} },
+{ 0x08c6,	16,	{0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x05} },
+{ 0x08d6,	16,	{0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x05, 0xf0, 0x75, 0x0a, 0x03, 0x22, 0xe5, 0x15, 0x60, 0x30, 0x15} },
+{ 0x08e6,	16,	{0x15, 0xe4, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x14, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00} },
+{ 0x08f6,	16,	{0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83} },
+{ 0x0906,	16,	{0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0xe4} },
+{ 0x0916,	2,	{0xf5, 0x0a} },
+{ 0x0918,	1,	{0x22} },
+{ 0x0919,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x0d, 0x2f, 0x09, 0xe9, 0x00, 0x0a, 0x5d, 0x01, 0x0a, 0xc9, 0x03} },
+{ 0x0929,	16,	{0x09, 0x3c, 0x06, 0x09, 0xdc, 0x08, 0x09, 0xd6, 0x09, 0x09, 0xbe, 0x0a, 0x09, 0xcd, 0x0b, 0x00} },
+{ 0x0939,	16,	{0x00, 0x0b, 0x18, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02} },
+{ 0x0949,	16,	{0x70, 0x69, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0b} },
+{ 0x0959,	16,	{0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0f, 0x27, 0x8b, 0x18, 0x8a, 0x19, 0x89, 0x1a, 0xea} },
+{ 0x0969,	16,	{0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5} },
+{ 0x0979,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f} },
+{ 0x0989,	16,	{0xea, 0xe0, 0xff, 0x12, 0x0f, 0x76, 0x8b, 0x18, 0x8a, 0x19, 0x89, 0x1a, 0xea, 0x49, 0x60, 0x11} },
+{ 0x0999,	16,	{0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0b} },
+{ 0x09a9,	16,	{0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x09b9,	16,	{0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0x00, 0xe5, 0x09, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01} },
+{ 0x09c9,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x09, 0x02, 0x0b, 0x1f, 0x12, 0x0b, 0x27} },
+{ 0x09d9,	16,	{0x02, 0x0b, 0x1f, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x0b, 0x1f} },
+{ 0x09e9,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} },
+{ 0x09f9,	16,	{0x0f, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x14, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} },
+{ 0x0a09,	16,	{0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0xe4, 0x90, 0x7f, 0x00} },
+{ 0x0a19,	16,	{0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x0a29,	16,	{0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} },
+{ 0x0a39,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} },
+{ 0x0a49,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x0a59,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03} },
+{ 0x0a69,	16,	{0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x0f, 0x02, 0x0b, 0x1f, 0x90} },
+{ 0x0a79,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90} },
+{ 0x0a89,	16,	{0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25} },
+{ 0x0a99,	16,	{0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x0aa9,	16,	{0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7} },
+{ 0x0ab9,	16,	{0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x56} },
+{ 0x0ac9,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4a, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x0ad9,	16,	{0xb4, 0x01, 0x04, 0xd2, 0x0f, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x36} },
+{ 0x0ae9,	16,	{0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54} },
+{ 0x0af9,	16,	{0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5} },
+{ 0x0b09,	16,	{0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x07, 0x90} },
+{ 0x0b19,	13,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0b26,	1,	{0x22} },
+{ 0x0b27,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x18, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0b37,	16,	{0x90, 0x7f, 0x95, 0x74, 0xc0, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0b47,	16,	{0x18, 0xf0, 0xe4, 0xf5, 0x8e, 0x90, 0x7f, 0xdf, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0xe4} },
+{ 0x0b57,	16,	{0xf5, 0x24, 0x75, 0x1b, 0x01, 0x7b, 0x00, 0x74, 0x24, 0x25, 0x1b, 0xf9, 0xe4, 0x34, 0x00, 0xfa} },
+{ 0x0b67,	16,	{0xe4, 0x12, 0x0c, 0xe5, 0x05, 0x1b, 0xe5, 0x1b, 0xb4, 0x09, 0xea, 0x75, 0x3a, 0x01, 0xe4, 0xf5} },
+{ 0x0b77,	16,	{0x38, 0xf5, 0x13, 0xf5, 0x36, 0xc2, 0x06, 0xc2, 0x0a, 0xc2, 0x04, 0xc2, 0x08, 0xc2, 0x12, 0xd2} },
+{ 0x0b87,	16,	{0x02, 0xd2, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x75, 0x44, 0x03, 0x90, 0xc0, 0x00, 0x74} },
+{ 0x0b97,	16,	{0x03, 0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0x7f, 0x10, 0x8f, 0x42, 0x12, 0x10, 0xf4} },
+{ 0x0ba7,	16,	{0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0x7f, 0x01, 0x8f, 0x40, 0xef, 0x44, 0x06, 0x90, 0xc0, 0x00} },
+{ 0x0bb7,	16,	{0xf0, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x75, 0x46, 0x80, 0x90, 0xc0, 0x00, 0x74, 0x80, 0xf0} },
+{ 0x0bc7,	16,	{0x0f, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0xe4, 0xff, 0x7e, 0xa3, 0xad, 0x06, 0x8d, 0x3e, 0x12, 0x12} },
+{ 0x0bd7,	16,	{0x24, 0x90, 0x7f, 0x98, 0x74, 0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f} },
+{ 0x0be7,	16,	{0x12, 0x12, 0x24, 0x7f, 0x01, 0x12, 0x13, 0x27, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x24, 0x20} },
+{ 0x0bf7,	16,	{0x19, 0x03, 0x02, 0x0c, 0x9c, 0x75, 0x2d, 0x01, 0x75, 0x1b, 0x01, 0x7b, 0x00, 0x74, 0x2d, 0x25} },
+{ 0x0c07,	16,	{0x1b, 0xf9, 0xe4, 0x34, 0x00, 0xfa, 0xe4, 0x12, 0x0c, 0xe5, 0x05, 0x1b, 0xe5, 0x1b, 0xb4, 0x09} },
+{ 0x0c17,	16,	{0xea, 0x75, 0x3b, 0x01, 0xe4, 0xf5, 0x39, 0xf5, 0x13, 0xf5, 0x37, 0xc2, 0x07, 0xc2, 0x0b, 0xc2} },
+{ 0x0c27,	16,	{0x05, 0xc2, 0x09, 0xc2, 0x12, 0xd2, 0x03, 0xd2, 0x01, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x75} },
+{ 0x0c37,	16,	{0x45, 0x03, 0x90, 0xc0, 0x00, 0x74, 0x03, 0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0x7f} },
+{ 0x0c47,	16,	{0x10, 0x8f, 0x43, 0x12, 0x11, 0x66, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0x7f, 0x01, 0x8f, 0x41} },
+{ 0x0c57,	16,	{0xef, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x75, 0x47, 0x80} },
+{ 0x0c67,	16,	{0x90, 0xc0, 0x00, 0x74, 0x80, 0xf0, 0x0f, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0xe4, 0xff, 0x7e, 0xa3} },
+{ 0x0c77,	16,	{0xad, 0x06, 0x8d, 0x3f, 0x12, 0x12, 0x6e, 0x90, 0x7f, 0x98, 0x74, 0x09, 0xf0, 0x90, 0xc0, 0x00} },
+{ 0x0c87,	16,	{0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f, 0x12, 0x12, 0x6e, 0x7f, 0x01, 0x12, 0x13, 0x48, 0x7f, 0x03} },
+{ 0x0c97,	7,	{0x7d, 0x07, 0x12, 0x12, 0x6e, 0xd2, 0x11} },
+{ 0x0c9e,	1,	{0x22} },
+{ 0x0c9f,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x0caf,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0cb8,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x0cc8,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x0cd8,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0ce5,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} },
+{ 0x0cf5,	2,	{0xf3, 0x22} },
+{ 0x0cf7,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x0d07,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x0d17,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x0d27,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x0d2f,	16,	{0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} },
+{ 0x0d3f,	16,	{0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} },
+{ 0x0d4f,	6,	{0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} },
+{ 0x0d55,	16,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83} },
+{ 0x0d65,	16,	{0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86} },
+{ 0x0d75,	16,	{0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5} },
+{ 0x0d85,	16,	{0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0} },
+{ 0x0d95,	16,	{0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05, 0x86, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x08, 0xf0} },
+{ 0x0da5,	16,	{0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0} },
+{ 0x0db5,	16,	{0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86, 0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90} },
+{ 0x0dc5,	16,	{0x7f, 0x98, 0x74, 0x08, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2} },
+{ 0x0dd5,	16,	{0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05} },
+{ 0x0de5,	4,	{0x86, 0xd2, 0xaf, 0x22} },
+{ 0x0de9,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x0df9,	1,	{0x22} },
+{ 0x0dfa,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x0e0a,	16,	{0xd2, 0x17, 0x90, 0x7f, 0x92, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0xff, 0xd3, 0x92} },
+{ 0x0e1a,	16,	{0x0f, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde, 0x74} },
+{ 0x0e2a,	16,	{0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0} },
+{ 0x0e3a,	16,	{0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f} },
+{ 0x0e4a,	16,	{0xae, 0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0x18, 0x12, 0x13, 0x02, 0xc2, 0x10, 0xe4, 0xf5} },
+{ 0x0e5a,	16,	{0x0b, 0xf5, 0x13, 0xc2, 0x15, 0xc2, 0x11, 0x90, 0x7f, 0xa1, 0x04, 0xf0, 0x90, 0x7f, 0xd8, 0xe0} },
+{ 0x0e6a,	16,	{0x65, 0x17, 0x60, 0x10, 0x30, 0x11, 0x05, 0xd2, 0x18, 0x12, 0x00, 0x46, 0x90, 0x7f, 0xd8, 0xe0} },
+{ 0x0e7a,	16,	{0xf5, 0x17, 0x80, 0x08, 0x30, 0x11, 0x05, 0xc2, 0x18, 0x12, 0x00, 0x46, 0x30, 0x10, 0x07, 0xc2} },
+{ 0x0e8a,	16,	{0x10, 0x12, 0x09, 0x19, 0x80, 0xd6, 0x30, 0x16, 0xd3, 0xc2, 0x16, 0x12, 0x14, 0x4f, 0x80, 0xcc} },
+{ 0x0e9a,	1,	{0x22} },
+{ 0x0e9b,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x47, 0x02, 0x0e, 0xe2} },
+{ 0x0ea7,	16,	{0x02, 0x0e, 0x0a, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x0eb7,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x0ec7,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x0ed7,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x13, 0x69, 0xe4, 0x7e} },
+{ 0x0ee7,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x0ef7,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x0f07,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x0f17,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x0f27,	16,	{0xe4, 0xfe, 0x75, 0x1d, 0xff, 0x75, 0x1e, 0x19, 0x75, 0x1f, 0x12, 0xab, 0x1d, 0xaa, 0x1e, 0xa9} },
+{ 0x0f37,	16,	{0x1f, 0x90, 0x00, 0x01, 0x12, 0x0c, 0xb8, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x0f47,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x0c, 0xf7, 0x85, 0xf0, 0x1b, 0xf5, 0x1c, 0x62, 0x1b} },
+{ 0x0f57,	16,	{0xe5, 0x1b, 0x62, 0x1c, 0xe5, 0x1c, 0x62, 0x1b, 0x29, 0xfd, 0xe5, 0x1b, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x0f67,	14,	{0x1d, 0xff, 0xf5, 0x1e, 0x89, 0x1f, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0f75,	1,	{0x22} },
+{ 0x0f76,	2,	{0x8f, 0x1b} },
+{ 0x0f78,	16,	{0xe4, 0xf5, 0x1c, 0x75, 0x1d, 0xff, 0x75, 0x1e, 0x19, 0x75, 0x1f, 0x86, 0xab, 0x1d, 0xaa, 0x1e} },
+{ 0x0f88,	16,	{0xa9, 0x1f, 0x90, 0x00, 0x01, 0x12, 0x0c, 0xb8, 0xb4, 0x03, 0x1d, 0xaf, 0x1c, 0x05, 0x1c, 0xef} },
+{ 0x0f98,	16,	{0xb5, 0x1b, 0x01, 0x22, 0x12, 0x0c, 0x9f, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x0fa8,	14,	{0x1d, 0xff, 0xf5, 0x1e, 0x89, 0x1f, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0fb6,	1,	{0x22} },
+{ 0x0fb7,	16,	{0x8f, 0x1a, 0x05, 0x0d, 0xe5, 0x0d, 0xae, 0x0c, 0x70, 0x02, 0x05, 0x0c, 0x14, 0xf5, 0x82, 0x8e} },
+{ 0x0fc7,	16,	{0x83, 0xe5, 0x1a, 0xf0, 0x12, 0x00, 0x36, 0x05, 0x0d, 0xe5, 0x0d, 0xac, 0x0c, 0x70, 0x02, 0x05} },
+{ 0x0fd7,	16,	{0x0c, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x08, 0xe5, 0x08, 0x60, 0x0a, 0x12, 0x13} },
+{ 0x0fe7,	9,	{0xcc, 0x8f, 0x1a, 0xef, 0x42, 0x36, 0x80, 0xca, 0x22} },
+{ 0x0ff0,	16,	{0x8f, 0x1a, 0x05, 0x0d, 0xe5, 0x0d, 0xae, 0x0c, 0x70, 0x02, 0x05, 0x0c, 0x14, 0xf5, 0x82, 0x8e} },
+{ 0x1000,	16,	{0x83, 0xe5, 0x1a, 0xf0, 0x12, 0x13, 0xe4, 0x05, 0x0d, 0xe5, 0x0d, 0xac, 0x0c, 0x70, 0x02, 0x05} },
+{ 0x1010,	16,	{0x0c, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x08, 0xe5, 0x08, 0x60, 0x0a, 0x12, 0x14} },
+{ 0x1020,	9,	{0x37, 0x8f, 0x1a, 0xef, 0x42, 0x37, 0x80, 0xca, 0x22} },
+{ 0x1029,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} },
+{ 0x1039,	16,	{0x1a, 0x04, 0xc2, 0x1a, 0x80, 0x02, 0xd2, 0x16, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x1049,	14,	{0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1057,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} },
+{ 0x1067,	16,	{0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} },
+{ 0x1077,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1081,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} },
+{ 0x1091,	16,	{0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} },
+{ 0x10a1,	7,	{0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x10a8,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x10b8,	16,	{0x10, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x10c8,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x10ce,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x10de,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x10ee,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x10f4,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1104,	16,	{0x12, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x1114,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x111a,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x112a,	16,	{0x14, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x113a,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1140,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1150,	16,	{0x16, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x1160,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1166,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1176,	16,	{0x0a, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x1186,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x118c,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x119c,	16,	{0x08, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11ac,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11b2,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x11c2,	16,	{0x09, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11d2,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11d8,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x11e8,	16,	{0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11f8,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11fe,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x120e,	16,	{0x0e, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x121e,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1224,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x1234,	16,	{0x98, 0x74, 0x17, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90} },
+{ 0x1244,	5,	{0xc0, 0x00, 0xed, 0xf0, 0x22} },
+{ 0x1249,	16,	{0x12, 0x13, 0xb4, 0x8f, 0x1a, 0x12, 0x13, 0xb4, 0x8f, 0x1b, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x12} },
+{ 0x1259,	16,	{0x12, 0x13, 0xb4, 0x8f, 0x1a, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x07, 0x12, 0x13, 0xb4, 0x8f, 0x1b} },
+{ 0x1269,	4,	{0x80, 0xe8, 0xaf, 0x1a} },
+{ 0x126d,	1,	{0x22} },
+{ 0x126e,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x127e,	16,	{0x98, 0x74, 0x0f, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0d, 0xf0, 0x90} },
+{ 0x128e,	5,	{0xc0, 0x00, 0xed, 0xf0, 0x22} },
+{ 0x1293,	16,	{0x12, 0x14, 0x1f, 0x8f, 0x1a, 0x12, 0x14, 0x1f, 0x8f, 0x1b, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x12} },
+{ 0x12a3,	16,	{0x12, 0x14, 0x1f, 0x8f, 0x1a, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x07, 0x12, 0x14, 0x1f, 0x8f, 0x1b} },
+{ 0x12b3,	4,	{0x80, 0xe8, 0xaf, 0x1a} },
+{ 0x12b7,	1,	{0x22} },
+{ 0x12b8,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x12c8,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x12d8,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x12dd,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x12ed,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x12fd,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1302,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x18, 0x04, 0xe0, 0x44} },
+{ 0x1312,	16,	{0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x13, 0x85, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x1322,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x1327,	16,	{0xae, 0x07, 0xe4, 0xff, 0xe5, 0x3e, 0x54, 0x7f, 0xfd, 0x12, 0x12, 0x24, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1337,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x3e, 0x44, 0x80, 0xfd, 0x12, 0x12, 0x24} },
+{ 0x1347,	1,	{0x22} },
+{ 0x1348,	16,	{0xae, 0x07, 0xe4, 0xff, 0xe5, 0x3f, 0x54, 0x7f, 0xfd, 0x12, 0x12, 0x6e, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1358,	16,	{0x09, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x3f, 0x44, 0x80, 0xfd, 0x12, 0x12, 0x6e} },
+{ 0x1368,	1,	{0x22} },
+{ 0x1369,	16,	{0x05, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x14, 0x03, 0x00, 0x00, 0xc1, 0x10, 0xc1, 0x16} },
+{ 0x1379,	7,	{0xc1, 0x0f, 0xc1, 0x94, 0x01, 0x0a, 0x00} },
+{ 0x1380,	2,	{0xc1, 0x19} },
+{ 0x1382,	2,	{0xc1, 0x9a} },
+{ 0x1384,	1,	{0x00} },
+{ 0x1385,	16,	{0x8e, 0x18, 0x8f, 0x19, 0xe5, 0x19, 0x15, 0x19, 0xae, 0x18, 0x70, 0x02, 0x15, 0x18, 0x4e, 0x60} },
+{ 0x1395,	7,	{0x05, 0x12, 0x0d, 0xe9, 0x80, 0xee, 0x22} },
+{ 0x139c,	12,	{0x90, 0x7f, 0x98, 0x74, 0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13a8,	12,	{0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13b4,	12,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13c0,	12,	{0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13cc,	12,	{0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13d8,	12,	{0x90, 0x7f, 0x98, 0x74, 0x16, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13e4,	12,	{0x90, 0x7f, 0x98, 0x74, 0x08, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13f0,	12,	{0x90, 0x7f, 0x98, 0x74, 0x09, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13fc,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x1400,	16,	{0x02, 0x10, 0x81, 0x00, 0x02, 0x12, 0xb8, 0x00, 0x02, 0x10, 0x57, 0x00, 0x02, 0x10, 0x29, 0x00} },
+{ 0x1410,	3,	{0x02, 0x12, 0xdd} },
+{ 0x1413,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x141f,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x142b,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x1437,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0d, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x1443,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0e, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x144f,	10,	{0x12, 0x00, 0x03, 0x12, 0x0d, 0xfa, 0x12, 0x0b, 0x27, 0x22} },
+{ 0x1900,	16,	{0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x12, 0x01, 0x00, 0x00, 0x01, 0x02} },
+{ 0x1910,	16,	{0x00, 0x01, 0x09, 0x02, 0x74, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e} },
+{ 0x1920,	16,	{0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1930,	16,	{0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00} },
+{ 0x1940,	16,	{0x07, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x1950,	16,	{0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x82, 0x02} },
+{ 0x1960,	16,	{0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00} },
+{ 0x1970,	16,	{0x00, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x07} },
+{ 0x1980,	16,	{0x05, 0x87, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00} },
+{ 0x1990,	16,	{0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00} },
+{ 0x19a0,	16,	{0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00} },
+{ 0x19b0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00} },
+{ 0x19c0,	16,	{0x6f, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} },
+{ 0x19d0,	16,	{0x2e, 0x00, 0x36, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00} },
+{ 0x19e0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00} },
+{ 0x19f0,	16,	{0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x61, 0x00} },
+{ 0x1a00,	10,	{0x70, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa19_fw.h linux.18p17/drivers/usb/serial/keyspan_usa19_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa19_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa19_fw.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,299 @@
+/* keyspan_usa19_fw.h
+  
+   Generated from Keyspan firmware image Wed Jul  5 09:18:29 2000 EST
+   This firmware is for the Keyspan USA-19 Serial Adaptor
+
+   "The firmware contained herein as keyspan_usa19_fw.h is
+   Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated
+   ("Keyspan"), as an unpublished work.  This notice does not imply
+   unrestricted or public access to this firmware which is a trade secret of
+   Keyspan, and which may not be reproduced, used, sold or transferred to any
+   third party without Keyspan's prior written consent.  All Rights Reserved.
+
+   This firmware may not be modified and may only be used with the Keyspan 
+   USA-19 Serial Adapter.  Distribution and/or Modification of the
+   keyspan.c driver which includes this firmware, in whole or in part,
+   requires the inclusion of this statement."
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa19_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x0b, 0x82} },
+{ 0x0003,	16,	{0x8e, 0x13, 0x8f, 0x14, 0xe5, 0x14, 0x15, 0x14, 0xae, 0x13, 0x70, 0x02, 0x15, 0x13, 0x4e, 0x60} },
+{ 0x0013,	7,	{0x05, 0x12, 0x0f, 0x38, 0x80, 0xee, 0x22} },
+{ 0x001a,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x0023,	3,	{0x02, 0x00, 0x46} },
+{ 0x0026,	10,	{0x12, 0x0d, 0xac, 0x12, 0x0f, 0x49, 0x12, 0x0d, 0x08, 0x22} },
+{ 0x0033,	3,	{0x02, 0x00, 0x1a} },
+{ 0x0043,	3,	{0x02, 0x0f, 0x00} },
+{ 0x0046,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0, 0xd0, 0x75, 0xd0, 0x08} },
+{ 0x0056,	16,	{0x30, 0x99, 0x0e, 0x30, 0x06, 0x07, 0xa2, 0x09, 0x92, 0x9b, 0x85, 0x35, 0x99, 0xc2, 0x99, 0xd2} },
+{ 0x0066,	16,	{0x0d, 0x20, 0x0d, 0x03, 0x02, 0x04, 0x25, 0xc2, 0x0d, 0x20, 0x01, 0x03, 0x02, 0x02, 0x50, 0x20} },
+{ 0x0076,	16,	{0x06, 0x03, 0x02, 0x01, 0x27, 0xe5, 0x37, 0xc3, 0x95, 0x50, 0x50, 0x3e, 0x20, 0x07, 0x36, 0x20} },
+{ 0x0086,	16,	{0x04, 0x33, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe3, 0x03, 0x20, 0x0c, 0x29, 0x30, 0x0b, 0x12, 0xaf} },
+{ 0x0096,	16,	{0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92} },
+{ 0x00a6,	16,	{0x09, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0} },
+{ 0x00b6,	16,	{0xf5, 0x35, 0x02, 0x04, 0x23, 0xc2, 0x06, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xc2} },
+{ 0x00c6,	16,	{0x01, 0x30, 0x08, 0x0c, 0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0xc2, 0x06, 0x02, 0x04, 0x23} },
+{ 0x00d6,	16,	{0x90, 0x7f, 0xc8, 0xe0, 0x30, 0xe1, 0x05, 0xc2, 0x06, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc9, 0xe0} },
+{ 0x00e6,	16,	{0xf5, 0x50, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92, 0x08, 0x20, 0x07, 0x2d, 0x20, 0x04, 0x2a, 0x90} },
+{ 0x00f6,	16,	{0x7f, 0x9b, 0xe0, 0x30, 0xe3, 0x03, 0x20, 0x0c, 0x20, 0x30, 0x0b, 0x11, 0x90, 0x7d, 0xc1, 0xe0} },
+{ 0x0106,	16,	{0x13, 0x92, 0x09, 0xa3, 0xe0, 0xf5, 0x35, 0x75, 0x37, 0x03, 0x02, 0x04, 0x23, 0x75, 0x37, 0x02} },
+{ 0x0116,	16,	{0x90, 0x7d, 0xc1, 0xe0, 0xf5, 0x35, 0x02, 0x04, 0x23, 0x75, 0x37, 0x01, 0xc2, 0x06, 0x02, 0x04} },
+{ 0x0126,	16,	{0x23, 0xe5, 0x37, 0xc3, 0x95, 0x50, 0x50, 0x03, 0x02, 0x01, 0xc9, 0x90, 0x7f, 0xc6, 0xe0, 0x30} },
+{ 0x0136,	16,	{0xe1, 0x07, 0xc2, 0x0f, 0xd2, 0x0e, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x50, 0x90} },
+{ 0x0146,	16,	{0x7e, 0x40, 0xe0, 0x13, 0x92, 0x08, 0x20, 0x07, 0x72, 0x20, 0x04, 0x6f, 0x90, 0x7f, 0x9b, 0xe0} },
+{ 0x0156,	16,	{0x30, 0xe3, 0x03, 0x20, 0x0c, 0x65, 0x30, 0x0b, 0x10, 0x90, 0x7e, 0x41, 0xe0, 0x13, 0x92, 0x9b} },
+{ 0x0166,	16,	{0xa3, 0xe0, 0xf5, 0x99, 0x75, 0x37, 0x03, 0x80, 0x09, 0x90, 0x7e, 0x41, 0xe0, 0xf5, 0x99, 0x75} },
+{ 0x0176,	16,	{0x37, 0x02, 0xe5, 0x37, 0xc3, 0x95, 0x50, 0x40, 0x17, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xc2, 0x01} },
+{ 0x0186,	16,	{0x20, 0x08, 0x03, 0x02, 0x04, 0x23, 0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0x02, 0x04, 0x23} },
+{ 0x0196,	16,	{0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5} },
+{ 0x01a6,	16,	{0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x01b6,	16,	{0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x35, 0xd2, 0x06, 0x02, 0x04, 0x23, 0x75, 0x37, 0x01, 0xc2, 0x0f} },
+{ 0x01c6,	16,	{0x02, 0x04, 0x23, 0x30, 0x07, 0x03, 0x02, 0x02, 0x4b, 0x20, 0x04, 0x79, 0x90, 0x7f, 0x9b, 0xe0} },
+{ 0x01d6,	16,	{0x30, 0xe3, 0x03, 0x20, 0x0c, 0x6f, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f} },
+{ 0x01e6,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x9b, 0xaf, 0x37, 0x05, 0x37, 0x74} },
+{ 0x01f6,	16,	{0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x99, 0xe5, 0x37, 0xc3, 0x95} },
+{ 0x0206,	16,	{0x50, 0x40, 0x17, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xc2, 0x01, 0x20, 0x08, 0x03, 0x02, 0x04, 0x23} },
+{ 0x0216,	16,	{0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0x02, 0x04, 0x23, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05} },
+{ 0x0226,	16,	{0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf} },
+{ 0x0236,	16,	{0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x35} },
+{ 0x0246,	16,	{0xd2, 0x06, 0x02, 0x04, 0x23, 0xc2, 0x0f, 0x02, 0x04, 0x23, 0x20, 0x06, 0x03, 0x02, 0x03, 0x02} },
+{ 0x0256,	16,	{0xe5, 0x37, 0xc3, 0x95, 0x50, 0x50, 0x3e, 0x20, 0x07, 0x36, 0x20, 0x04, 0x33, 0x90, 0x7f, 0x9b} },
+{ 0x0266,	16,	{0xe0, 0x30, 0xe3, 0x03, 0x20, 0x0c, 0x29, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0} },
+{ 0x0276,	16,	{0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf, 0x37, 0x05, 0x37} },
+{ 0x0286,	16,	{0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x35, 0x02, 0x04, 0x23} },
+{ 0x0296,	16,	{0xc2, 0x06, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0xd2, 0x01, 0x30, 0x08, 0x0c, 0xc2} },
+{ 0x02a6,	16,	{0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0xc2, 0x06, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc6, 0xe0, 0x30} },
+{ 0x02b6,	16,	{0xe1, 0x05, 0xc2, 0x06, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x50, 0x90, 0x7e, 0x40} },
+{ 0x02c6,	16,	{0xe0, 0x13, 0x92, 0x08, 0x20, 0x07, 0x2d, 0x20, 0x04, 0x2a, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe3} },
+{ 0x02d6,	16,	{0x03, 0x20, 0x0c, 0x20, 0x30, 0x0b, 0x11, 0x90, 0x7e, 0x41, 0xe0, 0x13, 0x92, 0x09, 0xa3, 0xe0} },
+{ 0x02e6,	16,	{0xf5, 0x35, 0x75, 0x37, 0x03, 0x02, 0x04, 0x23, 0x75, 0x37, 0x02, 0x90, 0x7e, 0x41, 0xe0, 0xf5} },
+{ 0x02f6,	16,	{0x35, 0x02, 0x04, 0x23, 0x75, 0x37, 0x01, 0xc2, 0x06, 0x02, 0x04, 0x23, 0xe5, 0x37, 0xc3, 0x95} },
+{ 0x0306,	16,	{0x50, 0x50, 0x03, 0x02, 0x03, 0xa4, 0x90, 0x7f, 0xc8, 0xe0, 0x30, 0xe1, 0x07, 0xc2, 0x0f, 0xd2} },
+{ 0x0316,	16,	{0x0e, 0x02, 0x04, 0x23, 0x90, 0x7f, 0xc9, 0xe0, 0xf5, 0x50, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92} },
+{ 0x0326,	16,	{0x08, 0x20, 0x07, 0x72, 0x20, 0x04, 0x6f, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe3, 0x03, 0x20, 0x0c} },
+{ 0x0336,	16,	{0x65, 0x30, 0x0b, 0x10, 0x90, 0x7d, 0xc1, 0xe0, 0x13, 0x92, 0x9b, 0xa3, 0xe0, 0xf5, 0x99, 0x75} },
+{ 0x0346,	16,	{0x37, 0x03, 0x80, 0x09, 0x90, 0x7d, 0xc1, 0xe0, 0xf5, 0x99, 0x75, 0x37, 0x02, 0xe5, 0x37, 0xc3} },
+{ 0x0356,	16,	{0x95, 0x50, 0x40, 0x17, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0xd2, 0x01, 0x20, 0x08, 0x03, 0x02, 0x04} },
+{ 0x0366,	16,	{0x23, 0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0x02, 0x04, 0x23, 0x30, 0x0b, 0x12, 0xaf, 0x37} },
+{ 0x0376,	16,	{0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x09} },
+{ 0x0386,	16,	{0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5} },
+{ 0x0396,	16,	{0x35, 0xd2, 0x06, 0x02, 0x04, 0x23, 0x75, 0x37, 0x01, 0xc2, 0x0f, 0x02, 0x04, 0x23, 0x30, 0x07} },
+{ 0x03a6,	16,	{0x03, 0x02, 0x04, 0x21, 0x20, 0x04, 0x74, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe3, 0x03, 0x20, 0x0c} },
+{ 0x03b6,	16,	{0x6a, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d} },
+{ 0x03c6,	16,	{0xf5, 0x83, 0xe0, 0x13, 0x92, 0x9b, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x03d6,	16,	{0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x99, 0xe5, 0x37, 0xc3, 0x95, 0x50, 0x40, 0x13, 0x90, 0x7f} },
+{ 0x03e6,	16,	{0xc9, 0xe4, 0xf0, 0xd2, 0x01, 0x30, 0x08, 0x35, 0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x04, 0xf0, 0x80} },
+{ 0x03f6,	16,	{0x2c, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d} },
+{ 0x0406,	16,	{0xf5, 0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x0416,	16,	{0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x35, 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x0f, 0xd2, 0x11, 0x20} },
+{ 0x0426,	5,	{0x98, 0x03, 0x02, 0x05, 0x61} },
+{ 0x042b,	16,	{0xc2, 0x98, 0x20, 0x00, 0x03, 0x02, 0x04, 0xce, 0x20, 0x10, 0x27, 0xaf, 0x36, 0x05, 0x36, 0x74} },
+{ 0x043b,	16,	{0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x99, 0xf0, 0x30, 0x0b, 0x4d, 0xaf} },
+{ 0x044b,	16,	{0x36, 0x05, 0x36, 0x74, 0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0} },
+{ 0x045b,	16,	{0x80, 0x3a, 0x85, 0x99, 0x10, 0xe5, 0x10, 0xb5, 0x44, 0x04, 0xd2, 0x04, 0x80, 0x2e, 0xe5, 0x10} },
+{ 0x046b,	16,	{0xb5, 0x43, 0x04, 0xc2, 0x04, 0x80, 0x25, 0xaf, 0x36, 0x05, 0x36, 0x74, 0x80, 0x2f, 0xf5, 0x82} },
+{ 0x047b,	16,	{0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x10, 0xf0, 0x30, 0x0b, 0x11, 0xaf, 0x36, 0x05, 0x36, 0x74} },
+{ 0x048b,	16,	{0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0xd2, 0x0a, 0xe5, 0x36} },
+{ 0x049b,	16,	{0xc3, 0x95, 0x40, 0x50, 0x03, 0x02, 0x05, 0x5f, 0x90, 0x7f, 0xb8, 0xe0, 0x30, 0xe1, 0x16, 0xe5} },
+{ 0x04ab,	16,	{0x36, 0xc3, 0x94, 0x40, 0x50, 0x03, 0x02, 0x05, 0x5f, 0x15, 0x36, 0x15, 0x36, 0x05, 0x2b, 0x43} },
+{ 0x04bb,	16,	{0x33, 0x01, 0x02, 0x05, 0x5f, 0x90, 0x7f, 0xb7, 0xe5, 0x36, 0xf0, 0x75, 0x36, 0x00, 0xc2, 0x00} },
+{ 0x04cb,	16,	{0x02, 0x05, 0x5f, 0x20, 0x10, 0x27, 0xaf, 0x36, 0x05, 0x36, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x04db,	16,	{0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x99, 0xf0, 0x30, 0x0b, 0x4d, 0xaf, 0x36, 0x05, 0x36, 0x74, 0x00} },
+{ 0x04eb,	16,	{0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0x80, 0x3a, 0x85, 0x99, 0x10} },
+{ 0x04fb,	16,	{0xe5, 0x10, 0xb5, 0x44, 0x04, 0xd2, 0x04, 0x80, 0x2e, 0xe5, 0x10, 0xb5, 0x43, 0x04, 0xc2, 0x04} },
+{ 0x050b,	16,	{0x80, 0x25, 0xaf, 0x36, 0x05, 0x36, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83} },
+{ 0x051b,	16,	{0xe5, 0x10, 0xf0, 0x30, 0x0b, 0x11, 0xaf, 0x36, 0x05, 0x36, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x052b,	16,	{0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0xd2, 0x0a, 0xe5, 0x36, 0xc3, 0x95, 0x40, 0x40, 0x24} },
+{ 0x053b,	16,	{0x90, 0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x12, 0xe5, 0x36, 0xc3, 0x94, 0x40, 0x40, 0x16, 0x15, 0x36} },
+{ 0x054b,	16,	{0x15, 0x36, 0x05, 0x2b, 0x43, 0x33, 0x01, 0x80, 0x0b, 0x90, 0x7f, 0xb9, 0xe5, 0x36, 0xf0, 0x75} },
+{ 0x055b,	6,	{0x36, 0x00, 0xd2, 0x00, 0xd2, 0x11} },
+{ 0x0561,	16,	{0x30, 0x11, 0x05, 0xc2, 0x11, 0x02, 0x00, 0x56, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x82, 0xd0, 0x83} },
+{ 0x0571,	3,	{0xd0, 0xe0, 0x32} },
+{ 0x0574,	16,	{0x90, 0x7f, 0xca, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x06, 0xa5, 0xe4, 0xf5, 0x13, 0x74, 0x40, 0x25} },
+{ 0x0584,	16,	{0x13, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x13, 0x7c, 0x00, 0x7b, 0x00} },
+{ 0x0594,	16,	{0x24, 0x38, 0xf9, 0xec, 0x34, 0x00, 0xfa, 0xef, 0x12, 0x0c, 0x54, 0x05, 0x13, 0xe5, 0x13, 0xb4} },
+{ 0x05a4,	16,	{0x18, 0xdb, 0xe5, 0x38, 0x60, 0x0c, 0x75, 0xc9, 0x20, 0x75, 0xc8, 0x34, 0x85, 0x39, 0xca, 0x85} },
+{ 0x05b4,	16,	{0x3a, 0xcb, 0xe5, 0x3b, 0x13, 0x92, 0x0b, 0x92, 0x9f, 0xe5, 0x3c, 0x13, 0x92, 0x0c, 0xe5, 0x3d} },
+{ 0x05c4,	16,	{0x13, 0x92, 0x10, 0xe5, 0x3e, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfb, 0xf0, 0x80, 0x07} },
+{ 0x05d4,	16,	{0x90, 0x7f, 0x98, 0xe0, 0x44, 0x04, 0xf0, 0xe5, 0x3f, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54} },
+{ 0x05e4,	16,	{0x7f, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0x98, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x45, 0x60, 0x0b, 0xc2} },
+{ 0x05f4,	16,	{0x07, 0xc2, 0x04, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x46, 0x60, 0x0c, 0xd2, 0x04} },
+{ 0x0604,	16,	{0x43, 0x33, 0x01, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x47, 0x60, 0x17, 0xc2, 0xaf} },
+{ 0x0614,	16,	{0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0xc2, 0x06, 0xd2, 0x01, 0xc2, 0x08, 0xf5} },
+{ 0x0624,	16,	{0x50, 0xf5, 0x37, 0xd2, 0xaf, 0xe5, 0x48, 0x60, 0x05, 0x30, 0x10, 0x02, 0xd2, 0x04, 0xe5, 0x49} },
+{ 0x0634,	16,	{0x60, 0x15, 0x90, 0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0x9e, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0644,	16,	{0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfd, 0xf0, 0xe5, 0x4a, 0x60, 0x0a, 0xd2, 0x9c, 0xc2, 0x98, 0x75} },
+{ 0x0654,	16,	{0x2c, 0x01, 0x75, 0x31, 0x1e, 0xe5, 0x4b, 0x60, 0x07, 0xc2, 0x9c, 0xe4, 0xf5, 0x36, 0xf5, 0x2c} },
+{ 0x0664,	16,	{0xe5, 0x4c, 0x60, 0x03, 0xe4, 0xf5, 0x36, 0xe5, 0x4d, 0x60, 0x02, 0xd2, 0x02, 0xe5, 0x4e, 0x60} },
+{ 0x0674,	16,	{0x0a, 0xe5, 0x4a, 0x70, 0x02, 0xf5, 0x31, 0xe5, 0x4e, 0x42, 0x33, 0xe5, 0x4f, 0x60, 0x1b, 0x90} },
+{ 0x0684,	16,	{0x7f, 0xd7, 0x74, 0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74, 0x12, 0xf0, 0x74, 0x32, 0xf0, 0x74, 0x13} },
+{ 0x0694,	16,	{0xf0, 0x74, 0x33, 0xf0, 0x74, 0x14, 0xf0, 0x74, 0x34, 0xf0, 0xd2, 0x03, 0xe4, 0x90, 0x7f, 0xcb} },
+{ 0x06a4,	16,	{0xf0, 0xa2, 0x07, 0xe4, 0x33, 0xff, 0x65, 0x29, 0x60, 0x05, 0x8f, 0x29, 0x43, 0x33, 0x01, 0xa2} },
+{ 0x06b4,	16,	{0x04, 0xe4, 0x33, 0xff, 0x65, 0x2a, 0x60, 0x05, 0x8f, 0x2a, 0x43, 0x33, 0x01, 0x90, 0x7f, 0x9b} },
+{ 0x06c4,	16,	{0xe0, 0x54, 0x08, 0xb5, 0x25, 0x0a, 0xe0, 0x54, 0x08, 0x64, 0x08, 0xf5, 0x25, 0x43, 0x33, 0x01} },
+{ 0x06d4,	16,	{0x90, 0x7f, 0x9b, 0xe0, 0x54, 0x10, 0xb5, 0x26, 0x0a, 0xe0, 0x54, 0x10, 0x64, 0x10, 0xf5, 0x26} },
+{ 0x06e4,	16,	{0x43, 0x33, 0x01, 0x90, 0x7f, 0x9b, 0xe0, 0x54, 0x40, 0xb5, 0x27, 0x0a, 0xe0, 0x54, 0x40, 0x64} },
+{ 0x06f4,	16,	{0x40, 0xf5, 0x27, 0x43, 0x33, 0x01, 0x90, 0x7f, 0x9b, 0xe0, 0x54, 0x20, 0xb5, 0x28, 0x0a, 0xe0} },
+{ 0x0704,	16,	{0x54, 0x20, 0x64, 0x20, 0xf5, 0x28, 0x43, 0x33, 0x01, 0x30, 0x02, 0x35, 0xc2, 0xaf, 0x30, 0x00} },
+{ 0x0714,	16,	{0x18, 0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x27, 0xe5, 0x36, 0x60, 0x09, 0x90, 0x7f, 0xb7, 0xf0} },
+{ 0x0724,	16,	{0xe4, 0xf5, 0x36, 0xc2, 0x00, 0xc2, 0x02, 0x80, 0x16, 0x90, 0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x0f} },
+{ 0x0734,	16,	{0xe5, 0x36, 0x60, 0x09, 0x90, 0x7f, 0xb9, 0xf0, 0xe4, 0xf5, 0x36, 0xd2, 0x00, 0xc2, 0x02, 0xd2} },
+{ 0x0744,	16,	{0xaf, 0x30, 0x0e, 0x37, 0x30, 0x01, 0x1b, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x2d, 0x90, 0x7e} },
+{ 0x0754,	16,	{0x40, 0xe0, 0x13, 0x92, 0x08, 0x75, 0x37, 0x01, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x50, 0xc2, 0x0e} },
+{ 0x0764,	16,	{0x80, 0x19, 0x90, 0x7f, 0xc8, 0xe0, 0x20, 0xe1, 0x12, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92, 0x08} },
+{ 0x0774,	16,	{0x75, 0x37, 0x01, 0x90, 0x7f, 0xc9, 0xe0, 0xf5, 0x50, 0xc2, 0x0e, 0x30, 0x0f, 0x03, 0x02, 0x08} },
+{ 0x0784,	16,	{0x87, 0x30, 0x0e, 0x03, 0x02, 0x08, 0x87, 0x30, 0x0c, 0x0a, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe3} },
+{ 0x0794,	16,	{0x03, 0x02, 0x08, 0x87, 0x30, 0x04, 0x03, 0x02, 0x08, 0x87, 0x30, 0x07, 0x03, 0x02, 0x08, 0x87} },
+{ 0x07a4,	16,	{0x30, 0x01, 0x62, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x07b4,	16,	{0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x18, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5} },
+{ 0x07c4,	16,	{0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x14, 0xe5, 0x37, 0xc3, 0x95, 0x50, 0x50, 0x2a} },
+{ 0x07d4,	16,	{0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5} },
+{ 0x07e4,	16,	{0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf, 0x37, 0x05, 0x37, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x07f4,	16,	{0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x35, 0xd2, 0x06, 0x80, 0x6b, 0xc2, 0x06, 0xe4, 0x90, 0x7f, 0xc7} },
+{ 0x0804,	16,	{0xf0, 0xc2, 0x01, 0x80, 0x60, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5} },
+{ 0x0814,	16,	{0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x18, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0} },
+{ 0x0824,	16,	{0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x14, 0xe5, 0x37, 0xc3, 0x95, 0x50} },
+{ 0x0834,	16,	{0x50, 0x2a, 0x30, 0x0b, 0x12, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0844,	16,	{0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x09, 0xaf, 0x37, 0x05, 0x37, 0x74, 0xc0, 0x2f, 0xf5, 0x82} },
+{ 0x0854,	16,	{0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x35, 0xd2, 0x06, 0x80, 0x09, 0xc2, 0x06, 0xe4, 0x90} },
+{ 0x0864,	16,	{0x7f, 0xc9, 0xf0, 0xd2, 0x01, 0x30, 0x0b, 0x04, 0xa2, 0x18, 0x92, 0x9b, 0xd2, 0x0f, 0xc2, 0xaf} },
+{ 0x0874,	16,	{0x85, 0x14, 0x99, 0x20, 0x06, 0x0b, 0x30, 0x08, 0x08, 0xc2, 0x08, 0x90, 0x7f, 0xbb, 0x74, 0x01} },
+{ 0x0884,	16,	{0xf0, 0xd2, 0xaf, 0x90, 0x7f, 0xbc, 0xe0, 0x20, 0xe1, 0x51, 0xe5, 0x33, 0x60, 0x4d, 0xe5, 0x31} },
+{ 0x0894,	16,	{0x70, 0x49, 0xe5, 0x33, 0x30, 0xe1, 0x08, 0xe4, 0xf5, 0x2f, 0x75, 0x33, 0x01, 0x80, 0x0b, 0xa2} },
+{ 0x08a4,	16,	{0x03, 0xe4, 0x33, 0xf5, 0x2f, 0xc2, 0x03, 0xe4, 0xf5, 0x33, 0xe4, 0xf5, 0x13, 0x7e, 0x00, 0x7b} },
+{ 0x08b4,	16,	{0x00, 0x74, 0x24, 0x25, 0x13, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x0c, 0x0e, 0xff, 0x74, 0x00} },
+{ 0x08c4,	16,	{0x25, 0x13, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x13, 0xe5, 0x13, 0xb4} },
+{ 0x08d4,	11,	{0x0c, 0xdb, 0x90, 0x7f, 0xbd, 0x74, 0x0c, 0xf0, 0x75, 0x31, 0x10} },
+{ 0x08df,	1,	{0x22} },
+{ 0x08e0,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x0c, 0x66, 0x09, 0xb1, 0x00, 0x0a, 0x25, 0x01, 0x0a, 0x91, 0x03} },
+{ 0x08f0,	16,	{0x09, 0x03, 0x06, 0x09, 0xa4, 0x08, 0x09, 0x9e, 0x09, 0x09, 0x86, 0x0a, 0x09, 0x95, 0x0b, 0x00} },
+{ 0x0900,	16,	{0x00, 0x0a, 0xe0, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x4b, 0x24, 0x02} },
+{ 0x0910,	16,	{0x70, 0x6a, 0x74, 0x0f, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x64, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0a} },
+{ 0x0920,	16,	{0xe7, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x04, 0x7f, 0x02, 0x80, 0x02, 0x7f, 0x03, 0x75, 0x82, 0xb5} },
+{ 0x0930,	16,	{0x75, 0x83, 0x0f, 0xef, 0xf0, 0x75, 0x82, 0xae, 0x75, 0x83, 0x0f, 0xf0, 0x75, 0x82, 0xa7, 0x75} },
+{ 0x0940,	16,	{0x83, 0x0f, 0xf0, 0x75, 0x82, 0xa0, 0x75, 0x83, 0x0f, 0xf0, 0x74, 0x0f, 0x90, 0x7f, 0xd4, 0xf0} },
+{ 0x0950,	16,	{0x74, 0x76, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0d} },
+{ 0x0960,	16,	{0xf1, 0xea, 0x49, 0x60, 0x0d, 0xea, 0x90, 0x7f, 0xd4, 0xf0, 0xe9, 0x90, 0x7f, 0xd5, 0xf0, 0x02} },
+{ 0x0970,	16,	{0x0a, 0xe7, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x0980,	16,	{0x44, 0x01, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0x00, 0xe5, 0x18, 0xf0, 0x90, 0x7f, 0xb5, 0x74} },
+{ 0x0990,	16,	{0x01, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x18, 0x02, 0x0a, 0xe7, 0x12, 0x0d} },
+{ 0x09a0,	16,	{0x08, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x0a} },
+{ 0x09b0,	16,	{0xe7, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b} },
+{ 0x09c0,	16,	{0xa2, 0x12, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x15, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00} },
+{ 0x09d0,	16,	{0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0a, 0xe7, 0xe4, 0x90, 0x7f} },
+{ 0x09e0,	16,	{0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xec} },
+{ 0x09f0,	16,	{0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24} },
+{ 0x0a00,	16,	{0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4} },
+{ 0x0a10,	16,	{0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x0a20,	16,	{0x01, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60} },
+{ 0x0a30,	16,	{0x03, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x12, 0x02, 0x0a, 0xe7} },
+{ 0x0a40,	16,	{0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0a, 0xe7, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38} },
+{ 0x0a50,	16,	{0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f} },
+{ 0x0a60,	16,	{0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec} },
+{ 0x0a70,	16,	{0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f} },
+{ 0x0a80,	16,	{0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80} },
+{ 0x0a90,	16,	{0x56, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4a, 0x90, 0x7f, 0xea} },
+{ 0x0aa0,	16,	{0xe0, 0xb4, 0x01, 0x04, 0xd2, 0x12, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80} },
+{ 0x0ab0,	16,	{0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4} },
+{ 0x0ac0,	16,	{0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f} },
+{ 0x0ad0,	16,	{0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x07} },
+{ 0x0ae0,	14,	{0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0aee,	1,	{0x22} },
+{ 0x0aef,	16,	{0x20, 0x14, 0x03, 0x02, 0x0b, 0x81, 0xe5, 0x31, 0x60, 0x02, 0x15, 0x31, 0xe5, 0x36, 0x60, 0x4f} },
+{ 0x0aff,	16,	{0x65, 0x34, 0x70, 0x45, 0xe5, 0x32, 0xf4, 0x60, 0x02, 0x05, 0x32, 0xe5, 0x32, 0xc3, 0x95, 0x41} },
+{ 0x0b0f,	16,	{0x40, 0x3d, 0xc2, 0xaf, 0x30, 0x00, 0x18, 0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x27, 0x90, 0x7f} },
+{ 0x0b1f,	16,	{0xb7, 0xe5, 0x36, 0xf0, 0xc2, 0x00, 0xe4, 0xf5, 0x36, 0xf5, 0x32, 0xf5, 0x34, 0x80, 0x16, 0x90} },
+{ 0x0b2f,	16,	{0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x0f, 0x90, 0x7f, 0xb9, 0xe5, 0x36, 0xf0, 0xd2, 0x00, 0xe4, 0xf5} },
+{ 0x0b3f,	16,	{0x36, 0xf5, 0x32, 0xf5, 0x34, 0xd2, 0xaf, 0x80, 0x06, 0x85, 0x36, 0x34, 0xe4, 0xf5, 0x32, 0xe5} },
+{ 0x0b4f,	16,	{0x2c, 0x60, 0x2f, 0x20, 0x0a, 0x07, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe0, 0x0f, 0xe5, 0x2d, 0x60} },
+{ 0x0b5f,	16,	{0x06, 0xe4, 0xf5, 0x2d, 0x43, 0x33, 0x01, 0xe4, 0xf5, 0x30, 0x80, 0x14, 0xe5, 0x30, 0xd3, 0x95} },
+{ 0x0b6f,	16,	{0x42, 0x50, 0x0d, 0xe5, 0x30, 0xb5, 0x42, 0x06, 0x75, 0x2d, 0x01, 0x43, 0x33, 0x01, 0x05, 0x30} },
+{ 0x0b7f,	3,	{0xc2, 0x0a, 0x22} },
+{ 0x0b82,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x50, 0x02, 0x0b, 0xc9} },
+{ 0x0b8e,	16,	{0x02, 0x0c, 0x8c, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x0b9e,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x0bae,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x0bbe,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0d, 0x5c, 0xe4, 0x7e} },
+{ 0x0bce,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x0bde,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x0bee,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x0bfe,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x0c0e,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x0c1e,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0c27,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x0c37,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x0c47,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0c54,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} },
+{ 0x0c64,	2,	{0xf3, 0x22} },
+{ 0x0c66,	16,	{0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} },
+{ 0x0c76,	16,	{0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} },
+{ 0x0c86,	6,	{0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} },
+{ 0x0c8c,	16,	{0x75, 0x12, 0x01, 0x90, 0x7f, 0x92, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0xff, 0xd3} },
+{ 0x0c9c,	16,	{0x92, 0x12, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde} },
+{ 0x0cac,	16,	{0x74, 0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9} },
+{ 0x0cbc,	16,	{0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90} },
+{ 0x0ccc,	16,	{0x7f, 0xae, 0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0xbc, 0xd2, 0x18, 0x12, 0x0f, 0x13, 0xc2} },
+{ 0x0cdc,	16,	{0x13, 0x30, 0x14, 0x03, 0x12, 0x05, 0x74, 0x90, 0x7f, 0xd8, 0xe0, 0x65, 0x11, 0x60, 0x08, 0xe0} },
+{ 0x0cec,	16,	{0xf5, 0x11, 0x12, 0x0a, 0xef, 0x80, 0xea, 0x30, 0x13, 0x07, 0xc2, 0x13, 0x12, 0x08, 0xe0, 0x80} },
+{ 0x0cfc,	11,	{0xe0, 0x30, 0x16, 0xdd, 0xc2, 0x16, 0x12, 0x00, 0x26, 0x80, 0xd6} },
+{ 0x0d07,	1,	{0x22} },
+{ 0x0d08,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0x90, 0x7f, 0x96, 0x74, 0x10} },
+{ 0x0d18,	16,	{0xf0, 0x90, 0x7f, 0x94, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0x9d, 0x04, 0xf0, 0x90, 0x7f, 0x97, 0x74} },
+{ 0x0d28,	16,	{0x20, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0x03, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x84, 0xf0, 0xe4, 0x90} },
+{ 0x0d38,	16,	{0x7f, 0x98, 0xf0, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x90, 0x7f, 0xcb, 0xf0, 0x75} },
+{ 0x0d48,	16,	{0x98, 0x40, 0x43, 0xa8, 0x10, 0x90, 0x7f, 0xde, 0x74, 0x1f, 0xf0, 0x90, 0x7f, 0xdf, 0x74, 0x0f} },
+{ 0x0d58,	4,	{0xf0, 0xd2, 0x14, 0x22} },
+{ 0x0d5c,	12,	{0xc1, 0x13, 0xc1, 0x16, 0xc1, 0x12, 0xc1, 0x95, 0xc1, 0x14, 0xc1, 0x11} },
+{ 0x0d68,	16,	{0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30} },
+{ 0x0d78,	16,	{0x00, 0x01, 0x33, 0x01, 0x01, 0x32, 0x00, 0x01, 0x37, 0x00, 0x01, 0x50, 0x00, 0x01, 0x36, 0x00} },
+{ 0x0d88,	16,	{0x01, 0x34, 0x00, 0xc1, 0x03, 0xc1, 0x0a, 0xc1, 0x8e, 0xc1, 0x0d, 0xc1, 0x02, 0xc1, 0x0c, 0xc1} },
+{ 0x0d98,	16,	{0x10, 0xc1, 0x08, 0xc1, 0x0f, 0xc1, 0x06, 0xc1, 0x07, 0xc1, 0x04, 0xc1, 0x0b, 0xc1, 0x80, 0xc1} },
+{ 0x0da8,	1,	{0x81} },
+{ 0x0da9,	2,	{0xc1, 0x97} },
+{ 0x0dab,	1,	{0x00} },
+{ 0x0dac,	16,	{0xe4, 0x90, 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9d} },
+{ 0x0dbc,	16,	{0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0x97, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0x9d, 0xe0, 0x54} },
+{ 0x0dcc,	16,	{0xfd, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x20, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0, 0x90, 0x7f, 0x9d} },
+{ 0x0ddc,	16,	{0xe0, 0x44, 0xfd, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0xff, 0xf0, 0xe4} },
+{ 0x0dec,	5,	{0x90, 0x7f, 0x98, 0xf0, 0x22} },
+{ 0x0df1,	2,	{0x8f, 0x13} },
+{ 0x0df3,	16,	{0xe4, 0xf5, 0x14, 0x75, 0x15, 0xff, 0x75, 0x16, 0x0f, 0x75, 0x17, 0xb9, 0xab, 0x15, 0xaa, 0x16} },
+{ 0x0e03,	16,	{0xa9, 0x17, 0x90, 0x00, 0x01, 0x12, 0x0c, 0x27, 0xb4, 0x03, 0x1d, 0xaf, 0x14, 0x05, 0x14, 0xef} },
+{ 0x0e13,	16,	{0xb5, 0x13, 0x01, 0x22, 0x12, 0x0c, 0x0e, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x0e23,	14,	{0x15, 0xff, 0xf5, 0x16, 0x89, 0x17, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0e31,	1,	{0x22} },
+{ 0x0e32,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} },
+{ 0x0e42,	16,	{0x17, 0x04, 0xc2, 0x17, 0x80, 0x02, 0xd2, 0x16, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x0e52,	14,	{0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e60,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} },
+{ 0x0e70,	16,	{0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} },
+{ 0x0e80,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e8a,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} },
+{ 0x0e9a,	16,	{0x13, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} },
+{ 0x0eaa,	7,	{0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0eb1,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x0ec1,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x0ed1,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0ed6,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x0ee6,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x0ef6,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0f00,	16,	{0x02, 0x0e, 0x8a, 0x00, 0x02, 0x0e, 0xb1, 0x00, 0x02, 0x0e, 0x60, 0x00, 0x02, 0x0e, 0x32, 0x00} },
+{ 0x0f10,	3,	{0x02, 0x0e, 0xd6} },
+{ 0x0f13,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x18, 0x04, 0xe0, 0x44} },
+{ 0x0f23,	16,	{0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x00, 0x03, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x0f33,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x0f38,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x0f48,	1,	{0x22} },
+{ 0x0f49,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x0f64,	16,	{0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x07, 0x01, 0x01, 0x00, 0x01, 0x02} },
+{ 0x0f74,	16,	{0x00, 0x01, 0x09, 0x02, 0x43, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x07} },
+{ 0x0f84,	16,	{0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x0f94,	16,	{0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00} },
+{ 0x0fa4,	16,	{0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x0fb4,	16,	{0x84, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x10, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79} },
+{ 0x0fc4,	16,	{0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x0e, 0x03, 0x53, 0x00, 0x65, 0x00, 0x72} },
+{ 0x0fd4,	9,	{0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa19w_fw.h linux.18p17/drivers/usb/serial/keyspan_usa19w_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa19w_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa19w_fw.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,304 @@
+/* keyspan_usa19w_fw.h
+  
+   Generated from Keyspan firmware image Wed Jul  5 09:18:29 2000 EST
+   This firmware is for the Keyspan USA-19W Serial Adaptor
+
+   "The firmware contained herein as keyspan_usa19w_fw.h is
+   Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated
+   ("Keyspan"), as an unpublished work.  This notice does not imply
+   unrestricted or public access to this firmware which is a trade secret of
+   Keyspan, and which may not be reproduced, used, sold or transferred to any
+   third party without Keyspan's prior written consent.  All Rights Reserved.
+
+   This firmware may not be modified and may only be used with the Keyspan 
+   USA-19W Serial Adapter.  Distribution and/or Modification of the
+   keyspan.c driver which includes this firmware, in whole or in part,
+   requires the inclusion of this statement."
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa19w_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x09, 0x60} },
+{ 0x0003,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0013,	16,	{0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90} },
+{ 0x0023,	15,	{0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x17, 0xf0, 0xe4, 0x90, 0x7f, 0x98, 0xf0, 0x22} },
+{ 0x0033,	3,	{0x02, 0x0e, 0x43} },
+{ 0x0036,	12,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0043,	3,	{0x02, 0x0e, 0x00} },
+{ 0x0046,	16,	{0x30, 0x0d, 0x13, 0x12, 0x0e, 0x1f, 0xef, 0xc3, 0x95, 0x14, 0x50, 0x73, 0x90, 0x7f, 0xbf, 0x74} },
+{ 0x0056,	16,	{0x01, 0xf0, 0xc2, 0x0d, 0x80, 0x69, 0x30, 0x0a, 0x34, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x5f} },
+{ 0x0066,	16,	{0x12, 0x0e, 0x1f, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x56, 0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92, 0x0d} },
+{ 0x0076,	16,	{0x90, 0x7f, 0xc7, 0xe0, 0x14, 0xf5, 0x23, 0x7e, 0x7e, 0x7f, 0x41, 0x75, 0x27, 0x7e, 0x75, 0x28} },
+{ 0x0086,	16,	{0x41, 0x12, 0x08, 0xab, 0xc2, 0x0a, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x80, 0x32, 0x90, 0x7f, 0xc8} },
+{ 0x0096,	16,	{0xe0, 0x20, 0xe1, 0x2b, 0x12, 0x0e, 0x1f, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x22, 0x90, 0x7d, 0xc0} },
+{ 0x00a6,	16,	{0xe0, 0x13, 0x92, 0x0d, 0x90, 0x7f, 0xc9, 0xe0, 0x14, 0xf5, 0x23, 0x7e, 0x7d, 0x7f, 0xc1, 0x75} },
+{ 0x00b6,	16,	{0x27, 0x7d, 0x75, 0x28, 0xc1, 0x12, 0x08, 0xab, 0xd2, 0x0a, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0x90} },
+{ 0x00c6,	16,	{0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x01, 0x4d, 0x53, 0x11, 0x80, 0x12, 0x0e, 0x2b, 0xef} },
+{ 0x00d6,	16,	{0x42, 0x11, 0x12, 0x0c, 0xc8, 0x8f, 0x34, 0xef, 0xc3, 0x95, 0x13, 0x50, 0x0f, 0x12, 0x0d, 0xf1} },
+{ 0x00e6,	16,	{0xef, 0x30, 0xe0, 0x08, 0xe5, 0x11, 0x20, 0xe7, 0x03, 0x30, 0x10, 0x5b, 0xc2, 0x10, 0xe5, 0x34} },
+{ 0x00f6,	16,	{0x70, 0x04, 0xf5, 0x11, 0x80, 0x51, 0xe5, 0x11, 0x30, 0xe7, 0x26, 0xe5, 0x34, 0xd3, 0x94, 0x20} },
+{ 0x0106,	16,	{0x40, 0x03, 0x75, 0x34, 0x20, 0x85, 0x34, 0x23, 0x7e, 0x7e, 0x7f, 0x80, 0x75, 0x27, 0x7e, 0x75} },
+{ 0x0116,	16,	{0x28, 0x80, 0xaf, 0x11, 0x12, 0x0b, 0x01, 0xe5, 0x34, 0x25, 0xe0, 0x90, 0x7f, 0xb7, 0xf0, 0x80} },
+{ 0x0126,	16,	{0x26, 0xe5, 0x34, 0xd3, 0x94, 0x3f, 0x40, 0x03, 0x75, 0x34, 0x3f, 0x85, 0x34, 0x23, 0xe4, 0x90} },
+{ 0x0136,	16,	{0x7e, 0x80, 0xf0, 0x7e, 0x7e, 0x7f, 0x81, 0x75, 0x27, 0x7e, 0x75, 0x28, 0x81, 0x12, 0x08, 0xd0} },
+{ 0x0146,	16,	{0xe5, 0x34, 0x04, 0x90, 0x7f, 0xb7, 0xf0, 0x90, 0x7f, 0xce, 0xe0, 0x30, 0xe1, 0x06, 0x20, 0x0b} },
+{ 0x0156,	16,	{0x03, 0x02, 0x03, 0xb2, 0xe4, 0xf5, 0x33, 0x74, 0x40, 0x25, 0x33, 0xf5, 0x82, 0xe4, 0x34, 0x7c} },
+{ 0x0166,	16,	{0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x33, 0x7c, 0x00, 0x7b, 0x01, 0x7a, 0x7e, 0x79, 0x00, 0x24, 0x00} },
+{ 0x0176,	16,	{0xf9, 0xec, 0x34, 0x7e, 0xfa, 0xef, 0x12, 0x08, 0x3b, 0x05, 0x33, 0xe5, 0x33, 0xb4, 0x20, 0xd7} },
+{ 0x0186,	16,	{0x90, 0x7e, 0x00, 0xe0, 0x60, 0x6e, 0x7f, 0x01, 0x90, 0x7e, 0x11, 0xe0, 0xfd, 0x12, 0x0c, 0xa3} },
+{ 0x0196,	16,	{0x90, 0x7e, 0x01, 0xe0, 0xff, 0x12, 0x0b, 0xe5, 0x90, 0x7e, 0x02, 0xe0, 0xff, 0x12, 0x0c, 0x0b} },
+{ 0x01a6,	16,	{0xd2, 0x0e, 0xd2, 0x0f, 0x75, 0x34, 0x04, 0x90, 0x7e, 0x03, 0xe0, 0x60, 0x05, 0xc2, 0x0f, 0x43} },
+{ 0x01b6,	16,	{0x34, 0xc0, 0x90, 0x7e, 0x04, 0xe0, 0xb4, 0x01, 0x07, 0xc2, 0x0f, 0x43, 0x34, 0x0b, 0x80, 0x10} },
+{ 0x01c6,	16,	{0x90, 0x7e, 0x04, 0xe0, 0x60, 0x07, 0xc2, 0x0e, 0x43, 0x34, 0x09, 0x80, 0x03, 0x43, 0x34, 0x02} },
+{ 0x01d6,	16,	{0x7f, 0x03, 0xad, 0x34, 0x12, 0x0c, 0xa3, 0x43, 0x19, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0} },
+{ 0x01e6,	16,	{0x90, 0xc0, 0x00, 0xe5, 0x19, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5, 0x16, 0x44, 0x06} },
+{ 0x01f6,	16,	{0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x05, 0xe0, 0x60, 0x12, 0xa3, 0xe0, 0x54, 0x3f, 0xf5, 0x18} },
+{ 0x0206,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x18, 0xf0, 0x90, 0x7e, 0x07, 0xe0} },
+{ 0x0216,	16,	{0x60, 0x42, 0x90, 0x7e, 0x13, 0xe0, 0x60, 0x05, 0x43, 0x15, 0x04, 0x80, 0x03, 0x53, 0x15, 0xfb} },
+{ 0x0226,	16,	{0xe4, 0xff, 0xad, 0x15, 0x12, 0x0c, 0xa3, 0x90, 0x7e, 0x08, 0xe0, 0x60, 0x05, 0x43, 0x17, 0x80} },
+{ 0x0236,	16,	{0x80, 0x03, 0x53, 0x17, 0x7f, 0x53, 0x17, 0xfc, 0x90, 0x7e, 0x09, 0xe0, 0x60, 0x11, 0x43, 0x17} },
+{ 0x0246,	16,	{0x02, 0xa3, 0xe0, 0xff, 0x12, 0x0c, 0x57, 0x90, 0x7e, 0x0b, 0xe0, 0xff, 0x12, 0x0c, 0x7d, 0xaf} },
+{ 0x0256,	16,	{0x17, 0x12, 0x0c, 0x31, 0x90, 0x7e, 0x0e, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x19} },
+{ 0x0266,	16,	{0x01, 0x80, 0x03, 0x53, 0x19, 0xfe, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5} },
+{ 0x0276,	16,	{0x19, 0xf0, 0x90, 0x7e, 0x0c, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x19, 0x02, 0x80} },
+{ 0x0286,	16,	{0x03, 0x53, 0x19, 0xfd, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x19, 0xf0} },
+{ 0x0296,	16,	{0x90, 0x7e, 0x12, 0xe0, 0xf5, 0x13, 0xa3, 0xe0, 0x13, 0x92, 0x11, 0xa3, 0xe0, 0xf5, 0x14, 0xa3} },
+{ 0x02a6,	16,	{0xe0, 0x60, 0x05, 0x43, 0x19, 0x10, 0x80, 0x03, 0x53, 0x19, 0xef, 0x90, 0x7f, 0x98, 0x74, 0x14} },
+{ 0x02b6,	16,	{0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x19, 0xf0, 0x90, 0x7e, 0x16, 0xe0, 0x60, 0x32, 0x53, 0x18, 0xbf} },
+{ 0x02c6,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x02d6,	16,	{0x98, 0x74, 0x11, 0xf0, 0x12, 0x0d, 0xe5, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0x53, 0x15} },
+{ 0x02e6,	16,	{0xfd, 0xe4, 0xff, 0xad, 0x15, 0x12, 0x0c, 0xa3, 0xe4, 0xf5, 0x0e, 0xf5, 0x0d, 0xd2, 0x0c, 0x90} },
+{ 0x02f6,	16,	{0x7e, 0x17, 0xe0, 0x60, 0x0f, 0x43, 0x15, 0x02, 0xe4, 0xff, 0xad, 0x15, 0x12, 0x0c, 0xa3, 0x75} },
+{ 0x0306,	16,	{0x0d, 0x01, 0xd2, 0x0c, 0x90, 0x7e, 0x18, 0xe0, 0x60, 0x1b, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0} },
+{ 0x0316,	16,	{0xe5, 0x16, 0x44, 0x04, 0x90, 0xc0, 0x00, 0xf0, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9} },
+{ 0x0326,	16,	{0xf0, 0xd2, 0x0a, 0xc2, 0x0d, 0x90, 0x7e, 0x19, 0xe0, 0x60, 0x11, 0x43, 0x18, 0x40, 0x90, 0x7f} },
+{ 0x0336,	16,	{0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1a, 0xe0} },
+{ 0x0346,	16,	{0x60, 0x0f, 0x53, 0x15, 0xfe, 0xe4, 0xff, 0xad, 0x15, 0x12, 0x0c, 0xa3, 0x75, 0x0f, 0x01, 0xd2} },
+{ 0x0356,	16,	{0x0c, 0x90, 0x7e, 0x1b, 0xe0, 0x60, 0x0f, 0x43, 0x15, 0x01, 0xe4, 0xff, 0xad, 0x15, 0x12, 0x0c} },
+{ 0x0366,	16,	{0xa3, 0xe4, 0xf5, 0x0f, 0xd2, 0x0c, 0x90, 0x7e, 0x1c, 0xe0, 0x60, 0x0e, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0376,	16,	{0x12, 0xf0, 0xe5, 0x16, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1d, 0xe0, 0x60, 0x02} },
+{ 0x0386,	16,	{0xd2, 0x10, 0x90, 0x7e, 0x1e, 0xe0, 0x60, 0x08, 0x75, 0x10, 0x01, 0xe4, 0xf5, 0x12, 0xd2, 0x0c} },
+{ 0x0396,	16,	{0x90, 0x7e, 0x1f, 0xe0, 0x60, 0x0f, 0x90, 0x7f, 0xd7, 0x74, 0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74} },
+{ 0x03a6,	16,	{0x15, 0xf0, 0x74, 0x35, 0xf0, 0xc2, 0x0b, 0xe4, 0x90, 0x7f, 0xcf, 0xf0, 0x30, 0x13, 0x71, 0xe5} },
+{ 0x03b6,	16,	{0x12, 0x60, 0x02, 0x15, 0x12, 0xe5, 0x2e, 0xd3, 0x94, 0x00, 0x40, 0x04, 0x15, 0x2e, 0x80, 0x60} },
+{ 0x03c6,	16,	{0x75, 0x2e, 0x0a, 0x12, 0x0d, 0xe5, 0xef, 0x54, 0x01, 0xf5, 0x34, 0x65, 0x0e, 0x60, 0x07, 0x85} },
+{ 0x03d6,	16,	{0x34, 0x0e, 0xd2, 0x0c, 0x80, 0x11, 0x12, 0x0e, 0x37, 0xef, 0x54, 0x10, 0xf5, 0x34, 0x65, 0x09} },
+{ 0x03e6,	16,	{0x60, 0x05, 0x85, 0x34, 0x09, 0xd2, 0x0c, 0x12, 0x0e, 0x37, 0xef, 0x54, 0x80, 0xf5, 0x34, 0x65} },
+{ 0x03f6,	16,	{0x0a, 0x60, 0x05, 0x85, 0x34, 0x0a, 0xd2, 0x0c, 0x12, 0x0e, 0x37, 0xef, 0x54, 0x20, 0xf5, 0x34} },
+{ 0x0406,	16,	{0x65, 0x0b, 0x60, 0x08, 0x85, 0x34, 0x0b, 0x30, 0x0e, 0x02, 0xd2, 0x0c, 0x12, 0x0e, 0x37, 0xef} },
+{ 0x0416,	16,	{0x54, 0x40, 0xf5, 0x34, 0x65, 0x0c, 0x60, 0x08, 0x85, 0x34, 0x0c, 0x30, 0x0f, 0x02, 0xd2, 0x0c} },
+{ 0x0426,	16,	{0x30, 0x13, 0x2a, 0x90, 0x7f, 0xd2, 0xe0, 0x20, 0xe1, 0x23, 0x90, 0x7b, 0x40, 0xe0, 0x60, 0x09} },
+{ 0x0436,	16,	{0xe0, 0xf5, 0x30, 0x90, 0x7b, 0x42, 0xe0, 0xf5, 0x31, 0x90, 0x7b, 0x41, 0xe0, 0x60, 0x09, 0x90} },
+{ 0x0446,	16,	{0x7f, 0xd7, 0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0, 0xe4, 0x90, 0x7f, 0xd3, 0xf0, 0x90, 0x7f, 0xc2} },
+{ 0x0456,	16,	{0xe0, 0x30, 0xe1, 0x03, 0x02, 0x05, 0x17, 0xe5, 0x25, 0x70, 0x40, 0x30, 0x0c, 0x39, 0xe5, 0x12} },
+{ 0x0466,	16,	{0x70, 0x35, 0xc2, 0x0c, 0xf5, 0x33, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x08, 0x25, 0x33, 0xf9, 0xee} },
+{ 0x0476,	16,	{0x34, 0x00, 0xfa, 0x12, 0x07, 0xf5, 0xff, 0x74, 0x80, 0x25, 0x33, 0xf5, 0x82, 0xe4, 0x34, 0x7b} },
+{ 0x0486,	16,	{0xf5, 0x83, 0xef, 0xf0, 0x05, 0x33, 0xe5, 0x33, 0xb4, 0x09, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x09} },
+{ 0x0496,	16,	{0xf0, 0x75, 0x12, 0x10, 0xe4, 0xf5, 0x10, 0x75, 0x25, 0x02, 0x22, 0xe5, 0x25, 0x64, 0x02, 0x70} },
+{ 0x04a6,	16,	{0x36, 0x30, 0x05, 0x2f, 0xc2, 0x05, 0xf5, 0x33, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x29, 0x25, 0x33} },
+{ 0x04b6,	16,	{0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x07, 0xf5, 0xff, 0x74, 0x80, 0x25, 0x33, 0xf5, 0x82, 0xe4} },
+{ 0x04c6,	16,	{0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x33, 0xe5, 0x33, 0xb4, 0x05, 0xdb, 0x90, 0x7f, 0xc3} },
+{ 0x04d6,	16,	{0x74, 0x05, 0xf0, 0x75, 0x25, 0x03, 0x22, 0xe5, 0x30, 0x60, 0x33, 0x75, 0x2f, 0x03, 0x15, 0x30} },
+{ 0x04e6,	16,	{0xe4, 0xf5, 0x33, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x2f, 0x25, 0x33, 0xf9, 0xee, 0x34, 0x00, 0xfa} },
+{ 0x04f6,	16,	{0x12, 0x07, 0xf5, 0xff, 0x74, 0x80, 0x25, 0x33, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef} },
+{ 0x0506,	16,	{0xf0, 0x05, 0x33, 0xe5, 0x33, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0xe4, 0xf5} },
+{ 0x0516,	1,	{0x25} },
+{ 0x0517,	1,	{0x22} },
+{ 0x0518,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x08, 0x85, 0x05, 0xe8, 0x00, 0x06, 0x5c, 0x01, 0x06, 0xc9, 0x03} },
+{ 0x0528,	16,	{0x05, 0x3b, 0x06, 0x05, 0xdb, 0x08, 0x05, 0xd5, 0x09, 0x05, 0xbd, 0x0a, 0x05, 0xcc, 0x0b, 0x00} },
+{ 0x0538,	16,	{0x00, 0x07, 0x19, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02} },
+{ 0x0548,	16,	{0x70, 0x69, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x07} },
+{ 0x0558,	16,	{0x20, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0a, 0x71, 0x8b, 0x33, 0x8a, 0x34, 0x89, 0x35, 0xea} },
+{ 0x0568,	16,	{0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5} },
+{ 0x0578,	16,	{0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f} },
+{ 0x0588,	16,	{0xea, 0xe0, 0xff, 0x12, 0x0a, 0xc0, 0x8b, 0x33, 0x8a, 0x34, 0x89, 0x35, 0xea, 0x49, 0x60, 0x11} },
+{ 0x0598,	16,	{0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x07} },
+{ 0x05a8,	16,	{0x20, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x05b8,	16,	{0x01, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0x00, 0xe5, 0x24, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01} },
+{ 0x05c8,	16,	{0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x24, 0x02, 0x07, 0x20, 0x12, 0x07, 0x28} },
+{ 0x05d8,	16,	{0x02, 0x07, 0x20, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x07, 0x20} },
+{ 0x05e8,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} },
+{ 0x05f8,	16,	{0x01, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x06, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} },
+{ 0x0608,	16,	{0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x07, 0x20, 0xe4, 0x90, 0x7f, 0x00} },
+{ 0x0618,	16,	{0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x0628,	16,	{0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} },
+{ 0x0638,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} },
+{ 0x0648,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x0658,	16,	{0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1e, 0x24, 0x02, 0x60, 0x03} },
+{ 0x0668,	16,	{0x02, 0x07, 0x20, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x06, 0x12, 0x0e, 0x47, 0x02, 0x07, 0x20} },
+{ 0x0678,	16,	{0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x07, 0x20, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38} },
+{ 0x0688,	16,	{0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f} },
+{ 0x0698,	16,	{0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec} },
+{ 0x06a8,	16,	{0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f} },
+{ 0x06b8,	16,	{0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x60, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80} },
+{ 0x06c8,	16,	{0x57, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x19, 0x24, 0x02, 0x70, 0x4b, 0x90, 0x7f, 0xea} },
+{ 0x06d8,	16,	{0xe0, 0xb4, 0x01, 0x05, 0x12, 0x0d, 0xfd, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0} },
+{ 0x06e8,	16,	{0x80, 0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff} },
+{ 0x06f8,	16,	{0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0708,	16,	{0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80} },
+{ 0x0718,	15,	{0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0727,	1,	{0x22} },
+{ 0x0728,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0738,	16,	{0x90, 0x7f, 0x95, 0x74, 0xc0, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0748,	16,	{0x18, 0xf0, 0xe4, 0xf5, 0x8e, 0x90, 0x7f, 0xdf, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0xe4} },
+{ 0x0758,	16,	{0xf5, 0x08, 0x7f, 0x01, 0x7b, 0x00, 0x74, 0x08, 0x2f, 0xf9, 0xe4, 0x34, 0x00, 0xfa, 0xe4, 0x12} },
+{ 0x0768,	16,	{0x08, 0x3b, 0x0f, 0xbf, 0x09, 0xee, 0x75, 0x13, 0x01, 0xe4, 0xf5, 0x12, 0xf5, 0x2e, 0xf5, 0x11} },
+{ 0x0778,	16,	{0xc2, 0x0c, 0xc2, 0x10, 0xc2, 0x0b, 0xc2, 0x0d, 0xc2, 0x04, 0xd2, 0x0a, 0xd2, 0x09, 0x90, 0x7f} },
+{ 0x0788,	16,	{0x98, 0x74, 0x13, 0xf0, 0x75, 0x18, 0x03, 0x90, 0xc0, 0x00, 0x74, 0x03, 0xf0, 0x7f, 0x0c, 0xe4} },
+{ 0x0798,	16,	{0xfd, 0x12, 0x0c, 0xa3, 0x7f, 0x10, 0x8f, 0x17, 0x12, 0x0c, 0x31, 0x90, 0x7f, 0x98, 0x74, 0x12} },
+{ 0x07a8,	16,	{0xf0, 0x7f, 0x01, 0x8f, 0x16, 0xef, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x07b8,	16,	{0x14, 0xf0, 0x75, 0x19, 0x80, 0x90, 0xc0, 0x00, 0x74, 0x80, 0xf0, 0x0f, 0xe4, 0xfd, 0x12, 0x0c} },
+{ 0x07c8,	16,	{0xa3, 0xe4, 0xff, 0x7e, 0xa3, 0xad, 0x06, 0x8d, 0x15, 0x12, 0x0c, 0xa3, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x07d8,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f, 0x12, 0x0c, 0xa3, 0x7f, 0x01} },
+{ 0x07e8,	12,	{0x12, 0x0d, 0x7d, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x0c, 0xa3, 0xd2, 0x03} },
+{ 0x07f4,	1,	{0x22} },
+{ 0x07f5,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x0805,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x080e,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x081e,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x082e,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x083b,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} },
+{ 0x084b,	2,	{0xf3, 0x22} },
+{ 0x084d,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x085d,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x086d,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x087d,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x0885,	16,	{0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} },
+{ 0x0895,	16,	{0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} },
+{ 0x08a5,	6,	{0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} },
+{ 0x08ab,	16,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x23, 0xe5, 0x28, 0xf5, 0x82, 0xe5, 0x27, 0xf5, 0x83} },
+{ 0x08bb,	16,	{0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86} },
+{ 0x08cb,	16,	{0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x23, 0xe5, 0x28, 0xf5} },
+{ 0x08db,	16,	{0x82, 0xe5, 0x27, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0} },
+{ 0x08eb,	16,	{0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05, 0x86, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x08, 0xf0} },
+{ 0x08fb,	16,	{0xaf, 0x23, 0xe5, 0x28, 0xf5, 0x82, 0xe5, 0x27, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0} },
+{ 0x090b,	16,	{0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86, 0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90} },
+{ 0x091b,	16,	{0x7f, 0x98, 0x74, 0x08, 0xf0, 0xaf, 0x23, 0xe5, 0x28, 0xf5, 0x82, 0xe5, 0x27, 0xf5, 0x83, 0xc2} },
+{ 0x092b,	16,	{0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05} },
+{ 0x093b,	4,	{0x86, 0xd2, 0xaf, 0x22} },
+{ 0x093f,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x094f,	1,	{0x22} },
+{ 0x0950,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x0960,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x3a, 0x02, 0x09, 0xa7} },
+{ 0x096c,	16,	{0x02, 0x09, 0xec, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x097c,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x098c,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x099c,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0d, 0x9e, 0xe4, 0x7e} },
+{ 0x09ac,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x09bc,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x09cc,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x09dc,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x09ec,	16,	{0xd2, 0x12, 0x90, 0x7f, 0x92, 0xe0, 0x44, 0x02, 0xf0, 0x12, 0x0e, 0x47, 0xd2, 0xe8, 0x43, 0xd8} },
+{ 0x09fc,	16,	{0x20, 0x90, 0x7f, 0xde, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff} },
+{ 0x0a0c,	16,	{0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0} },
+{ 0x0a1c,	16,	{0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0x74, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0x13, 0x12, 0x0d, 0x37} },
+{ 0x0a2c,	16,	{0xc2, 0x02, 0xe4, 0xf5, 0x26, 0xf5, 0x2e, 0xc2, 0x07, 0xc2, 0x03, 0x90, 0x7f, 0xa1, 0x04, 0xf0} },
+{ 0x0a3c,	16,	{0x90, 0x7f, 0xd8, 0xe0, 0x65, 0x32, 0x60, 0x10, 0x30, 0x03, 0x05, 0xd2, 0x13, 0x12, 0x00, 0x46} },
+{ 0x0a4c,	16,	{0x90, 0x7f, 0xd8, 0xe0, 0xf5, 0x32, 0x80, 0x08, 0x30, 0x03, 0x05, 0xc2, 0x13, 0x12, 0x00, 0x46} },
+{ 0x0a5c,	16,	{0x30, 0x02, 0x07, 0xc2, 0x02, 0x12, 0x05, 0x18, 0x80, 0xd6, 0x30, 0x08, 0xd3, 0xc2, 0x08, 0x12} },
+{ 0x0a6c,	4,	{0x0b, 0x68, 0x80, 0xcc} },
+{ 0x0a70,	1,	{0x22} },
+{ 0x0a71,	16,	{0xe4, 0xfe, 0x75, 0x38, 0xff, 0x75, 0x39, 0x19, 0x75, 0x3a, 0x12, 0xab, 0x38, 0xaa, 0x39, 0xa9} },
+{ 0x0a81,	16,	{0x3a, 0x90, 0x00, 0x01, 0x12, 0x08, 0x0e, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x0a91,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x08, 0x4d, 0x85, 0xf0, 0x36, 0xf5, 0x37, 0x62, 0x36} },
+{ 0x0aa1,	16,	{0xe5, 0x36, 0x62, 0x37, 0xe5, 0x37, 0x62, 0x36, 0x29, 0xfd, 0xe5, 0x36, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x0ab1,	14,	{0x38, 0xff, 0xf5, 0x39, 0x89, 0x3a, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0abf,	1,	{0x22} },
+{ 0x0ac0,	2,	{0x8f, 0x36} },
+{ 0x0ac2,	16,	{0xe4, 0xf5, 0x37, 0x75, 0x38, 0xff, 0x75, 0x39, 0x19, 0x75, 0x3a, 0x86, 0xab, 0x38, 0xaa, 0x39} },
+{ 0x0ad2,	16,	{0xa9, 0x3a, 0x90, 0x00, 0x01, 0x12, 0x08, 0x0e, 0xb4, 0x03, 0x1d, 0xaf, 0x37, 0x05, 0x37, 0xef} },
+{ 0x0ae2,	16,	{0xb5, 0x36, 0x01, 0x22, 0x12, 0x07, 0xf5, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x0af2,	14,	{0x38, 0xff, 0xf5, 0x39, 0x89, 0x3a, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0b00,	1,	{0x22} },
+{ 0x0b01,	16,	{0x8f, 0x35, 0x05, 0x28, 0xe5, 0x28, 0xae, 0x27, 0x70, 0x02, 0x05, 0x27, 0x14, 0xf5, 0x82, 0x8e} },
+{ 0x0b11,	16,	{0x83, 0xe5, 0x35, 0xf0, 0x12, 0x00, 0x36, 0x05, 0x28, 0xe5, 0x28, 0xac, 0x27, 0x70, 0x02, 0x05} },
+{ 0x0b21,	16,	{0x27, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x23, 0xe5, 0x23, 0x60, 0x0a, 0x12, 0x0e} },
+{ 0x0b31,	9,	{0x2b, 0x8f, 0x35, 0xef, 0x42, 0x11, 0x80, 0xca, 0x22} },
+{ 0x0b3a,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} },
+{ 0x0b4a,	16,	{0x14, 0x04, 0xc2, 0x14, 0x80, 0x02, 0xd2, 0x08, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x0b5a,	14,	{0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0b68,	16,	{0x90, 0x7f, 0xd8, 0xe0, 0xf5, 0x33, 0x12, 0x00, 0x03, 0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0} },
+{ 0x0b78,	16,	{0x12, 0x09, 0x50, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x0e, 0x30, 0x01, 0x05, 0x12, 0x0d, 0xcf} },
+{ 0x0b88,	11,	{0x80, 0x06, 0x12, 0x0d, 0x5c, 0xef, 0x60, 0xe1, 0x12, 0x07, 0x28} },
+{ 0x0b93,	1,	{0x22} },
+{ 0x0b94,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} },
+{ 0x0ba4,	16,	{0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} },
+{ 0x0bb4,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0bbe,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} },
+{ 0x0bce,	16,	{0x02, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} },
+{ 0x0bde,	7,	{0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0be5,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0bf5,	16,	{0x10, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54} },
+{ 0x0c05,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x0c0b,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0c1b,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54} },
+{ 0x0c2b,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x0c31,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0c41,	16,	{0x12, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54} },
+{ 0x0c51,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x0c57,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0c67,	16,	{0x14, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54} },
+{ 0x0c77,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x0c7d,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0c8d,	16,	{0x16, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54} },
+{ 0x0c9d,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x0ca3,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x18, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x0cb3,	16,	{0x98, 0x74, 0x17, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90} },
+{ 0x0cc3,	5,	{0xc0, 0x00, 0xed, 0xf0, 0x22} },
+{ 0x0cc8,	16,	{0x12, 0x0e, 0x13, 0x8f, 0x35, 0x12, 0x0e, 0x13, 0x8f, 0x36, 0xe5, 0x35, 0x65, 0x36, 0x60, 0x12} },
+{ 0x0cd8,	16,	{0x12, 0x0e, 0x13, 0x8f, 0x35, 0xe5, 0x35, 0x65, 0x36, 0x60, 0x07, 0x12, 0x0e, 0x13, 0x8f, 0x36} },
+{ 0x0ce8,	4,	{0x80, 0xe8, 0xaf, 0x35} },
+{ 0x0cec,	1,	{0x22} },
+{ 0x0ced,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x0cfd,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x0d0d,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d12,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x0d22,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x0d32,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d37,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x13, 0x04, 0xe0, 0x44} },
+{ 0x0d47,	16,	{0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0d, 0xb8, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x0d57,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x0d5c,	16,	{0x90, 0x7f, 0xd8, 0xe0, 0xf5, 0x34, 0x12, 0x07, 0x28, 0x12, 0x0e, 0x37, 0xef, 0x30, 0xe6, 0x0b} },
+{ 0x0d6c,	16,	{0x90, 0x7f, 0xd8, 0xe0, 0x65, 0x34, 0x60, 0xf1, 0x7f, 0x01, 0x22, 0x12, 0x00, 0x03, 0x7f, 0x00} },
+{ 0x0d7c,	1,	{0x22} },
+{ 0x0d7d,	16,	{0xae, 0x07, 0xe4, 0xff, 0xe5, 0x15, 0x54, 0x7f, 0xfd, 0x12, 0x0c, 0xa3, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0d8d,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x15, 0x44, 0x80, 0xfd, 0x12, 0x0c, 0xa3} },
+{ 0x0d9d,	1,	{0x22} },
+{ 0x0d9e,	16,	{0x05, 0x29, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2f, 0x03, 0x00, 0x00, 0xc1, 0x02, 0xc1, 0x08} },
+{ 0x0dae,	7,	{0xc1, 0x01, 0xc1, 0x86, 0x01, 0x25, 0x00} },
+{ 0x0db5,	2,	{0xc1, 0x94} },
+{ 0x0db7,	1,	{0x00} },
+{ 0x0db8,	16,	{0x8e, 0x34, 0x8f, 0x35, 0xe5, 0x35, 0x15, 0x35, 0xae, 0x34, 0x70, 0x02, 0x15, 0x34, 0x4e, 0x60} },
+{ 0x0dc8,	7,	{0x05, 0x12, 0x09, 0x3f, 0x80, 0xee, 0x22} },
+{ 0x0dcf,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x0d, 0x7e, 0x00, 0x12, 0x0d, 0xb8, 0x90, 0x7f} },
+{ 0x0ddf,	6,	{0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} },
+{ 0x0de5,	12,	{0x90, 0x7f, 0x98, 0x74, 0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0df1,	12,	{0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0dfd,	3,	{0xd2, 0x01, 0x22} },
+{ 0x0e00,	16,	{0x02, 0x0b, 0xbe, 0x00, 0x02, 0x0c, 0xed, 0x00, 0x02, 0x0b, 0x94, 0x00, 0x02, 0x0b, 0x3a, 0x00} },
+{ 0x0e10,	3,	{0x02, 0x0d, 0x12} },
+{ 0x0e13,	12,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0e1f,	12,	{0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0e2b,	12,	{0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0e37,	12,	{0x90, 0x7f, 0x98, 0x74, 0x16, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0e43,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x0e47,	3,	{0xc2, 0x01, 0x22} },
+{ 0x1900,	16,	{0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x08, 0x01, 0x00, 0x00, 0x01, 0x02} },
+{ 0x1910,	16,	{0x00, 0x01, 0x09, 0x02, 0x74, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e} },
+{ 0x1920,	16,	{0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1930,	16,	{0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00} },
+{ 0x1940,	16,	{0x07, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x1950,	16,	{0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x82, 0x02} },
+{ 0x1960,	16,	{0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00} },
+{ 0x1970,	16,	{0x00, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x07} },
+{ 0x1980,	16,	{0x05, 0x87, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00} },
+{ 0x1990,	16,	{0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00} },
+{ 0x19a0,	16,	{0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00} },
+{ 0x19b0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00} },
+{ 0x19c0,	16,	{0x6f, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} },
+{ 0x19d0,	16,	{0x2e, 0x00, 0x36, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00} },
+{ 0x19e0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00} },
+{ 0x19f0,	16,	{0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x61, 0x00} },
+{ 0x1a00,	10,	{0x70, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa26msg.h linux.18p17/drivers/usb/serial/keyspan_usa26msg.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa26msg.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa26msg.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,248 @@
+/*
+	usa26msg.h
+
+	Copyright (c) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Firmware to run on Anchor EZ-USB
+
+	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 this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	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 InnoSys Incorprated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``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 OR CONTRIBUTORS 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.    
+
+	Third revision: USA28X version (aka USA26)
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USA26, transmit) messages contain a 
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the 
+	completion of transmit; 0x00 otherwise), followed by data:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of 63.
+
+	USB IN (USA26 -> host, receive) messages contain either a zero
+	flag (indicating no error in any data bytes):
+
+		00 DAT DAT DAT ...
+
+	for a total of 63 data bytes, or a non-zero status flag (indicating 
+	that all data bytes will be preceded by status flag):
+
+		STAT DAT STAT DAT STAT DAT ...
+
+	for a total of 32 data bytes.  The valid bits in the STAT bytes are:
+
+		OVERRUN	0x02
+		PARITY	0x04
+		FRAMING	0x08
+		BREAK	0x10
+
+	Note: a "no status" RX data message (first byte zero) can serve as
+	a "break off" indicator.
+
+	revision history:
+
+	1999feb10	add reportHskiaChanges to allow us to ignore them
+	1999feb10	add txAckThreshold for fast+loose throughput enhancement
+	1999mar30	beef up support for RX error reporting
+	1999apr14	add resetDataToggle to control message
+	2000jan04	merge with usa17msg.h
+	2000jun01	add extended BSD-style copyright text
+
+	Note on shared names:
+
+	In the case of fields which have been merged between the USA17
+	and USA26 definitions, the USA26 definition is the first part
+	of the name and the USA17 definition is the second part of the
+	name; both meanings are described below.
+*/
+
+#ifndef	__USA26MSG__
+#define	__USA26MSG__
+
+#ifndef __stubs__
+#include "datadefs.h"
+#endif
+
+typedef struct txAckMessage
+{
+	u8	dummy;
+} txAckMessage;
+
+typedef struct portControlMessage
+{
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the USA26):
+	*/
+	u8	setClocking,	// BOTH: host requests baud rate be set
+		baudLo,			// BOTH: host does baud divisor calculation
+		baudHi,			// BOTH: baudHi is only used for first port (gives lower rates)
+		externalClock_txClocking,
+						// USA26: 0=internal, other=external
+						// USA17: 0=internal, other=external/RI
+		rxClocking,		// USA17: 0=internal, 1=external/RI, other=external/DSR
+
+
+		setLcr,			// BOTH: host requests lcr be set
+		lcr,			// BOTH: use PARITY, STOPBITS, DATABITS below
+
+		setFlowControl,	// BOTH: host requests flow control be set
+		ctsFlowControl,	// BOTH: 1=use CTS flow control, 0=don't
+		xonFlowControl,	// BOTH: 1=use XON/XOFF flow control, 0=don't
+		xonChar,		// BOTH: specified in current character format
+		xoffChar,		// BOTH: specified in current character format
+
+		setTxTriState_setRts,
+						// USA26: host requests TX tri-state be set
+						// USA17: host requests RTS output be set
+		txTriState_rts,	// BOTH: 1=active (normal), 0=tristate (off)
+
+		setHskoa_setDtr,
+						// USA26: host requests HSKOA output be set
+						// USA17: host requests DTR output be set
+		hskoa_dtr,		// BOTH: 1=on, 0=off
+
+		setPrescaler,	// USA26: host requests prescalar be set (default: 13)
+		prescaler;		// BOTH: specified as N/8; values 8-ff are valid
+						// must be set any time internal baud rate is set;
+						// must not be set when external clocking is used
+						// note: in USA17, prescaler is applied whenever
+						// setClocking is requested
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be specified correctly in every host message).
+	*/
+	u8	forwardingLength,  // BOTH: forward when this number of chars available
+		reportHskiaChanges_dsrFlowControl,
+						// USA26: 1=normal; 0=ignore external clock
+						// USA17: 1=use DSR flow control, 0=don't
+		txAckThreshold,	// BOTH: 0=not allowed, 1=normal, 2-255 deliver ACK faster
+		loopbackMode;	// BOTH: 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// BOTH: enable transmitting (and continue if there's data)
+		_txOff,			// BOTH: stop transmitting
+		txFlush,		// BOTH: toss outbound data
+		txBreak,		// BOTH: turn on break (cleared by _txOn)
+		rxOn,			// BOTH: turn on receiver
+		rxOff,			// BOTH: turn off receiver
+		rxFlush,		// BOTH: toss inbound data
+		rxForward,		// BOTH: forward all inbound data, NOW (as if fwdLen==1)
+		returnStatus,	// BOTH: return current status (even if it hasn't changed)
+		resetDataToggle;// BOTH: reset data toggle state to DATA0
+	
+} portControlMessage;
+
+// defines for bits in lcr
+#define	USA_DATABITS_5		0x00
+#define	USA_DATABITS_6		0x01
+#define	USA_DATABITS_7		0x02
+#define	USA_DATABITS_8		0x03
+#define	STOPBITS_5678_1	0x00	// 1 stop bit for all byte sizes
+#define	STOPBITS_5_1p5	0x04	// 1.5 stop bits for 5-bit byte
+#define	STOPBITS_678_2	0x04	// 2 stop bits for 6/7/8-bit byte
+#define	USA_PARITY_NONE		0x00
+#define	USA_PARITY_ODD		0x08
+#define	USA_PARITY_EVEN		0x18
+#define	PARITY_1		0x28
+#define	PARITY_0		0x38
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+typedef struct portStatusMessage	// one for each port
+{
+	u8	port,			// BOTH: 0=first, 1=second, other=see below
+		hskia_cts,		// USA26: reports HSKIA pin
+						// USA17: reports CTS pin
+		gpia_dcd,		// USA26: reports GPIA pin
+						// USA17: reports DCD pin
+		dsr,			// USA17: reports DSR pin
+		ri,				// USA17: reports RI pin
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		controlResponse;// 1=a control message has been processed
+} portStatusMessage;
+
+// bits in RX data message when STAT byte is included
+#define	RXERROR_OVERRUN	0x02
+#define	RXERROR_PARITY	0x04
+#define	RXERROR_FRAMING	0x08
+#define	RXERROR_BREAK	0x10
+
+typedef struct globalControlMessage
+{
+	u8	sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+} globalControlMessage;
+
+typedef struct globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+} globalStatusMessage;
+
+typedef struct globalDebugMessage
+{
+	u8	port,				// 2
+		a,
+		b,
+		c,
+		d;
+} globalDebugMessage;
+
+// ie: the maximum length of an EZUSB endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+// status rationing tuning value (each port gets checked each n ms)
+#define	STATUS_RATION	10
+
+#endif
+
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa28_fw.h linux.18p17/drivers/usb/serial/keyspan_usa28_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa28_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa28_fw.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,462 @@
+/* keyspan_usa28_fw.h
+  
+   Generated from Keyspan firmware image Wed Jul  5 09:18:29 2000 EST
+   This firmware is for the Keyspan USA-28 Serial Adaptor
+
+   "The firmware contained herein as keyspan_usa28_fw.h is
+   Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated
+   ("Keyspan"), as an unpublished work.  This notice does not imply
+   unrestricted or public access to this firmware which is a trade secret of
+   Keyspan, and which may not be reproduced, used, sold or transferred to any
+   third party without Keyspan's prior written consent.  All Rights Reserved.
+
+   This firmware may not be modified and may only be used with the Keyspan 
+   USA-28 Serial Adapter.  Distribution and/or Modification of the
+   keyspan.c driver which includes this firmware, in whole or in part,
+   requires the inclusion of this statement."
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa28_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x15, 0xc5} },
+{ 0x0003,	3,	{0x02, 0x00, 0x46} },
+{ 0x0006,	16,	{0x8e, 0x12, 0x8f, 0x13, 0xe5, 0x13, 0x15, 0x13, 0xae, 0x12, 0x70, 0x02, 0x15, 0x12, 0x4e, 0x60} },
+{ 0x0016,	7,	{0x05, 0x12, 0x18, 0x7d, 0x80, 0xee, 0x22} },
+{ 0x001d,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x0023,	3,	{0x02, 0x00, 0x46} },
+{ 0x0026,	10,	{0x12, 0x17, 0x91, 0x12, 0x18, 0x8e, 0x12, 0x14, 0x47, 0x22} },
+{ 0x0033,	3,	{0x02, 0x00, 0x1d} },
+{ 0x003b,	3,	{0x02, 0x00, 0x46} },
+{ 0x0043,	3,	{0x02, 0x1b, 0x00} },
+{ 0x0046,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0, 0xd0, 0x75, 0xd0, 0x08} },
+{ 0x0056,	16,	{0x30, 0x99, 0x0e, 0x30, 0x0e, 0x07, 0xa2, 0x14, 0x92, 0x9b, 0x85, 0x46, 0x99, 0xc2, 0x99, 0xd2} },
+{ 0x0066,	16,	{0x1c, 0x30, 0xc1, 0x0e, 0x30, 0x0f, 0x07, 0xa2, 0x15, 0x92, 0xc3, 0x85, 0x47, 0xc1, 0xc2, 0xc1} },
+{ 0x0076,	16,	{0xd2, 0x1d, 0x20, 0x1c, 0x03, 0x02, 0x04, 0x36, 0xc2, 0x1c, 0x20, 0x02, 0x03, 0x02, 0x02, 0x61} },
+{ 0x0086,	16,	{0x20, 0x0e, 0x03, 0x02, 0x01, 0x38, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x50, 0x3e, 0x20, 0x10, 0x36} },
+{ 0x0096,	16,	{0x20, 0x08, 0x33, 0x90, 0x7f, 0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x20, 0x19, 0x29, 0x30, 0x18, 0x12} },
+{ 0x00a6,	16,	{0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13} },
+{ 0x00b6,	16,	{0x92, 0x14, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83} },
+{ 0x00c6,	16,	{0xe0, 0xf5, 0x46, 0x02, 0x04, 0x34, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc7, 0xe4, 0xf0} },
+{ 0x00d6,	16,	{0xc2, 0x02, 0x30, 0x12, 0x0c, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0, 0xc2, 0x0e, 0x02, 0x04} },
+{ 0x00e6,	16,	{0x34, 0x90, 0x7f, 0xc8, 0xe0, 0x30, 0xe1, 0x05, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc9} },
+{ 0x00f6,	16,	{0xe0, 0xf5, 0x7c, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92, 0x12, 0x20, 0x10, 0x2d, 0x20, 0x08, 0x2a} },
+{ 0x0106,	16,	{0x90, 0x7f, 0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x20, 0x19, 0x20, 0x30, 0x18, 0x11, 0x90, 0x7d, 0xc1} },
+{ 0x0116,	16,	{0xe0, 0x13, 0x92, 0x14, 0xa3, 0xe0, 0xf5, 0x46, 0x75, 0x4a, 0x03, 0x02, 0x04, 0x34, 0x75, 0x4a} },
+{ 0x0126,	16,	{0x02, 0x90, 0x7d, 0xc1, 0xe0, 0xf5, 0x46, 0x02, 0x04, 0x34, 0x75, 0x4a, 0x01, 0xc2, 0x0e, 0x02} },
+{ 0x0136,	16,	{0x04, 0x34, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x50, 0x03, 0x02, 0x01, 0xda, 0x90, 0x7f, 0xc6, 0xe0} },
+{ 0x0146,	16,	{0x30, 0xe1, 0x07, 0xc2, 0x20, 0xd2, 0x1e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x7c} },
+{ 0x0156,	16,	{0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92, 0x12, 0x20, 0x10, 0x72, 0x20, 0x08, 0x6f, 0x90, 0x7f, 0x9b} },
+{ 0x0166,	16,	{0xe0, 0x20, 0xe3, 0x03, 0x20, 0x19, 0x65, 0x30, 0x18, 0x10, 0x90, 0x7e, 0x41, 0xe0, 0x13, 0x92} },
+{ 0x0176,	16,	{0x9b, 0xa3, 0xe0, 0xf5, 0x99, 0x75, 0x4a, 0x03, 0x80, 0x09, 0x90, 0x7e, 0x41, 0xe0, 0xf5, 0x99} },
+{ 0x0186,	16,	{0x75, 0x4a, 0x02, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x40, 0x17, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xc2} },
+{ 0x0196,	16,	{0x02, 0x20, 0x12, 0x03, 0x02, 0x04, 0x34, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0, 0x02, 0x04} },
+{ 0x01a6,	16,	{0x34, 0x30, 0x18, 0x12, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e} },
+{ 0x01b6,	16,	{0xf5, 0x83, 0xe0, 0x13, 0x92, 0x14, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4} },
+{ 0x01c6,	16,	{0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x46, 0xd2, 0x0e, 0x02, 0x04, 0x34, 0x75, 0x4a, 0x01, 0xc2} },
+{ 0x01d6,	16,	{0x20, 0x02, 0x04, 0x34, 0x30, 0x10, 0x03, 0x02, 0x02, 0x5c, 0x20, 0x08, 0x79, 0x90, 0x7f, 0x9b} },
+{ 0x01e6,	16,	{0xe0, 0x20, 0xe3, 0x03, 0x20, 0x19, 0x6f, 0x30, 0x18, 0x12, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40} },
+{ 0x01f6,	16,	{0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x9b, 0xae, 0x4a, 0x05, 0x4a} },
+{ 0x0206,	16,	{0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5, 0x99, 0xe5, 0x4a, 0xc3} },
+{ 0x0216,	16,	{0x95, 0x7c, 0x40, 0x17, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xc2, 0x02, 0x20, 0x12, 0x03, 0x02, 0x04} },
+{ 0x0226,	16,	{0x34, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0, 0x02, 0x04, 0x34, 0x30, 0x18, 0x12, 0xae, 0x4a} },
+{ 0x0236,	16,	{0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x14} },
+{ 0x0246,	16,	{0xae, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xf5} },
+{ 0x0256,	16,	{0x46, 0xd2, 0x0e, 0x02, 0x04, 0x34, 0xc2, 0x20, 0x02, 0x04, 0x34, 0x20, 0x0e, 0x03, 0x02, 0x03} },
+{ 0x0266,	16,	{0x13, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x50, 0x3e, 0x20, 0x10, 0x36, 0x20, 0x08, 0x33, 0x90, 0x7f} },
+{ 0x0276,	16,	{0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x20, 0x19, 0x29, 0x30, 0x18, 0x12, 0xae, 0x4a, 0x05, 0x4a, 0x74} },
+{ 0x0286,	16,	{0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x14, 0xae, 0x4a, 0x05} },
+{ 0x0296,	16,	{0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x46, 0x02, 0x04} },
+{ 0x02a6,	16,	{0x34, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0xd2, 0x02, 0x30, 0x12, 0x0c} },
+{ 0x02b6,	16,	{0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc6, 0xe0} },
+{ 0x02c6,	16,	{0x30, 0xe1, 0x05, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x7c, 0x90, 0x7e} },
+{ 0x02d6,	16,	{0x40, 0xe0, 0x13, 0x92, 0x12, 0x20, 0x10, 0x2d, 0x20, 0x08, 0x2a, 0x90, 0x7f, 0x9b, 0xe0, 0x20} },
+{ 0x02e6,	16,	{0xe3, 0x03, 0x20, 0x19, 0x20, 0x30, 0x18, 0x11, 0x90, 0x7e, 0x41, 0xe0, 0x13, 0x92, 0x14, 0xa3} },
+{ 0x02f6,	16,	{0xe0, 0xf5, 0x46, 0x75, 0x4a, 0x03, 0x02, 0x04, 0x34, 0x75, 0x4a, 0x02, 0x90, 0x7e, 0x41, 0xe0} },
+{ 0x0306,	16,	{0xf5, 0x46, 0x02, 0x04, 0x34, 0x75, 0x4a, 0x01, 0xc2, 0x0e, 0x02, 0x04, 0x34, 0xe5, 0x4a, 0xc3} },
+{ 0x0316,	16,	{0x95, 0x7c, 0x50, 0x03, 0x02, 0x03, 0xb5, 0x90, 0x7f, 0xc8, 0xe0, 0x30, 0xe1, 0x07, 0xc2, 0x20} },
+{ 0x0326,	16,	{0xd2, 0x1e, 0x02, 0x04, 0x34, 0x90, 0x7f, 0xc9, 0xe0, 0xf5, 0x7c, 0x90, 0x7d, 0xc0, 0xe0, 0x13} },
+{ 0x0336,	16,	{0x92, 0x12, 0x20, 0x10, 0x72, 0x20, 0x08, 0x6f, 0x90, 0x7f, 0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x20} },
+{ 0x0346,	16,	{0x19, 0x65, 0x30, 0x18, 0x10, 0x90, 0x7d, 0xc1, 0xe0, 0x13, 0x92, 0x9b, 0xa3, 0xe0, 0xf5, 0x99} },
+{ 0x0356,	16,	{0x75, 0x4a, 0x03, 0x80, 0x09, 0x90, 0x7d, 0xc1, 0xe0, 0xf5, 0x99, 0x75, 0x4a, 0x02, 0xe5, 0x4a} },
+{ 0x0366,	16,	{0xc3, 0x95, 0x7c, 0x40, 0x17, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0xd2, 0x02, 0x20, 0x12, 0x03, 0x02} },
+{ 0x0376,	16,	{0x04, 0x34, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0, 0x02, 0x04, 0x34, 0x30, 0x18, 0x12, 0xae} },
+{ 0x0386,	16,	{0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92} },
+{ 0x0396,	16,	{0x14, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0} },
+{ 0x03a6,	16,	{0xf5, 0x46, 0xd2, 0x0e, 0x02, 0x04, 0x34, 0x75, 0x4a, 0x01, 0xc2, 0x20, 0x02, 0x04, 0x34, 0x30} },
+{ 0x03b6,	16,	{0x10, 0x03, 0x02, 0x04, 0x32, 0x20, 0x08, 0x74, 0x90, 0x7f, 0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x20} },
+{ 0x03c6,	16,	{0x19, 0x6a, 0x30, 0x18, 0x12, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x03d6,	16,	{0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x9b, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82} },
+{ 0x03e6,	16,	{0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x99, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x40, 0x13, 0x90} },
+{ 0x03f6,	16,	{0x7f, 0xc9, 0xe4, 0xf0, 0xd2, 0x02, 0x30, 0x12, 0x35, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x04, 0xf0} },
+{ 0x0406,	16,	{0x80, 0x2c, 0x30, 0x18, 0x12, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0416,	16,	{0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x14, 0xae, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2e, 0xf5, 0x82} },
+{ 0x0426,	16,	{0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x46, 0xd2, 0x0e, 0x80, 0x02, 0xc2, 0x20, 0xd2, 0x24} },
+{ 0x0436,	16,	{0x20, 0x1d, 0x03, 0x02, 0x07, 0xf4, 0xc2, 0x1d, 0x20, 0x03, 0x03, 0x02, 0x06, 0x1f, 0x20, 0x0f} },
+{ 0x0446,	16,	{0x03, 0x02, 0x04, 0xf6, 0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x50, 0x3e, 0x20, 0x11, 0x36, 0x20, 0x0a} },
+{ 0x0456,	16,	{0x33, 0x90, 0x7f, 0x9a, 0xe0, 0x20, 0xe5, 0x03, 0x20, 0x1b, 0x29, 0x30, 0x1a, 0x12, 0xae, 0x4b} },
+{ 0x0466,	16,	{0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x15} },
+{ 0x0476,	16,	{0xae, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5} },
+{ 0x0486,	16,	{0x47, 0x02, 0x07, 0xf2, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcb, 0xe4, 0xf0, 0xc2, 0x03} },
+{ 0x0496,	16,	{0x30, 0x13, 0x0c, 0xc2, 0x13, 0x90, 0x7f, 0xc1, 0x04, 0xf0, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0x90} },
+{ 0x04a6,	16,	{0x7f, 0xcc, 0xe0, 0x30, 0xe1, 0x05, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcd, 0xe0, 0xf5} },
+{ 0x04b6,	16,	{0x7d, 0x90, 0x7c, 0xc0, 0xe0, 0x13, 0x92, 0x13, 0x20, 0x11, 0x2d, 0x20, 0x0a, 0x2a, 0x90, 0x7f} },
+{ 0x04c6,	16,	{0x9a, 0xe0, 0x20, 0xe5, 0x03, 0x20, 0x1b, 0x20, 0x30, 0x1a, 0x11, 0x90, 0x7c, 0xc1, 0xe0, 0x13} },
+{ 0x04d6,	16,	{0x92, 0x15, 0xa3, 0xe0, 0xf5, 0x47, 0x75, 0x4b, 0x03, 0x02, 0x07, 0xf2, 0x75, 0x4b, 0x02, 0x90} },
+{ 0x04e6,	16,	{0x7c, 0xc1, 0xe0, 0xf5, 0x47, 0x02, 0x07, 0xf2, 0x75, 0x4b, 0x01, 0xc2, 0x0f, 0x02, 0x07, 0xf2} },
+{ 0x04f6,	16,	{0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x50, 0x03, 0x02, 0x05, 0x98, 0x90, 0x7f, 0xca, 0xe0, 0x30, 0xe1} },
+{ 0x0506,	16,	{0x07, 0xc2, 0x21, 0xd2, 0x1f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcb, 0xe0, 0xf5, 0x7d, 0x90, 0x7d} },
+{ 0x0516,	16,	{0x40, 0xe0, 0x13, 0x92, 0x13, 0x20, 0x11, 0x72, 0x20, 0x0a, 0x6f, 0x90, 0x7f, 0x9a, 0xe0, 0x20} },
+{ 0x0526,	16,	{0xe5, 0x03, 0x20, 0x1b, 0x65, 0x30, 0x1a, 0x10, 0x90, 0x7d, 0x41, 0xe0, 0x13, 0x92, 0xc3, 0xa3} },
+{ 0x0536,	16,	{0xe0, 0xf5, 0xc1, 0x75, 0x4b, 0x03, 0x80, 0x09, 0x90, 0x7d, 0x41, 0xe0, 0xf5, 0xc1, 0x75, 0x4b} },
+{ 0x0546,	16,	{0x02, 0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x40, 0x17, 0x90, 0x7f, 0xcb, 0xe4, 0xf0, 0xc2, 0x03, 0x20} },
+{ 0x0556,	16,	{0x13, 0x03, 0x02, 0x07, 0xf2, 0xc2, 0x13, 0x90, 0x7f, 0xc1, 0x04, 0xf0, 0x02, 0x07, 0xf2, 0x30} },
+{ 0x0566,	16,	{0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83} },
+{ 0x0576,	16,	{0xe0, 0x13, 0x92, 0x15, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d} },
+{ 0x0586,	16,	{0xf5, 0x83, 0xe0, 0xf5, 0x47, 0xd2, 0x0f, 0x02, 0x07, 0xf2, 0x75, 0x4b, 0x01, 0xc2, 0x21, 0x02} },
+{ 0x0596,	16,	{0x07, 0xf2, 0x30, 0x11, 0x03, 0x02, 0x06, 0x1a, 0x20, 0x0a, 0x79, 0x90, 0x7f, 0x9a, 0xe0, 0x20} },
+{ 0x05a6,	16,	{0xe5, 0x03, 0x20, 0x1b, 0x6f, 0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5} },
+{ 0x05b6,	16,	{0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0xc3, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0x40} },
+{ 0x05c6,	16,	{0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0xc1, 0xe5, 0x4b, 0xc3, 0x95, 0x7d} },
+{ 0x05d6,	16,	{0x40, 0x17, 0x90, 0x7f, 0xcb, 0xe4, 0xf0, 0xc2, 0x03, 0x20, 0x13, 0x03, 0x02, 0x07, 0xf2, 0xc2} },
+{ 0x05e6,	16,	{0x13, 0x90, 0x7f, 0xc1, 0x04, 0xf0, 0x02, 0x07, 0xf2, 0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b} },
+{ 0x05f6,	16,	{0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x15, 0xae, 0x4b} },
+{ 0x0606,	16,	{0x05, 0x4b, 0x74, 0x40, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x47, 0xd2} },
+{ 0x0616,	16,	{0x0f, 0x02, 0x07, 0xf2, 0xc2, 0x21, 0x02, 0x07, 0xf2, 0x20, 0x0f, 0x03, 0x02, 0x06, 0xd1, 0xe5} },
+{ 0x0626,	16,	{0x4b, 0xc3, 0x95, 0x7d, 0x50, 0x3e, 0x20, 0x11, 0x36, 0x20, 0x0a, 0x33, 0x90, 0x7f, 0x9a, 0xe0} },
+{ 0x0636,	16,	{0x20, 0xe5, 0x03, 0x20, 0x1b, 0x29, 0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e} },
+{ 0x0646,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x15, 0xae, 0x4b, 0x05, 0x4b, 0x74} },
+{ 0x0656,	16,	{0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0xf5, 0x47, 0x02, 0x07, 0xf2, 0xc2} },
+{ 0x0666,	16,	{0x0f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcd, 0xe4, 0xf0, 0xd2, 0x03, 0x30, 0x13, 0x0c, 0xc2, 0x13} },
+{ 0x0676,	16,	{0x90, 0x7f, 0xc1, 0x04, 0xf0, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xca, 0xe0, 0x30, 0xe1} },
+{ 0x0686,	16,	{0x05, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcb, 0xe0, 0xf5, 0x7d, 0x90, 0x7d, 0x40, 0xe0} },
+{ 0x0696,	16,	{0x13, 0x92, 0x13, 0x20, 0x11, 0x2d, 0x20, 0x0a, 0x2a, 0x90, 0x7f, 0x9a, 0xe0, 0x20, 0xe5, 0x03} },
+{ 0x06a6,	16,	{0x20, 0x1b, 0x20, 0x30, 0x1a, 0x11, 0x90, 0x7d, 0x41, 0xe0, 0x13, 0x92, 0x15, 0xa3, 0xe0, 0xf5} },
+{ 0x06b6,	16,	{0x47, 0x75, 0x4b, 0x03, 0x02, 0x07, 0xf2, 0x75, 0x4b, 0x02, 0x90, 0x7d, 0x41, 0xe0, 0xf5, 0x47} },
+{ 0x06c6,	16,	{0x02, 0x07, 0xf2, 0x75, 0x4b, 0x01, 0xc2, 0x0f, 0x02, 0x07, 0xf2, 0xe5, 0x4b, 0xc3, 0x95, 0x7d} },
+{ 0x06d6,	16,	{0x50, 0x03, 0x02, 0x07, 0x73, 0x90, 0x7f, 0xcc, 0xe0, 0x30, 0xe1, 0x07, 0xc2, 0x21, 0xd2, 0x1f} },
+{ 0x06e6,	16,	{0x02, 0x07, 0xf2, 0x90, 0x7f, 0xcd, 0xe0, 0xf5, 0x7d, 0x90, 0x7c, 0xc0, 0xe0, 0x13, 0x92, 0x13} },
+{ 0x06f6,	16,	{0x20, 0x11, 0x72, 0x20, 0x0a, 0x6f, 0x90, 0x7f, 0x9a, 0xe0, 0x20, 0xe5, 0x03, 0x20, 0x1b, 0x65} },
+{ 0x0706,	16,	{0x30, 0x1a, 0x10, 0x90, 0x7c, 0xc1, 0xe0, 0x13, 0x92, 0xc3, 0xa3, 0xe0, 0xf5, 0xc1, 0x75, 0x4b} },
+{ 0x0716,	16,	{0x03, 0x80, 0x09, 0x90, 0x7c, 0xc1, 0xe0, 0xf5, 0xc1, 0x75, 0x4b, 0x02, 0xe5, 0x4b, 0xc3, 0x95} },
+{ 0x0726,	16,	{0x7d, 0x40, 0x17, 0x90, 0x7f, 0xcd, 0xe4, 0xf0, 0xd2, 0x03, 0x20, 0x13, 0x03, 0x02, 0x07, 0xf2} },
+{ 0x0736,	16,	{0xc2, 0x13, 0x90, 0x7f, 0xc1, 0x04, 0xf0, 0x02, 0x07, 0xf2, 0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05} },
+{ 0x0746,	16,	{0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x15, 0xae} },
+{ 0x0756,	16,	{0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0xf5, 0x47} },
+{ 0x0766,	16,	{0xd2, 0x0f, 0x02, 0x07, 0xf2, 0x75, 0x4b, 0x01, 0xc2, 0x21, 0x02, 0x07, 0xf2, 0x30, 0x11, 0x03} },
+{ 0x0776,	16,	{0x02, 0x07, 0xf0, 0x20, 0x0a, 0x74, 0x90, 0x7f, 0x9a, 0xe0, 0x20, 0xe5, 0x03, 0x20, 0x1b, 0x6a} },
+{ 0x0786,	16,	{0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5} },
+{ 0x0796,	16,	{0x83, 0xe0, 0x13, 0x92, 0xc3, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x07a6,	16,	{0x7c, 0xf5, 0x83, 0xe0, 0xf5, 0xc1, 0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x40, 0x13, 0x90, 0x7f, 0xcd} },
+{ 0x07b6,	16,	{0xe4, 0xf0, 0xd2, 0x03, 0x30, 0x13, 0x35, 0xc2, 0x13, 0x90, 0x7f, 0xc1, 0x04, 0xf0, 0x80, 0x2c} },
+{ 0x07c6,	16,	{0x30, 0x1a, 0x12, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5} },
+{ 0x07d6,	16,	{0x83, 0xe0, 0x13, 0x92, 0x15, 0xae, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x07e6,	16,	{0x7c, 0xf5, 0x83, 0xe0, 0xf5, 0x47, 0xd2, 0x0f, 0x80, 0x02, 0xc2, 0x21, 0xd2, 0x24, 0x20, 0x98} },
+{ 0x07f6,	16,	{0x03, 0x02, 0x09, 0x26, 0xc2, 0x98, 0x20, 0x00, 0x03, 0x02, 0x08, 0x98, 0x20, 0x22, 0x27, 0xae} },
+{ 0x0806,	16,	{0x48, 0x05, 0x48, 0x74, 0x80, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x99, 0xf0} },
+{ 0x0816,	16,	{0x30, 0x18, 0x49, 0xae, 0x48, 0x05, 0x48, 0x74, 0x80, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5} },
+{ 0x0826,	16,	{0x83, 0xe5, 0x98, 0xf0, 0x80, 0x36, 0xaf, 0x99, 0xef, 0xb5, 0x58, 0x04, 0xd2, 0x08, 0x80, 0x2c} },
+{ 0x0836,	3,	{0xef, 0xb5, 0x57} },
+{ 0x0839,	16,	{0x04, 0xc2, 0x08, 0x80, 0x24, 0xae, 0x48, 0x05, 0x48, 0x74, 0x80, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0849,	16,	{0x7e, 0xf5, 0x83, 0xef, 0xf0, 0x30, 0x18, 0x11, 0xae, 0x48, 0x05, 0x48, 0x74, 0x80, 0x2e, 0xf5} },
+{ 0x0859,	16,	{0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0xd2, 0x16, 0xe5, 0x48, 0xc3, 0x95, 0x54} },
+{ 0x0869,	16,	{0x50, 0x03, 0x02, 0x09, 0x24, 0x90, 0x7f, 0xb8, 0xe0, 0x30, 0xe1, 0x15, 0xe5, 0x48, 0xc3, 0x94} },
+{ 0x0879,	16,	{0x40, 0x50, 0x03, 0x02, 0x09, 0x24, 0x15, 0x48, 0x15, 0x48, 0x05, 0x2d, 0xd2, 0x09, 0x02, 0x09} },
+{ 0x0889,	16,	{0x24, 0x90, 0x7f, 0xb7, 0xe5, 0x48, 0xf0, 0x75, 0x48, 0x00, 0xc2, 0x00, 0x02, 0x09, 0x24, 0x20} },
+{ 0x0899,	16,	{0x22, 0x27, 0xae, 0x48, 0x05, 0x48, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83} },
+{ 0x08a9,	16,	{0xe5, 0x99, 0xf0, 0x30, 0x18, 0x49, 0xae, 0x48, 0x05, 0x48, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4} },
+{ 0x08b9,	16,	{0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0x80, 0x36, 0xaf, 0x99, 0xef, 0xb5, 0x58, 0x04, 0xd2} },
+{ 0x08c9,	16,	{0x08, 0x80, 0x2c, 0xef, 0xb5, 0x57, 0x04, 0xc2, 0x08, 0x80, 0x24, 0xae, 0x48, 0x05, 0x48, 0x74} },
+{ 0x08d9,	16,	{0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xef, 0xf0, 0x30, 0x18, 0x11, 0xae, 0x48} },
+{ 0x08e9,	16,	{0x05, 0x48, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0xd2} },
+{ 0x08f9,	16,	{0x16, 0xe5, 0x48, 0xc3, 0x95, 0x54, 0x40, 0x23, 0x90, 0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x11, 0xe5} },
+{ 0x0909,	16,	{0x48, 0xc3, 0x94, 0x40, 0x40, 0x15, 0x15, 0x48, 0x15, 0x48, 0x05, 0x2d, 0xd2, 0x09, 0x80, 0x0b} },
+{ 0x0919,	16,	{0x90, 0x7f, 0xb9, 0xe5, 0x48, 0xf0, 0x75, 0x48, 0x00, 0xd2, 0x00, 0xd2, 0x24, 0x20, 0xc0, 0x03} },
+{ 0x0929,	16,	{0x02, 0x0a, 0x58, 0xc2, 0xc0, 0x20, 0x01, 0x03, 0x02, 0x09, 0xca, 0x20, 0x23, 0x27, 0xae, 0x49} },
+{ 0x0939,	16,	{0x05, 0x49, 0x74, 0x80, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe5, 0xc1, 0xf0, 0x30} },
+{ 0x0949,	16,	{0x1a, 0x49, 0xae, 0x49, 0x05, 0x49, 0x74, 0x80, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83} },
+{ 0x0959,	16,	{0xe5, 0xc0, 0xf0, 0x80, 0x36, 0xaf, 0xc1, 0xef, 0xb5, 0x70, 0x04, 0xd2, 0x0a, 0x80, 0x2c, 0xef} },
+{ 0x0969,	16,	{0xb5, 0x6f, 0x04, 0xc2, 0x0a, 0x80, 0x24, 0xae, 0x49, 0x05, 0x49, 0x74, 0x80, 0x2e, 0xf5, 0x82} },
+{ 0x0979,	16,	{0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xef, 0xf0, 0x30, 0x1a, 0x11, 0xae, 0x49, 0x05, 0x49, 0x74, 0x80} },
+{ 0x0989,	16,	{0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe5, 0xc0, 0xf0, 0xd2, 0x17, 0xe5, 0x49, 0xc3} },
+{ 0x0999,	16,	{0x95, 0x6c, 0x50, 0x03, 0x02, 0x0a, 0x56, 0x90, 0x7f, 0xbc, 0xe0, 0x30, 0xe1, 0x15, 0xe5, 0x49} },
+{ 0x09a9,	16,	{0xc3, 0x94, 0x40, 0x50, 0x03, 0x02, 0x0a, 0x56, 0x15, 0x49, 0x15, 0x49, 0x05, 0x39, 0xd2, 0x0b} },
+{ 0x09b9,	16,	{0x02, 0x0a, 0x56, 0x90, 0x7f, 0xbb, 0xe5, 0x49, 0xf0, 0x75, 0x49, 0x00, 0xc2, 0x01, 0x02, 0x0a} },
+{ 0x09c9,	16,	{0x56, 0x20, 0x23, 0x27, 0xae, 0x49, 0x05, 0x49, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d} },
+{ 0x09d9,	16,	{0xf5, 0x83, 0xe5, 0xc1, 0xf0, 0x30, 0x1a, 0x49, 0xae, 0x49, 0x05, 0x49, 0x74, 0x00, 0x2e, 0xf5} },
+{ 0x09e9,	16,	{0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe5, 0xc0, 0xf0, 0x80, 0x36, 0xaf, 0xc1, 0xef, 0xb5, 0x70} },
+{ 0x09f9,	16,	{0x04, 0xd2, 0x0a, 0x80, 0x2c, 0xef, 0xb5, 0x6f, 0x04, 0xc2, 0x0a, 0x80, 0x24, 0xae, 0x49, 0x05} },
+{ 0x0a09,	16,	{0x49, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xef, 0xf0, 0x30, 0x1a, 0x11} },
+{ 0x0a19,	16,	{0xae, 0x49, 0x05, 0x49, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe5, 0xc0} },
+{ 0x0a29,	16,	{0xf0, 0xd2, 0x17, 0xe5, 0x49, 0xc3, 0x95, 0x6c, 0x40, 0x23, 0x90, 0x7f, 0xba, 0xe0, 0x30, 0xe1} },
+{ 0x0a39,	16,	{0x11, 0xe5, 0x49, 0xc3, 0x94, 0x40, 0x40, 0x15, 0x15, 0x49, 0x15, 0x49, 0x05, 0x39, 0xd2, 0x0b} },
+{ 0x0a49,	16,	{0x80, 0x0b, 0x90, 0x7f, 0xbd, 0xe5, 0x49, 0xf0, 0x75, 0x49, 0x00, 0xd2, 0x01, 0xd2, 0x24, 0x30} },
+{ 0x0a59,	7,	{0x24, 0x05, 0xc2, 0x24, 0x02, 0x00, 0x56} },
+{ 0x0a60,	11,	{0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0a6b,	16,	{0x90, 0x7f, 0xce, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x0b, 0x93, 0xe4, 0xf5, 0x12, 0x74, 0x40, 0x25} },
+{ 0x0a7b,	16,	{0x12, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x12, 0x7c, 0x00, 0x7b, 0x00} },
+{ 0x0a8b,	16,	{0x24, 0x4c, 0xf9, 0xec, 0x34, 0x00, 0xfa, 0xef, 0x12, 0x15, 0x55, 0x05, 0x12, 0xe5, 0x12, 0xb4} },
+{ 0x0a9b,	16,	{0x18, 0xdb, 0xe5, 0x4c, 0x60, 0x0c, 0x75, 0xc9, 0x20, 0x75, 0xc8, 0x36, 0x85, 0x4d, 0xca, 0x85} },
+{ 0x0aab,	16,	{0x4e, 0xcb, 0xe5, 0x4f, 0x13, 0x92, 0x18, 0x92, 0x9f, 0xe5, 0x50, 0x13, 0x92, 0x19, 0xe5, 0x51} },
+{ 0x0abb,	16,	{0x13, 0x92, 0x22, 0xe5, 0x52, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfb, 0xf0, 0x80, 0x07} },
+{ 0x0acb,	16,	{0x90, 0x7f, 0x98, 0xe0, 0x44, 0x04, 0xf0, 0xe5, 0x53, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54} },
+{ 0x0adb,	16,	{0x7f, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0x98, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x59, 0x60, 0x0b, 0xc2} },
+{ 0x0aeb,	16,	{0x10, 0xc2, 0x08, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x5a, 0x60, 0x0b, 0xd2, 0x08} },
+{ 0x0afb,	16,	{0xd2, 0x09, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x5b, 0x60, 0x17, 0xc2, 0xaf, 0xe4} },
+{ 0x0b0b,	16,	{0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0xc2, 0x0e, 0xd2, 0x02, 0xc2, 0x12, 0xf5, 0x7c} },
+{ 0x0b1b,	16,	{0xf5, 0x4a, 0xd2, 0xaf, 0xe5, 0x5c, 0x60, 0x05, 0x30, 0x22, 0x02, 0xd2, 0x08, 0xe5, 0x5d, 0x60} },
+{ 0x0b2b,	16,	{0x15, 0x90, 0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0x9e, 0xe0, 0x44, 0x02, 0xf0, 0x90} },
+{ 0x0b3b,	16,	{0x7f, 0x98, 0xe0, 0x54, 0xfd, 0xf0, 0xe5, 0x5e, 0x60, 0x0a, 0xd2, 0x9c, 0xc2, 0x98, 0x75, 0x2e} },
+{ 0x0b4b,	16,	{0x01, 0x75, 0x40, 0x28, 0xe5, 0x5f, 0x60, 0x07, 0xc2, 0x9c, 0xe4, 0xf5, 0x48, 0xf5, 0x2e, 0xe5} },
+{ 0x0b5b,	16,	{0x60, 0x60, 0x03, 0xe4, 0xf5, 0x48, 0xe5, 0x61, 0x60, 0x02, 0xd2, 0x04, 0xe5, 0x62, 0x60, 0x08} },
+{ 0x0b6b,	16,	{0xe5, 0x5e, 0x70, 0x02, 0xf5, 0x40, 0xd2, 0x09, 0xe5, 0x63, 0x60, 0x15, 0x90, 0x7f, 0xd7, 0x74} },
+{ 0x0b7b,	16,	{0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74, 0x12, 0xf0, 0x74, 0x32, 0xf0, 0x74, 0x15, 0xf0, 0x74, 0x35} },
+{ 0x0b8b,	16,	{0xf0, 0xd2, 0x06, 0xe4, 0x90, 0x7f, 0xcf, 0xf0, 0xa2, 0x10, 0xe4, 0x33, 0xff, 0x65, 0x2b, 0x60} },
+{ 0x0b9b,	16,	{0x04, 0x8f, 0x2b, 0xd2, 0x09, 0xa2, 0x08, 0xe4, 0x33, 0xff, 0x65, 0x2c, 0x60, 0x04, 0x8f, 0x2c} },
+{ 0x0bab,	16,	{0xd2, 0x09, 0x90, 0x7f, 0x9b, 0xe0, 0x54, 0x08, 0x65, 0x27, 0x60, 0x07, 0xe0, 0x54, 0x08, 0xf5} },
+{ 0x0bbb,	16,	{0x27, 0xd2, 0x09, 0x90, 0x7f, 0x9b, 0xe0, 0x54, 0x40, 0xb5, 0x29, 0x09, 0xe0, 0x54, 0x40, 0x64} },
+{ 0x0bcb,	16,	{0x40, 0xf5, 0x29, 0xd2, 0x09, 0x30, 0x04, 0x35, 0xc2, 0xaf, 0x30, 0x00, 0x18, 0x90, 0x7f, 0xb8} },
+{ 0x0bdb,	16,	{0xe0, 0x20, 0xe1, 0x27, 0xe5, 0x48, 0x60, 0x09, 0x90, 0x7f, 0xb7, 0xf0, 0xe4, 0xf5, 0x48, 0xc2} },
+{ 0x0beb,	16,	{0x00, 0xc2, 0x04, 0x80, 0x16, 0x90, 0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x0f, 0xe5, 0x48, 0x60, 0x09} },
+{ 0x0bfb,	16,	{0x90, 0x7f, 0xb9, 0xf0, 0xe4, 0xf5, 0x48, 0xd2, 0x00, 0xc2, 0x04, 0xd2, 0xaf, 0x30, 0x1e, 0x37} },
+{ 0x0c0b,	16,	{0x30, 0x02, 0x1b, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x2d, 0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92} },
+{ 0x0c1b,	16,	{0x12, 0x75, 0x4a, 0x01, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x7c, 0xc2, 0x1e, 0x80, 0x19, 0x90, 0x7f} },
+{ 0x0c2b,	16,	{0xc8, 0xe0, 0x20, 0xe1, 0x12, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92, 0x12, 0x75, 0x4a, 0x01, 0x90} },
+{ 0x0c3b,	16,	{0x7f, 0xc9, 0xe0, 0xf5, 0x7c, 0xc2, 0x1e, 0x30, 0x20, 0x03, 0x02, 0x0d, 0x4a, 0x30, 0x1e, 0x03} },
+{ 0x0c4b,	16,	{0x02, 0x0d, 0x4a, 0x30, 0x19, 0x0a, 0x90, 0x7f, 0x9b, 0xe0, 0x20, 0xe3, 0x03, 0x02, 0x0d, 0x4a} },
+{ 0x0c5b,	16,	{0x30, 0x08, 0x03, 0x02, 0x0d, 0x4a, 0x30, 0x10, 0x03, 0x02, 0x0d, 0x4a, 0x30, 0x02, 0x62, 0x30} },
+{ 0x0c6b,	16,	{0x18, 0x12, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83} },
+{ 0x0c7b,	16,	{0xe0, 0x13, 0x92, 0x2c, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e} },
+{ 0x0c8b,	16,	{0xf5, 0x83, 0xe0, 0xf5, 0x13, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x50, 0x2a, 0x30, 0x18, 0x12, 0xaf} },
+{ 0x0c9b,	16,	{0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0x13, 0x92} },
+{ 0x0cab,	16,	{0x14, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0} },
+{ 0x0cbb,	16,	{0xf5, 0x46, 0xd2, 0x0e, 0x80, 0x6b, 0xc2, 0x0e, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0xc2, 0x02, 0x80} },
+{ 0x0ccb,	16,	{0x60, 0x30, 0x18, 0x12, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d} },
+{ 0x0cdb,	16,	{0xf5, 0x83, 0xe0, 0x13, 0x92, 0x2c, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x0ceb,	16,	{0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x13, 0xe5, 0x4a, 0xc3, 0x95, 0x7c, 0x50, 0x2a, 0x30, 0x18} },
+{ 0x0cfb,	16,	{0x12, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0} },
+{ 0x0d0b,	16,	{0x13, 0x92, 0x14, 0xaf, 0x4a, 0x05, 0x4a, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5} },
+{ 0x0d1b,	16,	{0x83, 0xe0, 0xf5, 0x46, 0xd2, 0x0e, 0x80, 0x09, 0xc2, 0x0e, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0xd2} },
+{ 0x0d2b,	16,	{0x02, 0x30, 0x18, 0x04, 0xa2, 0x2c, 0x92, 0x9b, 0xd2, 0x20, 0xc2, 0xaf, 0x85, 0x13, 0x99, 0x20} },
+{ 0x0d3b,	16,	{0x0e, 0x0b, 0x30, 0x12, 0x08, 0xc2, 0x12, 0x90, 0x7f, 0xbf, 0x74, 0x01, 0xf0, 0xd2, 0xaf, 0x90} },
+{ 0x0d4b,	16,	{0x7f, 0xd0, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x0e, 0x71, 0xe4, 0xf5, 0x12, 0x74, 0xc0, 0x25, 0x12} },
+{ 0x0d5b,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x12, 0x7c, 0x00, 0x7b, 0x00, 0x24} },
+{ 0x0d6b,	16,	{0x64, 0xf9, 0xec, 0x34, 0x00, 0xfa, 0xef, 0x12, 0x15, 0x55, 0x05, 0x12, 0xe5, 0x12, 0xb4, 0x18} },
+{ 0x0d7b,	16,	{0xdb, 0xe5, 0x64, 0x60, 0x0b, 0x75, 0x89, 0x60, 0x75, 0x88, 0x40, 0xd2, 0xdf, 0x85, 0x65, 0x8d} },
+{ 0x0d8b,	16,	{0xe5, 0x67, 0x13, 0x92, 0x1a, 0x92, 0xc7, 0xe5, 0x68, 0x13, 0x92, 0x1b, 0xe5, 0x69, 0x13, 0x92} },
+{ 0x0d9b,	16,	{0x23, 0xe5, 0x6a, 0x60, 0x09, 0x90, 0x7f, 0x97, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0x7f} },
+{ 0x0dab,	16,	{0x97, 0xe0, 0x44, 0x10, 0xf0, 0xe5, 0x6b, 0x60, 0x09, 0x90, 0x7f, 0x97, 0xe0, 0x54, 0x7f, 0xf0} },
+{ 0x0dbb,	16,	{0x80, 0x07, 0x90, 0x7f, 0x97, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x71, 0x60, 0x0b, 0xc2, 0x11, 0xc2} },
+{ 0x0dcb,	16,	{0x0a, 0x90, 0x7f, 0x94, 0xe0, 0x44, 0x08, 0xf0, 0xe5, 0x72, 0x60, 0x0b, 0xd2, 0x0a, 0xd2, 0x0b} },
+{ 0x0ddb,	16,	{0x90, 0x7f, 0x94, 0xe0, 0x44, 0x08, 0xf0, 0xe5, 0x73, 0x60, 0x17, 0xc2, 0xaf, 0xe4, 0x90, 0x7f} },
+{ 0x0deb,	16,	{0xcb, 0xf0, 0x90, 0x7f, 0xcd, 0xf0, 0xc2, 0x0f, 0xd2, 0x03, 0xc2, 0x13, 0xf5, 0x7d, 0xf5, 0x4b} },
+{ 0x0dfb,	16,	{0xd2, 0xaf, 0xe5, 0x74, 0x60, 0x05, 0x30, 0x23, 0x02, 0xd2, 0x0a, 0xe5, 0x75, 0x60, 0x15, 0x90} },
+{ 0x0e0b,	16,	{0x7f, 0x94, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x7f, 0x9d, 0xe0, 0x44, 0x08, 0xf0, 0x90, 0x7f, 0x97} },
+{ 0x0e1b,	16,	{0xe0, 0x54, 0xf7, 0xf0, 0xe5, 0x76, 0x60, 0x0a, 0xd2, 0xc4, 0xc2, 0xc0, 0x75, 0x3a, 0x01, 0x75} },
+{ 0x0e2b,	16,	{0x41, 0x28, 0xe5, 0x77, 0x60, 0x07, 0xc2, 0xc4, 0xe4, 0xf5, 0x49, 0xf5, 0x3a, 0xe5, 0x78, 0x60} },
+{ 0x0e3b,	16,	{0x03, 0xe4, 0xf5, 0x49, 0xe5, 0x79, 0x60, 0x02, 0xd2, 0x05, 0xe5, 0x7a, 0x60, 0x08, 0xe5, 0x76} },
+{ 0x0e4b,	16,	{0x70, 0x02, 0xf5, 0x41, 0xd2, 0x0b, 0xe5, 0x7b, 0x60, 0x15, 0x90, 0x7f, 0xd7, 0x74, 0x13, 0xf0} },
+{ 0x0e5b,	16,	{0x74, 0x33, 0xf0, 0x74, 0x14, 0xf0, 0x74, 0x34, 0xf0, 0x74, 0x16, 0xf0, 0x74, 0x36, 0xf0, 0xd2} },
+{ 0x0e6b,	16,	{0x07, 0xe4, 0x90, 0x7f, 0xd1, 0xf0, 0xa2, 0x11, 0xe4, 0x33, 0xff, 0x65, 0x37, 0x60, 0x04, 0x8f} },
+{ 0x0e7b,	16,	{0x37, 0xd2, 0x0b, 0xa2, 0x0a, 0xe4, 0x33, 0xff, 0x65, 0x38, 0x60, 0x04, 0x8f, 0x38, 0xd2, 0x0b} },
+{ 0x0e8b,	16,	{0x90, 0x7f, 0x9a, 0xe0, 0x54, 0x20, 0x65, 0x33, 0x60, 0x07, 0xe0, 0x54, 0x20, 0xf5, 0x33, 0xd2} },
+{ 0x0e9b,	16,	{0x0b, 0x90, 0x7f, 0x9a, 0xe0, 0x54, 0x40, 0xb5, 0x35, 0x09, 0xe0, 0x54, 0x40, 0x64, 0x40, 0xf5} },
+{ 0x0eab,	16,	{0x35, 0xd2, 0x0b, 0x30, 0x05, 0x35, 0xc2, 0xaf, 0x30, 0x01, 0x18, 0x90, 0x7f, 0xbc, 0xe0, 0x20} },
+{ 0x0ebb,	16,	{0xe1, 0x27, 0xe5, 0x49, 0x60, 0x09, 0x90, 0x7f, 0xbb, 0xf0, 0xe4, 0xf5, 0x49, 0xc2, 0x01, 0xc2} },
+{ 0x0ecb,	16,	{0x05, 0x80, 0x16, 0x90, 0x7f, 0xba, 0xe0, 0x20, 0xe1, 0x0f, 0xe5, 0x49, 0x60, 0x09, 0x90, 0x7f} },
+{ 0x0edb,	16,	{0xbd, 0xf0, 0xe4, 0xf5, 0x49, 0xd2, 0x01, 0xc2, 0x05, 0xd2, 0xaf, 0x30, 0x1f, 0x37, 0x30, 0x03} },
+{ 0x0eeb,	16,	{0x1b, 0x90, 0x7f, 0xca, 0xe0, 0x20, 0xe1, 0x2d, 0x90, 0x7d, 0x40, 0xe0, 0x13, 0x92, 0x13, 0x75} },
+{ 0x0efb,	16,	{0x4b, 0x01, 0x90, 0x7f, 0xcb, 0xe0, 0xf5, 0x7d, 0xc2, 0x1f, 0x80, 0x19, 0x90, 0x7f, 0xcc, 0xe0} },
+{ 0x0f0b,	16,	{0x20, 0xe1, 0x12, 0x90, 0x7c, 0xc0, 0xe0, 0x13, 0x92, 0x13, 0x75, 0x4b, 0x01, 0x90, 0x7f, 0xcd} },
+{ 0x0f1b,	16,	{0xe0, 0xf5, 0x7d, 0xc2, 0x1f, 0x30, 0x21, 0x03, 0x02, 0x10, 0x28, 0x30, 0x1f, 0x03, 0x02, 0x10} },
+{ 0x0f2b,	16,	{0x28, 0x30, 0x1b, 0x0a, 0x90, 0x7f, 0x9a, 0xe0, 0x20, 0xe5, 0x03, 0x02, 0x10, 0x28, 0x30, 0x0a} },
+{ 0x0f3b,	16,	{0x03, 0x02, 0x10, 0x28, 0x30, 0x11, 0x03, 0x02, 0x10, 0x28, 0x30, 0x03, 0x62, 0x30, 0x1a, 0x12} },
+{ 0x0f4b,	16,	{0xaf, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13} },
+{ 0x0f5b,	16,	{0x92, 0x2c, 0xaf, 0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83} },
+{ 0x0f6b,	16,	{0xe0, 0xf5, 0x13, 0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x50, 0x2a, 0x30, 0x1a, 0x12, 0xaf, 0x4b, 0x05} },
+{ 0x0f7b,	16,	{0x4b, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x15, 0xaf} },
+{ 0x0f8b,	16,	{0x4b, 0x05, 0x4b, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xf5, 0x47} },
+{ 0x0f9b,	16,	{0xd2, 0x0f, 0x80, 0x6b, 0xc2, 0x0f, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0xc2, 0x03, 0x80, 0x60, 0x30} },
+{ 0x0fab,	16,	{0x1a, 0x12, 0xaf, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83} },
+{ 0x0fbb,	16,	{0xe0, 0x13, 0x92, 0x2c, 0xaf, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7c} },
+{ 0x0fcb,	16,	{0xf5, 0x83, 0xe0, 0xf5, 0x13, 0xe5, 0x4b, 0xc3, 0x95, 0x7d, 0x50, 0x2a, 0x30, 0x1a, 0x12, 0xaf} },
+{ 0x0fdb,	16,	{0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0x13, 0x92} },
+{ 0x0feb,	16,	{0x15, 0xaf, 0x4b, 0x05, 0x4b, 0x74, 0xc0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0} },
+{ 0x0ffb,	16,	{0xf5, 0x47, 0xd2, 0x0f, 0x80, 0x09, 0xc2, 0x0f, 0xe4, 0x90, 0x7f, 0xcd, 0xf0, 0xd2, 0x03, 0x30} },
+{ 0x100b,	16,	{0x1a, 0x04, 0xa2, 0x2c, 0x92, 0xc3, 0xd2, 0x21, 0xc2, 0xaf, 0x85, 0x13, 0xc1, 0x20, 0x0f, 0x0b} },
+{ 0x101b,	16,	{0x30, 0x13, 0x08, 0xc2, 0x13, 0x90, 0x7f, 0xc1, 0x74, 0x01, 0xf0, 0xd2, 0xaf, 0x90, 0x7f, 0xc2} },
+{ 0x102b,	16,	{0xe0, 0x30, 0xe1, 0x03, 0x02, 0x10, 0xfe, 0xe5, 0x1a, 0x70, 0x46, 0x30, 0x09, 0x3f, 0xe5, 0x40} },
+{ 0x103b,	16,	{0x70, 0x3b, 0xa2, 0x06, 0x33, 0xf5, 0x31, 0xc2, 0x06, 0xc2, 0x09, 0xe4, 0xf5, 0x12, 0x7e, 0x00} },
+{ 0x104b,	16,	{0x7b, 0x00, 0x74, 0x26, 0x25, 0x12, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x15, 0x0f, 0xff, 0x74} },
+{ 0x105b,	16,	{0x80, 0x25, 0x12, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x12, 0xe5, 0x12} },
+{ 0x106b,	16,	{0xb4, 0x0c, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x0c, 0xf0, 0x75, 0x40, 0x10, 0x22, 0x75, 0x1a, 0x01} },
+{ 0x107b,	16,	{0x22, 0xe5, 0x1a, 0x64, 0x01, 0x70, 0x45, 0x30, 0x0b, 0x3e, 0xe5, 0x41, 0x70, 0x3a, 0xa2, 0x07} },
+{ 0x108b,	16,	{0x33, 0xf5, 0x3d, 0xc2, 0x07, 0xc2, 0x0b, 0xe4, 0xf5, 0x12, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x32} },
+{ 0x109b,	16,	{0x25, 0x12, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x15, 0x0f, 0xff, 0x74, 0x80, 0x25, 0x12, 0xf5} },
+{ 0x10ab,	16,	{0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x12, 0xe5, 0x12, 0xb4, 0x0c, 0xdb, 0x90} },
+{ 0x10bb,	16,	{0x7f, 0xc3, 0x74, 0x0c, 0xf0, 0x75, 0x41, 0x10, 0x75, 0x1a, 0x02, 0x22, 0xe5, 0x1c, 0x60, 0x30} },
+{ 0x10cb,	16,	{0x15, 0x1c, 0xe4, 0xf5, 0x12, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x1b, 0x25, 0x12, 0xf9, 0xee, 0x34} },
+{ 0x10db,	16,	{0x00, 0xfa, 0x12, 0x15, 0x0f, 0xff, 0x74, 0x80, 0x25, 0x12, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5} },
+{ 0x10eb,	16,	{0x83, 0xef, 0xf0, 0x05, 0x12, 0xe5, 0x12, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0} },
+{ 0x10fb,	3,	{0xe4, 0xf5, 0x1a} },
+{ 0x10fe,	1,	{0x22} },
+{ 0x10ff,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x15, 0x9f, 0x11, 0xc4, 0x00, 0x12, 0x38, 0x01, 0x12, 0xa4, 0x03} },
+{ 0x110f,	16,	{0x11, 0x22, 0x06, 0x11, 0xb7, 0x08, 0x11, 0xb1, 0x09, 0x11, 0xa4, 0x0a, 0x12, 0xfa, 0x0b, 0x00} },
+{ 0x111f,	16,	{0x00, 0x12, 0xf3, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02} },
+{ 0x112f,	16,	{0x70, 0x69, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x12} },
+{ 0x113f,	16,	{0xfa, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x16, 0xd3, 0x8b, 0x12, 0x8a, 0x13, 0x89, 0x14, 0xea} },
+{ 0x114f,	16,	{0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5} },
+{ 0x115f,	16,	{0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f} },
+{ 0x116f,	16,	{0xea, 0xe0, 0xff, 0x12, 0x17, 0x22, 0x8b, 0x12, 0x8a, 0x13, 0x89, 0x14, 0xea, 0x49, 0x60, 0x11} },
+{ 0x117f,	16,	{0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x12} },
+{ 0x118f,	16,	{0xfa, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x119f,	16,	{0x01, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02} },
+{ 0x11af,	16,	{0x12, 0xfa, 0x12, 0x14, 0x47, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f} },
+{ 0x11bf,	16,	{0xb5, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31} },
+{ 0x11cf,	16,	{0x24, 0x02, 0x70, 0x5b, 0xa2, 0x25, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x29, 0xe4, 0x33} },
+{ 0x11df,	16,	{0x4f, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x12} },
+{ 0x11ef,	16,	{0xfa, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x12} },
+{ 0x11ff,	16,	{0xfa, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07} },
+{ 0x120f,	16,	{0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90} },
+{ 0x121f,	16,	{0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x12, 0xfa, 0x90} },
+{ 0x122f,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60} },
+{ 0x123f,	16,	{0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2} },
+{ 0x124f,	16,	{0x25, 0x02, 0x12, 0xfa, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x12, 0xfa, 0x90, 0x7f} },
+{ 0x125f,	16,	{0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff} },
+{ 0x126f,	16,	{0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4} },
+{ 0x127f,	16,	{0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54} },
+{ 0x128f,	16,	{0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x129f,	16,	{0x44, 0x01, 0xf0, 0x80, 0x56, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70} },
+{ 0x12af,	16,	{0x4a, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04, 0xd2, 0x25, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x12bf,	16,	{0x44, 0x01, 0xf0, 0x80, 0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4} },
+{ 0x12cf,	16,	{0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5} },
+{ 0x12df,	16,	{0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x12ef,	16,	{0x01, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x12ff,	2,	{0x02, 0xf0} },
+{ 0x1301,	1,	{0x22} },
+{ 0x1302,	16,	{0x20, 0x27, 0x03, 0x02, 0x14, 0x46, 0xe5, 0x40, 0x60, 0x02, 0x15, 0x40, 0xe5, 0x48, 0x60, 0x4f} },
+{ 0x1312,	16,	{0x65, 0x44, 0x70, 0x45, 0xe5, 0x42, 0xf4, 0x60, 0x02, 0x05, 0x42, 0xe5, 0x42, 0xc3, 0x95, 0x55} },
+{ 0x1322,	16,	{0x40, 0x3d, 0xc2, 0xaf, 0x30, 0x00, 0x18, 0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x27, 0x90, 0x7f} },
+{ 0x1332,	16,	{0xb7, 0xe5, 0x48, 0xf0, 0xc2, 0x00, 0xe4, 0xf5, 0x48, 0xf5, 0x42, 0xf5, 0x44, 0x80, 0x16, 0x90} },
+{ 0x1342,	16,	{0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x0f, 0x90, 0x7f, 0xb9, 0xe5, 0x48, 0xf0, 0xd2, 0x00, 0xe4, 0xf5} },
+{ 0x1352,	16,	{0x48, 0xf5, 0x42, 0xf5, 0x44, 0xd2, 0xaf, 0x80, 0x06, 0x85, 0x48, 0x44, 0xe4, 0xf5, 0x42, 0xe5} },
+{ 0x1362,	16,	{0x2e, 0x60, 0x2d, 0x20, 0x16, 0x07, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe0, 0x0e, 0xe5, 0x2f, 0x60} },
+{ 0x1372,	16,	{0x05, 0xe4, 0xf5, 0x2f, 0xd2, 0x09, 0xe4, 0xf5, 0x3e, 0x80, 0x13, 0xe5, 0x3e, 0xd3, 0x95, 0x56} },
+{ 0x1382,	16,	{0x50, 0x0c, 0xe5, 0x3e, 0xb5, 0x56, 0x05, 0x75, 0x2f, 0x01, 0xd2, 0x09, 0x05, 0x3e, 0xc2, 0x16} },
+{ 0x1392,	16,	{0xe5, 0x41, 0x60, 0x02, 0x15, 0x41, 0xe5, 0x49, 0x60, 0x4f, 0x65, 0x45, 0x70, 0x45, 0xe5, 0x43} },
+{ 0x13a2,	16,	{0xf4, 0x60, 0x02, 0x05, 0x43, 0xe5, 0x43, 0xc3, 0x95, 0x6d, 0x40, 0x3d, 0xc2, 0xaf, 0x30, 0x01} },
+{ 0x13b2,	16,	{0x18, 0x90, 0x7f, 0xbc, 0xe0, 0x20, 0xe1, 0x27, 0x90, 0x7f, 0xbb, 0xe5, 0x49, 0xf0, 0xc2, 0x01} },
+{ 0x13c2,	16,	{0xe4, 0xf5, 0x49, 0xf5, 0x43, 0xf5, 0x45, 0x80, 0x16, 0x90, 0x7f, 0xba, 0xe0, 0x20, 0xe1, 0x0f} },
+{ 0x13d2,	16,	{0x90, 0x7f, 0xbd, 0xe5, 0x49, 0xf0, 0xd2, 0x01, 0xe4, 0xf5, 0x49, 0xf5, 0x43, 0xf5, 0x45, 0xd2} },
+{ 0x13e2,	16,	{0xaf, 0x80, 0x06, 0x85, 0x49, 0x45, 0xe4, 0xf5, 0x43, 0xe5, 0x3a, 0x60, 0x2d, 0x20, 0x17, 0x07} },
+{ 0x13f2,	16,	{0x90, 0x7f, 0x9a, 0xe0, 0x30, 0xe2, 0x0e, 0xe5, 0x3b, 0x60, 0x05, 0xe4, 0xf5, 0x3b, 0xd2, 0x0b} },
+{ 0x1402,	16,	{0xe4, 0xf5, 0x3f, 0x80, 0x13, 0xe5, 0x3f, 0xd3, 0x95, 0x6e, 0x50, 0x0c, 0xe5, 0x3f, 0xb5, 0x6e} },
+{ 0x1412,	16,	{0x05, 0x75, 0x3b, 0x01, 0xd2, 0x0b, 0x05, 0x3f, 0xc2, 0x17, 0x90, 0x7f, 0xd2, 0xe0, 0x20, 0xe1} },
+{ 0x1422,	16,	{0x23, 0x90, 0x7b, 0x40, 0xe0, 0x60, 0x09, 0xe0, 0xf5, 0x1c, 0x90, 0x7b, 0x42, 0xe0, 0xf5, 0x1d} },
+{ 0x1432,	16,	{0x90, 0x7b, 0x41, 0xe0, 0x60, 0x09, 0x90, 0x7f, 0xd7, 0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0, 0xe4} },
+{ 0x1442,	5,	{0x90, 0x7f, 0xd3, 0xf0, 0x22} },
+{ 0x1447,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0x90, 0x7f, 0x96, 0xe0, 0x44} },
+{ 0x1457,	16,	{0x10, 0xf0, 0x90, 0x7f, 0x94, 0x74, 0x0d, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0x9a, 0xf0, 0x90, 0x7f} },
+{ 0x1467,	16,	{0x97, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0x23, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x84} },
+{ 0x1477,	16,	{0xf0, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x90, 0x7f, 0xcf, 0xf0, 0x75, 0x98} },
+{ 0x1487,	16,	{0x40, 0x43, 0xa8, 0x10, 0xc2, 0x18, 0xc2, 0x20, 0xc2, 0x08, 0xc2, 0x10, 0xf5, 0x7c, 0xf5, 0x4a} },
+{ 0x1497,	16,	{0xc2, 0x0e, 0xc2, 0x12, 0xf5, 0x42, 0xc2, 0x16, 0xf5, 0x44, 0xf5, 0x48, 0xc2, 0x22, 0xc2, 0x19} },
+{ 0x14a7,	16,	{0xf5, 0x2d, 0xf5, 0x2f, 0xc2, 0x04, 0xc2, 0x1c, 0xf5, 0x3e, 0xc2, 0x06, 0xd2, 0x00, 0xd2, 0x02} },
+{ 0x14b7,	16,	{0xd2, 0x09, 0xd2, 0x1e, 0xf5, 0x26, 0x90, 0x7f, 0xcb, 0xf0, 0x90, 0x7f, 0xcd, 0xf0, 0x90, 0x7f} },
+{ 0x14c7,	16,	{0xd1, 0xf0, 0x75, 0xc0, 0x40, 0x43, 0xa8, 0x40, 0xc2, 0x1a, 0xc2, 0x21, 0xc2, 0x0a, 0xc2, 0x11} },
+{ 0x14d7,	16,	{0xf5, 0x7d, 0xf5, 0x4b, 0xc2, 0x0f, 0xc2, 0x13, 0xf5, 0x43, 0xc2, 0x17, 0xf5, 0x45, 0xf5, 0x49} },
+{ 0x14e7,	16,	{0xc2, 0x23, 0xc2, 0x1b, 0xf5, 0x39, 0xf5, 0x3b, 0xc2, 0x05, 0xc2, 0x1d, 0xf5, 0x3f, 0xc2, 0x07} },
+{ 0x14f7,	16,	{0xd2, 0x01, 0xd2, 0x03, 0xd2, 0x0b, 0xd2, 0x1f, 0x75, 0x32, 0x01, 0x90, 0x7f, 0xdf, 0x74, 0xff} },
+{ 0x1507,	8,	{0xf0, 0x90, 0x7f, 0xde, 0xf0, 0xd2, 0x27, 0x22} },
+{ 0x150f,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x151f,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x1528,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x1538,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x1548,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x1555,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} },
+{ 0x1565,	2,	{0xf3, 0x22} },
+{ 0x1567,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x1577,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x1587,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x1597,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x159f,	16,	{0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} },
+{ 0x15af,	16,	{0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} },
+{ 0x15bf,	6,	{0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} },
+{ 0x15c5,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x7d, 0x02, 0x16, 0x0c} },
+{ 0x15d1,	16,	{0x02, 0x16, 0x51, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x15e1,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x15f1,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x1601,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x18, 0x9e, 0xe4, 0x7e} },
+{ 0x1611,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x1621,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x1631,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x1641,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x1651,	16,	{0x75, 0x11, 0x01, 0x90, 0x7f, 0x92, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0xff, 0xd3} },
+{ 0x1661,	16,	{0x92, 0x25, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde} },
+{ 0x1671,	16,	{0x74, 0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9} },
+{ 0x1681,	16,	{0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90} },
+{ 0x1691,	16,	{0x7f, 0xae, 0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0xbc, 0xd2, 0xbe, 0xd2, 0x2c, 0x12, 0x18} },
+{ 0x16a1,	16,	{0x58, 0xc2, 0x26, 0xc2, 0x24, 0xc2, 0x27, 0x30, 0x27, 0x03, 0x12, 0x0a, 0x6b, 0x90, 0x7f, 0xd8} },
+{ 0x16b1,	16,	{0xe0, 0x65, 0x10, 0x60, 0x08, 0xe0, 0xf5, 0x10, 0x12, 0x13, 0x02, 0x80, 0xea, 0x30, 0x26, 0x07} },
+{ 0x16c1,	16,	{0xc2, 0x26, 0x12, 0x10, 0xff, 0x80, 0xe0, 0x30, 0x2a, 0xdd, 0xc2, 0x2a, 0x12, 0x00, 0x26, 0x80} },
+{ 0x16d1,	1,	{0xd6} },
+{ 0x16d2,	1,	{0x22} },
+{ 0x16d3,	16,	{0xe4, 0xfe, 0x75, 0x17, 0xff, 0x75, 0x18, 0x19, 0x75, 0x19, 0x12, 0xab, 0x17, 0xaa, 0x18, 0xa9} },
+{ 0x16e3,	16,	{0x19, 0x90, 0x00, 0x01, 0x12, 0x15, 0x28, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x16f3,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x15, 0x67, 0x85, 0xf0, 0x15, 0xf5, 0x16, 0x62, 0x15} },
+{ 0x1703,	16,	{0xe5, 0x15, 0x62, 0x16, 0xe5, 0x16, 0x62, 0x15, 0x29, 0xfd, 0xe5, 0x15, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x1713,	14,	{0x17, 0xff, 0xf5, 0x18, 0x89, 0x19, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x1721,	1,	{0x22} },
+{ 0x1722,	2,	{0x8f, 0x15} },
+{ 0x1724,	16,	{0xe4, 0xf5, 0x16, 0x75, 0x17, 0xff, 0x75, 0x18, 0x19, 0x75, 0x19, 0x86, 0xab, 0x17, 0xaa, 0x18} },
+{ 0x1734,	16,	{0xa9, 0x19, 0x90, 0x00, 0x01, 0x12, 0x15, 0x28, 0xb4, 0x03, 0x1d, 0xaf, 0x16, 0x05, 0x16, 0xef} },
+{ 0x1744,	16,	{0xb5, 0x15, 0x01, 0x22, 0x12, 0x15, 0x0f, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x1754,	14,	{0x17, 0xff, 0xf5, 0x18, 0x89, 0x19, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x1762,	1,	{0x22} },
+{ 0x1763,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} },
+{ 0x1773,	16,	{0x2b, 0x04, 0xc2, 0x2b, 0x80, 0x02, 0xd2, 0x2a, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x1783,	14,	{0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1791,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0xf0, 0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x9d} },
+{ 0x17a1,	16,	{0x74, 0x02, 0xf0, 0x90, 0x7f, 0x97, 0xf0, 0xe4, 0x90, 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74} },
+{ 0x17b1,	12,	{0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x98, 0xf0, 0x90, 0x7f, 0x9d, 0xf0, 0x22} },
+{ 0x17bd,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} },
+{ 0x17cd,	16,	{0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} },
+{ 0x17dd,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x17e7,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} },
+{ 0x17f7,	16,	{0x26, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} },
+{ 0x1807,	7,	{0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x180e,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x181e,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x182e,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1833,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x1843,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x1853,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1858,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x2c, 0x04, 0xe0, 0x44} },
+{ 0x1868,	16,	{0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x00, 0x06, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x1878,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x187d,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x188d,	1,	{0x22} },
+{ 0x188e,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x189e,	16,	{0x01, 0x1a, 0x00, 0x03, 0x1b, 0x03, 0x00, 0x00, 0xc1, 0x26, 0xc1, 0x2a, 0xc1, 0x25, 0xc1, 0xa9} },
+{ 0x18ae,	2,	{0xc1, 0xab} },
+{ 0x18b0,	1,	{0x00} },
+{ 0x1900,	16,	{0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x02} },
+{ 0x1910,	16,	{0x00, 0x01, 0x09, 0x02, 0x74, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e} },
+{ 0x1920,	16,	{0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1930,	16,	{0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00} },
+{ 0x1940,	16,	{0x07, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x1950,	16,	{0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x82, 0x02} },
+{ 0x1960,	16,	{0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00} },
+{ 0x1970,	16,	{0x00, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x07} },
+{ 0x1980,	16,	{0x05, 0x87, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00} },
+{ 0x1990,	16,	{0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00} },
+{ 0x19a0,	16,	{0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00} },
+{ 0x19b0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00} },
+{ 0x19c0,	16,	{0x6f, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} },
+{ 0x19d0,	16,	{0x2e, 0x00, 0x36, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00} },
+{ 0x19e0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00} },
+{ 0x19f0,	16,	{0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x61, 0x00} },
+{ 0x1a00,	10,	{0x70, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00} },
+{ 0x1b00,	16,	{0x02, 0x17, 0xe7, 0x00, 0x02, 0x18, 0x0e, 0x00, 0x02, 0x17, 0xbd, 0x00, 0x02, 0x17, 0x63, 0x00} },
+{ 0x1b10,	3,	{0x02, 0x18, 0x33} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa28msg.h linux.18p17/drivers/usb/serial/keyspan_usa28msg.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa28msg.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa28msg.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,212 @@
+/*
+	usa28msg.h
+
+	Copyright (c) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Firmware to run on Anchor EZ-USB
+
+	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 this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	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 InnoSys Incorprated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``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 OR CONTRIBUTORS 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.    
+
+	Note: these message formats are common to USA18, USA19, and USA28;
+	(for USA28X, see usa26msg.h)
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USA28, transmit) messages contain a 
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the 
+	completion of transmit; 0x00 otherwise), followed by data.
+	If the port is configured for parity, the data will be an 
+	alternating string of parity and data bytes, so the message
+	format will be:
+
+		RQSTACK PAR DAT PAR DAT ...
+
+	so the maximum length is 63 bytes (1 + 62, or 31 data bytes);
+	always an odd number for the total message length.
+
+	If there is no parity, the format is simply:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of 63.
+
+	USB IN (USA28 -> host, receive) messages contain data and parity
+	if parity is configred, thusly:
+	
+		DAT PAR DAT PAR DAT PAR ...
+
+	for a total of 32 data bytes;
+	
+	If parity is not configured, the format is:
+
+		DAT DAT DAT ...
+
+	for a total of 64 data bytes.
+
+	In the TX messages (USB OUT), the 0x01 bit of the PARity byte is 
+	the parity bit.  In the RX messages (USB IN), the PARity byte is 
+	the content of the 8051's status register; the parity bit 
+	(RX_PARITY_BIT) is the 0x04 bit.
+
+	revision history:
+
+	1999may06	add resetDataToggle to control message
+	2000mar21	add rs232invalid to status response message
+	2000apr04	add 230.4Kb definition to setBaudRate
+	2000apr13	add/remove loopbackMode switch
+	2000apr13	change definition of setBaudRate to cover 115.2Kb, too
+	2000jun01	add extended BSD-style copyright text
+*/
+
+#ifndef	__USA28MSG__
+#define	__USA28MSG__
+
+/*#ifndef STUBS
+#include "datadefs.h"
+#endif*/
+
+typedef struct txAckMessage
+{
+	u8	dummy;
+} txAckMessage;
+
+typedef struct portControlMessage
+{
+	/*
+		there are four types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the USA28):
+	*/
+	u8	setBaudRate,	// 0=don't set, 1=baudLo/Hi, 2=115.2K, 3=230.4K
+		baudLo,			// host does baud divisor calculation
+		baudHi;			// baudHi is only used for first port (gives lower rates)
+
+	/*
+		2.	configuration changes which are done every time (because it's
+			hardly more trouble to do them than to check whether to do them):
+	*/
+	u8	parity,			// 1=use parity, 0=don't
+		ctsFlowControl,	// 1=use CTS flow control, 0=don't
+		xonFlowControl,	// 1=use XON/XOFF flow control, 0=don't
+		rts,			// 1=on, 0=off
+		dtr;			// 1=on, 0=off
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be correct in every host message).
+	*/
+	u8	forwardingLength,  // forward when this number of chars available
+		forwardMs,		// forward this many ms after last rx data
+		breakThreshold,	// specified in ms, 1-255 (see note below)
+		xonChar,		// specified in current character format
+		xoffChar;		// specified in current character format
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// enable transmitting (and continue if there's data)
+		_txOff,			// stop transmitting
+		txFlush,		// toss outbound data
+		txForceXoff,	// pretend we've received XOFF
+		txBreak,		// turn on break (leave on until txOn clears it)
+		rxOn,			// turn on receiver
+		rxOff,			// turn off receiver
+		rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW
+		returnStatus,	// return current status n times (1 or 2)
+		resetDataToggle;// reset data toggle state to DATA0
+	
+} portControlMessage;
+
+typedef struct portStatusMessage
+{
+	u8	port,			// 0=first, 1=second, 2=global (see below)
+		cts,
+		dsr,			// (not used in all products)
+		dcd,
+
+		ri,				// (not used in all products)
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		dataLost,		// count of lost chars; wraps; not guaranteed exact
+
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		rxBreak,		// 1=we're in break state
+		rs232invalid,	// 1=no valid signals on rs-232 inputs
+		controlResponse;// 1=a control messages has been processed
+} portStatusMessage;
+
+// bit defines in txState
+#define	TX_OFF			0x01	// requested by host txOff command
+#define	TX_XOFF			0x02	// either real, or simulated by host
+
+typedef struct globalControlMessage
+{
+	u8	sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+} globalControlMessage;
+
+typedef struct globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+} globalStatusMessage;
+
+typedef struct globalDebugMessage
+{
+	u8	port,				// 2
+		n,					// typically a count/status byte
+		b;					// typically a data byte
+} globalDebugMessage;
+
+// ie: the maximum length of an EZUSB endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// the parity bytes have only one significant bit
+#define	RX_PARITY_BIT			0x04
+#define	TX_PARITY_BIT			0x01
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/keyspan_usa28x_fw.h linux.18p17/drivers/usb/serial/keyspan_usa28x_fw.h
--- linux.vanilla/drivers/usb/serial/keyspan_usa28x_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/keyspan_usa28x_fw.h	Fri Sep  8 17:02:03 2000
@@ -0,0 +1,406 @@
+/* keyspan_usa28x_fw.h
+  
+   Generated from Keyspan firmware image Wed Jul  5 09:18:29 2000 EST
+   This firmware is for the Keyspan USA-28X Serial Adaptor
+
+   "The firmware contained herein as keyspan_usa28x_fw.h is
+   Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated
+   ("Keyspan"), as an unpublished work.  This notice does not imply
+   unrestricted or public access to this firmware which is a trade secret of
+   Keyspan, and which may not be reproduced, used, sold or transferred to any
+   third party without Keyspan's prior written consent.  All Rights Reserved.
+
+   This firmware may not be modified and may only be used with the Keyspan 
+   USA-28X Serial Adapter.  Distribution and/or Modification of the
+   keyspan.c driver which includes this firmware, in whole or in part,
+   requires the inclusion of this statement."
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa28x_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x0e, 0x9b} },
+{ 0x0003,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0013,	16,	{0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90} },
+{ 0x0023,	15,	{0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x07, 0xf0, 0xe4, 0x90, 0x7f, 0x98, 0xf0, 0x22} },
+{ 0x0033,	3,	{0x02, 0x13, 0xfc} },
+{ 0x0036,	12,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x0043,	3,	{0x02, 0x14, 0x00} },
+{ 0x0046,	16,	{0x30, 0x08, 0x13, 0x12, 0x13, 0xc0, 0xef, 0xc3, 0x95, 0x3c, 0x50, 0x73, 0x90, 0x7f, 0xbf, 0x74} },
+{ 0x0056,	16,	{0x01, 0xf0, 0xc2, 0x08, 0x80, 0x69, 0x30, 0x02, 0x34, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x5f} },
+{ 0x0066,	16,	{0x12, 0x13, 0xc0, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x56, 0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92, 0x08} },
+{ 0x0076,	16,	{0x90, 0x7f, 0xc7, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7e, 0x7f, 0x41, 0x75, 0x0c, 0x7e, 0x75, 0x0d} },
+{ 0x0086,	16,	{0x41, 0x12, 0x0d, 0x55, 0xc2, 0x02, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x80, 0x32, 0x90, 0x7f, 0xc8} },
+{ 0x0096,	16,	{0xe0, 0x20, 0xe1, 0x2b, 0x12, 0x13, 0xc0, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x22, 0x90, 0x7d, 0xc0} },
+{ 0x00a6,	16,	{0xe0, 0x13, 0x92, 0x08, 0x90, 0x7f, 0xc9, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7d, 0x7f, 0xc1, 0x75} },
+{ 0x00b6,	16,	{0x0c, 0x7d, 0x75, 0x0d, 0xc1, 0x12, 0x0d, 0x55, 0xd2, 0x02, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0x90} },
+{ 0x00c6,	16,	{0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x01, 0x53, 0x53, 0x36, 0x80, 0x12, 0x13, 0xcc, 0xef} },
+{ 0x00d6,	16,	{0x42, 0x36, 0x12, 0x12, 0x49, 0x8f, 0x19, 0xef, 0xc3, 0x95, 0x3a, 0x50, 0x0f, 0x12, 0x13, 0xa8} },
+{ 0x00e6,	16,	{0xef, 0x30, 0xe0, 0x08, 0xe5, 0x36, 0x20, 0xe7, 0x03, 0x30, 0x0a, 0x61, 0xc2, 0x0a, 0xe5, 0x19} },
+{ 0x00f6,	16,	{0x70, 0x04, 0xf5, 0x36, 0x80, 0x57, 0x12, 0x13, 0xcc, 0xef, 0x42, 0x36, 0xe5, 0x36, 0x30, 0xe7} },
+{ 0x0106,	16,	{0x26, 0xe5, 0x19, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x19, 0x20, 0x85, 0x19, 0x08, 0x7e, 0x7e} },
+{ 0x0116,	16,	{0x7f, 0x80, 0x75, 0x0c, 0x7e, 0x75, 0x0d, 0x80, 0xaf, 0x36, 0x12, 0x0f, 0xb7, 0xe5, 0x19, 0x25} },
+{ 0x0126,	16,	{0xe0, 0x90, 0x7f, 0xb7, 0xf0, 0x80, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x3f, 0x40, 0x03, 0x75, 0x19} },
+{ 0x0136,	16,	{0x3f, 0x85, 0x19, 0x08, 0xe4, 0x90, 0x7e, 0x80, 0xf0, 0x7e, 0x7e, 0x7f, 0x81, 0x75, 0x0c, 0x7e} },
+{ 0x0146,	16,	{0x75, 0x0d, 0x81, 0x12, 0x0d, 0x7a, 0xe5, 0x19, 0x04, 0x90, 0x7f, 0xb7, 0xf0, 0x90, 0x7f, 0xce} },
+{ 0x0156,	16,	{0xe0, 0x30, 0xe1, 0x06, 0x20, 0x04, 0x03, 0x02, 0x03, 0xbd, 0xc2, 0x04, 0xe4, 0xf5, 0x18, 0x74} },
+{ 0x0166,	16,	{0x40, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7c, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0x7c, 0x00} },
+{ 0x0176,	16,	{0x7b, 0x01, 0x7a, 0x7e, 0x79, 0x00, 0x24, 0x00, 0xf9, 0xec, 0x34, 0x7e, 0xfa, 0xef, 0x12, 0x0c} },
+{ 0x0186,	16,	{0xe5, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x20, 0xd7, 0x90, 0x7e, 0x00, 0xe0, 0x60, 0x68, 0x90, 0x7e} },
+{ 0x0196,	16,	{0x03, 0xe0, 0x60, 0x24, 0x7f, 0x01, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0x7f, 0x03, 0x7d, 0xcd, 0x12} },
+{ 0x01a6,	16,	{0x12, 0x24, 0x43, 0x46, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46} },
+{ 0x01b6,	16,	{0xf0, 0xe4, 0x90, 0x7e, 0x13, 0xf0, 0x80, 0x30, 0x90, 0x7e, 0x01, 0xe0, 0xff, 0x12, 0x10, 0xa8} },
+{ 0x01c6,	16,	{0x90, 0x7e, 0x02, 0xe0, 0xff, 0x12, 0x10, 0xce, 0x7f, 0x01, 0x90, 0x7e, 0x11, 0xe0, 0xfd, 0x12} },
+{ 0x01d6,	16,	{0x12, 0x24, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x24, 0x43, 0x46, 0x80, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x01e6,	16,	{0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5, 0x40} },
+{ 0x01f6,	16,	{0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x03, 0xe0, 0x70, 0x06, 0x90, 0x7e, 0x13, 0xe0} },
+{ 0x0206,	16,	{0x70, 0x08, 0xe4, 0x90, 0x7e, 0x13, 0xf0, 0x75, 0x25, 0xff, 0x90, 0x7e, 0x05, 0xe0, 0x60, 0x12} },
+{ 0x0216,	16,	{0xa3, 0xe0, 0x54, 0x3f, 0xf5, 0x44, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe5} },
+{ 0x0226,	16,	{0x44, 0xf0, 0x90, 0x7e, 0x07, 0xe0, 0x60, 0x2b, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x42, 0x80, 0x80} },
+{ 0x0236,	16,	{0x03, 0x53, 0x42, 0x7f, 0x53, 0x42, 0xfc, 0x90, 0x7e, 0x09, 0xe0, 0x60, 0x11, 0x43, 0x42, 0x02} },
+{ 0x0246,	16,	{0xa3, 0xe0, 0xff, 0x12, 0x11, 0x1a, 0x90, 0x7e, 0x0b, 0xe0, 0xff, 0x12, 0x11, 0x40, 0xaf, 0x42} },
+{ 0x0256,	16,	{0x12, 0x10, 0xf4, 0x90, 0x7e, 0x03, 0xe0, 0x60, 0x08, 0x53, 0x42, 0x7f, 0xaf, 0x42, 0x12, 0x10} },
+{ 0x0266,	16,	{0xf4, 0x90, 0x7e, 0x0c, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x46, 0x02, 0x80, 0x03} },
+{ 0x0276,	16,	{0x53, 0x46, 0xfd, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90} },
+{ 0x0286,	16,	{0x7e, 0x0e, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x46, 0x01, 0x80, 0x03, 0x53, 0x46} },
+{ 0x0296,	16,	{0xfe, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7e, 0x12} },
+{ 0x02a6,	16,	{0xe0, 0xf5, 0x3a, 0xa3, 0xe0, 0x13, 0x92, 0x0c, 0xa3, 0xe0, 0xf5, 0x3c, 0xa3, 0xe0, 0x60, 0x05} },
+{ 0x02b6,	16,	{0x43, 0x46, 0x10, 0x80, 0x03, 0x53, 0x46, 0xef, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0} },
+{ 0x02c6,	16,	{0x00, 0xe5, 0x46, 0xf0, 0x90, 0x7e, 0x16, 0xe0, 0x60, 0x32, 0x53, 0x44, 0xbf, 0x90, 0x7f, 0x98} },
+{ 0x02d6,	16,	{0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x11} },
+{ 0x02e6,	16,	{0xf0, 0x12, 0x13, 0x9c, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0x53, 0x3e, 0xfd, 0xe4, 0xff} },
+{ 0x02f6,	16,	{0xad, 0x3e, 0x12, 0x12, 0x24, 0xe4, 0xf5, 0x2a, 0xf5, 0x29, 0xd2, 0x06, 0x90, 0x7e, 0x17, 0xe0} },
+{ 0x0306,	16,	{0x60, 0x0f, 0x43, 0x3e, 0x02, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0x75, 0x29, 0x01, 0xd2} },
+{ 0x0316,	16,	{0x06, 0x90, 0x7e, 0x18, 0xe0, 0x60, 0x1b, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5, 0x40, 0x44} },
+{ 0x0326,	16,	{0x04, 0x90, 0xc0, 0x00, 0xf0, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0xd2, 0x02} },
+{ 0x0336,	16,	{0xc2, 0x08, 0x90, 0x7e, 0x19, 0xe0, 0x60, 0x11, 0x43, 0x44, 0x40, 0x90, 0x7f, 0x98, 0x74, 0x13} },
+{ 0x0346,	16,	{0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1a, 0xe0, 0x60, 0x0f, 0x53} },
+{ 0x0356,	16,	{0x3e, 0xfe, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0x75, 0x2b, 0x01, 0xd2, 0x06, 0x90, 0x7e} },
+{ 0x0366,	16,	{0x1b, 0xe0, 0x60, 0x0f, 0x43, 0x3e, 0x01, 0xe4, 0xff, 0xad, 0x3e, 0x12, 0x12, 0x24, 0xe4, 0xf5} },
+{ 0x0376,	16,	{0x2b, 0xd2, 0x06, 0x90, 0x7e, 0x1c, 0xe0, 0x60, 0x0e, 0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0xe5} },
+{ 0x0386,	16,	{0x40, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x1d, 0xe0, 0x60, 0x02, 0xd2, 0x0a, 0x90} },
+{ 0x0396,	16,	{0x7e, 0x1e, 0xe0, 0x60, 0x08, 0x75, 0x2c, 0x01, 0xe4, 0xf5, 0x38, 0xd2, 0x06, 0x90, 0x7e, 0x1f} },
+{ 0x03a6,	16,	{0xe0, 0x60, 0x0f, 0x90, 0x7f, 0xd7, 0x74, 0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74, 0x15, 0xf0, 0x74} },
+{ 0x03b6,	16,	{0x35, 0xf0, 0xe4, 0x90, 0x7f, 0xcf, 0xf0, 0x30, 0x18, 0x52, 0xe5, 0x38, 0x60, 0x02, 0x15, 0x38} },
+{ 0x03c6,	16,	{0x20, 0x12, 0x49, 0xe5, 0x13, 0xd3, 0x94, 0x00, 0x40, 0x04, 0x15, 0x13, 0x80, 0x3e, 0x75, 0x13} },
+{ 0x03d6,	16,	{0x0a, 0x30, 0x19, 0x02, 0xd2, 0x12, 0x12, 0x13, 0x9c, 0xef, 0x54, 0x01, 0xf5, 0x19, 0x65, 0x2a} },
+{ 0x03e6,	16,	{0x60, 0x05, 0x85, 0x19, 0x2a, 0xd2, 0x06, 0x12, 0x13, 0xd8, 0xef, 0x54, 0x80, 0xf5, 0x19, 0x65} },
+{ 0x03f6,	16,	{0x26, 0x60, 0x05, 0x85, 0x19, 0x26, 0xd2, 0x06, 0x30, 0x0c, 0x11, 0x12, 0x13, 0xd8, 0xef, 0x54} },
+{ 0x0406,	16,	{0x10, 0xf5, 0x19, 0x65, 0x25, 0x60, 0x05, 0x85, 0x19, 0x25, 0xd2, 0x06, 0x20, 0x19, 0x03, 0x02} },
+{ 0x0416,	16,	{0x07, 0xe4, 0x30, 0x09, 0x13, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x95, 0x3d, 0x50, 0x73, 0x90, 0x7f} },
+{ 0x0426,	16,	{0xc1, 0x74, 0x01, 0xf0, 0xc2, 0x09, 0x80, 0x69, 0x30, 0x03, 0x34, 0x90, 0x7f, 0xca, 0xe0, 0x20} },
+{ 0x0436,	16,	{0xe1, 0x5f, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x56, 0x90, 0x7d, 0x40, 0xe0, 0x13} },
+{ 0x0446,	16,	{0x92, 0x09, 0x90, 0x7f, 0xcb, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7d, 0x7f, 0x41, 0x75, 0x0c, 0x7d} },
+{ 0x0456,	16,	{0x75, 0x0d, 0x41, 0x12, 0x0d, 0x9f, 0xc2, 0x03, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0x80, 0x32, 0x90} },
+{ 0x0466,	16,	{0x7f, 0xcc, 0xe0, 0x20, 0xe1, 0x2b, 0x12, 0x14, 0x2b, 0xef, 0xc3, 0x94, 0x40, 0x50, 0x22, 0x90} },
+{ 0x0476,	16,	{0x7c, 0xc0, 0xe0, 0x13, 0x92, 0x09, 0x90, 0x7f, 0xcd, 0xe0, 0x14, 0xf5, 0x08, 0x7e, 0x7c, 0x7f} },
+{ 0x0486,	16,	{0xc1, 0x75, 0x0c, 0x7c, 0x75, 0x0d, 0xc1, 0x12, 0x0d, 0x9f, 0xd2, 0x03, 0xe4, 0x90, 0x7f, 0xcd} },
+{ 0x0496,	16,	{0xf0, 0x90, 0x7f, 0xba, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x05, 0x25, 0x53, 0x37, 0x80, 0x12, 0x14} },
+{ 0x04a6,	16,	{0x37, 0xef, 0x42, 0x37, 0x12, 0x12, 0x93, 0x8f, 0x19, 0xef, 0xc3, 0x95, 0x3b, 0x50, 0x0f, 0x12} },
+{ 0x04b6,	16,	{0x14, 0x13, 0xef, 0x30, 0xe0, 0x08, 0xe5, 0x37, 0x20, 0xe7, 0x03, 0x30, 0x0b, 0x61, 0xc2, 0x0b} },
+{ 0x04c6,	16,	{0xe5, 0x19, 0x70, 0x04, 0xf5, 0x37, 0x80, 0x57, 0x12, 0x14, 0x37, 0xef, 0x42, 0x37, 0xe5, 0x37} },
+{ 0x04d6,	16,	{0x30, 0xe7, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x19, 0x20, 0x85, 0x19, 0x08} },
+{ 0x04e6,	16,	{0x7e, 0x7d, 0x7f, 0x80, 0x75, 0x0c, 0x7d, 0x75, 0x0d, 0x80, 0xaf, 0x37, 0x12, 0x0f, 0xf0, 0xe5} },
+{ 0x04f6,	16,	{0x19, 0x25, 0xe0, 0x90, 0x7f, 0xbb, 0xf0, 0x80, 0x26, 0xe5, 0x19, 0xd3, 0x94, 0x3f, 0x40, 0x03} },
+{ 0x0506,	16,	{0x75, 0x19, 0x3f, 0x85, 0x19, 0x08, 0xe4, 0x90, 0x7d, 0x80, 0xf0, 0x7e, 0x7d, 0x7f, 0x81, 0x75} },
+{ 0x0516,	16,	{0x0c, 0x7d, 0x75, 0x0d, 0x81, 0x12, 0x0d, 0xc4, 0xe5, 0x19, 0x04, 0x90, 0x7f, 0xbb, 0xf0, 0x90} },
+{ 0x0526,	16,	{0x7f, 0xd0, 0xe0, 0x30, 0xe1, 0x06, 0x20, 0x05, 0x03, 0x02, 0x07, 0x8f, 0xc2, 0x05, 0xe4, 0xf5} },
+{ 0x0536,	16,	{0x18, 0x74, 0xc0, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18} },
+{ 0x0546,	16,	{0x7c, 0x00, 0x7b, 0x01, 0x7a, 0x7e, 0x79, 0x20, 0x24, 0x20, 0xf9, 0xec, 0x34, 0x7e, 0xfa, 0xef} },
+{ 0x0556,	16,	{0x12, 0x0c, 0xe5, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x20, 0xd7, 0x90, 0x7e, 0x20, 0xe0, 0x60, 0x68} },
+{ 0x0566,	16,	{0x90, 0x7e, 0x23, 0xe0, 0x60, 0x24, 0x7f, 0x01, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0x7f, 0x03, 0x7d} },
+{ 0x0576,	16,	{0xcd, 0x12, 0x12, 0x6e, 0x43, 0x47, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00} },
+{ 0x0586,	16,	{0xe5, 0x47, 0xf0, 0xe4, 0x90, 0x7e, 0x33, 0xf0, 0x80, 0x30, 0x90, 0x7e, 0x21, 0xe0, 0xff, 0x12} },
+{ 0x0596,	16,	{0x11, 0x8c, 0x90, 0x7e, 0x22, 0xe0, 0xff, 0x12, 0x11, 0xb2, 0x7f, 0x01, 0x90, 0x7e, 0x31, 0xe0} },
+{ 0x05a6,	16,	{0xfd, 0x12, 0x12, 0x6e, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x6e, 0x43, 0x47, 0x80, 0x90, 0x7f} },
+{ 0x05b6,	16,	{0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0} },
+{ 0x05c6,	16,	{0xe5, 0x41, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x23, 0xe0, 0x70, 0x06, 0x90, 0x7e} },
+{ 0x05d6,	16,	{0x33, 0xe0, 0x70, 0x08, 0xe4, 0x90, 0x7e, 0x33, 0xf0, 0x75, 0x2e, 0xff, 0x90, 0x7e, 0x25, 0xe0} },
+{ 0x05e6,	16,	{0x60, 0x12, 0xa3, 0xe0, 0x54, 0x3f, 0xf5, 0x45, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0} },
+{ 0x05f6,	16,	{0x00, 0xe5, 0x45, 0xf0, 0x90, 0x7e, 0x27, 0xe0, 0x60, 0x2b, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x43} },
+{ 0x0606,	16,	{0x80, 0x80, 0x03, 0x53, 0x43, 0x7f, 0x53, 0x43, 0xfc, 0x90, 0x7e, 0x29, 0xe0, 0x60, 0x11, 0x43} },
+{ 0x0616,	16,	{0x43, 0x02, 0xa3, 0xe0, 0xff, 0x12, 0x11, 0xd8, 0x90, 0x7e, 0x2b, 0xe0, 0xff, 0x12, 0x11, 0xfe} },
+{ 0x0626,	16,	{0xaf, 0x43, 0x12, 0x11, 0x66, 0x90, 0x7e, 0x23, 0xe0, 0x60, 0x08, 0x53, 0x43, 0x7f, 0xaf, 0x43} },
+{ 0x0636,	16,	{0x12, 0x11, 0x66, 0x90, 0x7e, 0x2c, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x47, 0x02} },
+{ 0x0646,	16,	{0x80, 0x03, 0x53, 0x47, 0xfd, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47} },
+{ 0x0656,	16,	{0xf0, 0x90, 0x7e, 0x2e, 0xe0, 0x60, 0x18, 0xa3, 0xe0, 0x60, 0x05, 0x43, 0x47, 0x01, 0x80, 0x03} },
+{ 0x0666,	16,	{0x53, 0x47, 0xfe, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90} },
+{ 0x0676,	16,	{0x7e, 0x32, 0xe0, 0xf5, 0x3b, 0xa3, 0xe0, 0x13, 0x92, 0x0d, 0xa3, 0xe0, 0xf5, 0x3d, 0xa3, 0xe0} },
+{ 0x0686,	16,	{0x60, 0x05, 0x43, 0x47, 0x10, 0x80, 0x03, 0x53, 0x47, 0xef, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0} },
+{ 0x0696,	16,	{0x90, 0xc0, 0x00, 0xe5, 0x47, 0xf0, 0x90, 0x7e, 0x36, 0xe0, 0x60, 0x32, 0x53, 0x45, 0xbf, 0x90} },
+{ 0x06a6,	16,	{0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98} },
+{ 0x06b6,	16,	{0x74, 0x09, 0xf0, 0x12, 0x13, 0xf0, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0x53, 0x3f, 0xfd} },
+{ 0x06c6,	16,	{0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0xe4, 0xf5, 0x33, 0xf5, 0x32, 0xd2, 0x07, 0x90, 0x7e} },
+{ 0x06d6,	16,	{0x37, 0xe0, 0x60, 0x0f, 0x43, 0x3f, 0x02, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0x75, 0x32} },
+{ 0x06e6,	16,	{0x01, 0xd2, 0x07, 0x90, 0x7e, 0x38, 0xe0, 0x60, 0x1b, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0xe5} },
+{ 0x06f6,	16,	{0x41, 0x44, 0x04, 0x90, 0xc0, 0x00, 0xf0, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0x90, 0x7f, 0xcd, 0xf0} },
+{ 0x0706,	16,	{0xd2, 0x03, 0xc2, 0x09, 0x90, 0x7e, 0x39, 0xe0, 0x60, 0x11, 0x43, 0x45, 0x40, 0x90, 0x7f, 0x98} },
+{ 0x0716,	16,	{0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x3a, 0xe0, 0x60} },
+{ 0x0726,	16,	{0x0f, 0x53, 0x3f, 0xfe, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e, 0x75, 0x34, 0x01, 0xd2, 0x07} },
+{ 0x0736,	16,	{0x90, 0x7e, 0x3b, 0xe0, 0x60, 0x0f, 0x43, 0x3f, 0x01, 0xe4, 0xff, 0xad, 0x3f, 0x12, 0x12, 0x6e} },
+{ 0x0746,	16,	{0xe4, 0xf5, 0x34, 0xd2, 0x07, 0x90, 0x7e, 0x3c, 0xe0, 0x60, 0x0e, 0x90, 0x7f, 0x98, 0x74, 0x0a} },
+{ 0x0756,	16,	{0xf0, 0xe5, 0x41, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7e, 0x3d, 0xe0, 0x60, 0x02, 0xd2} },
+{ 0x0766,	16,	{0x0b, 0x90, 0x7e, 0x3e, 0xe0, 0x60, 0x08, 0x75, 0x35, 0x01, 0xe4, 0xf5, 0x39, 0xd2, 0x07, 0x90} },
+{ 0x0776,	16,	{0x7e, 0x3f, 0xe0, 0x60, 0x0f, 0x90, 0x7f, 0xd7, 0x74, 0x13, 0xf0, 0x74, 0x33, 0xf0, 0x74, 0x16} },
+{ 0x0786,	16,	{0xf0, 0x74, 0x36, 0xf0, 0xe4, 0x90, 0x7f, 0xd1, 0xf0, 0x30, 0x18, 0x52, 0xe5, 0x39, 0x60, 0x02} },
+{ 0x0796,	16,	{0x15, 0x39, 0x30, 0x12, 0x49, 0xe5, 0x13, 0xd3, 0x94, 0x00, 0x40, 0x04, 0x15, 0x13, 0x80, 0x3e} },
+{ 0x07a6,	16,	{0x75, 0x13, 0x0a, 0x30, 0x19, 0x02, 0xc2, 0x12, 0x12, 0x13, 0xf0, 0xef, 0x54, 0x01, 0xf5, 0x19} },
+{ 0x07b6,	16,	{0x65, 0x33, 0x60, 0x05, 0x85, 0x19, 0x33, 0xd2, 0x07, 0x12, 0x14, 0x43, 0xef, 0x54, 0x80, 0xf5} },
+{ 0x07c6,	16,	{0x19, 0x65, 0x2f, 0x60, 0x05, 0x85, 0x19, 0x2f, 0xd2, 0x07, 0x30, 0x0d, 0x11, 0x12, 0x14, 0x43} },
+{ 0x07d6,	16,	{0xef, 0x54, 0x10, 0xf5, 0x19, 0x65, 0x2e, 0x60, 0x05, 0x85, 0x19, 0x2e, 0xd2, 0x07, 0x30, 0x18} },
+{ 0x07e6,	16,	{0x2a, 0x90, 0x7f, 0xd2, 0xe0, 0x20, 0xe1, 0x23, 0x90, 0x7b, 0x40, 0xe0, 0x60, 0x09, 0xe0, 0xf5} },
+{ 0x07f6,	16,	{0x15, 0x90, 0x7b, 0x42, 0xe0, 0xf5, 0x16, 0x90, 0x7b, 0x41, 0xe0, 0x60, 0x09, 0x90, 0x7f, 0xd7} },
+{ 0x0806,	16,	{0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0, 0xe4, 0x90, 0x7f, 0xd3, 0xf0, 0x90, 0x7f, 0xc2, 0xe0, 0x30} },
+{ 0x0816,	16,	{0xe1, 0x03, 0x02, 0x09, 0x18, 0xe5, 0x0a, 0x70, 0x40, 0x30, 0x06, 0x39, 0xe5, 0x38, 0x70, 0x35} },
+{ 0x0826,	16,	{0xc2, 0x06, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x24, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00} },
+{ 0x0836,	16,	{0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83} },
+{ 0x0846,	16,	{0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x09, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x09, 0xf0, 0x75} },
+{ 0x0856,	16,	{0x38, 0x10, 0xe4, 0xf5, 0x2c, 0x75, 0x0a, 0x01, 0x22, 0xe5, 0x0a, 0x64, 0x01, 0x70, 0x40, 0x30} },
+{ 0x0866,	16,	{0x07, 0x39, 0xe5, 0x39, 0x70, 0x35, 0xc2, 0x07, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x2d} },
+{ 0x0876,	16,	{0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5} },
+{ 0x0886,	16,	{0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x09, 0xdb, 0x90} },
+{ 0x0896,	16,	{0x7f, 0xc3, 0x74, 0x09, 0xf0, 0x75, 0x39, 0x10, 0xe4, 0xf5, 0x35, 0x75, 0x0a, 0x02, 0x22, 0xe5} },
+{ 0x08a6,	16,	{0x0a, 0x64, 0x02, 0x70, 0x36, 0x30, 0x13, 0x2f, 0xc2, 0x13, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00} },
+{ 0x08b6,	16,	{0x74, 0x0e, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25} },
+{ 0x08c6,	16,	{0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x05} },
+{ 0x08d6,	16,	{0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x05, 0xf0, 0x75, 0x0a, 0x03, 0x22, 0xe5, 0x15, 0x60, 0x30, 0x15} },
+{ 0x08e6,	16,	{0x15, 0xe4, 0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x14, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00} },
+{ 0x08f6,	16,	{0xfa, 0x12, 0x0c, 0x9f, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83} },
+{ 0x0906,	16,	{0xef, 0xf0, 0x05, 0x18, 0xe5, 0x18, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0xe4} },
+{ 0x0916,	2,	{0xf5, 0x0a} },
+{ 0x0918,	1,	{0x22} },
+{ 0x0919,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x0d, 0x2f, 0x09, 0xe9, 0x00, 0x0a, 0x5d, 0x01, 0x0a, 0xc9, 0x03} },
+{ 0x0929,	16,	{0x09, 0x3c, 0x06, 0x09, 0xdc, 0x08, 0x09, 0xd6, 0x09, 0x09, 0xbe, 0x0a, 0x09, 0xcd, 0x0b, 0x00} },
+{ 0x0939,	16,	{0x00, 0x0b, 0x18, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02} },
+{ 0x0949,	16,	{0x70, 0x69, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0b} },
+{ 0x0959,	16,	{0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0f, 0x27, 0x8b, 0x18, 0x8a, 0x19, 0x89, 0x1a, 0xea} },
+{ 0x0969,	16,	{0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5} },
+{ 0x0979,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f} },
+{ 0x0989,	16,	{0xea, 0xe0, 0xff, 0x12, 0x0f, 0x76, 0x8b, 0x18, 0x8a, 0x19, 0x89, 0x1a, 0xea, 0x49, 0x60, 0x11} },
+{ 0x0999,	16,	{0xae, 0x02, 0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0b} },
+{ 0x09a9,	16,	{0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x09b9,	16,	{0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0x00, 0xe5, 0x09, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01} },
+{ 0x09c9,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x09, 0x02, 0x0b, 0x1f, 0x12, 0x0b, 0x27} },
+{ 0x09d9,	16,	{0x02, 0x0b, 0x1f, 0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x0b, 0x1f} },
+{ 0x09e9,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} },
+{ 0x09f9,	16,	{0x0f, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x14, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} },
+{ 0x0a09,	16,	{0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0xe4, 0x90, 0x7f, 0x00} },
+{ 0x0a19,	16,	{0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x0a29,	16,	{0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} },
+{ 0x0a39,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} },
+{ 0x0a49,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x0a59,	16,	{0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03} },
+{ 0x0a69,	16,	{0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x0f, 0x02, 0x0b, 0x1f, 0x90} },
+{ 0x0a79,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x0b, 0x1f, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90} },
+{ 0x0a89,	16,	{0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25} },
+{ 0x0a99,	16,	{0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x0aa9,	16,	{0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7} },
+{ 0x0ab9,	16,	{0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x56} },
+{ 0x0ac9,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4a, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x0ad9,	16,	{0xb4, 0x01, 0x04, 0xd2, 0x0f, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x36} },
+{ 0x0ae9,	16,	{0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54} },
+{ 0x0af9,	16,	{0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5} },
+{ 0x0b09,	16,	{0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x07, 0x90} },
+{ 0x0b19,	13,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0b26,	1,	{0x22} },
+{ 0x0b27,	16,	{0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x18, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} },
+{ 0x0b37,	16,	{0x90, 0x7f, 0x95, 0x74, 0xc0, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x0b47,	16,	{0x18, 0xf0, 0xe4, 0xf5, 0x8e, 0x90, 0x7f, 0xdf, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0xe4} },
+{ 0x0b57,	16,	{0xf5, 0x24, 0x75, 0x1b, 0x01, 0x7b, 0x00, 0x74, 0x24, 0x25, 0x1b, 0xf9, 0xe4, 0x34, 0x00, 0xfa} },
+{ 0x0b67,	16,	{0xe4, 0x12, 0x0c, 0xe5, 0x05, 0x1b, 0xe5, 0x1b, 0xb4, 0x09, 0xea, 0x75, 0x3a, 0x01, 0xe4, 0xf5} },
+{ 0x0b77,	16,	{0x38, 0xf5, 0x13, 0xf5, 0x36, 0xc2, 0x06, 0xc2, 0x0a, 0xc2, 0x04, 0xc2, 0x08, 0xc2, 0x12, 0xd2} },
+{ 0x0b87,	16,	{0x02, 0xd2, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x75, 0x44, 0x03, 0x90, 0xc0, 0x00, 0x74} },
+{ 0x0b97,	16,	{0x03, 0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0x7f, 0x10, 0x8f, 0x42, 0x12, 0x10, 0xf4} },
+{ 0x0ba7,	16,	{0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0x7f, 0x01, 0x8f, 0x40, 0xef, 0x44, 0x06, 0x90, 0xc0, 0x00} },
+{ 0x0bb7,	16,	{0xf0, 0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x75, 0x46, 0x80, 0x90, 0xc0, 0x00, 0x74, 0x80, 0xf0} },
+{ 0x0bc7,	16,	{0x0f, 0xe4, 0xfd, 0x12, 0x12, 0x24, 0xe4, 0xff, 0x7e, 0xa3, 0xad, 0x06, 0x8d, 0x3e, 0x12, 0x12} },
+{ 0x0bd7,	16,	{0x24, 0x90, 0x7f, 0x98, 0x74, 0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f} },
+{ 0x0be7,	16,	{0x12, 0x12, 0x24, 0x7f, 0x01, 0x12, 0x13, 0x27, 0x7f, 0x03, 0x7d, 0x07, 0x12, 0x12, 0x24, 0x20} },
+{ 0x0bf7,	16,	{0x19, 0x03, 0x02, 0x0c, 0x9c, 0x75, 0x2d, 0x01, 0x75, 0x1b, 0x01, 0x7b, 0x00, 0x74, 0x2d, 0x25} },
+{ 0x0c07,	16,	{0x1b, 0xf9, 0xe4, 0x34, 0x00, 0xfa, 0xe4, 0x12, 0x0c, 0xe5, 0x05, 0x1b, 0xe5, 0x1b, 0xb4, 0x09} },
+{ 0x0c17,	16,	{0xea, 0x75, 0x3b, 0x01, 0xe4, 0xf5, 0x39, 0xf5, 0x13, 0xf5, 0x37, 0xc2, 0x07, 0xc2, 0x0b, 0xc2} },
+{ 0x0c27,	16,	{0x05, 0xc2, 0x09, 0xc2, 0x12, 0xd2, 0x03, 0xd2, 0x01, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x75} },
+{ 0x0c37,	16,	{0x45, 0x03, 0x90, 0xc0, 0x00, 0x74, 0x03, 0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0x7f} },
+{ 0x0c47,	16,	{0x10, 0x8f, 0x43, 0x12, 0x11, 0x66, 0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0x7f, 0x01, 0x8f, 0x41} },
+{ 0x0c57,	16,	{0xef, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x75, 0x47, 0x80} },
+{ 0x0c67,	16,	{0x90, 0xc0, 0x00, 0x74, 0x80, 0xf0, 0x0f, 0xe4, 0xfd, 0x12, 0x12, 0x6e, 0xe4, 0xff, 0x7e, 0xa3} },
+{ 0x0c77,	16,	{0xad, 0x06, 0x8d, 0x3f, 0x12, 0x12, 0x6e, 0x90, 0x7f, 0x98, 0x74, 0x09, 0xf0, 0x90, 0xc0, 0x00} },
+{ 0x0c87,	16,	{0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f, 0x12, 0x12, 0x6e, 0x7f, 0x01, 0x12, 0x13, 0x48, 0x7f, 0x03} },
+{ 0x0c97,	7,	{0x7d, 0x07, 0x12, 0x12, 0x6e, 0xd2, 0x11} },
+{ 0x0c9e,	1,	{0x22} },
+{ 0x0c9f,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x0caf,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0cb8,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x0cc8,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x0cd8,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0ce5,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} },
+{ 0x0cf5,	2,	{0xf3, 0x22} },
+{ 0x0cf7,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x0d07,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x0d17,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x0d27,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x0d2f,	16,	{0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} },
+{ 0x0d3f,	16,	{0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} },
+{ 0x0d4f,	6,	{0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} },
+{ 0x0d55,	16,	{0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83} },
+{ 0x0d65,	16,	{0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86} },
+{ 0x0d75,	16,	{0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x10, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5} },
+{ 0x0d85,	16,	{0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0} },
+{ 0x0d95,	16,	{0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05, 0x86, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0x98, 0x74, 0x08, 0xf0} },
+{ 0x0da5,	16,	{0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2, 0xaf, 0x05, 0x86, 0x90, 0xc0} },
+{ 0x0db5,	16,	{0x00, 0x05, 0x86, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0x05, 0x86, 0xdf, 0xf7, 0xd2, 0xaf, 0x22, 0x90} },
+{ 0x0dc5,	16,	{0x7f, 0x98, 0x74, 0x08, 0xf0, 0xaf, 0x08, 0xe5, 0x0d, 0xf5, 0x82, 0xe5, 0x0c, 0xf5, 0x83, 0xc2} },
+{ 0x0dd5,	16,	{0xaf, 0x05, 0x86, 0x90, 0xc0, 0x00, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xf7, 0x05} },
+{ 0x0de5,	4,	{0x86, 0xd2, 0xaf, 0x22} },
+{ 0x0de9,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x0df9,	1,	{0x22} },
+{ 0x0dfa,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x0e0a,	16,	{0xd2, 0x17, 0x90, 0x7f, 0x92, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0xff, 0xd3, 0x92} },
+{ 0x0e1a,	16,	{0x0f, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde, 0x74} },
+{ 0x0e2a,	16,	{0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0} },
+{ 0x0e3a,	16,	{0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f} },
+{ 0x0e4a,	16,	{0xae, 0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0x18, 0x12, 0x13, 0x02, 0xc2, 0x10, 0xe4, 0xf5} },
+{ 0x0e5a,	16,	{0x0b, 0xf5, 0x13, 0xc2, 0x15, 0xc2, 0x11, 0x90, 0x7f, 0xa1, 0x04, 0xf0, 0x90, 0x7f, 0xd8, 0xe0} },
+{ 0x0e6a,	16,	{0x65, 0x17, 0x60, 0x10, 0x30, 0x11, 0x05, 0xd2, 0x18, 0x12, 0x00, 0x46, 0x90, 0x7f, 0xd8, 0xe0} },
+{ 0x0e7a,	16,	{0xf5, 0x17, 0x80, 0x08, 0x30, 0x11, 0x05, 0xc2, 0x18, 0x12, 0x00, 0x46, 0x30, 0x10, 0x07, 0xc2} },
+{ 0x0e8a,	16,	{0x10, 0x12, 0x09, 0x19, 0x80, 0xd6, 0x30, 0x16, 0xd3, 0xc2, 0x16, 0x12, 0x14, 0x4f, 0x80, 0xcc} },
+{ 0x0e9a,	1,	{0x22} },
+{ 0x0e9b,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x47, 0x02, 0x0e, 0xe2} },
+{ 0x0ea7,	16,	{0x02, 0x0e, 0x0a, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x0eb7,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x0ec7,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x0ed7,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x13, 0x69, 0xe4, 0x7e} },
+{ 0x0ee7,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x0ef7,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x0f07,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x0f17,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x0f27,	16,	{0xe4, 0xfe, 0x75, 0x1d, 0xff, 0x75, 0x1e, 0x19, 0x75, 0x1f, 0x12, 0xab, 0x1d, 0xaa, 0x1e, 0xa9} },
+{ 0x0f37,	16,	{0x1f, 0x90, 0x00, 0x01, 0x12, 0x0c, 0xb8, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x0f47,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x0c, 0xf7, 0x85, 0xf0, 0x1b, 0xf5, 0x1c, 0x62, 0x1b} },
+{ 0x0f57,	16,	{0xe5, 0x1b, 0x62, 0x1c, 0xe5, 0x1c, 0x62, 0x1b, 0x29, 0xfd, 0xe5, 0x1b, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x0f67,	14,	{0x1d, 0xff, 0xf5, 0x1e, 0x89, 0x1f, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0f75,	1,	{0x22} },
+{ 0x0f76,	2,	{0x8f, 0x1b} },
+{ 0x0f78,	16,	{0xe4, 0xf5, 0x1c, 0x75, 0x1d, 0xff, 0x75, 0x1e, 0x19, 0x75, 0x1f, 0x86, 0xab, 0x1d, 0xaa, 0x1e} },
+{ 0x0f88,	16,	{0xa9, 0x1f, 0x90, 0x00, 0x01, 0x12, 0x0c, 0xb8, 0xb4, 0x03, 0x1d, 0xaf, 0x1c, 0x05, 0x1c, 0xef} },
+{ 0x0f98,	16,	{0xb5, 0x1b, 0x01, 0x22, 0x12, 0x0c, 0x9f, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x0fa8,	14,	{0x1d, 0xff, 0xf5, 0x1e, 0x89, 0x1f, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0fb6,	1,	{0x22} },
+{ 0x0fb7,	16,	{0x8f, 0x1a, 0x05, 0x0d, 0xe5, 0x0d, 0xae, 0x0c, 0x70, 0x02, 0x05, 0x0c, 0x14, 0xf5, 0x82, 0x8e} },
+{ 0x0fc7,	16,	{0x83, 0xe5, 0x1a, 0xf0, 0x12, 0x00, 0x36, 0x05, 0x0d, 0xe5, 0x0d, 0xac, 0x0c, 0x70, 0x02, 0x05} },
+{ 0x0fd7,	16,	{0x0c, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x08, 0xe5, 0x08, 0x60, 0x0a, 0x12, 0x13} },
+{ 0x0fe7,	9,	{0xcc, 0x8f, 0x1a, 0xef, 0x42, 0x36, 0x80, 0xca, 0x22} },
+{ 0x0ff0,	16,	{0x8f, 0x1a, 0x05, 0x0d, 0xe5, 0x0d, 0xae, 0x0c, 0x70, 0x02, 0x05, 0x0c, 0x14, 0xf5, 0x82, 0x8e} },
+{ 0x1000,	16,	{0x83, 0xe5, 0x1a, 0xf0, 0x12, 0x13, 0xe4, 0x05, 0x0d, 0xe5, 0x0d, 0xac, 0x0c, 0x70, 0x02, 0x05} },
+{ 0x1010,	16,	{0x0c, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x08, 0xe5, 0x08, 0x60, 0x0a, 0x12, 0x14} },
+{ 0x1020,	9,	{0x37, 0x8f, 0x1a, 0xef, 0x42, 0x37, 0x80, 0xca, 0x22} },
+{ 0x1029,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} },
+{ 0x1039,	16,	{0x1a, 0x04, 0xc2, 0x1a, 0x80, 0x02, 0xd2, 0x16, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x1049,	14,	{0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1057,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} },
+{ 0x1067,	16,	{0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} },
+{ 0x1077,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1081,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} },
+{ 0x1091,	16,	{0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} },
+{ 0x10a1,	7,	{0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x10a8,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x10b8,	16,	{0x10, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x10c8,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x10ce,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x10de,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x10ee,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x10f4,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1104,	16,	{0x12, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x1114,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x111a,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x112a,	16,	{0x14, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x113a,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1140,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1150,	16,	{0x16, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54} },
+{ 0x1160,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1166,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1176,	16,	{0x0a, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x1186,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x118c,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x119c,	16,	{0x08, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11ac,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11b2,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x11c2,	16,	{0x09, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11d2,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11d8,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x11e8,	16,	{0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x11f8,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x11fe,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x120e,	16,	{0x0e, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54} },
+{ 0x121e,	6,	{0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} },
+{ 0x1224,	16,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0xe5, 0x44, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x1234,	16,	{0x98, 0x74, 0x17, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90} },
+{ 0x1244,	5,	{0xc0, 0x00, 0xed, 0xf0, 0x22} },
+{ 0x1249,	16,	{0x12, 0x13, 0xb4, 0x8f, 0x1a, 0x12, 0x13, 0xb4, 0x8f, 0x1b, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x12} },
+{ 0x1259,	16,	{0x12, 0x13, 0xb4, 0x8f, 0x1a, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x07, 0x12, 0x13, 0xb4, 0x8f, 0x1b} },
+{ 0x1269,	4,	{0x80, 0xe8, 0xaf, 0x1a} },
+{ 0x126d,	1,	{0x22} },
+{ 0x126e,	16,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0xe5, 0x45, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x7f} },
+{ 0x127e,	16,	{0x98, 0x74, 0x0f, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x0d, 0xf0, 0x90} },
+{ 0x128e,	5,	{0xc0, 0x00, 0xed, 0xf0, 0x22} },
+{ 0x1293,	16,	{0x12, 0x14, 0x1f, 0x8f, 0x1a, 0x12, 0x14, 0x1f, 0x8f, 0x1b, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x12} },
+{ 0x12a3,	16,	{0x12, 0x14, 0x1f, 0x8f, 0x1a, 0xe5, 0x1a, 0x65, 0x1b, 0x60, 0x07, 0x12, 0x14, 0x1f, 0x8f, 0x1b} },
+{ 0x12b3,	4,	{0x80, 0xe8, 0xaf, 0x1a} },
+{ 0x12b7,	1,	{0x22} },
+{ 0x12b8,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x12c8,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x12d8,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x12dd,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53} },
+{ 0x12ed,	16,	{0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82} },
+{ 0x12fd,	5,	{0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x1302,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x18, 0x04, 0xe0, 0x44} },
+{ 0x1312,	16,	{0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x13, 0x85, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x1322,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x1327,	16,	{0xae, 0x07, 0xe4, 0xff, 0xe5, 0x3e, 0x54, 0x7f, 0xfd, 0x12, 0x12, 0x24, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1337,	16,	{0x11, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x3e, 0x44, 0x80, 0xfd, 0x12, 0x12, 0x24} },
+{ 0x1347,	1,	{0x22} },
+{ 0x1348,	16,	{0xae, 0x07, 0xe4, 0xff, 0xe5, 0x3f, 0x54, 0x7f, 0xfd, 0x12, 0x12, 0x6e, 0x90, 0x7f, 0x98, 0x74} },
+{ 0x1358,	16,	{0x09, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x3f, 0x44, 0x80, 0xfd, 0x12, 0x12, 0x6e} },
+{ 0x1368,	1,	{0x22} },
+{ 0x1369,	16,	{0x05, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x14, 0x03, 0x00, 0x00, 0xc1, 0x10, 0xc1, 0x16} },
+{ 0x1379,	7,	{0xc1, 0x0f, 0xc1, 0x94, 0x01, 0x0a, 0x00} },
+{ 0x1380,	2,	{0xc1, 0x99} },
+{ 0x1382,	2,	{0xc1, 0x9a} },
+{ 0x1384,	1,	{0x00} },
+{ 0x1385,	16,	{0x8e, 0x18, 0x8f, 0x19, 0xe5, 0x19, 0x15, 0x19, 0xae, 0x18, 0x70, 0x02, 0x15, 0x18, 0x4e, 0x60} },
+{ 0x1395,	7,	{0x05, 0x12, 0x0d, 0xe9, 0x80, 0xee, 0x22} },
+{ 0x139c,	12,	{0x90, 0x7f, 0x98, 0x74, 0x11, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13a8,	12,	{0x90, 0x7f, 0x98, 0x74, 0x12, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13b4,	12,	{0x90, 0x7f, 0x98, 0x74, 0x13, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13c0,	12,	{0x90, 0x7f, 0x98, 0x74, 0x14, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13cc,	12,	{0x90, 0x7f, 0x98, 0x74, 0x15, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13d8,	12,	{0x90, 0x7f, 0x98, 0x74, 0x16, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13e4,	12,	{0x90, 0x7f, 0x98, 0x74, 0x08, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13f0,	12,	{0x90, 0x7f, 0x98, 0x74, 0x09, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x13fc,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x1400,	16,	{0x02, 0x10, 0x81, 0x00, 0x02, 0x12, 0xb8, 0x00, 0x02, 0x10, 0x57, 0x00, 0x02, 0x10, 0x29, 0x00} },
+{ 0x1410,	3,	{0x02, 0x12, 0xdd} },
+{ 0x1413,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0a, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x141f,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0b, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x142b,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0c, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x1437,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0d, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x1443,	12,	{0x90, 0x7f, 0x98, 0x74, 0x0e, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} },
+{ 0x144f,	10,	{0x12, 0x00, 0x03, 0x12, 0x0d, 0xfa, 0x12, 0x0b, 0x27, 0x22} },
+{ 0x1900,	16,	{0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x10, 0x01, 0x00, 0x00, 0x01, 0x02} },
+{ 0x1910,	16,	{0x00, 0x01, 0x09, 0x02, 0x74, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e} },
+{ 0x1920,	16,	{0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1930,	16,	{0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00} },
+{ 0x1940,	16,	{0x07, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x1950,	16,	{0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x82, 0x02} },
+{ 0x1960,	16,	{0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00} },
+{ 0x1970,	16,	{0x00, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x07} },
+{ 0x1980,	16,	{0x05, 0x87, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00} },
+{ 0x1990,	16,	{0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00} },
+{ 0x19a0,	16,	{0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00} },
+{ 0x19b0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00} },
+{ 0x19c0,	16,	{0x6f, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} },
+{ 0x19d0,	16,	{0x2e, 0x00, 0x36, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00} },
+{ 0x19e0,	16,	{0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00} },
+{ 0x19f0,	16,	{0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x61, 0x00} },
+{ 0x1a00,	10,	{0x70, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/omninet.c linux.18p17/drivers/usb/serial/omninet.c
--- linux.vanilla/drivers/usb/serial/omninet.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/omninet.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,407 @@
+/*
+ * USB ZyXEL omni.net LCD PLUS driver
+ *
+ *	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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * Please report both successes and troubles to the author at omninet@kroah.com
+ *
+ * (08/28/2000) gkh
+ *	Added locks for SMP safeness.
+ *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more 
+ *	than once.
+ *	Fixed potential race in omninet_write_bulk_callback
+ *
+ * (07/19/2000) gkh
+ *	Added module_init and module_exit functions to handle the fact that this
+ *	driver is a loadable module now.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define isalpha(x) ( ( x > 96 && x < 123) || ( x > 64 && x < 91) || (x > 47 && x < 58) )
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+
+#define ZYXEL_VENDOR_ID		0x0586
+#define ZYXEL_OMNINET_ID	0x1000
+
+/* function prototypes */
+static int  omninet_open		(struct usb_serial_port *port, struct file *filp);
+static void omninet_close		(struct usb_serial_port *port, struct file *filp);
+static void omninet_read_bulk_callback	(struct urb *urb);
+static void omninet_write_bulk_callback	(struct urb *urb);
+static int  omninet_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int  omninet_write_room		(struct usb_serial_port *port);
+static void omninet_shutdown		(struct usb_serial *serial);
+
+/* All of the device info needed for the omni.net */
+static __u16	zyxel_vendor_id			= ZYXEL_VENDOR_ID;
+static __u16	zyxel_omninet_product_id	= ZYXEL_OMNINET_ID;
+
+struct usb_serial_device_type zyxel_omninet_device = {
+	name:			"ZyXEL - omni.net lcd plus usb",
+	idVendor:		&zyxel_vendor_id,
+	idProduct:		&zyxel_omninet_product_id,
+	needs_interrupt_in:	MUST_HAVE,
+	needs_bulk_in:		MUST_HAVE,
+	needs_bulk_out:		MUST_HAVE,
+	num_interrupt_in:	1,
+	num_bulk_in:		1,
+	num_bulk_out:		2,
+	num_ports:		1,
+	open:			omninet_open,
+	close:			omninet_close,
+	write:			omninet_write,
+	write_room:		omninet_write_room,
+	read_bulk_callback:	omninet_read_bulk_callback,
+	write_bulk_callback:	omninet_write_bulk_callback,
+	shutdown:		omninet_shutdown,
+};
+
+
+/* The protocol.
+ *
+ * The omni.net always exchange 64 bytes of data with the host. The first
+ * four bytes are the control header, you can see it in the above structure.
+ *
+ * oh_seq is a sequence number. Don't know if/how it's used.
+ * oh_len is the length of the data bytes in the packet.
+ * oh_xxx Bit-mapped, related to handshaking and status info.
+ *	I normally set it to 0x03 in trasmitted frames.
+ *	7: Active when the TA is in a CONNECTed state.
+ *	6: unknown
+ *	5: handshaking, unknown
+ *	4: handshaking, unknown
+ *	3: unknown, usually 0
+ *	2: unknown, usually 0
+ *	1: handshaking, unknown, usually set to 1 in trasmitted frames
+ *	0: handshaking, unknown, usually set to 1 in trasmitted frames
+ * oh_pad Probably a pad byte.
+ *
+ * After the header you will find data bytes if oh_len was greater than zero.
+ *
+ */
+
+struct omninet_header
+{
+	__u8	oh_seq;
+	__u8	oh_len;
+	__u8	oh_xxx;
+	__u8	oh_pad;
+};
+
+struct omninet_data
+{
+	__u8	od_outseq;	// Sequence number for bulk_out URBs
+};
+
+static int omninet_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial;
+	struct usb_serial_port	*wport;
+	struct omninet_data	*od;
+	unsigned long		flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	serial = get_usb_serial (port, __FUNCTION__);
+	if (!serial)
+		return -ENODEV;
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	MOD_INC_USE_COUNT;
+	++port->open_count;
+
+	if (!port->active) {
+		port->active = 1;
+
+		od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
+		if( !od ) {
+			err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct omninet_data));
+			--port->open_count;
+			port->active = 0;
+			spin_unlock_irqrestore (&port->port_lock, flags);
+			MOD_DEC_USE_COUNT;
+			return -ENOMEM;
+		}
+
+		port->private = od;
+		wport = &serial->port[1];
+		wport->tty = port->tty;
+
+		/* Start reading from the device */
+		if (usb_submit_urb(port->read_urb))
+			dbg(__FUNCTION__" - read bulk (%p) failed", port->read_urb);
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return (0);
+}
+
+static void omninet_close (struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial 	*serial;
+	struct usb_serial_port 	*wport;
+	struct omninet_data 	*od;
+	unsigned long		flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	serial = get_usb_serial (port, __FUNCTION__);
+	if (!serial)
+		return;
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	--port->open_count;
+	MOD_DEC_USE_COUNT;
+
+	if (port->open_count <= 0) {
+		od = (struct omninet_data *)port->private;
+		wport = &serial->port[1];
+
+		usb_unlink_urb (wport->write_urb);
+		usb_unlink_urb (port->read_urb);
+
+		port->active = 0;
+		port->open_count = 0;
+		if (od)
+			kfree(od);
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+}
+
+
+#define OMNINET_DATAOFFSET	0x04
+#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
+#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
+
+static void omninet_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port 	*port 	= (struct usb_serial_port *)urb->context;
+	struct usb_serial	*serial = get_usb_serial (port, __FUNCTION__);
+
+	unsigned char 		*data 	= urb->transfer_buffer;
+	struct omninet_header 	*header = (struct omninet_header *) &data[0];
+
+	int i;
+
+//	dbg("omninet_read_bulk_callback");
+
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+#ifdef DEBUG
+	if(header->oh_xxx != 0x30) {
+		if (urb->actual_length) {
+			printk (KERN_DEBUG __FILE__ ": omninet_read %d: ", header->oh_len);
+			for (i = 0; i < (header->oh_len + OMNINET_HEADERLEN); i++) {
+				printk ("%.2x ", data[i]);
+			}
+			printk ("\n");
+		}
+	}
+#endif
+
+	if (urb->actual_length && header->oh_len) {
+		for (i = 0; i < header->oh_len; i++) {
+			 tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0);
+	  	}
+	  	tty_flip_buffer_push(port->tty);
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		dbg(__FUNCTION__" - failed resubmitting read urb");
+
+	return;
+}
+
+static int omninet_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial 	*serial	= port->serial;
+	struct usb_serial_port 	*wport	= &serial->port[1];
+
+	struct omninet_data 	*od 	= (struct omninet_data   *) port->private;
+	struct omninet_header	*header = (struct omninet_header *) wport->write_urb->transfer_buffer;
+
+	unsigned long		flags;
+/*
+#ifdef DEBUG
+	int i;
+#endif
+*/
+
+//	dbg("omninet_write port %d", port->number);
+
+	if (count == 0) {
+		dbg(__FUNCTION__" - write request of 0 bytes");
+		return (0);
+	}
+/*
+#ifdef DEBUG
+	printk (KERN_DEBUG __FILE__ ": omninet_write %d: ", count);
+		for (i = 0; i < count; i++) {
+			if( isalpha(buf[i]) )
+				printk ("%c ", buf[i]);
+			else
+				printk ("%.2x ", buf[i]);
+		}
+		printk ("\n");
+#endif
+*/
+	if (wport->write_urb->status == -EINPROGRESS) {
+		dbg (__FUNCTION__" - already writing");
+		return (0);
+	}
+
+	spin_lock_irqsave (&port->port_lock, flags);
+	
+	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
+
+	if (from_user) {
+		copy_from_user(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count);
+	}
+	else {
+		memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count);
+	}
+
+
+	header->oh_seq 	= od->od_outseq++;
+	header->oh_len 	= count;
+	header->oh_xxx  = 0x03;
+	header->oh_pad 	= 0x00;
+
+	/* send the data out the bulk port, always 64 bytes */
+	wport->write_urb->transfer_buffer_length = 64;
+
+	if (usb_submit_urb(wport->write_urb)) {
+		dbg(__FUNCTION__" - usb_submit_urb(write bulk) failed");
+		spin_unlock_irqrestore (&port->port_lock, flags);
+		return 0;
+	}
+
+//	dbg("omninet_write returns %d", count);
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+	return (count);
+}
+
+
+static int omninet_write_room (struct usb_serial_port *port)
+{
+	struct usb_serial 	*serial = port->serial;
+	struct usb_serial_port 	*wport 	= &serial->port[1];
+
+	int room = 0; // Default: no room
+
+	if (wport->write_urb->status != -EINPROGRESS)
+		room = wport->bulk_out_size - OMNINET_HEADERLEN;
+
+//	dbg("omninet_write_room returns %d", room);
+
+	return (room);
+}
+
+static void omninet_write_bulk_callback (struct urb *urb)
+{
+/*	struct omninet_header	*header = (struct omninet_header  *) urb->transfer_buffer; */
+	struct usb_serial_port 	*port   = (struct usb_serial_port *) urb->context;
+	struct usb_serial 	*serial;
+
+//	dbg("omninet_write_bulk_callback, port %0x\n", port);
+
+
+	if (port_paranoia_check (port, __FUNCTION__)) {
+		return;
+	}
+
+	serial = port->serial;
+	if (serial_paranoia_check (serial, __FUNCTION__)) {
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__" - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+//	dbg("omninet_write_bulk_callback, tty %0x\n", tty);
+
+	return;
+}
+
+
+static void omninet_shutdown (struct usb_serial *serial)
+{
+	dbg (__FUNCTION__);
+
+	while (serial->port[0].open_count > 0) {
+		omninet_close (&serial->port[0], NULL);
+	}
+}
+
+
+static int __init omninet_init (void)
+{
+	usb_serial_register (&zyxel_omninet_device);
+	return 0;
+}
+
+
+static void __exit omninet_exit (void)
+{
+	usb_serial_deregister (&zyxel_omninet_device);
+}
+
+
+module_init(omninet_init);
+module_exit(omninet_exit);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/usb-serial.h linux.18p17/drivers/usb/serial/usb-serial.h
--- linux.vanilla/drivers/usb/serial/usb-serial.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/usb-serial.h	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,225 @@
+/*
+ * USB Serial Converter driver
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (09/11/2000) gkh
+ *	Added usb_serial_debug_data function to help get rid of #DEBUG in the
+ *	drivers.
+ *
+ * (08/28/2000) gkh
+ *	Added port_lock to port structure.
+ *
+ * (08/08/2000) gkh
+ *	Added open_count to port structure.
+ *
+ * (07/23/2000) gkh
+ *	Added bulk_out_endpointAddress to port structure.
+ *
+ * (07/19/2000) gkh, pberger, and borchers
+ *	Modifications to allow usb-serial drivers to be modules.
+ *
+ * 
+ */
+
+
+#ifndef __LINUX_USB_SERIAL_H
+#define __LINUX_USB_SERIAL_H
+
+#include <linux/config.h>
+
+#define SERIAL_TTY_MAJOR	188	/* Nice legal number now */
+#define SERIAL_TTY_MINORS	255	/* loads of devices :) */
+
+#define MAX_NUM_PORTS		8	/* The maximum number of ports one device can grab at once */
+
+#define USB_SERIAL_MAGIC	0x6702	/* magic number for usb_serial struct */
+#define USB_SERIAL_PORT_MAGIC	0x7301	/* magic number for usb_serial_port struct */
+
+/* parity check flag */
+#define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+
+struct usb_serial_port {
+	int			magic;
+	struct usb_serial	*serial;	/* pointer back to the owner of this port */
+	struct tty_struct *	tty;		/* the coresponding tty for this port */
+	unsigned char		number;
+	char			active;		/* someone has this device open */
+
+	unsigned char *		interrupt_in_buffer;
+	struct urb *		interrupt_in_urb;
+
+	unsigned char *		bulk_in_buffer;
+	struct urb *		read_urb;
+
+	unsigned char *		bulk_out_buffer;
+	int			bulk_out_size;
+	struct urb *		write_urb;
+	__u8			bulk_out_endpointAddress;
+
+	wait_queue_head_t	write_wait;
+
+	struct tq_struct	tqueue;		/* task queue for line discipline waking up */
+	int			open_count;	/* number of times this port has been opened */
+	spinlock_t		port_lock;
+	
+	void *			private;	/* data private to the specific port */
+};
+
+struct usb_serial {
+	int				magic;
+	struct usb_device *		dev;
+	struct usb_serial_device_type *	type;			/* the type of usb serial device this is */
+	struct usb_interface *		interface;		/* the interface for this device */
+	struct tty_driver *		tty_driver;		/* the tty_driver for this device */
+	unsigned char			minor;			/* the starting minor number for this device */
+	unsigned char			num_ports;		/* the number of ports this device has */
+	char				num_interrupt_in;	/* number of interrupt in endpoints we have */
+	char				num_bulk_in;		/* number of bulk in endpoints we have */
+	char				num_bulk_out;		/* number of bulk out endpoints we have */
+	struct usb_serial_port		port[MAX_NUM_PORTS];
+
+	void *			private;		/* data private to the specific driver */
+};
+
+
+#define MUST_HAVE_NOT	0x01
+#define MUST_HAVE	0x02
+#define DONT_CARE	0x03
+
+#define	HAS		0x02
+#define HAS_NOT		0x01
+
+#define NUM_DONT_CARE	(-1)
+
+
+/* This structure defines the individual serial converter. */
+struct usb_serial_device_type {
+	char	*name;
+	__u16	*idVendor;
+	__u16	*idProduct;
+	char	needs_interrupt_in;
+	char	needs_bulk_in;
+	char	needs_bulk_out;
+	char	num_interrupt_in;
+	char	num_bulk_in;
+	char	num_bulk_out;
+	char	num_ports;		/* number of serial ports this device has */
+
+	struct list_head	driver_list;
+	
+	/* function call to make before accepting driver */
+	int (*startup) (struct usb_serial *serial);	/* return 0 to continue initialization, anything else to abort */
+	void (*shutdown) (struct usb_serial *serial);
+
+	/* serial function calls */
+	int  (*open)		(struct usb_serial_port *port, struct file * filp);
+	void (*close)		(struct usb_serial_port *port, struct file * filp);
+	int  (*write)		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+	int  (*write_room)	(struct usb_serial_port *port);
+	int  (*ioctl)		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+	void (*set_termios)	(struct usb_serial_port *port, struct termios * old);
+	void (*break_ctl)	(struct usb_serial_port *port, int break_state);
+	int  (*chars_in_buffer)	(struct usb_serial_port *port);
+	void (*throttle)	(struct usb_serial_port *port);
+	void (*unthrottle)	(struct usb_serial_port *port);
+
+	void (*read_int_callback)(struct urb *urb);
+	void (*read_bulk_callback)(struct urb *urb);
+	void (*write_bulk_callback)(struct urb *urb);
+};
+
+extern int  usb_serial_register(struct usb_serial_device_type *new_device);
+extern void usb_serial_deregister(struct usb_serial_device_type *device);
+
+/* determine if we should include the EzUSB loader functions */
+#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_WHITEHEAT) || defined(CONFIG_USB_SERIAL_KEYSPAN) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE) || defined(CONFIG_USB_SERIAL_WHITEHEAT_MODULE) || defined(CONFIG_USB_SERIAL_KEYSPAN_MODULE)
+	#define	USES_EZUSB_FUNCTIONS
+	extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
+	extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
+#else
+	#undef 	USES_EZUSB_FUNCTIONS
+#endif
+
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
+{
+	if (!serial) {
+		dbg("%s - serial == NULL", function);
+		return -1;
+	}
+	if (serial->magic != USB_SERIAL_MAGIC) {
+		dbg("%s - bad magic number for serial", function);
+		return -1;
+	}
+	if (!serial->type) {
+		dbg("%s - serial->type == NULL!", function);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
+{
+	if (!port) {
+		dbg("%s - port == NULL", function);
+		return -1;
+	}
+	if (port->magic != USB_SERIAL_PORT_MAGIC) {
+		dbg("%s - bad magic number for port", function);
+		return -1;
+	}
+	if (!port->serial) {
+		dbg("%s - port->serial == NULL", function);
+		return -1;
+	}
+	if (!port->tty) {
+		dbg("%s - port->tty == NULL", function);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static inline struct usb_serial* get_usb_serial (struct usb_serial_port *port, const char *function) 
+{ 
+	/* if no port was specified, or it fails a paranoia check */
+	if (!port || 
+		port_paranoia_check (port, function) ||
+		serial_paranoia_check (port->serial, function)) {
+		/* then say that we dont have a valid usb_serial thing, which will
+		 * end up genrating -ENODEV return values */ 
+		return NULL;
+	}
+
+	return port->serial;
+}
+
+
+static inline void usb_serial_debug_data (const char *file, const char *function, int size, const unsigned char *data)
+{
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	int i;
+	printk (KERN_DEBUG "%s: %s - length = %d, data = ", file, function, size);
+	for (i = 0; i < size; ++i) {
+		printk ("%.2x ", data[i]);
+	}
+	printk ("\n");
+#endif
+}
+
+#endif	/* ifdef __LINUX_USB_SERIAL_H */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/usbserial.c linux.18p17/drivers/usb/serial/usbserial.c
--- linux.vanilla/drivers/usb/serial/usbserial.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/usbserial.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,1383 @@
+/*
+ * USB Serial Converter driver
+ *
+ * Copyright (C) 1999, 2000 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (c) 2000 Al Borchers (borchers@steinerpoint.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * This driver was originally based on the ACM driver by Armin Fuerst (which was 
+ * based on a driver by Brad Keryan)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ * (09/11/2000) gkh
+ *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
+ *
+ * (08/28/2000) gkh
+ *	Added port_lock to port structure.
+ *	Added locks for SMP safeness to generic driver
+ *	Fixed the ability to open a generic device's port more than once.
+ *
+ * (07/23/2000) gkh
+ *	Added bulk_out_endpointAddress to port structure.
+ *
+ * (07/19/2000) gkh, pberger, and borchers
+ *	Modifications to allow usb-serial drivers to be modules.
+ *
+ * (07/03/2000) gkh
+ *	Added more debugging to serial_ioctl call
+ * 
+ * (06/25/2000) gkh
+ *	Changed generic_write_bulk_callback to not call wake_up_interruptible
+ *	directly, but to have port_softint do it at a safer time.
+ *
+ * (06/23/2000) gkh
+ *	Cleaned up debugging statements in a quest to find UHCI timeout bug.
+ *
+ * (05/22/2000) gkh
+ *	Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be 
+ *	removed from the individual device source files.
+ *
+ * (05/03/2000) gkh
+ *	Added the Digi Acceleport driver from Al Borchers and Peter Berger.
+ * 
+ * (05/02/2000) gkh
+ *	Changed devfs and tty register code to work properly now. This was based on
+ *	the ACM driver changes by Vojtech Pavlik.
+ *
+ * (04/27/2000) Ryan VanderBijl
+ * 	Put calls to *_paranoia_checks into one function.
+ * 
+ * (04/23/2000) gkh
+ *	Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports.
+ *	Moved when the startup code printed out the devices that are supported.
+ *
+ * (04/19/2000) gkh
+ *	Added driver for ZyXEL omni.net lcd plus ISDN TA
+ *	Made startup info message specify which drivers were compiled in.
+ *
+ * (04/03/2000) gkh
+ *	Changed the probe process to remove the module unload races.
+ *	Changed where the tty layer gets initialized to have devfs work nicer.
+ *	Added initial devfs support.
+ *
+ * (03/26/2000) gkh
+ *	Split driver up into device specific pieces.
+ * 
+ * (03/19/2000) gkh
+ *	Fixed oops that could happen when device was removed while a program
+ *	was talking to the device.
+ *	Removed the static urbs and now all urbs are created and destroyed
+ *	dynamically.
+ *	Reworked the internal interface. Now everything is based on the 
+ *	usb_serial_port structure instead of the larger usb_serial structure.
+ *	This fixes the bug that a multiport device could not have more than
+ *	one port open at one time.
+ *
+ * (03/17/2000) gkh
+ *	Added config option for debugging messages.
+ *	Added patch for keyspan pda from Brian Warner.
+ *
+ * (03/06/2000) gkh
+ *	Added the keyspan pda code from Brian Warner <warner@lothar.com>
+ *	Moved a bunch of the port specific stuff into its own structure. This
+ *	is in anticipation of the true multiport devices (there's a bug if you
+ *	try to access more than one port of any multiport device right now)
+ *
+ * (02/21/2000) gkh
+ *	Made it so that any serial devices only have to specify which functions
+ *	they want to overload from the generic function calls (great, 
+ *	inheritance in C, in a driver, just what I wanted...)
+ *	Added support for set_termios and ioctl function calls. No drivers take
+ *	advantage of this yet.
+ *	Removed the #ifdef MODULE, now there is no module specific code.
+ *	Cleaned up a few comments in usb-serial.h that were wrong (thanks again
+ *	to Miles Lott).
+ *	Small fix to get_free_serial.
+ *
+ * (02/14/2000) gkh
+ *	Removed the Belkin and Peracom functionality from the driver due to
+ *	the lack of support from the vendor, and me not wanting people to 
+ *	accidenatly buy the device, expecting it to work with Linux.
+ *	Added read_bulk_callback and write_bulk_callback to the type structure
+ *	for the needs of the FTDI and WhiteHEAT driver.
+ *	Changed all reverences to FTDI to FTDI_SIO at the request of Bill
+ *	Ryder.
+ *	Changed the output urb size back to the max endpoint size to make
+ *	the ftdi_sio driver have it easier, and due to the fact that it didn't
+ *	really increase the speed any.
+ *
+ * (02/11/2000) gkh
+ *	Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
+ *	patch from Miles Lott (milos@insync.net).
+ *	Fixed bug with not restoring the minor range that a device grabs, if
+ *	the startup function fails (thanks Miles for finding this).
+ *
+ * (02/05/2000) gkh
+ *	Added initial framework for the Keyspan PDA serial converter so that
+ *	Brian Warner has a place to put his code.
+ *	Made the ezusb specific functions generic enough that different
+ *	devices can use them (whiteheat and keyspan_pda both need them).
+ *	Split out a whole bunch of structure and other stuff to a seperate
+ *	usb-serial.h file.
+ *	Made the Visor connection messages a little more understandable, now
+ *	that Miles Lott (milos@insync.net) has gotten the Generic channel to
+ *	work. Also made them always show up in the log file.
+ * 
+ * (01/25/2000) gkh
+ *	Added initial framework for FTDI serial converter so that Bill Ryder
+ *	has a place to put his code.
+ *	Added the vendor specific info from Handspring. Now we can print out
+ *	informational debug messages as well as understand what is happening.
+ *
+ * (01/23/2000) gkh
+ *	Fixed problem of crash when trying to open a port that didn't have a
+ *	device assigned to it. Made the minor node finding a little smarter,
+ *	now it looks to find a continous space for the new device.
+ *
+ * (01/21/2000) gkh
+ *	Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
+ *	Fixed get_serial_by_minor which was all messed up for multi port 
+ *	devices. Fixed multi port problem for generic devices. Now the number
+ *	of ports is determined by the number of bulk out endpoints for the
+ *	generic device.
+ *
+ * (01/19/2000) gkh
+ *	Removed lots of cruft that was around from the old (pre urb) driver 
+ *	interface.
+ *	Made the serial_table dynamic. This should save lots of memory when
+ *	the number of minor nodes goes up to 256.
+ *	Added initial support for devices that have more than one port. 
+ *	Added more debugging comments for the Visor, and added a needed 
+ *	set_configuration call.
+ *
+ * (01/17/2000) gkh
+ *	Fixed the WhiteHEAT firmware (my processing tool had a bug)
+ *	and added new debug loader firmware for it.
+ *	Removed the put_char function as it isn't really needed.
+ *	Added visor startup commands as found by the Win98 dump.
+ * 
+ * (01/13/2000) gkh
+ *	Fixed the vendor id for the generic driver to the one I meant it to be.
+ *
+ * (01/12/2000) gkh
+ *	Forget the version numbering...that's pretty useless...
+ *	Made the driver able to be compiled so that the user can select which
+ *	converter they want to use. This allows people who only want the Visor
+ *	support to not pay the memory size price of the WhiteHEAT.
+ *	Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
+ *	grabbed the root hub. Not good.
+ * 
+ * version 0.4.0 (01/10/2000) gkh
+ *	Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
+ *	device. Added startup function to allow firmware to be downloaded to
+ *	a device if it needs to be.
+ *	Added firmware download logic to the WhiteHEAT device.
+ *	Started to add #defines to split up the different drivers for potential
+ *	configuration option.
+ *	
+ * version 0.3.1 (12/30/99) gkh
+ *      Fixed problems with urb for bulk out.
+ *      Added initial support for multiple sets of endpoints. This enables
+ *      the Handspring Visor to be attached successfully. Only the first
+ *      bulk in / bulk out endpoint pair is being used right now.
+ *
+ * version 0.3.0 (12/27/99) gkh
+ *	Added initial support for the Handspring Visor based on a patch from
+ *	Miles Lott (milos@sneety.insync.net)
+ *	Cleaned up the code a bunch and converted over to using urbs only.
+ *
+ * version 0.2.3 (12/21/99) gkh
+ *	Added initial support for the Connect Tech WhiteHEAT converter.
+ *	Incremented the number of ports in expectation of getting the
+ *	WhiteHEAT to work properly (4 ports per connection).
+ *	Added notification on insertion and removal of what port the
+ *	device is/was connected to (and what kind of device it was).
+ *
+ * version 0.2.2 (12/16/99) gkh
+ *	Changed major number to the new allocated number. We're legal now!
+ *
+ * version 0.2.1 (12/14/99) gkh
+ *	Fixed bug that happens when device node is opened when there isn't a
+ *	device attached to it. Thanks to marek@webdesign.no for noticing this.
+ *
+ * version 0.2.0 (11/10/99) gkh
+ *	Split up internals to make it easier to add different types of serial 
+ *	converters to the code.
+ *	Added a "generic" driver that gets it's vendor and product id
+ *	from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
+ *	for the idea and sample code (from the usb scanner driver.)
+ *	Cleared up any licensing questions by releasing it under the GNU GPL.
+ *
+ * version 0.1.2 (10/25/99) gkh
+ * 	Fixed bug in detecting device.
+ *
+ * version 0.1.1 (10/05/99) gkh
+ * 	Changed the major number to not conflict with anything else.
+ *
+ * version 0.1 (09/28/99) gkh
+ * 	Can recognize the two different devices and start up a read from
+ *	device when asked to. Writes also work. No control signals yet, this
+ *	all is vendor specific data (i.e. no spec), also no control for
+ *	different baud rates or other bit settings.
+ *	Currently we are using the same devid as the acm driver. This needs
+ *	to change.
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+/* Module information */
+MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");
+MODULE_DESCRIPTION("USB Serial Driver");
+
+#include "usb-serial.h"
+
+#define MAX(a,b)	(((a)>(b))?(a):(b))
+
+/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
+/* need to always compile these in, as some of the other devices use these functions as their own. */
+/* if a driver does not provide a function pointer, the generic function will be called. */
+static int  generic_open		(struct usb_serial_port *port, struct file *filp);
+static void generic_close		(struct usb_serial_port *port, struct file *filp);
+static int  generic_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int  generic_write_room		(struct usb_serial_port *port);
+static int  generic_chars_in_buffer	(struct usb_serial_port *port);
+static void generic_read_bulk_callback	(struct urb *urb);
+static void generic_write_bulk_callback	(struct urb *urb);
+static void generic_shutdown		(struct usb_serial *serial);
+
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+static __u16	vendor	= 0x05f9;
+static __u16	product	= 0xffff;
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+/* All of the device info needed for the Generic Serial Converter */
+static struct usb_serial_device_type generic_device = {
+	name:			"Generic",
+	idVendor:		&vendor,		/* use the user specified vendor id */
+	idProduct:		&product,		/* use the user specified product id */
+	needs_interrupt_in:	DONT_CARE,		/* don't have to have an interrupt in endpoint */
+	needs_bulk_in:		DONT_CARE,		/* don't have to have a bulk in endpoint */
+	needs_bulk_out:		DONT_CARE,		/* don't have to have a bulk out endpoint */
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	shutdown:		generic_shutdown,
+};
+#endif
+
+
+/* local function prototypes */
+static int  serial_open (struct tty_struct *tty, struct file * filp);
+static void serial_close (struct tty_struct *tty, struct file * filp);
+static int  serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static int  serial_write_room (struct tty_struct *tty);
+static int  serial_chars_in_buffer (struct tty_struct *tty);
+static void serial_throttle (struct tty_struct * tty);
+static void serial_unthrottle (struct tty_struct * tty);
+static int  serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
+static void serial_set_termios (struct tty_struct *tty, struct termios * old);
+
+static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver usb_serial_driver = {
+	name:		"serial",
+	probe:		usb_serial_probe,
+	disconnect:	usb_serial_disconnect,
+};
+
+static int			serial_refcount;
+static struct tty_driver	serial_tty_driver;
+static struct tty_struct *	serial_tty[SERIAL_TTY_MINORS];
+static struct termios *		serial_termios[SERIAL_TTY_MINORS];
+static struct termios *		serial_termios_locked[SERIAL_TTY_MINORS];
+static struct usb_serial	*serial_table[SERIAL_TTY_MINORS] = {NULL, };
+
+LIST_HEAD(usb_serial_driver_list);
+
+
+static struct usb_serial *get_serial_by_minor (int minor)
+{
+	return serial_table[minor];
+}
+
+
+static struct usb_serial *get_free_serial (int num_ports, int *minor)
+{
+	struct usb_serial *serial = NULL;
+	int i, j;
+	int good_spot;
+
+	dbg(__FUNCTION__ " %d", num_ports);
+
+	*minor = 0;
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		if (serial_table[i])
+			continue;
+
+		good_spot = 1;
+		for (j = 1; j <= num_ports-1; ++j)
+			if (serial_table[i+j])
+				good_spot = 0;
+		if (good_spot == 0)
+			continue;
+			
+		if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) {
+			err(__FUNCTION__ " - Out of memory");
+			return NULL;
+		}
+		memset(serial, 0, sizeof(struct usb_serial));
+		serial->magic = USB_SERIAL_MAGIC;
+		serial_table[i] = serial;
+		*minor = i;
+		dbg(__FUNCTION__ " - minor base = %d", *minor);
+		for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
+			serial_table[i] = serial;
+		return serial;
+	}
+	return NULL;
+}
+
+
+static void return_serial (struct usb_serial *serial)
+{
+	int i;
+
+	dbg(__FUNCTION__);
+
+	if (serial == NULL)
+		return;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		serial_table[serial->minor + i] = NULL;
+	}
+
+	return;
+}
+
+
+#ifdef USES_EZUSB_FUNCTIONS
+/* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
+#define CPUCS_REG    0x7F92
+
+int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
+{
+	int result;
+	unsigned char *transfer_buffer =  kmalloc (length, GFP_KERNEL);
+
+//	dbg("ezusb_writememory %x, %d", address, length);
+
+	if (!transfer_buffer) {
+		err(__FUNCTION__ " - kmalloc(%d) failed.", length);
+		return -ENOMEM;
+	}
+	memcpy (transfer_buffer, data, length);
+	result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 300);
+	kfree (transfer_buffer);
+	return result;
+}
+
+
+int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
+{
+	int	response;
+	dbg(__FUNCTION__ " - %d", reset_bit);
+	response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
+	if (response < 0) {
+		err(__FUNCTION__ "- %d failed", reset_bit);
+	}
+	return response;
+}
+
+#endif	/* USES_EZUSB_FUNCTIONS */
+
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+static int serial_open (struct tty_struct *tty, struct file * filp)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int portNumber;
+	
+	dbg(__FUNCTION__);
+
+	/* initialize the pointer incase something fails */
+	tty->driver_data = NULL;
+
+	/* get the serial object associated with this tty pointer */
+	serial = get_serial_by_minor (MINOR(tty->device));
+
+	if (serial_paranoia_check (serial, __FUNCTION__)) {
+		return -ENODEV;
+	}
+
+	/* set up our port structure making the tty driver remember our port object, and us it */
+	portNumber = MINOR(tty->device) - serial->minor;
+	port = &serial->port[portNumber];
+	tty->driver_data = port;
+	port->tty = tty;
+	 
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->open) {
+		return (serial->type->open(port, filp));
+	} else {
+		return (generic_open(port, filp));
+	}
+}
+
+
+static void serial_close(struct tty_struct *tty, struct file * filp)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not opened");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->close) {
+		serial->type->close(port, filp);
+	} else {
+		generic_close(port, filp);
+	}
+}	
+
+
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	
+	if (!serial) {
+		return -ENODEV;
+	}
+	
+	dbg(__FUNCTION__ " - port %d, %d byte(s)", port->number, count);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not opened");
+		return -EINVAL;
+	}
+	
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->write) {
+		return (serial->type->write(port, from_user, buf, count));
+	} else {
+		return (generic_write(port, from_user, buf, count));
+	}
+}
+
+
+static int serial_write_room (struct tty_struct *tty) 
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return -ENODEV;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return -EINVAL;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->write_room) {
+		return (serial->type->write_room(port));
+	} else {
+		return (generic_write_room(port));
+	}
+}
+
+
+static int serial_chars_in_buffer (struct tty_struct *tty) 
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return -ENODEV;
+	}
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return -EINVAL;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->chars_in_buffer) {
+		return (serial->type->chars_in_buffer(port));
+	} else {
+		return (generic_chars_in_buffer(port));
+	}
+}
+
+
+static void serial_throttle (struct tty_struct * tty)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function */
+	if (serial->type->throttle) {
+		serial->type->throttle(port);
+	}
+
+	return;
+}
+
+
+static void serial_unthrottle (struct tty_struct * tty)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function */
+	if (serial->type->unthrottle) {
+		serial->type->unthrottle(port);
+	}
+
+	return;
+}
+
+
+static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return -ENODEV;
+	}
+
+	dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return -ENODEV;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->ioctl) {
+		return (serial->type->ioctl(port, file, cmd, arg));
+	} else {
+		return -ENOIOCTLCMD;
+	}
+}
+
+
+static void serial_set_termios (struct tty_struct *tty, struct termios * old)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->set_termios) {
+		serial->type->set_termios(port, old);
+	}
+	
+	return;
+}
+
+
+static void serial_break (struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial) {
+		return;
+	}
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!port->active) {
+		dbg (__FUNCTION__ " - port not open");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function if it is
+           available */
+	if (serial->type->break_ctl) {
+		serial->type->break_ctl(port, break_state);
+	}
+}
+
+
+
+/*****************************************************************************
+ * generic devices specific driver functions
+ *****************************************************************************/
+static int generic_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+	
+	if (!port->active) {
+		port->active = 1;
+
+		/* if we have a bulk interrupt, start reading from it */
+		if (serial->num_bulk_in) {
+			/*Start reading from the device*/
+			if (usb_submit_urb(port->read_urb))
+				dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+		}
+	}
+	
+	spin_unlock_irqrestore (&port->port_lock, flags);
+	
+	return 0;
+}
+
+
+static void generic_close (struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+		/* shutdown any bulk reads that might be going on */
+		if (serial->num_bulk_out)
+			usb_unlink_urb (port->write_urb);
+		if (serial->num_bulk_in)
+			usb_unlink_urb (port->read_urb);
+		
+		port->active = 0;
+		port->open_count = 0;
+	}
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+}
+
+
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (count == 0) {
+		dbg(__FUNCTION__ " - write request of 0 bytes");
+		return (0);
+	}
+
+	/* only do something if we have a bulk out endpoint */
+	if (serial->num_bulk_out) {
+		if (port->write_urb->status == -EINPROGRESS) {
+			dbg (__FUNCTION__ " - already writing");
+			return (0);
+		}
+
+		spin_lock_irqsave (&port->port_lock, flags);
+		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+		usb_serial_debug_data (__FILE__, __FUNCTION__, count, buf);
+
+		if (from_user) {
+			copy_from_user(port->write_urb->transfer_buffer, buf, count);
+		}
+		else {
+			memcpy (port->write_urb->transfer_buffer, buf, count);
+		}  
+
+		/* send the data out the bulk port */
+		port->write_urb->transfer_buffer_length = count;
+
+		if (usb_submit_urb(port->write_urb)) {
+			dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed");
+			spin_unlock_irqrestore (&port->port_lock, flags);
+			return 0;
+		}
+
+		spin_unlock_irqrestore (&port->port_lock, flags);
+		return (count);
+	}
+	
+	/* no bulk out, so return 0 bytes written */
+	return (0);
+} 
+
+
+static int generic_write_room (struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	int room = 0;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (serial->num_bulk_out)
+		if (port->write_urb->status != -EINPROGRESS)
+			room = port->bulk_out_size;
+	
+	dbg(__FUNCTION__ " - returns %d", room);
+	return (room);
+}
+
+
+static int generic_chars_in_buffer (struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	int chars = 0;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (serial->num_bulk_out)
+		if (port->write_urb->status == -EINPROGRESS)
+			chars = port->write_urb->transfer_buffer_length;
+
+	dbg (__FUNCTION__ " - returns %d", chars);
+	return (chars);
+}
+
+
+static void generic_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			 tty_insert_flip_char(tty, data[i], 0);
+	  	}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		dbg(__FUNCTION__ " - failed resubmitting read urb");
+
+	return;
+}
+
+
+static void generic_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	return;
+}
+
+
+static void generic_shutdown (struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			generic_close (&serial->port[i], NULL);
+		}
+	}
+}
+
+
+static void port_softint(void *private)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)private;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		return;
+	}
+ 	
+	tty = port->tty;
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+		dbg(__FUNCTION__ " - write wakeup call.");
+		(tty->ldisc.write_wakeup)(tty);
+	}
+
+	wake_up_interruptible(&tty->write_wait);
+}
+
+
+
+static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_serial *serial = NULL;
+	struct usb_serial_port *port;
+	struct usb_interface *interface;
+	struct usb_interface_descriptor *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
+	struct usb_serial_device_type *type = NULL;
+	struct list_head *tmp;
+	int found;
+	int minor;
+	int buffer_size;
+	int i;
+	char interrupt_pipe;
+	char bulk_in_pipe;
+	char bulk_out_pipe;
+	int num_interrupt_in = 0;
+	int num_bulk_in = 0;
+	int num_bulk_out = 0;
+	int num_ports;
+	int max_endpoints;
+
+	
+	/* loop through our list of known serial converters, and see if this
+	   device matches. */
+	found = 0;
+	list_for_each (tmp, &usb_serial_driver_list) {
+		type = list_entry(tmp, struct usb_serial_device_type, driver_list);
+		dbg ("Looking at %s Vendor id=%.4x Product id=%.4x", 
+			type->name, *(type->idVendor), *(type->idProduct));
+
+		/* look at the device descriptor */
+		if ((dev->descriptor.idVendor == *(type->idVendor)) &&
+		    (dev->descriptor.idProduct == *(type->idProduct))) {
+			dbg("descriptor matches");
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		/* no match */
+		dbg("none matched");
+		return(NULL);
+	}
+	
+	/* descriptor matches, let's find the endpoints needed */
+	interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT;
+			
+	/* check out the endpoints */
+	interface = &dev->actconfig->interface[ifnum];
+	iface_desc = &interface->altsetting[0];
+	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i];
+		
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk in endpoint */
+			dbg("found bulk in");
+			bulk_in_pipe = HAS;
+			bulk_in_endpoint[num_bulk_in] = endpoint;
+			++num_bulk_in;
+		}
+
+		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk out endpoint */
+			dbg("found bulk out");
+			bulk_out_pipe = HAS;
+			bulk_out_endpoint[num_bulk_out] = endpoint;
+			++num_bulk_out;
+		}
+		
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x03)) {
+			/* we found a interrupt in endpoint */
+			dbg("found interrupt in");
+			interrupt_pipe = HAS;
+			interrupt_in_endpoint[num_interrupt_in] = endpoint;
+			++num_interrupt_in;
+		}
+	}
+	
+	/* verify that we found all of the endpoints that we need */
+	if (!((interrupt_pipe & type->needs_interrupt_in) &&
+	      (bulk_in_pipe & type->needs_bulk_in) &&
+	      (bulk_out_pipe & type->needs_bulk_out))) {
+		/* nope, they don't match what we expected */
+		info("descriptors matched, but endpoints did not");
+		return NULL;
+	}
+
+	/* found all that we need */
+	MOD_INC_USE_COUNT;
+	info("%s converter detected", type->name);
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	if (type == &generic_device) {
+		num_ports = num_bulk_out;
+		if (num_ports == 0) {
+			err("Generic device with no bulk out, not allowed.");
+			MOD_DEC_USE_COUNT;
+			return NULL;
+		}
+	} else
+#endif
+		num_ports = type->num_ports;
+
+	serial = get_free_serial (num_ports, &minor);
+	if (serial == NULL) {
+		err("No more free serial devices");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	
+	serial->dev = dev;
+	serial->type = type;
+	serial->interface = interface;
+	serial->minor = minor;
+	serial->num_ports = num_ports;
+	serial->num_bulk_in = num_bulk_in;
+	serial->num_bulk_out = num_bulk_out;
+	serial->num_interrupt_in = num_interrupt_in;
+
+	/* if this device type has a startup function, call it */
+	if (type->startup) {
+		if (type->startup (serial)) {
+			goto probe_error;
+		}
+	}
+
+	/* set up the endpoint information */
+	for (i = 0; i < num_bulk_in; ++i) {
+		endpoint = bulk_in_endpoint[i];
+		port = &serial->port[i];
+		port->read_urb = usb_alloc_urb (0);
+		if (!port->read_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->bulk_in_buffer) {
+			err("Couldn't allocate bulk_in_buffer");
+			goto probe_error;
+		}
+		FILL_BULK_URB(port->read_urb, dev, 
+			      usb_rcvbulkpipe(dev, endpoint->bEndpointAddress),
+			      port->bulk_in_buffer, buffer_size, 
+			      ((serial->type->read_bulk_callback) ?
+			       serial->type->read_bulk_callback :
+			       generic_read_bulk_callback), 
+			      port);
+	}
+
+	for (i = 0; i < num_bulk_out; ++i) {
+		endpoint = bulk_out_endpoint[i];
+		port = &serial->port[i];
+		port->write_urb = usb_alloc_urb(0);
+		if (!port->write_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->bulk_out_size = buffer_size;
+		port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+		port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->bulk_out_buffer) {
+			err("Couldn't allocate bulk_out_buffer");
+			goto probe_error;
+		}
+		FILL_BULK_URB(port->write_urb, dev, 
+			      usb_sndbulkpipe(dev, endpoint->bEndpointAddress),
+			      port->bulk_out_buffer, buffer_size,
+			      ((serial->type->write_bulk_callback) ? 
+			       serial->type->write_bulk_callback : 
+			       generic_write_bulk_callback), 
+			      port);
+	}
+
+	for (i = 0; i < num_interrupt_in; ++i) {
+		endpoint = interrupt_in_endpoint[i];
+		port = &serial->port[i];
+		port->interrupt_in_urb = usb_alloc_urb(0);
+		if (!port->interrupt_in_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->interrupt_in_buffer) {
+			err("Couldn't allocate interrupt_in_buffer");
+			goto probe_error;
+		}
+		FILL_INT_URB(port->interrupt_in_urb, dev, 
+			     usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			     port->interrupt_in_buffer, buffer_size, 
+			     serial->type->read_int_callback,
+			     port, 
+			     endpoint->bInterval);
+	}
+
+	/* initialize some parts of the port structures */
+	/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
+	max_endpoints = MAX(num_bulk_in, num_bulk_out);
+	max_endpoints = MAX(max_endpoints, num_interrupt_in);
+	for (i = 0; i < max_endpoints; ++i) {
+		port = &serial->port[i];
+		port->number = i + serial->minor;
+		port->serial = serial;
+		port->magic = USB_SERIAL_PORT_MAGIC;
+		port->tqueue.routine = port_softint;
+		port->tqueue.data = port;
+		spin_lock_init (&port->port_lock);
+	}
+	
+	/* initialize the devfs nodes for this device and let the user know what ports we are bound to */
+	for (i = 0; i < serial->num_ports; ++i) {
+		tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number);
+		info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", 
+		     type->name, serial->port[i].number, serial->port[i].number);
+	}
+	
+	return serial; /* success */
+
+
+probe_error:
+	for (i = 0; i < num_bulk_in; ++i) {
+		port = &serial->port[i];
+		if (port->read_urb)
+			usb_free_urb (port->read_urb);
+		if (port->bulk_in_buffer)
+			kfree (port->bulk_in_buffer);
+	}
+	for (i = 0; i < num_bulk_out; ++i) {
+		port = &serial->port[i];
+		if (port->write_urb)
+			usb_free_urb (port->write_urb);
+		if (port->bulk_out_buffer)
+			kfree (port->bulk_out_buffer);
+	}
+	for (i = 0; i < num_interrupt_in; ++i) {
+		port = &serial->port[i];
+		if (port->interrupt_in_urb)
+			usb_free_urb (port->interrupt_in_urb);
+		if (port->interrupt_in_buffer)
+			kfree (port->interrupt_in_buffer);
+	}
+		
+	/* return the minor range that this device had */
+	return_serial (serial);
+
+	/* free up any memory that we allocated */
+	kfree (serial);
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+
+static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_serial *serial = (struct usb_serial *) ptr;
+	struct usb_serial_port *port;
+	int i;
+
+	if (serial) {
+		/* fail all future close/read/write/ioctl/etc calls */
+		for (i = 0; i < serial->num_ports; ++i) {
+			if (serial->port[i].tty != NULL)
+				serial->port[i].tty->driver_data = NULL;
+		}
+
+		if (serial->type->shutdown)
+			serial->type->shutdown(serial);
+
+		for (i = 0; i < serial->num_ports; ++i)
+			serial->port[i].active = 0;
+
+		for (i = 0; i < serial->num_bulk_in; ++i) {
+			port = &serial->port[i];
+			if (port->read_urb) {
+				usb_unlink_urb (port->read_urb);
+				usb_free_urb (port->read_urb);
+			}
+			if (port->bulk_in_buffer)
+				kfree (port->bulk_in_buffer);
+		}
+		for (i = 0; i < serial->num_bulk_out; ++i) {
+			port = &serial->port[i];
+			if (port->write_urb) {
+				usb_unlink_urb (port->write_urb);
+				usb_free_urb (port->write_urb);
+			}
+			if (port->bulk_out_buffer)
+				kfree (port->bulk_out_buffer);
+		}
+		for (i = 0; i < serial->num_interrupt_in; ++i) {
+			port = &serial->port[i];
+			if (port->interrupt_in_urb) {
+				usb_unlink_urb (port->interrupt_in_urb);
+				usb_free_urb (port->interrupt_in_urb);
+			}
+			if (port->interrupt_in_buffer)
+				kfree (port->interrupt_in_buffer);
+		}
+
+		for (i = 0; i < serial->num_ports; ++i) {
+			tty_unregister_devfs (&serial_tty_driver, serial->port[i].number);
+			info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->port[i].number);
+		}
+
+		/* return the minor range that this device had */
+		return_serial (serial);
+
+		/* free up any memory that we allocated */
+		kfree (serial);
+
+	} else {
+		info("device disconnected");
+	}
+	
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct tty_driver serial_tty_driver = {
+	magic:			TTY_DRIVER_MAGIC,
+	driver_name:		"usb-serial",
+	name:			"usb/tts/%d",
+	major:			SERIAL_TTY_MAJOR,
+	minor_start:		0,
+	num:			SERIAL_TTY_MINORS,
+	type:			TTY_DRIVER_TYPE_SERIAL,
+	subtype:		SERIAL_TYPE_NORMAL,
+	flags:			TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
+	
+	refcount:		&serial_refcount,
+	table:			serial_tty,
+	termios:		serial_termios,
+	termios_locked:		serial_termios_locked,
+	
+	open:			serial_open,
+	close:			serial_close,
+	write:			serial_write,
+	write_room:		serial_write_room,
+	ioctl:			serial_ioctl,
+	set_termios:		serial_set_termios,
+	throttle:		serial_throttle,
+	unthrottle:		serial_unthrottle,
+	break_ctl:		serial_break,
+	chars_in_buffer:	serial_chars_in_buffer,
+};
+
+
+int usb_serial_init(void)
+{
+	int i;
+	int result;
+
+	/* Initalize our global data */
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		serial_table[i] = NULL;
+	}
+
+	/* register the tty driver */
+	serial_tty_driver.init_termios          = tty_std_termios;
+	serial_tty_driver.init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	if (tty_register_driver (&serial_tty_driver)) {
+		err(__FUNCTION__ " - failed to register tty driver");
+		return -1;
+	}
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	/* register our generic driver with ourselves */
+	usb_serial_register (&generic_device);
+#endif
+	
+	/* register the USB driver */
+	result = usb_register(&usb_serial_driver);
+	if (result < 0) {
+		tty_unregister_driver(&serial_tty_driver);
+		err("usb_register failed for the usb-serial driver. Error number %d", result);
+		return -1;
+	}
+	
+	
+	return 0;
+}
+
+
+void usb_serial_exit(void)
+{
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	/* remove our generic driver */
+	usb_serial_deregister (&generic_device);
+#endif
+	
+	usb_deregister(&usb_serial_driver);
+	tty_unregister_driver(&serial_tty_driver);
+}
+
+
+module_init(usb_serial_init);
+module_exit(usb_serial_exit);
+
+
+int usb_serial_register(struct usb_serial_device_type *new_device)
+{
+	/* Add this device to our list of devices */
+	list_add(&new_device->driver_list, &usb_serial_driver_list);
+
+	info ("USB Serial support registered for %s", new_device->name);
+
+	usb_scan_devices();
+
+	return 0;
+}
+
+
+void usb_serial_deregister(struct usb_serial_device_type *device)
+{
+	struct usb_serial *serial;
+	int i;
+
+	info("USB Serial deregistering driver %s", device->name);
+
+	/* clear out the serial_table if the device is attached to a port */
+	for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		serial = serial_table[i];
+		if ((serial != NULL) && (serial->type == device)) {
+			usb_driver_release_interface (&usb_serial_driver, serial->interface);
+			usb_serial_disconnect (NULL, serial);
+		}
+	}
+
+	list_del(&device->driver_list);
+}
+
+
+
+/* If the usb-serial core is build into the core, the usb-serial drivers
+   need these symbols to load properly as modules. */
+EXPORT_SYMBOL(usb_serial_register);
+EXPORT_SYMBOL(usb_serial_deregister);
+#ifdef USES_EZUSB_FUNCTIONS
+	EXPORT_SYMBOL(ezusb_writememory);
+	EXPORT_SYMBOL(ezusb_set_reset);
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/visor.c linux.18p17/drivers/usb/serial/visor.c
--- linux.vanilla/drivers/usb/serial/visor.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/visor.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,581 @@
+/*
+ * USB HandSpring Visor driver
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ * (09/11/2000) gkh
+ *	Got rid of always calling kmalloc for every urb we wrote out to the
+ *	device.
+ *	Added visor_read_callback so we can keep track of bytes in and out for
+ *	those people who like to know the speed of their device.
+ *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
+ *
+ * (09/06/2000) gkh
+ *	Fixed oops in visor_exit.  Need to uncomment usb_unlink_urb call _after_
+ *	the host controller drivers set urb->dev = NULL when the urb is finished.
+ *
+ * (08/28/2000) gkh
+ *	Added locks for SMP safeness.
+ *
+ * (08/08/2000) gkh
+ *	Fixed endian problem in visor_startup.
+ *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more 
+ *	than once.
+ * 
+ * (07/23/2000) gkh
+ *	Added pool of write urbs to speed up transfers to the visor.
+ * 
+ * (07/19/2000) gkh
+ *	Added module_init and module_exit functions to handle the fact that this
+ *	driver is a loadable module now.
+ *
+ * (07/03/2000) gkh
+ *	Added visor_set_ioctl and visor_set_termios functions (they don't do much
+ *	of anything, but are good for debugging.)
+ * 
+ * (06/25/2000) gkh
+ *	Fixed bug in visor_unthrottle that should help with the disconnect in PPP
+ *	bug that people have been reporting.
+ *
+ * (06/23/2000) gkh
+ *	Cleaned up debugging statements in a quest to find UHCI timeout bug.
+ *
+ * (04/27/2000) Ryan VanderBijl
+ * 	Fixed memory leak in visor_close
+ *
+ * (03/26/2000) gkh
+ *	Split driver up into device specific pieces.
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "visor.h"
+
+#define MIN(a,b)                (((a)<(b))?(a):(b))
+
+/* function prototypes for a handspring visor */
+static int  visor_open		(struct usb_serial_port *port, struct file *filp);
+static void visor_close		(struct usb_serial_port *port, struct file *filp);
+static int  visor_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static void visor_throttle	(struct usb_serial_port *port);
+static void visor_unthrottle	(struct usb_serial_port *port);
+static int  visor_startup	(struct usb_serial *serial);
+static void visor_shutdown	(struct usb_serial *serial);
+static int  visor_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+static void visor_set_termios	(struct usb_serial_port *port, struct termios *old_termios);
+static void visor_write_bulk_callback	(struct urb *urb);
+static void visor_read_bulk_callback	(struct urb *urb);
+
+/* All of the device info needed for the Handspring Visor */
+static __u16	handspring_vendor_id	= HANDSPRING_VENDOR_ID;
+static __u16	handspring_product_id	= HANDSPRING_VISOR_ID;
+struct usb_serial_device_type handspring_device = {
+	name:			"Handspring Visor",
+	idVendor:		&handspring_vendor_id,	/* the Handspring vendor ID */
+	idProduct:		&handspring_product_id,	/* the Handspring Visor product id */
+	needs_interrupt_in:	MUST_HAVE_NOT,		/* this device must not have an interrupt in endpoint */
+	needs_bulk_in:		MUST_HAVE,		/* this device must have a bulk in endpoint */
+	needs_bulk_out:		MUST_HAVE,		/* this device must have a bulk out endpoint */
+	num_interrupt_in:	0,
+	num_bulk_in:		2,
+	num_bulk_out:		2,
+	num_ports:		2,
+	open:			visor_open,
+	close:			visor_close,
+	throttle:		visor_throttle,
+	unthrottle:		visor_unthrottle,
+	startup:		visor_startup,
+	shutdown:		visor_shutdown,
+	ioctl:			visor_ioctl,
+	set_termios:		visor_set_termios,
+	write:			visor_write,
+	write_bulk_callback:	visor_write_bulk_callback,
+	read_bulk_callback:	visor_read_bulk_callback,
+};
+
+
+#define NUM_URBS			24
+#define URB_TRANSFER_BUFFER_SIZE	64
+static struct urb	*write_urb_pool[NUM_URBS];
+static spinlock_t	write_urb_pool_lock;
+static int		bytes_in;
+static int		bytes_out;
+
+
+/******************************************************************************
+ * Handspring Visor specific driver functions
+ ******************************************************************************/
+static int visor_open (struct usb_serial_port *port, struct file *filp)
+{
+	unsigned long flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+	
+	if (!port->active) {
+		port->active = 1;
+		bytes_in = 0;
+		bytes_out = 0;
+
+		/*Start reading from the device*/
+		if (usb_submit_urb(port->read_urb))
+			dbg(__FUNCTION__  " - usb_submit_urb(read bulk) failed");
+	}
+	
+	spin_unlock_irqrestore (&port->port_lock, flags);
+	
+	return 0;
+}
+
+
+static void visor_close (struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial *serial;
+	unsigned char *transfer_buffer;
+	unsigned long flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+			 
+	serial = get_usb_serial (port, __FUNCTION__);
+	if (!serial)
+		return;
+	
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	--port->open_count;
+	MOD_DEC_USE_COUNT;
+
+	if (port->open_count <= 0) {
+		transfer_buffer =  kmalloc (0x12, GFP_KERNEL);
+		if (!transfer_buffer) {
+			err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12);
+		} else {
+			/* send a shutdown message to the device */
+			usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
+					0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+			kfree (transfer_buffer);
+		}
+
+		/* shutdown our bulk read */
+		usb_unlink_urb (port->read_urb);
+		port->active = 0;
+		port->open_count = 0;
+
+	}
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	/* Uncomment the following line if you want to see some statistics in your syslog */
+	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
+}
+
+
+static int visor_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct urb *urb;
+	const unsigned char *current_position = buf;
+	unsigned long flags;
+	int status;
+	int i;
+	int bytes_sent = 0;
+	int transfer_size;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, count, buf);
+	
+	while (count > 0) {
+		/* try to find a free urb in our list of them */
+		urb = NULL;
+		spin_lock_irqsave (&write_urb_pool_lock, flags);
+		for (i = 0; i < NUM_URBS; ++i) {
+			if (write_urb_pool[i]->status != -EINPROGRESS) {
+				urb = write_urb_pool[i];
+				break;
+			}
+		}
+		spin_unlock_irqrestore (&write_urb_pool_lock, flags);
+		if (urb == NULL) {
+			dbg (__FUNCTION__ " - no more free urbs");
+			goto exit;
+		}
+		if (urb->transfer_buffer == NULL) {
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+			if (urb->transfer_buffer == NULL) {
+				err(__FUNCTION__" no more kernel memory...");
+				goto exit;
+			}
+		}
+		
+		transfer_size = MIN (count, URB_TRANSFER_BUFFER_SIZE);
+		if (from_user)
+			copy_from_user (urb->transfer_buffer, current_position, transfer_size);
+		else
+			memcpy (urb->transfer_buffer, current_position, transfer_size);
+		
+		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+		/* build up our urb */
+		FILL_BULK_URB (urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), 
+				urb->transfer_buffer, transfer_size, visor_write_bulk_callback, port);
+		urb->transfer_flags |= USB_QUEUE_BULK;
+
+		/* send it down the pipe */
+		status = usb_submit_urb(urb);
+		if (status)
+			dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status);
+
+		current_position += transfer_size;
+		bytes_sent += transfer_size;
+		count -= transfer_size;
+		bytes_out += transfer_size;
+	}
+
+exit:
+	return bytes_sent;
+} 
+
+
+static void visor_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	return;
+}
+
+
+static void visor_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+		bytes_in += urb->actual_length;
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		dbg(__FUNCTION__ " - failed resubmitting read urb");
+	return;
+}
+
+
+static void visor_throttle (struct usb_serial_port *port)
+{
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	usb_unlink_urb (port->read_urb);
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return;
+}
+
+
+static void visor_unthrottle (struct usb_serial_port *port)
+{
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	spin_lock_irqsave (&port->port_lock, flags);
+
+	if (usb_submit_urb (port->read_urb))
+		dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+
+	spin_unlock_irqrestore (&port->port_lock, flags);
+
+	return;
+}
+
+
+static int  visor_startup (struct usb_serial *serial)
+{
+	int response;
+	int i;
+	unsigned char *transfer_buffer =  kmalloc (256, GFP_KERNEL);
+
+	if (!transfer_buffer) {
+		err(__FUNCTION__ " - kmalloc(%d) failed.", 256);
+		return -ENOMEM;
+	}
+
+	dbg(__FUNCTION__);
+
+	dbg(__FUNCTION__ " - Set config to 1");
+	usb_set_configuration (serial->dev, 1);
+
+	/* send a get connection info request */
+	response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION,
+					0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+	if (response < 0) {
+		err(__FUNCTION__ " - error getting connection information");
+	} else {
+		struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer;
+		char *string;
+
+		le16_to_cpus(&connection_info->num_ports);
+		info("%s: Number of ports: %d", serial->type->name, connection_info->num_ports);
+		for (i = 0; i < connection_info->num_ports; ++i) {
+			switch (connection_info->connections[i].port_function_id) {
+				case VISOR_FUNCTION_GENERIC:
+					string = "Generic";
+					break;
+				case VISOR_FUNCTION_DEBUGGER:
+					string = "Debugger";
+					break;
+				case VISOR_FUNCTION_HOTSYNC:
+					string = "HotSync";
+					break;
+				case VISOR_FUNCTION_CONSOLE:
+					string = "Console";
+					break;
+				case VISOR_FUNCTION_REMOTE_FILE_SYS:
+					string = "Remote File System";
+					break;
+				default:
+					string = "unknown";
+					break;	
+			}
+			info("%s: port %d, is for %s use and is bound to ttyUSB%d", serial->type->name, connection_info->connections[i].port, string, serial->minor + i);
+		}
+	}
+
+	/* ask for the number of bytes available, but ignore the response as it is broken */
+	response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE,
+					0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
+	if (response < 0) {
+		err(__FUNCTION__ " - error getting bytes available request");
+	}
+
+	kfree (transfer_buffer);
+
+	/* continue on with initialization */
+	return 0;
+}
+
+
+static void visor_shutdown (struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			visor_close (&serial->port[i], NULL);
+		}
+	}
+}
+
+
+static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd);
+
+	return -ENOIOCTLCMD;
+}
+
+
+/* This function is all nice and good, but we don't change anything based on it :) */
+static void visor_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+	unsigned int cflag = port->tty->termios->c_cflag;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+			dbg(__FUNCTION__ " - nothing to change...");
+			return;
+		}
+	}
+
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg(__FUNCTION__" - no tty structures");
+		return;
+	}
+
+	/* get the byte size */
+	switch (cflag & CSIZE) {
+		case CS5:	dbg(__FUNCTION__ " - data bits = 5");   break;
+		case CS6:	dbg(__FUNCTION__ " - data bits = 6");   break;
+		case CS7:	dbg(__FUNCTION__ " - data bits = 7");   break;
+		default:
+		case CS8:	dbg(__FUNCTION__ " - data bits = 8");   break;
+	}
+	
+	/* determine the parity */
+	if (cflag & PARENB)
+		if (cflag & PARODD)
+			dbg(__FUNCTION__ " - parity = odd");
+		else
+			dbg(__FUNCTION__ " - parity = even");
+	else
+		dbg(__FUNCTION__ " - parity = none");
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		dbg(__FUNCTION__ " - stop bits = 2");
+	else
+		dbg(__FUNCTION__ " - stop bits = 1");
+
+	
+	/* figure out the flow control settings */
+	if (cflag & CRTSCTS)
+		dbg(__FUNCTION__ " - RTS/CTS is enabled");
+	else
+		dbg(__FUNCTION__ " - RTS/CTS is disabled");
+	
+	/* determine software flow control */
+	if (I_IXOFF(port->tty))
+		dbg(__FUNCTION__ " - XON/XOFF is enabled, XON = %2x, XOFF = %2x", START_CHAR(port->tty), STOP_CHAR(port->tty));
+	else
+		dbg(__FUNCTION__ " - XON/XOFF is disabled");
+
+	/* get the baud rate wanted */
+	dbg(__FUNCTION__ " - baud rate = %d", tty_get_baud_rate(port->tty));
+
+	return;
+}
+
+
+static int __init visor_init (void)
+{
+	struct urb *urb;
+	int i;
+
+	usb_serial_register (&handspring_device);
+	
+	/* create our write urb pool and transfer buffers */ 
+	spin_lock_init (&write_urb_pool_lock);
+	for (i = 0; i < NUM_URBS; ++i) {
+		urb = usb_alloc_urb(0);
+		write_urb_pool[i] = urb;
+		if (urb == NULL) {
+			err("No more urbs???");
+			continue;
+		}
+
+		urb->transfer_buffer = NULL;
+		urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			err (__FUNCTION__ " - out of memory for urb buffers.");
+			continue;
+		}
+	}
+	
+	return 0;
+}
+
+
+static void __exit visor_exit (void)
+{
+	int i;
+	unsigned long flags;
+
+	usb_serial_deregister (&handspring_device);
+
+	spin_lock_irqsave (&write_urb_pool_lock, flags);
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (write_urb_pool[i]) {
+			/* FIXME - uncomment the following usb_unlink_urb call when
+			 * the host controllers get fixed to set urb->dev = NULL after
+			 * the urb is finished.  Otherwise this call oopses. */
+			/* usb_unlink_urb(write_urb_pool[i]); */
+			if (write_urb_pool[i]->transfer_buffer)
+				kfree(write_urb_pool[i]->transfer_buffer);
+			usb_free_urb (write_urb_pool[i]);
+		}
+	}
+
+	spin_unlock_irqrestore (&write_urb_pool_lock, flags);
+}
+
+
+module_init(visor_init);
+module_exit(visor_exit);
+
+MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
+MODULE_DESCRIPTION("USB HandSpring Visor driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/visor.h linux.18p17/drivers/usb/serial/visor.h
--- linux.vanilla/drivers/usb/serial/visor.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/visor.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,74 @@
+/*
+ * USB HandSpring Visor driver
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ */
+
+#ifndef __LINUX_USB_SERIAL_VISOR_H
+#define __LINUX_USB_SERIAL_VISOR_H
+
+
+#define HANDSPRING_VENDOR_ID		0x082d
+#define HANDSPRING_VISOR_ID		0x0100
+
+/****************************************************************************
+ * Handspring Visor Vendor specific request codes (bRequest values)
+ * A big thank you to Handspring for providing the following information.
+ * If anyone wants the original file where these values and structures came
+ * from, send email to <greg@kroah.com>.
+ ****************************************************************************/
+
+/****************************************************************************
+ * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ ****************************************************************************/
+#define VISOR_REQUEST_BYTES_AVAILABLE		0x01
+
+/****************************************************************************
+ * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ ****************************************************************************/
+#define VISOR_CLOSE_NOTIFICATION		0x02
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ ****************************************************************************/
+#define VISOR_GET_CONNECTION_INFORMATION	0x03
+
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ ****************************************************************************/
+struct visor_connection_info {
+	__u16	num_ports;
+	struct {
+		__u8	port_function_id;
+		__u8	port;
+	} connections[2];
+};
+
+
+/* struct visor_connection_info.connection[x].port defines: */
+#define VISOR_ENDPOINT_1		0x01
+#define VISOR_ENDPOINT_2		0x02
+
+/* struct visor_connection_info.connection[x].port_function_id defines: */
+#define VISOR_FUNCTION_GENERIC		0x00
+#define VISOR_FUNCTION_DEBUGGER		0x01
+#define VISOR_FUNCTION_HOTSYNC		0x02
+#define VISOR_FUNCTION_CONSOLE		0x03
+#define VISOR_FUNCTION_REMOTE_FILE_SYS	0x04
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/whiteheat.c linux.18p17/drivers/usb/serial/whiteheat.c
--- linux.vanilla/drivers/usb/serial/whiteheat.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/whiteheat.c	Fri Sep 22 14:59:06 2000
@@ -0,0 +1,532 @@
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *	Copyright (C) 1999, 2000
+ *	    Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ * 
+ * (09/11/2000) gkh
+ *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
+ *
+ * (07/19/2000) gkh
+ *	Added module_init and module_exit functions to handle the fact that this
+ *	driver is a loadable module now.
+ *	Fixed bug with port->minor that was found by Al Borchers
+ *
+ * (07/04/2000) gkh
+ *	Added support for port settings. Baud rate can now be changed. Line signals
+ *	are not transferred to and from the tty layer yet, but things seem to be 
+ *	working well now.
+ *
+ * (05/04/2000) gkh
+ *	First cut at open and close commands. Data can flow through the ports at
+ *	default speeds now.
+ *
+ * (03/26/2000) gkh
+ *	Split driver up into device specific pieces.
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "whiteheat_fw.h"		/* firmware for the ConnectTech WhiteHEAT device */
+
+#include "whiteheat.h"			/* WhiteHEAT specific commands */
+
+#define CONNECT_TECH_VENDOR_ID		0x0710
+#define CONNECT_TECH_FAKE_WHITE_HEAT_ID	0x0001
+#define CONNECT_TECH_WHITE_HEAT_ID	0x8001
+
+/* function prototypes for the Connect Tech WhiteHEAT serial converter */
+static int  whiteheat_open		(struct usb_serial_port *port, struct file *filp);
+static void whiteheat_close		(struct usb_serial_port *port, struct file *filp);
+static int  whiteheat_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+static void whiteheat_set_termios	(struct usb_serial_port *port, struct termios * old);
+static void whiteheat_throttle		(struct usb_serial_port *port);
+static void whiteheat_unthrottle	(struct usb_serial_port *port);
+static int  whiteheat_startup		(struct usb_serial *serial);
+static void whiteheat_shutdown		(struct usb_serial *serial);
+
+/* All of the device info needed for the Connect Tech WhiteHEAT */
+static __u16	connecttech_vendor_id			= CONNECT_TECH_VENDOR_ID;
+static __u16	connecttech_whiteheat_fake_product_id	= CONNECT_TECH_FAKE_WHITE_HEAT_ID;
+static __u16	connecttech_whiteheat_product_id	= CONNECT_TECH_WHITE_HEAT_ID;
+struct usb_serial_device_type whiteheat_fake_device = {
+	name:			"Connect Tech - WhiteHEAT - (prerenumeration)",
+	idVendor:		&connecttech_vendor_id,			/* the Connect Tech vendor id */
+	idProduct:		&connecttech_whiteheat_fake_product_id,	/* the White Heat initial product id */
+	needs_interrupt_in:	DONT_CARE,				/* don't have to have an interrupt in endpoint */
+	needs_bulk_in:		DONT_CARE,				/* don't have to have a bulk in endpoint */
+	needs_bulk_out:		DONT_CARE,				/* don't have to have a bulk out endpoint */
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		whiteheat_startup	
+};
+struct usb_serial_device_type whiteheat_device = {
+	name:			"Connect Tech - WhiteHEAT",
+	idVendor:		&connecttech_vendor_id,			/* the Connect Tech vendor id */
+	idProduct:		&connecttech_whiteheat_product_id,	/* the White Heat real product id */
+	needs_interrupt_in:	DONT_CARE,				/* don't have to have an interrupt in endpoint */
+	needs_bulk_in:		DONT_CARE,				/* don't have to have a bulk in endpoint */
+	needs_bulk_out:		DONT_CARE,				/* don't have to have a bulk out endpoint */
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		4,
+	open:			whiteheat_open,
+	close:			whiteheat_close,
+	throttle:		whiteheat_throttle,
+	unthrottle:		whiteheat_unthrottle,
+	ioctl:			whiteheat_ioctl,
+	set_termios:		whiteheat_set_termios,
+	shutdown:		whiteheat_shutdown,
+};
+
+struct whiteheat_private {
+	__u8			command_finished;
+	wait_queue_head_t	wait_command;	/* for handling sleeping while waiting for a command to finish */
+};
+
+
+/* local function prototypes */
+static inline void set_rts	(struct usb_serial_port *port, unsigned char rts);
+static inline void set_dtr	(struct usb_serial_port *port, unsigned char dtr);
+static inline void set_break	(struct usb_serial_port *port, unsigned char brk);
+
+
+
+#define COMMAND_PORT		4
+#define COMMAND_TIMEOUT		(2*HZ)	/* 2 second timeout for a command */
+
+/*****************************************************************************
+ * Connect Tech's White Heat specific driver functions
+ *****************************************************************************/
+static void command_port_write_callback (struct urb *urb)
+{
+	dbg (__FUNCTION__);
+
+	if (urb->status) {
+		dbg ("nonzero urb status: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+
+	return;
+}
+
+
+static void command_port_read_callback (struct urb *urb)
+{
+	struct whiteheat_private *info = (struct whiteheat_private *)urb->context;
+	unsigned char *data = urb->transfer_buffer;
+
+	dbg (__FUNCTION__);
+
+	if (urb->status) {
+		dbg (__FUNCTION__ " - nonzero urb status: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	/* right now, if the command is COMMAND_COMPLETE, just flip the bit saying the command finished */
+	/* in the future we're going to have to pay attention to the actual command that completed */
+	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
+		info->command_finished = TRUE;
+	}
+	
+	return;
+}
+
+
+static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *data, __u8 datasize)
+{
+	struct whiteheat_private *info;
+	struct usb_serial_port *port;
+	int timeout;
+	__u8 *transfer_buffer;
+
+	dbg(__FUNCTION__" - command %d", command);
+
+	port = &serial->port[COMMAND_PORT];
+	info = (struct whiteheat_private *)port->private;
+	info->command_finished = FALSE;
+	
+	transfer_buffer = (__u8 *)port->write_urb->transfer_buffer;
+	transfer_buffer[0] = command;
+	memcpy (&transfer_buffer[1], data, datasize);
+	port->write_urb->transfer_buffer_length = datasize + 1;
+	if (usb_submit_urb (port->write_urb)) {
+		dbg (__FUNCTION__" - submit urb failed");
+		return -1;
+	}
+
+	/* wait for the command to complete */
+	timeout = COMMAND_TIMEOUT;
+	while (timeout && (info->command_finished == FALSE)) {
+		timeout = interruptible_sleep_on_timeout (&info->wait_command, timeout);
+	}
+
+	if (info->command_finished == FALSE) {
+		dbg (__FUNCTION__ " - command timed out.");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct whiteheat_min_set	open_command;
+	struct usb_serial_port 		*command_port;
+	struct whiteheat_private	*info;
+
+	dbg(__FUNCTION__" - port %d", port->number);
+
+	if (port->active) {
+		dbg (__FUNCTION__ " - device already open");
+		return -EINVAL;
+	}
+	port->active = 1;
+
+	/* set up some stuff for our command port */
+	command_port = &port->serial->port[COMMAND_PORT];
+	if (command_port->private == NULL) {
+		info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL);
+		if (info == NULL) {
+			err(__FUNCTION__ " - out of memory");
+			return -ENOMEM;
+		}
+		
+		init_waitqueue_head(&info->wait_command);
+		command_port->private = info;
+		command_port->write_urb->complete = command_port_write_callback;
+		command_port->read_urb->complete = command_port_read_callback;
+		usb_submit_urb (command_port->read_urb);	
+	}
+	
+	/* Start reading from the device */
+	if (usb_submit_urb(port->read_urb))
+		dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+
+	/* send an open port command */
+	open_command.port = port->number - port->serial->minor;
+	whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
+
+	/* Need to do device specific setup here (control lines, baud rate, etc.) */
+	/* FIXME!!! */
+
+	dbg(__FUNCTION__ " - exit");
+	
+	return 0;
+}
+
+
+static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
+{
+	struct whiteheat_min_set	close_command;
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	/* send a close command to the port */
+	close_command.port = port->number - port->serial->minor;
+	whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
+
+	/* Need to change the control lines here */
+	/* FIXME */
+	
+	/* shutdown our bulk reads and writes */
+	usb_unlink_urb (port->write_urb);
+	usb_unlink_urb (port->read_urb);
+	port->active = 0;
+}
+
+
+static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd);
+
+	return -ENOIOCTLCMD;
+}
+
+
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+	unsigned int cflag = port->tty->termios->c_cflag;
+	struct whiteheat_port_settings port_settings;
+
+	dbg(__FUNCTION__ " -port %d", port->number);
+
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+			dbg(__FUNCTION__ " - nothing to change...");
+			return;
+		}
+	}
+
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg(__FUNCTION__" - no tty structures");
+		return;
+	}
+	
+	/* get the byte size */
+	switch (cflag & CSIZE) {
+		case CS5:	port_settings.bits = 5;   break;
+		case CS6:	port_settings.bits = 6;   break;
+		case CS7:	port_settings.bits = 7;   break;
+		default:
+		case CS8:	port_settings.bits = 8;   break;
+	}
+	dbg(__FUNCTION__ " - data bits = %d", port_settings.bits);
+	
+	/* determine the parity */
+	if (cflag & PARENB)
+		if (cflag & PARODD)
+			port_settings.parity = 'o';
+		else
+			port_settings.parity = 'e';
+	else
+		port_settings.parity = 'n';
+	dbg(__FUNCTION__ " - parity = %c", port_settings.parity);
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		port_settings.stop = 2;
+	else
+		port_settings.stop = 1;
+	dbg(__FUNCTION__ " - stop bits = %d", port_settings.stop);
+
+	
+	/* figure out the flow control settings */
+	if (cflag & CRTSCTS)
+		port_settings.hflow = (WHITEHEAT_CTS_FLOW | WHITEHEAT_RTS_FLOW);
+	else
+		port_settings.hflow = 0;
+	dbg(__FUNCTION__ " - hardware flow control = %s %s %s %s",
+	    (port_settings.hflow | WHITEHEAT_CTS_FLOW) ? "CTS" : "",
+	    (port_settings.hflow | WHITEHEAT_RTS_FLOW) ? "RTS" : "",
+	    (port_settings.hflow | WHITEHEAT_DSR_FLOW) ? "DSR" : "",
+	    (port_settings.hflow | WHITEHEAT_DTR_FLOW) ? "DTR" : "");
+	
+	/* determine software flow control */
+	if (I_IXOFF(port->tty))
+		port_settings.sflow = 'b';
+	else
+		port_settings.sflow = 'n';
+	dbg(__FUNCTION__ " - software flow control = %c", port_settings.sflow);
+	
+	port_settings.xon = START_CHAR(port->tty);
+	port_settings.xoff = STOP_CHAR(port->tty);
+	dbg(__FUNCTION__ " - XON = %2x, XOFF = %2x", port_settings.xon, port_settings.xoff);
+
+	/* get the baud rate wanted */
+	port_settings.baud = tty_get_baud_rate(port->tty);
+	dbg(__FUNCTION__ " - baud rate = %d", port_settings.baud);
+
+	/* handle any settings that aren't specified in the tty structure */
+	port_settings.lloop = 0;
+	
+	/* now send the message to the device */
+	whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings));
+	
+	return;
+}
+
+
+static void whiteheat_throttle (struct usb_serial_port *port)
+{
+	dbg(__FUNCTION__" - port %d", port->number);
+
+	/* Change the control signals */
+	/* FIXME!!! */
+
+	return;
+}
+
+
+static void whiteheat_unthrottle (struct usb_serial_port *port)
+{
+	dbg(__FUNCTION__" - port %d", port->number);
+
+	/* Change the control signals */
+	/* FIXME!!! */
+
+	return;
+}
+
+
+/* steps to download the firmware to the WhiteHEAT device:
+ - hold the reset (by writing to the reset bit of the CPUCS register)
+ - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
+ - release the reset (by writing to the CPUCS register)
+ - download the WH.HEX file for all addresses greater than 0x1b3f using
+   VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
+ - hold the reset
+ - download the WH.HEX file for all addresses less than 0x1b40 using
+   VENDOR_REQUEST_ANCHOR_LOAD
+ - release the reset
+ - device renumerated itself and comes up as new device id with all
+   firmware download completed.
+*/
+static int  whiteheat_startup (struct usb_serial *serial)
+{
+	int response;
+	const struct whiteheat_hex_record *record;
+	
+	dbg(__FUNCTION__);
+	
+	response = ezusb_set_reset (serial, 1);
+
+	record = &whiteheat_loader[0];
+	while (record->address != 0xffff) {
+		response = ezusb_writememory (serial, record->address, 
+				(unsigned char *)record->data, record->data_size, 0xa0);
+		if (response < 0) {
+			err(__FUNCTION__ " - ezusb_writememory failed for loader (%d %04X %p %d)", 
+				response, record->address, record->data, record->data_size);
+			break;
+		}
+		++record;
+	}
+
+	response = ezusb_set_reset (serial, 0);
+
+	record = &whiteheat_firmware[0];
+	while (record->address < 0x1b40) {
+		++record;
+	}
+	while (record->address != 0xffff) {
+		response = ezusb_writememory (serial, record->address, 
+				(unsigned char *)record->data, record->data_size, 0xa3);
+		if (response < 0) {
+			err(__FUNCTION__ " - ezusb_writememory failed for first firmware step (%d %04X %p %d)", 
+				response, record->address, record->data, record->data_size);
+			break;
+		}
+		++record;
+	}
+	
+	response = ezusb_set_reset (serial, 1);
+
+	record = &whiteheat_firmware[0];
+	while (record->address < 0x1b40) {
+		response = ezusb_writememory (serial, record->address, 
+				(unsigned char *)record->data, record->data_size, 0xa0);
+		if (response < 0) {
+			err(__FUNCTION__" - ezusb_writememory failed for second firmware step (%d %04X %p %d)", 
+				response, record->address, record->data, record->data_size);
+			break;
+		}
+		++record;
+	}
+
+	response = ezusb_set_reset (serial, 0);
+
+	/* we want this device to fail to have a driver assigned to it. */
+	return 1;
+}
+
+
+static void whiteheat_shutdown (struct usb_serial *serial)
+{
+	struct usb_serial_port 		*command_port;
+
+	dbg(__FUNCTION__);
+
+	/* free up our private data for our command port */
+	command_port = &serial->port[COMMAND_PORT];
+	if (command_port->private != NULL) {
+		kfree (command_port->private);
+		command_port->private = NULL;
+	}
+
+	return;
+}
+
+
+
+
+static void set_command (struct usb_serial_port *port, unsigned char state, unsigned char command)
+{
+	struct whiteheat_rdb_set rdb_command;
+	
+	/* send a set rts command to the port */
+	rdb_command.port = port->number - port->serial->minor;
+	rdb_command.state = state;
+
+	whiteheat_send_cmd (port->serial, command, (__u8 *)&rdb_command, sizeof(rdb_command));
+}
+
+
+static inline void set_rts (struct usb_serial_port *port, unsigned char rts)
+{
+	set_command (port, rts, WHITEHEAT_SET_RTS);
+}
+
+
+static inline void set_dtr (struct usb_serial_port *port, unsigned char dtr)
+{
+	set_command (port, dtr, WHITEHEAT_SET_DTR);
+}
+
+
+static inline void set_break (struct usb_serial_port *port, unsigned char brk)
+{
+	set_command (port, brk, WHITEHEAT_SET_BREAK);
+}
+
+
+static int __init whiteheat_init (void)
+{
+	usb_serial_register (&whiteheat_fake_device);
+	usb_serial_register (&whiteheat_device);
+	return 0;
+}
+
+
+static void __exit whiteheat_exit (void)
+{
+	usb_serial_deregister (&whiteheat_fake_device);
+	usb_serial_deregister (&whiteheat_device);
+}
+
+
+module_init(whiteheat_init);
+module_exit(whiteheat_exit);
+
+MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
+MODULE_DESCRIPTION("USB ConnectTech WhiteHEAT driver");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/whiteheat.h linux.18p17/drivers/usb/serial/whiteheat.h
--- linux.vanilla/drivers/usb/serial/whiteheat.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/whiteheat.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,169 @@
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ *      Copyright (C) 1999, 2000
+ *          Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_WHITEHEAT_H
+#define __LINUX_USB_SERIAL_WHITEHEAT_H
+
+
+#define FALSE				0
+#define TRUE				1
+
+/* WhiteHEAT commands */
+#define WHITEHEAT_OPEN			1	/* open the port */
+#define WHITEHEAT_CLOSE			2	/* close the port */
+#define WHITEHEAT_SETUP_PORT		3	/* change port settings */
+#define WHITEHEAT_SET_RTS		4	/* turn RTS on or off */
+#define WHITEHEAT_SET_DTR		5	/* turn DTR on or off */
+#define WHITEHEAT_SET_BREAK		6	/* turn BREAK on or off */
+#define WHITEHEAT_DUMP			7	/* dump memory */
+#define WHITEHEAT_STATUS		8	/* get status */
+#define WHITEHEAT_PURGE			9	/* clear the UART fifos */
+#define WHITEHEAT_GET_DTR_RTS		10	/* get the state of DTR and RTS for a port */
+#define WHITEHEAT_GET_HW_INFO		11	/* get EEPROM info and hardware ID */
+#define WHITEHEAT_REPORT_TX_DONE	12	/* get the next TX done */
+#define WHITEHEAT_EVENT			13	/* unsolicited status events */
+#define WHITEHEAT_ECHO			14	/* send data to the indicated IN endpoint */
+#define WHITEHEAT_DO_TEST		15	/* perform the specified test */
+#define WHITEHEAT_CMD_COMPLETE		16	/* reply for certain commands */
+
+/* Data for the WHITEHEAT_SETUP_PORT command */
+#define WHITEHEAT_CTS_FLOW		0x08
+#define WHITEHEAT_RTS_FLOW		0x80
+#define WHITEHEAT_DSR_FLOW		0x10
+#define WHITEHEAT_DTR_FLOW		0x02
+struct whiteheat_port_settings {
+	__u8	port;		/* port number (1 to N) */
+	__u32	baud;		/* any value allowed, default 9600, arrives little endian, range is 7 - 460800 */
+	__u8	bits;		/* 5, 6, 7, or 8, default 8 */
+	__u8	stop;		/* 1 or 2, default 1 (2 = 1.5 if bits = 5) */
+	__u8	parity;		/* 'n, e, o, 0, or 1' (ascii), default 'n'
+				 *	n = none	e = even	o = odd
+				 *	0 = force 0	1 = force 1	*/
+	__u8	sflow;		/* 'n, r, t, or b' (ascii), default 'n'
+				 *	n = none
+				 *	r = receive (XOFF/XON transmitted when receiver fills / empties)
+				 *	t = transmit (XOFF/XON received will stop/start TX)
+				 *	b = both 	*/
+	__u8	xoff;		/* XOFF byte value, default 0x13 */
+	__u8	xon;		/* XON byte value, default 0x11 */
+	__u8	hflow;		/* bits indicate mode as follows:
+				 *	CTS (0x08) (CTS off/on will control/cause TX off/on)
+				 *	DSR (0x10) (DSR off/on will control/cause TX off/on)
+				 *	RTS (0x80) (RTS off/on when receiver fills/empties)
+				 *	DTR (0x02) (DTR off/on when receiver fills/empties) */
+	__u8	lloop;		/* local loopback 0 or 1, default 0 */
+};
+
+/* data for WHITEHEAT_SET_RTS, WHITEHEAT_SET_DTR, and WHITEHEAT_SET_BREAK commands */
+struct whiteheat_rdb_set {
+	__u8	port;		/* port number (1 to N) */
+	__u8	state;		/* 0 = off, non-zero = on */
+};
+
+/* data for:
+	WHITEHEAT_OPEN
+	WHITEHEAT_CLOSE
+	WHITEHEAT_STATUS
+	WHITEHEAT_GET_DTR_RTS
+	WHITEHEAT_REPORT_TX_DONE */
+struct whiteheat_min_set {
+	__u8	port;		/* port number (1 to N) */
+};
+
+/* data for WHITEHEAT_PURGE command */
+#define WHITEHEAT_PURGE_INPUT		0x01
+#define WHITEHEAT_PURGE_OUTPUT		0x02
+struct whiteheat_purge_set {
+	__u8	port;		/* port number (1 to N) */
+	__u8	what;		/* bit pattern of what to purge */
+};
+
+/* data for WHITEHEAT_DUMP command */
+struct whiteheat_dump_info {
+	__u8	mem_type;	/* memory type: 'd' = data, 'i' = idata, 'b' = bdata, 'x' = xdata */
+	__u16	addr;		/* memory address to dump, address range depends on the above mem_type:
+				 *	'd' = 0 to ff (80 to FF is SFR's)
+				 *	'i' = 80 to ff
+				 *	'b' = 20 to 2f (bits returned as bytes)
+				 *	'x' = 0000 to ffff (also code space)	*/
+	__u16	length;		/* number of bytes to dump, max 64 */
+};
+
+/* data for WHITEHEAT_ECHO command */
+struct whiteheat_echo_set {
+	__u8	port;		/* port number (1 to N) */
+	__u8	length;		/* length of message to echo */
+	__u8	echo_data[61];	/* data to echo */
+};
+
+/* data returned from WHITEHEAT_STATUS command */
+#define WHITEHEAT_OVERRUN_ERROR		0x02
+#define WHITEHEAT_PARITY_ERROR		0x04
+#define WHITEHEAT_FRAMING_ERROR		0x08
+#define WHITEHEAT_BREAK_ERROR		0x10
+
+#define WHITEHEAT_OHFLOW		0x01	/* TX is stopped by CTS (waiting for CTS to go ON) */
+#define WHITEHEAT_IHFLOW		0x02	/* remote TX is stopped by RTS */
+#define WHITEHEAT_OSFLOW		0x04	/* TX is stopped by XOFF received (waiting for XON to occur) */
+#define WHITEHEAT_ISFLOW		0x08	/* remote TX is stopped by XOFF transmitted */
+#define WHITEHEAT_TX_DONE		0x80	/* TX has completed */
+
+#define WHITEHEAT_MODEM_EVENT		0x01
+#define WHITEHEAT_ERROR_EVENT		0x02
+#define WHITEHEAT_FLOW_EVENT		0x04
+#define WHITEHEAT_CONNECT_EVENT		0x08
+struct whiteheat_status_info {
+	__u8	port;		/* port number (1 to N) */
+	__u8	event;		/* indicates which of the following bytes are the current event */
+	__u8	modem;		/* modem signal status (copy of UART MSR register) */
+	__u8	error;		/* PFO and RX break (copy of UART LSR register) */
+	__u8	flow;		/* flow control state */
+	__u8	connect;	/* connect state, non-zero value indicates connected */
+};
+
+/* data returned from WHITEHEAT_EVENT command */
+struct whiteheat_event {
+	__u8	port;		/* port number (1 to N) */
+	__u8	event;		/* indicates which of the following bytes are the current event */
+	__u8	info;		/* either modem, error, flow, or connect information */
+};
+
+/* data retured by the WHITEHEAT_GET_HW_INFO command */
+struct whiteheat_hw_info {
+	__u8	hw_id;		/* hardware id number, WhiteHEAT = 0 */
+	__u8	sw_major_rev;	/* major version number */
+	__u8	sw_minor_rev;	/* minor version number */
+	struct whiteheat_hw_eeprom_info {
+		__u8	b0;			/* B0 */
+		__u8	vendor_id_low;		/* vendor id (low byte) */
+		__u8	vendor_id_high;		/* vendor id (high byte) */
+		__u8	product_id_low;		/* product id (low byte) */
+		__u8	product_id_high;	/* product id (high byte) */
+		__u8	device_id_low;		/* device id (low byte) */
+		__u8	device_id_high;		/* device id (high byte) */
+		__u8	not_used_1;
+		__u8	serial_number_0;	/* serial number (low byte) */
+		__u8	serial_number_1;	/* serial number */
+		__u8	serial_number_2;	/* serial number */
+		__u8	serial_number_3;	/* serial number (high byte) */
+		__u8	not_used_2;
+		__u8	not_used_3;
+		__u8	checksum_low;		/* checksum (low byte) */
+		__u8	checksum_high;		/* checksum (high byte */
+	} hw_eeprom_info;	/* EEPROM contents */
+};
+
+#endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/whiteheat_fw.h linux.18p17/drivers/usb/serial/whiteheat_fw.h
--- linux.vanilla/drivers/usb/serial/whiteheat_fw.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/serial/whiteheat_fw.h	Fri Sep  1 13:51:29 2000
@@ -0,0 +1,1556 @@
+/*****************************************************************************
+ *
+ *      whiteheat.h  --  ConnectTech WhiteHEAT Firmware.
+ *
+ *      Copyright (C) 2000  ConnectTech Inc (http://www.connecttech.com/)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * (04/09/2000) gkh
+ *	Updated the firmware with the latest provided by ConnectTech.
+ *
+ * (01/16/2000) gkh
+ *	Fixed my intel hex processing tool, so now the firmware actually
+ *	matches the original file (this was causing a few problems...)
+ *
+ * (01/15/2000) gkh
+ *	Added debug loader firmware if DEBUG is #defined:
+ *		Port 1 LED flashes when the vend_ax program is running
+ *		Port 2 LED flashes when any SETUP command arrives
+ *		Port 3 LED flashes when any valid VENDOR request occurs
+ *		Port 4 LED flashes when the EXTERNAL RAM DOWNLOAD request occurs
+ *
+ * version 1.0 (01/09/2000) gkh
+ *	Original firmware from ConnectTech massaged a little to be program
+ *	readable.
+ *
+ *****************************************************************************/
+
+#define whiteheat_DATE "20000106"
+
+struct whiteheat_hex_record {
+	__u16	address;
+	__u8	data_size;
+	__u8	data[16];
+};
+
+static const struct whiteheat_hex_record whiteheat_firmware[] = {
+{ 0x0000,	3,	{0x02, 0x93, 0xa9} },
+{ 0x0003,	3,	{0x02, 0x13, 0x12} },
+{ 0x000b,	3,	{0x02, 0x0a, 0x80} },
+{ 0x0033,	3,	{0x02, 0x08, 0x15} },
+{ 0x0043,	3,	{0x02, 0x0a, 0x00} },
+{ 0x005b,	3,	{0x02, 0x82, 0x31} },
+{ 0x0370,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x73, 0x14, 0x70, 0x03, 0x02, 0x04, 0xe7, 0x24} },
+{ 0x0380,	16,	{0xfe, 0x70, 0x03, 0x02, 0x05, 0x45, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x64, 0x14, 0x70, 0x03} },
+{ 0x0390,	16,	{0x02, 0x04, 0x52, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x49, 0x24} },
+{ 0x03a0,	16,	{0x05, 0x60, 0x03, 0x02, 0x05, 0x94, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60} },
+{ 0x03b0,	16,	{0x36, 0x24, 0x02, 0x70, 0x7b, 0x74, 0x12, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5} },
+{ 0x03c0,	16,	{0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x09, 0x7b, 0xea, 0x49, 0x60, 0x0d} },
+{ 0x03d0,	16,	{0xea, 0x90, 0x7f, 0xd4, 0xf0, 0xe9, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4} },
+{ 0x03e0,	16,	{0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x09, 0x3a, 0xea} },
+{ 0x03f0,	16,	{0x49, 0x60, 0x33, 0x12, 0x9b, 0x72, 0xf5, 0x4b, 0x90, 0x7f, 0xee, 0xe0, 0xff, 0xe5, 0x4b, 0xd3} },
+{ 0x0400,	16,	{0x9f, 0x40, 0x03, 0xe0, 0xf5, 0x4b, 0xe5, 0x4b, 0xd3, 0x94, 0x40, 0x40, 0x03, 0x75, 0x4b, 0x40} },
+{ 0x0410,	16,	{0xae, 0x02, 0xaf, 0x01, 0x7c, 0x7f, 0x7d, 0x00, 0xab, 0x4b, 0x12, 0x8d, 0xd9, 0x90, 0x7f, 0xb5} },
+{ 0x0420,	16,	{0xe5, 0x4b, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b} },
+{ 0x0430,	16,	{0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0x00, 0xe5, 0x21, 0xf0} },
+{ 0x0440,	16,	{0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x21, 0x02} },
+{ 0x0450,	16,	{0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x31, 0xd2, 0x02, 0x43, 0x88, 0x10, 0xd2, 0xeb, 0xd2} },
+{ 0x0460,	16,	{0xa8, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0x00, 0xe5, 0x31, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0} },
+{ 0x0470,	16,	{0x02, 0x05, 0x9b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02} },
+{ 0x0480,	16,	{0x70, 0x5b, 0xa2, 0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x05, 0xe4, 0x33, 0x4f, 0x90} },
+{ 0x0490,	16,	{0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0xe4} },
+{ 0x04a0,	16,	{0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0x90} },
+{ 0x04b0,	16,	{0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25} },
+{ 0x04c0,	16,	{0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0x01, 0x90, 0x7f, 0x00} },
+{ 0x04d0,	16,	{0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4} },
+{ 0x04e0,	16,	{0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24} },
+{ 0x04f0,	16,	{0x02, 0x60, 0x03, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x00, 0x02} },
+{ 0x0500,	16,	{0x05, 0x9b, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x0510,	16,	{0x70, 0x2a, 0x90, 0x7f, 0xec, 0xe0, 0xff, 0xf4, 0x54, 0x80, 0xfe, 0xc4, 0x54, 0x0f, 0xfe, 0xef} },
+{ 0x0520,	16,	{0x54, 0x07, 0xfd, 0x2e, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4} },
+{ 0x0530,	16,	{0xf0, 0x90, 0x7f, 0xd7, 0xed, 0xf0, 0xef, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x0540,	16,	{0x44, 0x01, 0xf0, 0x80, 0x56, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70} },
+{ 0x0550,	16,	{0x4a, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04, 0xd2, 0x00, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x0560,	16,	{0x44, 0x01, 0xf0, 0x80, 0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4} },
+{ 0x0570,	16,	{0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5} },
+{ 0x0580,	16,	{0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x0590,	16,	{0x01, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44} },
+{ 0x05a0,	2,	{0x02, 0xf0} },
+{ 0x05a2,	1,	{0x22} },
+{ 0x05a3,	16,	{0x75, 0x47, 0xff, 0x75, 0x46, 0xff, 0x75, 0x45, 0x0f, 0x75, 0x44, 0x00, 0xd2, 0x03, 0xc2, 0x06} },
+{ 0x05b3,	16,	{0xc2, 0x02, 0xc2, 0x00, 0xc2, 0x05, 0xc2, 0x01, 0x90, 0x03, 0x00, 0x74, 0x19, 0xf0, 0xe4, 0x90} },
+{ 0x05c3,	16,	{0x01, 0xbc, 0xf0, 0xc2, 0x04, 0x90, 0x01, 0xc0, 0xf0, 0xa3, 0xf0, 0xc2, 0xaf, 0xc2, 0xa8, 0x12} },
+{ 0x05d3,	16,	{0x0b, 0x8d, 0xe4, 0x90, 0x02, 0xaf, 0xf0, 0x90, 0x01, 0x00, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3} },
+{ 0x05e3,	16,	{0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x08, 0xf0, 0x7e} },
+{ 0x05f3,	16,	{0x01, 0x7f, 0x00, 0x12, 0x19, 0xc1, 0x75, 0x49, 0x12, 0x75, 0x4a, 0x0a, 0x90, 0x01, 0x0b, 0xe0} },
+{ 0x0603,	16,	{0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83} },
+{ 0x0613,	16,	{0xef, 0xf0, 0x90, 0x01, 0x0c, 0xe0, 0x44, 0x80, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70} },
+{ 0x0623,	16,	{0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x01, 0x0d, 0xe0, 0xff, 0x05} },
+{ 0x0633,	16,	{0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0} },
+{ 0x0643,	16,	{0x90, 0x01, 0x0e, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14} },
+{ 0x0653,	16,	{0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x12, 0x0a, 0xe4, 0x93, 0xff, 0x74, 0x01, 0x93, 0x90} },
+{ 0x0663,	16,	{0x01, 0x1c, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x01, 0x1c, 0xe0, 0xff, 0xa3, 0xe0, 0xfe, 0xef} },
+{ 0x0673,	16,	{0x6e, 0xff, 0x90, 0x01, 0x1c, 0xf0, 0xa3, 0xe0, 0x6f, 0xff, 0xf0, 0x90, 0x01, 0x1c, 0xe0, 0x6f} },
+{ 0x0683,	16,	{0xf0, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xe4, 0xfc, 0xfd, 0x75, 0x4f, 0x10, 0x75, 0x50, 0x02, 0x75} },
+{ 0x0693,	16,	{0x51, 0x12, 0x75, 0x52, 0xac, 0x12, 0x90, 0x15, 0x75, 0x49, 0x12, 0x75, 0x4a, 0xb2, 0x90, 0x01} },
+{ 0x06a3,	16,	{0x0d, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82} },
+{ 0x06b3,	16,	{0x8c, 0x83, 0xef, 0xf0, 0x90, 0x01, 0x0e, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70} },
+{ 0x06c3,	16,	{0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4} },
+{ 0x06d3,	16,	{0x54, 0x0f, 0x24, 0x41, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14} },
+{ 0x06e3,	16,	{0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x05, 0x4a, 0xe5, 0x4a, 0xae, 0x49, 0x70, 0x02, 0x05, 0x49} },
+{ 0x06f3,	16,	{0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe4, 0xf0, 0x75, 0x82, 0x10, 0x75, 0x83, 0x01, 0xe0, 0xfc, 0xa3} },
+{ 0x0703,	16,	{0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x90, 0x01, 0x18, 0x12, 0x9d, 0x25, 0x7e, 0x01} },
+{ 0x0713,	16,	{0x7f, 0x18, 0x12, 0x85, 0x08, 0x90, 0x01, 0x18, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe} },
+{ 0x0723,	16,	{0xa3, 0xe0, 0xff, 0x75, 0x4f, 0x0a, 0x75, 0x50, 0x06, 0x75, 0x51, 0x12, 0x75, 0x52, 0xb8, 0x12} },
+{ 0x0733,	16,	{0x90, 0x15, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x53, 0x91, 0xef} },
+{ 0x0743,	16,	{0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0x44, 0x1f, 0xf0, 0xd2, 0xaf} },
+{ 0x0753,	16,	{0x20, 0x01, 0x2e, 0x20, 0x01, 0x2b, 0xa2, 0x03, 0x92, 0x07, 0x12, 0x09, 0xca, 0x75, 0x43, 0x50} },
+{ 0x0763,	16,	{0x75, 0x42, 0x6d, 0x75, 0x41, 0x33, 0x75, 0x40, 0x00, 0x20, 0x01, 0xe4, 0x7f, 0xff, 0x7e, 0xff} },
+{ 0x0773,	16,	{0x7d, 0xff, 0x7c, 0xff, 0x78, 0x40, 0x12, 0x9d, 0x0e, 0xec, 0x4d, 0x4e, 0x4f, 0x60, 0xd1, 0x80} },
+{ 0x0783,	16,	{0xe8, 0x30, 0x01, 0x05, 0x12, 0x03, 0x70, 0xc2, 0x01, 0x30, 0x06, 0x0d, 0x12, 0x0a, 0xed, 0x50} },
+{ 0x0793,	16,	{0x06, 0x12, 0x0b, 0x00, 0x12, 0x0a, 0xf2, 0xc2, 0x06, 0x12, 0x92, 0x38, 0x90, 0x01, 0xbd, 0xe0} },
+{ 0x07a3,	16,	{0x60, 0x10, 0x7e, 0x7b, 0x7f, 0x40, 0x12, 0x8e, 0xc1, 0xe4, 0x90, 0x01, 0xbd, 0xf0, 0x90, 0x7f} },
+{ 0x07b3,	16,	{0xd3, 0xf0, 0x90, 0x02, 0xaf, 0xe0, 0xb4, 0x0f, 0x03, 0x12, 0x95, 0x30, 0x12, 0x99, 0xcc, 0xe4} },
+{ 0x07c3,	16,	{0xff, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xfe, 0x90, 0x01, 0xbc} },
+{ 0x07d3,	16,	{0xe0, 0x5e, 0x60, 0x14, 0x74, 0x27, 0x2f, 0xf8, 0xe6, 0xd3, 0x94, 0x0a, 0x40, 0x04, 0x7e, 0x01} },
+{ 0x07e3,	16,	{0x80, 0x02, 0x7e, 0x00, 0x8e, 0x48, 0x80, 0x03, 0x75, 0x48, 0x01, 0x74, 0x68, 0x2f, 0xf5, 0x82} },
+{ 0x07f3,	16,	{0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe5, 0x48, 0xf0, 0x0f, 0xbf, 0x04, 0xc5, 0xe5, 0x2b, 0xd3, 0x94} },
+{ 0x0803,	16,	{0x0a, 0x40, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0x90, 0x20, 0x6c, 0xef, 0xf0, 0x02, 0x07} },
+{ 0x0813,	1,	{0x84} },
+{ 0x0814,	1,	{0x22} },
+{ 0x0815,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x0819,	16,	{0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0x74, 0x89} },
+{ 0x0829,	16,	{0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x09, 0x23, 0x90, 0x7f} },
+{ 0x0839,	16,	{0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x02, 0x7d, 0xff} },
+{ 0x0849,	16,	{0x12, 0x81, 0xe0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02} },
+{ 0x0859,	16,	{0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0} },
+{ 0x0869,	16,	{0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf} },
+{ 0x0879,	16,	{0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f} },
+{ 0x0889,	7,	{0x32, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x22} },
+{ 0x0890,	16,	{0x90, 0x7f, 0x96, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x80, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12} },
+{ 0x08a0,	16,	{0x09, 0x23, 0x7f, 0x02, 0xe4, 0xfd, 0x12, 0x81, 0xe0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23} },
+{ 0x08b0,	16,	{0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f} },
+{ 0x08c0,	16,	{0x96, 0xe0, 0x44, 0x04, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0} },
+{ 0x08d0,	16,	{0x54, 0xf7, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x01} },
+{ 0x08e0,	12,	{0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x12, 0x0b, 0x00, 0x22} },
+{ 0x08ec,	16,	{0x90, 0x11, 0xfc, 0xe4, 0x93, 0x70, 0x2f, 0x90, 0x7f, 0x93, 0x74, 0x30, 0xf0, 0x90, 0x7f, 0x94} },
+{ 0x08fc,	16,	{0x74, 0x3c, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0xc6, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90, 0x7f} },
+{ 0x090c,	16,	{0x9d, 0x74, 0x02, 0xf0, 0x90, 0x7f, 0xe2, 0x74, 0x12, 0xf0, 0x12, 0x08, 0x19, 0x75, 0x82, 0xfc} },
+{ 0x091c,	7,	{0x75, 0x83, 0x11, 0x74, 0xff, 0xf0, 0x22} },
+{ 0x0923,	16,	{0x8e, 0x58, 0x8f, 0x59, 0xe5, 0x59, 0x15, 0x59, 0xae, 0x58, 0x70, 0x02, 0x15, 0x58, 0x4e, 0x60} },
+{ 0x0933,	7,	{0x05, 0x12, 0x09, 0xef, 0x80, 0xee, 0x22} },
+{ 0x093a,	2,	{0x8f, 0x4c} },
+{ 0x093c,	16,	{0xe4, 0xf5, 0x4d, 0x75, 0x4e, 0xff, 0x75, 0x4f, 0x12, 0x75, 0x50, 0x6a, 0xab, 0x4e, 0xaa, 0x4f} },
+{ 0x094c,	16,	{0xa9, 0x50, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0xb4, 0x03, 0x1d, 0xaf, 0x4d, 0x05, 0x4d, 0xef} },
+{ 0x095c,	16,	{0xb5, 0x4c, 0x01, 0x22, 0x12, 0x9b, 0x72, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x096c,	14,	{0x4e, 0xff, 0xf5, 0x4f, 0x89, 0x50, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x097a,	1,	{0x22} },
+{ 0x097b,	16,	{0xe4, 0xfe, 0x75, 0x4e, 0xff, 0x75, 0x4f, 0x12, 0x75, 0x50, 0x12, 0xab, 0x4e, 0xaa, 0x4f, 0xa9} },
+{ 0x098b,	16,	{0x50, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x099b,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x9b, 0xe4, 0x85, 0xf0, 0x4c, 0xf5, 0x4d, 0x62, 0x4c} },
+{ 0x09ab,	16,	{0xe5, 0x4c, 0x62, 0x4d, 0xe5, 0x4d, 0x62, 0x4c, 0x29, 0xfd, 0xe5, 0x4c, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x09bb,	14,	{0x4e, 0xff, 0xf5, 0x4f, 0x89, 0x50, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x09c9,	1,	{0x22} },
+{ 0x09ca,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x07, 0x04, 0xe0, 0x44} },
+{ 0x09da,	16,	{0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x09, 0x23, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x09ea,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x09ef,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x09ff,	1,	{0x22} },
+{ 0x0a00,	16,	{0x02, 0x0b, 0xaa, 0x00, 0x02, 0x0b, 0xdd, 0x00, 0x02, 0x0b, 0xc2, 0x00, 0x02, 0x0c, 0x1c, 0x00} },
+{ 0x0a10,	16,	{0x02, 0x0c, 0x06, 0x00, 0x02, 0x0a, 0xf7, 0x00, 0x02, 0x0a, 0xf8, 0x00, 0x02, 0x0a, 0xf9, 0x00} },
+{ 0x0a20,	16,	{0x02, 0x0c, 0x37, 0x00, 0x02, 0x0d, 0x27, 0x00, 0x02, 0x0c, 0x73, 0x00, 0x02, 0x0d, 0x7b, 0x00} },
+{ 0x0a30,	16,	{0x02, 0x0c, 0xaf, 0x00, 0x02, 0x0d, 0xcf, 0x00, 0x02, 0x0c, 0xeb, 0x00, 0x02, 0x0e, 0x23, 0x00} },
+{ 0x0a40,	16,	{0x02, 0x0a, 0xfa, 0x00, 0x02, 0x0a, 0xfc, 0x00, 0x02, 0x0a, 0xfb, 0x00, 0x02, 0x0a, 0xfd, 0x00} },
+{ 0x0a50,	8,	{0x02, 0x0e, 0x77, 0x00, 0x02, 0x0e, 0x8d, 0x00} },
+{ 0x0a58,	16,	{0x53, 0x8e, 0xf7, 0xe5, 0x89, 0x54, 0xf1, 0x44, 0x01, 0xf5, 0x89, 0x75, 0x8c, 0xb1, 0xd2, 0xa9} },
+{ 0x0a68,	16,	{0x75, 0x98, 0x40, 0x75, 0xcb, 0xff, 0x75, 0xca, 0xf3, 0x75, 0xc8, 0x34, 0xe4, 0xff, 0x7f, 0x05} },
+{ 0x0a78,	7,	{0x78, 0x27, 0xe4, 0xf6, 0x08, 0xdf, 0xfc} },
+{ 0x0a7f,	1,	{0x22} },
+{ 0x0a80,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x06, 0xc0} },
+{ 0x0a90,	1,	{0x07} },
+{ 0x0a91,	16,	{0x30, 0x04, 0x16, 0x75, 0x8c, 0xf8, 0x75, 0x8a, 0x30, 0x7f, 0x2f, 0xae, 0x07, 0x1f, 0xee, 0x60} },
+{ 0x0aa1,	16,	{0x3c, 0x90, 0x20, 0x00, 0x74, 0x55, 0xf0, 0x80, 0xf2, 0x75, 0x8c, 0xb1, 0x7f, 0x27, 0xef, 0xd3} },
+{ 0x0ab1,	16,	{0x94, 0x2b, 0x50, 0x09, 0xa8, 0x07, 0xe6, 0x60, 0x01, 0x16, 0x0f, 0x80, 0xf1, 0x90, 0x03, 0x00} },
+{ 0x0ac1,	16,	{0xe0, 0x60, 0x02, 0x14, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x0e, 0x90} },
+{ 0x0ad1,	13,	{0x01, 0xc1, 0xe0, 0x24, 0xff, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x34, 0xff, 0xf0} },
+{ 0x0ade,	15,	{0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0aed,	5,	{0x12, 0x08, 0x90, 0xd3, 0x22} },
+{ 0x0af2,	5,	{0x12, 0x08, 0x19, 0xd3, 0x22} },
+{ 0x0af7,	1,	{0x32} },
+{ 0x0af8,	1,	{0x32} },
+{ 0x0af9,	1,	{0x32} },
+{ 0x0afa,	1,	{0x32} },
+{ 0x0afb,	1,	{0x32} },
+{ 0x0afc,	1,	{0x32} },
+{ 0x0afd,	1,	{0x32} },
+{ 0x0b00,	9,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} },
+{ 0x0b7d,	16,	{0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x0b8d,	16,	{0xd2, 0x00, 0x75, 0x8e, 0x10, 0xe4, 0x90, 0x7f, 0x92, 0xf0, 0x12, 0x10, 0x0c, 0x12, 0x08, 0xec} },
+{ 0x0b9d,	13,	{0x12, 0x0e, 0xa9, 0x12, 0x90, 0xe6, 0x12, 0x1b, 0x0c, 0x12, 0x0a, 0x58, 0x22} },
+{ 0x0baa,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} },
+{ 0x0bba,	8,	{0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0bc2,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0bd2,	11,	{0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0bdd,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0x90} },
+{ 0x0bed,	16,	{0x7f, 0xd8, 0xe0, 0x70, 0x0d, 0x90, 0x7f, 0xd9, 0xe0, 0x70, 0x07, 0xe5, 0x2b, 0x70, 0x03, 0x75} },
+{ 0x0bfd,	9,	{0x2b, 0x14, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0c06,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} },
+{ 0x0c16,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0c1c,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x30, 0x02, 0x02, 0xd2, 0x06, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0c2c,	11,	{0xab, 0x74, 0x08, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0c37,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0c47,	16,	{0xa9, 0x74, 0x02, 0xf0, 0xe5, 0x30, 0x30, 0xe0, 0x13, 0xe5, 0x3b, 0x30, 0xe0, 0x07, 0x90, 0x20} },
+{ 0x0c57,	16,	{0x04, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x01, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} },
+{ 0x0c67,	12,	{0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0c73,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0c83,	16,	{0xa9, 0x74, 0x04, 0xf0, 0xe5, 0x30, 0x30, 0xe1, 0x13, 0xe5, 0x3b, 0x30, 0xe1, 0x07, 0x90, 0x20} },
+{ 0x0c93,	16,	{0x0c, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x09, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} },
+{ 0x0ca3,	12,	{0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0caf,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0cbf,	16,	{0xa9, 0x74, 0x08, 0xf0, 0xe5, 0x30, 0x30, 0xe2, 0x13, 0xe5, 0x3b, 0x30, 0xe2, 0x07, 0x90, 0x20} },
+{ 0x0ccf,	16,	{0x14, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x11, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} },
+{ 0x0cdf,	12,	{0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0ceb,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0cfb,	16,	{0xa9, 0x74, 0x10, 0xf0, 0xe5, 0x30, 0x30, 0xe3, 0x13, 0xe5, 0x3b, 0x30, 0xe3, 0x07, 0x90, 0x20} },
+{ 0x0d0b,	16,	{0x1c, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} },
+{ 0x0d1b,	12,	{0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d27,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x0d37,	16,	{0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x02, 0xf0, 0xe5, 0x30, 0x20} },
+{ 0x0d47,	16,	{0xe0, 0x06, 0x90, 0x7f, 0xc7, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe0, 0x0a, 0x90, 0x7f, 0xc7} },
+{ 0x0d57,	16,	{0xe0, 0x90, 0x02, 0xf8, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x01, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} },
+{ 0x0d67,	16,	{0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} },
+{ 0x0d77,	4,	{0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d7b,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x0d8b,	16,	{0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x04, 0xf0, 0xe5, 0x30, 0x20} },
+{ 0x0d9b,	16,	{0xe1, 0x06, 0x90, 0x7f, 0xc9, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe1, 0x0a, 0x90, 0x7f, 0xc9} },
+{ 0x0dab,	16,	{0xe0, 0x90, 0x02, 0xf9, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x09, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} },
+{ 0x0dbb,	16,	{0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} },
+{ 0x0dcb,	4,	{0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0dcf,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x0ddf,	16,	{0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x08, 0xf0, 0xe5, 0x30, 0x20} },
+{ 0x0def,	16,	{0xe2, 0x06, 0x90, 0x7f, 0xcb, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe2, 0x0a, 0x90, 0x7f, 0xcb} },
+{ 0x0dff,	16,	{0xe0, 0x90, 0x02, 0xfa, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x11, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} },
+{ 0x0e0f,	16,	{0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} },
+{ 0x0e1f,	4,	{0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e23,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x0e33,	16,	{0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x10, 0xf0, 0xe5, 0x30, 0x20} },
+{ 0x0e43,	16,	{0xe3, 0x06, 0x90, 0x7f, 0xcd, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe3, 0x0a, 0x90, 0x7f, 0xcd} },
+{ 0x0e53,	16,	{0xe0, 0x90, 0x02, 0xfb, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x19, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} },
+{ 0x0e63,	16,	{0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} },
+{ 0x0e73,	4,	{0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e77,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x80, 0xf0, 0xd0} },
+{ 0x0e87,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e8d,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x80, 0xf0, 0x90} },
+{ 0x0e9d,	12,	{0x01, 0xbd, 0x74, 0xff, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0ea9,	16,	{0x90, 0x01, 0x20, 0x12, 0x9d, 0x31, 0x00, 0x00, 0x25, 0x80, 0x90, 0x01, 0x24, 0x74, 0x08, 0xf0} },
+{ 0x0eb9,	16,	{0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x6e, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x13, 0xf0, 0xa3, 0x74} },
+{ 0x0ec9,	16,	{0x11, 0xf0, 0xe4, 0xa3, 0xf0, 0xa3, 0xf0, 0x90, 0x01, 0x1e, 0xf0, 0x90, 0x01, 0x1e, 0xe0, 0xff} },
+{ 0x0ed9,	16,	{0xc3, 0x94, 0x04, 0x50, 0x13, 0xef, 0x04, 0xa3, 0xf0, 0x7e, 0x01, 0x7f, 0x1f, 0x12, 0x85, 0xe1} },
+{ 0x0ee9,	16,	{0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0x80, 0xe3, 0xe4, 0xf5, 0x26, 0x90, 0x01, 0x1e, 0xf0, 0x90} },
+{ 0x0ef9,	16,	{0x01, 0x1e, 0xe0, 0xff, 0xc3, 0x94, 0x04, 0x50, 0x1a, 0x74, 0xf8, 0x2f, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0f09,	16,	{0x02, 0xf5, 0x83, 0xe4, 0xf0, 0x74, 0x22, 0x2f, 0xf8, 0xe4, 0xf6, 0x90, 0x01, 0x1e, 0xe0, 0x04} },
+{ 0x0f19,	16,	{0xf0, 0x80, 0xdc, 0xe4, 0xf5, 0x30, 0xe5, 0xc0, 0x60, 0x2f, 0x90, 0x01, 0x1e, 0x74, 0x01, 0xf0} },
+{ 0x0f29,	16,	{0x90, 0x01, 0x1e, 0xe0, 0xff, 0xd3, 0x94, 0x04, 0x50, 0x1f, 0xef, 0x14, 0xff, 0x74, 0x01, 0xa8} },
+{ 0x0f39,	16,	{0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42, 0x30, 0x7e, 0x01, 0x7f, 0x1e, 0x12, 0x83} },
+{ 0x0f49,	16,	{0x5f, 0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0x80, 0xd7, 0xe4, 0xf5, 0x3a, 0x90, 0x01, 0x1e, 0xf0} },
+{ 0x0f59,	16,	{0x90, 0x01, 0x1e, 0xe0, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x34, 0x20} },
+{ 0x0f69,	16,	{0xf5, 0x83, 0xe0, 0x54, 0xf0, 0xfe, 0x74, 0xc5, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5, 0x83} },
+{ 0x0f79,	16,	{0xee, 0xf0, 0x74, 0x36, 0x2f, 0xf8, 0xa6, 0x06, 0x74, 0x32, 0x2f, 0xf8, 0xe4, 0xf6, 0x74, 0x2c} },
+{ 0x0f89,	16,	{0x2f, 0xf8, 0xe4, 0xf6, 0x74, 0xfc, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0} },
+{ 0x0f99,	16,	{0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0xe0, 0xb4, 0x04, 0xb6, 0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f} },
+{ 0x0fa9,	16,	{0xf5, 0x4b, 0x60, 0x5e, 0xe4, 0x90, 0x01, 0x1e, 0xf0, 0x90, 0x01, 0x1e, 0xe0, 0xff, 0xc3, 0x94} },
+{ 0x0fb9,	16,	{0x04, 0x50, 0xe7, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x55, 0x4b} },
+{ 0x0fc9,	16,	{0x60, 0x38, 0x90, 0x01, 0x1e, 0xe0, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4} },
+{ 0x0fd9,	16,	{0x34, 0x20, 0xf5, 0x83, 0xe0, 0xfe, 0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4} },
+{ 0x0fe9,	16,	{0x34, 0x20, 0xf5, 0x83, 0xe0, 0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0ff9,	16,	{0x20, 0xf5, 0x83, 0xe0, 0xfe, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0} },
+{ 0x1009,	3,	{0x80, 0xa7, 0x22} },
+{ 0x100c,	16,	{0x7b, 0xff, 0x7a, 0x12, 0x79, 0x1b, 0x90, 0x00, 0x04, 0x12, 0x9b, 0x8b, 0xfd, 0x8b, 0x4d, 0x75} },
+{ 0x101c,	16,	{0x4e, 0x12, 0x75, 0x4f, 0x24, 0xe4, 0x90, 0x7f, 0xe1, 0xf0, 0x90, 0x7f, 0xe0, 0xf0, 0xf5, 0x4b} },
+{ 0x102c,	16,	{0xf5, 0x4c, 0x90, 0x02, 0xae, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0x90, 0x7f} },
+{ 0x103c,	16,	{0xa9, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0xe4, 0xfc, 0xec, 0x25, 0xe0, 0x24, 0xb4, 0xf5} },
+{ 0x104c,	16,	{0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x0c, 0xbc, 0x10, 0xee, 0xe4, 0x90, 0x7f, 0xdd} },
+{ 0x105c,	16,	{0xf0, 0xaf, 0x05, 0x1d, 0xef, 0x70, 0x03, 0x02, 0x11, 0xd2, 0xab, 0x4d, 0xaa, 0x4e, 0xa9, 0x4f} },
+{ 0x106c,	16,	{0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0x64, 0x05, 0x60, 0x03, 0x02, 0x11, 0xc1, 0x90, 0x00, 0x03} },
+{ 0x107c,	16,	{0x12, 0x9b, 0x8b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x11, 0x48, 0x90, 0x00, 0x02, 0x12, 0x9b, 0x8b} },
+{ 0x108c,	16,	{0xff, 0x54, 0x7f, 0xfc, 0xd3, 0x94, 0x07, 0x50, 0x03, 0x02, 0x11, 0x22, 0xec, 0xc3, 0x94, 0x10} },
+{ 0x109c,	16,	{0x40, 0x03, 0x02, 0x11, 0x22, 0xef, 0x30, 0xe7, 0x42, 0xe5, 0x4c, 0xae, 0x4b, 0x78, 0x02, 0xce} },
+{ 0x10ac,	16,	{0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0x74, 0xf0, 0x2c, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5} },
+{ 0x10bc,	16,	{0x83, 0xef, 0xf0, 0x90, 0x7f, 0xe0, 0xe0, 0xff, 0xec, 0x24, 0xf8, 0xfe, 0x74, 0x01, 0xa8, 0x06} },
+{ 0x10cc,	16,	{0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4f, 0x90, 0x7f, 0xe0, 0xf0, 0x90, 0x02, 0xae, 0xe0} },
+{ 0x10dc,	16,	{0x04, 0xf0, 0x90, 0x7f, 0xdd, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x3e, 0xe5, 0x4c, 0xae, 0x4b, 0x78} },
+{ 0x10ec,	16,	{0x02, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0x74, 0xe8, 0x2c, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x10fc,	16,	{0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x90, 0x7f, 0xe1, 0xe0, 0xff, 0xec, 0x24, 0xf8, 0xfe, 0x74, 0x01} },
+{ 0x110c,	16,	{0xa8, 0x06, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4f, 0x90, 0x7f, 0xe1, 0xf0, 0x90, 0x02} },
+{ 0x111c,	16,	{0xae, 0xe0, 0x04, 0xf0, 0x80, 0x03, 0x7f, 0xff, 0x22, 0x90, 0x00, 0x04, 0x12, 0x9b, 0x8b, 0x25} },
+{ 0x112c,	16,	{0x4c, 0xf5, 0x4c, 0xe4, 0x35, 0x4b, 0xf5, 0x4b, 0x90, 0x00, 0x05, 0x12, 0x9b, 0x8b, 0xfe, 0xe4} },
+{ 0x113c,	16,	{0x25, 0x4c, 0xf5, 0x4c, 0xee, 0x35, 0x4b, 0xf5, 0x4b, 0x02, 0x11, 0xc4, 0xab, 0x4d, 0xaa, 0x4e} },
+{ 0x114c,	16,	{0xa9, 0x4f, 0x90, 0x00, 0x03, 0x12, 0x9b, 0x8b, 0xff, 0x64, 0x02, 0x60, 0x05, 0xef, 0x64, 0x03} },
+{ 0x115c,	16,	{0x70, 0x60, 0x90, 0x00, 0x02, 0x12, 0x9b, 0x8b, 0xff, 0x54, 0x7f, 0xfc, 0xd3, 0x94, 0x07, 0x50} },
+{ 0x116c,	16,	{0x4e, 0xef, 0x30, 0xe7, 0x1e, 0x90, 0x7f, 0xde, 0xe0, 0xff, 0x74, 0x01, 0xa8, 0x04, 0x08, 0x80} },
+{ 0x117c,	16,	{0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xfe, 0x4f, 0x90, 0x7f, 0xde, 0xf0, 0x90, 0x7f, 0xac, 0xe0, 0x4e} },
+{ 0x118c,	16,	{0xf0, 0x80, 0x35, 0x90, 0x7f, 0xdf, 0xe0, 0xff, 0x74, 0x01, 0xa8, 0x04, 0x08, 0x80, 0x02, 0xc3} },
+{ 0x119c,	16,	{0x33, 0xd8, 0xfc, 0xfe, 0x4f, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xad, 0xe0, 0x4e, 0xf0, 0xec} },
+{ 0x11ac,	16,	{0x25, 0xe0, 0x24, 0xc5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xec, 0xf0, 0x80, 0x09, 0x7f} },
+{ 0x11bc,	16,	{0xff, 0x22, 0x7f, 0xff, 0x22, 0x7f, 0xff, 0x22, 0x74, 0x07, 0x25, 0x4f, 0xf5, 0x4f, 0xe4, 0x35} },
+{ 0x11cc,	16,	{0x4e, 0xf5, 0x4e, 0x02, 0x10, 0x5d, 0x20, 0x03, 0x0d, 0x90, 0x02, 0xae, 0xe0, 0x60, 0x07, 0x90} },
+{ 0x11dc,	8,	{0x7f, 0xae, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0x00} },
+{ 0x11e4,	1,	{0x22} },
+{ 0x11e5,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xff, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33} },
+{ 0x11f5,	7,	{0xd8, 0xfc, 0x42, 0x3a, 0x7f, 0x00, 0x22} },
+{ 0x11fc,	3,	{0x00, 0x03, 0x15} },
+{ 0x1200,	16,	{0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40, 0x10, 0x07, 0x01, 0x80, 0x42, 0x00, 0x01, 0x02} },
+{ 0x1210,	16,	{0x03, 0x01, 0x09, 0x02, 0x58, 0x00, 0x01, 0x01, 0x04, 0x80, 0x3c, 0x09, 0x04, 0x00, 0x00, 0x0a} },
+{ 0x1220,	16,	{0xff, 0xff, 0xff, 0x05, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40} },
+{ 0x1230,	16,	{0x00, 0x00, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00} },
+{ 0x1240,	16,	{0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} },
+{ 0x1250,	16,	{0x84, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x87, 0x02} },
+{ 0x1260,	16,	{0x40, 0x00, 0x00, 0x07, 0x05, 0x07, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x24, 0x03} },
+{ 0x1270,	16,	{0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00} },
+{ 0x1280,	16,	{0x54, 0x00, 0x65, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} },
+{ 0x1290,	16,	{0x2e, 0x00, 0x18, 0x03, 0x57, 0x00, 0x68, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, 0x48, 0x00} },
+{ 0x12a0,	16,	{0x45, 0x00, 0x41, 0x00, 0x54, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x1a, 0x03, 0x58, 0x00, 0x58, 0x00} },
+{ 0x12b0,	16,	{0x2d, 0x00, 0x58, 0x00, 0x58, 0x00, 0x2d, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00} },
+{ 0x12c0,	16,	{0x58, 0x00, 0x58, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} },
+{ 0x12d0,	16,	{0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} },
+{ 0x12e0,	16,	{0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} },
+{ 0x12f0,	16,	{0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} },
+{ 0x1300,	16,	{0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} },
+{ 0x1310,	2,	{0x00, 0x00} },
+{ 0x1312,	16,	{0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0xc0, 0xd0} },
+{ 0x1322,	16,	{0x75, 0x86, 0x00, 0x75, 0xd0, 0x18, 0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f, 0xf5, 0xf0, 0x70, 0x11} },
+{ 0x1332,	16,	{0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0} },
+{ 0x1342,	16,	{0x32, 0x75, 0x86, 0x00, 0x10, 0xf0, 0x0b, 0x10, 0xf1, 0x12, 0x10, 0xf2, 0x19, 0x10, 0xf3, 0x20} },
+{ 0x1352,	16,	{0x80, 0xd4, 0xe5, 0x27, 0x70, 0x03, 0x75, 0x27, 0x14, 0x02, 0x13, 0x7c, 0xe5, 0x28, 0x70, 0x03} },
+{ 0x1362,	16,	{0x75, 0x28, 0x14, 0x02, 0x15, 0x0d, 0xe5, 0x29, 0x70, 0x03, 0x75, 0x29, 0x14, 0x02, 0x16, 0x9e} },
+{ 0x1372,	16,	{0xe5, 0x2a, 0x70, 0x03, 0x75, 0x2a, 0x14, 0x02, 0x18, 0x2f, 0x90, 0x20, 0x02, 0xe0, 0x54, 0x3f} },
+{ 0x1382,	16,	{0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14, 0x60, 0x09, 0x02, 0x13} },
+{ 0x1392,	16,	{0x43, 0x02, 0x14, 0x65, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5, 0x36, 0x02, 0x13, 0x43} },
+{ 0x13a2,	16,	{0x43, 0x82, 0x04, 0xe0, 0x43, 0x2c, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x05} },
+{ 0x13b2,	16,	{0xe0, 0x42, 0x32, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13, 0x43, 0x30, 0xe1, 0x02} },
+{ 0x13c2,	16,	{0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe0, 0x0a, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x04, 0xe0} },
+{ 0x13d2,	16,	{0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74, 0x00, 0xf0, 0x90, 0x20} },
+{ 0x13e2,	16,	{0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86} },
+{ 0x13f2,	16,	{0x90, 0x7e, 0x80, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f} },
+{ 0x1402,	16,	{0xe5, 0xe5, 0x3c, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0} },
+{ 0x1412,	16,	{0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x90} },
+{ 0x1422,	16,	{0x7f, 0xb7, 0xed, 0xf0, 0x90, 0x20, 0x01, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x7f, 0x40} },
+{ 0x1432,	16,	{0x90, 0x7e, 0x80, 0x05, 0x86, 0x90, 0x20, 0x00, 0xe5, 0x84, 0xfe, 0x24, 0x05, 0xfd, 0x8d, 0x84} },
+{ 0x1442,	16,	{0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xef, 0x05} },
+{ 0x1452,	16,	{0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xb7, 0xf0, 0x05, 0x86, 0xa3, 0xe0, 0x54, 0xfe, 0xf0} },
+{ 0x1462,	16,	{0x02, 0x13, 0x43, 0x53, 0x2c, 0xfa, 0xe5, 0x22, 0x60, 0x08, 0x75, 0x22, 0x00, 0xd2, 0xe7, 0xfe} },
+{ 0x1472,	16,	{0x80, 0x0a, 0x90, 0x7f, 0xc7, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x14, 0xff, 0x90, 0x20, 0x50, 0x74} },
+{ 0x1482,	16,	{0x00, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90} },
+{ 0x1492,	16,	{0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7e, 0x40, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0} },
+{ 0x14a2,	16,	{0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0, 0x24, 0x38, 0xf0, 0x05} },
+{ 0x14b2,	16,	{0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60, 0x30, 0x03, 0x03, 0x03} },
+{ 0x14c2,	16,	{0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0} },
+{ 0x14d2,	16,	{0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11, 0x8b, 0x22, 0x90, 0x7f} },
+{ 0x14e2,	16,	{0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80, 0x1b, 0xe0, 0xde, 0xfd} },
+{ 0x14f2,	16,	{0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x90, 0x20, 0x01} },
+{ 0x1502,	16,	{0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xc7, 0xf0, 0x02, 0x13, 0x43, 0x90, 0x20, 0x0a, 0xe0, 0x54} },
+{ 0x1512,	16,	{0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14, 0x60, 0x09, 0x02} },
+{ 0x1522,	16,	{0x13, 0x43, 0x02, 0x15, 0xf6, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5, 0x37, 0x02, 0x13} },
+{ 0x1532,	16,	{0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2d, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82, 0xf8, 0x43, 0x82} },
+{ 0x1542,	16,	{0x05, 0xe0, 0x42, 0x33, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13, 0x43, 0x30, 0xe1} },
+{ 0x1552,	16,	{0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe1, 0x0a, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x04} },
+{ 0x1562,	16,	{0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74, 0x01, 0xf0, 0x90} },
+{ 0x1572,	16,	{0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05} },
+{ 0x1582,	16,	{0x86, 0x90, 0x7e, 0x00, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90} },
+{ 0x1592,	16,	{0x7f, 0xe5, 0xe5, 0x3d, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0} },
+{ 0x15a2,	16,	{0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} },
+{ 0x15b2,	16,	{0x90, 0x7f, 0xb9, 0xed, 0xf0, 0x90, 0x20, 0x09, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x7f} },
+{ 0x15c2,	16,	{0x40, 0x90, 0x7e, 0x00, 0x05, 0x86, 0x90, 0x20, 0x08, 0xe5, 0x84, 0xfe, 0x24, 0x05, 0xfd, 0x8d} },
+{ 0x15d2,	16,	{0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xef} },
+{ 0x15e2,	16,	{0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xb9, 0xf0, 0x05, 0x86, 0xa3, 0xe0, 0x54, 0xfe} },
+{ 0x15f2,	16,	{0xf0, 0x02, 0x13, 0x43, 0x53, 0x2d, 0xfa, 0xe5, 0x23, 0x60, 0x08, 0x75, 0x23, 0x00, 0xd2, 0xe7} },
+{ 0x1602,	16,	{0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xc9, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x16, 0x90, 0x90, 0x20, 0x50} },
+{ 0x1612,	16,	{0x74, 0x01, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0} },
+{ 0x1622,	16,	{0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0xc0, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84} },
+{ 0x1632,	16,	{0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0, 0x24, 0x38, 0xf0} },
+{ 0x1642,	16,	{0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60, 0x30, 0x03, 0x03} },
+{ 0x1652,	16,	{0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0} },
+{ 0x1662,	16,	{0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11, 0x8b, 0x23, 0x90} },
+{ 0x1672,	16,	{0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80, 0x1b, 0xe0, 0xde} },
+{ 0x1682,	14,	{0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} },
+{ 0x1690,	16,	{0x90, 0x20, 0x09, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x02, 0x13, 0x43, 0x90, 0x20} },
+{ 0x16a0,	16,	{0x12, 0xe0, 0x54, 0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14} },
+{ 0x16b0,	16,	{0x60, 0x09, 0x02, 0x13, 0x43, 0x02, 0x17, 0x87, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5} },
+{ 0x16c0,	16,	{0x38, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2e, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82} },
+{ 0x16d0,	16,	{0xf8, 0x43, 0x82, 0x05, 0xe0, 0x42, 0x34, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13} },
+{ 0x16e0,	16,	{0x43, 0x30, 0xe1, 0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe2, 0x0a, 0x53, 0x82, 0xf8} },
+{ 0x16f0,	16,	{0x43, 0x82, 0x04, 0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74} },
+{ 0x1700,	16,	{0x02, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90} },
+{ 0x1710,	16,	{0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x80, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0} },
+{ 0x1720,	16,	{0x05, 0x86, 0x90, 0x7f, 0xe5, 0xe5, 0x3e, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0} },
+{ 0x1730,	16,	{0xf0, 0xf0, 0xf0, 0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58} },
+{ 0x1740,	16,	{0x74, 0x00, 0xf0, 0x90, 0x7f, 0xbb, 0xed, 0xf0, 0x90, 0x20, 0x11, 0xe0, 0x54, 0xfe, 0xf0, 0x02} },
+{ 0x1750,	16,	{0x13, 0x43, 0x7f, 0x40, 0x90, 0x7d, 0x80, 0x05, 0x86, 0x90, 0x20, 0x10, 0xe5, 0x84, 0xfe, 0x24} },
+{ 0x1760,	16,	{0x05, 0xfd, 0x8d, 0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05} },
+{ 0x1770,	16,	{0x86, 0xdf, 0xef, 0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xbb, 0xf0, 0x05, 0x86, 0xa3} },
+{ 0x1780,	16,	{0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x53, 0x2e, 0xfa, 0xe5, 0x24, 0x60, 0x08, 0x75, 0x24} },
+{ 0x1790,	16,	{0x00, 0xd2, 0xe7, 0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xcb, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x18, 0x21} },
+{ 0x17a0,	16,	{0x90, 0x20, 0x50, 0x74, 0x02, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0} },
+{ 0x17b0,	16,	{0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x40, 0x05, 0x86, 0xe5, 0x85, 0xf0} },
+{ 0x17c0,	16,	{0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0} },
+{ 0x17d0,	16,	{0x24, 0x38, 0xf0, 0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60} },
+{ 0x17e0,	16,	{0x30, 0x03, 0x03, 0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0} },
+{ 0x17f0,	16,	{0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11} },
+{ 0x1800,	16,	{0x8b, 0x24, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80} },
+{ 0x1810,	16,	{0x1b, 0xe0, 0xde, 0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00} },
+{ 0x1820,	16,	{0xf0, 0x90, 0x20, 0x11, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xcb, 0xf0, 0x02, 0x13, 0x43, 0x90} },
+{ 0x1830,	16,	{0x20, 0x1a, 0xe0, 0x54, 0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5} },
+{ 0x1840,	16,	{0x14, 0x60, 0x09, 0x02, 0x13, 0x43, 0x02, 0x19, 0x18, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0} },
+{ 0x1850,	16,	{0xf5, 0x39, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2f, 0x01, 0x02, 0x13, 0x43, 0x53} },
+{ 0x1860,	16,	{0x82, 0xf8, 0x43, 0x82, 0x05, 0xe0, 0x42, 0x35, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02} },
+{ 0x1870,	16,	{0x13, 0x43, 0x30, 0xe1, 0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe3, 0x0a, 0x53, 0x82} },
+{ 0x1880,	16,	{0xf8, 0x43, 0x82, 0x04, 0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50} },
+{ 0x1890,	16,	{0x74, 0x03, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0} },
+{ 0x18a0,	16,	{0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x00, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84} },
+{ 0x18b0,	16,	{0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xe5, 0x3f, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0} },
+{ 0x18c0,	16,	{0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20} },
+{ 0x18d0,	16,	{0x58, 0x74, 0x00, 0xf0, 0x90, 0x7f, 0xbd, 0xed, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x54, 0xfe, 0xf0} },
+{ 0x18e0,	16,	{0x02, 0x13, 0x43, 0x7f, 0x40, 0x90, 0x7d, 0x00, 0x05, 0x86, 0x90, 0x20, 0x18, 0xe5, 0x84, 0xfe} },
+{ 0x18f0,	16,	{0x24, 0x05, 0xfd, 0x8d, 0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3} },
+{ 0x1900,	16,	{0x05, 0x86, 0xdf, 0xef, 0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xbd, 0xf0, 0x05, 0x86} },
+{ 0x1910,	16,	{0xa3, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x53, 0x2f, 0xfa, 0xe5, 0x25, 0x60, 0x08, 0x75} },
+{ 0x1920,	16,	{0x25, 0x00, 0xd2, 0xe7, 0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xcd, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x19} },
+{ 0x1930,	16,	{0xb2, 0x90, 0x20, 0x50, 0x74, 0x03, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2} },
+{ 0x1940,	16,	{0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7c, 0xc0, 0x05, 0x86, 0xe5, 0x85} },
+{ 0x1950,	16,	{0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86} },
+{ 0x1960,	16,	{0xe0, 0x24, 0x38, 0xf0, 0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78} },
+{ 0x1970,	16,	{0x60, 0x30, 0x03, 0x03, 0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0} },
+{ 0x1980,	16,	{0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70} },
+{ 0x1990,	16,	{0x11, 0x8b, 0x25, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} },
+{ 0x19a0,	16,	{0x80, 0x1b, 0xe0, 0xde, 0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74} },
+{ 0x19b0,	16,	{0x00, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xcd, 0xf0, 0x02, 0x13, 0x43} },
+{ 0x19c0,	1,	{0x32} },
+{ 0x19c1,	4,	{0xad, 0x07, 0xac, 0x06} },
+{ 0x19c5,	16,	{0x79, 0x06, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb} },
+{ 0x19d5,	16,	{0x4a, 0x70, 0x03, 0x02, 0x1b, 0x09, 0xe9, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x1a, 0xdb, 0x90} },
+{ 0x19e5,	16,	{0x19, 0xeb, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x1a, 0xb9, 0x02, 0x1a, 0x71, 0x02, 0x1a, 0x5a, 0x02} },
+{ 0x19f5,	16,	{0x1a, 0x40, 0x02, 0x1a, 0x2f, 0x02, 0x1a, 0x1a, 0x02, 0x1a, 0x00, 0x90, 0x7f, 0xa5, 0xe0, 0x44} },
+{ 0x1a05,	16,	{0x80, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90, 0x7f, 0xa6} },
+{ 0x1a15,	16,	{0xf0, 0x19, 0x02, 0x1a, 0xdb, 0x19, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0xc3, 0x94, 0x20, 0x40, 0x0a} },
+{ 0x1a25,	16,	{0xa3, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x02, 0x1a, 0xdb, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3} },
+{ 0x1a35,	16,	{0xe0, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x19, 0x02, 0x1a, 0xdb, 0x90, 0x7f, 0xa5, 0xe0, 0x44} },
+{ 0x1a45,	16,	{0x80, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa1, 0x90, 0x7f, 0xa6} },
+{ 0x1a55,	16,	{0xf0, 0x19, 0x02, 0x1a, 0xdb, 0xeb, 0x64, 0x01, 0x4a, 0x70, 0x08, 0x90, 0x7f, 0xa5, 0xe0, 0x44} },
+{ 0x1a65,	16,	{0x20, 0xf0, 0x19, 0x90, 0x7f, 0xa6, 0xe0, 0xf5, 0x54, 0x19, 0x80, 0x6a, 0xed, 0x24, 0x04, 0xf5} },
+{ 0x1a75,	16,	{0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0x64, 0x02, 0x4e, 0x70, 0x08, 0x90, 0x7f} },
+{ 0x1a85,	16,	{0xa5, 0xe0, 0x44, 0x20, 0xf0, 0x19, 0x90, 0x7f, 0xa6, 0xe0, 0xff, 0xed, 0x24, 0x06, 0xf5, 0x82} },
+{ 0x1a95,	16,	{0xe4, 0x3c, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83} },
+{ 0x1aa5,	16,	{0xef, 0xf0, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0x74, 0xff, 0xf5, 0xf0, 0x12} },
+{ 0x1ab5,	16,	{0x9b, 0xb8, 0x80, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xff} },
+{ 0x1ac5,	16,	{0xed, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfa, 0xa3, 0xe0, 0xf5, 0x82, 0x8a} },
+{ 0x1ad5,	16,	{0x83, 0xef, 0xf0, 0x7f, 0x08, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0xf5, 0x54, 0x30, 0xe0, 0xf7, 0x30} },
+{ 0x1ae5,	16,	{0xe2, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06, 0x22, 0xe9, 0xd3, 0x94, 0x02, 0x50, 0x03, 0x02} },
+{ 0x1af5,	16,	{0x19, 0xc7, 0xe5, 0x54, 0x30, 0xe1, 0x03, 0x02, 0x19, 0xc7, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40} },
+{ 0x1b05,	6,	{0xf0, 0x7f, 0x07, 0x22, 0x7f, 0x08} },
+{ 0x1b0b,	1,	{0x22} },
+{ 0x1b0c,	16,	{0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x02, 0x7d, 0xff, 0x12, 0x81, 0xe0, 0x7f, 0x05} },
+{ 0x1b1c,	13,	{0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x03, 0x7d, 0xff, 0x12, 0x81, 0xe0, 0x22} },
+{ 0x1b29,	16,	{0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0} },
+{ 0x8000,	4,	{0x8e, 0x54, 0x8f, 0x55} },
+{ 0x8004,	16,	{0x75, 0x56, 0x03, 0xe5, 0x55, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xfe} },
+{ 0x8014,	16,	{0xa3, 0xe0, 0x4e, 0x70, 0x03, 0x02, 0x81, 0x0e, 0xe5, 0x56, 0x60, 0x4e, 0x14, 0x60, 0x38, 0x14} },
+{ 0x8024,	16,	{0x60, 0x20, 0x14, 0x60, 0x03, 0x02, 0x80, 0xb2, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0x85} },
+{ 0x8034,	16,	{0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90, 0x7f, 0xa6, 0xf0} },
+{ 0x8044,	16,	{0x80, 0x6c, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0xc3, 0x94, 0x20, 0x40, 0x09, 0xa3, 0xa3} },
+{ 0x8054,	16,	{0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x57, 0x15, 0x56, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3} },
+{ 0x8064,	16,	{0xa3, 0xe0, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x44, 0xe5, 0x55, 0x24, 0x06, 0xf5, 0x82} },
+{ 0x8074,	16,	{0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5} },
+{ 0x8084,	16,	{0x83, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe5, 0x55, 0x24} },
+{ 0x8094,	16,	{0x04, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0x74, 0xff, 0xf5, 0xf0, 0x12, 0x9b, 0xb8, 0x85} },
+{ 0x80a4,	16,	{0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xb8, 0x90, 0x7f} },
+{ 0x80b4,	16,	{0xa5, 0xe0, 0xf5, 0x57, 0x30, 0xe0, 0xf7, 0x30, 0xe2, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06} },
+{ 0x80c4,	16,	{0x22, 0xe5, 0x57, 0x20, 0xe1, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x07, 0x22} },
+{ 0x80d4,	16,	{0xe5, 0x56, 0x70, 0x31, 0x7f, 0x01, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44} },
+{ 0x80e4,	16,	{0x80, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90} },
+{ 0x80f4,	16,	{0x7f, 0xa6, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0xf5, 0x57, 0x30, 0xe0, 0xf7, 0x30, 0xe1, 0xd5, 0x75} },
+{ 0x8104,	12,	{0x56, 0x03, 0x02, 0x80, 0x07, 0x15, 0x56, 0x02, 0x80, 0x07, 0x7f, 0x08} },
+{ 0x8110,	1,	{0x22} },
+{ 0x8111,	2,	{0xac, 0x07} },
+{ 0x8113,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xec, 0x25, 0xe0, 0x44, 0x41, 0x90, 0x7f, 0xa6, 0xf0} },
+{ 0x8123,	16,	{0x7b, 0x3c, 0xaf, 0x03, 0x1b, 0xef, 0x70, 0x16, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90} },
+{ 0x8133,	16,	{0x7f, 0xa6, 0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x60, 0xd4, 0x80, 0xf8, 0x90, 0x7f} },
+{ 0x8143,	16,	{0xa5, 0xe0, 0xfd, 0x30, 0xe0, 0xdc, 0x20, 0xe1, 0x09, 0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f} },
+{ 0x8153,	16,	{0xf9, 0x22, 0xed, 0x30, 0xe2, 0x0c, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f} },
+{ 0x8163,	16,	{0xfa, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0x7b, 0x1e} },
+{ 0x8173,	16,	{0xaf, 0x03, 0x1b, 0xef, 0x70, 0x16, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6} },
+{ 0x8183,	16,	{0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x60, 0x86, 0x80, 0xf8, 0x90, 0x7f, 0xa5, 0xe0} },
+{ 0x8193,	16,	{0xfd, 0x20, 0xe0, 0xdc, 0x7b, 0x3c, 0xaf, 0x03, 0x1b, 0xef, 0x70, 0x19, 0x90, 0x7f, 0xa5, 0xe0} },
+{ 0x81a3,	16,	{0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x70, 0x03} },
+{ 0x81b3,	16,	{0x02, 0x81, 0x13, 0x80, 0xf5, 0x90, 0x7f, 0xa5, 0xe0, 0xfd, 0x30, 0xe0, 0xd9, 0x30, 0xe2, 0x09} },
+{ 0x81c3,	16,	{0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f, 0xfa, 0x22, 0xc2, 0xaf, 0x90, 0x7f, 0xa5, 0xe0, 0x44} },
+{ 0x81d3,	12,	{0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0xd2, 0xaf, 0xff, 0x7e, 0x00} },
+{ 0x81df,	1,	{0x22} },
+{ 0x81e0,	2,	{0xae, 0x07} },
+{ 0x81e2,	16,	{0x7c, 0x02, 0xec, 0x14, 0x60, 0x15, 0x14, 0x70, 0x1e, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0} },
+{ 0x81f2,	16,	{0xee, 0x25, 0xe0, 0x44, 0x40, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xa6, 0xed, 0xf0} },
+{ 0x8202,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0xfb, 0x30, 0xe0, 0xf8, 0xbc} },
+{ 0x8212,	16,	{0x02, 0x0a, 0x20, 0xe1, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x07, 0x22, 0xeb, 0x30, 0xe2, 0x0a} },
+{ 0x8222,	14,	{0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06, 0x22, 0xdc, 0xb6, 0x7f, 0x08} },
+{ 0x8230,	1,	{0x22} },
+{ 0x8231,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc2, 0xa9, 0x90, 0x03, 0x00, 0x74, 0x19, 0xf0, 0xd2, 0xa9} },
+{ 0x8241,	15,	{0x53, 0x91, 0x7f, 0x90, 0x01, 0xc4, 0xe4, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x8250,	16,	{0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xab, 0x82, 0xfa, 0xf5} },
+{ 0x8260,	16,	{0x83, 0xa3, 0xe4, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x5e, 0x74, 0xbf} },
+{ 0x8270,	16,	{0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xe0, 0x44, 0x10, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3} },
+{ 0x8280,	16,	{0xa3, 0xa3, 0xe4, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xf0, 0xf9, 0xed, 0x60, 0x1d, 0x74, 0x01} },
+{ 0x8290,	16,	{0x7e, 0x00, 0xa8, 0x07, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0xe4} },
+{ 0x82a0,	16,	{0xef, 0x55, 0x3b, 0x60, 0x04, 0x79, 0x09, 0x80, 0x02, 0x79, 0x0d, 0x8b, 0x82, 0x8a, 0x83, 0xa3} },
+{ 0x82b0,	16,	{0xa3, 0xa3, 0x74, 0xbf, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xe0, 0x54, 0xef, 0xf0, 0x8b} },
+{ 0x82c0,	16,	{0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xa3, 0xe5, 0x5e, 0xf0, 0xae, 0x02, 0xaf, 0x03, 0x8f, 0x82, 0x8e} },
+{ 0x82d0,	4,	{0x83, 0xa3, 0xe9, 0xf0} },
+{ 0x82d4,	1,	{0x22} },
+{ 0x82d5,	4,	{0x8f, 0x5e, 0x8d, 0x5f} },
+{ 0x82d9,	16,	{0xe4, 0xf5, 0x60, 0x74, 0x3c, 0x2f, 0xf8, 0x76, 0x08, 0xe5, 0x5e, 0x75, 0xf0, 0x0d, 0xa4, 0x24} },
+{ 0x82e9,	16,	{0x02, 0xf5, 0x82, 0xe4, 0x34, 0x03, 0xf5, 0x83, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe} },
+{ 0x82f9,	16,	{0xa3, 0xe0, 0xff, 0x7b, 0x80, 0x7a, 0x25, 0x79, 0x00, 0x78, 0x00, 0xc3, 0x12, 0x9c, 0xea, 0x50} },
+{ 0x8309,	16,	{0x3c, 0xe5, 0x5e, 0x75, 0xf0, 0x0d, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, 0x34, 0x03, 0xf5, 0x83} },
+{ 0x8319,	16,	{0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x7b, 0x00, 0x7a, 0x96, 0x78} },
+{ 0x8329,	16,	{0x00, 0xc3, 0x12, 0x9c, 0xea, 0x40, 0x0c, 0x75, 0x60, 0x40, 0x74, 0x3c, 0x25, 0x5e, 0xf8, 0x76} },
+{ 0x8339,	16,	{0x10, 0x80, 0x0a, 0x75, 0x60, 0x80, 0x74, 0x3c, 0x25, 0x5e, 0xf8, 0x76, 0x38, 0xe5, 0x60, 0x45} },
+{ 0x8349,	16,	{0x5f, 0x44, 0x01, 0xff, 0xe5, 0x5e, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x8359,	5,	{0x20, 0xf5, 0x83, 0xef, 0xf0} },
+{ 0x835e,	1,	{0x22} },
+{ 0x835f,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x54, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82} },
+{ 0x836f,	16,	{0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x55, 0x8f, 0x56, 0xe5, 0x54, 0x25, 0xe0, 0x24, 0xc6, 0xf5} },
+{ 0x837f,	16,	{0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x0f, 0xe5, 0x54, 0x25, 0xe0, 0x24, 0xc7} },
+{ 0x838f,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x74, 0x22, 0x25, 0x54, 0xf8, 0xe4, 0xf6} },
+{ 0x839f,	16,	{0xe5, 0x56, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x55, 0xf5, 0x83, 0xe0, 0x44, 0x03, 0xf0, 0xaf} },
+{ 0x83af,	16,	{0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0xaf, 0x54, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x85, 0x56, 0x82} },
+{ 0x83bf,	16,	{0x85, 0x55, 0x83, 0xa3, 0xa3, 0xe0, 0x20, 0xe0, 0x22, 0xe0, 0xff, 0xe5, 0x56, 0x24, 0x05, 0xf5} },
+{ 0x83cf,	16,	{0x82, 0xe4, 0x35, 0x55, 0xf5, 0x83, 0xe0, 0xe5, 0x56, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x55} },
+{ 0x83df,	16,	{0xf5, 0x83, 0xe0, 0xff, 0xaf, 0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x74, 0xf8, 0x25, 0x54, 0xf5} },
+{ 0x83ef,	16,	{0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x54, 0x25, 0xe0, 0xff, 0xc3, 0x74, 0x0c} },
+{ 0x83ff,	16,	{0x9f, 0x75, 0xf0, 0x40, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x7b, 0xaf, 0x82, 0xfe} },
+{ 0x840f,	16,	{0xe5, 0x54, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xee, 0xf0, 0xa3} },
+{ 0x841f,	16,	{0xef, 0xf0, 0xaf, 0x54, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42} },
+{ 0x842f,	3,	{0x30, 0x7f, 0x00} },
+{ 0x8432,	1,	{0x22} },
+{ 0x8433,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x54, 0xff, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0x74, 0xf8} },
+{ 0x8443,	16,	{0x25, 0x54, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x54, 0x75, 0xf0, 0x08} },
+{ 0x8453,	16,	{0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x56, 0x8f, 0x57, 0xf5, 0x83} },
+{ 0x8463,	16,	{0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xaf} },
+{ 0x8473,	16,	{0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0xe5, 0x57, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x56, 0xf5} },
+{ 0x8483,	16,	{0x83, 0xe0, 0x30, 0xe0, 0x09, 0x85, 0x57, 0x82, 0x85, 0x56, 0x83, 0xe0, 0xf5, 0x55, 0xaf, 0x54} },
+{ 0x8493,	16,	{0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf4, 0x52, 0x30, 0xe5, 0x54} },
+{ 0x84a3,	16,	{0x25, 0xe0, 0x24, 0xc6, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x0f, 0xe5} },
+{ 0x84b3,	16,	{0x54, 0x25, 0xe0, 0x24, 0xc7, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x7f, 0x00} },
+{ 0x84c3,	1,	{0x22} },
+{ 0x84c4,	4,	{0x8e, 0x54, 0x8f, 0x55} },
+{ 0x84c8,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x56, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x01, 0xf5, 0x82} },
+{ 0x84d8,	16,	{0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x54, 0x03, 0x70, 0x23, 0x85, 0x55, 0x82, 0x8e, 0x83, 0xa3} },
+{ 0x84e8,	16,	{0xe0, 0x30, 0xe0, 0x07, 0xaf, 0x56, 0x7d, 0x02, 0x12, 0x82, 0xd5, 0x85, 0x55, 0x82, 0x85, 0x54} },
+{ 0x84f8,	15,	{0x83, 0xa3, 0xe0, 0x30, 0xe1, 0x07, 0xaf, 0x56, 0x7d, 0x04, 0x12, 0x82, 0xd5, 0x7f, 0x00} },
+{ 0x8507,	1,	{0x22} },
+{ 0x8508,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0xa3, 0xa3, 0xa3, 0xe0, 0xfc, 0xed} },
+{ 0x8518,	16,	{0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xc0, 0x83, 0xc0} },
+{ 0x8528,	16,	{0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0} },
+{ 0x8538,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0xe0, 0xfd, 0xec, 0x6d, 0xd0} },
+{ 0x8548,	16,	{0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x8f} },
+{ 0x8558,	16,	{0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82} },
+{ 0x8568,	16,	{0x8e, 0x83, 0xa3, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xe0} },
+{ 0x8578,	16,	{0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xc0, 0x83, 0xc0} },
+{ 0x8588,	16,	{0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xff, 0xed, 0x6f, 0xd0, 0x82, 0xd0} },
+{ 0x8598,	3,	{0x83, 0xf0, 0x22} },
+{ 0x859b,	16,	{0x79, 0x0d, 0x8e, 0x54, 0x8f, 0x55, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x0d, 0xa4, 0x24} },
+{ 0x85ab,	16,	{0xf4, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xaf, 0x82, 0xfe, 0xad, 0x01, 0x19, 0xed, 0x60, 0x24, 0x0f} },
+{ 0x85bb,	16,	{0xef, 0xac, 0x06, 0x70, 0x01, 0x0e, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe0, 0xfd, 0x05, 0x55, 0xe5} },
+{ 0x85cb,	16,	{0x55, 0xaa, 0x54, 0x70, 0x02, 0x05, 0x54, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xe0, 0x6d, 0x60, 0xd9} },
+{ 0x85db,	5,	{0x7f, 0x01, 0x22, 0x7f, 0x00} },
+{ 0x85e0,	1,	{0x22} },
+{ 0x85e1,	4,	{0x8e, 0x54, 0x8f, 0x55} },
+{ 0x85e5,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x5b, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82} },
+{ 0x85f5,	16,	{0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x5c, 0x8f, 0x5d, 0xaa, 0x06, 0xa9, 0x55, 0x7b, 0x01, 0xc0} },
+{ 0x8605,	16,	{0x03, 0xc0, 0x01, 0xe5, 0x5b, 0x75, 0xf0, 0x0d, 0xa4, 0x24, 0x01, 0xf9, 0x74, 0x03, 0x35, 0xf0} },
+{ 0x8615,	16,	{0xa8, 0x01, 0xfc, 0xad, 0x03, 0xd0, 0x01, 0xd0, 0x03, 0x7e, 0x00, 0x7f, 0x0d, 0x12, 0x9b, 0x49} },
+{ 0x8625,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3} },
+{ 0x8635,	16,	{0xe0, 0xff, 0x7b, 0x08, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xd3, 0x12, 0x9c, 0xea, 0x40, 0x10} },
+{ 0x8645,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0x12, 0x9d, 0x31, 0x00, 0x00, 0x00, 0x08, 0x80, 0x2e} },
+{ 0x8655,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3} },
+{ 0x8665,	16,	{0xe0, 0xff, 0x7b, 0x00, 0x7a, 0x08, 0x79, 0x07, 0x78, 0x00, 0xc3, 0x12, 0x9c, 0xea, 0x50, 0x0e} },
+{ 0x8675,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0x12, 0x9d, 0x31, 0x00, 0x07, 0x08, 0x00, 0x85, 0x55} },
+{ 0x8685,	16,	{0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb} },
+{ 0x8695,	16,	{0x7f, 0x00, 0x7e, 0x50, 0x7d, 0x46, 0x7c, 0x00, 0x12, 0x9c, 0x58, 0x8f, 0x59, 0x8e, 0x58, 0x8d} },
+{ 0x86a5,	16,	{0x57, 0x8c, 0x56, 0x7b, 0x0a, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0x12, 0x9c, 0x58, 0xaf, 0x03} },
+{ 0x86b5,	16,	{0x8f, 0x5a, 0xaf, 0x59, 0xae, 0x58, 0xad, 0x57, 0xac, 0x56, 0x7b, 0x0a, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x86c5,	16,	{0x78, 0x00, 0x12, 0x9c, 0x58, 0x8f, 0x59, 0x8e, 0x58, 0x8d, 0x57, 0x8c, 0x56, 0xe5, 0x5a, 0xc3} },
+{ 0x86d5,	16,	{0x94, 0x05, 0x40, 0x15, 0xe5, 0x59, 0x24, 0x01, 0xf5, 0x59, 0xe4, 0x35, 0x58, 0xf5, 0x58, 0xe4} },
+{ 0x86e5,	16,	{0x35, 0x57, 0xf5, 0x57, 0xe4, 0x35, 0x56, 0xf5, 0x56, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3} },
+{ 0x86f5,	16,	{0xe4, 0xf0, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x44, 0x80, 0xf0, 0x85} },
+{ 0x8705,	16,	{0x5d, 0x82, 0x85, 0x5c, 0x83, 0xe5, 0x59, 0xf0, 0xaf, 0x59, 0xae, 0x58, 0xad, 0x57, 0xac, 0x56} },
+{ 0x8715,	16,	{0x78, 0x08, 0x12, 0x9c, 0xfb, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xef, 0xf0, 0x85, 0x5d} },
+{ 0x8725,	16,	{0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0xe4, 0xf5, 0x5a, 0xe5, 0x55} },
+{ 0x8735,	16,	{0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xb4, 0x62, 0x05, 0x43, 0x5a} },
+{ 0x8745,	16,	{0x0a, 0x80, 0x10, 0xef, 0xb4, 0x72, 0x05, 0x43, 0x5a, 0x08, 0x80, 0x07, 0xef, 0xb4, 0x74, 0x03} },
+{ 0x8755,	16,	{0x43, 0x5a, 0x02, 0xe5, 0x55, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff} },
+{ 0x8765,	16,	{0x30, 0xe3, 0x03, 0x43, 0x5a, 0x80, 0xef, 0x30, 0xe7, 0x12, 0x43, 0x5a, 0x40, 0xe5, 0x5d, 0x24} },
+{ 0x8775,	16,	{0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x55, 0x24, 0x0b} },
+{ 0x8785,	16,	{0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0x20, 0xe1, 0x03, 0x30, 0xe4, 0x23, 0xaf} },
+{ 0x8795,	16,	{0x5b, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42, 0x3b, 0xe5, 0x5d} },
+{ 0x87a5,	16,	{0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x44, 0x01, 0xf0, 0xe4, 0xf5, 0x5a} },
+{ 0x87b5,	16,	{0x80, 0x10, 0xaf, 0x5b, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf4} },
+{ 0x87c5,	16,	{0x52, 0x3b, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0x74, 0xbf, 0xf0, 0x85, 0x5d} },
+{ 0x87d5,	16,	{0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xe4, 0xf0, 0xe5, 0x5a, 0xf0, 0xe5, 0x55, 0x24, 0x0a, 0xf5} },
+{ 0x87e5,	16,	{0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35} },
+{ 0x87f5,	16,	{0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x55, 0x24, 0x0a, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83} },
+{ 0x8805,	16,	{0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5} },
+{ 0x8815,	16,	{0x55, 0x24, 0x09, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x06} },
+{ 0x8825,	16,	{0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x55, 0x24, 0x09, 0xf5, 0x82, 0xe4} },
+{ 0x8835,	16,	{0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5} },
+{ 0x8845,	16,	{0x83, 0xef, 0xf0, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe4, 0xf0, 0x85, 0x5d} },
+{ 0x8855,	16,	{0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xf0, 0xaf, 0x5b, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x75, 0x5a} },
+{ 0x8865,	16,	{0x08, 0xe5, 0x55, 0x24, 0x0c, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0x60, 0x03, 0x43} },
+{ 0x8875,	16,	{0x5a, 0x10, 0xe5, 0x5d, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x54, 0x03} },
+{ 0x8885,	16,	{0x45, 0x5a, 0xf0, 0xe5, 0x55, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0x14} },
+{ 0x8895,	16,	{0xff, 0x25, 0xe0, 0x25, 0xe0, 0xff, 0xe5, 0x55, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5} },
+{ 0x88a5,	16,	{0x83, 0xe0, 0x24, 0xfb, 0x4f, 0xf5, 0x5a, 0xe5, 0x55, 0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x54} },
+{ 0x88b5,	16,	{0xf5, 0x83, 0xe0, 0x24, 0xd0, 0x60, 0x15, 0x14, 0x60, 0x17, 0x24, 0xc2, 0x60, 0x09, 0x24, 0x0a} },
+{ 0x88c5,	16,	{0x70, 0x12, 0x43, 0x5a, 0x18, 0x80, 0x0d, 0x43, 0x5a, 0x08, 0x80, 0x08, 0x43, 0x5a, 0x38, 0x80} },
+{ 0x88d5,	16,	{0x03, 0x43, 0x5a, 0x28, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe5, 0x5a, 0xf0} },
+{ 0x88e5,	9,	{0xaf, 0x5b, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x7f, 0x00} },
+{ 0x88ee,	1,	{0x22} },
+{ 0x88ef,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4} },
+{ 0x88ff,	16,	{0x34, 0x20, 0xad, 0x82, 0xfc, 0x90, 0x01, 0x2c, 0x74, 0x08, 0xf0, 0xef, 0x04, 0xa3, 0xf0, 0xe4} },
+{ 0x890f,	16,	{0xa3, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5} },
+{ 0x891f,	16,	{0x83, 0xe0, 0x90, 0x01, 0x2f, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0xf5, 0x82} },
+{ 0x892f,	16,	{0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0x1e, 0x90, 0x01, 0x30, 0xf0, 0x74, 0x2c, 0x2f, 0xf8} },
+{ 0x893f,	16,	{0xe6, 0xa3, 0xf0, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x54} },
+{ 0x894f,	16,	{0x7f, 0x02, 0x12, 0x81, 0x11, 0xc3, 0xee, 0x64, 0x80, 0x94, 0x80, 0x40, 0xf3, 0xe5, 0x54, 0x5f} },
+{ 0x895f,	16,	{0x90, 0x01, 0x32, 0xf0, 0x7e, 0x01, 0x7f, 0x2c, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e} },
+{ 0x896f,	11,	{0x00, 0x75, 0x5d, 0x00, 0x7d, 0x07, 0x12, 0x8e, 0x0c, 0x7f, 0x00} },
+{ 0x897a,	1,	{0x22} },
+{ 0x897b,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} },
+{ 0x898b,	16,	{0xaf, 0x82, 0xfe, 0x90, 0x01, 0x33, 0x74, 0x0a, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x24} },
+{ 0x899b,	16,	{0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x90, 0x01, 0x34, 0xf0, 0x7e, 0x01, 0x7f} },
+{ 0x89ab,	16,	{0x33, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x02, 0x12} },
+{ 0x89bb,	4,	{0x8e, 0x0c, 0x7f, 0x00} },
+{ 0x89bf,	1,	{0x22} },
+{ 0x89c0,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} },
+{ 0x89d0,	16,	{0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0f, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c} },
+{ 0x89e0,	16,	{0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x11, 0xae, 0x04, 0xaf, 0x05, 0xef, 0x24, 0x04, 0xf5} },
+{ 0x89f0,	11,	{0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0xf0, 0x7f, 0x00} },
+{ 0x89fb,	1,	{0x22} },
+{ 0x89fc,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} },
+{ 0x8a0c,	16,	{0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0f, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c} },
+{ 0x8a1c,	16,	{0xf5, 0x83, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x11, 0xae, 0x04, 0xaf, 0x05, 0xef, 0x24, 0x04, 0xf5} },
+{ 0x8a2c,	11,	{0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x00} },
+{ 0x8a37,	1,	{0x22} },
+{ 0x8a38,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} },
+{ 0x8a48,	16,	{0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0d, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3} },
+{ 0x8a58,	16,	{0xe0, 0x44, 0x40, 0xf0, 0x80, 0x0f, 0xae, 0x04, 0xaf, 0x05, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3} },
+{ 0x8a68,	7,	{0xa3, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x00} },
+{ 0x8a6f,	1,	{0x22} },
+{ 0x8a70,	4,	{0x8e, 0x54, 0x8f, 0x55} },
+{ 0x8a74,	16,	{0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xe0, 0xf5, 0x58, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x24, 0xfe, 0x60} },
+{ 0x8a84,	16,	{0x16, 0x14, 0x60, 0x1f, 0x14, 0x60, 0x28, 0x24, 0x03, 0x70, 0x2e, 0x7e, 0x7e, 0x7f, 0x80, 0x75} },
+{ 0x8a94,	16,	{0x56, 0x7e, 0x75, 0x57, 0x80, 0x80, 0x22, 0x7e, 0x7e, 0x7f, 0x00, 0x75, 0x56, 0x7e, 0x75, 0x57} },
+{ 0x8aa4,	16,	{0x00, 0x80, 0x16, 0x7e, 0x7d, 0x7f, 0x80, 0x75, 0x56, 0x7d, 0x75, 0x57, 0x80, 0x80, 0x0a, 0x7e} },
+{ 0x8ab4,	16,	{0x7d, 0x7f, 0x00, 0x75, 0x56, 0x7d, 0x75, 0x57, 0x00, 0xe5, 0x58, 0x70, 0x20, 0x85, 0x57, 0x82} },
+{ 0x8ac4,	16,	{0x85, 0x56, 0x83, 0x74, 0xff, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x25, 0xe0, 0x24} },
+{ 0x8ad4,	16,	{0xb5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x4d, 0xe5, 0x55, 0x24} },
+{ 0x8ae4,	16,	{0x02, 0xff, 0xe4, 0x35, 0x54, 0xfe, 0xe5, 0x58, 0x60, 0x23, 0x0f, 0xef, 0xac, 0x06, 0x70, 0x01} },
+{ 0x8af4,	16,	{0x0e, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe0, 0xfd, 0x05, 0x57, 0xe5, 0x57, 0xaa, 0x56, 0x70, 0x02} },
+{ 0x8b04,	16,	{0x05, 0x56, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xed, 0xf0, 0x15, 0x58, 0x80, 0xd9, 0x85, 0x55, 0x82} },
+{ 0x8b14,	16,	{0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x25, 0xe0, 0x24} },
+{ 0x8b24,	12,	{0xb5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x7f, 0x00} },
+{ 0x8b30,	1,	{0x22} },
+{ 0x8b31,	16,	{0xef, 0x24, 0x05, 0xf5, 0x55, 0xe4, 0x3e, 0xf5, 0x54, 0x90, 0x01, 0x35, 0x74, 0x07, 0xf0, 0x90} },
+{ 0x8b41,	16,	{0x01, 0x7a, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x36, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3} },
+{ 0x8b51,	16,	{0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0x8e, 0x56, 0xf5, 0x57, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} },
+{ 0x8b61,	16,	{0xe0, 0x24, 0x9e, 0x60, 0x61, 0x24, 0xf9, 0x60, 0x0e, 0x24, 0xf1, 0x70, 0x03, 0x02, 0x8c, 0x12} },
+{ 0x8b71,	16,	{0x24, 0x14, 0x60, 0x03, 0x02, 0x8c, 0x5e, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe} },
+{ 0x8b81,	16,	{0xa3, 0xe0, 0xff, 0xc3, 0xe4, 0x9f, 0xf5, 0x59, 0x74, 0x01, 0x9e, 0xf5, 0x58, 0xd3, 0xe5, 0x59} },
+{ 0x8b91,	16,	{0x94, 0x40, 0xe5, 0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x40, 0xd3, 0xe5} },
+{ 0x8ba1,	16,	{0x57, 0x95, 0x59, 0xe5, 0x56, 0x95, 0x58, 0x50, 0x03, 0x02, 0x8c, 0x61, 0xae, 0x58, 0xaf, 0x59} },
+{ 0x8bb1,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e} },
+{ 0x8bc1,	16,	{0x56, 0xf5, 0x57, 0x02, 0x8c, 0x61, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3} },
+{ 0x8bd1,	16,	{0xe0, 0xff, 0xc3, 0x74, 0x30, 0x9f, 0xf5, 0x59, 0xe4, 0x9e, 0xf5, 0x58, 0xd3, 0xe5, 0x59, 0x94} },
+{ 0x8be1,	16,	{0x10, 0xe5, 0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x10, 0xd3, 0xe5, 0x57} },
+{ 0x8bf1,	16,	{0x95, 0x59, 0xe5, 0x56, 0x95, 0x58, 0x40, 0x68, 0xae, 0x58, 0xaf, 0x59, 0x85, 0x55, 0x82, 0x85} },
+{ 0x8c01,	16,	{0x54, 0x83, 0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e, 0x56, 0xf5, 0x57, 0x80} },
+{ 0x8c11,	16,	{0x4f, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0xe4, 0x9f} },
+{ 0x8c21,	16,	{0xf5, 0x59, 0xe4, 0x9e, 0xf5, 0x58, 0x45, 0x59, 0x60, 0x0b, 0xd3, 0xe5, 0x59, 0x94, 0x40, 0xe5} },
+{ 0x8c31,	16,	{0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x40, 0xd3, 0xe5, 0x57, 0x95, 0x59} },
+{ 0x8c41,	16,	{0xe5, 0x56, 0x95, 0x58, 0x40, 0x17, 0xae, 0x58, 0xaf, 0x59, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} },
+{ 0x8c51,	16,	{0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e, 0x56, 0xf5, 0x57, 0x7f, 0x01, 0x22} },
+{ 0x8c61,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x24, 0x9e, 0x70, 0x03, 0x02, 0x8d, 0x21, 0x24, 0xf9} },
+{ 0x8c71,	16,	{0x60, 0x58, 0x24, 0xf1, 0x70, 0x03, 0x02, 0x8d, 0x71, 0x24, 0x14, 0x60, 0x03, 0x02, 0x8d, 0xb5} },
+{ 0x8c81,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xd3, 0x94, 0xff, 0xee} },
+{ 0x8c91,	16,	{0x94, 0x00, 0x40, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x75, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57} },
+{ 0x8ca1,	16,	{0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x70, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x75, 0xe0} },
+{ 0x8cb1,	16,	{0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b} },
+{ 0x8cc1,	16,	{0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd2, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} },
+{ 0x8cd1,	16,	{0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0x94, 0x80, 0xee, 0x94, 0x00, 0x50, 0x03, 0x02, 0x8d} },
+{ 0x8ce1,	16,	{0xb5, 0xd3, 0xef, 0x94, 0xff, 0xee, 0x94, 0x00, 0x40, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x76} },
+{ 0x8cf1,	16,	{0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x70, 0x03, 0x02} },
+{ 0x8d01,	16,	{0x8d, 0xb5, 0x90, 0x01, 0x76, 0xe0, 0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a} },
+{ 0x8d11,	16,	{0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd2} },
+{ 0x8d21,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0x94, 0x20, 0xee} },
+{ 0x8d31,	16,	{0x94, 0x00, 0x50, 0x03, 0x02, 0x8d, 0xb5, 0xd3, 0xef, 0x94, 0x2f, 0xee, 0x94, 0x00, 0x50, 0x74} },
+{ 0x8d41,	16,	{0x90, 0x01, 0x77, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e} },
+{ 0x8d51,	16,	{0x60, 0x62, 0x90, 0x01, 0x77, 0xe0, 0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a} },
+{ 0x8d61,	16,	{0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd5} },
+{ 0x8d71,	16,	{0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0x01, 0x78, 0xcf, 0xf0} },
+{ 0x8d81,	16,	{0xa3, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x60, 0x24} },
+{ 0x8d91,	16,	{0x90, 0x01, 0x78, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xe0} },
+{ 0x8da1,	16,	{0xff, 0x90, 0x01, 0x7a, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83} },
+{ 0x8db1,	16,	{0xef, 0xf0, 0x80, 0xcf, 0x7e, 0x01, 0x7f, 0x35, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3} },
+{ 0x8dc1,	16,	{0xa3, 0xe0, 0xa3, 0xe0, 0x04, 0xfd, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75} },
+{ 0x8dd1,	7,	{0x5d, 0x00, 0x12, 0x8e, 0x0c, 0x7f, 0x00} },
+{ 0x8dd8,	1,	{0x22} },
+{ 0x8dd9,	16,	{0x8e, 0x61, 0x8f, 0x62, 0x8c, 0x63, 0x8d, 0x64, 0xaf, 0x03, 0x1b, 0xef, 0x60, 0x24, 0x05, 0x62} },
+{ 0x8de9,	16,	{0xe5, 0x62, 0xae, 0x61, 0x70, 0x02, 0x05, 0x61, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe0, 0xff, 0x05} },
+{ 0x8df9,	16,	{0x64, 0xe5, 0x64, 0xac, 0x63, 0x70, 0x02, 0x05, 0x63, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0} },
+{ 0x8e09,	3,	{0x80, 0xd6, 0x22} },
+{ 0x8e0c,	16,	{0x8d, 0x5c, 0xab, 0x07, 0xaa, 0x06, 0xaf, 0x60, 0xae, 0x5f, 0xad, 0x5e, 0xac, 0x5d, 0xec, 0x4d} },
+{ 0x8e1c,	16,	{0x4e, 0x4f, 0x60, 0x1c, 0x90, 0x7f, 0xc2, 0xe0, 0x30, 0xe1, 0x1c, 0x7f, 0xff, 0x7e, 0xff, 0x7d} },
+{ 0x8e2c,	16,	{0xff, 0x7c, 0xff, 0x78, 0x5d, 0x12, 0x9d, 0x0e, 0xec, 0x4d, 0x4e, 0x4f, 0x60, 0x09, 0x80, 0xe4} },
+{ 0x8e3c,	16,	{0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0xf9, 0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x16, 0xaf, 0x03} },
+{ 0x8e4c,	16,	{0xae, 0x02, 0x7c, 0x7b, 0x7d, 0x80, 0xab, 0x5c, 0x12, 0x8d, 0xd9, 0x90, 0x7f, 0xc3, 0xe5, 0x5c} },
+{ 0x8e5c,	7,	{0xf0, 0x7f, 0x01, 0x22, 0x7f, 0x00, 0x22} },
+{ 0x8e63,	16,	{0x90, 0x01, 0x84, 0x74, 0x0b, 0xf0, 0x90, 0x20, 0x70, 0xe0, 0x54, 0xf0, 0xff, 0xc4, 0x54, 0x0f} },
+{ 0x8e73,	16,	{0x90, 0x01, 0x85, 0xf0, 0x90, 0x11, 0xfd, 0xe4, 0x93, 0x90, 0x01, 0x86, 0xf0, 0x90, 0x11, 0xfe} },
+{ 0x8e83,	16,	{0xe4, 0x93, 0x90, 0x01, 0x87, 0xf0, 0xe4, 0x90, 0x01, 0x7c, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3} },
+{ 0x8e93,	16,	{0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x88, 0xf0, 0x7e} },
+{ 0x8ea3,	16,	{0x01, 0x7f, 0x7c, 0x12, 0x19, 0xc1, 0x7e, 0x01, 0x7f, 0x84, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00} },
+{ 0x8eb3,	13,	{0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x14, 0x12, 0x8e, 0x0c, 0x7f, 0x00} },
+{ 0x8ec0,	1,	{0x22} },
+{ 0x8ec1,	4,	{0x8e, 0x4b, 0x8f, 0x4c} },
+{ 0x8ec5,	16,	{0x85, 0x4b, 0x4e, 0x85, 0x4c, 0x4f, 0xe5, 0x4f, 0x24, 0x01, 0xf5, 0x53, 0xe4, 0x35, 0x4e, 0xf5} },
+{ 0x8ed5,	16,	{0x52, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xe0, 0x14, 0xb4, 0x0f, 0x00, 0x40, 0x03, 0x02, 0x8f} },
+{ 0x8ee5,	16,	{0xd6, 0x90, 0x8e, 0xed, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x8f, 0x1a, 0x02, 0x8f, 0x26, 0x02, 0x8f} },
+{ 0x8ef5,	16,	{0x32, 0x02, 0x8f, 0x5c, 0x02, 0x8f, 0x67, 0x02, 0x8f, 0x72, 0x02, 0x8f, 0x7d, 0x02, 0x8f, 0x88} },
+{ 0x8f05,	16,	{0x02, 0x8f, 0x93, 0x02, 0x8f, 0x9e, 0x02, 0x8f, 0xa9, 0x02, 0x8f, 0xb0, 0x02, 0x8f, 0xd6, 0x02} },
+{ 0x8f15,	16,	{0x8f, 0xbb, 0x02, 0x8f, 0xc6, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x83, 0x5f, 0x8f, 0x4d, 0x02, 0x8f} },
+{ 0x8f25,	16,	{0xd6, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x84, 0x33, 0x8f, 0x4d, 0x02, 0x8f, 0xd6, 0x85, 0x52, 0x50} },
+{ 0x8f35,	16,	{0x85, 0x53, 0x51, 0xe5, 0x51, 0x24, 0x01, 0xff, 0xe4, 0x35, 0x50, 0xfe, 0x12, 0x85, 0x08, 0xaf} },
+{ 0x8f45,	16,	{0x51, 0xae, 0x50, 0x12, 0x85, 0x9b, 0xef, 0x70, 0x03, 0x02, 0x8f, 0xd6, 0xaf, 0x51, 0xae, 0x50} },
+{ 0x8f55,	16,	{0x12, 0x85, 0xe1, 0x8f, 0x4d, 0x80, 0x7a, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0xc0, 0x8f, 0x4d} },
+{ 0x8f65,	16,	{0x80, 0x6f, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0xfc, 0x8f, 0x4d, 0x80, 0x64, 0xaf, 0x53, 0xae} },
+{ 0x8f75,	16,	{0x52, 0x12, 0x8a, 0x38, 0x8f, 0x4d, 0x80, 0x59, 0xaf, 0x4c, 0xae, 0x4b, 0x12, 0x8b, 0x31, 0x8f} },
+{ 0x8f85,	16,	{0x4d, 0x80, 0x4e, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x88, 0xef, 0x8f, 0x4d, 0x80, 0x43, 0xaf, 0x53} },
+{ 0x8f95,	16,	{0xae, 0x52, 0x12, 0x84, 0xc4, 0x8f, 0x4d, 0x80, 0x38, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0x7b} },
+{ 0x8fa5,	16,	{0x8f, 0x4d, 0x80, 0x2d, 0x12, 0x8e, 0x63, 0x8f, 0x4d, 0x80, 0x26, 0xaf, 0x53, 0xae, 0x52, 0x12} },
+{ 0x8fb5,	16,	{0x11, 0xe5, 0x8f, 0x4d, 0x80, 0x1b, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x8a, 0x70, 0x8f, 0x4d, 0x80} },
+{ 0x8fc5,	16,	{0x10, 0xaf, 0x4c, 0xae, 0x4b, 0x7c, 0x02, 0x7d, 0xaf, 0x7b, 0x40, 0x12, 0x8d, 0xd9, 0xe4, 0xf5} },
+{ 0x8fd5,	16,	{0x4d, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xe0, 0xff, 0x14, 0x24, 0xfa, 0x50, 0x04, 0x24, 0xfe} },
+{ 0x8fe5,	16,	{0x70, 0x2b, 0x90, 0x01, 0x98, 0x74, 0x10, 0xf0, 0xa3, 0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} },
+{ 0x8ff5,	16,	{0x83, 0xe0, 0x90, 0x01, 0x9a, 0xf0, 0x7e, 0x01, 0x7f, 0x98, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00} },
+{ 0x9005,	15,	{0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x03, 0x12, 0x8e, 0x0c, 0x8f, 0x4d, 0xaf, 0x4d} },
+{ 0x9014,	1,	{0x22} },
+{ 0x9015,	8,	{0x8f, 0x4e, 0x8e, 0x4d, 0x8d, 0x4c, 0x8c, 0x4b} },
+{ 0x901d,	16,	{0x75, 0x55, 0x01, 0x75, 0x56, 0x9c, 0xe4, 0xf5, 0x54, 0xaf, 0x50, 0x15, 0x50, 0xef, 0x70, 0x03} },
+{ 0x902d,	16,	{0x02, 0x90, 0xb3, 0xaf, 0x4f, 0xe4, 0xfc, 0xfd, 0xfe, 0xf8, 0xf9, 0xfa, 0xab, 0x07, 0xaf, 0x4e} },
+{ 0x903d,	16,	{0xae, 0x4d, 0xad, 0x4c, 0xac, 0x4b, 0x12, 0x9c, 0x58, 0xaf, 0x03, 0x8f, 0x53, 0xaf, 0x4e, 0xae} },
+{ 0x904d,	16,	{0x4d, 0xad, 0x4c, 0xac, 0x4b, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xaf, 0x4f, 0xe4} },
+{ 0x905d,	16,	{0xfc, 0xfd, 0xfe, 0xf8, 0xf9, 0xfa, 0xab, 0x07, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04} },
+{ 0x906d,	16,	{0x12, 0x9c, 0x58, 0x8f, 0x4e, 0x8e, 0x4d, 0x8d, 0x4c, 0x8c, 0x4b, 0xe5, 0x53, 0x24, 0x30, 0xf5} },
+{ 0x907d,	16,	{0x53, 0xd3, 0x94, 0x39, 0x40, 0x06, 0x74, 0x07, 0x25, 0x53, 0xf5, 0x53, 0x05, 0x56, 0xe5, 0x56} },
+{ 0x908d,	16,	{0xae, 0x55, 0x70, 0x02, 0x05, 0x55, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe4, 0xf0, 0x05, 0x56, 0xe5} },
+{ 0x909d,	16,	{0x56, 0xae, 0x55, 0x70, 0x02, 0x05, 0x55, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe5, 0x53, 0xf0, 0x05} },
+{ 0x90ad,	16,	{0x54, 0x05, 0x54, 0x02, 0x90, 0x26, 0xe5, 0x56, 0x15, 0x56, 0x70, 0x02, 0x15, 0x55, 0xaf, 0x54} },
+{ 0x90bd,	16,	{0x15, 0x54, 0xef, 0x60, 0x23, 0xe5, 0x56, 0x15, 0x56, 0xae, 0x55, 0x70, 0x02, 0x15, 0x55, 0xf5} },
+{ 0x90cd,	16,	{0x82, 0x8e, 0x83, 0xe0, 0xff, 0x05, 0x52, 0xe5, 0x52, 0xac, 0x51, 0x70, 0x02, 0x05, 0x51, 0x14} },
+{ 0x90dd,	8,	{0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x80, 0xd6} },
+{ 0x90e5,	1,	{0x22} },
+{ 0x90e6,	16,	{0xe4, 0x90, 0x01, 0xc9, 0xf0, 0x7e, 0x01, 0x7f, 0xca, 0x90, 0x01, 0xbe, 0xee, 0xf0, 0xa3, 0xef} },
+{ 0x90f6,	10,	{0xf0, 0x90, 0x01, 0xc2, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x22} },
+{ 0x9100,	16,	{0xaa, 0x07, 0xa9, 0x05, 0x90, 0x01, 0xc9, 0xe0, 0xc3, 0x94, 0x40, 0x50, 0x61, 0xac, 0x02, 0x74} },
+{ 0x9110,	16,	{0x01, 0x7e, 0x00, 0xa8, 0x04, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff} },
+{ 0x9120,	16,	{0xe4, 0xef, 0x55, 0x30, 0x60, 0x45, 0xea, 0x04, 0xff, 0x90, 0x01, 0xc2, 0xe0, 0xfc, 0xa3, 0xe0} },
+{ 0x9130,	16,	{0xfd, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0xa3, 0xe9, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3} },
+{ 0x9140,	16,	{0xeb, 0xf0, 0x90, 0x01, 0xc2, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x9b, 0xb8, 0xfc, 0xd3, 0xe5, 0xf0} },
+{ 0x9150,	16,	{0x94, 0x87, 0xec, 0x94, 0x02, 0x40, 0x0a, 0x90, 0x01, 0xc2, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0xca} },
+{ 0x9160,	16,	{0xf0, 0xc2, 0xaf, 0x90, 0x01, 0xc9, 0xe0, 0x04, 0xf0, 0xd2, 0xaf, 0x7f, 0x01, 0x22, 0x7f, 0x00} },
+{ 0x9170,	1,	{0x22} },
+{ 0x9171,	16,	{0x90, 0x01, 0xc9, 0xe0, 0xd3, 0x94, 0x00, 0x40, 0x55, 0x90, 0x01, 0xbe, 0xe0, 0xfc, 0xa3, 0xe0} },
+{ 0x9181,	16,	{0xaa, 0x04, 0xf9, 0x7b, 0x01, 0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0xaa, 0x06, 0xa9, 0x07, 0xa8} },
+{ 0x9191,	16,	{0x01, 0xac, 0x02, 0xad, 0x03, 0xd0, 0x01, 0xd0, 0x02, 0xd0, 0x03, 0x7e, 0x00, 0x7f, 0x03, 0x12} },
+{ 0x91a1,	16,	{0x9b, 0x49, 0x90, 0x01, 0xbe, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x9b, 0xb8, 0xfc, 0xd3, 0xe5, 0xf0} },
+{ 0x91b1,	16,	{0x94, 0x87, 0xec, 0x94, 0x02, 0x40, 0x0a, 0x90, 0x01, 0xbe, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0xca} },
+{ 0x91c1,	16,	{0xf0, 0xc2, 0xaf, 0x90, 0x01, 0xc9, 0xe0, 0x14, 0xf0, 0xd2, 0xaf, 0x7f, 0x01, 0x22, 0x7f, 0x00} },
+{ 0x91d1,	1,	{0x22} },
+{ 0x91d2,	16,	{0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x5e, 0x7e, 0x7b, 0x7f, 0x80, 0x75, 0x50, 0x7b, 0x75, 0x51} },
+{ 0x91e2,	16,	{0x80, 0xe5, 0x51, 0x24, 0x01, 0xff, 0xe4, 0x35, 0x50, 0xa9, 0x07, 0x7b, 0x01, 0x8b, 0x52, 0xf5} },
+{ 0x91f2,	16,	{0x53, 0x89, 0x54, 0xfe, 0x12, 0x91, 0x71, 0xef, 0x60, 0x3b, 0xab, 0x52, 0xaa, 0x53, 0xa9, 0x54} },
+{ 0x9202,	16,	{0x12, 0x9b, 0x72, 0x14, 0xff, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0xb4, 0x02, 0x16, 0xc2, 0xaf} },
+{ 0x9212,	16,	{0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x44} },
+{ 0x9222,	16,	{0x04, 0xf0, 0xd2, 0xaf, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0x74, 0x0d, 0xf0, 0x90, 0x7f, 0xc3} },
+{ 0x9232,	5,	{0x74, 0x04, 0xf0, 0xd2, 0xaf} },
+{ 0x9237,	1,	{0x22} },
+{ 0x9238,	16,	{0x12, 0x91, 0xd2, 0xe4, 0xf5, 0x4b, 0x74, 0x36, 0x25, 0x4b, 0xf8, 0xe6, 0x54, 0xf0, 0xf5, 0x4c} },
+{ 0x9248,	16,	{0x74, 0xc5, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5, 0x83, 0xe0, 0x65, 0x4c, 0xff, 0xc4} },
+{ 0x9258,	16,	{0x54, 0x0f, 0xf5, 0x4d, 0x60, 0x22, 0x74, 0xc5, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5} },
+{ 0x9268,	16,	{0x83, 0xe5, 0x4c, 0xf0, 0xaf, 0x4b, 0x7d, 0x01, 0xe5, 0x4c, 0x45, 0x4d, 0xfb, 0x12, 0x91, 0x00} },
+{ 0x9278,	16,	{0xef, 0x70, 0x05, 0x12, 0x91, 0xd2, 0x80, 0xec, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40} },
+{ 0x9288,	16,	{0xb5, 0x12, 0x91, 0xd2, 0xe5, 0x3a, 0x60, 0x48, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8} },
+{ 0x9298,	16,	{0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x55, 0x3a, 0x60, 0x29, 0xe5, 0x4b} },
+{ 0x92a8,	16,	{0x75, 0xf0, 0x08, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x30, 0xe6} },
+{ 0x92b8,	16,	{0x16, 0xaf, 0x4b, 0x7d, 0x04, 0x7b, 0x80, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05, 0x12, 0x91, 0xd2} },
+{ 0x92c8,	16,	{0x80, 0xef, 0xe5, 0x4c, 0xf4, 0x52, 0x3a, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0xbb} },
+{ 0x92d8,	16,	{0x90, 0x03, 0x00, 0xe0, 0x60, 0x03, 0x02, 0x93, 0xa5, 0x74, 0x19, 0xf0, 0x7f, 0x02, 0x12, 0x81} },
+{ 0x92e8,	16,	{0x11, 0x8e, 0x4e, 0x8f, 0x4f, 0xc3, 0xe5, 0x4e, 0x64, 0x80, 0x94, 0x80, 0x40, 0xee, 0x90, 0x01} },
+{ 0x92f8,	16,	{0xbc, 0xe0, 0x65, 0x4f, 0xf0, 0x60, 0x37, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8, 0x07} },
+{ 0x9308,	16,	{0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x90, 0x01, 0xbc, 0xe0, 0x55, 0x4c, 0x60} },
+{ 0x9318,	16,	{0x14, 0xaf, 0x4b, 0x7d, 0x08, 0xe5, 0x4c, 0x55, 0x4f, 0xfb, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05} },
+{ 0x9328,	16,	{0x12, 0x91, 0xd2, 0x80, 0xec, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0xcc, 0x90, 0x01} },
+{ 0x9338,	16,	{0xbc, 0xe5, 0x4f, 0xf0, 0xe4, 0xf5, 0x4b, 0xc2, 0xaf, 0x74, 0x32, 0x25, 0x4b, 0xf8, 0xe6, 0xf5} },
+{ 0x9348,	16,	{0x4c, 0xe4, 0xf6, 0xd2, 0xaf, 0x53, 0x4c, 0x1e, 0xe5, 0x4c, 0x60, 0x11, 0xaf, 0x4b, 0x7d, 0x02} },
+{ 0x9358,	16,	{0xab, 0x4c, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05, 0x12, 0x91, 0xd2, 0x80, 0xef, 0x74, 0x2c, 0x25} },
+{ 0x9368,	16,	{0x4b, 0xf8, 0xe6, 0xf5, 0x4c, 0x74, 0xfc, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83} },
+{ 0x9378,	16,	{0xe0, 0x65, 0x4c, 0x60, 0x11, 0xaf, 0x4b, 0x7d, 0x04, 0xab, 0x4c, 0x12, 0x91, 0x00, 0xef, 0x70} },
+{ 0x9388,	16,	{0x05, 0x12, 0x91, 0xd2, 0x80, 0xef, 0x74, 0xfc, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5} },
+{ 0x9398,	16,	{0x83, 0xe5, 0x4c, 0xf0, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0x9a, 0x12, 0x91, 0xd2} },
+{ 0x93a8,	1,	{0x22} },
+{ 0x93a9,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x64, 0x02, 0x93, 0xf0} },
+{ 0x93b5,	16,	{0x02, 0x05, 0xa3, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x93c5,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x93d5,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x93e5,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x94, 0x35, 0xe4, 0x7e} },
+{ 0x93f5,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x9405,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x9415,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x9425,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x9435,	16,	{0x60, 0x24, 0x02, 0x8a, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x81, 0x82, 0x84, 0x88} },
+{ 0x9445,	16,	{0x90, 0xa0, 0xc0, 0xc1, 0xc2, 0xc4, 0xc8, 0xd0, 0xe0, 0xe1, 0xe2, 0xe4, 0xe8, 0xf0, 0xf1, 0xf2} },
+{ 0x9455,	8,	{0xf4, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xfe, 0xff} },
+{ 0x945d,	1,	{0x00} },
+{ 0x945e,	11,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x18} },
+{ 0x9469,	16,	{0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f, 0xfe, 0x30, 0xe0, 0x05, 0x90, 0x20, 0x02, 0xe0, 0xff, 0xee} },
+{ 0x9479,	16,	{0x30, 0xe1, 0x05, 0x90, 0x20, 0x0a, 0xe0, 0xff, 0xee, 0x30, 0xe2, 0x05, 0x90, 0x20, 0x12, 0xe0} },
+{ 0x9489,	16,	{0xff, 0xee, 0x30, 0xe3, 0x05, 0x90, 0x20, 0x1a, 0xe0, 0xff, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x1e} },
+{ 0x9499,	10,	{0x04, 0xe4, 0xf0, 0x80, 0x05, 0x90, 0x01, 0xc4, 0xee, 0xf0} },
+{ 0x94a3,	9,	{0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x94ac,	2,	{0xa9, 0x03} },
+{ 0x94ae,	16,	{0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xab, 0x82, 0xfa, 0xe5} },
+{ 0x94be,	16,	{0x57, 0x45, 0x58, 0xf5, 0x59, 0xe9, 0x60, 0x14, 0x8a, 0x83, 0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82} },
+{ 0x94ce,	16,	{0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x4d, 0xf0, 0xe4, 0xfe, 0x80, 0x13, 0xeb, 0x24, 0x04, 0xf5} },
+{ 0x94de,	16,	{0x82, 0xe4, 0x3a, 0xf5, 0x83, 0xe0, 0xff, 0xed, 0xf4, 0xfc, 0xef, 0x5c, 0xf0, 0xae, 0x59, 0xeb} },
+{ 0x94ee,	16,	{0x24, 0x06, 0xf5, 0x82, 0xe4, 0x3a, 0xf5, 0x83, 0xe0, 0x55, 0x59, 0xfc, 0xb5, 0x06, 0x03, 0xaf} },
+{ 0x94fe,	16,	{0x05, 0x22, 0xe5, 0x57, 0x5c, 0xfe, 0xe5, 0x58, 0x5c, 0xfd, 0xe9, 0x60, 0x16, 0xee, 0x70, 0x04} },
+{ 0x950e,	16,	{0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0xae, 0x07, 0xed, 0x70, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f} },
+{ 0x951e,	16,	{0x00, 0xad, 0x07, 0xee, 0x60, 0x03, 0xaf, 0x57, 0x22, 0xed, 0x60, 0x03, 0xaf, 0x58, 0x22, 0x7f} },
+{ 0x952e,	1,	{0x00} },
+{ 0x952f,	1,	{0x22} },
+{ 0x9530,	16,	{0x75, 0x50, 0x02, 0x75, 0x51, 0xb0, 0x90, 0x03, 0x35, 0x74, 0x0f, 0xf0, 0x85, 0x51, 0x82, 0x85} },
+{ 0x9540,	16,	{0x50, 0x83, 0xe0, 0xff, 0x90, 0x03, 0x36, 0xf0, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xa3, 0xe0} },
+{ 0x9550,	16,	{0x90, 0x03, 0x37, 0xf0, 0xa3, 0x74, 0xff, 0xf0, 0x75, 0x52, 0x03, 0x75, 0x53, 0x39, 0xef, 0x14} },
+{ 0x9560,	16,	{0xb4, 0x0b, 0x00, 0x40, 0x03, 0x02, 0x99, 0x98, 0x90, 0x95, 0x6f, 0xf8, 0x28, 0x28, 0x73, 0x02} },
+{ 0x9570,	16,	{0x95, 0x90, 0x02, 0x96, 0x2f, 0x02, 0x97, 0x34, 0x02, 0x97, 0x54, 0x02, 0x97, 0x54, 0x02, 0x97} },
+{ 0x9580,	16,	{0xef, 0x02, 0x98, 0x2a, 0x02, 0x98, 0x4f, 0x02, 0x99, 0x0d, 0x02, 0x99, 0x39, 0x02, 0x99, 0x65} },
+{ 0x9590,	16,	{0xe4, 0xf5, 0x4b, 0xe5, 0x4b, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20} },
+{ 0x95a0,	16,	{0xaf, 0x82, 0xf5, 0x4e, 0x8f, 0x4f, 0xe4, 0xff, 0xe4, 0xfe, 0xef, 0x60, 0x10, 0x74, 0x8a, 0x2e} },
+{ 0x95b0,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0xf4, 0xf5, 0x4c, 0x80, 0x0d, 0x74, 0x8a, 0x2e} },
+{ 0x95c0,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0xf5, 0x4c, 0xe5, 0x4f, 0x24, 0x07, 0xf5, 0x82} },
+{ 0x95d0,	16,	{0xe4, 0x35, 0x4e, 0xf5, 0x83, 0xe5, 0x4c, 0xf0, 0xe0, 0xf5, 0x4d, 0x65, 0x4c, 0x60, 0x38, 0xe4} },
+{ 0x95e0,	16,	{0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xfd, 0x05, 0x53, 0xe5, 0x53, 0xaa, 0x52, 0x70, 0x02} },
+{ 0x95f0,	16,	{0x05, 0x52, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xed, 0xf0, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52, 0x70} },
+{ 0x9600,	16,	{0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe5, 0x4c, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} },
+{ 0x9610,	16,	{0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0x0e, 0xbe, 0x24, 0x8f, 0x0f, 0xef, 0x64, 0x02, 0x70} },
+{ 0x9620,	16,	{0x87, 0x05, 0x4b, 0xe5, 0x4b, 0x64, 0x04, 0x60, 0x03, 0x02, 0x95, 0x93, 0x02, 0x99, 0x9e, 0xe4} },
+{ 0x9630,	16,	{0xf5, 0x4b, 0xaf, 0x4b, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0x05, 0x4b, 0xe5, 0x4b, 0xd3, 0x94, 0x03} },
+{ 0x9640,	16,	{0x40, 0xf0, 0x90, 0x00, 0x04, 0x74, 0x94, 0xf0, 0xa3, 0x74, 0x5e, 0xf0, 0xe4, 0xf5, 0x4d, 0x7e} },
+{ 0x9650,	16,	{0x20, 0x7f, 0x00, 0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8} },
+{ 0x9660,	16,	{0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x90, 0x01, 0xc4, 0xf0, 0x90, 0x01} },
+{ 0x9670,	16,	{0xc0, 0xe4, 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0x74, 0x02} },
+{ 0x9680,	16,	{0xf0, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x4c, 0x34, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0} },
+{ 0x9690,	16,	{0x70, 0xef, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xff, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52} },
+{ 0x96a0,	16,	{0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} },
+{ 0x96b0,	16,	{0x83, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x01, 0xc4, 0xf0, 0x75, 0x4d, 0xff, 0x90, 0x01, 0xc4, 0xe0} },
+{ 0x96c0,	16,	{0xff, 0x60, 0x37, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xfe, 0x05, 0x53, 0xe5, 0x53} },
+{ 0x96d0,	16,	{0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xee, 0xf0, 0x05, 0x53, 0xe5} },
+{ 0x96e0,	16,	{0x53, 0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x85, 0x53} },
+{ 0x96f0,	16,	{0x82, 0x85, 0x52, 0x83, 0xe5, 0x4c, 0xf0, 0x75, 0x4d, 0xff, 0xe5, 0x4d, 0x70, 0x16, 0x74, 0x08} },
+{ 0x9700,	16,	{0x25, 0x4f, 0xf5, 0x4f, 0xe4, 0x35, 0x4e, 0xf5, 0x4e, 0x05, 0x4b, 0xe5, 0x4b, 0x64, 0x04, 0x60} },
+{ 0x9710,	16,	{0x03, 0x02, 0x96, 0x5b, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x05, 0x4b} },
+{ 0x9720,	16,	{0xe5, 0x4b, 0xd3, 0x94, 0x03, 0x40, 0xf0, 0x90, 0x00, 0x04, 0x74, 0x13, 0xf0, 0xa3, 0x74, 0x12} },
+{ 0x9730,	16,	{0xf0, 0x02, 0x99, 0x9e, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xa3, 0xe0, 0x14, 0xff, 0x74, 0x01} },
+{ 0x9740,	16,	{0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x90, 0x02, 0xf7, 0xf0, 0x90, 0x01, 0xc4} },
+{ 0x9750,	16,	{0xf0, 0x02, 0x99, 0x9e, 0x90, 0x01, 0xc0, 0x74, 0x03, 0xf0, 0xa3, 0x74, 0xe8, 0xf0, 0xe4, 0xf5} },
+{ 0x9760,	16,	{0x4d, 0x90, 0x02, 0xf7, 0xe0, 0xff, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x07, 0x19, 0x90, 0x01, 0xc0} },
+{ 0x9770,	16,	{0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70, 0xea, 0x90, 0x03, 0x38, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} },
+{ 0x9780,	16,	{0x83, 0x74, 0xff, 0xf0, 0xf5, 0x4d, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x99, 0x9e, 0x90, 0x01, 0xc0} },
+{ 0x9790,	16,	{0xf0, 0xa3, 0x74, 0x96, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70, 0xf6, 0x7f} },
+{ 0x97a0,	16,	{0x02, 0x12, 0x81, 0x11, 0xc3, 0xee, 0x64, 0x80, 0x94, 0x80, 0x40, 0xf3, 0xef, 0x54, 0x0f, 0xf5} },
+{ 0x97b0,	16,	{0x4d, 0x90, 0x02, 0xf7, 0xe0, 0x55, 0x4d, 0x70, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0x8f} },
+{ 0x97c0,	16,	{0x4c, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xe0, 0xb4, 0x05, 0x0c, 0xe5, 0x4c, 0x70, 0x04, 0x7f} },
+{ 0x97d0,	16,	{0x01, 0x80, 0x02, 0x7f, 0x00, 0x8f, 0x4c, 0xe5, 0x4c, 0x70, 0x03, 0x02, 0x99, 0x9e, 0xe4, 0x90} },
+{ 0x97e0,	16,	{0x03, 0x38, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52, 0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0xe4} },
+{ 0x97f0,	16,	{0xff, 0xfd, 0x12, 0x82, 0x50, 0x7e, 0x20, 0x7f, 0x00, 0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0x85} },
+{ 0x9800,	16,	{0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x44, 0x80, 0xf0, 0x85, 0x4f, 0x82, 0x85} },
+{ 0x9810,	16,	{0x4e, 0x83, 0x74, 0x01, 0xf0, 0xa3, 0xe4, 0xf0, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0xa3} },
+{ 0x9820,	16,	{0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0xd2, 0x04, 0x02, 0x99, 0x9e, 0xc2, 0x04, 0x7e, 0x20, 0x7f, 0x00} },
+{ 0x9830,	16,	{0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0xe5, 0x4f, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x4e, 0xf5} },
+{ 0x9840,	16,	{0x83, 0xe0, 0x30, 0xe6, 0xf1, 0xe4, 0xff, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x02, 0x99, 0x9e, 0xe4} },
+{ 0x9850,	16,	{0xf5, 0x4d, 0xf5, 0x4b, 0xaf, 0x4b, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0xe5, 0x4b, 0x75, 0xf0, 0x08} },
+{ 0x9860,	16,	{0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x4e, 0x8f, 0x4f, 0xf5, 0x83} },
+{ 0x9870,	16,	{0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xaf} },
+{ 0x9880,	16,	{0x4b, 0x7d, 0x01, 0x7b, 0x01, 0x75, 0x57, 0x80, 0x75, 0x58, 0x40, 0x12, 0x94, 0xac, 0x8f, 0x4d} },
+{ 0x9890,	16,	{0xe5, 0x4d, 0x70, 0x11, 0xaf, 0x4b, 0x7d, 0x02, 0x7b, 0x01, 0x75, 0x57, 0x10, 0x75, 0x58, 0x20} },
+{ 0x98a0,	16,	{0x12, 0x94, 0xac, 0x8f, 0x4d, 0xe5, 0x4d, 0x70, 0x10, 0xaf, 0x4b, 0x7d, 0x01, 0xfb, 0x75, 0x57} },
+{ 0x98b0,	16,	{0x80, 0x75, 0x58, 0x40, 0x12, 0x94, 0xac, 0x8f, 0x4d, 0xe5, 0x4d, 0x70, 0x10, 0xaf, 0x4b, 0x7d} },
+{ 0x98c0,	16,	{0x02, 0xfb, 0x75, 0x57, 0x10, 0x75, 0x58, 0x20, 0x12, 0x94, 0xac, 0x8f, 0x4d, 0xaf, 0x4b, 0x7d} },
+{ 0x98d0,	16,	{0x01, 0x12, 0x82, 0x50, 0xe5, 0x4d, 0x60, 0x26, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04} },
+{ 0x98e0,	16,	{0xff, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83} },
+{ 0x98f0,	16,	{0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52, 0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0x05, 0x4b} },
+{ 0x9900,	16,	{0xe5, 0x4b, 0xd3, 0x94, 0x03, 0x50, 0x03, 0x02, 0x98, 0x54, 0x02, 0x99, 0x9e, 0xe4, 0x90, 0x03} },
+{ 0x9910,	16,	{0x59, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74} },
+{ 0x9920,	16,	{0x1b, 0xf0, 0xa3, 0x74, 0x29, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x80, 0x00, 0xef, 0x64, 0x08} },
+{ 0x9930,	16,	{0x60, 0x6c, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0x80, 0x65, 0xe4, 0x90, 0x03, 0x59, 0xf0, 0xa3, 0xf0} },
+{ 0x9940,	16,	{0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0xe5, 0x52, 0xf0, 0xa3, 0xe5} },
+{ 0x9950,	16,	{0x53, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x19, 0xc1, 0xef, 0x64, 0x08, 0x60, 0x40, 0xe4, 0x90} },
+{ 0x9960,	16,	{0x03, 0x38, 0xf0, 0x80, 0x39, 0xe4, 0x90, 0x03, 0x59, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0} },
+{ 0x9970,	16,	{0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xe5, 0x51, 0x24, 0x02, 0x90, 0x03, 0x60, 0xf0, 0xe4, 0x35} },
+{ 0x9980,	16,	{0x50, 0x90, 0x03, 0x5f, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x80, 0x00, 0xef, 0x64, 0x08, 0x60} },
+{ 0x9990,	16,	{0x0d, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0x80, 0x06, 0x90, 0x03, 0x38, 0x74, 0x01, 0xf0, 0x90, 0x01} },
+{ 0x99a0,	16,	{0xc0, 0xe4, 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70} },
+{ 0x99b0,	16,	{0xf6, 0x7e, 0x03, 0x7f, 0x35, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75, 0x5d} },
+{ 0x99c0,	11,	{0x00, 0x7d, 0x24, 0x12, 0x8e, 0x0c, 0xe4, 0x90, 0x02, 0xaf, 0xf0} },
+{ 0x99cb,	1,	{0x22} },
+{ 0x99cc,	16,	{0xe4, 0xff, 0x74, 0xf8, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02} },
+{ 0x99dc,	16,	{0x9a, 0x6f, 0x74, 0x36, 0x2f, 0xf8, 0xe6, 0x20, 0xe5, 0x03, 0x02, 0x9a, 0x6f, 0xef, 0x75, 0xf0} },
+{ 0x99ec,	16,	{0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xad, 0x82, 0xfc, 0xf5, 0x83, 0xe5, 0x82} },
+{ 0x99fc,	16,	{0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0x60, 0x64, 0x60, 0x70, 0x63} },
+{ 0x9a0c,	16,	{0xef, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01} },
+{ 0x9a1c,	16,	{0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xe0, 0x8d, 0x82, 0x8c, 0x83, 0xf0, 0x74, 0xf8} },
+{ 0x9a2c,	16,	{0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0x14, 0xf0, 0x70, 0x36, 0xef, 0x25, 0xe0} },
+{ 0x9a3c,	16,	{0x24, 0xc7, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0xef, 0x25, 0xe0, 0xfe, 0xc3} },
+{ 0x9a4c,	16,	{0x74, 0x0c, 0x9e, 0x75, 0xf0, 0x40, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x7b, 0xad} },
+{ 0x9a5c,	16,	{0x82, 0xfc, 0xef, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xec, 0xf0} },
+{ 0x9a6c,	12,	{0xa3, 0xed, 0xf0, 0x0f, 0xef, 0x64, 0x04, 0x60, 0x03, 0x02, 0x99, 0xce} },
+{ 0x9a78,	1,	{0x22} },
+{ 0x9a79,	16,	{0xe7, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e} },
+{ 0x9a89,	16,	{0x88, 0x82, 0x8c, 0x83, 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08} },
+{ 0x9a99,	16,	{0xdf, 0xfa, 0x80, 0x78, 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83} },
+{ 0x9aa9,	16,	{0xe3, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08} },
+{ 0x9ab9,	16,	{0xdf, 0xfa, 0x80, 0x58, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c} },
+{ 0x9ac9,	16,	{0x80, 0xd2, 0x80, 0xfa, 0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10} },
+{ 0x9ad9,	16,	{0x80, 0xa6, 0x80, 0xea, 0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33} },
+{ 0x9ae9,	16,	{0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83} },
+{ 0x9af9,	16,	{0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80} },
+{ 0x9b09,	16,	{0x0d, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0} },
+{ 0x9b19,	16,	{0xed, 0xfb, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc} },
+{ 0x9b29,	16,	{0xc5, 0x83, 0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde} },
+{ 0x9b39,	16,	{0xe8, 0x80, 0xdb, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc} },
+{ 0x9b49,	16,	{0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xc2, 0xf5, 0x82, 0xeb, 0x24, 0x02, 0xb4} },
+{ 0x9b59,	16,	{0x04, 0x00, 0x50, 0xb8, 0x23, 0x23, 0x45, 0x82, 0xf5, 0x82, 0xef, 0x4e, 0x60, 0xae, 0xef, 0x60} },
+{ 0x9b69,	9,	{0x01, 0x0e, 0xe5, 0x82, 0x23, 0x90, 0x9a, 0xc9, 0x73} },
+{ 0x9b72,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x9b82,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x9b8b,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x9b9b,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x9bab,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x9bb8,	16,	{0xc5, 0xf0, 0xf8, 0xa3, 0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02} },
+{ 0x9bc8,	6,	{0x15, 0x83, 0xe0, 0x38, 0xf0, 0x22} },
+{ 0x9bce,	16,	{0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83} },
+{ 0x9bde,	6,	{0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22} },
+{ 0x9be4,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x9bf4,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x9c04,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x9c14,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x9c1c,	16,	{0x75, 0xf0, 0x08, 0x75, 0x82, 0x00, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xcd, 0x33, 0xcd, 0xcc} },
+{ 0x9c2c,	16,	{0x33, 0xcc, 0xc5, 0x82, 0x33, 0xc5, 0x82, 0x9b, 0xed, 0x9a, 0xec, 0x99, 0xe5, 0x82, 0x98, 0x40} },
+{ 0x9c3c,	16,	{0x0c, 0xf5, 0x82, 0xee, 0x9b, 0xfe, 0xed, 0x9a, 0xfd, 0xec, 0x99, 0xfc, 0x0f, 0xd5, 0xf0, 0xd6} },
+{ 0x9c4c,	16,	{0xe4, 0xce, 0xfb, 0xe4, 0xcd, 0xfa, 0xe4, 0xcc, 0xf9, 0xa8, 0x82, 0x22, 0xb8, 0x00, 0xc1, 0xb9} },
+{ 0x9c5c,	16,	{0x00, 0x59, 0xba, 0x00, 0x2d, 0xec, 0x8b, 0xf0, 0x84, 0xcf, 0xce, 0xcd, 0xfc, 0xe5, 0xf0, 0xcb} },
+{ 0x9c6c,	16,	{0xf9, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc, 0xeb} },
+{ 0x9c7c,	16,	{0x33, 0xfb, 0x10, 0xd7, 0x03, 0x99, 0x40, 0x04, 0xeb, 0x99, 0xfb, 0x0f, 0xd8, 0xe5, 0xe4, 0xf9} },
+{ 0x9c8c,	16,	{0xfa, 0x22, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc} },
+{ 0x9c9c,	16,	{0xc9, 0x33, 0xc9, 0x10, 0xd7, 0x05, 0x9b, 0xe9, 0x9a, 0x40, 0x07, 0xec, 0x9b, 0xfc, 0xe9, 0x9a} },
+{ 0x9cac,	16,	{0xf9, 0x0f, 0xd8, 0xe0, 0xe4, 0xc9, 0xfa, 0xe4, 0xcc, 0xfb, 0x22, 0x75, 0xf0, 0x10, 0xef, 0x2f} },
+{ 0x9cbc,	16,	{0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xcc, 0x33, 0xcc, 0xc8, 0x33, 0xc8, 0x10, 0xd7, 0x07} },
+{ 0x9ccc,	16,	{0x9b, 0xec, 0x9a, 0xe8, 0x99, 0x40, 0x0a, 0xed, 0x9b, 0xfd, 0xec, 0x9a, 0xfc, 0xe8, 0x99, 0xf8} },
+{ 0x9cdc,	14,	{0x0f, 0xd5, 0xf0, 0xda, 0xe4, 0xcd, 0xfb, 0xe4, 0xcc, 0xfa, 0xe4, 0xc8, 0xf9, 0x22} },
+{ 0x9cea,	16,	{0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} },
+{ 0x9cfa,	1,	{0x22} },
+{ 0x9cfb,	16,	{0xe8, 0x60, 0x0f, 0xec, 0xc3, 0x13, 0xfc, 0xed, 0x13, 0xfd, 0xee, 0x13, 0xfe, 0xef, 0x13, 0xff} },
+{ 0x9d0b,	3,	{0xd8, 0xf1, 0x22} },
+{ 0x9d0e,	16,	{0x08, 0x08, 0x08, 0xe6, 0xcf, 0x2f, 0xf6, 0x18, 0xe6, 0xce, 0x3e, 0xf6, 0x18, 0xe6, 0xcd, 0x3d} },
+{ 0x9d1e,	7,	{0xf6, 0x18, 0xe6, 0xcc, 0x3c, 0xf6, 0x22} },
+{ 0x9d25,	12,	{0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x22} },
+{ 0x9d31,	16,	{0xa8, 0x82, 0x85, 0x83, 0xf0, 0xd0, 0x83, 0xd0, 0x82, 0x12, 0x9d, 0x48, 0x12, 0x9d, 0x48, 0x12} },
+{ 0x9d41,	16,	{0x9d, 0x48, 0x12, 0x9d, 0x48, 0xe4, 0x73, 0xe4, 0x93, 0xa3, 0xc5, 0x83, 0xc5, 0xf0, 0xc5, 0x83} },
+{ 0x9d51,	16,	{0xc8, 0xc5, 0x82, 0xc8, 0xf0, 0xa3, 0xc5, 0x83, 0xc5, 0xf0, 0xc5, 0x83, 0xc8, 0xc5, 0x82, 0xc8} },
+{ 0x9d61,	1,	{0x22} },
+{ 0xffff,	0,	{0x00} }
+};
+
+#ifdef DEBUG
+static const struct whiteheat_hex_record whiteheat_loader[] = {
+{ 0x0000,	3,	{0x02, 0x09, 0x8d} },
+{ 0x0033,	3,	{0x02, 0x0e, 0x70} },
+{ 0x0043,	3,	{0x02, 0x0b, 0x00} },
+{ 0x004b,	3,	{0x02, 0x05, 0xb3} },
+{ 0x0100,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x54, 0x10, 0xff, 0xc4, 0x54, 0x0f, 0x44, 0x50, 0xf5, 0x0f, 0x13, 0xe4} },
+{ 0x0110,	16,	{0x33, 0xf5, 0x11, 0x90, 0x7f, 0xe9, 0xe0, 0x24, 0x5e, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x03} },
+{ 0x0120,	16,	{0x7c, 0x90, 0x01, 0x28, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x01, 0xbc, 0x02, 0x01, 0xbc, 0x02, 0x01} },
+{ 0x0130,	16,	{0x91, 0x02, 0x01, 0x3d, 0x02, 0x01, 0x53, 0x02, 0x01, 0x6f, 0x02, 0x01, 0x9a, 0x90, 0x7f, 0x00} },
+{ 0x0140,	16,	{0xe5, 0x11, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0150,	16,	{0x02, 0x03, 0x7c, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x90, 0x7f, 0x00, 0xf0, 0x90} },
+{ 0x0160,	16,	{0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03, 0x7c, 0x12} },
+{ 0x0170,	16,	{0x0a, 0x89, 0x50, 0x07, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0x80, 0x06, 0x90, 0x7f, 0x00, 0x74, 0x0f} },
+{ 0x0180,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03} },
+{ 0x0190,	16,	{0x7c, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0f, 0x02, 0x03, 0x7c, 0x90, 0x7f, 0x00, 0x74, 0x07, 0xf0} },
+{ 0x01a0,	16,	{0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xe8, 0x7e} },
+{ 0x01b0,	16,	{0x03, 0x12, 0x0d, 0xd5, 0xd2, 0x06, 0x12, 0x0d, 0x0d, 0x02, 0x03, 0x7c, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x01c0,	16,	{0x75, 0x29, 0x00, 0xf5, 0x2a, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x29, 0x90, 0x7f, 0xee, 0xe0} },
+{ 0x01d0,	16,	{0x75, 0x2b, 0x00, 0xf5, 0x2c, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x2b, 0x90, 0x7f, 0xe8, 0xe0} },
+{ 0x01e0,	16,	{0x64, 0xc0, 0x60, 0x03, 0x02, 0x02, 0xc9, 0xe5, 0x2c, 0x45, 0x2b, 0x70, 0x03, 0x02, 0x03, 0x7c} },
+{ 0x01f0,	16,	{0xc3, 0xe5, 0x2c, 0x94, 0x40, 0xe5, 0x2b, 0x94, 0x00, 0x50, 0x08, 0x85, 0x2b, 0x2d, 0x85, 0x2c} },
+{ 0x0200,	16,	{0x2e, 0x80, 0x06, 0x75, 0x2d, 0x00, 0x75, 0x2e, 0x40, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70} },
+{ 0x0210,	16,	{0x34, 0xf5, 0x31, 0xf5, 0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5, 0x31, 0x95, 0x2d, 0x50, 0x5c} },
+{ 0x0220,	16,	{0xe5, 0x2a, 0x25, 0x32, 0xf5, 0x82, 0xe5, 0x31, 0x35, 0x29, 0xf5, 0x83, 0xe0, 0xff, 0x74, 0x00} },
+{ 0x0230,	16,	{0x25, 0x32, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70} },
+{ 0x0240,	16,	{0x02, 0x05, 0x31, 0x80, 0xd0, 0xe4, 0xf5, 0x31, 0xf5, 0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5} },
+{ 0x0250,	16,	{0x31, 0x95, 0x2d, 0x50, 0x18, 0x74, 0x00, 0x25, 0x32, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83} },
+{ 0x0260,	16,	{0x74, 0xcd, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70, 0x02, 0x05, 0x31, 0x80, 0xdd, 0xaf, 0x2a, 0xae} },
+{ 0x0270,	16,	{0x29, 0xad, 0x2e, 0x7a, 0x7f, 0x79, 0x00, 0x7b, 0x00, 0x12, 0x0b, 0xf4, 0x90, 0x7f, 0xb5, 0xe5} },
+{ 0x0280,	16,	{0x2e, 0xf0, 0xe5, 0x2e, 0x25, 0x2a, 0xf5, 0x2a, 0xe5, 0x2d, 0x35, 0x29, 0xf5, 0x29, 0xc3, 0xe5} },
+{ 0x0290,	16,	{0x2c, 0x95, 0x2e, 0xf5, 0x2c, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0x90, 0x7f, 0x92, 0xe0, 0xff} },
+{ 0x02a0,	16,	{0xc4, 0x54, 0x0f, 0x75, 0x2f, 0x00, 0xf5, 0x30, 0xd3, 0x94, 0x00, 0xe5, 0x2f, 0x94, 0x00, 0x50} },
+{ 0x02b0,	16,	{0x0c, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe1, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xb4} },
+{ 0x02c0,	16,	{0xe0, 0x20, 0xe2, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xe8, 0xe0, 0x64, 0x40, 0x60} },
+{ 0x02d0,	16,	{0x03, 0x02, 0x03, 0x7c, 0xe5, 0x2c, 0x45, 0x2b, 0x70, 0x03, 0x02, 0x03, 0x7c, 0xe4, 0x90, 0x7f} },
+{ 0x02e0,	16,	{0xc5, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x75, 0x2f, 0x00, 0xf5, 0x30, 0xd3} },
+{ 0x02f0,	16,	{0x94, 0x00, 0xe5, 0x2f, 0x94, 0x00, 0x50, 0x09, 0x90, 0x7f, 0xc4, 0xe0, 0x30, 0xe1, 0x09, 0x80} },
+{ 0x0300,	16,	{0xf7, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe3, 0xf9, 0x90, 0x7f, 0xc5, 0xe0, 0x75, 0x2d, 0x00, 0xf5} },
+{ 0x0310,	16,	{0x2e, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70, 0x38, 0x90, 0x20, 0x6b, 0xf0, 0xf5, 0x31, 0xf5} },
+{ 0x0320,	16,	{0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5, 0x31, 0x95, 0x2d, 0x50, 0x34, 0x74, 0xc0, 0x25, 0x32} },
+{ 0x0330,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x2a, 0x25, 0x32, 0xf5, 0x82, 0xe5} },
+{ 0x0340,	16,	{0x31, 0x35, 0x29, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70, 0x02, 0x05, 0x31, 0x80} },
+{ 0x0350,	16,	{0xd0, 0xaf, 0x2a, 0xae, 0x29, 0xad, 0x2e, 0x7a, 0x7e, 0x79, 0xc0, 0x7b, 0xc0, 0x12, 0x0c, 0x80} },
+{ 0x0360,	16,	{0xe5, 0x2e, 0x25, 0x2a, 0xf5, 0x2a, 0xe5, 0x2d, 0x35, 0x29, 0xf5, 0x29, 0xc3, 0xe5, 0x2c, 0x95} },
+{ 0x0370,	13,	{0x2e, 0xf5, 0x2c, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0x02, 0x02, 0xd4, 0xc3} },
+{ 0x037d,	1,	{0x22} },
+{ 0x037e,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x56, 0x14, 0x70, 0x03, 0x02, 0x04, 0xd2, 0x24} },
+{ 0x038e,	16,	{0xfe, 0x70, 0x03, 0x02, 0x05, 0x46, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x50, 0x14, 0x70, 0x03} },
+{ 0x039e,	16,	{0x02, 0x04, 0x4a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3e, 0x14, 0x70, 0x03, 0x02, 0x04, 0x44, 0x24} },
+{ 0x03ae,	16,	{0x05, 0x60, 0x03, 0x02, 0x05, 0x9a, 0x12, 0x0e, 0x7b, 0x40, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f} },
+{ 0x03be,	16,	{0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02, 0x70, 0x69, 0x74, 0x11, 0x90} },
+{ 0x03ce,	16,	{0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x03de,	16,	{0xff, 0x12, 0x0b, 0x58, 0x8b, 0x26, 0x8a, 0x27, 0x89, 0x28, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02} },
+{ 0x03ee,	16,	{0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90} },
+{ 0x03fe,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0c} },
+{ 0x040e,	16,	{0x3f, 0x8b, 0x26, 0x8a, 0x27, 0x89, 0x28, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f} },
+{ 0x041e,	16,	{0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x042e,	16,	{0x44, 0x01, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xab} },
+{ 0x043e,	16,	{0x12, 0x0e, 0x52, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x60, 0x02, 0x05, 0xab, 0x12, 0x0a, 0xf7, 0x02} },
+{ 0x044e,	16,	{0x05, 0xab, 0x12, 0x08, 0xf1, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x7d, 0x40, 0x03, 0x02, 0x05, 0xab} },
+{ 0x045e,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} },
+{ 0x046e,	16,	{0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x02, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} },
+{ 0x047e,	16,	{0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0xe4, 0x90, 0x7f, 0x00} },
+{ 0x048e,	16,	{0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x049e,	16,	{0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} },
+{ 0x04ae,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} },
+{ 0x04be,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x04ce,	16,	{0xf0, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x7f, 0x40, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xe8, 0xe0} },
+{ 0x04de,	16,	{0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0, 0xb4} },
+{ 0x04ee,	16,	{0x01, 0x05, 0xc2, 0x00, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05} },
+{ 0x04fe,	16,	{0xab, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4} },
+{ 0x050e,	16,	{0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f} },
+{ 0x051e,	16,	{0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f} },
+{ 0x052e,	16,	{0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x6e, 0x90} },
+{ 0x053e,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x65, 0x12, 0x0e, 0x81, 0x50, 0x60, 0x90, 0x7f, 0xe8} },
+{ 0x054e,	16,	{0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x54, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04} },
+{ 0x055e,	16,	{0xd2, 0x00, 0x80, 0x49, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x40, 0x90, 0x7f, 0xea} },
+{ 0x056e,	16,	{0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0} },
+{ 0x057e,	16,	{0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01} },
+{ 0x058e,	16,	{0xf0, 0x80, 0x1a, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x11, 0xe4, 0x90, 0x20, 0x6a} },
+{ 0x059e,	16,	{0xf0, 0x12, 0x01, 0x00, 0x50, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4} },
+{ 0x05ae,	4,	{0xe0, 0x44, 0x02, 0xf0} },
+{ 0x05b2,	1,	{0x22} },
+{ 0x05b3,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x05c3,	16,	{0xd0, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0x7f, 0xa5} },
+{ 0x05d3,	16,	{0xe0, 0x30, 0xe2, 0x06, 0x75, 0x0d, 0x06, 0x02, 0x06, 0x7f, 0x90, 0x7f, 0xa5, 0xe0, 0x20, 0xe1} },
+{ 0x05e3,	16,	{0x0c, 0xe5, 0x0d, 0x64, 0x02, 0x60, 0x06, 0x75, 0x0d, 0x07, 0x02, 0x06, 0x7f, 0xaf, 0x0d, 0xef} },
+{ 0x05f3,	16,	{0x24, 0xfe, 0x60, 0x48, 0x14, 0x60, 0x2c, 0x24, 0xfe, 0x60, 0x77, 0x24, 0x04, 0x60, 0x03, 0x02} },
+{ 0x0603,	16,	{0x06, 0x7f, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xaf, 0x0c, 0x05, 0x0c, 0x8f, 0x82, 0x75, 0x83} },
+{ 0x0613,	16,	{0x00, 0x12, 0x07, 0x85, 0x90, 0x7f, 0xa6, 0xf0, 0xe5, 0x0c, 0x65, 0x08, 0x70, 0x5e, 0x75, 0x0d} },
+{ 0x0623,	16,	{0x05, 0x80, 0x59, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e} },
+{ 0x0633,	16,	{0x82, 0x75, 0x83, 0x00, 0x12, 0x07, 0xb2, 0x75, 0x0d, 0x02, 0x80, 0x40, 0xe5, 0x08, 0x24, 0xfe} },
+{ 0x0643,	16,	{0xb5, 0x0c, 0x07, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0xe5, 0x08, 0x14, 0xb5, 0x0c, 0x0a} },
+{ 0x0653,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09} },
+{ 0x0663,	16,	{0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e, 0x82, 0x75, 0x83, 0x00, 0x12, 0x07, 0xb2, 0x05, 0x0c} },
+{ 0x0673,	16,	{0x80, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x53, 0x91, 0xdf, 0xd0} },
+{ 0x0683,	16,	{0x07, 0xd0, 0x06, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x86, 0xd0} },
+{ 0x0693,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x069d,	16,	{0xc2, 0x04, 0xd2, 0x05, 0xe4, 0xf5, 0x25, 0xc2, 0x03, 0xc2, 0x00, 0xc2, 0x02, 0xc2, 0x01, 0x12} },
+{ 0x06ad,	16,	{0x0e, 0x74, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9} },
+{ 0x06bd,	16,	{0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0x90} },
+{ 0x06cd,	16,	{0x7f, 0x93, 0x74, 0x30, 0xf0, 0x12, 0x0a, 0x19, 0x75, 0x24, 0x48, 0x75, 0x23, 0x92, 0x75, 0x22} },
+{ 0x06dd,	16,	{0x00, 0x75, 0x21, 0x00, 0xe4, 0xff, 0xfe, 0x7e, 0x05, 0x90, 0x20, 0x68, 0x74, 0x01, 0xf0, 0xa3} },
+{ 0x06ed,	16,	{0xde, 0xfc, 0x7e, 0x00, 0x7f, 0x05, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae} },
+{ 0x06fd,	16,	{0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0x12, 0x0e, 0x68, 0x30, 0x01, 0x0a, 0xe4, 0x90, 0x20, 0x69} },
+{ 0x070d,	16,	{0xf0, 0x12, 0x03, 0x7e, 0xc2, 0x01, 0x30, 0x04, 0x1a, 0x12, 0x0e, 0x77, 0x50, 0x13, 0x12, 0x09} },
+{ 0x071d,	16,	{0x00, 0x30, 0x00, 0x07, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0xf3, 0x12, 0x0d, 0x8b, 0x12, 0x0e} },
+{ 0x072d,	16,	{0x79, 0xc2, 0x03, 0x7f, 0xff, 0x7e, 0xff, 0x7d, 0xff, 0x7c, 0xff, 0x78, 0x21, 0x12, 0x08, 0x1d} },
+{ 0x073d,	16,	{0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xc3, 0x12, 0x08, 0x0c, 0x70, 0x1b, 0x75, 0x24} },
+{ 0x074d,	16,	{0x48, 0x75, 0x23, 0x92, 0xf5, 0x22, 0xf5, 0x21, 0x63, 0x25, 0xff, 0x90, 0x20, 0x68, 0xe5, 0x25} },
+{ 0x075d,	14,	{0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0x12, 0x08, 0xff, 0x80, 0x9b} },
+{ 0x076b,	1,	{0x22} },
+{ 0x076c,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x077c,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0785,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x0795,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x07a5,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x07b2,	16,	{0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0} },
+{ 0x07c2,	16,	{0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8} },
+{ 0x07d2,	2,	{0xf2, 0x22} },
+{ 0x07d4,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x07e4,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x07f4,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x0804,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x080c,	16,	{0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} },
+{ 0x081c,	1,	{0x22} },
+{ 0x081d,	16,	{0x08, 0x08, 0x08, 0xe6, 0x2f, 0xff, 0xf6, 0x18, 0xe6, 0x3e, 0xfe, 0xf6, 0x18, 0xe6, 0x3d, 0xfd} },
+{ 0x082d,	7,	{0xf6, 0x18, 0xe6, 0x3c, 0xfc, 0xf6, 0x22} },
+{ 0x0834,	4,	{0x8c, 0x34, 0x8d, 0x35} },
+{ 0x0838,	16,	{0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0xe4, 0xf5, 0x36, 0xf5, 0x37, 0xc3, 0xe5, 0x37, 0x95} },
+{ 0x0848,	16,	{0x35, 0xe5, 0x36, 0x95, 0x34, 0x50, 0x69, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5} },
+{ 0x0858,	16,	{0x83, 0x74, 0xff, 0xf0, 0xf4, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36} },
+{ 0x0868,	16,	{0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36} },
+{ 0x0878,	16,	{0x3e, 0xf5, 0x83, 0x74, 0xaa, 0xf0, 0x64, 0xaa, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5} },
+{ 0x0888,	16,	{0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0x74, 0x55, 0xf0, 0x64, 0x55, 0x60, 0x02, 0xc3, 0x22, 0xad} },
+{ 0x0898,	16,	{0x37, 0xe5, 0x37, 0x2f, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xed, 0xf0, 0xfc, 0xac, 0x05} },
+{ 0x08a8,	16,	{0xed, 0x6c, 0x60, 0x02, 0xc3, 0x22, 0x05, 0x37, 0xe5, 0x37, 0x70, 0x02, 0x05, 0x36, 0x80, 0x8c} },
+{ 0x08b8,	16,	{0xe4, 0xf5, 0x36, 0xf5, 0x37, 0xc3, 0xe5, 0x37, 0x95, 0x35, 0xe5, 0x36, 0x95, 0x34, 0x50, 0x27} },
+{ 0x08c8,	16,	{0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xe0, 0x65, 0x37, 0x60, 0x02, 0xc3} },
+{ 0x08d8,	16,	{0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x37, 0xe5} },
+{ 0x08e8,	8,	{0x37, 0x70, 0x02, 0x05, 0x36, 0x80, 0xce, 0xd3} },
+{ 0x08f0,	1,	{0x22} },
+{ 0x08f1,	14,	{0x90, 0x7f, 0x00, 0xe5, 0x10, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} },
+{ 0x08ff,	1,	{0x22} },
+{ 0x0900,	9,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} },
+{ 0x097d,	16,	{0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x098d,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x3a, 0x02, 0x09, 0xd4} },
+{ 0x0999,	16,	{0x02, 0x06, 0x9d, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x09a9,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x09b9,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x09c9,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0e, 0x2d, 0xe4, 0x7e} },
+{ 0x09d9,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x09e9,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x09f9,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x0a09,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x0a19,	16,	{0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0x74, 0x89} },
+{ 0x0a29,	16,	{0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0d, 0xd5, 0x90, 0x7f} },
+{ 0x0a39,	16,	{0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x7f, 0x05, 0x7e, 0x00} },
+{ 0x0a49,	16,	{0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02, 0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05} },
+{ 0x0a59,	16,	{0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00} },
+{ 0x0a69,	16,	{0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d} },
+{ 0x0a79,	16,	{0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x22} },
+{ 0x0a89,	16,	{0x75, 0x33, 0x01, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x01, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x0e} },
+{ 0x0a99,	16,	{0x7d, 0x00, 0x7c, 0x01, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12} },
+{ 0x0aa9,	16,	{0x0e, 0x18, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x02, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x80, 0x7d} },
+{ 0x0ab9,	16,	{0x00, 0x7c, 0x80, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0e} },
+{ 0x0ac9,	16,	{0x18, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x03, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x20, 0x7d, 0x40} },
+{ 0x0ad9,	16,	{0x7c, 0x5b, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0e, 0x18} },
+{ 0x0ae9,	13,	{0xe5, 0x33, 0x60, 0x05, 0xe4, 0xff, 0x12, 0x0e, 0x18, 0xe5, 0x33, 0x24, 0xff} },
+{ 0x0af6,	1,	{0x22} },
+{ 0x0af7,	8,	{0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x10, 0xd3, 0x22} },
+{ 0x0aff,	1,	{0x32} },
+{ 0x0b00,	16,	{0x02, 0x0d, 0xa5, 0x00, 0x02, 0x0d, 0xec, 0x00, 0x02, 0x0d, 0x70, 0x00, 0x02, 0x0d, 0xbd, 0x00} },
+{ 0x0b10,	16,	{0x02, 0x0e, 0x02, 0x00, 0x02, 0x0a, 0xff, 0x00, 0x02, 0x0e, 0x83, 0x00, 0x02, 0x0e, 0x84, 0x00} },
+{ 0x0b20,	16,	{0x02, 0x0e, 0x85, 0x00, 0x02, 0x0e, 0x86, 0x00, 0x02, 0x0e, 0x87, 0x00, 0x02, 0x0e, 0x88, 0x00} },
+{ 0x0b30,	16,	{0x02, 0x0e, 0x89, 0x00, 0x02, 0x0e, 0x8a, 0x00, 0x02, 0x0e, 0x8b, 0x00, 0x02, 0x0e, 0x8c, 0x00} },
+{ 0x0b40,	16,	{0x02, 0x0e, 0x8d, 0x00, 0x02, 0x0e, 0x8e, 0x00, 0x02, 0x0e, 0x8f, 0x00, 0x02, 0x0e, 0x90, 0x00} },
+{ 0x0b50,	8,	{0x02, 0x0e, 0x91, 0x00, 0x02, 0x0e, 0x92, 0x00} },
+{ 0x0b58,	16,	{0xe4, 0xfe, 0x75, 0x2b, 0xff, 0x75, 0x2c, 0x11, 0x75, 0x2d, 0x12, 0xab, 0x2b, 0xaa, 0x2c, 0xa9} },
+{ 0x0b68,	16,	{0x2d, 0x90, 0x00, 0x01, 0x12, 0x07, 0x85, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x0b78,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x07, 0xd4, 0x85, 0xf0, 0x29, 0xf5, 0x2a, 0x62, 0x29} },
+{ 0x0b88,	16,	{0xe5, 0x29, 0x62, 0x2a, 0xe5, 0x2a, 0x62, 0x29, 0x29, 0xfd, 0xe5, 0x29, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x0b98,	14,	{0x2b, 0xff, 0xf5, 0x2c, 0x89, 0x2d, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0ba6,	1,	{0x22} },
+{ 0x0ba7,	6,	{0xab, 0x07, 0xaa, 0x06, 0xac, 0x05} },
+{ 0x0bad,	16,	{0xe4, 0xfd, 0xe5, 0x11, 0x60, 0x11, 0xea, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24, 0x10, 0xf5, 0x82} },
+{ 0x0bbd,	16,	{0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xeb, 0xae, 0x05, 0x0d, 0x74, 0x10, 0x2e, 0xf5, 0x82} },
+{ 0x0bcd,	16,	{0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xeb, 0xf0, 0xaf, 0x05, 0x0d, 0x74, 0x10, 0x2f, 0xf5, 0x82, 0xe4} },
+{ 0x0bdd,	16,	{0x34, 0x0f, 0xf5, 0x83, 0xec, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f, 0x7b, 0x10, 0x12, 0x0d, 0x51, 0x7f} },
+{ 0x0bed,	6,	{0x0a, 0x7e, 0x00, 0x12, 0x0d, 0xd5} },
+{ 0x0bf3,	1,	{0x22} },
+{ 0x0bf4,	10,	{0x8e, 0x33, 0x8f, 0x34, 0x8d, 0x35, 0x8a, 0x36, 0x8b, 0x37} },
+{ 0x0bfe,	16,	{0xe4, 0xfd, 0xf5, 0x38, 0xe5, 0x11, 0x60, 0x12, 0xe5, 0x33, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24} },
+{ 0x0c0e,	16,	{0x13, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x34, 0xae, 0x05, 0x0d, 0x74} },
+{ 0x0c1e,	16,	{0x13, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xe5, 0x34, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f} },
+{ 0x0c2e,	16,	{0x7b, 0x13, 0x12, 0x0d, 0x51, 0xaf, 0x0f, 0xad, 0x35, 0xab, 0x37, 0xaa, 0x36, 0x12, 0x0d, 0x32} },
+{ 0x0c3e,	1,	{0x22} },
+{ 0x0c3f,	2,	{0x8f, 0x29} },
+{ 0x0c41,	16,	{0xe4, 0xf5, 0x2a, 0x75, 0x2b, 0xff, 0x75, 0x2c, 0x11, 0x75, 0x2d, 0x32, 0xab, 0x2b, 0xaa, 0x2c} },
+{ 0x0c51,	16,	{0xa9, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x07, 0x85, 0xb4, 0x03, 0x1d, 0xaf, 0x2a, 0x05, 0x2a, 0xef} },
+{ 0x0c61,	16,	{0xb5, 0x29, 0x01, 0x22, 0x12, 0x07, 0x6c, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x0c71,	14,	{0x2b, 0xff, 0xf5, 0x2c, 0x89, 0x2d, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0c7f,	1,	{0x22} },
+{ 0x0c80,	10,	{0x8e, 0x33, 0x8f, 0x34, 0x8d, 0x35, 0x8a, 0x36, 0x8b, 0x37} },
+{ 0x0c8a,	16,	{0xe4, 0xf5, 0x38, 0xe5, 0x38, 0xc3, 0x95, 0x35, 0x50, 0x20, 0x05, 0x34, 0xe5, 0x34, 0xae, 0x33} },
+{ 0x0c9a,	16,	{0x70, 0x02, 0x05, 0x33, 0x14, 0xff, 0xe5, 0x37, 0x25, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x36, 0xf5} },
+{ 0x0caa,	10,	{0x83, 0xe0, 0xfd, 0x12, 0x0b, 0xa7, 0x05, 0x38, 0x80, 0xd9} },
+{ 0x0cb4,	1,	{0x22} },
+{ 0x0cb5,	16,	{0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x25, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} },
+{ 0x0cc5,	16,	{0x44, 0x01, 0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a} },
+{ 0x0cd5,	13,	{0x0a, 0x89, 0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x03, 0xd3, 0x22, 0xc3, 0x22} },
+{ 0x0ce2,	16,	{0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} },
+{ 0x0cf2,	16,	{0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a, 0x0a, 0x89} },
+{ 0x0d02,	11,	{0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x01, 0xd3, 0x22, 0xc3, 0x22} },
+{ 0x0d0d,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x06, 0x04, 0xe0, 0x44} },
+{ 0x0d1d,	16,	{0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x0d2d,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x0d32,	16,	{0x12, 0x0c, 0xb5, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} },
+{ 0x0d42,	15,	{0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} },
+{ 0x0d51,	16,	{0x12, 0x0c, 0xe2, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} },
+{ 0x0d61,	15,	{0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} },
+{ 0x0d70,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0d80,	11,	{0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d8b,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x12, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x14, 0x7e, 0x00, 0x12} },
+{ 0x0d9b,	10,	{0x0d, 0xd5, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} },
+{ 0x0da5,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} },
+{ 0x0db5,	8,	{0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0dbd,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x03, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x0dcd,	8,	{0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0dd5,	16,	{0x8e, 0x39, 0x8f, 0x3a, 0xe5, 0x3a, 0x15, 0x3a, 0xae, 0x39, 0x70, 0x02, 0x15, 0x39, 0x4e, 0x60} },
+{ 0x0de5,	7,	{0x05, 0x12, 0x0e, 0x41, 0x80, 0xee, 0x22} },
+{ 0x0dec,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0} },
+{ 0x0dfc,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e02,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} },
+{ 0x0e12,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0e18,	16,	{0xae, 0x07, 0x7f, 0x21, 0x7d, 0x01, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xab, 0x82} },
+{ 0x0e28,	5,	{0xfa, 0x12, 0x0d, 0x51, 0x22} },
+{ 0x0e2d,	16,	{0x50, 0x0f, 0x00, 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6} },
+{ 0x0e3d,	3,	{0xa1, 0x86, 0x8e} },
+{ 0x0e40,	1,	{0x00} },
+{ 0x0e41,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x0e51,	1,	{0x22} },
+{ 0x0e52,	14,	{0x90, 0x7f, 0x00, 0xe5, 0x0e, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} },
+{ 0x0e60,	8,	{0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0e, 0xd3, 0x22} },
+{ 0x0e68,	8,	{0xe4, 0xf5, 0x0d, 0xd2, 0xe9, 0xd2, 0xaf, 0x22} },
+{ 0x0e70,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x0e74,	3,	{0xd2, 0x00, 0x22} },
+{ 0x0e77,	2,	{0xd3, 0x22} },
+{ 0x0e79,	2,	{0xd3, 0x22} },
+{ 0x0e7b,	2,	{0xd3, 0x22} },
+{ 0x0e7d,	2,	{0xd3, 0x22} },
+{ 0x0e7f,	2,	{0xd3, 0x22} },
+{ 0x0e81,	2,	{0xd3, 0x22} },
+{ 0x0e83,	1,	{0x32} },
+{ 0x0e84,	1,	{0x32} },
+{ 0x0e85,	1,	{0x32} },
+{ 0x0e86,	1,	{0x32} },
+{ 0x0e87,	1,	{0x32} },
+{ 0x0e88,	1,	{0x32} },
+{ 0x0e89,	1,	{0x32} },
+{ 0x0e8a,	1,	{0x32} },
+{ 0x0e8b,	1,	{0x32} },
+{ 0x0e8c,	1,	{0x32} },
+{ 0x0e8d,	1,	{0x32} },
+{ 0x0e8e,	1,	{0x32} },
+{ 0x0e8f,	1,	{0x32} },
+{ 0x0e90,	1,	{0x32} },
+{ 0x0e91,	1,	{0x32} },
+{ 0x0e92,	1,	{0x32} },
+{ 0x1100,	16,	{0x12, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x47, 0x05, 0x10, 0x27, 0x01, 0x00, 0x01, 0x02} },
+{ 0x1110,	16,	{0x00, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x03, 0xa0, 0x00, 0x09, 0x04, 0x00, 0x00, 0x02} },
+{ 0x1120,	16,	{0xff, 0x00, 0x00, 0x04, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1130,	16,	{0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x26, 0x03, 0x41, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x68, 0x00} },
+{ 0x1140,	16,	{0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00, 0x69, 0x00, 0x70, 0x00, 0x73, 0x00} },
+{ 0x1150,	16,	{0x2c, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x28, 0x03, 0x46, 0x00} },
+{ 0x1160,	16,	{0x69, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00} },
+{ 0x1170,	16,	{0x46, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00} },
+{ 0x1180,	16,	{0x6b, 0x00, 0x73, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} },
+{ 0x1190,	16,	{0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} },
+{ 0x11a0,	16,	{0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} },
+{ 0x11b0,	16,	{0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} },
+{ 0x11c0,	16,	{0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} },
+{ 0x11d0,	2,	{0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
+
+#else
+
+static const struct whiteheat_hex_record whiteheat_loader[] = {
+{ 0x0000,	3,	{0x02, 0x09, 0x8d} },
+{ 0x0033,	3,	{0x02, 0x08, 0xfb} },
+{ 0x0043,	3,	{0x02, 0x0b, 0x00} },
+{ 0x004b,	3,	{0x02, 0x05, 0xaa} },
+{ 0x0100,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x54, 0x10, 0xff, 0xc4, 0x54, 0x0f, 0x44, 0x50, 0xf5, 0x0f, 0x13, 0xe4} },
+{ 0x0110,	16,	{0x33, 0xf5, 0x11, 0x90, 0x7f, 0xe9, 0xe0, 0x24, 0x5e, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x03} },
+{ 0x0120,	16,	{0x78, 0x90, 0x01, 0x28, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x01, 0xbc, 0x02, 0x01, 0xbc, 0x02, 0x01} },
+{ 0x0130,	16,	{0x91, 0x02, 0x01, 0x3d, 0x02, 0x01, 0x53, 0x02, 0x01, 0x6f, 0x02, 0x01, 0x9a, 0x90, 0x7f, 0x00} },
+{ 0x0140,	16,	{0xe5, 0x11, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} },
+{ 0x0150,	16,	{0x02, 0x03, 0x78, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x90, 0x7f, 0x00, 0xf0, 0x90} },
+{ 0x0160,	16,	{0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03, 0x78, 0x12} },
+{ 0x0170,	16,	{0x0a, 0x89, 0x50, 0x07, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0x80, 0x06, 0x90, 0x7f, 0x00, 0x74, 0x0f} },
+{ 0x0180,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03} },
+{ 0x0190,	16,	{0x78, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0f, 0x02, 0x03, 0x78, 0x90, 0x7f, 0x00, 0x74, 0x07, 0xf0} },
+{ 0x01a0,	16,	{0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xe8, 0x7e} },
+{ 0x01b0,	16,	{0x03, 0x12, 0x0d, 0x94, 0xd2, 0x06, 0x12, 0x0c, 0xcc, 0x02, 0x03, 0x78, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x01c0,	16,	{0x75, 0x28, 0x00, 0xf5, 0x29, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x28, 0x90, 0x7f, 0xee, 0xe0} },
+{ 0x01d0,	16,	{0x75, 0x2a, 0x00, 0xf5, 0x2b, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x2a, 0x90, 0x7f, 0xe8, 0xe0} },
+{ 0x01e0,	16,	{0x64, 0xc0, 0x60, 0x03, 0x02, 0x02, 0xc9, 0xe5, 0x2b, 0x45, 0x2a, 0x70, 0x03, 0x02, 0x03, 0x78} },
+{ 0x01f0,	16,	{0xc3, 0xe5, 0x2b, 0x94, 0x40, 0xe5, 0x2a, 0x94, 0x00, 0x50, 0x08, 0x85, 0x2a, 0x2c, 0x85, 0x2b} },
+{ 0x0200,	16,	{0x2d, 0x80, 0x06, 0x75, 0x2c, 0x00, 0x75, 0x2d, 0x40, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70} },
+{ 0x0210,	16,	{0x34, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31, 0x95, 0x2d, 0xe5, 0x30, 0x95, 0x2c, 0x50, 0x5c} },
+{ 0x0220,	16,	{0xe5, 0x29, 0x25, 0x31, 0xf5, 0x82, 0xe5, 0x30, 0x35, 0x28, 0xf5, 0x83, 0xe0, 0xff, 0x74, 0x00} },
+{ 0x0230,	16,	{0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70} },
+{ 0x0240,	16,	{0x02, 0x05, 0x30, 0x80, 0xd0, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31, 0x95, 0x2d, 0xe5} },
+{ 0x0250,	16,	{0x30, 0x95, 0x2c, 0x50, 0x18, 0x74, 0x00, 0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83} },
+{ 0x0260,	16,	{0x74, 0xcd, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70, 0x02, 0x05, 0x30, 0x80, 0xdd, 0xaf, 0x29, 0xae} },
+{ 0x0270,	16,	{0x28, 0xad, 0x2d, 0x7a, 0x7f, 0x79, 0x00, 0x7b, 0x00, 0x12, 0x0b, 0xf4, 0x90, 0x7f, 0xb5, 0xe5} },
+{ 0x0280,	16,	{0x2d, 0xf0, 0xe5, 0x2d, 0x25, 0x29, 0xf5, 0x29, 0xe5, 0x2c, 0x35, 0x28, 0xf5, 0x28, 0xc3, 0xe5} },
+{ 0x0290,	16,	{0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0xe5, 0x2a, 0x95, 0x2c, 0xf5, 0x2a, 0x90, 0x7f, 0x92, 0xe0, 0xff} },
+{ 0x02a0,	16,	{0xc4, 0x54, 0x0f, 0x75, 0x2e, 0x00, 0xf5, 0x2f, 0xd3, 0x94, 0x00, 0xe5, 0x2e, 0x94, 0x00, 0x50} },
+{ 0x02b0,	16,	{0x0c, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe1, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xb4} },
+{ 0x02c0,	16,	{0xe0, 0x20, 0xe2, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xe8, 0xe0, 0x64, 0x40, 0x60} },
+{ 0x02d0,	16,	{0x03, 0x02, 0x03, 0x78, 0xe5, 0x2b, 0x45, 0x2a, 0x70, 0x03, 0x02, 0x03, 0x78, 0xe4, 0x90, 0x7f} },
+{ 0x02e0,	16,	{0xc5, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x75, 0x2e, 0x00, 0xf5, 0x2f, 0xd3} },
+{ 0x02f0,	16,	{0x94, 0x00, 0xe5, 0x2e, 0x94, 0x00, 0x50, 0x09, 0x90, 0x7f, 0xc4, 0xe0, 0x30, 0xe1, 0x09, 0x80} },
+{ 0x0300,	16,	{0xf7, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe3, 0xf9, 0x90, 0x7f, 0xc5, 0xe0, 0x75, 0x2c, 0x00, 0xf5} },
+{ 0x0310,	16,	{0x2d, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70, 0x34, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31} },
+{ 0x0320,	16,	{0x95, 0x2d, 0xe5, 0x30, 0x95, 0x2c, 0x50, 0x34, 0x74, 0xc0, 0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0330,	1,	{0x7e} },
+{ 0x0331,	16,	{0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x29, 0x25, 0x31, 0xf5, 0x82, 0xe5, 0x30, 0x35, 0x28, 0xf5, 0x83} },
+{ 0x0341,	16,	{0xef, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70, 0x02, 0x05, 0x30, 0x80, 0xd0, 0xaf, 0x29, 0xae, 0x28} },
+{ 0x0351,	16,	{0xad, 0x2d, 0x7a, 0x7e, 0x79, 0xc0, 0x7b, 0xc0, 0x12, 0x0c, 0x3f, 0xe5, 0x2d, 0x25, 0x29, 0xf5} },
+{ 0x0361,	16,	{0x29, 0xe5, 0x2c, 0x35, 0x28, 0xf5, 0x28, 0xc3, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0xe5, 0x2a} },
+{ 0x0371,	9,	{0x95, 0x2c, 0xf5, 0x2a, 0x02, 0x02, 0xd4, 0xc3, 0x22} },
+{ 0x037a,	16,	{0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x52, 0x14, 0x70, 0x03, 0x02, 0x04, 0xce, 0x24} },
+{ 0x038a,	16,	{0xfe, 0x70, 0x03, 0x02, 0x05, 0x42, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x4c, 0x14, 0x70, 0x03} },
+{ 0x039a,	16,	{0x02, 0x04, 0x46, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x40, 0x24} },
+{ 0x03aa,	16,	{0x05, 0x60, 0x03, 0x02, 0x05, 0x96, 0x12, 0x0e, 0x44, 0x40, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f} },
+{ 0x03ba,	16,	{0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02, 0x70, 0x69, 0x74, 0x11, 0x90} },
+{ 0x03ca,	16,	{0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0} },
+{ 0x03da,	16,	{0xff, 0x12, 0x0b, 0x58, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02} },
+{ 0x03ea,	16,	{0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90} },
+{ 0x03fa,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x08} },
+{ 0x040a,	16,	{0xba, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f} },
+{ 0x041a,	16,	{0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0} },
+{ 0x042a,	16,	{0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2} },
+{ 0x043a,	16,	{0x12, 0x0e, 0x1f, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x2d, 0x02, 0x05, 0xa2, 0x12, 0x0a, 0xf7, 0x02} },
+{ 0x044a,	16,	{0x05, 0xa2, 0x12, 0x0e, 0x11, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x46, 0x40, 0x03, 0x02, 0x05, 0xa2} },
+{ 0x045a,	16,	{0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} },
+{ 0x046a,	16,	{0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x02, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} },
+{ 0x047a,	16,	{0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0xe4, 0x90, 0x7f, 0x00} },
+{ 0x048a,	16,	{0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xec, 0xe0} },
+{ 0x049a,	16,	{0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} },
+{ 0x04aa,	16,	{0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} },
+{ 0x04ba,	16,	{0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x04ca,	16,	{0xf0, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x48, 0x40, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xe8, 0xe0} },
+{ 0x04da,	16,	{0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0, 0xb4} },
+{ 0x04ea,	16,	{0x01, 0x05, 0xc2, 0x00, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05} },
+{ 0x04fa,	16,	{0xa2, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4} },
+{ 0x050a,	16,	{0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f} },
+{ 0x051a,	16,	{0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f} },
+{ 0x052a,	16,	{0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x69, 0x90} },
+{ 0x053a,	16,	{0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x60, 0x12, 0x0e, 0x4a, 0x50, 0x5b, 0x90, 0x7f, 0xe8} },
+{ 0x054a,	16,	{0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4f, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04} },
+{ 0x055a,	16,	{0xd2, 0x00, 0x80, 0x44, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x3b, 0x90, 0x7f, 0xea} },
+{ 0x056a,	16,	{0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0} },
+{ 0x057a,	16,	{0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01} },
+{ 0x058a,	16,	{0xf0, 0x80, 0x15, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x0c, 0x12, 0x01, 0x00, 0x50} },
+{ 0x059a,	16,	{0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x22} },
+{ 0x05aa,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} },
+{ 0x05ba,	16,	{0xd0, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0x7f, 0xa5} },
+{ 0x05ca,	16,	{0xe0, 0x30, 0xe2, 0x06, 0x75, 0x0d, 0x06, 0x02, 0x06, 0x76, 0x90, 0x7f, 0xa5, 0xe0, 0x20, 0xe1} },
+{ 0x05da,	16,	{0x0c, 0xe5, 0x0d, 0x64, 0x02, 0x60, 0x06, 0x75, 0x0d, 0x07, 0x02, 0x06, 0x76, 0xaf, 0x0d, 0xef} },
+{ 0x05ea,	16,	{0x24, 0xfe, 0x60, 0x48, 0x14, 0x60, 0x2c, 0x24, 0xfe, 0x60, 0x77, 0x24, 0x04, 0x60, 0x03, 0x02} },
+{ 0x05fa,	16,	{0x06, 0x76, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xaf, 0x0c, 0x05, 0x0c, 0x8f, 0x82, 0x75, 0x83} },
+{ 0x060a,	16,	{0x00, 0x12, 0x08, 0x22, 0x90, 0x7f, 0xa6, 0xf0, 0xe5, 0x0c, 0x65, 0x08, 0x70, 0x5e, 0x75, 0x0d} },
+{ 0x061a,	16,	{0x05, 0x80, 0x59, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e} },
+{ 0x062a,	16,	{0x82, 0x75, 0x83, 0x00, 0x12, 0x08, 0x4f, 0x75, 0x0d, 0x02, 0x80, 0x40, 0xe5, 0x08, 0x24, 0xfe} },
+{ 0x063a,	16,	{0xb5, 0x0c, 0x07, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0xe5, 0x08, 0x14, 0xb5, 0x0c, 0x0a} },
+{ 0x064a,	16,	{0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09} },
+{ 0x065a,	16,	{0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e, 0x82, 0x75, 0x83, 0x00, 0x12, 0x08, 0x4f, 0x05, 0x0c} },
+{ 0x066a,	16,	{0x80, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x53, 0x91, 0xdf, 0xd0} },
+{ 0x067a,	16,	{0x07, 0xd0, 0x06, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x86, 0xd0} },
+{ 0x068a,	10,	{0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0694,	16,	{0x8c, 0x33, 0x8d, 0x34, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x36} },
+{ 0x06a4,	16,	{0xc3, 0xe5, 0x36, 0x95, 0x34, 0xe5, 0x35, 0x95, 0x33, 0x50, 0x69, 0xef, 0x25, 0x36, 0xf5, 0x82} },
+{ 0x06b4,	16,	{0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0xff, 0xf0, 0xf4, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36} },
+{ 0x06c4,	16,	{0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36} },
+{ 0x06d4,	16,	{0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0xaa, 0xf0, 0x64, 0xaa, 0x60, 0x02, 0xc3, 0x22} },
+{ 0x06e4,	16,	{0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0x55, 0xf0, 0x64, 0x55, 0x60} },
+{ 0x06f4,	16,	{0x02, 0xc3, 0x22, 0xad, 0x36, 0xe5, 0x36, 0x2f, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xed} },
+{ 0x0704,	16,	{0xf0, 0xfc, 0xac, 0x05, 0xed, 0x6c, 0x60, 0x02, 0xc3, 0x22, 0x05, 0x36, 0xe5, 0x36, 0x70, 0x02} },
+{ 0x0714,	16,	{0x05, 0x35, 0x80, 0x8c, 0xe4, 0xf5, 0x35, 0xf5, 0x36, 0xc3, 0xe5, 0x36, 0x95, 0x34, 0xe5, 0x35} },
+{ 0x0724,	16,	{0x95, 0x33, 0x50, 0x27, 0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe0, 0x65} },
+{ 0x0734,	16,	{0x36, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe4} },
+{ 0x0744,	13,	{0xf0, 0x05, 0x36, 0xe5, 0x36, 0x70, 0x02, 0x05, 0x35, 0x80, 0xce, 0xd3, 0x22} },
+{ 0x0751,	16,	{0xc2, 0x04, 0xd2, 0x05, 0xc2, 0x03, 0xc2, 0x00, 0xc2, 0x02, 0xc2, 0x01, 0x12, 0x0e, 0x3d, 0xd2} },
+{ 0x0761,	16,	{0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90, 0x7f} },
+{ 0x0771,	16,	{0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0x90, 0x7f, 0x93, 0x74} },
+{ 0x0781,	16,	{0x30, 0xf0, 0x12, 0x0a, 0x19, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0xe0} },
+{ 0x0791,	16,	{0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0x12, 0x0e, 0x35, 0x20, 0x01, 0x42, 0x75, 0x24, 0x00, 0x75, 0x23} },
+{ 0x07a1,	16,	{0x00, 0x75, 0x22, 0x00, 0x75, 0x21, 0x00, 0x7f, 0x48, 0x7e, 0x92, 0x7d, 0x00, 0x7c, 0x00, 0xab} },
+{ 0x07b1,	16,	{0x24, 0xaa, 0x23, 0xa9, 0x22, 0xa8, 0x21, 0xc3, 0x12, 0x08, 0xa9, 0x50, 0xdb, 0x20, 0x01, 0xd8} },
+{ 0x07c1,	16,	{0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xe5, 0x24, 0x24, 0x01, 0xf5, 0x24, 0xea, 0x35, 0x23, 0xf5} },
+{ 0x07d1,	16,	{0x23, 0xe9, 0x35, 0x22, 0xf5, 0x22, 0xe8, 0x35, 0x21, 0xf5, 0x21, 0x80, 0xca, 0x30, 0x01, 0x05} },
+{ 0x07e1,	16,	{0x12, 0x03, 0x7a, 0xc2, 0x01, 0x30, 0x04, 0x1a, 0x12, 0x0e, 0x40, 0x50, 0x13, 0x12, 0x09, 0x00} },
+{ 0x07f1,	16,	{0x30, 0x00, 0x07, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0xf3, 0x12, 0x0d, 0x4a, 0x12, 0x0e, 0x42} },
+{ 0x0801,	8,	{0xc2, 0x03, 0x12, 0x08, 0xff, 0x80, 0xd6, 0x22} },
+{ 0x0809,	16,	{0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} },
+{ 0x0819,	9,	{0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x0822,	16,	{0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} },
+{ 0x0832,	16,	{0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} },
+{ 0x0842,	13,	{0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} },
+{ 0x084f,	16,	{0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0} },
+{ 0x085f,	16,	{0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8} },
+{ 0x086f,	2,	{0xf2, 0x22} },
+{ 0x0871,	16,	{0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} },
+{ 0x0881,	16,	{0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} },
+{ 0x0891,	16,	{0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} },
+{ 0x08a1,	8,	{0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} },
+{ 0x08a9,	16,	{0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} },
+{ 0x08b9,	1,	{0x22} },
+{ 0x08ba,	2,	{0x8f, 0x28} },
+{ 0x08bc,	16,	{0xe4, 0xf5, 0x29, 0x75, 0x2a, 0xff, 0x75, 0x2b, 0x11, 0x75, 0x2c, 0x32, 0xab, 0x2a, 0xaa, 0x2b} },
+{ 0x08cc,	16,	{0xa9, 0x2c, 0x90, 0x00, 0x01, 0x12, 0x08, 0x22, 0xb4, 0x03, 0x1d, 0xaf, 0x29, 0x05, 0x29, 0xef} },
+{ 0x08dc,	16,	{0xb5, 0x28, 0x01, 0x22, 0x12, 0x08, 0x09, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} },
+{ 0x08ec,	14,	{0x2a, 0xff, 0xf5, 0x2b, 0x89, 0x2c, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x08fa,	1,	{0x22} },
+{ 0x08fb,	4,	{0x53, 0xd8, 0xef, 0x32} },
+{ 0x08ff,	1,	{0x22} },
+{ 0x0900,	9,	{0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} },
+{ 0x097d,	16,	{0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} },
+{ 0x098d,	12,	{0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x39, 0x02, 0x09, 0xd4} },
+{ 0x0999,	16,	{0x02, 0x07, 0x51, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} },
+{ 0x09a9,	16,	{0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} },
+{ 0x09b9,	16,	{0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} },
+{ 0x09c9,	16,	{0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0d, 0xec, 0xe4, 0x7e} },
+{ 0x09d9,	16,	{0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} },
+{ 0x09e9,	16,	{0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} },
+{ 0x09f9,	16,	{0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} },
+{ 0x0a09,	16,	{0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} },
+{ 0x0a19,	16,	{0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0x74, 0x89} },
+{ 0x0a29,	16,	{0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0d, 0x94, 0x90, 0x7f} },
+{ 0x0a39,	16,	{0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x7f, 0x05, 0x7e, 0x00} },
+{ 0x0a49,	16,	{0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02, 0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05} },
+{ 0x0a59,	16,	{0x7e, 0x00, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00} },
+{ 0x0a69,	16,	{0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d} },
+{ 0x0a79,	16,	{0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x22} },
+{ 0x0a89,	16,	{0x75, 0x32, 0x01, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x01, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x0e} },
+{ 0x0a99,	16,	{0x7d, 0x00, 0x7c, 0x01, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12} },
+{ 0x0aa9,	16,	{0x0d, 0xd7, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x02, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x80, 0x7d} },
+{ 0x0ab9,	16,	{0x00, 0x7c, 0x80, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0d} },
+{ 0x0ac9,	16,	{0xd7, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x03, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x20, 0x7d, 0x40} },
+{ 0x0ad9,	16,	{0x7c, 0x5b, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0d, 0xd7} },
+{ 0x0ae9,	14,	{0xe5, 0x32, 0x60, 0x05, 0xe4, 0xff, 0x12, 0x0d, 0xd7, 0xe5, 0x32, 0x24, 0xff, 0x22} },
+{ 0x0af7,	8,	{0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x10, 0xd3, 0x22} },
+{ 0x0aff,	1,	{0x32} },
+{ 0x0b00,	16,	{0x02, 0x0d, 0x64, 0x00, 0x02, 0x0d, 0xab, 0x00, 0x02, 0x0d, 0x2f, 0x00, 0x02, 0x0d, 0x7c, 0x00} },
+{ 0x0b10,	16,	{0x02, 0x0d, 0xc1, 0x00, 0x02, 0x0a, 0xff, 0x00, 0x02, 0x0e, 0x4c, 0x00, 0x02, 0x0e, 0x4d, 0x00} },
+{ 0x0b20,	16,	{0x02, 0x0e, 0x4e, 0x00, 0x02, 0x0e, 0x4f, 0x00, 0x02, 0x0e, 0x50, 0x00, 0x02, 0x0e, 0x51, 0x00} },
+{ 0x0b30,	16,	{0x02, 0x0e, 0x52, 0x00, 0x02, 0x0e, 0x53, 0x00, 0x02, 0x0e, 0x54, 0x00, 0x02, 0x0e, 0x55, 0x00} },
+{ 0x0b40,	16,	{0x02, 0x0e, 0x56, 0x00, 0x02, 0x0e, 0x57, 0x00, 0x02, 0x0e, 0x58, 0x00, 0x02, 0x0e, 0x59, 0x00} },
+{ 0x0b50,	8,	{0x02, 0x0e, 0x5a, 0x00, 0x02, 0x0e, 0x5b, 0x00} },
+{ 0x0b58,	16,	{0xe4, 0xfe, 0x75, 0x2a, 0xff, 0x75, 0x2b, 0x11, 0x75, 0x2c, 0x12, 0xab, 0x2a, 0xaa, 0x2b, 0xa9} },
+{ 0x0b68,	16,	{0x2c, 0x90, 0x00, 0x01, 0x12, 0x08, 0x22, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} },
+{ 0x0b78,	16,	{0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x08, 0x71, 0x85, 0xf0, 0x28, 0xf5, 0x29, 0x62, 0x28} },
+{ 0x0b88,	16,	{0xe5, 0x28, 0x62, 0x29, 0xe5, 0x29, 0x62, 0x28, 0x29, 0xfd, 0xe5, 0x28, 0x3a, 0xa9, 0x05, 0x75} },
+{ 0x0b98,	14,	{0x2a, 0xff, 0xf5, 0x2b, 0x89, 0x2c, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} },
+{ 0x0ba6,	1,	{0x22} },
+{ 0x0ba7,	16,	{0xab, 0x07, 0xaa, 0x06, 0xac, 0x05, 0xe4, 0xfd, 0xe5, 0x11, 0x60, 0x11, 0xea, 0xff, 0xae, 0x05} },
+{ 0x0bb7,	16,	{0x0d, 0xee, 0x24, 0x10, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xeb, 0xae, 0x05} },
+{ 0x0bc7,	16,	{0x0d, 0x74, 0x10, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xeb, 0xf0, 0xaf, 0x05, 0x0d} },
+{ 0x0bd7,	16,	{0x74, 0x10, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xec, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f} },
+{ 0x0be7,	13,	{0x7b, 0x10, 0x12, 0x0d, 0x10, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x22} },
+{ 0x0bf4,	16,	{0x8e, 0x32, 0x8f, 0x33, 0x8d, 0x34, 0x8a, 0x35, 0x8b, 0x36, 0xe4, 0xfd, 0xf5, 0x37, 0xe5, 0x11} },
+{ 0x0c04,	16,	{0x60, 0x12, 0xe5, 0x32, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24, 0x13, 0xf5, 0x82, 0xe4, 0x34, 0x0f} },
+{ 0x0c14,	16,	{0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x33, 0xae, 0x05, 0x0d, 0x74, 0x13, 0x2e, 0xf5, 0x82, 0xe4, 0x34} },
+{ 0x0c24,	16,	{0x0f, 0xf5, 0x83, 0xe5, 0x33, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f, 0x7b, 0x13, 0x12, 0x0d, 0x10, 0xaf} },
+{ 0x0c34,	11,	{0x0f, 0xad, 0x34, 0xab, 0x36, 0xaa, 0x35, 0x12, 0x0c, 0xf1, 0x22} },
+{ 0x0c3f,	16,	{0x8e, 0x32, 0x8f, 0x33, 0x8d, 0x34, 0x8a, 0x35, 0x8b, 0x36, 0xe4, 0xf5, 0x37, 0xe5, 0x37, 0xc3} },
+{ 0x0c4f,	16,	{0x95, 0x34, 0x50, 0x20, 0x05, 0x33, 0xe5, 0x33, 0xae, 0x32, 0x70, 0x02, 0x05, 0x32, 0x14, 0xff} },
+{ 0x0c5f,	16,	{0xe5, 0x36, 0x25, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x35, 0xf5, 0x83, 0xe0, 0xfd, 0x12, 0x0b, 0xa7} },
+{ 0x0c6f,	5,	{0x05, 0x37, 0x80, 0xd9, 0x22} },
+{ 0x0c74,	16,	{0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x25, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} },
+{ 0x0c84,	16,	{0x44, 0x01, 0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a} },
+{ 0x0c94,	13,	{0x0a, 0x89, 0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x03, 0xd3, 0x22, 0xc3, 0x22} },
+{ 0x0ca1,	16,	{0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} },
+{ 0x0cb1,	16,	{0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a, 0x0a, 0x89} },
+{ 0x0cc1,	11,	{0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x01, 0xd3, 0x22, 0xc3, 0x22} },
+{ 0x0ccc,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x06, 0x04, 0xe0, 0x44} },
+{ 0x0cdc,	16,	{0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} },
+{ 0x0cec,	5,	{0xe0, 0x44, 0x04, 0xf0, 0x22} },
+{ 0x0cf1,	16,	{0x12, 0x0c, 0x74, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} },
+{ 0x0d01,	15,	{0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} },
+{ 0x0d10,	16,	{0x12, 0x0c, 0xa1, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} },
+{ 0x0d20,	15,	{0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} },
+{ 0x0d2f,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} },
+{ 0x0d3f,	11,	{0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d4a,	16,	{0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x12, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x14, 0x7e, 0x00, 0x12} },
+{ 0x0d5a,	10,	{0x0d, 0x94, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} },
+{ 0x0d64,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} },
+{ 0x0d74,	8,	{0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d7c,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x03, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} },
+{ 0x0d8c,	8,	{0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0d94,	16,	{0x8e, 0x38, 0x8f, 0x39, 0xe5, 0x39, 0x15, 0x39, 0xae, 0x38, 0x70, 0x02, 0x15, 0x38, 0x4e, 0x60} },
+{ 0x0da4,	7,	{0x05, 0x12, 0x0e, 0x00, 0x80, 0xee, 0x22} },
+{ 0x0dab,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0} },
+{ 0x0dbb,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0dc1,	16,	{0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} },
+{ 0x0dd1,	6,	{0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} },
+{ 0x0dd7,	16,	{0xae, 0x07, 0x7f, 0x21, 0x7d, 0x01, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xab, 0x82} },
+{ 0x0de7,	5,	{0xfa, 0x12, 0x0d, 0x10, 0x22} },
+{ 0x0dec,	16,	{0x50, 0x0f, 0x00, 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6} },
+{ 0x0dfc,	3,	{0xa1, 0x86, 0x8e} },
+{ 0x0dff,	1,	{0x00} },
+{ 0x0e00,	16,	{0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} },
+{ 0x0e10,	1,	{0x22} },
+{ 0x0e11,	14,	{0x90, 0x7f, 0x00, 0xe5, 0x10, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} },
+{ 0x0e1f,	14,	{0x90, 0x7f, 0x00, 0xe5, 0x0e, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} },
+{ 0x0e2d,	8,	{0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0e, 0xd3, 0x22} },
+{ 0x0e35,	8,	{0xe4, 0xf5, 0x0d, 0xd2, 0xe9, 0xd2, 0xaf, 0x22} },
+{ 0x0e3d,	3,	{0xd2, 0x00, 0x22} },
+{ 0x0e40,	2,	{0xd3, 0x22} },
+{ 0x0e42,	2,	{0xd3, 0x22} },
+{ 0x0e44,	2,	{0xd3, 0x22} },
+{ 0x0e46,	2,	{0xd3, 0x22} },
+{ 0x0e48,	2,	{0xd3, 0x22} },
+{ 0x0e4a,	2,	{0xd3, 0x22} },
+{ 0x0e4c,	1,	{0x32} },
+{ 0x0e4d,	1,	{0x32} },
+{ 0x0e4e,	1,	{0x32} },
+{ 0x0e4f,	1,	{0x32} },
+{ 0x0e50,	1,	{0x32} },
+{ 0x0e51,	1,	{0x32} },
+{ 0x0e52,	1,	{0x32} },
+{ 0x0e53,	1,	{0x32} },
+{ 0x0e54,	1,	{0x32} },
+{ 0x0e55,	1,	{0x32} },
+{ 0x0e56,	1,	{0x32} },
+{ 0x0e57,	1,	{0x32} },
+{ 0x0e58,	1,	{0x32} },
+{ 0x0e59,	1,	{0x32} },
+{ 0x0e5a,	1,	{0x32} },
+{ 0x0e5b,	1,	{0x32} },
+{ 0x1100,	16,	{0x12, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x47, 0x05, 0x10, 0x27, 0x01, 0x00, 0x01, 0x02} },
+{ 0x1110,	16,	{0x00, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x03, 0xa0, 0x00, 0x09, 0x04, 0x00, 0x00, 0x02} },
+{ 0x1120,	16,	{0xff, 0x00, 0x00, 0x04, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} },
+{ 0x1130,	16,	{0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x26, 0x03, 0x41, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x68, 0x00} },
+{ 0x1140,	16,	{0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00, 0x69, 0x00, 0x70, 0x00, 0x73, 0x00} },
+{ 0x1150,	16,	{0x2c, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x28, 0x03, 0x46, 0x00} },
+{ 0x1160,	16,	{0x69, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00} },
+{ 0x1170,	16,	{0x46, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00} },
+{ 0x1180,	16,	{0x6b, 0x00, 0x73, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} },
+{ 0x1190,	16,	{0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} },
+{ 0x11a0,	16,	{0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} },
+{ 0x11b0,	16,	{0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} },
+{ 0x11c0,	16,	{0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} },
+{ 0x11d0,	2,	{0x00, 0x00} },
+{ 0xffff,	0,	{0x00} }
+};
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/stopusb linux.18p17/drivers/usb/stopusb
--- linux.vanilla/drivers/usb/stopusb	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/stopusb	Thu Jan  1 01:00:00 1970
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-killall ohci-control
-
-sleep 2
-
-rmmod usb-ohci
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/uhci-debug.c linux.18p17/drivers/usb/uhci-debug.c
--- linux.vanilla/drivers/usb/uhci-debug.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/uhci-debug.c	Thu Jan  1 01:00:00 1970
@@ -1,168 +0,0 @@
-/*
- * UHCI-specific debugging code. Invaluable when something
- * goes wrong, but don't get in my face.
- *
- * (C) Copyright 1999 Linus Torvalds
- */
-
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#include "uhci.h"
-
-void show_td(struct uhci_td * td)
-{
-	printk("%08x ", td->link);
-	printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
-		((td->status >> 29) & 1) ? "SPD " : "",
-		((td->status >> 27) & 3),
-		((td->status >> 26) & 1) ? "LS " : "",
-		((td->status >> 25) & 1) ? "IOS " : "",
-		((td->status >> 24) & 1) ? "IOC " : "",
-		((td->status >> 23) & 1) ? "Active " : "",
-		((td->status >> 22) & 1) ? "Stalled " : "",
-		((td->status >> 21) & 1) ? "DataBufErr " : "",
-		((td->status >> 20) & 1) ? "Babble " : "",
-		((td->status >> 19) & 1) ? "NAK " : "",
-		((td->status >> 18) & 1) ? "CRC/Timeo " : "",
-		((td->status >> 17) & 1) ? "BitStuff " : "",
-		td->status & 0x7ff);
-	printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ",
-		td->info >> 21,
-		 ((td->info >> 19) & 1) ? "DT " : "",
-		 (td->info >> 15) & 15,
-		 (td->info >> 8) & 127,
-		 td->info & 0xff);
-	printk("(buf=%08x)\n", td->buffer);
-}
-
-static void show_sc(int port, unsigned short status)
-{
-	printk("  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
-		port,
-		status,
-		(status & (1 << 12)) ? " PortSuspend" : "",
-		(status & (1 << 9)) ? " PortReset" : "",
-		(status & (1 << 8)) ? " LowSpeed" : "",
-		(status & 0x40) ? " ResumeDetect" : "",
-		(status & 0x08) ? " EnableChange" : "",
-		(status & 0x04) ? " PortEnabled" : "",
-		(status & 0x02) ? " ConnectChange" : "",
-		(status & 0x01) ? " PortConnected" : "");
-}
-
-void show_status(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-	unsigned short usbcmd, usbstat, usbint, usbfrnum;
-	unsigned int flbaseadd;
-	unsigned char sof;
-	unsigned short portsc1, portsc2;
-
-	usbcmd    = inw(io_addr + 0);
-	usbstat   = inw(io_addr + 2);
-	usbint    = inw(io_addr + 4);
-	usbfrnum  = inw(io_addr + 6);
-	flbaseadd = inl(io_addr + 8);
-	sof       = inb(io_addr + 12);
-	portsc1   = inw(io_addr + 16);
-	portsc2   = inw(io_addr + 18);
-
-	printk("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
-		usbcmd,
-		(usbcmd & 0x80) ? " Maxp64" : " Maxp32",
-		(usbcmd & 0x40) ? " CF" : "",
-		(usbcmd & 0x20) ? " SWDBG" : "",
-		(usbcmd & 0x10) ? " FGR" : "",
-		(usbcmd & 0x08) ? " EGSM" : "",
-		(usbcmd & 0x04) ? " GRESET" : "",
-		(usbcmd & 0x02) ? " HCRESET" : "",
-		(usbcmd & 0x01) ? " RS" : "");
-
-	printk("  usbstat   =     %04x   %s%s%s%s%s%s\n",
-		usbstat,
-		(usbstat & 0x20) ? " HCHalted" : "",
-		(usbstat & 0x10) ? " HostControllerProcessError" : "",
-		(usbstat & 0x08) ? " HostSystemError" : "",
-		(usbstat & 0x04) ? " ResumeDetect" : "",
-		(usbstat & 0x02) ? " USBError" : "",
-		(usbstat & 0x01) ? " USBINT" : "");
-
-	printk("  usbint    =     %04x\n", usbint);
-	printk("  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1, 0xfff & (4*(unsigned int)usbfrnum));
-	printk("  flbaseadd = %08x\n", flbaseadd);
-	printk("  sof       =       %02x\n", sof);
-	show_sc(1, portsc1);
-	show_sc(2, portsc2);
-}
-
-#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x))
-
-struct uhci_td * uhci_link_to_td(unsigned int link)
-{
-	if (link & 1)
-		return NULL;
-
-	return bus_to_virt(link & ~15);
-}
-
-void show_queue(struct uhci_qh *qh)
-{
-	struct uhci_td *td;
-	int i = 0;
-
-#if 0
-	printk("    link = %p, element = %p\n", qh->link, qh->element);
-#endif
-	if(!qh->element) {
-		printk("    td 0 = NULL\n");
-		return;
-	}
-
-	for(td = uhci_link_to_td(qh->element); td; 
-	    td = uhci_link_to_td(td->link)) {
-		printk("    td %d = %p\n", i++, td);
-		printk("    ");
-		show_td(td);
-	}
-}
-
-int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
-{
-	int j;
-
-	for (j = 0; j < UHCI_MAXQH; j++)
-		if (qh == uhci->root_hub->qh + j)
-			return 1;
-
-	return 0;
-}
-
-static const char *qh_names[] = {"isochronous", "interrupt2", "interrupt4",
-				 "interrupt8", "interrupt16", "interrupt32",
-				 "interrupt64", "interrupt128", "interrupt256",
-				 "control", "bulk0", "bulk1", "bulk2", "bulk3",
-				 "unused", "unused"};
-
-void show_queues(struct uhci *uhci)
-{
-	int i;
-	struct uhci_qh *qh;
-
-	for (i = 0; i < UHCI_MAXQH; ++i) {
-		printk("  %s:\n", qh_names[i]);
-#if 0
-		printk("  qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i));
-		show_queue(uhci->root_hub->qh + i);
-#endif
-
-		qh = uhci_link_to_qh(uhci->root_hub->qh[i].link);
-		for (; qh; qh = uhci_link_to_qh(qh->link)) {
-			if (is_skeleton_qh(uhci, qh))
-				break;
-
-			show_queue(qh);
-		}
-	}
-}
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/uhci-debug.h linux.18p17/drivers/usb/uhci-debug.h
--- linux.vanilla/drivers/usb/uhci-debug.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/uhci-debug.h	Thu Oct 19 01:49:59 2000
@@ -0,0 +1,316 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * Kernel visible pointers are surrounded in []'s and bus
+ * visible pointers are surrounded in ()'s
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "uhci.h"
+
+void uhci_show_td(struct uhci_td *td)
+{
+	char *spid;
+
+	printk("%08x ", td->link);
+	printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+		((td->status >> 27) & 3),
+		(td->status & TD_CTRL_SPD) ?      "SPD " : "",
+		(td->status & TD_CTRL_LS) ?       "LS " : "",
+		(td->status & TD_CTRL_IOC) ?      "IOC " : "",
+		(td->status & TD_CTRL_ACTIVE) ?   "Active " : "",
+		(td->status & TD_CTRL_STALLED) ?  "Stalled " : "",
+		(td->status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
+		(td->status & TD_CTRL_BABBLE) ?   "Babble " : "",
+		(td->status & TD_CTRL_NAK) ?      "NAK " : "",
+		(td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+		(td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
+		td->status & 0x7ff);
+
+	switch (td->info & 0xff) {
+		case USB_PID_SETUP:
+			spid = "SETUP";
+			break;
+		case USB_PID_OUT:
+			spid = "OUT";
+			break;
+		case USB_PID_IN:
+			spid = "IN";
+			break;
+		default:
+			spid = "?";
+			break;
+	}
+
+	printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+		td->info >> 21,
+		((td->info >> 19) & 1),
+		(td->info >> 15) & 15,
+		(td->info >> 8) & 127,
+		(td->info & 0xff),
+		spid);
+	printk("(buf=%08x)\n", td->buffer);
+}
+
+static void uhci_show_sc(int port, unsigned short status)
+{
+	printk("  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
+		port,
+		status,
+		(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+		(status & USBPORTSC_PR) ?   "PortReset " : "",
+		(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+		(status & USBPORTSC_RD) ?   "ResumeDetect " : "",
+		(status & USBPORTSC_PEC) ?  "EnableChange " : "",
+		(status & USBPORTSC_PE) ?   "PortEnabled " : "",
+		(status & USBPORTSC_CSC) ?  "ConnectChange " : "",
+		(status & USBPORTSC_CCS) ?  "PortConnected " : "");
+}
+
+void uhci_show_status(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	usbcmd    = inw(io_addr + 0);
+	usbstat   = inw(io_addr + 2);
+	usbint    = inw(io_addr + 4);
+	usbfrnum  = inw(io_addr + 6);
+	flbaseadd = inl(io_addr + 8);
+	sof       = inb(io_addr + 12);
+	portsc1   = inw(io_addr + 16);
+	portsc2   = inw(io_addr + 18);
+
+	printk("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
+		usbcmd,
+		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
+		(usbcmd & USBCMD_CF) ?      "CF " : "",
+		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
+		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
+		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
+		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
+		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+		(usbcmd & USBCMD_RS) ?      "RS " : "");
+
+	printk("  usbstat   =     %04x   %s%s%s%s%s%s\n",
+		usbstat,
+		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
+		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
+		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
+		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
+		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
+		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	printk("  usbint    =     %04x\n", usbint);
+	printk("  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
+		0xfff & (4*(unsigned int)usbfrnum));
+	printk("  flbaseadd = %08x\n", flbaseadd);
+	printk("  sof       =       %02x\n", sof);
+	uhci_show_sc(1, portsc1);
+	uhci_show_sc(2, portsc2);
+}
+
+#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x))
+
+struct uhci_td *uhci_link_to_td(unsigned int link)
+{
+	if (link & UHCI_PTR_TERM)
+		return NULL;
+
+	return bus_to_virt(link & ~UHCI_PTR_BITS);
+}
+
+void uhci_show_urb_queue(struct urb *urb)
+{
+	struct urb_priv *urbp = urb->hcpriv;
+	struct list_head *head, *tmp;
+	int i, checked = 0, prevactive = 0;
+
+	printk("  URB [%p] urbp [%p]\n", urb, urbp);
+
+	if (urbp->qh)
+		printk("    QH [%p]\n", urbp->qh);
+	else
+		printk("    QH [%p] element (%08x) link (%08x)\n", urbp->qh,
+			urbp->qh->element, urbp->qh->link);
+
+	i = 0;
+
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		printk("      td %d: [%p]\n", i++, td);
+		printk("      ");
+		uhci_show_td(td);
+
+		if (i > 10 && !checked && prevactive && tmp != head) {
+			struct list_head *ntmp = tmp;
+			struct uhci_td *ntd = td;
+			int active = 1, ni = i;
+
+			checked = 1;
+
+			while (ntmp != head && ntmp->next != head && active) {
+				ntd = list_entry(ntmp, struct uhci_td, list);
+
+				ntmp = ntmp->next;
+
+				active = ntd->status & TD_CTRL_ACTIVE;
+
+				ni++;
+			}
+
+			if (active && ni > i) {
+				printk("      [skipped %d active TD's]\n", ni - i);
+				tmp = ntmp;
+				td = ntd;
+				i = ni;
+			}
+		}
+
+		prevactive = td->status & TD_CTRL_ACTIVE;
+	}
+}
+
+void uhci_show_queue(struct uhci_qh *qh)
+{
+	struct uhci_td *td, *first;
+	int i = 0, count = 1000;
+
+	if (qh->element & UHCI_PTR_QH)
+		printk("      Element points to QH (bug?)\n");
+
+	if (qh->element & UHCI_PTR_DEPTH)
+		printk("      Depth traverse\n");
+
+	if (qh->element & UHCI_PTR_TERM)
+		printk("      Terminate\n");
+
+	if (!(qh->element & ~UHCI_PTR_BITS)) {
+		printk("      td 0: [NULL]\n");
+		return;
+	}
+
+	first = uhci_link_to_td(qh->element);
+
+	/* Make sure it doesn't runaway */
+	for (td = first; td && count > 0; 
+	     td = uhci_link_to_td(td->link), --count) {
+		printk("      td %d: [%p]\n", i++, td);
+		printk("      ");
+		uhci_show_td(td);
+
+		if (td == uhci_link_to_td(td->link)) {
+			printk(KERN_ERR "td links to itself!\n");
+			break;
+		}
+	}
+}
+
+static int uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
+{
+	int j;
+
+	for (j = 0; j < UHCI_NUM_SKELTD; j++)
+		if (td == uhci->skeltd + j)
+			return 1;
+
+	return 0;
+}
+
+static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+	int j;
+
+	for (j = 0; j < UHCI_NUM_SKELQH; j++)
+		if (qh == uhci->skelqh + j)
+			return 1;
+
+	return 0;
+}
+
+static const char *td_names[] = {"interrupt1", "interrupt2", "interrupt4",
+				 "interrupt8", "interrupt16", "interrupt32",
+				 "interrupt64", "interrupt128", "interrupt256" };
+static const char *qh_names[] = { "control", "bulk" };
+
+void uhci_show_queues(struct uhci *uhci)
+{
+	int i, isqh = 0;
+	struct uhci_qh *qh;
+	struct uhci_td *td;
+
+	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+		int shown = 0;
+
+		td = uhci_link_to_td(uhci->fl->frame[i]);
+		if (td)
+			isqh = uhci->fl->frame[i] & UHCI_PTR_QH;
+		while (td && !isqh) {
+			if (uhci_is_skeleton_td(uhci, td))
+				break;
+
+			if (!shown) {
+				printk("   Frame %d\n", i);
+				shown = 1;
+			}
+
+			printk("[%p] ", td);
+
+			uhci_show_td(td);
+			td = uhci_link_to_td(td->link);
+			if (td)
+				isqh = td->link & UHCI_PTR_QH;
+		}
+	}
+	for (i = 0; i < UHCI_NUM_SKELTD; ++i) {
+		printk("  %s: [%p] (%08x)\n", td_names[i],
+			&uhci->skeltd[i],
+			uhci->skeltd[i].link);
+
+		td = uhci_link_to_td(uhci->skeltd[i].link);
+		if (td)
+			isqh = uhci->skeltd[i].link & UHCI_PTR_QH;
+		while (td && !isqh) {
+			if (uhci_is_skeleton_td(uhci, td))
+				break;
+
+			printk("[%p] ", td);
+
+			uhci_show_td(td);
+			td = uhci_link_to_td(td->link);
+			if (td)
+				isqh = td->link & UHCI_PTR_QH;
+		}
+	}
+	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
+		printk("  %s: [%p] (%08x) (%08x)\n", qh_names[i],
+			&uhci->skelqh[i],
+			uhci->skelqh[i].link, uhci->skelqh[i].element);
+
+		qh = uhci_link_to_qh(uhci->skelqh[i].link);
+		for (; qh; qh = uhci_link_to_qh(qh->link)) {
+			if (uhci_is_skeleton_qh(uhci, qh))
+				break;
+
+			printk("    [%p] (%08x) (%08x)\n",
+				qh, qh->link, qh->element);
+
+			uhci_show_queue(qh);
+		}
+	}
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/uhci.c linux.18p17/drivers/usb/uhci.c
--- linux.vanilla/drivers/usb/uhci.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/uhci.c	Sat Sep 30 14:39:03 2000
@@ -2,15 +2,18 @@
  * Universal Host Controller Interface driver for USB.
  *
  * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@sventech.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
  *
  * Intel documents this fairly well, and as far as I know there
  * are no royalties or anything like that, but even so there are
  * people who decided that they want to do the same thing in a
  * completely different way.
  *
- * Oh, well. The intel version is the more common by far. As such,
- * that's the one I care about right now.
- *
  * WARNING! The USB documentation is downright evil. Most of it
  * is just crap, written by a committee. You're better off ignoring
  * most of it, the important stuff is:
@@ -18,738 +21,1997 @@
  *  - working around the horridness of the rest
  */
 
-/* 4/4/1999 added data toggle for interrupt pipes -keryan */
-
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
 #include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+//#define DEBUG
+#include <linux/usb.h>
 
-#include <asm/spinlock.h>
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 
 #include "uhci.h"
-#include "inits.h"
+#include "uhci-debug.h"
 
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-static int handle_apm_event(apm_event_t event);
-static int apm_resume = 0;
-#endif
+#include <linux/pm.h>
+static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+static int debug = 1;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
 
-#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0)
+static kmem_cache_t *uhci_td_cachep;
+static kmem_cache_t *uhci_qh_cachep;
+static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
 
-static struct wait_queue *uhci_configure = NULL;
+static LIST_HEAD(uhci_list);
+
+static int rh_submit_urb(struct urb *urb);
+static int rh_unlink_urb(struct urb *urb);
+static int uhci_get_current_frame_number(struct usb_device *dev);
+static int uhci_unlink_generic(struct urb *urb);
+static int uhci_unlink_urb(struct urb *urb);
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
 
 /*
- * Return the result of a TD..
+ * Only the USB core should call uhci_alloc_dev and uhci_free_dev
  */
-static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td)
+static int uhci_alloc_dev(struct usb_device *dev)
 {
-	unsigned int status;
+	return 0;
+}
 
-	status = (td->status >> 16) & 0xff;
+static int uhci_free_dev(struct usb_device *dev)
+{
+	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
+	struct list_head *tmp, *head = &uhci->urb_list;
+	unsigned long flags;
 
-	/* Some debugging code */
-	if (status) {
-		int i = 10;
-		struct uhci_td *tmp = dev->control_td;
-		printk("uhci_td_result() failed with status %d\n", status);
-		show_status(dev->uhci);
-		do {
-			show_td(tmp);
-			tmp++;
-			if (!--i)
-				break;
-		} while (tmp <= td);
+	/* Walk through the entire URB list and forcefully remove any */
+	/*  URBs that are still active for that device */
+	nested_lock(&uhci->urblist_lock, flags);
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		if (u->dev == dev)
+			uhci_unlink_urb(u);
 	}
-	return status;		
+	nested_unlock(&uhci->urblist_lock, flags);
+
+	return 0;
 }
 
-/*
- * Inserts a td into qh list at the top.
- *
- * Careful about atomicity: even on UP this
- * requires a locked access due to the concurrent
- * DMA engine.
- *
- * NOTE! This assumes that first->last is a valid
- * list of TD's with the proper backpointers set
- * up and all..
- */
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *first, struct uhci_td *last)
+static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb)
 {
-	unsigned int link = qh->element;
-	unsigned int new = 4 | virt_to_bus(first);
+	unsigned long flags;
 
-	for (;;) {
-		unsigned char success;
+	nested_lock(&uhci->urblist_lock, flags);
+	list_add(&urb->urb_list, &uhci->urb_list);
+	nested_unlock(&uhci->urblist_lock, flags);
+}
 
-		last->link = link;
-		first->backptr = &qh->element;
-		asm volatile("lock ; cmpxchg %4,%2 ; sete %0"
-			:"=q" (success), "=a" (link)
-			:"m" (qh->element), "1" (link), "r" (new)
-			:"memory");
-		if (success) {
-			/* Was there a successor entry? Fix it's backpointer.. */
-			if ((link & 1) == 0) {
-				struct uhci_td *next = bus_to_virt(link & ~15);
-				next->backptr = &last->link;
-			}
-			break;
-		}
+static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb)
+{
+	unsigned long flags;
+
+	nested_lock(&uhci->urblist_lock, flags);
+	if (!list_empty(&urb->urb_list)) {
+		list_del(&urb->urb_list);
+		INIT_LIST_HEAD(&urb->urb_list);
 	}
+	nested_unlock(&uhci->urblist_lock, flags);
 }
 
-static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct uhci_td *td)
+void uhci_set_next_interrupt(struct uhci *uhci)
 {
-	uhci_insert_tds_in_qh(qh, td, td);
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+	uhci->skel_term_td.status |= TD_CTRL_IOC;
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
-static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh)
+void uhci_clear_next_interrupt(struct uhci *uhci)
 {
-	newqh->link = qh->link;
-	qh->link = virt_to_bus(newqh) | 2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+	uhci->skel_term_td.status &= ~TD_CTRL_IOC;
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
-static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh)
+static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
 {
-	unsigned int remphys = virt_to_bus(remqh);
-	struct uhci_qh *lqh = qh;
+	struct uhci_td *td;
 
-	while ((lqh->link & ~0xF) != remphys) {
-		if (lqh->link & 1)
-			break;
+	td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+	if (!td)
+		return NULL;
+
+	td->link = UHCI_PTR_TERM;
+	td->buffer = 0;
+
+	td->frameptr = NULL;
+	td->nexttd = td->prevtd = NULL;
+	td->dev = dev;
+	INIT_LIST_HEAD(&td->list);
+
+	usb_inc_dev_use(dev);
+
+	return td;
+}
 
-		lqh = bus_to_virt(lqh->link & ~0xF);
+static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
+		__u32 info, __u32 buffer)
+{
+	td->status = status;
+	td->info = info;
+	td->buffer = buffer;
+}
+
+static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+
+	/* Fix the linked list pointers */
+	td->nexttd = skeltd->nexttd;
+	td->prevtd = skeltd;
+	if (skeltd->nexttd)
+		skeltd->nexttd->prevtd = td;
+	skeltd->nexttd = td;
+
+	td->link = skeltd->link;
+	skeltd->link = virt_to_bus(td);
+
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
+/*
+ * We insert Isochronous transfers directly into the frame list at the
+ * beginning
+ * The layout looks as follows:
+ * frame list pointer -> iso td's (if any) ->
+ * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh
+ */
+
+static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum)
+{
+	unsigned long flags;
+	struct uhci_td *nexttd;
+
+	framenum %= UHCI_NUMFRAMES;
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+
+	td->frameptr = &uhci->fl->frame[framenum];
+	td->link = uhci->fl->frame[framenum];
+	if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) {
+		nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link);
+		td->nexttd = nexttd;
+		nexttd->prevtd = td;
+		nexttd->frameptr = NULL;
 	}
+	uhci->fl->frame[framenum] = virt_to_bus(td);
+
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
+static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
+{
+	unsigned long flags;
 
-	if (lqh->link & 1) {
-		printk("couldn't find qh in chain!\n");
+	/* If it's not inserted, don't remove it */
+	if (!td->frameptr && !td->prevtd && !td->nexttd)
 		return;
-	}
 
-	lqh->link = remqh->link;
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+	if (td->frameptr) {
+		*(td->frameptr) = td->link;
+		if (td->nexttd) {
+			td->nexttd->frameptr = td->frameptr;
+			td->nexttd->prevtd = NULL;
+			td->nexttd = NULL;
+		}
+		td->frameptr = NULL;
+	} else {
+		if (td->prevtd) {
+			td->prevtd->nexttd = td->nexttd;
+			td->prevtd->link = td->link;
+		}
+		if (td->nexttd)
+			td->nexttd->prevtd = td->prevtd;
+		td->prevtd = td->nexttd = NULL;
+	}
+	td->link = UHCI_PTR_TERM;
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
 /*
- * Removes td from qh if present.
- *
- * NOTE! We keep track of both forward and back-pointers,
- * so this should be trivial, right?
- *
- * Wrong. While all TD insert/remove operations are synchronous
- * on the CPU, the UHCI controller can (and does) play with the
- * very first forward pointer. So we need to validate the backptr
- * before we change it, so that we don't by mistake reset the QH
- * head to something old.
+ * Inserts a td into qh list at the top.
  */
-static void uhci_remove_td(struct uhci_td *td)
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
-	unsigned int *backptr = td->backptr;
-	unsigned int link = td->link;
-	unsigned int me;
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td, *prevtd;
 
-	if (!backptr)
+	if (!urbp)
 		return;
 
-	td->backptr = NULL;
+	head = &urbp->list;
+	tmp = head->next;
+	if (head == tmp)
+		return;
 
-	/*
-	 * This is the easy case: the UHCI will never change "td->link",
-	 * so we can always just look at that and fix up the backpointer
-	 * of any next element..
-	 */
-	if (!(link & 1)) {
-		struct uhci_td *next = bus_to_virt(link & ~15);
-		next->backptr = backptr;
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Add the first TD to the QH element pointer */
+	qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+	prevtd = td;
+
+	/* Then link the rest of the TD's */
+	tmp = tmp->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+		prevtd = td;
 	}
 
-	/*
-	 * The nasty case is "backptr->next", which we need to
-	 * update to "link" _only_ if "backptr" still points
-	 * to us (it may not: maybe backptr is a QH->element
-	 * pointer and the UHCI has changed the value).
-	 */
-	me = virt_to_bus(td) | (0xe & *backptr);
-	asm volatile("lock ; cmpxchg %0,%1"
-		:
-		:"r" (link), "m" (*backptr), "a" (me)
-		:"memory");
+	prevtd->link = UHCI_PTR_TERM;
 }
 
-static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev)
+static void uhci_free_td(struct uhci_td *td)
+{
+	if (!list_empty(&td->list))
+		dbg("td is still in URB list!");
+
+	if (td->dev)
+		usb_dec_dev_use(td->dev);
+
+	kmem_cache_free(uhci_td_cachep, td);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct usb_device *dev)
 {
 	struct uhci_qh *qh;
-	int inuse;
 
-	qh = dev->qh;
-	for (; (inuse = test_and_set_bit(0, &qh->inuse)) != 0 && qh < &dev->qh[UHCI_MAXQH]; qh++)
-		;
+	qh = kmem_cache_alloc(uhci_qh_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+	if (!qh)
+		return NULL;
+
+	qh->element = UHCI_PTR_TERM;
+	qh->link = UHCI_PTR_TERM;
+
+	qh->dev = dev;
+	qh->prevqh = qh->nextqh = NULL;
 
-	if (!inuse)
-		return(qh);
+	INIT_LIST_HEAD(&qh->remove_list);
 
-	printk("ran out of qh's for dev %p\n", dev);
-	return(NULL);
+	usb_inc_dev_use(dev);
+
+	return qh;
 }
 
-static void uhci_qh_deallocate(struct uhci_qh *qh)
+static void uhci_free_qh(struct uhci_qh *qh)
 {
-	if (qh->element != 1)
-		printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
-
-	qh->element = 1;
-	qh->link = 1;
+	if (qh->dev)
+		usb_dec_dev_use(qh->dev);
 
-	clear_bit(0, &qh->inuse);
+	kmem_cache_free(uhci_qh_cachep, qh);
 }
 
-static struct uhci_td *uhci_td_allocate(struct uhci_device *dev)
+static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh *qh)
 {
-	struct uhci_td *td;
-	int inuse;
+	unsigned long flags;
 
-	td = dev->td;
-	for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++)
-		;
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
 
-	if (!inuse)
-		return(td);
+	/* Fix the linked list pointers */
+	qh->nextqh = skelqh->nextqh;
+	qh->prevqh = skelqh;
+	if (skelqh->nextqh)
+		skelqh->nextqh->prevqh = qh;
+	skelqh->nextqh = qh;
 
-	printk("ran out of td's for dev %p\n", dev);
-	return(NULL);
+	qh->link = skelqh->link;
+	skelqh->link = virt_to_bus(qh) | UHCI_PTR_QH;
+
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
-/*
- * This MUST only be called when it has been removed from a QH already (or
- * the QH has been removed from the skeleton
- */
-static void uhci_td_deallocate(struct uhci_td *td)
+static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
-	td->link = 1;
+	unsigned long flags;
+	int delayed;
+
+	/* If the QH isn't queued, then we don't need to delay unlink it */
+	delayed = (qh->prevqh || qh->nextqh);
+
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
+	if (qh->prevqh) {
+		qh->prevqh->nextqh = qh->nextqh;
+		qh->prevqh->link = qh->link;
+	}
+	if (qh->nextqh)
+		qh->nextqh->prevqh = qh->prevqh;
+	qh->prevqh = qh->nextqh = NULL;
+	qh->element = qh->link = UHCI_PTR_TERM;
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 
-	clear_bit(0, &td->inuse);
+	if (delayed) {
+		spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+
+		/* Check to see if the remove list is empty */
+		/* Set the IOC bit to force an interrupt so we can remove the QH */
+		if (list_empty(&uhci->qh_remove_list))
+			uhci_set_next_interrupt(uhci);
+
+		/* Add it */
+		list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+		spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+	} else
+		uhci_free_qh(qh);
 }
 
-/*
- * UHCI interrupt list operations..
- */
-static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t uhci_append_urb_lock = SPIN_LOCK_UNLOCKED;
 
-static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, usb_device_irq completed, void *dev_id)
+/* This function will append one URB's QH to another URB's QH. This is for */
+/*  USB_QUEUE_BULK support */
+static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb)
 {
+	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+	struct list_head *tmp;
+	struct uhci_td *td, *ltd;
 	unsigned long flags;
 
-	td->completed = completed;
-	td->dev_id = dev_id;
+	eurbp = eurb->hcpriv;
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci_append_urb_lock, flags);
+
+	/* Find the beginning URB in the queue */
+	if (eurbp->queued) {
+		struct list_head *head = &eurbp->urb_queue_list;
 
-	spin_lock_irqsave(&irqlist_lock, flags);
-	list_add(&td->irq_list, &uhci->interrupt_list);
-	spin_unlock_irqrestore(&irqlist_lock, flags);
+		tmp = head->next;
+		while (tmp != head) {
+			struct urb_priv *turbp =
+				list_entry(tmp, struct urb_priv, urb_queue_list);
+
+			tmp = tmp->next;
+
+			if (!turbp->queued)
+				break;
+		}
+	} else
+		tmp = &eurbp->urb_queue_list;
+
+	furbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+
+	tmp = furbp->urb_queue_list.prev;
+	lurbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+
+	/* Add this one to the end */
+	list_add_tail(&urbp->urb_queue_list, &furbp->urb_queue_list);
+
+	/* Grab the last TD from the last URB */
+	ltd = list_entry(lurbp->list.prev, struct uhci_td, list);
+
+	/* Grab the first TD from the first URB */
+	td = list_entry(urbp->list.next, struct uhci_td, list);
+
+	/* No breadth since this will only be called for bulk transfers */
+	ltd->link = virt_to_bus(td);
+
+	spin_unlock_irqrestore(&uhci_append_urb_lock, flags);
 }
 
-static void uhci_remove_irq_list(struct uhci_td *td)
+static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
 {
+	struct urb_priv *urbp, *nurbp;
 	unsigned long flags;
 
-	spin_lock_irqsave(&irqlist_lock, flags);
-	list_del(&td->irq_list);
-	spin_unlock_irqrestore(&irqlist_lock, flags);
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci_append_urb_lock, flags);
+
+	nurbp = list_entry(urbp->urb_queue_list.next, struct urb_priv,
+			urb_queue_list);
+
+	if (!urbp->queued) {
+		/* We're the head, so just insert the QH for the next URB */
+		uhci_insert_qh(uhci, &uhci->skel_bulk_qh, nurbp->qh);
+		nurbp->queued = 0;
+	} else {
+		struct urb_priv *purbp;
+		struct uhci_td *ptd;
+
+		/* We're somewhere in the middle (or end). A bit trickier */
+		/*  than the head scenario */
+		purbp = list_entry(urbp->urb_queue_list.prev, struct urb_priv,
+				urb_queue_list);
+
+		ptd = list_entry(purbp->list.prev, struct uhci_td, list);
+		if (nurbp->queued)
+			/* Close the gap between the two */
+			ptd->link = virt_to_bus(list_entry(nurbp->list.next,
+					struct uhci_td, list));
+		else
+			/* The next URB happens to be the beggining, so */
+			/*  we're the last, end the chain */
+			ptd->link = UHCI_PTR_TERM;
+		
+	}
+
+	list_del(&urbp->urb_queue_list);
+
+	spin_unlock_irqrestore(&uhci_append_urb_lock, flags);
 }
 
-/*
- * Request a interrupt handler..
- */
-static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
 {
-	struct uhci_device *dev = usb_to_uhci(usb_dev);
-	struct uhci_td *td = uhci_td_allocate(dev);
-	struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev);
+	struct urb_priv *urbp;
 
-	unsigned int destination, status;
+	urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+	if (!urbp)
+		return NULL;
 
-	/* Destination: pipe destination with INPUT */
-	destination = (pipe & 0x0007ff00)  |  0x69;
+	memset((void *)urbp, 0, sizeof(*urbp));
 
-	/* Status:    slow/fast,      Interrupt,   Active,    Short Packet Detect     Infinite Errors */
-	status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23)   |   (1 << 29)       |    (0 << 27);
+	urbp->inserttime = jiffies;
+	urbp->urb = urb;
+	
+	INIT_LIST_HEAD(&urbp->list);
+	INIT_LIST_HEAD(&urbp->urb_queue_list);
 
-	if(interrupt_qh->element != 1)
-		printk("interrupt_qh->element = 0x%x\n", 
-		       interrupt_qh->element);
+	urb->hcpriv = urbp;
 
-	td->link = 1;
-	td->status = status;			/* In */
-	td->info = destination | (7 << 21);	/* 8 bytes of data */
-	td->buffer = virt_to_bus(dev->data);
-	td->qh = interrupt_qh;
-	interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh;
+	usb_inc_dev_use(urb->dev);
 
-	uhci_add_irq_list(dev->uhci, td, handler, dev_id);
+	return urbp;
+}
 
-	uhci_insert_td_in_qh(interrupt_qh, td);
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
-	/* Add it into the skeleton */
-	uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh);
-	return 0;
-}
+	td->urb = urb;
 
-/*
- * Control thread operations: we just mark the last TD
- * in a control thread as an interrupt TD, and wake up
- * the front-end on completion.
- *
- * We need to remove the TD from the lists (both interrupt
- * list and TD lists) by hand if something bad happens!
- */
-static struct wait_queue *control_wakeup;
+	list_add_tail(&td->list, &urbp->list);
+}
 
-static int uhci_control_completed(int status, void *buffer, void *dev_id)
+static void uhci_remove_td_from_urb(struct urb *urb, struct uhci_td *td)
 {
-	wake_up(&control_wakeup);
-	return 0;			/* Don't re-instate */
+	urb = NULL;	/* No warnings */
+
+	if (list_empty(&td->list))
+		return;
+
+	list_del(&td->list);
+	INIT_LIST_HEAD(&td->list);
+
+	td->urb = NULL;
 }
 
-/* td points to the last td in the list, which interrupts on completion */
-static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+static void uhci_destroy_urb_priv(struct urb *urb)
 {
-	struct wait_queue wait = { current, NULL };
-	struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev);
-	struct uhci_td *curtd;
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp;
+	struct uhci *uhci;
+	struct uhci_td *td;
+	unsigned long flags;
 
-	current->state = TASK_UNINTERRUPTIBLE;
-	add_wait_queue(&control_wakeup, &wait);
+	spin_lock_irqsave(&urb->lock, flags);
 
-	uhci_add_irq_list(dev->uhci, last, uhci_control_completed, NULL);
-	
-	/* FIXME: This is kinda kludged */
-	/* Walk the TD list and update the QH pointer */
-	{
-	int maxcount = 100;
-
-	curtd = first;
-	do {
-		curtd->qh = ctrl_qh;
-		if (curtd->link & 1)
-			break;
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp)
+		goto unlock;
 
-		curtd = bus_to_virt(curtd->link & ~0xF);
-		if (!--maxcount) {
-			printk("runaway tds!\n");
-			break;
-		}
-	} while (1);
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+		goto unlock;
+
+	uhci = urb->dev->bus->hcpriv;
+
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(urb, td);
+
+		uhci_remove_td(uhci, td);
+
+		uhci_free_td(td);
 	}
 
-	uhci_insert_tds_in_qh(ctrl_qh, first, last);
+	urb->hcpriv = NULL;
+	kmem_cache_free(uhci_up_cachep, urbp);
+
+	usb_dec_dev_use(urb->dev);
 
-	/* Add it into the skeleton */
-	uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+unlock:
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
 
-	schedule_timeout(HZ/10);
+static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
 
-	remove_wait_queue(&control_wakeup, &wait);
+	if (!urbp)
+		return;
 
-	/* Clean up in case it failed.. */
-	uhci_remove_irq_list(last);
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
 
-#if 0
-	printk("Looking for tds [%p, %p]\n", dev->control_td, td);
-#endif
+	if (!urbp->fsbr) {
+		urbp->fsbr = 1;
+		if (!uhci->fsbr++)
+			uhci->skel_term_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+	}
+
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
+static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (!urbp)
+		return;
 
-	/* Remove it from the skeleton */
-	uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+	spin_lock_irqsave(&uhci->framelist_lock, flags);
 
-	uhci_qh_deallocate(ctrl_qh);
+	if (urbp->fsbr) {
+		urbp->fsbr = 0;
+		if (!--uhci->fsbr)
+			uhci->skel_term_qh.link = UHCI_PTR_TERM;
+	}
 
-	return uhci_td_result(dev, last);
+	spin_unlock_irqrestore(&uhci->framelist_lock, flags);
 }
 
 /*
- * Send or receive a control message on a pipe.
- *
- * Note that the "pipe" structure is set up to map
- * easily to the uhci destination fields.
- *
- * A control message is built up from three parts:
- *  - The command itself
- *  - [ optional ] data phase
- *  - Status complete phase
- *
- * The data phase can be an arbitrary number of TD's
- * although we currently had better not have more than
- * 29 TD's here (we have 31 TD's allocated for control
- * operations, and two of them are used for command and
- * status).
+ * Map status to standard result codes
  *
- * 29 TD's is a minimum of 232 bytes worth of control
- * information, that's just ridiculously high. Most
- * control messages have just a few bytes of data.
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
+ * <dir_out> is True for output TDs and False for input TDs.
  */
-static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
+static int uhci_map_status(int status, int dir_out)
 {
-	struct uhci_device *dev = usb_to_uhci(usb_dev);
-	struct uhci_td *first, *td, *prevtd;
-	unsigned long destination, status;
-	int ret;
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)			/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)			/* Babble */
+		return -EPIPE;
+	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)			/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)			/* Active */
+		return 0;
 
-	if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29)
-		printk("Warning, too much data for a control packet, crashing\n");
+	return -EINVAL;
+}
 
-	first = td = uhci_td_allocate(dev);
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	unsigned char *data = urb->transfer_buffer;
 
-	/* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */
-	destination = (pipe & 0x0007ff00) | 0x2D;
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
 
-	/* Status:    slow/fast,       Active,    Short Packet Detect     Three Errors */
-	status = (pipe & (1 << 26)) | (1 << 23)   |   (1 << 29)       |    (3 << 27);
+	/* 3 errors */
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
 
 	/*
 	 * Build the TD for the control request
 	 */
-	td->status = status;				/* Try forever */
-	td->info = destination | (7 << 21);		/* 8 bytes of data */
-	td->buffer = virt_to_bus(cmd);
+	td = uhci_alloc_td(urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination | (7 << 21),
+		virt_to_bus(urb->setup_packet));
 
 	/*
 	 * If direction is "send", change the frame from SETUP (0x2D)
-	 * to OUT (0xE1). Else change it from SETUP to IN (0x69)
+	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
 	 */
-	destination ^= (0x2D ^ 0x69);			/* SETUP -> IN */
-	if (usb_pipeout(pipe))
-		destination ^= (0xE1 ^ 0x69);		/* IN -> OUT */
+	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
 
-	prevtd = td;
-	td = uhci_td_allocate(dev);
-	prevtd->link = 4 | virt_to_bus(td);
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
 
 	/*
 	 * Build the DATA TD's
 	 */
 	while (len > 0) {
-		/* Build the TD for control status */
 		int pktsze = len;
-		int maxsze = usb_maxpacket(pipe);
 
 		if (pktsze > maxsze)
 			pktsze = maxsze;
 
+		td = uhci_alloc_td(urb->dev);
+		if (!td)
+			return -ENOMEM;
+
 		/* Alternate Data0/1 (start with Data1) */
-		destination ^= 1 << 19;
+		destination ^= 1 << TD_TOKEN_TOGGLE;
 	
-		td->status = status;					/* Status */
-		td->info = destination | ((pktsze-1) << 21);		/* pktsze bytes of data */
-		td->buffer = virt_to_bus(data);
-		td->backptr = &prevtd->link;
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
+			virt_to_bus(data));
 
-		prevtd = td;
-		td = uhci_td_allocate(dev);
-		prevtd->link = 4 | virt_to_bus(td);			/* Update previous TD */
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/*
+	 * Build the final TD for control status 
+	 */
+	td = uhci_alloc_td(urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	/*
+	 * It's IN if the pipe is an output pipe or we're not expecting
+	 * data back.
+	 */
+	destination &= ~TD_PID;
+	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= 1 << TD_TOKEN_TOGGLE;		/* End in Data1 */
+
+	status &= ~TD_CTRL_SPD;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status | TD_CTRL_IOC,
+		destination | (UHCI_NULL_DATA_SIZE << 21), 0);
+
+	qh = uhci_alloc_qh(urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS) {
+		uhci_insert_tds_in_qh(qh, urb, 0);
+		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh);
+	} else {
+		uhci_insert_tds_in_qh(qh, urb, 1);
+		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
+		uhci_inc_fsbr(uhci, urb);
+	}
 
-		data += maxsze;
+	urbp->qh = qh;
+
+	uhci_add_urb_list(uhci, urb);
+
+	usb_inc_dev_use(urb->dev);
+
+	return -EINPROGRESS;
+}
+
+static int usb_control_retrigger_status(struct urb *urb);
+
+static int uhci_result_control(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	if (!urbp)
+		return -EINVAL;
+
+	head = &urbp->list;
+	if (head->next == head)
+		return -EINVAL;
+
+	if (urbp->short_control_packet) {
+		tmp = head->prev;
+		goto status_phase;
+	}
+
+	tmp = head->next;
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* The first TD is the SETUP phase, check the status, but skip */
+	/*  the count */
+	status = uhci_status_bits(td->status);
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	urb->actual_length = 0;
+
+	/* The rest of the TD's (but the last) are data */
+	tmp = tmp->next;
+	while (tmp != head && tmp->next != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
+		    !(td->status & TD_CTRL_ACTIVE)) {
+			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
+			urbp->fsbr_timeout = 0;
+			td->status &= ~TD_CTRL_IOC;
+		}
+
+		status = uhci_status_bits(td->status);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td->status);
+
+		if (status)
+			goto td_error;
+
+		/* Check to see if we received a short packet */
+		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			}
+
+			if (uhci_packetid(td->info) == USB_PID_IN)
+				return usb_control_retrigger_status(urb);
+			else
+				return 0;
+		}
+	}
+
+status_phase:
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Control status phase */
+	status = uhci_status_bits(td->status);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+	/* APC BackUPS Pro kludge */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
+	if (td->status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
+	    status & TD_CTRL_ACTIVE &&
+	    status & TD_CTRL_NAK)
+		return 0;
+#endif
+
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td->info));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+	    			uhci_packetout(td->info));
+
+err:
+	if (debug && ret != -EPIPE) {
+		/* Some debugging code */
+		dbg("uhci_result_control() failed with status %x", status);
+
+		/* Print the chain for debugging purposes */
+		uhci_show_urb_queue(urb);
+	}
+
+	return ret;
+}
+
+static int usb_control_retrigger_status(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = urb->dev->bus->hcpriv;
+
+	urbp->short_control_packet = 1;
+
+	/* Create a new QH to avoid pointer overwriting problems */
+	uhci_remove_qh(uhci, urbp->qh);
+
+	/* Delete all of the TD's except for the status TD at the end */
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head && tmp->next != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(urb, td);
+
+		uhci_remove_td(uhci, td);
+
+		uhci_free_td(td);
+	}
+
+	urbp->qh = uhci_alloc_qh(urb->dev);
+	if (!urbp->qh) {
+		err("unable to allocate new QH for control retrigger");
+		return -ENOMEM;
+	}
+
+	/* One TD, who cares about Breadth first? */
+	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS)
+		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+	else
+		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+	return -EINPROGRESS;
+}
+
+/*
+ * Interrupt transfers
+ */
+static int uhci_submit_interrupt(struct urb *urb)
+{
+	struct uhci_td *td;
+	unsigned long destination, status;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
+
+	td = uhci_alloc_td(urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	destination |= ((urb->transfer_buffer_length - 1) << 21);
+
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination,
+		virt_to_bus(urb->transfer_buffer));
+
+	uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td);
+
+	uhci_add_urb_list(uhci, urb);
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_interrupt(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	if (!urbp)
+		return -EINVAL;
+
+	urb->actual_length = 0;
+
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&
+		    !(td->status & TD_CTRL_ACTIVE)) {
+			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);
+			urbp->fsbr_timeout = 0;
+			td->status &= ~TD_CTRL_IOC;
+		}
+
+		status = uhci_status_bits(td->status);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td->status);
+
+		if (status)
+			goto td_error;
+
+		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
+			usb_settoggle(urb->dev, uhci_endpoint(td->info),
+				uhci_packetout(td->info),
+				uhci_toggle(td->info) ^ 1);
+
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			} else
+				return 0;
+		}
+	}
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td->info));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+	    			uhci_packetout(td->info));
+
+err:
+	if (debug && ret != -EPIPE) {
+		/* Some debugging code */
+		dbg("uhci_result_interrupt/bulk() failed with status %x",
+			status);
+
+		/* Print the chain for debugging purposes */
+		if (urbp->qh)
+			uhci_show_urb_queue(urb);
+		else
+			uhci_show_td(td);
+	}
+
+	return ret;
+}
+
+static void uhci_reset_interrupt(struct urb *urb)
+{
+	struct list_head *tmp;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+
+	if (!urbp)
+		return;
+
+	tmp = urbp->list.next;
+	td = list_entry(tmp, struct uhci_td, list);
+	if (!td)
+		return;
+
+	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
+	td->info &= ~(1 << TD_TOKEN_TOGGLE);
+	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+	urb->status = -EINPROGRESS;
+}
+
+/*
+ * Bulk transfers
+ */
+static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
+{
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	unsigned char *data = urb->transfer_buffer;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (len < 0)
+		return -EINVAL;
+
+	/* Can't have low speed bulk transfers */
+	if (urb->pipe & TD_CTRL_LS)
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	/* 3 errors */
+	status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
+
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	do {	/* Allow zero length packets */
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			virt_to_bus(data));
+
+		data += pktsze;
 		len -= maxsze;
+
+		if (len <= 0)
+			td->status |= TD_CTRL_IOC;
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	} while (len > 0);
+
+	qh = uhci_alloc_qh(urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+
+	/* Always assume depth first */
+	uhci_insert_tds_in_qh(qh, urb, 1);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK && eurb) {
+		urbp->queued = 1;
+		uhci_append_queued_urb(uhci, eurb, urb);
+	} else
+		uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
+
+	uhci_add_urb_list(uhci, urb);
+
+	uhci_inc_fsbr(uhci, urb);
+
+	return -EINPROGRESS;
+}
+
+/* We can use the result interrupt since they're identical */
+#define uhci_result_bulk uhci_result_interrupt
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *last_urb = NULL;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct list_head *tmp, *head = &uhci->urb_list;
+	int ret = 0;
+	unsigned long flags;
+
+	nested_lock(&uhci->urblist_lock, flags);
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		/* look for pending URB's with identical pipe handle */
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+		    (u->status == -EINPROGRESS) && (u != urb)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret = 0;
+	} else
+		ret = -1;	/* no previous urb found */
+
+	nested_unlock(&uhci->urblist_lock, flags);
+
+	return ret;
+}
+
+static int isochronous_find_start(struct urb *urb)
+{
+	int limits;
+	unsigned int start = 0, end = 0;
+
+	if (urb->number_of_packets > 900)	/* 900? Why? */
+		return -EFBIG;
+
+	limits = isochronous_find_limits(urb, &start, &end);
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		if (limits) {
+			int curframe;
+
+			curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES;
+			urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
+		} else
+			urb->start_frame = end;
+	} else {
+		urb->start_frame %= UHCI_NUMFRAMES;
+		/* FIXME: Sanity check */
+	}
+
+	return 0;
+}
+
+static int uhci_submit_isochronous(struct urb *urb)
+{
+	struct uhci_td *td;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	int i, ret, framenum;
+	int status, destination;
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	ret = isochronous_find_start(urb);
+	if (ret)
+		return ret;
+
+	framenum = urb->start_frame;
+	for (i = 0; i < urb->number_of_packets; i++, framenum++) {
+		if (!urb->iso_frame_desc[i].length)
+			continue;
+
+		td = uhci_alloc_td(urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
+			virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset));
+
+		if (i + 1 >= urb->number_of_packets)
+			td->status |= TD_CTRL_IOC;
+
+		uhci_insert_td_frame_list(uhci, td, framenum);
+	}
+
+	uhci_add_urb_list(uhci, urb);
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int status;
+	int i, ret = 0;
+
+	if (!urbp)
+		return -EINVAL;
+
+	urb->actual_length = 0;
+
+	i = 0;
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+		int actlength;
+
+		tmp = tmp->next;
+
+		if (td->status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		actlength = uhci_actual_length(td->status);
+		urb->iso_frame_desc[i].actual_length = actlength;
+		urb->actual_length += actlength;
+
+		status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe));
+		urb->iso_frame_desc[i].status = status;
+		if (status != 0) {
+			urb->error_count++;
+			ret = status;
+		}
+
+		i++;
+	}
+
+	return ret;
+}
+
+static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head = &uhci->urb_list;
+	unsigned long flags;
+	struct urb *u = NULL;
+
+	if (usb_pipeisoc(urb->pipe))
+		return NULL;
+
+	nested_lock(&uhci->urblist_lock, flags);
+	tmp = head->next;
+	while (tmp != head) {
+		u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		if (u->dev == urb->dev &&
+		    u->pipe == urb->pipe)
+			goto found;
+	}
+	u = NULL;
+
+found:
+	nested_unlock(&uhci->urblist_lock, flags);
+
+	return u;
+}
+
+static int uhci_submit_urb(struct urb *urb)
+{
+	int ret = -EINVAL;
+	struct uhci *uhci;
+	unsigned long flags;
+	struct urb *u;
+	int bustime;
+
+	if (!urb)
+		return -EINVAL;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+		return -ENODEV;
+
+	uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	/* Short circuit the virtual root hub */
+	if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
+		return rh_submit_urb(urb);
+
+	u = uhci_find_urb_ep(uhci, urb);
+	if (u && !(urb->transfer_flags & USB_QUEUE_BULK))
+		return -ENXIO;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	if (!uhci_alloc_urb_priv(urb)) {
+		spin_unlock_irqrestore(&urb->lock, flags);
+		return -ENOMEM;
 	}
 
-	/*
-	 * Build the final TD for control status
-	 */
-	destination ^= (0xE1 ^ 0x69);			/* OUT -> IN */
-	destination |= 1 << 19;				/* End in Data1 */
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_submit_control(urb);
+		break;
+	case PIPE_INTERRUPT:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			bustime = usb_check_bandwidth (urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = uhci_submit_interrupt(urb);
+				if (ret == -EINPROGRESS)
+					usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+			}
+		} else {	/* bandwidth is already set */
+			ret = uhci_submit_interrupt(urb);
+		}
+		break;
+	case PIPE_BULK:
+		ret = uhci_submit_bulk(urb, u);
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			if (urb->number_of_packets <= 0) {
+				ret = -EINVAL;
+				break;
+			}
+			bustime = usb_check_bandwidth (urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+				break;
+			}
+
+			ret = uhci_submit_isochronous(urb);
+			if (ret == -EINPROGRESS)
+				usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+		} else {	/* bandwidth is already set */
+			ret = uhci_submit_isochronous(urb);
+		}
+		break;
+	}
+
+	urb->status = ret;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (ret == -EINPROGRESS)
+		ret = 0;
+	else
+		uhci_unlink_generic(urb);
+
+	return ret;
+}
+
+/*
+ * Return the result of a transfer
+ *
+ * Must be called with urblist_lock acquired
+ */
+static void uhci_transfer_result(struct urb *urb)
+{
+	struct urb *turb;
+	int proceed = 0, is_ring = 0;
+	int ret = -EINVAL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_result_control(urb);
+		break;
+	case PIPE_INTERRUPT:
+		ret = uhci_result_interrupt(urb);
+		break;
+	case PIPE_BULK:
+		ret = uhci_result_bulk(urb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = uhci_result_isochronous(urb);
+		break;
+	}
+
+	urb->status = ret;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (ret == -EINPROGRESS)
+		return;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth (urb->dev, urb, 1);
+		uhci_unlink_generic(urb);
+		break;
+	case PIPE_INTERRUPT:
+		/* Interrupts are an exception */
+		urb->complete(urb);
+		if (urb->interval)
+			uhci_reset_interrupt(urb);
+		else {
+			/* Release bandwidth for Interrupt or Isoc. transfers */
+			/* Spinlock needed ? */
+			if (urb->bandwidth)
+				usb_release_bandwidth (urb->dev, urb, 0);
+			uhci_unlink_generic(urb);
+		}
+		return;		/* <-- Note the return */
+	}
+
+	if (urb->next) {
+		turb = urb->next;
+		do {
+			if (turb->status != -EINPROGRESS) {
+				proceed = 1;
+				break;
+			}
+
+			turb = turb->next;
+		} while (turb && turb != urb && turb != urb->next);
+
+		if (turb == urb || turb == urb->next)
+			is_ring = 1;
+	}
+
+	if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
+		urb->complete(urb);
+		if (!proceed && is_ring)
+			uhci_submit_urb(urb);
+	}
+
+	if (proceed && urb->next) {
+		turb = urb->next;
+		do {
+			if (turb->status != -EINPROGRESS &&
+			    uhci_submit_urb(turb) != 0)
+
+			turb = turb->next;
+		} while (turb && turb != urb->next);
+
+		if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE))
+			urb->complete(urb);
+	}
+}
+
+static int uhci_unlink_generic(struct urb *urb)
+{
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	if (!urbp)
+		return -EINVAL;
 
-	td->link = 1;					/* Terminate */
-	td->status = status | (1 << 24);		/* IOC */
-	td->info = destination | (0x7ff << 21);		/* 0 bytes of data */
-	td->buffer = 0;
-	td->backptr = &prevtd->link;
+	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
 
-	/* Start it up.. */
-	ret = uhci_run_control(dev, first, td);
+	uhci_remove_urb_list(uhci, urb);
 
-	{
-		int maxcount = 100;
-		struct uhci_td *curtd = first;
-		unsigned int nextlink;
+	if (urbp->qh)
+		/* The interrupt loop will reclaim the QH's */
+		uhci_remove_qh(uhci, urbp->qh);
 
-		do {
-			nextlink = curtd->link;
-			uhci_remove_td(curtd);
-			uhci_td_deallocate(curtd);
-			if (nextlink & 1)	/* Tail? */
-				break;
+	if (!list_empty(&urbp->urb_queue_list))
+		uhci_delete_queued_urb(uhci, urb);
 
-			curtd = bus_to_virt(nextlink & ~0xF);
-			if (!--maxcount) {
-				printk("runaway td's!?\n");
-				break;
-			}
-		} while (1);
-	}
+	uhci_destroy_urb_priv(urb);
 
-	return ret;
+	return 0;
 }
 
-static struct usb_device *uhci_usb_allocate(struct usb_device *parent)
+static int uhci_unlink_urb(struct urb *urb)
 {
-	struct usb_device *usb_dev;
-	struct uhci_device *dev;
-	int i;
+	struct uhci *uhci;
+	int ret = 0;
+	unsigned long flags;
 
-	usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
-	if (!usb_dev)
-		return NULL;
+	if (!urb)
+		return -EINVAL;
 
-	memset(usb_dev, 0, sizeof(*usb_dev));
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
 
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev) {
-		usb_destroy_configuration(usb_dev);
-		kfree(usb_dev);
-		return NULL;
-	}
+	uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
-	/* Initialize "dev" */
-	memset(dev, 0, sizeof(*dev));
+	/* Short circuit the virtual root hub */
+	if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
+		return rh_unlink_urb(urb);
+
+	/* Release bandwidth for Interrupt or Isoc. transfers */
+	/* Spinlock needed ? */
+	if (urb->bandwidth) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth (urb->dev, urb, 0);
+			break;
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth (urb->dev, urb, 1);
+			break;
+		default:
+			break;
+		}
+	}
 
-	usb_dev->hcpriv = dev;
-	dev->usb = usb_dev;
+	if (urb->status == -EINPROGRESS) {
+		uhci_unlink_generic(urb);
 
-	usb_dev->parent = parent;
+		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+			urb->status = -ECONNABORTED;
 
-	if (parent) {
-		usb_dev->bus = parent->bus;
-		dev->uhci = usb_to_uhci(parent)->uhci;
-	}
+			spin_lock_irqsave(&uhci->urb_remove_lock, flags);
 
-	/* Reset the QH's and TD's */
-	for (i = 0; i < UHCI_MAXQH; i++) {
-		dev->qh[i].link = 1;
-		dev->qh[i].element = 1;
-		dev->qh[i].inuse = 0;
-	}
+			/* Check to see if the remove list is empty */
+			if (list_empty(&uhci->urb_remove_list))
+				uhci_set_next_interrupt(uhci);
+			
+			list_add(&urb->urb_list, &uhci->urb_remove_list);
+
+			spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
+		} else {
+			urb->status = -ENOENT;
+
+			if (in_interrupt()) {	/* wait at least 1 frame */
+				static int errorcount = 10;
+
+				if (errorcount--)
+					dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
+				udelay(1000);
+			} else
+				schedule_timeout(1+1*HZ/1000); 
 
-	for (i = 0; i < UHCI_MAXTD; i++) {
-		dev->td[i].link = 1;
-		dev->td[i].inuse = 0;
+			if (urb->complete)
+				urb->complete(urb);
+		}
 	}
 
-	return usb_dev;
+	return ret;
 }
 
-static int uhci_usb_deallocate(struct usb_device *usb_dev)
+static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb)
 {
-	struct uhci_device *dev = usb_to_uhci(usb_dev);
-	int i;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
 
-	/* There are UHCI_MAXTD preallocated tds */
-	for (i = 0; i < UHCI_MAXTD; ++i) {
-		struct uhci_td *td = dev->td + i;
+	uhci_dec_fsbr(uhci, urb);
 
-		/* And remove it from the irq list, if it's active */
-		if (td->status & (1 << 23))
-			uhci_remove_irq_list(td);
+	/* There is a race with updating IOC in here, but it's not worth */
+	/*  trying to fix since this is merely an optimization. The only */
+	/*  time we'd lose is if the status of the packet got updated */
+	/*  and we'd be turning on FSBR next frame anyway, so it's a wash */
+	urbp->fsbr_timeout = 1;
 
-		if (td->inuse)
-			uhci_remove_td(td);
-	}
+	head = &urbp->list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 
-	/* Remove the td from any queues */
-	for (i = 0; i < UHCI_MAXQH; ++i) {
-		struct uhci_qh *qh = dev->qh + i;
+		tmp = tmp->next;
 
-		if (qh->inuse)
-			uhci_remove_qh(qh->skel, qh);
+		if (td->status & TD_CTRL_ACTIVE) {
+			td->status |= TD_CTRL_IOC;
+			break;
+		}
 	}
 
-	kfree(dev);
-	usb_destroy_configuration(usb_dev);
-	kfree(usb_dev);
-
 	return 0;
 }
 
-struct usb_operations uhci_device_operations = {
-	uhci_usb_allocate,
-	uhci_usb_deallocate,
-	uhci_control_msg,
-	uhci_request_irq,
-};
-
 /*
- * This is just incredibly fragile. The timings must be just
- * right, and they aren't really documented very well.
+ * uhci_get_current_frame_number()
  *
- * Note the short delay between disabling reset and enabling
- * the port..
+ * returns the current frame number for a USB bus/controller.
  */
-static void uhci_reset_port(unsigned int port)
+static int uhci_get_current_frame_number(struct usb_device *dev)
 {
-	unsigned short status;
+	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
 
-	status = inw(port);
-	outw(status | USBPORTSC_PR, port);		/* reset port */
-	wait_ms(10);
-	outw(status & ~USBPORTSC_PR, port);
-	udelay(5);
+	return inw(uhci->io_addr + USBFRNUM);
+}
 
-	status = inw(port);
-	outw(status | USBPORTSC_PE, port);		/* enable port */
-	wait_ms(10);
+struct usb_operations uhci_device_operations = {
+	uhci_alloc_dev,
+	uhci_free_dev,
+	uhci_get_current_frame_number,
+	uhci_submit_urb,
+	uhci_unlink_urb
+};
 
-	status = inw(port);
-	if(!(status & USBPORTSC_PE)) {
-		outw(status | USBPORTSC_PE, port);	/* one more try at enabling port */
-		wait_ms(50);
-	}
+/* -------------------------------------------------------------------
+   Virtual Root Hub
+   ------------------------------------------------------------------- */
+
+static __u8 root_hub_dev_des[] =
+{
+ 	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.0 */
+	0x01,
+	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  bDeviceSubClass; */
+	0x00,			/*  __u8  bDeviceProtocol; */
+	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,			/*  __u16 idVendor; */
+	0x00,
+	0x00,			/*  __u16 idProduct; */
+	0x00,
+	0x00,			/*  __u16 bcdDevice; */
+	0x00,
+	0x00,			/*  __u8  iManufacturer; */
+	0x02,			/*  __u8  iProduct; */
+	0x01,			/*  __u8  iSerialNumber; */
+	0x01			/*  __u8  bNumConfigurations; */
+};
 
-}
 
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x02,			/*  __u8  bDescriptorType; Configuration */
+	0x19,			/*  __u16 wTotalLength; */
+	0x00,
+	0x01,			/*  __u8  bNumInterfaces; */
+	0x01,			/*  __u8  bConfigurationValue; */
+	0x00,			/*  __u8  iConfiguration; */
+	0x40,			/*  __u8  bmAttributes;
+					Bit 7: Bus-powered, 6: Self-powered,
+					Bit 5 Remote-wakeup, 4..0: resvd */
+	0x00,			/*  __u8  MaxPower; */
+
+	/* interface */
+	0x09,			/*  __u8  if_bLength; */
+	0x04,			/*  __u8  if_bDescriptorType; Interface */
+	0x00,			/*  __u8  if_bInterfaceNumber; */
+	0x00,			/*  __u8  if_bAlternateSetting; */
+	0x01,			/*  __u8  if_bNumEndpoints; */
+	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  if_bInterfaceSubClass; */
+	0x00,			/*  __u8  if_bInterfaceProtocol; */
+	0x00,			/*  __u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,			/*  __u8  ep_bLength; */
+	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
+	0x08,			/*  __u16 ep_wMaxPacketSize; 8 Bytes */
+	0x00,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
 
-/*
- * This gets called if the connect status on the root
- * hub (and the root hub only) changes.
- */
-static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr)
+static __u8 root_hub_hub_des[] =
 {
-	struct usb_device *usb_dev;
-	struct uhci_device *dev;
-	unsigned short status;
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
 
-	printk("uhci_connect_change: called for %d\n", nr);
+/*-------------------------------------------------------------------------*/
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+static int rh_send_irq(struct urb *urb)
+{
+	int i, len = 1;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 data = 0;
 
-	/*
-	 * Even if the status says we're connected,
-	 * the fact that the status bits changed may
-	 * that we got disconnected and then reconnected.
-	 *
-	 * So start off by getting rid of any old devices..
-	 */
-	usb_disconnect(&uhci->root_hub->usb->children[nr]);
+	for (i = 0; i < uhci->rh.numports; i++) {
+		data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
 
-	status = inw(port);
+	*(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
+	urb->actual_length = len;
+	urb->status = USB_ST_NOERROR;
 
-	/* If we have nothing connected, then clear change status and disable the port */
-	status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC;
-	if (!(status & USBPORTSC_CCS)) {
-		outw(status, port);
-		return;
+	if ((data > 0) && (uhci->rh.send != 0)) {
+		dbg("root-hub INT complete: port1: %x port2: %x data: %x",
+			inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
+		urb->complete(urb);
 	}
 
-	/*
-	 * Ok, we got a new connection. Allocate a device to it,
-	 * and find out what it wants to do..
-	 */
-	usb_dev = uhci_usb_allocate(uhci->root_hub->usb);
-	dev = usb_dev->hcpriv;
+	return USB_ST_NOERROR;
+}
 
-	dev->uhci = uhci;
+/*-------------------------------------------------------------------------*/
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+static int rh_init_int_timer(struct urb *urb);
 
-	usb_connect(usb_dev);
+static void rh_int_timer_do(unsigned long ptr)
+{
+	struct urb *urb = (struct urb *)ptr;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct list_head *tmp, *head = &uhci->urb_list;
+	struct urb_priv *urbp;
+	int len;
+	unsigned long flags;
+
+	if (uhci->rh.send) {
+		len = rh_send_irq(urb);
+		if (len > 0) {
+			urb->actual_length = len;
+			if (urb->complete)
+				urb->complete(urb);
+		}
+	}
 
-	uhci->root_hub->usb->children[nr] = usb_dev;
+	nested_lock(&uhci->urblist_lock, flags);
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, urb_t, urb_list);
 
-	wait_ms(200); /* wait for powerup */
-	uhci_reset_port(port);
+		tmp = tmp->next;
 
-	/* Get speed information */
-	usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0;
+		urbp = (struct urb_priv *)u->hcpriv;
+		if (urbp) {
+			/* Check if the FSBR timed out */
+			if (urbp->fsbr && time_after(urbp->inserttime + IDLE_TIMEOUT, jiffies))
+				uhci_fsbr_timeout(uhci, u);
+
+			/* Check if the URB timed out */
+			if (u->timeout && time_after(u->timeout, jiffies)) {
+				u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+				uhci_unlink_urb(u);
+			}
+		}
+	}
+	nested_unlock(&uhci->urblist_lock, flags);
 
-	/*
-	 * Ok, all the stuff specific to the root hub has been done.
-	 * The rest is generic for any new USB attach, regardless of
-	 * hub type.
-	 */
-	usb_new_device(usb_dev);
+	rh_init_int_timer(urb);
 }
 
-/*
- * This gets called when the root hub configuration
- * has changed. Just go through each port, seeing if
- * there is something interesting happening.
- */
-static void uhci_check_configuration(struct uhci *uhci)
+/*-------------------------------------------------------------------------*/
+/* Root Hub INTs are polled by this timer */
+static int rh_init_int_timer(struct urb *urb)
 {
-	unsigned int io_addr = uhci->io_addr + USBPORTSC1;
-	int maxchild = uhci->root_hub->usb->maxchild;
-	int nr = 0;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
 
-	do {
-		unsigned short status = inw(io_addr);
+	uhci->rh.interval = urb->interval;
+	init_timer(&uhci->rh.rh_int_timer);
+	uhci->rh.rh_int_timer.function = rh_int_timer_do;
+	uhci->rh.rh_int_timer.data = (unsigned long)urb;
+	uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+	add_timer(&uhci->rh.rh_int_timer);
+
+	return 0;
+}
 
-		if (status & USBPORTSC_CSC)
-			uhci_connect_change(uhci, io_addr, nr);
+/*-------------------------------------------------------------------------*/
+#define OK(x)			len = (x); break
 
-		nr++; io_addr += 2;
-	} while (nr < maxchild);
+#define CLR_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) & ~(x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) | (x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+
+/*-------------------------------------------------------------------------*/
+/*************************
+ ** Root Hub Control Pipe
+ *************************/
+
+static int rh_submit_urb(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	devrequest *cmd = (devrequest *)urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = 0;
+	int stat = USB_ST_NOERROR;
+	int i;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 cstatus;
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
+		uhci->rh.urb = urb;
+		uhci->rh.send = 1;
+		uhci->rh.interval = urb->interval;
+		rh_init_int_timer(urb);
+
+		return USB_ST_NOERROR;
+	}
+
+	bmRType_bReq = cmd->requesttype | cmd->request << 8;
+	wValue = le16_to_cpu(cmd->value);
+	wIndex = le16_to_cpu(cmd->index);
+	wLength = le16_to_cpu(cmd->length);
+
+	for (i = 0; i < 8; i++)
+		uhci->rh.c_p_r[i] = 0;
+
+	switch (bmRType_bReq) {
+		/* Request Destination:
+		   without flags: Device,
+		   RH_INTERFACE: interface,
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here,
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
+		*/
+
+	case RH_GET_STATUS:
+		*(__u16 *)data = cpu_to_le16(1);
+		OK(2);
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *)data = cpu_to_le32(0);
+		OK(4);		/* hub power */
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1)) |
+			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
+			status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |      /* power on */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *)data = cpu_to_le16(status);
+		*(__u16 *)(data + 2) = cpu_to_le16(cstatus);
+		OK(4);
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case RH_ENDPOINT_STALL:
+			OK(0);
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case RH_C_HUB_OVER_CURRENT:
+			OK(0);	/* hub power over current */
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_ENABLE:
+			CLR_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		case RH_PORT_SUSPEND:
+			CLR_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case RH_PORT_POWER:
+			OK(0);	/* port power */
+		case RH_C_PORT_CONNECTION:
+			SET_RH_PORTSTAT(USBPORTSC_CSC);
+			OK(0);
+		case RH_C_PORT_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PEC);
+			OK(0);
+		case RH_C_PORT_SUSPEND:
+			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK(0);
+		case RH_C_PORT_OVER_CURRENT:
+			OK(0);	/* port power over current */
+		case RH_C_PORT_RESET:
+			uhci->rh.c_p_r[wIndex - 1] = 0;
+			OK(0);
+		}
+		break;
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_SUSPEND:
+			SET_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case RH_PORT_RESET:
+			SET_RH_PORTSTAT(USBPORTSC_PR);
+			wait_ms(50);	/* USB v1.1 7.1.7.3 */
+			uhci->rh.c_p_r[wIndex - 1] = 1;
+			CLR_RH_PORTSTAT(USBPORTSC_PR);
+			udelay(10);
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			wait_ms(10);
+			SET_RH_PORTSTAT(0xa);
+			OK(0);
+		case RH_PORT_POWER:
+			OK(0); /* port power ** */
+		case RH_PORT_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		}
+		break;
+	case RH_SET_ADDRESS:
+		uhci->rh.devnum = wValue;
+		OK(0);
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case 0x01:	/* device descriptor */
+			len = min(leni, min(sizeof(root_hub_dev_des), wLength));
+			memcpy(data, root_hub_dev_des, len);
+			OK(len);
+		case 0x02:	/* configuration descriptor */
+			len = min(leni, min(sizeof(root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK(len);
+		case 0x03:	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+				uhci->io_addr, "UHCI-alt",
+				data, wLength);
+			if (len > 0) {
+				OK (min (leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = uhci->rh.numports;
+		len = min(leni, min(sizeof(root_hub_hub_des), wLength));
+		memcpy(data, root_hub_hub_des, len);
+		OK(len);
+	case RH_GET_CONFIGURATION:
+		*(__u8 *)data = 0x01;
+		OK(1);
+	case RH_SET_CONFIGURATION:
+		OK(0);
+	case RH_GET_INTERFACE | RH_INTERFACE:
+		*(__u8 *)data = 0x00;
+		OK(1);
+	case RH_SET_INTERFACE | RH_INTERFACE:
+		OK(0);
+	default:
+		stat = -EPIPE;
+	}
+
+	urb->actual_length = len;
+	urb->status = stat;
+	if (urb->complete)
+		urb->complete(urb);
+
+	return USB_ST_NOERROR;
+}
+/*-------------------------------------------------------------------------*/
+
+static int rh_unlink_urb(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	if (uhci->rh.urb == urb) {
+		uhci->rh.send = 0;
+		del_timer(&uhci->rh.rh_int_timer);
+	}
+	return 0;
 }
+/*-------------------------------------------------------------------*/
 
-static void uhci_interrupt_notify(struct uhci *uhci)
+void uhci_free_pending_qhs(struct uhci *uhci)
 {
-	struct list_head *head = &uhci->interrupt_list;
-	struct list_head *tmp;
+	struct list_head *tmp, *head;
+	unsigned long flags;
 
-	spin_lock(&irqlist_lock);
+	/* Free any pending QH's */
+	spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+	head = &uhci->qh_remove_list;
 	tmp = head->next;
 	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);
-		struct list_head *next;
+		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
 
-		next = tmp->next;
+		tmp = tmp->next;
 
-		if (!(td->status & (1 << 23))) {	/* No longer active? */
-			/* remove from IRQ list */
-			__list_del(tmp->prev, next);
-			INIT_LIST_HEAD(tmp);
-			if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {
-				struct uhci_qh *interrupt_qh = td->qh;
-
-				list_add(&td->irq_list, &uhci->interrupt_list);
-				td->info ^= 1 << 19; /* toggle between data0 and data1 */
-				td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24);	/* active */
-
-				/* Remove then readd? Is that necessary */
-				uhci_remove_td(td);
-				uhci_insert_td_in_qh(interrupt_qh, td);
-			}
-			/* If completed wants to not reactivate, then it's */
-			/* responsible for free'ing the TD's and QH's */
-			/* or another function (such as run_control) */
-		}
-		tmp = next;
-	}
-	spin_unlock(&irqlist_lock);
-}
+		list_del(&qh->remove_list);
 
-/*
- * Check port status - Connect Status Change - for
- * each of the attached ports (defaults to two ports,
- * but at least in theory there can be more of them).
- *
- * Wake up the configurator if something happened, we
- * can't really do much at interrupt time.
- */
-static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr)
-{
-	if (waitqueue_active(&uhci_configure)) {
-		int ports = uhci->root_hub->usb->maxchild;
-		io_addr += USBPORTSC1;
-		do {
-			if (inw(io_addr) & USBPORTSC_CSC) {
-				wake_up(&uhci_configure);
-				return;
-			}
-			io_addr += 2;
-		} while (--ports > 0);
+		uhci_free_qh(qh);
 	}
+	spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
 }
 
 static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
@@ -757,39 +2019,63 @@
 	struct uhci *uhci = __uhci;
 	unsigned int io_addr = uhci->io_addr;
 	unsigned short status;
+	unsigned long flags;
+	struct list_head *tmp, *head;
 
 	/*
-	 * Read the interrupt status, and write it back to clear the interrupt cause
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
 	 */
 	status = inw(io_addr + USBSTS);
+	if (!status)	/* shared interrupt, not mine */
+		return;
 	outw(status, io_addr + USBSTS);
 
-	/* Walk the list of pending TD's to see which ones completed.. */
-	uhci_interrupt_notify(uhci);
+	if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) {
+		if (status & USBSTS_RD)
+			printk(KERN_INFO "uhci: resume detected, not implemented\n");
+		if (status & USBSTS_HSE)
+			printk(KERN_ERR "uhci: host system error, PCI problems?\n");
+		if (status & USBSTS_HCPE)
+			printk(KERN_ERR "uhci: host controller process error. something bad happened\n");
+		if (status & USBSTS_HCH) {
+			printk(KERN_ERR "uhci: host controller halted. very bad\n");
+			/* FIXME: Reset the controller, fix the offending TD */
+		}
+	}
 
-	/* Check if there are any events on the root hub.. */
-	uhci_root_hub_events(uhci, io_addr);
-}
+	uhci_free_pending_qhs(uhci);
 
-/*
- * We init one packet, and mark it just IOC and _not_
- * active. Which will result in no actual USB traffic,
- * but _will_ result in an interrupt every second.
- *
- * Which is exactly what we want.
- */
-static void uhci_init_ticktd(struct uhci *uhci)
-{
-	struct uhci_device *dev = uhci->root_hub;
-	struct uhci_td *td = uhci_td_allocate(dev);
+	spin_lock(&uhci->urb_remove_lock);
+	head = &uhci->urb_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
 
-	td->link = 1;
-	td->status = (1 << 24);					/* interrupt on completion */
-	td->info = (15 << 21) | 0x7f69;				/* (ignored) input packet, 16 bytes, device 127 */
-	td->buffer = 0;
-	td->qh = NULL;
+		tmp = tmp->next;
+
+		list_del(&urb->urb_list);
+
+		if (urb->complete)
+			urb->complete(urb);
+	}
+	spin_unlock(&uhci->urb_remove_lock);
+
+	uhci_clear_next_interrupt(uhci);
+
+	/* Walk the list of pending TD's to see which ones completed */
+	nested_lock(&uhci->urblist_lock, flags);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
 
-	uhci->fl->frame[0] = virt_to_bus(td);
+		tmp = tmp->next;
+
+		/* Checks the status and does all of the magic necessary */
+		uhci_transfer_result(urb);
+	}
+	nested_unlock(&uhci->urblist_lock, flags);
 }
 
 static void reset_hc(struct uhci *uhci)
@@ -797,9 +2083,9 @@
 	unsigned int io_addr = uhci->io_addr;
 
 	/* Global reset for 50ms */
-	outw(USBCMD_GRESET, io_addr+USBCMD);
+	outw(USBCMD_GRESET, io_addr + USBCMD);
 	wait_ms(50);
-	outw(0, io_addr+USBCMD);
+	outw(0, io_addr + USBCMD);
 	wait_ms(10);
 }
 
@@ -808,8 +2094,6 @@
 	unsigned int io_addr = uhci->io_addr;
 	int timeout = 1000;
 
-	uhci_init_ticktd(uhci);
-
 	/*
 	 * Reset the HC - this will force us to get a
 	 * new notification of any already connected
@@ -819,21 +2103,25 @@
 	outw(USBCMD_HCRESET, io_addr + USBCMD);
 	while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
 		if (!--timeout) {
-			printk("USBCMD_HCRESET timed out!\n");
+			printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
 			break;
 		}
 	}
 
-	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+	/* Turn on all interrupts */
+	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+		io_addr + USBINTR);
+
+	/* Start at frame 0 */
 	outw(0, io_addr + USBFRNUM);
 	outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
 
 	/* Run and mark it configured with a 64-byte max packet */
-	outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD);
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 }
 
 /*
- * Allocate a frame list, and four regular queues.
+ * Allocate a frame list, and then setup the skeleton
  *
  * The hardware doesn't really know any difference
  * in the queues, but the order does matter for the
@@ -843,20 +2131,14 @@
  *    of the queues. We don't do that here, because
  *    we'll create the actual TD entries on demand.
  *  - The first queue is the "interrupt queue".
- *  - The second queue is the "control queue".
+ *  - The second queue is the "control queue", split into low and high speed
  *  - The third queue is "bulk data".
- *
- * We could certainly have multiple queues of the same
- * type, and maybe we should. We could have per-device
- * queues, for example. We begin small.
  */
-static struct uhci *alloc_uhci(unsigned int io_addr)
+static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
 {
-	int i;
+	int i, port;
 	struct uhci *uhci;
 	struct usb_bus *bus;
-	struct uhci_device *dev;
-	struct usb_device *usb;
 
 	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
 	if (!uhci)
@@ -866,69 +2148,87 @@
 
 	uhci->irq = -1;
 	uhci->io_addr = io_addr;
-	INIT_LIST_HEAD(&uhci->interrupt_list);
+	uhci->io_size = io_size;
+
+	spin_lock_init(&uhci->qh_remove_lock);
+	INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+	spin_lock_init(&uhci->urb_remove_lock);
+	INIT_LIST_HEAD(&uhci->urb_remove_list);
+
+	nested_init(&uhci->urblist_lock);
+	INIT_LIST_HEAD(&uhci->urb_list);
+
+	spin_lock_init(&uhci->framelist_lock);
 
 	/* We need exactly one page (per UHCI specs), how convenient */
+	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
 	uhci->fl = (void *)__get_free_page(GFP_KERNEL);
+	if (!uhci->fl)
+		goto au_free_uhci;
 
-	bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+	bus = usb_alloc_bus(&uhci_device_operations);
 	if (!bus)
-		return NULL;
-
-	memset(bus, 0, sizeof(*bus));
+		goto au_free_fl;
 
 	uhci->bus = bus;
 	bus->hcpriv = uhci;
-	bus->op = &uhci_device_operations;
 
-	/*
-	 * We allocate a 8kB area for the UHCI hub. The area
-	 * is described by the uhci_device structure, and basically
-	 * contains everything needed for normal operation.
-	 *
-	 * The first page is the actual device descriptor for the
-	 * hub.
-	 *
-	 * The second page is used for the frame list.
-	 */
-	usb = uhci_usb_allocate(NULL);
-	if (!usb)
-		return NULL;
+	/* Initialize the root hub */
 
-	dev = uhci->root_hub = usb_to_uhci(usb);
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/*  they may have more but give no way to determine how many they */
+	/*  have. However, according to the UHCI spec, Bit 7 is always set */
+	/*  to 1. So we try to use this to our advantage */
+	for (port = 0; port < (io_size - 0x10) / 2; port++) {
+		unsigned int portstatus;
 
-	usb->bus = bus;
+		portstatus = inw(io_addr + 0x10 + (port * 2));
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	if (debug)
+		info("detected %d ports", port);
 
-	/* Initialize the root hub */
-	/* UHCI specs says devices must have 2 ports, but goes on to say */
-	/* they may have more but give no way to determine how many they */
-	/* have, so default to 2 */
-	usb->maxchild = 2;
-	usb_init_root_hub(usb);
+	/* This is experimental so anything less than 2 or greater than 8 is */
+	/*  something weird and we'll ignore it */
+	if (port < 2 || port > 8) {
+		info("port count misdetected? forcing to 2 ports");
+		port = 2;
+	}
+
+	uhci->rh.numports = port;
 
 	/*
-	 * Initialize the queues. They all start out empty,
-	 * linked to each other in the proper order.
+	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
+	 * then link int1 to control and control to bulk
 	 */
-	for (i = 1 ; i < 9; i++) {
-		dev->qh[i].link = 2 | virt_to_bus(&dev->skel_control_qh);
-		dev->qh[i].element = 1;
+	for (i = 1; i < 9; i++) {
+		struct uhci_td *td = &uhci->skeltd[i];
+
+		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+		td->link = virt_to_bus(&uhci->skeltd[i - 1]);
 	}
-	
-	dev->skel_control_qh.link = 2 | virt_to_bus(&dev->skel_bulk0_qh);
-	dev->skel_control_qh.element = 1;
 
-	dev->skel_bulk0_qh.link = 2 | virt_to_bus(&dev->skel_bulk1_qh);
-	dev->skel_bulk0_qh.element = 1;
 
-	dev->skel_bulk1_qh.link = 2 | virt_to_bus(&dev->skel_bulk2_qh);
-	dev->skel_bulk1_qh.element = 1;
+	uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+	uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_ls_control_qh) | UHCI_PTR_QH;
+
+	uhci->skel_ls_control_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+	uhci->skel_ls_control_qh.element = UHCI_PTR_TERM;
+
+	uhci->skel_hs_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH;
+	uhci->skel_hs_control_qh.element = UHCI_PTR_TERM;
+
+	uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH;
+	uhci->skel_bulk_qh.element = UHCI_PTR_TERM;
 
-	dev->skel_bulk2_qh.link = 2 | virt_to_bus(&dev->skel_bulk3_qh);
-	dev->skel_bulk2_qh.element = 1;
+	/* This dummy TD is to work around a bug in Intel PIIX controllers */
+	uhci_fill_td(&uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+	uhci->skel_term_td.link = UHCI_PTR_TERM;
 
-	dev->skel_bulk3_qh.link = 1;
-	dev->skel_bulk3_qh.element = 1;
+	uhci->skel_term_qh.link = UHCI_PTR_TERM;
+	uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td);
 
 	/*
 	 * Fill the frame list: make all entries point to
@@ -939,7 +2239,8 @@
 	 * us a reasonable dynamic range for irq latencies.
 	 */
 	for (i = 0; i < 1024; i++) {
-		struct uhci_qh * irq = &dev->skel_int2_qh;
+		struct uhci_td *irq = &uhci->skel_int1_td;
+
 		if (i & 1) {
 			irq++;
 			if (i & 2) {
@@ -952,21 +2253,31 @@
 							irq++;
 							if (i & 32) {
 								irq++;
-								if (i & 64) {
+								if (i & 64)
 									irq++;
-								}
 							}
 						}
 					}
 				}
 			}
 		}
-		uhci->fl->frame[i] =  2 | virt_to_bus(irq);
+
+		/* Only place we don't use the frame list routines */
+		uhci->fl->frame[i] =  virt_to_bus(irq);
 	}
 
 	return uhci;
-}
 
+/*
+ * error exits:
+ */
+au_free_fl:
+	free_page((unsigned long)uhci->fl);
+au_free_uhci:
+	kfree(uhci);
+
+	return NULL;
+}
 
 /*
  * De-allocate all resources..
@@ -978,236 +2289,258 @@
 		uhci->irq = -1;
 	}
 
-#if 0
-	if (uhci->root_hub) {
-		uhci_usb_deallocate(uhci_to_usb(uhci->root_hub));
-		uhci->root_hub = NULL;
-	}
-#endif
-
 	if (uhci->fl) {
 		free_page((unsigned long)uhci->fl);
 		uhci->fl = NULL;
 	}
 
-	kfree(uhci->bus);
+	usb_free_bus(uhci->bus);
 	kfree(uhci);
 }
 
-void cleanup_drivers(void);
-
-static int uhci_control_thread(void * __uhci)
+int uhci_start_root_hub(struct uhci *uhci)
 {
-	struct uhci *uhci = (struct uhci *)__uhci;
+	struct usb_device *dev;
 
-	lock_kernel();
-	request_region(uhci->io_addr, 32, "usb-uhci");
+	dev = usb_alloc_dev(NULL, uhci->bus);
+	if (!dev)
+		return -1;
 
-	/*
-	 * This thread doesn't need any user-level access,
-	 * so get rid of all our resources..
-	 */
-	printk("uhci_control_thread at %p\n", &uhci_control_thread);
-	exit_mm(current);
-	exit_files(current);
-	exit_fs(current);
+	uhci->bus->root_hub = dev;
+	usb_connect(dev);
 
-	strcpy(current->comm, "uhci-control");
-
-	/*
-	 * Ok, all systems are go..
-	 */
-	start_hc(uhci);
-	for(;;) {
-		siginfo_t info;
-		int unsigned long signr;
-
-		interruptible_sleep_on(&uhci_configure);
-#ifdef CONFIG_APM
-		if (apm_resume) {
-			apm_resume = 0;
-			start_hc(uhci);
-			continue;
-		}
-#endif
-		uhci_check_configuration(uhci);
+	if (usb_new_device(dev) != 0) {
+		usb_free_dev(dev);
 
-		if(signal_pending(current)) {
-			/* sending SIGUSR1 makes us print out some info */
-			spin_lock_irq(&current->sigmask_lock);
-			signr = dequeue_signal(&current->blocked, &info);
-			spin_unlock_irq(&current->sigmask_lock);
-
-			if(signr == SIGUSR1) {
-				printk("UHCI queue dump:\n");
-				show_queues(uhci);
-			} else {
-				break;
-			}
-		}
+		return -1;
 	}
 
-#if 0
-	if(uhci->root_hub)
-		for(i = 0; i < uhci->root_hub->usb->maxchild; i++)
-			usb_disconnect(uhci->root_hub->usb->children + i);
-#endif
-
-	cleanup_drivers();
-
-	reset_hc(uhci);
-	release_region(uhci->io_addr, 32);
-
-	release_uhci(uhci);
-	MOD_DEC_USE_COUNT;
-
-	printk("uhci_control_thread exiting\n");
-
 	return 0;
-}	
+}
 
 /*
  * If we've successfully found a UHCI, now is the time to increment the
- * module usage count, start the control thread, and return success..
+ * module usage count, and return success..
  */
-static int found_uhci(int irq, unsigned int io_addr)
+static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
 {
 	int retval;
 	struct uhci *uhci;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
 
-	uhci = alloc_uhci(io_addr);
+	uhci = alloc_uhci(io_addr, io_size);
 	if (!uhci)
 		return -ENOMEM;
 
+	INIT_LIST_HEAD(&uhci->uhci_list);
+	list_add(&uhci->uhci_list, &uhci_list);
+
+	request_region(uhci->io_addr, io_size, "usb-uhci");
+
 	reset_hc(uhci);
 
-	retval = -EBUSY;
-	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) {
-		int pid;
+	usb_register_bus(uhci->bus);
+	start_hc(uhci);
 
-		MOD_INC_USE_COUNT;
+	retval = -EBUSY;
+	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) {
 		uhci->irq = irq;
-		pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-		if (pid >= 0)
-			return 0;
 
-		MOD_DEC_USE_COUNT;
-		retval = pid;
+		if (!uhci_start_root_hub(uhci)) {
+			struct pm_dev *pmdev;
+
+			pmdev = pm_register(PM_PCI_DEV,
+					    PM_PCI_ID(dev),
+					    handle_pm_event);
+			if (pmdev)
+				pmdev->data = uhci;
+			return 0;
+		}
 	}
+
+	/* Couldn't allocate IRQ if we got here */
+	list_del(&uhci->uhci_list);
+	INIT_LIST_HEAD(&uhci->uhci_list);
+
+	reset_hc(uhci);
+	release_region(uhci->io_addr, uhci->io_size);
 	release_uhci(uhci);
+
 	return retval;
 }
 
-static int start_uhci(struct pci_dev *dev)
+static int found_uhci(struct pci_dev *dev)
 {
 	int i;
 
+	/* disable legacy emulation */
+	pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+	if (pci_enable_device(dev) < 0)
+		return -1;
+
+	if (!dev->irq) {
+		err("found UHCI device with no IRQ assigned. check BIOS settings!");
+		return -1;
+	}
+
 	/* Search for the IO base address.. */
 	for (i = 0; i < 6; i++) {
-		unsigned int io_addr = dev->base_address[i];
+		unsigned int io_addr = pci_resource_start(dev, i);
+		unsigned int io_size = pci_resource_len(dev, i);
 
 		/* IO address? */
-		if (!(io_addr & 1))
+		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
 			continue;
 
-		io_addr &= PCI_BASE_ADDRESS_IO_MASK;
-
 		/* Is it already in use? */
-		if (check_region(io_addr, 32))
+		if (check_region(io_addr, io_size))
 			break;
 
-		return found_uhci(dev->irq, io_addr);
+		return setup_uhci(dev, dev->irq, io_addr, io_size);
 	}
+
 	return -1;
 }
 
-#ifdef CONFIG_APM
-static int handle_apm_event(apm_event_t event)
+static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data)
 {
-	static int down = 0;
-
-	switch (event) {
-	case APM_SYS_SUSPEND:
-	case APM_USER_SUSPEND:
-		if (down) {
-			printk(KERN_DEBUG "uhci: received extra suspend event\n");
-			break;
-		}
-		down = 1;
+	struct uhci *uhci = dev->data;
+	switch (rqst) {
+	case PM_SUSPEND:
+		reset_hc(uhci);
 		break;
-	case APM_NORMAL_RESUME:
-	case APM_CRITICAL_RESUME:
-		if (!down) {
-			printk(KERN_DEBUG "uhci: received bogus resume event\n");
-			break;
-		}
-		down = 0;
-		if (waitqueue_active(&uhci_configure)) {
-			apm_resume = 1;
-			wake_up(&uhci_configure);
-		}
+	case PM_RESUME:
+		reset_hc(uhci);
+		start_hc(uhci);
 		break;
 	}
 	return 0;
 }
-#endif
-
-#ifdef MODULE
 
-void cleanup_module(void)
+static int __init uhci_init(void)
 {
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-}
+	int retval;
+	struct pci_dev *dev;
+	u8 type;
 
-#define uhci_init init_module
+	retval = -ENOMEM;
 
-#endif
+	/* We throw all of the TD's and QH's into a kmem cache */
+	/* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */
+	/*  does this for us */
+	uhci_td_cachep = kmem_cache_create("uhci_td",
+		sizeof(struct uhci_td), 0,
+		SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+	if (!uhci_td_cachep)
+		goto td_failed;
+
+	uhci_qh_cachep = kmem_cache_create("uhci_qh",
+		sizeof(struct uhci_qh), 0,
+		SLAB_HWCACHE_ALIGN, NULL, NULL);
 
-int uhci_init(void)
-{
-	int retval;
-	struct pci_dev *dev = NULL;
-	u8 type;
+	if (!uhci_qh_cachep)
+		goto qh_failed;
+
+	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+		sizeof(struct urb_priv), 0, 0, NULL, NULL);
+
+	if (!uhci_up_cachep)
+		goto up_failed;
 
 	retval = -ENODEV;
+	dev = NULL;
 	for (;;) {
-		dev = pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev);
+		dev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, dev);
 		if (!dev)
 			break;
-		/* Is it UHCI */
+
+		/* Is it the UHCI programming interface? */
 		pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
-		if(type != 0)
+		if (type != 0)
 			continue;
+
 		/* Ok set it up */
-		retval = start_uhci(dev);
-		if (retval < 0)
-			continue;
+		retval = found_uhci(dev);
+	}
 
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
-#ifdef CONFIG_APM
-		apm_register_callback(&handle_apm_event);
-#endif
+	/* We only want to return an error code if ther was an error */
+	/*  and we didn't find a UHCI controller */
+	if (retval && list_empty(&uhci_list))
+		goto init_failed;
 
-		return 0;
-	}
+	return 0;
+
+init_failed:
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+up_failed:
+	if (kmem_cache_destroy(uhci_qh_cachep))
+		printk(KERN_INFO "uhci: not all QH's were freed\n");
+
+qh_failed:
+	if (kmem_cache_destroy(uhci_td_cachep))
+		printk(KERN_INFO "uhci: not all TD's were freed\n");
+
+td_failed:
 	return retval;
 }
 
-void cleanup_drivers(void)
+void uhci_cleanup(void)
 {
-	hub_cleanup();
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_cleanup();
-#endif
+	struct list_head *tmp, *head = &uhci_list;
+
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list);
+
+		tmp = tmp->next;
+
+		list_del(&uhci->uhci_list);
+		INIT_LIST_HEAD(&uhci->uhci_list);
+
+		if (uhci->bus->root_hub)
+			usb_disconnect(&uhci->bus->root_hub);
+
+		usb_deregister_bus(uhci->bus);
+
+		reset_hc(uhci);
+		release_region(uhci->io_addr, uhci->io_size);
+
+		uhci_free_pending_qhs(uhci);
+
+		release_uhci(uhci);
+	}
+
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+	if (kmem_cache_destroy(uhci_qh_cachep))
+		printk(KERN_INFO "uhci: not all QH's were freed\n");
+
+	if (kmem_cache_destroy(uhci_td_cachep))
+		printk(KERN_INFO "uhci: not all TD's were freed\n");
+}
+
+static void __exit uhci_exit(void)
+{
+	pm_unregister_all(handle_pm_event);
+	uhci_cleanup();
 }
+
+module_init(uhci_init);
+module_exit(uhci_exit);
+
+MODULE_AUTHOR("Linus Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber");
+MODULE_DESCRIPTION("USB Universal Host Controller Interface driver");
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/uhci.h linux.18p17/drivers/usb/uhci.h
--- linux.vanilla/drivers/usb/uhci.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/uhci.h	Thu Oct 19 01:49:59 2000
@@ -2,8 +2,37 @@
 #define __LINUX_UHCI_H
 
 #include <linux/list.h>
+#include <linux/usb.h>
 
-#include "usb.h"
+/*
+ * This nested spinlock code is courtesy of Davide Libenzi <dlibenzi@maticad.it>
+ */
+struct s_nested_lock {
+	spinlock_t lock;
+	void *uniq;
+	short int count;
+};
+
+#define nested_init(snl) \
+	spin_lock_init(&(snl)->lock); \
+	(snl)->uniq = NULL; \
+	(snl)->count = 0;
+
+#define nested_lock(snl, flags) \
+	if ((snl)->uniq == current) { \
+		(snl)->count++; \
+		flags = 0; /* No warnings */ \
+	} else { \
+		spin_lock_irqsave(&(snl)->lock, flags); \
+		(snl)->count++; \
+		(snl)->uniq = current; \
+	}
+
+#define nested_unlock(snl, flags) \
+	if (!--(snl)->count) { \
+		(snl)->uniq = NULL; \
+		spin_unlock_irqrestore(&(snl)->lock, flags); \
+	}
 
 /*
  * Universal Host Controller Interface data structures and defines
@@ -53,17 +82,83 @@
 #define   USBPORTSC_PR		0x0200	/* Port Reset */
 #define   USBPORTSC_SUSP	0x1000	/* Suspend */
 
-struct uhci_qh {
-	unsigned int link;	/* Next queue */
-	unsigned int element;	/* Queue element pointer */
-	int inuse;		/* Inuse? */
-	struct uhci_qh *skel;	/* Skeleton head */
-} __attribute__((aligned(16)));
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT	0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7FF	/* for UHCI controller TD */
+
+#define UHCI_PTR_BITS		0x000F
+#define UHCI_PTR_TERM		0x0001
+#define UHCI_PTR_QH		0x0002
+#define UHCI_PTR_DEPTH		0x0004
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
 
 struct uhci_framelist {
-	unsigned int frame[1024];
+	__u32 frame[UHCI_NUMFRAMES];
 } __attribute__((aligned(4096)));
 
+struct uhci_td;
+
+struct uhci_qh {
+	/* Hardware fields */
+	__u32 link;				/* Next queue */
+	__u32 element;				/* Queue element pointer */
+
+	/* Software fields */
+	/* Can't use list_head since we want a specific order */
+	struct usb_device *dev;			/* The owning device */
+
+	struct uhci_qh *prevqh, *nextqh;
+
+	struct list_head remove_list;
+} __attribute__((aligned(16)));
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT	27
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7FF		/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+#define uhci_ptr_to_virt(x)	bus_to_virt(x & ~UHCI_PTR_BITS)
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE		19
+#define TD_PID			0xFF
+
+#define uhci_maxlen(token)	((token) >> 21)
+#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define uhci_packetid(token)	((token) & 0xff)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
 /*
  * The documentation says "4 words for hardware, 4 words for software".
  *
@@ -75,7 +170,7 @@
  * On 64-bit machines we probably want to take advantage of the fact that
  * hw doesn't really care about the size of the sw-only area.
  *
- * Alas, not anymore, we have more than 4 words of software, woops
+ * Alas, not anymore, we have more than 4 words for software, woops
  */
 struct uhci_td {
 	/* Hardware fields */
@@ -85,48 +180,14 @@
 	__u32 buffer;
 
 	/* Software fields */
-	struct list_head irq_list;	/* Active interrupt list.. */
-	usb_device_irq completed;	/* Completion handler routine */
-	unsigned int *backptr;		/* Where to remove this from.. */
-	void *dev_id;
-	int inuse;			/* Inuse? */
-	struct uhci_qh *qh;
-} __attribute__((aligned(32)));
-
-/*
- * Note the alignment requirements of the entries
- *
- * Each UHCI device has pre-allocated QH and TD entries.
- * You can use more than the pre-allocated ones, but I
- * don't see you usually needing to.
- */
-struct uhci;
+	unsigned int *frameptr;		/* Frame list pointer */
+	struct uhci_td *prevtd, *nexttd; /* Previous and next TD in queue */
 
-#define UHCI_MAXTD 64
+	struct usb_device *dev;
+	struct urb *urb;		/* URB this TD belongs to */
 
-#define UHCI_MAXQH	16
-
-/* The usb device part must be first! */
-struct uhci_device {
-	struct usb_device	*usb;
-
-	struct uhci		*uhci;
-	struct uhci_qh		qh[UHCI_MAXQH];		/* These are the "common" qh's for each device */
-	struct uhci_td		td[UHCI_MAXTD];
-
-	unsigned long		data[16];
-};
-
-#define uhci_to_usb(uhci)	((uhci)->usb)
-#define usb_to_uhci(usb)	((struct uhci_device *)(usb)->hcpriv)
-
-/*
- * The root hub pre-allocated QH's and TD's have
- * some special global uses..
- */
-#define control_td	td		/* Td's 0-30 */
-/* This is only for the root hub's TD list */
-#define tick_td		td[31]
+	struct list_head list;
+} __attribute__((aligned(16)));
 
 /*
  * There are various standard queues. We set up several different
@@ -151,49 +212,92 @@
  * be scared, it kinda makes sense. Look at this wonderful picture care of
  * Linus:
  *
- *  generic-iso-QH  ->  dev1-iso-QH  ->  generic-irq-QH  ->  dev1-irq-QH  -> ...
- *       |                       |                  |                   |
- *      End          dev1-iso-TD1          End            dev1-irq-TD1
- *                       |
- *                   dev1-iso-TD2
- *                       |
- *                      ....
+ *  generic-  ->  dev1-  ->  generic-  ->  dev1-  ->  control-  ->  bulk- -> ...
+ *   iso-QH      iso-QH       irq-QH      irq-QH        QH           QH
+ *      |           |            |           |           |            |
+ *     End     dev1-iso-TD1     End     dev1-irq-TD1    ...          ... 
+ *                  |
+ *             dev1-iso-TD2
+ *                  |
+ *                ....
  *
  * This may vary a bit (the UHCI docs don't explicitly say you can put iso
  * transfers in QH's and all of their pictures don't have that either) but
  * other than that, that is what we're doing now
  *
- * To keep with Linus' nomenclature, this is called the qh skeleton. These
- * labels (below) are only signficant to the root hub's qh's
+ * And now we don't put Iso transfers in QH's, so we don't waste one on it
+ * --jerdfelt
+ *
+ * To keep with Linus' nomenclature, this is called the QH skeleton. These
+ * labels (below) are only signficant to the root hub's QH's
  */
-#define skel_iso_qh		qh[0]
 
-#define skel_int2_qh		qh[1]
-#define skel_int4_qh		qh[2]
-#define skel_int8_qh		qh[3]
-#define skel_int16_qh		qh[4]
-#define skel_int32_qh		qh[5]
-#define skel_int64_qh		qh[6]
-#define skel_int128_qh		qh[7]
-#define skel_int256_qh		qh[8]
-
-#define skel_control_qh		qh[9]
-
-#define skel_bulk0_qh		qh[10]
-#define skel_bulk1_qh		qh[11]
-#define skel_bulk2_qh		qh[12]
-#define skel_bulk3_qh		qh[13]
-
-/*
- * These are significant to the devices allocation of QH's
- */
-#if 0
-#define iso_qh			qh[0]
-#define int_qh			qh[1]	/* We have 2 "common" interrupt QH's */
-#define control_qh		qh[3]
-#define bulk_qh			qh[4]	/* We have 4 "common" bulk QH's */
-#define extra_qh		qh[8]	/* The rest, anything goes */
-#endif
+#define UHCI_NUM_SKELTD		10
+#define skel_int1_td		skeltd[0]
+#define skel_int2_td		skeltd[1]
+#define skel_int4_td		skeltd[2]
+#define skel_int8_td		skeltd[3]
+#define skel_int16_td		skeltd[4]
+#define skel_int32_td		skeltd[5]
+#define skel_int64_td		skeltd[6]
+#define skel_int128_td		skeltd[7]
+#define skel_int256_td		skeltd[8]
+#define skel_term_td		skeltd[9]	/* To work around PIIX UHCI bug */
+
+#define UHCI_NUM_SKELQH		4
+#define skel_ls_control_qh	skelqh[0]
+#define skel_hs_control_qh	skelqh[1]
+#define skel_bulk_qh		skelqh[2]
+#define skel_term_qh		skelqh[3]
+
+/*
+ * Search tree for determining where <interval> fits in the
+ * skelqh[] skeleton.
+ *
+ * An interrupt request should be placed into the slowest skelqh[]
+ * which meets the interval/period/frequency requirement.
+ * An interrupt request is allowed to be faster than <interval> but not slower.
+ *
+ * For a given <interval>, this function returns the appropriate/matching
+ * skelqh[] index value.
+ *
+ * NOTE: For UHCI, we don't really need int256_qh since the maximum interval
+ * is 255 ms.  However, we do need an int1_qh since 1 is a valid interval
+ * and we should meet that frequency when requested to do so.
+ * This will require some change(s) to the UHCI skeleton.
+ */
+static inline int __interval_to_skel(int interval)
+{
+	if (interval < 16) {
+		if (interval < 4) {
+			if (interval < 2)
+				return 0;	/* int1 for 0-1 ms */
+			return 1;		/* int2 for 2-3 ms */
+		}
+		if (interval < 8)
+			return 2;		/* int4 for 4-7 ms */
+		return 3;			/* int8 for 8-15 ms */
+	}
+	if (interval < 64) {
+		if (interval < 32)
+			return 4;		/* int16 for 16-31 ms */
+		return 5;			/* int32 for 32-63 ms */
+	}
+	if (interval < 128)
+		return 6;			/* int64 for 64-127 ms */
+	return 7;				/* int128 for 128-255 ms (Max.) */
+}
+
+struct virt_root_hub {
+	int devnum;		/* Address of Root Hub endpoint */
+	void *urb;
+	void *int_addr;
+	int send;
+	int interval;
+	int numports;
+	int c_p_r[8];
+	struct timer_list rh_int_timer;
+};
 
 /*
  * This describes the full uhci information.
@@ -202,28 +306,117 @@
  * a subset of what the full implementation needs.
  */
 struct uhci {
+	/* Grabbed from PCI */
 	int irq;
 	unsigned int io_addr;
+	unsigned int io_size;
+
+	struct list_head uhci_list;
 
 	struct usb_bus *bus;
 
-#if 0
-	/* These are "standard" QH's for the entire bus */
-	struct uhci_qh qh[UHCI_MAXQH];
-#endif
-	struct uhci_device *root_hub;		/* Root hub device descriptor.. */
+	struct uhci_td skeltd[UHCI_NUM_SKELTD];	/* Skeleton TD's */
+	struct uhci_qh skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
 
+	spinlock_t framelist_lock;
 	struct uhci_framelist *fl;		/* Frame list */
-	struct list_head interrupt_list;	/* List of interrupt-active TD's for this uhci */
+	int fsbr;				/* Full speed bandwidth reclamation */
+
+	spinlock_t qh_remove_lock;
+	struct list_head qh_remove_list;
+
+	spinlock_t urb_remove_lock;
+	struct list_head urb_remove_list;
+
+	struct s_nested_lock urblist_lock;
+	struct list_head urb_list;
+
+	struct virt_root_hub rh;	/* private data of the virtual root hub */
+};
+
+struct urb_priv {
+	struct urb *urb;
+
+	struct uhci_qh *qh;		/* QH for this URB */
+
+	int fsbr : 1;			/* URB turned on FSBR */
+	int fsbr_timeout : 1;		/* URB timed out on FSBR */
+	int queued : 1;			/* QH was queued (not linked in) */
+	int short_control_packet : 1;	/* If we get a short packet during */
+					/*  a control transfer, retrigger */
+					/*  the status phase */
+
+	unsigned long inserttime;	/* In jiffies */
+
+	struct list_head list;
+
+	struct list_head urb_queue_list;	/* URB's linked together */
 };
 
+/* -------------------------------------------------------------------------
+   Virtual Root HUB
+   ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_DEVICE		0x00
+#define RH_INTERFACE		0x01
+#define RH_ENDPOINT		0x02
+#define RH_OTHER		0x03
+
+#define RH_CLASS		0x20
+#define RH_VENDOR		0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS		0x0080
+#define RH_CLEAR_FEATURE	0x0100
+#define RH_SET_FEATURE		0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR	0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE		0x0280
+#define RH_GET_INTERFACE	0x0A80
+#define RH_SET_INTERFACE	0x0B00
+#define RH_SYNC_FRAME		0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP		0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION	0x00
+#define RH_PORT_ENABLE		0x01
+#define RH_PORT_SUSPEND		0x02
+#define RH_PORT_OVER_CURRENT	0x03
+#define RH_PORT_RESET		0x04
+#define RH_PORT_POWER		0x08
+#define RH_PORT_LOW_SPEED	0x09
+#define RH_C_PORT_CONNECTION	0x10
+#define RH_C_PORT_ENABLE	0x11
+#define RH_C_PORT_SUSPEND	0x12
+#define RH_C_PORT_OVER_CURRENT	0x13
+#define RH_C_PORT_RESET		0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER	0x00
+#define RH_C_HUB_OVER_CURRENT	0x01
+#define RH_DEVICE_REMOTE_WAKEUP	0x00
+#define RH_ENDPOINT_STALL	0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP		0x00
+
+#define RH_ACK			0x01
+#define RH_REQ_ERR		-1
+#define RH_NACK			0x00
+
 /* needed for the debugging code */
 struct uhci_td *uhci_link_to_td(unsigned int element);
 
 /* Debugging code */
-void show_td(struct uhci_td * td);
-void show_status(struct uhci *uhci);
-void show_queues(struct uhci *uhci);
+void uhci_show_td(struct uhci_td *td);
+void uhci_show_status(struct uhci *uhci);
+void uhci_show_urb_queue(struct urb *urb);
+void uhci_show_queue(struct uhci_qh *qh);
+void uhci_show_queues(struct uhci *uhci);
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-core.c linux.18p17/drivers/usb/usb-core.c
--- linux.vanilla/drivers/usb/usb-core.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-core.c	Sat Oct 14 00:50:34 2000
@@ -0,0 +1,51 @@
+/*
+ * driver/usb/usb-core.c
+ *
+ * (C) Copyright David Waite 1999
+ * based on code from usb.c, by Linus Torvalds
+ *
+ * The purpose of this file is to pull any and all generic modular code from
+ * usb.c and put it in a separate file. This way usb.c is kept as a generic
+ * library, while this file handles starting drivers, etc.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * USB core
+ */
+extern int usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+extern int usb_major_init(void);
+extern void usb_major_cleanup(void);
+
+
+/*
+ * Cleanup
+ */
+static void __exit usb_exit(void)
+{
+	usb_major_cleanup();
+	usbdevfs_cleanup();
+	usb_hub_cleanup();
+}
+
+/*
+ * Init
+ */
+int usb_init(void)
+{
+	usb_major_init();
+	usbdevfs_init();
+	usb_hub_init();
+
+	return 0;
+}
+
+module_init (usb_init);
+module_exit (usb_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-debug.c linux.18p17/drivers/usb/usb-debug.c
--- linux.vanilla/drivers/usb/usb-debug.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/usb-debug.c	Fri Sep  1 13:51:30 2000
@@ -4,31 +4,49 @@
  * I just want these out of the way where they aren't in your
  * face, but so that you can still use them..
  */
+#include <linux/config.h>
+#include <linux/version.h>
 #include <linux/kernel.h>
-
-#include "usb.h"
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
 
 static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint)
 {
 	usb_show_endpoint_descriptor(endpoint);
 }
 
-static void usb_show_interface(struct usb_interface_descriptor *interface)
+static void usb_show_interface(struct usb_interface_descriptor *altsetting)
 {
 	int i;
 
-	usb_show_interface_descriptor(interface);
-	for (i = 0 ; i < interface->bNumEndpoints; i++)
-		usb_show_endpoint(interface->endpoint + i);
+	usb_show_interface_descriptor(altsetting);
+
+	for (i = 0; i < altsetting->bNumEndpoints; i++)
+		usb_show_endpoint(altsetting->endpoint + i);
 }
 
 static void usb_show_config(struct usb_config_descriptor *config)
 {
-	int i;
+	int i, j;
+	struct usb_interface *ifp;
 
 	usb_show_config_descriptor(config);
-	for (i = 0 ; i < config->bNumInterfaces; i++)
-		usb_show_interface(config->interface + i);
+	for (i = 0; i < config->bNumInterfaces; i++) {
+		ifp = config->interface + i;
+
+		if (!ifp)
+			break;
+
+		printk("\n  Interface: %d\n", i);
+		for (j = 0; j < ifp->num_altsetting; j++)
+			usb_show_interface(ifp->altsetting + j);
+	}
 }
 
 void usb_show_device(struct usb_device *dev)
@@ -40,26 +58,54 @@
 		usb_show_config(dev->config + i);
 }
 
-
 /*
  * Parse and show the different USB descriptors.
  */
 void usb_show_device_descriptor(struct usb_device_descriptor *desc)
 {
-	printk("  USB version %x.%02x\n", desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
-	printk("  Vendor:  %04x\n", desc->idVendor);
-	printk("  Product: %04x\n", desc->idProduct);
-	printk("  Configurations: %d\n", desc->bNumConfigurations);
+	if (!desc)
+	{
+		printk("Invalid USB device descriptor (NULL POINTER)\n");
+		return;
+	}
+	printk("  Length              = %2d%s\n", desc->bLength,
+		desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");
+	printk("  DescriptorType      = %02x\n", desc->bDescriptorType);
+
+	printk("  USB version         = %x.%02x\n",
+		desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
+	printk("  Vendor:Product      = %04x:%04x\n",
+		desc->idVendor, desc->idProduct);
+	printk("  MaxPacketSize0      = %d\n", desc->bMaxPacketSize0);
+	printk("  NumConfigurations   = %d\n", desc->bNumConfigurations);
+	printk("  Device version      = %x.%02x\n",
+		desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
 
-	printk("  Device Class: %d\n", desc->bDeviceClass);
+	printk("  Device Class:SubClass:Protocol = %02x:%02x:%02x\n",
+		desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
 	switch (desc->bDeviceClass) {
 	case 0:
 		printk("    Per-interface classes\n");
 		break;
-	case 9:
+	case USB_CLASS_AUDIO:
+		printk("    Audio device class\n");
+		break;
+	case USB_CLASS_COMM:
+		printk("    Communications class\n");
+		break;
+	case USB_CLASS_HID:
+		printk("    Human Interface Devices class\n");
+		break;
+	case USB_CLASS_PRINTER:
+		printk("    Printer device class\n");
+		break;
+	case USB_CLASS_MASS_STORAGE:
+		printk("    Mass Storage device class\n");
+		break;
+	case USB_CLASS_HUB:
 		printk("    Hub device class\n");
 		break;
-	case 0xff:
+	case USB_CLASS_VENDOR_SPEC:
 		printk("    Vendor class\n");
 		break;
 	default:
@@ -67,11 +113,11 @@
 	}
 }
 
-void usb_show_config_descriptor(struct usb_config_descriptor * desc)
+void usb_show_config_descriptor(struct usb_config_descriptor *desc)
 {
 	printk("Configuration:\n");
 	printk("  bLength             = %4d%s\n", desc->bLength,
-		desc->bLength == 9 ? "" : " (!!!)");
+		desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)");
 	printk("  bDescriptorType     =   %02x\n", desc->bDescriptorType);
 	printk("  wTotalLength        = %04x\n", desc->wTotalLength);
 	printk("  bNumInterfaces      =   %02x\n", desc->bNumInterfaces);
@@ -81,47 +127,77 @@
 	printk("  MaxPower            = %4dmA\n", desc->MaxPower * 2);
 }
 
-void usb_show_interface_descriptor(struct usb_interface_descriptor * desc)
+void usb_show_interface_descriptor(struct usb_interface_descriptor *desc)
 {
-	printk("  Interface:\n");
+	printk("  Alternate Setting: %2d\n", desc->bAlternateSetting);
 	printk("    bLength             = %4d%s\n", desc->bLength,
-		desc->bLength == 9 ? "" : " (!!!)");
+		desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)");
 	printk("    bDescriptorType     =   %02x\n", desc->bDescriptorType);
 	printk("    bInterfaceNumber    =   %02x\n", desc->bInterfaceNumber);
 	printk("    bAlternateSetting   =   %02x\n", desc->bAlternateSetting);
 	printk("    bNumEndpoints       =   %02x\n", desc->bNumEndpoints);
-	printk("    bInterfaceClass     =   %02x\n", desc->bInterfaceClass);
-	printk("    bInterfaceSubClass  =   %02x\n", desc->bInterfaceSubClass);
-	printk("    bInterfaceProtocol  =   %02x\n", desc->bInterfaceProtocol);
+	printk("    bInterface Class:SubClass:Protocol =   %02x:%02x:%02x\n",
+		desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol);
 	printk("    iInterface          =   %02x\n", desc->iInterface);
 }
 
-void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc)
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc)
 {
+	char *LengthCommentString = (desc->bLength ==
+		USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength ==
+		USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)";
 	char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };
+
 	printk("    Endpoint:\n");
-	printk("      bLength             = %4d%s\n", desc->bLength,
-		desc->bLength == 7 ? "" : " (!!!)");
+	printk("      bLength             = %4d%s\n",
+		desc->bLength, LengthCommentString);
 	printk("      bDescriptorType     =   %02x\n", desc->bDescriptorType);
 	printk("      bEndpointAddress    =   %02x (%s)\n", desc->bEndpointAddress,
-		(desc->bEndpointAddress & 0x80) ? "in" : "out");
+		(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+			USB_ENDPOINT_XFER_CONTROL ? "i/o" :
+		(desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out");
 	printk("      bmAttributes        =   %02x (%s)\n", desc->bmAttributes,
-		EndpointType[3 & desc->bmAttributes]);
+		EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]);
 	printk("      wMaxPacketSize      = %04x\n", desc->wMaxPacketSize);
 	printk("      bInterval           =   %02x\n", desc->bInterval);
+
+	/* Audio extensions to the endpoint descriptor */
+	if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) {
+		printk("      bRefresh            =   %02x\n", desc->bRefresh);
+		printk("      bSynchAddress       =   %02x\n", desc->bSynchAddress);
+	}
 }
 
-void usb_show_hub_descriptor(struct usb_hub_descriptor * desc)
+void usb_show_string(struct usb_device *dev, char *id, int index)
 {
-	int len = 7;
-	unsigned char *ptr = (unsigned char *) desc;
+	char *buf;
 
-	printk("Interface:");
-	while (len) {
-		printk(" %02x", *ptr);
-		ptr++; len--;
-	}
-	printk("\n");
+	if (!index)
+		return;
+	if (!(buf = kmalloc(256, GFP_KERNEL)))
+		return;
+	if (usb_string(dev, index, buf, 256) > 0)
+		printk(KERN_INFO "%s: %s\n", id, buf);
+	kfree(buf);
+}
+
+void usb_dump_urb (purb_t purb)
+{
+	printk ("urb                   :%p\n", purb);
+	printk ("next                  :%p\n", purb->next);
+	printk ("dev                   :%p\n", purb->dev);
+	printk ("pipe                  :%08X\n", purb->pipe);
+	printk ("status                :%d\n", purb->status);
+	printk ("transfer_flags        :%08X\n", purb->transfer_flags);
+	printk ("transfer_buffer       :%p\n", purb->transfer_buffer);
+	printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+	printk ("actual_length         :%d\n", purb->actual_length);
+	printk ("setup_packet          :%p\n", purb->setup_packet);
+	printk ("start_frame           :%d\n", purb->start_frame);
+	printk ("number_of_packets     :%d\n", purb->number_of_packets);
+	printk ("interval              :%d\n", purb->interval);
+	printk ("error_count           :%d\n", purb->error_count);
+	printk ("context               :%p\n", purb->context);
+	printk ("complete              :%p\n", purb->complete);
 }
-
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-ohci.c linux.18p17/drivers/usb/usb-ohci.c
--- linux.vanilla/drivers/usb/usb-ohci.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-ohci.c	Sat Oct 14 00:50:34 2000
@@ -0,0 +1,2134 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * 
+ * [ Initialisation is based on Linus'  ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds  ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ * 
+ * 
+ * History:
+ * 
+ * v5.2 1999/12/07 URB 3rd preview, 
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume 
+ * 	i386: HUB, Keyboard, Mouse, Printer 
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18 
+ * v3.0 1999/06/25 
+ * v2.1 1999/05/09  code clean up
+ * v2.0 1999/05/04 
+ * v1.0 1999/04/27 initial release
+ * 
+ 
+ */
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/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() */
+#undef DEBUG
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#define OHCI_USE_NPS		// force NoPowerSwitching mode
+
+#include "usb-ohci.h"
+
+#include <linux/pm.h>
+static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/adb.h>
+#include <asm/pmu.h>
+#endif
+
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define	OHCI_CONTROL_INIT \
+	(OHCI_CTRL_CBSR & 0x3) \
+	| OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+static DECLARE_WAIT_QUEUE_HEAD (op_wakeup); 
+static LIST_HEAD (ohci_hcd_list);
+static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+
+/*-------------------------------------------------------------------------*
+ * URB support functions 
+ *-------------------------------------------------------------------------*/ 
+ 
+/* free the private part of an URB */
+ 
+static void urb_rm_priv (urb_t * urb) 
+{
+	urb_priv_t * urb_priv = urb->hcpriv;
+	int i;
+	
+	if (!urb_priv) return;
+	
+	for (i = 0; i < urb_priv->length; i++) {
+		if (urb_priv->td [i]) {
+			OHCI_FREE (urb_priv->td [i]);
+		}
+	}
+	kfree (urb->hcpriv);
+	urb->hcpriv = NULL;
+
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB     
+ * small: 0) header + data packets 1) just header */
+ 
+static void urb_print (urb_t * urb, char * str, int small)
+{
+	unsigned int pipe= urb->pipe;
+	int i, len;
+	
+	if (!urb->dev || !urb->dev->bus) {
+		dbg("%s URB: no dev", str);
+		return;
+	}
+	
+	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", 
+			str,
+		 	sohci_get_current_frame_number (urb->dev), 
+		 	usb_pipedevice (pipe),
+		 	usb_pipeendpoint (pipe), 
+		 	usb_pipeout (pipe)? 'O': 'I',
+		 	usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+		 		(usb_pipecontrol (pipe)? "CTRL": "BULK"),
+		 	urb->transfer_flags, 
+		 	urb->actual_length, 
+		 	urb->transfer_buffer_length,
+		 	urb->status, urb->status);
+	if (!small) {
+		if (usb_pipecontrol (pipe)) {
+			printk (KERN_DEBUG __FILE__ ": cmd(8):");
+			for (i = 0; i < 8 ; i++) 
+				printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+			printk ("\n");
+		}
+		if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+			printk (KERN_DEBUG __FILE__ ": data(%d/%d):", 
+				urb->actual_length, 
+				urb->transfer_buffer_length);
+			len = usb_pipeout (pipe)? 
+						urb->transfer_buffer_length: urb->actual_length;
+			for (i = 0; i < 16 && i < len; i++) 
+				printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+			printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+		}
+	} 
+}
+
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t * ohci, char * str) {
+	int i, j;
+	 __u32 * ed_p;
+	for (i= 0; i < 32; i++) {
+		j = 5;
+		ed_p = &(ohci->hcca.int_table [i]);
+		if (*ed_p == 0)
+		    continue;
+		printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
+		while (*ed_p != 0 && j--) {
+			ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p));
+			printk (" ed: %4x;", ed->hwINFO);
+			ed_p = &ed->hwNextED;
+		}
+		printk ("\n");
+	}
+}
+
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+		label,
+		mask,
+		(mask & OHCI_INTR_MIE) ? " MIE" : "",
+		(mask & OHCI_INTR_OC) ? " OC" : "",
+		(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+		(mask & OHCI_INTR_FNO) ? " FNO" : "",
+		(mask & OHCI_INTR_UE) ? " UE" : "",
+		(mask & OHCI_INTR_RD) ? " RD" : "",
+		(mask & OHCI_INTR_SF) ? " SF" : "",
+		(mask & OHCI_INTR_WDH) ? " WDH" : "",
+		(mask & OHCI_INTR_SO) ? " SO" : ""
+		);
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+	if (value)
+		dbg ("%s %08x", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+	switch (state) {
+		case OHCI_USB_RESET:	return "reset";
+		case OHCI_USB_RESUME:	return "resume";
+		case OHCI_USB_OPER:	return "operational";
+		case OHCI_USB_SUSPEND:	return "suspend";
+	}
+	return "?";
+}
+
+// dump control and status registers
+static void ohci_dump_status (ohci_t *controller)
+{
+	struct ohci_regs	*regs = controller->regs;
+	__u32			temp;
+
+	temp = readl (&regs->revision) & 0xff;
+	if (temp != 0x10)
+		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+	temp = readl (&regs->control);
+	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+		(temp & OHCI_CTRL_RWE) ? " RWE" : "",
+		(temp & OHCI_CTRL_RWC) ? " RWC" : "",
+		(temp & OHCI_CTRL_IR) ? " IR" : "",
+		hcfs2string (temp & OHCI_CTRL_HCFS),
+		(temp & OHCI_CTRL_BLE) ? " BLE" : "",
+		(temp & OHCI_CTRL_CLE) ? " CLE" : "",
+		(temp & OHCI_CTRL_IE) ? " IE" : "",
+		(temp & OHCI_CTRL_PLE) ? " PLE" : "",
+		temp & OHCI_CTRL_CBSR
+		);
+
+	temp = readl (&regs->cmdstatus);
+	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+		(temp & OHCI_SOC) >> 16,
+		(temp & OHCI_OCR) ? " OCR" : "",
+		(temp & OHCI_BLF) ? " BLF" : "",
+		(temp & OHCI_CLF) ? " CLF" : "",
+		(temp & OHCI_HCR) ? " HCR" : ""
+		);
+
+	ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+	ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+	// intrdisable always same as intrenable
+	// ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
+
+	maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+	maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+	maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+	maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+	maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+	maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+	struct ohci_regs	*regs = controller->regs;
+	__u32			temp, ndp, i;
+
+	temp = readl (&regs->roothub.a);
+	ndp = (temp & RH_A_NDP);
+
+	if (verbose) {
+		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+			((temp & RH_A_POTPGT) >> 24) & 0xff,
+			(temp & RH_A_NOCP) ? " NOCP" : "",
+			(temp & RH_A_OCPM) ? " OCPM" : "",
+			(temp & RH_A_DT) ? " DT" : "",
+			(temp & RH_A_NPS) ? " NPS" : "",
+			(temp & RH_A_PSM) ? " PSM" : "",
+			ndp
+			);
+		temp = readl (&regs->roothub.b);
+		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+			temp,
+			(temp & RH_B_PPCM) >> 16,
+			(temp & RH_B_DR)
+			);
+		temp = readl (&regs->roothub.status);
+		dbg ("roothub.status: %08x%s%s%s%s%s%s",
+			temp,
+			(temp & RH_HS_CRWE) ? " CRWE" : "",
+			(temp & RH_HS_OCIC) ? " OCIC" : "",
+			(temp & RH_HS_LPSC) ? " LPSC" : "",
+			(temp & RH_HS_DRWE) ? " DRWE" : "",
+			(temp & RH_HS_OCI) ? " OCI" : "",
+			(temp & RH_HS_LPS) ? " LPS" : ""
+			);
+	}
+	
+	for (i = 0; i < ndp; i++) {
+		temp = readl (&regs->roothub.portstatus [i]);
+		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+			i,
+			temp,
+			(temp & RH_PS_PRSC) ? " PRSC" : "",
+			(temp & RH_PS_OCIC) ? " OCIC" : "",
+			(temp & RH_PS_PSSC) ? " PSSC" : "",
+			(temp & RH_PS_PESC) ? " PESC" : "",
+			(temp & RH_PS_CSC) ? " CSC" : "",
+
+			(temp & RH_PS_LSDA) ? " LSDA" : "",
+			(temp & RH_PS_PPS) ? " PPS" : "",
+			(temp & RH_PS_PRS) ? " PRS" : "",
+			(temp & RH_PS_POCI) ? " POCI" : "",
+			(temp & RH_PS_PSS) ? " PSS" : "",
+
+			(temp & RH_PS_PES) ? " PES" : "",
+			(temp & RH_PS_CCS) ? " CCS" : ""
+			);
+	}
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+	dbg ("OHCI controller %p state", controller->regs);
+
+	// dumps some of the state we know about
+	ohci_dump_status (controller);
+	if (verbose)
+		ep_print_int_eds (controller, "hcca");
+	dbg ("hcca frame #%04x", controller->hcca.frame_no);
+	ohci_dump_roothub (controller, 1);
+}
+
+
+#endif
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* return a request to the completion handler */
+ 
+static int sohci_return_urb (urb_t * urb)
+{
+	urb_priv_t * urb_priv = urb->hcpriv;
+	urb_t * urbt;
+	unsigned long flags;
+	int i;
+	
+	/* just to be sure */
+	if (!urb->complete) {
+		urb_rm_priv (urb);
+		usb_dec_dev_use (urb->dev);
+		return -1;
+	}
+	
+	if (!urb_priv) return -1; /* urb already unlinked */
+	
+#ifdef DEBUG
+	urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+	switch (usb_pipetype (urb->pipe)) {
+  		case PIPE_INTERRUPT:
+			urb->complete (urb); /* call complete and requeue URB */	
+  			urb->actual_length = 0;
+  			urb->status = USB_ST_URB_PENDING;
+  			if (urb_priv->state != URB_DEL)
+  				td_submit_urb (urb);
+  			break;
+  			
+		case PIPE_ISOCHRONOUS:
+			for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
+			if (urbt) { /* send the reply and requeue URB */	
+				urb->complete (urb);
+				
+				spin_lock_irqsave (&usb_ed_lock, flags);
+				urb->actual_length = 0;
+  				urb->status = USB_ST_URB_PENDING;
+  				urb->start_frame = urb_priv->ed->last_iso + 1;
+  				if (urb_priv->state != URB_DEL) {
+  					for (i = 0; i < urb->number_of_packets; i++) {
+  						urb->iso_frame_desc[i].actual_length = 0;
+  						urb->iso_frame_desc[i].status = -EXDEV;
+  					}
+  					td_submit_urb (urb);
+  				}
+  				spin_unlock_irqrestore (&usb_ed_lock, flags);
+  				
+  			} else { /* unlink URB, call complete */
+				urb_rm_priv (urb);
+				usb_dec_dev_use (urb->dev);
+				urb->complete (urb); 	
+			}		
+			break;
+  				
+		case PIPE_BULK:
+		case PIPE_CONTROL: /* unlink URB, call complete */
+			urb_rm_priv (urb);
+			usb_dec_dev_use (urb->dev);
+			urb->complete (urb);	
+			break;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+ 
+static int sohci_submit_urb (urb_t * urb)
+{
+	ohci_t * ohci;
+	ed_t * ed;
+	urb_priv_t * urb_priv;
+	unsigned int pipe = urb->pipe;
+	int i, size = 0;
+	unsigned long flags;
+	
+	if (!urb->dev || !urb->dev->bus) return -EINVAL;
+	
+	if (urb->hcpriv) return -EINVAL; /* urb already in use */
+
+//	if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) 
+//		return -EPIPE;
+	
+	usb_inc_dev_use (urb->dev);
+	ohci = (ohci_t *) urb->dev->bus->hcpriv;
+	
+#ifdef DEBUG
+	urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+	
+	/* a request to the virtual root hub */
+	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
+		return rh_submit_urb (urb);
+	
+	/* when controller's hung, permit only hub cleanup attempts
+	 * such as powering down ports */
+	if (ohci->disabled)
+		return -ESHUTDOWN;
+
+	/* every endpoint has a ed, locate and fill it */
+	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+
+	/* for the private part of the URB we need the number of TDs (size) */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_BULK:	/* one TD for every 4096 Byte */
+			size = (urb->transfer_buffer_length - 1) / 4096 + 1;
+			break;
+		case PIPE_ISOCHRONOUS: /* number of packets from URB */
+			size = urb->number_of_packets;
+			for (i = 0; i < urb->number_of_packets; i++) {
+  				urb->iso_frame_desc[i].actual_length = 0;
+  				urb->iso_frame_desc[i].status = -EXDEV;
+  			}
+			break;
+		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+			size = (urb->transfer_buffer_length == 0)? 2: 
+						(urb->transfer_buffer_length - 1) / 4096 + 3;
+			break;
+		case PIPE_INTERRUPT: /* one TD */
+			size = 1;
+			
+			break;
+	}
+
+	/* allocate the private part or the URB */
+	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), 
+							in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	if (!urb_priv) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+	memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
+	
+	/* fill the private part of the URB */
+	urb->hcpriv = urb_priv;
+	urb_priv->length = size;
+	urb_priv->td_cnt = 0;
+	urb_priv->state = 0;
+	urb_priv->ed = ed;	
+	
+	/* allocate the TDs */
+	for (i = 0; i < size; i++) { 
+		OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
+		if (!urb_priv->td[i]) {
+			usb_dec_dev_use (urb->dev);	
+			urb_rm_priv (urb);
+			return -ENOMEM;
+		}
+	}	
+	spin_lock_irqsave (&usb_ed_lock, flags);	
+	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+		urb_rm_priv(urb);
+		usb_dec_dev_use (urb->dev);	
+		spin_unlock_irqrestore(&usb_ed_lock, flags);
+		return -EINVAL;
+	}
+	
+	/* for ISOC transfers calculate start frame index */
+	if (urb->transfer_flags & USB_ISO_ASAP) { 
+		urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): 
+								(le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
+	}	
+	urb->status = USB_ST_URB_PENDING;
+	urb->actual_length = 0;
+	
+	if (ed->state != ED_OPER)  /* link the ed into a chain if is not already */
+		ep_link (ohci, ed);
+	
+	urb->status = USB_ST_URB_PENDING; 
+	td_submit_urb (urb); /* fill the TDs and link it to the ed */
+
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+	return 0;	
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* deactivate all TDs and remove the private part of the URB */
+ 
+static int sohci_unlink_urb (urb_t * urb)
+{
+	unsigned long flags;
+	ohci_t * ohci;
+	DECLARE_WAITQUEUE (wait, current);
+	
+	if (!urb) /* just to be sure */ 
+		return -EINVAL;
+		
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+
+	ohci = (ohci_t *) urb->dev->bus->hcpriv; 
+
+#ifdef DEBUG
+	urb_print (urb, "UNLINK", 1);
+#endif		  
+	
+	if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) {
+		usb_dec_dev_use(urb->dev);
+		return rh_unlink_urb (urb); /* a request to the virtual root hub */
+	}
+
+	if (urb->hcpriv) { 
+		/* URB active? */
+		if (urb->status == USB_ST_URB_PENDING && !ohci->disabled) {
+			urb_priv_t  * urb_priv = urb->hcpriv;
+			urb_priv->state = URB_DEL; 
+
+			/* we want to delete the TDs of an URB from an ed 
+			 * request the deletion, it will be handled at the
+			 * next USB-frame */
+			
+			spin_lock_irqsave (&usb_ed_lock, flags);
+			ep_rm_ed (urb->dev, urb_priv->ed);
+			urb_priv->ed->state |= ED_URB_DEL;
+			spin_unlock_irqrestore (&usb_ed_lock, flags);
+			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+				usb_dec_dev_use (urb->dev);	
+				add_wait_queue (&op_wakeup, &wait);
+				current->state = TASK_UNINTERRUPTIBLE;
+				if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */
+					err("unlink URB timeout!");
+				remove_wait_queue (&op_wakeup, &wait); 
+				urb->status = -ENOENT;
+			} else {
+				/* usb_dec_dev_use done in dl_del_list() */
+				urb->status = -EINPROGRESS;
+			}
+		} else {
+			usb_dec_dev_use (urb->dev);	
+			urb_rm_priv (urb);
+			if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) {
+				urb->complete (urb); 
+				urb->status = 0;	
+			} else 
+				urb->status = -ENOENT;
+		}	
+	}	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* allocate private data space for a usb device */
+
+static int sohci_alloc_dev (struct usb_device *usb_dev)
+{
+	struct ohci_device * dev;
+
+	dev = kmalloc (sizeof (*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+		
+	memset (dev, 0, sizeof (*dev));
+
+	usb_dev->hcpriv = dev;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* free private data space of usb device */
+  
+static int sohci_free_dev (struct usb_device * usb_dev)
+{
+	unsigned long flags;
+	int i, cnt = 0;
+	ed_t * ed;
+	DECLARE_WAITQUEUE (wait, current);
+	struct ohci_device * dev = usb_to_ohci (usb_dev);
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	
+	if (!dev) return 0;
+	
+	if (usb_dev->devnum >= 0) {
+	
+		/* delete all TDs of all EDs */
+		spin_lock_irqsave (&usb_ed_lock, flags);	
+		for(i = 0; i < NUM_EDS; i++) {
+  			ed = &(dev->ed[i]);
+  			if (ed->state != ED_NEW) {
+  				if (ed->state == ED_OPER) ep_unlink (ohci, ed);
+  				ep_rm_ed (usb_dev, ed);
+  				ed->state = ED_DEL;
+  				cnt++;
+  			}
+  		}
+  		spin_unlock_irqrestore (&usb_ed_lock, flags);
+  		
+		if (cnt > 0) {
+			add_wait_queue (&op_wakeup, &wait);
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout (HZ / 10);
+			remove_wait_queue (&op_wakeup, &wait);
+		}
+	}
+	kfree (dev);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev) 
+{
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	
+	return le16_to_cpu (ohci->hcca.frame_no);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_operations sohci_device_operations = {
+	sohci_alloc_dev,
+	sohci_free_dev,
+	sohci_get_current_frame_number,
+	sohci_submit_urb,
+	sohci_unlink_urb
+};
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/  
+		
+/* search for the right branch to insert an interrupt ed into the int tree 
+ * do some load ballancing;
+ * returns the branch and 
+ * sets the interval to interval = 2^integer (ld (interval)) */
+
+static int ep_int_ballance (ohci_t * ohci, int interval, int load)
+{
+	int i, branch = 0;
+   
+	/* search for the least loaded interrupt endpoint branch of all 32 branches */
+	for (i = 0; i < 32; i++) 
+		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; 
+  
+	branch = branch % interval;
+	for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
+
+	return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*  2^int( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{	
+	int i;
+	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); 
+	return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree 
+ * in order to process it sequentially the indexes of the branches have to be mapped 
+ * the mapping reverses the bits of a word of num_bits length */
+ 
+static int ep_rev (int num_bits, int word)
+{
+	int i, wout = 0;
+
+	for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
+	return wout;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t * ohci, ed_t * edi)
+{	 
+	int int_branch;
+	int i;
+	int inter;
+	int interval;
+	int load;
+	__u32 * ed_p;
+	volatile ed_t * ed = edi;
+	
+	ed->state = ED_OPER;
+	
+	switch (ed->type) {
+	case CTRL:
+		ed->hwNextED = 0;
+		if (ohci->ed_controltail == NULL) {
+			writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+		} else {
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+		}
+		ed->ed_prev = ohci->ed_controltail;
+		ohci->ed_controltail = edi;	  
+		break;
+		
+	case BULK:  
+		ed->hwNextED = 0;
+		if (ohci->ed_bulktail == NULL) {
+			writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+		} else {
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+		}
+		ed->ed_prev = ohci->ed_bulktail;
+		ohci->ed_bulktail = edi;	  
+		break;
+		
+	case INT:
+		load = ed->int_load;
+		interval = ep_2_n_interval (ed->int_period);
+		ed->int_interval = interval;
+		int_branch = ep_int_ballance (ohci, interval, load);
+		ed->int_branch = int_branch;
+		
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			inter = 1;
+			for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]); 
+				(*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval); 
+				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+			ed->hwNextED = *ed_p; 
+			*ed_p = cpu_to_le32 (virt_to_bus (ed));
+		}
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_INT");
+#endif
+		break;
+		
+	case ISO:
+		ed->hwNextED = 0;
+		ed->int_interval = 1;
+		if (ohci->ed_isotail != NULL) {
+			ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ed->ed_prev = ohci->ed_isotail;
+		} else {
+			for ( i = 0; i < 32; i += inter) {
+				inter = 1;
+				for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); 
+					*ed_p != 0; 
+					ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
+						inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+				*ed_p = cpu_to_le32 (virt_to_bus (ed));	
+			}	
+			ed->ed_prev = NULL;
+		}	
+		ohci->ed_isotail = edi;  
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_ISO");
+#endif
+		break;
+	}	 	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* unlink an ed from one of the HC chains. 
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed */
+
+static int ep_unlink (ohci_t * ohci, ed_t * ed) 
+{
+	int int_branch;
+	int i;
+	int inter;
+	int interval;
+	__u32 * ed_p;
+	 
+    
+	switch (ed->type) {
+	case CTRL: 
+		if (ed->ed_prev == NULL) {
+			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if(ohci->ed_controltail == ed) {
+			ohci->ed_controltail = ed->ed_prev;
+		} else {
+			((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+      
+	case BULK: 
+		if (ed->ed_prev == NULL) {
+			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_bulktail == ed) {
+			ohci->ed_bulktail = ed->ed_prev;
+		} else {
+			((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+      
+    case INT: 
+      	int_branch = ed->int_branch;
+		interval = ed->int_interval;
+
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1; 
+				(*ed_p != 0) && (*ed_p != ed->hwNextED); 
+				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED), 
+				inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) {				
+					if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+			  			*ed_p = ed->hwNextED;		
+			  			break;
+			  		}
+			  }
+		}
+		for (i = int_branch; i < 32; i += interval)
+		    ohci->ohci_int_load[i] -= ed->int_load;
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_INT");
+#endif
+		break;
+		
+    case ISO:
+    	if (ohci->ed_isotail == ed)
+				ohci->ed_isotail = ed->ed_prev;
+		if (ed->hwNextED != 0) 
+				((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+				
+		if (ed->ed_prev != NULL) {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		} else {
+			for (i = 0; i < 32; i += inter) {
+				inter = 1;
+				for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); 
+					*ed_p != 0; 
+					ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) {
+						inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+						if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+			  				*ed_p = ed->hwNextED;		
+			  				break;
+			  			}
+			  	}
+			}	
+		}	
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_ISO");
+#endif
+		break;
+    }
+    ed->state = ED_UNLINK;
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
+ * but the USB stack is a little bit stateless  so we do it at every transaction
+ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are setted anyway even though most of them should not change */
+ 
+static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load)
+{
+   	ohci_t * ohci = usb_dev->bus->hcpriv;
+	td_t * td;
+	ed_t * ed_ret;
+	volatile ed_t * ed; 
+ 	
+ 	
+	spin_lock (&usb_ed_lock);
+
+	ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 
+			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+
+	if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) 
+		return NULL; /* pending delete request */
+	
+	if (ed->state == ED_NEW) {
+		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
+  		OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
+  		if(!td) return NULL; /* out of memory */
+		ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+		ed->hwHeadP = ed->hwTailP;	
+		ed->state = ED_UNLINK;
+		ed->type = usb_pipetype (pipe);
+		usb_to_ohci (usb_dev)->ed_cnt++;
+	}
+
+	ohci->dev[usb_pipedevice (pipe)] = usb_dev;
+	
+	ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
+			| usb_pipeendpoint (pipe) << 7
+			| (usb_pipeisoc (pipe)? 0x8000: 0)
+			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) 
+			| usb_pipeslow (pipe) << 13
+			| usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
+  
+  	if (ed->type == INT && ed->state == ED_UNLINK) {
+  		ed->int_period = interval;
+  		ed->int_load = load;
+  	}
+  	
+	spin_unlock(&usb_ed_lock);
+	return ed_ret; 
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+/* request the removal of an endpoint
+ * put the ep on the rm_list and request a stop of the bulk or ctrl list 
+ * real removal is done at the next start of frame (SOF) hardware interrupt */
+ 
+static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
+{    
+	unsigned int frame;
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+
+	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return;
+	
+	ed->hwINFO  |=  cpu_to_le32 (OHCI_ED_SKIP);
+	
+	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+	writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
+
+	frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
+	ed->ed_rm_list = ohci->ed_rm_list[frame];
+	ohci->ed_rm_list[frame] = ed;
+
+	switch (ed->type) {
+		case CTRL: /* stop CTRL list */
+			writel (ohci->hc_control &= ~OHCI_CTRL_CLE, &ohci->regs->control); 
+  			break;
+		case BULK: /* stop BULK list */
+			writel (ohci->hc_control &= ~OHCI_CTRL_BLE, &ohci->regs->control); 
+			break;
+	}
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* prepare a TD */
+
+static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index)
+{
+	volatile td_t  * td, * td_pt;
+	urb_priv_t * urb_priv = urb->hcpriv;
+
+	if (index >= urb_priv->length) {
+		err("internal OHCI error: TD index > length");
+		return;
+	}
+	
+	td_pt = urb_priv->td [index];
+	/* fill the old dummy TD */
+	td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
+	td->ed = urb_priv->ed;
+	td->index = index;
+	td->urb = urb; 
+	td->hwINFO = cpu_to_le32 (info);
+	if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
+		td->hwCBP = cpu_to_le32 (((!data || !len)? 
+								0 : virt_to_bus (data)) & 0xFFFFF000);
+		td->ed->last_iso = info & 0xffff;
+	} else {
+		td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data))); 
+	}			
+	td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1));
+	td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
+	td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
+	td_pt->hwNextTD = 0;
+	td->ed->hwTailP = td->hwNextTD;
+   
+	td->next_dl_td = NULL; //td_pt;
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+/* prepare all TDs of a transfer */
+
+static void td_submit_urb (urb_t * urb)
+{ 
+	urb_priv_t * urb_priv = urb->hcpriv;
+	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
+	void * ctrl = urb->setup_packet;
+	void * data = urb->transfer_buffer;
+	int data_len = urb->transfer_buffer_length;
+	int cnt = 0; 
+	__u32 info = 0;
+  	unsigned int toggle = 0;
+
+	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+  	if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
+  		toggle = TD_T_TOGGLE;
+	} else {
+  		toggle = TD_T_DATA0;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
+	}
+	
+	urb_priv->td_cnt = 0;
+	
+	switch (usb_pipetype (urb->pipe)) {
+		case PIPE_BULK:
+			info = usb_pipeout (urb->pipe)? 
+				TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
+			while(data_len > 4096) {		
+				td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
+				data += 4096; data_len -= 4096; cnt++;
+			}
+			info = usb_pipeout (urb->pipe)?
+				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+			td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
+			cnt++;
+			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+			break;
+
+		case PIPE_INTERRUPT:
+			info = usb_pipeout (urb->pipe)? 
+				TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
+			td_fill (info, data, data_len, urb, cnt++);
+			break;
+
+		case PIPE_CONTROL:
+			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+			td_fill (info, ctrl, 8, urb, cnt++); 
+			if (data_len > 0) {  
+				info = usb_pipeout (urb->pipe)? 
+					TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+				td_fill (info, data, data_len, urb, cnt++);  
+			} 
+			info = usb_pipeout (urb->pipe)? 
+ 				TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+			td_fill (info, NULL, 0, urb, cnt++);
+			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+			break;
+
+		case PIPE_ISOCHRONOUS:
+			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+				td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), 
+ 					(__u8 *) data + urb->iso_frame_desc[cnt].offset, 
+					urb->iso_frame_desc[cnt].length, urb, cnt); 
+			}
+			break;
+	} 
+	if (urb_priv->length != cnt) 
+		dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length(td_t * td)
+{
+	__u32 tdINFO, tdBE, tdCBP;
+ 	__u16 tdPSW;
+ 	urb_t * urb = td->urb;
+ 	urb_priv_t * urb_priv = urb->hcpriv;
+	int dlen = 0;
+	int cc = 0;
+	
+	tdINFO = le32_to_cpup (&td->hwINFO);
+  	tdBE   = le32_to_cpup (&td->hwBE);
+  	tdCBP  = le32_to_cpup (&td->hwCBP);
+
+
+  	if (tdINFO & TD_ISO) {
+ 		tdPSW = le16_to_cpu (td->hwPSW[0]);
+ 		cc = (tdPSW >> 12) & 0xF;
+		if (cc < 0xE)  {
+			if (usb_pipeout(urb->pipe)) {
+				dlen = urb->iso_frame_desc[td->index].length;
+			} else {
+				dlen = tdPSW & 0x3ff;
+			}
+			urb->actual_length += dlen;
+			urb->iso_frame_desc[td->index].actual_length = dlen;
+			if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
+				cc = TD_CC_NOERROR;
+					 
+			urb->iso_frame_desc[td->index].status = cc_to_error[cc];
+		}
+	} else { /* BULK, INT, CONTROL DATA */
+		if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && 
+				((td->index == 0) || (td->index == urb_priv->length - 1)))) {
+ 			if (tdBE != 0) {
+ 				if (td->hwCBP == 0)
+					urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1;
+  				else
+					urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer;
+			}
+  		}
+  	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+ 
+static td_t * dl_reverse_done_list (ohci_t * ohci)
+{
+	__u32 td_list_hc;
+	td_t * td_rev = NULL;
+	td_t * td_list = NULL;
+  	urb_priv_t * urb_priv = NULL;
+  	unsigned long flags;
+  	
+  	spin_lock_irqsave (&usb_ed_lock, flags);
+  	
+	td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;
+	ohci->hcca.done_head = 0;
+	
+	while (td_list_hc) {		
+		td_list = (td_t *) bus_to_virt (td_list_hc);
+
+		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
+			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
+			dbg(" USB-error/status: %x : %p", 
+					TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
+			if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
+				if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
+					td_list->ed->hwHeadP = 
+						(urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
+									(td_list->ed->hwHeadP & cpu_to_le32 (0x2));
+					urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
+				} else 
+					td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
+			}
+		}
+
+		td_list->next_dl_td = td_rev;	
+		td_rev = td_list;
+		td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;	
+	}	
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+	return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some pending requests to remove 
+ * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
+ * - some URBs/TDs if urb_priv->state == URB_DEL */
+ 
+static void dl_del_list (ohci_t  * ohci, unsigned int frame)
+{
+	unsigned long flags;
+	ed_t * ed;
+	__u32 edINFO;
+	__u32 tdINFO;
+	td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
+	__u32 * td_p;
+	int ctrl = 0, bulk = 0;
+
+	spin_lock_irqsave (&usb_ed_lock, flags);
+	for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
+
+   	 	tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+		tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+		edINFO = le32_to_cpup (&ed->hwINFO);
+		td_p = &ed->hwHeadP;
+
+		for (td = tdHeadP; td != tdTailP; td = td_next) { 
+			urb_t * urb = td->urb;
+			urb_priv_t * urb_priv = td->urb->hcpriv;
+			
+			td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+			if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
+				tdINFO = le32_to_cpup (&td->hwINFO);
+				if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td);
+				*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
+				if(++ (urb_priv->td_cnt) == urb_priv->length) 
+					urb_rm_priv (urb);
+					if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+						usb_dec_dev_use (urb->dev);
+						urb->status = -ECONNRESET;
+						urb->complete (urb); 
+					} else {	
+						wake_up (&op_wakeup);
+					}
+			} else {
+				td_p = &td->hwNextTD;
+			}
+			
+		}
+		if (ed->state & ED_DEL) { /* set by sohci_free_dev */
+			struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
+   	 		OHCI_FREE (tdTailP); /* free dummy td */
+   	 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
+   	 		ed->state = ED_NEW; 
+   	 		/* if all eds are removed wake up sohci_free_dev */
+   	 		if (!--dev->ed_cnt)
+				wake_up (&op_wakeup);
+   	 	}
+   	 	else {
+   	 		ed->state &= ~ED_URB_DEL;
+   	 		ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
+   	 	}
+   	 	
+   	 	if ((ed->type & 3) == CTRL) ctrl |= 1;
+  		if ((ed->type & 3) == BULK) bulk |= 1;  
+   	}
+   	
+   	if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
+   	if (bulk) writel (0, &ohci->regs->ed_bulkcurrent);    /* reset BULK list */
+	if (!ohci->ed_rm_list[!frame]) { 		/* enable CTRL and BULK lists */ 
+  		ohci->hc_control |= OHCI_CTRL_CLE | OHCI_CTRL_BLE;
+  		writel (ohci->hc_control, &ohci->regs->control);   
+	}
+   	ohci->ed_rm_list[frame] = NULL;
+
+   	spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+
+  		
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+
+static void dl_done_list (ohci_t * ohci, td_t * td_list)
+{
+  	td_t * td_list_next = NULL;
+	ed_t * ed;
+	int cc = 0;
+	urb_t * urb;
+	urb_priv_t * urb_priv;
+ 	__u32 tdINFO, edHeadP, edTailP;
+ 	
+ 	unsigned long flags;
+ 	
+  	while (td_list) {
+   		td_list_next = td_list->next_dl_td;
+   		
+  		urb = td_list->urb;
+  		urb_priv = urb->hcpriv;
+  		tdINFO = le32_to_cpup (&td_list->hwINFO);
+  		
+   		ed = td_list->ed;
+   		
+   		dl_transfer_length(td_list);
+ 			
+  		/* error code of transfer */
+  		cc = TD_CC_GET (tdINFO);
+  		if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+  		
+  		if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
+						cc = TD_CC_NOERROR;
+  		if (++(urb_priv->td_cnt) == urb_priv->length) {
+  			if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) { 
+  				urb->status = cc_to_error[cc];
+  				sohci_return_urb (urb);
+  			} else {
+  				urb_rm_priv (urb);
+  			}
+  		}
+  		
+  		spin_lock_irqsave (&usb_ed_lock, flags);
+  		if (ed->state != ED_NEW) { 
+  			edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+  			edTailP = le32_to_cpup (&ed->hwTailP);
+
+     		if((edHeadP == edTailP) && (ed->state == ED_OPER)) 
+     			ep_unlink (ohci, ed); /* unlink eds if they are not busy */
+     		
+     	}	
+     	spin_unlock_irqrestore (&usb_ed_lock, flags);
+     	
+    	td_list = td_list_next;
+  	}  
+}
+
+
+
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub 
+ *-------------------------------------------------------------------------*/
+ 
+/* Device descriptor */
+static __u8 root_hub_dev_des[] =
+{
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x10,	    /*  __u16 bcdUSB; v1.1 */
+	0x01,
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x00,       /*  __u8  bDeviceProtocol; */
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,       /*  __u16 idVendor; */
+	0x00,
+	0x00,       /*  __u16 idProduct; */
+ 	0x00,
+	0x00,       /*  __u16 bcdDevice; */
+ 	0x00,
+	0x00,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19,       /*  __u16 wTotalLength; */
+	0x00,
+	0x01,       /*  __u8  bNumInterfaces; */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0x40,       /*  __u8  bmAttributes; 
+                 Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+      
+	/* interface */	  
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; */
+	0x00,       /*  __u8  if_iInterface; */
+     
+	/* endpoint */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x02,       /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+ 	0x00,
+	0xff        /*  __u8  ep_bInterval; 255 ms */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ 
+ 
+static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
+{
+	int num_ports;
+	int i;
+	int ret;
+	int len;
+
+	__u8 data[8];
+
+	num_ports = readl (&ohci->regs->roothub.a) & RH_A_NDP; 
+	*(__u8 *) data = (readl (&ohci->regs->roothub.status) & (RH_HS_LPSC | RH_HS_OCIC))
+		? 1: 0;
+	ret = *(__u8 *) data;
+
+	for ( i = 0; i < num_ports; i++) {
+		*(__u8 *) (data + (i + 1) / 8) |= 
+			((readl (&ohci->regs->roothub.portstatus[i]) &
+				(RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC))
+			    ? 1: 0) << ((i + 1) % 8);
+		ret += *(__u8 *) (data + (i + 1) / 8);
+	}
+	len = i/8 + 1;
+  
+	if (ret > 0) { 
+		memcpy (rh_data, data, min (len, min (rh_len, sizeof(data))));
+		return len;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+ 
+static void rh_int_timer_do (unsigned long ptr)
+{
+	int len; 
+
+	urb_t * urb = (urb_t *) ptr;
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	if (ohci->disabled)
+	    return;
+	
+	if(ohci->rh.send) { 
+		len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
+		if (len > 0) {
+			urb->actual_length = len;
+#ifdef DEBUG
+			urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
+#endif
+			if (urb->complete) urb->complete (urb);
+		}
+	}	
+	rh_init_int_timer (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Root Hub INTs are polled by this timer */
+
+static int rh_init_int_timer (urb_t * urb) 
+{
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	ohci->rh.interval = urb->interval;
+	init_timer (&ohci->rh.rh_int_timer);
+	ohci->rh.rh_int_timer.function = rh_int_timer_do;
+	ohci->rh.rh_int_timer.data = (unsigned long) urb;
+	ohci->rh.rh_int_timer.expires = 
+			jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
+	add_timer (&ohci->rh.rh_int_timer);
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x) 			len = (x); break
+#define WR_RH_STAT(x) 		writel((x), &ohci->regs->roothub.status)
+#define WR_RH_PORTSTAT(x) 	writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
+#define RD_RH_STAT		readl(&ohci->regs->roothub.status)
+#define RD_RH_PORTSTAT		readl(&ohci->regs->roothub.portstatus[wIndex-1])
+
+/* request to virtual root hub */
+
+static int rh_submit_urb (urb_t * urb)
+{
+	struct usb_device * usb_dev = urb->dev;
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	devrequest * cmd = (devrequest *) urb->setup_packet;
+	void * data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = TD_CC_NOERROR;
+	
+	__u32 datab[4];
+	__u8  * data_buf = (__u8 *) datab;
+	
+ 	__u16 bmRType_bReq;
+	__u16 wValue; 
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipeint(pipe)) {
+	
+		ohci->rh.urb =  urb;
+		ohci->rh.send = 1;
+		ohci->rh.interval = urb->interval;
+		rh_init_int_timer(urb);
+		urb->status = cc_to_error [TD_CC_NOERROR];
+		
+		return 0;
+	}
+
+	bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
+	wValue        = le16_to_cpu (cmd->value);
+	wIndex        = le16_to_cpu (cmd->index);
+	wLength       = le16_to_cpu (cmd->length);
+
+	dbg ("rh_submit_urb, req = %d(%x) len=%d", bmRType_bReq,
+		bmRType_bReq, wLength);
+
+	switch (bmRType_bReq) {
+	/* Request Destination:
+	   without flags: Device, 
+	   RH_INTERFACE: interface, 
+	   RH_ENDPOINT: endpoint,
+	   RH_CLASS means HUB here, 
+	   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+	*/
+  
+		case RH_GET_STATUS: 				 		
+				*(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
+		case RH_GET_STATUS | RH_INTERFACE: 	 		
+				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+		case RH_GET_STATUS | RH_ENDPOINT:	 		
+				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);   
+		case RH_GET_STATUS | RH_CLASS: 				
+				*(__u32 *) data_buf = cpu_to_le32 (
+					RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+				OK (4);
+		case RH_GET_STATUS | RH_OTHER | RH_CLASS: 	
+				*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
+
+		case RH_CLEAR_FEATURE | RH_ENDPOINT:  
+			switch (wValue) {
+				case (RH_ENDPOINT_STALL): OK (0);
+			}
+			break;
+
+		case RH_CLEAR_FEATURE | RH_CLASS:
+			switch (wValue) {
+				case RH_C_HUB_LOCAL_POWER:
+					OK(0);
+				case (RH_C_HUB_OVER_CURRENT): 
+						WR_RH_STAT(RH_HS_OCIC); OK (0);
+			}
+			break;
+		
+		case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+			switch (wValue) {
+				case (RH_PORT_ENABLE): 			
+						WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+				case (RH_PORT_SUSPEND):			
+						WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+				case (RH_PORT_POWER):			
+						WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+				case (RH_C_PORT_CONNECTION):	
+						WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+				case (RH_C_PORT_ENABLE):		
+						WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+				case (RH_C_PORT_SUSPEND):		
+						WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+				case (RH_C_PORT_OVER_CURRENT):	
+						WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+				case (RH_C_PORT_RESET):			
+						WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); 
+			}
+			break;
+ 
+		case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+			switch (wValue) {
+				case (RH_PORT_SUSPEND):			
+						WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); 
+				case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+						if (RD_RH_PORTSTAT & RH_PS_CCS)
+						    WR_RH_PORTSTAT (RH_PS_PRS);
+						OK (0);
+				case (RH_PORT_POWER):			
+						WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); 
+				case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+						if (RD_RH_PORTSTAT & RH_PS_CCS)
+						    WR_RH_PORTSTAT (RH_PS_PES );
+						OK (0);
+			}
+			break;
+
+		case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
+
+		case RH_GET_DESCRIPTOR:
+			switch ((wValue & 0xff00) >> 8) {
+				case (0x01): /* device descriptor */
+					len = min (leni, min (sizeof (root_hub_dev_des), wLength));
+					data_buf = root_hub_dev_des; OK(len);
+				case (0x02): /* configuration descriptor */
+					len = min (leni, min (sizeof (root_hub_config_des), wLength));
+					data_buf = root_hub_config_des; OK(len);
+				case (0x03): /* string descriptors */
+					len = usb_root_hub_string (wValue & 0xff,
+						(int)(long) ohci->regs, "OHCI",
+						data, wLength);
+					if (len > 0) {
+						data_buf = data;
+						OK (min (leni, len));
+					}
+					// else fallthrough
+				default: 
+					status = TD_CC_STALL;
+			}
+			break;
+		
+		case RH_GET_DESCRIPTOR | RH_CLASS:
+		    {
+			    __u32 temp = readl (&ohci->regs->roothub.a);
+
+			    data_buf [0] = 9;		// min length;
+			    data_buf [1] = 0x29;
+			    data_buf [2] = temp & RH_A_NDP;
+			    data_buf [3] = 0;
+			    if (temp & RH_A_PSM) 	/* per-port power switching? */
+				data_buf [3] |= 0x1;
+			    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
+				data_buf [3] |= 0x10;
+			    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
+				data_buf [3] |= 0x8;
+
+			    datab [1] = 0;
+			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+			    temp = readl (&ohci->regs->roothub.b);
+			    data_buf [7] = temp & RH_B_DR;
+			    if (data_buf [2] < 7) {
+				data_buf [8] = 0xff;
+			    } else {
+				data_buf [0] += 2;
+				data_buf [8] = (temp & RH_B_DR) >> 8;
+				data_buf [10] = data_buf [9] = 0xff;
+			    }
+				
+			    len = min (leni, min (data_buf [0], wLength));
+			    OK (len);
+			}
+ 
+		case RH_GET_CONFIGURATION: 	*(__u8 *) data_buf = 0x01; OK (1);
+
+		case RH_SET_CONFIGURATION: 	WR_RH_STAT (0x10000); OK (0);
+
+		default: 
+			dbg ("unsupported root hub command");
+			status = TD_CC_STALL;
+	}
+	
+#ifdef	DEBUG
+	ohci_dump_roothub (ohci, 0);
+#endif
+
+	len = min(len, leni);
+	if (data != data_buf)
+	    memcpy (data, data_buf, len);
+  	urb->actual_length = len;
+	urb->status = cc_to_error [status];
+	
+#ifdef DEBUG
+	urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+#endif
+
+	if (urb->complete) urb->complete (urb);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_unlink_urb (urb_t * urb)
+{
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+ 
+	if (ohci->rh.urb == urb) {
+		ohci->rh.send = 0;
+		del_timer (&ohci->rh.rh_int_timer);
+	}
+	return 0;
+}
+ 
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (ohci_t * ohci)
+{
+	int timeout = 30;
+	int smm_timeout = 50; /* 0,5 sec */
+	 	
+	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+		dbg("USB HC TakeOver from SMM");
+		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+			wait_ms (10);
+			if (--smm_timeout == 0) {
+				err("USB HC TakeOver failed!");
+				return -1;
+			}
+		}
+	}	
+		
+	/* Disable HC interrupts */
+	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+	dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control));
+
+  	/* Reset USB (needed by some controllers) */
+	writel (0, &ohci->regs->control);
+      	
+	/* HC Reset requires max 10 ms delay */
+	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+		if (--timeout == 0) {
+			err("USB HC reset timed out!");
+			return -1;
+		}	
+		udelay (1);
+	}	 
+	ohci->disabled = 0;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts 
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+  	__u32 mask;
+  	unsigned int fminterval;
+  	struct usb_device  * usb_dev;
+	struct ohci_device * dev;
+	
+	/* Tell the controller where the control and bulk lists are
+	 * The lists are empty now. */
+	 
+	writel (0, &ohci->regs->ed_controlhead);
+	writel (0, &ohci->regs->ed_bulkhead);
+	
+	writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */
+   
+  	fminterval = 0x2edf;
+	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); 
+	writel (fminterval, &ohci->regs->fminterval);	
+	writel (0x628, &ohci->regs->lsthresh);
+
+ 	/* start controller operations */
+ 	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ 	writel (ohci->hc_control, &ohci->regs->control);
+ 
+	/* Choose the interrupts we care about now, others later on demand */
+	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	writel (mask, &ohci->regs->intrenable);
+	writel (mask, &ohci->regs->intrstatus);
+
+#ifdef	OHCI_USE_NPS
+	writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM,
+		&ohci->regs->roothub.a);
+	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+	// POTPGT delay is bits 24-31, in 2 ms units.
+	mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);
+#endif	/* OHCI_USE_NPS */
+ 
+	/* connect the virtual root hub */
+	ohci->rh.devnum = 0;
+	usb_dev = usb_alloc_dev (NULL, ohci->bus);
+	if (!usb_dev)
+	    return -ENOMEM;
+
+	dev = usb_to_ohci (usb_dev);
+	ohci->bus->root_hub = usb_dev;
+	usb_connect (usb_dev);
+	if (usb_new_device (usb_dev) != 0) {
+		usb_free_dev (usb_dev); 
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
+{
+	ohci_t * ohci = __ohci;
+	struct ohci_regs * regs = ohci->regs;
+ 	int ints; 
+
+	if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) {
+		ints =  OHCI_INTR_WDH;
+	} else { 
+ 		if ((ints = (readl (&regs->intrstatus) & readl (&regs->intrenable))) == 0)
+			return;
+	} 
+
+	// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
+
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, controller disabled");
+		// e.g. due to PCI Master/Target Abort
+	}
+  
+	if (ints & OHCI_INTR_WDH) {
+		writel (OHCI_INTR_WDH, &regs->intrdisable);	
+		dl_done_list (ohci, dl_reverse_done_list (ohci));
+		writel (OHCI_INTR_WDH, &regs->intrenable); 
+	}
+  
+	if (ints & OHCI_INTR_SO) {
+		dbg("USB Schedule overrun");
+		writel (OHCI_INTR_SO, &regs->intrenable); 	 
+	}
+
+	if (ints & OHCI_INTR_SF) { 
+		unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1;
+		writel (OHCI_INTR_SF, &regs->intrdisable);	
+		if (ohci->ed_rm_list[!frame] != NULL) {
+			dl_del_list (ohci, !frame);
+		}
+		if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, &regs->intrenable);	
+	}
+	writel (ints, &regs->intrstatus);
+	writel (OHCI_INTR_MIE, &regs->intrenable);	
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* reinitialize after controller reset */
+
+static void hc_reinit_ohci (ohci_t *ohci)
+{
+	int i;
+
+	/* for load balancing of the interrupt branches */
+	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
+	for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0;
+	
+	ohci->ed_rm_list [0] = NULL;
+	ohci->ed_rm_list [1] = NULL;
+
+	/* end of control and bulk lists */	 
+	ohci->ed_isotail     = NULL;
+	ohci->ed_controltail = NULL;
+	ohci->ed_bulktail    = NULL;
+} 
+
+/* allocate OHCI */
+
+static ohci_t * hc_alloc_ohci (void * mem_base)
+{
+	ohci_t * ohci;
+	struct usb_bus * bus;
+
+	ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1);
+	if (!ohci)
+		return NULL;
+		
+	memset (ohci, 0, sizeof (ohci_t));
+	
+	ohci->irq = -1;
+	ohci->regs = mem_base;   
+
+	bus = usb_alloc_bus (&sohci_device_operations);
+	if (!bus) {
+		free_pages ((unsigned long) ohci, 1);
+		return NULL;
+	}
+
+	ohci->bus = bus;
+	bus->hcpriv = (void *) ohci;
+ 
+	return ohci;
+} 
+
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t * ohci)
+{	
+	dbg("USB HC release ohci");
+
+	/* disconnect all devices */    
+	if (ohci->bus->root_hub)
+		usb_disconnect (&ohci->bus->root_hub);
+
+	hc_reset (ohci);
+	writel (OHCI_USB_RESET, &ohci->regs->control);
+	wait_ms (10);
+	
+	if (ohci->irq >= 0) {
+		free_irq (ohci->irq, ohci);
+		ohci->irq = -1;
+	}
+
+	usb_deregister_bus (ohci->bus);
+	usb_free_bus (ohci->bus);
+    
+	/* unmap the IO address space */
+	iounmap (ohci->regs);
+       
+	free_pages ((unsigned long) ohci, 1);	
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Increment the module usage count, start the control thread and
+ * return success. */
+ 
+static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base)
+{
+	ohci_t * ohci;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+		(unsigned long)	mem_base, bufp);
+    
+	ohci = hc_alloc_ohci (mem_base);
+	if (!ohci) {
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
+
+	if (hc_reset (ohci) < 0) {
+		hc_release_ohci (ohci);
+		return -ENODEV;
+	}
+
+	writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
+	wait_ms (10);
+	usb_register_bus (ohci->bus);
+	
+	if (request_irq (irq, hc_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) {
+		struct pm_dev *pmdev;
+
+		ohci->irq = irq;     
+		hc_start (ohci);
+
+		pmdev = pm_register (PM_PCI_DEV,
+				     PM_PCI_ID(dev),
+				     handle_pm_event);
+		if (pmdev)
+			pmdev->data = ohci;
+
+#ifdef	DEBUG
+		ohci_dump (ohci, 1);
+#endif
+
+		return 0;
+ 	}	
+ 	err("request interrupt %d failed", irq);
+	hc_release_ohci (ohci);
+	return -EBUSY;
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+static int hc_start_ohci (struct pci_dev * dev)
+{
+	unsigned long mem_base;
+	u16 cmd;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+	
+	pci_set_master (dev);
+	mem_base = pci_resource_start(dev, 0);
+	mem_base = (unsigned long) ioremap_nocache (mem_base, 4096);
+
+	/* Some Mac firmware will switch memory response off */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
+	if (!mem_base) {
+		err("Error mapping OHCI memory");
+		return -EFAULT;
+	}
+	return hc_found_ohci (dev, dev->irq, (void *) mem_base);
+} 
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PMAC_PBOOK
+
+/* On Powerbooks, put the controller into suspend mode when going
+ * to sleep, and do a resume when waking up. */
+
+static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when)
+{
+	struct list_head * ohci_l;
+	ohci_t * ohci;
+       
+	for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
+	ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
+
+		switch (when) {
+		case PBOOK_SLEEP_NOW:
+			disable_irq (ohci->irq);
+			writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control);
+			wait_ms (10);
+			break;
+		case PBOOK_WAKE:
+ 			writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control);
+			wait_ms (20);
+			ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+			writel (ohci->hc_control, &ohci->regs->control);
+			enable_irq (ohci->irq);
+			break;
+		}
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+static struct pmu_sleep_notifier ohci_sleep_notifier = {
+       ohci_sleep_notify, SLEEP_LEVEL_MISC,
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+/*-------------------------------------------------------------------------*/
+
+static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	ohci_t * ohci = (ohci_t*) dev->data;
+	int temp = 0;
+
+	if (ohci) {
+		switch (rqst) {
+		case PM_SUSPEND:
+			/* act as if usb suspend can always be used */
+			dbg("USB suspend: %p", ohci->regs);
+			ohci->hc_control = OHCI_USB_SUSPEND;
+			writel (ohci->hc_control, &ohci->regs->control);
+			wait_ms (10);
+			break;
+
+		case PM_RESUME:
+			/* did we suspend, or were we powered off? */
+			ohci->hc_control = readl (&ohci->regs->control);
+			temp = ohci->hc_control & OHCI_CTRL_HCFS;
+			switch (temp) {
+
+			case OHCI_USB_RESET:	// lost power
+				dbg("USB reset: %p", ohci->regs);
+				ohci->disabled = 1;
+				if (ohci->bus->root_hub)
+					usb_disconnect (&ohci->bus->root_hub);
+				hc_reinit_ohci (ohci);
+				if ((temp = hc_reset (ohci)) < 0
+					|| (temp = hc_start (ohci)) < 0) {
+					ohci->disabled = 1;
+					err ("can't restart, %d", temp);
+				}
+				dbg ("reset done");
+				break;
+
+			case OHCI_USB_SUSPEND:	// host wakeup
+			case OHCI_USB_RESUME:	// remote wakeup
+				dbg("USB resume: %p", ohci->regs);
+				ohci->hc_control = OHCI_USB_RESUME;
+				writel (ohci->hc_control, &ohci->regs->control);
+				wait_ms (20);
+
+				ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+				writel (ohci->hc_control, &ohci->regs->control);
+				break;
+
+			default:
+				warn ("odd PM_RESUME");
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
+ 
+static int __init ohci_hcd_init (void) 
+{
+	int ret = -ENODEV;
+	struct pci_dev * dev = NULL;
+ 
+	while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) { 
+		if (hc_start_ohci(dev) >= 0) ret = 0;
+	}
+    
+#ifdef CONFIG_PMAC_PBOOK
+	pmu_register_sleep_notifier (&ohci_sleep_notifier);
+#endif  
+    return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_cleanup (void) 
+{	
+	ohci_t * ohci;
+	
+	pm_unregister_all (handle_pm_event);
+
+#ifdef CONFIG_PMAC_PBOOK
+	pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
+#endif  
+
+	while (!list_empty (&ohci_hcd_list)) {
+		ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list);
+		list_del (&ohci->ohci_hcd_list);
+		INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+		hc_release_ohci (ohci);
+	}		
+}
+
+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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-ohci.h linux.18p17/drivers/usb/usb-ohci.h
--- linux.vanilla/drivers/usb/usb-ohci.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-ohci.h	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,450 @@
+ /*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ * 
+ *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * 
+ * usb-ohci.h
+ * 
+ */
+
+ 
+#define MODSTR "ohci: "
+
+
+static int cc_to_error[16] = { 
+
+/* mapping of the OHCI CC status to error codes */ 
+#ifdef USB_ST_CRC /* status codes */
+	/* No  Error  */               USB_ST_NOERROR,
+	/* CRC Error  */               USB_ST_CRC,
+	/* Bit Stuff  */               USB_ST_BITSTUFF,
+	/* Data Togg  */               USB_ST_CRC,
+	/* Stall      */               USB_ST_STALL,
+	/* DevNotResp */               USB_ST_NORESPONSE,
+	/* PIDCheck   */               USB_ST_BITSTUFF,
+	/* UnExpPID   */               USB_ST_BITSTUFF,
+	/* DataOver   */               USB_ST_DATAOVERRUN,
+	/* DataUnder  */               USB_ST_DATAUNDERRUN,
+	/* reservd    */               USB_ST_NORESPONSE,
+	/* reservd    */               USB_ST_NORESPONSE,
+	/* BufferOver */               USB_ST_BUFFEROVERRUN,
+	/* BuffUnder  */               USB_ST_BUFFERUNDERRUN,
+	/* Not Access */               USB_ST_NORESPONSE,
+	/* Not Access */               USB_ST_NORESPONSE 
+};
+
+#else  /* error codes */
+	/* No  Error  */               0,
+	/* CRC Error  */               -EILSEQ,
+	/* Bit Stuff  */               -EPROTO,
+	/* Data Togg  */               -EILSEQ,
+	/* Stall      */               -EPIPE,
+	/* DevNotResp */               -ETIMEDOUT,
+	/* PIDCheck   */               -EPROTO,
+	/* UnExpPID   */               -EPROTO,
+	/* DataOver   */               -EOVERFLOW,
+	/* DataUnder  */               -EREMOTEIO,
+	/* reservd    */               -ETIMEDOUT,
+	/* reservd    */               -ETIMEDOUT,
+	/* BufferOver */               -ECOMM,
+	/* BuffUnder  */               -ECOMM,
+	/* Not Access */               -ETIMEDOUT,
+	/* Not Access */               -ETIMEDOUT  
+};
+#define USB_ST_URB_PENDING		-EINPROGRESS
+#endif
+
+ 
+
+struct ed;
+struct td;
+/* for ED and TD structures */
+
+/* ED States */
+
+#define ED_NEW 		0x00
+#define ED_UNLINK 	0x01
+#define ED_OPER		0x02
+#define ED_DEL		0x04
+#define ED_URB_DEL  0x08
+
+/* usb_ohci_ed */
+typedef struct ed {
+	__u32 hwINFO;       
+	__u32 hwTailP;
+	__u32 hwHeadP;
+	__u32 hwNextED;
+
+	struct ed * ed_prev;  
+	__u8 int_period;
+	__u8 int_branch;
+	__u8 int_load; 
+	__u8 int_interval;
+	__u8 state;
+	__u8 type; 
+	__u16 last_iso;
+    struct ed * ed_rm_list;
+   
+} ed_t;
+
+ 
+/* TD info field */
+#define TD_CC       0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC       0x0C000000
+#define TD_T        0x03000000
+#define TD_T_DATA0  0x02000000
+#define TD_T_DATA1  0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R        0x00040000
+#define TD_DI       0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP       0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN    0x00100000
+#define TD_DP_OUT   0x00080000
+
+#define TD_ISO		0x00010000
+#define TD_DEL      0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+#define TD_NOTACCESSED     0x0F
+
+
+#define MAXPSW 1
+
+typedef struct td { 
+	__u32 hwINFO;
+  	__u32 hwCBP;		/* Current Buffer Pointer */
+  	__u32 hwNextTD;		/* Next TD Pointer */
+  	__u32 hwBE;		/* Memory Buffer End Pointer */
+  	__u16 hwPSW[MAXPSW];
+
+  	__u8 type;
+  	__u8 index;
+  	struct ed * ed;
+  	struct td * next_dl_td;
+  	urb_t * urb;
+} td_t;
+
+
+/* TD types */
+#define BULK		0x03
+#define INT			0x01
+#define CTRL		0x02
+#define ISO			0x00
+ 
+#define SEND            0x01
+#define ST_ADDR         0x02
+#define ADD_LEN         0x04
+#define DEL             0x08
+
+
+#define OHCI_ED_SKIP	(1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of.  It must be 256-byte aligned.
+ */
+ 
+#define NUM_INTS 32	/* part of the OHCI standard */
+struct ohci_hcca {
+    __u32	int_table[NUM_INTS];	/* Interrupt ED table */
+	__u16	frame_no;		/* current frame number */
+	__u16	pad1;			/* set to 0 on each frame_no change */
+	__u32	done_head;		/* info returned for an interrupt */
+	u8		reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+  
+/*
+ * Maximum number of root hub ports.  
+ */
+#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region.  This is Memory Mapped I/O.  You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+	/* control and status registers */
+	__u32	revision;
+	__u32	control;
+	__u32	cmdstatus;
+	__u32	intrstatus;
+	__u32	intrenable;
+	__u32	intrdisable;
+	/* memory pointers */
+	__u32	hcca;
+	__u32	ed_periodcurrent;
+	__u32	ed_controlhead;
+	__u32	ed_controlcurrent;
+	__u32	ed_bulkhead;
+	__u32	ed_bulkcurrent;
+	__u32	donehead;
+	/* frame counters */
+	__u32	fminterval;
+	__u32	fmremaining;
+	__u32	fmnumber;
+	__u32	periodicstart;
+	__u32	lsthresh;
+	/* Root hub ports */
+	struct	ohci_roothub_regs {
+		__u32	a;
+		__u32	b;
+		__u32	status;
+		__u32	portstatus[MAX_ROOT_PORTS];
+	} roothub;
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
+#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
+#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
+#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
+#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
+#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
+#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
+#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
+#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#	define OHCI_USB_RESET	(0 << 6)
+#	define OHCI_USB_RESUME	(1 << 6)
+#	define OHCI_USB_OPER	(2 << 6)
+#	define OHCI_USB_SUSPEND	(3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR	(1 << 0)	/* host controller reset */
+#define OHCI_CLF  	(1 << 1)	/* control list filled */
+#define OHCI_BLF  	(1 << 2)	/* bulk list filled */
+#define OHCI_OCR  	(1 << 3)	/* ownership change request */
+#define OHCI_SOC  	(3 << 16)	/* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
+#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
+#define OHCI_INTR_SF	(1 << 2)	/* start frame */
+#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
+#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
+#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
+#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
+#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
+#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
+
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+	int devnum; /* Address of Root Hub endpoint */ 
+	void * urb;
+	void * int_addr;
+	int send;
+	int interval;
+	struct timer_list rh_int_timer;
+};
+
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+ 
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14  
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+ 
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS            0x00000001   	/* current connect status */
+#define RH_PS_PES            0x00000002   	/* port enable status*/
+#define RH_PS_PSS            0x00000004   	/* port suspend status */
+#define RH_PS_POCI           0x00000008   	/* port over current indicator */
+#define RH_PS_PRS            0x00000010  	/* port reset status */
+#define RH_PS_PPS            0x00000100   	/* port power status */
+#define RH_PS_LSDA           0x00000200    	/* low speed device attached */
+#define RH_PS_CSC            0x00010000 	/* connect status change */
+#define RH_PS_PESC           0x00020000   	/* port enable status change */
+#define RH_PS_PSSC           0x00040000    	/* port suspend status change */
+#define RH_PS_OCIC           0x00080000    	/* over current indicator change */
+#define RH_PS_PRSC           0x00100000   	/* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS	     0x00000001		/* local power status */
+#define RH_HS_OCI	     0x00000002		/* over current indicator */
+#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
+#define RH_HS_LPSC	     0x00010000		/* local power status change */
+#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
+#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR		0x0000ffff		/* device removable flags */
+#define RH_B_PPCM	0xffff0000		/* port power control mask */
+
+/* roothub.a masks */
+#define	RH_A_NDP	(0xff << 0)		/* number of downstream ports */
+#define	RH_A_PSM	(1 << 8)		/* power switching mode */
+#define	RH_A_NPS	(1 << 9)		/* no power switching */
+#define	RH_A_DT		(1 << 10)		/* device type (mbz) */
+#define	RH_A_OCPM	(1 << 11)		/* over current protection mode */
+#define	RH_A_NOCP	(1 << 12)		/* no over current protection */
+#define	RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
+
+#define min(a,b) (((a)<(b))?(a):(b))  
+ 
+
+/* urb */
+typedef struct 
+{
+	ed_t * ed;
+	__u16 length;	// number of tds associated with this request
+	__u16 td_cnt;	// number of tds already serviced
+	int   state;
+	void * wait;
+	td_t * td[0];	// list pointer to all corresponding TDs associated with this request
+
+} urb_priv_t;
+#define URB_DEL 1
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+	struct ohci_hcca hcca;		/* hcca */
+
+	int irq;
+	int disabled;			/* e.g. got a UE, we're hung */
+
+	struct ohci_regs * regs;	/* OHCI controller's memory */
+	struct list_head ohci_hcd_list;	/* list of all ohci_hcd */
+
+	struct ohci * next; 		// chain of uhci device contexts
+	struct list_head urb_list; 	// list of all pending urbs
+	spinlock_t urb_list_lock; 	// lock to keep consistency 
+  
+	int ohci_int_load[32];		/* load of the 32 Interrupt Chains (for load balancing)*/
+	ed_t * ed_rm_list[2];     /* lists of all endpoints to be removed */
+	ed_t * ed_bulktail;       /* last endpoint of bulk list */
+	ed_t * ed_controltail;    /* last endpoint of control list */
+ 	ed_t * ed_isotail;        /* last endpoint of iso list */
+	int intrstatus;
+	__u32 hc_control;		/* copy of the hc control reg */
+	struct usb_bus * bus;    
+	struct usb_device * dev[128];
+	struct virt_root_hub rh;
+} ohci_t;
+
+
+#define NUM_TDS	0		/* num of preallocated transfer descriptors */
+#define NUM_EDS 32		/* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+	ed_t 	ed[NUM_EDS];
+	int ed_cnt;
+	void  * wait;
+};
+
+// #define ohci_to_usb(ohci)	((ohci)->usb)
+#define usb_to_ohci(usb)	((struct ohci_device *)(usb)->hcpriv)
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load);
+static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
+/* td */
+static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index);
+static void td_submit_urb(urb_t * urb);
+/* root hub */
+static int rh_submit_urb(urb_t * urb);
+static int rh_unlink_urb(urb_t * urb);
+static int rh_init_int_timer(urb_t * urb);
+
+#ifdef DEBUG
+#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x)
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
+static int __ohci_free_cnt = 0;
+#else
+#define OHCI_FREE(x) kfree(x) 
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) 
+#endif
+ 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-storage-debug.h linux.18p17/drivers/usb/usb-storage-debug.h
--- linux.vanilla/drivers/usb/usb-storage-debug.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-storage-debug.h	Fri Sep  1 14:42:41 2000
@@ -0,0 +1,89 @@
+#include <linux/config.h>
+#ifdef CONFIG_USB_STORAGE_DEBUG
+
+/* Debug output for Driver for USB mass storage (scsi-like) devices
+ * 
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+void us_show_command(Scsi_Cmnd *srb)
+{
+	char *what = NULL;
+
+	switch (srb->cmnd[0]) {
+	case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+	case REZERO_UNIT: what = "REZERO_UNIT"; break;
+	case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+	case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+	case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+	case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+	case READ_6: what = "READ_6"; break;
+	case WRITE_6: what = "WRITE_6"; break;
+	case SEEK_6: what = "SEEK_6"; break;
+	case READ_REVERSE: what = "READ_REVERSE"; break;
+	case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+	case SPACE: what = "SPACE"; break;
+	case INQUIRY: what = "INQUIRY"; break;
+	case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+	case MODE_SELECT: what = "MODE_SELECT"; break;
+	case RESERVE: what = "RESERVE"; break;
+	case RELEASE: what = "RELEASE"; break;
+	case COPY: what = "COPY"; break;
+	case ERASE: what = "ERASE"; break;
+	case MODE_SENSE: what = "MODE_SENSE"; break;
+	case START_STOP: what = "START_STOP"; break;
+	case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+	case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+	case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+	case SET_WINDOW: what = "SET_WINDOW"; break;
+	case READ_CAPACITY: what = "READ_CAPACITY"; break;
+	case READ_10: what = "READ_10"; break;
+	case WRITE_10: what = "WRITE_10"; break;
+	case SEEK_10: what = "SEEK_10"; break;
+	case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+	case VERIFY: what = "VERIFY"; break;
+	case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+	case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+	case SEARCH_LOW: what = "SEARCH_LOW"; break;
+	case SET_LIMITS: what = "SET_LIMITS"; break;
+	case READ_POSITION: what = "READ_POSITION"; break;
+	case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+	case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+	case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+	case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+	case COMPARE: what = "COMPARE"; break;
+	case COPY_VERIFY: what = "COPY_VERIFY"; break;
+	case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+	case READ_BUFFER: what = "READ_BUFFER"; break;
+	case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+	case READ_LONG: what = "READ_LONG"; break;
+	case WRITE_LONG: what = "WRITE_LONG"; break;
+	case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+	case WRITE_SAME: what = "WRITE_SAME"; break;
+	case READ_TOC: what = "READ_TOC"; break;
+	case LOG_SELECT: what = "LOG_SELECT"; break;
+	case LOG_SENSE: what = "LOG_SENSE"; break;
+	case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+	case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+	case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+	case READ_12: what = "READ_12"; break;
+	case WRITE_12: what = "WRITE_12"; break;
+	case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+	case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+	case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+	case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+	case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+	case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+	case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+	default: break;
+	}
+	printk(KERN_DEBUG USB_STORAGE
+	       "Command %s (%d bytes)\n", what, srb->cmd_len);
+	printk(KERN_DEBUG USB_STORAGE 
+	       "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	       srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], 
+	       srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-storage.c linux.18p17/drivers/usb/usb-storage.c
--- linux.vanilla/drivers/usb/usb-storage.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-storage.c	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,2200 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * Initial work by:
+ *   (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * Current development and maintainance by:
+ *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices.  Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document.  The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/usb.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb-storage.h"
+#include "usb-storage-debug.h"
+
+/* direction table -- this indicates the direction of the data
+ * transfer for each command code -- a 1 indicates input
+ */
+/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd
+ * structure, not this table.  First we need to evaluate if it's being set
+ * correctly for us, though
+ */
+unsigned char us_direction[256/8] = {
+	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, 
+	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+struct us_data;
+
+typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
+typedef int (*trans_reset)(struct us_data*);
+typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
+
+/* we allocate one of these for every device that we remember */
+struct us_data {
+	struct us_data		*next;		 /* next device */
+
+	/* the device we're working with */
+	struct semaphore	dev_semaphore;	 /* protect pusb_dev */
+	struct usb_device	*pusb_dev;	 /* this usb_device */
+
+	unsigned int		flags;		 /* from filter initially */
+
+	/* information about the device -- always good */
+	char			vendor[USB_STOR_STRING_LEN];
+	char			product[USB_STOR_STRING_LEN];
+	char			serial[USB_STOR_STRING_LEN];
+	char			*transport_name;
+	char			*protocol_name;
+	u8			subclass;
+	u8			protocol;
+
+	/* information about the device -- only good if device is attached */
+	u8			ifnum;		 /* interface number   */
+	u8			ep_in;		 /* bulk in endpoint   */
+	u8			ep_out;		 /* bulk out endpoint  */
+	struct usb_endpoint_descriptor *ep_int;	 /* interrupt endpoint */ 
+
+	/* function pointers for this device */
+	trans_cmnd		transport;	 /* transport function	   */
+	trans_reset		transport_reset; /* transport device reset */
+	proto_cmnd		proto_handler;	 /* protocol handler	   */
+
+	/* SCSI interfaces */
+	GUID(guid);				 /* unique dev id	*/
+	struct Scsi_Host	*host;		 /* our dummy host data */
+	Scsi_Host_Template	htmplt;		 /* own host template	*/
+	int			host_number;	 /* to find us		*/
+	int			host_no;	 /* allocated by scsi	*/
+	Scsi_Cmnd		*srb;		 /* current srb		*/
+	
+	/* thread information */
+	Scsi_Cmnd		*queue_srb;	 /* the single queue slot */
+	int			action;		 /* what to do		  */
+	int			pid;		 /* control thread	  */
+
+	/* interrupt info for CBI devices -- only good if attached */
+	struct semaphore	ip_waitq;	 /* for CBI interrupts	 */
+	int			ip_wanted;	 /* is an IRQ expected?	 */
+
+	/* interrupt communications data */
+	struct semaphore	irq_urb_sem;	 /* to protect irq_urb	 */
+	struct urb		*irq_urb;	 /* for USB int requests */
+	unsigned char		irqbuf[2];	 /* buffer for USB IRQ	 */
+
+	/* control and bulk communications data */
+	struct semaphore	current_urb_sem; /* to protect irq_urb	 */
+	struct urb		*current_urb;	 /* non-int USB requests */
+
+	/* mutual exclusion structures */
+	struct semaphore	notify;		 /* thread begin/end	    */
+	struct semaphore	sleeper;	 /* to sleep the thread on  */
+	struct semaphore	queue_exclusion; /* to protect data structs */
+};
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND		1
+#define US_ACT_DEVICE_RESET	2
+#define US_ACT_BUS_RESET	3
+#define US_ACT_HOST_RESET	4
+#define US_ACT_EXIT		5
+
+/* The list of structures and the protective lock for them */
+static struct us_data *us_list;
+struct semaphore us_list_semaphore;
+
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
+static void storage_disconnect(struct usb_device *dev, void *ptr);
+static struct usb_driver storage_driver = {
+	name:		"usb-storage",
+	probe:		storage_probe,
+	disconnect:	storage_disconnect,
+};
+
+struct proc_dir_entry proc_scsi_usb_scsi =
+{PROC_SCSI_USB_SCSI, 3, "usb", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+/***********************************************************************
+ * Data transfer routines
+ ***********************************************************************/
+
+/* This is the completion handler which will wake us up when an URB
+ * completes.
+ */
+static void usb_stor_blocking_completion(urb_t *urb)
+{
+	api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+	if (waitqueue_active(awd->wakeup))
+		wake_up(awd->wakeup);
+}
+
+/* This is our function to emulate usb_control_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
+			 u8 request, u8 requesttype, u16 value, u16 index, 
+			 void *data, u16 size)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+	devrequest *dr;
+
+	/* allocate the device request structure */
+	dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	if (!dr)
+		return -ENOMEM;
+
+	/* fill in the structure */
+	dr->requesttype = requesttype;
+	dr->request = request;
+	dr->value = cpu_to_le16(value);
+	dr->index = cpu_to_le16(index);
+	dr->length = cpu_to_le16(size);
+
+	/* set up data structures for the wakeup system */
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	add_wait_queue(&wqh, &wait);
+
+	/* lock the URB */
+	down(&(us->current_urb_sem));
+
+	/* fill the URB */
+	FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
+			 (unsigned char*) dr, data, size, 
+			 usb_stor_blocking_completion, &awd);
+
+	/* submit the URB */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	status = usb_submit_urb(us->current_urb);
+	if (status) {
+		/* something went wrong */
+		up(&(us->current_urb_sem));
+		remove_wait_queue(&wqh, &wait);
+		kfree(dr);
+		return status;
+	}
+
+	/* wait for the completion of the URB */
+	up(&(us->current_urb_sem));
+	if (us->current_urb->status == -EINPROGRESS)
+		schedule_timeout(10*HZ);
+	down(&(us->current_urb_sem));
+
+	/* we either timed out or got woken up -- clean up either way */
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&wqh, &wait);
+
+	/* did we time out? */
+	if (us->current_urb->status == -EINPROGRESS) {
+		US_DEBUGP("usb_stor_control_msg() timeout\n");
+		usb_unlink_urb(us->current_urb);
+		status = -ETIMEDOUT;
+	} else
+		status = us->current_urb->status;
+
+	/* return the actual length of the data transferred if no error*/
+	if (status >= 0)
+		status = us->current_urb->actual_length;
+
+	/* release the lock and return status */
+	up(&(us->current_urb_sem));
+	kfree(dr);
+  	return status;
+}
+
+/* This is our function to emulate usb_bulk_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
+		      unsigned int len, unsigned int *act_len)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+
+	/* set up data structures for the wakeup system */
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	add_wait_queue(&wqh, &wait);
+
+	/* lock the URB */
+	down(&(us->current_urb_sem));
+
+	/* fill the URB */
+	FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
+		      usb_stor_blocking_completion, &awd);
+
+	/* submit the URB */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	status = usb_submit_urb(us->current_urb);
+	if (status) {
+		/* something went wrong */
+		up(&(us->current_urb_sem));
+		remove_wait_queue(&wqh, &wait);
+		return status;
+	}
+
+	/* wait for the completion of the URB */
+	up(&(us->current_urb_sem));
+	if (us->current_urb->status == -EINPROGRESS)
+		schedule_timeout(10*HZ);
+	down(&(us->current_urb_sem));
+
+	/* we either timed out or got woken up -- clean up either way */
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&wqh, &wait);
+
+	/* did we time out? */
+	if (us->current_urb->status == -EINPROGRESS) {
+		US_DEBUGP("usb_stor_bulk_msg() timeout\n");
+		usb_unlink_urb(us->current_urb);
+		status = -ETIMEDOUT;
+	} else
+		status = us->current_urb->status;
+
+	/* return the actual length of the data transferred */
+	*act_len = us->current_urb->actual_length;
+
+	/* release the lock and return status */
+	up(&(us->current_urb_sem));
+	return status;
+}
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory.  Good performance is achieved by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
+ *
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit.  Thus we don't have to worry about it for individual
+ * packets.
+ */
+static int us_transfer_partial(struct us_data *us, char *buf, int length)
+{
+	int result;
+	int partial;
+	int pipe;
+
+	/* calculate the appropriate pipe information */
+	if (US_DIRECTION(us->srb->cmnd[0]))
+		pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	else
+		pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+	/* transfer the data */
+	US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length);
+	result = usb_stor_bulk_msg(us, buf, pipe, length, &partial);
+	US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n",
+		  result, partial, length);
+
+	/* if we stall, we need to clear it before we go on */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
+	
+	/* did we send all the data? */
+	if (partial == length) {
+		US_DEBUGP("us_transfer_partial(): transfer complete\n");
+		return US_BULK_TRANSFER_GOOD;
+	}
+
+	/* uh oh... we have an error code, so something went wrong. */
+	if (result) {
+		/* NAK - that means we've retried a few times allready */
+		if (result == -ETIMEDOUT) {
+			US_DEBUGP("us_transfer_partial(): device NAKed\n");
+			return US_BULK_TRANSFER_FAILED;
+		}
+
+		/* -ENOENT -- we canceled this transfer */
+		if (result == -ENOENT) {
+			US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+			return US_BULK_TRANSFER_ABORTED;
+		}
+
+		/* the catch-all case */
+		US_DEBUGP("us_transfer_partial(): unknown error\n");
+		return US_BULK_TRANSFER_FAILED;
+	}
+
+	/* no error code, so we must have transferred some data, 
+	 * just not all of it */
+	return US_BULK_TRANSFER_SHORT;
+}
+
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_transfer_partial to achieve it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately.  For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in)
+{
+	int i;
+	int result = -1;
+	struct scatterlist *sg;
+
+	/* are we scatter-gathering? */
+	if (srb->use_sg) {
+
+		/* loop over all the scatter gather structures and 
+		 * make the appropriate requests for each, until done
+		 */
+		sg = (struct scatterlist *) srb->request_buffer;
+		for (i = 0; i < srb->use_sg; i++) {
+			result = us_transfer_partial(us, sg[i].address, 
+						     sg[i].length);
+			if (result)
+				break;
+		}
+	}
+	else
+		/* no scatter-gather, just make the request */
+		result = us_transfer_partial(us, srb->request_buffer, 
+					     srb->request_bufflen);
+
+	/* return the result in the data structure itself */
+	srb->result = result;
+}
+
+/* Calculate the length of the data transfer (not the command) for any
+ * given SCSI command
+ */
+static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int i;
+	unsigned int total = 0;
+	struct scatterlist *sg;
+
+	/* support those devices which need the length calculated
+	 * differently 
+	 */
+	if (us->flags & US_FL_ALT_LENGTH) {
+		if (srb->cmnd[0] == INQUIRY) {
+			srb->cmnd[4] = 36;
+		}
+
+		if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE))
+			return srb->cmnd[4];
+
+		if (srb->cmnd[0] == TEST_UNIT_READY)
+			return 0;
+	}
+
+	/* Are we going to scatter gather? */
+	if (srb->use_sg) {
+		/* Add up the sizes of all the scatter-gather segments */
+		sg = (struct scatterlist *) srb->request_buffer;
+		for (i = 0; i < srb->use_sg; i++)
+			total += sg[i].length;
+
+		return total;
+	}
+	else
+		/* Just return the length of the buffer */
+		return srb->request_bufflen;
+}
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+/* Invoke the transport and basic error-handling/recovery methods
+ *
+ * This is used by the protocol layers to actually send the message to
+ * the device and recieve the response.
+ */
+static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int need_auto_sense;
+	int result;
+
+	/* send the command to the transport layer */
+	result = us->transport(srb, us);
+
+	/* Determine if we need to auto-sense
+	 *
+	 * I normally don't use a flag like this, but it's almost impossible
+	 * to understand what's going on here if I don't.
+	 */
+	need_auto_sense = 0;
+
+	/*
+	 * If we're running the CB transport, which is incapable
+	 * of determining status on it's own, we need to auto-sense almost
+	 * every time.
+	 */
+	if (us->protocol == US_PR_CB) {
+		US_DEBUGP("-- CB transport device requiring auto-sense\n");
+		need_auto_sense = 1;
+
+		/* There are some exceptions to this.  Notably, if this is
+		 * a UFI device and the command is REQUEST_SENSE or INQUIRY,
+		 * then it is impossible to truly determine status.
+		 */
+		if (us->subclass == US_SC_UFI &&
+		    ((srb->cmnd[0] == REQUEST_SENSE) ||
+		     (srb->cmnd[0] == INQUIRY))) {
+			US_DEBUGP("** no auto-sense for a special command\n");
+			need_auto_sense = 0;
+		}
+	}
+
+	/*
+	 * If we have an error, we're going to do a REQUEST_SENSE 
+	 * automatically.  Note that we differentiate between a command
+	 * "failure" and an "error" in the transport mechanism.
+	 */
+	if (result == USB_STOR_TRANSPORT_FAILED) {
+		US_DEBUGP("-- transport indicates command failure\n");
+		need_auto_sense = 1;
+	}
+	if (result == USB_STOR_TRANSPORT_ERROR) {
+		/* FIXME: we need to invoke a transport reset here */
+		US_DEBUGP("-- transport indicates transport failure\n");
+		need_auto_sense = 0;
+		srb->result = DID_ERROR << 16;
+		return;
+	}
+
+	/*
+	 * Also, if we have a short transfer on a command that can't have
+	 * a short transfer, we're going to do this.
+	 */
+	if ((srb->result == US_BULK_TRANSFER_SHORT) &&
+	    !((srb->cmnd[0] == REQUEST_SENSE) ||
+	      (srb->cmnd[0] == INQUIRY) ||
+	      (srb->cmnd[0] == MODE_SENSE) ||
+	      (srb->cmnd[0] == LOG_SENSE) ||
+	      (srb->cmnd[0] == MODE_SENSE_10))) {
+		US_DEBUGP("-- unexpectedly short transfer\n");
+		need_auto_sense = 1;
+	}
+
+	/* Now, if we need to do the auto-sense, let's do it */
+	if (need_auto_sense) {
+		int temp_result;
+		void* old_request_buffer;
+		int old_sg;
+		int old_request_bufflen;
+		unsigned char old_cmnd[MAX_COMMAND_SIZE];
+
+		US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
+
+		/* save the old command */
+		memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE);
+
+		srb->cmnd[0] = REQUEST_SENSE;
+		srb->cmnd[1] = 0;
+		srb->cmnd[2] = 0;
+		srb->cmnd[3] = 0;
+		srb->cmnd[4] = 18;
+		srb->cmnd[5] = 0;
+
+		/* set the buffer length for transfer */
+		old_request_buffer = srb->request_buffer;
+		old_request_bufflen = srb->request_bufflen;
+		old_sg = srb->use_sg;
+		srb->use_sg = 0;
+		srb->request_bufflen = 18;
+		srb->request_buffer = srb->sense_buffer;
+
+		/* issue the auto-sense command */
+		temp_result = us->transport(us->srb, us);
+		if (temp_result != USB_STOR_TRANSPORT_GOOD) {
+			/* FIXME: we need to invoke a transport reset here */
+			US_DEBUGP("-- auto-sense failure\n");
+			srb->result = DID_ERROR << 16;
+			return;
+		}
+
+		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+		US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+			  srb->sense_buffer[0],
+			  srb->sense_buffer[2] & 0xf,
+			  srb->sense_buffer[12], 
+			  srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		srb->result = CHECK_CONDITION;
+
+		/* we're done here, let's clean up */
+		srb->request_buffer = old_request_buffer;
+		srb->request_bufflen = old_request_bufflen;
+		srb->use_sg = old_sg;
+		memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
+
+		/* If things are really okay, then let's show that */
+		if ((srb->sense_buffer[2] & 0xf) == 0x0)
+			srb->result = GOOD;
+	} else /* if (need_auto_sense) */
+		srb->result = GOOD;
+
+	/* Regardless of auto-sense, if we _know_ we have an error
+	 * condition, show that in the result code
+	 */
+	if (result == USB_STOR_TRANSPORT_FAILED)
+		srb->result = CHECK_CONDITION;
+
+	/* If we think we're good, then make sure the sense data shows it.
+	 * This is necessary because the auto-sense for some devices always
+	 * sets byte 0 == 0x70, even if there is no error
+	 */
+	if ((us->protocol == US_PR_CB) && 
+	    (result == USB_STOR_TRANSPORT_GOOD) &&
+	    ((srb->sense_buffer[2] & 0xf) == 0x0))
+		srb->sense_buffer[0] = 0x0;
+}
+
+/*
+ * Control/Bulk/Interrupt transport
+ */
+
+/* The interrupt handler for CBI devices */
+static void CBI_irq(struct urb *urb)
+{
+	struct us_data *us = (struct us_data *)urb->context;
+
+	US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no);
+	US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length);
+	US_DEBUGP("-- IRQ state is %d\n", urb->status);
+
+	/* is the device removed? */
+	if (urb->status != -ENOENT) {
+		/* save the data for interpretation later */
+		US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
+			  ((unsigned char*)urb->transfer_buffer)[0], 
+			  ((unsigned char*)urb->transfer_buffer)[1]);
+
+
+		/* was this a wanted interrupt? */
+		if (us->ip_wanted) {
+			us->ip_wanted = 0;
+			up(&(us->ip_waitq));
+		} else
+			US_DEBUGP("ERROR: Unwanted interrupt received!\n");
+	} else
+		US_DEBUGP("-- device has been removed\n");
+}
+
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int result;
+
+	/* COMMAND STAGE */
+	/* let's send the command via the control pipe */
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+				      US_CBI_ADSC, 
+				      USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				      us->ifnum, srb->cmnd, srb->cmd_len);
+
+	/* check the return code for the command */
+	US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
+	if (result < 0) {
+		/* STALL must be cleared when they are detected */
+		if (result == -EPIPE) {
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
+			result = usb_clear_halt(us->pusb_dev,	
+						usb_sndctrlpipe(us->pusb_dev,
+								0));
+			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		/* Uh oh... serious problem here */
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	/* Set up for status notification */
+	us->ip_wanted = 1;
+
+	/* DATA STAGE */
+	/* transfer the data payload for this command, if one exists*/
+	if (us_transfer_length(srb, us)) {
+		us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
+	}
+
+	/* STATUS STAGE */
+
+	/* go to sleep until we get this interrupt */
+	down(&(us->ip_waitq));
+	
+	/* if we were woken up by an abort instead of the actual interrupt */
+	if (us->ip_wanted) {
+		US_DEBUGP("Did not get interrupt on CBI\n");
+		us->ip_wanted = 0;
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", 
+		  ((unsigned char*)us->irq_urb->transfer_buffer)[0],
+		  ((unsigned char*)us->irq_urb->transfer_buffer)[1]);
+	
+	/* UFI gives us ASC and ASCQ, like a request sense
+	 *
+	 * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI
+	 * devices, so we ignore the information for those commands.  Note
+	 * that this means we could be ignoring a real error on these
+	 * commands, but that can't be helped.
+	 */
+	if (us->subclass == US_SC_UFI) {
+		if (srb->cmnd[0] == REQUEST_SENSE ||
+		    srb->cmnd[0] == INQUIRY)
+			return USB_STOR_TRANSPORT_GOOD;
+		else
+			if (((unsigned char*)us->irq_urb->transfer_buffer)[0])
+				return USB_STOR_TRANSPORT_FAILED;
+			else
+				return USB_STOR_TRANSPORT_GOOD;
+	}
+	
+	/* If not UFI, we interpret the data as a result code 
+	 * The first byte should always be a 0x0
+	 * The second byte & 0x0F should be 0x0 for good, otherwise error 
+	 */
+	if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) {
+		US_DEBUGP("CBI IRQ data showed reserved bType\n");
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	switch (((unsigned char*)us->irq_urb->transfer_buffer)[1] & 0x0F) {
+	case 0x00: 
+		return USB_STOR_TRANSPORT_GOOD;
+	case 0x01: 
+		return USB_STOR_TRANSPORT_FAILED;
+	default: 
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	US_DEBUGP("CBI_transport() reached end of function\n");
+	return USB_STOR_TRANSPORT_ERROR;
+}
+
+/*
+ * Control/Bulk transport
+ */
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int result;
+
+	/* COMMAND STAGE */
+	/* let's send the command via the control pipe */
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+				      US_CBI_ADSC, 
+				      USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				      us->ifnum, srb->cmnd, srb->cmd_len);
+
+	/* check the return code for the command */
+	US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
+	if (result < 0) {
+		/* a stall is a fatal condition from the device */
+		if (result == -EPIPE) {
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
+			result = usb_clear_halt(us->pusb_dev, 
+						usb_sndctrlpipe(us->pusb_dev,
+								0));
+			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			return USB_STOR_TRANSPORT_FAILED;
+		}
+
+		/* Uh oh... serious problem here */
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	/* DATA STAGE */
+	/* transfer the data payload for this command, if one exists*/
+	if (us_transfer_length(srb, us)) {
+		us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
+	}
+	
+	
+	/* STATUS STAGE */
+	/* NOTE: CB does not have a status stage.  Silly, I know.  So
+	 * we have to catch this at a higher level.
+	 */
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Bulk only transport
+ */
+
+/* Determine what the maximum LUN supported is */
+static int Bulk_max_lun(struct us_data *us)
+{
+	unsigned char data;
+	int result;
+	int pipe;
+
+	/* issue the command */
+	pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
+	result = usb_control_msg(us->pusb_dev, pipe,
+				 US_BULK_GET_MAX_LUN, 
+				 USB_DIR_IN | USB_TYPE_CLASS | 
+				 USB_RECIP_INTERFACE,
+				 0, us->ifnum, &data, sizeof(data), HZ);
+
+	US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", 
+		  result, data);
+
+	/* if we have a successful request, return the result */
+	if (!result)
+		return data;
+
+	/* if we get a STALL, clear the stall */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
+
+	/* return the default -- no LUNs */
+	return 0;
+}
+
+static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+	struct bulk_cb_wrap bcb;
+	struct bulk_cs_wrap bcs;
+	int result;
+	int pipe;
+	int partial;
+	
+	/* set up the command wrapper */
+	bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
+	bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb, us));
+	bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+	bcb.Tag = srb->serial_number;
+	bcb.Lun = srb->cmnd[1] >> 5;
+	bcb.Length = srb->cmd_len;
+	
+	/* construct the pipe handle */
+	pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+	
+	/* copy the command payload */
+	memset(bcb.CDB, 0, sizeof(bcb.CDB));
+	memcpy(bcb.CDB, srb->cmnd, bcb.Length);
+	
+	/* send it to out endpoint */
+	US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n",
+		  le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, 
+		  bcb.DataTransferLength, bcb.Flags, bcb.Length);
+	result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, 
+				   &partial);
+	US_DEBUGP("Bulk command transfer result=%d\n", result);
+	
+	/* if we stall, we need to clear it before we go on */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
+	
+	/* if the command transfered well, then we go to the data stage */
+	if (result == 0) {
+		/* send/receive data payload, if there is any */
+		if (bcb.DataTransferLength) {
+			us_transfer(srb, us, bcb.Flags);
+			US_DEBUGP("Bulk data transfer result 0x%x\n", 
+				  srb->result);
+		}
+	}
+	
+	/* See flow chart on pg 15 of the Bulk Only Transport spec for
+	 * an explanation of how this code works.
+	 */
+	
+	/* construct the pipe handle */
+	pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	
+	/* get CSW for device status */
+	US_DEBUGP("Attempting to get CSW...\n");
+	result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, 
+				   &partial);
+	
+	/* did the attempt to read the CSW fail? */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	       
+		/* get the status again */
+		US_DEBUGP("Attempting to get CSW (2nd try)...\n");
+		result = usb_stor_bulk_msg(us, &bcs, pipe,
+					   US_BULK_CS_WRAP_LEN, &partial);
+		
+		/* if it fails again, we need a reset and return an error*/
+		if (result == -EPIPE) {
+			US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
+			usb_clear_halt(us->pusb_dev, pipe);
+			return USB_STOR_TRANSPORT_ERROR;
+		}
+	}
+	
+	/* if we still have a failure at this point, we're in trouble */
+	US_DEBUGP("Bulk status result = %d\n", result);
+	if (result) {
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	/* check bulk status */
+	US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
+		  le32_to_cpu(bcs.Signature), bcs.Tag, 
+		  bcs.Residue, bcs.Status);
+	if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) || 
+	    bcs.Tag != bcb.Tag || 
+	    bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
+		US_DEBUGP("Bulk logical error\n");
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	/* based on the status code, we report good or bad */
+	switch (bcs.Status) {
+	case US_BULK_STAT_OK:
+		/* command good -- note that we could be short on data */
+		return USB_STOR_TRANSPORT_GOOD;
+
+	case US_BULK_STAT_FAIL:
+		/* command failed */
+		return USB_STOR_TRANSPORT_FAILED;
+		
+	case US_BULK_STAT_PHASE:
+		/* phase error */
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	/* we should never get here, but if we do, we're in trouble */
+	return USB_STOR_TRANSPORT_ERROR;
+}
+
+/***********************************************************************
+ * Protocol routines
+ ***********************************************************************/
+
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int old_cmnd = 0;
+
+	/* Fix some commands -- this is a form of mode translation
+	 * ATAPI devices only accept 12 byte long commands 
+	 *
+	 * NOTE: This only works because a Scsi_Cmnd struct field contains
+	 * a unsigned char cmnd[12], so we know we have storage available
+	 */
+
+	/* set command length to 12 bytes */
+	srb->cmd_len = 12;
+
+	/* determine the correct (or minimum) data length for these commands */
+	switch (srb->cmnd[0]) {
+
+		/* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+	case MODE_SENSE:
+	case MODE_SELECT:
+		/* save the command so we can tell what it was */
+		old_cmnd = srb->cmnd[0];
+
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = 0;
+		srb->cmnd[4] = 0;
+		srb->cmnd[3] = 0;
+		srb->cmnd[2] = srb->cmnd[2];
+		srb->cmnd[1] = srb->cmnd[1];
+		srb->cmnd[0] = srb->cmnd[0] | 0x40;
+		break;
+
+		/* change READ_6/WRITE_6 to READ_10/WRITE_10, which 
+		 * are ATAPI commands */
+	case WRITE_6:
+	case READ_6:
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = srb->cmnd[3];
+		srb->cmnd[4] = srb->cmnd[2];
+		srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+		srb->cmnd[2] = 0;
+		srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+		srb->cmnd[0] = srb->cmnd[0] | 0x20;
+		break;
+	} /* end switch on cmnd[0] */
+
+	/* send the command to the transport layer */
+	invoke_transport(srb, us);
+
+	/* Fix the MODE_SENSE data if we translated the command
+	 */
+	if (old_cmnd == MODE_SENSE) {
+		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+		/* FIXME: we need to compress the entire data structure here
+		 */
+		dta[0] = dta[1];	/* data len */
+		dta[1] = dta[2];	/* med type */
+		dta[2] = dta[3];	/* dev-spec prm */
+		dta[3] = dta[7];	/* block desc len */
+		printk (KERN_DEBUG USB_STORAGE
+			"new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+			dta[0], dta[1], dta[2], dta[3]);
+	}
+
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+	 */
+	if (srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+	}
+}
+
+
+static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int old_cmnd = 0;
+
+	/* fix some commands -- this is a form of mode translation
+	 * UFI devices only accept 12 byte long commands 
+	 *
+	 * NOTE: This only works because a Scsi_Cmnd struct field contains
+	 * a unsigned char cmnd[12], so we know we have storage available
+	 */
+
+	/* set command length to 12 bytes (this affects the transport layer) */
+	srb->cmd_len = 12;
+
+	/* determine the correct (or minimum) data length for these commands */
+	switch (srb->cmnd[0]) {
+
+		/* for INQUIRY, UFI devices only ever return 36 bytes */
+	case INQUIRY:
+		srb->cmnd[4] = 36;
+		break;
+
+		/* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+	case MODE_SENSE:
+	case MODE_SELECT:
+		/* save the command so we can tell what it was */
+		old_cmnd = srb->cmnd[0];
+
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+
+		/* if we're sending data, we send all.	If getting data, 
+		 * get the minimum */
+		if (srb->cmnd[0] == MODE_SELECT)
+			srb->cmnd[8] = srb->cmnd[4];
+		else
+			srb->cmnd[8] = 8;
+
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = 0;
+		srb->cmnd[4] = 0;
+		srb->cmnd[3] = 0;
+		srb->cmnd[2] = srb->cmnd[2];
+		srb->cmnd[1] = srb->cmnd[1];
+		srb->cmnd[0] = srb->cmnd[0] | 0x40;
+		break;
+
+		/* again, for MODE_SENSE_10, we get the minimum (8) */
+	case MODE_SENSE_10:
+		srb->cmnd[7] = 0;
+		srb->cmnd[8] = 8;
+		break;
+
+		/* for REQUEST_SENSE, UFI devices only ever return 18 bytes */
+	case REQUEST_SENSE:
+		srb->cmnd[4] = 18;
+		break;
+
+		/* change READ_6/WRITE_6 to READ_10/WRITE_10, which 
+		 * are UFI commands */
+	case WRITE_6:
+	case READ_6:
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = srb->cmnd[3];
+		srb->cmnd[4] = srb->cmnd[2];
+		srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+		srb->cmnd[2] = 0;
+		srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+		srb->cmnd[0] = srb->cmnd[0] | 0x20;
+		break;
+	} /* end switch on cmnd[0] */
+
+	/* send the command to the transport layer */
+	invoke_transport(srb, us);
+	
+	/* Fix the MODE_SENSE data here if we had to translate the command
+	 */
+	if (old_cmnd == MODE_SENSE) {
+		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+		/* FIXME: we need to compress the entire data structure here
+		 */
+		dta[0] = dta[1];	/* data len */
+		dta[1] = dta[2];	/* med type */
+		dta[2] = dta[3];	/* dev-spec prm */
+		dta[3] = dta[7];	/* block desc len */
+		printk (KERN_DEBUG USB_STORAGE
+			"new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+			dta[0], dta[1], dta[2], dta[3]);
+	}
+
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+	 */
+	if (srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+	}
+}
+
+static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+	/* This code supports devices which do not support {READ|WRITE}_6
+	 * Apparently, neither Windows or MacOS will use these commands,
+	 * so some devices do not support them
+	 */
+	if (us->flags & US_FL_MODE_XLATE) {
+
+		/* translate READ_6 to READ_10 */
+		if (srb->cmnd[0] == 0x08) {
+
+			/* get the control */
+			srb->cmnd[9] = us->srb->cmnd[5];
+
+			/* get the length */
+			srb->cmnd[8] = us->srb->cmnd[6];
+			srb->cmnd[7] = 0;
+
+			/* set the reserved area to 0 */
+			srb->cmnd[6] = 0;	    
+
+			/* get LBA */
+			srb->cmnd[5] = us->srb->cmnd[3];
+			srb->cmnd[4] = us->srb->cmnd[2];
+			srb->cmnd[3] = 0;
+			srb->cmnd[2] = 0;
+
+			/* LUN and other info in cmnd[1] can stay */
+
+			/* fix command code */
+			srb->cmnd[0] = 0x28;
+
+			US_DEBUGP("Changing READ_6 to READ_10\n");
+			US_DEBUG(us_show_command(srb));
+		}
+
+		/* translate WRITE_6 to WRITE_10 */
+		if (srb->cmnd[0] == 0x0A) {
+
+			/* get the control */
+			srb->cmnd[9] = us->srb->cmnd[5];
+
+			/* get the length */
+			srb->cmnd[8] = us->srb->cmnd[4];
+			srb->cmnd[7] = 0;
+
+			/* set the reserved area to 0 */
+			srb->cmnd[6] = 0;	    
+
+			/* get LBA */
+			srb->cmnd[5] = us->srb->cmnd[3];
+			srb->cmnd[4] = us->srb->cmnd[2];
+			srb->cmnd[3] = 0;
+			srb->cmnd[2] = 0;
+	    
+			/* LUN and other info in cmnd[1] can stay */
+
+			/* fix command code */
+			srb->cmnd[0] = 0x2A;
+
+			US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
+			US_DEBUG(us_show_command(us->srb));
+		}
+	} /* if (us->flags & US_FL_MODE_XLATE) */
+
+	/* send the command to the transport layer */
+	invoke_transport(srb, us);
+
+	/* fix the results of an INQUIRY */
+	if (srb->cmnd[0] == INQUIRY) {
+		US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+		((unsigned char*)us->srb->request_buffer)[2] |= 2;
+	}
+}
+
+/***********************************************************************
+ * Reset routines
+ ***********************************************************************/
+
+/* This issues a CB[I] Reset to the device in question
+ */
+static int CB_reset(struct us_data *us)
+{
+	unsigned char cmd[12];
+	int result;
+
+	US_DEBUGP("CB_reset() called\n");
+
+	memset(cmd, 0xFF, sizeof(cmd));
+	cmd[0] = SEND_DIAGNOSTIC;
+	cmd[1] = 4;
+	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+				 US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
+
+	/* long wait for reset */
+	schedule_timeout(HZ*6);
+
+	US_DEBUGP("CB_reset: clearing endpoint halt\n");
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+
+	US_DEBUGP("CB_reset done\n");
+	return 0;
+}
+
+/* FIXME: Does this work? */
+static int Bulk_reset(struct us_data *us)
+{
+	int result;
+
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_sndctrlpipe(us->pusb_dev,0), 
+				 US_BULK_RESET_REQUEST, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 0, us->ifnum, NULL, 0, HZ*5);
+
+	if (result < 0)
+		US_DEBUGP("Bulk hard reset failed %d\n", result);
+
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+	/* long wait for reset */
+	schedule_timeout(HZ*6);
+
+	return result;
+}
+
+/***********************************************************************
+ * Host functions 
+ ***********************************************************************/
+
+static const char* us_info(struct Scsi_Host *host)
+{
+	return "SCSI emulation for USB Mass Storage devices";
+}
+
+/* detect a virtual adapter (always works) */
+static int us_detect(struct SHT *sht)
+{
+	struct us_data *us;
+	char local_name[32];
+
+	/* This is not nice at all, but how else are we to get the
+	 * data here? */
+	us = (struct us_data *)sht->proc_dir;
+
+	/* set up the name of our subdirectory under /proc/scsi/ */
+	sprintf(local_name, "usb-storage-%d", us->host_number);
+	sht->name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+	if (!sht->name)
+		return 0;
+	strcpy((char *)sht->name, local_name);
+
+	/* we start with no /proc directory entry */
+	sht->proc_dir = &proc_scsi_usb_scsi;
+
+	/* register the host */
+	us->host = scsi_register(sht, sizeof(us));
+	if (us->host) {
+		us->host->hostdata[0] = (unsigned long)us;
+		us->host_no = us->host->host_no;
+		return 1;
+	}
+
+	/* odd... didn't register properly.  Abort and free pointers */
+	kfree(sht->name);
+	sht->name = NULL;
+
+	return 0;
+}
+
+/* Release all resources used by the virtual host
+ *
+ * NOTE: There is no contention here, because we're allready deregistered
+ * the driver and we're doing each virtual host in turn, not in parallel
+ */
+static int us_release(struct Scsi_Host *psh)
+{
+	struct us_data *us = (struct us_data *)psh->hostdata[0];
+
+	US_DEBUGP("us_release() called for host %s\n", us->htmplt.name);
+
+	/* Kill the control threads
+	 *
+	 * Enqueue the command, wake up the thread, and wait for 
+	 * notification that it's exited.
+	 */
+	US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
+	us->action = US_ACT_EXIT;
+	up(&(us->sleeper));
+	down(&(us->notify));
+	
+	/* free the data structure we were using */
+	US_DEBUGP("-- freeing private host data structure\n");
+	kfree(us->current_urb);
+	kfree(us);
+	(struct us_data*)psh->hostdata[0] = NULL;
+
+	/* we always have a successful release */
+	return 0;
+}
+
+/* run command */
+static int us_command( Scsi_Cmnd *srb )
+{
+	US_DEBUGP("Bad use of us_command\n");
+
+	return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+	US_DEBUGP("us_queuecommand() called\n");
+	srb->host_scribble = (unsigned char *)us;
+
+	/* get exclusive access to the structures we want */
+	down(&(us->queue_exclusion));
+
+	/* enqueue the command */
+	us->queue_srb = srb;
+	srb->scsi_done = done;
+	us->action = US_ACT_COMMAND;
+
+	/* wake up the process task */
+	up(&(us->queue_exclusion));
+	up(&(us->sleeper));
+
+	return 0;
+}
+
+/***********************************************************************
+ * Error handling functions
+ ***********************************************************************/
+
+/* Command abort
+ *
+ * Note that this is really only meaningful right now for CBI transport
+ * devices which have failed to give us the command completion interrupt
+ */
+static int us_abort( Scsi_Cmnd *srb )
+{
+	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+	US_DEBUGP("us_abort() called\n");
+
+	/* if we're stuck waiting for an IRQ, simulate it */
+	if (us->ip_wanted) {
+		US_DEBUGP("-- simulating missing IRQ\n");
+		up(&(us->ip_waitq));
+		return SUCCESS;
+	}
+
+	return FAILED;
+}
+
+/* FIXME: this doesn't do anything right now */
+static int us_bus_reset( Scsi_Cmnd *srb )
+{
+	// struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+	printk(KERN_CRIT "usb-storage: bus_reset() requested but not implemented\n" );
+	US_DEBUGP("Bus reset requested\n");
+	//  us->transport_reset(us);
+	return FAILED;
+}
+
+/* FIXME: This doesn't actually reset anything */
+static int us_host_reset( Scsi_Cmnd *srb )
+{
+	printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
+	return FAILED;
+}
+
+/***********************************************************************
+ * /proc/scsi/ functions
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) \
+	do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
+
+int usb_stor_proc_info (char *buffer, char **start, off_t offset, 
+			int length, int hostno, int inout)
+{
+	struct us_data *us;
+	char *pos = buffer;
+
+	/* if someone is sending us data, just throw it away */
+	if (inout)
+		return length;
+
+	/* lock the data structures */
+	down(&us_list_semaphore);
+
+	/* find our data from hostno */
+	us = us_list;
+	while (us) {
+		if (us->host_no == hostno)
+			break;
+		us = us->next;
+	}
+
+	/* if we couldn't find it, we return an error */
+	if (!us) {
+		up(&us_list_semaphore);
+		return -ESRCH;
+	}
+	
+	/* print the controler name */
+	SPRINTF("   Host scsi%d: usb-storage\n", hostno);
+
+	/* print product, vendor, and serial number strings */
+	SPRINTF("	Vendor: %s\n", us->vendor);
+	SPRINTF("      Product: %s\n", us->product);
+	SPRINTF("Serial Number: %s\n", us->serial);
+
+	/* show the protocol and transport */
+	SPRINTF("     Protocol: %s\n", us->protocol_name);
+	SPRINTF("    Transport: %s\n", us->transport_name);
+
+	/* show the GUID of the device */
+	SPRINTF("	  GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
+
+	/* release our lock on the data structures */
+	up(&us_list_semaphore);
+
+	/*
+	 * Calculate start of next buffer, and return value.
+	 */
+	*start = buffer + offset;
+
+	if ((pos - buffer) < offset)
+		return (0);
+	else if ((pos - buffer - offset) < length)
+		return (pos - buffer - offset);
+	else
+		return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+static Scsi_Host_Template my_host_template = {
+	name:			"usb-storage",
+	proc_info:		usb_stor_proc_info,
+	info:			us_info,
+
+	detect:			us_detect,
+	release:		us_release,
+	command:		us_command,
+	queuecommand:		us_queuecommand,
+
+	eh_abort_handler:	us_abort,
+	eh_device_reset_handler:us_bus_reset,
+	eh_bus_reset_handler:	us_bus_reset,
+	eh_host_reset_handler:	us_host_reset,
+
+	can_queue:		1,
+	this_id:		-1,
+
+	sg_tablesize:		SG_ALL,
+	cmd_per_lun:		1,
+	present:		0,
+	unchecked_isa_dma:	FALSE,
+	use_clustering:		TRUE,
+	use_new_eh_code:	TRUE,
+	emulated:		TRUE
+};
+
+static unsigned char sense_notready[] = {
+	[0]	= 0x70,			    /* current error */
+	[2]	= 0x02,			    /* not ready */
+	[5]	= 0x0a,			    /* additional length */
+	[10]	= 0x04,			    /* not ready */
+	[11]	= 0x03			    /* manual intervention */
+};
+
+static int usb_stor_control_thread(void * __us)
+{
+	struct us_data *us = (struct us_data *)__us;
+	int action;
+
+	lock_kernel();
+
+	/*
+	 * This thread doesn't need any user-level access,
+	 * so get rid of all our resources..
+	 */
+	daemonize();
+
+	/* set our name for identification purposes */
+	sprintf(current->comm, "usb-storage-%d", us->host_number);
+
+	unlock_kernel();
+
+	/* signal that we've started the thread */
+	up(&(us->notify));
+
+	for(;;) {
+		US_DEBUGP("*** thread sleeping.\n");
+		down(&(us->sleeper));
+		down(&(us->queue_exclusion));
+		US_DEBUGP("*** thread awakened.\n");
+
+		/* take the command off the queue */
+		action = us->action;
+		us->action = 0;
+		us->srb = us->queue_srb;
+	
+		/* release the queue lock as fast as possible */
+		up(&(us->queue_exclusion));
+
+		switch (action) {
+		case US_ACT_COMMAND:
+			/* reject if target != 0 or if single-lun device
+			 * and LUN != 0
+			 */
+			if (us->srb->target ||
+			    ((us->flags & US_FL_SINGLE_LUN) && us->srb->lun)) {
+				US_DEBUGP("Bad device number (%d/%d)\n",
+					  us->srb->target, us->srb->lun);
+
+				us->srb->result = DID_BAD_TARGET << 16;
+
+				us->srb->scsi_done(us->srb);
+				us->srb = NULL;
+				break;
+			}
+
+			/* handle those devices which can't do a START_STOP */
+			if ((us->srb->cmnd[0] == START_STOP) &&
+			    (us->flags & US_FL_START_STOP)) {
+				us->srb->result = GOOD;
+				us->srb->scsi_done(us->srb);
+				us->srb = NULL;
+				break;
+			}
+				     
+			/* lock the device pointers */
+			down(&(us->dev_semaphore));
+
+			/* our device has gone - pretend not ready */
+			if (!us->pusb_dev) {
+				US_DEBUGP("Request is for removed device\n");
+				/* For REQUEST_SENSE, it's the data.  But
+				 * for anything else, it should look like
+				 * we auto-sensed for it.
+				 */
+				if (us->srb->cmnd[0] == REQUEST_SENSE) {
+					memcpy(us->srb->request_buffer, 
+					       sense_notready, 
+					       sizeof(sense_notready));
+					us->srb->result = GOOD;
+				} else {
+					memcpy(us->srb->sense_buffer, 
+					       sense_notready, 
+					       sizeof(sense_notready));
+					us->srb->result = CHECK_CONDITION;
+				}
+			} else { /* !us->pusb_dev */
+				/* we've got a command, let's do it! */
+				US_DEBUG(us_show_command(us->srb));
+				us->proto_handler(us->srb, us);
+			}
+
+			/* unlock the device pointers */
+			up(&(us->dev_semaphore));
+
+			/* indicate that the command is done */
+			US_DEBUGP("scsi cmd done, result=0x%x\n", 
+				  us->srb->result);
+			us->srb->scsi_done(us->srb);
+			us->srb = NULL;
+			break;
+
+		case US_ACT_DEVICE_RESET:
+			break;
+
+		case US_ACT_BUS_RESET:
+			break;
+
+		case US_ACT_HOST_RESET:
+			break;
+
+		} /* end switch on action */
+
+		/* exit if we get a signal to exit */
+		if (action == US_ACT_EXIT) {
+			US_DEBUGP("-- US_ACT_EXIT command recieved\n");
+			break;
+		}
+	} /* for (;;) */
+
+	/* notify the exit routine that we're actually exiting now */
+	up(&(us->notify));
+
+	return 0;
+}	
+
+/* This is the list of devices we recognize, along with their flag data */
+static struct us_unusual_dev us_unusual_dev_list[] = {
+	{ 0x03f0, 0x0107, 0x0200,
+	  "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, 
+	{ 0x04e6, 0x0001, 0x0200,
+	  "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN},
+	{ 0x04e6, 0x0002, 0x0100,
+	  "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, 
+	{ 0x04e6, 0x0006, 0x0100,
+	  "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, 
+	{ 0x057b, 0x0000, 0x0114,
+	  "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN},
+	{ 0x059b, 0x0030, 0x0100,
+	  "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN},
+	{ 0x0693, 0x0002, 0x0100,
+	  "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK,
+	  US_FL_ALT_LENGTH},
+	{ 0x0781, 0x0001, 0x0200,
+	  "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, 
+	  US_FL_SINGLE_LUN | US_FL_START_STOP},
+	{ 0x0781, 0x0002, 0x0009,
+	  "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, 
+	  US_FL_SINGLE_LUN | US_FL_IGNORE_SER},
+	{ 0x07af, 0x0005, 0x0100,
+	  "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, 
+	{ 0x0000, 0x0000, 0x0,
+	  "", 0, 0, 0}
+};
+
+/* Search our ususual device list, based on vendor/product combinations
+ * to see if we can support this device.  Returns a pointer to a structure
+ * defining how we should support this device, or NULL if it's not in the
+ * list
+ */
+static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, 
+					  u16 bcdDevice)
+{
+	struct us_unusual_dev* ptr;
+
+	US_DEBUGP("Searching unusual device list for (0x%x, 0x%x, 0x%x)...\n",
+		  idVendor, idProduct, bcdDevice);
+
+	ptr = us_unusual_dev_list;
+	while ((ptr->idVendor != 0x0000) && 
+	       !((ptr->idVendor == idVendor) && 
+		 (ptr->idProduct == idProduct) &&
+		 (ptr->bcdDevice == bcdDevice)))
+		ptr++;
+	
+	/* if the search ended because we hit the end record, we failed */
+	if (ptr->idVendor == 0x0000) {
+		US_DEBUGP("-- did not find a matching device\n");
+		return NULL;
+	}
+
+	/* otherwise, we found one! */
+	US_DEBUGP("-- found matching device: %s\n", ptr->name);
+	return ptr;
+}
+
+/* Set up the IRQ pipe and handler
+ * Note that this function assumes that all the data in the us_data
+ * strucuture is current.  This includes the ep_int field, which gives us
+ * the endpoint for the interrupt.
+ * Returns non-zero on failure, zero on success
+ */ 
+static int usb_stor_allocate_irq(struct us_data *ss)
+{
+	unsigned int pipe;
+	int maxp;
+	int result;
+
+	US_DEBUGP("Allocating IRQ for CBI transport\n");
+	
+	/* lock access to the data structure */
+	down(&(ss->irq_urb_sem));
+
+	/* allocate the URB */
+	ss->irq_urb = usb_alloc_urb(0);
+	if (!ss->irq_urb) {
+		up(&(ss->irq_urb_sem));
+		US_DEBUGP("couldn't allocate interrupt URB");
+		return 1;
+	}
+	
+	/* calculate the pipe and max packet size */
+	pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int->bEndpointAddress & 
+			      USB_ENDPOINT_NUMBER_MASK);
+	maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe));
+	if (maxp > sizeof(ss->irqbuf))
+		maxp = sizeof(ss->irqbuf);
+	
+	/* fill in the URB with our data */
+	FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, maxp, 
+		     CBI_irq, ss, ss->ep_int->bInterval); 
+	
+	/* submit the URB for processing */
+	result = usb_submit_urb(ss->irq_urb);
+	US_DEBUGP("usb_submit_urb() returns %d\n", result);
+	if (result) {
+		usb_free_urb(ss->irq_urb);
+		up(&(ss->irq_urb_sem));
+		return 2;
+	}
+
+	/* unlock the data structure and return success */
+	up(&(ss->irq_urb_sem));
+	return 0;
+}
+
+/* Probe to see if a new device is actually a SCSI device */
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	int i;
+	char mf[USB_STOR_STRING_LEN];		     /* manufacturer */
+	char prod[USB_STOR_STRING_LEN];		     /* product */
+	char serial[USB_STOR_STRING_LEN];	     /* serial number */
+	GUID(guid);			   /* Global Unique Identifier */
+	unsigned int flags;
+	struct us_unusual_dev *unusual_dev;
+	struct us_data *ss = NULL;
+	int result;
+
+	/* these are temporary copies -- we test on these, then put them
+	 * in the us-data structure 
+	 */
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	struct usb_endpoint_descriptor *ep_int = NULL;
+	u8 subclass = 0;
+	u8 protocol = 0;
+
+	/* the altsettting 0 on the interface we're probing */
+	struct usb_interface_descriptor *altsetting = 
+		&(dev->actconfig->interface[ifnum].altsetting[0]); 
+
+	/* clear the temporary strings */
+	memset(mf, 0, sizeof(mf));
+	memset(prod, 0, sizeof(prod));
+	memset(serial, 0, sizeof(serial));
+
+	/* search for this device in our unusual device list */
+	unusual_dev = us_find_dev(dev->descriptor.idVendor, 
+				  dev->descriptor.idProduct,
+				  dev->descriptor.bcdDevice);
+
+	/* 
+	 * Can we support this device, either because we know about it
+	 * from our unusual device list, or because it advertises that it's
+	 * compliant to the specification?
+	 */
+	if (!unusual_dev &&
+	    !(dev->descriptor.bDeviceClass == 0 &&
+	      altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+	      altsetting->bInterfaceSubClass >= US_SC_MIN &&
+	      altsetting->bInterfaceSubClass <= US_SC_MAX)) {
+		/* if it's not a mass storage, we go no further */
+		return NULL;
+	}
+
+	/* At this point, we know we've got a live one */
+	US_DEBUGP("USB Mass Storage device detected\n");
+
+	/* Determine subclass and protocol, or copy from the interface */
+	if (unusual_dev) {
+		subclass = unusual_dev->useProtocol;
+		protocol = unusual_dev->useTransport;
+		flags = unusual_dev->flags;
+	} else {
+		subclass = altsetting->bInterfaceSubClass;
+		protocol = altsetting->bInterfaceProtocol;
+		flags = 0;
+	}
+	
+	/*
+	 * Find the endpoints we need
+	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
+	 * An optional interrupt is OK (necessary for CBI protocol).
+	 * We will ignore any others.
+	 */
+	for (i = 0; i < altsetting->bNumEndpoints; i++) {
+		/* is it an BULK endpoint? */
+		if ((altsetting->endpoint[i].bmAttributes & 
+		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+			/* BULK in or out? */
+			if (altsetting->endpoint[i].bEndpointAddress & 
+			    USB_DIR_IN)
+				ep_in = &altsetting->endpoint[i];
+			else
+				ep_out = &altsetting->endpoint[i];
+		}
+
+		/* is it an interrupt endpoint? */
+		if ((altsetting->endpoint[i].bmAttributes & 
+		     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+			ep_int = &altsetting->endpoint[i];
+		}
+	}
+	US_DEBUGP("Endpoints: In: 0x%p Out: 0x%p Int: 0x%p (Period %d)\n",
+		  ep_in, ep_out, ep_int, ep_int ? ep_int->bInterval : 0);
+
+	/* set the interface -- STALL is an acceptable response here */
+	result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+	US_DEBUGP("Result from usb_set_interface is %d\n", result);
+	if (result == -EPIPE) {
+		US_DEBUGP("-- clearing stall on control interface\n");
+		usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+	} else if (result != 0) {
+		/* it's not a stall, but another error -- time to bail */
+		US_DEBUGP("-- Unknown error.  Rejecting device\n");
+		return NULL;
+	}
+
+	/* Do some basic sanity checks, and bail if we find a problem */
+	if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
+		US_DEBUGP("Sanity check failed.	 Rejecting device.\n");
+		return NULL;
+	}
+
+	/* At this point, we're committed to using the device */
+
+	/* clear the GUID and fetch the strings */
+	GUID_CLEAR(guid);
+	if (dev->descriptor.iManufacturer)
+		usb_string(dev, dev->descriptor.iManufacturer, 
+			   mf, sizeof(mf));
+	if (dev->descriptor.iProduct)
+		usb_string(dev, dev->descriptor.iProduct, 
+			   prod, sizeof(prod));
+	if (dev->descriptor.iSerialNumber && !(flags & US_FL_IGNORE_SER))
+		usb_string(dev, dev->descriptor.iSerialNumber, 
+			   serial, sizeof(serial));
+	
+	/* Create a GUID for this device */
+	if (dev->descriptor.iSerialNumber && serial[0]) {
+		/* If we have a serial number, and it's a non-NULL string */
+		make_guid(guid, dev->descriptor.idVendor, 
+			  dev->descriptor.idProduct, serial);
+	} else {
+		/* We don't have a serial number, so we use 0 */
+		make_guid(guid, dev->descriptor.idVendor, 
+			  dev->descriptor.idProduct, "0");
+	}
+
+	/* lock access to the data structures */
+	down(&us_list_semaphore);
+
+	/*
+	 * Now check if we have seen this GUID before
+	 * We're looking for a device with a matching GUID that isn't
+	 * allready on the system
+	 */
+	ss = us_list;
+	while ((ss != NULL) && 
+	       ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid)))
+		ss = ss->next;
+
+	if (ss != NULL) {
+		/* Existing device -- re-connect */
+		US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
+			  GUID_ARGS(guid));
+
+		/* establish the connection to the new device upon reconnect */
+		ss->ifnum = ifnum;
+		ss->pusb_dev = dev;
+	
+		/* copy over the endpoint data */
+		if (ep_in)
+			ss->ep_in = ep_in->bEndpointAddress & 
+				USB_ENDPOINT_NUMBER_MASK;
+		if (ep_out)
+			ss->ep_out = ep_out->bEndpointAddress & 
+				USB_ENDPOINT_NUMBER_MASK;
+		ss->ep_int = ep_int;
+
+		/* allocate an IRQ callback if one is needed */
+		if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss))
+			return NULL;
+	} else { 
+		/* New device -- allocate memory and initialize */
+		US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+	
+		if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), 
+						    GFP_KERNEL)) == NULL) {
+			printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+			up(&us_list_semaphore);
+			return NULL;
+		}
+		memset(ss, 0, sizeof(struct us_data));
+
+		/* allocate the URB we're going to use */
+		ss->current_urb = usb_alloc_urb(0);
+		if (!ss->current_urb) {
+			kfree(ss);
+			return NULL;
+		}
+
+		/* Initialize the mutexes only when the struct is new */
+		init_MUTEX_LOCKED(&(ss->sleeper));
+		init_MUTEX_LOCKED(&(ss->notify));
+		init_MUTEX_LOCKED(&(ss->ip_waitq));
+		init_MUTEX(&(ss->queue_exclusion));
+		init_MUTEX(&(ss->irq_urb_sem));
+		init_MUTEX(&(ss->current_urb_sem));
+		init_MUTEX(&(ss->dev_semaphore));
+
+		/* copy over the subclass and protocol data */
+		ss->subclass = subclass;
+		ss->protocol = protocol;
+		ss->flags = flags;
+
+		/* copy over the endpoint data */
+		if (ep_in)
+			ss->ep_in = ep_in->bEndpointAddress & 
+				USB_ENDPOINT_NUMBER_MASK;
+		if (ep_out)
+			ss->ep_out = ep_out->bEndpointAddress & 
+				USB_ENDPOINT_NUMBER_MASK;
+		ss->ep_int = ep_int;
+
+		/* establish the connection to the new device */
+		ss->ifnum = ifnum;
+		ss->pusb_dev = dev;
+
+		/* copy over the identifiying strings */
+		strncpy(ss->vendor, mf, USB_STOR_STRING_LEN);
+		strncpy(ss->product, prod, USB_STOR_STRING_LEN);
+		strncpy(ss->serial, serial, USB_STOR_STRING_LEN);
+		if (strlen(ss->vendor) == 0)
+			strncpy(ss->vendor, "Unknown", USB_STOR_STRING_LEN);
+		if (strlen(ss->product) == 0)
+			strncpy(ss->product, "Unknown", USB_STOR_STRING_LEN);
+		if (strlen(ss->serial) == 0)
+			strncpy(ss->serial, "None", USB_STOR_STRING_LEN);
+
+		/* copy the GUID we created before */
+		memcpy(ss->guid, guid, sizeof(guid));
+		
+		/* 
+		 * Set the handler pointers based on the protocol
+		 * Again, this data is persistant across reattachments
+		 */
+		switch (ss->protocol) {
+		case US_PR_CB:
+			ss->transport_name = "Control/Bulk";
+			ss->transport = CB_transport;
+			ss->transport_reset = CB_reset;
+			break;
+			
+		case US_PR_CBI:
+			ss->transport_name = "Control/Bulk/Interrupt";
+			ss->transport = CBI_transport;
+			ss->transport_reset = CB_reset;
+			break;
+			
+		case US_PR_BULK:
+			ss->transport_name = "Bulk";
+			ss->transport = Bulk_transport;
+			ss->transport_reset = Bulk_reset;
+			/* FIXME: for testing purposes only */
+			Bulk_max_lun(ss);
+			break;
+			
+		default:
+			ss->transport_name = "Unknown";
+			up(&us_list_semaphore);
+			kfree(ss->current_urb);
+			kfree(ss);
+			return NULL;
+			break;
+		}
+		US_DEBUGP("Transport: %s\n", ss->transport_name);
+
+		switch (ss->subclass) {
+		case US_SC_RBC:
+			ss->protocol_name = "Reduced Block Commands (RBC)";
+			ss->proto_handler = transparent_scsi_command;
+			break;
+
+		case US_SC_8020:
+			ss->protocol_name = "8020i";
+			ss->proto_handler = ATAPI_command;
+			break;
+
+		case US_SC_QIC:
+			ss->protocol_name = "QIC-157";
+			US_DEBUGP("Sorry, device not supported.	 Please\n");
+			US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n");
+			US_DEBUGP("if you see this message.\n");
+			up(&us_list_semaphore);
+			kfree(ss->current_urb);
+			kfree(ss);
+			return NULL;
+			break;
+
+		case US_SC_8070:
+			ss->protocol_name = "8070i";
+			ss->proto_handler = ATAPI_command;
+			break;
+
+		case US_SC_SCSI:
+			ss->protocol_name = "Transparent SCSI";
+			ss->proto_handler = transparent_scsi_command;
+			break;
+
+		case US_SC_UFI:
+			ss->protocol_name = "Uniform Floppy Interface (UFI)";
+			ss->proto_handler = ufi_command;
+			break;
+
+		default:
+			ss->protocol_name = "Unknown";
+			up(&us_list_semaphore);
+			kfree(ss->current_urb);
+			kfree(ss);
+			return NULL;
+			break;
+		}
+		US_DEBUGP("Protocol: %s\n", ss->protocol_name);
+
+		/* allocate an IRQ callback if one is needed */
+		if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss))
+			return NULL;
+		
+		/*
+		 * Since this is a new device, we need to generate a scsi 
+		 * host definition, and register with the higher SCSI layers
+		 */
+
+		/* Initialize the host template based on the default one */
+		memcpy(&(ss->htmplt), &my_host_template, 
+		       sizeof(my_host_template));
+
+		/* Grab the next host number */
+		ss->host_number = my_host_number++;
+			
+		/* We abuse this pointer so we can pass the ss pointer to 
+		 * the host controler thread in us_detect.  But how else are
+		 * we to do it?
+		 */
+		(struct us_data *)ss->htmplt.proc_dir = ss; 
+		
+		/* start up our control thread */
+		ss->pid = kernel_thread(usb_stor_control_thread, ss,
+					CLONE_FS | CLONE_FILES |
+					CLONE_SIGHAND);
+		if (ss->pid < 0) {
+			printk(KERN_WARNING USB_STORAGE 
+			       "Unable to start control thread\n");
+			kfree(ss->current_urb);
+			kfree(ss);
+			return NULL;
+		}
+		
+		/* wait for the thread to start */
+		down(&(ss->notify));
+			
+		/* now register	 - our detect function will be called */
+		ss->htmplt.module = THIS_MODULE;
+		scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt));
+		
+		/* put us in the list */
+		ss->next = us_list;
+		us_list = ss;
+	}
+
+	/* release the data structure lock */
+	up(&us_list_semaphore);
+
+	printk(KERN_DEBUG 
+	       "WARNING: USB Mass Storage data integrity not assured\n");
+	printk(KERN_DEBUG 
+	       "USB Mass Storage device found at %d\n", dev->devnum);
+
+	/* return a pointer for the disconnect function */
+	return ss;
+}
+
+/* Handle a disconnect event from the USB core */
+static void storage_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct us_data *ss = ptr;
+	int result;
+
+	US_DEBUGP("storage_disconnect() called\n");
+
+	/* this is the odd case -- we disconnected but weren't using it */
+	if (!ss) {
+		US_DEBUGP("-- device was not in use\n");
+		return;
+	}
+
+	/* lock access to the device data structure */
+	down(&(ss->dev_semaphore));
+
+	/* release the IRQ, if we have one */
+	down(&(ss->irq_urb_sem));
+	if (ss->irq_urb) {
+		US_DEBUGP("-- releasing irq handle\n");
+		result = usb_unlink_urb(ss->irq_urb);
+		ss->irq_urb = NULL;
+		US_DEBUGP("-- usb_unlink_urb() returned %d\n", result);
+		usb_free_urb(ss->irq_urb);
+	}
+	up(&(ss->irq_urb_sem));
+
+	/* mark the device as gone */
+	ss->pusb_dev = NULL;
+
+	/* lock access to the device data structure */
+	up(&(ss->dev_semaphore));
+}
+
+
+/***********************************************************************
+ * Initialization and registration
+ ***********************************************************************/
+
+int __init usb_stor_init(void)
+{
+	/* initialize internal global data elements */
+	us_list = NULL;
+	init_MUTEX(&us_list_semaphore);
+	my_host_number = 0;
+
+	/* register the driver, return -1 if error */
+	if (usb_register(&storage_driver) < 0)
+		return -1;
+
+	/* we're all set */
+	printk(KERN_INFO "USB Mass Storage support registered.\n");
+	return 0;
+}
+
+void __exit usb_stor_exit(void)
+{
+	struct us_data *next;
+	
+	US_DEBUGP("usb_stor_exit() called\n");
+
+	/* Deregister the driver
+	 * This eliminates races with probes and disconnects 
+	 */
+	US_DEBUGP("-- calling usb_deregister()\n");
+	usb_deregister(&storage_driver) ;
+	
+	/* lock access to the data structures */
+	down(&us_list_semaphore);
+
+	/* While there are still virtual hosts, unregister them
+	 *
+	 * Note that the us_release() routine will destroy the local data
+	 * structure.  So we have to peel these off the top of the list
+	 * and keep updating the head pointer as we go.
+	 */
+	while (us_list) {
+		/* keep track of where the next one is */
+		next = us_list->next;
+
+		US_DEBUGP("-- calling scsi_unregister_module()\n");
+		scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt));
+
+		/* advance the list pointer */
+		us_list = next;
+	}
+	
+	/* unlock the data structures */
+	up(&us_list_semaphore);
+}
+
+module_init(usb_stor_init) ;
+module_exit(usb_stor_exit) ;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-storage.h linux.18p17/drivers/usb/usb-storage.h
--- linux.vanilla/drivers/usb/usb-storage.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-storage.h	Fri Sep  1 14:42:41 2000
@@ -0,0 +1,159 @@
+/* Driver for USB mass storage - include file
+ *
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ */
+
+#include <linux/config.h>
+
+#define USB_STORAGE "usb-storage.c: "
+#define USB_STOR_STRING_LEN 32
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+void us_show_command(Scsi_Cmnd *srb);
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x 
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Sub Classes */
+
+#define US_SC_RBC	0x01		/* Typically, flash devices */
+#define US_SC_8020	0x02		/* CD-ROM */
+#define US_SC_QIC	0x03		/* QIC-157 Tapes */
+#define US_SC_UFI	0x04		/* Floppy */
+#define US_SC_8070	0x05		/* Removable media */
+#define US_SC_SCSI	0x06		/* Transparent */
+#define US_SC_MIN	US_SC_RBC
+#define US_SC_MAX	US_SC_SCSI
+
+/* Protocols */
+
+#define US_PR_CBI	0x00		/* Control/Bulk/Interrupt */
+#define US_PR_CB	0x01		/* Control/Bulk w/o interrupt */
+#define US_PR_BULK	0x50		/* bulk only */
+
+/*
+ * Bulk only data structures
+ */
+
+/* command block wrapper */
+struct bulk_cb_wrap {
+	__u32	Signature;		/* contains 'USBC' */
+	__u32	Tag;			/* unique per command id */
+	__u32	DataTransferLength;	/* size of data */
+	__u8	Flags;			/* direction in bit 0 */
+	__u8	Lun;			/* LUN normally 0 */
+	__u8	Length;			/* of of the CDB */
+	__u8	CDB[16];		/* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN	31
+#define US_BULK_CB_SIGN		0x43425355	/*spells out USBC */
+#define US_BULK_FLAG_IN		1
+#define US_BULK_FLAG_OUT	0
+
+/* command status wrapper */
+struct bulk_cs_wrap {
+	__u32	Signature;		/* should = 'USBS' */
+	__u32	Tag;			/* same as original command */
+	__u32	Residue;		/* amount not transferred */
+	__u8	Status;			/* see below */
+	__u8	Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN	13
+#define US_BULK_CS_SIGN		0x53425355	/* spells out 'USBS' */
+#define US_BULK_STAT_OK		0
+#define US_BULK_STAT_FAIL	1
+#define US_BULK_STAT_PHASE	2
+
+/* bulk-only class specific requests */
+#define US_BULK_RESET_REQUEST	0xff
+#define US_BULK_GET_MAX_LUN	0xfe
+
+/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD		0  /* good transfer                 */
+#define US_BULK_TRANSFER_SHORT		1  /* transfered less than expected */
+#define US_BULK_TRANSFER_FAILED		2  /* transfer died in the middle   */
+#define US_BULK_TRANSFER_ABORTED	3  /* transfer canceled             */
+
+/*
+ * Transport return codes
+ */
+
+#define USB_STOR_TRANSPORT_GOOD	   0   /* Transport good, command good	   */
+#define USB_STOR_TRANSPORT_FAILED  1   /* Transport good, command failed   */
+#define USB_STOR_TRANSPORT_ERROR   2   /* Transport bad (i.e. device dead) */
+#define USB_STOR_TRANSPORT_ABORTED 3   /* Transport aborted                */
+
+/*
+ * CBI accept device specific command
+ */
+
+#define US_CBI_ADSC		0
+
+/* 
+ * GUID definitions
+ */
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+	pg[0] = (vendor << 16) | product;
+	pg[1] = pg[2] = 0;
+	while (*serial) {
+		pg[1] <<= 4;
+		pg[1] |= pg[2] >> 28;
+		pg[2] <<= 4;
+		if (*serial >= 'a')
+			*serial -= 'a' - 'A';
+		pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+			: *serial - 'A' + 10;
+		serial++;
+	}
+}
+
+/*
+ * Unusual device list definitions 
+ */
+
+struct us_unusual_dev {
+	/* we search the list based on these parameters */
+	__u16 idVendor;
+	__u16 idProduct;
+	__u16 bcdDevice;
+
+	/* the list specifies these parameters */
+	const char* name;
+	__u8  useProtocol;
+	__u8  useTransport;
+	unsigned int flags;
+};
+
+/* Flag definitions */
+#define US_FL_SINGLE_LUN      0x00000001 /* allow access to only LUN 0	    */
+#define US_FL_MODE_XLATE      0x00000002 /* translate _6 to _10 comands for
+						    Win/MacOS compatibility */
+#define US_FL_START_STOP      0x00000004 /* ignore START_STOP commands	    */
+#define US_FL_ALT_LENGTH      0x00000008 /* use the alternate algorithm for
+						    us_transfer_length()    */
+#define US_FL_IGNORE_SER      0x00000010 /* Ignore the serial number given  */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-uhci-debug.h linux.18p17/drivers/usb/usb-uhci-debug.h
--- linux.vanilla/drivers/usb/usb-uhci-debug.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-uhci-debug.h	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,182 @@
+#ifdef DEBUG
+static void uhci_show_qh (puhci_desc_t qh)
+{
+	if (qh->type != QH_TYPE) {
+		dbg("qh has not QH_TYPE");
+		return;
+	}
+	dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
+
+	if (qh->hw.qh.head & UHCI_PTR_TERM)
+		dbg("    Head Terminate");
+	else 
+		dbg("    Head: %s @ %08X",
+		    (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.head & ~UHCI_PTR_BITS);
+
+	if (qh->hw.qh.element & UHCI_PTR_TERM)
+		dbg("    Element Terminate");
+	else 
+		dbg("    Element: %s @ %08X",
+		    (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.element & ~UHCI_PTR_BITS);
+}
+#endif
+
+static void uhci_show_td (puhci_desc_t td)
+{
+	char *spid;
+	
+	switch (td->hw.td.info & 0xff) {
+	case USB_PID_SETUP:
+		spid = "SETUP";
+		break;
+	case USB_PID_OUT:
+		spid = " OUT ";
+		break;
+	case USB_PID_IN:
+		spid = " IN  ";
+		break;
+	default:
+		spid = "  ?  ";
+		break;
+	}
+
+	warn("  TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+	     td, virt_to_bus (td),
+	     td->hw.td.info >> 21,
+	     ((td->hw.td.info >> 19) & 1),
+	     (td->hw.td.info >> 15) & 15,
+	     (td->hw.td.info >> 8) & 127,
+	     spid,
+	     td->hw.td.buffer);
+
+	warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+	     td->hw.td.status & 0x7ff,
+	     ((td->hw.td.status >> 27) & 3),
+	     (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
+	     (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
+	     (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
+	     (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
+	     (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
+	     (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+	     (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
+	     (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
+	     (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+	     (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
+		);
+
+	if (td->hw.td.link & UHCI_PTR_TERM)
+		warn("   TD Link Terminate");
+	else 
+		warn("    Link points to %s @ %08x, %s",
+		     (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+		     td->hw.td.link & ~UHCI_PTR_BITS,
+		     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
+}
+#ifdef DEBUG
+static void uhci_show_td_queue (puhci_desc_t td)
+{
+	//dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+	while (1) {
+		uhci_show_td (td);
+		if (td->hw.td.link & UHCI_PTR_TERM)
+			break;
+		if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
+			td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
+		else {
+			dbg("td points to itself!");
+			break;
+		}
+	}
+}
+
+static void uhci_show_queue (puhci_desc_t qh)
+{
+	uhci_desc_t *start_qh=qh;
+
+	dbg("uhci_show_queue %p:", qh);
+	while (1) {
+		uhci_show_qh (qh);
+
+		if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+			uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
+
+		if (qh->hw.qh.head & UHCI_PTR_TERM)
+			break;
+
+		if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS))
+			qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS);
+		else {
+			dbg("qh points to itself!");
+			break;
+		}
+		
+		if (qh==start_qh) { // avoid loop
+			dbg("Loop detect");
+			break;
+		}
+	}		
+}
+
+static void uhci_show_sc (int port, unsigned short status)
+{
+	dbg("  stat%d     =     %04x   %s%s%s%s%s%s%s%s",
+	     port,
+	     status,
+	     (status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+	     (status & USBPORTSC_PR) ? "PortReset " : "",
+	     (status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+	     (status & USBPORTSC_RD) ? "ResumeDetect " : "",
+	     (status & USBPORTSC_PEC) ? "EnableChange " : "",
+	     (status & USBPORTSC_PE) ? "PortEnabled " : "",
+	     (status & USBPORTSC_CSC) ? "ConnectChange " : "",
+	     (status & USBPORTSC_CCS) ? "PortConnected " : "");
+}
+
+void uhci_show_status (puhci_t s)
+{
+	unsigned int io_addr = s->io_addr;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	usbcmd = inw (io_addr + 0);
+	usbstat = inw (io_addr + 2);
+	usbint = inw (io_addr + 4);
+	usbfrnum = inw (io_addr + 6);
+	flbaseadd = inl (io_addr + 8);
+	sof = inb (io_addr + 12);
+	portsc1 = inw (io_addr + 16);
+	portsc2 = inw (io_addr + 18);
+
+	dbg("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s",
+	     usbcmd,
+	     (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
+	     (usbcmd & USBCMD_CF) ? "CF " : "",
+	     (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
+	     (usbcmd & USBCMD_FGR) ? "FGR " : "",
+	     (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
+	     (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
+	     (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+	     (usbcmd & USBCMD_RS) ? "RS " : "");
+
+	dbg("  usbstat   =     %04x   %s%s%s%s%s%s",
+	     usbstat,
+	     (usbstat & USBSTS_HCH) ? "HCHalted " : "",
+	     (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
+	     (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
+	     (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
+	     (usbstat & USBSTS_ERROR) ? "USBError " : "",
+	     (usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	dbg("  usbint    =     %04x", usbint);
+	dbg("  usbfrnum  =   (%d)%03x", (usbfrnum >> 10) & 1,
+	     0xfff & (4 * (unsigned int) usbfrnum));
+	dbg("  flbaseadd = %08x", flbaseadd);
+	dbg("  sof       =       %02x", sof);
+	uhci_show_sc (1, portsc1);
+	uhci_show_sc (2, portsc2);
+}
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-uhci.c linux.18p17/drivers/usb/usb-uhci.c
--- linux.vanilla/drivers/usb/usb-uhci.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-uhci.c	Sat Sep 30 14:39:03 2000
@@ -0,0 +1,2909 @@
+/* 
+ * Universal Host Controller Interface driver for USB (take II).
+ *
+ * (c) 1999-2000 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+ *               Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
+ *               Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
+ *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
+ *          
+ * HW-initalization based on material of
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * $Id: usb-uhci.c,v 1.237 2000/08/08 14:58:17 acher Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>	/* for in_interrupt() */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+/* This enables more detailed sanity checks in submit_iso */
+//#define ISO_SANITY_CHECK
+
+/* This enables debug printks */
+//#define DEBUG
+
+/* This enables all symbols to be exported, to ease debugging oopses */
+//#define DEBUG_SYMBOLS
+
+/* This enables an extra UHCI slab for memory debugging */
+#define DEBUG_SLAB
+
+#define VERSTR "$Revision: 1.237 $ time " __TIME__ " " __DATE__
+
+#include <linux/usb.h>
+#include "usb-uhci.h"
+#include "usb-uhci-debug.h"
+
+#undef DEBUG
+#undef dbg
+#define dbg(format, arg...) do {} while (0)
+#define DEBUG_SYMBOLS
+#ifdef DEBUG_SYMBOLS
+	#define _static
+	#ifndef EXPORT_SYMTAB
+		#define EXPORT_SYMTAB
+	#endif
+#else
+	#define _static static
+#endif
+
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
+#ifdef DEBUG_SLAB
+	static kmem_cache_t *uhci_desc_kmem;
+	static kmem_cache_t *urb_priv_kmem;
+#endif
+
+#define SLAB_FLAG     (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH 
+#define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+#define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+
+// stop bandwidth reclamation after (roughly) 50ms
+#define IDLE_TIMEOUT  (HZ/20)
+
+_static int rh_submit_urb (urb_t *urb);
+_static int rh_unlink_urb (urb_t *urb);
+_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
+
+static uhci_t *devs = NULL;
+
+/* used by userspace UHCI data structure dumper */
+uhci_t **uhci_devices = &devs;
+
+/*-------------------------------------------------------------------*/
+// Cleans up collected QHs
+void clean_descs(uhci_t *s, int force)
+{
+	struct list_head *q;
+	uhci_desc_t *qh;
+	int now=UHCI_GET_CURRENT_FRAME(s);
+
+	q=s->free_desc.prev;
+
+	while (q != &s->free_desc) {
+		qh = list_entry (q, uhci_desc_t, horizontal);
+		q=qh->horizontal.prev;
+
+		if ((qh->last_used!=now) || force)
+			delete_qh(s,qh);
+	}
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_switch_timer_int(uhci_t *s)
+{
+
+	if (!list_empty(&s->urb_unlinked)) {
+		s->td1ms->hw.td.status |= TD_CTRL_IOC;
+	}
+	else {
+		s->td1ms->hw.td.status &= ~TD_CTRL_IOC;
+	}
+
+	if (s->timeout_urbs) {
+		s->td32ms->hw.td.status |= TD_CTRL_IOC;
+	}
+	else {
+		s->td32ms->hw.td.status &= ~TD_CTRL_IOC;
+	}
+
+	wmb();
+}
+/*-------------------------------------------------------------------*/
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+_static void enable_desc_loop(uhci_t *s, urb_t *urb)
+{
+	int flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+	s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; 
+	mb();
+	s->loop_usage++;
+	((urb_priv_t*)urb->hcpriv)->use_loop=1;
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+_static void disable_desc_loop(uhci_t *s, urb_t *urb)
+{
+	int flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+
+	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
+		s->loop_usage--;
+
+		if (!s->loop_usage) {
+			s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+			mb();
+		}
+		((urb_priv_t*)urb->hcpriv)->use_loop=0;
+	}
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+}
+#endif
+/*-------------------------------------------------------------------*/
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
+{
+	struct list_head *p=&urb->urb_list;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	{
+		int type;
+		type=usb_pipetype (urb->pipe);
+
+		if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+			enable_desc_loop(s, urb);
+	}
+#endif
+	((urb_priv_t*)urb->hcpriv)->started=jiffies;
+	list_add (p, &s->urb_list);
+	if (urb->timeout)
+		s->timeout_urbs++;
+	uhci_switch_timer_int(s);
+}
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+	unsigned long flags=0;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	queue_urb_unlocked(s,urb);
+	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+_static void dequeue_urb (uhci_t *s, urb_t *urb)
+{
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	int type;
+
+	type=usb_pipetype (urb->pipe);
+
+	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+		disable_desc_loop(s, urb);
+#endif
+
+	list_del (&urb->urb_list);
+	if (urb->timeout && s->timeout_urbs)
+		s->timeout_urbs--;
+
+}
+/*-------------------------------------------------------------------*/
+_static int alloc_td (uhci_desc_t ** new, int flags)
+{
+#ifdef DEBUG_SLAB
+	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
+#else
+	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
+#endif
+	if (!*new)
+		return -ENOMEM;
+	 memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->hw.td.link = UHCI_PTR_TERM | (flags & UHCI_PTR_BITS);	// last by default
+	(*new)->type = TD_TYPE;
+	mb();
+	INIT_LIST_HEAD (&(*new)->vertical);
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
+{
+	unsigned long xxx;
+	
+	spin_lock_irqsave (&s->td_lock, xxx);
+
+	td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+       
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
+/* insert td at last position in td-list of qh (vertical) */
+_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
+{
+	uhci_desc_t *prev;
+	unsigned long xxx;
+	
+	spin_lock_irqsave (&s->td_lock, xxx);
+
+	list_add_tail (&new->vertical, &qh->vertical);
+
+	prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
+
+	if (qh == prev ) {
+		// virgin qh without any tds
+		qh->hw.qh.element = virt_to_bus (new);
+	}
+	else {
+		// already tds inserted, implicitely remove TERM bit of prev
+		prev->hw.td.link = virt_to_bus (new) | (flags & UHCI_PTR_DEPTH);
+	}
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, xxx);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert new_td after td (horizontal) */
+_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new)
+{
+	uhci_desc_t *next;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->td_lock, flags);
+
+	next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
+	list_add (&new->horizontal, &td->horizontal);
+	new->hw.td.link = td->hw.td.link;
+	td->hw.td.link = virt_to_bus (new);
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, flags);	
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
+{
+	uhci_desc_t *next, *prev;
+	int dir = 0;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->td_lock, flags);
+	
+	next = list_entry (element->vertical.next, uhci_desc_t, vertical);
+	
+	if (next == element) {
+		dir = 1;
+		prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+	}
+	else 
+		prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
+	
+	if (phys_unlink) {
+		// really remove HW linking
+		if (prev->type == TD_TYPE)
+			prev->hw.td.link = element->hw.td.link;
+		else
+			prev->hw.qh.element = element->hw.td.link;
+	}
+
+	mb ();
+
+	if (dir == 0)
+		list_del (&element->vertical);
+	else
+		list_del (&element->horizontal);
+	
+	spin_unlock_irqrestore (&s->td_lock, flags);	
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------*/
+_static int delete_desc (uhci_desc_t *element)
+{
+#ifdef DEBUG_SLAB
+	kmem_cache_free(uhci_desc_kmem, element);
+#else
+	kfree (element);
+#endif
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// Allocates qh element
+_static int alloc_qh (uhci_desc_t ** new)
+{
+#ifdef DEBUG_SLAB
+	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
+#else
+	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
+#endif	
+	if (!*new)
+		return -ENOMEM;
+	memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->hw.qh.head = UHCI_PTR_TERM;
+	(*new)->hw.qh.element = UHCI_PTR_TERM;
+	(*new)->type = QH_TYPE;
+	
+	mb();
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	INIT_LIST_HEAD (&(*new)->vertical);
+	
+	dbg("Allocated qh @ %p", *new);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// inserts new qh before/after the qh at pos
+// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
+_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
+{
+	uhci_desc_t *old;
+	unsigned long flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+
+	if (!order) {
+		// (OLD) (POS) -> (OLD) (NEW) (POS)
+		old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
+		list_add_tail (&new->horizontal, &pos->horizontal);
+		new->hw.qh.head = MAKE_QH_ADDR (pos) ;
+		if (!(old->hw.qh.head & UHCI_PTR_TERM))
+			old->hw.qh.head = MAKE_QH_ADDR (new) ;
+	}
+	else {
+		// (POS) (OLD) -> (POS) (NEW) (OLD)
+		old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
+		list_add (&new->horizontal, &pos->horizontal);
+		new->hw.qh.head = MAKE_QH_ADDR (old);
+		pos->hw.qh.head = MAKE_QH_ADDR (new) ;
+	}
+
+	mb ();
+	
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------*/
+_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
+{
+	uhci_desc_t  *prev;
+	unsigned long flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+	
+	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+	prev->hw.qh.head = element->hw.qh.head;
+
+	list_del(&element->horizontal);
+
+	mb ();
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int delete_qh (uhci_t *s, uhci_desc_t *qh)
+{
+	uhci_desc_t *td;
+	struct list_head *p;
+	
+	list_del (&qh->horizontal);
+
+	while ((p = qh->vertical.next) != &qh->vertical) {
+		td = list_entry (p, uhci_desc_t, vertical);
+		dbg("unlink td @ %p",td);
+		unlink_td (s, td, 0); // no physical unlink
+		delete_desc (td);
+	}
+
+	delete_desc (qh);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static void clean_td_chain (uhci_desc_t *td)
+{
+	struct list_head *p;
+	uhci_desc_t *td1;
+
+	if (!td)
+		return;
+	
+	while ((p = td->horizontal.next) != &td->horizontal) {
+		td1 = list_entry (p, uhci_desc_t, horizontal);
+		delete_desc (td1);
+	}
+	
+	delete_desc (td);
+}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+	td->hw.td.status = status;
+	td->hw.td.info = info;
+	td->hw.td.buffer = buffer;
+}
+/*-------------------------------------------------------------------*/
+// Removes ALL qhs in chain (paranoia!)
+_static void cleanup_skel (uhci_t *s)
+{
+	unsigned int n;
+	uhci_desc_t *td;
+
+	dbg("cleanup_skel");
+
+	clean_descs(s,1);
+
+	
+	if (s->td32ms) {
+	
+		unlink_td(s,s->td32ms,1);
+		delete_desc(s->td32ms);
+	}
+
+	for (n = 0; n < 8; n++) {
+		td = s->int_chain[n];
+		clean_td_chain (td);
+	}
+
+	if (s->iso_td) {
+		for (n = 0; n < 1024; n++) {
+			td = s->iso_td[n];
+			clean_td_chain (td);
+		}
+		kfree (s->iso_td);
+	}
+
+	if (s->framelist)
+		free_page ((unsigned long) s->framelist);
+
+	if (s->control_chain) {
+		// completed init_skel?
+		struct list_head *p;
+		uhci_desc_t *qh, *qh1;
+
+		qh = s->control_chain;
+		while ((p = qh->horizontal.next) != &qh->horizontal) {
+			qh1 = list_entry (p, uhci_desc_t, horizontal);
+			delete_qh (s, qh1);
+		}
+
+		delete_qh (s, qh);
+	}
+	else {
+		if (s->ls_control_chain)
+			delete_desc (s->ls_control_chain);
+		if (s->control_chain)
+			 delete_desc(s->control_chain);
+		if (s->bulk_chain)
+			delete_desc (s->bulk_chain);
+		if (s->chain_end)
+			delete_desc (s->chain_end);
+	}
+	dbg("cleanup_skel finished");	
+}
+/*-------------------------------------------------------------------*/
+// allocates framelist and qh-skeletons
+// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
+_static int init_skel (uhci_t *s)
+{
+	int n, ret;
+	uhci_desc_t *qh, *td;
+	
+	dbg("init_skel");
+	
+	s->framelist = (__u32 *) get_free_page (GFP_KERNEL);
+
+	if (!s->framelist)
+		return -ENOMEM;
+
+	memset (s->framelist, 0, 4096);
+
+	dbg("allocating iso desc pointer list");
+	s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
+	
+	if (!s->iso_td)
+		goto init_skel_cleanup;
+
+	s->ls_control_chain = NULL;
+	s->control_chain = NULL;
+	s->bulk_chain = NULL;
+	s->chain_end = NULL;
+
+	dbg("allocating iso descs");
+	for (n = 0; n < 1024; n++) {
+	 	// allocate skeleton iso/irq-tds
+		ret = alloc_td (&td, 0);
+		if (ret)
+			goto init_skel_cleanup;
+		s->iso_td[n] = td;
+		s->framelist[n] = ((__u32) virt_to_bus (td));
+	}
+
+	dbg("allocating qh: chain_end");
+	ret = alloc_qh (&qh);
+	
+	if (ret)
+		goto init_skel_cleanup;
+				
+	s->chain_end = qh;
+
+	ret = alloc_td (&td, 0);
+
+	if (ret)
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
+	insert_td (s, qh, td, 0);
+	s->td1ms=td;
+
+	dbg("allocating qh: bulk_chain");
+	ret = alloc_qh (&qh);
+	if (ret)
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->chain_end, qh, 0);
+	s->bulk_chain = qh;
+
+	dbg("allocating qh: control_chain");
+	ret = alloc_qh (&qh);
+	if (ret)
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->bulk_chain, qh, 0);
+	s->control_chain = qh;
+
+#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	// disabled reclamation loop
+	s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+	dbg("allocating qh: ls_control_chain");
+	ret = alloc_qh (&qh);
+	if (ret)
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->control_chain, qh, 0);
+	s->ls_control_chain = qh;
+
+	for (n = 0; n < 8; n++)
+		s->int_chain[n] = 0;
+
+	dbg("allocating skeleton INT-TDs");
+	
+	for (n = 0; n < 8; n++) {
+		uhci_desc_t *td;
+
+		alloc_td (&td, 0);
+		if (!td)
+			goto init_skel_cleanup;
+		s->int_chain[n] = td;
+		if (n == 0) {
+			s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
+		}
+		else {
+			s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
+		}
+	}
+
+	dbg("Linking skeleton INT-TDs");
+	
+	for (n = 0; n < 1024; n++) {
+		// link all iso-tds to the interrupt chains
+		int m, o;
+		dbg("framelist[%i]=%x",n,s->framelist[n]);
+		if ((n&127)==127) 
+			((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
+		else 
+			for (o = 1, m = 2; m <= 128; o++, m += m)
+				if ((n & (m - 1)) == ((m - 1) / 2))
+					((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
+	}
+
+	ret = alloc_td (&td, 0);
+
+	if (ret)
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt
+	s->td32ms=td;
+
+	insert_td_horizontal (s, s->int_chain[5], td);
+
+	mb();
+	//uhci_show_queue(s->control_chain);   
+	dbg("init_skel exit");
+	return 0;
+
+      init_skel_cleanup:
+	cleanup_skel (s);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------*/
+//                         LOW LEVEL STUFF
+//          assembles QHs und TDs for control, bulk and iso
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_control_urb (urb_t *urb)
+{
+	uhci_desc_t *qh, *td;
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+	unsigned long len;
+	char *data;
+	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
+
+	if (!maxsze) {
+		err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+		return -EINVAL;
+	}
+
+	dbg("uhci_submit_control start");
+	alloc_qh (&qh);		// alloc qh for this request
+
+	if (!qh)
+		return -ENOMEM;
+
+	alloc_td (&td, UHCI_PTR_DEPTH * depth_first);		// get td for setup stage
+
+	if (!td) {
+		delete_qh (s, qh);
+		return -ENOMEM;
+	}
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+	/* 3 errors */
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	/*  Build the TD for the control request, try forever, 8 bytes of data */
+	fill_td (td, status, destination | (7 << 21), virt_to_bus (urb->setup_packet));
+
+	insert_td (s, qh, td, 0);	// queue 'setup stage'-td in qh
+#if 0
+	{
+		char *sp=urb->setup_packet;
+		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+	}
+	//uhci_show_td(td);
+#endif
+
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+
+	/* If direction is "send", change the frame from SETUP (0x2D)
+	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
+
+	while (len > 0) {
+		int pktsze = len;
+
+		alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
+		if (!td) {
+			delete_qh (s, qh);
+			return -ENOMEM;
+		}
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1
+
+		fill_td (td, status, destination | ((pktsze - 1) << 21),
+			 virt_to_bus (data));	// Status, pktsze bytes of data
+
+		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/*  Build the final TD for control status */
+	/* It's only IN if the pipe is out AND we aren't expecting data */
+
+	destination &= ~UHCI_PID;
+
+	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */
+
+	alloc_td (&td, UHCI_PTR_DEPTH);
+	
+	if (!td) {
+		delete_qh (s, qh);
+		return -ENOMEM;
+	}
+	status &=~TD_CTRL_SPD;
+
+	/* no limit on errors on final packet , 0 bytes of data */
+	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
+		 0);
+
+	insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	urb->status = -EINPROGRESS;
+	queue_urb (s, urb);	// queue before inserting in desc chain
+
+	qh->hw.qh.element &= ~UHCI_PTR_TERM;
+
+	//uhci_show_queue(qh);
+	/* Start it up... put low speed first */
+	if (urb->pipe & TD_CTRL_LS)
+		insert_qh (s, s->control_chain, qh, 0);
+	else
+		insert_qh (s, s->bulk_chain, qh, 0);
+
+	dbg("uhci_submit_control end");
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	uhci_desc_t *qh, *td, *nqh, *bqh;
+	unsigned long destination, status;
+	char *data;
+	unsigned int pipe = urb->pipe;
+	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+	int info, len, last;
+	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
+	urb_priv_t *upriv, *bpriv;
+
+	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
+		return -EPIPE;
+
+	if (urb->transfer_buffer_length < 0) {
+		err("Negative transfer length in submit_bulk");
+		return -EINVAL;
+	}
+	
+	if (!maxsze)
+		return -EMSGSIZE;
+	
+	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
+
+	upriv=(urb_priv_t*)urb->hcpriv;
+
+	if (!bulk_urb) {
+		alloc_qh (&qh);		// get qh for this request
+		
+		if (!qh)
+			return -ENOMEM;
+
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			alloc_qh(&nqh); // placeholder for clean unlink
+			if (!nqh) {
+				delete_desc (qh);
+				return -ENOMEM;
+			}
+			upriv->next_qh = nqh;
+			queue_dbg("new next qh %p",nqh);
+		}
+	}
+	else { 
+		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
+		nqh = bpriv->next_qh;
+		upriv->next_qh=nqh;
+		bpriv->next_queued_urb=urb;
+		upriv->prev_queued_urb=bulk_urb;
+	}
+
+	queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		alloc_qh (&bqh); // "bottom" QH,
+		
+		if (!bqh) {
+			if (!bulk_urb) { 
+				delete_desc(qh);
+				delete_desc(nqh);
+			}
+			return -ENOMEM;
+		}
+		bqh->hw.qh.element = UHCI_PTR_TERM;
+		bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+		upriv->bottom_qh = bqh;
+		queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+	}
+	
+
+	/* The "pipe" thing contains the destination in bits 8--18. */
+	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
+
+	/* 3 errors */
+	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+		((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	/* Build the TDs for the bulk request */
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+	
+	do {					// TBD: Really allow zero-length packets?
+		int pktsze = len;
+
+		alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
+
+		if (!td) {
+			delete_qh (s, qh);
+			return -ENOMEM;
+		}
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		// pktsze bytes of data 
+		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
+			(usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+		fill_td (td, status, info, virt_to_bus (data));
+
+		data += pktsze;
+		len -= pktsze;
+
+		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_DISABLE_SPD)));
+
+		if (last)
+			td->hw.td.status |= TD_CTRL_IOC;	// last one generates INT
+
+		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
+		usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
+	} while (!last);
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		qh->hw.qh.element&=~UHCI_PTR_TERM;
+		append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+	}
+
+	urb->status = -EINPROGRESS;
+	queue_urb_unlocked (s, urb);
+	
+	qh->hw.qh.element  &= ~UHCI_PTR_TERM;
+
+	if (!bulk_urb) { 
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			spin_lock (&s->td_lock);			// both QHs in one go
+			insert_qh (s, s->chain_end, qh, 0);	// Main QH
+			insert_qh (s, s->chain_end, nqh, 0);	// Helper QH
+			spin_unlock (&s->td_lock);
+		}
+		else
+			insert_qh (s, s->chain_end, qh, 0);
+	}
+	
+	//uhci_show_queue(s->bulk_chain);
+	//dbg("uhci_submit_bulk_urb: exit\n");
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+
+	for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				unlink_td (s, td, 1);
+	}
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+
+	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				list_del (p);
+				delete_desc (td);
+	}
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
+
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+	uhci_desc_t *bqh, *nqh, *prevqh;
+	int now;
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+
+	now=UHCI_GET_CURRENT_FRAME(s);
+
+	dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+	bqh=priv->bottom_qh;	
+
+	if (!priv->next_queued_urb)  { // no more appended bulk queues
+
+		if (mode != 2) 
+			unlink_qh (s, qh);
+	
+		if (priv->prev_queued_urb) {
+			urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+			ppriv->bottom_qh = priv->bottom_qh;
+			ppriv->next_queued_urb = NULL;
+		}
+		else if (bqh) {  // queue dead
+			nqh=priv->next_qh;
+			
+			if (mode != 2)
+				unlink_qh(s, nqh);
+
+			if (mode) {
+				nqh->last_used = bqh->last_used = now;
+				list_add_tail (&nqh->horizontal, &s->free_desc);
+				list_add_tail (&bqh->horizontal, &s->free_desc);
+			}			
+		}
+	}
+	else { // there are queued urbs following
+		urb_t *nurb;
+		unsigned long flags;
+		
+       		nurb=priv->next_queued_urb;
+		spin_lock_irqsave (&s->qh_lock, flags);
+		
+		if (!priv->prev_queued_urb) { // top
+			if (mode !=2) {
+				prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+				queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);	
+			
+				list_del (&qh->horizontal);
+				list_add (&bqh->horizontal, &prevqh->horizontal);
+			}	
+		}
+		else {		//intermediate
+			urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+			uhci_desc_t * bnqh;
+			
+			bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+			ppriv->bottom_qh=bnqh;
+			ppriv->next_queued_urb=nurb;
+			
+			if (mode!=2) {
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+				queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);	
+			}
+		}
+		mb();
+		spin_unlock_irqrestore (&s->qh_lock, flags);
+		((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;	
+	}
+
+	if (mode) {
+		qh->last_used = now;
+		list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+	}
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	unsigned long flags=0;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+
+	if (!in_interrupt())		// shouldn't be called from interrupt at all...
+		spin_lock(&urb->lock); 
+	
+	if (urb->status == -EINPROGRESS) {
+		// URB probably still in work
+		dequeue_urb (s, urb);
+		uhci_switch_timer_int(s);
+		s->unlink_urb_done=1;
+
+		if (!in_interrupt())	
+			spin_unlock(&urb->lock); 
+
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);		
+		
+		urb->status = -ENOENT;	// mark urb as killed		
+		urb_priv = urb->hcpriv;
+
+		switch (usb_pipetype (urb->pipe)) {
+
+		case PIPE_INTERRUPT:
+			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+		case PIPE_ISOCHRONOUS:
+			uhci_clean_iso_step1(s, urb_priv);
+			uhci_wait_ms(1);
+			uhci_clean_iso_step2(s, urb_priv);
+			break;
+
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+			spin_lock_irqsave (&s->urb_list_lock, flags);
+			uhci_clean_transfer(s, urb, qh, 1);
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			uhci_wait_ms(1);
+		}
+		
+#ifdef DEBUG_SLAB
+		kmem_cache_free (urb_priv_kmem, urb->hcpriv);
+#else
+		kfree (urb->hcpriv);
+#endif
+		if (urb->complete) {
+			dbg("unlink_urb: calling completion");
+			urb->complete ((struct urb *) urb);
+		}
+		usb_dec_dev_use (urb->dev);
+	}
+	else {
+		if (!in_interrupt())	
+			spin_unlock(&urb->lock); 
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+	struct list_head *q;
+	urb_t *urb;
+	struct usb_device *dev;
+	int pipe,now;
+	urb_priv_t *urb_priv;
+
+	q=s->urb_unlinked.next;
+	now=UHCI_GET_CURRENT_FRAME(s);
+
+	while (q != &s->urb_unlinked) {
+
+		urb = list_entry (q, urb_t, urb_list);
+
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+		q = urb->urb_list.next;
+		
+		if (force ||
+ 		    ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+			async_dbg("async cleanup %p",urb);
+			switch (usb_pipetype (urb->pipe)) { // process descriptors
+			case PIPE_CONTROL:
+				process_transfer (s, urb, 2);
+				break;
+			case PIPE_BULK:
+				if (!s->avoid_bulk.counter)
+					process_transfer (s, urb, 2); // don't unlink (already done)
+				else
+					continue;
+				break;
+			case PIPE_ISOCHRONOUS:
+				process_iso (s, urb, 1); // force, don't unlink
+				break;
+			case PIPE_INTERRUPT:
+				process_interrupt (s, urb);
+				break;
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+		  		urb->status = -ECONNRESET; // mark as asynchronously killed
+
+			pipe = urb->pipe;		// completion may destroy all...
+			dev = urb->dev;
+			urb_priv = urb->hcpriv;
+
+			if (urb->complete) {
+				spin_unlock(&s->urb_list_lock);
+				urb->complete ((struct urb *) urb);
+				spin_lock(&s->urb_list_lock);
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+				urb->status = -ENOENT;  // now the urb is really dead
+			switch (usb_pipetype (pipe)) {
+			case PIPE_ISOCHRONOUS:
+			case PIPE_INTERRUPT:
+				uhci_clean_iso_step2(s, urb_priv);
+				break;
+			}
+	
+			usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+			kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+			kfree (urb_priv);
+#endif
+
+			list_del (&urb->urb_list);
+		}
+	}
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	
+	async_dbg("unlink_urb_async called %p",urb);
+
+	if ((urb->status == -EINPROGRESS) ||
+	    ((usb_pipetype (urb->pipe) ==  PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))
+	{
+		((urb_priv_t*)urb->hcpriv)->started = ~0;
+
+		dequeue_urb (s, urb);
+		list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+		uhci_switch_timer_int(s);
+			
+		s->unlink_urb_done = 1;
+		
+		urb->status = -ECONNABORTED;	// mark urb as "waiting to be killed"	
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+
+		switch (usb_pipetype (urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));		
+
+		case PIPE_ISOCHRONOUS:
+			uhci_clean_iso_step1 (s, urb_priv);
+			break;
+
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_clean_transfer (s, urb, qh, 0);
+			break;
+		}
+		((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+		return -EINPROGRESS;  // completion will follow
+	}		
+
+	return 0;    // URB already dead
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+	uhci_t *s;
+	unsigned long flags=0;
+	dbg("uhci_unlink_urb called for %p",urb);
+	if (!urb || !urb->dev)		// you never know...
+		return -EINVAL;
+	
+	s = (uhci_t*) urb->dev->bus->hcpriv;
+
+	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+		return rh_unlink_urb (urb);
+
+	if (!urb->hcpriv)
+		return -EINVAL;
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		int ret;
+
+       		spin_lock_irqsave (&s->urb_list_lock, flags);
+
+		// The URB needs to be locked if called outside completion context
+
+		if (!in_interrupt())
+			spin_lock(&urb->lock);
+
+		ret = uhci_unlink_urb_async(s, urb);
+
+		if (!in_interrupt())
+			spin_unlock(&urb->lock);
+
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);	
+
+		return ret;
+	}
+	else
+		return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
+// In case of ASAP iso transfer, search the URB-list for already queued URBs
+// for this EP and calculate the earliest start frame for the new
+// URB (easy seamless URB continuation!)
+_static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
+{
+	urb_t *u, *last_urb = NULL;
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	struct list_head *p;
+	int ret=-1;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	p=s->urb_list.prev;
+
+	for (; p != &s->urb_list; p = p->prev) {
+		u = list_entry (p, urb_t, urb_list);
+		// look for pending URBs with identical pipe handle
+		// works only because iso doesn't toggle the data bit!
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+	
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret=0;
+	}
+	
+	spin_unlock_irqrestore(&s->urb_list_lock, flags);
+	
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+// adjust start_frame according to scheduling constraints (ASAP etc)
+
+_static int iso_find_start (urb_t *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	unsigned int now;
+	unsigned int start_limit = 0, stop_limit = 0, queued_size;
+	int limits;
+
+	now = UHCI_GET_CURRENT_FRAME (s) & 1023;
+
+	if ((unsigned) urb->number_of_packets > 900)
+		return -EFBIG;
+	
+	limits = find_iso_limits (urb, &start_limit, &stop_limit);
+	queued_size = (stop_limit - start_limit) & 1023;
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		// first iso
+		if (limits) {
+			// 10ms setup should be enough //FIXME!
+			urb->start_frame = (now + 10) & 1023;
+		}
+		else {
+			urb->start_frame = stop_limit;		//seamless linkage
+
+			if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) {
+				info("iso_find_start: gap in seamless isochronous scheduling");
+				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
+					now, urb->start_frame, urb->number_of_packets, urb->pipe);
+				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough //FIXME!
+			}
+		}
+	}
+	else {
+		urb->start_frame &= 1023;
+		if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) {
+			dbg("iso_find_start: now between start_frame and end");
+			return -EAGAIN;
+		}
+	}
+
+	/* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
+	if (limits)
+		return 0;
+
+	if (((urb->start_frame - start_limit) & 1023) < queued_size ||
+	    ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
+		dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
+			urb->start_frame, urb->number_of_packets, start_limit, stop_limit);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// submits USB interrupt (ie. polling ;-) 
+// ASAP-flag set implicitely
+// if period==0, the the transfer is only done once
+
+_static int uhci_submit_int_urb (urb_t *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	int nint, n, ret;
+	uhci_desc_t *td;
+	int status, destination;
+	int info;
+	unsigned int pipe = urb->pipe;
+
+	if (urb->interval < 0 || urb->interval >= 256)
+		return -EINVAL;
+
+	if (urb->interval == 0)
+		nint = 0;
+	else {
+		for (nint = 0, n = 1; nint <= 8; nint++, n += n)	// round interval down to 2^n
+		 {
+			if (urb->interval < n) {
+				urb->interval = n / 2;
+				break;
+			}
+		}
+		nint--;
+	}
+
+	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
+
+	urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;	// remember start frame, just in case...
+
+	urb->number_of_packets = 1;
+
+	// INT allows only one packet
+	if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))
+		return -EINVAL;
+
+	ret = alloc_td (&td, UHCI_PTR_DEPTH);
+
+	if (ret)
+		return -ENOMEM;
+
+	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) |
+		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);
+
+
+	info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+	fill_td (td, status, info, virt_to_bus (urb->transfer_buffer));
+	list_add_tail (&td->desc_list, &urb_priv->desc_list);
+
+	urb->status = -EINPROGRESS;
+	queue_urb (s, urb);
+
+	insert_td_horizontal (s, s->int_chain[nint], td);	// store in INT-TDs
+
+	usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_iso_urb (urb_t *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+#ifdef ISO_SANITY_CHECK
+	int pipe=urb->pipe;
+	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+#endif
+	int n, ret, last=0;
+	uhci_desc_t *td, **tdm;
+	int status, destination;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();		      // Disable IRQs to schedule all ISO-TDs in time
+	ret = iso_find_start (urb);	// adjusts urb->start_frame for later use
+	
+	if (ret)
+		goto err;
+
+	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
+
+	if (!tdm) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	// First try to get all TDs
+	for (n = 0; n < urb->number_of_packets; n++) {
+		dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);
+		if (!urb->iso_frame_desc[n].length) {
+			// allows ISO striping by setting length to zero in iso_descriptor
+			tdm[n] = 0;
+			continue;
+		}
+
+#ifdef ISO_SANITY_CHECK
+		if(urb->iso_frame_desc[n].length > maxsze) {
+
+			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
+			tdm[n] = 0;
+			ret=-EINVAL;		
+		}
+		else
+#endif
+		ret = alloc_td (&td, UHCI_PTR_DEPTH);
+
+		if (ret) {
+			int i;	// Cleanup allocated TDs
+
+			for (i = 0; i < n; n++)
+				if (tdm[i])
+					 delete_desc(tdm[i]);
+			kfree (tdm);
+			goto err;
+		}
+		last=n;
+		tdm[n] = td;
+	}
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;	//| (urb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD);
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);
+
+	
+	// Queue all allocated TDs
+	for (n = 0; n < urb->number_of_packets; n++) {
+		td = tdm[n];
+		if (!td)
+			continue;
+			
+		if (n  == last)
+			status |= TD_CTRL_IOC;
+
+		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
+			 virt_to_bus (urb->transfer_buffer + urb->iso_frame_desc[n].offset));
+		list_add_tail (&td->desc_list, &urb_priv->desc_list);
+	
+		if (n == last) {
+			urb->status = -EINPROGRESS;
+			queue_urb (s, urb);
+		}
+		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
+		//uhci_show_td(td);
+
+	}
+
+	kfree (tdm);
+	dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);
+	ret = 0;
+
+      err:
+	__restore_flags(flags);
+	return ret;
+
+}
+/*-------------------------------------------------------------------*/
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+ 
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
+{
+	struct list_head *p;
+	urb_t *tmp;
+	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
+
+	dbg("search_dev_ep:");
+
+	p=s->urb_list.next;
+
+	for (; p != &s->urb_list; p = p->next) {
+		tmp = list_entry (p, urb_t, urb_list);
+		dbg("urb: %p", tmp);
+		// we can accept this urb if it is not queued at this time 
+		// or if non-iso transfer requests should be scheduled for the same device and pipe
+		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
+		    (urb == tmp)) {
+			return tmp;	// found another urb already queued for processing
+		}
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_urb (urb_t *urb)
+{
+	uhci_t *s;
+	urb_priv_t *urb_priv;
+	int ret = 0;
+	unsigned long flags;
+	urb_t *bulk_urb=NULL;
+		
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+
+	s = (uhci_t*) urb->dev->bus->hcpriv;
+	//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
+	
+	if (!s->running)
+		return -ENODEV;
+		
+	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+		return rh_submit_urb (urb);	/* virtual root hub */
+
+	usb_inc_dev_use (urb->dev);
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+
+	bulk_urb = search_dev_ep (s, urb);
+
+	if (bulk_urb) {
+
+		queue_dbg("found bulk urb %p\n",bulk_urb);
+
+		if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+		    ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			usb_dec_dev_use (urb->dev);
+			err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+			return -ENXIO;	// urb already queued
+		}
+	}
+
+#ifdef DEBUG_SLAB
+	urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
+#else
+	urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
+#endif
+	if (!urb_priv) {
+		usb_dec_dev_use (urb->dev);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		return -ENOMEM;
+	}
+
+	urb->hcpriv = urb_priv;
+	INIT_LIST_HEAD (&urb_priv->desc_list);
+	urb_priv->flags = 0;
+	dbg("submit_urb: scheduling %p", urb);
+	urb_priv->next_queued_urb = NULL;
+	urb_priv->prev_queued_urb = NULL;
+	urb_priv->bottom_qh = NULL;
+	urb_priv->next_qh = NULL;
+	
+	if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+	
+		if (bulk_urb) {
+			while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb)  // find last queued bulk
+				bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+			
+			((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+		}
+		atomic_inc (&s->avoid_bulk);
+		ret = uhci_submit_bulk_urb (urb, bulk_urb);
+		atomic_dec (&s->avoid_bulk);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+	}
+	else {
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		switch (usb_pipetype (urb->pipe)) {
+		case PIPE_ISOCHRONOUS:
+			ret = uhci_submit_iso_urb (urb);
+			break;
+		case PIPE_INTERRUPT:
+			ret = uhci_submit_int_urb (urb);
+			break;
+		case PIPE_CONTROL:
+			ret = uhci_submit_control_urb (urb);
+			break;
+		default:
+			ret = -EINVAL;
+		}
+	}
+
+	dbg("submit_urb: scheduled with ret: %d", ret);
+	
+	if (ret != 0) {
+		usb_dec_dev_use (urb->dev);
+#ifdef DEBUG_SLAB
+		kmem_cache_free(urb_priv_kmem, urb_priv);
+#else
+		kfree (urb_priv);
+#endif
+		return ret;
+	}
+
+	return 0;
+}
+
+// Checks for URB timeout and removes bandwidth reclamation 
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
+{
+	struct list_head *p,*p2;
+	urb_t *urb;
+	int type;	
+
+	p = s->urb_list.prev;	
+
+	while (p != &s->urb_list) {
+		urb_priv_t *hcpriv;
+
+		p2 = p;
+		p = p->prev;
+		urb = list_entry (p2, urb_t, urb_list);
+		type = usb_pipetype (urb->pipe);
+
+		hcpriv = (urb_priv_t*)urb->hcpriv;
+				
+		if ( urb->timeout && 
+			((hcpriv->started + urb->timeout) < jiffies)) {
+			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+			async_dbg("uhci_check_timeout: timeout for %p",urb);
+			uhci_unlink_urb_async(s, urb);
+		}
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+		     (hcpriv->use_loop) &&
+		     ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+			disable_desc_loop(s, urb);
+#endif
+
+	}
+	s->timeout_check=jiffies;
+}
+
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+_static __u8 root_hub_dev_des[] =
+{
+	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.0 */
+	0x01,
+	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  bDeviceSubClass; */
+	0x00,			/*  __u8  bDeviceProtocol; */
+	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,			/*  __u16 idVendor; */
+	0x00,
+	0x00,			/*  __u16 idProduct; */
+	0x00,
+	0x00,			/*  __u16 bcdDevice; */
+	0x00,
+	0x00,			/*  __u8  iManufacturer; */
+	0x02,			/*  __u8  iProduct; */
+	0x01,			/*  __u8  iSerialNumber; */
+	0x01			/*  __u8  bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+_static __u8 root_hub_config_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x02,			/*  __u8  bDescriptorType; Configuration */
+	0x19,			/*  __u16 wTotalLength; */
+	0x00,
+	0x01,			/*  __u8  bNumInterfaces; */
+	0x01,			/*  __u8  bConfigurationValue; */
+	0x00,			/*  __u8  iConfiguration; */
+	0x40,			/*  __u8  bmAttributes; 
+				   Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+	0x00,			/*  __u8  MaxPower; */
+
+     /* interface */
+	0x09,			/*  __u8  if_bLength; */
+	0x04,			/*  __u8  if_bDescriptorType; Interface */
+	0x00,			/*  __u8  if_bInterfaceNumber; */
+	0x00,			/*  __u8  if_bAlternateSetting; */
+	0x01,			/*  __u8  if_bNumEndpoints; */
+	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  if_bInterfaceSubClass; */
+	0x00,			/*  __u8  if_bInterfaceProtocol; */
+	0x00,			/*  __u8  if_iInterface; */
+
+     /* endpoint */
+	0x07,			/*  __u8  ep_bLength; */
+	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
+	0x08,			/*  __u16 ep_wMaxPacketSize; 8 Bytes */
+	0x00,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+
+_static __u8 root_hub_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/*-------------------------------------------------------------------------*/
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+_static int rh_send_irq (urb_t *urb)
+{
+	int len = 1;
+	int i;
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 data = 0;
+
+	for (i = 0; i < uhci->rh.numports; i++) {
+		data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
+
+	*(__u16 *) urb->transfer_buffer = cpu_to_le16 (data);
+	urb->actual_length = len;
+	urb->status = 0;
+	
+	if ((data > 0) && (uhci->rh.send != 0)) {
+		dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
+		     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
+		urb->complete (urb);
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+_static int rh_init_int_timer (urb_t *urb);
+
+_static void rh_int_timer_do (unsigned long ptr)
+{
+	int len;
+	urb_t *urb = (urb_t*) ptr;
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	if (uhci->rh.send) {
+		len = rh_send_irq (urb);
+		if (len > 0) {
+			urb->actual_length = len;
+			if (urb->complete)
+				urb->complete (urb);
+		}
+	}
+	rh_init_int_timer (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
+_static int rh_init_int_timer (urb_t *urb)
+{
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	uhci->rh.interval = urb->interval;
+	init_timer (&uhci->rh.rh_int_timer);
+	uhci->rh.rh_int_timer.function = rh_int_timer_do;
+	uhci->rh.rh_int_timer.data = (unsigned long) urb;
+	uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
+	add_timer (&uhci->rh.rh_int_timer);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#define OK(x) 			len = (x); break
+
+#define CLR_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) & ~(x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) | (x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+
+/*-------------------------------------------------------------------------*/
+/****
+ ** Root Hub Control Pipe
+ *************************/
+
+
+_static int rh_submit_urb (urb_t *urb)
+{
+	struct usb_device *usb_dev = urb->dev;
+	uhci_t *uhci = usb_dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	devrequest *cmd = (devrequest *) urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = 0;
+	int stat = 0;
+	int i;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 cstatus;
+
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+		dbg("Root-Hub submit IRQ: every %d ms", urb->interval);
+		uhci->rh.urb = urb;
+		uhci->rh.send = 1;
+		uhci->rh.interval = urb->interval;
+		rh_init_int_timer (urb);
+
+		return 0;
+	}
+
+
+	bmRType_bReq = cmd->requesttype | cmd->request << 8;
+	wValue = le16_to_cpu (cmd->value);
+	wIndex = le16_to_cpu (cmd->index);
+	wLength = le16_to_cpu (cmd->length);
+
+	for (i = 0; i < 8; i++)
+		uhci->rh.c_p_r[i] = 0;
+
+	dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x",
+	     uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+	switch (bmRType_bReq) {
+		/* Request Destination:
+		   without flags: Device, 
+		   RH_INTERFACE: interface, 
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here, 
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+		 */
+
+	case RH_GET_STATUS:
+		*(__u16 *) data = cpu_to_le16 (1);
+		OK (2);
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *) data = cpu_to_le32 (0);
+		OK (4);		/* hub power ** */
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1)) |
+			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
+		status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |	/* power on ** */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *) data = cpu_to_le16 (status);
+		*(__u16 *) (data + 2) = cpu_to_le16 (cstatus);
+		OK (4);
+
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case (RH_ENDPOINT_STALL):
+			OK (0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case (RH_C_HUB_OVER_CURRENT):
+			OK (0);	/* hub power over current ** */
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_ENABLE):
+			CLR_RH_PORTSTAT (USBPORTSC_PE);
+			OK (0);
+		case (RH_PORT_SUSPEND):
+			CLR_RH_PORTSTAT (USBPORTSC_SUSP);
+			OK (0);
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_C_PORT_CONNECTION):
+			SET_RH_PORTSTAT (USBPORTSC_CSC);
+			OK (0);
+		case (RH_C_PORT_ENABLE):
+			SET_RH_PORTSTAT (USBPORTSC_PEC);
+			OK (0);
+		case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK (0);
+		case (RH_C_PORT_OVER_CURRENT):
+			OK (0);	/* port power over current ** */
+		case (RH_C_PORT_RESET):
+			uhci->rh.c_p_r[wIndex - 1] = 0;
+			OK (0);
+		}
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_SUSPEND):
+			SET_RH_PORTSTAT (USBPORTSC_SUSP);
+			OK (0);
+		case (RH_PORT_RESET):
+			SET_RH_PORTSTAT (USBPORTSC_PR);
+			uhci_wait_ms (10);
+			uhci->rh.c_p_r[wIndex - 1] = 1;
+			CLR_RH_PORTSTAT (USBPORTSC_PR);
+			udelay (10);
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			uhci_wait_ms (10);
+			SET_RH_PORTSTAT (0xa);
+			OK (0);
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_PORT_ENABLE):
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			OK (0);
+		}
+		break;
+
+	case RH_SET_ADDRESS:
+		uhci->rh.devnum = wValue;
+		OK (0);
+
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case (0x01):	/* device descriptor */
+			len = min (leni, min (sizeof (root_hub_dev_des), wLength));
+			memcpy (data, root_hub_dev_des, len);
+			OK (len);
+		case (0x02):	/* configuration descriptor */
+			len = min (leni, min (sizeof (root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK (len);
+		case (0x03):	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+			        uhci->io_addr, "UHCI",
+				data, wLength);
+			if (len > 0) {
+				OK (min (leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = uhci->rh.numports;
+		len = min (leni, min (sizeof (root_hub_hub_des), wLength));
+		memcpy (data, root_hub_hub_des, len);
+		OK (len);
+
+	case RH_GET_CONFIGURATION:
+		*(__u8 *) data = 0x01;
+		OK (1);
+
+	case RH_SET_CONFIGURATION:
+		OK (0);
+	default:
+		stat = -EPIPE;
+	}
+
+	dbg("Root-Hub stat port1: %x port2: %x",
+	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
+
+	urb->actual_length = len;
+	urb->status = stat;
+	if (urb->complete)
+		urb->complete (urb);
+	return 0;
+}
+/*-------------------------------------------------------------------------*/
+
+_static int rh_unlink_urb (urb_t *urb)
+{
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	if (uhci->rh.urb==urb) {
+		dbg("Root-Hub unlink IRQ");
+		uhci->rh.send = 0;
+		del_timer (&uhci->rh.rh_int_timer);
+	}
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+_static int uhci_map_status (int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)	/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {	/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)	/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)	/* Babble */
+		return -EPIPE;
+	if (status & TD_CTRL_DBUFERR)	/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)	/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)	/* Active */
+		return 0;
+
+	return -EPROTO;
+}
+
+/*
+ * Only the USB core should call uhci_alloc_dev and uhci_free_dev
+ */
+_static int uhci_alloc_dev (struct usb_device *usb_dev)
+{
+	return 0;
+}
+
+_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all)
+{
+	unsigned long flags;
+	struct list_head *p;
+	struct list_head *p2;
+	urb_t *urb;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	p = s->urb_list.prev;	
+	while (p != &s->urb_list) {
+		p2 = p;
+		p = p->prev ;
+		urb = list_entry (p2, urb_t, urb_list);
+		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+		
+		//urb->transfer_flags |=USB_ASYNC_UNLINK; 
+			
+		if (remove_all || (usb_dev == urb->dev)) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			warn("forced removing of queued URB %p due to disconnect",urb);
+			uhci_unlink_urb(urb);
+			urb->dev = NULL; // avoid further processing of this UR
+			spin_lock_irqsave (&s->urb_list_lock, flags);
+			p = s->urb_list.prev;	
+		}
+	}
+	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+
+_static int uhci_free_dev (struct usb_device *usb_dev)
+{
+	uhci_t *s;
+	
+
+	if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
+		return -EINVAL;
+	
+	s=(uhci_t*) usb_dev->bus->hcpriv;	
+	uhci_unlink_urbs(s, usb_dev, 0);
+
+	return 0;
+}
+
+/*
+ * uhci_get_current_frame_number()
+ *
+ * returns the current frame number for a USB bus/controller.
+ */
+_static int uhci_get_current_frame_number (struct usb_device *usb_dev)
+{
+	return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv);
+}
+
+struct usb_operations uhci_device_operations =
+{
+	uhci_alloc_dev,
+	uhci_free_dev,
+	uhci_get_current_frame_number,
+	uhci_submit_urb,
+	uhci_unlink_urb
+};
+
+/* 
+ * For IN-control transfers, process_transfer gets a bit more complicated,
+ * since there are devices that return less data (eg. strings) than they
+ * have announced. This leads to a queue abort due to the short packet,
+ * the status stage is not executed. If this happens, the status stage
+ * is manually re-executed.
+ * mode: 0: QHs already unlinked
+ */
+
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
+{
+	int ret = 0;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *qhl = urb_priv->desc_list.next;
+	uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list);
+	struct list_head *p = qh->vertical.next;
+	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
+	int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));	// save initial data_toggle
+	int maxlength; 	// extracted and remapped info from TD
+	int actual_length;
+	int status = 0;
+
+	//dbg("process_transfer: urb contains bulk/control request");
+
+	/* if the status phase has been retriggered and the
+	   queue is empty or the last status-TD is inactive, the retriggered
+	   status stage is completed
+	 */
+
+	if (urb_priv->flags && 
+		((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) 
+		goto transfer_finished;
+
+	urb->actual_length=0;
+
+	for (; p != &qh->vertical; p = p->next) {
+		desc = list_entry (p, uhci_desc_t, vertical);
+
+		if (desc->hw.td.status & TD_CTRL_ACTIVE)	// do not process active TDs
+			return ret;
+	
+		actual_length = (desc->hw.td.status + 1) & 0x7ff;		// extract transfer parameters from TD
+		maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
+		status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
+
+		if (status == -EPIPE) { 		// see if EP is stalled
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		if (status && (status != -EPIPE)) {	// if any error occurred stop processing of further TDs
+			// only set ret if status returned an error
+  is_error:
+			ret = status;
+			urb->error_count++;
+			break;
+		}
+		else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
+			urb->actual_length += actual_length;
+
+		// got less data than requested
+		if ( (actual_length < maxlength)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				status = -EREMOTEIO;	// treat as real error
+				dbg("process_transfer: SPD!!");
+				break;	// exit after this TD because SP was detected
+			}
+
+			// short read during control-IN: re-start status stage
+			if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {
+				if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {
+			
+					qh->hw.qh.element = virt_to_bus (last_desc);  // re-trigger status stage
+					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
+					//uhci_show_td (desc);
+					//uhci_show_td (last_desc);
+					urb_priv->flags = 1; // mark as short control packet
+					return 0;
+				}
+			}
+			// all other cases: short read is OK
+			data_toggle = uhci_toggle (desc->hw.td.info);
+			break;
+		}
+		else if (status)
+			goto is_error;
+
+		data_toggle = uhci_toggle (desc->hw.td.info);
+		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      
+
+	}
+
+	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
+
+ transfer_finished:
+	
+	uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
+
+	urb->status = status;
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH	
+	disable_desc_loop(s,urb);
+#endif	
+
+	queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
+		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
+	return ret;
+}
+
+_static int process_interrupt (uhci_t *s, urb_t *urb)
+{
+	int i, ret = -EINPROGRESS;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+	int actual_length;
+	int status = 0;
+
+	//dbg("urb contains interrupt request");
+
+	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++)	// Maybe we allow more than one TD later ;-)
+	{
+		desc = list_entry (p, uhci_desc_t, desc_list);
+
+		if (desc->hw.td.status & TD_CTRL_ACTIVE) {
+			// do not process active TDs
+			//dbg("TD ACT Status @%p %08x",desc,desc->hw.td.status);
+			break;
+		}
+
+		if (!desc->hw.td.status & TD_CTRL_IOC) {
+			// do not process one-shot TDs, no recycling
+			break;
+		}
+		// extract transfer parameters from TD
+
+		actual_length = (desc->hw.td.status + 1) & 0x7ff;
+		status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
+
+		// see if EP is stalled
+		if (status == -EPIPE) {
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		// if any error occured: ignore this td, and continue
+		if (status != 0) {
+			//uhci_show_td (desc);
+			urb->error_count++;
+			goto recycle;
+		}
+		else
+			urb->actual_length = actual_length;
+
+	recycle:
+		if (urb->complete) {
+			//dbg("process_interrupt: calling completion, status %i",status);
+			urb->status = status;
+			((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion
+
+			spin_unlock(&s->urb_list_lock);
+			
+			urb->complete ((struct urb *) urb);
+			
+			spin_lock(&s->urb_list_lock);
+
+			((urb_priv_t*)urb->hcpriv)->flags=0;		       			
+		}
+		
+		if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
+			    (urb->status != -ENOENT)) {
+
+			urb->status = -EINPROGRESS;
+
+			// Recycle INT-TD if interval!=0, else mark TD as one-shot
+			if (urb->interval) {
+				
+				desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
+				if (status==0) {
+					((urb_priv_t*)urb->hcpriv)->started=jiffies;
+					desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+									    usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
+					usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+				} else {
+					desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+									     usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
+				}
+				desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+					(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+				mb();
+			}
+			else {
+				uhci_unlink_urb_async(s, urb);
+				desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD
+			}
+		}
+	}
+
+	return ret;
+}
+
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
+{
+	int i;
+	int ret = 0;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+	dbg("urb contains iso request");
+	if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
+		return -EXDEV;	// last TD not finished
+
+	urb->error_count = 0;
+	urb->actual_length = 0;
+	urb->status = 0;
+	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+	    urb->number_of_packets,mode,desc->hw.td.status);
+
+	for (i = 0; p != &urb_priv->desc_list;  i++) {
+		desc = list_entry (p, uhci_desc_t, desc_list);
+		
+		//uhci_show_td(desc);
+		if (desc->hw.td.status & TD_CTRL_ACTIVE) {
+			// means we have completed the last TD, but not the TDs before
+			desc->hw.td.status &= ~TD_CTRL_ACTIVE;
+			dbg("TD still active (%x)- grrr. paranoia!", desc->hw.td.status);
+			ret = -EXDEV;
+			urb->iso_frame_desc[i].status = ret;
+			unlink_td (s, desc, 1);
+			// FIXME: immediate deletion may be dangerous
+			goto err;
+		}
+
+		if (!mode)
+			unlink_td (s, desc, 1);
+
+		if (urb->number_of_packets <= i) {
+			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		if (urb->iso_frame_desc[i].offset + urb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) {
+			// Hm, something really weird is going on
+			dbg("Pointer Paranoia: %p!=%p", urb->iso_frame_desc[i].offset + urb->transfer_buffer, bus_to_virt (desc->hw.td.buffer));
+			ret = -EINVAL;
+			urb->iso_frame_desc[i].status = ret;
+			goto err;
+		}
+		urb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff;
+		urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
+		urb->actual_length += urb->iso_frame_desc[i].actual_length;
+
+	      err:
+
+		if (urb->iso_frame_desc[i].status != 0) {
+			urb->error_count++;
+			urb->status = urb->iso_frame_desc[i].status;
+		}
+		dbg("process_iso: %i: len:%d %08x status:%x",
+		     i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
+
+		list_del (p);
+		p = p->next;
+		delete_desc (desc);
+	}
+	
+	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
+	return ret;
+}
+
+
+_static int process_urb (uhci_t *s, struct list_head *p)
+{
+	int ret = 0;
+	urb_t *urb;
+
+
+	urb=list_entry (p, urb_t, urb_list);
+	//dbg("process_urb: found queued urb: %p", urb);
+
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = process_transfer (s, urb, 1);
+		break;
+	case PIPE_BULK:
+		if (!s->avoid_bulk.counter)
+			ret = process_transfer (s, urb, 1);
+		else
+			return 0;
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = process_iso (s, urb, 0);
+		break;
+	case PIPE_INTERRUPT:
+		ret = process_interrupt (s, urb);
+		break;
+	}
+
+	if (urb->status != -EINPROGRESS) {
+		int proceed = 0;
+
+		dbg("dequeued urb: %p", urb);
+		dequeue_urb (s, urb);
+
+#ifdef DEBUG_SLAB
+		kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+#else
+		kfree (urb->hcpriv);
+#endif
+
+		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {
+			urb_t *tmp = urb->next;	// pointer to first urb
+			int is_ring = 0;
+			
+			if (urb->next) {
+				do {
+					if (tmp->status != -EINPROGRESS) {
+						proceed = 1;
+						break;
+					}
+					tmp = tmp->next;
+				}
+				while (tmp != NULL && tmp != urb->next);
+				if (tmp == urb->next)
+					is_ring = 1;
+			}
+
+			spin_lock(&urb->lock);
+			spin_unlock(&s->urb_list_lock);
+
+			// In case you need the current URB status for your completion handler (before resubmit)
+			if (urb->complete && (!proceed )) {
+				dbg("process_transfer: calling early completion");
+				urb->complete ((struct urb *) urb);
+				if (!proceed && is_ring && (urb->status != -ENOENT))
+					uhci_submit_urb (urb);
+			}
+
+			if (proceed && urb->next) {
+				// if there are linked urbs - handle submitting of them right now.
+				tmp = urb->next;	// pointer to first urb
+
+				do {
+					if ((tmp->status != -EINPROGRESS) && (tmp->status != -ENOENT) && uhci_submit_urb (tmp) != 0)
+						break;
+					tmp = tmp->next;
+				}
+				while (tmp != NULL && tmp != urb->next);	// submit until we reach NULL or our own pointer or submit fails
+
+				if (urb->complete) {
+					dbg("process_transfer: calling completion");
+					urb->complete ((struct urb *) urb);
+				}
+			}
+
+			usb_dec_dev_use (urb->dev);
+			spin_unlock(&urb->lock);		
+			spin_lock(&s->urb_list_lock);
+		}
+	}
+
+	return ret;
+}
+
+_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
+{
+	uhci_t *s = __uhci;
+	unsigned int io_addr = s->io_addr;
+	unsigned short status;
+	struct list_head *p, *p2;
+
+	/*
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
+	 */
+
+	status = inw (io_addr + USBSTS);
+
+	if (!status)		/* shared interrupt, not mine */
+		return;
+
+	dbg("interrupt");
+
+	if (status != 1) {
+		warn("interrupt, status %x, frame# %i", status, 
+		     UHCI_GET_CURRENT_FRAME(s));
+
+		// remove host controller halted state
+		if ((status&0x20) && (s->running)) {
+			outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
+		}
+		//uhci_show_status (s);
+	}
+	/*
+	 * traverse the list in *reverse* direction, because new entries
+	 * may be added at the end.
+	 * also, because process_urb may unlink the current urb,
+	 * we need to advance the list before
+	 */
+
+	spin_lock (&s->urb_list_lock);
+restart:
+	s->unlink_urb_done=0;
+	p = s->urb_list.prev;	
+
+	while (p != &s->urb_list) {
+		p2 = p;
+		p = p->prev;
+		process_urb (s, p2);
+		if (s->unlink_urb_done) {
+			s->unlink_urb_done=0;
+			goto restart;
+		}
+	}
+	if ((jiffies - s->timeout_check) > (HZ/30)) 
+		uhci_check_timeouts(s);
+
+	clean_descs(s,0);
+	uhci_cleanup_unlink(s, 0);
+	uhci_switch_timer_int(s);
+							
+	spin_unlock (&s->urb_list_lock);
+	
+	outw (status, io_addr + USBSTS);
+
+	//dbg("uhci_interrupt: done");
+}
+
+_static void reset_hc (uhci_t *s)
+{
+	unsigned int io_addr = s->io_addr;
+
+	s->apm_state = 0;
+	/* Global reset for 50ms */
+	outw (USBCMD_GRESET, io_addr + USBCMD);
+	uhci_wait_ms (50);
+	outw (0, io_addr + USBCMD);
+	uhci_wait_ms (10);
+}
+
+_static void start_hc (uhci_t *s)
+{
+	unsigned int io_addr = s->io_addr;
+	int timeout = 1000;
+
+	/*
+	 * Reset the HC - this will force us to get a
+	 * new notification of any already connected
+	 * ports due to the virtual disconnect that it
+	 * implies.
+	 */
+	outw (USBCMD_HCRESET, io_addr + USBCMD);
+
+	while (inw (io_addr + USBCMD) & USBCMD_HCRESET) {
+		if (!--timeout) {
+			err("USBCMD_HCRESET timed out!");
+			break;
+		}
+	}
+
+	/* Turn on all interrupts */
+	outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+
+	/* Start at frame 0 */
+	outw (0, io_addr + USBFRNUM);
+	outl (virt_to_bus (s->framelist), io_addr + USBFLBASEADD);
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+	s->apm_state = 1;
+	s->running = 1;
+}
+
+_static void uhci_cleanup_dev(uhci_t *s)
+{
+	struct usb_device *root_hub = s->bus->root_hub;
+
+	s->running = 0;		    // Don't allow submit_urb
+
+	if (root_hub)
+		usb_disconnect (&root_hub);
+
+	reset_hc (s);
+	wait_ms (1);
+
+	uhci_unlink_urbs (s, 0, 1);  // Forced unlink of remaining URBs
+	uhci_cleanup_unlink (s, 1);  // force cleanup of async killed URBs
+	
+	usb_deregister_bus (s->bus);
+
+	release_region (s->io_addr, s->io_size);
+	free_irq (s->irq, s);
+	usb_free_bus (s->bus);
+	cleanup_skel (s);
+	kfree (s);
+}
+
+_static int __init uhci_start_usb (uhci_t *s)
+{				/* start it up */
+	/* connect the virtual root hub */
+	struct usb_device *usb_dev;
+
+	usb_dev = usb_alloc_dev (NULL, s->bus);
+	if (!usb_dev)
+		return -1;
+
+	s->bus->root_hub = usb_dev;
+	usb_connect (usb_dev);
+
+	if (usb_new_device (usb_dev) != 0) {
+		usb_free_dev (usb_dev);
+		return -1;
+	}
+
+	return 0;
+}
+
+_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	uhci_t *s = (uhci_t*) dev->data;
+	dbg("handle_apm_event(%d)", rqst);
+	if (s) {
+		switch (rqst) {
+		case PM_SUSPEND:
+			reset_hc (s);
+			break;
+		case PM_RESUME:
+			start_hc (s);
+			break;
+		}
+	}
+	return 0;
+}
+
+_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
+{
+	uhci_t *s;
+	struct usb_bus *bus;
+	struct pm_dev *pmdev;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
+
+	s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
+	if (!s)
+		return -1;
+
+	memset (s, 0, sizeof (uhci_t));
+	INIT_LIST_HEAD (&s->free_desc);
+	INIT_LIST_HEAD (&s->urb_list);
+	INIT_LIST_HEAD (&s->urb_unlinked);
+	spin_lock_init (&s->urb_list_lock);
+	spin_lock_init (&s->qh_lock);
+	spin_lock_init (&s->td_lock);
+	atomic_set(&s->avoid_bulk, 0);
+	s->timeout_urbs = 0;	
+	s->irq = -1;
+	s->io_addr = io_addr;
+	s->io_size = io_size;
+	s->next = devs;	//chain new uhci device into global list	
+	s->timeout_check = 0;
+	s->uhci_pci=dev;
+
+	bus = usb_alloc_bus (&uhci_device_operations);
+	if (!bus) {
+		kfree (s);
+		return -1;
+	}
+
+	s->bus = bus;
+	bus->hcpriv = s;
+
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/* they may have more but give no way to determine how many they */
+	/* have, so default to 2 */
+	/* According to the UHCI spec, Bit 7 is always set to 1. So we try */
+	/* to use this to our advantage */
+
+	for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) {
+		unsigned int portstatus;
+
+		portstatus = inw (io_addr + 0x10 + (s->maxports * 2));
+		dbg("port %i, adr %x status %x", s->maxports,
+			io_addr + 0x10 + (s->maxports * 2), portstatus);
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	warn("Detected %d ports", s->maxports);
+
+	/* This is experimental so anything less than 2 or greater than 8 is */
+	/*  something weird and we'll ignore it */
+	if (s->maxports < 2 || s->maxports > 8) {
+		dbg("Port count misdetected, forcing to 2 ports");
+		s->maxports = 2;
+	}
+
+	s->rh.numports = s->maxports;
+	s->loop_usage=0;
+	if (init_skel (s)) {
+		usb_free_bus (bus);
+		kfree(s);
+		return -1;
+	}
+
+	request_region (s->io_addr, io_size, MODNAME);
+	reset_hc (s);
+	usb_register_bus (s->bus);
+
+	start_hc (s);
+
+	if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
+		err("request_irq %d failed!",irq);
+		usb_free_bus (bus);
+		reset_hc (s);
+		release_region (s->io_addr, s->io_size);
+		cleanup_skel(s);
+		kfree(s);
+		return -1;
+	}
+
+	s->irq = irq;
+
+	if(uhci_start_usb (s) < 0) {
+		uhci_cleanup_dev(s);
+		return -1;
+	}
+
+	//chain new uhci device into global list
+	devs = s;
+
+	pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
+	if (pmdev)
+		pmdev->data = s;
+
+	return 0;
+}
+
+_static int __init start_uhci (struct pci_dev *dev)
+{
+	int i;
+
+	/* Search for the IO base address.. */
+	for (i = 0; i < 6; i++) {
+
+		unsigned int io_addr = dev->base_address[i];
+		unsigned int io_size = 0x14;
+		if (!(io_addr & 1))
+			continue;
+		io_addr &= ~1;
+
+		/* Is it already in use? */
+		if (check_region (io_addr, io_size))
+			break;
+		/* disable legacy emulation */
+		pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+	
+		return alloc_uhci(dev, dev->irq, io_addr, io_size);
+	}
+	return -1;
+}
+
+static int __init uhci_init (void)
+{
+	int retval = -ENODEV;
+	struct pci_dev *dev = NULL;
+	u8 type;
+	int i=0;
+
+#ifdef DEBUG_SLAB
+
+	uhci_desc_kmem = kmem_cache_create("uhci_desc", sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	
+	if(!uhci_desc_kmem) {
+		err("kmem_cache_create for uhci_desc failed (out of memory)");
+		return -ENOMEM;
+	}
+
+	urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	
+	if(!urb_priv_kmem) {
+		err("kmem_cache_create for urb_priv_t failed (out of memory)");
+		return -ENOMEM;
+	}
+#endif	
+	info(VERSTR);
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	info("High bandwidth mode enabled");	
+#endif
+	for (;;) {
+		dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
+		if (!dev)
+			break;
+
+		/* Is it UHCI */
+		pci_read_config_byte (dev, PCI_CLASS_PROG, &type);
+		if (type != 0)
+			continue;
+
+		if (pci_enable_device (dev) < 0)
+			continue;
+
+		if(!dev->irq)
+		{
+			err("Found UHCI device with no IRQ assigned. Check BIOS settings!");
+			continue;
+		}
+
+		/* Ok set it up */
+		retval = start_uhci (dev);
+	
+		if (!retval)
+			i++;
+	}
+
+	return retval;
+}
+
+static void __exit uhci_cleanup (void)
+{
+	uhci_t *s;
+	while ((s = devs)) {
+		devs = devs->next;
+		uhci_cleanup_dev(s);
+	}
+#ifdef DEBUG_SLAB
+	if(kmem_cache_destroy(uhci_desc_kmem))
+		err("uhci_desc_kmem remained");
+
+	if(kmem_cache_destroy(urb_priv_kmem))
+		err("urb_priv_kmem remained");
+#endif
+}
+
+static void __exit uhci_exit (void)
+{
+	pm_unregister_all (handle_pm_event);
+	uhci_cleanup ();
+}
+
+module_init(uhci_init);
+module_exit(uhci_exit);
+
+MODULE_AUTHOR("Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber");
+MODULE_DESCRIPTION("USB Universal Host Controller Interface driver");
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-uhci.h linux.18p17/drivers/usb/usb-uhci.h
--- linux.vanilla/drivers/usb/usb-uhci.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usb-uhci.h	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,282 @@
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+/*
+   $Id: usb-uhci.h,v 1.55 2000/05/13 12:50:30 acher Exp $
+ */
+#define MODNAME "usb-uhci"
+#define UHCI_LATENCY_TIMER 0
+
+static __inline__ void uhci_wait_ms(unsigned int ms)
+{
+	if(!in_interrupt())
+	{
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + ms * HZ / 1000);
+	}
+	else
+		mdelay(ms);
+}
+
+/* Command register */
+#define USBCMD		0
+#define   USBCMD_RS		0x0001	/* Run/Stop */
+#define   USBCMD_HCRESET	0x0002	/* Host reset */
+#define   USBCMD_GRESET		0x0004	/* Global reset */
+#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
+#define   USBCMD_FGR		0x0010	/* Force Global Resume */
+#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
+#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
+#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS		2
+#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
+#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
+#define   USBSTS_RD		0x0004	/* Resume Detect */
+#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
+#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
+#define   USBSTS_HCH		0x0020	/* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR		4
+#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
+#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
+#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
+#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
+
+#define USBFRNUM	6
+#define USBFLBASEADD	8
+#define USBSOF		12
+
+/* USB port status and control registers */
+#define USBPORTSC1	16
+#define USBPORTSC2	18
+#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
+#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
+#define   USBPORTSC_PE		0x0004	/* Port Enable */
+#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
+#define   USBPORTSC_LS		0x0030	/* Line Status */
+#define   USBPORTSC_RD		0x0040	/* Resume Detect */
+#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
+#define   USBPORTSC_PR		0x0200	/* Port Reset */
+#define   USBPORTSC_SUSP	0x1000	/* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7ff	/* for UHCI controller TD */
+#define UHCI_PID 		0xff	/* PID MASK */
+
+#define UHCI_PTR_BITS		0x000F
+#define UHCI_PTR_TERM		0x0001
+#define UHCI_PTR_QH		0x0002
+#define UHCI_PTR_DEPTH		0x0004
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7ff	/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK)	/* 1-based */
+#define uhci_ptr_to_virt(x)	bus_to_virt(x & ~UHCI_PTR_BITS)
+
+/*
+ * for TD <flags>:
+ */
+#define UHCI_TD_REMOVE		0x0001	/* Remove when done */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE		19
+
+#define uhci_maxlen(token)	((token) >> 21)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define uhci_packetid(token)	((token) & 0xff)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
+/* ------------------------------------------------------------------------------------
+   New TD/QH-structures
+   ------------------------------------------------------------------------------------ */
+typedef enum {
+	TD_TYPE, QH_TYPE
+} uhci_desc_type_t;
+
+typedef struct {
+	__u32 link;
+	__u32 status;
+	__u32 info;
+	__u32 buffer;
+} uhci_td_t, *puhci_td_t;
+
+typedef struct {
+	__u32 head;
+	__u32 element;		/* Queue element pointer */
+} uhci_qh_t, *puhci_qh_t;
+
+typedef struct {
+	union {
+		uhci_td_t td;
+		uhci_qh_t qh;
+	} hw;
+	uhci_desc_type_t type;
+	struct list_head horizontal;
+	struct list_head vertical;
+	struct list_head desc_list;
+	int last_used;
+} uhci_desc_t, *puhci_desc_t;
+
+typedef struct {
+	struct list_head desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
+	unsigned long started;
+	urb_t *next_queued_urb;         // next queued urb for this EP
+	urb_t *prev_queued_urb;
+	uhci_desc_t *bottom_qh;
+	uhci_desc_t *next_qh;       	// next helper QH
+	char use_loop;
+	char flags;
+} urb_priv_t, *purb_priv_t;
+
+struct virt_root_hub {
+	int devnum;		/* Address of Root Hub endpoint */
+	void *urb;
+	void *int_addr;
+	int send;
+	int interval;
+	int numports;
+	int c_p_r[8];
+	struct timer_list rh_int_timer;
+};
+
+typedef struct uhci {
+	int irq;
+	unsigned int io_addr;
+	unsigned int io_size;
+	unsigned int maxports;
+	int running;
+
+	int apm_state;
+
+	struct uhci *next;	// chain of uhci device contexts
+
+	struct list_head urb_list;	// list of all pending urbs
+
+	spinlock_t urb_list_lock;	// lock to keep consistency 
+
+	int unlink_urb_done;
+	atomic_t avoid_bulk;
+	
+	struct usb_bus *bus;	// our bus
+
+	__u32 *framelist;
+	uhci_desc_t **iso_td;
+	uhci_desc_t *int_chain[8];
+	uhci_desc_t *ls_control_chain;
+	uhci_desc_t *control_chain;
+	uhci_desc_t *bulk_chain;
+	uhci_desc_t *chain_end;
+	uhci_desc_t *td1ms;
+	uhci_desc_t *td32ms;
+	struct list_head free_desc;
+	spinlock_t qh_lock;
+	spinlock_t td_lock;
+	struct virt_root_hub rh;	//private data of the virtual root hub
+	int loop_usage;            // URBs using bandwidth reclamation
+
+	struct list_head urb_unlinked;	// list of all unlinked  urbs
+	long timeout_check;
+	int timeout_urbs;
+	struct pci_dev *uhci_pci;
+} uhci_t, *puhci_t;
+
+
+#define MAKE_TD_ADDR(a) (virt_to_bus(a)&~UHCI_PTR_QH)
+#define MAKE_QH_ADDR(a) (virt_to_bus(a)|UHCI_PTR_QH)
+#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM))
+
+/* ------------------------------------------------------------------------------------ 
+   Virtual Root HUB 
+   ------------------------------------------------------------------------------------ */
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS			0x0500
+#define RH_GET_DESCRIPTOR		0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP               0x00
+
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb.c linux.18p17/drivers/usb/usb.c
--- linux.vanilla/drivers/usb/usb.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/usb.c	Sat Oct 14 00:51:40 2000
@@ -1,7 +1,12 @@
 /*
- * driver/usb/usb.c
+ * drivers/usb/usb.c
  *
  * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -10,328 +15,1431 @@
  * Think of this as a "USB library" rather than anything else.
  * It should be considered a slave, with no callbacks. Callbacks
  * are evil.
- */
-
-/*
- * Table 9-2
  *
- * Offset	Field		Size	Value		Desc
- * 0		bmRequestType	1	Bitmap		D7:	Direction
- *							0 = Host-to-device
- *							1 = Device-to-host
- *							D6..5:	Type
- *							0 = Standard
- *							1 = Class
- *							2 = Vendor
- *							3 = Reserved
- *							D4..0:	Recipient
- *							0 = Device
- *							1 = Interface
- *							2 = Endpoint
- *							3 = Other
- *							4..31 = Reserved
- * 1		bRequest	1	Value		Specific request (9-3)
- * 2		wValue		2	Value		Varies
- * 4		wIndex		2	Index/Offset	Varies
- * 6		wLength		2	Count		Bytes for data
+ * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $
  */
 
+#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>  /* for in_interrupt() */
 
-#include "usb.h"
+
+#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
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+static const int usb_bandwidth_option =
+#ifdef CONFIG_USB_BANDWIDTH
+				1;
+#else
+				0;
+#endif
+
+/*
+ * Prototypes for the device driver probing/loading functions
+ */
+static void usb_find_drivers(struct usb_device *);
+static int  usb_find_interface_driver(struct usb_device *, unsigned int);
+static void usb_check_support(struct usb_device *);
 
 /*
  * We have a per-interface "registered driver" list.
  */
-static LIST_HEAD(usb_driver_list);
+LIST_HEAD(usb_driver_list);
+LIST_HEAD(usb_bus_list);
+
+static struct usb_busmap busmap;
+
+static struct usb_driver *usb_minors[16];
 
 int usb_register(struct usb_driver *new_driver)
 {
+	if (new_driver->fops != NULL) {
+		if (usb_minors[new_driver->minor/16]) {
+			 err("error registering %s driver", new_driver->name);
+			return -EINVAL;
+		}
+		usb_minors[new_driver->minor/16] = new_driver;
+	}
+
+	info("registered new driver %s", new_driver->name);
+
+	init_MUTEX(&new_driver->serialize);
+
 	/* Add it to the list of known drivers */
 	list_add(&new_driver->driver_list, &usb_driver_list);
 
-	/*
-	 * We should go through all existing devices, and see if any of
-	 * them would be acceptable to the new driver.. Let's do that
-	 * in version 2.0.
-	 */
+	usb_scan_devices();
+
 	return 0;
 }
 
+/*
+ * We go through all existing devices, and see if any of them would
+ * be acceptable to the new driver.. This is done using a depth-first
+ * search for devices without a registered driver already, then 
+ * running 'probe' with each of the drivers registered on every one 
+ * of these.
+ */
+void usb_scan_devices(void)
+{
+	struct list_head *tmp;
+
+	tmp = usb_bus_list.next;
+	while (tmp != &usb_bus_list) {
+		struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list);
+
+		tmp = tmp->next;
+		usb_check_support(bus->root_hub);
+	}
+}
+
+/*
+ * This function is part of a depth-first search down the device tree,
+ * removing any instances of a device driver.
+ */
+static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
+{
+	int i;
+
+	if (!dev) {
+		err("null device being purged!!!");
+		return;
+	}
+
+	for (i=0; i<USB_MAXCHILDREN; i++)
+		if (dev->children[i])
+			usb_drivers_purge(driver, dev->children[i]);
+
+	if (!dev->actconfig)
+		return;
+			
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *interface = &dev->actconfig->interface[i];
+		
+		if (interface->driver == driver) {
+			down(&driver->serialize);
+			driver->disconnect(dev, interface->private_data);
+			up(&driver->serialize);
+			usb_driver_release_interface(driver, interface);
+			/*
+			 * This will go through the list looking for another
+			 * driver that can handle the device
+			 */
+			usb_find_interface_driver(dev, i);
+		}
+	}
+}
+
+/*
+ * Unlink a driver from the driver list when it is unloaded
+ */
 void usb_deregister(struct usb_driver *driver)
 {
+	struct list_head *tmp;
+
+	info("deregistering driver %s", driver->name);
+	if (driver->fops != NULL)
+		usb_minors[driver->minor/16] = NULL;
+
+	/*
+	 * first we remove the driver, to be sure it doesn't get used by
+	 * another thread while we are stepping through removing entries
+	 */
 	list_del(&driver->driver_list);
+
+	tmp = usb_bus_list.next;
+	while (tmp != &usb_bus_list) {
+		struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list);
+
+		tmp = tmp->next;
+		usb_drivers_purge(driver, bus->root_hub);
+	}
+}
+
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+	int i;
+
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+		if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum)
+			return &dev->actconfig->interface[i];
+
+	return NULL;
+}
+
+/*
+ * usb_calc_bus_time:
+ *
+ * returns (approximate) USB bus time in nanoseconds for a USB transaction.
+ */
+static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
+{
+	unsigned long	tmp;
+
+	if (low_speed)		/* no isoc. here */
+	{
+		if (input_dir)
+		{
+			tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+		else
+		{
+			tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+	}
+
+	/* for full-speed: */
+
+	if (!isoc)		/* Input or Output */
+	{
+		tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+		return (9107L + BW_HOST_DELAY + tmp);
+	} /* end not Isoc */
+
+	/* for isoc: */
+
+	tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+	return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+}
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc.  Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ * However, this first cut at USB bandwidth allocation does not
+ * contain any frame allocation tracking.
+ */
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+	int		new_alloc;
+	int		old_alloc = dev->bus->bandwidth_allocated;
+	unsigned int	pipe = urb->pipe;
+	long		bustime;
+
+	bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe),
+			usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+	if (usb_pipeisoc(pipe))
+		bustime = NS_TO_US(bustime) / urb->number_of_packets;
+	else
+		bustime = NS_TO_US(bustime);
+
+	new_alloc = old_alloc + (int)bustime;
+		/* what new total allocated bus time would be */
+
+	if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
+		dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us",
+			usb_bandwidth_option ? "" : "would have ",
+			old_alloc, new_alloc, bustime);
+
+	if (!usb_bandwidth_option)	/* don't enforce it */
+		return (bustime);
+	return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR;
+}
+
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+	dev->bus->bandwidth_allocated += bustime;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs++;
+	else
+		dev->bus->bandwidth_int_reqs++;
+	urb->bandwidth = bustime;
+	
+	dbg("bw_alloc increased by %d to %d for %d requesters",
+		bustime,
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release a pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
+{
+	dev->bus->bandwidth_allocated -= urb->bandwidth;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs--;
+	else
+		dev->bus->bandwidth_int_reqs--;
+
+	dbg("bw_alloc reduced by %d to %d for %d requesters",
+		urb->bandwidth,
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+	urb->bandwidth = 0;
+}
+
+/*
+ * New functions for (de)registering a controller
+ */
+struct usb_bus *usb_alloc_bus(struct usb_operations *op)
+{
+	struct usb_bus *bus;
+
+	bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return NULL;
+
+	memset(&bus->devmap, 0, sizeof(struct usb_devmap));
+
+	bus->op = op;
+	bus->root_hub = NULL;
+	bus->hcpriv = NULL;
+	bus->busnum = -1;
+	bus->bandwidth_allocated = 0;
+	bus->bandwidth_int_reqs  = 0;
+	bus->bandwidth_isoc_reqs = 0;
+
+	INIT_LIST_HEAD(&bus->bus_list);
+	INIT_LIST_HEAD(&bus->inodes);
+
+	return bus;
+}
+
+void usb_free_bus(struct usb_bus *bus)
+{
+	if (!bus)
+		return;
+
+	kfree(bus);
+}
+
+void usb_register_bus(struct usb_bus *bus)
+{
+	int busnum;
+
+	busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1);
+	if (busnum < USB_MAXBUS) {
+		set_bit(busnum, busmap.busmap);
+		bus->busnum = busnum;
+	} else
+		warn("too many buses");
+
+	/* Add it to the list of buses */
+	list_add(&bus->bus_list, &usb_bus_list);
+
+	usbdevfs_add_bus(bus);
+
+	info("new USB bus registered, assigned bus number %d", bus->busnum);
+}
+
+void usb_deregister_bus(struct usb_bus *bus)
+{
+	info("USB bus %d deregistered", bus->busnum);
+
+	/*
+	 * NOTE: make sure that all the devices are removed by the
+	 * controller code, as well as having it call this when cleaning
+	 * itself up
+	 */
+	list_del(&bus->bus_list);
+
+        usbdevfs_remove_bus(bus);
+
+	clear_bit(bus->busnum, busmap.busmap);
+}
+
+/*
+ * This function is for doing a depth-first search for devices which
+ * have support, for dynamic loading of driver modules.
+ */
+static void usb_check_support(struct usb_device *dev)
+{
+	int i;
+
+	if (!dev) {
+		err("null device being checked!!!");
+		return;
+	}
+
+	for (i=0; i<USB_MAXCHILDREN; i++)
+		if (dev->children[i])
+			usb_check_support(dev->children[i]);
+
+	if (!dev->actconfig)
+		return;
+
+	/* now we check this device */
+	if (dev->devnum > 0)
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+			usb_find_interface_driver(dev, i);
+}
+
+
+/*
+ * This is intended to be used by usb device drivers that need to
+ * claim more than one interface on a device at once when probing
+ * (audio and acm are good examples).  No device driver should have
+ * to mess with the internal usb_interface or usb_device structure
+ * members.
+ */
+void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+{
+	if (!iface || !driver)
+		return;
+
+	dbg("%s driver claimed interface %p", driver->name, iface);
+
+	iface->driver = driver;
+	iface->private_data = priv;
+} /* usb_driver_claim_interface() */
+
+/*
+ * This should be used by drivers to check other interfaces to see if
+ * they are available or not.
+ */
+int usb_interface_claimed(struct usb_interface *iface)
+{
+	if (!iface)
+		return 0;
+
+	return (iface->driver != NULL);
+} /* usb_interface_claimed() */
+
+/*
+ * This should be used by drivers to release their claimed interfaces
+ */
+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 || iface->driver != driver)
+		return;
+
+	iface->driver = NULL;
+	iface->private_data = NULL;
 }
 
 /*
  * This entrypoint gets called for each new device.
  *
  * We now walk the list of registered USB drivers,
- * looking for one that will accept this device as
- * his..
+ * looking for one that will accept this interface.
+ *
+ * The probe return value is changed to be a private pointer.  This way
+ * the drivers don't have to dig around in our structures to set the
+ * private pointer if they only need one interface. 
+ *
+ * Returns: 0 if a driver accepted the interface, -1 otherwise
  */
-void usb_device_descriptor(struct usb_device *dev)
+static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
 {
 	struct list_head *tmp = usb_driver_list.next;
+	struct usb_interface *interface;
+	
+	if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
+		err("bad find_interface_driver params");
+		return -1;
+	}
+
+	interface = dev->actconfig->interface + ifnum;
+
+	if (usb_interface_claimed(interface))
+		return -1;
 
 	while (tmp != &usb_driver_list) {
-		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+		void *private;
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+		  				       driver_list);
+			
 		tmp = tmp->next;
-		if (driver->probe(dev))
+		down(&driver->serialize);
+		private = driver->probe(dev, ifnum);
+		up(&driver->serialize);
+		if (!private)
 			continue;
-		dev->driver = driver;
+		usb_driver_claim_interface(driver, interface, private);
+
+		return 0;
+	}
+	
+	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;
 	}
 
-	/*
-	 * Ok, no driver accepted the device, so show the info
-	 * for debugging..
+	/* 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.
 	 */
-	printk("Unknown new USB device:\n");
-	usb_show_device(dev);
+	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.
+ *
+ * All interfaces are scanned for matching drivers.
+ */
+static void usb_find_drivers(struct usb_device *dev)
+{
+	unsigned ifnum;
+	unsigned rejected = 0;
+	unsigned claimed = 0;
+
+	for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
+		/* if this interface hasn't already been claimed */
+		if (!usb_interface_claimed(dev->actconfig->interface + ifnum)) {
+			if (usb_find_interface_driver(dev, ifnum))
+				rejected++;
+			else
+				claimed++;
+		}
+	}
+ 
+	if (rejected)
+		dbg("unhandled interfaces on device");
+
+	if (!claimed) {
+		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
+	}
 }
 
 /*
- * Parse the fairly incomprehensible output of
- * the USB configuration data, and build up the
- * USB device database.
+ * Only HC's should call usb_alloc_dev and usb_free_dev directly
+ * Anybody may use usb_inc_dev_use or usb_dec_dev_use
  */
-static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
+struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
 {
-	int parsed = 0;
-	int n_len;
-	unsigned short n_desc;
+	struct usb_device *dev;
+
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	memset(dev, 0, sizeof(*dev));
 
-	for (;;) {
-		int i;
+	dev->bus = bus;
+	dev->parent = parent;
+	atomic_set(&dev->refcnt, 1);
+	INIT_LIST_HEAD(&dev->inodes);
+	INIT_LIST_HEAD(&dev->filelist);
 
-		if (len < descindex)
+	dev->bus->op->allocate(dev);
+
+	return dev;
+}
+
+void usb_free_dev(struct usb_device *dev)
+{
+	if (atomic_dec_and_test(&dev->refcnt)) {
+		dev->bus->op->deallocate(dev);
+		usb_destroy_configuration(dev);
+		kfree(dev);
+	}
+}
+
+void usb_inc_dev_use(struct usb_device *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+/* ------------------------------------------------------------------------------------- 
+ * New USB Core Functions
+ * -------------------------------------------------------------------------------------*/
+
+urb_t *usb_alloc_urb(int iso_packets)
+{
+	urb_t *urb;
+
+	urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t),
+	      in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	if (!urb) {
+		err("alloc_urb: kmalloc failed");
+		return NULL;
+	}
+
+	memset(urb, 0, sizeof(*urb));
+
+	spin_lock_init(&urb->lock);
+
+	return urb;
+}
+
+/*-------------------------------------------------------------------*/
+void usb_free_urb(urb_t* urb)
+{
+	if (urb)
+		kfree(urb);
+}
+/*-------------------------------------------------------------------*/
+int usb_submit_urb(urb_t *urb)
+{
+	if (urb && urb->dev)
+		return urb->dev->bus->op->submit_urb(urb);
+	else
+		return -ENODEV;
+}
+
+/*-------------------------------------------------------------------*/
+int usb_unlink_urb(urb_t *urb)
+{
+	if (urb && urb->dev)
+		return urb->dev->bus->op->unlink_urb(urb);
+	else
+		return -ENODEV;
+}
+/*-------------------------------------------------------------------*
+ *                     COMPLETION HANDLERS                           *
+ *-------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------*
+ * completion handler for compatibility wrappers (sync control/bulk) *
+ *-------------------------------------------------------------------*/
+static void usb_api_blocking_completion(urb_t *urb)
+{
+	api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+	if (waitqueue_active(awd->wakeup))
+		wake_up(awd->wakeup);
+#if 0
+	else
+		dbg("(blocking_completion): waitqueue empty!"); 
+		// even occurs if urb was unlinked by timeout...
+#endif
+}
+
+/*-------------------------------------------------------------------*
+ *                         COMPATIBILITY STUFF                       *
+ *-------------------------------------------------------------------*/
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+{ 
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+  
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	current->state = TASK_INTERRUPTIBLE;
+	add_wait_queue(&wqh, &wait);
+	urb->context = &awd;
+	status = usb_submit_urb(urb);
+	if (status) {
+		// something went wrong
+		usb_free_urb(urb);
+		remove_wait_queue(&wqh, &wait);
+		return status;
+	}
+
+	if (urb->status == -EINPROGRESS) {
+		while (timeout && urb->status == -EINPROGRESS)
+			status = timeout = schedule_timeout(timeout);
+	} else
+		status = 1;
+
+	remove_wait_queue(&wqh, &wait);
+
+	if (!status) {
+		// timeout
+		printk("usb_control/bulk_msg: timeout\n");
+		usb_unlink_urb(urb);  // remove urb safely
+		status = -ETIMEDOUT;
+	} else
+		status = urb->status;
+
+	if (actual_length)
+		*actual_length = urb->actual_length;
+
+	usb_free_urb(urb);
+  	return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, 
+			    devrequest *cmd,  void *data, int len, int timeout)
+{
+	urb_t *urb;
+	int retv;
+	int length;
+
+	urb = usb_alloc_urb(0);
+	if (!urb)
+		return -ENOMEM;
+  
+	FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len,    /* build urb */  
+		   (usb_complete_t)usb_api_blocking_completion,0);
+
+	retv = usb_start_wait_urb(urb, timeout, &length);
+	if (retv < 0)
+		return retv;
+	else
+		return length;
+	
+}
+
+/*-------------------------------------------------------------------*/
+int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+			 __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+	devrequest *dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	int ret;
+	
+	if (!dr)
+		return -ENOMEM;
+
+	dr->requesttype = requesttype;
+	dr->request = request;
+	dr->value = cpu_to_le16p(&value);
+	dr->index = cpu_to_le16p(&index);
+	dr->length = cpu_to_le16p(&size);
+
+	//dbg("usb_control_msg");	
+
+	ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+	kfree(dr);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------*/
+/* compatibility wrapper, builds bulk urb, and waits for completion */
+/* synchronous behavior */
+
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, 
+			void *data, int len, int *actual_length, int timeout)
+{
+	urb_t *urb;
+
+	if (len < 0)
+		return -EINVAL;
+
+	urb=usb_alloc_urb(0);
+	if (!urb)
+		return -ENOMEM;
+
+	FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len,   /* build urb */
+			(usb_complete_t)usb_api_blocking_completion,0);
+
+	return usb_start_wait_urb(urb,timeout,actual_length);
+}
+
+/*
+ * usb_get_current_frame_number()
+ *
+ * returns the current frame number for the parent USB bus/controller
+ * of the given USB device.
+ */
+int usb_get_current_frame_number(struct usb_device *usb_dev)
+{
+	return usb_dev->bus->op->get_frame_number (usb_dev);
+}
+/*-------------------------------------------------------------------*/
+
+static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size)
+{
+	struct usb_descriptor_header *header;
+	unsigned char *begin;
+	int parsed = 0, len, numskipped;
+
+	header = (struct usb_descriptor_header *)buffer;
+
+	/* Everything should be fine being passed into here, but we sanity */
+	/*  check JIC */
+	if (header->bLength > size) {
+		err("ran out of descriptors parsing");
+		return -1;
+	}
+		
+	if (header->bDescriptorType != USB_DT_ENDPOINT) {
+		warn("unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X",
+			endpoint->bDescriptorType, USB_DT_ENDPOINT);
+		return parsed;
+	}
+
+	if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE)
+		memcpy(endpoint, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
+	else
+		memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE);
+	
+	le16_to_cpus(&endpoint->wMaxPacketSize);
+
+	buffer += header->bLength;
+	size -= header->bLength;
+	parsed += header->bLength;
+
+	/* 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 < 2) {
+			err("invalid descriptor length of %d", header->bLength);
 			return -1;
-		n_desc = *(unsigned short *)ptr;
-		n_len = n_desc & 0xff;
+		}
+
+		/* 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;
+		parsed += header->bLength;
+	}
+	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) {
+		endpoint->extra = NULL;
+		endpoint->extralen = 0;
+		return parsed;
+	}
+
+	endpoint->extra = kmalloc(len, GFP_KERNEL);
+
+	if (!endpoint->extra) {
+		err("couldn't allocate memory for endpoint extra descriptors");
+		endpoint->extralen = 0;
+		return parsed;
+	}
 
-		if (n_desc == ((desctype << 8) + descindex))
-			break;
+	memcpy(endpoint->extra, begin, len);
+	endpoint->extralen = len;
 
-		if (((n_desc >> 8)&0xFF) == desctype &&
-			n_len > descindex)
-		{
-			printk("bug: oversized descriptor.\n");
-			break;
-		}
-			
-		if (n_len < 2 || n_len > len)
-		{
-			printk("Short descriptor.\n");
-			return -1;
-		}
-		printk(
-		"Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
-			desctype, descindex,
-			(n_desc >> 8) & 0xFF, n_desc & 0xFF);
-		for (i = 0 ; i < n_len; i++)
-			printk("   %d %02x\n", i, ptr[i]);
-		len -= n_len;
-		ptr += n_len;
-		parsed += n_len;
-	}
-	
-	printk("Found %02X:%02X\n",
-		desctype, descindex);
 	return parsed;
 }
 
-/*
- * Parse the even more incomprehensible mess made of the USB spec
- * by USB audio having private magic to go with it.
- */
- 
-static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desctype)
+static int usb_parse_interface(struct usb_device *dev, struct usb_interface *interface, unsigned char *buffer, int size)
 {
-	int n_len = ptr[0];
+	int i, len, numskipped, retval, parsed = 0;
+	struct usb_descriptor_header *header;
+	struct usb_interface_descriptor *ifp;
+	unsigned char *begin;
 
-	if (n_len < 2 || n_len > len)
-	{
-		printk("Short descriptor.\n");
+	interface->act_altsetting = 0;
+	interface->num_altsetting = 0;
+	interface->max_altsetting = USB_ALTSETTINGALLOC;
+
+	interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+	
+	if (!interface->altsetting) {
+		err("couldn't kmalloc interface->altsetting");
 		return -1;
 	}
 
-	if (ptr[1] == desctype)
-		return 0;
-		
-	return -1;
-}
+	while (size > 0) {
+		if (interface->num_altsetting >= interface->max_altsetting) {
+			void *ptr;
+			int oldmas;
+
+			oldmas = interface->max_altsetting;
+			interface->max_altsetting += USB_ALTSETTINGALLOC;
+			if (interface->max_altsetting > USB_MAXALTSETTING) {
+				warn("too many alternate settings (max %d)",
+					USB_MAXALTSETTING);
+				return -1;
+			}
+
+			ptr = interface->altsetting;
+			interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+			if (!interface->altsetting) {
+				err("couldn't kmalloc interface->altsetting");
+				interface->altsetting = ptr;
+				return -1;
+			}
+			memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas);
 
+			kfree(ptr);
+		}
 
-static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
-{
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);
-	int i;
+		ifp = interface->altsetting + interface->num_altsetting;
+		interface->num_altsetting++;
 
-	if (parsed < 0)
-		return parsed;
-	memcpy(endpoint, ptr + parsed, ptr[parsed]);
+		memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);
 
-	parsed += ptr[parsed];
-	len -= ptr[parsed];
+		/* Skip over the interface */
+		buffer += ifp->bLength;
+		parsed += ifp->bLength;
+		size -= ifp->bLength;
+
+		begin = buffer;
+		numskipped = 0;
+
+		/* Skip over any interface, class or vendor descriptors */
+		while (size >= sizeof(struct usb_descriptor_header)) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if (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 return */
+			if ((header->bDescriptorType == USB_DT_INTERFACE) ||
+			    (header->bDescriptorType == USB_DT_ENDPOINT) ||
+			    (header->bDescriptorType == USB_DT_CONFIG) ||
+			    (header->bDescriptorType == USB_DT_DEVICE))
+				break;
 
-	while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0)
-	{
-		usb_audio_endpoint(endpoint, ptr+parsed+i);
-		len -= ptr[parsed+i];
-		parsed += ptr[parsed+i];
-	}
-	
-	return parsed;// + ptr[parsed];
-}
+			numskipped++;
 
-static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
-{
-	int i;
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
-	int retval;
+			buffer += header->bLength;
+			parsed += header->bLength;
+			size -= header->bLength;
+		}
 
-	if (parsed < 0)
-		return parsed;
+		if (numskipped)
+			dbg("skipped %d class/vendor specific interface descriptors", numskipped);
 
-	memcpy(interface, ptr + parsed, *ptr);
-	len -= ptr[parsed];
-	parsed += ptr[parsed];
+		/* Copy any unknown descriptors into a storage area for */
+		/*  drivers to later parse */
+		len = (int)(buffer - begin);
+		if (!len) {
+			ifp->extra = NULL;
+			ifp->extralen = 0;
+		} else {
+			ifp->extra = kmalloc(len, GFP_KERNEL);
+
+			if (!ifp->extra) {
+				err("couldn't allocate memory for interface extra descriptors");
+				ifp->extralen = 0;
+				return -1;
+			}
+			memcpy(ifp->extra, begin, len);
+			ifp->extralen = len;
+		}
 
-	while((i=usb_check_descriptor(ptr+parsed, len, 0x24))>=0)
-	{
-		usb_audio_interface(interface, ptr+parsed+i);
-		len -= ptr[parsed+i];
-		parsed += ptr[parsed+i];
-	}
-	
-	if (interface->bNumEndpoints > USB_MAXENDPOINTS)
-	{
-		printk(KERN_WARNING "usb: too many endpoints.\n");
-		return -1;
-	}
+		/* Did we hit an unexpected descriptor? */
+		header = (struct usb_descriptor_header *)buffer;
+		if ((size >= sizeof(struct usb_descriptor_header)) &&
+		    ((header->bDescriptorType == USB_DT_CONFIG) ||
+		     (header->bDescriptorType == USB_DT_DEVICE)))
+			return parsed;
 
-	interface->endpoint = (struct usb_endpoint_descriptor *)
-		kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
-	if(interface->endpoint==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
-		return -1;	
-	}
-	memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor));
+		if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+			warn("too many endpoints");
+			return -1;
+		}
+
+		ifp->endpoint = (struct usb_endpoint_descriptor *)
+			kmalloc(ifp->bNumEndpoints *
+			sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
+		if (!ifp->endpoint) {
+			err("out of memory");
+			return -1;	
+		}
+
+		memset(ifp->endpoint, 0, ifp->bNumEndpoints *
+			sizeof(struct usb_endpoint_descriptor));
 	
-	for (i = 0; i < interface->bNumEndpoints; i++) {
-//		if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
-//			parsed += 9;	/* skip over the HID descriptor for now */
-//			len -= 9;
-//		}
-		retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len);
-		if (retval < 0) return retval;
-		parsed += retval;
-		len -= retval;
+		for (i = 0; i < ifp->bNumEndpoints; i++) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if (header->bLength > size) {
+				err("ran out of descriptors parsing");
+				return -1;
+			}
+		
+			retval = usb_parse_endpoint(dev, ifp->endpoint + i, buffer, size);
+			if (retval < 0)
+				return retval;
+
+			buffer += retval;
+			parsed += retval;
+			size -= retval;
+		}
+
+		/* We check to see if it's an alternate to this one */
+		ifp = (struct usb_interface_descriptor *)buffer;
+		if (size < USB_DT_INTERFACE_SIZE ||
+		    ifp->bDescriptorType != USB_DT_INTERFACE ||
+		    !ifp->bAlternateSetting)
+			return parsed;
 	}
+
 	return parsed;
 }
 
-static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len)
+int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer)
 {
 	int i;
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
-
-	if (parsed < 0)
-		return parsed;
+	int retval;
+	int size;
+	struct usb_descriptor_header *header;
 
-	memcpy(config, ptr + parsed, *ptr);
-	len -= *ptr;
-	parsed += *ptr;
+	memcpy(config, buffer, USB_DT_CONFIG_SIZE);
+	le16_to_cpus(&config->wTotalLength);
+	size = config->wTotalLength;
 
-	if (config->bNumInterfaces > USB_MAXINTERFACES)
-	{
-		printk(KERN_WARNING "usb: too many interfaces.\n");
+	if (config->bNumInterfaces > USB_MAXINTERFACES) {
+		warn("too many interfaces");
 		return -1;
-
 	}
 
-	config->interface = (struct usb_interface_descriptor *)
-		kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
-	if(config->interface==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
+	config->interface = (struct usb_interface *)
+		kmalloc(config->bNumInterfaces *
+		sizeof(struct usb_interface), GFP_KERNEL);
+	dbg("kmalloc IF %p, numif %i",config->interface,config->bNumInterfaces);
+	if (!config->interface) {
+		err("out of memory");
 		return -1;	
 	}
-	memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
+
+	memset(config->interface, 0,
+	       config->bNumInterfaces * sizeof(struct usb_interface));
+
+	buffer += config->bLength;
+	size -= config->bLength;
 	
 	for (i = 0; i < config->bNumInterfaces; i++) {
-		int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len);
-		if (retval < 0)
-			return parsed; // HACK
-//			return retval;
-		parsed += retval;
-		len -= retval;
-	}
-	return parsed;
-}
+		int numskipped, len;
+		char *begin;
 
-int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
-{
-	int i;
-	unsigned char *ptr = __buf;
+		/* 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;
 
-	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
-	{
-		printk(KERN_WARNING "usb: too many configurations.\n");
-		return -1;
-	}
+			dbg("skipping descriptor 0x%X", header->bDescriptorType);
+			numskipped++;
 
-	dev->config = (struct usb_config_descriptor *)
-		kmalloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor), GFP_KERNEL);
-	if(dev->config==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
-		return -1;	
-	}
-	memset(dev->config, 0, dev->descriptor.bNumConfigurations*sizeof(struct usb_config_descriptor));
-	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
-		int retval = usb_parse_config(dev, dev->config + i, ptr, bytes);
+			buffer += header->bLength;
+			size -= header->bLength;
+		}
+		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);
 		if (retval < 0)
 			return retval;
-		ptr += retval;
-		bytes += retval;
+
+		buffer += retval;
+		size -= retval;
 	}
-	return 0;
+
+	return size;
 }
 
 void usb_destroy_configuration(struct usb_device *dev)
 {
-	int c, i;
-	struct usb_config_descriptor *cf;
-	struct usb_interface_descriptor *ifp;
+	int c, i, j, k;
 	
-	if(dev->config==NULL)
+	if (!dev->config)
 		return;
-	for(c=0;c<dev->descriptor.bNumConfigurations;c++)
-	{
-		cf=&dev->config[c];
-		if(cf->interface==NULL)
+
+	if (dev->rawdescriptors) {
+		for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+			kfree(dev->rawdescriptors[i]);
+
+		kfree(dev->rawdescriptors);
+	}
+
+	for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+		struct usb_config_descriptor *cf = &dev->config[c];
+
+		if (!cf->interface)
 			break;
-		for(i=0;i<cf->bNumInterfaces;i++)
-		{
-			ifp=&cf->interface[i];
-			if(ifp->endpoint==NULL)
+
+		for (i = 0; i < cf->bNumInterfaces; i++) {
+			struct usb_interface *ifp =
+				&cf->interface[i];
+				
+			if (!ifp->altsetting)
 				break;
-			kfree(ifp->endpoint);
+
+			for (j = 0; j < ifp->num_altsetting; j++) {
+				struct usb_interface_descriptor *as =
+					&ifp->altsetting[j];
+					
+				if(as->extra) {
+					kfree(as->extra);
+				}
+
+				if (!as->endpoint)
+					break;
+					
+				for(k = 0; k < as->bNumEndpoints; k++) {
+					if(as->endpoint[k].extra) {
+						kfree(as->endpoint[k].extra);
+					}
+				}	
+				kfree(as->endpoint);
+			}
+
+			kfree(ifp->altsetting);
 		}
 		kfree(cf->interface);
 	}
 	kfree(dev->config);
 }
-			
-void usb_init_root_hub(struct usb_device *dev)
+
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *ascii++ & 0x7f;
+		*utf++ = 0;
+	}
+	return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+	char buf [30];
+
+	// assert (len > (2 * (sizeof (buf) + 1)));
+	// assert (strlen (type) <= 8);
+
+	// language ids
+	if (id == 0) {
+		*data++ = 4; *data++ = 3;	/* 4 bytes data */
+		*data++ = 0; *data++ = 0;	/* some language id */
+		return 4;
+
+	// serial number
+	} else if (id == 1) {
+		sprintf (buf, "%x", serial);
+
+	// product description
+	} else if (id == 2) {
+		sprintf (buf, "USB %s Root Hub", type);
+
+	// id 3 == vendor description
+
+	// unsupported IDs --> "stall"
+	} else
+	    return 0;
+
+	data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+	data [1] = 3;
+	return data [0];
+}
+
+/*
+ * __usb_get_extra_descriptor() finds a descriptor of specific type in the
+ * extra field of the interface and endpoint descriptor structs.
+ */
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
 {
-	dev->devnum = -1;
-	dev->slow = 0;
+	struct usb_descriptor_header *header;
+
+	while (size >= sizeof(struct usb_descriptor_header)) {
+		header = (struct usb_descriptor_header *)buffer;
+
+		if (header->bLength < 2) {
+			err("invalid descriptor length of %d", header->bLength);
+			return -1;
+		}
+
+		if (header->bDescriptorType == type) {
+			*ptr = header;
+			return 0;
+		}
+
+		buffer += header->bLength;
+		size -= header->bLength;
+	}
+	return -1;
 }
 
 /*
@@ -340,29 +1448,46 @@
 void usb_disconnect(struct usb_device **pdev)
 {
 	struct usb_device * dev = *pdev;
+	int i;
 
-	if (dev) {
-		int i;
+	if (!dev)
+		return;
 
-		*pdev = NULL;
+	*pdev = NULL;
 
-		printk("USB disconnect on device %d\n", dev->devnum);
+	info("USB disconnect on device %d", dev->devnum);
 
-		if(dev->driver) dev->driver->disconnect(dev);
+	call_policy ("remove", dev);
 
-		/* Free up all the children.. */
-		for (i = 0; i < USB_MAXCHILDREN; i++) {
-			struct usb_device **child = dev->children + i;
-			usb_disconnect(child);
+	if (dev->actconfig) {
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+			struct usb_interface *interface = &dev->actconfig->interface[i];
+			struct usb_driver *driver = interface->driver;
+			if (driver) {
+				down(&driver->serialize);
+				driver->disconnect(dev, interface->private_data);
+				up(&driver->serialize);
+				usb_driver_release_interface(driver, interface);
+			}
 		}
+	}
 
-		/* Free up the device itself, including its device number */
-		if (dev->devnum > 0)
-			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
-		dev->bus->op->deallocate(dev);
+	/* Free up all the children.. */
+	for (i = 0; i < USB_MAXCHILDREN; i++) {
+		struct usb_device **child = dev->children + i;
+		if (*child)
+			usb_disconnect(child);
+	}
+
+	/* 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);
+}
 
 /*
  * Connect a new USB device. This basically just initializes
@@ -373,9 +1498,11 @@
 void usb_connect(struct usb_device *dev)
 {
 	int devnum;
-
-	dev->descriptor.bMaxPacketSize0 = 8;  /* XXX fixed 8 bytes for now */
-
+	// FIXME needs locking for SMP!!
+	/* why? this is called only from the hub thread, 
+	 * which hopefully doesn't run on multiple CPU's simulatenously 8-)
+	 */
+	dev->descriptor.bMaxPacketSize0 = 8;  /* Start off at 8 bytes  */
 	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
 	if (devnum < 128) {
 		set_bit(devnum, dev->bus->devmap.devicemap);
@@ -387,287 +1514,578 @@
  * These are the actual routines to send
  * and receive control messages.
  */
-int usb_set_address(struct usb_device *dev)
-{
-	devrequest dr;
 
-	dr.requesttype = 0;
-	dr.request = USB_REQ_SET_ADDRESS;
-	dr.value = dev->devnum;
-	dr.index = 0;
-	dr.length = 0;
+#define GET_TIMEOUT 3
+#define SET_TIMEOUT 3
 
-	return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
+int usb_set_address(struct usb_device *dev)
+{
+	return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+		0, dev->devnum, 0, NULL, 0, HZ * GET_TIMEOUT);
 }
 
 int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
 {
-	devrequest dr;
-
-	dr.requesttype = 0x80;
-	dr.request = USB_REQ_GET_DESCRIPTOR;
-	dr.value = (type << 8) + index;
-	dr.index = 0;
-	dr.length = size;
+	int i = 5;
+	int result;
+	
+	memset(buf,0,size);	// Make sure we parse really received data
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+	while (i--) {
+		if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 ||
+		     result == -EPIPE)
+			break;
+	}
+	return result;
 }
 
-int usb_get_device_descriptor(struct usb_device *dev)
+int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
+		unsigned char type, unsigned char id, void *buf, int size)
 {
-	return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+		(type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
 }
 
-int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HUB | 0x80;
-	dr.request = USB_REQ_GET_DESCRIPTOR;
-	dr.value = (USB_DT_HUB << 8);
-	dr.index = 0;
-	dr.length = size;
-
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+		(USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
 }
 
-int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_device_descriptor(struct usb_device *dev)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_PORT;
-	dr.request = USB_REQ_CLEAR_FEATURE;
-	dr.value = feature;
-	dr.index = port;
-	dr.length = 0;
+	int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,
+				     sizeof(dev->descriptor));
+	if (ret >= 0) {
+		le16_to_cpus(&dev->descriptor.bcdUSB);
+		le16_to_cpus(&dev->descriptor.idVendor);
+		le16_to_cpus(&dev->descriptor.idProduct);
+		le16_to_cpus(&dev->descriptor.bcdDevice);
+	}
+	return ret;
+}
 
-	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+int usb_get_status(struct usb_device *dev, int type, int target, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT);
 }
 
-int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_protocol(struct usb_device *dev, int ifnum)
 {
-	devrequest dr;
+	unsigned char type;
+	int ret;
 
-	dr.requesttype = USB_RT_PORT;
-	dr.request = USB_REQ_SET_FEATURE;
-	dr.value = feature;
-	dr.index = port;
-	dr.length = 0;
+	if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+	    USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+	    0, ifnum, &type, 1, HZ * GET_TIMEOUT)) < 0)
+		return ret;
 
-	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+	return type;
 }
 
-int usb_get_hub_status(struct usb_device *dev, void *data)
+int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HUB | 0x80;
-	dr.request = USB_REQ_GET_STATUS;
-	dr.value = 0;
-	dr.index = 0;
-	dr.length = 4;
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		protocol, ifnum, NULL, 0, HZ * SET_TIMEOUT);
+}
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT);
 }
 
-int usb_get_port_status(struct usb_device *dev, int port, void *data)
+void usb_set_maxpacket(struct usb_device *dev)
 {
-	devrequest dr;
+	int i, b;
 
-	dr.requesttype = USB_RT_PORT | 0x80;
-	dr.request = USB_REQ_GET_STATUS;
-	dr.value = 0;
-	dr.index = port;
-	dr.length = 4;
+	for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *ifp = dev->actconfig->interface + i;
+		struct usb_interface_descriptor *as = ifp->altsetting + ifp->act_altsetting;
+		struct usb_endpoint_descriptor *ep = as->endpoint;
+		int e;
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+		for (e=0; e<as->bNumEndpoints; e++) {
+			b = ep[e].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+			if ((ep[e].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+				USB_ENDPOINT_XFER_CONTROL) {	/* Control => bidirectional */
+				dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+				dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+				}
+			else if (usb_endpoint_out(ep[e].bEndpointAddress)) {
+				if (ep[e].wMaxPacketSize > dev->epmaxpacketout[b])
+					dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+			}
+			else {
+				if (ep[e].wMaxPacketSize > dev->epmaxpacketin [b])
+					dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+			}
+		}
+	}
 }
 
-int usb_get_protocol(struct usb_device *dev)
+/*
+ * endp: endpoint number in bits 0-3;
+ *	direction flag in bit 7 (1 = IN, 0 = OUT)
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
 {
-	unsigned char buf[8];
-	devrequest dr;
+	int result;
+	__u16 status;
+	int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
 
-	dr.requesttype = USB_RT_HIDD | 0x80;
-	dr.request = USB_REQ_GET_PROTOCOL;
-	dr.value = 0;
-	dr.index = 1;
-	dr.length = 1;
+/*
+	if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp)))
+		return 0;
+*/
 
-	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
-		return -1;
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT);
 
-	return buf[0];
-}
+	/* don't clear if failed */
+	if (result < 0)
+		return result;
 
-int usb_set_protocol(struct usb_device *dev, int protocol)
-{
-	devrequest dr;
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
+		&status, sizeof(status), HZ * SET_TIMEOUT);
+	if (result < 0)
+		return result;
 
-	dr.requesttype = USB_RT_HIDD;
-	dr.request = USB_REQ_SET_PROTOCOL;
-	dr.value = protocol;
-	dr.index = 1;
-	dr.length = 0;
+	if (le16_to_cpu(status) & 1)
+		return -EPIPE;		/* still halted */
 
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+	/* toggle is reset on clear */
+
+	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
 
 	return 0;
 }
 
-/* keyboards want a nonzero duration according to HID spec, but
-   mice should use infinity (0) -keryan */
-int usb_set_idle(struct usb_device *dev,  int duration, int report_id)
-{
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HIDD;
-	dr.request = USB_REQ_SET_IDLE;
-	dr.value = (duration << 8) | report_id;
-	dr.index = 1;
-	dr.length = 0;
-
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+	struct usb_interface *iface;
+	int ret;
 
+	iface = usb_ifnum_to_if(dev, interface);
+	if (!iface) {
+		warn("selecting invalid interface %d", interface);
+		return -EINVAL;
+	}
+
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+	    USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
+	    interface, NULL, 0, HZ * 5)) < 0)
+		return ret;
+
+	iface->act_altsetting = alternate;
+	dev->toggle[0] = 0;	/* 9.1.1.5 says to do this */
+	dev->toggle[1] = 0;
+	usb_set_maxpacket(dev);
 	return 0;
 }
 
 int usb_set_configuration(struct usb_device *dev, int configuration)
 {
-	devrequest dr;
-
-	dr.requesttype = 0;
-	dr.request = USB_REQ_SET_CONFIGURATION;
-	dr.value = configuration;
-	dr.index = 0;
-	dr.length = 0;
+	int i, ret;
+	struct usb_config_descriptor *cp = NULL;
+	
+	for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+		if (dev->config[i].bConfigurationValue == configuration) {
+			cp = &dev->config[i];
+			break;
+		}
+	}
+	if (!cp) {
+		warn("selecting invalid configuration %d", configuration);
+		return -EINVAL;
+	}
 
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+	    USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0)
+		return ret;
+
+	dev->actconfig = cp;
+	dev->toggle[0] = 0;
+	dev->toggle[1] = 0;
+	usb_set_maxpacket(dev);
 
 	return 0;
 }
 
-int usb_get_report(struct usb_device *dev)
+int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
 {
-	unsigned char buf[8];
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HIDD | 0x80;
-	dr.request = USB_REQ_GET_REPORT;
-	dr.value = 0x100;
-	dr.index = 1;
-	dr.length = 3;
-
-	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
-		return -1;
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
+}
 
-	return buf[0];
+int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(type << 8) + id, ifnum, buf, size, HZ);
 }
 
 int usb_get_configuration(struct usb_device *dev)
 {
-	unsigned int cfgno,size;
-	unsigned char buffer[400];
-	unsigned char * bufptr;
-	
-	bufptr=buffer;
-        for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
-  		/* Get the first 8 bytes - guaranteed */
-	  	if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
-	    		return -1;
+	int result;
+	unsigned int cfgno, length;
+	unsigned char buffer[8];
+	unsigned char *bigbuffer;
+	struct usb_config_descriptor *desc =
+		(struct usb_config_descriptor *)buffer;
+
+	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
+		warn("too many configurations");
+		return -EINVAL;
+	}
+
+	if (dev->descriptor.bNumConfigurations < 1) {
+		warn("not enough configurations");
+		return -EINVAL;
+	}
+
+	dev->config = (struct usb_config_descriptor *)
+		kmalloc(dev->descriptor.bNumConfigurations *
+		sizeof(struct usb_config_descriptor), GFP_KERNEL);
+	if (!dev->config) {
+		err("out of memory");
+		return -ENOMEM;	
+	}
+	memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+		sizeof(struct usb_config_descriptor));
+
+	dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *
+		dev->descriptor.bNumConfigurations, GFP_KERNEL);
+	if (!dev->rawdescriptors) {
+		err("out of memory");
+		return -ENOMEM;
+	}
+
+	for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
+		/* We grab the first 8 bytes so we know how long the whole */
+		/*  configuration is */
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
+		if (result < 8) {
+			if (result < 0)
+				err("unable to get descriptor");
+			else {
+				err("config descriptor too short (expected %i, got %i)", 8, result);
+				result = -EINVAL;
+			}
+			goto err;
+		}
 
   	  	/* Get the full buffer */
-	  	size = *(unsigned short *)(bufptr+2);
-	  	if (bufptr+size > buffer+sizeof(buffer))
-	  	{
-			printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
-			size = buffer+sizeof(buffer)-bufptr;
+		length = le16_to_cpu(desc->wTotalLength);
+
+		bigbuffer = kmalloc(length, GFP_KERNEL);
+		if (!bigbuffer) {
+			err("unable to allocate memory for configuration descriptors");
+			result = -ENOMEM;
+			goto err;
+		}
+
+		/* Now that we know the length, get the whole thing */
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
+		if (result < 0) {
+			err("couldn't get all of config descriptors");
+			kfree(bigbuffer);
+			goto err;
+		}	
+	
+		if (result < length) {
+			err("config descriptor too short (expected %i, got %i)", length, result);
+			result = -EINVAL;
+			kfree(bigbuffer);
+			goto err;
+		}
+
+		dev->rawdescriptors[cfgno] = bigbuffer;
+
+		result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
+		if (result > 0)
+			dbg("descriptor data left");
+		else if (result < 0) {
+			result = -EINVAL;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	dev->descriptor.bNumConfigurations = cfgno;
+	return result;
+}
+
+/*
+ * usb_string:
+ *	returns string length (> 0) or error (< 0)
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+	unsigned char *tbuf;
+	int err;
+	unsigned int u, idx;
+
+	if (size <= 0 || !buf || !index)
+		return -EINVAL;
+	buf[0] = 0;
+	tbuf = kmalloc(256, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	/* get langid for strings if it's not yet known */
+	if (!dev->have_langid) {
+		err = usb_get_string(dev, 0, 0, tbuf, 4);
+		if (err < 0) {
+			err("error getting string descriptor 0 (error=%d)", err);
+			goto errout;
+		} else if (tbuf[0] < 4) {
+			err("string descriptor 0 too short");
+			err = -EINVAL;
+			goto errout;
+		} else {
+			dev->have_langid = -1;
+			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+				/* always use the first langid listed */
+			dbg("USB device number %d default language ID 0x%x",
+				dev->devnum, dev->string_langid);
 		}
-		if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size))
-			return -1;
-			
-		/* Prepare for next configuration */
-		bufptr+=size;
 	}
-	return usb_parse_configuration(dev, buffer, size);
+
+	/*
+	 * Just ask for a maximum length string and then take the length
+	 * that was returned.
+	 */
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, 255);
+	if (err < 0)
+		goto errout;
+
+	size--;		/* leave room for trailing NULL char in output buffer */
+	for (idx = 0, u = 2; u < err; u += 2) {
+		if (idx >= size)
+			break;
+		if (tbuf[u+1])			/* high byte */
+			buf[idx++] = '?';  /* non-ASCII character */
+		else
+			buf[idx++] = tbuf[u];
+	}
+	buf[idx] = 0;
+	err = idx;
+
+ errout:
+	kfree(tbuf);
+	return err;
 }
 
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
  * get the ball rolling..
+ *
+ * Returns 0 for success, != 0 for error.
  */
-void usb_new_device(struct usb_device *dev)
+int usb_new_device(struct usb_device *dev)
 {
-	int addr, i;
+	int err;
 
-	printk("USB new device connect, assigned device number %d\n",
-		dev->devnum);
+	info("USB new device connect, assigned device number %d", dev->devnum);
+ 
+	/* USB v1.1 5.5.3 */
+	/* We read the first 8 bytes from the device descriptor to get to */
+	/*  the bMaxPacketSize0 field. Then we set the maximum packet size */
+	/*  for the control pipe, and retrieve the rest */
+	dev->epmaxpacketin [0] = 8;
+	dev->epmaxpacketout[0] = 8;
+
+	err = usb_set_address(dev);
+	if (err < 0) {
+		err("USB device not accepting new address (error=%d)", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
 
-	dev->maxpacketsize = 0;		/* Default to 8 byte max packet size */
+	wait_ms(10);	/* Let the SET_ADDRESS settle */
 
-	addr = dev->devnum;
-	dev->devnum = 0;
+	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+	if (err < 8) {
+		if (err < 0)
+			err("USB device not responding, giving up (error=%d)", err);
+		else
+			err("USB device descriptor short read (expected %i, got %i)",8,err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+	dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
+	dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
+
+	err = usb_get_device_descriptor(dev);
+	if (err < sizeof(dev->descriptor)) {
+		if (err < 0)
+			err("unable to get device descriptor (error=%d)", err);
+		else
+			err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err);
+	
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+
+	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;
+	}
+
+	/* we set the default configuration here */
+	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",
+		dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+#ifdef DEBUG
+	if (dev->descriptor.iManufacturer)
+		usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
+	if (dev->descriptor.iProduct)
+		usb_show_string(dev, "Product", dev->descriptor.iProduct);
+	if (dev->descriptor.iSerialNumber)
+		usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#endif
 
-	/* Slow devices */
-	for (i = 0; i < 5; i++) {
-		if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
-			break;
+	/* now that the basic setup is over, add a /proc/bus/usb entry */
+	usbdevfs_add_device(dev);
 
-		printk("get_descriptor failed, waiting\n");
-		wait_ms(200);
-	}
-	if (i == 5) {
-		printk("giving up\n");
-		return;
-	}
+	/* find drivers willing to handle this device */
+	usb_find_drivers(dev);
 
-#if 0
-	printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
-#endif
-	switch (dev->descriptor.bMaxPacketSize0) {
-		case 8: dev->maxpacketsize = 0; break;
-		case 16: dev->maxpacketsize = 1; break;
-		case 32: dev->maxpacketsize = 2; break;
-		case 64: dev->maxpacketsize = 3; break;
-	}
-#if 0
-	printk("dev->mps: %d\n", dev->maxpacketsize);
-#endif
+	/* userspace may load modules and/or configure further */
+	call_policy ("add", dev);
 
-	dev->devnum = addr;
+	return 0;
+}
 
-	if (usb_set_address(dev)) {
-		printk("Unable to set address\n");
-		/* FIXME: We should disable the port */
-		return;
-	}
+static int usb_open(struct inode * inode, struct file * file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct usb_driver *c = usb_minors[minor/16];
+	int err = -ENODEV;
 
-	wait_ms(10);	/* Let the SET_ADDRESS settle */
+	if (!c || !c->fops)
+		return err;
 
-	if (usb_get_device_descriptor(dev)) {
-		printk("Unable to get device descriptor\n");
-		return;
-	}
+	file->f_op = c->fops;
 
-	if (usb_get_configuration(dev)) {
-		printk("Unable to get configuration\n");
-		return;
+	return file->f_op->open(inode,file);
+}
+
+static struct file_operations usb_fops = {
+	open:		usb_open,
+};
+
+int usb_major_init(void)
+{
+	if (register_chrdev(USB_MAJOR,"usb",&usb_fops)) {
+		err("unable to get major %d for usb devices", USB_MAJOR);
+		return -EBUSY;
 	}
+	return 0;
+}
+
+void usb_major_cleanup(void)
+{
+	unregister_chrdev(USB_MAJOR, "usb");
+}
 
-#if 0
-	printk("Vendor: %X\n", dev->descriptor.idVendor);
-	printk("Product: %X\n", dev->descriptor.idProduct);
-#endif
 
-	usb_device_descriptor(dev);
+#ifdef CONFIG_PROC_FS
+struct list_head *usb_driver_get_list(void)
+{
+	return &usb_driver_list;
 }
 
-int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+struct list_head *usb_bus_get_list(void)
 {
-	return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
+	return &usb_bus_list;
 }
+#endif
+
+/*
+ * USB may be built into the kernel or be built as modules.
+ * If the USB core [and maybe a host controller driver] is built
+ * into the kernel, and other device drivers are built as modules,
+ * then these symbols need to be exported for the modules to use.
+ */
+EXPORT_SYMBOL(usb_ifnum_to_if);
+
+EXPORT_SYMBOL(usb_register);
+EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL(usb_scan_devices);
+EXPORT_SYMBOL(usb_alloc_bus);
+EXPORT_SYMBOL(usb_free_bus);
+EXPORT_SYMBOL(usb_register_bus);
+EXPORT_SYMBOL(usb_deregister_bus);
+EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL(usb_free_dev);
+EXPORT_SYMBOL(usb_inc_dev_use);
+
+EXPORT_SYMBOL(usb_driver_claim_interface);
+EXPORT_SYMBOL(usb_interface_claimed);
+EXPORT_SYMBOL(usb_driver_release_interface);
+
+EXPORT_SYMBOL(usb_root_hub_string);
+EXPORT_SYMBOL(usb_new_device);
+EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_connect);
+EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(usb_check_bandwidth);
+EXPORT_SYMBOL(usb_claim_bandwidth);
+EXPORT_SYMBOL(usb_release_bandwidth);
+
+EXPORT_SYMBOL(usb_set_address);
+EXPORT_SYMBOL(usb_get_descriptor);
+EXPORT_SYMBOL(usb_get_class_descriptor);
+EXPORT_SYMBOL(__usb_get_extra_descriptor);
+EXPORT_SYMBOL(usb_get_device_descriptor);
+EXPORT_SYMBOL(usb_get_string);
+EXPORT_SYMBOL(usb_string);
+EXPORT_SYMBOL(usb_get_protocol);
+EXPORT_SYMBOL(usb_set_protocol);
+EXPORT_SYMBOL(usb_get_report);
+EXPORT_SYMBOL(usb_set_report);
+EXPORT_SYMBOL(usb_set_idle);
+EXPORT_SYMBOL(usb_clear_halt);
+EXPORT_SYMBOL(usb_set_interface);
+EXPORT_SYMBOL(usb_get_configuration);
+EXPORT_SYMBOL(usb_set_configuration);
+
+EXPORT_SYMBOL(usb_get_current_frame_number);
+
+EXPORT_SYMBOL(usb_alloc_urb);
+EXPORT_SYMBOL(usb_free_urb);
+EXPORT_SYMBOL(usb_submit_urb);
+EXPORT_SYMBOL(usb_unlink_urb);
 
+EXPORT_SYMBOL(usb_control_msg);
+EXPORT_SYMBOL(usb_bulk_msg);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb.h linux.18p17/drivers/usb/usb.h
--- linux.vanilla/drivers/usb/usb.h	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/usb/usb.h	Thu Jan  1 01:00:00 1970
@@ -1,376 +0,0 @@
-#ifndef __LINUX_USB_H
-#define __LINUX_USB_H
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/sched.h>
-
-static __inline__ void wait_ms(unsigned int ms)
-{
-        current->state = TASK_UNINTERRUPTIBLE;
-        schedule_timeout(1 + ms * HZ / 1000);
-}
-
-
-typedef struct {
-  unsigned char requesttype;
-  unsigned char request;
-  unsigned short value;
-  unsigned short index;
-  unsigned short length;
-} devrequest;
-
-/*
- * Class codes
- */
-#define USB_CLASS_HUB			9
-
-/*
- * Descriptor types
- */
-#define USB_DT_DEVICE			0x01
-#define USB_DT_CONFIG			0x02
-#define USB_DT_STRING			0x03
-#define USB_DT_INTERFACE		0x04
-#define USB_DT_ENDPOINT			0x05
-
-#define USB_DT_HUB			0x29
-#define USB_DT_HID			0x21
-
-/*
- * Standard requests
- */
-#define USB_REQ_GET_STATUS		0x00
-#define USB_REQ_CLEAR_FEATURE		0x01
-/* 0x02 is reserved */
-#define USB_REQ_SET_FEATURE		0x03
-/* 0x04 is reserved */
-#define USB_REQ_SET_ADDRESS		0x05
-#define USB_REQ_GET_DESCRIPTOR		0x06
-#define USB_REQ_SET_DESCRIPTOR		0x07
-#define USB_REQ_GET_CONFIGURATION	0x08
-#define USB_REQ_SET_CONFIGURATION	0x09
-#define USB_REQ_GET_INTERFACE		0x0A
-#define USB_REQ_SET_INTERFACE		0x0B
-#define USB_REQ_SYNCH_FRAME		0x0C
-
-/*
- * HIDD requests
- */
-#define USB_REQ_GET_REPORT		0x01
-#define USB_REQ_GET_IDLE		0x02
-#define USB_REQ_GET_PROTOCOL		0x03
-#define USB_REQ_SET_REPORT		0x09
-#define USB_REQ_SET_IDLE		0x0A
-#define USB_REQ_SET_PROTOCOL		0x0B
-
-#define USB_TYPE_STANDARD		(0x00 << 5)
-#define USB_TYPE_CLASS			(0x01 << 5)
-#define USB_TYPE_VENDOR			(0x02 << 5)
-#define USB_TYPE_RESERVED		(0x03 << 5)
-
-#define USB_RECIP_DEVICE		0x00
-#define USB_RECIP_INTERFACE		0x01
-#define USB_RECIP_ENDPOINT		0x02
-#define USB_RECIP_OTHER			0x03
-
-/*
- * Request target types.
- */
-#define USB_RT_DEVICE			0x00
-#define USB_RT_INTERFACE		0x01
-#define USB_RT_ENDPOINT			0x02
-
-#define USB_RT_HUB			(USB_TYPE_CLASS | USB_RECIP_DEVICE)
-#define USB_RT_PORT			(USB_TYPE_CLASS | USB_RECIP_OTHER)
-
-#define USB_RT_HIDD			(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
-
-/*
- * USB device number allocation bitmap. There's one bitmap
- * per USB tree.
- */
-struct usb_devmap {
-	unsigned long devicemap[128 / (8*sizeof(unsigned long))];
-};
-
-/*
- * This is a USB device descriptor.
- *
- * USB device information
- *
- */
-
-#define USB_MAXCONFIG		8
-#define USB_MAXINTERFACES	32
-#define USB_MAXENDPOINTS	32
-
-struct usb_device_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-	__u16 bcdUSB;
-	__u8  bDeviceClass;
-	__u8  bDeviceSubClass;
-	__u8  bDeviceProtocol;
-	__u8  bMaxPacketSize0;
-	__u16 idVendor;
-	__u16 idProduct;
-	__u16 bcdDevice;
-	__u8  iManufacturer;
-	__u8  iProduct;
-	__u8  iSerialNumber;
-	__u8  bNumConfigurations;
-};
-
-/* Endpoint descriptor */
-struct usb_endpoint_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-	__u8  bEndpointAddress;
-	__u8  bmAttributes;
-	__u16 wMaxPacketSize;
-	__u8  bInterval;
-	void  *audio;
-};
-
-/* Interface descriptor */
-struct usb_interface_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-	__u8  bInterfaceNumber;
-	__u8  bAlternateSetting;
-	__u8  bNumEndpoints;
-	__u8  bInterfaceClass;
-	__u8  bInterfaceSubClass;
-	__u8  bInterfaceProtocol;
-	__u8  iInterface;
-
-	struct usb_endpoint_descriptor *endpoint;
-	void  *audio;
-};
-
-/* Configuration descriptor information.. */
-struct usb_config_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-	__u16 wTotalLength;
-	__u8  bNumInterfaces;
-	__u8  bConfigurationValue;
-	__u8  iConfiguration;
-	__u8  bmAttributes;
-	__u8  MaxPower;
-
-	struct usb_interface_descriptor *interface;
-};
-
-/* String descriptor */
-struct usb_string_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-};
-
-/* Hub descriptor */
-struct usb_hub_descriptor {
-	__u8  bLength;
-	__u8  bDescriptorType;
-	__u8  bNbrPorts;
-	__u16 wHubCharacteristics;
-	__u8  bPwrOn2PwrGood;
-	__u8  bHubContrCurrent;
-	/* DeviceRemovable and PortPwrCtrlMask want to be variable-length 
-	   bitmaps that hold max 256 entries, but for now they're ignored */
-	__u8  filler;
-};
-
-struct usb_device;
-
-struct usb_driver {
-	const char * name;
-	int (*probe)(struct usb_device *);
-	void (*disconnect)(struct usb_device *);
-	struct list_head driver_list;
-};
-
-/*
- * Pointer to a device endpoint interrupt function -greg
- *   Parameters:
- *     int status - This needs to be defined.  Right now each HCD
- *         passes different transfer status bits back.  Don't use it
- *         until we come up with a common meaning.
- *     void *buffer - This is a pointer to the data used in this
- *         USB transfer.
- *     void *dev_id - This is a user defined pointer set when the IRQ
- *         is requested that is passed back.
- */
-typedef int (*usb_device_irq)(int, void *, void *);
-
-struct usb_operations {
-	struct usb_device *(*allocate)(struct usb_device *);
-	int (*deallocate)(struct usb_device *);
-	int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
-	int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
-};
-
-/*
- * Allocated per bus we have
- */
-struct usb_bus {
-	struct usb_devmap devmap;       /* Device map */
-	struct usb_operations *op;      /* Operations (specific to the HC) */
-	struct usb_device *root_hub;    /* Root hub */
-	void *hcpriv;                   /* Host Controller private data */
-};
-
-
-#define USB_MAXCHILDREN (8)
-
-struct usb_device {
-	int devnum;						/* Device number on USB bus */
-	int slow;						/* Slow device? */
-	int maxpacketsize;					/* Maximum packet size */
-
-	struct usb_bus *bus;					/* Bus we're apart of */
-	struct usb_driver *driver;				/* Driver */
-	struct usb_device_descriptor descriptor;		/* Descriptor */
-	struct usb_config_descriptor *config;			/* All of the configs */
-	struct usb_device *parent;
-  
-	/*
-	 * Child devices - these can be either new devices
-	 * (if this is a hub device), or different instances
-	 * of this same device.
-	 *
-	 * Each instance needs its own set of data structuctures.
-	 */
-
-	int maxchild;			/* Number of ports if hub */
-	struct usb_device *children[USB_MAXCHILDREN];
-
-	void *hcpriv;			/* Host Controller private data */
-	void *private;			/* Upper layer private data */
-};
-
-extern int usb_register(struct usb_driver *);
-extern void usb_deregister(struct usb_driver *);
-
-extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *);
-
-extern void usb_init_root_hub(struct usb_device *dev);
-extern void usb_connect(struct usb_device *dev);
-extern void usb_disconnect(struct usb_device **);
-extern void usb_device_descriptor(struct usb_device *dev);
-
-extern int  usb_parse_configuration(struct usb_device *dev, void *buf, int len);
-
-/*
- * Calling this entity a "pipe" is glorifying it. A USB pipe
- * is something embarrassingly simple: it basically consists
- * of the following information:
- *  - device number (7 bits)
- *  - endpoint number (4 bits)
- *  - current Data0/1 state (1 bit)
- *  - direction (1 bit)
- *  - speed (1 bit)
- *  - max packet size (2 bits: 8, 16, 32 or 64)
- *  - pipe type (2 bits: control, interrupt, bulk, isochronous)
- *
- * That's 18 bits. Really. Nothing more. And the USB people have
- * documented these eighteen bits as some kind of glorious
- * virtual data structure.
- *
- * Let's not fall in that trap. We'll just encode it as a simple
- * unsigned int. The encoding is:
- *
- *  - device:		bits 8-14
- *  - endpoint:		bits 15-18
- *  - Data0/1:		bit 19
- *  - direction:	bit 7		(0 = Host-to-Device, 1 = Device-to-Host)
- *  - speed:		bit 26		(0 = High, 1 = Low Speed)
- *  - max size:		bits 0-1	(00 = 8, 01 = 16, 10 = 32, 11 = 64)
- *  - pipe type:	bits 30-31	(00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
- *
- * Why? Because it's arbitrary, and whatever encoding we select is really
- * up to us. This one happens to share a lot of bit positions with the UCHI
- * specification, so that much of the uhci driver can just mask the bits
- * appropriately.
- */
-
-#define usb_maxpacket(pipe)	(8 << ((pipe) & 3))
-#define usb_packetid(pipe)	(((pipe) & 0x80) ? 0x69 : 0xE1)
-
-#define usb_pipedevice(pipe)	(((pipe) >> 8) & 0x7f)
-#define usb_pipeendpoint(pipe)	(((pipe) >> 15) & 0xf)
-#define usb_pipedata(pipe)	(((pipe) >> 19) & 1)
-#define usb_pipeout(pipe)	(((pipe) & 0x80) == 0)
-#define usb_pipeslow(pipe)	(((pipe) >> 26) & 1)
-
-#define usb_pipetype(pipe)	(((pipe) >> 30) & 3)
-#define usb_pipeisoc(pipe)	(usb_pipetype((pipe)) == 0)
-#define usb_pipeint(pipe)	(usb_pipetype((pipe)) == 1)
-#define usb_pipecontrol(pipe)	(usb_pipetype((pipe)) == 2)
-#define usb_pipebulk(pipe)	(usb_pipetype((pipe)) == 3)
-
-#define usb_pipe_endpdev(pipe)	(((pipe) >> 8) & 0x7ff)
-
-static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
-{
-	return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize;
-}
-
-static inline unsigned int __default_pipe(struct usb_device *dev)
-{
-	return (dev->slow << 26);
-}
-
-/* Create control pipes.. */
-#define usb_sndctrlpipe(dev,endpoint)	((2 << 30) | __create_pipe(dev,endpoint))
-#define usb_rcvctrlpipe(dev,endpoint)	((2 << 30) | __create_pipe(dev,endpoint) | 0x80)
-#define usb_snddefctrl(dev)		((2 << 30) | __default_pipe(dev))
-#define usb_rcvdefctrl(dev)		((2 << 30) | __default_pipe(dev) | 0x80)
-
-/* Create .. */
-
-/*
- * Send and receive control messages..
- */
-void usb_new_device(struct usb_device *dev);
-int usb_set_address(struct usb_device *dev);
-int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned
-char descindex, void *buf, int size);
-int usb_get_device_descriptor(struct usb_device *dev);
-int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size);
-int usb_clear_port_feature(struct usb_device *dev, int port, int feature);
-int usb_set_port_feature(struct usb_device *dev, int port, int feature);
-int usb_get_hub_status(struct usb_device *dev, void *data);
-int usb_get_port_status(struct usb_device *dev, int port, void *data);
-int usb_get_protocol(struct usb_device *dev);
-int usb_set_protocol(struct usb_device *dev, int protocol);
-int usb_set_idle(struct usb_device *dev, int duration, int report_id);
-int usb_set_configuration(struct usb_device *dev, int configuration);
-int usb_get_report(struct usb_device *dev);
-
-/*
- * Debugging helpers..
- */
-void usb_show_device_descriptor(struct usb_device_descriptor *);
-void usb_show_config_descriptor(struct usb_config_descriptor *);
-void usb_show_interface_descriptor(struct usb_interface_descriptor *);
-void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
-void usb_show_hub_descriptor(struct usb_hub_descriptor *);
-void usb_show_device(struct usb_device *);
-
-/*
- * Audio parsing helpers
- */
-
-#ifdef CONFIG_USB_AUDIO
-void usb_audio_interface(struct usb_interface_descriptor *, u8 *);
-void usb_audio_endpoint(struct usb_endpoint_descriptor *, u8 *);
-#else
-extern inline void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) {}
-extern inline void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) {}
-#endif
-
-#endif
-
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usbkbd.c linux.18p17/drivers/usb/usbkbd.c
--- linux.vanilla/drivers/usb/usbkbd.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usbkbd.c	Fri Sep 15 22:02:30 2000
@@ -0,0 +1,272 @@
+/*
+ * $Id: usbkbd.c,v 1.16 2000/08/14 21:05:26 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  USB HIDBP Keyboard support
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB HID Boot Protocol keyboard driver");
+
+static unsigned char usb_kbd_keycode[256] = {
+	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+	 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
+	 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+	 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+	 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
+	120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
+	115,114,  0,  0,  0,124,  0,181,182,183,184,185,186,187,188,189,
+	190,191,192,193,194,195,196,197,198,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+	150,158,159,128,136,177,178,176,142,152,173,140
+};
+
+struct usb_kbd {
+	struct input_dev dev;
+	unsigned char new[8];
+	unsigned char old[8];
+	struct urb irq, led;
+	devrequest dr;
+	unsigned char leds, newleds;
+	char name[128];
+	int open;
+};
+
+static void usb_kbd_irq(struct urb *urb)
+{
+	struct usb_kbd *kbd = urb->context;
+	int i;
+
+	if (urb->status) return;
+
+	for (i = 0; i < 8; i++)
+		input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
+
+	for (i = 2; i < 8; i++) {
+
+		if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
+			if (usb_kbd_keycode[kbd->old[i]])
+				input_report_key(&kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
+			else
+				info("Unknown key (scancode %#x) released.", kbd->old[i]);
+		}
+
+		if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
+			if (usb_kbd_keycode[kbd->new[i]])
+				input_report_key(&kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
+			else
+				info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
+		}
+	}
+
+	memcpy(kbd->old, kbd->new, 8);
+}
+
+int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (type != EV_LED) return -1;
+
+
+	kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
+		       (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
+		       (!!test_bit(LED_NUML,    dev->led));
+
+	if (kbd->led.status == -EINPROGRESS)
+		return 0;
+
+	if (kbd->leds == kbd->newleds)
+		return 0;
+
+	kbd->leds = kbd->newleds;
+	if (usb_submit_urb(&kbd->led))
+		err("usb_submit_urb(leds) failed");
+
+	return 0;
+}
+
+static void usb_kbd_led(struct urb *urb)
+{
+	struct usb_kbd *kbd = urb->context;
+
+	if (urb->status)
+		warn("led urb status %d received", urb->status);
+	
+	if (kbd->leds == kbd->newleds)
+		return;
+
+	kbd->leds = kbd->newleds;
+	if (usb_submit_urb(&kbd->led))
+		err("usb_submit_urb(leds) failed");
+}
+
+static int usb_kbd_open(struct input_dev *dev)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (kbd->open++)
+		return 0;
+
+	if (usb_submit_urb(&kbd->irq))
+		return -EIO;
+
+	return 0;
+}
+
+static void usb_kbd_close(struct input_dev *dev)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (!--kbd->open)
+		usb_unlink_urb(&kbd->irq);
+}
+
+static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_kbd *kbd;
+	int i, pipe, maxp;
+	char *buf;
+
+	if (dev->descriptor.bNumConfigurations != 1) return NULL;
+	interface = dev->config[0].interface[ifnum].altsetting + 0;
+
+	if (interface->bInterfaceClass != 3) return NULL;
+	if (interface->bInterfaceSubClass != 1) return NULL;
+	if (interface->bInterfaceProtocol != 1) return NULL;
+	if (interface->bNumEndpoints != 1) return NULL;
+
+	endpoint = interface->endpoint + 0;
+	if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
+	if ((endpoint->bmAttributes & 3) != 3) return NULL;
+
+	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	usb_set_protocol(dev, interface->bInterfaceNumber, 0);
+	usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
+
+	if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL;
+	memset(kbd, 0, sizeof(struct usb_kbd));
+
+	kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+	kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
+
+	for (i = 0; i < 255; i++)
+		set_bit(usb_kbd_keycode[i], kbd->dev.keybit);
+	clear_bit(0, kbd->dev.keybit);
+	
+	kbd->dev.private = kbd;
+	kbd->dev.event = usb_kbd_event;
+	kbd->dev.open = usb_kbd_open;
+	kbd->dev.close = usb_kbd_close;
+
+	FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp,
+		usb_kbd_irq, kbd, endpoint->bInterval);
+
+	kbd->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	kbd->dr.request = USB_REQ_SET_REPORT;
+	kbd->dr.value = 0x200;
+	kbd->dr.index = interface->bInterfaceNumber;
+	kbd->dr.length = 1;
+
+	kbd->dev.name = kbd->name;
+	kbd->dev.idbus = BUS_USB;
+	kbd->dev.idvendor = dev->descriptor.idVendor;
+	kbd->dev.idproduct = dev->descriptor.idProduct;
+	kbd->dev.idversion = dev->descriptor.bcdDevice;
+
+	if (!(buf = kmalloc(63, GFP_KERNEL))) {
+		kfree(kbd);
+		return NULL;
+	}
+
+	if (dev->descriptor.iManufacturer &&
+		usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+			strcat(kbd->name, buf);
+	if (dev->descriptor.iProduct &&
+		usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+			sprintf(kbd->name, "%s %s", kbd->name, buf);
+
+	if (!strlen(kbd->name))
+		sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x",
+			kbd->dev.idvendor, kbd->dev.idproduct);
+
+	kfree(buf);
+
+	FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0),
+		(void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd);
+			
+	input_register_device(&kbd->dev);
+
+	printk(KERN_INFO "input%d: %s on on usb%d:%d.%d\n",
+		 kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum);
+
+	return kbd;
+}
+
+static void usb_kbd_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_kbd *kbd = ptr;
+	usb_unlink_urb(&kbd->irq);
+	input_unregister_device(&kbd->dev);
+	kfree(kbd);
+}
+
+static struct usb_driver usb_kbd_driver = {
+	name:		"keyboard",
+	probe:		usb_kbd_probe,
+	disconnect:	usb_kbd_disconnect
+};
+
+static int __init usb_kbd_init(void)
+{
+	usb_register(&usb_kbd_driver);
+	return 0;
+}
+
+static void __exit usb_kbd_exit(void)
+{
+	usb_deregister(&usb_kbd_driver);
+}
+
+module_init(usb_kbd_init);
+module_exit(usb_kbd_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usbmouse.c linux.18p17/drivers/usb/usbmouse.c
--- linux.vanilla/drivers/usb/usbmouse.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/usbmouse.c	Fri Sep 15 22:02:30 2000
@@ -0,0 +1,188 @@
+/*
+ * $Id: usbmouse.c,v 1.5 2000/05/29 09:01:52 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  USB HIDBP Mouse support
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB HID Boot Protocol mouse driver");
+
+struct usb_mouse {
+	signed char data[8];
+	char name[128];
+	struct input_dev dev;
+	struct urb irq;
+	int open;
+};
+
+static void usb_mouse_irq(struct urb *urb)
+{
+	struct usb_mouse *mouse = urb->context;
+	signed char *data = mouse->data;
+	struct input_dev *dev = &mouse->dev;
+
+	if (urb->status) return;
+
+	input_report_key(dev, BTN_LEFT,   data[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);
+	input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
+	input_report_key(dev, BTN_SIDE,   data[0] & 0x08);
+	input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);
+
+	input_report_rel(dev, REL_X,     data[1]);
+	input_report_rel(dev, REL_Y,     data[2]);
+	input_report_rel(dev, REL_WHEEL, data[3]);
+}
+
+static int usb_mouse_open(struct input_dev *dev)
+{
+	struct usb_mouse *mouse = dev->private;
+
+	if (mouse->open++)
+		return 0;
+
+	if (usb_submit_urb(&mouse->irq))
+		return -EIO;
+
+	return 0;
+}
+
+static void usb_mouse_close(struct input_dev *dev)
+{
+	struct usb_mouse *mouse = dev->private;
+
+	if (!--mouse->open)
+		usb_unlink_urb(&mouse->irq);
+}
+
+static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_mouse *mouse;
+	int pipe, maxp;
+	char *buf;
+
+	if (dev->descriptor.bNumConfigurations != 1) return NULL;
+	interface = dev->config[0].interface[ifnum].altsetting + 0;
+
+	if (interface->bInterfaceClass != 3) return NULL;
+	if (interface->bInterfaceSubClass != 1) return NULL;
+	if (interface->bInterfaceProtocol != 2) return NULL;
+	if (interface->bNumEndpoints != 1) return NULL;
+
+	endpoint = interface->endpoint + 0;
+	if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
+	if ((endpoint->bmAttributes & 3) != 3) return NULL;
+
+	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
+
+	if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL;
+	memset(mouse, 0, sizeof(struct usb_mouse));
+
+	mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+	mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+	mouse->dev.relbit[0] |= BIT(REL_WHEEL);
+
+	mouse->dev.private = mouse;
+	mouse->dev.open = usb_mouse_open;
+	mouse->dev.close = usb_mouse_close;
+
+	mouse->dev.name = mouse->name;
+	mouse->dev.idbus = BUS_USB;
+	mouse->dev.idvendor = dev->descriptor.idVendor;
+	mouse->dev.idproduct = dev->descriptor.idProduct;
+	mouse->dev.idversion = dev->descriptor.bcdDevice;
+
+	if (!(buf = kmalloc(63, GFP_KERNEL))) {
+		kfree(mouse);
+		return NULL;
+	}
+
+	if (dev->descriptor.iManufacturer &&
+		usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+			strcat(mouse->name, buf);
+	if (dev->descriptor.iProduct &&
+		usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+			sprintf(mouse->name, "%s %s", mouse->name, buf);
+
+	if (!strlen(mouse->name))
+		sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x",
+			mouse->dev.idvendor, mouse->dev.idproduct);
+
+	kfree(buf);
+
+	FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp,
+		usb_mouse_irq, mouse, endpoint->bInterval);
+
+	input_register_device(&mouse->dev);
+
+	printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+		 mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum);
+
+	return mouse;
+}
+
+static void usb_mouse_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct usb_mouse *mouse = ptr;
+	usb_unlink_urb(&mouse->irq);
+	input_unregister_device(&mouse->dev);
+	kfree(mouse);
+}
+
+static struct usb_driver usb_mouse_driver = {
+	name:		"usb_mouse",
+	probe:		usb_mouse_probe,
+	disconnect:	usb_mouse_disconnect,
+};
+
+static int __init usb_mouse_init(void)
+{
+	usb_register(&usb_mouse_driver);
+	return 0;
+}
+
+static void __exit usb_mouse_exit(void)
+{
+	usb_deregister(&usb_mouse_driver);
+}
+
+module_init(usb_mouse_init);
+module_exit(usb_mouse_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/uss720.c linux.18p17/drivers/usb/uss720.c
--- linux.vanilla/drivers/usb/uss720.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/uss720.c	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,663 @@
+/*****************************************************************************/
+
+/*
+ *	uss720.c  --  USS720 USB Parport Cable.
+ *
+ *	Copyright (C) 1999
+ *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Based on parport_pc.c
+ *
+ *  History:
+ *   0.1  04.08.99  Created
+ *   0.2  07.08.99  Some fixes mainly suggested by Tim Waugh
+ *		    Interrupt handling currently disabled because
+ *		    usb_request_irq crashes somewhere within ohci.c
+ *		    for no apparent reason (that is for me, anyway)
+ *		    ECP currently untested
+ *   0.3  10.08.99  fixing merge errors
+ *   0.4  13.08.99  Added Vendor/Product ID of Brad Hard's cable
+ *   0.5  20.09.99  usb_control_msg wrapper used
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/parport.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/* --------------------------------------------------------------------- */
+
+struct parport_uss720_private {
+	struct usb_device *usbdev;
+	void *irqhandle;
+	unsigned int irqpipe;
+	unsigned char reg[7];  /* USB registers */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	static const unsigned char regindex[9] = {
+		4, 0, 1, 5, 5, 0, 2, 3, 6
+	};
+	int ret;
+
+	if (!usbdev)
+		return -1;
+	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, HZ);
+	if (ret) {
+		printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x\n",
+		       (unsigned int)reg, ret);
+	} else {
+#if 0
+		printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n",
+		       (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1],
+		       (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4],
+		       (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]);
+#endif
+		/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
+		if (priv->reg[2] & priv->reg[1] & 0x10)
+			parport_generic_irq(0, pp, NULL);
+	}
+	if (val)
+		*val = priv->reg[(reg >= 9) ? 0 : regindex[reg]];
+	return ret;
+}
+
+static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int ret;
+
+	if (!usbdev)
+		return -1;
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, HZ);
+	if (ret) {
+		printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", 
+		       (unsigned int)reg, (unsigned int)val, ret);
+	} else {
+#if 0
+		printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", 
+		       (unsigned int)reg, (unsigned int)val);
+#endif
+	}
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* ECR modes */
+#define ECR_SPP 00
+#define ECR_PS2 01
+#define ECR_PPF 02
+#define ECR_ECP 03
+#define ECR_EPP 04
+
+/* Safely change the mode bits in the ECR */
+static int change_mode(struct parport *pp, int m)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	int mode;
+
+	if (get_1284_register(pp, 6, NULL))
+		return -EIO;
+	/* Bits <7:5> contain the mode. */
+	mode = (priv->reg[2] >> 5) & 0x7;
+	if (mode == m)
+		return 0;
+	/* We have to go through mode 000 or 001 */
+	if (mode > ECR_PS2 && m > ECR_PS2)
+		if (change_mode(pp, ECR_PS2))
+			return -EIO;
+
+	if (m <= ECR_PS2 && !(priv->reg[1] & 0x20)) {
+		/* This mode resets the FIFO, so we may
+		 * have to wait for it to drain first. */
+		long expire = jiffies + pp->physport->cad->timeout;
+		switch (mode) {
+		case ECR_PPF: /* Parallel Port FIFO mode */
+		case ECR_ECP: /* ECP Parallel Port mode */
+			/* Poll slowly. */
+			for (;;) {
+				if (get_1284_register(pp, 6, NULL))
+					return -EIO;
+				if (priv->reg[2] & 0x01)
+					break;
+				if (time_after_eq (jiffies, expire))
+					/* The FIFO is stuck. */
+					return -EBUSY;
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout((HZ + 99) / 100);
+				if (signal_pending (current))
+					break;
+			}
+		}
+	}
+	/* Set the mode. */
+	if (set_1284_register(pp, 6, m << 5))
+		return -EIO;
+	return 0;
+}
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int clear_epp_timeout(struct parport *pp)
+{
+	unsigned char stat;
+
+	if (get_1284_register(pp, 1, &stat))
+		return 1;
+	return stat & 1;
+}
+
+/*
+ * Access functions.
+ */
+#if 0
+static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id)
+{
+	struct parport *pp = (struct parport *)dev_id;
+	struct parport_uss720_private *priv = pp->private_data;	
+
+	if (usbstatus != USB_ST_NOERROR || len < 4 || !buffer)
+		return 1;
+	memcpy(priv->reg, buffer, 4);
+	/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
+	if (priv->reg[2] & priv->reg[1] & 0x10)
+		parport_generic_irq(0, pp, NULL);
+	return 1;
+}
+#endif
+
+static void parport_uss720_write_data(struct parport *pp, unsigned char d)
+{
+	set_1284_register(pp, 0, d);
+}
+
+static unsigned char parport_uss720_read_data(struct parport *pp)
+{
+	unsigned char ret;
+
+	if (get_1284_register(pp, 0, &ret))
+		return 0;
+	return ret;
+}
+
+static void parport_uss720_write_control(struct parport *pp, unsigned char d)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+
+	d = (d & 0xf) | (priv->reg[1] & 0xf0);
+	if (set_1284_register(pp, 2, d))
+		return;
+	priv->reg[1] = d;
+}
+
+static unsigned char parport_uss720_read_control(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	return priv->reg[1] & 0xf; /* Use soft copy */
+}
+
+static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned char mask, unsigned char val)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	mask &= 0x0f;
+	val &= 0x0f;
+	d = (priv->reg[1] & (~mask)) ^ val;
+	if (set_1284_register(pp, 2, d))
+		return 0;
+	priv->reg[1] = d;
+	return d & 0xf;
+}
+
+static unsigned char parport_uss720_read_status(struct parport *pp)
+{
+	unsigned char ret;
+
+	if (get_1284_register(pp, 1, &ret))
+		return 0;
+	return ret & 0xf8;
+}
+
+static void parport_uss720_disable_irq(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] & ~0x10;
+	if (set_1284_register(pp, 2, d))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_enable_irq(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] | 0x10;
+	if (set_1284_register(pp, 2, d))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_data_forward (struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] & ~0x20;
+	if (set_1284_register(pp, 2, d))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_data_reverse (struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] | 0x20;
+	if (set_1284_register(pp, 2, d))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_init_state(struct pardevice *dev, struct parport_state *s)
+{
+	s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+	s->u.pc.ecr = 0x24;
+}
+
+static void parport_uss720_save_state(struct parport *pp, struct parport_state *s)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+
+	if (get_1284_register(pp, 2, NULL))
+		return;
+	s->u.pc.ctr = priv->reg[1];
+	s->u.pc.ecr = priv->reg[2];
+}
+
+static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s)
+{
+	set_1284_register(pp, 2, s->u.pc.ctr);
+	set_1284_register(pp, 6, s->u.pc.ecr);
+	get_1284_register(pp, 2, NULL);
+}
+
+static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t got = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; got < length; got++) {
+		if (get_1284_register(pp, 4, (char *)buf))
+			break;
+		((char*)buf)++;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return got;
+}
+
+static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, size_t length, int flags)
+{
+#if 0
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; written < length; written++) {
+		if (set_1284_register(pp, 4, (char *)buf))
+			break;
+		((char*)buf)++;
+		if (get_1284_register(pp, 1, NULL))
+			break;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+#else
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, HZ*20);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buf, length, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+#endif
+}
+
+static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t got = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; got < length; got++) {
+		if (get_1284_register(pp, 3, (char *)buf))
+			break;
+		((char*)buf)++;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return got;
+}
+
+static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; written < length; written++) {
+		if (set_1284_register(pp, 3, *(char *)buf))
+			break;
+		((char*)buf)++;
+		if (get_1284_register(pp, 1, NULL))
+			break;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+}
+
+static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, HZ*20);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, HZ*20);
+	if (i)
+		printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %u rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	for (; written < len; written++) {
+		if (set_1284_register(pp, 5, *(char *)buffer))
+			break;
+		((char*)buffer)++;
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+}
+
+static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_PPF))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, HZ*20);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+void parport_uss720_inc_use_count(void)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void parport_uss720_dec_use_count(void)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct parport_operations parport_uss720_ops = 
+{
+	parport_uss720_write_data,
+	parport_uss720_read_data,
+
+	parport_uss720_write_control,
+	parport_uss720_read_control,
+	parport_uss720_frob_control,
+
+	parport_uss720_read_status,
+
+	parport_uss720_enable_irq,
+	parport_uss720_disable_irq,
+
+	parport_uss720_data_forward,
+	parport_uss720_data_reverse,
+
+	parport_uss720_init_state,
+	parport_uss720_save_state,
+	parport_uss720_restore_state,
+
+	parport_uss720_inc_use_count,
+	parport_uss720_dec_use_count,
+
+	parport_uss720_epp_write_data,
+	parport_uss720_epp_read_data,
+	parport_uss720_epp_write_addr,
+	parport_uss720_epp_read_addr,
+
+	parport_uss720_ecp_write_data,
+	parport_uss720_ecp_read_data,
+	parport_uss720_ecp_write_addr,
+
+	parport_uss720_write_compat,
+	parport_ieee1284_read_nibble,
+	parport_ieee1284_read_byte,
+};
+
+/* --------------------------------------------------------------------- */
+
+static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct parport_uss720_private *priv;
+	struct parport *pp;
+	int i;
+
+	if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) &&
+	    (usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) &&
+	    (usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284))
+		return NULL;
+
+	printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n",
+	       usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
+
+	/* our known interfaces have 3 alternate settings */
+	if (usbdev->actconfig->interface[ifnum].num_altsetting != 3)
+		return NULL;
+
+	i = usb_set_interface(usbdev, ifnum, 2);
+	printk(KERN_DEBUG "uss720: set inteface result %d\n", i);
+
+	interface = &usbdev->actconfig->interface[ifnum].altsetting[2];
+
+	/*
+	 * Allocate parport interface 
+	 */
+	printk(KERN_INFO "uss720: (C) 1999 by Thomas Sailer, <sailer@ife.ee.ethz.ch>\n");
+
+	if (!(priv = kmalloc(sizeof(struct parport_uss720_private), GFP_KERNEL)))
+		return NULL;
+	if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) {
+		printk(KERN_WARNING "usb-uss720: could not register parport\n");
+		goto probe_abort;
+	}
+
+	pp->private_data = priv;
+	priv->usbdev = usbdev;
+	pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
+
+	/* set the USS720 control register to manual mode, no ECP compression, enable all ints */
+	set_1284_register(pp, 7, 0x00);
+	set_1284_register(pp, 6, 0x30);  /* PS/2 mode */
+	set_1284_register(pp, 2, 0x0c);
+	/* debugging */
+	get_1284_register(pp, 0, NULL);
+	printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n",
+	       priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]);
+
+	endpoint = &interface->endpoint[2];
+	printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->bEndpointAddress, endpoint->bInterval);
+#if 0
+	priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress);
+	i = usb_request_irq(usbdev, priv->irqpipe,
+				  uss720_irq, endpoint->bInterval,
+				  pp, &priv->irqhandle);
+	if (i) {
+		printk (KERN_WARNING "usb-uss720: usb_request_irq failed (0x%x)\n", i);
+		goto probe_abort_port;
+	}
+#endif
+	parport_proc_register(pp);
+	parport_announce_port(pp);
+
+	MOD_INC_USE_COUNT;
+	return pp;
+
+#if 0
+probe_abort_port:
+	parport_unregister_port(pp);
+#endif
+probe_abort:
+	kfree(priv);
+	return NULL;
+}
+
+static void uss720_disconnect(struct usb_device *usbdev, void *ptr)
+{
+	struct parport *pp = (struct parport *)ptr;
+	struct parport_uss720_private *priv = pp->private_data;
+
+#if 0
+	usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe);
+#endif
+	priv->usbdev = NULL;
+	parport_proc_unregister(pp);
+	parport_unregister_port(pp);
+	kfree(priv);
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver uss720_driver = {
+	"uss720",
+	uss720_probe,
+	uss720_disconnect,
+	{ NULL, NULL }
+};
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch");
+MODULE_DESCRIPTION("USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip");
+
+static int __init uss720_init(void)
+{
+	if (usb_register(&uss720_driver) < 0)
+		return -1;
+
+	printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.4 registered.\n"
+	       KERN_INFO "uss720: (C) 1999 by Thomas Sailer, <sailer@ife.ee.ethz.ch>\n");
+	return 0;
+}
+
+static void __exit uss720_cleanup(void)
+{
+	usb_deregister(&uss720_driver);
+}
+
+module_init(uss720_init);
+module_exit(uss720_cleanup);
+
+/* --------------------------------------------------------------------- */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/wacom.c linux.18p17/drivers/usb/wacom.c
--- linux.vanilla/drivers/usb/wacom.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/wacom.c	Wed Sep 27 12:08:21 2000
@@ -0,0 +1,334 @@
+/*
+ * $Id: wacom.c,v 1.9 2000/05/29 09:01:52 vojtech Exp $
+ *
+ *  Copyright (c) 2000 Vojtech Pavlik		<vojtech@suse.cz>
+ *  Copyright (c) 2000 Andreas Bach Aaen	<abach@stofanet.dk>
+ *  Copyright (c) 2000 Clifford Wolf		<clifford@clifford.at>
+ *  Copyright (c) 2000 Sam Mosel		<sam.mosel@computer.org>
+ *
+ *  USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ *  Sponsored by SuSE
+ *
+ *  ChangeLog:
+ *      v0.1 (vp)  - Initial release
+ *      v0.2 (aba) - Support for all buttons / combinations
+ *      v0.3 (vp)  - Support for Intuos added
+ *	v0.4 (sm)  - Support for more Intuos models, menustrip
+ *			relative mode, proximity.
+ *	v0.5 (vp)  - Big cleanup, nifty features removed,
+ * 			they belong in userspace
+ *	v1.8 (vp)  - Submit URB only when operating, moved to CVS,
+ *			use input_report_key instead of report_btn and
+ *			other cleanups
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("USB Wacom Graphire and Wacom Intuos tablet driver");
+
+/*
+ * Wacom Graphire packet:
+ *
+ * byte 0: report ID (2)
+ * byte 1: bit7		pointer in range
+ *	   bit5-6	pointer type 0 - pen, 1 - rubber, 2 - mouse
+ *	   bit4		1 ?
+ *	   bit3		0 ?
+ *	   bit2		mouse middle button / pen button2
+ *	   bit1		mouse right button / pen button1
+ *	   bit0		mouse left button / touch
+ * byte 2: X low bits
+ * byte 3: X high bits
+ * byte 4: Y low bits
+ * byte 5: Y high bits
+ * byte 6: pen pressure low bits / mouse wheel
+ * byte 7: pen presure high bits / mouse distance
+ * 
+ * There are also two single-byte feature reports (2 and 3).
+ *
+ * Wacom Intuos status packet:
+ *
+ * byte 0: report ID (2)
+ * byte 1: bit7		1 - sync bit 
+ *	   bit6		pointer in range
+ *	   bit5		pointer type report
+ *	   bit4		0 ?
+ *	   bit3		0 ?
+ *	   bit2		pen button2
+ *	   bit1		pen button1
+ *	   bit0		0 ?
+ * byte 2: X high bits
+ * byte 3: X low bits
+ * byte 4: Y high bits
+ * byte 5: Y low bits
+ * byte 6: bits 0-7: pressure	(bits 2-9)
+ * byte 7: bits 6-7: pressure	(bits 0-1)
+ * byte 7: bits 0-5: X tilt	(bits 1-6)
+ * byte 8: bit    7: X tilt	(bit  0)
+ * byte 8: bits 0-6: Y tilt	(bits 0-6)
+ * byte 9: bits 4-7: distance
+ */
+
+#define USB_VENDOR_ID_WACOM	0x056a
+
+struct wacom_features {
+	char *name;
+	int idProduct;
+	int pktlen;
+	int x_max;
+	int y_max;
+	int pressure_max;
+	int distance_max;
+	void (*irq)(struct urb *urb);
+	unsigned long evbit;
+	unsigned long absbit;
+	unsigned long relbit;
+	unsigned long btnbit;
+	unsigned long digibit;
+};
+
+struct wacom {
+	signed char data[10];
+	struct input_dev dev;
+	struct urb irq;
+	struct wacom_features *features;
+	int tool;
+	int open;
+};
+
+static void wacom_graphire_irq(struct urb *urb)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+
+	if (urb->status) return;
+
+	if (data[0] != 2)
+		dbg("received unknown report #%d", data[0]);
+
+	if ( data[1] & 0x80 ) {
+		input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
+		input_report_abs(dev, ABS_Y, data[4] | ((__u32)data[5] << 8));
+	}
+
+	switch ((data[1] >> 5) & 3) {
+
+		case 0:	/* Pen */
+			input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80);
+			break;
+
+		case 1: /* Rubber */
+			input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+			break;
+
+		case 2: /* Mouse */
+			input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24);
+			input_report_key(dev, BTN_LEFT, data[1] & 0x01);
+			input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
+			input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
+			input_report_abs(dev, ABS_DISTANCE, data[7]);
+			input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+			return;
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+
+	input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
+	input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
+	input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
+}
+
+static void wacom_intuos_irq(struct urb *urb)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+
+	if (urb->status) return;
+
+	if (data[0] != 2)
+		dbg("received unknown report #%d", data[0]);
+
+	if (((data[1] >> 5) & 0x3) == 0x2) {				/* Enter report */
+
+		switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
+			case 0x012: wacom->tool = BTN_TOOL_PENCIL;	break;	/* Inking pen */
+			case 0x822:
+			case 0x022: wacom->tool = BTN_TOOL_PEN;		break;	/* Pen */
+			case 0x032: wacom->tool = BTN_TOOL_BRUSH;	break;	/* Stroke pen */
+			case 0x094: wacom->tool = BTN_TOOL_MOUSE;	break;	/* Mouse 4D */
+			case 0x096: wacom->tool = BTN_TOOL_LENS;	break;	/* Lens cursor */
+			case 0x82a:
+			case 0x0fa: wacom->tool = BTN_TOOL_RUBBER;	break;	/* Eraser */
+			case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH;	break;	/* Airbrush */
+			default:    wacom->tool = BTN_TOOL_PEN;		break;	/* Unknown tool */
+		}			
+		input_report_key(dev, wacom->tool, 1);
+		return;
+	}
+
+	if ((data[1] | data[2] | data[3] | data[4] | data[5] |
+	     data[6] | data[7] | data[8] | data[9]) == 0x80) {		/* Exit report */
+		input_report_key(dev, wacom->tool, 0);
+		return;
+	}
+
+	input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]);
+	input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]);
+	input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+	input_report_abs(dev, ABS_DISTANCE, data[9] >> 4);
+	input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+	input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+
+	input_report_key(dev, BTN_STYLUS, data[1] & 2);
+	input_report_key(dev, BTN_STYLUS2, data[1] & 4);
+	input_report_key(dev, BTN_TOUCH, t > 10);
+}
+
+#define WACOM_INTUOS_TOOLS	(BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS))
+
+struct wacom_features wacom_features[] = {
+	{ "Wacom Graphire",     0x10,  8, 10206,  7422,  511, 32, wacom_graphire_irq,
+		BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 },
+	{ "Wacom Intuos 4x5",   0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Wacom Intuos 6x8",   0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Wacom Intuos 9x12",  0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Wacom Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ "Wacom Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq,
+		0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+	{ NULL , 0 }
+};
+
+static int wacom_open(struct input_dev *dev)
+{
+	struct wacom *wacom = dev->private;
+
+	if (wacom->open++)
+		return 0;
+
+	if (usb_submit_urb(&wacom->irq))
+		return -EIO;
+
+	return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+	struct wacom *wacom = dev->private;
+
+	if (!--wacom->open)
+		usb_unlink_urb(&wacom->irq);
+}
+
+static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_endpoint_descriptor *endpoint;
+	struct wacom *wacom;
+	int i;
+
+	if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM) return NULL;
+	for (i = 0; wacom_features[i].idProduct && wacom_features[i].idProduct != dev->descriptor.idProduct; i++);
+	if (!wacom_features[i].idProduct) return NULL;
+
+	endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+
+	if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
+	memset(wacom, 0, sizeof(struct wacom));
+
+	wacom->features = wacom_features + i;
+
+	wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | wacom->features->evbit;
+	wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE) | wacom->features->absbit;
+	wacom->dev.relbit[0] |= wacom->features->relbit;
+	wacom->dev.keybit[LONG(BTN_LEFT)] |= wacom->features->btnbit;
+	wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) |
+		BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | wacom->features->digibit;
+
+	wacom->dev.absmax[ABS_X] = wacom->features->x_max;
+	wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
+	wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
+	wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
+	wacom->dev.absmax[ABS_TILT_X] = 127;
+	wacom->dev.absmax[ABS_TILT_Y] = 127;
+
+	wacom->dev.private = wacom;
+	wacom->dev.open = wacom_open;
+	wacom->dev.close = wacom_close;
+
+	wacom->dev.name = wacom->features->name;
+	wacom->dev.idbus = BUS_USB;
+	wacom->dev.idvendor = dev->descriptor.idVendor;
+	wacom->dev.idproduct = dev->descriptor.idProduct;
+	wacom->dev.idversion = dev->descriptor.bcdDevice;
+
+	FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+		     wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval);
+
+	input_register_device(&wacom->dev);
+
+	printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+		 wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum);
+
+	return wacom;
+}
+
+static void wacom_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct wacom *wacom = ptr;
+	usb_unlink_urb(&wacom->irq);
+	input_unregister_device(&wacom->dev);
+	kfree(wacom);
+}
+
+static struct usb_driver wacom_driver = {
+	name:		"wacom",
+	probe:		wacom_probe,
+	disconnect:	wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+	usb_register(&wacom_driver);
+	return 0;
+}
+
+static void __exit wacom_exit(void)
+{
+	usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/wmforce.c linux.18p17/drivers/usb/wmforce.c
--- linux.vanilla/drivers/usb/wmforce.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/drivers/usb/wmforce.c	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,190 @@
+/*
+ * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $
+ *
+ *  Copyright (c) 2000 Vojtech Pavlik
+ *
+ *  USB Logitech WingMan Force joystick support
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+
+#define USB_VENDOR_ID_LOGITECH		0x046d
+#define USB_DEVICE_ID_LOGITECH_WMFORCE	0xc281
+
+struct wmforce {
+	signed char data[8];
+	struct input_dev dev;
+	struct urb irq;
+	int open;
+};
+
+static struct {
+        __s32 x;
+        __s32 y;
+} wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+static char *wmforce_name = "Logitech WingMan Force";
+
+static void wmforce_irq(struct urb *urb)
+{
+	struct wmforce *wmforce = urb->context;
+	unsigned char *data = wmforce->data;
+	struct input_dev *dev = &wmforce->dev;
+
+	if (urb->status) return;
+
+	if (data[0] != 1) {
+		if (data[0] != 2)
+			warn("received unknown report #%d", data[0]);
+		return;
+	}
+
+	input_report_abs(dev, ABS_X, (__s16) (((__s16)data[2] << 8) | data[1]));
+	input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[4] << 8) | data[3]));
+	input_report_abs(dev, ABS_THROTTLE, data[5]);
+	input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x);
+	input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y);
+
+	input_report_key(dev, BTN_TRIGGER, data[6] & 0x01);
+	input_report_key(dev, BTN_TOP,     data[6] & 0x02);
+	input_report_key(dev, BTN_THUMB,   data[6] & 0x04);
+	input_report_key(dev, BTN_TOP2,    data[6] & 0x08);
+	input_report_key(dev, BTN_BASE,    data[6] & 0x10);
+	input_report_key(dev, BTN_BASE2,   data[6] & 0x20);
+	input_report_key(dev, BTN_BASE3,   data[6] & 0x40);
+	input_report_key(dev, BTN_BASE4,   data[6] & 0x80);
+	input_report_key(dev, BTN_BASE5,   data[7] & 0x01);
+}
+
+static int wmforce_open(struct input_dev *dev)
+{
+	struct wmforce *wmforce = dev->private;
+
+	if (wmforce->open++)
+		return 0;
+
+	if (usb_submit_urb(&wmforce->irq))
+		return -EIO;
+
+	return 0;
+}
+
+static void wmforce_close(struct input_dev *dev)
+{
+	struct wmforce *wmforce = dev->private;
+
+	if (!--wmforce->open)
+		usb_unlink_urb(&wmforce->irq);
+}
+
+static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_endpoint_descriptor *endpoint;
+	struct wmforce *wmforce;
+	int i;
+
+	if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH ||
+	    dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE)
+		return NULL;
+
+	endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+
+	if (!(wmforce = kmalloc(sizeof(struct wmforce), GFP_KERNEL))) return NULL;
+	memset(wmforce, 0, sizeof(struct wmforce));
+
+	wmforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	wmforce->dev.keybit[LONG(BTN_JOYSTICK)] = BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) |
+					BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5);
+	wmforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y);
+
+	for (i = ABS_X; i <= ABS_Y; i++) {
+		wmforce->dev.absmax[i] =  1920;
+		wmforce->dev.absmin[i] = -1920;
+		wmforce->dev.absflat[i] = 128;
+	}
+
+	wmforce->dev.absmax[ABS_THROTTLE] = 255;
+	wmforce->dev.absmin[ABS_THROTTLE] = 0;
+
+	for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) {
+		wmforce->dev.absmax[i] =  1;
+		wmforce->dev.absmin[i] = -1;
+	}
+
+	wmforce->dev.private = wmforce;
+	wmforce->dev.open = wmforce_open;
+	wmforce->dev.close = wmforce_close;
+
+	wmforce->dev.name = wmforce_name;
+	wmforce->dev.idbus = BUS_USB;
+	wmforce->dev.idvendor = dev->descriptor.idVendor;
+	wmforce->dev.idproduct = dev->descriptor.idProduct;
+	wmforce->dev.idversion = dev->descriptor.bcdDevice;
+
+	FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval);
+
+	input_register_device(&wmforce->dev);
+
+	printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+		 wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum);
+
+	return wmforce;
+}
+
+static void wmforce_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct wmforce *wmforce = ptr;
+	usb_unlink_urb(&wmforce->irq);
+	input_unregister_device(&wmforce->dev);
+	kfree(wmforce);
+}
+
+static struct usb_driver wmforce_driver = {
+	name:		"wmforce",
+	probe:		wmforce_probe,
+	disconnect:	wmforce_disconnect,
+};
+
+static int __init wmforce_init(void)
+{
+	usb_register(&wmforce_driver);
+	return 0;
+}
+
+static void __exit wmforce_exit(void)
+{
+	usb_deregister(&wmforce_driver);
+}
+
+module_init(wmforce_init);
+module_exit(wmforce_exit);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Config.in linux.18p17/drivers/video/Config.in
--- linux.vanilla/drivers/video/Config.in	Sat Sep  9 18:42:46 2000
+++ linux.18p17/drivers/video/Config.in	Sat Oct 14 00:03:45 2000
@@ -6,7 +6,7 @@
   define_bool CONFIG_DUMMY_CONSOLE y
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_PCI" = "y" ]; then
-      bool 'Permedia2 support (experimental)' CONFIG_FB_PM2
+      tristate 'Permedia2 support (experimental)' CONFIG_FB_PM2
       if [ "$CONFIG_FB_PM2" = "y" ]; then
         if [ "$CONFIG_PCI" = "y" ]; then
           bool '  enable FIFO disconnect feature' CONFIG_FB_PM2_FIFO_DISCONNECT
@@ -51,7 +51,7 @@
     bool 'Atari native chipset support' CONFIG_FB_ATARI
   fi
   if [ "$CONFIG_ATARI" = "y" -o "$CONFIG_PCI" != "n" ]; then
-    bool 'ATI Mach64 display support' CONFIG_FB_ATY
+    tristate 'ATI Mach64 display support' CONFIG_FB_ATY
   fi
   if [ "$CONFIG_PPC" = "y" ]; then
     bool 'Open Firmware frame buffer device support' CONFIG_FB_OF
@@ -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
@@ -84,7 +86,7 @@
     define_bool CONFIG_BUS_I2C y
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    if [ "$CONFIG_PCI" != "n" ]; then
+    if [ "$CONFIG_PCI" != "n" -a "$ARCH" != "sparc" -a "$ARCH" != "sparc64" ]; then
       tristate 'Matrox acceleration' CONFIG_FB_MATROX
       if  [ "$CONFIG_FB_MATROX" != "n" ]; then
         bool '  Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM
@@ -92,7 +94,7 @@
         bool '  G100/G200 support' CONFIG_FB_MATROX_G100
         bool '  Multihead support' CONFIG_FB_MATROX_MULTIHEAD
       fi
-      bool 'ATI Rage128 display support' CONFIG_FB_ATY128
+      tristate 'ATI Rage128 display support' CONFIG_FB_ATY128
     fi
   fi
   if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Makefile linux.18p17/drivers/video/Makefile
--- linux.vanilla/drivers/video/Makefile	Sat Sep  9 18:42:46 2000
+++ linux.18p17/drivers/video/Makefile	Sat Oct 14 00:03:45 2000
@@ -137,10 +137,10 @@
 endif
 
 ifeq ($(CONFIG_FB_CYBER2000),y)
-L_OBJS += cyber2000fb.o
+LX_OBJS += cyber2000fb.o
 else
   ifeq ($(CONFIG_FB_CYBER2000),m)
-  M_OBJS += cyber2000fb.o
+  MX_OBJS += cyber2000fb.o
   endif
 endif
 
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/acornfb.c linux.18p17/drivers/video/acornfb.c
--- linux.vanilla/drivers/video/acornfb.c	Fri Apr 21 12:46:37 2000
+++ linux.18p17/drivers/video/acornfb.c	Fri Sep 15 23:31:12 2000
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/video/acorn.c
+ * linux/drivers/video/acornfb.c
  *
- * Copyright (C) 1998,1999 Russell King
+ * Copyright (C) 1998-2000 Russell King
  *
  * Frame buffer code for Acorn platforms
  *
@@ -34,6 +34,7 @@
 #include <video/fbcon-cfb4.h>
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
 
 /*
  * Default resolution.
@@ -42,6 +43,25 @@
  */
 #define DEFAULT_XRES	640
 #define DEFAULT_YRES	480
+/*
+ * The order here defines which BPP we
+ * pick depending on which resolutions
+ * we have configured.
+ */
+#if   defined(FBCON_HAS_CFB4)
+# define DEFAULT_BPP	4
+#elif defined(FBCON_HAS_CFB8)
+# define DEFAULT_BPP	8
+#elif defined(FBCON_HAS_CFB16)
+# define DEFAULT_BPP	16
+#elif defined(FBCON_HAS_CFB2)
+# define DEFAULT_BPP	2
+#elif defined(FBCON_HAS_MFB)
+# define DEFAULT_BPP	1
+#else
+#error No suitable framebuffers configured
+#endif
+
 
 /*
  * define this to debug the video mode selection
@@ -94,7 +114,6 @@
 	unsigned int	palette_size;
 	  signed int	montype;
 	  signed int	currcon;
-	unsigned int	allow_modeset	: 1;
 	unsigned int	using_vram	: 1;
 	unsigned int	dpms		: 1;
 
@@ -106,14 +125,37 @@
 	} cmap;
 };
 
+struct vidc_timing {
+	u_int	h_cycle;
+	u_int	h_sync_width;
+	u_int	h_border_start;
+	u_int	h_display_start;
+	u_int	h_display_end;
+	u_int	h_border_end;
+	u_int	h_interlace;
+
+	u_int	v_cycle;
+	u_int	v_sync_width;
+	u_int	v_border_start;
+	u_int	v_display_start;
+	u_int	v_display_end;
+	u_int	v_border_end;
+
+	u_int	control;
+
+	/* VIDC20 only */
+	u_int	pll_ctl;
+};
+
 /*
  * Translation from RISC OS monitor types to actual
  * HSYNC and VSYNC frequency ranges.  These are
- * probably not right...
+ * probably not right, but they're the best info I
+ * have.  Allow 1% either way on the nominal for TVs.
  */
 #define NR_MONTYPES	6
 static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
-	{ 15625, 15625, 50, 50, 0 },	/* TV		*/
+	{ 15469, 15781, 49, 51, 0 },	/* TV		*/
 	{     0, 99999,  0, 99, 0 },	/* Multi Freq	*/
 	{ 58608, 58608, 64, 64, 0 },	/* Hi-res mono	*/
 	{ 30000, 70000, 60, 60, 0 },	/* VGA		*/
@@ -124,34 +166,12 @@
 static struct display global_disp;
 static struct fb_info fb_info;
 static struct acornfb_par current_par;
+static struct vidc_timing current_vidc;
 static struct fb_var_screeninfo __initdata init_var = {};
 
 extern int acornfb_depth;	/* set by setup.c */
 extern unsigned int vram_size;	/* set by setup.c */
 
-
-static struct vidc_timing {
-	u_int	h_cycle;
-	u_int	h_sync_width;
-	u_int	h_border_start;
-	u_int	h_display_start;
-	u_int	h_display_end;
-	u_int	h_border_end;
-	u_int	h_interlace;
-
-	u_int	v_cycle;
-	u_int	v_sync_width;
-	u_int	v_border_start;
-	u_int	v_display_start;
-	u_int	v_display_end;
-	u_int	v_border_end;
-
-	u_int	control;
-
-	/* VIDC20 only */
-	u_int	pll_ctl;
-} current_vidc;
-
 #ifdef HAS_VIDC
 
 #define VID_CTL_VS_NVSYNC	(1 << 3)
@@ -254,7 +274,9 @@
 
 	bandwidth = var->pixclock * 8 / var->bits_per_pixel;
 	/* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */
-	if (bandwidth > 71750)
+	if (bandwidth > 143500)
+		vidc_ctl |= VIDC_CTRL_FIFO_3_7;
+	else if (bandwidth > 71750)
 		vidc_ctl |= VIDC_CTRL_FIFO_2_6;
 	else if (bandwidth > 35875)
 		vidc_ctl |= VIDC_CTRL_FIFO_1_5;
@@ -648,15 +670,19 @@
 
 	ext_ctl = VIDC20_ECTL_DAC | VIDC20_ECTL_REG(3);
 
-	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
-		ext_ctl |= VIDC20_ECTL_HS_HSYNC;
-	else
-		ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
+	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
+		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
+	else {
+		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+			ext_ctl |= VIDC20_ECTL_HS_HSYNC;
+		else
+			ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
 
-	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
-		ext_ctl |= VIDC20_ECTL_VS_VSYNC;
-	else
-		ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
+		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+			ext_ctl |= VIDC20_ECTL_VS_VSYNC;
+		else
+			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
+	}
 
 	outl(VIDC20_ECTL | ext_ctl, IO_VIDC_BASE);
 
@@ -740,8 +766,8 @@
  * Before selecting the timing parameters, adjust
  * the resolution to fit the rules.
  */
-static void
-acornfb_pre_adjust_timing(struct fb_var_screeninfo *var, int con)
+static int
+acornfb_adjust_timing(struct fb_var_screeninfo *var, int con)
 {
 	u_int font_line_len;
 	u_int fontht;
@@ -783,6 +809,13 @@
 	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
 	min_size = var->xres * var->yres * var->bits_per_pixel / 8;
 
+	/*
+	 * If minimum screen size is greater than that we have
+	 * available, reject it.
+	 */
+	if (min_size > current_par.screen_size)
+		return -EINVAL;
+
 	/* Find int 'y', such that y * fll == s * sam < maxsize
 	 * y = s * sam / fll; s = maxsize / sam
 	 */
@@ -794,14 +827,16 @@
 			break;
 	}
 
-	if (min_size > size) {
-		/*
-		 * failed, use ypan
-		 */
-		size = current_par.screen_size;
-		var->yres_virtual = size / (font_line_len / fontht);
-	} else
-		var->yres_virtual = nr_y * fontht;
+	if (var->accel_flags & FB_ACCELF_TEXT) {
+		if (min_size > size) {
+			/*
+			 * failed, use ypan
+			 */
+			size = current_par.screen_size;
+			var->yres_virtual = size / (font_line_len / fontht);
+		} else
+			var->yres_virtual = nr_y * fontht;
+	}
 
 	current_par.screen_end = current_par.screen_base_p + size;
 
@@ -818,16 +853,7 @@
 		if (var->yoffset + var->yres > var->yres_virtual)
 			var->yoffset = var->yres_virtual - var->yres;
 	}
-}
 
-/*
- * After selecting the timing parameters, adjust
- * the timing to suit the chip.
- * NOTE! Only minor adjustments should be made here.
- */
-static void
-acornfb_post_adjust_timing(struct fb_var_screeninfo *var)
-{
 	/* hsync_len must be even */
 	var->hsync_len = (var->hsync_len + 1) & ~1;
 
@@ -854,6 +880,31 @@
 
 	if (var->vsync_len < 1)
 		var->vsync_len = 1;
+
+	return 0;
+}
+
+static int
+acornfb_validate_timing(struct fb_var_screeninfo *var,
+			struct fb_monspecs *monspecs)
+{
+	unsigned long hs, vs;
+
+	/*
+	 * hs(Hz) = 10^12 / (pixclock * xtotal)
+	 * vs(Hz) = hs(Hz) / ytotal
+	 *
+	 * No need to do long long divisions or anything
+	 * like that if you factor it correctly
+	 */
+	hs = 1953125000 / var->pixclock;
+	hs = hs * 512 /
+	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);
+	vs = hs /
+	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
+
+	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
+		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
 }
 
 static inline void
@@ -895,32 +946,75 @@
 	return 0;
 }
 
+/*
+ * We have to take note of the VIDC20's 16-bit palette here.
+ * The VIDC20 looks up a 16 bit pixel as follows:
+ *
+ *   bits   111111
+ *          5432109876543210
+ *   red            ++++++++  (8 bits,  7 to 0)
+ *  green       ++++++++      (8 bits, 11 to 4)
+ *   blue   ++++++++          (8 bits, 15 to 8)
+ *
+ * We use a pixel which looks like:
+ *
+ *   bits   111111
+ *          5432109876543210
+ *   red               +++++  (5 bits,  4 to  0)
+ *  green         +++++       (5 bits,  9 to  5)
+ *   blue    +++++            (5 bits, 14 to 10)
+ */
 static int
 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 		  u_int trans, struct fb_info *info)
 {
 	union palette pal;
+	int bpp = fb_display[current_par.currcon].var.bits_per_pixel;
 
 	if (regno >= current_par.palette_size)
 		return 1;
 
 	pal = acornfb_palette_encode(regno, red, green, blue, trans);
-	acornfb_palette_write(regno, pal);
 	current_par.palette[regno] = pal;
 
+#ifdef HAS_VIDC20
 	if (regno < 16) {
-		switch (info->disp->var.bits_per_pixel) {
+		switch (bpp) {
 #ifdef FBCON_HAS_CFB16
-		case 16:	/* RGB555 */
-			current_par.cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno;
+		case 16:
+			current_par.cmap.cfb16[regno] =
+				regno | regno << 5 | regno << 10;
+			break;
+#endif
+#ifdef FBCON_HAS_CFB32
+		case 32:
+			current_par.cmap.cfb32[regno] =
+				regno | regno << 8 | regno << 16;
 			break;
 #endif
-
 		default:
 			break;
 		}
 	}
 
+#ifdef FBCON_HAS_CFB16
+	if (bpp == 16) {
+		int i;
+
+		pal.p = 0;
+		outl(0x10000000, IO_VIDC_BASE);
+		for (i = 0; i < 256; i += 1) {
+			pal.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
+			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
+			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
+			outl(pal.p, IO_VIDC_BASE);
+			/* Palette register pointer auto-increments */
+		}
+	} else
+#endif
+#endif
+		acornfb_palette_write(regno, pal);
+
 	return 0;
 }
 
@@ -963,6 +1057,8 @@
 static int
 acornfb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
 {
+	int err;
+
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_MFB
 	case 1:
@@ -988,10 +1084,18 @@
 		*visual = FB_VISUAL_PSEUDOCOLOR;
 		break;
 #endif
+#ifdef HAS_VIDC20
+#ifdef FBCON_HAS_CFB16
 	case 16:
-	case 24:
+		*visual = FB_VISUAL_DIRECTCOLOR;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
 	case 32:
 		*visual = FB_VISUAL_TRUECOLOR;
+		break;
+#endif
+#endif
 	default:
 		return -EINVAL;
 	}
@@ -1000,25 +1104,60 @@
 		return -EINVAL;
 
 	/*
-	 * Adjust the resolution before using it.
+	 * Validate and adjust the resolution to
+	 * match the video generator hardware.
+	 */
+	err = acornfb_adjust_timing(var, con);
+	if (err)
+		return err;
+
+	/*
+	 * Validate the timing against the
+	 * monitor hardware.
 	 */
-	acornfb_pre_adjust_timing(var, con);
+	err = acornfb_validate_timing(var, &fb_info.monspecs);
+	if (err)
+		return err;
 
 #if defined(HAS_VIDC20)
-	var->red.length	   = 8;
-	var->transp.length = 4;
+	switch (var->bits_per_pixel) {
+	case 1: case 2: case 4: case 8:
+		var->red.offset    = 0;
+		var->red.length    = 8;
+		var->green         = var->red;
+		var->blue          = var->red;
+		var->transp.offset = 0;
+		var->transp.length = 4;
+		break;
+
+	case 16:
+		var->red.offset    = 0;
+		var->red.length    = 5;
+		var->green.offset  = 5;
+		var->green.length  = 5;
+		var->blue.offset   = 10;
+		var->blue.length   = 5;
+		var->transp.offset = 15;
+		var->transp.length = 1;
+		break;
+
+	case 32:
+		var->red.offset    = 0;
+		var->red.length    = 8;
+		var->green.offset  = 8;
+		var->green.length  = 8;
+		var->blue.offset   = 16;
+		var->blue.length   = 8;
+		var->transp.offset = 24;
+		var->transp.length = 4;
+		break;
+	}
 #elif defined(HAS_VIDC)
 	var->red.length	   = 4;
+	var->green         = var->red;
+	var->blue          = var->red;
 	var->transp.length = 1;
 #endif
-	var->green = var->red;
-	var->blue  = var->red;
-
-	/*
-	 * Now adjust the timing parameters
-	 */
-	acornfb_post_adjust_timing(var);
-
 	return 0;
 }
 
@@ -1071,9 +1210,6 @@
 	else
 		display = &global_disp;
 
-	if (!current_par.allow_modeset && con != -1)
-		return -EINVAL;
-
 	err = acornfb_decode_var(var, con, &visual);
 	if (err)
 		return err;
@@ -1151,11 +1287,18 @@
 #endif
 #ifdef FBCON_HAS_CFB16
 	case 16:
-		current_par.palette_size = VIDC_PALETTE_SIZE;
+		current_par.palette_size = 32;
 		display->dispsw = &fbcon_cfb16;
 		display->dispsw_data = current_par.cmap.cfb16;
 		break;
 #endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		current_par.palette_size = VIDC_PALETTE_SIZE;
+		display->dispsw = &fbcon_cfb32;
+		display->dispsw_data = current_par.cmap.cfb32;
+		break;
+#endif
 	default:
 		display->dispsw = &fbcon_dummy;
 		break;
@@ -1194,9 +1337,7 @@
 		outl(control, IOMD_VIDCR);
 #endif
 		acornfb_update_dma(var);
-
-		if (current_par.allow_modeset)
-			acornfb_set_timing(var);
+		acornfb_set_timing(var);
 
 		if (display->cmap.len)
 			cmap = &display->cmap;
@@ -1288,236 +1429,154 @@
 static void
 acornfb_blank(int blank, struct fb_info *info)
 {
-	int i;
+	union palette p;
+	int i, bpp = fb_display[current_par.currcon].var.bits_per_pixel;
 
-	if (blank)
+	if (bpp != 16) {
 		for (i = 0; i < current_par.palette_size; i++) {
-			union palette p;
-
-			p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			if (blank)
+				p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			else
+				p = current_par.palette[i];
 
 			acornfb_palette_write(i, p);
 		}
-	else
-		for (i = 0; i < current_par.palette_size; i++)
+	}
+#ifdef FBCON_HAS_CFB16
+	else {
+		p.p = 0;
+
+		for (i = 0; i < 256; i++) {
+			if (blank)
+				p = acornfb_palette_encode(i, 0, 0, 0, 0);
+			else {
+				p.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
+				p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
+				p.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
+			}
 			acornfb_palette_write(i, current_par.palette[i]);
+		}
+	}
+#endif
 }
 
 /*
  * Everything after here is initialisation!!!
  */
-struct modey_params {
-	u_int	y_res;
-	u_int	u_margin;
-	u_int	b_margin;
-	u_int	vsync_len;
-	u_int	vf;
-};
-
-struct modex_params {
-	u_int	x_res;
-	u_int	l_margin;
-	u_int	r_margin;
-	u_int	hsync_len;
-	u_int	clock;
-	u_int	hf;
-	const struct modey_params *modey;
-};
-
-static const struct modey_params modey_640_15600[] __initdata = {
-	{  250,  38,  21,  3,  50 },	/*  640x 250, 50Hz */
-	{  256,  35,  18,  3,  50 },	/*  640x 256, 50Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_640_26800[] __initdata = {
-	{  512,  18,   1,  3,  50 },	/*  640x 512, 50Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_640_31500[] __initdata = {
-	{  250, 109,  88,  2,  70 },	/*  640x 250, 70Hz */
-	{  256, 106,  85,  2,  70 },	/*  640x 256, 70Hz */
-	{  352,  58,  37,  2,  70 },	/*  640x 352, 70Hz */
-	{  480,  32,  11,  2,  60 },	/*  640x 480, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_800_35200[] __initdata = {
-	{  600,  22,   1,  2,  56 },	/*  800x 600, 56Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_896_21800[] __initdata = {
-	{  352,   9,   0,  3,  60 },	/*  896x 352, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-/* everything after here is not supported */
-static const struct modey_params modey_1024_uk[] __initdata = {
-	{  768,   0,   0,  0,   0 },	/* 1024x 768 */
-	{    0,   0,   0,  0,   0 }
-};
+#define fb_videomode fb_2_3_videomode
 
-static const struct modey_params modey_1056_uk[] __initdata = {
-	{  250,   0,   0,  0,   0 },	/* 1056x 250 */
-	{  256,   0,   0,  0,   0 },	/* 1056x 256 */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1152_uk[] __initdata = {
-	{  896,   0,   0,  0,   0 },	/* 1152x 896 */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1280_63600[] __initdata = {
-	{ 1024,   0,   0,  0,  60 },	/* 1280x1024, 60Hz */
-	{    0,   0,   0,  0,   0 }
-};
-
-static const struct modey_params modey_1600_uk[] __initdata = {
-	{ 1280,   0,   0,  0,   0 },	/* 1600x1280 */
-	{    0,   0,   0,  0,   0 }
-};
-
-/*
- * Horizontal video programming requirements.
- * This table is searched for the required horizontal
- * and required frequency, and then the tables above
- * are then searched for the required vertical
- * resolution.
- *
- * NOTE! we can match multiple entries, so we search
- * all horizontal entries for which the hfreq is within
- * the monitor's range.
- */
-static const struct modex_params modex_params[] __initdata = {
-	{						/* X:  640, 15.6kHz */
-		 640,  185, 123,  76, 16000, 15625, modey_640_15600
-	},
-	{						/* X:  640, 26.8kHz */
-		 640,  113,  87,  56, 24000, 26800, modey_640_26800
-	},
-	{						/* X:  640, 31.5kHz */
-		 640,   48,  16,  96, 25175, 31500, modey_640_31500
-	},
-	{						/* X:  800, 35.2kHz */
-		 800,  101,  23, 100, 36000, 35200, modey_800_35200
-	},
-	{						/* X:  896, 21.8kHz */
-		 896,   59,  27, 118, 24000, 21800, modey_896_21800
-	},
-	{						/* X: 1024 */
-		1024,    0,   0,   0,     0,     0, modey_1024_uk
-	},
-	{						/* X: 1056 */
-		1056,    0,   0,   0,     0,     0, modey_1056_uk
-	},
-	{						/* X: 1152 */
-		1152,    0,   0,   0,     0,     0, modey_1152_uk
-	},
-	{						/* X: 1280, 63.6kHz */
-		1280,    0,   0,   0,     0, 63600, modey_1280_63600
-	},
-	{						/* X: 1600 */
-		1600,    0,   0,   0,     0,     0, modey_1600_uk
-	},
-	{
+struct fb_videomode {
+    const char *name;	/* optional */
+    u32 refresh;	/* optional */
+    u32 xres;
+    u32 yres;
+    u32 pixclock;
+    u32 left_margin;
+    u32 right_margin;
+    u32 upper_margin;
+    u32 lower_margin;
+    u32 hsync_len;
+    u32 vsync_len;
+    u32 sync;
+    u32 vmode;
+};
+
+static struct fb_videomode modedb[] __initdata = {
+	{	/* 320x256 @ 50Hz */
+		NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
+		FB_SYNC_COMP_HIGH_ACT,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x250 @ 50Hz, 15.6 kHz hsync */
+		NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x256 @ 50Hz, 15.6 kHz hsync */
+		NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x512 @ 50Hz, 26.8 kHz hsync */
+		NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x250 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x256 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x352 @ 70Hz, 31.5 kHz hsync */
+		NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
 		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 640x480 @ 60Hz, 31.5 kHz hsync */
+		NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 800x600 @ 56Hz, 35.2 kHz hsync */
+		NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 896x352 @ 60Hz, 21.8 kHz hsync */
+		NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 1024x 768 @ 60Hz, 48.4 kHz hsync */
+		NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
+		0,
+		FB_VMODE_NONINTERLACED
+	}, {	/* 1280x1024 @ 60Hz, 63.8 kHz hsync */
+		NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
+		0,
+		FB_VMODE_NONINTERLACED
 	}
 };
 
-__initfunc(static int
-acornfb_lookup_timing(struct fb_var_screeninfo *var))
-{
-	const struct modex_params *x;
-	const struct modey_params *y;
-
-	/*
-	 * We must adjust the resolution parameters
-	 * before selecting the timing parameters.
-	 */
-	acornfb_pre_adjust_timing(var, -1);
-
-	for (x = modex_params; x->x_res; x++) {
-
-		/*
-		 * Is this resolution one we're looking for?
-		 */
-		if (x->x_res != var->xres)
-			continue;
-
-		/*
-		 * Is the hsync frequency ok for our monitor?
-		 */
-		if (x->hf > fb_info.monspecs.hfmax ||
-		    x->hf < fb_info.monspecs.hfmin)
-			continue;
-
-		/*
-		 * Try to find a vertical resolution
-		 */
-		for (y = x->modey; y->y_res; y++) {
-			/*
-			 * Is this resolution one we're looking for?
-			 */
-			if (y->y_res != var->yres)
-				continue;
-
-			/*
-			 * Is the vsync frequency ok for our monitor?
-			 */
-			if (y->vf > fb_info.monspecs.vfmax ||
-			    y->vf < fb_info.monspecs.vfmin)
-				continue;
-
-			goto found;
-		}
-	}
-
-	var->pixclock = 0;
-
-	return -EINVAL;
-
-found:
-	/*
-	 * Why is pixclock in picoseconds?
-	 */
-	switch (x->clock) {
-	case 36000: var->pixclock =  27778;	break;
-	case 25175: var->pixclock =  39722;	break;
-	case 24000: var->pixclock =  41667;	break;
-	case 16000: var->pixclock =  62500;	break;
-	case 12000: var->pixclock =  83333;	break;
-	case  8000: var->pixclock = 125000;	break;
-	default:    var->pixclock =      0;     break;
-	}
-
-#ifdef DEBUG_MODE_SELECTION
-	printk(KERN_DEBUG "Found %dx%d at %d.%3dkHz, %dHz, pix %d\n",
-		x->x_res, y->y_res,
-		x->hf / 1000, x->hf % 1000,
-		y->vf, var->pixclock);
-#endif
-
-	var->left_margin	= x->l_margin;
-	var->right_margin	= x->r_margin;
-	var->upper_margin	= y->u_margin;
-	var->lower_margin	= y->b_margin;
-	var->hsync_len		= x->hsync_len;
-	var->vsync_len		= y->vsync_len;
-	var->sync		= 0;
-
-	/*
-	 * Now adjust the parameters we found
-	 */
-	acornfb_post_adjust_timing(var);
-
-	return 0;
+static struct fb_videomode __initdata
+acornfb_default_mode = {
+	name:		NULL,
+	refresh:	60,
+	xres:		640,
+	yres:		480,
+	pixclock:	39722,
+	left_margin:	56,
+	right_margin:	16,
+	upper_margin:	34,
+	lower_margin:	9,
+	hsync_len:	88,
+	vsync_len:	2,
+	sync:		0,
+	vmode:		FB_VMODE_NONINTERLACED
+};
+
+static int fb_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+			const char *mode_option, const struct fb_videomode *db,
+			unsigned int dbsize, const struct fb_videomode *default_mode,
+			unsigned int default_bpp)
+{
+	var->xres = default_mode->xres;
+	var->yres = default_mode->yres;
+	var->xres_virtual = default_mode->xres;
+	var->yres_virtual = default_mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->bits_per_pixel = default_bpp;
+	var->activate = 0;
+	var->pixclock = default_mode->pixclock;
+	var->left_margin = default_mode->left_margin;
+	var->right_margin = default_mode->right_margin;
+	var->upper_margin = default_mode->upper_margin;
+	var->lower_margin = default_mode->lower_margin;
+	var->hsync_len = default_mode->hsync_len;
+	var->vsync_len = default_mode->vsync_len;
+	var->sync = default_mode->sync;
+	var->vmode = default_mode->vmode;
+	return 3;
 }
 
-__initfunc(static void
-acornfb_init_fbinfo(void))
+static void __init
+acornfb_init_fbinfo(void)
 {
 	static int first = 1;
 
@@ -1543,20 +1602,6 @@
 	 * setup initial parameters
 	 */
 	memset(&init_var, 0, sizeof(init_var));
-	init_var.xres		   = DEFAULT_XRES;
-	init_var.yres		   = DEFAULT_YRES;
-
-#if   defined(FBCON_HAS_CFB4)
-	init_var.bits_per_pixel	   = 4;
-#elif defined(FBCON_HAS_CFB8)
-	init_var.bits_per_pixel    = 8;
-#elif defined(FBCON_HAS_CFB2)
-	init_var.bits_per_pixel    = 2;
-#elif defined(FBCON_HAS_MFB)
-	init_var.bits_per_pixel    = 1;
-#else
-#error No suitable framebuffers configured
-#endif
 
 #if defined(HAS_VIDC20)
 	init_var.red.length	   = 8;
@@ -1572,6 +1617,7 @@
 	init_var.height		   = -1;
 	init_var.width		   = -1;
 	init_var.vmode		   = FB_VMODE_NONINTERLACED;
+	init_var.accel_flags	   = FB_ACCELF_TEXT;
 
 	current_par.dram_size	   = 0;
 	current_par.montype	   = -1;
@@ -1610,48 +1656,62 @@
  *	size can optionally be followed by 'M' or 'K' for
  *	MB or KB respectively.
  */
-__initfunc(static void
-acornfb_parse_font(char *opt))
+static void __init
+acornfb_parse_font(char *opt)
 {
 	strcpy(fb_info.fontname, opt);
 }
 
-__initfunc(static void
-acornfb_parse_mon(char *opt))
+static void __init
+acornfb_parse_mon(char *opt)
 {
-	fb_info.monspecs.hfmin = simple_strtoul(opt, &opt, 0);
-	if (*opt == '-')
-		fb_info.monspecs.hfmax = simple_strtoul(opt + 1, &opt, 0);
+	char *p = opt;
+
+	current_par.montype = -2;
+
+	fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
+	if (*p == '-')
+		fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
 	else
 		fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto bad;
 
-	fb_info.monspecs.vfmin = simple_strtoul(opt + 1, &opt, 0);
-	if (*opt == '-')
-		fb_info.monspecs.vfmax = simple_strtoul(opt + 1, &opt, 0);
+	fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
+	if (*p == '-')
+		fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
 	else
 		fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	fb_info.monspecs.dpms = simple_strtoul(opt + 1, &opt, 0);
+	fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	init_var.width = simple_strtoul(opt + 1, &opt, 0);
+	init_var.width = simple_strtoul(p + 1, &p, 0);
 
-	if (*opt != ':')
-		return;
+	if (*p != ':')
+		goto check_values;
 
-	init_var.height = simple_strtoul(opt + 1, NULL, 0);
+	init_var.height = simple_strtoul(p + 1, NULL, 0);
+
+check_values:
+	if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
+	    fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
+		goto bad;
+	return;
+
+bad:
+	printk("Acornfb: bad monitor settings: %s\n", opt);
+	current_par.montype = -1;
 }
 
-__initfunc(static void
-acornfb_parse_montype(char *opt))
+static void __init
+acornfb_parse_montype(char *opt)
 {
 	current_par.montype = -2;
 
@@ -1692,8 +1752,8 @@
 	}
 }
 
-__initfunc(static void
-acornfb_parse_dram(char *opt))
+static void __init
+acornfb_parse_dram(char *opt)
 {
 	unsigned int size;
 
@@ -1726,8 +1786,8 @@
 	{ NULL, NULL }
 };
 
-__initfunc(void
-acornfb_setup(char *options, int *ints))
+void __init
+acornfb_setup(char *options, int *ints)
 {
 	struct options *optp;
 	char *opt;
@@ -1763,8 +1823,8 @@
  * Detect type of monitor connected
  *  For now, we just assume SVGA
  */
-__initfunc(static int
-acornfb_detect_monitortype(void))
+static int __init
+acornfb_detect_monitortype(void)
 {
 	return 4;
 }
@@ -1800,22 +1860,45 @@
 	printk("acornfb: freed %dK memory\n", mb_freed);
 }
 
-__initfunc(void
-acornfb_init(void))
+void __init
+acornfb_init(void)
 {
 	unsigned long size;
 	u_int h_sync, v_sync;
+	int rc, i;
 
 	acornfb_init_fbinfo();
 
 	if (current_par.montype == -1)
 		current_par.montype = acornfb_detect_monitortype();
 
-	if (current_par.montype < 0 || current_par.montype > NR_MONTYPES)
+	if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
 		current_par.montype = 4;
 
-	fb_info.monspecs = monspecs[current_par.montype];
-	fb_info.monspecs.dpms = current_par.dpms;
+	if (current_par.montype > 0) {
+		fb_info.monspecs = monspecs[current_par.montype];
+		fb_info.monspecs.dpms = current_par.dpms;
+	}
+
+	/*
+	 * Try to select a suitable default mode
+	 */
+	for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) {
+		unsigned long hs;
+
+		hs = modedb[i].refresh *
+		     (modedb[i].yres + modedb[i].upper_margin +
+		      modedb[i].lower_margin + modedb[i].vsync_len);
+		if (modedb[i].xres == DEFAULT_XRES &&
+		    modedb[i].yres == DEFAULT_YRES &&
+		    modedb[i].refresh >= fb_info.monspecs.vfmin &&
+		    modedb[i].refresh <= fb_info.monspecs.vfmax &&
+		    hs                >= fb_info.monspecs.hfmin &&
+		    hs                <= fb_info.monspecs.hfmax) {
+			acornfb_default_mode = modedb[i];
+			break;
+		}
+	}
 
 	current_par.currcon	   = -1;
 	current_par.screen_base	   = SCREEN2_BASE;
@@ -1839,7 +1922,7 @@
 
 	size = PAGE_ALIGN(size);
 
-#ifdef CONFIG_ARCH_RPC
+#if defined(HAS_VIDC20)
 	if (!current_par.using_vram) {
 		/*
 		 * RiscPC needs to allocate the DRAM memory
@@ -1847,17 +1930,30 @@
 		 * VRAM.  Archimedes/A5000 machines use a
 		 * fixed address for their framebuffers.
 		 */
-		current_par.screen_base = (unsigned long)kmalloc(size, GFP_KERNEL);
+		int order = 0;
+		unsigned long page, top;
+		while (size > (PAGE_SIZE * (1 << order)))
+			order++;
+		current_par.screen_base = __get_free_pages(GFP_KERNEL, order);
 		if (current_par.screen_base == 0) {
 			printk(KERN_ERR "acornfb: unable to allocate screen "
 			       "memory\n");
 			return;
 		}
+		top = current_par.screen_base + (PAGE_SIZE * (1 << order));
+		/* Mark the framebuffer pages as reserved so mmap will work. */
+		for (page = current_par.screen_base;
+		     page < PAGE_ALIGN(current_par.screen_base + size);
+		     page += PAGE_SIZE)
+			mem_map[MAP_NR(page)].flags |= (1 << PG_reserved);
+		/* Hand back any excess pages that we allocated. */
+		for (page = current_par.screen_base + size; page < top; page += PAGE_SIZE)
+			free_page(page);
 		current_par.screen_base_p =
 			virt_to_phys(current_par.screen_base);
 	}
 #endif
-#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_ARC)
+#if defined(HAS_VIDC)
 #define MAX_SIZE	480*1024
 	/*
 	 * Limit maximum screen size.
@@ -1873,22 +1969,20 @@
 	
 	current_par.screen_size	   = size;
 	current_par.palette_size   = VIDC_PALETTE_SIZE;
-	current_par.allow_modeset  = 1;
 
 	/*
 	 * Lookup the timing for this resolution.  If we can't
 	 * find it, then we can't restore it if we change
 	 * the resolution, so we disable this feature.
 	 */
-	if (acornfb_lookup_timing(&init_var))
-		current_par.allow_modeset = 0;
-
-	/*
-	 * Again, if this does not succeed, then we disallow
-	 * changes to the resolution parameters.
-	 */
-	if (acornfb_set_var(&init_var, -1, &fb_info))
-		current_par.allow_modeset = 0;
+	rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
+			 sizeof(modedb) / sizeof(*modedb),
+			 &acornfb_default_mode, DEFAULT_BPP);
+
+	if (!rc && fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
+				&acornfb_default_mode, DEFAULT_BPP)) {
+		printk("Acornfb: no valid mode found\n");
+	}
 
 	h_sync = 1953125000 / init_var.pixclock;
 	h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin +
@@ -1901,6 +1995,15 @@
 		current_par.using_vram ? 'V' : 'D',
 		VIDC_NAME, init_var.xres, init_var.yres,
 		h_sync / 1000, h_sync % 1000, v_sync);
+
+	printk("Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
+		fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
+		fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
+		fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
+		fb_info.monspecs.dpms ? ", DPMS" : "");
+
+	if (acornfb_set_var(&init_var, -1, &fb_info))
+		printk(KERN_ERR "Acornfb: unable to set display parameters\n");
 
 	register_framebuffer(&fb_info);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty128fb.c linux.18p17/drivers/video/aty128fb.c
--- linux.vanilla/drivers/video/aty128fb.c	Sat Sep  9 18:42:46 2000
+++ linux.18p17/drivers/video/aty128fb.c	Tue Sep  5 22:01:37 2000
@@ -2614,14 +2614,14 @@
 MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
 MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
 
-int __init
+int
 init_module(void)
 {
     aty128fb_init();
     return 0;
 }
 
-void __exit
+void
 cleanup_module(void)
 {
     struct fb_info_aty128 *info = board_list;
@@ -2636,9 +2636,9 @@
             mtrr_del(info->mtrr.vram, info->frame_buffer_phys,
                      info->vram_size);
 #endif /* CONFIG_MTRR */
-        iounmap (tmp->regbase);
-        iounmap (tmp->frame_buffer);
-        kfree (tmp);
+        iounmap (info->regbase);
+        iounmap (&info->frame_buffer);
+        kfree (info);
     }
 }
 #endif /* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/atyfb.c linux.18p17/drivers/video/atyfb.c
--- linux.vanilla/drivers/video/atyfb.c	Sat Sep  9 18:42:46 2000
+++ linux.18p17/drivers/video/atyfb.c	Fri Sep  1 13:39:05 2000
@@ -394,7 +394,7 @@
      *  Interface used by the world
      */
 
-void atyfb_init(void);
+int atyfb_init(void);
 #ifdef CONFIG_FB_OF
 void atyfb_of_init(struct device_node *dp);
 #endif
@@ -2880,7 +2880,7 @@
     return 1;
 }
 
-__initfunc(void atyfb_init(void))
+int __init atyfb_init(void)
 {
 #if defined(CONFIG_FB_OF)
     /* We don't want to be called like this. */
@@ -2900,7 +2900,7 @@
 
     /* Do not attach when we have a serial console. */
     if (!con_is_present())
-	return;
+	return 0;
 #else
     u16 tmp;
 #endif
@@ -2912,7 +2912,7 @@
 	    info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
 	    if (!info) {
 		printk("atyfb_init: can't alloc fb_info_aty\n");
-		return;
+		return 0;
 	    }
 	    memset(info, 0, sizeof(struct fb_info_aty));
 
@@ -2948,7 +2948,7 @@
 	    if (!info->mmap_map) {
 		printk("atyfb_init: can't alloc mmap_map\n");
 		kfree(info);
-		return;
+		return 0;
 	    }
 	    memset(info->mmap_map, 0, j * sizeof(*info->mmap_map));
 
@@ -3133,7 +3133,7 @@
 
 	    if(!info->ati_regbase) {
 		    kfree(info);
-		    return;
+		    return 0;
 	    }
 
 	    info->ati_regbase_phys += 0xc00;
@@ -3160,7 +3160,7 @@
 
 	    if(!info->frame_buffer) {
 		    kfree(info);
-		    return;
+		    return 0;
 	    }
 
 #endif /* __sparc__ */
@@ -3169,7 +3169,7 @@
 		if (info->mmap_map)
 		    kfree(info->mmap_map);
 		kfree(info);
-		return;
+		return 0;
 	    }
 
 #ifdef __sparc__
@@ -3207,7 +3207,7 @@
 	info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
 	if (!info) {
 	    printk("atyfb_init: can't alloc fb_info_aty\n");
-	    return;
+	    return 0;
 	}
 	memset(info, 0, sizeof(struct fb_info_aty));
 
@@ -3223,10 +3223,11 @@
 	if (!aty_init(info, "ISA bus")) {
 	    kfree(info);
 	    /* This is insufficient! kernel_map has added two large chunks!! */
-	    return;
+	    return 0;
 	}
     }
 #endif
+    return 1;
 }
 
 #ifdef CONFIG_FB_OF
@@ -4213,3 +4214,46 @@
 	return result;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+#ifdef MODULE
+
+int blink = 1;
+static u32 vram = 0;
+static int pll = 0;
+static int mclk = 0;
+#if defined(CONFIG_PPC)
+static int vmode = VMODE_NVRAM;
+static int cmode = CMODE_NVRAM;
+#endif
+
+MODULE_PARM(noaccel, "i");
+MODULE_PARM_DESC(noaccel, "Do not use accelerating engine (0 or 1=disabled) (default=0)");
+MODULE_PARM(blink, "i");
+MODULE_PARM_DESC(blink, "Enables hardware cursor blinking (0 or 1) (default=1)");
+#ifdef CONFIG_PPC
+MODULE_PARM(vmode, "i");
+MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)");
+MODULE_PARM(cmode, "i");
+MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)");
+#endif
+
+int init_module(void)
+{
+	curblink = blink;
+	default_vram = vram;
+	default_pll = pll;
+	default_mclk = mclk;
+#ifdef CONFIG_PPC
+	default_vmode = vmode;
+	default_cmode = cmode;
+#endif
+	if (!atyfb_init())
+		return -ENXIO;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif	/* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/cyber2000fb.c linux.18p17/drivers/video/cyber2000fb.c
--- linux.vanilla/drivers/video/cyber2000fb.c	Fri Apr 21 12:46:38 2000
+++ linux.18p17/drivers/video/cyber2000fb.c	Fri Sep 15 23:31:13 2000
@@ -35,7 +35,9 @@
 #define DEFAULT_YRES	480
 #define DEFAULT_BPP	8
 
-static volatile unsigned char	*CyberRegs;
+#define MMIO_SIZE	0x000c0000
+
+static char			*CyberRegs;
 
 #include "cyber2000fb.h"
 
@@ -45,7 +47,7 @@
 static struct display_switch	*dispsw;
 static struct fb_var_screeninfo __initdata init_var = {};
 
-#ifdef DEBUG
+#if defined(DEBUG) && defined(CONFIG_DEBUG_LL)
 static void debug_printf(char *fmt, ...)
 {
 	char buffer[128];
@@ -61,168 +63,6 @@
 #define debug_printf(x...) do { } while (0)
 #endif
 
-/*
- *    Predefined Video Modes
- */
-static const struct res cyber2000_res[] = {
-	{
-		640, 480,
-		{
-			0x5f, 0x4f, 0x50, 0x80, 0x52, 0x9d, 0x0b, 0x3e,
-			0x00, 0x40,
-			0xe9, 0x8b, 0xdf, 0x50, 0x00, 0xe6, 0x04, 0xc3
-		},
-		0x00,
-		{ 0xd2, 0xce, 0xdb, 0x54 }
-	},
-
-	{
-		800, 600,
-		{
-			0x7f, 0x63, 0x64, 0x00, 0x66, 0x10, 0x6f, 0xf0,
-			0x00, 0x60,
-			0x5b, 0x8f, 0x57, 0x64, 0x00, 0x59, 0x6e, 0xe3
-		},
-		0x00,
-		{ 0x52, 0x85, 0xdb, 0x54 }
-	},
-
-	{
-		1024, 768,
-		{
-			0x9f, 0x7f, 0x80, 0x80, 0x8b, 0x94, 0x1e, 0xfd,
-			0x00, 0x60,
-			0x03, 0x86, 0xff, 0x80, 0x0f, 0x00, 0x1e, 0xe3
-		},
-		0x00,
-		{ 0xd0, 0x52, 0xdb, 0x54 }
-	},
-#if 0
-	{
-		1152, 886,
-		{
-		},
-		{
-		}
-	},
-#endif
-	{
-		1280, 1024,
-		{
-			0xce, 0x9f, 0xa0, 0x8f, 0xa2, 0x1f, 0x28, 0x52,
-			0x00, 0x40,
-			0x08, 0x8f, 0xff, 0xa0, 0x00, 0x03, 0x27, 0xe3
-		},
-		0x1d,
-		{ 0xb4, 0x4b, 0xdb, 0x54 }
-	},
-
-	{
-		1600, 1200,
-		{
-			0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
-			0x00, 0x40,
-			0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
-		},
-		0x1f,
-		{ 0xbd, 0x10, 0xdb, 0x54 }
-	}
-};
-
-#define NUM_TOTAL_MODES    arraysize(cyber2000_res)
-
-static const char igs_regs[] = {
-	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
-	0x30, 0x21,	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
-	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
-	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
-	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
-	0x70, 0x0b,	0x71, 0x10,	0x72, 0x45,	0x73, 0x30,
-	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
-};
-
-static const char crtc_idx[] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09,
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
-};
-
-static void cyber2000_init_hw(const struct res *res)
-{
-	int i;
-
-	debug_printf("init vga hw for %dx%d\n", res->xres, res->yres);
-
-	cyber2000_outb(0xef, 0x3c2);
-	cyber2000_crtcw(0x0b, 0x11);
-	cyber2000_attrw(0x00, 0x11);
-
-	cyber2000_seqw(0x01, 0x00);
-	cyber2000_seqw(0x01, 0x01);
-	cyber2000_seqw(0x0f, 0x02);
-	cyber2000_seqw(0x00, 0x03);
-	cyber2000_seqw(0x0e, 0x04);
-	cyber2000_seqw(0x03, 0x00);
-
-	for (i = 0; i < sizeof(crtc_idx); i++)
-		cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]);
-
-	for (i = 0x0a; i < 0x10; i++)
-		cyber2000_crtcw(0, i);
-
-	cyber2000_crtcw(0xff, 0x18);
-
-	cyber2000_grphw(0x00, 0x00);
-	cyber2000_grphw(0x00, 0x01);
-	cyber2000_grphw(0x00, 0x02);
-	cyber2000_grphw(0x00, 0x03);
-	cyber2000_grphw(0x00, 0x04);
-	cyber2000_grphw(0x60, 0x05);
-	cyber2000_grphw(0x05, 0x06);
-	cyber2000_grphw(0x0f, 0x07);
-	cyber2000_grphw(0xff, 0x08);
-
-	for (i = 0; i < 16; i++)
-		cyber2000_attrw(i, i);
-
-	cyber2000_attrw(0x01, 0x10);
-	cyber2000_attrw(0x00, 0x11);
-	cyber2000_attrw(0x0f, 0x12);
-	cyber2000_attrw(0x00, 0x13);
-	cyber2000_attrw(0x00, 0x14);
-
-	for (i = 0; i < sizeof(igs_regs); i += 2)
-		cyber2000_grphw(igs_regs[i+1], igs_regs[i]);
-
-	cyber2000_grphw(res->crtc_ofl, 0x11);
-
-	for (i = 0; i < 4; i += 1)
-		cyber2000_grphw(res->clk_regs[i], 0xb0 + i);
-
-	cyber2000_grphw(0x01, 0x90);
-	cyber2000_grphw(0x80, 0xb9);
-	cyber2000_grphw(0x00, 0xb9);
-
-	cyber2000_outb(0x56, 0x3ce);
-	i = cyber2000_inb(0x3cf);
-	cyber2000_outb(i | 4, 0x3cf);
-	cyber2000_outb(0x04, 0x3c6);
-	cyber2000_outb(i,    0x3cf);
-
-	cyber2000_outb(0x20, 0x3c0);
-	cyber2000_outb(0xff, 0x3c6);
-
-	for (i = 0; i < 256; i++) {
-		cyber2000_outb(i, 0x3c8);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-	}
-}
-
-
-static struct fb_ops cyber2000fb_ops;
-
 /* -------------------- Hardware specific routines ------------------------- */
 
 /*
@@ -232,10 +72,10 @@
 {
 	int count = 10000;
 
-	while (cyber2000_inb(0xbf011) & 0x80) {
+	while (cyber2000_inb(CO_REG_CONTROL) & 0x80) {
 		if (!count--) {
 			debug_printf("accel_wait timed out\n");
-			cyber2000_outb(0, 0xbf011);
+			cyber2000_outb(0, CO_REG_CONTROL);
 			return;
 		}
 		udelay(10);
@@ -252,45 +92,54 @@
 cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
 		int height, int width)
 {
-	unsigned long src, dst, chwidth = p->var.xres_virtual * fontheight(p);
-	int v = 0x8000;
+	unsigned long src, dst;
+	unsigned int fh, fw;
+	int cmd = CO_CMD_L_PATTERN_FGCOL;
+
+	fw    = fontwidth(p);
+	sx    *= fw;
+	dx    *= fw;
+	width *= fw;
+	width -= 1;
 
 	if (sx < dx) {
-		sx += width - 1;
-		dx += width - 1;
-		v |= 4;
+		sx += width;
+		dx += width;
+		cmd |= CO_CMD_L_INC_LEFT;
 	}
 
+	fh     = fontheight(p);
+	sy     *= fh;
+	dy     *= fh;
+	height *= fh;
+	height -= 1;
+
 	if (sy < dy) {
-		sy += height - 1;
-		dy += height - 1;
-		v |= 2;
+		sy += height;
+		dy += height;
+		cmd |= CO_CMD_L_INC_UP;
 	}
 
-	sx *= fontwidth(p);
-	dx *= fontwidth(p);
-	src = sx + sy * chwidth;
-	dst = dx + dy * chwidth;
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	src    = sx + sy * p->var.xres_virtual;
+	dst    = dx + dy * p->var.xres_virtual;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
+	cyber2000_outb(0x00,  CO_REG_CONTROL);
+	cyber2000_outb(0x03,  CO_REG_FORE_MIX);
+	cyber2000_outw(width, CO_REG_WIDTH);
 
 	if (p->var.bits_per_pixel != 24) {
-		cyber2000_outl(dst, 0xbf178);
-		cyber2000_outl(src, 0xbf170);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
+		cyber2000_outl(src, CO_REG_SRC_PTR);
 	} else {
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
-		cyber2000_outl(src * 3, 0xbf170);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst,     CO_REG_X_PHASE);
+		cyber2000_outl(src * 3, CO_REG_SRC_PTR);
 	}
 
-	cyber2000_outw(height, 0xbf062);
-	cyber2000_outw(v,      0xbf07c);
-	cyber2000_outw(0x2800, 0xbf07e);
+	cyber2000_outw(height, CO_REG_HEIGHT);
+	cyber2000_outw(cmd,    CO_REG_CMD_L);
+	cyber2000_outw(0x2800, CO_REG_CMD_H);
 }
 
 static void
@@ -298,35 +147,40 @@
 		int height, int width)
 {
 	unsigned long dst;
+	unsigned int fw, fh;
 	u32 bgx = attr_bgcol_ec(p, conp);
 
-	dst = sx * fontwidth(p) + sy * p->var.xres_virtual * fontheight(p);
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	fw = fontwidth(p);
+	fh = fontheight(p);
+
+	dst    = sx * fw + sy * p->var.xres_virtual * fh;
+	width  = width * fw - 1;
+	height = height * fh - 1;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
-	cyber2000_outw(height, 0xbf062);
+	cyber2000_outb(0x00,   CO_REG_CONTROL);
+	cyber2000_outb(0x03,   CO_REG_FORE_MIX);
+	cyber2000_outw(width,  CO_REG_WIDTH);
+	cyber2000_outw(height, CO_REG_HEIGHT);
 
 	switch (p->var.bits_per_pixel) {
+	case 15:
 	case 16:
 		bgx = ((u16 *)p->dispsw_data)[bgx];
 	case 8:
-		cyber2000_outl(dst, 0xbf178);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
 		break;
 
 	case 24:
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst, CO_REG_X_PHASE);
 		bgx = ((u32 *)p->dispsw_data)[bgx];
 		break;
 	}
 
-	cyber2000_outl(bgx,    0xbf058);
-	cyber2000_outw(0x8000, 0xbf07c);
-	cyber2000_outw(0x0800, 0xbf07e);
+	cyber2000_outl(bgx, CO_REG_FOREGROUND);
+	cyber2000_outw(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L);
+	cyber2000_outw(0x0800, CO_REG_CMD_H);
 }
 
 static void
@@ -415,14 +269,27 @@
 	current_par.palette[regno].blue  = blue;
 
 	switch (fb_display[current_par.currcon].var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
 	case 8:
 		cyber2000_outb(regno, 0x3c8);
 		cyber2000_outb(red,   0x3c9);
 		cyber2000_outb(green, 0x3c9);
 		cyber2000_outb(blue,  0x3c9);
 		break;
+#endif
 
 #ifdef FBCON_HAS_CFB16
+	case 15:
+		if (regno < 32) {
+			cyber2000_outb(regno << 3, 0x3c8);
+			cyber2000_outb(red, 0x3c9);
+			cyber2000_outb(green, 0x3c9);
+			cyber2000_outb(blue, 0x3c9);
+		}
+		if (regno < 16)
+			current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+		break;
+
 	case 16:
 		if (regno < 64) {
 			/* write green */
@@ -464,86 +331,136 @@
 	return 0;
 }
 
-static int cyber2000fb_set_timing(struct fb_var_screeninfo *var)
-{
-	int width = var->xres_virtual;
-	int scr_pitch, fetchrow;
-	int i;
-	char b, col;
+struct par_info {
+	/*
+	 * Hardware
+	 */
+	unsigned char	clock_mult;
+	unsigned char	clock_div;
+	unsigned char	visualid;
+	unsigned char	pixformat;
+	unsigned char	crtc_ofl;
+	unsigned char	crtc[19];
+	unsigned int	width;
+	unsigned int	pitch;
 
-	switch (var->bits_per_pixel) {
-	case 8:	/* PSEUDOCOLOUR, 256 */
-		b = 0;
-		col = 1;
-		scr_pitch = var->xres_virtual / 8;
-		break;
+	/*
+	 * Other
+	 */
+	unsigned int	visual;
+};
 
-	case 16:/* DIRECTCOLOUR, 64k */
-		b = 1;
-		col = 2;
-		scr_pitch = var->xres_virtual / 8 * 2;
-		break;
-	case 24:/* TRUECOLOUR, 16m */
-		b = 2;
-		col = 4;
-		scr_pitch = var->xres_virtual / 8 * 3;
-		width *= 3;
-		break;
+static const char crtc_idx[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+};
 
-	default:
-		return 1;
+static void cyber2000fb_set_timing(struct par_info *hw)
+{
+	unsigned int fetchrow, i;
+
+	/*
+	 * Blank palette
+	 */
+	for (i = 0; i < 256; i++) {
+		cyber2000_outb(i, 0x3c8);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
 	}
 
-	for (i = 0; i < NUM_TOTAL_MODES; i++)
-		if (var->xres == cyber2000_res[i].xres &&
-		    var->yres == cyber2000_res[i].yres)
-			break;
+	cyber2000_outb(0xef, 0x3c2);
+	cyber2000_crtcw(0x11, 0x0b);
+	cyber2000_attrw(0x11, 0x00);
 
-	if (i < NUM_TOTAL_MODES)
-		cyber2000_init_hw(cyber2000_res + i);
+	cyber2000_seqw(0x00, 0x01);
+	cyber2000_seqw(0x01, 0x01);
+	cyber2000_seqw(0x02, 0x0f);
+	cyber2000_seqw(0x03, 0x00);
+	cyber2000_seqw(0x04, 0x0e);
+	cyber2000_seqw(0x00, 0x03);
 
-	fetchrow = scr_pitch + 1;
+	for (i = 0; i < sizeof(crtc_idx); i++)
+		cyber2000_crtcw(crtc_idx[i], hw->crtc[i]);
+
+	for (i = 0x0a; i < 0x10; i++)
+		cyber2000_crtcw(i, 0);
+
+	cyber2000_grphw(0x11, hw->crtc_ofl);
+	cyber2000_grphw(0x00, 0x00);
+	cyber2000_grphw(0x01, 0x00);
+	cyber2000_grphw(0x02, 0x00);
+	cyber2000_grphw(0x03, 0x00);
+	cyber2000_grphw(0x04, 0x00);
+	cyber2000_grphw(0x05, 0x60);
+	cyber2000_grphw(0x06, 0x05);
+	cyber2000_grphw(0x07, 0x0f);
+	cyber2000_grphw(0x08, 0xff);
 
-	debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n",
-		     scr_pitch, fetchrow, col, b);
+	/* Attribute controller registers */
+	for (i = 0; i < 16; i++)
+		cyber2000_attrw(i, i);
 
-	cyber2000_outb(0x13, 0x3d4);
-	cyber2000_outb(scr_pitch, 0x3d5);
-	cyber2000_outb(0x14, 0x3ce);
-	cyber2000_outb(fetchrow, 0x3cf);
-	cyber2000_outb(0x15, 0x3ce);
-					/* FIXME: is this the right way round? */
-	cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf);
-	cyber2000_outb(0x77, 0x3ce);
-	cyber2000_outb(col, 0x3cf);
+	cyber2000_attrw(0x10, 0x01);
+	cyber2000_attrw(0x11, 0x00);
+	cyber2000_attrw(0x12, 0x0f);
+	cyber2000_attrw(0x13, 0x00);
+	cyber2000_attrw(0x14, 0x00);
+
+	/* PLL registers */
+	cyber2000_grphw(0xb0, hw->clock_mult);
+	cyber2000_grphw(0xb1, hw->clock_div);
+	cyber2000_grphw(0xb2, 0xdb);
+	cyber2000_grphw(0xb3, 0x54);		/* MCLK: 75MHz */
+	cyber2000_grphw(0x90, 0x01);
+	cyber2000_grphw(0xb9, 0x80);
+	cyber2000_grphw(0xb9, 0x00);
 
+	cyber2000_outb(0x56, 0x3ce);
+	i = cyber2000_inb(0x3cf);
+	cyber2000_outb(i | 4, 0x3cf);
+	cyber2000_outb(0x04, 0x3c6);
+	cyber2000_outb(i,    0x3cf);
 
-	cyber2000_outb(0x33, 0x3ce);
-	cyber2000_outb(0x1c, 0x3cf);
+	cyber2000_outb(0x20, 0x3c0);
+	cyber2000_outb(0xff, 0x3c6);
 
-	cyber2000_outw(width - 1, 0xbf018);
-	cyber2000_outw(width - 1, 0xbf218);
-	cyber2000_outb(b, 0xbf01c);
+	fetchrow = hw->pitch + 1;
+	cyber2000_grphw(0x14, fetchrow);
+	/* FIXME: is this the right way round? */
+	cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((hw->pitch >> 8) & 0x0f));
+	cyber2000_grphw(0x77, hw->visualid);
+	cyber2000_grphw(0x33, 0x1c);
 
-	return 0;
+	/*
+	 * Set up accelerator registers
+	 */
+	cyber2000_outw(hw->width, CO_REG_SRC_WIDTH);
+	cyber2000_outw(hw->width, CO_REG_DEST_WIDTH);
+	cyber2000_outb(hw->pixformat, CO_REG_PIX_FORMAT);
 }
 
-static inline void
+static inline int
 cyber2000fb_update_start(struct fb_var_screeninfo *var)
 {
-#if 0
 	unsigned int base;
 
 	base = var->yoffset * var->xres_virtual + var->xoffset;
 
-	cyber2000_outb(0x0c, 0x3d4);
-	cyber2000_outb(base, 0x3d5);
-	cyber2000_outb(0x0d, 0x3d4);
-	cyber2000_outb(base >> 8, 0x3d5);
-	/* FIXME: need the upper bits of the start offset */
-/*	cyber2000_outb(0x??, 0x3d4);
-	cyber2000_outb(base >> 16, 0x3d5);*/
-#endif
+	base >>= 2;
+
+	if (base >= 1 << 20)
+		return -EINVAL;
+
+	/*
+	 * FIXME: need the upper bits of the start offset
+	 */
+	cyber2000_grphw(0x10, base >> 16 | 0x10);
+	cyber2000_crtcw(0x0c, base >> 8);
+	cyber2000_crtcw(0x0d, base);
+
+	return 0;
 }
 
 /*
@@ -612,29 +529,257 @@
 	return err;
 }
 
+static int cyber2000fb_decode_crtc(struct par_info *hw, struct fb_var_screeninfo *var)
+{
+	unsigned int Htotal, Hblankend, Hsyncend;
+	unsigned int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
+#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
+
+	hw->crtc[13] = hw->pitch;
+	hw->crtc[17] = 0xe3;
+	hw->crtc[14] = 0;
+	hw->crtc[8]  = 0;
+
+	Htotal      = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+	if (Htotal > 2080)
+		return -EINVAL;
+
+	hw->crtc[0] = (Htotal >> 3) - 5;			/* Htotal	*/
+	hw->crtc[1] = (var->xres >> 3) - 1;			/* Hdispend	*/
+	hw->crtc[2] = var->xres >> 3;				/* Hblankstart	*/
+	hw->crtc[4] = (var->xres + var->right_margin) >> 3;	/* Hsyncstart	*/
+
+	Hblankend   = (Htotal - 4*8) >> 3;
+
+	hw->crtc[3] = BIT(Hblankend,  0, 0x1f,  0) |		/* Hblankend	*/
+		      BIT(1,          0, 0x01,  7);
+
+	Hsyncend    = (var->xres + var->right_margin + var->hsync_len) >> 3;
+
+	hw->crtc[5] = BIT(Hsyncend,   0, 0x1f,  0) |		/* Hsyncend	*/
+		      BIT(Hblankend,  5, 0x01,  7);
+
+	Vdispend    = var->yres - 1;
+	Vsyncstart  = var->yres + var->lower_margin;
+	Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
+	Vtotal      = var->yres + var->lower_margin + var->vsync_len + var->upper_margin - 2;
+
+	if (Vtotal > 2047)
+		return -EINVAL;
+
+	Vblankstart = var->yres + 6;
+	Vblankend   = Vtotal - 10;
+
+	hw->crtc[6]  = Vtotal;
+	hw->crtc[7]  = BIT(Vtotal,     8, 0x01,  0) |
+			BIT(Vdispend,   8, 0x01,  1) |
+			BIT(Vsyncstart, 8, 0x01,  2) |
+			BIT(Vblankstart,8, 0x01,  3) |
+			BIT(1,          0, 0x01,  4) |
+	        	BIT(Vtotal,     9, 0x01,  5) |
+			BIT(Vdispend,   9, 0x01,  6) |
+			BIT(Vsyncstart, 9, 0x01,  7);
+	hw->crtc[9]  = BIT(0,          0, 0x1f,  0) |
+		        BIT(Vblankstart,9, 0x01,  5) |
+			BIT(1,          0, 0x01,  6);
+	hw->crtc[10] = Vsyncstart;
+	hw->crtc[11] = BIT(Vsyncend,   0, 0x0f,  0) |
+		       BIT(1,          0, 0x01,  7);
+	hw->crtc[12] = Vdispend;
+	hw->crtc[15] = Vblankstart;
+	hw->crtc[16] = Vblankend;
+	hw->crtc[18] = 0xff;
+
+	/* overflow - graphics reg 0x11 */
+/* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10 4=LINECOMP:10 5-IVIDEO 6=FIXCNT */
+	hw->crtc_ofl =
+		BIT(Vtotal,     10, 0x01,  0) |
+		BIT(Vdispend,   10, 0x01,  1) |
+		BIT(Vsyncstart, 10, 0x01,  2) |
+		BIT(Vblankstart,10, 0x01,  3) |
+		1 << 4;
+
+	return 0;
+}
+
+/*
+ * The following was discovered by a good monitor,
+ * bit twiddling, theorising and but mostly luck.
+ * Strangely, it looks like everyone elses' PLL!
+ *
+ * Clock registers:
+ *   fclock = fpll / div2
+ *   fpll   = fref * mult / div1
+ * where:
+ *   fref = 14.318MHz (69842ps)
+ *   mult = reg0xb0.7:0
+ *   div1 = (reg0xb1.5:0 + 1)
+ *   div2 =  2^(reg0xb1.7:6)
+ *   fpll should be between 115 and 260 MHz
+ *  (8696ps and 3846ps)
+ */
 static int
-cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
+cyber2000fb_decode_clock(struct par_info *hw, struct fb_var_screeninfo *var)
 {
+	static unsigned int divisors_2000[] = { 1, 2, 4, 8 };
+	static unsigned int divisors_2010[] = { 1, 2, 4, 6 };
+	u_long pll_ps = var->pixclock;
+	const u_long ref_ps = 69842;
+	unsigned int *divisors;
+	u_int div2, t_div1, best_div1, best_mult;
+	int best_diff;
+
+	/*
+	 * Step 1:
+	 *   find div2 such that 115MHz < fpll < 260MHz
+	 *   and 0 <= div2 < 4
+	 */
+	if (current_par.dev_id == PCI_DEVICE_ID_INTERG_2010)
+		divisors = divisors_2010;
+	else
+		divisors = divisors_2000;
+ 
+	for (div2 = 0; div2 < 4; div2++) {
+		u_long new_pll;
+
+		new_pll = pll_ps / divisors[div2];
+		if (8696 > new_pll && new_pll > 3846) {
+			pll_ps = new_pll;
+			break;
+		}
+	}
+
+	if (div2 == 4)
+		return -EINVAL;
+
+	/*
+	 * Step 2:
+	 *  Given pll_ps and ref_ps, find:
+	 *    pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005
+	 *  where { 1 < best_div1 < 32, 1 < best_mult < 256 }
+	 *    pll_ps_calc = best_div1 / (ref_ps * best_mult)
+	 */
+	best_diff = 0x7fffffff;
+	best_mult = 32;
+	best_div1 = 255;
+	for (t_div1 = 32; t_div1 > 1; t_div1 -= 1) {
+		u_int rr, t_mult, t_pll_ps;
+		int diff;
+
+		/*
+		 * Find the multiplier for this divisor
+		 */
+		rr = ref_ps * t_div1;
+		t_mult = (rr + pll_ps / 2) / pll_ps;
+
+		/*
+		 * Is the multiplier within the correct range?
+		 */
+		if (t_mult > 256 || t_mult < 2)
+			continue;
+
+		/*
+		 * Calculate the actual clock period from this multiplier
+		 * and divisor, and estimate the error.
+		 */
+		t_pll_ps = (rr + t_mult / 2) / t_mult;
+		diff = pll_ps - t_pll_ps;
+		if (diff < 0)
+			diff = -diff;
+
+		if (diff < best_diff) {
+			best_diff = diff;
+			best_mult = t_mult;
+			best_div1 = t_div1;
+		}
+
+		/*
+		 * If we hit an exact value, there is no point in continuing.
+		 */
+		if (diff == 0)
+			break;
+	}
+
+	/*
+	 * Step 3:
+	 *  combine values
+	 */
+	hw->clock_mult = best_mult - 1;
+	hw->clock_div  = div2 << 6 | (best_div1 - 1);
+
+	return 0;
+}
+
+/*
+ * Decode the info required for the hardware.
+ * This involves the PLL parameters for the dot clock,
+ * CRTC registers, and accelerator settings.
+ */
+static int
+cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, struct par_info *hw)
+{
+	int err;
+
+	hw->width = var->xres_virtual;
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
-	case 8:
-		*visual = FB_VISUAL_PSEUDOCOLOR;
+	case 8:	/* PSEUDOCOLOUR, 256 */
+		hw->visual    = FB_VISUAL_PSEUDOCOLOR;
+		hw->pixformat = PIXFORMAT_8BPP;
+		hw->visualid  = VISUALID_256;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
-	case 16:
-		*visual = FB_VISUAL_DIRECTCOLOR;
+	case 15:/* DIRECTCOLOUR, 32k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_32K;
+		hw->pitch     = hw->width >> 2;
+		break;
+
+	case 16:/* DIRECTCOLOUR, 64k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_64K;
+		hw->pitch     = hw->width >> 2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB24
-	case 24:
-		*visual = FB_VISUAL_TRUECOLOR;
+	case 24:/* TRUECOLOUR, 16m */
+		hw->visual    = FB_VISUAL_TRUECOLOR;
+		hw->pixformat = PIXFORMAT_24BPP;
+		hw->visualid  = VISUALID_16M;
+		hw->width    *= 3;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 	default:
 		return -EINVAL;
 	}
 
+	err = cyber2000fb_decode_clock(hw, var);
+	if (err)
+		return err;
+
+	err = cyber2000fb_decode_crtc(hw, var);
+	if (err)
+		return err;
+
+	debug_printf("Clock: %02X %02X\n",
+		hw->clock_mult, hw->clock_div);
+	{
+		int i;
+
+		for (i = 0; i < 19; i++)
+			debug_printf("%2d ", i);
+		debug_printf("\n");
+		for (i = 0; i < 18; i++)
+			debug_printf("%02X ", hw->crtc[i]);
+		debug_printf("%02X\n", hw->crtc_ofl);
+	}
+	hw->width     -= 1;
+
 	return 0;
 }
 
@@ -648,17 +793,17 @@
 	struct display *display;
 
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id, "Cyber2000");
+	strcpy(fix->id, current_par.dev_name);
 
 	if (con >= 0)
 		display = fb_display + con;
 	else
 		display = &global_disp;
 
-	fix->smem_start	 = (char *)current_par.screen_base_p;
+	fix->smem_start	 = (void *)current_par.screen_base_p;
 	fix->smem_len	 = current_par.screen_size;
-	fix->mmio_start	 = (char *)current_par.regs_base_p;
-	fix->mmio_len	 = 0x000c0000;
+	fix->mmio_start	 = (void *)current_par.regs_base_p;
+	fix->mmio_len	 = MMIO_SIZE;
 	fix->type	 = display->type;
 	fix->type_aux	 = display->type_aux;
 	fix->xpanstep	 = 0;
@@ -694,14 +839,15 @@
 cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	struct display *display;
-	int err, chgvar = 0, visual;
+	struct par_info hw;
+	int err, chgvar = 0;
 
 	if (con >= 0)
 		display = fb_display + con;
 	else
 		display = &global_disp;
 
-	err = cyber2000fb_decode_var(var, con, &visual);
+	err = cyber2000fb_decode_var(var, con, &hw);
 	if (err)
 		return err;
 
@@ -737,15 +883,17 @@
 	}
 
 	display->var = *var;
+	display->var.activate &= ~FB_ACTIVATE_ALL;
 
-	display->screen_base	= (char *)current_par.screen_base;
-	display->visual		= visual;
+	if (var->activate & FB_ACTIVATE_ALL)
+		global_disp.var = display->var;
+
+	display->screen_base	= current_par.screen_base;
+	display->visual		= hw.visual;
 	display->type		= FB_TYPE_PACKED_PIXELS;
 	display->type_aux	= 0;
-	display->ypanstep	= 0;
+	display->ypanstep	= 1;
 	display->ywrapstep	= 0;
-	display->line_length	=
-	display->next_line	= (var->xres_virtual * var->bits_per_pixel) / 8;
 	display->can_soft_blank = 1;
 	display->inverse	= 0;
 
@@ -754,27 +902,33 @@
 	case 8:
 		dispsw = &fbcon_cfb8;
 		display->dispsw_data = NULL;
+		display->next_line = var->xres_virtual;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
+	case 15:
 	case 16:
 		dispsw = &fbcon_cfb16;
 		display->dispsw_data = current_par.c_table.cfb16;
+		display->next_line = var->xres_virtual * 2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB24
 	case 24:
 		dispsw = &fbcon_cfb24;
 		display->dispsw_data = current_par.c_table.cfb24;
+		display->next_line = var->xres_virtual * 3;
 		break;
 #endif
 	default:
-		printk(KERN_WARNING "cyber2000: no support for %dbpp\n",
-		       display->var.bits_per_pixel);
+		printk(KERN_WARNING "%s: no support for %dbpp\n",
+		       current_par.dev_name, display->var.bits_per_pixel);
 		dispsw = &fbcon_dummy;
 		break;
 	}
 
+	display->line_length = display->next_line;
+
 	if (display->var.accel_flags & FB_ACCELF_TEXT &&
 	    dispsw != &fbcon_dummy)
 		display->dispsw = &fbcon_cyber_accel;
@@ -788,7 +942,7 @@
 		struct fb_cmap *cmap;
 
 		cyber2000fb_update_start(var);
-		cyber2000fb_set_timing(var);
+		cyber2000fb_set_timing(&hw);
 
 		if (display->cmap.len)
 			cmap = &display->cmap;
@@ -818,9 +972,9 @@
 		return -EINVAL;
 	if (y_bottom > fb_display[con].var.yres_virtual)
 		return -EINVAL;
-return -EINVAL;
 
-	cyber2000fb_update_start(var);
+	if (cyber2000fb_update_start(var))
+		return -EINVAL;
 
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -849,9 +1003,12 @@
 static int
 cyber2000fb_updatevar(int con, struct fb_info *info)
 {
+	int ret = 0;
+
 	if (con == current_par.currcon)
-		cyber2000fb_update_start(&fb_display[con].var);
-	return 0;
+		ret = cyber2000fb_update_start(&fb_display[con].var);
+
+	return ret;
 }
 
 static int
@@ -882,27 +1039,53 @@
 {
 	int i;
 
-	if (blank) {
+	/*
+	 *  Blank the screen if blank_mode != 0, else unblank. If
+	 *  blank == NULL then the caller blanks by setting the CLUT
+	 *  (Color Look Up Table) to all black. Return 0 if blanking
+	 *  succeeded, != 0 if un-/blanking failed due to e.g. a
+	 *  video mode which doesn't support it. Implements VESA
+	 *  suspend and powerdown modes on hardware that supports
+	 *  disabling hsync/vsync:
+	 *    blank_mode == 2: suspend vsync
+	 *    blank_mode == 3: suspend hsync
+	 *    blank_mode == 4: powerdown
+	 *
+	 *  wms...Enable VESA DMPS compatible powerdown mode
+	 *  run "setterm -powersave powerdown" to take advantage
+	 */
+     
+	switch (blank) {
+	case 4:	/* powerdown - both sync lines down */
+    		cyber2000_grphw(0x16, 0x05);
+		break;	
+	case 3:	/* hsync off */
+    		cyber2000_grphw(0x16, 0x01);
+		break;	
+	case 2:	/* vsync off */
+    		cyber2000_grphw(0x16, 0x04);
+		break;	
+	case 1:	/* just software blanking of screen */
+		cyber2000_grphw(0x16, 0x00);
 		for (i = 0; i < 256; i++) {
 			cyber2000_outb(i, 0x3c8);
 			cyber2000_outb(0, 0x3c9);
 			cyber2000_outb(0, 0x3c9);
 			cyber2000_outb(0, 0x3c9);
 		}
-	} else {
+		break;
+	default: /* case 0, or anything else: unblank */
+		cyber2000_grphw(0x16, 0x00);
 		for (i = 0; i < 256; i++) {
 			cyber2000_outb(i, 0x3c8);
 			cyber2000_outb(current_par.palette[i].red, 0x3c9);
 			cyber2000_outb(current_par.palette[i].green, 0x3c9);
 			cyber2000_outb(current_par.palette[i].blue, 0x3c9);
 		}
+		break;
 	}
 }
 
-__initfunc(void cyber2000fb_setup(char *options, int *ints))
-{
-}
-
 static struct fb_ops cyber2000fb_ops =
 {
 	cyber2000fb_open,
@@ -916,8 +1099,96 @@
 	cyber2000fb_ioctl
 };
 
-__initfunc(static void
-cyber2000fb_init_fbinfo(void))
+int cyber2000fb_attach(struct cyberpro_info *info)
+{
+	if (current_par.initialised) {
+		info->regs    = CyberRegs;
+		info->fb      = current_par.screen_base;
+		info->fb_size = current_par.screen_size;
+
+		strncpy(info->dev_name, current_par.dev_name, sizeof(info->dev_name));
+
+		MOD_INC_USE_COUNT;
+	}
+
+	return current_par.initialised;
+}
+
+void cyber2000fb_detach(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+EXPORT_SYMBOL(cyber2000fb_attach);
+EXPORT_SYMBOL(cyber2000fb_detach);
+
+#define fb_videomode fb_2_3_videomode
+
+/* stuff from 2.3 */
+struct fb_videomode {
+	const char *name;
+	u32 refresh;
+	u32 xres;
+	u32 yres;
+	u32 pixclock;
+	u32 left_margin;
+	u32 right_margin;
+	u32 upper_margin;
+	u32 lower_margin;
+	u32 hsync_len;
+	u32 vsync_len;
+	u32 sync;
+	u32 vmode;
+};
+
+static int fb_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+			const char *mode_option, const struct fb_videomode *db,
+			unsigned int dbsize, const struct fb_videomode *default_mode,
+			unsigned int default_bpp)
+{
+	var->xres = default_mode->xres;
+	var->yres = default_mode->yres;
+	var->xres_virtual = default_mode->xres;
+	var->yres_virtual = default_mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->bits_per_pixel = default_bpp;
+	var->activate = 0;
+	var->pixclock = default_mode->pixclock;
+	var->left_margin = default_mode->left_margin;
+	var->right_margin = default_mode->right_margin;
+	var->upper_margin = default_mode->upper_margin;
+	var->lower_margin = default_mode->lower_margin;
+	var->hsync_len = default_mode->hsync_len;
+	var->vsync_len = default_mode->vsync_len;
+	var->sync = default_mode->sync;
+	var->vmode = default_mode->vmode;
+	return 3;
+}
+
+/*
+ * These parameters give
+ * 640x480, hsync 31.5kHz, vsync 60Hz
+ */
+static struct fb_videomode __initdata
+cyber2000fb_default_mode = {
+	name:		NULL,
+	refresh:	60,
+	xres:		640,
+	yres:		480,
+	pixclock:	39722,
+	left_margin:	56,
+	right_margin:	16,
+	upper_margin:	34,
+	lower_margin:	9,
+	hsync_len:	88,
+	vsync_len:	2,
+	sync:		FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	vmode:		FB_VMODE_NONINTERLACED
+};
+
+static void __init 
+cyber2000fb_init_fbinfo(void)
 {
 	static int first = 1;
 
@@ -941,18 +1212,15 @@
 	 * setup initial parameters
 	 */
 	memset(&init_var, 0, sizeof(init_var));
-	init_var.xres_virtual		=
-	init_var.xres			= DEFAULT_XRES;
-	init_var.yres_virtual		=
-	init_var.yres			= DEFAULT_YRES;
-	init_var.bits_per_pixel		= DEFAULT_BPP;
 
 	init_var.red.msb_right		= 0;
 	init_var.green.msb_right	= 0;
 	init_var.blue.msb_right		= 0;
 
 	switch(init_var.bits_per_pixel) {
-	case 8:
+	default:
+		init_var.bits_per_pixel = 8;
+	case 8: /* PSEUDOCOLOUR */
 		init_var.bits_per_pixel	= 8;
 		init_var.red.offset	= 0;
 		init_var.red.length	= 8;
@@ -962,7 +1230,17 @@
 		init_var.blue.length	= 8;
 		break;
 
-	case 16:
+	case 15: /* RGB555 */
+		init_var.bits_per_pixel = 15;
+		init_var.red.offset	= 10;
+		init_var.red.length	= 5;
+		init_var.green.offset	= 5;
+		init_var.green.length	= 5;
+		init_var.blue.offset	= 0;
+		init_var.blue.length	= 5;
+		break;
+
+	case 16: /* RGB565 */
 		init_var.bits_per_pixel = 16;
 		init_var.red.offset	= 11;
 		init_var.red.length	= 5;
@@ -972,7 +1250,7 @@
 		init_var.blue.length	= 5;
 		break;
 
-	case 24:
+	case 24: /* RGB888 */
 		init_var.bits_per_pixel = 24;
 		init_var.red.offset	= 16;
 		init_var.red.length	= 8;
@@ -988,36 +1266,175 @@
 	init_var.height			= -1;
 	init_var.width			= -1;
 	init_var.accel_flags		= FB_ACCELF_TEXT;
-	init_var.sync			= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
-	init_var.vmode			= FB_VMODE_NONINTERLACED;
 }
 
 /*
+ * Cyber2000 options:
+ *
+ *  font:fontname
+ *	Set the fontname
+ *
+ *  res:XxY
+ *	Set the default display resolution
+ */
+static void __init
+cyber2000fb_parse_font(char *opt)
+{
+	strcpy(fb_info.fontname, opt);
+}
+
+static struct options {
+	char *name;
+	void (*parse)(char *opt);
+} opt_table[] __initdata = {
+	{ "font",	cyber2000fb_parse_font		},
+	{ NULL,		NULL				}
+};
+
+int __init
+cyber2000fb_setup(char *options)
+{
+	struct options *optp;
+	char *opt;
+
+	if (!options || !*options)
+		return 0;
+
+	cyber2000fb_init_fbinfo();
+
+	for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) {
+		if (!*opt)
+			continue;
+
+		for (optp = opt_table; optp->name; optp++) {
+			int optlen;
+
+			optlen = strlen(optp->name);
+
+			if (strncmp(opt, optp->name, optlen) == 0 &&
+			    opt[optlen] == ':') {
+				optp->parse(opt + optlen + 1);
+				break;
+			}
+		}
+
+		if (!optp->name)
+			printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n",
+				opt);
+	}
+	return 0;
+}
+
+static char igs_regs[] __initdata = {
+	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
+					0x16, 0x00,
+/*	0x30, 0x21,*/	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
+	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
+	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
+	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
+	0x70, 0x0b,/*	0x71, 0x10,	0x72, 0x45,*/	0x73, 0x30,
+	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
+};
+
+static void __init cyber2000fb_hw_init(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(igs_regs); i += 2)
+		cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
+}
+
+static unsigned short device_ids[] __initdata = {
+	PCI_DEVICE_ID_INTERG_2000,
+	PCI_DEVICE_ID_INTERG_2010,
+	PCI_DEVICE_ID_INTERG_5000
+};
+
+/*
  *    Initialization
  */
-__initfunc(void cyber2000fb_init(void))
+int __init cyber2000fb_init(void)
 {
-	struct pci_dev *dev;
+	struct pci_dev *dev = NULL;
 	u_int h_sync, v_sync;
+	u_long mmio_base, smem_base, smem_size;
+	int err = 0, i;
+
+	for (i = 0; i < sizeof(device_ids) / sizeof(device_ids[0]); i++) {
+		dev = pci_find_device(PCI_VENDOR_ID_INTERG,
+				      device_ids[i], NULL);
+		if (dev)
+			break;
+	}
 
-	dev = pci_find_device(PCI_VENDOR_ID_INTERG, 0x2000, NULL);
 	if (!dev)
-		return;
+		return -ENXIO;
+
+	sprintf(current_par.dev_name, "CyberPro%4X", dev->device);
 
-	CyberRegs = bus_to_virt(dev->base_address[0]) + 0x00800000;/*FIXME*/
+	smem_base = dev->base_address[0] & ~3;
+	mmio_base = smem_base + 0x00800000;
+	current_par.dev_id = dev->device;
+
+	/*
+	 * Map in the registers
+	 */
+	CyberRegs = ioremap(mmio_base, MMIO_SIZE);
+	if (!CyberRegs) {
+		printk("%s: unable to map memory mapped IO\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_mmio_resource;
+	}
 
 	cyber2000_outb(0x18, 0x46e8);
 	cyber2000_outb(0x01, 0x102);
 	cyber2000_outb(0x08, 0x46e8);
 
+	/*
+	 * get the video RAM size from the VGA register.
+	 * This should have been already initialised by the BIOS,
+	 * but if it's garbage, claim default 1MB VRAM (woody)
+	 */
+	cyber2000_outb(0x72, 0x3ce);
+	switch (cyber2000_inb(0x3cf) & 3) {
+	case 2:	 smem_size = 0x00400000; break;
+	case 1:	 smem_size = 0x00200000; break;
+	default: smem_size = 0x00100000; break;
+	}
+
+	/*
+	 * Map in screen memory
+	 */
+	current_par.screen_base = ioremap(smem_base, smem_size);
+	if (!current_par.screen_base) {
+		printk("%s: unable to map screen memory\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_smem_resource;
+	}
+current_par.screen_base += IO_FUDGE_FACTOR;
+	current_par.screen_size   = smem_size;
+	current_par.screen_base_p = smem_base + 0x80000000;
+	current_par.regs_base_p   = mmio_base + 0x80000000;
+	current_par.currcon	  = -1;
+
 	cyber2000fb_init_fbinfo();
 
-	current_par.currcon		= -1;
-	current_par.screen_base_p	= 0x80000000 + dev->base_address[0];
-	current_par.screen_base		= (u_int)bus_to_virt(dev->base_address[0]);
-	current_par.screen_size		= 0x00200000;
-	current_par.regs_base_p		= 0x80800000 + dev->base_address[0];
+	if (!fb_find_mode(&init_var, &fb_info, NULL,
+	    NULL, 0, &cyber2000fb_default_mode, 8)) {
+		printk("%s: no valid mode found\n",
+			current_par.dev_name);
+		goto release_smem_resource;
+	}
 
+	init_var.yres_virtual = smem_size * 8 /
+			(init_var.bits_per_pixel * init_var.xres_virtual);
+
+	if (init_var.yres_virtual < init_var.yres)
+		init_var.yres_virtual = init_var.yres;
+    
+	cyber2000fb_hw_init();
 	cyber2000fb_set_var(&init_var, -1, &fb_info);
 
 	h_sync = 1953125000 / init_var.pixclock;
@@ -1026,23 +1443,40 @@
 	v_sync = h_sync / (init_var.yres + init_var.upper_margin +
 		 init_var.lower_margin + init_var.vsync_len);
 
-	printk("Cyber2000: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+	printk("%s: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+		current_par.dev_name,
 		current_par.screen_size >> 10,
 		init_var.xres, init_var.yres,
 		h_sync / 1000, h_sync % 1000, v_sync);
 
-	if (register_framebuffer(&fb_info) < 0)
-		return;
+	if (register_framebuffer(&fb_info) < 0) {
+		err = -EINVAL;
+		goto release_smem;
+	}
+
+	current_par.initialised = 1;
 
 	MOD_INC_USE_COUNT;	/* TODO: This driver cannot be unloaded yet */
-}
+	return 0;
 
+release_smem:
+	iounmap(current_par.screen_base);
+release_smem_resource:
+	iounmap(CyberRegs);
+release_mmio_resource:
 
+	return err;
+}
 
 #ifdef MODULE
-int init_module(void)
+int __init init_module(void)
 {
-	cyber2000fb_init();
+	int ret;
+
+	ret = cyber2000fb_init();
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -1052,6 +1486,12 @@
 	   decremented to zero */
 	unregister_framebuffer(&fb_info);
 	/* TODO: clean up ... */
+
+	iounmap(current_par.screen_base);
+	iounmap(CyberRegs);
+
+	release_mem_region(smem_base, current_par.screen_size);
+	release_mem_region(mmio_base, MMIO_SIZE);
 }
 
 #endif				/* MODULE */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/cyber2000fb.h linux.18p17/drivers/video/cyber2000fb.h
--- linux.vanilla/drivers/video/cyber2000fb.h	Fri Apr 21 12:46:38 2000
+++ linux.18p17/drivers/video/cyber2000fb.h	Fri Sep 15 23:31:13 2000
@@ -5,27 +5,28 @@
  */
 
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
-#define cyber2000_outb(dat,reg)	(CyberRegs[reg] = dat)
-#define cyber2000_outw(dat,reg)	(*(unsigned short *)&CyberRegs[reg] = dat)
-#define cyber2000_outl(dat,reg)	(*(unsigned long *)&CyberRegs[reg] = dat)
-
-#define cyber2000_inb(reg)	(CyberRegs[reg])
-#define cyber2000_inw(reg)	(*(unsigned short *)&CyberRegs[reg])
-#define cyber2000_inl(reg)	(*(unsigned long *)&CyberRegs[reg])
 
-static inline void cyber2000_crtcw(int val, int reg)
+#define cyber2000_outb(dat,reg)	writeb(dat, CyberRegs + reg)
+#define cyber2000_outw(dat,reg)	writew(dat, CyberRegs + reg)
+#define cyber2000_outl(dat,reg)	writel(dat, CyberRegs + reg)
+
+#define cyber2000_inb(reg)	readb(CyberRegs + reg)
+#define cyber2000_inw(reg)	readw(CyberRegs + reg)
+#define cyber2000_inl(reg)	readl(CyberRegs + reg)
+
+static inline void cyber2000_crtcw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3d4);
 	cyber2000_outb(val, 0x3d5);
 }
 
-static inline void cyber2000_grphw(int val, int reg)
+static inline void cyber2000_grphw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3ce);
 	cyber2000_outb(val, 0x3cf);
 }
 
-static inline void cyber2000_attrw(int val, int reg)
+static inline void cyber2000_attrw(int reg, int val)
 {
 	cyber2000_inb(0x3da);
 	cyber2000_outb(reg, 0x3c0);
@@ -33,14 +34,14 @@
 	cyber2000_outb(val, 0x3c0);
 }
 
-static inline void cyber2000_seqw(int val, int reg)
+static inline void cyber2000_seqw(int reg, int val)
 {
 	cyber2000_outb(reg, 0x3c4);
 	cyber2000_outb(val, 0x3c5);
 }
 
 struct cyber2000fb_par {
-	unsigned long	screen_base;
+	char *		screen_base;
 	unsigned long	screen_base_p;
 	unsigned long	regs_base;
 	unsigned long	regs_base_p;
@@ -48,6 +49,10 @@
 	unsigned long	screen_size;
 	unsigned int	palette_size;
 	  signed int	currcon;
+	char		dev_name[32];
+	unsigned int	initialised;
+	unsigned int	dev_id;
+
 	/*
 	 * palette
 	 */
@@ -69,10 +74,161 @@
 	} c_table;
 };
 
-struct res {
-	int	xres;
-	int	yres;
-	unsigned char crtc_regs[18];
-	unsigned char crtc_ofl;
-	unsigned char clk_regs[4];
+#define PIXFORMAT_8BPP		0
+#define PIXFORMAT_16BPP		1
+#define PIXFORMAT_24BPP		2
+
+#define VISUALID_256		1
+#define VISUALID_64K		2
+#define VISUALID_16M		4
+#define VISUALID_32K		6
+
+#define K_CAP_X2_CTL1		0x49
+
+#define CAP_X_START		0x60
+#define CAP_X_END		0x62
+#define CAP_Y_START		0x64
+#define CAP_Y_END		0x66
+#define CAP_DDA_X_INIT		0x68
+#define CAP_DDA_X_INC		0x6a
+#define CAP_DDA_Y_INIT		0x6c
+#define CAP_DDA_Y_INC		0x6e
+
+#define EXT_FIFO_CTL		0x74
+
+#define CAP_PIP_X_START		0x80
+#define CAP_PIP_X_END		0x82
+#define CAP_PIP_Y_START		0x84
+#define CAP_PIP_Y_END		0x86
+
+#define CAP_NEW_CTL1		0x88
+
+#define CAP_NEW_CTL2		0x89
+
+#define CAP_MODE1		0xa4
+#define CAP_MODE1_8BIT			0x01	/* enable 8bit capture mode	*/
+#define CAP_MODE1_CCIR656		0x02	/* CCIR656 mode			*/
+#define CAP_MODE1_IGNOREVGT		0x04	/* ignore VGT			*/
+#define CAP_MODE1_ALTFIFO		0x10	/* use alternate FIFO for capture */
+#define CAP_MODE1_SWAPUV		0x20	/* swap UV bytes		*/
+#define CAP_MODE1_MIRRORY		0x40	/* mirror vertically		*/
+#define CAP_MODE1_MIRRORX		0x80	/* mirror horizontally		*/
+
+#define CAP_MODE2		0xa5
+
+#define Y_TV_CTL		0xae
+
+#define EXT_MEM_START		0xc0		/* ext start address 21 bits */
+#define HOR_PHASE_SHIFT		0xc2		/* high 3 bits */
+#define EXT_SRC_WIDTH		0xc3		/* ext offset phase  10 bits */
+#define EXT_SRC_HEIGHT		0xc4		/* high 6 bits */
+#define EXT_X_START		0xc5		/* ext->screen, 16 bits */
+#define EXT_X_END		0xc7		/* ext->screen, 16 bits */
+#define EXT_Y_START		0xc9		/* ext->screen, 16 bits */
+#define EXT_Y_END		0xcb		/* ext->screen, 16 bits */
+#define EXT_SRC_WIN_WIDTH	0xcd		/* 8 bits */
+#define EXT_COLOUR_COMPARE	0xce		/* 24 bits */
+#define EXT_DDA_X_INIT		0xd1		/* ext->screen 16 bits */
+#define EXT_DDA_X_INC		0xd3		/* ext->screen 16 bits */
+#define EXT_DDA_Y_INIT		0xd5		/* ext->screen 16 bits */
+#define EXT_DDA_Y_INC		0xd7		/* ext->screen 16 bits */
+
+#define VID_FIFO_CTL		0xd9
+
+#define VID_CAP_VFC		0xdb
+#define VID_CAP_VFC_YUV422		0x00	/* formats - does this cause conversion? */
+#define VID_CAP_VFC_RGB555		0x01
+#define VID_CAP_VFC_RGB565		0x02
+#define VID_CAP_VFC_RGB888_24		0x03
+#define VID_CAP_VFC_RGB888_32		0x04
+#define VID_CAP_VFC_DUP_PIX_ZOON	0x08	/* duplicate pixel zoom			*/
+#define VID_CAP_VFC_MOD_3RD_PIX		0x20	/* modify 3rd duplicated pixel		*/
+#define VID_CAP_VFC_DBL_H_PIX		0x40	/* double horiz pixels			*/
+#define VID_CAP_VFC_UV128		0x80	/* UV data offset by 128		*/
+
+#define VID_DISP_CTL1		0xdc
+#define VID_DISP_CTL1_INTRAM		0x01	/* video pixels go to internal RAM	*/
+#define VID_DISP_CTL1_IGNORE_CCOMP	0x02	/* ignore colour compare registers	*/
+#define VID_DISP_CTL1_NOCLIP		0x04	/* do not clip to 16235,16240		*/
+#define VID_DISP_CTL1_UV_AVG		0x08	/* U/V data is averaged			*/
+#define VID_DISP_CTL1_Y128		0x10	/* Y data offset by 128			*/
+#define VID_DISP_CTL1_VINTERPOL_OFF	0x20	/* vertical interpolation off		*/
+#define VID_DISP_CTL1_VID_OUT_WIN_FULL	0x40	/* video out window full		*/
+#define VID_DISP_CTL1_ENABLE_VID_WINDOW	0x80	/* enable video window			*/
+
+#define VID_FIFO_CTL1		0xdd
+
+#define VFAC_CTL1		0xe8
+#define VFAC_CTL1_CAPTURE		0x01	/* capture enable			*/
+#define VFAC_CTL1_VFAC_ENABLE		0x02	/* vfac enable				*/
+#define VFAC_CTL1_FREEZE_CAPTURE	0x04	/* freeze capture			*/
+#define VFAC_CTL1_FREEZE_CAPTURE_SYNC	0x08	/* sync freeze capture			*/
+#define VFAC_CTL1_VALIDFRAME_SRC	0x10	/* select valid frame source		*/
+#define VFAC_CTL1_PHILIPS		0x40	/* select Philips mode			*/
+#define VFAC_CTL1_MODVINTERPOLCLK	0x80	/* modify vertical interpolation clocl	*/
+
+#define VFAC_CTL2		0xe9
+#define VFAC_CTL2_INVERT_VIDDATAVALID	0x01	/* invert video data valid		*/
+#define VFAC_CTL2_INVERT_GRAPHREADY	0x02	/* invert graphic ready output sig	*/
+#define VFAC_CTL2_INVERT_DATACLK	0x04	/* invert data clock signal		*/
+#define VFAC_CTL2_INVERT_HSYNC		0x08	/* invert hsync input			*/
+#define VFAC_CTL2_INVERT_VSYNC		0x10	/* invert vsync input			*/
+#define VFAC_CTL2_INVERT_FRAME		0x20	/* invert frame odd/even input		*/
+#define VFAC_CTL2_INVERT_BLANK		0x40	/* invert blank output			*/
+#define VFAC_CTL2_INVERT_OVSYNC		0x80	/* invert other vsync input		*/
+
+#define VFAC_CTL3		0xea
+
+#define CAP_MEM_START		0xeb		/* 18 bits */
+#define CAP_MAP_WIDTH		0xed		/* high 6 bits */
+#define CAP_PITCH		0xee		/* 8 bits */
+
+#define CAP_CTL_MISC		0xef
+#define CAP_CTL_MISC_HDIV		0x01
+#define CAP_CTL_MISC_HDIV4		0x02
+#define CAP_CTL_MISC_ODDEVEN		0x04
+#define CAP_CTL_MISC_HSYNCDIV2		0x08
+#define CAP_CTL_MISC_SYNCTZHIGH		0x10
+#define CAP_CTL_MISC_SYNCTZOR		0x20
+#define CAP_CTL_MISC_DISPUSED		0x80
+
+#define REG_BANK		0xfa
+#define REG_BANK_Y			0x01
+#define REG_BANK_K			0x05
+
+
+#define CO_CMD_L_PATTERN_FGCOL	0x8000
+#define CO_CMD_L_INC_LEFT	0x0004
+#define CO_CMD_L_INC_UP		0x0002
+
+#define CO_CMD_H_SRC_PIXMAP	0x2000
+#define CO_CMD_H_BLITTER	0x0800
+
+#define CO_REG_CONTROL		0xbf011
+#define CO_REG_SRC_WIDTH	0xbf018
+#define CO_REG_PIX_FORMAT	0xbf01c
+#define CO_REG_FORE_MIX		0xbf048
+#define CO_REG_FOREGROUND	0xbf058
+#define CO_REG_WIDTH		0xbf060
+#define CO_REG_HEIGHT		0xbf062
+#define CO_REG_X_PHASE		0xbf078
+#define CO_REG_CMD_L		0xbf07c
+#define CO_REG_CMD_H		0xbf07e
+#define CO_REG_SRC_PTR		0xbf170
+#define CO_REG_DEST_PTR		0xbf178
+#define CO_REG_DEST_WIDTH	0xbf218
+
+struct cyberpro_info {
+	unsigned char	*regs;
+	char		*fb;
+	char		dev_name[32];
+	unsigned int	fb_size;
 };
+
+/*
+ * Note! Writing to the Cyber20x0 registers from an interrupt
+ * routine is definitely a bad idea atm.
+ */
+int cyber2000fb_attach(struct cyberpro_info *info);
+void cyber2000fb_detach(void);
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/cyberfb.c linux.18p17/drivers/video/cyberfb.c
--- linux.vanilla/drivers/video/cyberfb.c	Fri Apr 21 12:46:38 2000
+++ linux.18p17/drivers/video/cyberfb.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/cyberfb.h linux.18p17/drivers/video/cyberfb.h
--- linux.vanilla/drivers/video/cyberfb.h	Fri Apr 21 12:46:38 2000
+++ linux.18p17/drivers/video/cyberfb.h	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/fbcon-mac.c linux.18p17/drivers/video/fbcon-mac.c
--- linux.vanilla/drivers/video/fbcon-mac.c	Fri Apr 21 12:46:39 2000
+++ linux.18p17/drivers/video/fbcon-mac.c	Fri Oct 13 23:52:23 2000
@@ -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++)
@@ -455,7 +456,7 @@
   u8 *dest, bit;
   u16 *dest16;
   u32 *dest32;
-  u8 pixel;
+  u8 pixel = 0x5a;
 
   switch (p->var.bits_per_pixel) {
   case 1:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/macfb.c linux.18p17/drivers/video/macfb.c
--- linux.vanilla/drivers/video/macfb.c	Fri Apr 21 12:46:39 2000
+++ linux.18p17/drivers/video/macfb.c	Fri Oct 13 23:52:23 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/pm2fb.c linux.18p17/drivers/video/pm2fb.c
--- linux.vanilla/drivers/video/pm2fb.c	Sat Sep  9 18:42:46 2000
+++ linux.18p17/drivers/video/pm2fb.c	Fri Sep  1 13:39:05 2000
@@ -2041,12 +2041,12 @@
 		board_table[i->board].cleanup(i);
 }
 
-__initfunc(void pm2fb_init(void)) {
+int __init pm2fb_init(void) {
 
 	memset(&fb_info, 0, sizeof(fb_info));
 	memcpy(&fb_info.current_par, &pm2fb_options.user_mode, sizeof(fb_info.current_par));
 	if (!pm2fb_conf(&fb_info))
-		return;
+		return 0;
 	pm2fb_reset(&fb_info);
 	fb_info.disp.scrollmode=SCROLL_YNOMOVE;
 	fb_info.gen.parsize=sizeof(struct pm2fb_par);
@@ -2065,7 +2065,7 @@
 	fbgen_install_cmap(0, &fb_info.gen);
 	if (register_framebuffer(&fb_info.gen.info)<0) {
 		printk("pm2fb: unable to register.\n");
-		return;
+		return 0;
 	}
 	printk("fb%d: %s (%s), using %uK of video memory.\n",
 				GET_FB_IDX(fb_info.gen.info.node),
@@ -2073,6 +2073,7 @@
 				permedia2_name,
 				(u32 )(fb_info.regions.fb_size>>10));
 	MOD_INC_USE_COUNT;
+	return 1;
 }
 
 static void __init pm2fb_mode_setup(char* options) {
@@ -2127,7 +2128,8 @@
 
 int init_module(void) {
 	if (mode) pm2fb_mode_setup(mode);
-  	pm2fb_init();
+	if (!pm2fb_init())
+		return -ENXIO;
 }
 
 void cleanup_module(void) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/q40fb.c linux.18p17/drivers/video/q40fb.c
--- linux.vanilla/drivers/video/q40fb.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/drivers/video/q40fb.c	Sat Oct 14 00:00:18 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/retz3fb.c linux.18p17/drivers/video/retz3fb.c
--- linux.vanilla/drivers/video/retz3fb.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/drivers/video/retz3fb.c	Fri Oct 13 23:58:25 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/Config.in linux.18p17/fs/Config.in
--- linux.vanilla/fs/Config.in	Fri Apr 21 12:46:41 2000
+++ linux.18p17/fs/Config.in	Sun Oct 15 21:15:17 2000
@@ -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
@@ -91,6 +92,12 @@
     fi
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS
+  if [ "$CONFIG_SMB_FS" != "n" ]; then
+     bool '   Use a default NLS' CONFIG_SMB_NLS_DEFAULT
+     if [ "$CONFIG_SMB_NLS_DEFAULT" = "y" ]; then
+        string '  Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "cp437"
+     fi
+  fi   
 fi
 if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/Makefile linux.18p17/fs/adfs/Makefile
--- linux.vanilla/fs/adfs/Makefile	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/Makefile	Fri Sep 15 23:24:41 2000
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile.
 
 O_TARGET := adfs.o
-O_OBJS   := dir.o file.o inode.o map.o namei.o super.o
+O_OBJS   := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/adfs.h linux.18p17/fs/adfs/adfs.h
--- linux.vanilla/fs/adfs/adfs.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/adfs/adfs.h	Sun Sep 17 17:49:17 2000
@@ -0,0 +1,137 @@
+/* Internal data structures for ADFS */
+
+#define ADFS_FREE_FRAG		 0
+#define ADFS_BAD_FRAG		 1
+#define ADFS_ROOT_FRAG		 2
+
+#define ADFS_NDA_OWNER_READ	(1 << 0)
+#define ADFS_NDA_OWNER_WRITE	(1 << 1)
+#define ADFS_NDA_LOCKED		(1 << 2)
+#define ADFS_NDA_DIRECTORY	(1 << 3)
+#define ADFS_NDA_EXECUTE	(1 << 4)
+#define ADFS_NDA_PUBLIC_READ	(1 << 5)
+#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
+
+#include "dir_f.h"
+
+/*
+ * Directory handling
+ */
+struct adfs_dir {
+	struct super_block	*sb;
+
+	int			nr_buffers;
+	struct buffer_head	*bh[4];
+	unsigned int		pos;
+	unsigned int		parent_id;
+
+	struct adfs_dirheader	dirhead;
+	union  adfs_dirtail	dirtail;
+};
+
+/*
+ * This is the overall maximum name length
+ */
+#define ADFS_MAX_NAME_LEN	256
+struct object_info {
+	__u32		parent_id;		/* parent object id	*/
+	__u32		file_id;		/* object id		*/
+	__u32		loadaddr;		/* load address		*/
+	__u32		execaddr;		/* execution address	*/
+	__u32		size;			/* size			*/
+	__u8		attr;			/* RISC OS attributes	*/
+	unsigned char	name_len;		/* name length		*/
+	char		name[ADFS_MAX_NAME_LEN];/* file name		*/
+};
+
+struct adfs_dir_ops {
+	int	(*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
+	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
+	int	(*getnext)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
+	void	(*free)(struct adfs_dir *dir);
+};
+
+struct adfs_discmap {
+	struct buffer_head	*dm_bh;
+	__u32			dm_startblk;
+	unsigned int		dm_startbit;
+	unsigned int		dm_endbit;
+};
+
+/* dir stuff */
+
+
+/* Inode stuff */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+int adfs_get_block(struct inode *inode, long block,
+		   struct buffer_head *bh, int create);
+#else
+int adfs_bmap(struct inode *inode, int block);
+#endif
+struct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
+void adfs_read_inode(struct inode *inode);
+void adfs_write_inode(struct inode *inode);
+int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
+
+/* map.c */
+extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset);
+extern unsigned int adfs_map_free(struct super_block *sb);
+
+/* Misc */
+void __adfs_error(struct super_block *sb, const char *function,
+		  const char *fmt, ...);
+#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt)
+
+/* namei.c */
+extern struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry);
+
+/* super.c */
+
+/*
+ * Inodes and file operations
+ */
+
+/* dir_*.c */
+extern struct inode_operations adfs_dir_inode_operations;
+extern struct file_operations adfs_dir_operations;
+extern struct dentry_operations adfs_dentry_operations;
+extern struct adfs_dir_ops adfs_f_dir_ops;
+extern struct adfs_dir_ops adfs_fplus_dir_ops;
+
+extern int adfs_dir_update(struct super_block *sb, struct object_info *obj);
+
+/* file.c */
+extern struct inode_operations adfs_file_inode_operations;
+extern struct file_operations adfs_file_operations;
+
+extern inline __u32 signed_asl(__u32 val, signed int shift)
+{
+	if (shift >= 0)
+		val <<= shift;
+	else
+		val >>= -shift;
+	return val;
+}
+
+/*
+ * Calculate the address of a block in an object given the block offset
+ * and the object identity.
+ *
+ * The root directory ID should always be looked up in the map [3.4]
+ */
+extern inline int
+__adfs_block_map(struct super_block *sb, unsigned int object_id,
+		 unsigned int block)
+{
+	if (object_id & 255) {
+		unsigned int off;
+
+		off = (object_id & 255) - 1;
+		block += off << sb->u.adfs_sb.s_log2sharesize;
+	}
+
+	return adfs_map_lookup(sb, object_id >> 8, block);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/dir.c linux.18p17/fs/adfs/dir.c
--- linux.vanilla/fs/adfs/dir.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/dir.c	Fri Sep 15 23:24:41 2000
@@ -1,347 +1,297 @@
 /*
- *  linux/fs/adfs/dir.c
+ * linux/fs/adfs/dir.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1999-2000 Russell King
+ *
+ * Common directory handling for ADFS
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 
-static ssize_t adfs_dirread (struct file *filp, char *buf,
-			     size_t siz, loff_t *ppos)
-{
-	return -EISDIR;
-}
+#include <asm/spinlock.h>
 
-static int adfs_readdir (struct file *, void *, filldir_t);
-
-static struct file_operations adfs_dir_operations = {
-	NULL,			/* lseek - default */
-	adfs_dirread,		/* read */
-	NULL,			/* write - bad */
-	adfs_readdir,		/* readdir */
-	NULL,			/* select - default */
-	NULL,			/* ioctl */
-	NULL,			/* mmap */
-	NULL,			/* no special open code */
-	NULL,			/* flush */
-	NULL,			/* no special release code */
-	file_fsync,		/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* check_media_change */
-	NULL			/* revalidate */
-};
+#include "adfs.h"
 
 /*
- * directories can handle most operations...
+ * For future.  This should probably be per-directory.
  */
-struct inode_operations adfs_dir_inode_operations = {
-	&adfs_dir_operations,	/* default directory file-ops */
-	NULL,			/* create */
-	adfs_lookup,		/* lookup */
-	NULL,			/* link */
-	NULL,			/* unlink */
-	NULL,			/* symlink */
-	NULL,			/* mkdir */
-	NULL,			/* rmdir */
-	NULL,			/* mknod */
-	NULL,			/* rename */
-	NULL,			/* read link */
-	NULL,			/* follow link */
-	NULL,			/* read page */
-	NULL,			/* write page */
-	NULL,			/* bmap */
-	NULL,			/* truncate */
-	NULL,			/* permission */
-	NULL			/* smap */
-};
+static rwlock_t adfs_dir_lock;
 
-unsigned int adfs_val (unsigned char *p, int len)
+static int
+adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	unsigned int val = 0;
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct object_info obj;
+	struct adfs_dir dir;
+	int ret = 0;
+
+	if (filp->f_pos >> 32)
+		goto out;
+
+	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	if (ret)
+		goto out;
+
+	switch ((unsigned long)filp->f_pos) {
+	case 0:
+		if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
+			goto free_out;
+		filp->f_pos += 1;
+
+	case 1:
+		if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0)
+			goto free_out;
+		filp->f_pos += 1;
 
-	switch (len) {
-	case 4:
-		val |= p[3] << 24;
-	case 3:
-		val |= p[2] << 16;
-	case 2:
-		val |= p[1] << 8;
 	default:
-		val |= p[0];
+		break;
 	}
-	return val;
-}
-
-static unsigned int adfs_filetype (unsigned int load)
-{
-	if ((load & 0xfff00000) != 0xfff00000)
-		return (unsigned int) -1;
-	return (load >> 8) & 0xfff;
-}
 
-static unsigned int adfs_time (unsigned int load, unsigned int exec)
-{
-	unsigned int high, low;
+	read_lock(&adfs_dir_lock);
 
-	/* Check for unstamped files.  */
-	if ((load & 0xfff00000) != 0xfff00000)
-		return 0;
-
-	high = ((load << 24) | (exec >> 8));
-	low  =  exec & 255;
-
-	/* Files dated pre 1970.  */
-	if (high < 0x336e996a)
-		return 0;
+	ret = ops->setpos(&dir, filp->f_pos - 2);
+	if (ret)
+		goto unlock_out;
+	while (ops->getnext(&dir, &obj) == 0) {
+		if (filldir(dirent, obj.name, obj.name_len,
+			    filp->f_pos, obj.file_id) < 0)
+			goto unlock_out;
+		filp->f_pos += 1;
+	}
 
-	high -= 0x336e996a;
+unlock_out:
+	read_unlock(&adfs_dir_lock);
 
-	/* Files dated post 2038 ish.  */
-	if (high > 0x31ffffff)
-		return 0x7fffffff;
-
-	/* 65537 = h256,l1
-	 * (h256 % 100) = 56         h256 / 100 = 2
-	 *    56 << 8 = 14336           2 * 256 = 512
-	 *      + l1 = 14337
-	 *        / 100 = 143
-	 *          + 512 = 655
-	 */
-	return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
-}
+free_out:
+	ops->free(&dir);
 
-int adfs_readname (char *buf, char *ptr, int maxlen)
-{
-	int size = 0;
-	while (*ptr >= ' ' && maxlen--) {
-		switch (*ptr) {
-		case '/':
-			*buf++ = '.';
-			break;
-		default:
-			*buf++ = *ptr;
-			break;
-		}
-		ptr++;
-		size ++;
-	}
-	*buf = '\0';
-	return size;
+out:
+	return ret;
 }
 
-int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
+int
+adfs_dir_update(struct super_block *sb, struct object_info *obj)
 {
-	struct super_block *sb;
-	int i, size;
+	int ret = -EINVAL;
+#if 0
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct adfs_dir dir;
 
-	sb = inode->i_sb;
+	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
+		 obj->file_id, obj->parent_id);
 
-	size = 2048 >> sb->s_blocksize_bits;
-
-	for (i = 0; i < size; i++) {
-		int block;
-
-		block = adfs_parent_bmap (inode, i);
-		if (block)
-			bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
-		else
-			adfs_error (sb, "adfs_dir_read_parent",
-				    "directory %lu with a hole at offset %d", inode->i_ino, i);
-		if (!block || !bhp[i]) {
-			int j;
-			for (j = i - 1; j >= 0; j--)
-				brelse (bhp[j]);
-			return 0;
-		}
+	if (!ops->update) {
+		ret = -EINVAL;
+		goto out;
 	}
-	return i;
+
+	ret = ops->read(sb, obj->parent_id, 0, &dir);
+	if (ret)
+		goto out;
+
+	write_lock(&adfs_dir_lock);
+	ret = ops->update(&dir, obj);
+	write_unlock(&adfs_dir_lock);
+
+	ops->free(&dir);
+out:
+#endif
+	return ret;
 }
 
-int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
+static int
+adfs_match(struct qstr *name, struct object_info *obj)
 {
-	struct super_block *sb;
-	int i, size;
+	int i;
 
-	if (!inode || !S_ISDIR(inode->i_mode))
+	if (name->len != obj->name_len)
 		return 0;
 
-	sb = inode->i_sb;
+	for (i = 0; i < name->len; i++) {
+		char c1, c2;
 
-	size = inode->i_size >> sb->s_blocksize_bits;
+		c1 = name->name[i];
+		c2 = obj->name[i];
 
-	for (i = 0; i < size; i++) {
-		int block;
+		if (c1 >= 'A' && c1 <= 'Z')
+			c1 += 'a' - 'A';
+		if (c2 >= 'A' && c2 <= 'Z')
+			c2 += 'a' - 'A';
 
-		block = adfs_bmap (inode, i);
-		if (block)
-			bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
-		else
-			adfs_error (sb, "adfs_dir_read",
-				    "directory %lX,%lX with a hole at offset %d",
-				    inode->i_ino, inode->u.adfs_i.file_id, i);
-		if (!block || !bhp[i]) {
-			int j;
-			for (j = i - 1; j >= 0; j--)
-				brelse (bhp[j]);
+		if (c1 != c2)
 			return 0;
-		}
 	}
-	return i;
+	return 1;
 }
 
-int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
+static int
+adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
 {
-	struct adfs_dirheader dh;
-	union adfs_dirtail dt;
+	struct super_block *sb = inode->i_sb;
+	struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+	struct adfs_dir dir;
+	int ret;
+
+	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	if (ret)
+		goto out;
+
+	if (inode->u.adfs_i.parent_id != dir.parent_id) {
+		adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
+			   inode->u.adfs_i.parent_id, dir.parent_id);
+		ret = -EIO;
+		goto free_out;
+	}
 
-	memcpy (&dh, bhp[0]->b_data, sizeof (dh));
-	memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
+	obj->parent_id = inode->i_ino;
 
-	if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
-	    (memcmp (&dh.startname, "Nick", 4) &&
-	     memcmp (&dh.startname, "Hugo", 4))) {
-		adfs_error (inode->i_sb, "adfs_check_dir",
-			    "corrupted directory inode %lX,%lX",
-			    inode->i_ino, inode->u.adfs_i.file_id);
-		return 1;
+	/*
+	 * '.' is handled by reserved_lookup() in fs/namei.c
+	 */
+	if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
+		/*
+		 * Currently unable to fill in the rest of 'obj',
+		 * but this is better than nothing.  We need to
+		 * ascend one level to find it's parent.
+		 */
+		obj->name_len = 0;
+		obj->file_id  = obj->parent_id;
+		goto free_out;
 	}
-	if (dtp)
-		*dtp = dt;
-	return 0;
-}
 
-void adfs_dir_free (struct buffer_head **bhp, int buffers)
-{
-	int i;
+	read_lock(&adfs_dir_lock);
+
+	ret = ops->setpos(&dir, 0);
+	if (ret)
+		goto unlock_out;
+
+	ret = -ENOENT;
+	while (ops->getnext(&dir, obj) == 0) {
+		if (adfs_match(name, obj)) {
+			ret = 0;
+			break;
+		}
+	}
+
+unlock_out:
+	read_unlock(&adfs_dir_lock);
 
-	for (i = buffers - 1; i >= 0; i--)
-		brelse (bhp[i]);
+free_out:
+	ops->free(&dir);
+out:
+	return ret;
 }
 
-/* convert a disk-based directory entry to a Linux ADFS directory entry */
-static inline void
-adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
+static ssize_t
+adfs_dir_no_read(struct file *filp, char *buf, size_t siz, loff_t *ppos)
 {
-	ide->name_len =	adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
-	ide->file_id  = adfs_val(de->dirinddiscadd, 3);
-	ide->size     = adfs_val(de->dirlen, 4);
-	ide->mode     = de->newdiratts;
-	ide->mtime    = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
-	ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
+	return -EISDIR;
 }
 
-int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
-		  int buffers, int pos, unsigned long parent_object_id,
-		  struct adfs_idir_entry *ide)
-{
-	struct adfs_direntry de;
-	int thissize, buffer, offset;
+struct file_operations adfs_dir_operations = {
+	read:		adfs_dir_no_read,
+	readdir:	adfs_readdir,
+	fsync:		file_fsync,
+};
 
-	offset = pos & (sb->s_blocksize - 1);
-	buffer = pos >> sb->s_blocksize_bits;
+static int
+adfs_hash(struct dentry *parent, struct qstr *qstr)
+{
+	const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen;
+	const unsigned char *name;
+	unsigned long hash;
+	int i;
 
-	if (buffer > buffers)
+	if (qstr->len < name_len)
 		return 0;
 
-	thissize = sb->s_blocksize - offset;
-	if (thissize > 26)
-		thissize = 26;
-
-	memcpy (&de, bhp[buffer]->b_data + offset, thissize);
-	if (thissize != 26)
-		memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
+	/*
+	 * Truncate the name in place, avoids
+	 * having to define a compare function.
+	 */
+	qstr->len = i = name_len;
+	name = qstr->name;
+	hash = init_name_hash();
+	while (i--) {
+		char c;
+
+		c = *name++;
+		if (c >= 'A' && c <= 'Z')
+			c += 'a' - 'A';
 
-	if (!de.dirobname[0])
-		return 0;
+		hash = partial_name_hash(c, hash);
+	}
+	qstr->hash = end_name_hash(hash);
 
-	ide->inode_no = adfs_inode_generate (parent_object_id, pos);
-	adfs_dirent_to_idirent(ide, &de);
-	return 1;
+	return 0;
 }
 
-int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
-			 int buffers, unsigned int pos,
-			 struct adfs_idir_entry *ide)
+/*
+ * Compare two names, taking note of the name length
+ * requirements of the underlying filesystem.
+ */
+static int
+adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
 {
-	struct adfs_direntry de;
-	int offset, buffer, thissize;
-
-	offset = pos & (sb->s_blocksize - 1);
-	buffer = pos >> sb->s_blocksize_bits;
-
-	if (buffer > buffers)
-		return 0;
-
-	thissize = sb->s_blocksize - offset;
-	if (thissize > 26)
-		thissize = 26;
-
-	memcpy (&de, bhp[buffer]->b_data + offset, thissize);
-	if (thissize != 26)
-		memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
-
-	if (!de.dirobname[0])
-		return 0;
+	int i;
 
-	adfs_dirent_to_idirent(ide, &de);
-	return 1;
-}	
+	if (entry->len != name->len)
+		return 1;
 
-static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
-{
-	struct inode *inode = filp->f_dentry->d_inode;
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	union  adfs_dirtail dt;
-	unsigned long parent_object_id, dir_object_id;
-	int buffers, pos;
-
-	sb = inode->i_sb;
-
-	if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
-		return -ENOENT;
-
-	if (!(buffers = adfs_dir_read (inode, bh))) {
-		adfs_error (sb, "adfs_readdir", "unable to read directory");
-		return -EINVAL;
-	}
+	for (i = 0; i < name->len; i++) {
+		char a, b;
 
-	if (adfs_dir_check (inode, bh, buffers, &dt)) {
-		adfs_dir_free (bh, buffers);
-		return -ENOENT;
-	}
+		a = entry->name[i];
+		b = name->name[i];
 
-	parent_object_id = adfs_val (dt.new.dirparent, 3);
-	dir_object_id = adfs_inode_objid (inode);
+		if (a >= 'A' && a <= 'Z')
+			a += 'a' - 'A';
+		if (b >= 'A' && b <= 'Z')
+			b += 'a' - 'A';
 
-	if (filp->f_pos < 2) {
-		if (filp->f_pos < 1) {
-			if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
-				return 0;
-			filp->f_pos ++;
-		}
-		if (filldir (dirent, "..", 2, 1,
-			     adfs_inode_generate (parent_object_id, 0)) < 0)
-			return 0;
-		filp->f_pos ++;
+		if (a != b)
+			return 1;
 	}
+	return 0;
+}
 
-	pos = 5 + (filp->f_pos - 2) * 26;
-	while (filp->f_pos < 79) {
-		struct adfs_idir_entry ide;
-
-		if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
-			break;
+struct dentry_operations adfs_dentry_operations = {
+	d_hash:		adfs_hash,
+	d_compare:	adfs_compare,
+};
 
-		if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
-			return 0;
-		filp->f_pos ++;
-		pos += 26;
+struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = NULL;
+	struct object_info obj;
+	int error;
+
+	dentry->d_op = &adfs_dentry_operations;	
+	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
+	if (error == 0) {
+		error = -EACCES;
+		/*
+		 * This only returns NULL if get_empty_inode
+		 * fails.
+		 */
+		inode = adfs_iget(dir->i_sb, &obj);
+		if (inode)
+			error = 0;
 	}
-	adfs_dir_free (bh, buffers);
-	return 0;
+	d_add(dentry, inode);
+	return ERR_PTR(error);
 }
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations adfs_dir_inode_operations = {
+	default_file_ops:	&adfs_dir_operations,
+	lookup:		adfs_lookup,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/dir_f.c linux.18p17/fs/adfs/dir_f.c
--- linux.vanilla/fs/adfs/dir_f.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/adfs/dir_f.c	Fri Sep 15 23:24:41 2000
@@ -0,0 +1,475 @@
+/*
+ *  linux/fs/adfs/dir_f.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * E and F format directory handling
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+#include <asm/spinlock.h>
+
+#include "adfs.h"
+#include "dir_f.h"
+
+static void adfs_f_free(struct adfs_dir *dir);
+
+/*
+ * Read an (unaligned) value of length 1..4 bytes
+ */
+static inline unsigned int adfs_readval(unsigned char *p, int len)
+{
+	unsigned int val = 0;
+
+	switch (len) {
+	case 4:		val |= p[3] << 24;
+	case 3:		val |= p[2] << 16;
+	case 2:		val |= p[1] << 8;
+	default:	val |= p[0];
+	}
+	return val;
+}
+
+static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
+{
+	switch (len) {
+	case 4:		p[3] = val >> 24;
+	case 3:		p[2] = val >> 16;
+	case 2:		p[1] = val >> 8;
+	default:	p[0] = val;
+	}
+}
+
+static inline int adfs_readname(char *buf, char *ptr, int maxlen)
+{
+	char *old_buf = buf;
+
+	while (*ptr >= ' ' && maxlen--) {
+		if (*ptr == '/')
+			*buf++ = '.';
+		else
+			*buf++ = *ptr;
+		ptr++;
+	}
+	*buf = '\0';
+
+	return buf - old_buf;
+}
+
+static inline void adfs_writename(char *to, char *from, int maxlen)
+{
+	int i;
+
+	for (i = 0; i < maxlen; i++) {
+		if (from[i] == '\0')
+			break;
+		if (from[i] == '.')
+			to[i] = '/';
+		else
+			to[i] = from[i];
+	}
+
+	for (; i < maxlen; i++)
+		to[i] = '\0';
+}
+
+#define ror13(v) ((v >> 13) | (v << 19))
+
+#define dir_u8(idx)				\
+	({ int _buf = idx >> blocksize_bits;	\
+	   int _off = idx - (_buf << blocksize_bits);\
+	  *(u8 *)(bh[_buf]->b_data + _off);	\
+	})
+
+#define dir_u32(idx)				\
+	({ int _buf = idx >> blocksize_bits;	\
+	   int _off = idx - (_buf << blocksize_bits);\
+	  *(u32 *)(bh[_buf]->b_data + _off);	\
+	})
+
+#define bufoff(_bh,_idx)			\
+	({ int _buf = _idx >> blocksize_bits;	\
+	   int _off = _idx - (_buf << blocksize_bits);\
+	  (u8 *)(_bh[_buf]->b_data + _off);	\
+	})
+
+/*
+ * There are some algorithms that are nice in
+ * assembler, but a bitch in C...  This is one
+ * of them.
+ */
+static u8
+adfs_dir_checkbyte(const struct adfs_dir *dir)
+{
+	struct buffer_head * const *bh = dir->bh;
+	const int blocksize_bits = dir->sb->s_blocksize_bits;
+	union { u32 *ptr32; u8 *ptr8; } ptr, end;
+	u32 dircheck = 0;
+	int last = 5 - 26;
+	int i = 0;
+
+	/*
+	 * Accumulate each word up to the last whole
+	 * word of the last directory entry.  This
+	 * can spread across several buffer heads.
+	 */
+	do {
+		last += 26;
+		do {
+			dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck);
+
+			i += sizeof(u32);
+		} while (i < (last & ~3));
+	} while (dir_u8(last) != 0);
+
+	/*
+	 * Accumulate the last few bytes.  These
+	 * bytes will be within the same bh.
+	 */
+	if (i != last) {
+		ptr.ptr8 = bufoff(bh, i);
+		end.ptr8 = ptr.ptr8 + last - i;
+
+		do
+			dircheck = *ptr.ptr8++ ^ ror13(dircheck);
+		while (ptr.ptr8 < end.ptr8);
+	}
+
+	/*
+	 * The directory tail is in the final bh
+	 * Note that contary to the RISC OS PRMs,
+	 * the first few bytes are NOT included
+	 * in the check.  All bytes are in the
+	 * same bh.
+	 */
+	ptr.ptr8 = bufoff(bh, 2008);
+	end.ptr8 = ptr.ptr8 + 36;
+
+	do {
+		unsigned int v = *ptr.ptr32++;
+		dircheck = cpu_to_le32(v) ^ ror13(dircheck);
+	} while (ptr.ptr32 < end.ptr32);
+
+	return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
+}
+
+/*
+ * Read and check that a directory is valid
+ */
+int
+adfs_dir_read(struct super_block *sb, unsigned long object_id,
+	      unsigned int size, struct adfs_dir *dir)
+{
+	const unsigned int blocksize_bits = sb->s_blocksize_bits;
+	int blk = 0;
+
+	/*
+	 * Directories which are not a multiple of 2048 bytes
+	 * are considered bad v2 [3.6]
+	 */
+	if (size & 2047)
+		goto bad_dir;
+
+	size >>= blocksize_bits;
+
+	dir->nr_buffers = 0;
+	dir->sb = sb;
+
+	for (blk = 0; blk < size; blk++) {
+		int phys;
+
+		phys = __adfs_block_map(sb, object_id, blk);
+		if (!phys) {
+			adfs_error(sb, "dir object %lX has a hole at offset %d",
+				   object_id, blk);
+			goto release_buffers;
+		}
+
+		dir->bh[blk] = bread(sb->s_dev, phys, sb->s_blocksize);
+		if (!dir->bh[blk])
+			goto release_buffers;
+	}
+
+	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+		goto bad_dir;
+
+	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+	    memcmp(&dir->dirhead.startname, "Hugo", 4))
+		goto bad_dir;
+
+	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+		goto bad_dir;
+
+	dir->nr_buffers = blk;
+
+	return 0;
+
+bad_dir:
+	adfs_error(sb, "corrupted directory fragment %lX",
+		   object_id);
+release_buffers:
+	for (blk -= 1; blk >= 0; blk -= 1)
+		brelse(dir->bh[blk]);
+
+	dir->sb = NULL;
+
+	return -EIO;
+}
+
+/*
+ * convert a disk-based directory entry to a Linux ADFS directory entry
+ */
+static inline void
+adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
+{
+	obj->name_len =	adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
+	obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
+	obj->loadaddr = adfs_readval(de->dirload, 4);
+	obj->execaddr = adfs_readval(de->direxec, 4);
+	obj->size     = adfs_readval(de->dirlen,  4);
+	obj->attr     = de->newdiratts;
+}
+
+/*
+ * convert a Linux ADFS directory entry to a disk-based directory entry
+ */
+static inline void
+adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
+{
+	adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
+	adfs_writeval(de->dirload, 4, obj->loadaddr);
+	adfs_writeval(de->direxec, 4, obj->execaddr);
+	adfs_writeval(de->dirlen,  4, obj->size);
+	de->newdiratts = obj->attr;
+}
+
+/*
+ * get a directory entry.  Note that the caller is responsible
+ * for holding the relevent locks.
+ */
+int
+__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	struct adfs_direntry de;
+	int thissize, buffer, offset;
+
+	buffer = pos >> sb->s_blocksize_bits;
+
+	if (buffer > dir->nr_buffers)
+		return -EINVAL;
+
+	offset = pos & (sb->s_blocksize - 1);
+	thissize = sb->s_blocksize - offset;
+	if (thissize > 26)
+		thissize = 26;
+
+	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+	if (thissize != 26)
+		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+		       26 - thissize);
+
+	if (!de.dirobname[0])
+		return -ENOENT;
+
+	adfs_dir2obj(obj, &de);
+
+	return 0;
+}
+
+int
+__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	struct adfs_direntry de;
+	int thissize, buffer, offset;
+
+	buffer = pos >> sb->s_blocksize_bits;
+
+	if (buffer > dir->nr_buffers)
+		return -EINVAL;
+
+	offset = pos & (sb->s_blocksize - 1);
+	thissize = sb->s_blocksize - offset;
+	if (thissize > 26)
+		thissize = 26;
+
+	/*
+	 * Get the entry in total
+	 */
+	memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+	if (thissize != 26)
+		memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+		       26 - thissize);
+
+	/*
+	 * update it
+	 */
+	adfs_obj2dir(&de, obj);
+
+	/*
+	 * Put the new entry back
+	 */
+	memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
+	if (thissize != 26)
+		memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
+		       26 - thissize);
+
+	return 0;
+}
+
+/*
+ * the caller is responsible for holding the necessary
+ * locks.
+ */
+static int
+adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
+{
+	int pos, ret;
+
+	ret = -ENOENT;
+
+	for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
+		struct object_info obj;
+
+		if (!__adfs_dir_get(dir, pos, &obj))
+			break;
+
+		if (obj.file_id == object_id) {
+			ret = pos;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int
+adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+	int ret;
+
+	if (sz != ADFS_NEWDIR_SIZE)
+		return -EIO;
+
+	ret = adfs_dir_read(sb, id, sz, dir);
+	if (ret)
+		adfs_error(sb, "unable to read directory");
+	else
+		dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
+
+	return ret;
+}
+
+static int
+adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+	if (fpos >= ADFS_NUM_DIR_ENTRIES)
+		return -ENOENT;
+
+	dir->pos = 5 + fpos * 26;
+	return 0;
+}
+
+static int
+adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+	unsigned int ret;
+
+	ret = __adfs_dir_get(dir, dir->pos, obj);
+	if (ret == 0)
+		dir->pos += 26;
+
+	return ret;
+}
+
+static int
+adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
+{
+	struct super_block *sb = dir->sb;
+	int ret, i;
+
+	ret = adfs_dir_find_entry(dir, obj->file_id);
+	if (ret < 0) {
+		adfs_error(dir->sb, "unable to locate entry to update");
+		goto out;
+	}
+
+	__adfs_dir_put(dir, ret, obj);
+ 
+	/*
+	 * Increment directory sequence number
+	 */
+	dir->bh[0]->b_data[0] += 1;
+	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
+
+	ret = adfs_dir_checkbyte(dir);
+	/*
+	 * Update directory check byte
+	 */
+	dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
+
+#if 1
+	{
+	const unsigned int blocksize_bits = sb->s_blocksize_bits;
+
+	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+	if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+	    memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+		goto bad_dir;
+
+	if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+	    memcmp(&dir->dirhead.startname, "Hugo", 4))
+		goto bad_dir;
+
+	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+		goto bad_dir;
+	}
+#endif
+	for (i = dir->nr_buffers - 1; i >= 0; i--)
+		mark_buffer_dirty(dir->bh[i], 1);
+
+	ret = 0;
+out:
+	return ret;
+#if 1
+bad_dir:
+	adfs_error(dir->sb, "whoops!  I broke a directory!");
+	return -EIO;
+#endif
+}
+
+static void
+adfs_f_free(struct adfs_dir *dir)
+{
+	int i;
+
+	for (i = dir->nr_buffers - 1; i >= 0; i--) {
+		brelse(dir->bh[i]);
+		dir->bh[i] = NULL;
+	}
+
+	dir->nr_buffers = 0;
+	dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_f_dir_ops = {
+	adfs_f_read,
+	adfs_f_setpos,
+	adfs_f_getnext,
+	adfs_f_update,
+	NULL,
+	NULL,
+	adfs_f_free
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/dir_f.h linux.18p17/fs/adfs/dir_f.h
--- linux.vanilla/fs/adfs/dir_f.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/adfs/dir_f.h	Fri Sep 15 23:24:41 2000
@@ -0,0 +1,61 @@
+/*
+ * linux/fs/adfs/dir_f.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * Structures of directories on the F format disk
+ */
+#ifndef ADFS_DIR_F_H
+#define ADFS_DIR_F_H
+
+/*
+ * Directory header
+ */
+struct adfs_dirheader {
+	unsigned char startmasseq;
+	unsigned char startname[4];
+};
+
+#define ADFS_NEWDIR_SIZE	2048
+#define ADFS_NUM_DIR_ENTRIES	77
+
+/*
+ * Directory entries
+ */
+struct adfs_direntry {
+#define ADFS_F_NAME_LEN 10
+	char dirobname[ADFS_F_NAME_LEN];
+	__u8 dirload[4];
+	__u8 direxec[4];
+	__u8 dirlen[4];
+	__u8 dirinddiscadd[3];
+	__u8 newdiratts;
+};
+
+/*
+ * Directory tail
+ */
+union adfs_dirtail {
+	struct {
+		unsigned char dirlastmask;
+		char dirname[10];
+		unsigned char dirparent[3];
+		char dirtitle[19];
+		unsigned char reserved[14];
+		unsigned char endmasseq;
+		unsigned char endname[4];
+		unsigned char dircheckbyte;
+	} old;
+	struct {
+		unsigned char dirlastmask;
+		unsigned char reserved[2];
+		unsigned char dirparent[3];
+		char dirtitle[19];
+		char dirname[10];
+		unsigned char endmasseq;
+		unsigned char endname[4];
+		unsigned char dircheckbyte;
+	} new;
+};
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/dir_fplus.c linux.18p17/fs/adfs/dir_fplus.c
--- linux.vanilla/fs/adfs/dir_fplus.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/adfs/dir_fplus.c	Fri Sep 15 23:24:41 2000
@@ -0,0 +1,178 @@
+/*
+ *  linux/fs/adfs/dir_fplus.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+#include <asm/spinlock.h>
+
+#include "adfs.h"
+#include "dir_fplus.h"
+
+static int
+adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+	struct adfs_bigdirheader *h;
+	struct adfs_bigdirtail *t;
+	unsigned long block;
+	unsigned int blk, size;
+	int i, ret = -EIO;
+
+	dir->nr_buffers = 0;
+
+	block = __adfs_block_map(sb, id, 0);
+	if (!block) {
+		adfs_error(sb, "dir object %X has a hole at offset 0", id);
+		goto out;
+	}
+
+	dir->bh[0] = bread(sb->s_dev, block, sb->s_blocksize);
+	if (!dir->bh[0])
+		goto out;
+	dir->nr_buffers += 1;
+
+	h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	size = le32_to_cpu(h->bigdirsize);
+	if (size != sz) {
+		printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n"
+				" does not match directory size\n");
+	}
+
+	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
+	    h->bigdirversion[2] != 0 || size & 2047 ||
+	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME))
+		goto out;
+
+	size >>= sb->s_blocksize_bits;
+	for (blk = 1; blk < size; blk++) {
+		block = __adfs_block_map(sb, id, blk);
+		if (!block) {
+			adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
+			goto out;
+		}
+
+		dir->bh[blk] = bread(sb->s_dev, block, sb->s_blocksize);
+		if (!dir->bh[blk])
+			goto out;
+		dir->nr_buffers = blk;
+	}
+
+	t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8));
+
+	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
+	    t->bigdirendmasseq != h->startmasseq ||
+	    t->reserved[0] != 0 || t->reserved[1] != 0)
+		goto out;
+
+	dir->parent_id = le32_to_cpu(h->bigdirparent);
+	dir->sb = sb;
+	return 0;
+out:
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bh[i]);
+	dir->sb = NULL;
+	return ret;
+}
+
+static int
+adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	int ret = -ENOENT;
+
+	if (fpos <= le32_to_cpu(h->bigdirentries)) {
+		dir->pos = fpos;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void
+dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
+{
+	struct super_block *sb = dir->sb;
+	unsigned int buffer, partial, remainder;
+
+	buffer = offset >> sb->s_blocksize_bits;
+	offset &= sb->s_blocksize - 1;
+
+	partial = sb->s_blocksize - offset;
+
+	if (partial >= len)
+		memcpy(to, dir->bh[buffer]->b_data + offset, len);
+	else {
+		char *c = (char *)to;
+
+		remainder = len - partial;
+
+		memcpy(c, dir->bh[buffer]->b_data + offset, partial);
+		memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder);
+	}
+}
+
+static int
+adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+	struct adfs_bigdirentry bde;
+	unsigned int offset;
+	int i, ret = -ENOENT;
+
+	if (dir->pos >= le32_to_cpu(h->bigdirentries))
+		goto out;
+
+	offset = offsetof(struct adfs_bigdirheader, bigdirname);
+	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+	offset += dir->pos * sizeof(struct adfs_bigdirentry);
+
+	dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
+
+	obj->loadaddr = le32_to_cpu(bde.bigdirload);
+	obj->execaddr = le32_to_cpu(bde.bigdirexec);
+	obj->size     = le32_to_cpu(bde.bigdirlen);
+	obj->file_id  = le32_to_cpu(bde.bigdirindaddr);
+	obj->attr     = le32_to_cpu(bde.bigdirattr);
+	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
+
+	offset = offsetof(struct adfs_bigdirheader, bigdirname);
+	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+	offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
+	offset += le32_to_cpu(bde.bigdirobnameptr);
+
+	dir_memcpy(dir, offset, obj->name, obj->name_len);
+	for (i = 0; i < obj->name_len; i++)
+		if (obj->name[i] == '/')
+			obj->name[i] = '.';
+
+	dir->pos += 1;
+	ret = 0;
+out:
+	return ret;
+}
+
+static void
+adfs_fplus_free(struct adfs_dir *dir)
+{
+	int i;
+
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bh[i]);
+	dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_fplus_dir_ops = {
+	adfs_fplus_read,
+	adfs_fplus_setpos,
+	adfs_fplus_getnext,
+	NULL,
+	NULL,
+	NULL,
+	adfs_fplus_free
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/dir_fplus.h linux.18p17/fs/adfs/dir_fplus.h
--- linux.vanilla/fs/adfs/dir_fplus.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/adfs/dir_fplus.h	Fri Sep 15 23:24:41 2000
@@ -0,0 +1,41 @@
+/*
+ * linux/fs/adfs/dir_fplus.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * Structures of directories on the F+ format disk
+ */
+
+#define ADFS_FPLUS_NAME_LEN	255
+
+#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24)
+#define BIGDIRENDNAME	('o' | 'v' << 8 | 'e' << 16 | 'n' << 24)
+
+struct adfs_bigdirheader {
+	__u8	startmasseq;
+	__u8	bigdirversion[3];
+	__u32	bigdirstartname;
+	__u32	bigdirnamelen;
+	__u32	bigdirsize;
+	__u32	bigdirentries;
+	__u32	bigdirnamesize;
+	__u32	bigdirparent;
+	char	bigdirname[1];
+};
+
+struct adfs_bigdirentry {
+	__u32	bigdirload;
+	__u32	bigdirexec;
+	__u32	bigdirlen;
+	__u32	bigdirindaddr;
+	__u32	bigdirattr;
+	__u32	bigdirobnamelen;
+	__u32	bigdirobnameptr;
+};
+
+struct adfs_bigdirtail {
+	__u32	bigdirendname;
+	__u8	bigdirendmasseq;
+	__u8	reserved[2];
+	__u8	bigdircheckbyte;
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/file.c linux.18p17/fs/adfs/file.c
--- linux.vanilla/fs/adfs/file.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/file.c	Fri Sep 15 23:24:41 2000
@@ -1,7 +1,7 @@
 /*
  *  linux/fs/adfs/file.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  * from:
  *
  *  linux/fs/ext2/file.c
@@ -19,52 +19,23 @@
  *
  *  adfs regular file handling primitives           
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
 #include <linux/fcntl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 
-/*
- * We have mostly NULLs here: the current defaults are OK for
- * the adfs filesystem.
- */
-static struct file_operations adfs_file_operations = {
-	NULL,			/* lseek - default	*/
-	generic_file_read,	/* read			*/
-	NULL,			/* write		*/
-	NULL,			/* readdir - bad	*/
-	NULL,			/* select - default	*/
-	NULL,			/* ioctl		*/
-	generic_file_mmap,	/* mmap			*/
-	NULL,			/* open - not special	*/
-	NULL,			/* flush		*/
-	NULL,			/* release		*/
-	file_fsync,		/* fsync		*/
-	NULL,			/* fasync		*/
-	NULL,			/* check_media_change	*/
-	NULL			/* revalidate		*/
+#include "adfs.h"
+
+struct file_operations adfs_file_operations = {
+	read:		generic_file_read,
+	mmap:		generic_file_mmap,
+	fsync:		file_fsync,
 };
 
 struct inode_operations adfs_file_inode_operations = {
-	&adfs_file_operations,	/* default file operations	*/
-	NULL,			/* create			*/
-	NULL,			/* lookup			*/
-	NULL,			/* link				*/
-	NULL,			/* unlink			*/
-	NULL,			/* symlink			*/
-	NULL,			/* mkdir			*/
-	NULL,			/* rmdir			*/
-	NULL,			/* mknod			*/
-	NULL,			/* rename			*/
-	NULL,			/* readlink			*/
-	NULL,			/* follow_link			*/
-	generic_readpage,	/* readpage			*/
-	NULL,			/* writepage			*/
-	adfs_bmap,		/* bmap				*/
-	NULL,			/* truncate			*/
-	NULL,			/* permission			*/
-	NULL			/* smap				*/
+	default_file_ops:	&adfs_file_operations,
+	readpage:	generic_readpage,
+	bmap:		adfs_bmap,
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/inode.c linux.18p17/fs/adfs/inode.c
--- linux.vanilla/fs/adfs/inode.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/inode.c	Fri Sep 15 23:24:41 2000
@@ -1,9 +1,9 @@
 /*
  *  linux/fs/adfs/inode.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
@@ -13,210 +13,310 @@
 #include <linux/locks.h>
 #include <linux/mm.h>
 
+#include "adfs.h"
+
 /*
- * Old Inode numbers:
- *  bit 30 - 16		FragID of parent object
- *  bit 15		0			1
- *  bit 14 - 0		FragID of object	Offset into parent FragID
- *
- * New Inode numbers:
- *  Inode = Frag ID of parent (14) + Frag Offset (8) + (index into directory + 1)(8)
+ * Lookup/Create a block at offset 'block' into 'inode'.  We currently do
+ * not support creation of new blocks, so we return -EIO for this case.
  */
-#define inode_frag(ino)		((ino) >> 8)
-#define inode_idx(ino)		((ino) & 0xff)
-#define inode_dirindex(idx)	(((idx) & 0xff) * 26 - 21)
+int adfs_bmap(struct inode *inode, int block)
+{
+	if (block >= inode->i_blocks)
+		return 0;
 
-#define frag_id(x)		(((x) >> 8) & 0x7fff)
-#define off(x)			(((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0)
+	return __adfs_block_map(inode->i_sb, inode->i_ino, block);
+}
 
-static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no)
+static inline unsigned int
+adfs_filetype(struct inode *inode)
 {
-	unsigned long max_frag_id;
+	unsigned int type;
 
-	max_frag_id = sb->u.adfs_sb.s_map_size * sb->u.adfs_sb.s_ids_per_zone;
+	if (inode->u.adfs_i.stamped)
+		type = (inode->u.adfs_i.loadaddr >> 8) & 0xfff;
+	else
+		type = (unsigned int) -1;
 
-	return (inode_no & 0x800000ff) ||
-	       (frag_id (inode_frag (inode_no)) > max_frag_id) ||
-	       (frag_id (inode_frag (inode_no)) < 2);
+	return type;
 }
 
-int adfs_inode_validate (struct inode *inode)
+/*
+ * Convert ADFS attributes and filetype to Linux permission.
+ */
+static umode_t
+adfs_atts2mode(struct super_block *sb, struct inode *inode)
 {
-	struct super_block *sb = inode->i_sb;
+	unsigned int filetype, attr = inode->u.adfs_i.attr;
+	umode_t mode, rmask;
 
-	return adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00) ||
-	       adfs_inode_validate_no (sb, inode->u.adfs_i.file_id << 8);
-}
+	if (attr & ADFS_NDA_DIRECTORY) {
+		mode = S_IRUGO & sb->u.adfs_sb.s_owner_mask;
+		return S_IFDIR | S_IXUGO | mode;
+	}
 
-unsigned long adfs_inode_generate (unsigned long parent_id, int diridx)
-{
-	if (!parent_id)
-		return -1;
+	filetype = adfs_filetype(inode);
+
+	switch (filetype) {
+	case 0xfc0:	/* LinkFS */
+		return S_IFLNK|S_IRWXUGO;
+
+	case 0xfe6:	/* UnixExec */
+		rmask = S_IRUGO | S_IXUGO;
+		break;
+
+	default:
+		rmask = S_IRUGO;
+	}
+
+	mode = S_IFREG;
+
+	if (attr & ADFS_NDA_OWNER_READ)
+		mode |= rmask & sb->u.adfs_sb.s_owner_mask;
 
-	if (diridx)
-		diridx = (diridx + 21) / 26;
+	if (attr & ADFS_NDA_OWNER_WRITE)
+		mode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
 
-	return (parent_id << 8) | diridx;
+	if (attr & ADFS_NDA_PUBLIC_READ)
+		mode |= rmask & sb->u.adfs_sb.s_other_mask;
+
+	if (attr & ADFS_NDA_PUBLIC_WRITE)
+		mode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
+	return mode;
 }
 
-unsigned long adfs_inode_objid (struct inode *inode)
+/*
+ * Convert Linux permission to ADFS attribute.  We try to do the reverse
+ * of atts2mode, but there is not a 1:1 translation.
+ */
+static int
+adfs_mode2atts(struct super_block *sb, struct inode *inode)
 {
-	if (adfs_inode_validate (inode)) {
-		adfs_error (inode->i_sb, "adfs_inode_objid",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
-	}
+	umode_t mode;
+	int attr;
+
+	/* FIXME: should we be able to alter a link? */
+	if (S_ISLNK(inode->i_mode))
+		return inode->u.adfs_i.attr;
+
+	if (S_ISDIR(inode->i_mode))
+		attr = ADFS_NDA_DIRECTORY;
+	else
+		attr = 0;
 
-	return inode->u.adfs_i.file_id;
+	mode = inode->i_mode & sb->u.adfs_sb.s_owner_mask;
+	if (mode & S_IRUGO)
+		attr |= ADFS_NDA_OWNER_READ;
+	if (mode & S_IWUGO)
+		attr |= ADFS_NDA_OWNER_WRITE;
+
+	mode = inode->i_mode & sb->u.adfs_sb.s_other_mask;
+	mode &= ~sb->u.adfs_sb.s_owner_mask;
+	if (mode & S_IRUGO)
+		attr |= ADFS_NDA_PUBLIC_READ;
+	if (mode & S_IWUGO)
+		attr |= ADFS_NDA_PUBLIC_WRITE;
+
+	return attr;
 }
 
-int adfs_bmap (struct inode *inode, int block)
+/*
+ * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time
+ * referenced to 1 Jan 1900 (til 2248)
+ */
+static unsigned int
+adfs_adfs2unix_time(struct inode *inode)
 {
-	struct super_block *sb = inode->i_sb;
-	unsigned int blk;
+	unsigned int high, low;
 
-	if (adfs_inode_validate (inode)) {
-		adfs_error (sb, "adfs_bmap",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
-	}
+	if (inode->u.adfs_i.stamped == 0)
+		return CURRENT_TIME;
 
-	if (block < 0) {
-		adfs_error(sb, "adfs_bmap", "block(%d) < 0", block);
-		return 0;
-	}
+	high = inode->u.adfs_i.loadaddr << 24;
+	low  = inode->u.adfs_i.execaddr;
 
-	if (block > inode->i_blocks)
+	high |= low >> 8;
+	low  &= 255;
+
+	/* Files dated pre  01 Jan 1970 00:00:00. */
+	if (high < 0x336e996a)
 		return 0;
 
-	block += off(inode->u.adfs_i.file_id);
+	/* Files dated post 18 Jan 2038 03:14:05. */
+	if (high >= 0x656e9969)
+		return 0x7ffffffd;
 
-	if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + block;
-	else
-		blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block);
-	return blk;
+	/* discard 2208988800 (0x336e996a00) seconds of time */
+	high -= 0x336e996a;
+
+	/* convert 40-bit centi-seconds to 32-bit seconds */
+	return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
 }
 
-unsigned int adfs_parent_bmap (struct inode *inode, int block)
+/*
+ * Convert an Unix time to ADFS time.  We only do this if the entry has a
+ * time/date stamp already.
+ */
+static void
+adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
 {
-	struct super_block *sb = inode->i_sb;
-	unsigned int blk, fragment;
+	unsigned int high, low;
 
-	if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
-		adfs_error (sb, "adfs_parent_bmap",
-			    "bad inode number: %lu (%X,%X)",
-			    inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
-		return 0;
+	if (inode->u.adfs_i.stamped) {
+		/* convert 32-bit seconds to 40-bit centi-seconds */
+		low  = (secs & 255) * 100;
+		high = (secs / 256) * 100 + (low << 8) + 0x336e996a;
+
+		inode->u.adfs_i.loadaddr = (high >> 24) |
+				(inode->u.adfs_i.loadaddr & ~0xff);
+		inode->u.adfs_i.execaddr = (low & 255) | (high << 8);
 	}
-
-	fragment = inode_frag (inode->i_ino);
-	if (frag_id (fragment) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + off(fragment) + block;
-	else
-		blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block);
-	return blk;	
 }
 
-static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype)
+/*
+ * Fill in the inode information from the object information.
+ *
+ * Note that this is an inode-less filesystem, so we can't use the inode
+ * number to reference the metadata on the media.  Instead, we use the
+ * inode number to hold the object ID, which in turn will tell us where
+ * the data is held.  We also save the parent object ID, and with these
+ * two, we can locate the metadata.
+ *
+ * This does mean that we rely on an objects parent remaining the same at
+ * all times - we cannot cope with a cross-directory rename (yet).
+ */
+struct inode *
+adfs_iget(struct super_block *sb, struct object_info *obj)
 {
-	int omode = 0;
+	struct inode *inode;
 
-	if (filetype == 0xfc0 /* LinkFS */) {
-		omode = S_IFLNK|S_IRUSR|S_IWUSR|S_IXUSR|
-				S_IRGRP|S_IWGRP|S_IXGRP|
-				S_IROTH|S_IWOTH|S_IXOTH;
-	} else {
-		if (mode & ADFS_NDA_DIRECTORY) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
-			omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH;
-		} else
-			omode |= S_IFREG;
+	inode = get_empty_inode();
+	if (!inode)
+		goto out;
 
-		if (mode & ADFS_NDA_OWNER_READ) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
-			if (filetype == 0xfe6 /* UnixExec */)
-				omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask;
-		}
+	inode->i_version = ++global_event;
+	inode->i_sb	 = sb;
+	inode->i_dev	 = sb->s_dev;
+	inode->i_uid	 = sb->u.adfs_sb.s_uid;
+	inode->i_gid	 = sb->u.adfs_sb.s_gid;
+	inode->i_ino	 = obj->file_id;
+	inode->i_size	 = obj->size;
+	inode->i_nlink	 = 2;
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >>
+			    sb->s_blocksize_bits;
+
+	/*
+	 * we need to save the parent directory ID so that
+	 * write_inode can update the directory information
+	 * for this file.  This will need special handling
+	 * for cross-directory renames.
+	 */
+	inode->u.adfs_i.parent_id = obj->parent_id;
+	inode->u.adfs_i.loadaddr  = obj->loadaddr;
+	inode->u.adfs_i.execaddr  = obj->execaddr;
+	inode->u.adfs_i.attr      = obj->attr;
+	inode->u.adfs_i.stamped	  = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
+
+	inode->i_mode	 = adfs_atts2mode(sb, inode);
+	inode->i_mtime	 =
+	inode->i_atime	 =
+	inode->i_ctime	 = adfs_adfs2unix_time(inode);
+
+	if (S_ISDIR(inode->i_mode)) {
+		inode->i_op	= &adfs_dir_inode_operations;
+	} else if (S_ISREG(inode->i_mode)) {
+		inode->i_op	= &adfs_file_inode_operations;
+	}
 
-		if (mode & ADFS_NDA_OWNER_WRITE)
-			omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
+	insert_inode_hash(inode);
 
-		if (mode & ADFS_NDA_PUBLIC_READ) {
-			omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask;
-			if (filetype == 0xfe6 /* UnixExec */)
-				omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask;
-		}
+out:
+	return inode;
+}
 
-		if (mode & ADFS_NDA_PUBLIC_WRITE)
-			omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
-	}
-	return omode;
+/*
+ * This is no longer a valid way to obtain the metadata associated with the
+ * inode number on this filesystem.  This means that this filesystem cannot
+ * be shared via NFS.
+ */
+void adfs_read_inode(struct inode *inode)
+{
+	adfs_error(inode->i_sb, "unsupported method of reading inode");
+	make_bad_inode(inode);
 }
 
-void adfs_read_inode (struct inode *inode)
+/*
+ * Validate and convert a changed access mode/time to their ADFS equivalents.
+ * adfs_write_inode will actually write the information back to the directory
+ * later.
+ */
+int
+adfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	struct adfs_idir_entry ide;
-	int buffers;
+	struct inode *inode = dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	unsigned int ia_valid = attr->ia_valid;
+	int error;
 
-	sb = inode->i_sb;
-	inode->i_uid = sb->u.adfs_sb.s_uid;
-	inode->i_gid = sb->u.adfs_sb.s_gid;
-	inode->i_version = ++global_event;
+	error = inode_change_ok(inode, attr);
 
-	if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
-		adfs_error (sb, "adfs_read_inode",
-			    "bad inode number: %lu", inode->i_ino);
-		goto bad;
-	}
-
-	if (frag_id(inode_frag (inode->i_ino)) == ADFS_ROOT_FRAG &&
-	    inode_idx (inode->i_ino) == 0) {
-		/* root dir */
-		inode->i_mode	 = S_IRWXUGO | S_IFDIR;
-		inode->i_nlink	 = 2;
-		inode->i_size	 = ADFS_NEWDIR_SIZE;
-		inode->i_blksize = PAGE_SIZE;
-		inode->i_blocks  = inode->i_size / sb->s_blocksize;
-		inode->i_mtime   =
-		inode->i_atime   =
-		inode->i_ctime   = 0;
-		inode->u.adfs_i.file_id = inode_frag (inode->i_ino);
-	} else {
-		if (!(buffers = adfs_dir_read_parent (inode, bh)))
-			goto bad;
-
-		if (adfs_dir_check (inode, bh, buffers, NULL)) {
-			adfs_dir_free (bh, buffers);
-			goto bad;
-		}
-
-		if (!adfs_dir_find_entry (sb, bh, buffers, inode_dirindex (inode->i_ino), &ide)) {
-			adfs_dir_free (bh, buffers);
-			goto bad;
-		}
-		adfs_dir_free (bh, buffers);
-		inode->i_mode	 = adfs_atts2mode(sb, ide.mode, ide.filetype);
-		inode->i_nlink	 = 2;
-		inode->i_size    = ide.size;
-		inode->i_blksize = PAGE_SIZE;
-		inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
-		inode->i_mtime	 =
-		inode->i_atime   =
-		inode->i_ctime   = ide.mtime;
-		inode->u.adfs_i.file_id = ide.file_id;
+	/*
+	 * we can't change the UID or GID of any file -
+	 * we have a global UID/GID in the superblock
+	 */
+	if ((ia_valid & ATTR_UID && attr->ia_uid != sb->u.adfs_sb.s_uid) ||
+	    (ia_valid & ATTR_GID && attr->ia_gid != sb->u.adfs_sb.s_gid))
+		error = -EPERM;
+
+	if (error)
+		goto out;
+
+	if (ia_valid & ATTR_SIZE)
+		inode->i_size = attr->ia_size;
+	if (ia_valid & ATTR_MTIME) {
+		inode->i_mtime = attr->ia_mtime;
+		adfs_unix2adfs_time(inode, attr->ia_mtime);
+	}
+	/*
+	 * FIXME: should we make these == to i_mtime since we don't
+	 * have the ability to represent them in our filesystem?
+	 */
+	if (ia_valid & ATTR_ATIME)
+		inode->i_atime = attr->ia_atime;
+	if (ia_valid & ATTR_CTIME)
+		inode->i_ctime = attr->ia_ctime;
+	if (ia_valid & ATTR_MODE) {
+		inode->u.adfs_i.attr = adfs_mode2atts(sb, inode);
+		inode->i_mode = adfs_atts2mode(sb, inode);
 	}
 
-	if (S_ISDIR(inode->i_mode))
-		inode->i_op	 = &adfs_dir_inode_operations;
-	else if (S_ISREG(inode->i_mode))
-		inode->i_op	 = &adfs_file_inode_operations;
-	return;
+	/*
+	 * FIXME: should we be marking this inode dirty even if
+	 * we don't have any metadata to write back?
+	 */
+	if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))
+		mark_inode_dirty(inode);
+out:
+	return error;
+}
 
-bad:
-	make_bad_inode(inode);
+/*
+ * write an existing inode back to the directory, and therefore the disk.
+ * The adfs-specific inode data has already been updated by
+ * adfs_notify_change()
+ */
+void adfs_write_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct object_info obj;
+
+	obj.file_id	= inode->i_ino;
+	obj.name_len	= 0;
+	obj.parent_id	= inode->u.adfs_i.parent_id;
+	obj.loadaddr	= inode->u.adfs_i.loadaddr;
+	obj.execaddr	= inode->u.adfs_i.execaddr;
+	obj.attr	= inode->u.adfs_i.attr;
+	obj.size	= inode->i_size;
+
+	adfs_dir_update(sb, &obj);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/map.c linux.18p17/fs/adfs/map.c
--- linux.vanilla/fs/adfs/map.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/map.c	Fri Sep 15 23:24:41 2000
@@ -1,108 +1,288 @@
 /*
  *  linux/fs/adfs/map.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 
-static inline unsigned int
-adfs_convert_map_to_sector (const struct super_block *sb, unsigned int mapoff)
-{
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		mapoff <<= sb->u.adfs_sb.s_map2blk;
-	else
-		mapoff >>= -sb->u.adfs_sb.s_map2blk;
-	return mapoff;
-}
+#include <asm/spinlock.h>
 
-static inline unsigned int
-adfs_convert_sector_to_map (const struct super_block *sb, unsigned int secoff)
-{
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		secoff >>= sb->u.adfs_sb.s_map2blk;
-	else
-		secoff <<= -sb->u.adfs_sb.s_map2blk;
-	return secoff;
-}
+#include "adfs.h"
 
-static int lookup_zone (struct super_block *sb, int zone, int frag_id, int *offset)
-{
-	unsigned int mapptr, idlen, mapsize;
-	unsigned long *map;
+/*
+ * For the future...
+ */
+static rwlock_t adfs_map_lock;
 
-	map     = ((unsigned long *)sb->u.adfs_sb.s_map[zone]->b_data) + 1;
-	zone    =
-	mapptr  = zone == 0 ? (ADFS_DR_SIZE << 3) : 0;
-	idlen   = sb->u.adfs_sb.s_idlen;
-	mapsize = sb->u.adfs_sb.s_zonesize;
+/*
+ * return the map bit offset of the fragment frag_id in
+ * the zone dm.
+ * Note that the loop is optimised for best asm code -
+ * look at the output of:
+ *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
+ */
+static int
+lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
+	    const unsigned int frag_id, unsigned int *offset)
+{
+	const unsigned int mapsize = dm->dm_endbit;
+	const unsigned int idmask = (1 << idlen) - 1;
+	unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
+	unsigned int start = dm->dm_startbit;
+	unsigned int mapptr;
 
 	do {
-		unsigned long v1, v2;
-		unsigned int start;
+		unsigned long frag;
 
-		v1 = map[mapptr>>5];
-		v2 = map[(mapptr>>5)+1];
+		/*
+		 * get fragment id
+		 */
+		{
+			unsigned long v2;
+			unsigned int tmp;
 
-		v1 = (v1 >> (mapptr & 31)) | (v2 << (32 - (mapptr & 31)));
-		start = mapptr;
-		mapptr += idlen;
+			tmp = start >> 5;
+
+			frag = le32_to_cpu(map[tmp]);
+			v2   = le32_to_cpu(map[tmp + 1]);
+
+			tmp  = start & 31;
 
-		v2 = map[mapptr >> 5] >> (mapptr & 31);
-		if (!v2) {
-			mapptr = (mapptr + 32) & ~31;
-			for (; (v2 = map[mapptr >> 5]) == 0 && mapptr < mapsize; mapptr += 32);
+			frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+			frag &= idmask;
 		}
-		for (; (v2 & 255) == 0; v2 >>= 8, mapptr += 8);
-		for (; (v2 & 1) == 0; v2 >>= 1, mapptr += 1);
-		mapptr += 1;
-
-		if ((v1 & ((1 << idlen) - 1)) == frag_id) {
-			int length = mapptr - start;
-			if (*offset >= length)
-				*offset -= length;
-			else
-				return start + *offset - zone;
+
+		mapptr = start + idlen;
+
+		/*
+		 * find end of fragment
+		 */
+		{
+			unsigned long v2;
+
+			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+				mapptr = (mapptr & ~31) + 32;
+				if (mapptr >= mapsize)
+					goto error;
+			}
+
+			mapptr += 1 + ffz(~v2);
 		}
+
+		if (frag == frag_id)
+			goto found;
+again:
+		start = mapptr;
 	} while (mapptr < mapsize);
+
+error:
 	return -1;
+
+found:
+	{
+		int length = mapptr - start;
+		if (*offset >= length) {
+			*offset -= length;
+			goto again;
+		}
+	}
+	return start + *offset;
 }
 
-int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+/*
+ * Scan the free space map, for this zone, calculating the total
+ * number of map bits in each free space fragment.
+ *
+ * Note: idmask is limited to 15 bits [3.2]
+ */
+static unsigned int
+scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 {
-	unsigned int start_zone, zone, max_zone, mapoff, secoff;
+	const unsigned int mapsize = dm->dm_endbit + 32;
+	const unsigned int idlen  = asb->s_idlen;
+	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
+	const unsigned int idmask = (1 << frag_idlen) - 1;
+	unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
+	unsigned int start = 8, mapptr;
+	unsigned long frag;
+	unsigned long total = 0;
+
+	/*
+	 * get fragment id
+	 */
+	{
+		unsigned long v2;
+		unsigned int tmp;
+
+		tmp = start >> 5;
 
-	zone = start_zone = frag_id / sb->u.adfs_sb.s_ids_per_zone;
-	max_zone = sb->u.adfs_sb.s_map_size;
+		frag = le32_to_cpu(map[tmp]);
+		v2   = le32_to_cpu(map[tmp + 1]);
 
-	if (start_zone >= max_zone) {
-		adfs_error (sb, "adfs_map_lookup", "fragment %X is invalid (zone = %d, max = %d)",
-			    frag_id, start_zone, max_zone);
-		return 0;
+		tmp  = start & 31;
+
+		frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+		frag &= idmask;
 	}
 
-	/* Convert sector offset to map offset */
-	mapoff = adfs_convert_sector_to_map (sb, offset);
-	/* Calculate sector offset into map block */
-	secoff = offset - adfs_convert_map_to_sector (sb, mapoff);
+	/*
+	 * If the freelink is null, then no free fragments
+	 * exist in this zone.
+	 */
+	if (frag == 0)
+		return 0;
 
 	do {
-		int result = lookup_zone (sb, zone, frag_id, &mapoff);
+		start += frag;
+
+		/*
+		 * get fragment id
+		 */
+		{
+			unsigned long v2;
+			unsigned int tmp;
+
+			tmp = start >> 5;
+
+			frag = le32_to_cpu(map[tmp]);
+			v2   = le32_to_cpu(map[tmp + 1]);
+
+			tmp  = start & 31;
 
-		if (result != -1) {
-			result += zone ? (zone * sb->u.adfs_sb.s_zonesize) - (ADFS_DR_SIZE << 3): 0;
-			return adfs_convert_map_to_sector (sb, result) + secoff;
+			frag = (frag >> tmp) | (v2 << (32 - tmp));
+
+			frag &= idmask;
+		}
+
+		mapptr = start + idlen;
+
+		/*
+		 * find end of fragment
+		 */
+		{
+			unsigned long v2;
+
+			while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+				mapptr = (mapptr & ~31) + 32;
+				if (mapptr >= mapsize)
+					goto error;
+			}
+
+			mapptr += 1 + ffz(~v2);
 		}
 
-		zone ++;
-		if (zone >= max_zone)
-			zone = 0;
+		total += mapptr - start;
+	} while (frag >= idlen + 1);
+
+	if (frag != 0)
+		printk(KERN_ERR "adfs: undersized free fragment\n");
+
+	return total;
+error:
+	printk(KERN_ERR "adfs: oversized free fragment\n");
+	return 0;
+}
+
+static int
+scan_map(struct adfs_sb_info *asb, unsigned int zone,
+	 const unsigned int frag_id, unsigned int mapoff)
+{
+	const unsigned int idlen = asb->s_idlen;
+	struct adfs_discmap *dm, *dm_end;
+	int result;
+
+	dm	= asb->s_map + zone;
+	zone	= asb->s_map_size;
+	dm_end	= asb->s_map + zone;
+
+	do {
+		result = lookup_zone(dm, idlen, frag_id, &mapoff);
+
+		if (result != -1)
+			goto found;
+
+		dm ++;
+		if (dm == dm_end)
+			dm = asb->s_map;
+	} while (--zone > 0);
+
+	return -1;
+found:
+	result -= dm->dm_startbit;
+	result += dm->dm_startblk;
+
+	return result;
+}
+
+/*
+ * calculate the amount of free blocks in the map.
+ *
+ *              n=1
+ *  total_free = E(free_in_zone_n)
+ *              nzones
+ */
+unsigned int
+adfs_map_free(struct super_block *sb)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	struct adfs_discmap *dm;
+	unsigned int total = 0;
+	unsigned int zone;
+
+	dm   = asb->s_map;
+	zone = asb->s_map_size;
+
+	do {
+		total += scan_free_map(asb, dm++);
+	} while (--zone > 0);
+
+	return signed_asl(total, asb->s_map2blk);
+}
 
-	} while (zone != start_zone);
+int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	unsigned int zone, mapoff;
+	int result;
+
+	/*
+	 * map & root fragment is special - it starts in the center of the
+	 * disk.  The other fragments start at zone (frag / ids_per_zone)
+	 */
+	if (frag_id == ADFS_ROOT_FRAG)
+		zone = asb->s_map_size >> 1;
+	else
+		zone = frag_id / asb->s_ids_per_zone;
+
+	if (zone >= asb->s_map_size)
+		goto bad_fragment;
+
+	/* Convert sector offset to map offset */
+	mapoff = signed_asl(offset, -asb->s_map2blk);
+
+	read_lock(&adfs_map_lock);
+	result = scan_map(asb, zone, frag_id, mapoff);
+	read_unlock(&adfs_map_lock);
+
+	if (result > 0) {
+		unsigned int secoff;
+
+		/* Calculate sector offset into map block */
+		secoff = offset - signed_asl(mapoff, asb->s_map2blk);
+		return secoff + signed_asl(result, asb->s_map2blk);
+	}
 
-	adfs_error (sb, "adfs_map_lookup", "fragment %X at offset %d not found in map (start zone %d)",
-		    frag_id, offset, start_zone);
+	adfs_error(sb, "fragment %04X at offset %d not found in map",
+		   frag_id, offset);
+	return 0;
+
+bad_fragment:
+	adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
+		   frag_id, zone, asb->s_map_size);
 	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/namei.c linux.18p17/fs/adfs/namei.c
--- linux.vanilla/fs/adfs/namei.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/namei.c	Thu Jan  1 01:00:00 1970
@@ -1,113 +0,0 @@
-/*
- *  linux/fs/adfs/namei.c
- *
- * Copyright (C) 1997 Russell King
- */
-
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/adfs_fs.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/locks.h>
-
-/*
- * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure
- */
-static int adfs_match (int len, const char * const name, struct adfs_idir_entry *de)
-{
-	int i;
-
-	if (!de || len > ADFS_NAME_LEN)
-		return 0;
-	/*
-	 * "" means "." ---> so paths like "/usr/lib//libc.a" work
-	 */
-	if (!len && de->name_len == 1 && de->name[0] == '.' &&
-	    de->name[1] == '\0')
-		return 1;
-	if (len != de->name_len)
-		return 0;
-
-	for (i = 0; i < len; i++)
-		if ((de->name[i] ^ name[i]) & 0x5f)
-			return 0;
-	return 1;
-}
-
-static int adfs_find_entry (struct inode *dir, const char * const name, int namelen,
-			    struct adfs_idir_entry *ide)
-{
-	struct super_block *sb;
-	struct buffer_head *bh[4];
-	union  adfs_dirtail dt;
-	unsigned long parent_object_id, dir_object_id;
-	int buffers, pos;
-
-	sb = dir->i_sb;
-
-	if (adfs_inode_validate (dir)) {
-		adfs_error (sb, "adfs_find_entry",
-			    "invalid inode number: %lu", dir->i_ino);
-		return 0;
-	}
-
-	if (!(buffers = adfs_dir_read (dir, bh))) {
-		adfs_error (sb, "adfs_find_entry", "unable to read directory");
-		return 0;
-	}
-
-	if (adfs_dir_check (dir, bh, buffers, &dt)) {
-		adfs_dir_free (bh, buffers);
-		return 0;
-	}
-
-	parent_object_id = adfs_val (dt.new.dirparent, 3);
-	dir_object_id = adfs_inode_objid (dir);
-
-	if (namelen == 2 && name[0] == '.' && name[1] == '.') {
-		ide->name_len = 2;
-		ide->name[0] = ide->name[1] = '.';
-		ide->name[2] = '\0';
-		ide->inode_no = adfs_inode_generate (parent_object_id, 0);
-		adfs_dir_free (bh, buffers);
-		return 1;
-	}
-
-	pos = 5;
-
-	do {
-		if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, ide))
-			break;
-
-		if (adfs_match (namelen, name, ide)) {
-			adfs_dir_free (bh, buffers);
-			return pos;
-		}
-		pos += 26;
-	} while (1);
-	adfs_dir_free (bh, buffers);
-	return 0;
-}
-
-struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry)
-{
-	struct inode *inode = NULL;
-	struct adfs_idir_entry de;
-	unsigned long ino;
-
-	if (dentry->d_name.len > ADFS_NAME_LEN)
-		return ERR_PTR(-ENAMETOOLONG);
-
-	if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) {
-		ino = de.inode_no;
-		inode = iget (dir->i_sb, ino);
-
-		if (!inode)
-			return ERR_PTR(-EACCES);
-	}
-	d_add(dentry, inode);
-	return NULL;
-}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/adfs/super.c linux.18p17/fs/adfs/super.c
--- linux.vanilla/fs/adfs/super.c	Fri Apr 21 12:46:40 2000
+++ linux.18p17/fs/adfs/super.c	Mon Oct  2 10:04:16 2000
@@ -1,9 +1,9 @@
 /*
  *  linux/fs/adfs/super.c
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
-
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -21,12 +21,11 @@
 
 #include <stdarg.h>
 
-static void adfs_put_super(struct super_block *sb);
-static int adfs_remount(struct super_block *sb, int *flags, char *data);
-static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
-void adfs_read_inode(struct inode *inode);
+#include "adfs.h"
+#include "dir_f.h"
+#include "dir_fplus.h"
 
-void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
+void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
 {
 	char error_buf[128];
 	va_list args;
@@ -40,7 +39,74 @@
 		function ? function : "", error_buf);
 }
 
-static unsigned char adfs_calczonecheck(struct super_block *sb, char *map)
+static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
+{
+	int i;
+
+	/* sector size must be 256, 512 or 1024 bytes */
+	if (dr->log2secsize != 8 &&
+	    dr->log2secsize != 9 &&
+	    dr->log2secsize != 10)
+		return 1;
+
+	/* idlen must be at least log2secsize + 3 */
+	if (dr->idlen < dr->log2secsize + 3)
+		return 1;
+
+	/* we cannot have such a large disc that we
+	 * are unable to represent sector offsets in
+	 * 32 bits.  This works out at 2.0 TB.
+	 */
+	if (dr->disc_size_high >> dr->log2secsize)
+		return 1;
+
+	/*
+	 * The following checks are not required for F+
+	 * stage 1.
+	 */
+#if 0
+	/* idlen must be smaller be no greater than 15 */
+	if (dr->idlen > 15)
+		return 1;
+
+	/* nzones must be less than 128 for the root
+	 * directory to be addressable
+	 */
+	if (dr->nzones >= 128 && dr->nzones_high == 0)
+		return 1;
+
+	/* root must be of the form 0x2.. */
+	if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
+		return 1;
+#else
+	/*
+	 * Stage 2 F+ does not require the following check
+	 */
+#if 0
+	/* idlen must be no greater than 16 v2 [1.0] */
+	if (dr->idlen > 16)
+		return 1;
+
+	/* we can't handle F+ discs yet */
+	if (dr->format_version || dr->root_size)
+		return 1;
+
+#else
+	/* idlen must be no greater than 19 v2 [1.0] */
+	if (dr->idlen > 19)
+		return 1;
+#endif
+#endif
+
+	/* reserved bytes should be zero */
+	for (i = 0; i < sizeof(dr->unused52); i++)
+		if (dr->unused52[i] != 0)
+			return 1;
+
+	return 0;
+}
+
+static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
 {
 	unsigned int v0, v1, v2, v3;
 	int i;
@@ -64,46 +130,34 @@
 	return v0 ^ v1 ^ v2 ^ v3;
 }
 
-static int adfs_checkmap(struct super_block *sb)
+static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
 {
 	unsigned char crosscheck = 0, zonecheck = 1;
 	int i;
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-		char *map;
+		unsigned char *map;
+
+		map = dm[i].dm_bh->b_data;
 
-		map = sb->u.adfs_sb.s_map[i]->b_data;
 		if (adfs_calczonecheck(sb, map) != map[0]) {
-			adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i);
+			adfs_error(sb, "zone %d fails zonecheck", i);
 			zonecheck = 0;
 		}
 		crosscheck ^= map[3];
 	}
 	if (crosscheck != 0xff)
-		adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff");
+		adfs_error(sb, "crosscheck != 0xff");
 	return crosscheck == 0xff && zonecheck;
 }
 
-static struct super_operations adfs_sops = {
-	adfs_read_inode,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	adfs_put_super,
-	NULL,
-	adfs_statfs,
-	adfs_remount
-};
-
 static void adfs_put_super(struct super_block *sb)
 {
 	int i;
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-		brelse(sb->u.adfs_sb.s_map[i]);
+		brelse(sb->u.adfs_sb.s_map[i].dm_bh);
 	kfree(sb->u.adfs_sb.s_map);
-	brelse(sb->u.adfs_sb.s_sbh);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -159,13 +213,104 @@
 	return parse_options(sb, data);
 }
 
+static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	struct adfs_sb_info *asb = &sb->u.adfs_sb;
+	struct statfs tmp;
+
+	tmp.f_type    = ADFS_SUPER_MAGIC;
+	tmp.f_namelen = asb->s_namelen;
+	tmp.f_bsize   = sb->s_blocksize;
+	tmp.f_blocks  = asb->s_size;
+	tmp.f_files   = asb->s_ids_per_zone * asb->s_map_size;
+	tmp.f_bavail  =
+	tmp.f_bfree   = adfs_map_free(sb);
+	tmp.f_ffree   = tmp.f_bfree * tmp.f_files / tmp.f_blocks;
+
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+static struct super_operations adfs_sops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+	read_inode:	adfs_read_inode,
+	notify_change:	adfs_notify_change,
+#endif
+	write_inode:	adfs_write_inode,
+	put_super:	adfs_put_super,
+	statfs:		adfs_statfs,
+	remount_fs:	adfs_remount,
+};
+
+static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
+{
+	struct adfs_discmap *dm;
+	unsigned int map_addr, zone_size, nzones;
+	int i, zone;
+
+	nzones    = sb->u.adfs_sb.s_map_size;
+	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
+	map_addr  = (nzones >> 1) * zone_size -
+		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
+	map_addr  = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
+
+	sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
+
+	dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
+	if (dm == NULL) {
+		adfs_error(sb, "not enough memory");
+		return NULL;
+	}
+
+	for (zone = 0; zone < nzones; zone++, map_addr++) {
+		dm[zone].dm_startbit = 0;
+		dm[zone].dm_endbit   = zone_size;
+		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
+		dm[zone].dm_bh       = bread(sb->s_dev, map_addr, sb->s_blocksize);
+
+		if (!dm[zone].dm_bh) {
+			adfs_error(sb, "unable to read map");
+			goto error_free;
+		}
+	}
+
+	/* adjust the limits for the first and last map zones */
+	i = zone - 1;
+	dm[0].dm_startblk = 0;
+	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
+	dm[i].dm_endbit   = (dr->disc_size_high << (32 - dr->log2bpmb)) +
+			    (dr->disc_size >> dr->log2bpmb) +
+			    (ADFS_DR_SIZE_BITS - i * zone_size);
+
+	if (adfs_checkmap(sb, dm))
+		return dm;
+
+	adfs_error(sb, NULL, "map corrupted");
+
+error_free:
+	while (--zone >= 0)
+		brelse(dm[zone].dm_bh);
+
+	kfree(dm);
+	return NULL;
+}
+
+static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
+{
+	unsigned long discsize;
+
+	discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
+	discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
+
+	return discsize;
+}
+
 struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
 {
 	struct adfs_discrecord *dr;
 	struct buffer_head *bh;
+	struct object_info root_obj;
 	unsigned char *b_data;
 	kdev_t dev = sb->s_dev;
-	int i, j;
 
 	/* set default options */
 	sb->u.adfs_sb.s_uid = 0;
@@ -180,7 +325,7 @@
 	lock_super(sb);
 	set_blocksize(dev, BLOCK_SIZE);
 	if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
-		adfs_error(sb, NULL, "unable to read superblock");
+		adfs_error(sb, "unable to read superblock");
 		goto error_unlock;
 	}
 
@@ -192,8 +337,19 @@
 				"%s.\n", kdevname(dev));
 		goto error_free_bh;
 	}
+
 	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
 
+	/*
+	 * Do some sanity checks on the ADFS disc record
+	 */
+	if (adfs_checkdiscrecord(dr)) {
+		if (!silent)
+			printk("VPS: Can't find an adfs filesystem on dev "
+				"%s.\n", kdevname(dev));
+		goto error_free_bh;
+	}
+
 	sb->s_blocksize_bits = dr->log2secsize;
 	sb->s_blocksize = 1 << sb->s_blocksize_bits;
 	if (sb->s_blocksize != BLOCK_SIZE &&
@@ -204,13 +360,13 @@
 		set_blocksize(dev, sb->s_blocksize);
 		bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
 		if (!bh) {
-			adfs_error(sb, NULL, "couldn't read superblock on "
+			adfs_error(sb, "couldn't read superblock on "
 				"2nd try.");
 			goto error_unlock;
 		}
 		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
 		if (adfs_checkbblk(b_data)) {
-			adfs_error(sb, NULL, "disc record mismatch, very weird!");
+			adfs_error(sb, "disc record mismatch, very weird!");
 			goto error_free_bh;
 		}
 		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
@@ -221,99 +377,73 @@
 				"%s.\n", kdevname(dev));
 		goto error_free_bh;
 	}
-	/* blocksize on this device should now be set to the adfs log2secsize */
-
-	sb->u.adfs_sb.s_sbh		= bh;
-	sb->u.adfs_sb.s_dr		= dr;
 
-	/* s_zone_size = size of 1 zone (1 sector) * bits_in_byte - zone_spare =>
-	 * number of map bits in a zone
-	 */
-	sb->u.adfs_sb.s_zone_size	= (8 << dr->log2secsize) - dr->zone_spare;
-
-	/* s_ids_per_zone = bit size of 1 zone / min. length of fragment block =>
-	 * number of ids in one zone
+	/*
+	 * blocksize on this device should now be set to the ADFS log2secsize
 	 */
-	sb->u.adfs_sb.s_ids_per_zone	= sb->u.adfs_sb.s_zone_size / (dr->idlen + 1);
 
-	/* s_idlen = length of 1 id */
-	sb->u.adfs_sb.s_idlen		= dr->idlen;
+	sb->s_magic		 = ADFS_SUPER_MAGIC;
+	sb->u.adfs_sb.s_idlen	 = dr->idlen;
+	sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
+	sb->u.adfs_sb.s_map2blk	 = dr->log2bpmb - dr->log2secsize;
+	sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
+	sb->u.adfs_sb.s_version  = dr->format_version;
+	sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
 
-	/* map size (in sectors) = number of zones */
-	sb->u.adfs_sb.s_map_size	= dr->nzones;
-
-	/* zonesize = size of sector - zonespare */
-	sb->u.adfs_sb.s_zonesize	= (sb->s_blocksize << 3) - dr->zone_spare;
-
-	/* map start (in sectors) = start of zone (number of zones) / 2 */
-	sb->u.adfs_sb.s_map_block	= (dr->nzones >> 1) * sb->u.adfs_sb.s_zone_size -
-					   ((dr->nzones > 1) ? 8 * ADFS_DR_SIZE : 0);
-
-	/* (signed) number of bits to shift left a map address to a sector address */
-	sb->u.adfs_sb.s_map2blk		= dr->log2bpmb - dr->log2secsize;
-
-	if (sb->u.adfs_sb.s_map2blk >= 0)
-		sb->u.adfs_sb.s_map_block <<= sb->u.adfs_sb.s_map2blk;
-	else
-		sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk;
-
-	printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n",
-		sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone,
-		sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size);
-	printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n",
-		1 << dr->log2secsize, 1 << dr->log2bpmb,
-		1 << (dr->log2secsize + dr->log2sharesize));
-
-	sb->s_magic = ADFS_SUPER_MAGIC;
-
-	sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size *
-				sizeof(struct buffer_head *), GFP_KERNEL);
-	if (sb->u.adfs_sb.s_map == NULL) {
-		adfs_error(sb, NULL, "not enough memory");
+	sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
+	if (!sb->u.adfs_sb.s_map)
 		goto error_free_bh;
-	}
 
-	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-		sb->u.adfs_sb.s_map[i] = bread(dev,
-						sb->u.adfs_sb.s_map_block + i,
-						sb->s_blocksize);
-		if (!sb->u.adfs_sb.s_map[i]) {
-			for (j = 0; j < i; j++)
-				brelse(sb->u.adfs_sb.s_map[j]);
-			kfree(sb->u.adfs_sb.s_map);
-			adfs_error(sb, NULL, "unable to read map");
-			goto error_free_bh;
-		}
-	}
-	if (!adfs_checkmap(sb)) {
-		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse(sb->u.adfs_sb.s_map[i]);
-		adfs_error(sb, NULL, "map corrupted");
-		goto error_free_bh;
-	}
+	brelse(bh);
 
-	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4);
+	/*
+	 * set up enough so that we can read an inode
+	 */
+	sb->s_op = &adfs_sops;
 	unlock_super(sb);
 
+	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
+
+	root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
+	root_obj.name_len  = 0;
+	root_obj.loadaddr  = 0;
+	root_obj.execaddr  = 0;
+	root_obj.size	   = ADFS_NEWDIR_SIZE;
+	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |
+			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
+
 	/*
-	 * set up enough so that it can read an inode
+	 * If this is a F+ disk with variable length directories,
+	 * get the root_size from the disc record.
 	 */
-	sb->s_op = &adfs_sops;
-	sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0);
-	sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
+	if (sb->u.adfs_sb.s_version) {
+		root_obj.size = dr->root_size;
+		sb->u.adfs_sb.s_dir     = &adfs_fplus_dir_ops;
+		sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
+	} else {
+		sb->u.adfs_sb.s_dir     = &adfs_f_dir_ops;
+		sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
+	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
+#else
+	sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj), NULL);
+#endif
 	if (!sb->s_root) {
+		int i;
+
 		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse(sb->u.adfs_sb.s_map[i]);
-		brelse(bh);
-		adfs_error(sb, NULL, "get root inode failed\n");
+			brelse(sb->u.adfs_sb.s_map[i].dm_bh);
+		kfree(sb->u.adfs_sb.s_map);
+		adfs_error(sb, "get root inode failed\n");
 		goto error_dec_use;
-	}
+	} else
+		sb->s_root->d_op = &adfs_dentry_operations;
 	return sb;
 
 error_free_bh:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 error_unlock:
 	unlock_super(sb);
 error_dec_use:
@@ -323,70 +453,11 @@
 	return NULL;
 }
 
-static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
-{
-	struct statfs tmp;
-	const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1;
-
-	tmp.f_type = ADFS_SUPER_MAGIC;
-	tmp.f_bsize = sb->s_blocksize;
-	tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) |
-		       sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits;
-	tmp.f_files = tmp.f_blocks >> nidlen;
-	{
-		unsigned int i, j = 0;
-		const unsigned mask = (1 << (nidlen - 1)) - 1;
-		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-			const char *map = sb->u.adfs_sb.s_map[i]->b_data;
-			unsigned freelink, mapindex = 24;
-			j -= nidlen;
-			do {
-				unsigned char k, l, m;
-				unsigned off = (mapindex - nidlen) >> 3;
-				unsigned rem;
-				const unsigned boff = mapindex & 7;
-
-				/* get next freelink */
-
-				k = map[off++];
-				l = map[off++];
-				m = map[off++];
-				freelink = (m << 16) | (l << 8) | k;
-				rem = freelink >> (boff + nidlen - 1);
-				freelink = (freelink >> boff) & mask;
-				mapindex += freelink;
-
-				/* find its length and add it to running total */
-
-				while (rem == 0) {
-					j += 8;
-					rem = map[off++];
-				}
-				if ((rem & 0xff) == 0) j+=8, rem>>=8;
-				if ((rem & 0xf) == 0) j+=4, rem>>=4;
-				if ((rem & 0x3) == 0) j+=2, rem>>=2;
-				if ((rem & 0x1) == 0) j+=1;
-				j += nidlen - boff;
-				if (freelink <= nidlen) break;
-			} while (mapindex < 8 * sb->s_blocksize);
-			if (mapindex > 8 * sb->s_blocksize)
-				adfs_error(sb, NULL, "oversized free fragment\n");
-			else if (freelink)
-				adfs_error(sb, NULL, "undersized free fragment\n");
-		}
-		tmp.f_bfree = tmp.f_bavail = j <<
-			(sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits);
-	}
-	tmp.f_ffree = tmp.f_bfree >> nidlen;
-	tmp.f_namelen = ADFS_NAME_LEN;
-	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
-}
-
 static struct file_system_type adfs_fs_type = {
 	"adfs", FS_REQUIRES_DEV, adfs_read_super, NULL
 };
 
-__initfunc(int init_adfs_fs(void))
+int __init init_adfs_fs(void)
 {
 	return register_filesystem(&adfs_fs_type);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/block_dev.c linux.18p17/fs/block_dev.c
--- linux.vanilla/fs/block_dev.c	Fri Apr 21 12:46:41 2000
+++ linux.18p17/fs/block_dev.c	Thu Sep 14 15:44:52 2000
@@ -24,7 +24,7 @@
 	ssize_t block, blocks;
 	loff_t offset;
 	ssize_t chars;
-	ssize_t written = 0;
+	ssize_t written = 0, retval = 0;
 	struct buffer_head * bhlist[NBUF];
 	size_t size;
 	kdev_t dev;
@@ -54,8 +54,10 @@
 	else
 		size = INT_MAX;
 	while (count>0) {
-		if (block >= size)
-			return written ? written : -ENOSPC;
+		if (block >= size) {
+			retval = -ENOSPC;
+			goto cleanup;
+		}
 		chars = blocksize - offset;
 		if (chars > count)
 			chars=count;
@@ -67,15 +69,19 @@
 			if (chars != blocksize)
 				fn = bread;
 			bh = fn(dev, block, blocksize);
-			if (!bh)
-				return written ? written : -EIO;
+			if (!bh) {
+				retval = -EIO;
+				goto cleanup;
+			}
 			if (!buffer_uptodate(bh))
 				wait_on_buffer(bh);
 		}
 #else
 		bh = getblk(dev, block, blocksize);
-		if (!bh)
-			return written ? written : -EIO;
+		if (!bh) {
+			retval = -EIO;
+			goto cleanup;
+		}
 
 		if (!buffer_uptodate(bh))
 		{
@@ -99,7 +105,8 @@
 		        if (!bhlist[i])
 			{
 			  while(i >= 0) brelse(bhlist[i--]);
-			  return written ? written : -EIO;
+			  retval = -EIO;
+			  goto cleanup;
 		        }
 		      }
 		    }
@@ -108,7 +115,8 @@
 		    wait_on_buffer(bh);
 		    if (!buffer_uptodate(bh)) {
 			  brelse(bh);
-			  return written ? written : -EIO;
+			  retval = -EIO;
+			  goto cleanup;
 		    }
 		  };
 		};
@@ -141,6 +149,7 @@
 		if(write_error)
 			break;
 	}
+	cleanup:
 	if ( buffercount ){
 		ll_rw_block(WRITE, buffercount, bufferlist);
 		for(i=0; i<buffercount; i++){
@@ -150,10 +159,11 @@
 			brelse(bufferlist[i]);
 		}
 	}		
-	filp->f_reada = 1;
+	if(!retval)
+		filp->f_reada = 1;
 	if(write_error)
 		return -EIO;
-	return written;
+	return written ? written : retval;
 }
 
 ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/dquot.c linux.18p17/fs/dquot.c
--- linux.vanilla/fs/dquot.c	Fri Apr 21 12:46:41 2000
+++ linux.18p17/fs/dquot.c	Mon Sep 11 17:18:30 2000
@@ -1265,14 +1265,6 @@
 	if (!inode)
 		return -ENOENT;
 	/*
-	 * Find out if this filesystem uses i_blocks.
-	 */
-	if (inode->i_blksize == 0)
-		blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
-	else
-		blocks = (inode->i_blocks / 2);
-
-	/*
 	 * Build the transfer_from and transfer_to lists and check quotas to see
 	 * if operation is permitted.
 	 */
@@ -1331,14 +1323,29 @@
 		 * dqget() could block and so the first structure might got
 		 * invalidated or locked...
 		 */
-		if (!transfer_to[cnt]->dq_mnt || !transfer_from[cnt]->dq_mnt ||
-		    check_idq(transfer_to[cnt], cnt, 1, initiator, tty) == NO_QUOTA ||
-		    check_bdq(transfer_to[cnt], cnt, blocks, initiator, tty, 0) == NO_QUOTA) {
+		if (!transfer_to[cnt]->dq_mnt || !transfer_from[cnt]->dq_mnt) {
 			cnt++;
 			goto put_all;
 		}
 	}
 
+	/*
+	 * Find out if this filesystem uses i_blocks.
+	 */
+	if (inode->i_blksize == 0)
+		blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
+	else
+		blocks = (inode->i_blocks / 2);
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+	{
+		if (transfer_to[cnt] == NODQUOT)
+			continue;
+		if (check_idq(transfer_to[cnt], cnt, 1, initiator, tty) == NO_QUOTA ||
+		    check_bdq(transfer_to[cnt], cnt, blocks, initiator, tty, 0) == NO_QUOTA) {
+			cnt = MAXQUOTAS;
+			goto put_all;
+		}
+}
 	if ((error = notify_change(dentry, iattr)))
 		goto put_all; 
 	/*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/balloc.c linux.18p17/fs/ext2/balloc.c
--- linux.vanilla/fs/ext2/balloc.c	Sun Jun 11 21:44:18 2000
+++ linux.18p17/fs/ext2/balloc.c	Sat Sep  2 23:23:43 2000
@@ -553,20 +553,21 @@
 	 * Do block preallocation now if required.
 	 */
 #ifdef EXT2_PREALLOCATE
-	if (prealloc_block) {
+	if (prealloc_count && !*prealloc_count) {
 		int	prealloc_goal;
+		unsigned long	next_block = tmp + 1;
 
 		prealloc_goal = es->s_prealloc_blocks ?
 			es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS;
 
-		*prealloc_count = 0;
-		*prealloc_block = tmp + 1;
+		*prealloc_block = next_block;
 		for (k = 1;
 		     k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
-		     k++) {
+		     k++, next_block++) {
 			if (DQUOT_PREALLOC_BLOCK(sb, inode, 1))
 				break;
-			if (ext2_set_bit (j + k, bh->b_data)) {
+			if (*prealloc_block + *prealloc_count != next_block ||
+			    ext2_set_bit (j + k, bh->b_data)) {
 				DQUOT_FREE_BLOCK(sb, inode, 1);
  				break;
 			}
@@ -574,12 +575,12 @@
 		}	
 		gdp->bg_free_blocks_count =
 			cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) -
-			       *prealloc_count);
+			       (k - 1));
 		es->s_free_blocks_count =
 			cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) -
-			       *prealloc_count);
+			       (k - 1));
 		ext2_debug ("Preallocated a further %lu bits.\n",
-			    *prealloc_count);
+			       (k - 1));
 	}
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/file.c linux.18p17/fs/ext2/file.c
--- linux.vanilla/fs/ext2/file.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/file.c	Mon Sep 11 17:37:18 2000
@@ -136,7 +136,7 @@
 	return offset;
 }
 
-static inline void remove_suid(struct inode *inode)
+inline void ext2_remove_suid(struct inode *inode)
 {
 	unsigned int mode;
 
@@ -190,7 +190,7 @@
 			      inode->i_mode);
 		return -EINVAL;
 	}
-	remove_suid(inode);
+	ext2_remove_suid(inode);
 
 	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/fsync.c linux.18p17/fs/ext2/fsync.c
--- linux.vanilla/fs/ext2/fsync.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/fsync.c	Tue Sep  5 22:00:39 2000
@@ -39,40 +39,6 @@
 {
 	struct buffer_head * bh;
 	
-	if (!*block)
-		return 0;
-	bh = get_hash_table (inode->i_dev, *block, blocksize);
-	if (!bh)
-		return 0;
-	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-		/* There can be a parallell read(2) that started read-I/O
-		   on the buffer so we can't assume that there's been
-		   an I/O error without first waiting I/O completation. */
-		wait_on_buffer(bh);
-		if (!buffer_uptodate(bh))
-		{
-			brelse (bh);
-			return -1;
-		}
-	}
-	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
-		if (wait)
-			/* when we return from fsync all the blocks
-			   must be _just_ stored on disk */
-			wait_on_buffer(bh);
-		brelse (bh);
-		return 0;
-	}
-	ll_rw_block (WRITE, 1, &bh);
-	bh->b_count--;
-	return 0;
-}
-
-#ifndef __LITTLE_ENDIAN
-static int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
-{
-	struct buffer_head * bh;
-	
 	if (!le32_to_cpu(*block))
 		return 0;
 	bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
@@ -101,10 +67,6 @@
 	bh->b_count--;
 	return 0;
 }
-#else
-#define sync_block_swab32 sync_block
-#endif
-
 
 static int sync_iblock (struct inode * inode, u32 * iblock, 
 			struct buffer_head ** bh, int wait) 
@@ -112,29 +74,10 @@
 	int rc, tmp;
 	
 	*bh = NULL;
-	tmp = *iblock;
-	if (!tmp)
-		return 0;
-	rc = sync_block (inode, iblock, wait);
-	if (rc)
-		return rc;
-	*bh = bread (inode->i_dev, tmp, blocksize);
-	if (!*bh)
-		return -1;
-	return 0;
-}
-
-#ifndef __LITTLE_ENDIAN
-static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, 
-			       struct buffer_head ** bh, int wait) 
-{
-	int rc, tmp;
-	
-	*bh = NULL;
 	tmp = le32_to_cpu(*iblock);
 	if (!tmp)
 		return 0;
-	rc = sync_block_swab32 (inode, iblock, wait);
+	rc = sync_block (inode, iblock, wait);
 	if (rc)
 		return rc;
 	*bh = bread (inode->i_dev, tmp, blocksize);
@@ -142,9 +85,6 @@
 		return -1;
 	return 0;
 }
-#else
-#define sync_iblock_swab32 sync_iblock
-#endif
 
 static int sync_direct (struct inode * inode, int wait)
 {
@@ -170,9 +110,7 @@
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_block_swab32 (inode, 
-					((u32 *) ind_bh->b_data) + i,
-					wait);
+		rc = sync_block (inode, ((u32 *) ind_bh->b_data) + i, wait);
 		if (rc)
 			err = rc;
 	}
@@ -180,31 +118,6 @@
 	return err;
 }
 
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
-{
-	int i;
-	struct buffer_head * ind_bh;
-	int rc, err = 0;
-
-	rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
-	if (rc || !ind_bh)
-		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_block_swab32 (inode, 
-					((u32 *) ind_bh->b_data) + i,
-					wait);
-		if (rc)
-			err = rc;
-	}
-	brelse (ind_bh);
-	return err;
-}
-#else
-#define sync_indirect_swab32 sync_indirect
-#endif
-
 static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
 {
 	int i;
@@ -216,40 +129,13 @@
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_indirect_swab32 (inode,
-					   ((u32 *) dind_bh->b_data) + i,
-					   wait);
-		if (rc)
-			err = rc;
-	}
-	brelse (dind_bh);
-	return err;
-}
-
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
-{
-	int i;
-	struct buffer_head * dind_bh;
-	int rc, err = 0;
-
-	rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
-	if (rc || !dind_bh)
-		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_indirect_swab32 (inode,
-					   ((u32 *) dind_bh->b_data) + i,
-					   wait);
+		rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait);
 		if (rc)
 			err = rc;
 	}
 	brelse (dind_bh);
 	return err;
 }
-#else
-#define sync_dindirect_swab32 sync_dindirect
-#endif
 
 static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
 {
@@ -262,9 +148,7 @@
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_dindirect_swab32 (inode,
-					    ((u32 *) tind_bh->b_data) + i,
-					    wait);
+		rc = sync_dindirect(inode, ((u32 *)tind_bh->b_data) + i, wait);
 		if (rc)
 			err = rc;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/ialloc.c linux.18p17/fs/ext2/ialloc.c
--- linux.vanilla/fs/ext2/ialloc.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/ialloc.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/inode.c linux.18p17/fs/ext2/inode.c
--- linux.vanilla/fs/ext2/inode.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/inode.c	Sun Oct 15 21:15:17 2000
@@ -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;
@@ -63,18 +63,7 @@
 	ext2_free_inode (inode);
 }
 
-#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
-
-static inline int block_bmap (struct buffer_head * bh, int nr)
-{
-	int tmp;
-
-	if (!bh)
-		return 0;
-	tmp = le32_to_cpu(((u32 *) bh->b_data)[nr]);
-	brelse (bh);
-	return tmp;
-}
+#define inode_bmap(inode, nr) le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])
 
 /* 
  * ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
@@ -149,61 +138,90 @@
 	return result;
 }
 
+/**
+ *	ext2_block_to_path - parse the block number into array of offsets
+ *	@inode: inode in question (we are only interested in its superblock)
+ *	@i_block: block number to be parsed
+ *	@offsets: array to store the offsets in
+ *
+ *	To store the locations of file's data ext2 uses a data structure common
+ *	for UNIX filesystems - tree of pointers anchored in the inode, with
+ *	data blocks at leaves and indirect blocks in intermediate nodes.
+ *	This function translates the block number into path in that tree -
+ *	return value is the path length and @offsets[n] is the offset of
+ *	pointer to (n+1)th node in the nth one. If @block is out of range
+ *	(negative or too large) warning is printed and zero returned.
+ *
+ *	Note: function doesn't find node addresses, so no IO is needed. All
+ *	we need to know is the capacity of indirect blocks (taken from the
+ *	inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext2_block_to_path(struct inode *inode, long i_block, int offsets[4])
+{
+	int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+	int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+	const long direct_blocks = EXT2_NDIR_BLOCKS,
+		indirect_blocks = ptrs,
+		double_blocks = (1 << (ptrs_bits * 2));
+	int n = 0;
+
+	if (i_block < 0) {
+		ext2_warning (inode->i_sb, "ext2_block_to_path", "block < 0");
+	} else if (i_block < direct_blocks) {
+		offsets[n++] = i_block;
+	} else if ( (i_block -= direct_blocks) < indirect_blocks) {
+		offsets[n++] = EXT2_IND_BLOCK;
+		offsets[n++] = i_block;
+	} else if ((i_block -= indirect_blocks) < double_blocks) {
+		offsets[n++] = EXT2_DIND_BLOCK;
+		offsets[n++] = i_block >> ptrs_bits;
+		offsets[n++] = i_block & (ptrs - 1);
+	} else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+		offsets[n++] = EXT2_TIND_BLOCK;
+		offsets[n++] = i_block >> (ptrs_bits * 2);
+		offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+		offsets[n++] = i_block & (ptrs - 1);
+	} else {
+		ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");
+	}
+	return n;
+}
+
 
 int ext2_bmap (struct inode * inode, int block)
 {
+	int offsets[4], *p;
+	int depth = ext2_block_to_path(inode, block, offsets);
+	struct buffer_head *bh;
 	int i;
-	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
-	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
 
-	if (block < 0) {
-		ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
-		return 0;
-	}
-	if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
-		(1 << (addr_per_block_bits * 2)) +
-		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
-		ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
-		return 0;
-	}
-	if (block < EXT2_NDIR_BLOCKS)
-		return inode_bmap (inode, block);
-	block -= EXT2_NDIR_BLOCKS;
-	if (block < addr_per_block) {
-		i = inode_bmap (inode, EXT2_IND_BLOCK);
+	if (depth == 0)
+		goto fail;
+	i = inode_bmap (inode, *(p=offsets));
+	while (--depth) {
 		if (!i)
-			return 0;
-		return block_bmap (bread (inode->i_dev, i,
-					  inode->i_sb->s_blocksize), block);
+			break;
+		bh = bread (inode->i_dev, i, inode->i_sb->s_blocksize);
+		if (!bh)
+			goto fail;
+		++p;
+		i = le32_to_cpu(((u32 *) bh->b_data)[*p]);
+		brelse (bh);
 	}
-	block -= addr_per_block;
-	if (block < (1 << (addr_per_block_bits * 2))) {
-		i = inode_bmap (inode, EXT2_DIND_BLOCK);
-		if (!i)
-			return 0;
-		i = block_bmap (bread (inode->i_dev, i,
-				       inode->i_sb->s_blocksize),
-				block >> addr_per_block_bits);
-		if (!i)
-			return 0;
-		return block_bmap (bread (inode->i_dev, i,
-					  inode->i_sb->s_blocksize),
-				   block & (addr_per_block - 1));
-	}
-	block -= (1 << (addr_per_block_bits * 2));
-	i = inode_bmap (inode, EXT2_TIND_BLOCK);
-	if (!i)
-		return 0;
-	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			block >> (addr_per_block_bits * 2));
-	if (!i)
-		return 0;
-	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			(block >> addr_per_block_bits) & (addr_per_block - 1));
-	if (!i)
-		return 0;
-	return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			   block & (addr_per_block - 1));
+	return i;
+fail:
+	return 0;
 }
 
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
@@ -218,7 +236,8 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		struct buffer_head * result;
+		result = getblk (inode->i_dev, le32_to_cpu(tmp), inode->i_sb->s_blocksize);
 		if (tmp == *p)
 			return result;
 		brelse (result);
@@ -236,7 +255,7 @@
 	if (!goal) {
 		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (inode->u.ext2_i.i_data[tmp]) {
-				goal = inode->u.ext2_i.i_data[tmp];
+				goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]);
 				break;
 			}
 		}
@@ -257,7 +276,7 @@
 		brelse (result);
 		goto repeat;
 	}
-	*p = tmp;
+	*p = cpu_to_le32(tmp);
 	inode->u.ext2_i.i_next_alloc_block = new_block;
 	inode->u.ext2_i.i_next_alloc_goal = tmp;
 	inode->i_ctime = CURRENT_TIME;
@@ -349,21 +368,12 @@
 				  int create, int * err)
 {
 	struct buffer_head * bh;
-	unsigned long b;
-	unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
-	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+	int offsets[4], *p;
+	int depth = ext2_block_to_path(inode, block, offsets);
 
 	*err = -EIO;
-	if (block < 0) {
-		ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
-		return NULL;
-	}
-	if (block > EXT2_NDIR_BLOCKS + addr_per_block +
-		(1 << (addr_per_block_bits * 2)) +
-		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
-		ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
-		return NULL;
-	}
+	if (depth == 0)
+		goto fail;
 	/*
 	 * If this is a sequential block allocation, set the next_alloc_block
 	 * to this block now so that all the indblock and data block
@@ -380,31 +390,14 @@
 	}
 
 	*err = -ENOSPC;
-	b = block;
-	if (block < EXT2_NDIR_BLOCKS)
-		return inode_getblk (inode, block, create, b, err);
-	block -= EXT2_NDIR_BLOCKS;
-	if (block < addr_per_block) {
-		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err);
-		return block_getblk (inode, bh, block, create,
-				     inode->i_sb->s_blocksize, b, err);
-	}
-	block -= addr_per_block;
-	if (block < (1 << (addr_per_block_bits * 2))) {
-		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
-		bh = block_getblk (inode, bh, block >> addr_per_block_bits,
-				   create, inode->i_sb->s_blocksize, b, err);
-		return block_getblk (inode, bh, block & (addr_per_block - 1),
-				     create, inode->i_sb->s_blocksize, b, err);
-	}
-	block -= (1 << (addr_per_block_bits * 2));
-	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
-	bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
-			   create, inode->i_sb->s_blocksize, b, err);
-	bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
-			   create, inode->i_sb->s_blocksize, b, err);
-	return block_getblk (inode, bh, block & (addr_per_block - 1), create,
-			     inode->i_sb->s_blocksize, b, err);
+	bh = inode_getblk (inode, *(p=offsets), create, block, err);
+	while (--depth) {
+		bh = block_getblk (inode, bh, *++p, create,
+				     inode->i_sb->s_blocksize, block, err);
+	}
+	return bh;
+fail:
+	return NULL;
 }
 
 struct buffer_head * ext2_bread (struct inode * inode, int block, 
@@ -519,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);
@@ -543,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;
@@ -553,11 +555,8 @@
 			    "New inode has non-zero prealloc count!");
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		inode->i_rdev = to_kdev_t(le32_to_cpu(raw_inode->i_block[0]));
-	else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]);
+		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
 	brelse (bh);
 	inode->i_op = NULL;
 	if (inode->i_ino == EXT2_ACL_IDX_INO ||
@@ -658,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);
@@ -674,14 +674,10 @@
 		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 if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]);
+		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	mark_buffer_dirty(bh, 1);
 	if (do_sync) {
 		ll_rw_block (WRITE, 1, &bh);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/ioctl.c linux.18p17/fs/ext2/ioctl.c
--- linux.vanilla/fs/ext2/ioctl.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/ioctl.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ext2/truncate.c linux.18p17/fs/ext2/truncate.c
--- linux.vanilla/fs/ext2/truncate.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/ext2/truncate.c	Mon Sep 11 17:37:18 2000
@@ -130,11 +130,7 @@
 			goto in_use;
 
 	if (bh->b_count == 1) {
-		int tmp;
-		if (ind_bh)
-			tmp = le32_to_cpu(*p);
-		else
-			tmp = *p;
+		int tmp = le32_to_cpu(*p);
 		*p = 0;
 		inode->i_blocks -= (inode->i_sb->s_blocksize / 512);
 		mark_inode_dirty(inode);
@@ -170,7 +166,7 @@
 
 	for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
 		u32 * p = inode->u.ext2_i.i_data + i;
-		int tmp = *p;
+		int tmp = le32_to_cpu(*p);
 
 		if (!tmp)
 			continue;
@@ -215,11 +211,11 @@
 	unsigned long block_to_free = 0, free_count = 0;
 	int indirect_block, addr_per_block, blocks;
 
-	tmp = dind_bh ? le32_to_cpu(*p) : *p;
+	tmp = le32_to_cpu(*p);
 	if (!tmp)
 		return 0;
 	ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) {
+	if (tmp != le32_to_cpu(*p)) {
 		brelse (ind_bh);
 		return 1;
 	}
@@ -297,11 +293,11 @@
 	int i, tmp, retry = 0;
 	int dindirect_block, addr_per_block;
 
-	tmp = tind_bh ? le32_to_cpu(*p) : *p;
+	tmp = le32_to_cpu(*p);
 	if (!tmp)
 		return 0;
 	dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) {
+	if (tmp != le32_to_cpu(*p)) {
 		brelse (dind_bh);
 		return 1;
 	}
@@ -346,7 +342,8 @@
 
 	if (!(tmp = *p))
 		return 0;
-	tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	tind_bh = bread (inode->i_dev, le32_to_cpu(tmp),
+						inode->i_sb->s_blocksize);
 	if (tmp != *p) {
 		brelse (tind_bh);
 		return 1;
@@ -355,7 +352,7 @@
 	if (!tind_bh) {
 		ext2_error(inode->i_sb, "trunc_tindirect",
 			"Read failure, inode=%ld, block=%d",
-			inode->i_ino, tmp);
+			inode->i_ino, le32_to_cpu(tmp));
 		*p = 0;
 		mark_inode_dirty(inode);
 		return 0;
@@ -391,6 +388,8 @@
 		return;
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		return;
+	
+	ext2_remove_suid(inode); 
 	ext2_discard_prealloc(inode);
 	while (1) {
 		retry = trunc_direct(inode);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/fat/dir.c linux.18p17/fs/fat/dir.c
--- linux.vanilla/fs/fat/dir.c	Sun Jun 11 21:44:18 2000
+++ linux.18p17/fs/fat/dir.c	Thu Aug 31 14:33:07 2000
@@ -139,6 +139,7 @@
 	unsigned char long_slots;
 	int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
 	int utf8 = MSDOS_SB(sb)->options.utf8;
+	int small_letter = MSDOS_SB(sb)->options.small_letter;
 	unsigned char *unicode = NULL;
 	struct nls_table *nls = MSDOS_SB(sb)->nls_io;
 	int res = 0;
@@ -226,7 +227,7 @@
 
 		for (i = 0, last = 0; i < 8;) {
 			if (!(c = de->name[i])) break;
-			if (c >= 'A' && c <= 'Z') c += 32;
+			if (small_letter) c = tolower(c);
 			if (c == 0x05) c = 0xE5;
 			if ((bufname[i++] = c) != ' ')
 				last = i;
@@ -235,7 +236,7 @@
 		bufname[i++] = '.';
 		for (i2 = 0; i2 < 3; i2++) {
 			if (!(c = de->ext[i2])) break;
-			if (c >= 'A' && c <= 'Z') c += 32;
+			if (small_letter) c = tolower(c);
 			if ((bufname[i++] = c) != ' ')
 				last = i;
 		}
@@ -301,6 +302,7 @@
 	unsigned char long_slots;
 	int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
 	int utf8 = MSDOS_SB(sb)->options.utf8;
+	int small_letter = MSDOS_SB(sb)->options.small_letter;
 	unsigned char *unicode = NULL;
 	struct nls_table *nls = MSDOS_SB(sb)->nls_io;
 	char bufname[14];
@@ -419,7 +421,7 @@
 	}
 	for (i = 0, last = 0; i < 8;) {
 		if (!(c = de->name[i])) break;
-		if (c >= 'A' && c <= 'Z') c += 32;
+		if (small_letter) c = tolower(c);
 		/* see namei.c, msdos_format_name */
 		if (c == 0x05) c = 0xE5;
 		if ((ptname[i++] = c) != ' ')
@@ -429,7 +431,7 @@
 	ptname[i++] = '.';
 	for (i2 = 0; i2 < 3; i2++) {
 		if (!(c = de->ext[i2])) break;
-		if (c >= 'A' && c <= 'Z') c += 32;
+		if (small_letter) c = tolower(c);
 		if ((ptname[i++] = c) != ' ')
 			last = i;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/fat/inode.c linux.18p17/fs/fat/inode.c
--- linux.vanilla/fs/fat/inode.c	Sun Jun 11 21:44:18 2000
+++ linux.18p17/fs/fat/inode.c	Mon Oct  2 10:25:07 2000
@@ -216,6 +216,7 @@
 	opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
 	opts->codepage = 0;
 	opts->utf8 = 0;
+	opts->small_letter = 1;		/* Default to old behaviour */
 	opts->iocharset = NULL;
 	*debug = *fat = 0;
 
@@ -260,6 +261,12 @@
 		}
 		else if (!strcmp(this_char,"showexec")) {
 			opts->showexec = 1;
+		}
+		else if (!strcmp(this_char,"small")) {
+			opts->small_letter = 1;
+		}
+		else if (!strcmp(this_char,"big")) {
+			opts->small_letter = 0;
 		}
 		else if (!strcmp(this_char,"dotsOK") && value) {
 			if (!strcmp(value,"yes")) opts->dotsOK = 1;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/filesystems.c linux.18p17/fs/filesystems.c
--- linux.vanilla/fs/filesystems.c	Fri Apr 21 12:46:42 2000
+++ linux.18p17/fs/filesystems.c	Mon Sep 25 16:06:01 2000
@@ -49,6 +49,9 @@
 extern int init_devpts_fs(void);
 #endif
 
+extern int init_adfs_fs(void);
+
+
 void __init filesystem_setup(void)
 {
 #ifdef CONFIG_EXT2_FS
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/inode.c linux.18p17/fs/inode.c
--- linux.vanilla/fs/inode.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/inode.c	Fri Sep 15 22:10:43 2000
@@ -529,7 +529,7 @@
 /*
  * Called with the inode lock held.
  */
-static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head)
+static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
 {
 	struct list_head *tmp;
 	struct inode * inode;
@@ -545,6 +545,8 @@
 			continue;
 		if (inode->i_ino != ino)
 			continue;
+		if (find_actor && !find_actor(inode, ino, opaque))
+			continue;
 		inode->i_count++;
 		break;
 	}
@@ -619,7 +621,7 @@
  * We no longer cache the sb_flags in i_flags - see fs.h
  *	-- rmk@arm.uk.linux.org
  */
-static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head)
+static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
 {
 	struct inode * inode;
 	struct list_head * tmp = inode_unused.next;
@@ -664,7 +666,7 @@
 	inode = grow_inodes();
 	if (inode) {
 		/* We released the lock, so.. */
-		struct inode * old = find_inode(sb, ino, head);
+		struct inode * old = find_inode(sb, ino, head, find_actor, opaque);
 		if (!old)
 			goto add_new_inode;
 		list_add(&inode->i_list, &inode_unused);
@@ -694,7 +696,7 @@
 retry:
 	if (counter > max_reserved) {
 		head = inode_hashtable + hash(sb,counter);
-		inode = find_inode(sb, res = counter++, head);
+		inode = find_inode(sb, res = counter++, head, NULL, NULL);
 		if (!inode) {
 			spin_unlock(&inode_lock);
 			return res;
@@ -720,13 +722,13 @@
 	return inode;
 }
 
-struct inode *iget(struct super_block *sb, unsigned long ino)
+struct inode *iget4(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
 {
 	struct list_head * head = inode_hashtable + hash(sb,ino);
 	struct inode * inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode(sb, ino, head);
+	inode = find_inode(sb, ino, head, find_actor, opaque);
 	if (inode) {
 		spin_unlock(&inode_lock);
 		wait_on_inode(inode);
@@ -737,7 +739,12 @@
 	 * the inode lock and re-trying the search in case it
 	 * had to block at any point.
 	 */
-	return get_new_inode(sb, ino, head);
+	return get_new_inode(sb, ino, head, find_actor, opaque);
+}
+
+struct inode *iget(struct super_block *sb, unsigned long ino)
+{
+	return iget4(sb, ino, NULL, NULL);
 }
 
 void insert_inode_hash(struct inode *inode)
@@ -898,13 +905,13 @@
 	struct inode * inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode(sb, ino, head);
+	inode = find_inode(sb, ino, head, NULL, NULL);
 	if (inode) {
 		spin_unlock(&inode_lock);
 		wait_on_inode(inode);
 	}
 	else
-		inode = get_new_inode (sb, ino, head);
+		inode = get_new_inode (sb, ino, head, NULL, NULL);
 
 	/* When we get the inode, we have to check if it is in use. We
 	   have to release it if it is not. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ioctl.c linux.18p17/fs/ioctl.c
--- linux.vanilla/fs/ioctl.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/ioctl.c	Thu Aug 31 15:57:50 2000
@@ -84,8 +84,12 @@
 			/* Did FASYNC state change ? */
 			if ((flag ^ filp->f_flags) & FASYNC) {
 				if (filp->f_op && filp->f_op->fasync)
-					filp->f_op->fasync(fd, filp, on); 
+					error = filp->f_op->fasync(fd, filp, on);
+				else error = -ENOTTY;
 			}
+			if (error != 0)
+				break;
+
 			if (on)
 				filp->f_flags |= FASYNC;
 			else
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/Makefile linux.18p17/fs/lockd/Makefile
--- linux.vanilla/fs/lockd/Makefile	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/Makefile	Sun Oct 15 21:15:17 2000
@@ -9,7 +9,12 @@
 
 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 svc4proc.o
+#endif
+
 OX_OBJS  := lockd_syms.o
 M_OBJS   := $(O_TARGET)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/clntlock.c linux.18p17/fs/lockd/clntlock.c
--- linux.vanilla/fs/lockd/clntlock.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/clntlock.c	Fri Sep 15 22:10:43 2000
@@ -71,7 +71,7 @@
 	 * a 1 minute timeout would do. See the comment before
 	 * nlmclnt_lock for an explanation.
 	 */
-	sleep_on_timeout(&block.b_wait, 30*HZ);
+	interruptible_sleep_on_timeout(&block.b_wait, 30*HZ);
 
 	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
 		if (*head == &block) {
@@ -152,7 +152,7 @@
 		host->h_monitored = 0;
 		host->h_nsmstate = newstate;
 		host->h_state++;
-		host->h_count++;
+		nlm_get_host(host);
 		kernel_thread(reclaimer, host, 0);
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/clntproc.c linux.18p17/fs/lockd/clntproc.c
--- linux.vanilla/fs/lockd/clntproc.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/clntproc.c	Fri Sep 15 22:10:43 2000
@@ -46,11 +46,13 @@
 {
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_lock	*lock = &argp->lock;
+	struct dentry   *dentry = fl->fl_file->f_dentry;
+	struct nfs_fh   *fh = NFS_FH(dentry);
 
 	memset(argp, 0, sizeof(*argp));
 	nlmclnt_next_cookie(&argp->cookie);
 	argp->state   = nsm_local_state;
-	lock->fh      = *NFS_FH(fl->fl_file->f_dentry);
+	memcpy(&lock->fh, fh, sizeof(*fh));
 	lock->caller  = system_utsname.nodename;
 	lock->oh.data = req->a_owner;
 	lock->oh.len  = sprintf(req->a_owner, "%d@%s",
@@ -100,15 +102,22 @@
 int
 nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
 {
-	struct nfs_server	*nfssrv = NFS_SERVER(inode);
 	struct nlm_host		*host;
 	struct nlm_rqst		reqst, *call = &reqst;
 	sigset_t		oldset;
 	unsigned long		flags;
-	int			status;
+	int			status, proto, vers;
 
-	/* Always use NLM version 1 over UDP for now... */
-	if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), IPPROTO_UDP, 1)))
+	vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
+	if (NFS_PROTO(inode)->version > 3) {
+		printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
+		return -ENOLCK;
+	}
+
+	/* Retrieve transport protocol from NFS client */
+	proto = NFS_CLIENT(inode)->cl_xprt->prot;
+
+	if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
 		return -ENOLCK;
 
 	/* Create RPC client handle if not there, and copy soft
@@ -122,9 +131,9 @@
 			status = -ENOLCK;
 			goto done;
 		}
-		clnt->cl_softrtry = nfssrv->client->cl_softrtry;
-		clnt->cl_intr     = nfssrv->client->cl_intr;
-		clnt->cl_chatty   = nfssrv->client->cl_chatty;
+		clnt->cl_softrtry = NFS_CLIENT(inode)->cl_softrtry;
+		clnt->cl_intr     = NFS_CLIENT(inode)->cl_intr;
+		clnt->cl_chatty   = NFS_CLIENT(inode)->cl_chatty;
 	}
 
 	/* Keep the old signal mask */
@@ -141,6 +150,10 @@
 		spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
 		call = nlmclnt_alloc_call();
+		if (!call) {
+			status = -ENOMEM;
+			goto out_restore;
+		}
 		call->a_flags = RPC_TASK_ASYNC;
 	} else {
 		spin_unlock_irqrestore(&current->sigmask_lock, flags);
@@ -164,8 +177,9 @@
 	}
 
 	if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
-		rpc_free(call);
+		kfree(call);
 
+out_restore:
 	spin_lock_irqsave(&current->sigmask_lock, flags);
 	current->blocked = oldset;
 	recalc_sigpending(current);
@@ -199,8 +213,7 @@
 	struct nlm_rqst	*call;
 
 	while (!signalled()) {
-		call = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
-					sizeof(struct nlm_rqst));
+		call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
 		if (call)
 			return call;
 		printk("nlmclnt_alloc_call: failed, waiting for memory\n");
@@ -220,11 +233,21 @@
 	struct rpc_clnt	*clnt;
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_res	*resp = &req->a_res;
+	struct file	*filp = argp->lock.fl.fl_file;
+	struct rpc_message msg;
 	int		status;
 
 	dprintk("lockd: call procedure %s on %s\n",
 			nlm_procname(proc), host->h_name);
 
+	msg.rpc_proc = proc;
+	msg.rpc_argp = argp;
+	msg.rpc_resp = resp;
+	if (filp)
+		msg.rpc_cred = nfs_file_cred(filp);
+	else
+		msg.rpc_cred = NULL;
+
 	do {
 		if (host->h_reclaiming && !argp->reclaim) {
 			interruptible_sleep_on(&host->h_gracewait);
@@ -236,7 +259,7 @@
 			return -ENOLCK;
 
 		/* Perform the RPC call. If an error occurs, try again */
-		if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) {
+		if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
 			dprintk("lockd: rpc_call returned error %d\n", -status);
 			switch (status) {
 			case -EPROTONOSUPPORT:
@@ -245,6 +268,7 @@
 			case -ECONNREFUSED:
 			case -ETIMEDOUT:
 			case -ENOTCONN:
+				nlm_rebind_host(host);
 				status = -EAGAIN;
 				break;
 			case -ERESTARTSYS:
@@ -252,10 +276,7 @@
 			default:
 				break;
 			}
-			if (req->a_args.block)
-				nlm_rebind_host(host);
-			else
-				break;
+			break;
 		} else
 		if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
 			dprintk("lockd: server in grace period\n");
@@ -288,13 +309,15 @@
 /*
  * Generic NLM call, async version.
  */
-int
-nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+static int
+_nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback,
+		    struct rpc_cred *cred)
 {
 	struct nlm_host	*host = req->a_host;
 	struct rpc_clnt	*clnt;
 	struct nlm_args	*argp = &req->a_args;
 	struct nlm_res	*resp = &req->a_res;
+	struct rpc_message msg;
 	int		status;
 
 	dprintk("lockd: call procedure %s on %s (async)\n",
@@ -304,16 +327,42 @@
 	if ((clnt = nlm_bind_host(host)) == NULL)
 		return -ENOLCK;
 
+	/* Increment host refcount */
+        nlm_get_host(host);
+
         /* bootstrap and kick off the async RPC call */
-        status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC,
-					callback, req);
+	msg.rpc_proc = proc;
+	msg.rpc_argp = argp;
+	msg.rpc_resp =resp;
+	msg.rpc_cred = cred;
+        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
 
-	/* If the async call is proceeding, increment host refcount */
-        if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC))
-                host->h_count++;
+	if (status < 0)
+		nlm_release_host(host);
 	return status;
 }
 
+
+int
+nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+	struct nlm_args	*argp = &req->a_args;
+	struct file	*filp = argp->lock.fl.fl_file;
+	struct rpc_cred *cred = NULL;
+
+	if (filp)
+		cred = nfs_file_cred(filp);
+
+	return _nlmclnt_async_call(req, proc, callback, cred);
+}
+
+int
+nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+	return _nlmclnt_async_call(req, proc, callback, NULL);
+}
+
+
 /*
  * TEST for the presence of a conflicting lock
  */
@@ -328,7 +377,7 @@
 	status = req->a_res.status;
 	if (status == NLM_LCK_GRANTED) {
 		fl->fl_type = F_UNLCK;
-	} if (status == NLM_LCK_DENIED) {
+	} else if (status == NLM_LCK_DENIED) {
 		/*
 		 * Report the conflicting lock back to the application.
 		 * FIXME: Is it OK to report the pid back as well?
@@ -342,6 +391,21 @@
 	return 0;
 }
 
+static
+void nlmclnt_insert_lock_callback(struct file_lock *fl)
+{
+	nlm_get_host(fl->fl_u.nfs_fl.host);
+}
+static
+void nlmclnt_remove_lock_callback(struct file_lock *fl)
+{
+	if (fl->fl_u.nfs_fl.host) {
+		nlm_release_host(fl->fl_u.nfs_fl.host);
+		fl->fl_u.nfs_fl.host = NULL;
+	}
+}
+
+
 /*
  * LOCK: Try to create a lock
  *
@@ -375,7 +439,7 @@
 		return -ENOLCK;
 	}
 
-	while (1) {
+	do {
 		if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
 			if (resp->status != NLM_LCK_BLOCKED)
 				break;
@@ -383,11 +447,14 @@
 		}
 		if (status < 0)
 			return status;
-	}
+	} while (resp->status == NLM_LCK_BLOCKED);
 
 	if (resp->status == NLM_LCK_GRANTED) {
 		fl->fl_u.nfs_fl.state = host->h_state;
 		fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
+		fl->fl_u.nfs_fl.host = host;
+		fl->fl_insert = nlmclnt_insert_lock_callback;
+		fl->fl_remove = nlmclnt_remove_lock_callback;
 	}
 
 	return nlm_stat_to_errno(resp->status);
@@ -439,15 +506,9 @@
 static int
 nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
 {
-	struct nlm_host	*host = req->a_host;
 	struct nlm_res	*resp = &req->a_res;
 	int		status;
 
-	/* No monitor, no lock: see nlmclnt_lock().
-	 * Since this is an UNLOCK, don't try to setup monitoring here. */
-	if (!host->h_monitored)
-		return -ENOLCK;
-
 	/* Clean the GRANTED flag now so the lock doesn't get
 	 * reclaimed while we're stuck in the unlock call. */
 	fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
@@ -482,17 +543,20 @@
 
 	if (task->tk_status < 0) {
 		dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
-		nlm_rebind_host(req->a_host);
-		rpc_restart_call(task);
-		return;
+		goto retry_unlock;
 	}
 	if (status != NLM_LCK_GRANTED
 	 && status != NLM_LCK_DENIED_GRACE_PERIOD) {
 		printk("lockd: unexpected unlock status: %d\n", status);
 	}
-
-die:
-	rpc_release_task(task);
+ die:
+	nlm_release_host(req->a_host);
+	kfree(req);
+	return;
+ retry_unlock:
+	nlm_rebind_host(req->a_host);
+	rpc_restart_call(task);
+	return;
 }
 
 /*
@@ -515,10 +579,9 @@
 	recalc_sigpending(current);
 	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
-	do {
-		req = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
-							sizeof(*req));
-	} while (req == NULL);
+	req = nlmclnt_alloc_call();
+	if (!req)
+		return -ENOMEM;
 	req->a_host  = host;
 	req->a_flags = RPC_TASK_ASYNC;
 
@@ -527,7 +590,7 @@
 	status = nlmclnt_async_call(req, NLMPROC_CANCEL,
 					nlmclnt_cancel_callback);
 	if (status < 0)
-		rpc_free(req);
+		kfree(req);
 
 	spin_lock_irqsave(&current->sigmask_lock, flags);
 	current->blocked = oldset;
@@ -568,7 +631,6 @@
 	}
 
 die:
-	rpc_release_task(task);
 	nlm_release_host(req->a_host);
 	kfree(req);
 	return;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/host.c linux.18p17/fs/lockd/host.c
--- linux.vanilla/fs/lockd/host.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/host.c	Fri Sep 15 22:10:43 2000
@@ -15,6 +15,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
 
 
 #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
@@ -105,8 +106,7 @@
 				host->h_next = nlm_hosts[hash];
 				nlm_hosts[hash] = host;
 			}
-			host->h_expires = jiffies + NLM_HOST_EXPIRE;
-			host->h_count++;
+			nlm_get_host(host);
 			up(&nlm_host_sema);
 			return host;
 		}
@@ -171,9 +171,12 @@
 	down(&host->h_sema);
 
 	/* If we've already created an RPC client, check whether
-	 * RPC rebind is required */
+	 * RPC rebind is required
+	 * Note: why keep rebinding if we're on a tcp connection?
+	 */
 	if ((clnt = host->h_rpcclnt) != NULL) {
-		if (time_after_eq(jiffies, host->h_nextrebind)) {
+		xprt = clnt->cl_xprt;
+		if (!xprt->stream && time_after_eq(jiffies, host->h_nextrebind)) {
 			clnt->cl_port = 0;
 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 			dprintk("lockd: next rebind in %ld jiffies\n",
@@ -229,13 +232,27 @@
 }
 
 /*
+ * Increment NLM host count
+ */
+struct nlm_host * nlm_get_host(struct nlm_host *host)
+{
+	if (host) {
+		dprintk("lockd: get host %s\n", host->h_name);
+		host->h_count ++;
+		host->h_expires = jiffies + NLM_HOST_EXPIRE;
+	}
+	return host;
+}
+
+/*
  * Release NLM host after use
  */
-void
-nlm_release_host(struct nlm_host *host)
+void nlm_release_host(struct nlm_host *host)
 {
-	dprintk("lockd: release host %s\n", host->h_name);
-	host->h_count -= 1;
+	if (host && host->h_count) {
+		dprintk("lockd: release host %s\n", host->h_name);
+		host->h_count --;
+	}
 }
 
 /*
@@ -307,8 +324,10 @@
 			}
 			dprintk("lockd: delete host %s\n", host->h_name);
 			*q = host->h_next;
+			if (host->h_monitored)
+				nsm_unmonitor(host);
 			if ((clnt = host->h_rpcclnt) != NULL) {
-				if (clnt->cl_users) {
+				if (atomic_read(&clnt->cl_users)) {
 					printk(KERN_WARNING
 						"lockd: active RPC handle\n");
 					clnt->cl_dead = 1;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/lockd_syms.c linux.18p17/fs/lockd/lockd_syms.c
--- linux.vanilla/fs/lockd/lockd_syms.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/lockd_syms.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/mon.c linux.18p17/fs/lockd/mon.c
--- linux.vanilla/fs/lockd/mon.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/mon.c	Fri Sep 15 22:10:43 2000
@@ -4,6 +4,10 @@
  * The kernel statd client.
  *
  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Note: In a future release, we should fold all NSM activity into
+ * rpc.mountd and the mount program, respectively. Stuff like this
+ * really doesn't belong in the kernel.	--okir
  */
 
 #include <linux/types.h>
@@ -30,14 +34,12 @@
  * Common procedure for SM_MON/SM_UNMON calls
  */
 static int
-nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
+nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res)
 {
 	struct rpc_clnt	*clnt;
 	int		status;
 	struct nsm_args	args;
-	struct nsm_res	res;
 
-	dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
 	status = -EACCES;
 	clnt = nsm_create();
 	if (!clnt)
@@ -47,23 +49,15 @@
 	args.prog = NLM_PROGRAM;
 	args.vers = 1;
 	args.proc = NLMPROC_NSM_NOTIFY;
+	memset(res, 0, sizeof(*res));
 
-	status = rpc_call(clnt, proc, &args, &res, 0);
-	if (status < 0) {
+	status = rpc_call(clnt, proc, &args, res, 0);
+	if (status < 0)
 		printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
 			status);
-		goto out;
-	}
-
-	status = -EACCES;
-	if (res.status != 0) {
-		printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
-		goto out;
-	}
-
-	nsm_local_state = res.state;
-	status = 0;
-out:
+	else
+		status = 0;
+ out:
 	return status;
 }
 
@@ -73,10 +67,16 @@
 int
 nsm_monitor(struct nlm_host *host)
 {
+	struct nsm_res	res;
 	int		status;
 
-	status = nsm_mon_unmon(host, "monitor", SM_MON);
-	if (status >= 0)
+	dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
+
+	status = nsm_mon_unmon(host, SM_MON, &res);
+
+	if (status < 0 || res.status != 0)
+		printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
+	else
 		host->h_monitored = 1;
 	return status;
 }
@@ -87,9 +87,15 @@
 int
 nsm_unmonitor(struct nlm_host *host)
 {
+	struct nsm_res	res;
 	int		status;
 
-	if ((status = nsm_mon_unmon(host, "unmonitor", SM_UNMON)) >= 0)
+	dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
+
+	status = nsm_mon_unmon(host, SM_UNMON, &res);
+	if (status < 0)
+		printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", host->h_name);
+	else
 		host->h_monitored = 0;
 	return status;
 }
@@ -155,15 +161,15 @@
 	 */
 	sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
 				 	(addr>>8) & 0xff,  (addr) & 0xff);
-	if (!(p = xdr_encode_string(p, buffer))
-	 || !(p = xdr_encode_string(p, system_utsname.nodename)))
+	if (!(p = xdr_encode_string(p, buffer, -1))
+	 || !(p = xdr_encode_string(p, system_utsname.nodename, -1)))
 		return -EIO;
 	*p++ = htonl(argp->prog);
 	*p++ = htonl(argp->vers);
 	*p++ = htonl(argp->proc);
 
 	/* This is the private part. Needed only for SM_MON call */
-	if (rqstp->rq_task->tk_proc == SM_MON) {
+	if (rqstp->rq_task->tk_msg.rpc_proc == SM_MON) {
 		*p++ = argp->addr;
 		*p++ = 0;
 		*p++ = 0;
@@ -187,7 +193,7 @@
 static int
 xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
 {
-	resp->status = ntohl(*p++);
+	resp->state = ntohl(*p++);
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svc.c linux.18p17/fs/lockd/svc.c
--- linux.vanilla/fs/lockd/svc.c	Sat Sep  9 18:42:47 2000
+++ linux.18p17/fs/lockd/svc.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svc4proc.c linux.18p17/fs/lockd/svc4proc.c
--- linux.vanilla/fs/lockd/svc4proc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/lockd/svc4proc.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svclock.c linux.18p17/fs/lockd/svclock.c
--- linux.vanilla/fs/lockd/svclock.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/svclock.c	Sun Oct 15 21:15:17 2000
@@ -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;
 }
 
 /*
@@ -520,7 +531,7 @@
 	nlmsvc_insert_block(block, jiffies + 30 * HZ);
 
 	/* Call the client */
-	nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+	nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
 						nlmsvc_grant_callback);
 	up(&file->f_sema);
 }
@@ -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;
@@ -564,7 +577,6 @@
 	block->b_incall = 0;
 
 	nlm_release_host(call->a_host);
-	rpc_release_task(task);
 }
 
 /*
@@ -617,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)
@@ -628,6 +640,5 @@
 
 	if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
 		return (block->b_when - jiffies);
-
 	return MAX_SCHEDULE_TIMEOUT;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svcproc.c linux.18p17/fs/lockd/svcproc.c
--- linux.vanilla/fs/lockd/svcproc.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/svcproc.c	Sun Oct 15 21:15:17 2000
@@ -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);
@@ -476,7 +493,7 @@
 	call->a_host  = host;
 	memcpy(&call->a_args, resp, sizeof(*resp));
 
-	if (nlmclnt_async_call(call, proc, nlmsvc_callback_exit) < 0)
+	if (nlmsvc_async_call(call, proc, nlmsvc_callback_exit) < 0)
 		return rpc_system_err;
 
 	return rpc_success;
@@ -492,10 +509,30 @@
 					task->tk_pid, -task->tk_status);
 	}
 	nlm_release_host(call->a_host);
-	rpc_release_task(task);
 	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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svcshare.c linux.18p17/fs/lockd/svcshare.c
--- linux.vanilla/fs/lockd/svcshare.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/svcshare.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/svcsubs.c linux.18p17/fs/lockd/svcsubs.c
--- linux.vanilla/fs/lockd/svcsubs.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/svcsubs.c	Sun Oct 15 21:15:17 2000
@@ -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>
@@ -48,7 +49,7 @@
 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 					struct nfs_fh *f)
 {
-	struct knfs_fh	*fh = (struct knfs_fh *) f;
+	struct knfs_fh	*fh = (struct knfs_fh *) f->data;
 	struct nlm_file	*file;
 	unsigned int	hash;
 	u32		nfserr;
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/xdr.c linux.18p17/fs/lockd/xdr.c
--- linux.vanilla/fs/lockd/xdr.c	Fri Apr 21 12:46:43 2000
+++ linux.18p17/fs/lockd/xdr.c	Sun Oct 15 21:15:17 2000
@@ -6,20 +6,25 @@
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/utsname.h>
 #include <linux/nfs.h>
+#include <linux/nfs2.h>
 
 #include <linux/sunrpc/xdr.h>
 #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>
 
+
 #define NLMDBG_FACILITY		NLMDBG_XDR
 #define NLM_MAXSTRLEN		1024
+#define OFFSET_MAX		LONG_MAX
 
 #define QUADLEN(len)		(((len) + 3) >> 2)
 
@@ -29,6 +34,8 @@
 
 
 typedef struct nlm_args	nlm_args;
+static void nlm_register_stats(void);
+static void nlm_unregister_stats(void);
 
 /*
  * Initialization of NFS status variables
@@ -47,7 +54,26 @@
 	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();
+}
+
+void
+nlmxdr_shutdown(void)
+{
+	nlm_unregister_stats();
 }
 
 /*
@@ -93,22 +119,23 @@
 {
 	unsigned int	len;
 
-	if ((len = ntohl(*p++)) != sizeof(*f)) {
+	if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
 		printk(KERN_NOTICE
 			"lockd: bad fhandle size %x (should be %d)\n",
-			len, sizeof(*f));
+			len, NFS2_FHSIZE);
 		return NULL;
 	}
-	memcpy(f, p, sizeof(*f));
-	return p + XDR_QUADLEN(sizeof(*f));
+	f->size = NFS2_FHSIZE;
+	memcpy(f->data, p, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
 nlm_encode_fh(u32 *p, struct nfs_fh *f)
 {
-	*p++ = htonl(sizeof(*f));
-	memcpy(p, f, sizeof(*f));
-	return p + XDR_QUADLEN(sizeof(*f));
+	*p++ = htonl(NFS2_FHSIZE);
+	memcpy(p, f->data, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 /*
@@ -145,7 +172,7 @@
 	fl->fl_start = ntohl(*p++);
 	len = ntohl(*p++);
 	if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
-		fl->fl_end = NLM_OFFSET_MAX;
+		fl->fl_end = OFFSET_MAX;
 	return p;
 }
 
@@ -157,17 +184,21 @@
 {
 	struct file_lock	*fl = &lock->fl;
 
-	if (!(p = xdr_encode_string(p, lock->caller))
+	if (!(p = xdr_encode_string(p, lock->caller, -1))
 	 || !(p = nlm_encode_fh(p, &lock->fh))
 	 || !(p = nlm_encode_oh(p, &lock->oh)))
 		return NULL;
 
+	if (fl->fl_start > NLM_OFFSET_MAX
+	 || (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
+		return NULL;
+
 	*p++ = htonl(fl->fl_pid);
-	*p++ = htonl(lock->fl.fl_start);
-	if (lock->fl.fl_end == NLM_OFFSET_MAX)
+	*p++ = htonl(fl->fl_start);
+	if (fl->fl_end == OFFSET_MAX)
 		*p++ = xdr_zero;
 	else
-		*p++ = htonl(lock->fl.fl_end - lock->fl.fl_start + 1);
+		*p++ = htonl(fl->fl_end - fl->fl_start + 1);
 
 	return p;
 }
@@ -193,7 +224,7 @@
 			return 0;
 
 		*p++ = htonl(fl->fl_start);
-		if (fl->fl_end == NLM_OFFSET_MAX)
+		if (fl->fl_end == OFFSET_MAX)
 			*p++ = xdr_zero;
 		else
 			*p++ = htonl(fl->fl_end - fl->fl_start + 1);
@@ -428,7 +459,7 @@
 		fl->fl_start = ntohl(*p++);
 		len = ntohl(*p++);
 		if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
-			fl->fl_end = NLM_OFFSET_MAX;
+			fl->fl_end = OFFSET_MAX;
 	}
 	return 0;
 }
@@ -512,10 +543,10 @@
  */
 #define NLM_void_sz		0
 #define NLM_cookie_sz		3	/* 1 len , 2 data */
-#define NLM_caller_sz		1+QUADLEN(sizeof(system_utsname.nodename))
+#define NLM_caller_sz		1+QUADLEN(NLM_MAXSTRLEN)
 #define NLM_netobj_sz		1+QUADLEN(XDR_MAX_NETOBJ)
 /* #define NLM_owner_sz		1+QUADLEN(NLM_MAXOWNER) */
-#define NLM_fhandle_sz		1+QUADLEN(NFS_FHSIZE)
+#define NLM_fhandle_sz		1+QUADLEN(NFS2_FHSIZE)
 #define NLM_lock_sz		3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
 #define NLM_holder_sz		4+NLM_netobj_sz
 
@@ -541,7 +572,8 @@
     { "nlm_" #proc,						\
       (kxdrproc_t) nlmclt_encode_##argtype,			\
       (kxdrproc_t) nlmclt_decode_##restype,			\
-      MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2		\
+      MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2,		\
+      0                                                         \
     }
 
 static struct rpc_procinfo	nlm_procedures[] = {
@@ -586,14 +618,21 @@
 	3, 24, nlm_procedures,
 };
 
+#ifdef CONFIG_NFS_V3
+extern struct rpc_version nlm_version4;
+#endif
+
 static struct rpc_version *	nlm_versions[] = {
 	NULL,
 	&nlm_version1,
 	NULL,
 	&nlm_version3,
+#ifdef CONFIG_NFS_V3
+	&nlm_version4,
+#endif
 };
 
-static struct rpc_stat		nlm_stats;
+static struct rpc_stat		nlm_stats = { &nlm_program };
 
 struct rpc_program		nlm_program = {
 	"lockd",
@@ -613,3 +652,13 @@
 }
 #endif
 
+static void nlm_register_stats(void) {
+#ifdef CONFIG_PROC_FS
+	rpc_proc_register(&nlm_stats);
+#endif
+}
+static void nlm_unregister_stats(void) {
+#ifdef CONFIG_PROC_FS
+	rpc_proc_unregister("lockd");
+#endif
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/lockd/xdr4.c linux.18p17/fs/lockd/xdr4.c
--- linux.vanilla/fs/lockd/xdr4.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/lockd/xdr4.c	Sun Oct 15 21:15:17 2000
@@ -0,0 +1,594 @@
+/*
+ * linux/fs/lockd/xdr4.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1999, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY		NLMDBG_XDR
+#define NLM_MAXSTRLEN		1024
+#define OFFSET_MAX		((off_t)LONG_MAX)
+
+#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;
+
+static inline off_t
+size_to_off_t(__s64 size)
+{
+        size = (size > (__s64)LONG_MAX) ? (off_t)LONG_MAX : (off_t) size;
+        return (size < (__s64)-LONG_MAX) ? (off_t)-LONG_MAX : (off_t) size;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static u32 *
+nlm4_decode_cookie(u32 *p, struct nlm_cookie *c)
+{
+	unsigned int	len;
+
+	len = ntohl(*p++);
+	
+	if(len==0)
+	{
+		c->len=4;
+		memset(c->data, 0, 4);	/* hockeypux brain damage */
+	}
+	else if(len<=8)
+	{
+		c->len=len;
+		memcpy(c->data, p, len);
+		p+=(len+3)>>2;
+	}
+	else 
+	{
+		printk(KERN_NOTICE
+			"lockd: bad cookie size %d (only cookies under 8 bytes are supported.)\n", len);
+		return NULL;
+	}
+	return p;
+}
+
+static u32 *
+nlm4_encode_cookie(u32 *p, struct nlm_cookie *c)
+{
+	*p++ = htonl(c->len);
+	memcpy(p, c->data, c->len);
+	p+=(c->len+3)>>2;
+	return p;
+}
+
+static u32 *
+nlm4_decode_fh(u32 *p, struct nfs_fh *f)
+{
+	memset(f->data, 0, sizeof(f->data));
+	f->size = ntohl(*p++);
+	if (f->size > NFS_MAXFHSIZE) {
+		printk(KERN_NOTICE
+			"lockd: bad fhandle size %x (should be %d)\n",
+			f->size, NFS_MAXFHSIZE);
+		return NULL;
+	}
+      	memcpy(f->data, p, f->size);
+	return p + XDR_QUADLEN(f->size);
+}
+
+static u32 *
+nlm4_encode_fh(u32 *p, struct nfs_fh *f)
+{
+	*p++ = htonl(f->size);
+	memcpy(p, f->data, f->size);
+	return p + XDR_QUADLEN(f->size);
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static u32 *
+nlm4_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_decode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_encode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+	__s64			len, start, end;
+	int			tmp;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &tmp, NLM_MAXSTRLEN))
+	 || !(p = nlm4_decode_fh(p, &lock->fh))
+	 || !(p = nlm4_decode_oh(p, &lock->oh)))
+		return NULL;
+
+	memset(fl, 0, sizeof(*fl));
+	fl->fl_owner = current->files;
+	fl->fl_pid   = ntohl(*p++);
+	fl->fl_flags = FL_POSIX;
+	fl->fl_type  = F_RDLCK;		/* as good as anything else */
+	p = xdr_decode_hyper(p, &start);
+	p = xdr_decode_hyper(p, &len);
+	end = start + len - 1;
+
+	fl->fl_start = size_to_off_t(start);
+	fl->fl_end = size_to_off_t(end);
+
+	if (len == 0 || fl->fl_end < 0)
+		fl->fl_end = OFFSET_MAX;
+	return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm4_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+
+	if (!(p = xdr_encode_string(p, lock->caller, -1))
+	 || !(p = nlm4_encode_fh(p, &lock->fh))
+	 || !(p = nlm4_encode_oh(p, &lock->oh)))
+		return NULL;
+
+	*p++ = htonl(fl->fl_pid);
+	p = xdr_encode_hyper(p, fl->fl_start);
+	if (fl->fl_end == OFFSET_MAX)
+		p = xdr_encode_hyper(p, 0);
+	else
+		p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1);
+
+	return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+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 == nlm4_lck_denied) {
+		struct file_lock	*fl = &resp->lock.fl;
+
+		*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+		*p++ = htonl(fl->fl_pid);
+
+		/* Encode owner handle. */
+		if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+			return 0;
+
+		p = xdr_encode_hyper(p, fl->fl_start);
+		if (fl->fl_end == OFFSET_MAX)
+			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;
+}
+
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_argbuf;
+
+	return p - buf->base <= buf->buflen;
+}
+
+static int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlm4svc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+
+	exclusive = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_testres(p, resp)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block  = ntohl(*p++);
+	exclusive    = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	argp->reclaim = ntohl(*p++);
+	argp->state   = ntohl(*p++);
+	argp->monitor = 1;		/* monitor client by default */
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block = ntohl(*p++);
+	exclusive = ntohl(*p++);
+	if (!(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+	 || !(p = nlm4_decode_lock(p, &argp->lock)))
+		return 0;
+	argp->lock.fl.fl_type = F_UNLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	memset(lock, 0, sizeof(*lock));
+	lock->fl.fl_pid = ~(u32) 0;
+
+	if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+	 || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+	 || !(p = nlm4_decode_fh(p, &lock->fh))
+	 || !(p = nlm4_decode_oh(p, &lock->oh)))
+		return 0;
+	argp->fsm_mode = ntohl(*p++);
+	argp->fsm_access = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	*p++ = xdr_zero;		/* sequence argument */
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+	if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	argp->addr = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return 0;
+	resp->status = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlm4clt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	return 0;
+}
+
+static int
+nlm4clt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	if (resp->status == NLM_LCK_DENIED) {
+		struct file_lock	*fl = &resp->lock.fl;
+		u32			excl;
+		s64			start, end, len;
+
+		memset(&resp->lock, 0, sizeof(resp->lock));
+		excl = ntohl(*p++);
+		fl->fl_pid = ntohl(*p++);
+		if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
+			return -EIO;
+
+		fl->fl_flags = FL_POSIX;
+		fl->fl_type  = excl? F_WRLCK : F_RDLCK;
+		p = xdr_decode_hyper(p, &start);
+		p = xdr_decode_hyper(p, &len);
+		end = start + len - 1;
+
+		fl->fl_start = size_to_off_t(start);
+		fl->fl_end = size_to_off_t(end);
+		if (len == 0 || fl->fl_end < 0)
+			fl->fl_end = OFFSET_MAX;
+	}
+	return 0;
+}
+
+
+static int
+nlm4clt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	*p++ = argp->reclaim? xdr_one : xdr_zero;
+	*p++ = htonl(argp->state);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+		return -EIO;
+	if (!(p = nlm4_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+		return -EIO;
+	*p++ = resp->status;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_encode_testres(p, resp)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM4_void_sz		0
+#define NLM4_cookie_sz		3	/* 1 len , 2 data */
+#define NLM4_caller_sz		1+XDR_QUADLEN(NLM_MAXSTRLEN)
+#define NLM4_netobj_sz		1+XDR_QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM4_owner_sz		1+XDR_QUADLEN(NLM4_MAXOWNER) */
+#define NLM4_fhandle_sz		1+XDR_QUADLEN(NFS3_FHSIZE)
+#define NLM4_lock_sz		5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz
+#define NLM4_holder_sz		6+NLM4_netobj_sz
+
+#define NLM4_testargs_sz	NLM4_cookie_sz+1+NLM4_lock_sz
+#define NLM4_lockargs_sz	NLM4_cookie_sz+4+NLM4_lock_sz
+#define NLM4_cancargs_sz	NLM4_cookie_sz+2+NLM4_lock_sz
+#define NLM4_unlockargs_sz	NLM4_cookie_sz+NLM4_lock_sz
+
+#define NLM4_testres_sz		NLM4_cookie_sz+1+NLM4_holder_sz
+#define NLM4_res_sz		NLM4_cookie_sz+1
+#define NLM4_norep_sz		0
+
+#ifndef MAX
+# define MAX(a,b)		(((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm4clt_decode_norep	NULL
+
+#define PROC(proc, argtype, restype)				\
+    { "nlm4_" #proc,						\
+      (kxdrproc_t) nlm4clt_encode_##argtype,			\
+      (kxdrproc_t) nlm4clt_decode_##restype,			\
+      MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2,	\
+      0                                                         \
+    }
+
+static struct rpc_procinfo	nlm4_procedures[] = {
+    PROC(null,		void,		void),
+    PROC(test,		testargs,	testres),
+    PROC(lock,		lockargs,	res),
+    PROC(canc,		cancargs,	res),
+    PROC(unlock,	unlockargs,	res),
+    PROC(granted,	testargs,	res),
+    PROC(test_msg,	testargs,	norep),
+    PROC(lock_msg,	lockargs,	norep),
+    PROC(canc_msg,	cancargs,	norep),
+    PROC(unlock_msg,	unlockargs,	norep),
+    PROC(granted_msg,	testargs,	norep),
+    PROC(test_res,	testres,	norep),
+    PROC(lock_res,	res,		norep),
+    PROC(canc_res,	res,		norep),
+    PROC(unlock_res,	res,		norep),
+    PROC(granted_res,	res,		norep),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+    PROC(share,		shareargs,	shareres),
+    PROC(unshare,	shareargs,	shareres),
+    PROC(nm_lock,	lockargs,	res),
+    PROC(free_all,	notify,		void),
+#else
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#endif
+};
+
+struct rpc_version	nlm_version4 = {
+	4, 24, nlm4_procedures,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/locks.c linux.18p17/fs/locks.c
--- linux.vanilla/fs/locks.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/locks.c	Fri Sep 15 22:10:43 2000
@@ -415,13 +415,9 @@
 	 */
 	if (IS_MANDLOCK(inode) &&
 	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
-	    inode->i_mmap) {
-		struct vm_area_struct *vma = inode->i_mmap;
+	    inode->i_mmap_shared) {
 		error = -EAGAIN;
-		do {
-			if (vma->vm_flags & VM_MAYSHARE)
-				goto out_putf;
-		} while ((vma = vma->vm_next_share) != NULL);
+		goto out_putf;
 	}
 
 	error = -EINVAL;
@@ -1134,6 +1130,8 @@
 		new->fl_start = fl->fl_start;
 		new->fl_end = fl->fl_end;
 		new->fl_notify = fl->fl_notify;
+		new->fl_insert = fl->fl_insert;
+		new->fl_remove = fl->fl_remove;
 		new->fl_u = fl->fl_u;
 	}
 	return new;
@@ -1152,6 +1150,9 @@
 	fl->fl_next = *pos;	/* insert into file's list */
 	*pos = fl;
 
+	if (fl->fl_insert)
+		fl->fl_insert(fl);
+
 	return;
 }
 
@@ -1179,6 +1180,9 @@
 		prevfl->fl_nextlink = nextfl;
 	else
 		file_lock_table = nextfl;
+
+	if (thisfl->fl_remove)
+		thisfl->fl_remove(thisfl);
 	
 	locks_wake_up_blocks(thisfl, wait);
 	locks_free_lock(thisfl);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/namei.c linux.18p17/fs/namei.c
--- linux.vanilla/fs/namei.c	Sat Sep  9 18:42:47 2000
+++ linux.18p17/fs/namei.c	Fri Sep 15 22:10:43 2000
@@ -136,13 +136,11 @@
  * for filesystem access without changing the "normal" uids which
  * are used for other things..
  */
-int permission(struct inode * inode,int mask)
+int vfs_permission(struct inode * inode,int mask)
 {
 	int mode = inode->i_mode;
 
-	if (inode->i_op && inode->i_op->permission)
-		return inode->i_op->permission(inode, mask);
-	else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
+	if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
 		 (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
 		return -EROFS; /* Nobody gets write access to a read-only fs */
 	else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
@@ -159,6 +157,13 @@
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;
+}
+
+int permission(struct inode * inode,int mask)
+{
+	if (inode->i_op && inode->i_op->permission)
+		return inode->i_op->permission(inode, mask);
+	return vfs_permission(inode, mask);
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ncpfs/file.c linux.18p17/fs/ncpfs/file.c
--- linux.vanilla/fs/ncpfs/file.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/ncpfs/file.c	Mon Sep 11 18:56:51 2000
@@ -252,7 +252,7 @@
 		}
 		if (ncp_write_kernel(NCP_SERVER(inode), 
 		    NCP_FINFO(inode)->file_handle,
-		    pos, to_write, buf, &written_this_time) != 0) {
+		    pos, to_write, bouncebuffer, &written_this_time) != 0) {
 			errno = -EIO;
 			break;
 		}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/Makefile linux.18p17/fs/nfs/Makefile
--- linux.vanilla/fs/nfs/Makefile	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/Makefile	Fri Sep 15 22:10:43 2000
@@ -9,10 +9,13 @@
 
 O_TARGET := nfs.o
 O_OBJS   := inode.o file.o read.o write.o dir.o symlink.o proc.o \
-	    nfs2xdr.o
+	    nfs2xdr.o flushd.o unlink.o
 
 ifdef CONFIG_ROOT_NFS
   O_OBJS += nfsroot.o mount_clnt.o
+endif
+ifdef CONFIG_NFS_V3
+  O_OBJS += nfs3proc.o nfs3xdr.o
 endif
 
 M_OBJS   := $(O_TARGET)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/dir.c linux.18p17/fs/nfs/dir.c
--- linux.vanilla/fs/nfs/dir.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/dir.c	Fri Sep 15 22:16:06 2000
@@ -14,6 +14,9 @@
  *              Following Linus comments on my original hack, this version
  *              depends only on the dcache stuff and doesn't touch the inode
  *              layer (iput() and friends).
+ *  6 Jun 1999  Cache readdir lookups in the page cache. -DaveM
+ *  7 Oct 1999  Rewrite of Dave's readdir stuff for NFSv3 support, and in order
+ *              to simplify cookie handling. -Trond
  */
 
 #include <linux/sched.h>
@@ -23,32 +26,22 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <asm/pgtable.h>
 #include <linux/mm.h>
 #include <linux/sunrpc/types.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
 
 #include <asm/segment.h>	/* for fs functions */
 
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
-	dev_t			dev;		/* device number */
-	ino_t			ino;		/* inode number */
-	u32			cookie;		/* cookie of first entry */
-	unsigned short		valid  : 1,	/* data is valid */
-				locked : 1;	/* entry locked */
-	unsigned int		size;		/* # of entries */
-	unsigned long		age;		/* last used */
-	unsigned long		mtime;		/* last attr stamp */
-	struct wait_queue *	wait;
-	__u32 *			entry;		/* three __u32's per entry */
-};
-
 static int nfs_safe_remove(struct dentry *);
 
 static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -92,276 +85,382 @@
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	NULL,			/* readpage */
-	NULL,			/* writepage */
-	NULL,			/* bmap */
+ 	NULL,			/* writepage */
+ 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,			/* permission */
+	nfs_permission,		/* permission */
 	NULL,			/* smap */
 	NULL,			/* updatepage */
 	nfs_revalidate,		/* revalidate */
 };
 
 static ssize_t
-nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+nfs_dir_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
 	return -EISDIR;
 }
 
-static struct nfs_dirent	dircache[NFS_MAX_DIRCACHE];
+typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
+typedef struct {
+	struct file	*file;
+	struct page	*page;
+	unsigned long	page_index;
+	unsigned	page_offset;
+	u64		target;
+	struct nfs_entry *entry;
+	decode_dirent_t	decode;
+	int		plus;
+	int		error;
+} nfs_readdir_descriptor_t;
 
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic.  Only the most recent open
- * directory is cached.  This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
  *
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * NOTE: Dirent information verification is done always by the
+ *	 page-in of the RPC reply, nowhere else, this simplies
+ *	 things substantially.
  */
-
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static
+int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
 {
-	struct dentry 		*dentry = filp->f_dentry;
-	struct inode 		*inode = dentry->d_inode;
-	static struct wait_queue *readdir_wait = NULL;
-	struct wait_queue	**waitp = NULL;
-	struct nfs_dirent	*cache, *free;
-	unsigned long		age, dead;
-	u32			cookie;
-	int			ismydir, result;
-	int			i, j, index = 0;
-	__u32			*entry;
-	char			*name, *start;
-
-	dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
-	if (result < 0)
-		goto out;
-
-	/*
-	 * Try to find the entry in the cache
+	struct file	*file = desc->file;
+	struct dentry	*dir = file->f_dentry;
+	struct inode	*inode = dir->d_inode;
+	struct nfs_fattr dir_attr;
+	void		*buffer = (void *)page_address(page);
+	int		plus = NFS_USE_READDIRPLUS(inode);
+	int		error;
+
+	dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->offset);
+
+ again:
+	error = NFS_CALL(readdir, inode, (dir, &dir_attr,
+					  nfs_file_cred(file),
+					  desc->entry->cookie, buffer,
+					  NFS_SERVER(inode)->dtsize, plus));
+	nfs_refresh_inode(inode, &dir_attr);
+	/* We requested READDIRPLUS, but the server doesn't grok it */
+	if (desc->plus && error == -ENOTSUPP) {
+		NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+		plus = 0;
+		goto again;
+	}
+	if (error < 0)
+		goto error;
+	flush_dcache_page(page_address(page)); /* Is this correct? */
+	set_bit(PG_uptodate, &page->flags);
+
+	/* Ensure consistent page alignment of the data.
+	 * Note: assumes we have exclusive access to this inode either
+	 *	 throught inode->i_sem or some other mechanism.
 	 */
-again:
-	if (waitp) {
-		interruptible_sleep_on(waitp);
-		if (signal_pending(current))
-			return -ERESTARTSYS;
-		waitp = NULL;
-	}
-
-	cookie = filp->f_pos;
-	entry  = NULL;
-	free   = NULL;
-	age    = ~(unsigned long) 0;
-	dead   = jiffies - NFS_ATTRTIMEO(inode);
-
-	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
-		/*
-		dprintk("NFS: dircache[%d] valid %d locked %d\n",
-					i, cache->valid, cache->locked);
-		 */
-		ismydir = (cache->dev == inode->i_dev
-				&& cache->ino == inode->i_ino);
-		if (cache->locked) {
-			if (!ismydir || cache->cookie != cookie)
-				continue;
-			dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
-			waitp = &cache->wait;
-			goto again;
-		}
-
-		if (ismydir && cache->mtime != inode->i_mtime)
-			cache->valid = 0;
-
-		if (!cache->valid || cache->age < dead) {
-			free = cache;
-			age  = 0;
-		} else if (cache->age < age) {
-			free = cache;
-			age  = cache->age;
-		}
-
-		if (!ismydir || !cache->valid)
-			continue;
-
-		if (cache->cookie == cookie && cache->size > 0) {
-			entry = cache->entry + (index = 0);
-			cache->locked = 1;
-			break;
-		}
-		for (j = 0; j < cache->size; j++) {
-			__u32 *this_ent = cache->entry + j*3;
+	if (page->offset == 0)
+		invalidate_inode_pages(inode);
+	nfs_unlock_page(page);
+	return 0;
+ error:
+	set_bit(PG_error, &page->flags);
+	nfs_unlock_page(page);
+	desc->error = error;
+	return -EIO;
+}
 
-			if (*(this_ent+1) != cookie)
-				continue;
-			if (j < cache->size - 1) {
-				index = j + 1;
-				entry = this_ent + 3;
-			} else if (*(this_ent+2) & (1 << 15)) {
-				/* eof */
-				return 0;
-			}
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
+ *
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
+ */
+static inline
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)page_address(page),
+			*p = start;
+	int		loop_count = 0,
+			status = 0;
+
+	for(;;) {
+		p = (char *)desc->decode((u32*)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			status = PTR_ERR(p);
 			break;
 		}
-		if (entry) {
-			dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
-						(int)(cache - dircache));
-			cache->locked = 1;
+		desc->page_offset = p - start;
+		dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie);
+		if (entry->prev_cookie == desc->target)
 			break;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
 	}
+	dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
+	return status;
+}
 
-	/*
-	 * Okay, entry not present in cache, or locked and inaccessible.
-	 * Set up the cache entry and attempt a READDIR call.
-	 */
-	if (entry == NULL) {
-		if ((cache = free) == NULL) {
-			dfprintk(DIRCACHE, "NFS: dircache contention\n");
-			waitp = &readdir_wait;
-			goto again;
-		}
-		dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
-				(int)(free - dircache));
-		cache->cookie = cookie;
-		cache->locked = 1;
-		cache->valid  = 0;
-		cache->dev    = inode->i_dev;
-		cache->ino    = inode->i_ino;
-		if (!cache->entry) {
-			result = -ENOMEM;
-			cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
-			if (!cache->entry)
-				goto done;
-		}
-
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
-					cookie, PAGE_SIZE, cache->entry);
-		if (result <= 0)
-			goto done;
-		cache->size  = result;
-		cache->valid = 1;
-		entry = cache->entry + (index = 0);
-	}
-	cache->mtime = inode->i_mtime;
-	cache->age = jiffies;
-
-	/*
-	 * Yowza! We have a cache entry...
-	 */
-	start = (char *) cache->entry;
-	while (index < cache->size) {
-		__u32	fileid  = *entry++;
-		__u32	nextpos = *entry++; /* cookie */
-		__u32	length  = *entry++;
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
+ */
+static inline
+int find_dirent_page(nfs_readdir_descriptor_t *desc)
+{
+	struct inode	*inode = desc->file->f_dentry->d_inode;
+	struct page	*page;
+	unsigned long	index = desc->page_index;
+	int		status;
 
-		/*
-		 * Unpack the eof flag, offset, and length
-		 */
-		result = length & (1 << 15); /* eof flag */
-		name = start + ((length >> 16) & 0xFFFF);
-		length &= 0x7FFF;
-		/*
-		dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
-				(int) length, name, length,
-				(unsigned int) filp->f_pos,
-				fileid, result);
-		 */
+	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
 
-		if (filldir(dirent, name, length, cookie, fileid) < 0)
-			break;
-		cookie = nextpos;
-		index++;
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
 	}
-	filp->f_pos = cookie;
-	result = 0;
 
-	/* XXX: May want to kick async readdir-ahead here. Not too hard
-	 * to do. */
-
-done:
-	dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
-	cache->locked = 0;
-	wake_up(&cache->wait);
-	wake_up(&readdir_wait);
+	page = read_cache_page(inode, index,
+			       (filler_t *)nfs_readdir_filler, desc);
+	if (IS_ERR(page)) {
+		status = PTR_ERR(page);
+		goto out;
+	}
 
-out:
-	return result;
+	/* NOTE: Someone else may have changed the READDIRPLUS flag */
+	desc->plus = NFS_USE_READDIRPLUS(inode);
+	status = find_dirent(desc, page);
+	if (status >= 0)
+		desc->page = page;
+	else
+		page_cache_release(page);
+ out:
+	dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status);
+	return status;
 }
 
 /*
- * Invalidate dircache entries for an inode.
+ * Recurse through the page cache pages, and return a
+ * filled nfs_entry structure of the next directory entry if possible.
+ *
+ * The target for the search is 'desc->target'.
  */
-void
-nfs_invalidate_dircache(struct inode *inode)
+static inline
+int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
 {
-	struct nfs_dirent *cache = dircache;
-	dev_t		dev = inode->i_dev;
-	ino_t		ino = inode->i_ino;
-	int		i;
-
-	dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (cache->ino != ino)
-			continue;
-		if (cache->dev != dev)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked for %s/%ld\n",
-				kdevname(dev), (long) ino);
-			continue;
+	int		res = 0;
+	int		loop_count = 0;
+
+	dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
+	for (;;) {
+		res = find_dirent_page(desc);
+		if (res != -EAGAIN)
+			break;
+		/* Align to beginning of next page */
+		desc->page_offset = 0;
+		desc->page_index += PAGE_CACHE_SIZE;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
-		cache->valid = 0;	/* brute force */
 	}
+	dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res);
+	return res;
 }
 
 /*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+ * Once we've found the start of the dirent within a page: fill 'er up...
  */
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
-{
-	struct nfs_dirent *cache = dircache;
-	int		i;
-
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (sb && sb->s_dev != cache->dev)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked at umount %s\n",
-				(cache->entry ? "(lost a page!)" : ""));
-			continue;
+static 
+int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
+		   filldir_t filldir)
+{
+	struct file	*file = desc->file;
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)page_address(desc->page),
+			*p = start + desc->page_offset;
+	unsigned long	fileid;
+	int		loop_count = 0,
+			res = 0;
+
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
+
+	for(;;) {
+		/* Note: entry->prev_cookie contains the cookie for
+		 *	 retrieving the current dirent on the server */
+		fileid = nfs_fileid_to_ino_t(entry->ino);
+		res = filldir(dirent, entry->name, entry->len, 
+			      entry->prev_cookie, fileid);
+		if (res < 0)
+			break;
+		file->f_pos = desc->target = entry->cookie;
+		p = (char *)desc->decode((u32 *)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			if (PTR_ERR(p) == -EAGAIN) {
+				desc->page_offset = 0;
+				desc->page_index += PAGE_CACHE_SIZE;
+			}
+			break;
 		}
-		cache->valid = 0;	/* brute force */
-		if (cache->entry) {
-			free_page((unsigned long) cache->entry);
-			cache->entry = NULL;
+		desc->page_offset = p - start;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
 	}
+	page_cache_release(desc->page);
+	desc->page = NULL;
+
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
+	return res;
 }
 
 /*
- * Free directory cache memory
- * Called from cleanup_module
+ * If we cannot find a cookie in our cache, we suspect that this is
+ * because it points to a deleted file, so we ask the server to return
+ * whatever it thinks is the next entry. We then feed this to filldir.
+ * If all goes well, we should then be able to find our way round the
+ * cache on the next call to readdir_search_pagecache();
+ *
+ * NOTE: we cannot add the anonymous page to the pagecache because
+ *	 the data it contains might not be page aligned. Besides,
+ *	 we should already have a complete representation of the
+ *	 directory in the page cache by the time we get here.
  */
-void
-nfs_free_dircache(void)
+static inline
+int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
+		     filldir_t filldir)
+{
+	struct file	*file = desc->file;
+	struct dentry	*dir = file->f_dentry;
+	struct inode	*inode = dir->d_inode;
+	struct nfs_fattr dir_attr;
+	struct page	*page = NULL;
+	unsigned long	cache_page;
+	u32		*p;
+	int		status = -EIO;
+
+	dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target);
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
+	}
+
+	cache_page = page_cache_alloc();
+	if (!cache_page) {
+		status = -ENOMEM;
+		goto out;
+	}
+	page = page_cache_entry(cache_page);
+	p = (u32 *)page_address(page);
+	status = NFS_CALL(readdir, inode, (dir, &dir_attr,
+					   nfs_file_cred(file),
+					   desc->target, p,
+					   NFS_SERVER(inode)->dtsize, 0));
+	nfs_refresh_inode(inode, &dir_attr);
+	if (status >= 0) {
+		p = desc->decode(p, desc->entry, 0);
+		if (IS_ERR(p))
+			status = PTR_ERR(p);
+		else
+			desc->entry->prev_cookie = desc->target;
+	}
+	if (status < 0)
+		goto out_release;
+
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	desc->page = page;
+	status = nfs_do_filldir(desc, dirent, filldir);
+
+	/* Reset read descriptor so it searches the page cache from
+	 * the start upon the next call to readdir_search_pagecache() */
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	memset(desc->entry, 0, sizeof(*desc->entry));
+ out:
+	dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status);
+	return status;
+ out_release:
+	page_cache_release(page);
+	goto out;
+}
+
+/* The file offset position is now represented as a true offset into the
+ * page cache as is the case in most of the other filesystems.
+ */
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	dfprintk(DIRCACHE, "NFS: freeing dircache\n");
-	nfs_invalidate_dircache_sb(NULL);
+	struct dentry	*dentry = filp->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	nfs_readdir_descriptor_t my_desc,
+			*desc = &my_desc;
+	struct nfs_entry my_entry;
+	long		res;
+
+	res = nfs_revalidate(dentry);
+	if (res < 0)
+		return res;
+
+	/*
+	 * filp->f_pos points to the file offset in the page cache.
+	 * but if the cache has meanwhile been zapped, we need to
+	 * read from the last dirent to revalidate f_pos
+	 * itself.
+	 */
+	memset(desc, 0, sizeof(*desc));
+	memset(&my_entry, 0, sizeof(my_entry));
+
+	desc->file = filp;
+	desc->target = filp->f_pos;
+	desc->entry = &my_entry;
+	desc->decode = NFS_PROTO(inode)->decode_dirent;
+
+	while(!desc->entry->eof) {
+		res = readdir_search_pagecache(desc);
+		if (res == -EBADCOOKIE) {
+			/* This means either end of directory */
+			if (desc->entry->cookie == desc->target) {
+				res = 0;
+				break;
+			}
+			/* Or that the server has 'lost' a cookie */
+			res = uncached_readdir(desc, dirent, filldir);
+			if (res >= 0)
+				continue;
+		}
+		if (res < 0)
+			break;
+
+		res = nfs_do_filldir(desc, dirent, filldir);
+		if (res < 0) {
+			res = 0;
+			break;
+		}
+	}
+	if (desc->page)
+		page_cache_release(desc->page);
+	if (desc->error < 0)
+		return desc->error;
+	if (res < 0)
+		return res;
+	return 0;
 }
 
+
 /*
  * Whenever an NFS operation succeeds, we know that the dentry
  * is valid, so we update the revalidation timestamp.
  */
-static inline void nfs_renew_times(struct dentry * dentry)
+static inline void
+nfs_renew_times(struct dentry * dentry)
 {
-	dentry->d_time = jiffies;
+		dentry->d_time = jiffies;
 }
 
 static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags)
@@ -384,7 +483,7 @@
 		if (diff < 15*60)
 			timeout = 0;
 	}
-	
+
 	return time_after(jiffies,dentry->d_time + timeout);
 }
 
@@ -398,8 +497,9 @@
 #define NFS_REVALIDATE_NEGATIVE (1 * HZ)
 static inline int nfs_neg_need_reval(struct dentry *dentry)
 {
-	unsigned long timeout = NFS_ATTRTIMEO(dentry->d_parent->d_inode);
-	long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime;
+	struct inode *dir = dentry->d_parent->d_inode;
+	unsigned long timeout = NFS_ATTRTIMEO(dir);
+	long diff = CURRENT_TIME - dir->i_mtime;
 
 	if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE)
 		timeout = NFS_REVALIDATE_NEGATIVE;
@@ -421,11 +521,12 @@
  */
 static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
 {
-	struct dentry * parent = dentry->d_parent;
-	struct inode * inode = dentry->d_inode;
+	struct dentry		*dir = dentry->d_parent;
+	struct inode		*inode = dentry->d_inode,
+				*dir_i = dir->d_inode;
+	struct nfs_fh		fhandle;
+	struct nfs_fattr	fattr, dir_attr;
 	int error;
-	struct nfs_fh fhandle;
-	struct nfs_fattr fattr;
 
 	/*
 	 * If we don't have an inode, let's look at the parent
@@ -440,39 +541,52 @@
 
 	if (is_bad_inode(inode)) {
 		dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
-			parent->d_name.name, dentry->d_name.name);
+			dir->d_name.name, dentry->d_name.name);
 		goto out_bad;
 	}
 
-	if (IS_ROOT(dentry))
-		goto out_valid;
-
 	if (!nfs_dentry_force_reval(dentry, flags))
 		goto out_valid;
 
+	if (IS_ROOT(dentry)) {
+		__nfs_revalidate_inode(dentry);
+		goto out_valid_renew;
+	}
+
+	if (NFS_FLAGS(inode) & NFS_INO_STALE)
+		goto out_bad;
+
 	/*
 	 * Do a new lookup and check the dentry attributes.
 	 */
-	error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
-				dentry->d_name.name, &fhandle, &fattr);
-	if (error)
+	error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+				  &dentry->d_name, &fhandle, &fattr));
+	if (error < 0)
 		goto out_bad;
 
 	/* Inode number matches? */
-	if (fattr.fileid != inode->i_ino)
+	if (!(fattr.valid & NFS_ATTR_FATTR) ||
+	    NFS_FSID(inode) != fattr.fsid ||
+	    NFS_FILEID(inode) != fattr.fileid)
 		goto out_bad;
 
 	/* Filehandle matches? */
-	if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
+	if (NFS_FH(dentry)->size == 0)
+		goto out_bad;
+
+	if (NFS_FH(dentry)->size != fhandle.size ||
+	    memcmp(NFS_FH(dentry)->data, fhandle.data, fhandle.size))
 		goto out_bad;
 
 	/* Ok, remeber that we successfully checked it.. */
-	nfs_renew_times(dentry);
 	nfs_refresh_inode(inode, &fattr);
+	nfs_refresh_inode(dir_i, &dir_attr);
 
-out_valid:
+ out_valid_renew:
+	nfs_renew_times(dentry);
+ out_valid:
 	return 1;
-out_bad:
+ out_bad:
 	if (!list_empty(&dentry->d_subdirs))
 		shrink_dcache_parent(dentry);
 	/* If we have submounts, don't unhash ! */
@@ -480,35 +594,37 @@
 		goto out_valid;
 	d_drop(dentry);
 	if (dentry->d_parent->d_inode)
-		nfs_invalidate_dircache(dentry->d_parent->d_inode);
+		NFS_CACHEINV(dentry->d_parent->d_inode);
 	if (inode && S_ISDIR(inode->i_mode))
-		nfs_invalidate_dircache(inode);
+		NFS_CACHEINV(inode);
 	return 0;
 }
 
 /*
  * This is called from dput() when d_count is going to 0.
- * We use it to clean up silly-renamed files.
  */
 static void nfs_dentry_delete(struct dentry *dentry)
 {
-	dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		dentry->d_flags);
-
 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
-		int error;
-		
-		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-		/* Unhash it first */
+		/* Unhash it, so that ->d_iput() would be called */
 		d_drop(dentry);
-		error = nfs_safe_remove(dentry);
-		if (error)
-			printk("NFS: can't silly-delete %s/%s, error=%d\n",
-				dentry->d_parent->d_name.name,
-				dentry->d_name.name, error);
 	}
+}
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+	struct nfs_fh *p;
 
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return NULL;
+	memset(p, 0, sizeof(*p));
+	return p;
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+	kfree(p);
 }
 
 /*
@@ -516,8 +632,21 @@
  */
 static void nfs_dentry_release(struct dentry *dentry)
 {
-	if (dentry->d_fsdata)
-		kfree(dentry->d_fsdata);
+	if (dentry->d_fsdata) {
+		nfs_fh_free(dentry->d_fsdata);
+		dentry->d_fsdata = NULL;
+	}
+}
+
+/*
+ * Called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+		nfs_complete_unlink(dentry);
+	iput(inode);
 }
 
 struct dentry_operations nfs_dentry_operations = {
@@ -526,36 +655,47 @@
 	NULL,			/* d_compare */
 	nfs_dentry_delete,	/* d_delete(struct dentry *) */
 	nfs_dentry_release,	/* d_release(struct dentry *) */
-	NULL			/* d_iput */
+	nfs_dentry_iput		/* d_iput */
 };
 
-static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry)
 {
+	struct dentry *dir = dentry->d_parent;
 	struct inode *inode;
 	int error;
 	struct nfs_fh fhandle;
-	struct nfs_fattr fattr;
+	struct nfs_fattr fattr, dir_attr;
 
 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
-	if (dentry->d_name.len > NFS_MAXNAMLEN)
+	if (dentry->d_name.len > NFS_SERVER(dir_i)->namelen)
 		goto out;
 
-	error = -ENOMEM;
+	dentry->d_op = &nfs_dentry_operations;
+
 	if (!dentry->d_fsdata) {
-		dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
-		if (!dentry->d_fsdata)
+		dentry->d_fsdata = nfs_fh_alloc();
+		if (!dentry->d_fsdata) {
+			error = -ENOMEM;
 			goto out;
+		}
 	}
-	dentry->d_op = &nfs_dentry_operations;
 
-	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent), 
-				dentry->d_name.name, &fhandle, &fattr);
+#if NFS_FIXME
+	inode = nfs_dircache_lookup(dir_i, dentry);
+	if (inode)
+		goto no_entry;
+#endif
+
+	error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+				 &dentry->d_name, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
 	inode = NULL;
 	if (error == -ENOENT)
 		goto no_entry;
+
 	if (!error) {
 		error = -EACCES;
 		inode = nfs_fhget(dentry, &fhandle, &fattr);
@@ -594,29 +734,46 @@
  * that the operation succeeded on the server, but an error in the
  * reply path made it appear to have failed.
  */
-static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_create(struct inode *dir_i, struct dentry *dentry, int mode)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
+	struct dentry	*dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
 
 	dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	sattr.mode = mode;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+#ifdef NFSD_BROKEN_UID
+	/* We set uid/gid in the request because IBM's broken nfsd
+	 * uses the root uid/gid otherwise. Argh!
+	 * (Hopefully the server will override the gid when the directory
+	 * has the sticky bit set. Irix may have a problem here...)
+	 */
+	attr.ia_mode = mode;
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_mode = mode;
+	attr.ia_valid = ATTR_MODE;
+#endif
 
 	/*
 	 * Invalidate the dir cache before the operation to avoid a race.
+	 * The 0 argument passed into the create function should one day
+	 * contain the O_EXCL flag if requested. This allows NFSv3 to
+	 * select the appropriate create strategy. Currently open_namei
+	 * does not pass the create flags.
 	 */
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-			dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(create, dir_i, (dir, &dir_attr, &dentry->d_name,
+			&attr, 0, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
-	if (error)
+	if (error || fhandle.size == 0)
 		d_drop(dentry);
 	return error;
 }
@@ -624,28 +781,35 @@
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+static int nfs_mknod(struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
+	struct dentry	*dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
 
 	dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	sattr.mode = mode;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	if (S_ISCHR(mode) || S_ISBLK(mode))
-		sattr.size = rdev; /* get out your barf bag */
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
-
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_mode = mode;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode;
+#endif
+
+
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(mknod, dir_i, (dir, &dir_attr, &dentry->d_name,
+				&attr, rdev, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
-	if (error)
+	if (error || fhandle.size == 0)
 		d_drop(dentry);
 	return error;
 }
@@ -653,52 +817,55 @@
 /*
  * See comments for nfs_proc_create regarding failed operations.
  */
-static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode)
 {
-	int error;
-	struct nfs_sattr sattr;
-	struct nfs_fattr fattr;
-	struct nfs_fh fhandle;
-
-	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	struct dentry   *dir = dentry->d_parent;
+	struct iattr	 attr;
+	struct nfs_fattr fattr, dir_attr;
+	struct nfs_fh	 fhandle;
+	int		 error;
+
+	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s)\n",
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
+
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+	attr.ia_mode = mode | S_IFDIR;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode | S_IFDIR;
+#endif
 
-	sattr.mode = mode | S_IFDIR;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(mkdir, dir_i, (dir, &dir_attr,
+				&dentry->d_name, &attr, &fhandle, &fattr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && fhandle.size != 0)
+		error = nfs_instantiate(dentry, &fhandle, &fattr);
 
-	/*
-	 * Always drop the dentry, we can't always depend on
-	 * the fattr returned by the server (AIX seems to be
-	 * broken). We're better off doing another lookup than
-	 * depending on potentially bogus information.
-	 */
-	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error)
-		dir->i_nlink++;
+	if (error || fhandle.size == 0)
+		d_drop(dentry);
 	return error;
 }
 
-static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry)
 {
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_fattr dir_attr;
+	int		 error;
 
 	dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
 
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name);
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(rmdir, dir_i, (dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(dir_i, &dir_attr);
 
-	/* Update i_nlink and invalidate dentry. */
-	if (!error) {
-		d_drop(dentry);
-		if (dir->i_nlink)
-			dir->i_nlink--;
-	}
+	/* Free the inode */
+	if (!error)
+		d_delete(dentry);
 
 	return error;
 }
@@ -758,15 +925,18 @@
 	return sdentry;
 }
 
-static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry)
 {
+	struct dentry	*dir = dentry->d_parent;
 	static unsigned int sillycounter = 0;
-	const int      i_inosize  = sizeof(dir->i_ino)*2;
-	const int      countersize = sizeof(sillycounter)*2;
-	const int      slen       = strlen(".nfs") + i_inosize + countersize;
-	char           silly[slen+1];
-	struct dentry *sdentry;
-	int            error = -EIO;
+	struct nfs_fattr dir_attr;
+	const int        i_inosize  = sizeof(dir_i->i_ino)*2;
+	const int        countersize = sizeof(sillycounter)*2;
+	const int        slen       = strlen(".nfs") + i_inosize + countersize;
+	struct qstr      qsilly;
+	char             silly[slen+1];
+	struct dentry *  sdentry;
+	int              error = -EIO;
 
 	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name, 
@@ -776,15 +946,13 @@
 	 * Note that a silly-renamed file can be deleted once it's
 	 * no longer in use -- it's just an ordinary file now.
 	 */
-	if (dentry->d_count == 1) {
-		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+	if (dentry->d_count == 1)
 		goto out;  /* No need to silly rename. */
-	}
 
 #ifdef NFS_PARANOIA
-if (!dentry->d_inode)
-printk("NFS: silly-renaming %s/%s, negative dentry??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+	if (!dentry->d_inode)
+		printk(KERN_ERR "NFS: silly-renaming %s/%s, negative dentry??\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	/*
 	 * We don't allow a dentry to be silly-renamed twice.
@@ -800,7 +968,8 @@
 	do {
 		char *suffix = silly + slen - countersize;
 
-		dput(sdentry);
+		if (sdentry)
+			dput(sdentry);
 		sillycounter++;
 		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
 
@@ -816,14 +985,16 @@
 			goto out;
 	} while(sdentry->d_inode != NULL); /* need negative lookup */
 
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rename(NFS_SERVER(dir),
-				NFS_FH(dentry->d_parent), dentry->d_name.name,
-				NFS_FH(dentry->d_parent), silly);
+	nfs_zap_caches(dir_i);
+	qsilly.name = silly;
+	qsilly.len  = strlen(silly);
+	error = NFS_CALL(rename, dir_i, (dir, &dir_attr, &dentry->d_name,
+				  dir, &dir_attr, &qsilly));
+	nfs_refresh_inode(dir_i, &dir_attr);
 	if (!error) {
 		nfs_renew_times(dentry);
 		d_move(dentry, sdentry);
-		dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+		error = nfs_async_unlink(dentry);
  		/* If we return 0 we don't unlink */
 	}
 	dput(sdentry);
@@ -840,54 +1011,63 @@
  */
 static int nfs_safe_remove(struct dentry *dentry)
 {
-	struct inode *dir = dentry->d_parent->d_inode;
-	struct inode *inode = dentry->d_inode;
-	int error, rehash = 0;
+	struct nfs_fattr dir_attr;
+	struct dentry	*dir = dentry->d_parent;
+	struct inode	*dir_i = dir->d_inode,   
+			*inode = dentry->d_inode;
+	int		 error = 0, rehash = 0;
 		
-	dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		inode->i_ino);
+	/*
+	 * Unhash the dentry while we remove the file ...
+	 */
+	if (!list_empty(&dentry->d_hash)) {
+		d_drop(dentry);
+		rehash = 1;
+	}
+
+	/* If the dentry was sillyrenamed, we simply call d_delete() */
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+		goto out_delete;
+
+	dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	/* N.B. not needed now that d_delete is done in advance? */
 	error = -EBUSY;
 	if (!inode) {
 #ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s already negative??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+		printk(KERN_ERR "nfs_safe_remove: %s/%s already negative??\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	}
 
 	if (dentry->d_count > 1) {
 #ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s busy, d_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+		printk(KERN_INFO "nfs_safe_remove: %s/%s busy, d_count=%d\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
 #endif
 		goto out;
 	}
+
+	nfs_zap_caches(dir_i);
+	if (inode)
+		NFS_CACHEINV(inode);
+	error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (error < 0)
+		goto out;
+
+ out_delete:
 	/*
-	 * Unhash the dentry while we remove the file ...
-	 */
-	if (!list_empty(&dentry->d_hash)) {
-		d_drop(dentry);
-		rehash = 1;
-	}
-	/*
-	 * Update i_nlink and free the inode before unlinking.
+	 * Free the inode
 	 */
-	if (inode) {
-		if (inode->i_nlink)
-			inode->i_nlink --;
-		d_delete(dentry);
-	}
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name);
+	d_delete(dentry);
+out:
 	/*
 	 * Rehash the negative dentry if the operation succeeded.
 	 */
-	if (!error && rehash)
-		d_add(dentry, NULL);
-out:
+	if (rehash)
+		d_rehash(dentry);
 	return error;
 }
 
@@ -903,6 +1083,8 @@
 	dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
+	if (dentry->d_inode)
+		nfs_wb_all(dentry->d_inode);
 	error = nfs_sillyrename(dir, dentry);
 	if (error && error != -EBUSY) {
 		error = nfs_safe_remove(dentry);
@@ -914,45 +1096,60 @@
 }
 
 static int
-nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+nfs_symlink(struct inode *dir_i, struct dentry *dentry, const char *symname)
 {
-	struct nfs_sattr sattr;
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_fattr dir_attr, sym_attr;
+	struct nfs_fh    sym_fh;
+	struct iattr     attr;
+	struct qstr      qsymname;
+	int              error, mode, maxlen;
 
 	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
-		dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+		dir_i->i_dev, dir_i->i_ino, dentry->d_name.name, symname);
 
 	error = -ENAMETOOLONG;
-	if (strlen(symname) > NFS_MAXPATHLEN)
+	maxlen = (NFS_PROTO(dir_i)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
+	if (strlen(symname) > maxlen)
 		goto out;
 
 #ifdef NFS_PARANOIA
-if (dentry->d_inode)
-printk("nfs_proc_symlink: %s/%s not negative!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+	if (dentry->d_inode)
+		printk(KERN_WARNING "nfs_proc_symlink: %s/%s not negative!\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 	/*
 	 * Fill in the sattr for the call.
+
  	 * Note: SunOS 4.1.2 crashes if the mode isn't initialized!
 	 */
-	sattr.mode = S_IFLNK | S_IRWXUGO;
-	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
-	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+#ifdef NFSD_BROKEN_UID
+	attr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
+	attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+	attr.ia_uid = current->fsuid;
+	attr.ia_gid = current->fsgid;
+#else
+	attr.ia_valid = ATTR_MODE;
+	attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+#endif
 
-	/*
-	 * Drop the dentry in advance to force a new lookup.
-	 * Since nfs_proc_symlink doesn't return a fattr, we
-	 * can't instantiate the new inode.
-	 */
-	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
-				dentry->d_name.name, symname, &sattr);
-	if (!error) {
-		nfs_renew_times(dentry->d_parent);
-	} else if (error == -EEXIST) {
-		printk("nfs_proc_symlink: %s/%s already exists??\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+
+	qsymname.name = symname;
+	qsymname.len  = strlen(symname);
+
+	nfs_zap_caches(dir_i);
+	error = NFS_CALL(symlink, dir_i, (dir, &dir_attr,
+				&dentry->d_name, &qsymname, &attr,
+				&sym_fh, &sym_attr));
+	nfs_refresh_inode(dir_i, &dir_attr);
+	if (!error && sym_fh.size != 0 && (sym_attr.valid & NFS_ATTR_FATTR)) {
+		error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+	} else {
+		if (error == -EEXIST)
+			printk(KERN_INFO "nfs_proc_symlink: %s/%s already exists??\n",
+			       dentry->d_parent->d_name.name,
+			       dentry->d_name.name);
+		d_drop(dentry);
 	}
 
 out:
@@ -960,10 +1157,12 @@
 }
 
 static int 
-nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
 {
-	struct inode *inode = old_dentry->d_inode;
-	int error;
+	struct dentry	*dir = dentry->d_parent;
+	struct inode	*inode = old_dentry->d_inode;
+	struct nfs_fattr old_attr, dir_attr;
+	int		 error;
 
 	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -975,16 +1174,12 @@
 	 * we can't use the existing dentry.
 	 */
 	d_drop(dentry);
-	nfs_invalidate_dircache(dir);
-	error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
-				NFS_FH(dentry->d_parent), dentry->d_name.name);
-	if (!error) {
- 		/*
-		 * Update the link count immediately, as some apps
-		 * (e.g. pine) test this after making a link.
-		 */
-		inode->i_nlink++;
-	}
+	nfs_zap_caches(dir_i);
+	NFS_CACHEINV(inode);
+	error = NFS_CALL(link, inode, (old_dentry, &old_attr,
+				       dir, &dir_attr, &dentry->d_name));
+	nfs_refresh_inode(inode, &old_attr);
+	nfs_refresh_inode(dir_i, &dir_attr);
 	return error;
 }
 
@@ -1011,14 +1206,28 @@
  * no pending writes (if it's a file), and the use count must be 1.
  * If these conditions are met, we can drop the dentries before doing
  * the rename.
+ *
+ * FIXME: Sun seems to take this even one step further. The connectathon
+ * test suite has a file that renames open file A to open file B,
+ * and expects a silly rename to happen for B.
  */
 static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		      struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode *old_inode = old_dentry->d_inode;
-	struct inode *new_inode = new_dentry->d_inode;
-	struct dentry *dentry = NULL;
-	int error, rehash = 0;
+	struct nfs_fattr old_attr, new_attr;
+	struct inode *   old_inode = old_dentry->d_inode;
+	struct inode *   new_inode = new_dentry->d_inode;
+	struct dentry *  dentry = NULL, *rehash = NULL;
+	int              error;
+
+	/*
+	 * To prevent any new references to the target during the rename,
+	 * we unhash the dentry and free the inode in advance.
+	 */
+	if (!list_empty(&new_dentry->d_hash)) {
+		d_drop(new_dentry);
+		rehash = new_dentry;
+	}
 
 	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -1036,6 +1245,8 @@
 	 * With directories check is done in VFS.
 	 */
 	error = -EBUSY;
+	if (new_inode)
+		nfs_wb_all(new_inode);
 	if (new_dentry->d_count > 1 && new_inode) {
 		int err;
 		/* copy the target dentry's name */
@@ -1047,17 +1258,18 @@
 		/* silly-rename the existing target ... */
 		err = nfs_sillyrename(new_dir, new_dentry);
 		if (!err) {
-			new_dentry = dentry;
+			new_dentry = rehash = dentry;
 			new_inode = NULL;
 			/* hash the replacement target */
-			d_add(new_dentry, NULL);
+			d_instantiate(new_dentry, NULL);
 		}
 
 		/* dentry still busy? */
 		if (new_dentry->d_count > 1) {
 #ifdef NFS_PARANOIA
-printk("nfs_rename: target %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+		printk(KERN_INFO "nfs_rename: target %s/%s busy, d_count=%d\n",
+			new_dentry->d_parent->d_name.name,
+			new_dentry->d_name.name,new_dentry->d_count);
 #endif
 			goto out;
 		}
@@ -1073,40 +1285,75 @@
 
 	if (new_dentry->d_count > 1 && new_inode) {
 #ifdef NFS_PARANOIA
-printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+		printk(KERN_INFO "nfs_rename: new dentry %s/%s busy, d_count=%d\n",
+			new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
 #endif
 		goto out;
 	}
 
-	/*
-	 * To prevent any new references to the target during the rename,
-	 * we unhash the dentry and free the inode in advance.
-	 */
-	if (!list_empty(&new_dentry->d_hash)) {
-		d_drop(new_dentry);
-		rehash = 1;
-	}
 	if (new_inode)
 		d_delete(new_dentry);
 
-	nfs_invalidate_dircache(new_dir);
-	nfs_invalidate_dircache(old_dir);
-	error = nfs_proc_rename(NFS_DSERVER(old_dentry),
-			NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
-			NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
+	nfs_zap_caches(new_dir);
+	nfs_zap_caches(old_dir);
+	error = NFS_CALL(rename, old_dir,
+			 (old_dentry->d_parent, &old_attr, &old_dentry->d_name,
+			  new_dentry->d_parent, &new_attr, &new_dentry->d_name));
+	nfs_refresh_inode(old_dir, &old_attr);
+	nfs_refresh_inode(new_dir, &new_attr);
 
+out:
 	/* Update the dcache if needed */
 	if (rehash)
-		d_add(new_dentry, NULL);
+		d_rehash(rehash);
 	if (!error && !S_ISDIR(old_inode->i_mode))
 		d_move(old_dentry, new_dentry);
-
-out:
 	/* new dentry created? */
 	if (dentry)
 		dput(dentry);
 	return error;
+}
+
+int
+nfs_permission(struct inode *i, int msk)
+{
+	struct nfs_fattr	fattr;
+	struct dentry		*de = NULL;
+	int			err = vfs_permission(i, msk);
+	struct list_head	*start, *tmp;
+
+	if (!NFS_PROTO(i)->access)
+		goto out;
+	/*
+	 * Trust UNIX mode bits except:
+	 *
+	 * 1) When override capabilities may have been invoked
+	 * 2) When root squashing may be involved
+	 * 3) When ACLs may overturn a negative answer */
+	if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
+	    && (current->fsuid != 0) && (current->fsgid != 0)
+	    && err != -EACCES)
+		goto out;
+
+	tmp = start = &i->i_dentry;
+	while ((tmp = tmp->next) != start) {
+		de = list_entry(tmp, struct dentry, d_alias);
+		if (de->d_inode == i)
+			break;
+	}
+	if (!de || de->d_inode != i)
+		return 0;
+
+	err = NFS_CALL(access, i, (de, msk, &fattr, 0));
+
+	if (err == -EACCES && NFS_CLIENT(i)->cl_droppriv &&
+	    current->uid != 0 && current->gid != 0 &&
+	    (current->fsuid != current->uid || current->fsgid != current->gid))
+		err = NFS_CALL(access, i, (de, msk, &fattr, 1));
+
+	nfs_refresh_inode(i, &fattr);
+ out:
+	return err;
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/file.c linux.18p17/fs/nfs/file.c
--- linux.vanilla/fs/nfs/file.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/file.c	Sat Sep 23 13:28:09 2000
@@ -1,6 +1,8 @@
 /*
  *  linux/fs/nfs/file.c
  *
+ *  NFS regular file handling.
+ *
  *  Copyright (C) 1992  Rick Sladkey
  *
  *  Changes Copyright (C) 1994 by Florian La Roche
@@ -12,8 +14,6 @@
  *  Expire cache on write to a file by Wai S Kok (Oct 1994).
  *
  *  Total rewrite of read side for new NFS buffer cache.. Linus.
- *
- *  nfs regular file handling functions
  */
 
 #include <linux/sched.h>
@@ -21,11 +21,16 @@
 #include <linux/errno.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
 #include <linux/lockd/bind.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -37,6 +42,8 @@
 static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry);
+static int  nfs_prepare_write(struct file *, struct page *, unsigned, unsigned);
+static int  nfs_sync_page(struct page *page);
 
 static struct file_operations nfs_file_operations = {
 	NULL,			/* lseek - default */
@@ -73,10 +80,12 @@
 	nfs_writepage,		/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL,			/* permission */
+	nfs_permission,		/* permission */
 	NULL,			/* smap */
 	nfs_updatepage,		/* updatepage */
 	nfs_revalidate,		/* revalidate */
+	nfs_prepare_write,	/* prepare_write */
+	nfs_sync_page,		/* sync_page */
 };
 
 /* Hack for future NFS swap support */
@@ -91,17 +100,11 @@
 static int
 nfs_file_flush(struct file *file)
 {
-	struct inode	*inode = file->f_dentry->d_inode;
-	int		status;
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
 
 	dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino);
-
-	status = nfs_wb_file(inode, file);
-	if (!status) {
-		status = file->f_error;
-		file->f_error = 0;
-	}
-	return status;
+	return nfs_fsync(file, dentry);
 }
 
 static ssize_t
@@ -114,7 +117,7 @@
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		(unsigned long) count, (unsigned long) *ppos);
 
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	result = nfs_revalidate_inode(dentry);
 	if (!result)
 		result = generic_file_read(file, buf, count, ppos);
 	return result;
@@ -129,7 +132,7 @@
 	dfprintk(VFS, "nfs: mmap(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	status = nfs_revalidate_inode(dentry);
 	if (!status)
 		status = generic_file_mmap(file, vma);
 	return status;
@@ -157,6 +160,34 @@
 }
 
 
+static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	return nfs_flush_incompatible(file, page);
+}
+
+/*
+ * The following is used by wait_on_page(), generic_file_readahead()
+ * to initiate the completion of any page readahead operations.
+ */
+static int nfs_sync_page(struct page *page)
+{
+	struct inode	*inode = page->inode;
+	unsigned long	index = page_index(page);
+	unsigned int	rpages;
+	int		result;
+
+	if (!inode)
+		return 0;
+
+	rpages = NFS_SERVER(inode)->rpages;
+	result = nfs_pagein_inode(inode, index, rpages);
+	if (result < 0)
+		goto out_bad;
+	return 0;
+ out_bad:
+	return result;
+}
+
 /* 
  * Write to a file (through the page cache).
  */
@@ -174,7 +205,7 @@
 	result = -EBUSY;
 	if (IS_SWAPFILE(inode))
 		goto out_swapfile;
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	result = nfs_revalidate_inode(dentry);
 	if (result)
 		goto out;
 
@@ -197,7 +228,8 @@
 int
 nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
-	struct inode * inode = filp->f_dentry->d_inode;
+	struct dentry * dentry = filp->f_dentry;
+	struct inode * inode = dentry->d_inode;
 	int	status = 0;
 
 	dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
@@ -235,18 +267,21 @@
 	 */
 	status = nfs_wb_all(inode);
 	if (status < 0)
-		return status;
+		goto out_unlock;
 
-	if ((status = nlmclnt_proc(inode, cmd, fl)) < 0)
-		return status;
-	else
+	status = nlmclnt_proc(inode, cmd, fl);
+	if (status > 0)
 		status = 0;
 
 	/*
-	 * Make sure we re-validate anything we've got cached.
+	 * Make sure we clear the cache whenever we try to get the lock.
 	 * This makes locking act as a cache coherency point.
 	 */
+ out_unlock:
  out_ok:
-	NFS_CACHEINV(inode);
+	if ((cmd == F_SETLK || cmd == F_SETLKW) && fl->fl_type != F_UNLCK) {
+		nfs_wb_all(inode);	/* we may have slept */
+		nfs_zap_caches(inode);
+	}
 	return status;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/flushd.c linux.18p17/fs/nfs/flushd.c
--- linux.vanilla/fs/nfs/flushd.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/nfs/flushd.c	Fri Sep 15 22:10:43 2000
@@ -0,0 +1,276 @@
+/*
+ * linux/fs/nfs/flushd.c
+ *
+ * For each NFS mount, there is a separate cache object that contains
+ * a hash table of all clusters. With this cache, an async RPC task
+ * (`flushd') is associated, which wakes up occasionally to inspect
+ * its list of dirty buffers.
+ * (Note that RPC tasks aren't kernel threads. Take a look at the
+ * rpciod code to understand what they are).
+ *
+ * Inside the cache object, we also maintain a count of the current number
+ * of dirty pages, which may not exceed a certain threshold.
+ * (FIXME: This threshold should be configurable).
+ *
+ * The code is streamlined for what I think is the prevalent case for
+ * NFS traffic, which is sequential write access without concurrent
+ * access by different processes.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include <linux/sched.h>
+
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_mount.h>
+
+/*
+ * Various constants
+ */
+#define NFSDBG_FACILITY         NFSDBG_PAGECACHE
+
+/*
+ * This is the wait queue all cluster daemons sleep on
+ */
+static struct rpc_wait_queue    flushd_queue = RPC_INIT_WAITQ("nfs_flushd");
+
+/*
+ * Local function declarations.
+ */
+static void	nfs_flushd(struct rpc_task *);
+static void	nfs_flushd_exit(struct rpc_task *);
+
+
+int nfs_reqlist_init(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	struct rpc_task		*task;
+	int			status = 0;
+
+	dprintk("NFS: writecache_init\n");
+	cache = server->rw_requests;
+
+	if (cache->task)
+		return 0;
+
+	/* Create the RPC task */
+	status = -ENOMEM;
+	task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC);
+	if (!task)
+		goto out_err;
+
+	task->tk_calldata = server;
+
+	cache->task = task;
+
+	/* Run the task */
+	cache->runat = jiffies;
+
+	cache->auth = server->client->cl_auth;
+	task->tk_action   = nfs_flushd;
+	task->tk_exit   = nfs_flushd_exit;
+
+	rpc_execute(task);
+	return 0;
+ out_err:
+	return status;
+}
+
+void nfs_reqlist_exit(struct nfs_server *server)
+{
+	struct nfs_reqlist      *cache;
+
+	cache = server->rw_requests;
+	if (!cache)
+		return;
+
+	dprintk("NFS: reqlist_exit (ptr %p rpc %p)\n", cache, cache->task);
+	while (cache->task || cache->inodes) {
+		if (!cache->task) {
+			nfs_reqlist_init(server);
+		} else {
+			cache->task->tk_status = -ENOMEM;
+			rpc_wake_up_task(cache->task);
+		}
+		interruptible_sleep_on_timeout(&cache->request_wait, 1 * HZ);
+	}
+}
+
+int nfs_reqlist_alloc(struct nfs_server *server)
+{
+	struct nfs_reqlist	*cache;
+	if (server->rw_requests)
+		return 0;
+
+	cache = (struct nfs_reqlist *)kmalloc(sizeof(*cache), GFP_KERNEL);
+	if (!cache)
+		return -ENOMEM;
+
+	memset(cache, 0, sizeof(*cache));
+	server->rw_requests = cache;
+
+	return 0;
+}
+
+void nfs_reqlist_free(struct nfs_server *server)
+{
+	if (server->rw_requests) {
+		kfree(server->rw_requests);
+		server->rw_requests = NULL;
+	}
+}
+
+void nfs_wake_flushd()
+{
+	rpc_wake_up_status(&flushd_queue, -ENOMEM);
+}
+
+static void inode_append_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	if (NFS_FLAGS(inode) & NFS_INO_FLUSH)
+		return;
+	inode->u.nfs_i.hash_next = NULL;
+
+	q = &cache->inodes;
+	while (*q)
+		q = &(*q)->u.nfs_i.hash_next;
+	*q = inode;
+
+	/* Note: we increase the inode i_count in order to prevent
+	 *	 it from disappearing when on the flush list
+	 */
+	NFS_FLAGS(inode) |= NFS_INO_FLUSH;
+	inode->i_count++;
+}
+
+void inode_remove_flushd(struct inode *inode)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct inode		**q;
+
+	if (!(NFS_FLAGS(inode) & NFS_INO_FLUSH))
+		return;
+
+	q = &cache->inodes;
+	while (*q && *q != inode)
+		q = &(*q)->u.nfs_i.hash_next;
+	if (*q) {
+		*q = inode->u.nfs_i.hash_next;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+		iput(inode);
+	}
+}
+
+void inode_schedule_scan(struct inode *inode, unsigned long time)
+{
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct rpc_task		*task;
+	unsigned long		mintimeout;
+
+	if (time_after(NFS_NEXTSCAN(inode), time))
+		NFS_NEXTSCAN(inode) = time;
+	mintimeout = jiffies + 1 * HZ;
+	if (time_before(mintimeout, NFS_NEXTSCAN(inode)))
+		mintimeout = NFS_NEXTSCAN(inode);
+	inode_append_flushd(inode);
+
+	task = cache->task;
+	if (!task) {
+		nfs_reqlist_init(NFS_SERVER(inode));
+	} else {
+		if (time_after(cache->runat, mintimeout))
+			rpc_wake_up_task(task);
+	}
+}
+
+
+static void
+nfs_flushd(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	struct inode		*inode, *next;
+	unsigned long		delay = jiffies + NFS_WRITEBACK_LOCKDELAY;
+	int			flush = (task->tk_status == -ENOMEM);
+
+        dprintk("NFS: %4d flushd starting\n", task->tk_pid);
+	server = (struct nfs_server *) task->tk_calldata;
+        cache = server->rw_requests;
+
+	next = cache->inodes;
+	cache->inodes = NULL;
+
+	while ((inode = next) != NULL) {
+		next = next->u.nfs_i.hash_next;
+		inode->u.nfs_i.hash_next = NULL;
+		NFS_FLAGS(inode) &= ~NFS_INO_FLUSH;
+
+		if (flush) {
+			nfs_pagein_inode(inode, 0, 0);
+			nfs_sync_file(inode, NULL, 0, 0, FLUSH_AGING);
+		} else if (time_after(jiffies, NFS_NEXTSCAN(inode))) {
+			NFS_NEXTSCAN(inode) = jiffies + NFS_WRITEBACK_LOCKDELAY;
+			nfs_pagein_timeout(inode);
+			nfs_flush_timeout(inode, FLUSH_AGING);
+#ifdef CONFIG_NFS_V3
+			nfs_commit_timeout(inode, FLUSH_AGING);
+#endif
+		}
+
+		if (nfs_have_writebacks(inode) || nfs_have_read(inode)) {
+			inode_append_flushd(inode);
+			if (time_after(delay, NFS_NEXTSCAN(inode)))
+				delay = NFS_NEXTSCAN(inode);
+		}
+		iput(inode);
+	}
+
+	dprintk("NFS: %4d flushd back to sleep\n", task->tk_pid);
+	if (time_after(jiffies + 1 * HZ, delay))
+		delay = 1 * HZ;
+	else
+		delay = delay - jiffies;
+	task->tk_status = 0;
+	task->tk_action = nfs_flushd;
+	task->tk_timeout = delay;
+	cache->runat = jiffies + task->tk_timeout;
+
+	if (!cache->nr_requests && !cache->inodes) {
+		cache->task = NULL;
+		task->tk_action = NULL;
+	} else
+		rpc_sleep_on(&flushd_queue, task, NULL, NULL);
+}
+
+static void
+nfs_flushd_exit(struct rpc_task *task)
+{
+	struct nfs_server	*server;
+	struct nfs_reqlist	*cache;
+	server = (struct nfs_server *) task->tk_calldata;
+	cache = server->rw_requests;
+
+	if (cache->task == task)
+		cache->task = NULL;
+	wake_up(&cache->request_wait);
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/inode.c linux.18p17/fs/nfs/inode.c
--- linux.vanilla/fs/nfs/inode.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nfs/inode.c	Sun Oct 15 21:15:17 2000
@@ -24,11 +24,19 @@
 #include <linux/errno.h>
 #include <linux/locks.h>
 #include <linux/unistd.h>
+#include <linux/major.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
 #include <linux/lockd/bind.h>
 
+#include <asm/spinlock.h>
+
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
@@ -37,16 +45,16 @@
 #define NFS_PARANOIA 1
 
 static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
-static void nfs_zap_caches(struct inode *);
-static void nfs_invalidate_inode(struct inode *);
 
 static void nfs_read_inode(struct inode *);
 static void nfs_put_inode(struct inode *);
 static void nfs_delete_inode(struct inode *);
 static int  nfs_notify_change(struct dentry *, struct iattr *);
 static void nfs_put_super(struct super_block *);
-static void nfs_umount_begin(struct super_block *);
 static int  nfs_statfs(struct super_block *, struct statfs *, int);
+static void nfs_umount_begin(struct super_block *);
+static struct nfs_file *nfs_file_alloc(void);
+static void nfs_file_free(struct nfs_file *p);
 
 static struct super_operations nfs_sops = { 
 	nfs_read_inode,		/* read inode */
@@ -57,12 +65,44 @@
 	nfs_put_super,		/* put superblock */
 	NULL,			/* write superblock */
 	nfs_statfs,		/* stat filesystem */
-	NULL,			/* no remount */
-	NULL,			/* no clear inode */
+	NULL,			/* remount */
+	NULL,			/* clear inode */
 	nfs_umount_begin	/* umount attempt begin */
 };
 
-struct rpc_stat			nfs_rpcstat = { &nfs_program };
+
+/*
+ * RPC crutft for NFS
+ */
+static struct rpc_stat		nfs_rpcstat = { &nfs_program };
+static struct rpc_version *	nfs_version[] = {
+	NULL,
+	NULL,
+	&nfs_version2,
+#ifdef CONFIG_NFS_V3
+	&nfs_version3,
+#endif
+};
+
+struct rpc_program		nfs_program = {
+	"nfs",
+	NFS_PROGRAM,
+	sizeof(nfs_version) / sizeof(nfs_version[0]),
+	nfs_version,
+	&nfs_rpcstat,
+};
+
+static inline unsigned long
+nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
+{
+	return nfs_fileid_to_ino_t(fattr->fileid);
+}
+
+/*
+ * We don't keep the file handle in the inode anymore to avoid bloating
+ * struct inode and use a pointer to external memory instead.
+ */
+#define NFS_SB_FHSIZE(sb)	((sb)->u.nfs_sb.s_fhsize)
 
 /*
  * The "read_inode" function doesn't actually do anything:
@@ -78,8 +118,19 @@
 	inode->i_mode = 0;
 	inode->i_rdev = 0;
 	inode->i_op = NULL;
+	NFS_FILEID(inode) = 0;
+	NFS_FSID(inode) = 0;
+	INIT_LIST_HEAD(&inode->u.nfs_i.read);
+	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
+	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
+	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
+	inode->u.nfs_i.nread = 0;
+	inode->u.nfs_i.ndirty = 0;
+	inode->u.nfs_i.ncommit = 0;
+	inode->u.nfs_i.npages = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 }
 
 static void
@@ -96,32 +147,15 @@
 static void
 nfs_delete_inode(struct inode * inode)
 {
-	int failed;
-
 	dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+#ifdef NFS_DEBUG_VERBOSE
 	/*
 	 * Flush out any pending write requests ...
 	 */
-	if (NFS_WRITEBACK(inode) != NULL) {
-		unsigned long timeout = jiffies + 5*HZ;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
-#endif
-		nfs_inval(inode);
-		while (NFS_WRITEBACK(inode) != NULL &&
-		       time_before(jiffies, timeout)) {
-			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout(HZ/10);
-		}
-		current->state = TASK_RUNNING;
-		if (NFS_WRITEBACK(inode) != NULL)
-			printk("NFS: Arghhh, stuck RPC requests!\n");
+	if (nfs_have_writebacks(inode) || nfs_have_read(inode)) {
+		printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
 	}
-
-	failed = nfs_check_failed_request(inode);
-	if (failed)
-		printk("NFS: inode %ld had %d failed requests\n",
-			inode->i_ino, failed);
+#endif
 	clear_inode(inode);
 }
 
@@ -131,18 +165,22 @@
 	struct nfs_server *server = &sb->u.nfs_sb.s_server;
 	struct rpc_clnt	*rpc;
 
+	/*
+	 * First get rid of the request flushing daemon.
+	 * Relies on rpc_shutdown_client() waiting on all
+	 * client tasks to finish.
+	 */
+	nfs_reqlist_exit(server);
+
 	if ((rpc = server->client) != NULL)
 		rpc_shutdown_client(rpc);
 
-#if 0
+	nfs_reqlist_free(server);
+
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
-#endif
+
 	rpciod_down();		/* release rpciod */
-	/*
-	 * Invalidate the dircache for this superblock.
-	 */
-	nfs_invalidate_dircache_sb(sb);
 
 	kfree(server->hostname);
 
@@ -160,17 +198,10 @@
 		rpc_killall_tasks(rpc);
 }
 
-/*
- * Compute and set NFS server blocksize
- */
-static unsigned int
-nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
-{
-	if (bsize < 1024)
-		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
-		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
 
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
 	/* make sure blocksize is a power of two */
 	if ((bsize & (bsize - 1)) || nrbitsp) {
 		unsigned int	nrbits;
@@ -180,14 +211,56 @@
 		bsize = 1 << nrbits;
 		if (nrbitsp)
 			*nrbitsp = nrbits;
-		if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
-			bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
 	}
 
 	return bsize;
 }
 
 /*
+ * Calculate the number of 512byte blocks used.
+ */
+static inline unsigned long
+nfs_calc_block_size(u64 tsize)
+{
+	off_t used = nfs_size_to_off_t(tsize);
+	return (used + 511) >> 9;
+}
+
+/*
+ * Compute and set NFS server blocksize
+ */
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+	if (bsize < 1024)
+		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+	return nfs_block_bits(bsize, nrbitsp);
+}
+
+/*
+ * Obtain the root inode of the file system.
+ */
+static struct inode *
+nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh)
+{
+	struct nfs_server	*server = &sb->u.nfs_sb.s_server;
+	struct nfs_fattr	fattr;
+	struct inode		*inode;
+	int			error;
+
+	if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) {
+		printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
+		return NULL;
+	}
+
+	inode = __nfs_fhget(sb, &fattr);
+	return inode;
+}
+
+/*
  * The way this works is that the mount process passes a structure
  * in the data argument which contains the server's IP address
  * and the root file handle obtained from the server's mount
@@ -198,38 +271,47 @@
 {
 	struct nfs_mount_data	*data = (struct nfs_mount_data *) raw_data;
 	struct nfs_server	*server;
-	struct rpc_xprt		*xprt;
-	struct rpc_clnt		*clnt;
-	struct nfs_fh		*root_fh;
-	struct inode		*root_inode;
+	struct rpc_xprt		*xprt = 0;
+	struct rpc_clnt		*clnt = 0;
+	struct nfs_fh		*root_fh = NULL,
+				*root = &data->root,
+				fh;
+	struct inode		*root_inode = NULL;
 	unsigned int		authflavor;
-	int			tcp;
 	struct sockaddr_in	srvaddr;
 	struct rpc_timeout	timeparms;
-	struct nfs_fattr	fattr;
+	struct nfs_fsinfo       fsinfo;
+	int                     tcp, version, maxlen;
 
 	MOD_INC_USE_COUNT;
-	if (!data)
-		goto out_miss_args;
+	memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb));
+	if (!data) {
+		printk(KERN_NOTICE "nfs_read_super: missing data argument\n");
+		goto failure;
+	}
 
-	/* No NFS V3. */
-	if (data->flags & NFS_MOUNT_VER3)
-		goto out_fail;
-
-	/* Don't complain if "mount" is newer. */
-	if (data->version < NFS_MOUNT_VERSION) {
-		printk("nfs warning: mount version %s than kernel\n",
+	memset(&fh, 0, sizeof(fh));
+	if (data->version != NFS_MOUNT_VERSION) {
+		printk(KERN_WARNING "nfs warning: mount version %s than kernel\n",
 			data->version < NFS_MOUNT_VERSION ? "older" : "newer");
 		if (data->version < 2)
 			data->namlen = 0;
 		if (data->version < 3)
 			data->bsize  = 0;
+		if (data->version < 4) {
+			data->flags &= ~NFS_MOUNT_VER3;
+			root = &fh;
+			root->size = NFS2_FHSIZE;
+			memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+		}
 	}
 
 	/* We now require that the mount process passes the remote address */
 	memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
-	if (srvaddr.sin_addr.s_addr == INADDR_ANY)
-		goto out_no_remote;
+	if (srvaddr.sin_addr.s_addr == INADDR_ANY) {
+		printk(KERN_WARNING "NFS: mount program didn't pass remote address!\n");
+		goto failure;
+	}
 
 	lock_super(sb);
 
@@ -237,12 +319,16 @@
 
 	sb->s_magic      = NFS_SUPER_MAGIC;
 	sb->s_op         = &nfs_sops;
-	sb->s_blocksize  = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
-	sb->u.nfs_sb.s_root = data->root;
+
+	sb->s_blocksize_bits = 0;
+	sb->s_blocksize = nfs_block_bits(data->bsize, &sb->s_blocksize_bits);
+
 	server           = &sb->u.nfs_sb.s_server;
+	memset(server, 0, sizeof(*server));
+
 	server->rsize    = nfs_block_size(data->rsize, NULL);
 	server->wsize    = nfs_block_size(data->wsize, NULL);
-	server->flags    = data->flags;
+	server->flags    = data->flags & NFS_MOUNT_FLAGMASK;
 
 	if (data->flags & NFS_MOUNT_NOAC) {
 		data->acregmin = data->acregmax = 0;
@@ -253,11 +339,34 @@
 	server->acdirmin = data->acdirmin*HZ;
 	server->acdirmax = data->acdirmax*HZ;
 
+	server->namelen  = data->namlen;
 	server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
 	if (!server->hostname)
-		goto out_unlock;
+		goto failure_unlock;
 	strcpy(server->hostname, data->hostname);
 
+ nfsv3_try_again:
+	/* Check NFS protocol revision and initialize RPC op vector
+	 * and file handle pool. */
+	if (data->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3
+		server->rpc_ops = &nfs_v3_clientops;
+		NFS_SB_FHSIZE(sb) = sizeof(unsigned short) + NFS3_FHSIZE;
+		version = 3;
+		if (data->version < 4) {
+			printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
+			goto failure_unlock;
+		}
+#else
+		printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
+		goto failure_unlock;
+#endif
+	} else {
+		server->rpc_ops = &nfs_v2_clientops;
+		NFS_SB_FHSIZE(sb) = sizeof(unsigned short) + NFS2_FHSIZE;
+		version = 2;
+	}
+
 	/* Which protocol do we use? */
 	tcp   = (data->flags & NFS_MOUNT_TCP);
 
@@ -267,11 +376,18 @@
 	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
 	timeparms.to_exponential = 1;
 
+	if (!timeparms.to_initval)
+		timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
+	if (!timeparms.to_retries)
+		timeparms.to_retries = 5;
+
 	/* Now create transport and client */
 	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
 						&srvaddr, &timeparms);
-	if (xprt == NULL)
-		goto out_no_xprt;
+	if (xprt == NULL) {
+		printk(KERN_NOTICE "NFS: cannot create RPC transport. \n");
+		goto failure_unlock;
+	}
 
 	/* Choose authentication flavor */
 	authflavor = RPC_AUTH_UNIX;
@@ -281,92 +397,145 @@
 		authflavor = RPC_AUTH_KRB;
 
 	clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
-						NFS_VERSION, authflavor);
-	if (clnt == NULL)
-		goto out_no_client;
+						version, authflavor);
+	if (clnt == NULL) {
+		printk(KERN_NOTICE "NFS: cannot create RPC client \n");
+		goto failure_unlock;
+	}
 
 	clnt->cl_intr     = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
 	clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
+	clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0;
 	clnt->cl_chatty   = 1;
 	server->client    = clnt;
 
 	/* Fire up rpciod if not yet running */
-	if (rpciod_up() != 0)
-		goto out_no_iod;
+	if (rpciod_up() != 0) {
+		printk(KERN_NOTICE "NFS: cannot start rpciod!\n");
+		goto failure_unlock;
+	}
 
 	/*
 	 * Keep the super block locked while we try to get 
 	 * the root fh attributes.
 	 */
-	root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+	root_fh = nfs_fh_alloc();
 	if (!root_fh)
 		goto out_no_fh;
-	*root_fh = data->root;
-
-	if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
-		goto out_no_fattr;
+	memcpy((u8*)root_fh, (u8*)root, sizeof(*root_fh));
 
-	root_inode = __nfs_fhget(sb, &fattr);
+	/* Did getting the root inode fail? */
+	if ((root->size > NFS_SB_FHSIZE(sb)
+	     || ! (root_inode = nfs_get_root(sb, root)))
+	    && (data->flags & NFS_MOUNT_VER3)) {
+		data->flags &= ~NFS_MOUNT_VER3;
+		nfs_fh_free(root_fh);
+		rpciod_down();
+		rpc_shutdown_client(server->client);
+		goto nfsv3_try_again;
+	}
 	if (!root_inode)
-		goto out_no_root;
-	sb->s_root = d_alloc_root(root_inode, NULL);
-	if (!sb->s_root)
-		goto out_no_root;
+		goto failure_put_root;
+
+	if (! (sb->s_root = d_alloc_root(root_inode, NULL)))
+		goto failure_put_root;
+
 	sb->s_root->d_op = &nfs_dentry_operations;
 	sb->s_root->d_fsdata = root_fh;
+	sb->u.nfs_sb.s_root = root_fh;
+
+	/* Get some general file system info */
+	if (server->rpc_ops->statfs(server, root, &fsinfo) >= 0) {
+		if (server->namelen == 0)
+			server->namelen = fsinfo.namelen;
+	} else {
+		printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
+		goto failure_put_root;
+	}
+
+	/* Fire up the writeback cache */
+	if (nfs_reqlist_alloc(server) < 0) {
+		printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n");
+                goto failure_kill_reqlist;
+	}
+
+	if (data->rsize == 0 && tcp)
+		server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
+	if (data->wsize == 0 && tcp)
+		server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
+
+	/* NFSv3: we don't have bsize, but rather rtmult and wtmult... */
+	if (!fsinfo.bsize)
+		fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult;
+	/* Also make sure we don't go below rsize/wsize since
+	 * RPC calls are expensive */
+	if (fsinfo.bsize < server->rsize)
+		fsinfo.bsize = server->rsize;
+	if (fsinfo.bsize < server->wsize)
+		fsinfo.bsize = server->wsize;
+
+	if (data->bsize == 0)
+		sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits);
+	if (server->rsize > fsinfo.rtmax)
+		server->rsize = fsinfo.rtmax;
+	server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (server->rpages > NFS_READ_MAXIOV) {
+		server->rpages = NFS_READ_MAXIOV;
+		server->rsize = server->rpages << PAGE_CACHE_SHIFT;
+	}
+
+	if (server->wsize > fsinfo.wtmax)
+		server->wsize = fsinfo.wtmax;
+	server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (server->wpages > NFS_WRITE_MAXIOV) {
+		server->wpages = NFS_WRITE_MAXIOV;
+		server->wsize = server->wpages << PAGE_CACHE_SHIFT;
+	}
+
+	server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
+	if (server->dtsize > PAGE_CACHE_SIZE)
+		server->dtsize = PAGE_CACHE_SIZE;
+	if (server->dtsize > server->rsize)
+		server->dtsize = server->rsize;
+
+	maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN;
+
+	if (server->namelen == 0 || server->namelen > maxlen)
+		server->namelen = maxlen;
 
 	/* 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. */
-out_no_root:
-	printk("nfs_read_super: get root inode failed\n");
-	iput(root_inode);
-	goto out_free_fh;
-
-out_no_fattr:
-	printk("nfs_read_super: get root fattr failed\n");
-out_free_fh:
-	kfree(root_fh);
-out_no_fh:
+ failure_kill_reqlist:
+	nfs_reqlist_exit(server);
+ failure_put_root:
+	if (root_inode)
+		iput(root_inode);
+	if (root_fh)
+		nfs_fh_free(root_fh);
+ out_no_fh:
 	rpciod_down();
-	goto out_shutdown;
-
-out_no_iod:
-	printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
-out_shutdown:
-	rpc_shutdown_client(server->client);
-	goto out_free_host;
-
-out_no_client:
-	printk(KERN_WARNING "NFS: cannot create RPC client.\n");
-	xprt_destroy(xprt);
-	goto out_free_host;
 
-out_no_xprt:
-	printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
-
-out_free_host:
-	kfree(server->hostname);
-out_unlock:
+ failure_unlock:
+	/* Yargs. It didn't work out. */
+	if (clnt)
+		rpc_shutdown_client(server->client);
+	else if (xprt)
+		xprt_destroy(xprt);
 	unlock_super(sb);
-	goto out_fail;
-
-out_no_remote:
-	printk("NFS: mount program didn't pass remote address!\n");
-	goto out_fail;
-
-out_miss_args:
-	printk("nfs_read_super: missing data argument\n");
+	nfs_reqlist_free(server);
+	if (server->hostname)
+		kfree(server->hostname);
+	printk(KERN_NOTICE "NFS: cannot create RPC transport.\n");
 
-out_fail:
+failure:
 	sb->s_dev = 0;
 	MOD_DEC_USE_COUNT;
 	return NULL;
@@ -375,27 +544,51 @@
 static int
 nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
-	int error;
-	struct nfs_fsinfo res;
-	struct statfs tmp;
+	struct nfs_sb_info	*si = &sb->u.nfs_sb;
+	struct nfs_server	*server = &si->s_server;
+	unsigned char		blockbits;
+	unsigned long		blockres;
+	int			error;
+	struct nfs_fsinfo	res;
+	struct statfs		tmp;
 
-	error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
-		&res);
+	error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root), &res);
 	if (error) {
-		printk("nfs_statfs: statfs error = %d\n", -error);
-		res.bsize = res.blocks = res.bfree = res.bavail = 0;
+		printk(KERN_NOTICE "nfs_statfs: statfs error = %d\n", -error);
+		memset(&res, 0, sizeof(res));
 	}
 	tmp.f_type = NFS_SUPER_MAGIC;
-	tmp.f_bsize = res.bsize;
-	tmp.f_blocks = res.blocks;
-	tmp.f_bfree = res.bfree;
-	tmp.f_bavail = res.bavail;
-	tmp.f_files = 0;
-	tmp.f_ffree = 0;
-	tmp.f_namelen = NAME_MAX;
+	if (res.bsize == 0)
+		res.bsize = sb->s_blocksize;
+	if (res.namelen == 0)
+		res.namelen = server->namelen;
+	tmp.f_bsize   = nfs_block_bits(res.bsize, &blockbits);
+	blockres = (1 << blockbits) - 1;
+	tmp.f_blocks  = (res.tbytes + blockres) >> blockbits;
+	tmp.f_bfree   = (res.fbytes + blockres) >> blockbits;
+	tmp.f_bavail  = (res.abytes + blockres) >> blockbits;
+	tmp.f_files   = res.tfiles;
+	tmp.f_ffree   = res.ffiles;
+	tmp.f_namelen = res.namelen;
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
+#if 0
+int nfs_remountfs(struct super_block *sb, int *flags, char *data)
+{
+	struct nfs_server *server = &sb->u.nfs_sb.s_server;
+
+	if (*flags & ~(NFS_MOUNT_NONLM|MS_RDONLY))
+		return -EINVAL;
+
+	if (*flags & ~NFS_MOUNT_NONLM)
+		return 0;
+
+	if ((*flags & NFS_MOUNT_NONLM) == (server->flags & NFS_MOUNT_NONLM))
+		return 0;
+}
+#endif
+
 /*
  * Free all unused dentries in an inode's alias list.
  *
@@ -437,23 +630,19 @@
 }
 
 /*
- * Invalidate the local caches
+ * Zap the caches.
  */
-static void
-nfs_zap_caches(struct inode *inode)
+void nfs_zap_caches(struct inode *inode)
 {
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+
+	invalidate_inode_pages(inode);
 
-	if (S_ISDIR(inode->i_mode))
-		nfs_invalidate_dircache(inode);
-	else
-		invalidate_inode_pages(inode);
+	memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+	NFS_CACHEINV(inode);
 }
 
-/*
- * Invalidate, but do not unhash, the inode
- */
 static void
 nfs_invalidate_inode(struct inode *inode)
 {
@@ -461,7 +650,6 @@
 
 	make_bad_inode(inode);
 	inode->i_mode = save_mode;
-	nfs_inval(inode);
 	nfs_zap_caches(inode);
 }
 
@@ -476,6 +664,8 @@
 	 * do this once. (We don't allow inodes to change types.)
 	 */
 	if (inode->i_mode == 0) {
+		NFS_FILEID(inode) = fattr->fileid;
+		NFS_FSID(inode) = fattr->fsid;
 		inode->i_mode = fattr->mode;
 		if (S_ISREG(inode->i_mode))
 			inode->i_op = &nfs_file_inode_operations;
@@ -496,26 +686,62 @@
 		/*
 		 * Preset the size and mtime, as there's no need
 		 * to invalidate the caches.
-		 */ 
-		inode->i_size  = fattr->size;
-		inode->i_mtime = fattr->mtime.seconds;
-		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+		 */
+		inode->i_size  = nfs_size_to_off_t(fattr->size);
+		inode->i_mtime = nfs_time_to_secs(fattr->mtime);
+		inode->i_atime = nfs_time_to_secs(fattr->atime);
+		inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+		NFS_CACHE_CTIME(inode) = fattr->ctime;
+		NFS_CACHE_MTIME(inode) = fattr->mtime;
+		NFS_CACHE_ATIME(inode) = fattr->atime;
+		NFS_CACHE_ISIZE(inode) = fattr->size;
+		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 	}
 	nfs_refresh_inode(inode, fattr);
 }
 
+static struct inode *
+nfs_make_new_inode(struct super_block *sb, struct nfs_fattr *fattr)
+{
+	struct inode *inode = get_empty_inode();
+
+	if (!inode)
+		return NULL;	
+	inode->i_sb = sb;
+	inode->i_dev = sb->s_dev;
+	inode->i_flags = 0;
+	inode->i_ino = nfs_fattr_to_ino_t(fattr);
+	nfs_read_inode(inode);
+	nfs_fill_inode(inode, fattr);
+	return inode;
+}
+
 /*
- * The following may seem pretty minimal, but the stateless nature
- * of NFS means that we can't do too much more. Previous attempts to use
- * fattr->nlink to determine how well the cached state matches the
- * server suffer from races with stale dentries. You also risk killing
- * off processes by just doing 'mv file newdir' on the server.
- *
- * FIXME: Of course, if 2 exported files have the same fileid (but
- * different fsid which makes it legal) you're still buggered...
- *                                      Trond, August 1999.
+ * In NFSv3 we can have 64bit inode numbers. In order to support
+ * this, and re-exported directories (also seen in NFSv2)
+ * we are forced to allow 2 different inodes to have the same
+ * i_ino.
  */
 static int
+nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
+{
+	struct nfs_fattr *fattr = (struct nfs_fattr *)opaque;
+	if (NFS_FSID(inode) != fattr->fsid)
+		return 0;
+	if (NFS_FILEID(inode) != fattr->fileid)
+		return 0;
+	if (inode->i_mode &&
+	    (fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT))
+		return 0;
+	if (is_bad_inode(inode))
+		return 0;
+	if (NFS_FLAGS(inode) & NFS_INO_STALE)
+		return 0;
+	return 1;
+}
+
+static int
 nfs_inode_is_stale(struct inode *inode, struct nfs_fattr *fattr)
 {
 	int unhashed;
@@ -529,16 +755,16 @@
 		is_stale = 1;
 
 	/*
-	 * Free up unused cached dentries to see if it's wise to unhash
-	 * the inode (which we can do if all the dentries have been unhashed).
+	 * If the inode seems stale, free up cached dentries.
 	 */
 	unhashed = nfs_free_dentries(inode);
 
-	/* Assume we're holding 1 lock on the inode from 'iget'
+	/* Assume we're holding an i_count
 	 *
 	 * NB: sockets sometimes have volatile file handles
 	 *     don't invalidate their inodes even if all dentries are
-	 *     unhashed. */
+	 *     unhashed.
+	 */
 	if (unhashed && inode->i_count == unhashed + 1
 	    && !S_ISSOCK(inode->i_mode) && !S_ISFIFO(inode->i_mode))
 		is_stale = 1;
@@ -561,12 +787,12 @@
 {
 	struct super_block *sb = dentry->d_sb;
 
-	dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+	dprintk("NFS: nfs_fhget(%s/%s fileid=%Lu)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		fattr->fileid);
+		(long long) fattr->fileid);
 
 	/* Install the file handle in the dentry */
-	*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+	memcpy(NFS_FH(dentry), (u8*)fhandle, sizeof(*fhandle));
 
 #ifdef CONFIG_NFS_SNAPSHOT
 	/*
@@ -576,15 +802,9 @@
 	if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
 	    (dentry->d_name.len == 9 &&
 	     memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
-		struct inode *inode = get_empty_inode();
+		struct inode *inode = nfs_make_new_inode(sb, fattr);
 		if (!inode)
-			goto out;	
-		inode->i_sb = sb;
-		inode->i_dev = sb->s_dev;
-		inode->i_flags = 0;
-		inode->i_ino = fattr->fileid;
-		nfs_read_inode(inode);
-		nfs_fill_inode(inode, fattr);
+			goto out;
 		inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
 		dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
 	out:
@@ -605,27 +825,39 @@
 static struct inode *
 __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
 {
-	struct inode *inode = NULL;
+	struct inode	*inode = NULL;
+	unsigned long	ino;
 
-	if (!fattr)
+	if ((fattr->valid & NFS_ATTR_FATTR) == 0)
 		goto out_no_inode;
 
-	while (!inode) {
-		inode = iget(sb, fattr->fileid);
-		if (!inode)
-			goto out_no_inode;
-		/* N.B. This should be impossible ... */
-		if (inode->i_ino != fattr->fileid)
-			goto out_bad_id;
+	ino = nfs_fattr_to_ino_t(fattr);
+
+	while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
 
+		/*
+		 * Check for busy inodes, and attempt to get rid of any
+		 * unused local references. If successful, we release the
+		 * inode and try again.
+		 *
+		 * Note that the busy test uses the values in the fattr,
+		 * as the inode may have become a different object.
+		 * (We can probably handle modes changes here, too.)
+		 */
 		if (!nfs_inode_is_stale(inode,fattr))
 			break;
 
-		remove_inode_hash(inode);
-		nfs_invalidate_inode(inode);
+		dprintk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+		       inode->i_ino, inode->i_count);
+		/* Mark the inode as being stale */
+		NFS_FLAGS(inode) |= NFS_INO_STALE;
+		nfs_zap_caches(inode);
 		iput(inode);
-		inode = NULL;
 	}
+
+	if (!inode)
+		goto out_no_inode;
+
 	nfs_fill_inode(inode, fattr);
 	dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino, inode->i_count);
@@ -634,10 +866,7 @@
 	return inode;
 
 out_no_inode:
-	printk("__nfs_fhget: iget failed\n");
-	goto out;
-out_bad_id:
-	printk("__nfs_fhget: unexpected inode from iget\n");
+	printk(KERN_NOTICE "__nfs_fhget: iget failed\n");
 	goto out;
 }
 
@@ -645,9 +874,8 @@
 nfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
-	int error;
-	struct nfs_sattr sattr;
 	struct nfs_fattr fattr;
+	int              error;
 
 	/*
 	 * Make sure the inode is up-to-date.
@@ -655,87 +883,123 @@
 	error = nfs_revalidate(dentry);
 	if (error) {
 #ifdef NFS_PARANOIA
-printk("nfs_notify_change: revalidate failed, error=%d\n", error);
+		printk(KERN_DEBUG "nfs_notify_change: revalidate failed, error=%d\n", error);
 #endif
 		goto out;
 	}
 
-	sattr.mode = (u32) -1;
-	if (attr->ia_valid & ATTR_MODE) 
-		sattr.mode = attr->ia_mode;
-
-	sattr.uid = (u32) -1;
-	if (attr->ia_valid & ATTR_UID)
-		sattr.uid = attr->ia_uid;
-
-	sattr.gid = (u32) -1;
-	if (attr->ia_valid & ATTR_GID)
-		sattr.gid = attr->ia_gid;
-
-	sattr.size = (u32) -1;
-	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
-		sattr.size = attr->ia_size;
-
-	sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_MTIME) {
-		sattr.mtime.seconds = attr->ia_mtime;
-		sattr.mtime.useconds = 0;
-	}
-
-	sattr.atime.seconds = sattr.atime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_ATIME) {
-		sattr.atime.seconds = attr->ia_atime;
-		sattr.atime.useconds = 0;
-	}
+	if (!S_ISREG(inode->i_mode))
+		attr->ia_valid &= ~ATTR_SIZE;
 
 	error = nfs_wb_all(inode);
-	if (error)
+	if (error < 0)
 		goto out;
 
-	error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
-				&sattr, &fattr);
-	if (error)
+	/* Now perform the setattr call */
+	error = NFS_CALL(setattr, inode, (dentry, &fattr, attr));
+	if (error || !(fattr.valid & NFS_ATTR_FATTR)) {
+		nfs_zap_caches(inode);
 		goto out;
+	}
 	/*
 	 * If we changed the size or mtime, update the inode
 	 * now to avoid invalidating the page cache.
 	 */
-	if (sattr.size != (u32) -1) {
-		if (sattr.size != fattr.size)
-			printk("nfs_notify_change: sattr=%d, fattr=%d??\n",
-				sattr.size, fattr.size);
-		inode->i_size  = sattr.size;
-		inode->i_mtime = fattr.mtime.seconds;
+	if (!(fattr.valid & NFS_ATTR_WCC)) {
+		fattr.pre_size = NFS_CACHE_ISIZE(inode);
+		fattr.pre_mtime = NFS_CACHE_MTIME(inode);
+		fattr.pre_ctime = NFS_CACHE_CTIME(inode);
+		fattr.valid |= NFS_ATTR_WCC;
 	}
-	if (sattr.mtime.seconds != (u32) -1)
-		inode->i_mtime = fattr.mtime.seconds;
 	error = nfs_refresh_inode(inode, &fattr);
 out:
 	return error;
 }
 
+int
+nfs_update_atime(struct dentry *dentry)
+{
+	struct iattr attr;
+	struct inode *inode = dentry->d_inode;
+
+	nfs_revalidate(dentry);
+	if (!inode || time_before(inode->i_atime,nfs_time_to_secs(NFS_CACHE_ATIME(inode))))
+		return 0;
+
+	attr.ia_valid = ATTR_ATIME|ATTR_ATIME_SET;
+	attr.ia_atime = inode->i_atime;
+	return nfs_notify_change(dentry, &attr);
+}
+
 /*
- * Externally visible revalidation function
+ * Wait for the inode to get unlocked.
+ * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
  */
 int
-nfs_revalidate(struct dentry *dentry)
+nfs_wait_on_inode(struct inode *inode, int flag)
 {
-	return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
+	int error;
+	if (!(NFS_FLAGS(inode) & flag))
+		return 0;
+	inode->i_count++;
+	error = nfs_wait_event(clnt, inode->i_wait, !(NFS_FLAGS(inode) & flag));
+	iput(inode);
+	return error;
 }
 
 /*
- * These are probably going to contain hooks for
- * allocating and releasing RPC credentials for
- * the file. I'll have to think about Tronds patch
- * a bit more..
+ * Externally visible revalidation function
  */
+int
+nfs_revalidate(struct dentry *dentry)
+{
+	return nfs_revalidate_inode(dentry);
+}
+
+static __inline__ struct nfs_file *nfs_file_alloc(void)
+{
+	struct nfs_file	*p;
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		p->magic = NFS_FILE_MAGIC;
+	}
+	return p;
+}
+
+static __inline__ void nfs_file_free(struct nfs_file *p)
+{
+	if (p->magic == NFS_FILE_MAGIC) {
+		p->magic = 0;
+		kfree(p);
+	} else
+		printk(KERN_ERR "NFS: extra file info corrupted!\n");
+}
+
 int nfs_open(struct inode *inode, struct file *filp)
 {
+	struct rpc_auth	*auth = NFS_CLIENT(inode)->cl_auth;
+	struct nfs_file	*data;
+
+	data = nfs_file_alloc();
+	if (!data)
+		return -ENOMEM;
+	data->cred = rpcauth_lookupcred(auth, 0);
+	filp->private_data = data;
 	return 0;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
 {
+	struct nfs_file	*data = NFS_FILE(filp);
+	struct rpc_auth	*auth = NFS_CLIENT(inode)->cl_auth;
+	struct rpc_cred	*cred;
+
+	cred = nfs_file_cred(filp);
+	if (cred)
+		rpcauth_releasecred(auth, cred);
+	nfs_file_free(data);
 	return 0;
 }
 
@@ -744,25 +1008,44 @@
  * the cached attributes have to be refreshed.
  */
 int
-_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+__nfs_revalidate_inode(struct dentry *dentry)
 {
 	struct inode	*inode = dentry->d_inode;
-	int		 status = 0;
 	struct nfs_fattr fattr;
+	int		 status = 0;
 
 	dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		inode->i_ino);
-	status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
+
+	if (!inode || is_bad_inode(inode))
+		return -ESTALE;
+
+	while (NFS_REVALIDATING(inode)) {
+		status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
+		if (status < 0)
+			return status;
+		if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+			return 0;
+	}
+	NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
+
+	status = NFS_CALL(getattr, inode, (dentry, &fattr));
 	if (status) {
 		int error;
 		u32 *fh;
+		struct dentry *dir = dentry->d_parent;
 		struct nfs_fh fhandle;
+		struct nfs_fattr dir_attr;
+
 		dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, inode->i_ino, status);
+		       dentry->d_parent->d_name.name, dentry->d_name.name,
+		       inode->i_ino, status);
+		nfs_zap_caches(inode);
+
 		if (status != -ESTALE)
 			goto out;
+
 		/*
 		 * A "stale filehandle" error ... show the current fh
 		 * and find out what the filehandle should be.
@@ -770,8 +1053,9 @@
 		fh = (u32 *) NFS_FH(dentry);
 		dfprintk(PAGECACHE, "NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
 			fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
-		error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent), 
-					dentry->d_name.name, &fhandle, &fattr);
+		error = NFS_CALL(lookup, dir->d_inode, (dir, &dir_attr, 
+					&dentry->d_name, &fhandle, &fattr));
+		nfs_refresh_inode(dir->d_inode, &dir_attr);
 		if (error) {
 			dfprintk(PAGECACHE, "NFS: lookup failed, error=%d\n", error);
 			goto out;
@@ -787,13 +1071,16 @@
 	status = nfs_refresh_inode(inode, &fattr);
 	if (status) {
 		dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, inode->i_ino, status);
+			 dentry->d_parent->d_name.name, dentry->d_name.name,
+			 inode->i_ino, status);
 		goto out;
 	}
+
 	dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 out:
+	NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
+	wake_up(&inode->i_wait);
 	return status;
 }
 
@@ -802,29 +1089,54 @@
  * an operation.  Here we update the inode to reflect the state
  * of the server's inode.
  *
- * This is a bit tricky because we have to make sure all dirty pages
- * have been sent off to the server before calling invalidate_inode_pages.
- * To make sure no other process adds more write requests while we try
- * our best to flush them, we make them sleep during the attribute refresh.
+ * If we have reason to believe that any data we cached has become
+ * invalid, we schedule it to be flushed on the next occasion
+ * (i.e. when nfs_revalidate_inode is called).
  *
- * A very similar scenario holds for the dir cache.
+ * The reason we don't do it here is because nfs_refresh_inode can
+ * be called outside of the process context, e.g. from nfs_readpage_result,
+ * which is invoked by rpciod.
  */
 int
 nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
-	int invalid = 0;
-	int error = -EIO;
-
-	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
-		 inode->i_dev, inode->i_ino, inode->i_count);
+	off_t		new_size, new_isize;
+	__u64		new_mtime;
+	int		invalid = 0;
+	int		error = -EIO;
 
 	if (!inode || !fattr) {
-		printk("nfs_refresh_inode: inode or fattr is NULL\n");
+		printk(KERN_ERR "nfs_refresh_inode: inode or fattr is NULL\n");
+		goto out;
+	}
+	if (inode->i_mode == 0) {
+		printk(KERN_ERR "nfs_refresh_inode: empty inode\n");
 		goto out;
 	}
-	if (inode->i_ino != fattr->fileid) {
-		printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
-			inode->i_ino, fattr->fileid);
+
+	if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+		goto out;
+
+	if (is_bad_inode(inode))
+		goto out;
+
+	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
+			inode->i_dev, inode->i_ino, inode->i_count,
+			fattr->valid);
+
+
+	if (NFS_FSID(inode) != fattr->fsid ||
+	    NFS_FILEID(inode) != fattr->fileid) {
+		printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
+		       "expected (0x%lx%08lx/0x%lx%08lx), got (0x%lx%08lx/0x%lx%08lx)\n",
+		       (unsigned long) (NFS_FSID(inode)>>32),
+		       (unsigned long) (NFS_FSID(inode) & 0xFFFFFFFFUL),
+		       (unsigned long) (NFS_FILEID(inode)>>32),
+		       (unsigned long) (NFS_FILEID(inode) & 0xFFFFFFFFUL),
+		       (unsigned long) (fattr->fsid >> 32),
+		       (unsigned long) (fattr->fsid & 0xFFFFFFFFUL),
+		       (unsigned long) (fattr->fileid >> 32),
+		       (unsigned long) (fattr->fileid & 0xFFFFFFFFUL));
 		goto out;
 	}
 
@@ -834,54 +1146,101 @@
 	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
 		goto out_changed;
 
-	inode->i_mode = fattr->mode;
-	inode->i_nlink = fattr->nlink;
-	inode->i_uid = fattr->uid;
-	inode->i_gid = fattr->gid;
+ 	new_mtime = fattr->mtime;
+	new_size = fattr->size;
+ 	new_isize = nfs_size_to_off_t(fattr->size);
 
-	inode->i_blocks = fattr->blocks;
-	inode->i_atime = fattr->atime.seconds;
-	inode->i_ctime = fattr->ctime.seconds;
+	error = 0;
 
 	/*
 	 * Update the read time so we don't revalidate too often.
 	 */
 	NFS_READTIME(inode) = jiffies;
-	error = 0;
 
 	/*
-	 * If we have pending write-back entries, we don't want
-	 * to look at the size or the mtime the server sends us
-	 * too closely, as we're in the middle of modifying them.
+	 * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+	 *       NOT inode->i_size!!!
 	 */
-	if (NFS_WRITEBACK(inode))
-		goto out;
-
-	if (inode->i_size != fattr->size) {
+	if (NFS_CACHE_ISIZE(inode) != new_size) {
 #ifdef NFS_DEBUG_VERBOSE
-printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
 #endif
-		inode->i_size = fattr->size;
 		invalid = 1;
 	}
 
-	if (inode->i_mtime != fattr->mtime.seconds) {
+	/*
+	 * Note: we don't check inode->i_mtime since pipes etc.
+	 *       can change this value in VFS without requiring a
+	 *	 cache revalidation.
+	 */
+	if (NFS_CACHE_MTIME(inode) != new_mtime) {
 #ifdef NFS_DEBUG_VERBOSE
-printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+		printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
 #endif
-		inode->i_mtime = fattr->mtime.seconds;
 		invalid = 1;
 	}
 
-	if (invalid)
-		goto out_invalid;
+	/* Check Weak Cache Consistency data.
+	 * If size and mtime match the pre-operation values, we can
+	 * assume that any attribute changes were caused by our NFS
+         * operation, so there's no need to invalidate the caches.
+         */
+        if ((fattr->valid & NFS_ATTR_WCC)
+	    && NFS_CACHE_ISIZE(inode) == fattr->pre_size
+	    && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
+		invalid = 0;
+	}
+
+	/*
+	 * If we have pending writebacks, things can get
+	 * messy.
+	 */
+	if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
+		new_isize = inode->i_size;
+
+	NFS_CACHE_CTIME(inode) = fattr->ctime;
+	inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+	/* If we've been messing around with atime, don't
+	 * update it. Save the server value in NFS_CACHE_ATIME.
+	 */
+	NFS_CACHE_ATIME(inode) = fattr->atime;
+	if (time_before(inode->i_atime, nfs_time_to_secs(fattr->atime)))
+		inode->i_atime = nfs_time_to_secs(fattr->atime);
+
+	NFS_CACHE_MTIME(inode) = new_mtime;
+	inode->i_mtime = nfs_time_to_secs(new_mtime);
+
+	NFS_CACHE_ISIZE(inode) = new_size;
+	inode->i_size = new_isize;
 
+	inode->i_mode = fattr->mode;
+	inode->i_nlink = fattr->nlink;
+	inode->i_uid = fattr->uid;
+	inode->i_gid = fattr->gid;
+
+	if (fattr->valid & NFS_ATTR_FATTR_V3) {
+		/*
+		 * report the blocks in 512byte units
+		 */
+		inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+		inode->i_blksize = inode->i_sb->s_blocksize;
+ 	} else {
+ 		inode->i_blocks = fattr->du.nfs2.blocks;
+ 		inode->i_blksize = fattr->du.nfs2.blocksize;
+ 	}
+ 	inode->i_rdev = 0;
+ 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ 		inode->i_rdev = to_kdev_t(fattr->rdev);
+ 
 	/* Update attrtimeo value */
-	if (fattr->mtime.seconds == NFS_OLDMTIME(inode)) {
+	if (!invalid && time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
 			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 	}
-	NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+
+	if (invalid)
+		nfs_zap_caches(inode);
 
 out:
 	return error;
@@ -891,22 +1250,16 @@
 	 * Big trouble! The inode has become a different object.
 	 */
 #ifdef NFS_PARANOIA
-printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
-inode->i_ino, inode->i_mode, fattr->mode);
+	printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+	       inode->i_ino, inode->i_mode, fattr->mode);
 #endif
 	/*
 	 * No need to worry about unhashing the dentry, as the
 	 * lookup validation will know that the inode is bad.
+	 * (But we fall through to invalidate the caches.)
 	 */
 	nfs_invalidate_inode(inode);
 	goto out;
-
-out_invalid:
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
-#endif
-	nfs_zap_caches(inode);
-	goto out;
 }
 
 /*
@@ -926,8 +1279,6 @@
 init_nfs_fs(void)
 {
 #ifdef CONFIG_PROC_FS
-	rpc_register_sysctl();
-	rpc_proc_init();
 	rpc_proc_register(&nfs_rpcstat);
 #endif
         return register_filesystem(&nfs_fs_type);
@@ -955,6 +1306,5 @@
 	rpc_proc_unregister("nfs");
 #endif
 	unregister_filesystem(&nfs_fs_type);
-	nfs_free_dircache();
 }
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/mount_clnt.c linux.18p17/fs/nfs/mount_clnt.c
--- linux.vanilla/fs/nfs/mount_clnt.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/mount_clnt.c	Fri Sep 15 22:10:43 2000
@@ -17,17 +17,14 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/xprt.h>
 #include <linux/sunrpc/sched.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
 
 #ifdef RPC_DEBUG
 # define NFSDBG_FACILITY	NFSDBG_ROOT
 #endif
 
-#ifndef MAX
-# define MAX(a, b)	(((a) > (b))? (a) : (b))
-#endif
-
-
 /*
 #define MOUNT_PROGRAM		100005
 #define MOUNT_VERSION		1
@@ -35,7 +32,9 @@
 #define MOUNT_UMNT		3
  */
 
-static struct rpc_clnt *	mnt_create(char *, struct sockaddr_in *);
+static int			nfs_gen_mount(struct sockaddr_in *, char *,
+					      struct nfs_fh *, int);
+static struct rpc_clnt *	mnt_create(char *, struct sockaddr_in *, int);
 extern struct rpc_program	mnt_program;
 
 struct mnt_fhstatus {
@@ -43,30 +42,44 @@
 	struct nfs_fh *		fh;
 };
 
+int
+nfs_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+{
+	return nfs_gen_mount(addr, path, fh, NFS_MNT_VERSION);
+}
+
+int
+nfs3_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+{
+	return nfs_gen_mount(addr, path, fh, NFS_MNT3_VERSION);
+}
+
 /*
  * Obtain an NFS file handle for the given host and path
  */
-int
-nfs_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh)
+static int
+nfs_gen_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, int version)
 {
 	struct rpc_clnt		*mnt_clnt;
 	struct mnt_fhstatus	result = { 0, fh };
 	char			hostname[32];
 	int			status;
+	int			call;
 
 	dprintk("NFS:      nfs_mount(%08x:%s)\n",
 			(unsigned)ntohl(addr->sin_addr.s_addr), path);
 
 	strcpy(hostname, in_ntoa(addr->sin_addr.s_addr));
-	if (!(mnt_clnt = mnt_create(hostname, addr)))
+	if (!(mnt_clnt = mnt_create(hostname, addr, version)))
 		return -EACCES;
 
-	status = rpc_call(mnt_clnt, NFS_MNTPROC_MNT, path, &result, 0);
+	call = (version == 3) ? MOUNTPROC3_MNT : MNTPROC_MNT;
+	status = rpc_call(mnt_clnt, call, path, &result, 0);
 	return status < 0? status : (result.status? -EACCES : 0);
 }
 
 static struct rpc_clnt *
-mnt_create(char *hostname, struct sockaddr_in *srvaddr)
+mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version)
 {
 	struct rpc_xprt	*xprt;
 	struct rpc_clnt	*clnt;
@@ -75,7 +88,7 @@
 		return NULL;
 
 	clnt = rpc_create_client(xprt, hostname,
-				&mnt_program, NFS_MNT_VERSION,
+				&mnt_program, version,
 				RPC_AUTH_NULL);
 	if (!clnt) {
 		xprt_destroy(xprt);
@@ -100,7 +113,7 @@
 static int
 xdr_encode_dirpath(struct rpc_rqst *req, u32 *p, const char *path)
 {
-	p = xdr_encode_string(p, path);
+	p = xdr_encode_string(p, path, -1);
 
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -109,14 +122,39 @@
 static int
 xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
 {
-	if ((res->status = ntohl(*p++)) == 0)
-		memcpy(res->fh, p, sizeof(*res->fh));
+	struct nfs_fh *fh = res->fh;
+
+	memset((void *)fh, 0, sizeof(*fh));
+	if ((res->status = ntohl(*p++)) == 0) {
+		res->fh->size = NFS2_FHSIZE;
+		memcpy(res->fh->data, p, NFS2_FHSIZE);
+	}
+	return 0;
+}
+
+static int
+xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
+{
+	struct nfs_fh *fh = res->fh;
+
+	memset((void *)fh, 0, sizeof(*fh));
+	if ((res->status = ntohl(*p++)) == 0) {
+		unsigned int size = ntohl(*p++);
+		if (size <= NFS3_FHSIZE) {
+			res->fh->size = size;
+			memcpy(res->fh->data, p, size);
+		}
+	}
 	return 0;
 }
 
 #define MNT_dirpath_sz		(1 + 256)
 #define MNT_fhstatus_sz		(1 + 8)
 
+#ifndef MAX
+# define MAX(a, b)      (((a) > (b))? (a) : (b))
+#endif
+
 static struct rpc_procinfo	mnt_procedures[2] = {
 	{ "mnt_null",
 		(kxdrproc_t) xdr_error,	
@@ -124,16 +162,32 @@
 	{ "mnt_mount",
 		(kxdrproc_t) xdr_encode_dirpath,	
 		(kxdrproc_t) xdr_decode_fhstatus,
-		MAX(MNT_dirpath_sz, MNT_fhstatus_sz)<<2, 0},
+		MAX(MNT_dirpath_sz, MNT_fhstatus_sz) << 2, 0 },
+};
+
+static struct rpc_procinfo mnt3_procedures[2] = {
+	{ "mnt3_null",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+	{ "mnt3_mount",
+		(kxdrproc_t) xdr_encode_dirpath,
+		(kxdrproc_t) xdr_decode_fhstatus3,
+		MAX(MNT_dirpath_sz, MNT_fhstatus_sz) << 2, 0 },
 };
 
 static struct rpc_version	mnt_version1 = {
 	1, 2, mnt_procedures
 };
 
+static struct rpc_version	mnt_version3 = {
+	3, 2, mnt3_procedures
+};
+
 static struct rpc_version *	mnt_version[] = {
 	NULL,
 	&mnt_version1,
+	NULL,
+	&mnt_version3,
 };
 
 static struct rpc_stat		mnt_stats;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/nfs2xdr.c linux.18p17/fs/nfs/nfs2xdr.c
--- linux.vanilla/fs/nfs/nfs2xdr.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/nfs2xdr.c	Fri Sep 15 22:10:43 2000
@@ -1,13 +1,15 @@
 /*
- * linux/fs/nfs/xdr.c
+ * linux/fs/nfs/nfs2xdr.c
  *
  * XDR functions to encode/decode NFS RPC arguments and results.
  *
  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
  * Copyright (C) 1996 Olaf Kirch
+ * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
+ * 		FIFO's need special handling in NFSv2
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
@@ -20,6 +22,8 @@
 #include <linux/pagemap.h>
 #include <linux/proc_fs.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
 #include <linux/nfs_fs.h>
 
 /* Uncomment this to support servers requiring longword lengths */
@@ -28,8 +32,7 @@
 #define NFSDBG_FACILITY		NFSDBG_XDR
 /* #define NFS_PARANOIA 1 */
 
-#define QUADLEN(len)		(((len) + 3) >> 2)
-static int			nfs_stat_to_errno(int stat);
+extern int			nfs_stat_to_errno(int stat);
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO		EIO
@@ -40,8 +43,8 @@
  */
 #define NFS_fhandle_sz		8
 #define NFS_sattr_sz		8
-#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
+#define NFS_filename_sz		1+(NFS2_MAXNAMLEN>>2)
+#define NFS_path_sz		1+(NFS2_MAXPATHLEN>>2)
 #define NFS_fattr_sz		17
 #define NFS_info_sz		5
 #define NFS_entry_sz		NFS_filename_sz+3
@@ -49,6 +52,7 @@
 #define NFS_enc_void_sz		0
 #define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
 #define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readlinkargs_sz	NFS_fhandle_sz
 #define NFS_readargs_sz		NFS_fhandle_sz+3
 #define NFS_writeargs_sz	NFS_fhandle_sz+4
 #define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
@@ -60,8 +64,9 @@
 #define NFS_dec_void_sz		0
 #define NFS_attrstat_sz		1+NFS_fattr_sz
 #define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
+#define NFS_readlinkres_sz	1
 #define NFS_readres_sz		1+NFS_fattr_sz+1
+#define NFS_writeres_sz         NFS_attrstat_sz
 #define NFS_stat_sz		1
 #define NFS_readdirres_sz	1
 #define NFS_statfsres_sz	1+NFS_info_sz
@@ -72,15 +77,19 @@
 static inline u32 *
 xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
 {
-	*((struct nfs_fh *) p) = *fhandle;
-	return p + QUADLEN(sizeof(*fhandle));
+	memcpy(p, fhandle->data, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
 xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
 {
-	*fhandle = *((struct nfs_fh *) p);
-	return p + QUADLEN(sizeof(*fhandle));
+	/* Zero handle first to allow comparisons */
+	memset(fhandle, 0, sizeof(*fhandle));
+	/* NFSv2 handles have a fixed length */
+	fhandle->size = NFS2_FHSIZE;
+	memcpy(fhandle->data, p, NFS2_FHSIZE);
+	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
 static inline u32 *
@@ -91,7 +100,14 @@
 	if (*len > maxlen)
 		return NULL;
 	*string = (char *) p;
-	return p + QUADLEN(*len);
+	return p + XDR_QUADLEN(*len);
+}
+
+static inline u32*
+xdr_decode_time(u32 *p, u64 *timep)
+{
+	*timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+	return p;
 }
 
 static inline u32 *
@@ -103,33 +119,51 @@
 	fattr->uid = ntohl(*p++);
 	fattr->gid = ntohl(*p++);
 	fattr->size = ntohl(*p++);
-	fattr->blocksize = ntohl(*p++);
+	fattr->du.nfs2.blocksize = ntohl(*p++);
 	fattr->rdev = ntohl(*p++);
-	fattr->blocks = ntohl(*p++);
+	fattr->du.nfs2.blocks = ntohl(*p++);
 	fattr->fsid = ntohl(*p++);
 	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
+	p = xdr_decode_time(p, &fattr->atime);
+	p = xdr_decode_time(p, &fattr->mtime);
+	p = xdr_decode_time(p, &fattr->ctime);
+	fattr->valid |= NFS_ATTR_FATTR;
+	if (fattr->type == NFCHR && fattr->rdev == NFS2_FIFO_DEV) {
+		fattr->type = NFFIFO;
+		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+		fattr->rdev = 0;
+	}
 	return p;
 }
 
+#define SATTR(p, attr, flag, field) \
+        *p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+xdr_encode_sattr(u32 *p, struct iattr *attr)
 {
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
-	return p;
+	SATTR(p, attr, ATTR_MODE, ia_mode);
+	SATTR(p, attr, ATTR_UID, ia_uid);
+	SATTR(p, attr, ATTR_GID, ia_gid);
+	SATTR(p, attr, ATTR_SIZE, ia_size);
+
+	if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
+		*p++ = htonl(attr->ia_atime);
+		*p++ = 0;
+	} else {
+		*p++ = ~(u32) 0;
+		*p++ = ~(u32) 0;
+	}
+
+	if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
+		*p++ = htonl(attr->ia_mtime);
+		*p++ = 0;
+	} else {
+		*p++ = ~(u32) 0;	
+		*p++ = ~(u32) 0;
+	}
+  	return p;
 }
+#undef SATTR;
 
 /*
  * NFS encode functions
@@ -176,7 +210,7 @@
 nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -190,7 +224,8 @@
 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen, buflen;
+	size_t		replen;
+	unsigned int	nr;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	*p++ = htonl(args->offset);
@@ -198,21 +233,22 @@
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
+	/* Get the number of buffers in the receive iovec */
+        nr = args->nriov;
+
+        if (nr+1 > MAX_IOVEC) {
+                printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+                return -EINVAL;
+        }
+
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
 	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->count;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	req->rq_rvec[0].iov_len  = replen;
-#endif
+        /* Copy the iovec */
+        memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+	req->rq_rlen = args->count + replen;
+	req->rq_rnr += nr;
 
 	return 0;
 }
@@ -224,9 +260,9 @@
 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
 	struct iovec *iov = req->rq_rvec;
-	int	status, count, recvd, hdrlen;
+	u32	count, recvd, hdrlen;
+	int	status;
 
-	dprintk("RPC:      readres OK status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	p = xdr_decode_fattr(p, res->fattr);
@@ -234,22 +270,22 @@
 	count = ntohl(*p++);
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
 	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[2].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
-	if (count > recvd) {
-		printk("NFS: server cheating in read reply: "
-			"count %d > recvd %d\n", count, recvd);
-		count = recvd;
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READ header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
 
 	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count)
-		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+	if (count < res->count) {
+		xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count);
+		res->count = count;
+		res->eof = 1;  /* Silly NFSv3ism which can't be helped */
+	} else
+		res->eof = 0;
 
 	return count;
 }
@@ -261,6 +297,7 @@
 static int
 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
+	unsigned int nr;
 	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
@@ -270,28 +307,35 @@
 	*p++ = htonl(count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = count;
-	req->rq_slen += count;
-	req->rq_snr = 2;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+                printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
+                        "(nr %d max %d)\n", nr, MAX_IOVEC);
+                return -EINVAL;
+        }
+
+	/* Copy the iovec */
+        memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
 #ifdef NFS_PAD_WRITES
 	/*
 	 * Some old servers require that the message length
 	 * be a multiple of 4, so we pad it here if needed.
 	 */
-	count = ((count + 3) & ~3) - count;
-	if (count) {
-#if 0
-printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n",
-req->rq_svec[1].iov_len, req->rq_slen, count);
-#endif
-		req->rq_svec[2].iov_base = (void *) "\0\0\0";
-		req->rq_svec[2].iov_len  = count;
-		req->rq_slen += count;
-		req->rq_snr = 3;
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
 	}
 #endif
+	req->rq_slen += count;
+	req->rq_snr += nr;
 
 	return 0;
 }
@@ -304,7 +348,7 @@
 nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -317,9 +361,9 @@
 nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -332,7 +376,7 @@
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -344,8 +388,8 @@
 nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_string(p, args->topath);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
+	p = xdr_encode_string(p, args->topath, args->tolen);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -360,7 +404,7 @@
 	struct rpc_task	*task = req->rq_task;
 	struct rpc_auth	*auth = task->tk_auth;
 	u32		bufsiz = args->bufsiz;
-	int		replen;
+	size_t		replen;
 
 	/*
 	 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
@@ -376,119 +420,95 @@
 
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
 	req->rq_rvec[1].iov_len  = args->bufsiz;
 	req->rq_rlen = replen + args->bufsiz;
-	req->rq_rnr = 2;
-
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
+	req->rq_rnr ++;
 
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
+ * We're not really decoding anymore, we just leave the buffer untouched
+ * and only check that it is syntactically correct.
+ * The real decoding happens in nfs_decode_entry below, called directly
+ * from nfs_readdir for each entry.
  */
 static int
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
+	u32			hdrlen;
 	int			 status, nr;
-	char			*string, *start;
-	u32			*end, *entry, len, fileid, cookie;
+	u32			*end, *entry, len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk("NFS: Odd RPC header size in readdirres reply\n");
+	hdrlen = (u8*)p - (u8*) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READDIR header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
 
 	/* Get start and end address of XDR data */
 	p   = (u32 *) iov[1].iov_base;
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 
 	/* Get start and end of dirent buffer */
-	entry  = (u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = (char *) res->buffer + res->bufsiz;
-	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
+	if (res->buffer != p) {
+		printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
+		return -errno_NFSERR_IO;
+	}
 
+	for (nr = 0; *p++; nr++) {
+		entry = p - 1;
+		if (p + 2 > end)
+			goto short_pkt;
+		p++; /* fileid */
 		len = ntohl(*p++);
-		/*
-		 * Check whether the server has exceeded our reply buffer,
-		 * and set a flag to convert the size to longwords.
-		 */
-		if ((p + QUADLEN(len) + 3) > end) {
-			struct rpc_clnt *clnt = req->rq_task->tk_client;
-			printk(KERN_WARNING
-				"NFS: server %s, readdir reply truncated\n",
-				clnt->cl_server);
-			printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
-				nr, (end - p), len);
-			clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
-			break;
-		}
-		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
+		p += XDR_QUADLEN(len) + 1;	/* name plus cookie */
+		if (len > NFS2_MAXNAMLEN) {
+			printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
 						len);
 			return -errno_NFSERR_IO;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
-		}
-
-		memmove(string, p, len);
-		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
-	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+		if (p + 2 > end)
+			goto short_pkt;
+	}
+	return nr;
+ short_pkt:
+	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
+	entry[0] = entry[1] = 0;
 	return nr;
 }
 
+u32 *
+nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+	if (!*p++) {
+		if (!*p)
+			return ERR_PTR(-EAGAIN);
+		entry->eof = 1;
+		return ERR_PTR(-EBADCOOKIE);
+	}
+
+	entry->ino	  = ntohl(*p++);
+	entry->len	  = ntohl(*p++);
+	entry->name	  = (const char *) p;
+	p		 += XDR_QUADLEN(entry->len);
+	entry->prev_cookie	  = entry->cookie;
+	entry->cookie	  = ntohl(*p++);
+	entry->eof	  = !p[0] && p[1];
+
+	return p;
+}
+
 /*
  * NFS XDR decode functions
  */
@@ -523,12 +543,9 @@
 {
 	int	status;
 
-	dprintk("RPC:      attrstat status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	xdr_decode_fattr(p, fattr);
-	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
-		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
 	return 0;
 }
 
@@ -541,14 +558,31 @@
 {
 	int	status;
 
-	dprintk("RPC:      diropres status %lx\n", (long)ntohl(*p));
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	p = xdr_decode_fhandle(p, res->fh);
 	xdr_decode_fattr(p, res->fattr);
-	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
-		res->fattr->type, res->fattr->mode,
-		res->fattr->fsid, res->fattr->fileid);
+	return 0;
+}
+
+/*
+ * Encode READLINK args
+ */
+static int
+nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	size_t		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr ++;
 	return 0;
 }
 
@@ -558,33 +592,81 @@
 static int
 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
 {
+	struct iovec *iov = req->rq_rvec;
+	u32	hdrlen;
+	u32	*strlen;
+	char	*string;
 	int	status;
+	unsigned int len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
-
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	hdrlen = (u8*)p - (u8*) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov[0].iov_len) {
+		dprintk("NFS: Short READLINK header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+	strlen = (u32*)res->buffer;
+	/* Convert length of symlink */
+	len = ntohl(*strlen);
+	if (len > res->bufsiz - 5)
+		len = res->bufsiz - 5;
+	*strlen = len;
+	/* NULL terminate the string we got */
+	string = (char *)(strlen + 1);
+	string[len] = 0;
 	return 0;
 }
 
 /*
+ * Decode WRITE reply
+ */
+static int
+nfs_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	res->verf->committed = NFS_FILE_SYNC;
+	return nfs_xdr_attrstat(req, p, res->fattr);
+}
+
+/*
  * Decode STATFS reply
  */
 static int
 nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 {
 	int	status;
+	u32	xfer_size;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
+
+	/* For NFSv2, we more or less have to guess the preferred
+	 * read/write/readdir sizes from the single 'transfer size'
+	 * value.
+	 */
+	xfer_size = ntohl(*p++);	/* tsize */
+	res->rtmax  = 8 * 1024;
+	res->rtpref = xfer_size;
+	res->rtmult = xfer_size;
+	res->wtmax  = 8 * 1024;
+	res->wtpref = xfer_size;
+	res->wtmult = xfer_size;
+	res->dtpref = PAGE_CACHE_SIZE;
+	res->maxfilesize = 0x7FFFFFFF;	/* just a guess */
+	res->bsize  = ntohl(*p++);
+
+	res->tbytes = ntohl(*p++) * res->bsize;
+	res->fbytes = ntohl(*p++) * res->bsize;
+	res->abytes = ntohl(*p++) * res->bsize;
+	res->tfiles = 0;
+	res->ffiles = 0;
+	res->afiles = 0;
+	res->namelen = 0;
+
 	return 0;
 }
 
@@ -601,7 +683,7 @@
 	{ NFSERR_NOENT,		ENOENT		},
 	{ NFSERR_IO,		errno_NFSERR_IO	},
 	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
+/*	{ NFSERR_EAGAIN,	EAGAIN		}, */
 	{ NFSERR_ACCES,		EACCES		},
 	{ NFSERR_EXIST,		EEXIST		},
 	{ NFSERR_XDEV,		EXDEV		},
@@ -612,18 +694,31 @@
 	{ NFSERR_FBIG,		EFBIG		},
 	{ NFSERR_NOSPC,		ENOSPC		},
 	{ NFSERR_ROFS,		EROFS		},
-	{ NFSERR_OPNOTSUPP,	EOPNOTSUPP	},
+	{ NFSERR_MLINK,		EMLINK		},
 	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
 	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
 	{ NFSERR_DQUOT,		EDQUOT		},
 	{ NFSERR_STALE,		ESTALE		},
+	{ NFSERR_REMOTE,	EREMOTE		},
 #ifdef EWFLUSH
 	{ NFSERR_WFLUSH,	EWFLUSH		},
 #endif
+	{ NFSERR_BADHANDLE,	EBADHANDLE	},
+	{ NFSERR_NOT_SYNC,	ENOTSYNC	},
+	{ NFSERR_BAD_COOKIE,	EBADCOOKIE	},
+	{ NFSERR_NOTSUPP,	ENOTSUPP	},
+	{ NFSERR_TOOSMALL,	ETOOSMALL	},
+	{ NFSERR_SERVERFAULT,	ESERVERFAULT	},
+	{ NFSERR_BADTYPE,	EBADTYPE	},
+	{ NFSERR_JUKEBOX,	EJUKEBOX	},
 	{ -1,			EIO		}
 };
 
-static int
+/*
+ * Convert an NFS error code to a local one.
+ * This one is used jointly by NFSv2 and NFSv3.
+ */
+int
 nfs_stat_to_errno(int stat)
 {
 	int i;
@@ -632,7 +727,7 @@
 		if (nfs_errtbl[i].stat == stat)
 			return nfs_errtbl[i].errno;
 	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+	printk(KERN_ERR "nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
 	return nfs_errtbl[i].errno;
 }
 
@@ -644,7 +739,8 @@
     { "nfs_" #proc,					\
       (kxdrproc_t) nfs_xdr_##argtype,			\
       (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
+      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2,	\
+      0							\
     }
 
 static struct rpc_procinfo	nfs_procedures[18] = {
@@ -653,10 +749,10 @@
     PROC(setattr,	sattrargs,	attrstat),
     PROC(root,		enc_void,	dec_void),
     PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
+    PROC(readlink,	readlinkargs,	readlinkres),
     PROC(read,		readargs,	readres),
     PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
+    PROC(write,		writeargs,	writeres),
     PROC(create,	createargs,	diropres),
     PROC(remove,	diropargs,	stat),
     PROC(rename,	renameargs,	stat),
@@ -668,22 +764,8 @@
     PROC(statfs,	fhandle,	statfsres),
 };
 
-static struct rpc_version	nfs_version2 = {
+struct rpc_version		nfs_version2 = {
 	2,
 	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
 	nfs_procedures
-};
-
-static struct rpc_version *	nfs_version[] = {
-	NULL,
-	NULL,
-	&nfs_version2
-};
-
-struct rpc_program	nfs_program = {
-	"nfs",
-	NFS_PROGRAM,
-	sizeof(nfs_version) / sizeof(nfs_version[0]),
-	nfs_version,
-	&nfs_rpcstat,
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/nfs3proc.c linux.18p17/fs/nfs/nfs3proc.c
--- linux.vanilla/fs/nfs/nfs3proc.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/nfs/nfs3proc.c	Fri Sep 15 22:10:43 2000
@@ -0,0 +1,509 @@
+/*
+ *  linux/fs/nfs/nfs3proc.c
+ *
+ *  Client-side NFSv3 procedures stubs.
+ *
+ *  Copyright (C) 1997, Olaf Kirch
+ */
+
+#define NFS_NEED_NFS3_XDR_TYPES
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+
+#include <asm/segment.h>
+
+#define NFSDBG_FACILITY		NFSDBG_PROC
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+				struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("NFS call  getroot\n");
+	fattr->valid = 0;
+	status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0);
+	dprintk("NFS reply getroot\n");
+	return status;
+}
+
+/*
+ * One function for each procedure in the NFS protocol.
+ */
+static int
+nfs3_proc_getattr(struct dentry *dentry, struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("NFS call  getattr\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_GETATTR,
+			  NFS_FH(dentry), fattr, 0);
+	dprintk("NFS reply getattr\n");
+	return status;
+}
+
+static int
+nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
+			struct iattr *sattr)
+{
+	struct nfs3_sattrargs	arg = { NFS_FH(dentry), sattr, 0, 0 };
+	int	status;
+
+	dprintk("NFS call  setattr\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_SETATTR, &arg, fattr, 0);
+	dprintk("NFS reply setattr\n");
+	return status;
+}
+
+static int
+nfs3_proc_lookup(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  lookup %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_LOOKUP, &arg, &res, 0);
+	if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_GETATTR,
+			 fhandle, fattr, 0);
+	dprintk("NFS reply lookup: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_access(struct dentry *dentry, int mode, struct nfs_fattr *fattr, int ruid)
+{
+	struct nfs3_accessargs	arg = { NFS_FH(dentry), 0 };
+	struct nfs3_accessres	res = { fattr, 0 };
+	int	status, flags;
+
+	dprintk("NFS call  access\n");
+	fattr->valid = 0;
+
+	if (mode & MAY_READ)
+		arg.access |= NFS3_ACCESS_READ;
+	if (S_ISDIR(dentry->d_inode->i_mode)) {
+		if (mode & MAY_WRITE)
+			arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE;
+		if (mode & MAY_EXEC)
+			arg.access |= NFS3_ACCESS_LOOKUP;
+	} else {
+		if (mode & MAY_WRITE)
+			arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+		if (mode & MAY_EXEC)
+			arg.access |= NFS3_ACCESS_EXECUTE;
+	}
+	flags = (ruid) ? RPC_CALL_REALUID : 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_ACCESS, &arg, &res, flags);
+	dprintk("NFS reply access\n");
+
+	if (status == 0 && (arg.access & res.access) != arg.access)
+		status = -EACCES;
+	return status;
+}
+
+static int
+nfs3_proc_readlink(struct dentry *dentry, struct nfs_fattr *fattr,
+			void *buffer, unsigned int buflen)
+{
+	struct nfs3_readlinkargs args = { NFS_FH(dentry), buffer, buflen };
+	struct nfs3_readlinkres	res = { fattr, buffer, buflen };
+	int			status;
+
+	dprintk("NFS call  readlink\n");
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_READLINK,
+					&args, &res, 0);
+	dprintk("NFS reply readlink: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr,
+	       struct rpc_cred *cred, int flags,
+	       unsigned long offset, unsigned int count,
+	       void *buffer, int *eofp)
+{
+	struct nfs_readargs	arg = { NFS_FH(dentry), offset, count, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_readres	res = { fattr, count, 0 };
+	struct rpc_message	msg = { NFS3PROC_READ, &arg, &res, cred };
+	int			status;
+
+	dprintk("NFS call  read %d @ %ld\n", count, offset);
+	fattr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+	dprintk("NFS reply read: %d\n", status);
+	*eofp = res.eof;
+	return status;
+}
+
+static int
+nfs3_proc_write(struct dentry *dentry, struct nfs_fattr *fattr,
+		struct rpc_cred *cred, int flags,
+		unsigned long offset, unsigned int count,
+		void *buffer, struct nfs_writeverf *verf)
+{
+	struct nfs_writeargs	arg = { NFS_FH(dentry), offset, count,
+					NFS_FILE_SYNC, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_writeres	res = { fattr, verf, 0 };
+	struct rpc_message	msg = { NFS3PROC_WRITE, &arg, &res, cred };
+	int			status, rpcflags = 0;
+
+	dprintk("NFS call  write %d @ %ld\n", count, offset);
+	fattr->valid = 0;
+	if (flags & NFS_RW_SWAP)
+		rpcflags |= NFS_RPC_SWAPFLAGS;
+	arg.stable = (flags & NFS_RW_SYNC) ? NFS_FILE_SYNC : NFS_UNSTABLE;
+
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, rpcflags);
+
+	dprintk("NFS reply read: %d\n", status);
+	return status < 0? status : res.count;
+}
+
+/*
+ * Create a regular file.
+ * For now, we don't implement O_EXCL.
+ */
+static int
+nfs3_proc_create(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr, int flags,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr, 0, { 0, 0 } };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  create %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+
+	arg.createmode = NFS3_CREATE_UNCHECKED;
+	if (flags & O_EXCL) {
+		arg.createmode  = NFS3_CREATE_EXCLUSIVE;
+		arg.verifier[0] = jiffies;
+		arg.verifier[1] = current->pid;
+	}
+
+again:
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_CREATE, &arg, &res, 0);
+
+	/* If the server doesn't support the exclusive creation semantics,
+	 * try again with simple 'guarded' mode. */
+	if (status == NFSERR_NOTSUPP) {
+		switch (arg.createmode) {
+			case NFS3_CREATE_EXCLUSIVE:
+				arg.createmode = NFS3_CREATE_GUARDED;
+				break;
+
+			case NFS3_CREATE_GUARDED:
+				arg.createmode = NFS3_CREATE_UNCHECKED;
+				break;
+
+			case NFS3_CREATE_UNCHECKED:
+				goto exit;
+		}
+		goto again;
+	}
+
+exit:
+	dprintk("NFS reply create: %d\n", status);
+
+	/* When we created the file with exclusive semantics, make
+	 * sure we set the attributes afterwards. */
+	if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) {
+		struct nfs3_sattrargs	arg = { fhandle, sattr, 0, 0 };
+		dprintk("NFS call  setattr (post-create)\n");
+
+		/* Note: we could use a guarded setattr here, but I'm
+		 * not sure this buys us anything (and I'd have
+		 * to revamp the NFSv3 XDR code) */
+		fattr->valid = 0;
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SETATTR,
+						&arg, fattr, 0);
+		dprintk("NFS reply setattr (post-create): %d\n", status);
+	}
+
+	return status;
+}
+
+static int
+nfs3_proc_remove(struct dentry *dir, struct nfs_fattr *dir_attr,
+				struct qstr *name)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct rpc_message	msg = {NFS3PROC_REMOVE, &arg, dir_attr, NULL };
+	int			status;
+
+	dprintk("NFS call  remove %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+	dprintk("NFS reply remove: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
+{
+	struct nfs3_diropargs	*arg;
+	struct nfs_fattr	*res;
+	unsigned		size = sizeof(*arg)+sizeof(*res);
+
+	arg = (struct nfs3_diropargs *)kmalloc(size, GFP_KERNEL);
+	if (!arg)
+		return -ENOMEM;
+	memset(arg, 0, size);
+	res = (struct nfs_fattr*)(arg + 1);
+	arg->fh = NFS_FH(dir);
+	arg->name = name->name;
+	arg->len = name->len;
+	msg->rpc_proc = NFS3PROC_REMOVE;
+	msg->rpc_argp = arg;
+	msg->rpc_resp = res;
+	return 0;
+}
+
+static void
+nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+	struct nfs_fattr	*dir_attr = (struct nfs_fattr*)msg->rpc_resp;
+
+	if (msg->rpc_argp) {
+		nfs_refresh_inode(dir->d_inode, dir_attr);
+		kfree(msg->rpc_argp);
+	}
+}
+
+
+static int
+nfs3_proc_rename(struct dentry *old_dir, struct nfs_fattr *old_attr,
+			struct qstr *old_name,
+			struct dentry *new_dir, struct nfs_fattr *new_attr,
+			struct qstr *new_name)
+{
+	struct nfs3_renameargs	arg = { NFS_FH(old_dir),
+					old_name->name, old_name->len,
+					NFS_FH(new_dir),
+					new_name->name, new_name->len };
+	struct nfs3_renameres	res = { old_attr, new_attr };
+	int			status;
+
+	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+	old_attr->valid = 0;
+	new_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFS3PROC_RENAME, &arg, &res, 0);
+	dprintk("NFS reply rename: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_link(struct dentry *dentry, struct nfs_fattr *fattr,
+			struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name)
+{
+	struct nfs3_linkargs	arg = { NFS_FH(dentry), NFS_FH(dir),
+					name->name, name->len };
+	struct nfs3_linkres	res = { dir_attr, fattr };
+	int			status;
+
+	dprintk("NFS call  link %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFS3PROC_LINK, &arg, &res, 0);
+	dprintk("NFS reply link: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_symlink(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct qstr *path,
+			struct iattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_symlinkargs	arg = { NFS_FH(dir), name->name, name->len,
+					path->name, path->len, sattr };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_SYMLINK, &arg, &res, 0);
+	dprintk("NFS reply symlink: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_mkdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs3_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr, 0, { 0, 0 } };
+	struct nfs3_diropres	res = { dir_attr, fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  mkdir %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKDIR, &arg, &res, 0);
+	dprintk("NFS reply mkdir: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_rmdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+				struct qstr *name)
+{
+	struct nfs3_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	int			status;
+
+	dprintk("NFS call  rmdir %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_RMDIR, &arg, dir_attr, 0);
+	dprintk("NFS reply rmdir: %d\n", status);
+	return status;
+}
+
+/*
+ * The READDIR implementation is somewhat hackish - we pass the user buffer
+ * to the encode function, which installs it in the receive iovec.
+ * The decode function itself doesn't perform any decoding, it just makes
+ * sure the reply is syntactically correct.
+ *
+ * Also note that this implementation handles both plain readdir and
+ * readdirplus.
+ */
+static int
+nfs3_proc_readdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+		  struct rpc_cred *cred,
+		  u64 cookie, void *entry, unsigned int size, int plus)
+{
+	struct nfs3_readdirargs	arg = { NFS_FH(dir), cookie, {0, 0}, 0, 0, 0 };
+	struct nfs3_readdirres	res = { dir_attr, 0, 0, 0, 0 };
+	struct rpc_message	msg = { NFS3PROC_READDIR, &arg, &res, cred };
+	u32			*verf = NFS_COOKIEVERF(dir->d_inode);
+	int			status;
+
+	arg.buffer  = entry;
+	arg.bufsiz  = size;
+	arg.verf[0] = verf[0];
+	arg.verf[1] = verf[1];
+	arg.plus    = plus;
+	res.buffer  = entry;
+	res.bufsiz  = size;
+	res.verf    = verf;
+	res.plus    = plus;
+
+	if (plus)
+		msg.rpc_proc = NFS3PROC_READDIRPLUS;
+
+	dprintk("NFS call  readdir%s %d\n",
+			plus? "plus" : "", (unsigned int) cookie);
+
+	dir_attr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+	dprintk("NFS reply readdir: %d\n", status);
+	return status;
+}
+
+static int
+nfs3_proc_mknod(struct dentry *dir, struct nfs_fattr *dir_attr,
+			struct qstr *name, struct iattr *sattr,
+			dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+	struct nfs3_mknodargs	arg = { NFS_FH(dir), name->name, name->len, 0,
+					sattr, rdev };
+	struct nfs3_diropres	res = { dir_attr, fh, fattr };
+	int			status;
+
+	switch (sattr->ia_mode & S_IFMT) {
+	case S_IFBLK:	arg.type = NF3BLK;  break;
+	case S_IFCHR:	arg.type = NF3CHR;  break;
+	case S_IFIFO:	arg.type = NF3FIFO; break;
+	case S_IFSOCK:	arg.type = NF3SOCK; break;
+	default:	return -EINVAL;
+	}
+
+	dprintk("NFS call  mknod %s %x\n", name->name, rdev);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFS3PROC_MKNOD, &arg, &res, 0);
+	dprintk("NFS reply mknod: %d\n", status);
+	return status;
+}
+
+/*
+ * This is a combo call of fsstat and fsinfo
+ */
+static int
+nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_fsinfo *info)
+{
+	int	status;
+
+	dprintk("NFS call  fsstat\n");
+	memset((char *)info, 0, sizeof(*info));
+	status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, info, 0);
+	if (status < 0)
+		goto error;
+	status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0);
+
+error:
+	dprintk("NFS reply statfs: %d\n", status);
+	return status;
+}
+
+struct nfs_rpc_ops	nfs_v3_clientops = {
+	3,			/* protocol version */
+	nfs3_proc_get_root,
+	nfs3_proc_getattr,
+	nfs3_proc_setattr,
+	nfs3_proc_lookup,
+	nfs3_proc_access,
+	nfs3_proc_readlink,
+	nfs3_proc_read,
+	nfs3_proc_write,
+	NULL,			/* commit */
+	nfs3_proc_create,
+	nfs3_proc_remove,
+	nfs3_proc_unlink_setup,
+	nfs3_proc_unlink_done,
+	nfs3_proc_rename,
+	nfs3_proc_link,
+	nfs3_proc_symlink,
+	nfs3_proc_mkdir,
+	nfs3_proc_rmdir,
+	nfs3_proc_readdir,
+	nfs3_proc_mknod,
+	nfs3_proc_statfs,
+
+	nfs3_decode_dirent,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/nfs3xdr.c linux.18p17/fs/nfs/nfs3xdr.c
--- linux.vanilla/fs/nfs/nfs3xdr.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/nfs3xdr.c	Fri Sep 15 22:10:44 2000
@@ -1,94 +1,160 @@
 /*
- * linux/fs/nfs/nfs2xdr.c
+ * linux/fs/nfs/nfs3xdr.c
  *
  * XDR functions to encode/decode NFSv3 RPC arguments and results.
- * Note: this is incomplete!
  *
- * Copyright (C) 1996 Olaf Kirch
+ * Copyright (C) 1996, 1997 Olaf Kirch
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS3_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-#include <linux/nfs_fs.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
 #include <linux/proc_fs.h>
+#include <linux/kdev_t.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
 
-#ifdef RPC_DEBUG
-# define RPC_FACILITY		RPCDBG_NFS
-#endif
+/* Uncomment this to support servers requiring longword lengths */
+#define NFS_PAD_WRITES		1
 
-#define QUADLEN(len)		(((len) + 3) >> 2)
-static int			nfs_stat_to_errno(int stat);
+#define NFSDBG_FACILITY		NFSDBG_XDR
 
 /* Mapping from NFS error code to "errno" error code. */
 #define errno_NFSERR_IO		EIO
 
+extern int			nfs_stat_to_errno(int);
+
 /*
  * Declare the space requirements for NFS arguments and replies as
  * number of 32bit-words
  */
-#define NFS_fhandle_sz		(1+16)
-#define NFS_sattr_sz		8
-#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
-#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
-#define NFS_fattr_sz		17
-#define NFS_info_sz		5
-#define NFS_entry_sz		NFS_filename_sz+3
-
-#define NFS_enc_void_sz		0
-#define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
-#define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
-#define NFS_readargs_sz		NFS_fhandle_sz+3
-#define NFS_writeargs_sz	NFS_fhandle_sz+4
-#define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
-#define NFS_renameargs_sz	NFS_diropargs_sz+NFS_diropargs_sz
-#define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
-#define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
-#define NFS_readdirargs_sz	NFS_fhandle_sz+2
-
-#define NFS_dec_void_sz		0
-#define NFS_attrstat_sz		1+NFS_fattr_sz
-#define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
-#define NFS_readres_sz		1+NFS_fattr_sz+1
-#define NFS_stat_sz		1
-#define NFS_readdirres_sz	1
-#define NFS_statfsres_sz	1+NFS_info_sz
+#define NFS3_fhandle_sz		1+16
+#define NFS3_fh_sz		NFS3_fhandle_sz	/* shorthand */
+#define NFS3_sattr_sz		15
+#define NFS3_filename_sz	1+(NFS3_MAXNAMLEN>>2)
+#define NFS3_path_sz		1+(NFS3_MAXPATHLEN>>2)
+#define NFS3_fattr_sz		21
+#define NFS3_wcc_attr_sz		6
+#define NFS3_pre_op_attr_sz	1+NFS3_wcc_attr_sz
+#define NFS3_post_op_attr_sz	1+NFS3_fattr_sz
+#define NFS3_wcc_data_sz		NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz
+#define NFS3_fsstat_sz		
+#define NFS3_fsinfo_sz		
+#define NFS3_pathconf_sz		
+#define NFS3_entry_sz		NFS3_filename_sz+3
+
+#define NFS3_enc_void_sz	0
+#define NFS3_sattrargs_sz	NFS3_fh_sz+NFS3_sattr_sz+3
+#define NFS3_diropargs_sz	NFS3_fh_sz+NFS3_filename_sz
+#define NFS3_accessargs_sz	NFS3_fh_sz+1
+#define NFS3_readlinkargs_sz	NFS3_fh_sz
+#define NFS3_readargs_sz	NFS3_fh_sz+3
+#define NFS3_writeargs_sz	NFS3_fh_sz+5
+#define NFS3_createargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_mkdirargs_sz	NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_symlinkargs_sz	NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz
+#define NFS3_mknodargs_sz	NFS3_diropargs_sz+2+NFS3_sattr_sz
+#define NFS3_renameargs_sz	NFS3_diropargs_sz+NFS3_diropargs_sz
+#define NFS3_linkargs_sz		NFS3_fh_sz+NFS3_diropargs_sz
+#define NFS3_readdirargs_sz	NFS3_fh_sz+2
+#define NFS3_commitargs_sz	NFS3_fh_sz+3
+
+#define NFS3_dec_void_sz	0
+#define NFS3_attrstat_sz	1+NFS3_fattr_sz
+#define NFS3_wccstat_sz		1+NFS3_wcc_data_sz
+#define NFS3_lookupres_sz	1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)
+#define NFS3_accessres_sz	1+NFS3_post_op_attr_sz+1
+#define NFS3_readlinkres_sz	1+NFS3_post_op_attr_sz
+#define NFS3_readres_sz		1+NFS3_post_op_attr_sz+3
+#define NFS3_writeres_sz	1+NFS3_wcc_data_sz+4
+#define NFS3_createres_sz	1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_renameres_sz	1+(2 * NFS3_wcc_data_sz)
+#define NFS3_linkres_sz		1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_readdirres_sz	1+NFS3_post_op_attr_sz+2
+#define NFS3_fsstatres_sz	1+NFS3_post_op_attr_sz+13
+#define NFS3_fsinfores_sz	1+NFS3_post_op_attr_sz+12
+#define NFS3_pathconfres_sz	1+NFS3_post_op_attr_sz+6
+#define NFS3_commitres_sz	1+NFS3_wcc_data_sz+2
+
+/*
+ * Map file type to S_IFMT bits
+ */
+static struct {
+	unsigned int	mode;
+	unsigned int	nfs2type;
+} nfs_type2fmt[] = {
+      { 0,		NFNON	},
+      { S_IFREG,	NFREG	},
+      { S_IFDIR,	NFDIR	},
+      { S_IFBLK,	NFBLK	},
+      { S_IFCHR,	NFCHR	},
+      { S_IFLNK,	NFLNK	},
+      { S_IFSOCK,	NFSOCK	},
+      { S_IFIFO,	NFFIFO	},
+      { 0,		NFBAD	}
+};
 
 /*
  * Common NFS XDR functions as inlines
  */
 static inline u32 *
-xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
 {
 	*p++ = htonl(fh->size);
 	memcpy(p, fh->data, fh->size);
-	return p + QUADLEN(fh->size);
+	return p + XDR_QUADLEN(fh->size);
 }
 
 static inline u32 *
-xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
 {
+	/*
+	 * Zero all nonused bytes
+	 */
+	memset((u8 *)fh, 0, sizeof(*fh));
 	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
 		memcpy(fh->data, p, fh->size);
-		return p + QUADLEN(fh->size);
+		return p + XDR_QUADLEN(fh->size);
 	}
 	return NULL;
 }
 
-static inline enum nfs_ftype
-xdr_decode_ftype(u32 type)
+/*
+ * Encode/decode time.
+ * Since the VFS doesn't care for fractional times, we ignore the
+ * nanosecond field.
+ */
+static inline u32 *
+xdr_encode_time(u32 *p, time_t time)
+{
+	*p++ = htonl(time);
+	*p++ = 0;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_time3(u32 *p, u64 *timep)
+{
+	*timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_time3(u32 *p, u64 time)
 {
-	return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
+	*p++ = htonl(time >> 32);
+	*p++ = htonl(time & 0xFFFFFFFF);
+	return p;
 }
 
 static inline u32 *
@@ -99,47 +165,119 @@
 	if (*len > maxlen)
 		return NULL;
 	*string = (char *) p;
-	return p + QUADLEN(*len);
+	return p + XDR_QUADLEN(*len);
 }
 
 static inline u32 *
-xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
 {
-	fattr->type = xdr_decode_ftype(ntohl(*p++));
-	fattr->mode = ntohl(*p++);
+	unsigned int	type;
+	int		fmode;
+
+	type = ntohl(*p++);
+	if (type >= NF3BAD)
+		type = NF3BAD;
+	fmode = nfs_type2fmt[type].mode;
+	fattr->type = nfs_type2fmt[type].nfs2type;
+	fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
 	fattr->nlink = ntohl(*p++);
 	fattr->uid = ntohl(*p++);
 	fattr->gid = ntohl(*p++);
-	fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
-	fattr->rdev_maj = ntohl(*p++);
-	fattr->rdev_min = ntohl(*p++);
-	fattr->fsid = ntohl(*p++);
-	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
+	p = xdr_decode_hyper(p, &fattr->size);
+	p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
+	/* Turn remote device info into Linux-specific dev_t */
+	fattr->rdev = (ntohl(*p++) << MINORBITS) | (ntohl(*p++) & MINORMASK);
+	p = xdr_decode_hyper(p, &fattr->fsid);
+	p = xdr_decode_hyper(p, &fattr->fileid);
+	p = xdr_decode_time3(p, &fattr->atime);
+	p = xdr_decode_time3(p, &fattr->mtime);
+	p = xdr_decode_time3(p, &fattr->ctime);
+
+	/* Update the mode bits */
+	fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+	if (attr->ia_valid & ATTR_MODE) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_mode);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_UID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_uid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_GID) {
+		*p++ = xdr_one;
+		*p++ = htonl(attr->ia_gid);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_SIZE) {
+		*p++ = xdr_one;
+		p = xdr_encode_hyper(p, (__u64) attr->ia_size);
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_ATIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_atime);
+	} else if (attr->ia_valid & ATTR_ATIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	if (attr->ia_valid & ATTR_MTIME_SET) {
+		*p++ = xdr_two;
+		p = xdr_encode_time(p, attr->ia_mtime);
+	} else if (attr->ia_valid & ATTR_MTIME) {
+		*p++ = xdr_one;
+	} else {
+		*p++ = xdr_zero;
+	}
+	return p;
+}
+
+static inline u32 *
+xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_hyper(p, &fattr->pre_size);
+	p = xdr_decode_time3(p, &fattr->pre_mtime);
+	p = xdr_decode_time3(p, &fattr->pre_ctime);
+	fattr->valid |= NFS_ATTR_WCC;
+	return p;
+}
+
+static inline u32 *
+xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+	if (*p++)
+		p = xdr_decode_fattr(p, fattr);
 	return p;
 }
 
 static inline u32 *
-xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
 {
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size >> 32);
-	*p++ = htonl(sattr->size & 0xFFFFFFFF);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
+	if (*p++)
+		return xdr_decode_wcc_attr(p, fattr);
 	return p;
 }
 
+
+static inline u32 *
+xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+{
+	p = xdr_decode_pre_op_attr(p, fattr);
+	return xdr_decode_post_op_attr(p, fattr);
+}
+
 /*
  * NFS encode functions
  */
@@ -147,7 +285,7 @@
  * Encode void argument
  */
 static int
-nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
@@ -155,10 +293,9 @@
 
 /*
  * Encode file handle argument
- * GETATTR, READLINK, STATFS
  */
 static int
-nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
+nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
 {
 	p = xdr_encode_fhandle(p, fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -169,23 +306,37 @@
  * Encode SETATTR arguments
  */
 static int
-nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
 	p = xdr_encode_sattr(p, args->sattr);
+	*p++ = htonl(args->guard);
+	if (args->guard)
+		p = xdr_encode_time3(p, args->guardtime);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
  * Encode directory ops argument
- * LOOKUP, REMOVE, RMDIR
  */
 static int
-nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode access() argument
+ */
+static int
+nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	*p++ = htonl(args->access);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -193,149 +344,178 @@
 /*
  * Arguments to a READ call. Since we read data directly into the page
  * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page wewant to fetch.
+ * exactly to the page we want to fetch.
  */
 static int
-nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen, buflen;
+	size_t		replen;
+	unsigned int	nr;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
+	p = xdr_encode_hyper(p, args->offset);
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
+	/* Get the number of buffers in the receive iovec */
+	nr = args->nriov;
+
+	if (nr+1 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
+		return -EINVAL;
+	}
+
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->count;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
-#endif
+
+	/* Copy the iovec */
+	memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
+
+	req->rq_rlen = args->count + replen;
+	req->rq_rnr += nr;
 
 	return 0;
 }
 
 /*
- * Decode READ reply
+ * Write arguments. Splice the buffer to be written into the iovec.
  */
 static int
-nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
-	struct iovec *iov = req->rq_rvec;
-	int	status, count, recvd, hdrlen;
+	unsigned int	nr;
+	u32 count = args->count;
 
-	dprintk("RPC:      readres OK status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fattr(p, res->fattr);
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(count);
+	*p++ = htonl(args->stable);
+	*p++ = htonl(count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	count = ntohl(*p++);
-	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[2].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
-		return -errno_NFSERR_IO;
-	}
-	if (count > recvd) {
-		printk("NFS: server cheating in read reply: "
-			"count %d > recvd %d\n", count, recvd);
-		count = recvd;
+	/* Get the number of buffers in the send iovec */
+	nr = args->nriov;
+
+	if (nr+2 > MAX_IOVEC) {
+		printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs\n");
+		return -EINVAL;
 	}
 
-	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count)
-		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+	/* Copy the iovec */
+	memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
 
-	return count;
-}
+#ifdef NFS_PAD_WRITES
+	/*
+	 * Some old servers require that the message length
+	 * be a multiple of 4, so we pad it here if needed.
+	 */
+	if (count & 3) {
+		struct iovec	*iov = req->rq_svec + nr + 1;
+		int		pad = 4 - (count & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		count += pad;
+		nr++;
+	}
+#endif
+	req->rq_slen += count;
+	req->rq_snr  += nr;
 
+	return 0;
+}
 
 /*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * Encode CREATE arguments
  */
 static int
-nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
-	*p++ = htonl(args->count);
-	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	p = xdr_encode_string(p, args->name, args->len);
 
-	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = args->count;
-	req->rq_slen += args->count;
-	req->rq_snr = 2;
+	*p++ = htonl(args->createmode);
+	if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
+		*p++ = args->verifier[0];
+		*p++ = args->verifier[1];
+	} else
+		p = xdr_encode_sattr(p, args->sattr);
 
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode create arguments
- * CREATE, MKDIR
+ * Encode MKDIR arguments
  */
 static int
-nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_string(p, args->name, args->len);
 	p = xdr_encode_sattr(p, args->sattr);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode RENAME arguments
+ * Encode SYMLINK arguments
  */
 static int
-nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
+	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_string(p, args->topath, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode LINK arguments
+ * Encode MKNOD arguments
+ */
+static int
+nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name, args->len);
+	*p++ = htonl(args->type);
+	p = xdr_encode_sattr(p, args->sattr);
+	if (args->type == NF3CHR || args->type == NF3BLK) {
+		*p++ = htonl(args->rdev >> MINORBITS);
+		*p++ = htonl(args->rdev & MINORMASK);
+	}
+
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode RENAME arguments
  */
 static int
-nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname, args->fromlen);
 	p = xdr_encode_fhandle(p, args->tofh);
-	p = xdr_encode_string(p, args->toname);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
 
 /*
- * Encode SYMLINK arguments
+ * Encode LINK arguments
  */
 static int
-nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
 {
 	p = xdr_encode_fhandle(p, args->fromfh);
-	p = xdr_encode_string(p, args->fromname);
-	p = xdr_encode_string(p, args->topath);
-	p = xdr_encode_sattr(p, args->sattr);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname, args->tolen);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 	return 0;
 }
@@ -344,129 +524,168 @@
  * Encode arguments to readdir call
  */
 static int
-nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		replen;
+	size_t		replen;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->cookie);
+	p = xdr_encode_hyper(p, args->cookie);
+	*p++ = args->verf[0];
+	*p++ = args->verf[1];
+	if (args->plus) {
+		/* readdirplus: need dircount + buffer size.
+		 * We just make sure we make dircount big enough */
+		*p++ = htonl(args->bufsiz >> 3);
+	}
 	*p++ = htonl(args->bufsiz);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	/* set up reply iovec */
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
 	req->rq_rvec[1].iov_len  = args->bufsiz;
 	req->rq_rlen = replen + args->bufsiz;
-	req->rq_rnr = 2;
-
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
+	req->rq_rnr ++;
 
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
- */
-static int
-nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
-{
-	struct iovec		*iov = req->rq_rvec;
-	int			status, nr, len;
-	char			*string, *start;
-	u32			*end;
-	__u32			fileid, cookie, *entry;
-
-	if ((status = ntohl(*p++)))
+ * Decode the result of a readdir call.
+ * We just check for syntactical correctness.
+ */
+static int
+nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
+{
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	int		status, nr;
+	unsigned int	len;
+	u32		*entry, *end;
+
+	status = ntohl(*p++);
+	/* Decode post_op_attrs */
+	p = xdr_decode_post_op_attr(p, res->dir_attr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk("NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+	/* Decode verifier cookie */
+	if (res->verf) {
+		res->verf[0] = *p++;
+		res->verf[1] = *p++;
+	} else {
+		p += 2;
 	}
 
-	p = (u32 *) iov[1].iov_base;
-	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	if (p != res->buffer) {
-		printk("NFS: p != res->buffer in %s:%d!!!\n",
-					__FILE__, __LINE__);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen);
 		return -errno_NFSERR_IO;
 	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READDIR header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
 
-	entry  = (__u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = start + res->bufsiz;
+	p   = (u32 *) iov[1].iov_base;
+	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
-
-		len = ntohl(*p++);
-		if ((p + QUADLEN(len) + 3) > end) {
-			printk(KERN_NOTICE
-				"NFS: short packet in readdir reply!\n");
-			break;
-		}
-		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
+		entry = p - 1;
+		if (p + 3 > end)
+			goto short_pkt;
+		p += 2;				/* inode # */
+		len = ntohl(*p++);		/* string length */
+		p += XDR_QUADLEN(len) + 2;	/* name + cookie */
+		if (len > NFS3_MAXNAMLEN) {
+			printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
 						len);
 			return -errno_NFSERR_IO;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
+
+		if (res->plus) {
+			/* post_op_attr */
+			if (p > end)
+				goto short_pkt;
+			if (*p++)
+				p += 21;
+			if (p > end)
+				goto short_pkt;
+			/* post_op_fh3 */
+			if (*p++) {
+				len = ntohl(*p++);
+				if (len > NFS3_FHSIZE) {
+					printk(KERN_WARNING "NFS: giant filehandle in "
+						"readdir (len %x)!\n", len);
+					return -errno_NFSERR_IO;
+				}
+				p += XDR_QUADLEN(len);
+			}
 		}
+		if (p + 2 > end)
+			goto short_pkt;
+	}
 
-		memmove(string, p, len);
-		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
-		/*
-		dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
-			len, string, cookie, status);
-		 */
-	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
 	return nr;
+ short_pkt:
+	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
+	/* truncate listing */
+	entry[0] = entry[1] = 0;
+	return nr;
+}
+
+u32 *
+nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+	struct nfs_entry old = *entry;
+
+	if (!*p++) {
+		if (!*p)
+			return ERR_PTR(-EAGAIN);
+		entry->eof = 1;
+		return ERR_PTR(-EBADCOOKIE);
+	}
+
+	p = xdr_decode_hyper(p, &entry->ino);
+	entry->len  = ntohl(*p++);
+	entry->name = (const char *) p;
+	p += XDR_QUADLEN(entry->len);
+	entry->prev_cookie = entry->cookie;
+	p = xdr_decode_hyper(p, &entry->cookie);
+
+	if (plus) {
+		p = xdr_decode_post_op_attr(p, &entry->fattr);
+		/* In fact, a post_op_fh3: */
+		if (*p++) {
+			p = xdr_decode_fhandle(p, &entry->fh);
+			/* Ugh -- server reply was truncated */
+			if (p == NULL) {
+				dprintk("NFS: FH truncated\n");
+				*entry = old;
+				return ERR_PTR(-EAGAIN);
+			}
+		} else {
+			/* If we don't get a file handle, the attrs
+			 * aren't worth a lot. */
+			entry->fattr.valid = 0;
+		}
+	}
+
+	entry->eof = !p[0] && p[1];
+	return p;
+}
+
+/*
+ * Encode COMMIT arguments
+ */
+static int
+nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_hyper(p, args->offset);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
 }
 
 /*
@@ -476,59 +695,89 @@
  * Decode void reply
  */
 static int
-nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
 {
 	return 0;
 }
 
 /*
- * Decode simple status reply
+ * Decode attrstat reply.
  */
 static int
-nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	if ((status = ntohl(*p++)) != 0)
-		status = -nfs_stat_to_errno(status);
-	return status;
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_fattr(p, fattr);
+	return 0;
 }
 
 /*
- * Decode attrstat reply
- * GETATTR, SETATTR, WRITE
+ * Decode status+wcc_data reply
+ * SATTR, REMOVE, RMDIR
  */
 static int
-nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
 {
 	int	status;
 
-	dprintk("RPC:      attrstat status %lx\n", ntohl(*p));
 	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
-	xdr_decode_fattr(p, fattr);
-	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
-		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
-	return 0;
+		status = -nfs_stat_to_errno(status);
+	xdr_decode_wcc_data(p, fattr);
+	return status;
 }
 
 /*
- * Decode diropres reply
- * LOOKUP, CREATE, MKDIR
+ * Decode LOOKUP reply
  */
 static int
-nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
 	int	status;
 
-	dprintk("RPC:      diropres status %lx\n", ntohl(*p));
-	if ((status = ntohl(*p++)))
+	if ((status = ntohl(*p++))) {
+		status = -nfs_stat_to_errno(status);
+	} else {
+		if (!(p = xdr_decode_fhandle(p, res->fh)))
+			return -errno_NFSERR_IO;
+		p = xdr_decode_post_op_attr(p, res->fattr);
+	}
+	xdr_decode_post_op_attr(p, res->dir_attr);
+	return status;
+}
+
+/*
+ * Decode ACCESS reply
+ */
+static int
+nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+{
+	int	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	if (status)
 		return -nfs_stat_to_errno(status);
-	p = xdr_decode_fhandle(p, res->fh);
-	xdr_decode_fattr(p, res->fattr);
-	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
-		res->fattr->type, res->fattr->mode,
-		res->fattr->fsid, res->fattr->fileid);
+	res->access = ntohl(*p++);
+	return 0;
+}
+
+static int
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	size_t		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr ++;
 	return 0;
 }
 
@@ -536,155 +785,308 @@
  * Decode READLINK reply
  */
 static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkres *res)
 {
+	struct iovec	*iov = req->rq_rvec;
+	u32		hdrlen;
+	u32	*strlen;
+	char	*string;
 	int	status;
+	unsigned int len;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
 
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READLINK header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	strlen = (u32*)res->buffer;
+	/* Convert length of symlink */
+	len = ntohl(*strlen);
+	if (len > res->bufsiz - 5)
+		len = res->bufsiz - 5;
+	*strlen = len;
+	/* NULL terminate the string we got */
+	string = (char *)(strlen + 1);
+	string[len] = 0;
 	return 0;
 }
 
 /*
- * Decode STATFS reply
+ * Decode READ reply
  */
 static int
-nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
+	struct iovec *iov = req->rq_rvec;
+	u32	count, ocount, recvd, hdrlen;
 	int	status;
 
-	if ((status = ntohl(*p++)))
+	status = ntohl(*p++);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+
+	if (status != 0)
 		return -nfs_stat_to_errno(status);
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
-	return 0;
+
+	/* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+	 * in that it puts the count both in the res struct and in the
+	 * opaque data count. */
+	count    = ntohl(*p++);
+	res->eof = ntohl(*p++);
+	ocount   = ntohl(*p++);
+
+	if (ocount != count) {
+		printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+		return -errno_NFSERR_IO;
+	}
+
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (hdrlen > iov[0].iov_len) {
+		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (hdrlen != iov->iov_len) {
+		dprintk("NFS: Short READ header. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
+	recvd = req->rq_rlen - hdrlen;
+	if (count > recvd) {
+		printk(KERN_WARNING "NFS: server cheating in read reply: "
+			"count %d > recvd %d\n", count, recvd);
+		count = recvd;
+	}
+
+	if (count < res->count) {
+		xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count);
+		res->count = count;
+	}
+
+	return count;
 }
 
 /*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
+ * Decode WRITE response
  */
-static struct {
-	int stat;
-	int errno;
-} nfs_errtbl[] = {
-	{ NFS_OK,		0		},
-	{ NFSERR_PERM,		EPERM		},
-	{ NFSERR_NOENT,		ENOENT		},
-	{ NFSERR_IO,		errno_NFSERR_IO	},
-	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
-	{ NFSERR_ACCES,		EACCES		},
-	{ NFSERR_EXIST,		EEXIST		},
-	{ NFSERR_XDEV,		EXDEV		},
-	{ 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	},
-	{ NFSERR_DQUOT,		EDQUOT		},
-	{ NFSERR_STALE,		ESTALE		},
-#ifdef EWFLUSH
-	{ NFSERR_WFLUSH,	EWFLUSH		},
-#endif
-	{ -1,			EIO		}
-};
+static int
+nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int	status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
 
+	res->count = ntohl(*p++);
+	res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+
+	return res->count;
+}
+
+/*
+ * Decode a CREATE response
+ */
 static int
-nfs_stat_to_errno(int stat)
+nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
 {
-	int i;
+	int	status;
 
-	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-		if (nfs_errtbl[i].stat == stat)
-			return nfs_errtbl[i].errno;
+	status = ntohl(*p++);
+	if (status == 0) {
+		if (*p++) {
+			if (!(p = xdr_decode_fhandle(p, res->fh)))
+				return -errno_NFSERR_IO;
+			p = xdr_decode_post_op_attr(p, res->fattr);
+		} else {
+			memset(res->fh, 0, sizeof(*res->fh));
+			/* Do decode post_op_attr but set it to NULL */
+			p = xdr_decode_post_op_attr(p, res->fattr);
+			res->fattr->valid = 0;
+		}
+	} else {
+		status = -nfs_stat_to_errno(status);
 	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
-	return nfs_errtbl[i].errno;
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
 }
 
-#ifndef MAX
-# define MAX(a, b)	(((a) > (b))? (a) : (b))
-#endif
+/*
+ * Decode RENAME reply
+ */
+static int
+nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+{
+	int	status;
 
-#define PROC(proc, argtype, restype)	\
-    { "nfs_" #proc,					\
-      (kxdrproc_t) nfs_xdr_##argtype,			\
-      (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
-    }
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_wcc_data(p, res->fromattr);
+	p = xdr_decode_wcc_data(p, res->toattr);
+	return status;
+}
 
-static struct rpc_procinfo	nfs_procedures[18] = {
-    PROC(null,		enc_void,	dec_void),
-    PROC(getattr,	fhandle,	attrstat),
-    PROC(setattr,	sattrargs,	attrstat),
-    PROC(root,		enc_void,	dec_void),
-    PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
-    PROC(read,		readargs,	readres),
-    PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	attrstat),
-    PROC(create,	createargs,	diropres),
-    PROC(remove,	diropargs,	stat),
-    PROC(rename,	renameargs,	stat),
-    PROC(link,		linkargs,	stat),
-    PROC(symlink,	symlinkargs,	stat),
-    PROC(mkdir,		createargs,	diropres),
-    PROC(rmdir,		diropargs,	stat),
-    PROC(readdir,	readdirargs,	readdirres),
-    PROC(statfs,	fhandle,	statfsres),
-};
+/*
+ * Decode LINK reply
+ */
+static int
+nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+{
+	int	status;
 
-static struct rpc_version	nfs_version2 = {
-	2,
-	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
-	nfs_procedures
-};
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	p = xdr_decode_post_op_attr(p, res->fattr);
+	p = xdr_decode_wcc_data(p, res->dir_attr);
+	return status;
+}
 
-static struct rpc_version *	nfs_version[] = {
-	NULL,
-	NULL,
-	&nfs_version2
-};
+/*
+ * Decode FSSTAT reply
+ */
+static int
+nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
 
-struct rpc_program	nfs_program = {
-	"nfs",
-	NFS_PROGRAM,
-	sizeof(nfs_version) / sizeof(nfs_version[0]),
-	nfs_version,
-	&nfs_rpcstat,
-};
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	p = xdr_decode_hyper(p, &res->tbytes);
+	p = xdr_decode_hyper(p, &res->fbytes);
+	p = xdr_decode_hyper(p, &res->abytes);
+	p = xdr_decode_hyper(p, &res->tfiles);
+	p = xdr_decode_hyper(p, &res->ffiles);
+	p = xdr_decode_hyper(p, &res->afiles);
+
+	/* ignore invarsec */
+	return 0;
+}
 
 /*
- * RPC stats support
+ * Decode FSINFO reply
  */
 static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 {
-	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->rtmax  = ntohl(*p++);
+	res->rtpref = ntohl(*p++);
+	res->rtmult = ntohl(*p++);
+	res->wtmax  = ntohl(*p++);
+	res->wtpref = ntohl(*p++);
+	res->wtmult = ntohl(*p++);
+	res->dtpref = ntohl(*p++);
+	p = xdr_decode_hyper(p, &res->maxfilesize);
+
+	/* ignore time_delta and properties */
+	return 0;
 }
 
-static struct proc_dir_entry	proc_nfsclnt = {
-	0, 3, "nfs",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	6, &proc_net_inode_operations,
-	nfs_get_info
+/*
+ * Decode PATHCONF reply
+ */
+static int
+nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	struct nfs_fattr dummy;
+	int		status;
+
+	status = ntohl(*p++);
+
+	p = xdr_decode_post_op_attr(p, &dummy);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+	res->linkmax = ntohl(*p++);
+	res->namelen = ntohl(*p++);
+
+	/* ignore remaining fields */
+	return 0;
+}
+
+/*
+ * Decode COMMIT reply
+ */
+static int
+nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+	int		status;
+
+	status = ntohl(*p++);
+	p = xdr_decode_wcc_data(p, res->fattr);
+	if (status != 0)
+		return -nfs_stat_to_errno(status);
+
+	res->verf->verifier[0] = *p++;
+	res->verf->verifier[1] = *p++;
+	return 0;
+}
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype)				\
+    { "nfs3_" #proc,						\
+      (kxdrproc_t) nfs3_xdr_##argtype,				\
+      (kxdrproc_t) nfs3_xdr_##restype,				\
+      MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,	\
+      0							\
+    }
+
+static struct rpc_procinfo	nfs3_procedures[22] = {
+  PROC(null,		enc_void,	dec_void),
+  PROC(getattr,		fhandle,	attrstat),
+  PROC(setattr, 	sattrargs,	wccstat),
+  PROC(lookup,		diropargs,	lookupres),
+  PROC(access,		accessargs,	accessres),
+  PROC(readlink,	readlinkargs,	readlinkres),
+  PROC(read,		readargs,	readres),
+  PROC(write,		writeargs,	writeres),
+  PROC(create,		createargs,	createres),
+  PROC(mkdir,		mkdirargs,	createres),
+  PROC(symlink,		symlinkargs,	createres),
+  PROC(mknod,		mknodargs,	createres),
+  PROC(remove,		diropargs,	wccstat),
+  PROC(rmdir,		diropargs,	wccstat),
+  PROC(rename,		renameargs,	renameres),
+  PROC(link,		linkargs,	linkres),
+  PROC(readdir,		readdirargs,	readdirres),
+  PROC(readdirplus,	readdirargs,	readdirres),
+  PROC(fsstat,		fhandle,	fsstatres),
+  PROC(fsinfo,  	fhandle,	fsinfores),
+  PROC(pathconf,	fhandle,	pathconfres),
+  PROC(commit,		commitargs,	commitres),
 };
 
-struct rpc_stat			nfs_rpcstat = {
-	NULL,			/* next */
-	&proc_nfsclnt,		/* /proc/net directory entry */
-	&nfs_program,		/* RPC program */
+struct rpc_version		nfs_version3 = {
+	3,
+	sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+	nfs3_procedures
 };
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/nfsroot.c linux.18p17/fs/nfs/nfsroot.c
--- linux.vanilla/fs/nfs/nfsroot.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/nfsroot.c	Thu Oct 19 01:20:19 2000
@@ -61,8 +61,12 @@
  *	Martin Mares	:	Use root_server_addr appropriately during setup.
  *	Martin Mares	:	Rewrote parameter parsing, now hopefully giving
  *				correct overriding.
+ *	Trond Myklebust :	Add in preliminary support for NFSv3 and TCP.
+ *				Fix bug in root_nfs_addr(). nfs_data.namlen
+ *				is NOT for the length of the hostname.
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -70,7 +74,8 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/in.h>
@@ -85,6 +90,7 @@
 
 /* Default path we try to mount. "%s" gets replaced by our IP address */
 #define NFS_ROOT		"/tftpboot/%s"
+#define NFS_PORT		NFS2_PORT
 
 /* Parameters passed from the kernel command line */
 static char nfs_root_name[256] __initdata = "";
@@ -93,7 +99,7 @@
 static __u32 servaddr __initdata = 0;
 
 /* Name of directory to mount */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
+static char nfs_path[PATH_MAX] __initdata = { 0, };
 
 /* NFS-related data */
 static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
@@ -145,6 +151,15 @@
 	{ "nocto",	~NFS_MOUNT_NOCTO,	NFS_MOUNT_NOCTO },
 	{ "ac",		~NFS_MOUNT_NOAC,	0 },
 	{ "noac",	~NFS_MOUNT_NOAC,	NFS_MOUNT_NOAC },
+	{ "lock",	~NFS_MOUNT_NONLM,	0 },
+	{ "nolock",	~NFS_MOUNT_NONLM,	NFS_MOUNT_NONLM },
+#ifdef CONFIG_NFS_V3
+	{ "v2",		~NFS_MOUNT_VER3,	0 },
+	{ "v3",		~NFS_MOUNT_VER3,	NFS_MOUNT_VER3 },
+#endif
+	{ "udp",	~NFS_MOUNT_TCP,		0 },
+	{ "tcp",	~NFS_MOUNT_TCP,		NFS_MOUNT_TCP },
+	{ "broken_suid",~NFS_MOUNT_BROKEN_SUID,	NFS_MOUNT_BROKEN_SUID },
 	{ NULL,		0,			0 }
 };
 
@@ -211,8 +226,8 @@
 		}
 	}
 	if (name[0] && strcmp(name, "default")) {
-		strncpy(buf, name, NFS_MAXPATHLEN-1);
-		buf[NFS_MAXPATHLEN-1] = 0;
+		strncpy(buf, name, PATH_MAX-1);
+		buf[PATH_MAX-1] = 0;
 	}
 }
 
@@ -222,17 +237,19 @@
  */
 __initfunc(static int root_nfs_name(char *name))
 {
-	char buf[NFS_MAXPATHLEN];
+	char buf[PATH_MAX];
 	char *cp;
 
 	/* Set some default values */
 	memset(&nfs_data, 0, sizeof(nfs_data));
 	nfs_port          = -1;
 	nfs_data.version  = NFS_MOUNT_VERSION;
-	nfs_data.flags    = NFS_MOUNT_NONLM;	/* No lockd in nfs root yet */
-	nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	nfs_data.bsize	  = 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;
@@ -248,7 +265,7 @@
 	root_nfs_parse(name, buf);
 
 	cp = system_utsname.nodename;
-	if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+	if (strlen(buf) + strlen(cp) > PATH_MAX) {
 		printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
 		return -1;
 	}
@@ -269,7 +286,6 @@
 	}
 
 	strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
-	nfs_data.namlen = strlen(nfs_data.hostname);
 	return 0;
 }
 
@@ -356,14 +372,14 @@
 /*
  *  Query server portmapper for the port of a daemon program.
  */
-__initfunc(static int root_nfs_getport(int program, int version))
+__initfunc(static int root_nfs_getport(int program, int version, int proto))
 {
 	struct sockaddr_in sin;
 
 	printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
 		program, version, in_ntoa(servaddr));
 	set_sockaddr(&sin, servaddr, 0);
-	return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
+	return rpc_getport_external(&sin, program, version, proto);
 }
 
 
@@ -375,22 +391,39 @@
 __initfunc(static int root_nfs_ports(void))
 {
 	int port;
+	int nfsd_ver, mountd_ver;
+	int nfsd_port, mountd_port;
+	int proto;
+
+	if (nfs_data.flags & NFS_MOUNT_VER3) {
+		nfsd_ver = NFS3_VERSION;
+		mountd_ver = NFS_MNT3_VERSION;
+		nfsd_port = NFS_PORT;
+		mountd_port = NFS_MNT_PORT;
+	} else {
+		nfsd_ver = NFS2_VERSION;
+		mountd_ver = NFS_MNT_VERSION;
+		nfsd_port = NFS_PORT;
+		mountd_port = NFS_MNT_PORT;
+	}
+
+	proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
 
 	if (nfs_port < 0) {
-		if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) {
+		if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
 			printk(KERN_ERR "Root-NFS: Unable to get nfsd port "
 					"number from server, using default\n");
-			port = NFS_PORT;
+			port = nfsd_port;
 		}
 		nfs_port = htons(port);
 		dprintk("Root-NFS: Portmapper on server returned %d "
 			"as nfsd port\n", port);
 	}
 
-	if ((port = root_nfs_getport(NFS_MNT_PROGRAM, NFS_MNT_VERSION)) < 0) {
+	if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
 		printk(KERN_ERR "Root-NFS: Unable to get mountd port "
 				"number from server, using default\n");
-		port = NFS_MNT_PORT;
+		port = mountd_port;
 	}
 	mount_port = htons(port);
 	dprintk("Root-NFS: mountd port is %d\n", port);
@@ -409,7 +442,10 @@
 	int status;
 
 	set_sockaddr(&sin, servaddr, mount_port);
-	status = nfs_mount(&sin, nfs_path, &nfs_data.root);
+	if (nfs_data.flags & NFS_MOUNT_VER3)
+		status = nfs3_mount(&sin, nfs_path, &nfs_data.root);
+	else
+		status = nfs_mount(&sin, nfs_path, &nfs_data.root);
 	if (status < 0)
 		printk(KERN_ERR "Root-NFS: Server returned error %d "
 				"while mounting %s\n", status, nfs_path);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/proc.c linux.18p17/fs/nfs/proc.c
--- linux.vanilla/fs/nfs/proc.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/proc.c	Fri Sep 15 22:10:44 2000
@@ -13,10 +13,6 @@
  *  Note: Error returns are optimized for NFS_OK, which isn't translated via
  *  nfs_stat_to_errno(), but happens to be already the right return code.
  *
- *  FixMe: We ought to define a sensible small max size for
- *  things like getattr that are tiny packets and use the
- *  old get_free_page stuff with it.
- *
  *  Also, the code currently doesn't check the size of the packet, when
  *  it decodes the packet.
  *
@@ -25,211 +21,342 @@
  *  Completely rewritten to support the new RPC call interface;
  *  rewrote and moved the entire XDR stuff to xdr.c
  *  --Olaf Kirch June 1996
+ *
+ *  The code below initializes all auto variables explicitly, otherwise
+ *  it will fail to work as a module (gcc generates a memset call for an
+ *  incomplete struct).
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
 
+#include <linux/types.h>
 #include <linux/param.h>
+#include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/malloc.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
 #include <linux/nfs_fs.h>
 
 #include <asm/segment.h>
 
-#ifdef NFS_DEBUG
-# define NFSDBG_FACILITY	NFSDBG_PROC
-#endif
 
 
 /*
+ * Typing short-hands
+ */
+typedef struct nfs_fattr	fattr;
+typedef struct qstr		qstr;
+
+#define NFSDBG_FACILITY		NFSDBG_PROC
+
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+			fattr *fattr)
+{
+	int		status;
+
+	dprintk("NFS call  getroot\n");
+	fattr->valid = 0;
+	status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+	dprintk("NFS reply getroot\n");
+	return status;
+}
+
+/*
  * One function for each procedure in the NFS protocol.
  */
-int
-nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr)
+static int
+nfs_proc_getattr(struct dentry *dentry, fattr *fattr)
 {
 	int	status;
 
 	dprintk("NFS call  getattr\n");
-	status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_GETATTR,
+				NFS_FH(dentry), fattr, 0);
 	dprintk("NFS reply getattr\n");
 	return status;
 }
 
-int
-nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_sattr *sattr, struct nfs_fattr *fattr)
+static int
+nfs_proc_setattr(struct dentry *dentry, fattr *fattr,
+				struct iattr *sattr)
 {
-	struct nfs_sattrargs	arg = { fhandle, sattr };
+	struct nfs_sattrargs	arg = { NFS_FH(dentry), sattr };
 	int	status;
 
 	dprintk("NFS call  setattr\n");
-	status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_SETATTR, &arg, fattr, 0);
 	dprintk("NFS reply setattr\n");
 	return status;
 }
 
-int
-nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
-		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_lookup(struct dentry *dir, fattr *dir_attr, qstr *name,
+		struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_diropargs	arg = { dir, name };
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  lookup %s\n", name);
-	status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
+	dprintk("NFS call  lookup %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_LOOKUP, &arg, &res, 0);
 	dprintk("NFS reply lookup: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen)
+static int
+nfs_proc_readlink(struct dentry *dentry, fattr *fattr,
+			void *buffer, unsigned int bufsiz)
 {
-	struct nfs_readlinkres	res = { string, len, maxlen, NULL };
+	struct nfs_readlinkargs	args = { NFS_FH(dentry), buffer, bufsiz };
+	struct nfs_readlinkres	res = { buffer, bufsiz };
 	int			status;
 
 	dprintk("NFS call  readlink\n");
-	status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0);
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_READLINK,
+					&args, &res, 0);
 	dprintk("NFS reply readlink: %d\n", status);
-	if (!status)
-		*p0 = res.buffer;
-	else if (res.buffer)
-		kfree(res.buffer);
 	return status;
 }
 
-int
-nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
-			  unsigned long offset, unsigned int count,
-			  void *buffer, struct nfs_fattr *fattr)
-{
-	struct nfs_readargs	arg = { fhandle, offset, count, buffer };
-	struct nfs_readres	res = { fattr, count };
+static int
+nfs_proc_read(struct dentry *dentry, fattr *fattr,
+	      struct rpc_cred *cred, int flags,
+	      unsigned long offset, unsigned int count,
+	      void *buffer, int *eofp)
+{
+	struct nfs_readargs	arg = { NFS_FH(dentry), offset, count, 1,
+				       {{ buffer, count }, {0,0}, {0,0}, {0,0},
+					{0,0}, {0,0}, {0,0}, {0,0}} };
+	struct nfs_readres	res = { fattr, count, 0};
+	struct rpc_message	msg = { NFSPROC_READ, &arg, &res, cred };
 	int			status;
 
 	dprintk("NFS call  read %d @ %ld\n", count, offset);
-	status = rpc_call(server->client, NFSPROC_READ, &arg, &res,
-			swap? NFS_RPC_SWAPFLAGS : 0);
+	fattr->valid = 0;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
 	dprintk("NFS reply read: %d\n", status);
+	*eofp = res.eof;
 	return status;
 }
 
-int
-nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
-			unsigned long offset, unsigned int count,
-			const void *buffer, struct nfs_fattr *fattr)
-{
-	struct nfs_writeargs	arg = { fhandle, offset, count, buffer };
-	int			status;
+static int
+nfs_proc_write(struct dentry *dentry, fattr *fattr,
+	       struct rpc_cred *cred, int how,
+	       unsigned long offset, unsigned int count,
+	       void *buffer, struct nfs_writeverf *verf)
+{
+	struct nfs_writeargs	arg = {NFS_FH(dentry), offset, count,
+					NFS_FILE_SYNC, 1,
+					{{buffer, count}, {0,0}, {0,0}, {0,0},
+					 {0,0}, {0,0}, {0,0}, {0,0}}};
+	struct nfs_writeres     res = {fattr, verf, count};
+	struct rpc_message	msg = { NFSPROC_WRITE, &arg, &res, cred };
+	int			status, flags = 0;
 
 	dprintk("NFS call  write %d @ %ld\n", count, offset);
-	status = rpc_call(server->client, NFSPROC_WRITE, &arg, fattr,
-			swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
-	dprintk("NFS reply read: %d\n", status);
+	fattr->valid = 0;
+	if (how & NFS_RW_SWAP)
+		flags |= NFS_RPC_SWAPFLAGS;
+	status = rpc_call_sync(NFS_CLIENT(dentry->d_inode), &msg, flags);
+
+	dprintk("NFS reply write: %d\n", status);
+	verf->committed = NFS_FILE_SYNC;      /* NFSv2 always syncs data */
 	return status < 0? status : count;
 }
 
-int
-nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_create(struct dentry *dir, fattr *dir_attr,
+		qstr *name, struct iattr *sattr, int flags,
+		struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name,
+					name->len, sattr };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  create %s\n", name);
-	status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	dprintk("NFS call  create %s\n", name->name);
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
 	dprintk("NFS reply create: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+/*
+ * In NFSv2, mknod is grafted onto the create call.
+ */
+static int
+nfs_proc_mknod(struct dentry *dir, fattr *dir_attr,
+			qstr *name, struct iattr *sattr, dev_t rdev,
+			struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_diropargs	arg = { dir, name };
-	int			status;
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name,
+						     name->len, sattr };
+	struct nfs_diropok	res = { fhandle, fattr };
+	int			status, mode;
+
+	dprintk("NFS call  mknod %s\n", name->name);
+
+	mode = sattr->ia_mode;
+	if (S_ISFIFO(mode)) {
+		sattr->ia_mode = (mode & ~S_IFMT) | S_IFCHR;
+		sattr->ia_valid &= ~ATTR_SIZE;
+	} else if (S_ISCHR(mode) || S_ISBLK(mode)) {
+		sattr->ia_valid |= ATTR_SIZE;
+		sattr->ia_size   = rdev;	/* get out your barf bag */
+	}
+
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+
+	if (status == -EINVAL && S_ISFIFO(mode)) {
+		sattr->ia_mode = mode;
+		dir_attr->valid = 0;
+		fattr->valid = 0;
+		status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_CREATE, &arg, &res, 0);
+	}
+	dprintk("NFS reply mknod: %d\n", status);
+	return status;
+}
+  
+static int
+nfs_proc_remove(struct dentry *dir, fattr *dir_attr, qstr *name)
+{
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
+	struct rpc_message	msg = { NFSPROC_REMOVE, &arg, NULL, NULL };
+	int			status;
+
+	dir_attr->valid = 0;
+	dprintk("NFS call  remove %s\n", name->name);
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
 
-	dprintk("NFS call  remove %s\n", name);
-	status = rpc_call(server->client, NFSPROC_REMOVE, &arg, NULL, 0);
 	dprintk("NFS reply remove: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_rename(struct nfs_server *server,
-		struct nfs_fh *old_dir, const char *old_name,
-		struct nfs_fh *new_dir, const char *new_name)
+static int
+nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
 {
-	struct nfs_renameargs	arg = { old_dir, old_name, new_dir, new_name };
-	int			status;
+	struct nfs_diropargs	*arg;
 
-	dprintk("NFS call  rename %s -> %s\n", old_name, new_name);
-	status = rpc_call(server->client, NFSPROC_RENAME, &arg, NULL, 0);
+	arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL);
+	if (!arg)
+		return -ENOMEM;
+	memset(arg, 0, sizeof(*arg));
+	arg->fh = NFS_FH(dir);
+	arg->name = name->name;
+	arg->len = name->len;
+	msg->rpc_proc = NFSPROC_REMOVE;
+	msg->rpc_argp = arg;
+	return 0;
+}
+
+static void
+nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+	if (msg->rpc_argp) {
+		NFS_CACHEINV(dir->d_inode);
+		kfree(msg->rpc_argp);
+	}
+}
+
+static int
+nfs_proc_rename(struct dentry *old_dir, fattr *old_attr, qstr *old_name,
+		struct dentry *new_dir, fattr *new_attr, qstr *new_name)
+{
+	struct nfs_renameargs	arg = { NFS_FH(old_dir), old_name->name,
+					old_name->len,
+					NFS_FH(new_dir), new_name->name,
+					new_name->len};
+	int			status;
+
+	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+	old_attr->valid = 0;
+	new_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(old_dir->d_inode), NFSPROC_RENAME, &arg, NULL, 0);
 	dprintk("NFS reply rename: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fh *dir, const char *name)
-{
-	struct nfs_linkargs	arg = { fhandle, dir, name };
+static int
+nfs_proc_link(struct dentry *dentry, fattr *attr,
+	      struct dentry *dir, fattr *dir_attr, qstr *name)
+{
+	struct nfs_linkargs	arg = { NFS_FH(dentry), NFS_FH(dir),
+					name->name, name->len };
 	int			status;
 
-	dprintk("NFS call  link %s\n", name);
-	status = rpc_call(server->client, NFSPROC_LINK, &arg, NULL, 0);
+	dprintk("NFS call  link %s\n", name->name);
+	dir_attr->valid = 0;
+	attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dentry->d_inode), NFSPROC_LINK, &arg, NULL, 0);
 	dprintk("NFS reply link: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, const char *path,
-			struct nfs_sattr *sattr)
-{
-	struct nfs_symlinkargs	arg = { dir, name, path, sattr };
+static int
+nfs_proc_symlink(struct dentry *dir, fattr *dir_attr, qstr *name,
+		 qstr *path, struct iattr *sattr,
+		 struct nfs_fh *sym_fh, fattr *sym_attr)
+{
+	struct nfs_symlinkargs	arg = { NFS_FH(dir), name->name, name->len,
+					path->name, path->len, sattr };
 	int			status;
 
-	dprintk("NFS call  symlink %s -> %s\n", name, path);
-	status = rpc_call(server->client, NFSPROC_SYMLINK, &arg, NULL, 0);
+	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+	dir_attr->valid = 0;
+	sym_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_SYMLINK, &arg, NULL, 0);
 	dprintk("NFS reply symlink: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int
+nfs_proc_mkdir(struct dentry *dir, fattr *dir_attr, qstr *name,
+	       struct iattr *sattr,
+	       struct nfs_fh *fhandle, fattr *fattr)
 {
-	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_createargs	arg = { NFS_FH(dir), name->name, name->len,
+					sattr };
 	struct nfs_diropok	res = { fhandle, fattr };
 	int			status;
 
-	dprintk("NFS call  mkdir %s\n", name);
-	status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0);
+	dprintk("NFS call  mkdir %s\n", name->name);
+	dir_attr->valid = 0;
+	fattr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_MKDIR, &arg, &res, 0);
 	dprintk("NFS reply mkdir: %d\n", status);
 	return status;
 }
 
-int
-nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+static int
+nfs_proc_rmdir(struct dentry *dir, fattr *dir_attr, qstr *name)
 {
-	struct nfs_diropargs	arg = { dir, name };
+	struct nfs_diropargs	arg = { NFS_FH(dir), name->name, name->len };
 	int			status;
 
-	dprintk("NFS call  rmdir %s\n", name);
-	status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0);
+	dprintk("NFS call  rmdir %s\n", name->name);
+	dir_attr->valid = 0;
+	status = rpc_call(NFS_CLIENT(dir->d_inode), NFSPROC_RMDIR, &arg, NULL, 0);
 	dprintk("NFS reply rmdir: %d\n", status);
 	return status;
 }
@@ -237,66 +364,71 @@
 /*
  * The READDIR implementation is somewhat hackish - we pass a temporary
  * buffer to the encode function, which installs it in the receive
- * iovec. The dirent buffer itself is passed in the result struct.
+ * the receive iovec. The decode function just parses the reply to make
+ * sure it is syntactically correct; the entries itself are decoded
+ * from nfs_readdir by calling the decode_entry function directly.
  */
-int
-nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry)
+static int
+nfs_proc_readdir(struct dentry *dir, fattr *dir_attr,
+		 struct rpc_cred *cred,
+		 __u64 cookie, void *entry, unsigned int size, int plus)
 {
 	struct nfs_readdirargs	arg;
 	struct nfs_readdirres	res;
-	void *			buffer;
-	unsigned int		buf_size = PAGE_SIZE;
+	struct rpc_message	msg = { NFSPROC_READDIR, &arg, &res, cred };
 	int			status;
 
-	/* First get a temp buffer for the readdir reply */
-	/* N.B. does this really need to be cleared? */
-	status = -ENOMEM;
-	buffer = (void *) get_free_page(GFP_KERNEL);
-	if (!buffer)
-		goto out;
-
-	/*
-	 * Calculate the effective size the buffer.  To make sure
-	 * that the returned data will fit into the user's buffer,
-	 * we decrease the buffer size as necessary.
-	 *
-	 * Note: NFS returns three __u32 values for each entry,
-	 * and we assume that the data is packed into the user
-	 * buffer with the same efficiency. 
-	 */
-	if (size < buf_size)
-		buf_size = size;
-	if (server->rsize < buf_size)
-		buf_size = server->rsize;
-#if 0
-printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n",
-size, server->rsize, buf_size);
-#endif
-
-	arg.fh = fhandle;
+	dir_attr->valid = 0;
+	arg.fh = NFS_FH(dir);
 	arg.cookie = cookie;
-	arg.buffer = buffer;
-	arg.bufsiz = buf_size;
+	arg.buffer = entry;
+	arg.bufsiz = size;
 	res.buffer = entry;
 	res.bufsiz = size;
 
-	dprintk("NFS call  readdir %d\n", cookie);
-	status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
+	dir_attr->valid = 0;
+	dprintk("NFS call  readdir %d\n", (unsigned int)cookie);
+	status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+
 	dprintk("NFS reply readdir: %d\n", status);
-	free_page((unsigned long) buffer);
-out:
 	return status;
 }
 
-int
+static int
 nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 			struct nfs_fsinfo *info)
 {
 	int	status;
 
 	dprintk("NFS call  statfs\n");
+	memset((char *)info, 0, sizeof(*info));
 	status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0);
 	dprintk("NFS reply statfs: %d\n", status);
 	return status;
 }
+
+struct nfs_rpc_ops     nfs_v2_clientops = {
+       2,		       /* protocol version */
+       nfs_proc_get_root,
+       nfs_proc_getattr,
+       nfs_proc_setattr,
+       nfs_proc_lookup,
+       NULL,		       /* access */
+       nfs_proc_readlink,
+       nfs_proc_read,
+       nfs_proc_write,
+       NULL,		       /* commit */
+       nfs_proc_create,
+       nfs_proc_remove,
+       nfs_proc_unlink_setup,
+       nfs_proc_unlink_done,
+       nfs_proc_rename,
+       nfs_proc_link,
+       nfs_proc_symlink,
+       nfs_proc_mkdir,
+       nfs_proc_rmdir,
+       nfs_proc_readdir,
+       nfs_proc_mknod,
+       nfs_proc_statfs,
+       nfs_decode_dirent,
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/read.c linux.18p17/fs/nfs/read.c
--- linux.vanilla/fs/nfs/read.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/read.c	Fri Sep 15 22:10:44 2000
@@ -15,7 +15,8 @@
  * within the RPC code when root squashing is suspected.
  */
 
-#define NFS_NEED_XDR_TYPES
+#define NFS_NEED_NFS2_XDR_TYPES
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -23,43 +24,62 @@
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
+#include <asm/pgtable.h>
 #include <linux/pagemap.h>
+#include <linux/file.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/nfs_flushd.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
-#include <asm/pgtable.h>
 
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-struct nfs_rreq {
-	struct inode *		ra_inode;	/* inode from which to read */
-	struct page *		ra_page;	/* page to be read */
-	struct nfs_readargs	ra_args;	/* XDR argument struct */
-	struct nfs_readres	ra_res;		/* ... and result struct */
-	struct nfs_fattr	ra_fattr;	/* fattr storage */
+struct nfs_read_data {
+	struct rpc_task		task;
+	struct dentry		*dentry;
+	struct rpc_cred		*cred;
+	struct nfs_readargs	args;	/* XDR argument struct */
+	struct nfs_readres	res;	/* ... and result struct */
+	struct nfs_fattr	fattr;	/* fattr storage */
+	struct list_head	pages;	/* Coalesced read requests */
 };
 
+/*
+ * Local function declarations
+ */
+static void	nfs_readpage_result(struct rpc_task *task);
+
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)	(0)
 #endif
 
+static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
+{
+	struct nfs_read_data	*p;
+	p = (struct nfs_read_data*) kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->pages);
+	}
+	return p;
+}
 
-/*
- * Set up the NFS read request struct
- */
-static inline void
-nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
-		  unsigned long offset, void *buffer, unsigned int rsize)
+static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
 {
-	req->ra_args.fh     = fh;
-	req->ra_args.offset = offset;
-	req->ra_args.count  = rsize;
-	req->ra_args.buffer = buffer;
-	req->ra_res.fattr   = &req->ra_fattr;
-	req->ra_res.count   = rsize;
+	kfree(p);
+}
+
+static void nfs_readdata_release(struct rpc_task *task)
+{
+        struct nfs_read_data   *data = (struct nfs_read_data *)task->tk_calldata;
+        nfs_readdata_free(data);
 }
 
 
@@ -67,32 +87,37 @@
  * Read a page synchronously.
  */
 static int
-nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
+nfs_readpage_sync(struct file *file, struct page *page)
 {
-	struct nfs_rreq	rqst;
-	unsigned long	offset = page->offset;
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	struct rpc_cred	*cred = nfs_file_cred(file);
+	struct nfs_fattr fattr;
+	unsigned long	offset = nfs_page_offset(page);
 	char		*buffer = (char *) page_address(page);
 	int		rsize = NFS_SERVER(inode)->rsize;
 	int		result, refresh = 0;
-	int		count = PAGE_SIZE;
-	int		flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
+	int		count = PAGE_CACHE_SIZE, chunk, eof = 0;
+	int		flags = 0;
+
+	if (IS_SWAPFILE(inode))
+		flags |= NFS_RPC_SWAPFLAGS;
 
 	dprintk("NFS: nfs_readpage_sync(%p)\n", page);
 	clear_bit(PG_error, &page->flags);
 
 	do {
-		if (count < rsize)
-			rsize = count;
+		if ((chunk = rsize) > count)
+			chunk = count;
 
 		dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
 			NFS_SERVER(inode)->hostname,
 			dentry->d_parent->d_name.name, dentry->d_name.name,
-			offset, rsize, buffer);
+			offset, chunk, buffer);
 
-		/* Set up arguments and perform rpc call */
-		nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
-		result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
-					&rqst.ra_args, &rqst.ra_res, flags);
+		result = NFS_CALL(read, inode, (dentry, &fattr, cred, flags,
+						offset, chunk, buffer, &eof));
+		nfs_refresh_inode(inode, &fattr);
 
 		/*
 		 * Even if we had a partial success we can't mark the page
@@ -107,7 +132,7 @@
 		count  -= result;
 		offset += result;
 		buffer += result;
-		if (result < rsize)	/* NFSv2ism */
+		if (eof)
 			break;
 	} while (count);
 
@@ -118,97 +143,311 @@
 
 io_error:
 	/* Note: we don't refresh if the call returned error */
-	if (refresh && result >= 0)
-		nfs_refresh_inode(inode, &rqst.ra_fattr);
-	/* N.B. Use nfs_unlock_page here? */
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
+	if (result < 0)
+		set_bit(PG_error, &page->flags);
 	return result;
 }
 
+static inline struct nfs_page *
+_nfs_find_read(struct inode *inode, struct page *page)
+{
+	struct list_head	*head, *next;
+
+	head = &inode->u.nfs_i.read;
+	next = head->next;
+	while (next != head) {
+		struct nfs_page *req = nfs_list_entry(next);
+		next = next->next;
+		if (page_index(req->wb_page) != page_index(page))
+			continue;
+		req->wb_count++;
+		return req;
+	}
+	return NULL;
+}
+
+static struct nfs_page *
+nfs_find_read(struct inode *inode, struct page *page)
+{
+	struct nfs_page *req;
+	req = _nfs_find_read(inode, page);
+	return req;
+}
+
 /*
- * This is the callback from RPC telling us whether a reply was
- * received or some error occurred (timeout or socket shutdown).
+ * Add a request to the inode's asynchronous read list.
  */
-static void
-nfs_readpage_result(struct rpc_task *task)
+static inline void
+nfs_mark_request_read(struct nfs_page *req)
 {
-	struct nfs_rreq	*req = (struct nfs_rreq *) task->tk_calldata;
-	struct page	*page = req->ra_page;
-	unsigned long	address = page_address(page);
-	int		result = task->tk_status;
-	static int	succ = 0, fail = 0;
-
-	dprintk("NFS: %4d received callback for page %lx, result %d\n",
-			task->tk_pid, address, result);
-
-	if (result >= 0) {
-		result = req->ra_res.count;
-		if (result < PAGE_SIZE) {
-			memset((char *) address + result, 0, PAGE_SIZE - result);
+	struct inode *inode = req->wb_dentry->d_inode;
+
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.read);
+		inode->u.nfs_i.nread++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+
+static int
+nfs_readpage_async(struct file *file, struct page *page)
+{
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct nfs_page	*req, *new = NULL;
+	int		result;
+
+	for (;;) {
+		result = 0;
+		if (PageUptodate(page))
+			break;
+
+		req = nfs_find_read(inode, page);
+		if (req) {
+			if (page != req->wb_page) {
+				nfs_release_request(req);
+				nfs_pagein_inode(inode, page_index(page), 0);
+				continue;
+			}
+			nfs_release_request(req);
+			break;
 		}
-		nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
-		flush_dcache_page(address);
-		set_bit(PG_uptodate, &page->flags);
-		succ++;
-	} else {
-		set_bit(PG_error, &page->flags);
-		fail++;
-		dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
+
+		if (new) {
+			nfs_lock_request(new);
+			new->wb_timeout = jiffies + NFS_READ_DELAY;
+			nfs_mark_request_read(new);
+			nfs_unlock_request(new);
+			new = NULL;
+			break;
+		}
+
+		result = -ENOMEM;
+		new = nfs_create_request(file, page, 0, PAGE_CACHE_SIZE);
+		if (!new)
+			break;
 	}
-	/* N.B. Use nfs_unlock_page here? */
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
 
-	free_page(address);
+	if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages ||
+	    page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+		nfs_pagein_inode(inode, 0, 0);
+	if (new)
+		nfs_release_request(new);
+	return result;
+}
 
-	rpc_release_task(task);
-	kfree(req);
+/*
+ * Set up the NFS read request struct
+ */
+static void
+nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data)
+{
+	struct nfs_page		*req;
+	struct iovec		*iov;
+	unsigned int		count;
+
+	iov = data->args.iov;
+	count = 0;
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		iov->iov_base = (void *)(page_address(req->wb_page) + req->wb_offset);
+		iov->iov_len = req->wb_bytes;
+		count += req->wb_bytes;
+		iov++;
+		data->args.nriov++;
+	}
+	req = nfs_list_entry(data->pages.next);
+	data->dentry	  = req->wb_dentry;
+	data->cred	  = req->wb_cred;
+	data->args.fh     = NFS_FH(req->wb_dentry);
+	data->args.offset = nfs_page_offset(req->wb_page) + req->wb_offset;
+	data->args.count  = count;
+	data->res.fattr   = &data->fattr;
+	data->res.count   = count;
+	data->res.eof     = 0;
 }
 
-static inline int
-nfs_readpage_async(struct dentry *dentry, struct inode *inode,
-			struct page *page)
+static void
+nfs_async_read_error(struct list_head *head)
 {
-	unsigned long address = page_address(page);
-	struct nfs_rreq	*req;
-	int		result = -1, flags;
+	struct nfs_page	*req;
+	struct page	*page;
 
-	dprintk("NFS: nfs_readpage_async(%p)\n", page);
-	if (NFS_CONGESTED(inode))
-		goto out_defer;
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		page = req->wb_page;
+		nfs_list_remove_request(req);
+		set_bit(PG_error, &page->flags);
+		nfs_unlock_page(page);
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+	}
+}
+
+static int
+nfs_pagein_one(struct list_head *head, struct dentry *dentry)
+{
+	struct inode		*inode = dentry->d_inode;
+	struct rpc_task		*task;
+	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
+	struct nfs_read_data	*data;
+	struct rpc_message	msg;
+	int			flags;
+	sigset_t		oldset;
+
+	data = nfs_readdata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
 
 	/* N.B. Do we need to test? Never called for swapfile inode */
 	flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
-	req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req));
-	if (!req)
-		goto out_defer;
-
-	/* Initialize request */
-	/* N.B. Will the dentry remain valid for life of request? */
-	nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
-				(void *) address, PAGE_SIZE);
-	req->ra_inode = inode;
-	req->ra_page = page; /* count has been incremented by caller */
+
+	nfs_read_rpcsetup(head, data);
+
+	/* Finalize the task. */
+	rpc_init_task(task, clnt, nfs_readpage_result, flags);
+	task->tk_calldata = data;
+	/* Release requests */
+	task->tk_release = nfs_readdata_release;
+
+#ifdef CONFIG_NFS_V3
+	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
+#else
+	msg.rpc_proc = NFSPROC_READ;
+#endif
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
 
 	/* Start the async call */
-	dprintk("NFS: executing async READ request.\n");
-	result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
-				&req->ra_args, &req->ra_res, flags,
-				nfs_readpage_result, req);
-	if (result < 0)
-		goto out_free;
-	result = 0;
-out:
-	return result;
+	dprintk("NFS: %4d initiated read call (req %s/%s count %d nriov %d.\n",
+		task->tk_pid,
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		data->args.count, data->args.nriov);
+
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+out_bad:
+	nfs_async_read_error(head);
+	return -ENOMEM;
+}
+
+static int
+nfs_pagein_list(struct inode *inode, struct list_head *head)
+{
+	LIST_HEAD(one_request);
+	struct nfs_page		*req;
+	int			error = 0;
+	unsigned int		pages = 0,
+				rpages = NFS_SERVER(inode)->rpages;
+
+	while (!list_empty(head)) {
+		pages += nfs_coalesce_requests(head, &one_request, rpages);
+		req = nfs_list_entry(one_request.next);
+		error = nfs_pagein_one(&one_request, req->wb_dentry);
+		if (error < 0)
+			break;
+	}
+	if (error >= 0)
+		return pages;
+
+	nfs_async_read_error(head);
+	return error;
+}
 
-out_defer:
-	dprintk("NFS: deferring async READ request.\n");
-	goto out;
-out_free:
-	dprintk("NFS: failed to enqueue async READ request.\n");
-	kfree(req);
-	goto out;
+static int
+nfs_scan_read_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.read, dst, inode);
+	inode->u.nfs_i.nread -= pages;
+	if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
+	return pages;
+}
+
+static int
+nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages);
+	inode->u.nfs_i.nread -= res;
+	if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
+	return res;
+}
+
+int nfs_pagein_inode(struct inode *inode, unsigned long idx_start,
+		     unsigned int npages)
+{
+	LIST_HEAD(head);
+	int	res,
+		error = 0;
+
+	res = nfs_scan_read(inode, &head, idx_start, npages);
+	if (res)
+		error = nfs_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return res;
+}
+
+int nfs_pagein_timeout(struct inode *inode)
+{
+	LIST_HEAD(head);
+	int	pages,
+		error = 0;
+
+	pages = nfs_scan_read_timeout(inode, &head);
+	if (pages)
+		error = nfs_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return pages;
+}
+
+/*
+ * This is the callback from RPC telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_readpage_result(struct rpc_task *task)
+{
+	struct nfs_read_data	*data = (struct nfs_read_data *) task->tk_calldata;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
+	int			count = data->res.count;
+
+	dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
+		task->tk_pid, task->tk_status);
+
+	nfs_refresh_inode(inode, &data->fattr);
+	while (!list_empty(&data->pages)) {
+		struct nfs_page *req = nfs_list_entry(data->pages.next);
+		struct page *page = req->wb_page;
+		nfs_list_remove_request(req);
+
+		if (task->tk_status >= 0 && count >= 0) {
+			flush_dcache_page(page_address(page)); /* Is this correct? */
+			set_bit(PG_uptodate, &page->flags);
+			count -= PAGE_CACHE_SIZE;
+		} else
+			set_bit(PG_error, &page->flags);
+		nfs_unlock_page(page);
+
+		dprintk("NFS: read (%s/%s %d@%ld)\n",
+                        req->wb_dentry->d_parent->d_name.name,
+                        req->wb_dentry->d_name.name,
+                        req->wb_bytes,
+                        (nfs_page_offset(page) + req->wb_offset));
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+	}
 }
 
 /*
@@ -216,7 +455,7 @@
  * We read the page synchronously in the following cases:
  *  -	The file is a swap file. Swap-ins are always sync operations,
  *	so there's no need bothering to make async reads 100% fail-safe.
- *  -	The NFS rsize is smaller than PAGE_SIZE. We could kludge our way
+ *  -	The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way
  *	around this by creating several consecutive read requests, but
  *	that's hardly worth it.
  *  -	The error flag is set for this page. This happens only when a
@@ -228,41 +467,35 @@
 {
 	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
-	int		error;
+	int		error = 0,
+			rsize = NFS_SERVER(inode)->rsize;
+
+	while (!nfs_lock_page(page))
+		wait_on_page(page);
 
-	dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
-		page, PAGE_SIZE, page->offset);
-	atomic_inc(&page->count);
-	set_bit(PG_locked, &page->flags);
+	dprintk("NFS: nfs_readpage (%p %d@%ld)\n",
+		page, rsize, page->offset);
 
 	/*
-	 * Try to flush any pending writes to the file..
-	 *
-	 * NOTE! Because we own the page lock, there cannot
-	 * be any new pending writes generated at this point
-	 * for this page (other pages can be written to).
+	 * Try to flush any pending writes to the file
 	 */
 	error = nfs_wb_page(inode, page);
-	if (error)
-		goto out_error;
+	if (error < 0)
+		goto out_unlock;
 
 	error = -1;
-	if (!IS_SWAPFILE(inode) && !PageError(page) &&
-	    NFS_SERVER(inode)->rsize >= PAGE_SIZE)
-		error = nfs_readpage_async(dentry, inode, page);
+	if (!IS_SWAPFILE(inode) && !PageError(page) && rsize >= PAGE_CACHE_SIZE)
+		error = nfs_readpage_async(file, page);
+
 	if (error >= 0)
 		goto out;
 
-	error = nfs_readpage_sync(dentry, inode, page);
+	error = nfs_readpage_sync(file, page);
 	if (error < 0 && IS_SWAPFILE(inode))
-		printk("Aiee.. nfs swap-in of page failed!\n");
-	goto out_free;
+		printk(KERN_ERR "Aiee.. nfs swap-in of page failed!\n");
 
-out_error:
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-out_free:
-	free_page(page_address(page));
-out:
+ out_unlock:
+	nfs_unlock_page(page);
+ out:
 	return error;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/symlink.c linux.18p17/fs/nfs/symlink.c
--- linux.vanilla/fs/nfs/symlink.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/symlink.c	Fri Sep 15 22:10:44 2000
@@ -5,13 +5,20 @@
  *
  *  Optimization changes Copyright (C) 1994 Florian La Roche
  *
+ *  Jun 7 1999, cache symlink lookups in the page cache.  -DaveM
+ *
  *  nfs symlink handling code
  */
 
+#define NFS_NEED_XDR_TYPES
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
 #include <linux/stat.h>
+#include <asm/pgtable.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
@@ -37,70 +44,85 @@
 	NULL,			/* rename */
 	nfs_readlink,		/* readlink */
 	nfs_follow_link,	/* follow_link */
+	NULL,			/* get_block */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
-	NULL,			/* bmap */
+	NULL,			/* flushpage */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL			/* revalidate */
 };
 
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static int nfs_symlink_filler(struct dentry *dentry, struct page *page)
+{
+	struct inode *inode = dentry->d_inode;
+	struct nfs_fattr fattr;
+	void * buffer = (void *)page_address(page);
+	unsigned int error;
+
+	/* We place the length at the beginning of the page,
+	 * in client byte order, followed by the string.
+	 */
+	error = NFS_CALL(readlink, inode, (dentry, &fattr, buffer,
+					   PAGE_CACHE_SIZE-sizeof(u32)-4));
+	nfs_refresh_inode(inode, &fattr);
+	if (error < 0)
+		goto error;
+	flush_dcache_page(page_address(page)); /* Is this correct? */
+	set_bit(PG_uptodate, &page->flags);
+	nfs_unlock_page(page);
+	return 0;
+error:
+	set_bit(PG_error, &page->flags);
+	nfs_unlock_page(page);
+	return -EIO;
+}
+
 static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-
-	dfprintk(VFS, "nfs: readlink(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-					&mem, &res, &len, NFS_MAXPATHLEN);
-	if (! error) {
-		if (len > buflen)
-			len = buflen;
-		copy_to_user(buffer, res, len);
-		error = len;
-		kfree(mem);
-	}
-	return error;
+	struct inode *inode = dentry->d_inode;
+	struct page *page;
+	u32 *p, len;
+
+	/* Caller revalidated the directory inode already. */
+	page = read_cache_page(inode, 0,
+				(filler_t *)nfs_symlink_filler, dentry);
+	if (IS_ERR(page))
+		goto read_failed;
+
+	p = (u32 *) page_address(page);
+	len = *p++;
+	if (len > buflen)
+		len = buflen;
+	copy_to_user(buffer, p, len);
+	page_cache_release(page);
+	return len;
+read_failed:
+	return PTR_ERR(page);
 }
 
 static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
+nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-	char *path;
 	struct dentry *result;
-
-	dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-				 &mem, &res, &len, NFS_MAXPATHLEN);
-	result = ERR_PTR(error);
-	if (error)
-		goto out_dput;
-
-	result = ERR_PTR(-ENOMEM);
-	path = kmalloc(len + 1, GFP_KERNEL);
-	if (!path)
-		goto out_mem;
-	memcpy(path, res, len);
-	path[len] = 0;
-	kfree(mem);
-
-	result = lookup_dentry(path, base, follow);
-	kfree(path);
-out:
+	struct inode *inode = dentry->d_inode;
+	struct page *page;
+	u32 *p;
+
+	/* Caller revalidated the directory inode already. */
+	page = read_cache_page(inode, 0,
+				(filler_t *)nfs_symlink_filler, dentry);
+	if (IS_ERR(page))
+		goto read_failed;
+
+	p = (u32 *) page_address(page);
+	result = lookup_dentry((char *) (p + 1), base, follow);
+	page_cache_release(page);
 	return result;
-
-out_mem:
-	kfree(mem);
-out_dput:
-	dput(base);
-	goto out;
+read_failed:
+	return (struct dentry *)page;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/unlink.c linux.18p17/fs/nfs/unlink.c
--- linux.vanilla/fs/nfs/unlink.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/fs/nfs/unlink.c	Fri Sep 15 22:10:44 2000
@@ -0,0 +1,218 @@
+/*
+ *  linux/fs/nfs/unlink.c
+ *
+ * nfs sillydelete handling
+ *
+ * NOTE: we rely on holding the BKL for list manipulation protection.
+ *
+ * Copyright (C) 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/dcache.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+
+struct nfs_unlinkdata {
+	struct nfs_unlinkdata	*next;
+	struct dentry	*dir, *dentry;
+	struct qstr	name;
+	struct rpc_task	task;
+	struct rpc_cred	*cred;
+	unsigned int	count;
+};
+
+static struct nfs_unlinkdata	*nfs_deletes = NULL;
+static struct rpc_wait_queue	nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue");
+
+/**
+ * nfs_detach_unlinkdata - Remove asynchronous unlink from global list
+ * @data: pointer to descriptor
+ */
+static inline void
+nfs_detach_unlinkdata(struct nfs_unlinkdata *data)
+{
+	struct nfs_unlinkdata	**q;
+
+	for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) {
+		if (*q == data) {
+			*q = data->next;
+			break;
+		}
+	}
+}
+
+/**
+ * nfs_put_unlinkdata - release data from a sillydelete operation.
+ * @data: pointer to unlink structure.
+ */
+static void
+nfs_put_unlinkdata(struct nfs_unlinkdata *data)
+{
+	if (--data->count == 0) {
+		nfs_detach_unlinkdata(data);
+		if (data->name.name != NULL)
+			kfree(data->name.name);
+		kfree(data);
+	}
+}
+
+#define NAME_ALLOC_LEN(len)	((len+16) & ~15)
+/**
+ * nfs_copy_dname - copy dentry name to data structure
+ * @dentry: pointer to dentry
+ * @data: nfs_unlinkdata
+ */
+static inline void
+nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
+{
+	char		*str;
+	int		len = dentry->d_name.len;
+
+	str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
+	if (!str)
+		return;
+	memcpy(str, dentry->d_name.name, len);
+	if (!data->name.len) {
+		data->name.len = len;
+		data->name.name = str;
+	} else
+		kfree(str);
+}
+
+/**
+ * nfs_async_unlink_init - Initialize the RPC info
+ * @task: rpc_task of the sillydelete
+ *
+ * We delay initializing RPC info until after the call to dentry_iput()
+ * in order to minimize races against rename().
+ */
+static void
+nfs_async_unlink_init(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	struct dentry		*dir = data->dir;
+	struct rpc_message	msg;
+	int			status = -ENOENT;
+
+	if (!data->name.len)
+		goto out_err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.rpc_cred = data->cred;
+	status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
+	if (status < 0)
+		goto out_err;
+	rpc_call_setup(task, &msg, 0);
+	return;
+ out_err:
+	rpc_exit(task, status);
+}
+
+/**
+ * nfs_async_unlink_done - Sillydelete post-processing
+ * @task: rpc_task of the sillydelete
+ *
+ * Do the directory attribute update.
+ */
+static void
+nfs_async_unlink_done(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	struct dentry		*dir = data->dir;
+	struct inode		*dir_i;
+
+	if (!dir)
+		return;
+	dir_i = dir->d_inode;
+	nfs_zap_caches(dir_i);
+	NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg);
+	rpcauth_releasecred(task->tk_auth, data->cred);
+	data->cred = NULL;
+	dput(dir);
+}
+
+/**
+ * nfs_async_unlink_release - Release the sillydelete data.
+ * @task: rpc_task of the sillydelete
+ *
+ * We need to call nfs_put_unlinkdata as a 'tk_release' task since the
+ * rpc_task would be freed too.
+ */
+static void
+nfs_async_unlink_release(struct rpc_task *task)
+{
+	struct nfs_unlinkdata	*data = (struct nfs_unlinkdata *)task->tk_calldata;
+	nfs_put_unlinkdata(data);
+}
+
+/**
+ * nfs_async_unlink - asynchronous unlinking of a file
+ * @dir: directory in which the file resides.
+ * @name: name of the file to unlink.
+ */
+int
+nfs_async_unlink(struct dentry *dentry)
+{
+	struct dentry	*dir = dentry->d_parent;
+	struct nfs_unlinkdata	*data;
+	struct rpc_task	*task;
+	struct rpc_clnt	*clnt = NFS_CLIENT(dir->d_inode);
+	int		status = -ENOMEM;
+
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		goto out;
+	memset(data, 0, sizeof(*data));
+
+	data->dir = dget(dir);
+	data->dentry = dentry;
+
+	data->next = nfs_deletes;
+	nfs_deletes = data;
+	data->count = 1;
+
+	task = &data->task;
+	rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC);
+	task->tk_calldata = data;
+	task->tk_action = nfs_async_unlink_init;
+	task->tk_release = nfs_async_unlink_release;
+
+	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+	data->cred = rpcauth_lookupcred(clnt->cl_auth, 0);
+
+	rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL);
+	status = 0;
+ out:
+	return status;
+}
+
+/**
+ * nfs_complete_remove - Initialize completion of the sillydelete
+ * @dentry: dentry to delete
+ *
+ * Since we're most likely to be called by dentry_iput(), we
+ * only use the dentry to find the sillydelete. We then copy the name
+ * into the qstr.
+ */
+void
+nfs_complete_unlink(struct dentry *dentry)
+{
+	struct nfs_unlinkdata	*data;
+
+	for(data = nfs_deletes; data != NULL; data = data->next) {
+		if (dentry == data->dentry)
+			break;
+	}
+	if (!data)
+		return;
+	data->count++;
+	nfs_copy_dname(dentry, data);
+	dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+	if (data->task.tk_rpcwait == &nfs_delete_queue)
+		rpc_wake_up_task(&data->task);
+	nfs_put_unlinkdata(data);
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfs/write.c linux.18p17/fs/nfs/write.c
--- linux.vanilla/fs/nfs/write.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfs/write.c	Fri Sep 15 22:10:44 2000
@@ -10,26 +10,19 @@
  * RPC call to write the page is scheduled immediately; otherwise, the call
  * is delayed for a few seconds.
  *
- * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE.
+ * Just like readahead, no async I/O is performed if wsize < PAGE_CACHE_SIZE.
  *
- * Write requests are kept on the inode's writeback list. Each entry in
+ * Write requests are kept on the inode's dirty list. Each entry in
  * that list references the page (portion) to be written. When the
- * cache timeout has expired, the RPC task is woken up, and tries to
- * lock the page. As soon as it manages to do so, the request is moved
- * from the writeback list to the writelock list.
+ * cache timeout has expired, the flushd task is woken up, and tries to
+ * lock the request. As soon as it manages to do so, the request is removed
+ * from the dirty list. When the RPC write call completes, the request
+ * is either released or put on the inode's commit list.
  *
  * Note: we must make sure never to confuse the inode passed in the
  * write_page request with the one in page->inode. As far as I understand
  * it, these are different when doing a swap-out.
  *
- * To understand everything that goes on here and in the NFS read code,
- * one should be aware that a page is locked in exactly one of the following
- * cases:
- *
- *  -	A write request is in progress.
- *  -	A user process is in generic_file_write/nfs_update_page
- *  -	A user process is in generic_file_read
- *
  * Also note that because of the way pages are invalidated in
  * nfs_revalidate_inode, the following assertions hold:
  *
@@ -38,75 +31,147 @@
  *  -	If the page is not uptodate, there will be no pending write
  *	requests, and no process will be in nfs_update_page.
  *
- * FIXME: Interaction with the vmscan routines is not optimal yet.
- * Either vmscan must be made nfs-savvy, or we need a different page
- * reclaim concept that supports something like FS-independent
- * buffer_heads with a b_ops-> field.
- *
  * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust.
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
+#include <asm/pgtable.h>
 #include <linux/pagemap.h>
-#include <linux/file.h>
 
+#include <linux/sunrpc/auth.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_page.h>
 #include <asm/uaccess.h>
+#include <linux/file.h>
+
 
 #define NFS_PARANOIA 1
 #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
 
-static void			nfs_wback_begin(struct rpc_task *task);
-static void			nfs_wback_result(struct rpc_task *task);
-static void			nfs_cancel_request(struct nfs_wreq *req);
+static unsigned int	nfs_nr_requests = 0;
 
 /*
- * Cache parameters
+ * Local structures
+ * This is the struct where the WRITE/COMMIT arguments go.
  */
-#define NFS_WRITEBACK_DELAY	(10 * HZ)
-#define NFS_WRITEBACK_MAX	64
+struct nfs_write_data {
+	struct rpc_task		task;
+	struct dentry		*dentry;
+	struct rpc_cred		*cred;
+	struct nfs_writeargs	args;		/* argument struct */
+	struct nfs_writeres	res;		/* result struct */
+	struct nfs_fattr	fattr;
+	struct nfs_writeverf	verf;
+	struct list_head	pages;		/* Coalesced requests we wish to flush */
+};
 
 /*
- * Limit number of delayed writes
+ * Local function declarations
  */
-static int			nr_write_requests = 0;
-static struct rpc_wait_queue	write_queue = RPC_INIT_WAITQ("write_chain");
+static void	nfs_writeback_done(struct rpc_task *);
+#ifdef CONFIG_NFS_V3
+static void	nfs_commit_done(struct rpc_task *);
+#endif
 
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)	(0)
 #endif
 
+static __inline__ struct nfs_page *nfs_page_alloc(void)
+{
+	struct nfs_page *p;
+	p = (struct nfs_page *) kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->wb_hash);
+		INIT_LIST_HEAD(&p->wb_list);
+	}
+	return p;
+}
+
+static __inline__ void nfs_page_free(struct nfs_page *p)
+{
+	kfree(p);
+}
+
+static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
+{
+	struct nfs_write_data   *p;
+	p = (struct nfs_write_data *) kmalloc(sizeof(*p), GFP_NFS);
+	if (p) {
+		memset(p, 0, sizeof(*p));
+		INIT_LIST_HEAD(&p->pages);
+	}
+	return p;
+}
+
+static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
+{
+	kfree(p);
+}
+
+static void nfs_writedata_release(struct rpc_task *task)
+{
+	nfs_writedata_free(task->tk_calldata);
+}
+
+static __inline__ int
+nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+{
+	if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
+		fattr->pre_size  = NFS_CACHE_ISIZE(inode);
+		fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+		fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+		fattr->valid |= NFS_ATTR_WCC;
+	}
+	return nfs_refresh_inode(inode, fattr);
+}
+
 /*
  * Write a page synchronously.
  * Offset is the data offset within the page.
  */
 static int
-nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
-		struct page *page, unsigned long offset, unsigned int count)
+nfs_writepage_sync(struct file *file, struct page *page,
+		   unsigned long offset, unsigned int count)
 {
+	struct dentry	*dentry = file->f_dentry;
+	struct inode	*inode = dentry->d_inode;
+	struct rpc_cred	*cred = nfs_file_cred(file);
 	unsigned int	wsize = NFS_SERVER(inode)->wsize;
-	int		result, refresh = 0, written = 0;
+	int		result, refresh = 0, written = 0, flags;
 	u8		*buffer;
 	struct nfs_fattr fattr;
+	struct nfs_writeverf verifier;
 
 	dprintk("NFS:      nfs_writepage_sync(%s/%s %d@%ld)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		count, page->offset + offset);
+		count, nfs_page_offset(page) + offset);
 
 	buffer = (u8 *) page_address(page) + offset;
-	offset += page->offset;
+	offset += nfs_page_offset(page);
+
+	flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC;
 
 	do {
 		if (count < wsize && !IS_SWAPFILE(inode))
 			wsize = count;
 
-		result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
-					IS_SWAPFILE(inode), offset, wsize,
-					buffer, &fattr);
+		result = NFS_PROTO(inode)->write(dentry, &fattr, cred, flags,
+						 offset, wsize, buffer,
+						 &verifier);
+		nfs_write_attributes(inode, &fattr);
 
 		if (result < 0) {
 			/* Must mark the page invalid after I/O error */
@@ -114,7 +179,7 @@
 			goto io_error;
 		}
 		if (result != wsize)
-			printk("NFS: short write, wsize=%u, result=%d\n",
+			printk(KERN_ERR "NFS: short write, size=%u, result=%d\n",
 			wsize, result);
 		refresh = 1;
 		buffer  += wsize;
@@ -130,274 +195,677 @@
 	} while (count);
 
 io_error:
-	/* Note: we don't refresh if the call failed (fattr invalid) */
-	if (refresh && result >= 0) {
-		/* See comments in nfs_wback_result */
-		/* N.B. I don't think this is right -- sync writes in order */
-		if (fattr.size < inode->i_size)
-			fattr.size = inode->i_size;
-		if (fattr.mtime.seconds < inode->i_mtime)
-			printk("nfs_writepage_sync: prior time??\n");
-		/* Solaris 2.5 server seems to send garbled
-		 * fattrs occasionally */
-		if (inode->i_ino == fattr.fileid) {
-			/*
-			 * We expect the mtime value to change, and
-			 * don't want to invalidate the caches.
-			 */
-			inode->i_mtime = fattr.mtime.seconds;
-			nfs_refresh_inode(inode, &fattr);
-		} 
-		else
-			printk("nfs_writepage_sync: inode %ld, got %u?\n",
-				inode->i_ino, fattr.fileid);
-	}
-
 	return written? written : result;
 }
 
 /*
- * Append a writeback request to a list
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
  */
-static inline void
-append_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+int
+nfs_writepage(struct file * file, struct page *page)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	unsigned offset = PAGE_CACHE_SIZE;
+
+	if (page->offset >= inode->i_size)
+		return -EIO;
+	if (page->offset + offset > inode->i_size)
+		offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+	return nfs_writepage_sync(file, page, 0, offset);
+}
+
+/*
+ * Check whether the file range we want to write to is locked by
+ * us.
+ */
+static int
+region_locked(struct inode *inode, struct nfs_page *req)
 {
-	dprintk("NFS:      append_write_request(%p, %p)\n", q, wreq);
-	rpc_append_list(q, wreq);
+	struct file_lock	*fl;
+	unsigned long		rqstart, rqend;
+
+	/* Don't optimize writes if we don't use NLM */
+	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+		return 0;
+
+	rqstart = nfs_page_offset(req->wb_page) + req->wb_offset;
+	rqend = rqstart + req->wb_bytes;
+	for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+		if (fl->fl_owner == current->files && (fl->fl_flags & FL_POSIX)
+		    && fl->fl_type == F_WRLCK
+		    && fl->fl_start <= rqstart && rqend <= fl->fl_end) {
+			return 1;
+		}
+	}
+
+	return 0;
 }
 
 /*
- * Remove a writeback request from a list
+ * Insert a write request into an inode
  */
 static inline void
-remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 {
-	dprintk("NFS:      remove_write_request(%p, %p)\n", q, wreq);
-	rpc_remove_list(q, wreq);
+	if (!list_empty(&req->wb_hash))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
+	inode->u.nfs_i.npages++;
+	list_add(&req->wb_hash, &inode->u.nfs_i.writeback);
+	req->wb_count++;
 }
 
 /*
- * Find a non-busy write request for a given page to
- * try to combine with.
+ * Insert a write request into an inode
  */
-static inline struct nfs_wreq *
-find_write_request(struct inode *inode, struct page *page)
+static inline void
+nfs_inode_remove_request(struct nfs_page *req)
 {
-	pid_t pid = current->pid;
-	struct nfs_wreq	*head, *req;
+	struct inode *inode;
+	if (list_empty(&req->wb_hash))
+		return;
+
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted unhashed!\n");
+	inode = req->wb_dentry->d_inode;
+	list_del(&req->wb_hash);
+	INIT_LIST_HEAD(&req->wb_hash);
+	inode->u.nfs_i.npages--;
+	if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
+	if (!nfs_have_writebacks(inode) && !nfs_have_read(inode))
+		inode_remove_flushd(inode);
+	nfs_release_request(req);
+}
 
-	dprintk("NFS:      find_write_request(%x/%ld, %p)\n",
-				inode->i_dev, inode->i_ino, page);
-	if (!(req = head = NFS_WRITEBACK(inode)))
-		return NULL;
-	do {
-		/*
-		 * We can't combine with canceled requests or
-		 * requests that have already been started..
-		 */
-		if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS))
+/*
+ * Find a request
+ */
+static inline struct nfs_page *
+_nfs_find_request(struct inode *inode, struct page *page)
+{
+	struct list_head	*head, *next;
+
+	head = &inode->u.nfs_i.writeback;
+	next = head->next;
+	while (next != head) {
+		struct nfs_page *req = nfs_inode_wb_entry(next);
+		next = next->next;
+		if (page_index(req->wb_page) != page_index(page))
 			continue;
+		req->wb_count++;
+		return req;
+	}
+	return NULL;
+}
 
-		if (req->wb_page == page && req->wb_pid == pid)
-			return req;
+static struct nfs_page *
+nfs_find_request(struct inode *inode, struct page *page)
+{
+	struct nfs_page		*req;
 
-		/*
-		 * Ehh, don't keep too many tasks queued..
-		 */
-		rpc_wake_up_task(&req->wb_task);
+	req = _nfs_find_request(inode, page);
+	return req;
+}
 
-	} while ((req = WB_NEXT(req)) != head);
-	return NULL;
+/*
+ * Insert a write request into a sorted list
+ */
+void nfs_list_add_request(struct nfs_page *req, struct list_head *head)
+{
+	struct list_head *prev;
+
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Add to list failed!\n");
+		return;
+	}
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted added to list!\n");
+	prev = head->prev;
+	while (prev != head) {
+		struct nfs_page	*p = nfs_list_entry(prev);
+		if (page_index(p->wb_page) < page_index(req->wb_page))
+			break;
+		prev = prev->prev;
+	}
+	list_add(&req->wb_list, prev);
+	req->wb_list_head = head;
 }
 
 /*
- * Find and release all failed requests for this inode.
+ * Insert a write request into an inode
  */
-int
-nfs_check_failed_request(struct inode * inode)
+void nfs_list_remove_request(struct nfs_page *req)
 {
-	/* FIXME! */
-	return 0;
+	if (list_empty(&req->wb_list))
+		return;
+	if (!NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: unlocked request attempted removed from list!\n");
+	list_del(&req->wb_list);
+	INIT_LIST_HEAD(&req->wb_list);
+	req->wb_list_head = NULL;
 }
 
 /*
- * Try to merge adjacent write requests. This works only for requests
- * issued by the same user.
+ * Add a request to the inode's dirty list.
+ */
+static inline void
+nfs_mark_request_dirty(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_dentry->d_inode;
+
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.dirty);
+		inode->u.nfs_i.ndirty++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+
+/*
+ * Check if a request is dirty
  */
 static inline int
-update_write_request(struct nfs_wreq *req, unsigned int first,
-			unsigned int bytes)
+nfs_dirty_request(struct nfs_page *req)
 {
-	unsigned int	rqfirst = req->wb_offset,
-			rqlast = rqfirst + req->wb_bytes,
-			last = first + bytes;
+	struct inode *inode = req->wb_dentry->d_inode;
+	return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+}
 
-	dprintk("nfs:      trying to update write request %p\n", req);
+#ifdef CONFIG_NFS_V3
+/*
+ * Add a request to the inode's commit list.
+ */
+static inline void
+nfs_mark_request_commit(struct nfs_page *req)
+{
+	struct inode *inode = req->wb_dentry->d_inode;
 
-	/* not contiguous? */
-	if (rqlast < first || last < rqfirst)
-		return 0;
+	if (list_empty(&req->wb_list)) {
+		nfs_list_add_request(req, &inode->u.nfs_i.commit);
+		inode->u.nfs_i.ncommit++;
+	}
+	inode_schedule_scan(inode, req->wb_timeout);
+}
+#endif
 
-	if (first < rqfirst)
-		rqfirst = first;
-	if (rqlast < last)
-		rqlast = last;
+/*
+ * Create a write request.
+ * Page must be locked by the caller. This makes sure we never create
+ * two different requests for the same page, and avoids possible deadlock
+ * when we reach the hard limit on the number of dirty pages.
+ */
+struct nfs_page *nfs_create_request(struct file *file, struct page *page,
+				    unsigned int offset, unsigned int count)
+{
+	struct dentry		*dentry = file->f_dentry;
+	struct inode		*inode = dentry->d_inode;
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct nfs_page		*req = NULL;
+	long			timeout;
 
-	req->wb_offset = rqfirst;
-	req->wb_bytes  = rqlast - rqfirst;
-	req->wb_count++;
+	/* Deal with hard/soft limits.
+	 */
+	do {
+		/* If we're still over the global soft limit, wake up all requests */
+		if (nfs_nr_requests >= MAX_REQUEST_SOFT) {
+			dprintk("NFS:      hit soft limit (%d requests)\n",
+				nfs_nr_requests);
+			if (!cache->task)
+				nfs_reqlist_init(NFS_SERVER(inode));
+			nfs_wake_flushd();
+		}
+
+		/* If we haven't reached the hard limit yet,
+		 * try to allocate the request struct */
+		if (cache->nr_requests < MAX_REQUEST_HARD) {
+			req = nfs_page_alloc();
+			if (req != NULL)
+				break;
+		}
+
+		/* We're over the hard limit. Wait for better times */
+		dprintk("NFS:      create_request sleeping (total %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+
+		timeout = 1 * HZ;
+		if (NFS_SERVER(inode)->flags & NFS_MOUNT_INTR) {
+			interruptible_sleep_on_timeout(&cache->request_wait,
+						       timeout);
+			if (signalled())
+				break;
+		} else
+			sleep_on_timeout(&cache->request_wait, timeout);
+
+		dprintk("NFS:      create_request waking up (tot %d pid %d)\n",
+			nfs_nr_requests, current->pid);
+	} while (!req);
+	if (!req)
+		return NULL;
 
-	return 1;
+	/* Initialize the request struct. Initially, we assume a
+	 * long write-back delay. This will be adjusted in
+	 * update_nfs_request below if the region is not locked. */
+	req->wb_page    = page;
+	atomic_inc(&page->count);
+	req->wb_offset  = offset;
+	req->wb_bytes   = count;
+	req->wb_file    = file;
+	file->f_count++;
+	req->wb_dentry	= dentry;
+	req->wb_cred	= nfs_file_cred(file);
+	req->wb_count   = 1;
+
+	/* register request's existence */
+	cache->nr_requests++;
+	nfs_nr_requests++;
+	return req;
 }
 
-static inline void
-free_write_request(struct nfs_wreq * req)
+
+/*
+ * Release all resources associated with a write request after it
+ * has been committed to stable storage
+ */
+void
+nfs_release_request(struct nfs_page *req)
 {
-	if (!--req->wb_count)
-		kfree(req);
+	struct inode		*inode = req->wb_dentry->d_inode;
+	struct nfs_reqlist	*cache = NFS_REQUESTLIST(inode);
+	struct page		*page = req->wb_page;
+
+	if (--req->wb_count)
+		return;
+
+	if (!list_empty(&req->wb_list)) {
+		printk(KERN_ERR "NFS: Request released while still on a list!\n");
+		nfs_list_remove_request(req);
+	}
+	if (!list_empty(&req->wb_hash)) {
+		printk(KERN_ERR "NFS: Request released while still hashed!\n");
+		nfs_inode_remove_request(req);
+	}
+	if (NFS_WBACK_BUSY(req))
+		printk(KERN_ERR "NFS: Request released while still locked!\n");
+
+	fput(req->wb_file);
+	page_cache_release(page);
+	nfs_page_free(req);
+	/* wake up anyone waiting to allocate a request */
+	cache->nr_requests--;
+	nfs_nr_requests--;
+	wake_up(&cache->request_wait);
 }
 
 /*
- * Create and initialize a writeback request
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
  */
-static inline struct nfs_wreq *
-create_write_request(struct file * file, struct page *page, unsigned int offset, unsigned int bytes)
+static int
+nfs_wait_on_request(struct nfs_page *req)
 {
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
-	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
-	struct nfs_wreq *wreq;
-	struct rpc_task	*task;
+	struct inode	*inode = req->wb_dentry->d_inode;
+        struct rpc_clnt	*clnt = NFS_CLIENT(inode);
+        int retval;
 
-	dprintk("NFS:      create_write_request(%s/%s, %ld+%d)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name,
-		page->offset + offset, bytes);
+	if (!NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	retval = nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
+	nfs_release_request(req);
+        return retval;
+}
 
-	/* FIXME: Enforce hard limit on number of concurrent writes? */
-	wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL);
-	if (!wreq)
-		goto out_fail;
-	memset(wreq, 0, sizeof(*wreq));
-
-	task = &wreq->wb_task;
-	rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
-	task->tk_calldata = wreq;
-	task->tk_action = nfs_wback_begin;
-
-	rpcauth_lookupcred(task);	/* Obtain user creds */
-	if (task->tk_status < 0)
-		goto out_req;
-
-	/* Put the task on inode's writeback request list. */
-	wreq->wb_file = file;
-	wreq->wb_pid    = current->pid;
-	wreq->wb_page   = page;
-	wreq->wb_offset = offset;
-	wreq->wb_bytes  = bytes;
-	wreq->wb_count	= 2;		/* One for the IO, one for us */
-
-	append_write_request(&NFS_WRITEBACK(inode), wreq);
-
-	if (nr_write_requests++ > NFS_WRITEBACK_MAX*3/4)
-		rpc_wake_up_next(&write_queue);
-
-	return wreq;
-
-out_req:
-	rpc_release_task(task);
-	kfree(wreq);
-out_fail:
-	return NULL;
+/*
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
+ */
+static int
+nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	struct list_head	*p, *head;
+	unsigned long		idx_end;
+	unsigned int		res = 0;
+	int			error;
+
+	if (npages == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + npages - 1;
+
+	head = &inode->u.nfs_i.writeback;
+	p = head->next;
+	while (p != head) {
+		unsigned long pg_idx;
+		struct nfs_page *req = nfs_inode_wb_entry(p);
+
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+
+		if (!NFS_WBACK_BUSY(req))
+			continue;
+		req->wb_count++;
+		error = nfs_wait_on_request(req);
+		nfs_release_request(req);
+		if (error < 0)
+			return error;
+		p = head->next;
+		res++;
+	}
+	return res;
 }
 
 /*
- * Schedule a writeback RPC call.
- * If the server is congested, don't add to our backlog of queued
- * requests but call it synchronously.
- * The function returns whether we should wait for the thing or not.
- *
- * FIXME: Here we could walk the inode's lock list to see whether the
- * page we're currently writing to has been write-locked by the caller.
- * If it is, we could schedule an async write request with a long
- * delay in order to avoid writing back the page until the lock is
- * released.
+ * Scan cluster for dirty pages and send as many of them to the
+ * server as possible.
  */
-static inline int
-schedule_write_request(struct nfs_wreq *req, int sync)
+int nfs_scan_list_timeout(struct list_head *head, struct list_head *dst,
+			  struct inode *inode)
 {
-	struct rpc_task	*task = &req->wb_task;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	struct list_head	*p;
+        struct nfs_page		*req;
+        int			pages = 0;
 
-	if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX)
-		sync = 1;
+	p = head->next;
+        while (p != head) {
+		req = nfs_list_entry(p);
+		p = p->next;
+		if (time_after(req->wb_timeout, jiffies)) {
+			if (time_after(NFS_NEXTSCAN(inode), req->wb_timeout))
+				NFS_NEXTSCAN(inode) = req->wb_timeout;
+			continue;
+		}
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+	}
+	return pages;
+}
 
-	if (sync) {
-		sigset_t	oldmask;
-		struct rpc_clnt *clnt = NFS_CLIENT(inode);
-		dprintk("NFS: %4d schedule_write_request (sync)\n",
-					task->tk_pid);
-		/* Page is already locked */
-		rpc_clnt_sigmask(clnt, &oldmask);
-		rpc_execute(task);
-		rpc_clnt_sigunmask(clnt, &oldmask);
-	} else {
-		dprintk("NFS: %4d schedule_write_request (async)\n",
-					task->tk_pid);
-		task->tk_flags |= RPC_TASK_ASYNC;
-		task->tk_timeout = NFS_WRITEBACK_DELAY;
-		rpc_sleep_on(&write_queue, task, NULL, NULL);
+static int
+nfs_scan_dirty_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.dirty, dst, inode);
+	inode->u.nfs_i.ndirty -= pages;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	pages = nfs_scan_list_timeout(&inode->u.nfs_i.commit, dst, inode);
+	inode->u.nfs_i.ncommit -= pages;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	return pages;
+}
+#endif
+
+int nfs_scan_list(struct list_head *src, struct list_head *dst, 
+		  struct file *file, unsigned long idx_start, 
+		  unsigned int npages)
+{
+	struct list_head	*p;
+	struct nfs_page		*req;
+	unsigned long		idx_end;
+	int			res;
+
+	res = 0;
+	if (npages == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + npages - 1;
+	p = src->next;
+	while (p != src) {
+		unsigned long pg_idx;
+
+		req = nfs_list_entry(p);
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = page_index(req->wb_page);
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+
+		if (!nfs_lock_request(req))
+			continue;
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		res++;
 	}
+	return res;
+}
 
-	return sync;
+static int
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
+	inode->u.nfs_i.ndirty -= res;
+	if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+	return res;
+}
+
+#ifdef CONFIG_NFS_V3
+static int
+nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
+	inode->u.nfs_i.ncommit -= res;
+	if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+		printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+	return res;
+}
+#endif
+
+int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages)
+{
+	struct nfs_page		*req = NULL;
+	unsigned int		pages = 0;
+
+	while (!list_empty(src)) {
+		struct nfs_page	*prev = req;
+
+		req = nfs_list_entry(src->next);
+		if (prev) {
+			if (req->wb_file != prev->wb_file)
+				break;
+
+			if (page_index(req->wb_page) != page_index(prev->wb_page)+1)
+				break;
+
+			if (req->wb_offset != 0)
+				break;
+		}
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, dst);
+		pages++;
+		if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+			break;
+		if (pages >= maxpages)
+			break;
+	}
+	return pages;
 }
 
 /*
- * Wait for request to complete.
+ * Try to update any existing write request, or create one if there is none.
+ * In order to match, the request's credentials must match those of
+ * the calling process.
+ *
+ * Note: Should always be called with the Page Lock held!
  */
-static int
-wait_on_write_request(struct nfs_wreq *req)
+static struct nfs_page *
+nfs_update_request(struct file* file, struct page *page,
+		   unsigned int offset, unsigned int bytes)
 {
-	struct file		*file = req->wb_file;
-	struct dentry		*dentry = file->f_dentry;
-	struct inode		*inode = dentry->d_inode;
-	struct rpc_clnt		*clnt = NFS_CLIENT(inode);
-	struct wait_queue	wait = { current, NULL };
-	sigset_t		oldmask;
-	int retval;
-
-	/* Make sure it's started.. */
-	if (!WB_INPROGRESS(req))
-		rpc_wake_up_task(&req->wb_task);
+	struct inode		*inode = file->f_dentry->d_inode;
+	struct nfs_page		*req, *new = NULL;
+	unsigned long		rqend, end;
+
+	end = offset + bytes;
 
-	rpc_clnt_sigmask(clnt, &oldmask);
-	add_wait_queue(&req->wb_wait, &wait);
 	for (;;) {
-		current->state = TASK_INTERRUPTIBLE;
-		retval = 0;
-		if (req->wb_flags & NFS_WRITE_COMPLETE)
+		/* Loop over all inode entries and see if we find
+		 * A request for the page we wish to update
+		 */
+		req = _nfs_find_request(inode, page);
+		if (req) {
+			if (!nfs_lock_request(req)) {
+				nfs_wait_on_request(req);
+				nfs_release_request(req);
+				continue;
+			}
+			if (new)
+				nfs_release_request(new);
 			break;
-		retval = -ERESTARTSYS;
-		if (signalled())
+		}
+
+		req = new;
+		if (req) {
+			nfs_lock_request(req);
+			nfs_inode_add_request(inode, req);
+			nfs_mark_request_dirty(req);
 			break;
-		schedule();
+		}
+
+		/*
+		 * If we're over the soft limit, flush out old requests
+		 */
+		if (inode->u.nfs_i.npages >= MAX_REQUEST_SOFT)
+			nfs_wb_file(inode, file);
+		new = nfs_create_request(file, page, offset, bytes);
+		if (!new)
+			return ERR_PTR(-ENOMEM);
+		/* If the region is locked, adjust the timeout */
+		if (region_locked(inode, new))
+			new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY;
+		else
+			new->wb_timeout = jiffies + NFS_WRITEBACK_DELAY;
+	}
+
+	/* We have a request for our page.
+	 * If the creds don't match, or the
+	 * page addresses don't match,
+	 * tell the caller to wait on the conflicting
+	 * request.
+	 */
+	rqend = req->wb_offset + req->wb_bytes;
+	if (req->wb_file != file
+	    || req->wb_page != page
+	    || !nfs_dirty_request(req)
+	    || offset > rqend || end < req->wb_offset) {
+		nfs_unlock_request(req);
+		nfs_release_request(req);
+		return ERR_PTR(-EBUSY);
+	}
+
+	/* Okay, the request matches. Update the region */
+	if (offset < req->wb_offset) {
+		req->wb_offset = offset;
+		req->wb_bytes = rqend - req->wb_offset;
 	}
-	remove_wait_queue(&req->wb_wait, &wait);
-	current->state = TASK_RUNNING;
-	rpc_clnt_sigunmask(clnt, &oldmask);
-	return retval;
+
+	if (end > rqend)
+		req->wb_bytes = end - req->wb_offset;
+
+	nfs_unlock_request(req);
+
+	return req;
 }
 
 /*
- * Write a page to the server. This will be used for NFS swapping only
- * (for now), and we currently do this synchronously only.
+ * This is the strategy routine for NFS.
+ * It is called by nfs_updatepage whenever the user wrote up to the end
+ * of a page.
+ *
+ * We always try to submit a set of requests in parallel so that the
+ * server's write code can gather writes. This is mainly for the benefit
+ * of NFSv2.
+ *
+ * We never submit more requests than we think the remote can handle.
+ * For UDP sockets, we make sure we don't exceed the congestion window;
+ * for TCP, we limit the number of requests to 8.
+ *
+ * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
+ * should be sent out in one go. This is for the benefit of NFSv2 servers
+ * that perform write gathering.
+ *
+ * FIXME: Different servers may have different sweet spots.
+ * Record the average congestion window in server struct?
  */
+#define NFS_STRATEGY_PAGES      8
+static void
+nfs_strategy(struct inode *inode)
+{
+	unsigned int	dirty, wpages;
+
+	dirty  = inode->u.nfs_i.ndirty;
+	wpages = NFS_SERVER(inode)->wpages;
+#ifdef CONFIG_NFS_V3
+	if (NFS_PROTO(inode)->version == 2) {
+		if (dirty >= NFS_STRATEGY_PAGES * wpages)
+			nfs_flush_file(inode, NULL, 0, 0, 0);
+	} else {
+		if (dirty >= wpages)
+			nfs_flush_file(inode, NULL, 0, 0, 0);
+		if (inode->u.nfs_i.ncommit > NFS_STRATEGY_PAGES * wpages &&
+		    nfs_nr_requests > MAX_REQUEST_SOFT)
+			nfs_commit_file(inode, NULL, 0, 0, 0);
+	}
+#else
+	if (dirty >= NFS_STRATEGY_PAGES * wpages)
+		nfs_flush_file(inode, NULL, 0, 0, 0);
+#endif
+	/*
+	 * If we're running out of requests, flush out everything
+	 * in order to reduce memory useage...
+	 */
+	if (inode->u.nfs_i.npages > MAX_REQUEST_SOFT)
+		nfs_wb_all(inode);
+}
+
 int
-nfs_writepage(struct file * file, struct page *page)
+nfs_flush_incompatible(struct file *file, struct page *page)
 {
-	struct dentry *dentry = file->f_dentry;
-	return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct nfs_page	*req;
+	int		status = 0;
+	/*
+	 * Look for a request corresponding to this page. If there
+	 * is one, and it belongs to another file, we flush it out
+	 * before we try to copy anything into the page. Do this
+	 * due to the lack of an ACCESS-type call in NFSv2.
+	 * Also do the same if we find a request from an existing
+	 * dropped page.
+	 */
+	req = nfs_find_request(inode,page);
+	if (req) {
+		if (req->wb_file != file || req->wb_page != page)
+			status = nfs_wb_page(inode, page);
+		nfs_release_request(req);
+	}
+	return (status < 0) ? status : 0;
 }
 
 /*
@@ -411,270 +879,532 @@
 {
 	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
-	struct nfs_wreq	*req;
-	int		synchronous = sync;
-	int		retval;
+	struct nfs_page	*req;
+	int		status = 0;
 
 	dprintk("NFS:      nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
-		count, page->offset+offset, sync);
-
-	/*
-	 * Try to find a corresponding request on the writeback queue.
-	 * If there is one, we can be sure that this request is not
-	 * yet being processed, because we hold a lock on the page.
-	 *
-	 * If the request was created by us, update it. Otherwise,
-	 * transfer the page lock and flush out the dirty page now.
-	 * After returning, generic_file_write will wait on the
-	 * page and retry the update.
-	 */
-	req = find_write_request(inode, page);
-	if (req && req->wb_file == file && update_write_request(req, offset, count))
-		goto updated;
+		count, nfs_page_offset(page)+offset, sync);
 
 	/*
 	 * If wsize is smaller than page size, update and write
 	 * page synchronously.
 	 */
-	if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
-		return nfs_writepage_sync(dentry, inode, page, offset, count);
+	if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE)
+		return nfs_writepage_sync(file, page, offset, count);
 
-	/* Create the write request. */
-	req = create_write_request(file, page, offset, count);
-	if (!req)
-		return -ENOBUFS;
-
-	/*
-	 * Ok, there's another user of this page with the new request..
-	 * The IO completion will then free the page and the dentry.
-	 */
-	atomic_inc(&page->count);
-	file->f_count++;
-
-	/* Schedule request */
-	synchronous = schedule_write_request(req, sync);
+        /*
+         * Try to find an NFS request corresponding to this page
+         * and update it.
+         * If the existing request cannot be updated, we must flush
+	 * it out now.
+         */
+	do {
+		req = nfs_update_request(file, page, offset, count);
+		if (IS_ERR(req))
+			status = PTR_ERR(req);
+		if (status != -EBUSY)
+			break;
+		/* Request could not be updated. Flush it out and try again */
+		status = nfs_wb_page(inode, page);
+	} while (status >= 0);
+	if (status < 0)
+		goto done;
 
-updated:
-	if (req->wb_bytes == PAGE_SIZE)
+	if (req->wb_bytes == PAGE_CACHE_SIZE)
 		set_bit(PG_uptodate, &page->flags);
 
-	retval = count;
-	if (synchronous) {
-		int status = wait_on_write_request(req);
-		if (status) {
-			nfs_cancel_request(req);
-			retval = status;
-		} else {
-			status = req->wb_status;
-			if (status < 0)
-				retval = status;
-		}
+        status = count;         /* unless we detect an error */
 
-		if (retval < 0)
-			clear_bit(PG_uptodate, &page->flags);
-	}
+	/* If the user requested a sync write, do it now */
+	if (sync) {
+		int     error;
 
-	free_write_request(req);
-	return retval;
+		error = nfs_sync_file(inode, file, page_index(page), 1, FLUSH_SYNC|FLUSH_STABLE);
+		if (error < 0 || (error = file->f_error) < 0)
+			status = error;
+		file->f_error = 0;
+	} else {
+		/* If we wrote past the end of the page.
+		 * Call the strategy routine so it can send out a bunch
+		 * of requests.
+		 */
+		if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE)
+			nfs_strategy(inode);
+	}
+	nfs_release_request(req);
+done:
+        dprintk("NFS:      nfs_updatepage returns %d (isize %ld)\n",
+                                                status, inode->i_size);
+	if (status < 0)
+		clear_bit(PG_uptodate, &page->flags);
+	return status;
 }
 
 /*
- * Cancel a write request. We always mark it cancelled,
- * but if it's already in progress there's no point in
- * calling rpc_exit, and we don't want to overwrite the
- * tk_status field.
- */ 
+ * Set up the argument/result storage required for the RPC call.
+ */
 static void
-nfs_cancel_request(struct nfs_wreq *req)
+nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	req->wb_flags |= NFS_WRITE_CANCELLED;
-	if (!WB_INPROGRESS(req)) {
-		rpc_exit(&req->wb_task, 0);
-		rpc_wake_up_task(&req->wb_task);
+	struct nfs_page		*req;
+	struct iovec		*iov;
+	unsigned int		count;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	iov = data->args.iov;
+	count = 0;
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		iov->iov_base = (void *)(page_address(req->wb_page) + req->wb_offset);
+		iov->iov_len = req->wb_bytes;
+		count += req->wb_bytes;
+		iov++;
+		data->args.nriov++;
 	}
+	req = nfs_list_entry(data->pages.next);
+	data->dentry = req->wb_dentry;
+	data->cred = req->wb_cred;
+	data->args.fh     = NFS_FH(req->wb_dentry);
+	data->args.offset = nfs_page_offset(req->wb_page) + req->wb_offset;
+	data->args.count  = count;
+	data->res.fattr   = &data->fattr;
+	data->res.count   = count;
+	data->res.verf    = &data->verf;
 }
 
+
 /*
- * Cancel all writeback requests, both pending and in progress.
+ * Create an RPC task for the given write request and kick it.
+ * The page must have been locked by the caller.
+ *
+ * It may happen that the page we're passed is not marked dirty.
+ * This is the case if nfs_updatepage detects a conflicting request
+ * that has been written but not committed.
  */
-static void
-nfs_cancel_dirty(struct inode *inode, pid_t pid)
+static int
+nfs_flush_one(struct list_head *head, struct dentry *dentry, int how)
 {
-	struct nfs_wreq *head, *req;
+	struct inode            *inode = dentry->d_inode;
+	struct rpc_clnt 	*clnt = NFS_CLIENT(inode);
+	struct nfs_write_data	*data;
+	struct rpc_task		*task;
+	struct rpc_message	msg;
+	int                     flags,
+				async = !(how & FLUSH_SYNC),
+				stable = (how & FLUSH_STABLE);
+	sigset_t		oldset;
+
+
+	data = nfs_writedata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	/* Set the initial flags for the task.  */
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_write_rpcsetup(head, data);
+	if (stable) {
+		if (!inode->u.nfs_i.ncommit)
+			data->args.stable = NFS_FILE_SYNC;
+		else
+			data->args.stable = NFS_DATA_SYNC;
+	} else
+		data->args.stable = NFS_UNSTABLE;
+
+	/* Finalize the task. */
+	rpc_init_task(task, clnt, nfs_writeback_done, flags);
+	task->tk_calldata = data;
+	/* Release requests */
+	task->tk_release = nfs_writedata_release;
+
+#ifdef CONFIG_NFS_V3
+	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;
+#else
+	msg.rpc_proc = NFSPROC_WRITE;
+#endif
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated write call (req %s/%s count %d nriov %d)\n",
+		task->tk_pid, 
+		dentry->d_parent->d_name.name,
+		dentry->d_name.name,
+		data->args.count, data->args.nriov);
+
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		struct nfs_page *req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
+}
 
-	req = head = NFS_WRITEBACK(inode);
-	while (req != NULL) {
-		if (pid == 0 || req->wb_pid == pid)
-			nfs_cancel_request(req);
-		if ((req = WB_NEXT(req)) == head)
+static int
+nfs_flush_list(struct inode *inode, struct list_head *head, int how)
+{
+	LIST_HEAD(one_request);
+	struct nfs_page		*req;
+	int			error = 0;
+	unsigned int		pages = 0,
+				wpages = NFS_SERVER(inode)->wpages;
+
+	while (!list_empty(head)) {
+		pages += nfs_coalesce_requests(head, &one_request, wpages);
+		req = nfs_list_entry(one_request.next);
+		error = nfs_flush_one(&one_request, req->wb_dentry, how);
+		if (error < 0)
 			break;
 	}
+	if (error >= 0)
+		return pages;
+
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_dirty(req);
+		nfs_unlock_request(req);
+	}
+	return error;
 }
 
+
 /*
- * If we're waiting on somebody else's request
- * we need to increment the counter during the
- * wait so that the request doesn't disappear
- * from under us during the wait..
+ * This function is called when the WRITE call is complete.
  */
-static int FASTCALL(wait_on_other_req(struct nfs_wreq *));
-static int wait_on_other_req(struct nfs_wreq *req)
+static void
+nfs_writeback_done(struct rpc_task *task)
 {
-	int retval;
-	req->wb_count++;
-	retval = wait_on_write_request(req);
-	free_write_request(req);
-	return retval;
-}
+	struct nfs_write_data	*data = (struct nfs_write_data *) task->tk_calldata;
+	struct nfs_writeargs	*argp = &data->args;
+	struct nfs_writeres	*resp = &data->res;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
+	struct nfs_page		*req;
+	struct page		*page;
 
-/*
- * This writes back a set of requests according to the condition.
- *
- * If this ever gets much more convoluted, use a fn pointer for
- * the condition..
- */
-#define NFS_WB(inode, cond) { int retval = 0 ; \
-	do { \
-		struct nfs_wreq *req = NFS_WRITEBACK(inode); \
-		struct nfs_wreq *head = req; \
-		if (!req) break; \
-		for (;;) { \
-			if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \
-				if (cond) break; \
-			req = WB_NEXT(req); \
-			if (req == head) goto out; \
-		} \
-		retval = wait_on_other_req(req); \
-	} while (!retval); \
-out:	return retval; \
-}
+	dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
+		task->tk_pid, task->tk_status);
 
-int
-nfs_wb_all(struct inode *inode)
-{
-	NFS_WB(inode, 1);
+	/* We can't handle that yet but we check for it nevertheless */
+	if (resp->count < argp->count && task->tk_status >= 0) {
+		static unsigned long    complain = 0;
+		if (time_before(complain, jiffies)) {
+			printk(KERN_WARNING
+			       "NFS: Server wrote less than requested.\n");
+			complain = jiffies + 300 * HZ;
+		}
+		/* Can't do anything about it right now except throw
+		 * an error. */
+		task->tk_status = -EIO;
+	}
+#ifdef CONFIG_NFS_V3
+	if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
+		/* We tried a write call, but the server did not
+		 * commit data to stable storage even though we
+		 * requested it.
+		 */
+		static unsigned long    complain = 0;
+
+		if (time_before(complain, jiffies)) {
+			dprintk("NFS: faulty NFSv3 server %s:"
+				" (committed = %d) != (stable = %d)\n",
+				NFS_SERVER(inode)->hostname,
+				resp->verf->committed, argp->stable);
+			complain = jiffies + 300 * HZ;
+		}
+	}
+#endif
+
+	/* Update attributes as result of writeback. */
+	nfs_write_attributes(inode, resp->fattr);
+
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+		page = req->wb_page;
+
+		dprintk("NFS: write (%s/%s %d@%Ld)",
+			req->wb_dentry->d_parent->d_name.name,
+			req->wb_dentry->d_name.name,
+			req->wb_bytes,
+			(long long)(nfs_page_offset(page) + req->wb_offset));
+
+		if (task->tk_status < 0) {
+			clear_bit(PG_uptodate, &page->flags);
+			set_bit(PG_error, &page->flags);
+			nfs_inode_remove_request(req);
+			if (req->wb_file)
+				req->wb_file->f_error = task->tk_status;
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
+
+#ifdef CONFIG_NFS_V3
+		if (resp->verf->committed != NFS_UNSTABLE) {
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+
+		memcpy(&req->wb_verf, resp->verf, sizeof(req->wb_verf));
+		req->wb_timeout = jiffies + NFS_COMMIT_DELAY;
+		nfs_mark_request_commit(req);
+		dprintk(" marked for commit\n");
+#else
+		nfs_inode_remove_request(req);
+#endif
+	next:
+		nfs_unlock_request(req);
+	}
 }
 
+#ifdef CONFIG_NFS_V3
 /*
- * Write back all requests on one page - we do this before reading it.
+ * Set up the argument/result storage required for the RPC call.
  */
-int
-nfs_wb_page(struct inode *inode, struct page *page)
+static void
+nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 {
-	NFS_WB(inode, req->wb_page == page);
+	struct nfs_page		*req;
+	struct dentry		*dentry;
+	struct inode		*inode;
+	unsigned long		start, end, len;
+
+	/* Set up the RPC argument and reply structs
+	 * NB: take care not to mess about with data->commit et al. */
+
+	end = 0;
+	start = ~0;
+	req = nfs_list_entry(head->next);
+	dentry = req->wb_dentry;
+	data->dentry = dentry;
+	data->cred = req->wb_cred;
+	inode = dentry->d_inode;
+	while (!list_empty(head)) {
+		struct nfs_page	*req;
+		unsigned long	rqstart, rqend;
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_list_add_request(req, &data->pages);
+		rqstart = nfs_page_offset(req->wb_page) + req->wb_offset;
+		rqend = rqstart + req->wb_bytes;
+		if (rqstart < start)
+			start = rqstart;
+		if (rqend > end)
+			end = rqend;
+	}
+	data->args.fh     = NFS_FH(dentry);
+	data->args.offset = start;
+	len = end - start;
+	if (end >= inode->i_size || len > (~((u32)0) >> 1))
+		len = 0;
+	data->res.count   = data->args.count = (u32)len;
+	data->res.fattr   = &data->fattr;
+	data->res.verf    = &data->verf;
 }
 
 /*
- * Write back all pending writes from one file descriptor..
+ * Commit dirty pages
  */
-int
-nfs_wb_file(struct inode *inode, struct file *file)
-{
-	NFS_WB(inode, req->wb_file == file);
-}
-
-void
-nfs_inval(struct inode *inode)
+static int
+nfs_commit_list(struct list_head *head, int how)
 {
-	nfs_cancel_dirty(inode,0);
+	struct rpc_message	msg;
+	struct rpc_clnt		*clnt;
+	struct nfs_write_data	*data;
+	struct rpc_task         *task;
+	struct nfs_page         *req;
+	int                     flags,
+				async = !(how & FLUSH_SYNC);
+	sigset_t		oldset;
+
+	data = nfs_writedata_alloc();
+	if (!data)
+		goto out_bad;
+	task = &data->task;
+
+	flags = (async) ? RPC_TASK_ASYNC : 0;
+
+	/* Set up the argument struct */
+	nfs_commit_rpcsetup(head, data);
+	req = nfs_list_entry(data->pages.next);
+	clnt = NFS_CLIENT(req->wb_dentry->d_inode);
+
+	rpc_init_task(task, clnt, nfs_commit_done, flags);
+	task->tk_calldata = data;
+	task->tk_release = nfs_writedata_release;
+
+	msg.rpc_proc = NFS3PROC_COMMIT;
+	msg.rpc_argp = &data->args;
+	msg.rpc_resp = &data->res;
+	msg.rpc_cred = data->cred;
+
+	dprintk("NFS: %4d initiated commit call\n", task->tk_pid);
+	rpc_clnt_sigmask(clnt, &oldset);
+	rpc_call_setup(task, &msg, 0);
+	rpc_execute(task);
+	rpc_clnt_sigunmask(clnt, &oldset);
+	return 0;
+ out_bad:
+	while (!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_mark_request_commit(req);
+		nfs_unlock_request(req);
+	}
+	return -ENOMEM;
 }
 
 /*
- * The following procedures make up the writeback finite state machinery:
- *
- * 1.	Try to lock the page if not yet locked by us,
- *	set up the RPC call info, and pass to the call FSM.
+ * COMMIT call returned
  */
 static void
-nfs_wback_begin(struct rpc_task *task)
+nfs_commit_done(struct rpc_task *task)
 {
-	struct nfs_wreq	*req = (struct nfs_wreq *) task->tk_calldata;
-	struct page	*page = req->wb_page;
-	struct file	*file = req->wb_file;
-	struct dentry	*dentry = file->f_dentry;
+	struct nfs_write_data	*data = (struct nfs_write_data *)task->tk_calldata;
+	struct nfs_writeres	*resp = &data->res;
+	struct nfs_page		*req;
+	struct dentry		*dentry = data->dentry;
+	struct inode		*inode = dentry->d_inode;
 
-	dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, task->tk_status, req->wb_flags);
+        dprintk("NFS: %4d nfs_commit_done (status %d)\n",
+                                task->tk_pid, task->tk_status);
 
-	task->tk_status = 0;
+	nfs_write_attributes(inode, resp->fattr);
+	while (!list_empty(&data->pages)) {
+		req = nfs_list_entry(data->pages.next);
+		nfs_list_remove_request(req);
+
+		dprintk("NFS: commit (%s/%s %d@%ld)",
+			req->wb_dentry->d_parent->d_name.name,
+			req->wb_dentry->d_name.name,
+			req->wb_bytes,
+			nfs_page_offset(req->wb_page) + req->wb_offset);
+		if (task->tk_status < 0) {
+			if (req->wb_file)
+				req->wb_file->f_error = task->tk_status;
+			nfs_inode_remove_request(req);
+			dprintk(", error = %d\n", task->tk_status);
+			goto next;
+		}
 
-	/* Setup the task struct for a writeback call */
-	req->wb_flags |= NFS_WRITE_INPROGRESS;
-	req->wb_args.fh     = NFS_FH(dentry);
-	req->wb_args.offset = page->offset + req->wb_offset;
-	req->wb_args.count  = req->wb_bytes;
-	req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset);
+		/* Okay, COMMIT succeeded, apparently. Check the verifier
+		 * returned by the server against all stored verfs. */
+		if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
+			/* We have a match */
+			nfs_inode_remove_request(req);
+			dprintk(" OK\n");
+			goto next;
+		}
+		/* We have a mismatch. Write the page again */
+		dprintk(" mismatch\n");
+		nfs_mark_request_dirty(req);
+	next:
+		nfs_unlock_request(req);
+	}
+}
+#endif
 
-	rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0);
+int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		   unsigned int npages, int how)
+{
+	LIST_HEAD(head);
+	int			res,
+				error = 0;
 
-	return;
+	res = nfs_scan_dirty(inode, &head, file, idx_start, npages);
+	if (res)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return res;
 }
 
-/*
- * 2.	Collect the result
- */
-static void
-nfs_wback_result(struct rpc_task *task)
+int nfs_flush_timeout(struct inode *inode, int how)
 {
-	struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
-	struct file	*file = req->wb_file;
-	struct page	*page = req->wb_page;
-	int		status = task->tk_status;
-	struct dentry	*dentry = file->f_dentry;
-	struct inode	*inode = dentry->d_inode;
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
 
-	dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n",
-		task->tk_pid, dentry->d_parent->d_name.name,
-		dentry->d_name.name, status, req->wb_flags);
-
-	/* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
-	req->wb_flags |= NFS_WRITE_COMPLETE;
-	req->wb_status = status;
-
-	if (status < 0) {
-		req->wb_flags |= NFS_WRITE_INVALIDATE;
-		file->f_error = status;
-	} else if (!WB_CANCELLED(req)) {
-		struct nfs_fattr *fattr = &req->wb_fattr;
-		/* Update attributes as result of writeback. 
-		 * Beware: when UDP replies arrive out of order, we
-		 * may end up overwriting a previous, bigger file size.
-		 *
-		 * When the file size shrinks we cancel all pending
-		 * writebacks. 
-		 */
-		if (fattr->mtime.seconds >= inode->i_mtime) {
-			if (fattr->size < inode->i_size)
-				fattr->size = inode->i_size;
-
-			/* possible Solaris 2.5 server bug workaround */
-			if (inode->i_ino == fattr->fileid) {
-				/*
-				 * We expect these values to change, and
-				 * don't want to invalidate the caches.
-				 */
-				inode->i_size  = fattr->size;
-				inode->i_mtime = fattr->mtime.seconds;
-				nfs_refresh_inode(inode, fattr);
-			}
-			else
-				printk("nfs_wback_result: inode %ld, got %u?\n",
-					inode->i_ino, fattr->fileid);
-		}
+	pages = nfs_scan_dirty_timeout(inode, &head);
+	if (pages)
+		error = nfs_flush_list(inode, &head, how);
+	if (error < 0)
+		return error;
+	return pages;
+}
+
+#ifdef CONFIG_NFS_V3
+int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		    unsigned int npages, int how)
+{
+	LIST_HEAD(head);
+	int			res,
+				error = 0;
+
+	res = nfs_scan_commit(inode, &head, file, idx_start, npages);
+	if (res)
+		error = nfs_commit_list(&head, how);
+	if (error < 0)
+		return error;
+	return res;
+}
+
+int nfs_commit_timeout(struct inode *inode, int how)
+{
+	LIST_HEAD(head);
+	int			pages,
+				error = 0;
+
+	pages = nfs_scan_commit_timeout(inode, &head);
+	if (pages) {
+		pages += nfs_scan_commit(inode, &head, NULL, 0, 0);
+		error = nfs_commit_list(&head, how);
 	}
+	if (error < 0)
+		return error;
+	return pages;
+}
+#endif
 
-	rpc_release_task(task);
+int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start,
+		  unsigned int npages, int how)
+{
+	int	error,
+		wait;
 
-	if (WB_INVALIDATE(req))
-		clear_bit(PG_uptodate, &page->flags);
+	wait = how & FLUSH_WAIT;
+	how &= ~FLUSH_WAIT;
 
-	__free_page(page);
-	remove_write_request(&NFS_WRITEBACK(inode), req);
-	nr_write_requests--;
-	fput(req->wb_file);
+	if (!inode && file)
+		inode = file->f_dentry->d_inode;
 
-	wake_up(&req->wb_wait);
-	free_write_request(req);
+	do {
+		error = 0;
+		if (wait)
+			error = nfs_wait_on_requests(inode, file, idx_start, npages);
+		if (error == 0)
+			error = nfs_flush_file(inode, file, idx_start, npages, how);
+#ifdef CONFIG_NFS_V3
+		if (error == 0)
+			error = nfs_commit_file(inode, file, idx_start, npages, how);
+#endif
+	} while (error > 0);
+	return error;
 }
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/Makefile linux.18p17/fs/nfsd/Makefile
--- linux.vanilla/fs/nfsd/Makefile	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/Makefile	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/export.c linux.18p17/fs/nfsd/export.c
--- linux.vanilla/fs/nfsd/export.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/export.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/lockd.c linux.18p17/fs/nfsd/lockd.c
--- linux.vanilla/fs/nfsd/lockd.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/lockd.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfs3proc.c linux.18p17/fs/nfsd/nfs3proc.c
--- linux.vanilla/fs/nfsd/nfs3proc.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfs3proc.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfs3xdr.c linux.18p17/fs/nfsd/nfs3xdr.c
--- linux.vanilla/fs/nfsd/nfs3xdr.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfs3xdr.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfscache.c linux.18p17/fs/nfsd/nfscache.c
--- linux.vanilla/fs/nfsd/nfscache.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfscache.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfsctl.c linux.18p17/fs/nfsd/nfsctl.c
--- linux.vanilla/fs/nfsd/nfsctl.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfsctl.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfsfh.c linux.18p17/fs/nfsd/nfsfh.c
--- linux.vanilla/fs/nfsd/nfsfh.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfsfh.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfsproc.c linux.18p17/fs/nfsd/nfsproc.c
--- linux.vanilla/fs/nfsd/nfsproc.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfsproc.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfssvc.c linux.18p17/fs/nfsd/nfssvc.c
--- linux.vanilla/fs/nfsd/nfssvc.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfssvc.c	Thu Oct 19 01:13:04 2000
@@ -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;
@@ -81,11 +76,11 @@
 	if (error < 0)
 		goto failure;
 
-#if 0	/* Don't even pretend that TCP works. It doesn't. */
 	error = svc_makesock(serv, IPPROTO_TCP, port);
 	if (error < 0)
 		goto failure;
-#endif
+
+	nfsd_racache_init();	/* Readahead param cache */
 
 	while (nrservs--) {
 		error = svc_create_thread(nfsd, serv);
@@ -106,27 +101,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,15 +138,12 @@
 		 * recvfrom routine.
 		 */
 		while ((err = svc_recv(serv, rqstp,
-		        first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
-			if (first && 1) {
-				exp_readlock();
-				expire_all();
-				exp_unlock();
-			}
-		}
-		if (err < 0)
-			break;
+				       MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
+		    ;
+
+                if (err < 0)
+                        break;
+
 
 		/* Lock the export hash tables for reading. */
 		exp_readlock();
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/nfsxdr.c linux.18p17/fs/nfsd/nfsxdr.c
--- linux.vanilla/fs/nfsd/nfsxdr.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/nfsxdr.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/stats.c linux.18p17/fs/nfsd/stats.c
--- linux.vanilla/fs/nfsd/stats.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/stats.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nfsd/vfs.c linux.18p17/fs/nfsd/vfs.c
--- linux.vanilla/fs/nfsd/vfs.c	Fri Apr 21 12:46:44 2000
+++ linux.18p17/fs/nfsd/vfs.c	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/Config.in linux.18p17/fs/nls/Config.in
--- linux.vanilla/fs/nls/Config.in	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/Config.in	Mon Sep 11 17:07:03 2000
@@ -4,7 +4,8 @@
 
 # msdos and Joliet want NLS
 if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
-	-o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" ]; then
+	-o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" \
+	-o "$CONFIG_SMB_FS" != "n" ]; then
   define_bool CONFIG_NLS y
 else
   define_bool CONFIG_NLS n
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_base.c linux.18p17/fs/nls/nls_base.c
--- linux.vanilla/fs/nls/nls_base.c	Sat Sep  9 18:42:47 2000
+++ linux.18p17/fs/nls/nls_base.c	Mon Oct  2 10:17:08 2000
@@ -380,7 +380,7 @@
 	return;
 }
 
-void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp437.c linux.18p17/fs/nls/nls_cp437.c
--- linux.vanilla/fs/nls/nls_cp437.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp437.c	Mon Oct  2 10:17:08 2000
@@ -409,7 +409,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp737.c linux.18p17/fs/nls/nls_cp737.c
--- linux.vanilla/fs/nls/nls_cp737.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp737.c	Mon Oct  2 10:17:08 2000
@@ -337,7 +337,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp775.c linux.18p17/fs/nls/nls_cp775.c
--- linux.vanilla/fs/nls/nls_cp775.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp775.c	Mon Oct  2 10:17:08 2000
@@ -337,7 +337,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp850.c linux.18p17/fs/nls/nls_cp850.c
--- linux.vanilla/fs/nls/nls_cp850.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp850.c	Mon Oct  2 10:17:08 2000
@@ -301,7 +301,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp852.c linux.18p17/fs/nls/nls_cp852.c
--- linux.vanilla/fs/nls/nls_cp852.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp852.c	Mon Oct  2 10:17:08 2000
@@ -301,7 +301,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp855.c linux.18p17/fs/nls/nls_cp855.c
--- linux.vanilla/fs/nls/nls_cp855.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp855.c	Mon Oct  2 10:17:08 2000
@@ -301,7 +301,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp857.c linux.18p17/fs/nls/nls_cp857.c
--- linux.vanilla/fs/nls/nls_cp857.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp857.c	Mon Oct  2 10:17:08 2000
@@ -265,7 +265,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp860.c linux.18p17/fs/nls/nls_cp860.c
--- linux.vanilla/fs/nls/nls_cp860.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp860.c	Mon Oct  2 10:17:08 2000
@@ -373,7 +373,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp861.c linux.18p17/fs/nls/nls_cp861.c
--- linux.vanilla/fs/nls/nls_cp861.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp861.c	Mon Oct  2 10:17:08 2000
@@ -409,7 +409,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp862.c linux.18p17/fs/nls/nls_cp862.c
--- linux.vanilla/fs/nls/nls_cp862.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp862.c	Mon Oct  2 10:17:08 2000
@@ -445,7 +445,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp863.c linux.18p17/fs/nls/nls_cp863.c
--- linux.vanilla/fs/nls/nls_cp863.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp863.c	Mon Oct  2 10:17:08 2000
@@ -409,7 +409,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp864.c linux.18p17/fs/nls/nls_cp864.c
--- linux.vanilla/fs/nls/nls_cp864.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp864.c	Mon Oct  2 10:17:08 2000
@@ -400,7 +400,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp865.c linux.18p17/fs/nls/nls_cp865.c
--- linux.vanilla/fs/nls/nls_cp865.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp865.c	Mon Oct  2 10:17:08 2000
@@ -409,7 +409,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp866.c linux.18p17/fs/nls/nls_cp866.c
--- linux.vanilla/fs/nls/nls_cp866.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp866.c	Mon Oct  2 10:17:08 2000
@@ -337,7 +337,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp869.c linux.18p17/fs/nls/nls_cp869.c
--- linux.vanilla/fs/nls/nls_cp869.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp869.c	Mon Oct  2 10:17:08 2000
@@ -301,7 +301,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp874.c linux.18p17/fs/nls/nls_cp874.c
--- linux.vanilla/fs/nls/nls_cp874.c	Sun Jun 11 21:44:19 2000
+++ linux.18p17/fs/nls/nls_cp874.c	Mon Oct  2 10:17:08 2000
@@ -265,7 +265,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp932.c linux.18p17/fs/nls/nls_cp932.c
--- linux.vanilla/fs/nls/nls_cp932.c	Sat Sep  9 18:42:48 2000
+++ linux.18p17/fs/nls/nls_cp932.c	Mon Oct  2 10:17:08 2000
@@ -9806,55 +9806,74 @@
 	NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
 	NULL,   u2c_F9, u2c_FA, NULL,   NULL,   NULL,   NULL,   u2c_FF, };
 
-static void uni2char(unsigned char ch, unsigned char cl, unsigned char *out, int boundlen, int *outlen)
+static void uni2char(const unsigned char ch, unsigned char cl, unsigned char *out, int boundlen, int *outlen)
 {
 	unsigned char *uni2charset;
 
 	if (boundlen <= 0)
 		return;
-	if (ch == 0xFF && 0x61 <= cl && cl <= 0x9F){
+	if (ch == 0 && cl < 0x80) {
+		out[0] = cl;
+		*outlen = 1;
+		return;
+	}
+	else if (ch == 0xFF && 0x61 <= cl && cl <= 0x9F) {
 		out[0] = cl + 0x40;
 		*outlen = 1;
 		return;
 	}
+
 	uni2charset = page_uni2charset[ch];
-	*outlen = 0;
-	if (uni2charset){
+	if (uni2charset) {
 		if (boundlen <= 1)
 			return;
 		out[0] = uni2charset[cl*2];
 		out[1] = uni2charset[cl*2+1];
-		*outlen = 1;
-	} else if (ch==0 && cl)
+		*outlen = 2;
+	}
+	/*
+	else if (ch == 0 && cl) {
 		out[0] = cl;
-	else
+		*outlen = 1;
+	}
+	*/
+	else {
 		out[0] = '?';
-	(*outlen)++;
+		*outlen = 1;
+	}
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	unsigned char ch, cl;
 	struct nls_unicode *charset2uni;
 
 	ch = rawstring[0];
 	cl = rawstring[1];
-	if (0xA1 <= ch && ch <= 0xDF){
+	if (ch < 0x80) {
+		*uni1 = ch;
+		*uni2 = 0x00;
+		*offset = 1;
+		return;
+	}
+	else if (0xA1 <= ch && ch <= 0xDF) {
 		*uni1 = ch - 0x40;
 		*uni2 = 0xFF;
 		*offset = 1;
 		return;
 	}
+
 	charset2uni = page_charset2uni[ch];
-	if (charset2uni && cl){
+	*offset = 2;
+	if (charset2uni) {
 		*uni1 = charset2uni[cl].uni2;
 		*uni2 = charset2uni[cl].uni1;
-		*offset = 2;
-	} else{
-		*uni1 = ch;
+	}
+	else {
+		/* *uni1 = ch; */
+		*uni1 = '?';
 		*uni2 = 0x00;
-		*offset = 1;
 	}
 	return;
 }
@@ -9891,7 +9910,12 @@
 {
 	if (boundlen <= 0)
 		return;
-	if (ch == 0xFF && 0x61 <= cl && cl <= 0x9F){
+	if (ch == 0 && cl < 0x80) {
+		out[0] = cl;
+		*outlen = 1;
+		return;
+	}
+	else if (ch == 0xFF && 0x61 <= cl && cl <= 0x9F) {
 		if (boundlen <= 1)
 			return;
 		out[0] = 0x8E; /* EUCHANKAKU */
@@ -9899,6 +9923,8 @@
 		*outlen = 2;
 		return;
 	}
+
+	*outlen = 0;
 	uni2char(ch, cl, out, boundlen, outlen);
 	if (*outlen == 2) {
 		/* translate SJIS into EUC-JP */ 
@@ -9913,30 +9939,46 @@
 		else
 			out[1] = out[1] + 0x02;
 	}
+	return;
 }
 
-static void char2uni_euc_jp(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni_euc_jp(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
+	unsigned char ch, cl;
 	unsigned char sjis_temp[2];
 
-	if (rawstring[0] == 0x8E) {
-		if (0xA1 <= rawstring[1] && rawstring[1] <= 0xDF){
-			*uni1 = rawstring[1] - 0x40;
+	ch = rawstring[0];
+	cl = rawstring[1];
+	if (ch < 0x80) {
+		*uni1 = ch;
+		*uni2 = 0x00;
+		*offset = 1;
+		return;
+	}
+	else if (ch == 0x8E) {
+		if (0xA1 <= cl && cl <= 0xDF){
+			*uni1 = cl - 0x40;
 			*uni2 = 0xFF;
-			*offset = 2;
-			return;
 		}
+		else {
+			*uni1 = '?';
+			*uni2 = 0x00;
+		}
+		*offset = 2;
+		return;
 	}
+
 	/* translate EUC-JP into SJIS */
-	sjis_temp[0] = ((rawstring[0]-0x5f)/2) ^ 0xA0;
-	if (!(rawstring[0]&1))
-		sjis_temp[1] = rawstring[1] - 0x02;
-	else if (rawstring[1] < 0xE0)
-		sjis_temp[1] = rawstring[1] - 0x61;
+	sjis_temp[0] = ((ch-0x5f)/2) ^ 0xA0;
+	if (!(ch&1))
+		sjis_temp[1] = cl - 0x02;
+	else if (cl < 0xE0)
+		sjis_temp[1] = cl - 0x61;
 	else
-		sjis_temp[1] = rawstring[1] - 0x60;
+		sjis_temp[1] = cl - 0x60;
 
 	char2uni(sjis_temp, offset, uni1, uni2);
+	return;
 }
 
 static struct nls_table euc_jp_table = {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp936.c linux.18p17/fs/nls/nls_cp936.c
--- linux.vanilla/fs/nls/nls_cp936.c	Sat Sep  9 18:42:49 2000
+++ linux.18p17/fs/nls/nls_cp936.c	Mon Oct  2 10:17:08 2000
@@ -15609,7 +15609,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	unsigned char ch, cl;
 	struct nls_unicode *charset2uni;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp949.c linux.18p17/fs/nls/nls_cp949.c
--- linux.vanilla/fs/nls/nls_cp949.c	Sat Sep  9 18:42:49 2000
+++ linux.18p17/fs/nls/nls_cp949.c	Mon Oct  2 10:17:08 2000
@@ -18465,7 +18465,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	unsigned char ch, cl;
 	struct nls_unicode *charset2uni;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_cp950.c linux.18p17/fs/nls/nls_cp950.c
--- linux.vanilla/fs/nls/nls_cp950.c	Sat Sep  9 18:42:49 2000
+++ linux.18p17/fs/nls/nls_cp950.c	Mon Oct  2 10:17:08 2000
@@ -12753,7 +12753,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	unsigned char ch, cl;
 	struct nls_unicode *charset2uni;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-1.c linux.18p17/fs/nls/nls_iso8859-1.c
--- linux.vanilla/fs/nls/nls_iso8859-1.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-1.c	Mon Oct  2 10:17:08 2000
@@ -189,7 +189,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-14.c linux.18p17/fs/nls/nls_iso8859-14.c
--- linux.vanilla/fs/nls/nls_iso8859-14.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-14.c	Mon Oct  2 10:17:08 2000
@@ -236,7 +236,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-15.c linux.18p17/fs/nls/nls_iso8859-15.c
--- linux.vanilla/fs/nls/nls_iso8859-15.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-15.c	Mon Oct  2 10:17:08 2000
@@ -229,7 +229,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-2.c linux.18p17/fs/nls/nls_iso8859-2.c
--- linux.vanilla/fs/nls/nls_iso8859-2.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-2.c	Mon Oct  2 10:17:08 2000
@@ -261,7 +261,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-3.c linux.18p17/fs/nls/nls_iso8859-3.c
--- linux.vanilla/fs/nls/nls_iso8859-3.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-3.c	Mon Oct  2 10:17:08 2000
@@ -261,7 +261,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-4.c linux.18p17/fs/nls/nls_iso8859-4.c
--- linux.vanilla/fs/nls/nls_iso8859-4.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-4.c	Mon Oct  2 10:17:08 2000
@@ -261,7 +261,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-5.c linux.18p17/fs/nls/nls_iso8859-5.c
--- linux.vanilla/fs/nls/nls_iso8859-5.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-5.c	Mon Oct  2 10:17:08 2000
@@ -265,7 +265,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-6.c linux.18p17/fs/nls/nls_iso8859-6.c
--- linux.vanilla/fs/nls/nls_iso8859-6.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-6.c	Mon Oct  2 10:17:08 2000
@@ -225,7 +225,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-7.c linux.18p17/fs/nls/nls_iso8859-7.c
--- linux.vanilla/fs/nls/nls_iso8859-7.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-7.c	Mon Oct  2 10:17:08 2000
@@ -301,7 +301,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-8.c linux.18p17/fs/nls/nls_iso8859-8.c
--- linux.vanilla/fs/nls/nls_iso8859-8.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-8.c	Mon Oct  2 10:17:08 2000
@@ -265,7 +265,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_iso8859-9.c linux.18p17/fs/nls/nls_iso8859-9.c
--- linux.vanilla/fs/nls/nls_iso8859-9.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_iso8859-9.c	Mon Oct  2 10:17:08 2000
@@ -225,7 +225,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/nls/nls_koi8-r.c linux.18p17/fs/nls/nls_koi8-r.c
--- linux.vanilla/fs/nls/nls_koi8-r.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/nls/nls_koi8-r.c	Mon Oct  2 10:17:08 2000
@@ -337,7 +337,7 @@
 	return;
 }
 
-static void char2uni(unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
+static void char2uni(const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2)
 {
 	*uni1 = charset2uni[*rawstring].uni1;
 	*uni2 = charset2uni[*rawstring].uni2;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ntfs/support.c linux.18p17/fs/ntfs/support.c
--- linux.vanilla/fs/ntfs/support.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/ntfs/support.c	Mon Sep 11 17:49:08 2000
@@ -298,7 +298,7 @@
 	if(!result)return ENOMEM;
 	*out_len=in_len;
 	for(i=o=0;i<in_len;i++, o++){
-		unsigned short cl,ch;
+		unsigned short cl=0,ch=0;
 		if(in[i]!=':' || (vol->nct & nct_uni_xlate)==0){
 			int len;
 			unsigned char clc=cl, chc=ch;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/proc/root.c linux.18p17/fs/proc/root.c
--- linux.vanilla/fs/proc/root.c	Fri Apr 21 12:46:45 2000
+++ linux.18p17/fs/proc/root.c	Tue Sep 26 21:48:57 2000
@@ -29,7 +29,6 @@
 
 static int proc_root_readdir(struct file *, void *, filldir_t);
 static struct dentry *proc_root_lookup(struct inode *,struct dentry *);
-static int proc_unlink(struct inode *, struct dentry *);
 
 static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
 
@@ -86,7 +85,7 @@
 	NULL,			/* create */
 	proc_lookup,		/* lookup */
 	NULL,			/* link	*/
-	proc_unlink,		/* unlink(struct inode *, struct dentry *) */
+	NULL, 			/* unlink(struct inode *, struct dentry *) */
 	NULL,			/* symlink	*/
 	NULL,			/* mkdir */
 	NULL,			/* rmdir */
@@ -988,17 +987,5 @@
 			break;
 		filp->f_pos++;
 	}
-	return 0;
-}
-
-static int proc_unlink(struct inode *dir, struct dentry *dentry)
-{
-	struct proc_dir_entry * dp = dir->u.generic_ip;
-
-printk("proc_file_unlink: deleting %s/%s\n", dp->name, dentry->d_name.name);
-
-	remove_proc_entry(dentry->d_name.name, dp);
-	dentry->d_inode->i_nlink = 0;
-	d_delete(dentry);
 	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/ChangeLog linux.18p17/fs/smbfs/ChangeLog
--- linux.vanilla/fs/smbfs/ChangeLog	Sat Sep  9 18:42:49 2000
+++ linux.18p17/fs/smbfs/ChangeLog	Sat Sep  2 23:33:56 2000
@@ -1,5 +1,15 @@
 ChangeLog for smbfs.
 
+2000-09-01 Urban Widmark <urban@svenskatest.se>
+
+	* proc.c: add back lanman2 support (OS/2 and others)
+	* proc.c: check length of paths to avoid buffer overflow
+	* proc.c: don't do interruptable_sleep in smb_retry to avoid signal
+	  problem/race.
+	* proc.c: O_RDONLY & smb_revalidate_inode fix (tail -f)
+	* proc.c: add nls support
+	* sock.c: attempt to fix smb_data_callback (avoid infinite loop)
+
 2000-07-20 Urban Widmark <urban@svenskatest.se>
 
 	* proc.c: fix 2 places where bad server responses could cause an Oops.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/dir.c linux.18p17/fs/smbfs/dir.c
--- linux.vanilla/fs/smbfs/dir.c	Sat Sep  9 18:42:49 2000
+++ linux.18p17/fs/smbfs/dir.c	Sat Sep  2 23:33:56 2000
@@ -167,7 +167,7 @@
 }
 
 /*
- * Note: in order to allow the smbclient process to open the
+ * Note: in order to allow the smbmount process to open the
  * mount point, we don't revalidate if conn_pid is NULL.
  */
 static int
@@ -194,10 +194,10 @@
 
 static struct dentry_operations smbfs_dentry_operations =
 {
-	smb_lookup_validate,	/* d_revalidate(struct dentry *) */
-	smb_hash_dentry,	/* d_hash */
-	smb_compare_dentry,	/* d_compare */
-	smb_delete_dentry	/* d_delete(struct dentry *) */
+	d_revalidate:	smb_lookup_validate,
+	d_hash:		smb_hash_dentry,
+	d_compare:	smb_compare_dentry,
+	d_delete:	smb_delete_dentry
 };
 
 /*
@@ -256,8 +256,7 @@
 
 	if (a->len != b->len)
 		goto out;
-	for (i=0; i < a->len; i++)
-	{
+	for (i=0; i < a->len; i++) {
 		if (tolower(a->name[i]) != tolower(b->name[i]))
 			goto out;
 	}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/inode.c linux.18p17/fs/smbfs/inode.c
--- linux.vanilla/fs/smbfs/inode.c	Sat Sep  9 18:42:50 2000
+++ linux.18p17/fs/smbfs/inode.c	Mon Sep 11 17:07:03 2000
@@ -21,6 +21,7 @@
 #include <linux/init.h>
 #include <linux/file.h>
 #include <linux/dcache.h>
+#include <linux/nls.h>
 
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
@@ -31,6 +32,13 @@
 
 #include "smb_debug.h"
 
+/* Always pick a default string */
+#ifdef CONFIG_SMB_NLS_REMOTE
+#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE
+#else
+#define SMB_NLS_REMOTE ""
+#endif
+
 static void smb_read_inode(struct inode *);
 static void smb_put_inode(struct inode *);
 static void smb_delete_inode(struct inode *);
@@ -320,13 +328,22 @@
 	if (server->packet)
 		smb_vfree(server->packet);
 
+	if(sb->u.smbfs_sb.remote_nls) {
+		unload_nls(sb->u.smbfs_sb.remote_nls);
+		sb->u.smbfs_sb.remote_nls = NULL;
+	}
+	if(sb->u.smbfs_sb.local_nls) {
+		unload_nls(sb->u.smbfs_sb.local_nls);
+		sb->u.smbfs_sb.local_nls = NULL;
+	}
+
 	MOD_DEC_USE_COUNT;
 }
 
 struct super_block *
 smb_read_super(struct super_block *sb, void *raw_data, int silent)
 {
-	struct smb_mount_data *mnt;
+	struct smb_mount_data_kernel *mnt;
 	struct inode *root_inode;
 	struct smb_fattr root;
 
@@ -357,22 +374,36 @@
 		goto out_no_mem;
 
 	/* Allocate the global temp buffer */
-	sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL);
+	sb->u.smbfs_sb.temp_buf = kmalloc(2*SMB_MAXPATHLEN + 20, GFP_KERNEL);
 	if (!sb->u.smbfs_sb.temp_buf)
 		goto out_no_temp;
 
+	/* Setup NLS stuff */
+	sb->u.smbfs_sb.remote_nls = NULL;
+	sb->u.smbfs_sb.local_nls = NULL;
+	sb->u.smbfs_sb.name_buf = sb->u.smbfs_sb.temp_buf + SMB_MAXPATHLEN + 20;
+
 	/* Allocate the mount data structure */
-	mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
+	mnt = kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
 	if (!mnt)
 		goto out_no_mount;
-	*mnt = *((struct smb_mount_data *) raw_data);
+	memcpy(mnt, raw_data, sizeof(struct smb_mount_data));
+	strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
+		SMB_NLS_MAXNAMELEN);
+	strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
+		SMB_NLS_MAXNAMELEN);
+
 	/* FIXME: ** temp ** pass config flags in file mode */
-	mnt->version = (mnt->file_mode >> 9);
+	mnt->flags = (mnt->file_mode >> 9);
 	mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
 	mnt->file_mode |= S_IFREG;
 	mnt->dir_mode  &= (S_IRWXU | S_IRWXG | S_IRWXO);
 	mnt->dir_mode  |= S_IFDIR;
 	sb->u.smbfs_sb.mnt = mnt;
+
+	smb_setcodepage(&sb->u.smbfs_sb, &mnt->codepage);
+	if (!sb->u.smbfs_sb.convert)
+		PARANOIA("convert funcptr was NULL!\n");
 
 	/*
 	 * Keep the super block locked while we get the root inode.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/proc.c linux.18p17/fs/smbfs/proc.c
--- linux.vanilla/fs/smbfs/proc.c	Sat Sep  9 18:42:50 2000
+++ linux.18p17/fs/smbfs/proc.c	Sat Sep  2 23:33:56 2000
@@ -16,6 +16,7 @@
 #include <linux/fcntl.h>
 #include <linux/dcache.h>
 #include <linux/dirent.h>
+#include <linux/nls.h>
 
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
@@ -48,6 +49,19 @@
 		    struct smb_sb_info *server);
 
 
+static inline void
+smb_lock_server(struct smb_sb_info *server)
+{
+	down(&(server->sem));
+}
+
+static inline void
+smb_unlock_server(struct smb_sb_info *server)
+{
+	up(&(server->sem));
+}
+
+
 /* reverse a string inline. This is used by the dircache walking routines */
 static void reverse_string(char *buf, int len) {
 	char c;
@@ -60,6 +74,100 @@
 	}
 }
 
+/* no conversion, just a wrapper for memcpy. */
+static int convert_memcpy(char *output, int olen,
+			  const char *input, int ilen,
+			  struct nls_table *nls_from,
+			  struct nls_table *nls_to)
+{
+	memcpy(output, input, ilen);
+	return ilen;
+}
+
+/* convert from one "codepage" to another (possibly being utf8). */
+static int convert_cp(char *output, int olen,
+		      const char *input, int ilen,
+		      struct nls_table *nls_from,
+		      struct nls_table *nls_to)
+{
+	int len = 0;
+	int n;
+	unsigned char ch;
+	unsigned char cl;
+
+	/* we don't need this, but we are a bit paranoid ... */
+	if (!nls_from || !nls_to) {
+		PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to);
+		return convert_memcpy(output, olen, input, ilen, NULL, NULL);
+	}
+
+	while (ilen > 0) {
+		/* convert by changing to unicode and back to the new cp */
+		n = -1;
+		nls_from->char2uni((unsigned char *)input, &n, &cl, &ch);
+		if (n < 0)
+			goto out;
+		input += n;
+		ilen -= n;
+
+		n = -1;
+		nls_to->uni2char(ch, cl, output, olen, &n);
+		if (n < 0)
+			goto out;
+		output += n;
+		olen -= n;
+
+		len += n;
+	}
+out:
+	return len;
+}
+
+static int setcodepage(struct smb_sb_info *server,
+		       struct nls_table **p, char *name)
+{
+	struct nls_table *nls;
+
+	if (!name || !*name) {
+		nls = NULL;
+	} else if ( (nls = load_nls(name)) == NULL) {
+		printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name);
+		return -EINVAL;
+	}
+
+	/* if already set, unload the previous one. */
+	if (*p)
+		unload_nls(*p);
+	*p = nls;
+
+	return 0;
+}
+
+/* Handles all changes to codepage settings. */
+int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
+{
+	int n;
+
+	smb_lock_server(server);
+
+	n = setcodepage(server, &server->local_nls, cp->local_name);
+	if (n != 0)
+		goto out;
+	n = setcodepage(server, &server->remote_nls, cp->remote_name);
+	if (n != 0)
+		setcodepage(server, &server->local_nls, NULL);
+
+out:
+	if (server->local_nls != NULL && server->remote_nls != NULL)
+		server->convert = convert_cp;
+	else
+		server->convert = convert_memcpy;
+
+	smb_unlock_server(server);
+	return n;
+}
+
+
 /*****************************************************************************/
 /*                                                                           */
 /*  Encoding/Decoding section                                                */
@@ -81,12 +189,14 @@
 }
 
 /*
- * smb_build_path: build the path to entry and name storing it in buf.
+ * smb_encode_path: build the path to entry and name storing it in buf.
  * The path returned will have the trailing '\0'.
  */
-static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
+static int smb_encode_path(struct smb_sb_info *server, char * buf,
+			   struct dentry * entry, struct qstr * name)
 {
 	char *path = buf;
+	int len;
 
 	if (entry == NULL)
 		goto test_name_and_out;
@@ -106,9 +216,16 @@
 	 * and store it in reversed order [see reverse_string()]
 	 */
 	for (;;) {
-		memcpy(path, entry->d_name.name, entry->d_name.len);
-		reverse_string(path, entry->d_name.len);
-		path += entry->d_name.len;
+		if (entry->d_name.len > SMB_MAXNAMELEN)
+			return -ENAMETOOLONG;
+		if (path - buf + entry->d_name.len > SMB_MAXPATHLEN)
+			return -ENAMETOOLONG;
+
+		len = server->convert(path, SMB_MAXNAMELEN, 
+				      entry->d_name.name, entry->d_name.len,
+				      server->local_nls, server->remote_nls);
+		reverse_string(path, len);
+		path += len;
 
 		*(path++) = '\\';
 
@@ -124,22 +241,16 @@
 	if (name != NULL) {
 		*(path++) = '\\';
 name_and_out:
-		memcpy(path, name->name, name->len);
-		path += name->len;
+		len = server->convert(path, SMB_MAXNAMELEN, 
+				      name->name, name->len,
+				      server->local_nls, server->remote_nls);
+		path += len;
 	}
 out:
 	*(path++) = '\0';
 	return (path-buf);
 }
 
-static char *smb_encode_path(struct smb_sb_info *server, char *buf,
-			     struct dentry *dir, struct qstr *name)
-{
-	buf += smb_build_path(dir, name, buf);
-
-	return buf;
-}
-
 /* The following are taken directly from msdos-fs */
 
 /* Linear day numbers of the respective 1sts in non-leap years. */
@@ -434,24 +545,12 @@
 	return EIO;
 }
 
-static inline void
-smb_lock_server(struct smb_sb_info *server)
-{
-	down(&(server->sem));
-}
-
-static inline void
-smb_unlock_server(struct smb_sb_info *server)
-{
-	up(&(server->sem));
-}
-
 /*
  * smb_retry: This function should be called when smb_request_ok has
-   indicated an error. If the error was indicated because the
-   connection was killed, we try to reconnect. If smb_retry returns 0,
-   the error was indicated for another reason, so a retry would not be
-   of any use.
+ * indicated an error. If the error was indicated because the
+ * connection was killed, we try to reconnect. If smb_retry returns 0,
+ * the error was indicated for another reason, so a retry would not be
+ * of any use.
  * N.B. The server must be locked for this call.
  */
 static int
@@ -465,8 +564,7 @@
 
 	smb_close_socket(server);
 
-	if (pid == 0)
-	{
+	if (pid == 0) {
 		printk(KERN_ERR "smb_retry: no connection process\n");
 		server->state = CONN_RETRIED;
 		goto out;
@@ -481,26 +579,35 @@
 	 * Note: use the "priv" flag, as a user process may need to reconnect.
 	 */
 	error = kill_proc(pid, SIGUSR1, 1);
-	if (error)
-	{
+	if (error) {
 		printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error);
 		goto out_restore;
 	}
-	VERBOSE("signalled pid %d, waiting for new connection\n",
-		server->conn_pid);
+	VERBOSE("signalled pid %d, waiting for new connection\n", pid);
 
 	/*
 	 * Wait for the new connection.
 	 */
+#ifdef SMB_RETRY_INTR
 	interruptible_sleep_on_timeout(&server->wait,  5*HZ);
 	if (signal_pending(current))
 		printk(KERN_INFO "caught signal\n");
+#else
+	/*
+	 * We don't want to be interrupted. For example, what if 'current'
+	 * already has recieved a signal? sleep_on would terminate immediately
+	 * and smbmount would not be able to re-establish connection.
+	 *
+	 * smbmount should be able to reconnect later, but it can't because
+	 * it will get an -EIO on attempts to open the mountpoint!
+	 */
+	sleep_on_timeout(&server->wait, 5*HZ);
+#endif
 
 	/*
 	 * Check for a valid connection.
 	 */
-	if (server->state == CONN_VALID)
-	{
+	if (server->state == CONN_VALID) {
 		PARANOIA("sucessful, new pid=%d, generation=%d\n",
 			 server->conn_pid, server->generation);
 		result = 1;
@@ -599,8 +706,13 @@
 	    !capable(CAP_SYS_ADMIN))
 		goto out;
 
-	if (opt->protocol < SMB_PROTOCOL_NT1) {
-		printk(KERN_NOTICE "smbfs: protocols older than NT1 are not suppported\n");
+	/*
+	 * Support NT1 & LANMAN2, they are not that different and
+	 * some people are still using LANMAN2 (OS/2)
+	 */
+	if (opt->protocol < SMB_PROTOCOL_LANMAN2) {
+		printk(KERN_NOTICE "smbfs: protocols older than LANMAN2 are not"
+		       " suppported [%d]\n", opt->protocol);
 		goto out;
 	}
 
@@ -626,9 +738,10 @@
 
 	/* now that we have an established connection we can detect the server
 	   type and enable bug workarounds */
-	if ((server->opt.max_xmit < 0x1000) &&
+	if (server->opt.protocol == SMB_PROTOCOL_NT1 &&
+	    (server->opt.max_xmit < 0x1000) &&
 	    !(server->opt.capabilities & SMB_CAP_NT_SMBS)) {
-		server->mnt->version |= SMB_FIX_WIN95;
+		server->mnt->flags |= SMB_MOUNT_WIN95;
 #ifdef SMBFS_DEBUG_VERBOSE
 		printk(KERN_NOTICE "smb_newconn: detected WIN95 server\n");
 #endif
@@ -639,7 +752,11 @@
 		server->opt.capabilities);
 
 out:
+#ifdef SMB_RETRY_INTR
 	wake_up_interruptible(&server->wait);
+#else
+	wake_up(&server->wait);
+#endif
 	return error;
 
 out_putf:
@@ -706,7 +823,7 @@
 {
 	struct inode *ino = dentry->d_inode;
 	int mode, read_write = 0x42, read_only = 0x40;
-	int error;
+	int res;
 	char *p;
 
 	/*
@@ -716,29 +833,33 @@
 	if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
 		mode = read_only;
 #if 0
+	/* FIXME: why is this code not in? below we fix it so that a caller
+	   wanting RO doesn't get RW. smb_revalidate_inode does some 
+	   optimization based on access mode. tail -f needs it to be correct. */
 	if (!(wish & (O_WRONLY | O_RDWR)))
 		mode = read_only;
 #endif
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBopen, 2, 0);
 	WSET(server->packet, smb_vwv0, mode);
 	WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	res = smb_encode_path(server, p, dentry, NULL);
+	if (res < 0)
+		goto out;
+	p += res;
 	smb_setup_bcc(server, p);
 
-	error = smb_request_ok(server, SMBopen, 7, 0);
-	if (error != 0)
-	{
+	res = smb_request_ok(server, SMBopen, 7, 0);
+	if (res != 0) {
 		if (smb_retry(server))
 			goto retry;
 
 		if (mode == read_write &&
-		    (error == -EACCES || error == -ETXTBSY || error == -EROFS))
-		{
+		    (res == -EACCES || res == -ETXTBSY || res == -EROFS)) {
 			VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n",
-				DENTRY_PATH(dentry), error);
+				DENTRY_PATH(dentry), res);
 			mode = read_only;
 			goto retry;
 		}
@@ -751,10 +872,12 @@
 	/* smb_vwv2 has mtime */
 	/* smb_vwv4 has size  */
 	ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK);
+	if (!(wish & (O_WRONLY | O_RDWR)))
+		ino->u.smbfs_i.access = SMB_O_RDONLY;
 	ino->u.smbfs_i.open = server->generation;
 
 out:
-	return error;
+	return res;
 }
 
 /*
@@ -768,8 +891,7 @@
 	int result;
 
 	result = -ENOENT;
-	if (!inode)
-	{
+	if (!inode) {
 		printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n",
 		       DENTRY_PATH(dentry));
 		goto out;
@@ -780,16 +902,14 @@
 	 * currently open, we can be sure that the file isn't about
 	 * to be closed. (See smb_close_dentry() below.)
 	 */
-	if (!smb_is_open(inode))
-	{
+	if (!smb_is_open(inode)) {
 		struct smb_sb_info *server = SMB_SERVER(inode);
 		smb_lock_server(server);
 		result = 0;
 		if (!smb_is_open(inode))
 			result = smb_proc_open(server, dentry, wish);
 		smb_unlock_server(server);
-		if (result)
-		{
+		if (result) {
 			PARANOIA("%s/%s open failed, result=%d\n",
 				 DENTRY_PATH(dentry), result);
 			goto out;
@@ -805,8 +925,7 @@
 	 */
 	result = 0;
 	if (inode->u.smbfs_i.access != wish && 
-	    inode->u.smbfs_i.access != SMB_O_RDWR)
-	{
+	    inode->u.smbfs_i.access != SMB_O_RDWR) {
 		PARANOIA("%s/%s access denied, access=%x, wish=%x\n",
 			 DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish);
 		result = -EACCES;
@@ -871,6 +990,8 @@
 		/*
 		 * Force a revalidation after closing 
 		 */
+		if (server->opt.protocol < SMB_PROTOCOL_NT1)
+			ino->u.smbfs_i.oldmtime = 0;
 		ino->u.smbfs_i.closed = jiffies;
 	}
 	return result;
@@ -1029,31 +1150,33 @@
 {
 	struct smb_sb_info *server = server_from_dentry(dentry);
 	char *p;
-	int error;
+	int result;
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBcreate, 3, 0);
 	WSET(server->packet, smb_vwv0, attr);
 	DSET(server->packet, smb_vwv1, utc2local(server, ctime));
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	error = smb_request_ok(server, SMBcreate, 1, 0);
-	if (error < 0)
-	{
+	result = smb_request_ok(server, SMBcreate, 1, 0);
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
 	}
 	*fileid = WVAL(server->packet, smb_vwv0);
-	error = 0;
+	result = 0;
 
 out:
 	smb_unlock_server(server);
-	return error;
+	return result;
 }
 
 int
@@ -1065,17 +1188,22 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBmv, 1, 0);
 	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
 	*p++ = 4;
-	p = smb_encode_path(server, p, old_dentry, NULL);
+	result = smb_encode_path(server, p, old_dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	*p++ = 4;
-	p = smb_encode_path(server, p, new_dentry, NULL);
+	result = smb_encode_path(server, p, new_dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
-	{
+	if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1098,15 +1226,17 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, command, 0, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
 	result = smb_request_ok(server, command, 0, 0);
-	if (result < 0)
-	{
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1165,15 +1295,17 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBunlink, 1, 0);
 	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
-	{
+	if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
 #if SMBFS_POSIX_UNLINK
                 if (result == -EACCES && !flag) {
 			/* Posix semantics is for the read-only state
@@ -1292,7 +1424,7 @@
  */
 static char *
 smb_decode_long_dirent(struct smb_sb_info *server, char *p,
-		       struct cache_dirent *entry)
+		       struct cache_dirent *entry, int level)
 {
 	char *result;
 	unsigned int len = 0;
@@ -1302,19 +1434,39 @@
 	 */
 	entry->ino = 0;
 
-	result = p + WVAL(p, 0);
-	len = DVAL(p, 60);
-	if (len > SMB_MAXNAMELEN)
-		len = SMB_MAXNAMELEN;
-	/* NT4 null terminates */
-	entry->name = p + 94;
-	if (len && entry->name[len-1] == '\0')
-		len--;
-	entry->len = len;
-
-	VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
-		p, entry->len, entry->len, entry->name);
+	switch (level) {
+	case 1:
+		len = *((unsigned char *) p + 22);
+		entry->name = p + 23;
+		result = p + 24 + len;
+
+		VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
+			p, len, len, entry->name);
+		break;
+	case 260:
+		result = p + WVAL(p, 0);
+		len = DVAL(p, 60);
+		if (len > SMB_MAXNAMELEN)
+			len = SMB_MAXNAMELEN;
+		/* NT4 null terminates */
+		entry->name = p + 94;
+		if (len && entry->name[len-1] == '\0')
+			len--;
+
+		VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
+			p, len, len, entry->name);
+		break;
+	default:
+		PARANOIA("Unknown info level %d\n", level);
+		result = p + WVAL(p, 0);
+		goto out;
+	}
 
+	entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+				     entry->name, len,
+				     server->remote_nls, server->local_nls);
+	entry->name = server->name_buf;
+out:
 	return result;
 }
 
@@ -1336,9 +1488,11 @@
 		      void *cachep)
 {
 	unsigned char *p;
-	char *mask, *param = server->temp_buf;
+	char *mask, *lastname, *param = server->temp_buf;
 	__u16 command;
 	int first, entries, entries_seen;
+
+	/* Both NT and OS/2 accept info level 1 (but see note below). */
 	int info_level = 260;
 	const int max_matches = 512;
 
@@ -1348,19 +1502,30 @@
 	int resp_param_len = 0;
 	int ff_searchcount = 0;
 	int ff_eos = 0;
+	int ff_lastname = 0;
 	int ff_dir_handle = 0;
 	int loop_count = 0;
 	int mask_len, i, result;
 	static struct qstr star = { "*", 1, 0 };
 
+	/*
+	 * use info level 1 for older servers that don't do 260
+	 */
+	if (server->opt.protocol < SMB_PROTOCOL_NT1)
+		info_level = 1;
+
 	smb_lock_server(server);
 
-      retry:
+retry:
 	/*
 	 * Encode the initial path
 	 */
 	mask = param + 12;
-	mask_len = smb_encode_path(server, mask, dir, &star) - mask;
+	mask_len = smb_encode_path(server, mask, dir, &star);
+	if (mask_len < 0) {
+		entries = mask_len;
+		goto unlock_return;
+	}
 	first = 1;
 
 	VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask);
@@ -1386,15 +1551,10 @@
 			command = TRANSACT2_FINDFIRST;
 			WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
 			WSET(param, 2, max_matches);	/* max count */
-			WSET(param, 4, 
-			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
+			WSET(param, 4, SMB_CLOSE_IF_END); 
 			WSET(param, 6, info_level);
 			DSET(param, 8, 0);
 		} else {
-			/* we don't need the mask after the first bit */
-			mask_len = 0;
-			mask[0] = 0;
-
 			command = TRANSACT2_FINDNEXT;
 
 			VERBOSE("handle=0x%X, mask=%s\n",
@@ -1404,8 +1564,7 @@
 			WSET(param, 2, max_matches);	/* max count */
 			WSET(param, 4, info_level);
 			DSET(param, 6, 0);       	/* ff_resume_key */
-			WSET(param, 10,
-			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
+			WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
 		}
 
 		result = smb_trans2_request(server, command,
@@ -1444,14 +1603,42 @@
 			ff_dir_handle = WVAL(resp_param, 0);
 			ff_searchcount = WVAL(resp_param, 2);
 			ff_eos = WVAL(resp_param, 4);
+			ff_lastname = WVAL(resp_param, 8);
 		} else {
 			ff_searchcount = WVAL(resp_param, 0);
 			ff_eos = WVAL(resp_param, 2);
+			ff_lastname = WVAL(resp_param, 6);
 		}
 
 		if (ff_searchcount == 0)
 			break;
 
+		/* we might need the lastname for continuations */
+		mask_len = 0;
+		if (ff_lastname > 0) {
+			lastname = resp_data + ff_lastname;
+			switch (info_level) {
+			case 260:
+				if (ff_lastname < resp_data_len)
+					mask_len = resp_data_len - ff_lastname;
+				break;
+			case 1:
+				/* Win NT 4.0 doesn't set the length byte */
+				lastname++;
+				if (ff_lastname + 2 < resp_data_len)
+					mask_len = strlen(lastname);
+				break;
+			}
+			/*
+			 * Update the mask string for the next message.
+			 */
+			if (mask_len > 255)
+				mask_len = 255;
+			if (mask_len)
+				strncpy(mask, lastname, mask_len);
+		}
+		mask[mask_len] = 0;
+
 		/* Now we are ready to parse smb directory entries. */
 
 		/* point to the data bytes */
@@ -1470,7 +1657,7 @@
 				goto unlock_return;
 			}
 
-			p = smb_decode_long_dirent(server, p, entry);
+			p = smb_decode_long_dirent(server, p, entry,info_level);
 
 			/* ignore . and .. from the server */
 			if (entries_seen == 2 && entry->name[0] == '.') {
@@ -1517,10 +1704,13 @@
 	int result;
 	char *p;
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBgetatr, 0, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dir, NULL);
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
 	if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) {
@@ -1571,31 +1761,32 @@
 	int resp_param_len = 0;
 	int result;
 
-      retry:
+retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
 	DSET(param, 2, 0);
-	p = smb_encode_path(server, param + 6, dir, NULL);
+	p = param + 6;
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 
 	result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
 				    0, NULL, p - param, param,
 				    &resp_data_len, &resp_data,
 				    &resp_param_len, &resp_param);
-	if (result < 0)
-	{
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
 	}
-	if (server->rcls != 0)
-	{
+	if (server->rcls != 0) {
 		VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
 			&param[6], result, server->rcls, server->err);
 		result = -smb_errno(server);
 		goto out;
 	}
 	result = -ENOENT;
-	if (resp_data_len < 22)
-	{
+	if (resp_data_len < 22) {
 		PARANOIA("not enough data for %s, len=%d\n",
 			 &param[6], resp_data_len);
 		goto out;
@@ -1605,7 +1796,7 @@
 	 * Kludge alert: Win 95 swaps the date and time field,
 	 * contrary to the CIFS docs and Win NT practice.
 	 */
-	if (server->mnt->version & SMB_FIX_WIN95) {
+	if (server->mnt->flags & SMB_MOUNT_WIN95) {
 		off_date = 2;
 		off_time = 0;
 	}
@@ -1649,7 +1840,7 @@
 	 * Select whether to use core or trans2 getattr.
 	 * Win 95 appears to break with the trans2 getattr.
 	 */
-	if (server->mnt->version & SMB_FIX_WIN95) {
+	if (server->mnt->flags & SMB_MOUNT_WIN95) {
 		result = smb_proc_getattr_core(server, dir, fattr);
 	} else {
 		result = smb_proc_getattr_trans2(server, dir, fattr);
@@ -1702,7 +1893,10 @@
 	WSET(server->packet, smb_vwv6, 0);
 	WSET(server->packet, smb_vwv7, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	*p++ = 4;
 	*p++ = 0;
 	smb_setup_bcc(server, p);
@@ -1803,7 +1997,11 @@
       retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
 	DSET(param, 2, 0);
-	p = smb_encode_path(server, param + 6, dir, NULL);
+	p = param + 6;
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 
 	WSET(data, 0, 0); /* creation time */
 	WSET(data, 2, 0);
@@ -1867,7 +2065,7 @@
 
 	smb_lock_server(server);
 	/* setting the time on a Win95 server fails (tridge) */
-	if (!(server->mnt->version & SMB_FIX_WIN95)) {
+	if (!(server->mnt->flags & SMB_MOUNT_WIN95)) {
 		if (smb_is_open(inode) &&
 		    inode->u.smbfs_i.access != SMB_O_RDONLY)
 			result = smb_proc_setattr_ext(server, inode, fattr);
@@ -1907,7 +2105,7 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	smb_setup_header(server, SMBdskattr, 0, 0);
 
 	if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/smbfs/sock.c linux.18p17/fs/smbfs/sock.c
--- linux.vanilla/fs/smbfs/sock.c	Sat Sep  9 18:42:50 2000
+++ linux.18p17/fs/smbfs/sock.c	Sat Sep  2 23:33:56 2000
@@ -108,23 +108,24 @@
 	unsigned char peek_buf[4];
 	int result;
 	mm_segment_t fs;
+	int count = 100;   /* this is a lot, we should have some data waiting */
+	int found = 0;
 
 	fs = get_fs();
 	set_fs(get_ds());
 
 	lock_kernel();
-	while (1)
-	{
+	while (count-- > 0) {
+		peek_buf[0] = 0;
 		result = -EIO;
-		if (job->sk->dead)
-		{
+		if (job->sk->dead) {
 			PARANOIA("sock dead!\n");
 			break;
 		}
 
 		result = _recvfrom(socket, (void *) peek_buf, 1,
 				   MSG_PEEK | MSG_DONTWAIT);
-		if (result == -EAGAIN)
+		if (result < 0)
 			break;
 		if (peek_buf[0] != 0x85)
 			break;
@@ -135,13 +136,15 @@
 
 		DEBUG1("got SESSION KEEPALIVE\n");
 
-		if (result == -EAGAIN)
+		if (result < 0)
 			break;
+		found = 1;
 	}
 	unlock_kernel();
 	set_fs(fs);
 
-	if (result != -EAGAIN)
+	DEBUG1("found=%d, count=%d, result=%d\n", found, count, result);
+	if (found)
 		found_data(job->sk);
 	kfree(ptr);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/super.c linux.18p17/fs/super.c
--- linux.vanilla/fs/super.c	Fri Apr 21 12:46:45 2000
+++ linux.18p17/fs/super.c	Sun Oct 15 21:15:17 2000
@@ -187,9 +187,9 @@
         return 0;
 }
 
-#ifdef CONFIG_MODULES
 int unregister_filesystem(struct file_system_type * fs)
 {
+#ifdef CONFIG_MODULES
 	struct file_system_type ** tmp;
 
 	tmp = &file_systems;
@@ -201,9 +201,9 @@
 		}
 		tmp = &(*tmp)->next;
 	}
+#endif
 	return -EINVAL;
 }
-#endif
 
 static int fs_index(const char * __name)
 {
@@ -304,8 +304,10 @@
 	{ NFS_MOUNT_SOFT, ",soft" },
 	{ NFS_MOUNT_INTR, ",intr" },
 	{ NFS_MOUNT_POSIX, ",posix" },
+	{ NFS_MOUNT_TCP, ",tcp" },
 	{ NFS_MOUNT_NOCTO, ",nocto" },
 	{ NFS_MOUNT_NOAC, ",noac" },
+	{ NFS_MOUNT_NONLM, ",nolock" },
 	{ 0, NULL }
 };
 
@@ -330,6 +332,8 @@
 		}
 		if (!strcmp("nfs", tmp->mnt_sb->s_type->name)) {
 			nfss = &tmp->mnt_sb->u.nfs_sb.s_server;
+			len += sprintf(buf+len, ",v%d", nfss->rpc_ops->version);
+
 			if (nfss->rsize != NFS_DEF_FILE_IO_BUFFER_SIZE) {
 				len += sprintf(buf+len, ",rsize=%d",
 					       nfss->rsize);
@@ -562,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;
@@ -1150,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/ufs/ialloc.c linux.18p17/fs/ufs/ialloc.c
--- linux.vanilla/fs/ufs/ialloc.c	Fri Apr 21 12:46:45 2000
+++ linux.18p17/fs/ufs/ialloc.c	Tue Sep 12 12:55:12 2000
@@ -275,9 +275,7 @@
 	inode->i_nlink = 1;
 	inode->i_dev = sb->s_dev;
 	inode->i_uid = current->fsuid;
-	if (test_opt (sb, GRPID))
-		inode->i_gid = dir->i_gid;
-	else if (dir->i_mode & S_ISGID) {
+	if (dir->i_mode & S_ISGID) {
 		inode->i_gid = dir->i_gid;
 		if (S_ISDIR(mode))
 			mode |= S_ISGID;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/vfat/namei.c linux.18p17/fs/vfat/namei.c
--- linux.vanilla/fs/vfat/namei.c	Sun Jun 11 21:44:22 2000
+++ linux.18p17/fs/vfat/namei.c	Thu Aug 31 14:33:07 2000
@@ -120,6 +120,7 @@
 	opts->unicode_xlate = opts->posixfs = 0;
 	opts->numtail = 1;
 	opts->utf8 = 0;
+	opts->force_long = 0;
 
 	if (!options) return 1;
 	save = 0;
@@ -142,9 +143,10 @@
 			if (ret) opts->posixfs = val;
 		} else if (!strcmp(this_char,"nonumtail")) {
 			ret = simple_getbool(value, &val);
-			if (ret) {
-				opts->numtail = !val;
-			}
+			if (ret) opts->numtail = !val;
+		} else if (!strcmp(this_char,"forcelong") ||
+			   !strcmp(this_char,"force_long")) {
+			opts->force_long = 1;
 		}
 		if (this_char != options)
 			*(this_char-1) = ',';
@@ -383,7 +385,7 @@
 		len--;
 		if (utf8 && (c & 0x80)) return -EINVAL;
 		if (strchr(replace_chars,c)) return -EINVAL;
-  		if (c >= 'A' && c <= 'Z') return -EINVAL;
+  		if (islower(c)) return -EINVAL;
 		if (c < ' '|| c==':') return -EINVAL;
 		if (c == '.') break;
 		space = c == ' ';
@@ -406,7 +408,7 @@
 				return -EINVAL;
 			if (c < ' ' || c == '.'|| c==':')
 				return -EINVAL;
-			if (c >= 'A' && c <= 'Z') return -EINVAL;
+			if (islower(c)) return -EINVAL;
 			space = c == ' ';
 		}
 		if (space) return -EINVAL;
@@ -428,6 +430,16 @@
 	return 0;
 }
 
+static void vfat_format_name_fast(const char *name,int len,char *res)
+{
+	char *walk = res;
+
+	while (len-- && *name != '.') *walk++ = *name++;
+	while (walk-res < 8) *walk++ = ' ';
+	while (--len >= 0) *walk++ = *++name;
+	while (walk-res < MSDOS_NAME) *walk++ = ' ';
+}
+
 static int vfat_format_name(const char *name,int len,char *res,int utf8)
 {
 	char *walk;
@@ -439,10 +451,10 @@
 		if (walk-res == 8) return -EINVAL;
 		if (utf8 && (c & 0x80)) return -EINVAL;
 		if (strchr(replace_chars,c)) return -EINVAL;
-		if (c >= 'A' && c <= 'Z') return -EINVAL;
+		if (islower(c)) return -EINVAL;
 		if (c < ' '|| c==':') return -EINVAL;
 		space = c == ' ';
-		*walk = c >= 'a' && c <= 'z' ? c-32 : c;
+		*walk = c;
 	}
 	if (space) return -EINVAL;
 	if (len >= 0) {
@@ -455,9 +467,9 @@
 				return -EINVAL;
 			if (c < ' ' || c == '.'|| c==':')
 				return -EINVAL;
-			if (c >= 'A' && c <= 'Z') return -EINVAL;
+			if (islower(c)) return -EINVAL;
 			space = c == ' ';
-			*walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
+			*walk++ = c;
 		}
 		if (space) return -EINVAL;
 		if (len) return -EINVAL;
@@ -505,11 +517,7 @@
 
 			if (*ip == ' ')
 				break;
-			if (*ip >= 'A' && *ip <= 'Z') {
-				*p = *ip + 32;
-			} else {
-				*p = *ip;
-			}
+			*p = toupper(*ip);
 		}
 	}
 
@@ -555,11 +563,7 @@
 			*p++ = '_';
 			baselen++;
 		} else if (!strchr(skip_chars, *ip)) {
-			if (*ip >= 'a' && *ip <= 'z') {
-				*p = *ip - 32;
-			} else {
-				*p = *ip;
-			}
+			*p = toupper(*ip);
 			if (strchr(replace_chars, *p)) *p='_';
 			p++; baselen++;
 		}
@@ -576,11 +580,7 @@
 				*p++ = '_';
 				extlen++;
 			} else if (!strchr(skip_chars, *ip)) {
-				if (*ip >= 'a' && *ip <= 'z') {
-					*p = *ip - 32;
-				} else {
-					*p = *ip;
-				}
+				*p = toupper(*ip);
 				if (strchr(replace_chars, *p)) *p='_';
 				extlen++;
 				p++;
@@ -774,25 +774,30 @@
 {
 	struct msdos_dir_entry *de;
 	char msdos_name[MSDOS_NAME];
-	int res, xlate, utf8;
+	int res, xlate, utf8, force_long;
 	struct nls_table *nls;
 
 	de = (struct msdos_dir_entry *) ds;
 	xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
 	utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
+	force_long = MSDOS_SB(dir->i_sb)->options.force_long;
 	nls = MSDOS_SB(dir->i_sb)->nls_io;
 
 	*slots = 1;
-	res = vfat_valid_longname(name, len, xlate);
-	if (res < 0)
-		return res;
 	if (vfat_valid_shortname(name, len, utf8) >= 0) {
-		vfat_format_name(name, len, de->name, utf8);
-		return 0;
+		if (!force_long) {
+			vfat_format_name_fast(name, len, de->name);
+			return 0;
+		}
+		vfat_format_name_fast(name, len, msdos_name);
+	} else {
+		res = vfat_valid_longname(name, len, xlate);
+		if (res < 0)
+			return res;
+		res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
+		if (res < 0)
+			return res;
 	}
-	res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
-	if (res < 0)
-		return res;
 	return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate,
 					utf8, nls);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/delay.h linux.18p17/include/asm-alpha/delay.h
--- linux.vanilla/include/asm-alpha/delay.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/delay.h	Mon Oct  2 16:44:31 2000
@@ -40,7 +40,7 @@
 extern __inline__ void
 __udelay(unsigned long usecs, unsigned long lps)
 {
-	/* compute (usecs * 2**64 / 10**6) * loops_per_sec / 2**64 */
+	/* compute (usecs * 2**64 / 10**6) * loops_per_jiffy * HZ / 2**64 */
 
 	usecs *= 0x000010c6f7a0b5edUL;		/* 2**64 / 1000000 */
 	__asm__("umulh %1,%2,%0" :"=r" (usecs) :"r" (usecs),"r" (lps));
@@ -50,7 +50,7 @@
 extern __inline__ void
 __small_const_udelay(unsigned long usecs, unsigned long lps)
 {
-	/* 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 */
 	usecs *= lps;
@@ -62,15 +62,16 @@
 #define udelay(usecs)						\
 	(__builtin_constant_p(usecs) && usecs < 0x100000000UL	\
 	 ? __small_const_udelay(usecs,				\
-		cpu_data[smp_processor_id()].loops_per_sec)	\
+		cpu_data[smp_processor_id()].loops_per_jiffy*HZ)	\
 	 : __udelay(usecs,					\
-		cpu_data[smp_processor_id()].loops_per_sec))
+		cpu_data[smp_processor_id()].loops_per_jiffy*HZ))
 #else
 #define udelay(usecs)						\
 	(__builtin_constant_p(usecs) && usecs < 0x100000000UL	\
-	 ? __small_const_udelay(usecs, loops_per_sec)		\
-	 : __udelay(usecs, loops_per_sec))
+	 ? __small_const_udelay(usecs, loops_per_jiffy*HZ)		\
+	 : __udelay(usecs, loops_per_jiffy*HZ))
 #endif
 
 
 #endif /* defined(__ALPHA_DELAY_H) */
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/page.h linux.18p17/include/asm-alpha/page.h
--- linux.vanilla/include/asm-alpha/page.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/page.h	Sun Sep  3 00:05:00 2000
@@ -68,6 +68,19 @@
 	} while (count);
 }
 
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
 #ifdef STRICT_MM_TYPECHECKS
 /*
  * These are used to make use of C type-checking..
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/semaphore.h linux.18p17/include/asm-alpha/semaphore.h
--- linux.vanilla/include/asm-alpha/semaphore.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/semaphore.h	Fri Sep  1 21:38:57 2000
@@ -26,6 +26,11 @@
 
 #define sema_init(sem, val)	atomic_set(&((sem)->count), val)
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
 extern int  __down_trylock(struct semaphore * sem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/smp.h linux.18p17/include/asm-alpha/smp.h
--- linux.vanilla/include/asm-alpha/smp.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/smp.h	Mon Oct  2 16:44:31 2000
@@ -9,7 +9,7 @@
 
 /* make a multiple of 64-bytes */
 struct cpuinfo_alpha {
-	unsigned long loops_per_sec;
+	unsigned long loops_per_jiffy;
 	unsigned long last_asn;
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/smplock.h linux.18p17/include/asm-alpha/smplock.h
--- linux.vanilla/include/asm-alpha/smplock.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/smplock.h	Thu Aug 31 15:37:12 2000
@@ -17,8 +17,6 @@
 {
 	if (task->lock_depth >= 0)
 		spin_unlock(&kernel_flag);
-	release_irqlock(cpu);
-	__sti();
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-alpha/types.h linux.18p17/include/asm-alpha/types.h
--- linux.vanilla/include/asm-alpha/types.h	Fri Apr 21 12:46:47 2000
+++ linux.18p17/include/asm-alpha/types.h	Sat Sep  2 19:28:07 2000
@@ -73,5 +73,7 @@
 
 #endif
 
+typedef u32 dma_addr_t;
+
 #endif /* __KERNEL__ */
 #endif /* _ALPHA_TYPES_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-arc/hardware.h linux.18p17/include/asm-arm/arch-arc/hardware.h
--- linux.vanilla/include/asm-arm/arch-arc/hardware.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-arc/hardware.h	Fri Sep 15 23:32:34 2000
@@ -49,7 +49,7 @@
 #define SCREEN1_BASE		0x01f88000
 
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * for use with inb/outb
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-arc/keyboard.h linux.18p17/include/asm-arm/arch-arc/keyboard.h
--- linux.vanilla/include/asm-arm/arch-arc/keyboard.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-arc/keyboard.h	Fri Sep 15 23:32:34 2000
@@ -29,29 +29,3 @@
 #define kbd_enable_irq()		enable_irq(IRQ_KEYBOARDRX)
 
 #define SYSRQ_KEY	13
-
-/* resource allocation */
-#define KEYBOARD_IRQ                   1
-#define kbd_request_region() request_region(0x60, 16, "keyboard")
-#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
-                                             "keyboard", NULL)
-
-/* How to access the keyboard macros on this platform.  */
-#define kbd_read_input() inb(KBD_DATA_REG)
-#define kbd_read_status() inb(KBD_STATUS_REG)
-#define kbd_write_output(val) outb(val, KBD_DATA_REG)
-#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
-
-/* Some stoneage hardware needs delays after some operations.  */
-#define kbd_pause() do { } while(0)
-
-/*
- * Machine specific bits for the PS/2 driver
- */
-
-#define AUX_IRQ 12
-
-#define aux_request_irq(hand, dev_id)					\
-	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
-
-#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-arc/system.h linux.18p17/include/asm-arm/arch-arc/system.h
--- linux.vanilla/include/asm-arm/arch-arc/system.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-arc/system.h	Fri Sep 15 23:32:34 2000
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-arc/system.h
  *
- * Copyright (c) 1996 Russell King and Dave Gilbert
+ * Copyright (c) 1996-1999 Russell King and Dave Gilbert
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
@@ -23,14 +23,11 @@
 
 #endif
 
+#define arch_do_idle()		do { } while (0)
+
 extern __inline__ void arch_reset(char mode)
 {
 	extern void ecard_reset(int card);
-
-	/*
-	 * Do any cleanups that the processor may require
-	 */
-	processor._proc_fin();
 
 	/*
 	 * Reset all expansion cards.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-arc/time.h linux.18p17/include/asm-arm/arch-arc/time.h
--- linux.vanilla/include/asm-arm/arch-arc/time.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-arc/time.h	Fri Sep 15 23:32:34 2000
@@ -167,6 +167,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa110/hardware.h linux.18p17/include/asm-arm/arch-ebsa110/hardware.h
--- linux.vanilla/include/asm-arm/arch-ebsa110/hardware.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa110/hardware.h	Fri Sep 15 23:32:34 2000
@@ -8,7 +8,7 @@
 #ifndef __ASM_ARCH_HARDWARE_H
 #define __ASM_ARCH_HARDWARE_H
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * IO definitions
@@ -39,6 +39,8 @@
 
 #define FLUSH_BASE		0xdf000000
 #define PCIO_BASE		0xf0000000
+
+#define PARAMS_BASE		(PAGE_OFFSET + 0x400)
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa110/io.h linux.18p17/include/asm-arm/arch-ebsa110/io.h
--- linux.vanilla/include/asm-arm/arch-ebsa110/io.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa110/io.h	Fri Sep 15 23:32:34 2000
@@ -16,178 +16,60 @@
 #undef	ARCH_IO_DELAY
 
 /*
- * We use two different types of addressing - PC style addresses, and ARM
- * addresses.  PC style accesses the PC hardware with the normal PC IO
- * addresses, eg 0x3f8 for serial#1.  ARM addresses are 0x80000000+
- * and are translated to the start of IO.  Note that all addresses are
- * shifted left!
+ * Note that the translation here is weird -
+ * the ISA mem space and some peripherals (ethernet and VG468)
+ * appear as a 16-bit memory on a 32-bit bus.  This means that
+ * byte lanes 2 and 3 are not used, and this translation must
+ * be used.
  */
-#define __PORT_PCIO(x)	(!((x) & 0x80000000))
+#define __isa_addr(x)		(((x) & 1) | (((x) & 0xfffffffe)) << 1)
 
-/*
- * Dynamic IO functions - let the compiler
- * optimize the expressions
- */
-#define DECLARE_DYN_OUT(fnsuffix,instr)						\
-extern __inline__ void __out##fnsuffix (unsigned int value, unsigned int port)	\
-{										\
-	unsigned long temp;							\
-	__asm__ __volatile__(							\
-	"tst	%2, #0x80000000\n\t"						\
-	"mov	%0, %4\n\t"							\
-	"addeq	%0, %0, %3\n\t"							\
-	"str" ##instr## "	%1, [%0, %2, lsl #2]	@ out"###fnsuffix	\
-	: "=&r" (temp)								\
-	: "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)	\
-	: "cc");								\
-}
+#define __isa_io_addr(p)	(PCIO_BASE + ((p) << 2))
 
-#define DECLARE_DYN_IN(sz,fnsuffix,instr)					\
-extern __inline__ unsigned sz __in##fnsuffix (unsigned int port)		\
-{										\
-	unsigned long temp, value;						\
-	__asm__ __volatile__(							\
-	"tst	%2, #0x80000000\n\t"						\
-	"mov	%0, %4\n\t"							\
-	"addeq	%0, %0, %3\n\t"							\
-	"ldr" ##instr## "	%1, [%0, %2, lsl #2]	@ in"###fnsuffix	\
-	: "=&r" (temp), "=r" (value)						\
-	: "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)		\
-	: "cc");								\
-	return (unsigned sz)value;						\
-}
+#define __inb(p)	(*(volatile unsigned char *)__isa_io_addr(p))
+#define __inl(p)	(panic("__inl(%X) called", p),0)
 
-extern __inline__ unsigned int __ioaddr (unsigned int port)			\
-{										\
-	if (__PORT_PCIO(port))							\
-		return (unsigned int)(PCIO_BASE + (port << 2));			\
-	else									\
-		return (unsigned int)(IO_BASE + (port << 2));			\
+extern __inline__ unsigned int __inw(unsigned int port)
+{
+	unsigned int value;
+	__asm__ __volatile__(
+	"ldr%?h	%0, [%1]	@ inw"
+	: "=r" (value)
+	: "r" (__isa_io_addr(port)));
+	return value;
 }
 
-#define DECLARE_IO(sz,fnsuffix,instr)	\
-	DECLARE_DYN_OUT(fnsuffix,instr)	\
-	DECLARE_DYN_IN(sz,fnsuffix,instr)
-
-DECLARE_IO(char,b,"b")
-DECLARE_IO(short,w,"")
-DECLARE_IO(long,l,"")
-
-#undef DECLARE_IO
-#undef DECLARE_DYN_OUT
-#undef DECLARE_DYN_IN
+#define __outb(v,p)	(*(volatile unsigned char *)__isa_io_addr(p) = v)
+#define __outl(v,p)	panic("__outl(%X,%X) called", v, p)
 
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port)							\
-({										\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"strb	%0, [%1, %2]		@ outbc"			\
-		: : "r" (value), "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"strb	%0, [%1, %2]		@ outbc"			\
-		: : "r" (value), "r" (IO_BASE), "r" ((port) << 2));		\
-})
-
-#define __inbc(port)								\
-({										\
-	unsigned char result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldrb	%0, [%1, %2]		@ inbc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldrb	%0, [%1, %2]		@ inbc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result;									\
-})
-
-#define __outwc(value,port)							\
-({										\
-	unsigned long v = value;						\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outwc"			\
-		: : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2));	\
-	else									\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outwc"			\
-		: : "r" (v|v<<16), "r" (IO_BASE), "r" ((port) << 2));		\
-})
-
-#define __inwc(port)								\
-({										\
-	unsigned short result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inwc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inwc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result & 0xffff;							\
-})
-
-#define __outlc(v,p)								\
-({										\
-	unsigned long v = value;						\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outlc"			\
-		: : "r" (v), "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"str	%0, [%1, %2]		@ outlc"			\
-		: : "r" (v), "r" (IO_BASE), "r" ((port) << 2));			\
-})
-
-#define __inlc(port)								\
-({										\
-	unsigned long result;							\
-	if (__PORT_PCIO((port)))						\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inlc"				\
-		: "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2));		\
-	else									\
-		__asm__ __volatile__(						\
-		"ldr	%0, [%1, %2]		@ inlc"				\
-		: "=r" (result) : "r" (IO_BASE), "r" ((port) << 2));		\
-	result;									\
-})
-
-#define __ioaddrc(port)								\
-({										\
-	unsigned long addr;							\
-	if (__PORT_PCIO((port)))						\
-		addr = PCIO_BASE + ((port) << 2);				\
-	else									\
-		addr = IO_BASE + ((port) << 2);					\
-	addr;									\
-})
+extern __inline__ unsigned int __outw(unsigned int value, unsigned int port)
+{
+	__asm__ __volatile__(
+	"str%?h	%0, [%1]	@ outw"
+	: : "r" (value), "r" (__isa_io_addr(port)));
+}
+
+#define __ioaddr(p)	__isa_io_addr(p)
 
 /*
- * Translated address IO functions
- *
- * IO address has already been translated to a virtual address
+ * ioremap support - basic support only
  */
-#define outb_t(v,p)								\
-	(*(volatile unsigned char *)(p) = (v))
+#define ioremap(addr,size)	((void *)(addr))
+#define iounmap(addr)
 
-#define inb_t(p)								\
-	(*(volatile unsigned char *)(p))
+#define __isa_mem_addr(x)	((void *)(0xe0000000 + __isa_addr((unsigned long)(x))))
 
-#define outl_t(v,p)								\
-	(*(volatile unsigned long *)(p) = (v))
+#define readb(addr)		(*(volatile unsigned char *)__isa_mem_addr(addr))
+#define readw(addr)		(*(volatile unsigned short *)__isa_mem_addr(addr))
+#define readl(addr)		(*(volatile unsigned long *)__isa_mem_addr(addr))
+
+#define writeb(b,addr)		(*(volatile unsigned char *)__isa_mem_addr(addr) = (b))
+#define writew(b,addr)		(*(volatile unsigned short *)__isa_mem_addr(addr) = (b))
+#define writel(b,addr)		(*(volatile unsigned long *)__isa_mem_addr(addr) = (b))
+
+#define memset_io(a,b,c)	__ebsa110_set_isamem(__isa_mem_addr(a),(b),(c))
+#define memcpy_fromio(a,b,c)	__ebsa110_copy_fromisamem((a),__isa_mem_addr(b),(c))
+#define memcpy_toio(a,b,c)	__ebsa110_copy_toisamem(__isa_mem_addr(a),(b),(c))
 
-#define inl_t(p)								\
-	(*(volatile unsigned long *)(p))
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa110/system.h linux.18p17/include/asm-arm/arch-ebsa110/system.h
--- linux.vanilla/include/asm-arm/arch-ebsa110/system.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa110/system.h	Fri Sep 15 23:32:34 2000
@@ -1,17 +1,13 @@
 /*
  * linux/include/asm-arm/arch-ebsa110/system.h
  *
- * Copyright (c) 1996-1998 Russell King.
+ * Copyright (C) 1996-1999 Russell King.
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-extern __inline__ void arch_reset(char mode)
-{
-	/*
-	 * loop endlessly
-	 */
-	cli();
-}
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
+#define arch_reset(mode)	processor.u.armv3v4.reset(0x80000000)
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa110/time.h linux.18p17/include/asm-arm/arch-ebsa110/time.h
--- linux.vanilla/include/asm-arm/arch-ebsa110/time.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa110/time.h	Fri Sep 15 23:32:34 2000
@@ -23,13 +23,13 @@
 #define PIT1_COUNT 0xecbe
 #elif defined(MCLK_47_8)
 /*
- * This should be 0x10AE1, but that doesn't exactly fit.
+ * This should be 0x10B43, but that doesn't exactly fit.
  * We run the timer interrupt at 5ms, and then divide it by
  * two in software...  This is so that the user processes
  * see exactly the same model whichever ARM processor they're
  * running on.
  */
-#define PIT1_COUNT 0x8570
+#define PIT1_COUNT 0x85A1
 #define DIVISOR 2
 #endif
  
@@ -65,6 +65,9 @@
 			do_timer(regs);
 		}
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/hardware.h linux.18p17/include/asm-arm/arch-ebsa285/hardware.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/hardware.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/hardware.h	Fri Sep 15 23:32:34 2000
@@ -122,8 +122,10 @@
 #define GPIO_RESET		0x001
 
 /* CPLD pins */
-#define CPLD_DSRESET		8
+#define CPLD_DS_ENABLE		8
+#define CPLD_7111_DISABLE	4
 #define CPLD_UNMUTE		2
+#define CPLD_FLASH_WR_ENABLE	1
 
 #ifndef __ASSEMBLY__
 extern void gpio_modify_op(int mask, int set);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/io.h linux.18p17/include/asm-arm/arch-ebsa285/io.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/io.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/io.h	Fri Sep 15 23:32:34 2000
@@ -18,132 +18,33 @@
 #undef	ARCH_IO_DELAY
 #define ARCH_READWRITE
 
-/*
- * Dynamic IO functions - let the compiler
- * optimize the expressions
- */
-#define DECLARE_DYN_OUT(fnsuffix,instr,typ)				\
-extern __inline__ void							\
-__out##fnsuffix (unsigned int value, unsigned int port)			\
-{									\
-	__asm__ __volatile__(						\
-	"str%?" ##instr## "	%0, [%1, %2]	@ out"###fnsuffix	\
-	: 								\
-	: "r" (value), "r" (PCIO_BASE), typ (port));			\
-}
+#define __pci_io_addr(x)	(PCIO_BASE + (unsigned int)(x))
 
-#define DECLARE_DYN_IN(sz,fnsuffix,instr,typ)				\
-extern __inline__ unsigned sz						\
-__in##fnsuffix (unsigned int port)					\
-{									\
-	unsigned long value;						\
-	__asm__ __volatile__(						\
-	"ldr%?" ##instr## "	%0, [%1, %2]	@ in"###fnsuffix	\
-	: "=&r" (value)							\
-	: "r" (PCIO_BASE), typ (port));					\
-	return (unsigned sz)value;					\
-}
+#define __inb(p)		(*(volatile unsigned char *)__pci_io_addr(p))
+#define __inl(p)		(*(volatile unsigned long *)__pci_io_addr(p))
 
-extern __inline__ unsigned int __ioaddr (unsigned int port)		\
-{									\
-	return (unsigned int)(PCIO_BASE + port);			\
+extern __inline__ unsigned int __inw(unsigned int port)
+{
+	unsigned int value;
+	__asm__ __volatile__(
+	"ldr%?h	%0, [%1, %2]	@ inw"
+	: "=&r" (value)
+	: "r" (PCIO_BASE), "r" (port));
+	return value;
 }
 
-#define DECLARE_IO(sz,fnsuffix,instr,typ)	\
-	DECLARE_DYN_OUT(fnsuffix,instr,typ)	\
-	DECLARE_DYN_IN(sz,fnsuffix,instr,typ)
-
-DECLARE_IO(char,b,"b","Jr")
-DECLARE_IO(short,w,"h","r")
-DECLARE_IO(long,l,"","Jr")
-
-#undef DECLARE_IO
-#undef DECLARE_DYN_OUT
-#undef DECLARE_DYN_IN
-
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?b	%0, [%1, %2]				@ outbc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "Jr" (port));			\
-})
 
-#define __inbc(port)							\
-({									\
-	unsigned char result;						\
-	__asm__ __volatile__(						\
-	"ldr%?b	%0, [%1, %2]				@ inbc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "Jr" (port));				\
-	result;								\
-})
-
-#define __outwc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?h	%0, [%1, %2]				@ outwc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "r" (port));			\
-})
-
-#define __inwc(port)							\
-({									\
-	unsigned short result;						\
-	__asm__ __volatile__(						\
-	"ldr%?h	%0, [%1, %2]				@ inwc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "r" (port));					\
-	result & 0xffff;						\
-})
-
-#define __outlc(value,port)						\
-({									\
-	__asm__ __volatile__(						\
-	"str%?	%0, [%1, %2]				@ outlc"	\
-	:								\
-	: "r" (value), "r" (PCIO_BASE), "Jr" (port));			\
-})
+#define __outb(v,p)		(*(volatile unsigned char *)__pci_io_addr(p) = (v))
+#define __outl(v,p)		(*(volatile unsigned long *)__pci_io_addr(p) = (v))
 
-#define __inlc(port)							\
-({									\
-	unsigned long result;						\
-	__asm__ __volatile__(						\
-	"ldr%?	%0, [%1, %2]				@ inlc"		\
-	: "=r" (result)							\
-	: "r" (PCIO_BASE), "Jr" (port));				\
-	result;								\
-})
-
-#define __ioaddrc(port)							\
-({									\
-	unsigned long addr;						\
-	addr = PCIO_BASE + port;					\
-	addr;								\
-})
-
-/*
- * Translated address IO functions
- *
- * IO address has already been translated to a virtual address
- */
-#define outb_t(v,p)							\
-	(*(volatile unsigned char *)(p) = (v))
-
-#define inb_t(p)							\
-	(*(volatile unsigned char *)(p))
-
-#define outl_t(v,p)							\
-	(*(volatile unsigned long *)(p) = (v))
+extern __inline__ void __outw(unsigned int value, unsigned int port)
+{
+	__asm__ __volatile__(
+	"str%?h	%0, [%1, %2]	@ outw"
+	: : "r" (value), "r" (PCIO_BASE), "r" (port));
+}
 
-#define inl_t(p)							\
-	(*(volatile unsigned long *)(p))
+#define __ioaddr(p)	__pci_io_addr(p)
 
 /*
  * ioremap support - validate a PCI memory address,
@@ -160,6 +61,8 @@
  */
 #define IO_FUDGE_FACTOR		PCIMEM_BASE
 
+#define __pci_mem_addr(x)	((void *)(IO_FUDGE_FACTOR + (unsigned long)(x)))
+
 /*
  * ioremap takes a PCI memory address, as specified in
  * linux/Documentation/IO-mapping.txt
@@ -176,30 +79,14 @@
 
 #define ioremap_nocache(iomem_addr,size) ioremap((iomem_addr),(size))
 
-extern void iounmap(void *addr);
-
-#define DECLARE_PCI_WRITE(typ,fnsuffix)					\
-static inline void write##fnsuffix(unsigned typ val, unsigned int addr)	\
-{									\
-	*(volatile unsigned typ *)(IO_FUDGE_FACTOR + addr) = val;	\
-}
-
-#define DECLARE_PCI_READ(typ,fnsuffix)					\
-static inline unsigned typ read##fnsuffix (unsigned int addr)		\
-{									\
-	return *(volatile unsigned typ *)(IO_FUDGE_FACTOR + addr);	\
-}
+#define iounmap(_addr)	do { __iounmap(__pci_mem_addr((_addr))); } while (0)
 
-#define DECLARE_PCI(typ,fnsuffix)					\
-	DECLARE_PCI_WRITE(typ,fnsuffix)					\
-	DECLARE_PCI_READ(typ,fnsuffix)
-
-DECLARE_PCI(char,b)
-DECLARE_PCI(short,w)
-DECLARE_PCI(long,l)
-
-#undef DECLARE_PCI
-#undef DECLARE_PCI_READ
-#undef DECLARE_PCI_WRITE
+#define readb(addr)	(*(volatile unsigned char *)__pci_mem_addr(addr))
+#define readw(addr)	(*(volatile unsigned short *)__pci_mem_addr(addr))
+#define readl(addr)	(*(volatile unsigned long *)__pci_mem_addr(addr))
+
+#define writeb(b,addr)	(*(volatile unsigned char *)__pci_mem_addr(addr) = (b))
+#define writew(b,addr)	(*(volatile unsigned short *)__pci_mem_addr(addr) = (b))
+#define writel(b,addr)	(*(volatile unsigned long *)__pci_mem_addr(addr) = (b))
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/irq.h linux.18p17/include/asm-arm/arch-ebsa285/irq.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/irq.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/irq.h	Fri Sep 15 23:32:34 2000
@@ -159,6 +159,8 @@
 	if (isa_irq != -1) {
 		/*
 		 * Setup, and then probe for an ISA PIC
+		 * If the PIC is not there, then we
+		 * ignore the PIC.
 		 */
 		outb(0x11, PIC_LO);
 		outb(_ISA_IRQ(0), PIC_MASK_LO);	/* IRQ number		*/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/keyboard.h linux.18p17/include/asm-arm/arch-ebsa285/keyboard.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/keyboard.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/keyboard.h	Fri Sep 15 23:32:34 2000
@@ -73,7 +73,6 @@
 #define SYSRQ_KEY	0x54
 
 /* resource allocation */
-#define KEYBOARD_IRQ                   1
 #define kbd_request_region() request_region(0x60, 16, "keyboard")
 #define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
                                              "keyboard", NULL)
@@ -90,9 +89,6 @@
 /*
  * Machine specific bits for the PS/2 driver
  */
-
-#define AUX_IRQ 12
-
 #define aux_request_irq(hand, dev_id)					\
 	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/memory.h linux.18p17/include/asm-arm/arch-ebsa285/memory.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/memory.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/memory.h	Fri Sep 15 23:32:34 2000
@@ -15,8 +15,6 @@
 #ifndef __ASM_ARCH_MMU_H
 #define __ASM_ARCH_MMU_H
 
-#include <linux/config.h>
-
 #if defined(CONFIG_HOST_FOOTBRIDGE)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/system.h linux.18p17/include/asm-arm/arch-ebsa285/system.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/system.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/system.h	Fri Sep 15 23:32:34 2000
@@ -8,28 +8,17 @@
 #include <asm/hardware.h>
 #include <asm/leds.h>
 
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
 extern __inline__ void arch_reset(char mode)
 {
-	cli();
-
 	if (mode == 's') {
-		__asm__ volatile (
-		"mov	lr, #0x41000000		@ prepare to jump to ROM
-		 mov	r0, #0x130
-		 mcr	p15, 0, r0, c1, c0	@ MMU off
-		 mcr	p15, 0, ip, c7, c7	@ flush caches
-		 mov	pc, lr" : : : "cc");
+		/*
+		 * Jump into ROM
+		 */
+		processor.u.armv3v4.reset(0x41000000);
 	} else {
-		if (machine_is_ebsa285() || machine_is_co285()) {
-			/* To reboot, we set up the 21285 watchdog and
-			 * enable it.  We then wait for it to timeout.
-			 */
-			*CSR_TIMER4_LOAD = 0x8000;
-			*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
-					   TIMER_CNTL_AUTORELOAD |
-					   TIMER_CNTL_DIV16;
-			*CSR_SA110_CNTL |= 1 << 13;
-		} else if (machine_is_netwinder()) {
+		if (machine_is_netwinder()) {
 			/* open up the SuperIO chip
 			 */
 			outb(0x87, 0x370);
@@ -48,6 +37,15 @@
 			/* set a RED LED and toggle WD_TIMER for rebooting
 			 */
 			outb(0xc4, 0x338);
+		} else {
+			/* To reboot, we set up the 21285 watchdog and
+			 * enable it.  We then wait for it to timeout.
+			 */
+			*CSR_TIMER4_LOAD = 0x8000;
+			*CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
+					   TIMER_CNTL_AUTORELOAD |
+					   TIMER_CNTL_DIV16;
+			*CSR_SA110_CNTL |= 1 << 13;
 		}
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/time.h linux.18p17/include/asm-arm/arch-ebsa285/time.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/time.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/time.h	Fri Sep 15 23:32:34 2000
@@ -115,6 +115,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction isa_timer_irq = {
@@ -257,6 +260,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction __ebsa285_data timer1_irq = {
@@ -279,22 +285,20 @@
  */
 extern __inline__ void setup_timer(void)
 {
-	switch(machine_arch_type) {
-	case MACH_TYPE_CO285:
+	if (machine_arch_type == MACH_TYPE_CO285)
 		/*
 		 * Add-in 21285s shouldn't access the RTC
 		 */
 		rtc_base = 0;
-		break;
-
-	default:
+	else
 		rtc_base = 0x70;
-		break;
-	}
 
 	if (rtc_base) {
 		int reg_d, reg_b;
 
+		/*
+		 * Probe for the RTC.
+		 */
 		reg_d = CMOS_READ(RTC_REG_D);
 
 		/*
@@ -314,7 +318,7 @@
 		    CMOS_READ(RTC_REG_B) == reg_b) {
 
 			/*
-			 * Check the battery
+			 * We have a RTC.  Check the battery
 			 */
 			if ((reg_d & 0x80) == 0)
 				printk(KERN_WARNING "RTC: *** warning: CMOS battery bad\n");
@@ -332,8 +336,7 @@
 		xtime.tv_sec = mktime(1970, 1, 1, 0, 0, 0);
 		set_rtc_mmss = set_dummy_time;
 	}
-
-	if (machine_is_ebsa285()) {
+	if (machine_is_ebsa285() || machine_is_co285()) {
 		gettimeoffset = timer1_gettimeoffset;
 
 		*CSR_TIMER1_CLR  = 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-ebsa285/uncompress.h linux.18p17/include/asm-arm/arch-ebsa285/uncompress.h
--- linux.vanilla/include/asm-arm/arch-ebsa285/uncompress.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-ebsa285/uncompress.h	Fri Sep 15 23:32:34 2000
@@ -1,18 +1,24 @@
 /*
  * linux/include/asm-arm/arch-ebsa285/uncompress.h
  *
- * Copyright (C) 1996,1997,1998 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
 
 /*
  * Note! This could cause problems on the NetWinder
  */
-#define BASE 0x42000160
+#define DC21285_BASE ((volatile unsigned int *)0x42000160)
+#define SER0_BASE    ((volatile unsigned char *)0x7c0003f8)
 
 static __inline__ void putc(char c)
 {
-	while (*((volatile unsigned int *)(BASE + 0x18)) & 8);
-	*((volatile unsigned int *)(BASE)) = c;
+	if (machine_is_netwinder()) {
+		while ((SER0_BASE[5] & 0x60) != 0x60);
+		SER0_BASE[0] = c;
+	} else {
+		while (DC21285_BASE[6] & 8);
+		DC21285_BASE[0] = c;
+	}
 }
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-nexuspci/system.h linux.18p17/include/asm-arm/arch-nexuspci/system.h
--- linux.vanilla/include/asm-arm/arch-nexuspci/system.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-nexuspci/system.h	Fri Sep 15 23:32:34 2000
@@ -6,6 +6,8 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
+
 extern __inline__ void arch_reset(char mode)
 {
 	/*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-nexuspci/time.h linux.18p17/include/asm-arm/arch-nexuspci/time.h
--- linux.vanilla/include/asm-arm/arch-nexuspci/time.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-nexuspci/time.h	Fri Sep 15 23:32:34 2000
@@ -38,6 +38,9 @@
 	readb(UART_BASE + 0x14);
 
 	do_timer(regs);	
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-rpc/hardware.h linux.18p17/include/asm-arm/arch-rpc/hardware.h
--- linux.vanilla/include/asm-arm/arch-rpc/hardware.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-rpc/hardware.h	Fri Sep 15 23:32:34 2000
@@ -41,7 +41,7 @@
 #define FLUSH_BASE		0xdf000000
 
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 /*
  * for use with inb/outb
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-rpc/keyboard.h linux.18p17/include/asm-arm/arch-rpc/keyboard.h
--- linux.vanilla/include/asm-arm/arch-rpc/keyboard.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-rpc/keyboard.h	Fri Sep 15 23:32:34 2000
@@ -28,32 +28,3 @@
 #define kbd_enable_irq()		enable_irq(IRQ_KEYBOARDRX)
 
 #define SYSRQ_KEY	13
-
-/* resource allocation */
-#define KEYBOARD_IRQ                   1
-#define kbd_request_region() request_region(0x60, 16, "keyboard")
-#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
-                                             "keyboard", NULL)
-
-/* How to access the keyboard macros on this platform.  */
-#define kbd_read_input() inb(KBD_DATA_REG)
-#define kbd_read_status() inb(KBD_STATUS_REG)
-#define kbd_write_output(val) outb(val, KBD_DATA_REG)
-#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
-
-/* Some stoneage hardware needs delays after some operations.  */
-#define kbd_pause() do { } while(0)
-
-#define keyboard_setup()						\
-	request_region(0x60, 16, "keyboard")
-
-/*
- * Machine specific bits for the PS/2 driver
- */
-
-#define AUX_IRQ 12
-
-#define aux_request_irq(hand, dev_id)					\
-	request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
-
-#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-rpc/system.h linux.18p17/include/asm-arm/arch-rpc/system.h
--- linux.vanilla/include/asm-arm/arch-rpc/system.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-rpc/system.h	Fri Sep 15 23:32:34 2000
@@ -1,23 +1,27 @@
 /*
  * linux/include/asm-arm/arch-rpc/system.h
  *
- * Copyright (c) 1996 Russell King
+ * Copyright (c) 1996-1999 Russell King
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
 #include <asm/iomd.h>
+#include <asm/io.h>
 
-#define arch_reset(mode) {						\
-	extern void ecard_reset (int card);				\
-	outb (0, IOMD_ROMCR0);						\
-	ecard_reset(-1);						\
-	cli();								\
-	__asm__ __volatile__("msr  spsr, r1;"				\
-			     "mcr  p15, 0, %0, c1, c0, 0;"		\
-			     "movs pc, #0"				\
-			 : 						\
-			 : "r" (processor.u.armv3v4.reset()));		\
-	}
+#define arch_do_idle()		processor.u.armv3v4._do_idle()
 
+extern __inline__ void arch_reset(char mode)
+{
+	extern void ecard_reset(int card);
+
+	ecard_reset(-1);
+
+	outb(0, IOMD_ROMCR0);
+
+	/*
+	 * Jump into the ROM
+	 */
+	processor.u.armv3v4.reset(0);
+}
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/arch-rpc/time.h linux.18p17/include/asm-arm/arch-rpc/time.h
--- linux.vanilla/include/asm-arm/arch-rpc/time.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/arch-rpc/time.h	Fri Sep 15 23:32:34 2000
@@ -151,7 +151,7 @@
 	 */
 	buf[4] &= 0x1f;
 	buf[3] &= 0x3f;
-printk("Year %4d mon %02X day %02X hour %02X min %02X sec %02X\n", year, buf[4], buf[3], buf[2], buf[1], buf[0]);
+
 	for (i = 0; i < 5; i++)
 		BCD_TO_BIN(buf[i]);
 
@@ -175,6 +175,9 @@
 		else
 			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+
+	if (!user_mode(regs))
+		do_profile(instruction_pointer(regs));
 }
 
 static struct irqaction timerirq = {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/assembler.h linux.18p17/include/asm-arm/assembler.h
--- linux.vanilla/include/asm-arm/assembler.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/assembler.h	Fri Sep 15 23:32:34 2000
@@ -7,28 +7,5 @@
  * Do not include any C declarations in this file - it is included by
  * assembler source.
  */
-
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
+#include <asm/proc/ptrace.h>
 #include <asm/proc/assembler.h>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/checksum.h linux.18p17/include/asm-arm/checksum.h
--- linux.vanilla/include/asm-arm/checksum.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/checksum.h	Fri Sep 15 23:32:34 2000
@@ -117,12 +117,12 @@
 		   unsigned short proto, unsigned int sum)
 {
 	__asm__("
-	adds	%0, %0, %1
-	adcs	%0, %0, %2
+	adds	%0, %1, %2
 	adcs	%0, %0, %3
+	adcs	%0, %0, %4
 	adc	%0, %0, #0"
 	: "=&r"(sum)
-	: "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256), "0" (sum));
+	: "r" (sum), "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256));
 	return sum;
 }	
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/dec21285.h linux.18p17/include/asm-arm/dec21285.h
--- linux.vanilla/include/asm-arm/dec21285.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/dec21285.h	Fri Sep 15 23:32:34 2000
@@ -28,6 +28,12 @@
 #define CSR_PCICSRIOBASE	DC21285_IO(0x0014)
 #define CSR_PCISDRAMBASE	DC21285_IO(0x0018)
 #define CSR_PCIROMBASE		DC21285_IO(0x0030)
+#define CSR_MBOX0               DC21285_IO(0x0050)
+#define CSR_MBOX1               DC21285_IO(0x0054)
+#define CSR_MBOX2               DC21285_IO(0x0058)
+#define CSR_MBOX3               DC21285_IO(0x005c)
+#define CSR_DOORBELL            DC21285_IO(0x0060)
+#define CSR_DOORBELL_SETUP      DC21285_IO(0x0064)
 #define CSR_ROMWRITEREG		DC21285_IO(0x0068)
 #define CSR_CSRBASEMASK		DC21285_IO(0x00f8)
 #define CSR_CSRBASEOFFSET	DC21285_IO(0x00fc)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/elf.h linux.18p17/include/asm-arm/elf.h
--- linux.vanilla/include/asm-arm/elf.h	Fri Apr 21 12:46:48 2000
+++ linux.18p17/include/asm-arm/elf.h	Fri Sep 15 23:32:34 2000
@@ -47,7 +47,8 @@
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports. */
 
-#define ELF_HWCAP	(armidlist[armidindex].hwcap)
+extern unsigned int elf_hwcap;
+#define ELF_HWCAP	(elf_hwcap)
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/hardware.h linux.18p17/include/asm-arm/hardware.h
--- linux.vanilla/include/asm-arm/hardware.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/hardware.h	Fri Sep 15 23:32:34 2000
@@ -12,7 +12,7 @@
 #include <asm/arch/hardware.h>
 
 #ifdef HAS_EXPMASK
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __EXPMASK(offset)	(((volatile unsigned char *)EXPMASK_BASE)[offset])
 #else
 #define __EXPMASK(offset)	offset
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/io.h linux.18p17/include/asm-arm/io.h
--- linux.vanilla/include/asm-arm/io.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/io.h	Fri Sep 15 23:32:34 2000
@@ -16,22 +16,14 @@
 
 #ifdef __KERNEL__
 
+#include <linux/types.h>
+
 #ifndef NULL
 #define NULL	((void *) 0)
 #endif
 
-extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags);
-
-/*
- * String version of IO memory access ops:
- */
-extern void _memcpy_fromio(void *, unsigned long, unsigned long);
-extern void _memcpy_toio(unsigned long, const void *, unsigned long);
-extern void _memset_io(unsigned long, int, unsigned long);
-
-#define memcpy_fromio(to,from,len)	_memcpy_fromio((to),(unsigned long)(from),(len))
-#define memcpy_toio(to,from,len)	_memcpy_toio((unsigned long)(to),(from),(len))
-#define memset_io(addr,c,len)		_memset_io((unsigned long)(addr),(c),(len))
+extern void * __ioremap(unsigned long offset, size_t size, unsigned long flags);
+extern void __iounmap(void *addr);
 
 #endif
 
@@ -191,18 +183,44 @@
 
 #endif
 
-#ifndef ARCH_READWRITE
-
 /* for panic */
 #include <linux/kernel.h>
 
+#ifndef readb
 #define readb(p)	(panic("readb called, but not implemented"),0)
+#endif
+#ifndef readw
 #define readw(p)	(panic("readw called, but not implemented"),0)
+#endif
+#ifndef readl
 #define readl(p)	(panic("readl called, but not implemented"),0)
+#endif
+#ifndef writeb
 #define writeb(v,p)	panic("writeb called, but not implemented")
+#endif
+#ifndef writew
 #define writew(v,p)	panic("writew called, but not implemented")
+#endif
+#ifndef writel
 #define writel(v,p)	panic("writel called, but not implemented")
+#endif
+
+/*
+ * String version of IO memory access ops:
+ */
+#ifndef memcpy_fromio
+extern void _memcpy_fromio(void *, unsigned long, unsigned long);
+#define memcpy_fromio(to,from,len)	_memcpy_fromio((to),(unsigned long)(from),(len))
+#endif
+
+#ifndef memcpy_toio
+extern void _memcpy_toio(unsigned long, const void *, unsigned long);
+#define memcpy_toio(to,from,len)	_memcpy_toio((unsigned long)(to),(from),(len))
+#endif
 
+#ifndef memset_io
+extern void _memset_io(unsigned long, int, unsigned long);
+#define memset_io(addr,c,len)		_memset_io((unsigned long)(addr),(c),(len))
 #endif
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/ioc.h linux.18p17/include/asm-arm/ioc.h
--- linux.vanilla/include/asm-arm/ioc.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/ioc.h	Fri Sep 15 23:32:34 2000
@@ -5,7 +5,7 @@
 
 #ifndef IOC_CONTROL
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __IOC(offset)	(IOC_BASE + (offset >> 2))
 #else
 #define __IOC(offset)	offset
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/iomd.h linux.18p17/include/asm-arm/iomd.h
--- linux.vanilla/include/asm-arm/iomd.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/iomd.h	Fri Sep 15 23:32:34 2000
@@ -1,5 +1,13 @@
+/*
+ * linux/include/asm-arm/iomd.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * This file contains information out the IOMD ASIC used in the
+ * Acorn RiscPC and subsequently integrated into the CLPS7500 chips.
+ */
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 #define __IOMD(offset)	(IO_IOMD_BASE + (offset >> 2))
 #else
 #define __IOMD(offset)	offset
@@ -169,7 +177,7 @@
 #define VDMA_START	IOMD_VIDSTART
 #define VDMA_END	IOMD_VIDEND
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 extern unsigned int vram_half_sam;
 #define video_set_dma(start,end,offset)				\
 do {								\
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/irq.h linux.18p17/include/asm-arm/irq.h
--- linux.vanilla/include/asm-arm/irq.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/irq.h	Fri Sep 15 23:32:34 2000
@@ -24,8 +24,5 @@
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
-#define __STR(x) #x
-#define STR(x) __STR(x)
-
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/memc.h linux.18p17/include/asm-arm/memc.h
--- linux.vanilla/include/asm-arm/memc.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/memc.h	Fri Sep 15 23:32:34 2000
@@ -4,7 +4,7 @@
 #define VDMA_START	1
 #define VDMA_END	2
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 extern void memc_write(unsigned int reg, unsigned long val);
 
 #define video_set_dma(start,end,offset)				\
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/page.h linux.18p17/include/asm-arm/page.h
--- linux.vanilla/include/asm-arm/page.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/page.h	Sat Sep  2 19:32:21 2000
@@ -19,4 +19,17 @@
 /* void *__va(unsigned long x) */
 #define __va(x)			((void *)(__phys_to_virt((unsigned long)(x))))
 
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/assembler.h linux.18p17/include/asm-arm/proc-armo/assembler.h
--- linux.vanilla/include/asm-arm/proc-armo/assembler.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/assembler.h	Fri Sep 15 23:32:34 2000
@@ -6,54 +6,30 @@
  * This file contains arm architecture specific defines
  * for the different processors
  */
-
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
-#define N_BIT	(1 << 31)
-#define Z_BIT	(1 << 30)
-#define C_BIT	(1 << 29)
-#define V_BIT	(1 << 28)
-
-#define PCMASK	0xfc000003
-
-#ifdef __ASSEMBLER__
-
-#define I_BIT	(1 << 27)
-#define F_BIT	(1 << 26)
-
-#define MODE_USR	0
-#define MODE_FIQ	1
-#define MODE_IRQ	2
-#define MODE_SVC	3
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
+
+#define MODE_USR	USR26_MODE
+#define MODE_FIQ	FIQ26_MODE
+#define MODE_IRQ	IRQ26_MODE
+#define MODE_SVC	SVC26_MODE
 
 #define DEFAULT_FIQ	MODE_FIQ
 
+#ifdef __STDC__
 #define LOADREGS(cond, base, reglist...)\
 	ldm##cond	base,reglist^
 
 #define RETINSTR(instr, regs...)\
 	instr##s	regs
+#else
+#define LOADREGS(cond, base, reglist...)\
+	ldm/**/cond	base,reglist^
+
+#define RETINSTR(instr, regs...)\
+	instr/**/s	regs
+#endif
 
 #define MODENOP\
 	mov	r0, r0
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/pgtable.h linux.18p17/include/asm-arm/proc-armo/pgtable.h
--- linux.vanilla/include/asm-arm/proc-armo/pgtable.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/pgtable.h	Fri Sep 15 23:32:34 2000
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/proc-armo/pgtable.h
  *
- * Copyright (C) 1995, 1996 Russell King
+ * Copyright (C) 1995-1999 Russell King
  * Modified 18/19-Oct-1997 for two-level page table
  */
 #ifndef __ASM_PROC_PGTABLE_H
@@ -21,6 +21,8 @@
 #define flush_cache_range(mm,start,end)		do { } while (0)
 #define flush_cache_page(vma,vmaddr)		do { } while (0)
 #define flush_page_to_ram(page)			do { } while (0)
+#define clean_cache_range(_start,_end)		do { } while (0)
+#define clean_cache_area(_start,_size)		do { } while (0)
 #define flush_icache_range(start,end)		do { } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/processor.h linux.18p17/include/asm-arm/proc-armo/processor.h
--- linux.vanilla/include/asm-arm/proc-armo/processor.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/processor.h	Fri Sep 15 23:32:34 2000
@@ -26,11 +26,12 @@
 	unsigned long r7;
 	unsigned long r8;
 	unsigned long r9;
+	unsigned long sl;
 	unsigned long fp;
 	unsigned long pc;
 };
 
-#define INIT_CSS (struct context_save_struct){ 0, 0, 0, 0, 0, 0, 0, SVC26_MODE }
+#define INIT_CSS (struct context_save_struct){ 0, 0, 0, 0, 0, 0, 0, 0, SVC26_MODE }
 
 typedef struct {
 	void (*put_byte)(void);			/* Special calling convention */
@@ -43,7 +44,7 @@
 	unsigned long (*copy_to_user)(void *to, const void *from, unsigned long sz);
 	unsigned long (*clear_user)(void *addr, unsigned long sz);
 	unsigned long (*strncpy_from_user)(char *to, const char *from, unsigned long sz);
-	unsigned long (*strlen_user)(const char *s);
+	unsigned long (*strnlen_user)(const char *s, long n);
 } uaccess_t;
 
 extern uaccess_t uaccess_user, uaccess_kernel;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/ptrace.h linux.18p17/include/asm-arm/proc-armo/ptrace.h
--- linux.vanilla/include/asm-arm/proc-armo/ptrace.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/ptrace.h	Fri Sep 15 23:32:34 2000
@@ -1,12 +1,26 @@
 /*
  * linux/include/asm-arm/proc-armo/ptrace.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROC_PTRACE_H
 #define __ASM_PROC_PTRACE_H
 
+#define USR26_MODE	0x00
+#define FIQ26_MODE	0x01
+#define IRQ26_MODE	0x02
+#define SVC26_MODE	0x03
+#define MODE_MASK	0x03
+#define F_BIT		(1 << 26)
+#define I_BIT		(1 << 27)
+#define CC_V_BIT	(1 << 28)
+#define CC_C_BIT	(1 << 29)
+#define CC_Z_BIT	(1 << 30)
+#define CC_N_BIT	(1 << 31)
+#define PCMASK		0xfc000003
+
+#ifndef __ASSEMBLY__
+
 /* this struct defines the way the registers are stored on the
    stack during a system call. */
 
@@ -30,19 +44,9 @@
 #define ARM_r2		uregs[2]
 #define ARM_r1		uregs[1]
 #define ARM_r0		uregs[0]
-#define ARM_ORIG_r0	uregs[16] /* -1 */
+#define ARM_ORIG_r0	uregs[16]
 
-#define USR26_MODE	0x00
-#define FIQ26_MODE	0x01
-#define IRQ26_MODE	0x02
-#define SVC26_MODE	0x03
-#define MODE_MASK	0x03
-#define F_BIT		(1 << 26)
-#define I_BIT		(1 << 27)
-#define CC_V_BIT	(1 << 28)
-#define CC_C_BIT	(1 << 29)
-#define CC_Z_BIT	(1 << 30)
-#define CC_N_BIT	(1 << 31)
+#ifdef __KERNEL__
 
 #define processor_mode(regs) \
 	((regs)->ARM_pc & MODE_MASK)
@@ -70,11 +74,21 @@
  */
 static inline int valid_user_regs(struct pt_regs *regs)
 {
-	if (!user_mode(regs) || regs->ARM_pc & (F_BIT | I_BIT))
+	if (user_mode(regs) &&
+	    (regs->ARM_pc & (F_BIT | I_BIT)) == 0)
 		return 1;
 
+	/*
+	 * force it to be something sensible
+	 */
+	regs->ARM_pc &= ~(MODE_MASK | F_BIT | I_BIT);
+
 	return 0;
 }
+
+#endif	/* __KERNEL__ */
+
+#endif	/* __ASSEMBLY__ */
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/semaphore.h linux.18p17/include/asm-arm/proc-armo/semaphore.h
--- linux.vanilla/include/asm-arm/proc-armo/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/semaphore.h	Fri Sep 15 23:32:34 2000
@@ -14,13 +14,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%0]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%0]
-	mov	lr, pc, lsr #28
-	teqp	r0, lr, lsl #28
+	orrmi	r0, r0, #0x80000000	@ set N
+	teqp	r0, #0
 	movmi	r0, %0
 	blmi	" SYMBOL_NAME_STR(__down_failed)
 		:
@@ -39,14 +39,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%1]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%1]
-	mov	lr, pc, lsr #28
 	orrmi	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	teqp	r0, #0
 	movmi	r0, %1
 	movpl	r0, #0
 	blmi	" SYMBOL_NAME_STR(__down_interruptible_failed) "
@@ -64,14 +63,13 @@
 	@ atomic down operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%1]
+	and	r0, r0, #0x0c000003
 	subs	lr, lr, #1
 	str	lr, [%1]
-	mov	lr, pc, lsr #28
 	orrmi	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	teqp	r0, #0
 	movmi	r0, %1
 	movpl	r0, #0
 	blmi	" SYMBOL_NAME_STR(__down_trylock_failed) "
@@ -94,14 +92,13 @@
 	@ atomic up operation
 	mov	r0, pc
 	orr	lr, r0, #0x08000000
-	and	r0, r0, #0x0c000003
 	teqp	lr, #0
 	ldr	lr, [%0]
+	and	r0, r0, #0x0c000003
 	adds	lr, lr, #1
 	str	lr, [%0]
-	mov	lr, pc, lsr #28
-	orrls	r0, r0, #0x80000000	@ set N
-	teqp	r0, lr, lsl #28
+	orrle	r0, r0, #0x80000000	@ set N
+	teqp	r0, #0
 	movmi	r0, %0
 	blmi	" SYMBOL_NAME_STR(__up_wakeup)
 		:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/system.h linux.18p17/include/asm-arm/proc-armo/system.h
--- linux.vanilla/include/asm-arm/proc-armo/system.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/system.h	Fri Sep 15 23:32:34 2000
@@ -105,7 +105,7 @@
 "	bic	%0, %0, #0x0c000000\n"			\
 "	orr	%0, %0, %1\n"				\
 "	teqp	%0, #0\n"				\
-	  : "=r" (temp)					\
+	  : "=&r" (temp)				\
 	  : "r" (x)					\
 	  : "memory");					\
 	} while (0)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armo/uaccess.h linux.18p17/include/asm-arm/proc-armo/uaccess.h
--- linux.vanilla/include/asm-arm/proc-armo/uaccess.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armo/uaccess.h	Fri Sep 15 23:32:34 2000
@@ -136,5 +136,5 @@
 #define __do_strncpy_from_user(dst,src,count,res)			\
 	(res) = current->tss.uaccess->strncpy_from_user(dst,src,count)
 
-#define __do_strlen_user(s,res)						\
-	(res) = current->tss.uaccess->strlen_user(s)
+#define __do_strnlen_user(s,n,res)					\
+	(res) = current->tss.uaccess->strnlen_user(s,n)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/assembler.h linux.18p17/include/asm-arm/proc-armv/assembler.h
--- linux.vanilla/include/asm-arm/proc-armv/assembler.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/assembler.h	Fri Sep 15 23:32:34 2000
@@ -6,79 +6,81 @@
  * This file contains arm architecture specific defines
  * for the different processors
  */
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
 
-/*
- * LOADREGS: multiple register load (ldm) with pc in register list
- *		(takes account of ARM6 not using ^)
- *
- * RETINSTR: return instruction: adds the 's' in at the end of the
- *		instruction if this is not an ARM6
- *
- * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
- *		implicitly
- *
- * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
- *		implicitly with ldm ... ^ or movs.
- *
- * These next two need thinking about - can't easily use stack... (see system.S)
- * DISABLEIRQS: disable IRQS in SVC mode
- *
- * ENABLEIRQS: enable IRQS in SVC mode
- *
- * USERMODE: switch to USER mode
- *
- * SVCMODE: switch to SVC mode
- */
-
-#define N_BIT	(1 << 31)
-#define Z_BIT	(1 << 30)
-#define C_BIT	(1 << 29)
-#define V_BIT	(1 << 28)
-
-#define PCMASK	0
-
-#ifdef __ASSEMBLER__
-
-#define I_BIT	(1 << 7)
-#define F_BIT	(1 << 6)
-
-#define MODE_FIQ26	0x01
-#define MODE_FIQ32	0x11
+#define MODE_USR	USR_MODE
+#define MODE_FIQ	FIQ_MODE
+#define MODE_IRQ	IRQ_MODE
+#define MODE_SVC	SVC_MODE
 
-#define DEFAULT_FIQ	MODE_FIQ32
+#define DEFAULT_FIQ	MODE_FIQ
 
+/*
+ * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc})
+ * RETINSTR - return instruction (eg, mov pc, lr)
+ */
+#ifdef __STDC__
 #define LOADREGS(cond, base, reglist...)\
 	ldm##cond	base,reglist
 
 #define RETINSTR(instr, regs...)\
 	instr	regs
+#else
+#define LOADREGS(cond, base, reglist...)\
+	ldm/**/cond	base,reglist
 
+#define RETINSTR(instr, regs...)\
+	instr		regs
+#endif
+
+/*
+ * No nop required after mode change
+ */
 #define MODENOP
 
+/*
+ * Change to `mode'
+ */
 #define MODE(savereg,tmpreg,mode) \
 	mrs	savereg, cpsr; \
 	bic	tmpreg, savereg, $0x1f; \
 	orr	tmpreg, tmpreg, $mode; \
 	msr	cpsr, tmpreg
 
+/*
+ * Restore mode
+ */
 #define RESTOREMODE(savereg) \
 	msr	cpsr, savereg
 	
+/*
+ * save interrupt state (uses stack)
+ */
 #define SAVEIRQS(tmpreg)\
 	mrs	tmpreg, cpsr; \
 	str	tmpreg, [sp, $-4]!
 
+/*
+ * restore interrupt state (uses stack)
+ */
 #define RESTOREIRQS(tmpreg)\
 	ldr	tmpreg, [sp], $4; \
 	msr	cpsr, tmpreg
 
+/*
+ * disable IRQs
+ */
 #define DISABLEIRQS(tmpreg)\
 	mrs	tmpreg , cpsr; \
 	orr	tmpreg , tmpreg , $I_BIT; \
 	msr	cpsr, tmpreg
 
+/*
+ * enable IRQs
+ */
 #define ENABLEIRQS(tmpreg)\
 	mrs	tmpreg , cpsr; \
 	bic	tmpreg , tmpreg , $I_BIT; \
 	msr	cpsr, tmpreg
-#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/domain.h linux.18p17/include/asm-arm/proc-armv/domain.h
--- linux.vanilla/include/asm-arm/proc-armv/domain.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-arm/proc-armv/domain.h	Fri Sep 15 23:32:34 2000
@@ -0,0 +1,46 @@
+/*
+ * linux/include/asm-arm/proc-armv/domain.h
+ *
+ * Copyright (C) 1999 Russell King.
+ */
+#ifndef __ASM_PROC_DOMAIN_H
+#define __ASM_PROC_DOMAIN_H
+
+/*
+ * Domain numbers
+ *
+ *  DOMAIN_IO     - domain 2 includes all IO only
+ *  DOMAIN_KERNEL - domain 1 includes all kernel memory only
+ *  DOMAIN_USER   - domain 0 includes all user memory only
+ */
+#define DOMAIN_USER	0
+#define DOMAIN_KERNEL	1
+#define DOMAIN_TABLE	1
+#define DOMAIN_IO	2
+
+/*
+ * Domain types
+ */
+#define DOMAIN_NOACCESS	0
+#define DOMAIN_CLIENT	1
+#define DOMAIN_MANAGER	3
+
+#define domain_val(dom,type)	((type) << 2*(dom))
+
+#define set_domain(x)					\
+	do {						\
+	__asm__ __volatile__(				\
+	"mcr	p15, 0, %0, c3, c0	@ set domain"	\
+	  : : "r" (x));					\
+	} while (0)
+
+#define modify_domain(dom,type)				\
+	do {						\
+	unsigned int domain = current->tss.domain;	\
+	domain &= ~domain_val(dom, DOMAIN_MANAGER);	\
+	domain |= domain_val(dom, type);		\
+	current->tss.domain = domain;			\
+	set_domain(current->tss.domain);		\
+	} while (0)
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/pgtable.h linux.18p17/include/asm-arm/proc-armv/pgtable.h
--- linux.vanilla/include/asm-arm/proc-armv/pgtable.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/pgtable.h	Fri Sep 15 23:32:34 2000
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/proc-armv/pgtable.h
  *
- * Copyright (C) 1995, 1996, 1997 Russell King
+ * Copyright (C) 1995-1999 Russell King
  *
  * 12-Jan-1997	RMK	Altered flushing routines to use function pointers
  *			now possible to combine ARM6, ARM7 and StrongARM versions.
@@ -12,6 +12,7 @@
 #define __ASM_PROC_PGTABLE_H
 
 #include <asm/arch/memory.h>		/* For TASK_SIZE */
+#include <asm/proc/domain.h>
 
 #define LIBRARY_TEXT_START 0x0c000000
 
@@ -149,18 +150,7 @@
 #define VMALLOC_OFFSET	  (8*1024*1024)
 #define VMALLOC_START	  (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
-#define VMALLOC_END       (PAGE_OFFSET + 0x10000000)
-
-
-/*
- * Domains
- */
-#define DOMAIN_USER	0
-#define DOMAIN_KERNEL	1
-#define DOMAIN_TABLE	1
-#define DOMAIN_IO	2
-
-
+#define VMALLOC_END       (PAGE_OFFSET + 0x17f00000)
 
 #undef TEST_VERIFY_AREA
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/processor.h linux.18p17/include/asm-arm/proc-armv/processor.h
--- linux.vanilla/include/asm-arm/proc-armv/processor.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/processor.h	Fri Sep 15 23:32:34 2000
@@ -12,6 +12,8 @@
 #ifndef __ASM_PROC_PROCESSOR_H
 #define __ASM_PROC_PROCESSOR_H
 
+#include <asm/proc/domain.h>
+
 #define KERNEL_STACK_SIZE	PAGE_SIZE
 
 struct context_save_struct {
@@ -22,14 +24,21 @@
 	unsigned long r7;
 	unsigned long r8;
 	unsigned long r9;
+	unsigned long sl;
 	unsigned long fp;
 	unsigned long pc;
 };
 
-#define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0 }
+#define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+#define EXTRA_THREAD_STRUCT						\
+	unsigned int	domain;
+
+#define EXTRA_THREAD_STRUCT_INIT					\
+	, domain_val(DOMAIN_USER, DOMAIN_CLIENT) |			\
+	  domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |			\
+	  domain_val(DOMAIN_IO, DOMAIN_CLIENT)
 
-#define EXTRA_THREAD_STRUCT
-#define EXTRA_THREAD_STRUCT_INIT
 #define SWAPPER_PG_DIR	(((unsigned long)swapper_pg_dir) - PAGE_OFFSET)
 
 #define start_thread(regs,pc,sp)					\
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/ptrace.h linux.18p17/include/asm-arm/proc-armv/ptrace.h
--- linux.vanilla/include/asm-arm/proc-armv/ptrace.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/ptrace.h	Fri Sep 15 23:32:34 2000
@@ -1,12 +1,33 @@
 /*
  * linux/include/asm-arm/proc-armv/ptrace.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROC_PTRACE_H
 #define __ASM_PROC_PTRACE_H
 
+#define USR26_MODE	0x00
+#define FIQ26_MODE	0x01
+#define IRQ26_MODE	0x02
+#define SVC26_MODE	0x03
+#define USR_MODE	0x10
+#define FIQ_MODE	0x11
+#define IRQ_MODE	0x12
+#define SVC_MODE	0x13
+#define ABT_MODE	0x17
+#define UND_MODE	0x1b
+#define SYSTEM_MODE	0x1f
+#define MODE_MASK	0x1f
+#define F_BIT		0x40
+#define I_BIT		0x80
+#define CC_V_BIT	(1 << 28)
+#define CC_C_BIT	(1 << 29)
+#define CC_Z_BIT	(1 << 30)
+#define CC_N_BIT	(1 << 31)
+#define PCMASK		0
+
+#ifndef __ASSEMBLY__
+
 /* this struct defines the way the registers are stored on the
    stack during a system call. */
 
@@ -31,35 +52,12 @@
 #define ARM_r2		uregs[2]
 #define ARM_r1		uregs[1]
 #define ARM_r0		uregs[0]
-#define ARM_ORIG_r0	uregs[17] /* -1 */
+#define ARM_ORIG_r0	uregs[17]
 
-#define USR26_MODE	0x00
-#define FIQ26_MODE	0x01
-#define IRQ26_MODE	0x02
-#define SVC26_MODE	0x03
-#define USR_MODE	0x10
-#define FIQ_MODE	0x11
-#define IRQ_MODE	0x12
-#define SVC_MODE	0x13
-#define ABT_MODE	0x17
-#define UND_MODE	0x1b
-#define SYSTEM_MODE	0x1f
-#define MODE_MASK	0x1f
-#define F_BIT		0x40
-#define I_BIT		0x80
-#define CC_V_BIT	(1 << 28)
-#define CC_C_BIT	(1 << 29)
-#define CC_Z_BIT	(1 << 30)
-#define CC_N_BIT	(1 << 31)
+#ifdef __KERNEL__
 
-#if 0 /* GCC/egcs should be able to optimise this, IMHO */
-#define user_mode(regs)	\
-	((((regs)->ARM_cpsr & MODE_MASK) == USR_MODE) || \
-	 (((regs)->ARM_cpsr & MODE_MASK) == USR26_MODE))
-#else
 #define user_mode(regs)	\
 	(((regs)->ARM_cpsr & 0xf) == 0)
-#endif
 
 #define processor_mode(regs) \
 	((regs)->ARM_cpsr & MODE_MASK)
@@ -81,8 +79,8 @@
  */
 static inline int valid_user_regs(struct pt_regs *regs)
 {
-	if ((regs->ARM_cpsr & 0xf) == 0 ||
-	    (regs->ARM_cpsr & (F_BIT|I_BIT)))
+	if ((regs->ARM_cpsr & 0xf) == 0 &&
+	    (regs->ARM_cpsr & (F_BIT|I_BIT)) == 0)
 		return 1;
 
 	/*
@@ -93,5 +91,8 @@
 	return 0;
 }
 
-#endif
+#endif	/* __KERNEL__ */
 
+#endif	/* __ASSEMBLY__ */
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/semaphore.h linux.18p17/include/asm-arm/proc-armv/semaphore.h
--- linux.vanilla/include/asm-arm/proc-armv/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/semaphore.h	Fri Sep 15 23:32:34 2000
@@ -16,12 +16,12 @@
 	@ atomic down operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	blmi	" SYMBOL_NAME_STR(__down_failed)
@@ -42,12 +42,12 @@
 	@ atomic down interruptible operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	movpl	r0, #0
@@ -68,12 +68,12 @@
 	@ atomic down try lock operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	subs	%1, %1, #1
-	orrmi	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrmi	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	movpl	r0, #0
@@ -100,12 +100,12 @@
 	@ atomic up operation
 	mrs	%0, cpsr
 	orr	%1, %0, #128		@ disable IRQs
-	bic	%0, %0, #0x80000000	@ clear N
 	msr	cpsr, %1
 	ldr	%1, [%2]
+	bic	%0, %0, #0x80000000	@ clear N
 	adds	%1, %1, #1
-	orrls	%0, %0, #0x80000000	@ set N
 	str	%1, [%2]
+	orrle	%0, %0, #0x80000000	@ set N
 	msr	cpsr, %0
 	movmi	r0, %2
 	blmi	" SYMBOL_NAME_STR(__up_wakeup)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/system.h linux.18p17/include/asm-arm/proc-armv/system.h
--- linux.vanilla/include/asm-arm/proc-armv/system.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/system.h	Fri Sep 15 23:32:34 2000
@@ -116,7 +116,7 @@
 "	bic	%0, %0, #192\n"					\
 "	orr	%0, %0, %1\n"					\
 "	msr	cpsr, %0"					\
-	  : "=r" (temp)						\
+	  : "=&r" (temp)					\
 	  : "r" (x)						\
 	  : "memory");			\
 	} while (0)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-armv/uaccess.h linux.18p17/include/asm-arm/proc-armv/uaccess.h
--- linux.vanilla/include/asm-arm/proc-armv/uaccess.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-armv/uaccess.h	Fri Sep 15 23:32:34 2000
@@ -2,36 +2,8 @@
  * linux/include/asm-arm/proc-armv/uaccess.h
  */
 
-/*
- * The fs functions are implemented on the ARMV3 and V4 architectures
- * using the domain register.
- *
- *  DOMAIN_IO     - domain 2 includes all IO only
- *  DOMAIN_KERNEL - domain 1 includes all kernel memory only
- *  DOMAIN_USER   - domain 0 includes all user memory only
- */
-
 #include <asm/hardware.h>
-
-#define DOMAIN_CLIENT	1
-#define DOMAIN_MANAGER	3
- 
-#define DOMAIN_USER_CLIENT	((DOMAIN_CLIENT) << 0)
-#define DOMAIN_USER_MANAGER	((DOMAIN_MANAGER) << 0)
-
-#define DOMAIN_KERNEL_CLIENT	((DOMAIN_CLIENT) << 2)
-#define DOMAIN_KERNEL_MANAGER	((DOMAIN_MANAGER) << 2)
-
-#define DOMAIN_IO_CLIENT	((DOMAIN_CLIENT) << 4)
-#define DOMAIN_IO_MANAGER	((DOMAIN_MANAGER) << 4)
-
-/*
- * When we want to access kernel memory in the *_user functions,
- * we change the domain register to KERNEL_DS, thus allowing
- * unrestricted access
- */
-#define KERNEL_DOMAIN	(DOMAIN_USER_CLIENT | DOMAIN_KERNEL_MANAGER | DOMAIN_IO_CLIENT)
-#define USER_DOMAIN	(DOMAIN_USER_CLIENT | DOMAIN_KERNEL_CLIENT  | DOMAIN_IO_CLIENT)
+#include <asm/proc/domain.h>
 
 /*
  * Note that this is actually 0x1,0000,0000
@@ -48,8 +20,7 @@
 {
 	current->addr_limit = fs;
 
-	__asm__ __volatile__("mcr	p15, 0, %0, c3, c0" :
-		: "r" (fs ? USER_DOMAIN : KERNEL_DOMAIN));
+	modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
 }
 
 /* We use 33-bit arithmetic here... */
@@ -201,6 +172,6 @@
 #define __do_strncpy_from_user(dst,src,count,res)		\
 	(res) = __arch_strncpy_from_user(dst,src,count)
 
-extern unsigned long __arch_strlen_user(const char *s);
-#define __do_strlen_user(s,res)					\
-	(res) = __arch_strlen_user(s)
+extern unsigned long __arch_strnlen_user(const char *s, long n);
+#define __do_strnlen_user(s,n,res)				\
+	(res) = __arch_strnlen_user(s,n)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/proc-fns.h linux.18p17/include/asm-arm/proc-fns.h
--- linux.vanilla/include/asm-arm/proc-fns.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/proc-fns.h	Fri Sep 15 23:32:34 2000
@@ -86,7 +86,7 @@
 			/*
 			 * Special stuff for a reset
 			 */
-			unsigned long (*reset)(void);
+			unsigned long (*reset)(unsigned long addr);
 			/*
 			 * flush an icached page
 			 */
@@ -99,6 +99,14 @@
 			 * purge cached data without (necessarily) writing it back
 			 */
 			void (*_cache_purge_area)(unsigned long start, unsigned long end);
+			/*
+			 * Unused
+			 */
+			void (*unused1)(void);
+			/*
+			 * Idle the processor
+			 */
+			int (*_do_idle)(void);
 		} armv3v4;
 		struct {
 			/* MEMC
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/processor.h linux.18p17/include/asm-arm/processor.h
--- linux.vanilla/include/asm-arm/processor.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/processor.h	Fri Sep 15 23:32:34 2000
@@ -26,10 +26,9 @@
 
 #ifdef __KERNEL__
 
-#include <asm/assembler.h> 
-
 #define NR_DEBUGS	5
 
+#include <asm/ptrace.h>
 #include <asm/arch/processor.h>
 #include <asm/proc/processor.h>
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/procinfo.h linux.18p17/include/asm-arm/procinfo.h
--- linux.vanilla/include/asm-arm/procinfo.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/procinfo.h	Fri Sep 15 23:32:34 2000
@@ -1,34 +1,45 @@
 /*
  * linux/include/asm-arm/procinfo.h
  *
- * Copyright (C) 1996 Russell King
+ * Copyright (C) 1996-1999 Russell King
  */
-
 #ifndef __ASM_PROCINFO_H
 #define __ASM_PROCINFO_H
 
-#include <asm/proc-fns.h>
+#ifndef __ASSEMBLY__
 
-#ifndef __ASSEMBLER__
+#include <asm/proc-fns.h>
 
-#define HWCAP_SWP	(1 << 0)
-#define HWCAP_HALF	(1 << 1)
+struct proc_info_item {
+	const char	 *manufacturer;
+	const char	 *cpu_name;
+};
 
-struct armversions {
-	const unsigned long id;		/* Processor ID			*/
-	const unsigned long mask;	/* Processor ID mask		*/
-	const char *manu;		/* Manufacturer			*/
-	const char *name;		/* Processor name		*/
-	const char *arch_vsn;		/* Architecture version		*/
-	const char *elf_vsn;		/* ELF library version		*/
-	const int hwcap;		/* ELF HWCAP			*/
-	const struct processor *proc;	/* Processor-specific ASM	*/
+/*
+ * Note!  struct processor is always defined if we're
+ * using MULTI_CPU, otherwise this entry is unused,
+ * but still exists.  NOTE! This structure is used
+ * by assembler code!  Check:
+ *  arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S
+ */
+struct proc_info_list {
+	unsigned int	 cpu_val;
+	unsigned int	 cpu_mask;
+	unsigned long	 __cpu_mmu_flags;	/* used by head-armv.S */
+	unsigned long	 __cpu_flush;		/* used by head-armv.S */
+	const char	 *arch_name;
+	const char	 *elf_name;
+	unsigned int	 elf_hwcap;
+	struct proc_info_item *info;
+	struct processor *proc;
 };
 
-extern const struct armversions armidlist[];
-extern int armidindex;
+#endif	/* __ASSEMBLY__ */
 
-#endif
+#define HWCAP_SWP	1
+#define HWCAP_HALF	2
+#define HWCAP_THUMB	4
+#define HWCAP_26BIT	8	/* Play it safe */
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/resource.h linux.18p17/include/asm-arm/resource.h
--- linux.vanilla/include/asm-arm/resource.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/resource.h	Fri Sep 15 23:32:34 2000
@@ -29,7 +29,7 @@
 	{        0, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER },	\
-	{ NR_OPEN, NR_OPEN },				\
+	{ INR_OPEN, INR_OPEN },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/semaphore.h linux.18p17/include/asm-arm/semaphore.h
--- linux.vanilla/include/asm-arm/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/semaphore.h	Fri Sep  1 21:39:22 2000
@@ -16,6 +16,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 asmlinkage void __down_failed (void /* special register calling convention */);
 asmlinkage int  __down_interruptible_failed (void /* special register calling convention */);
 asmlinkage int  __down_failed_trylock(void  /* params in registers */);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/setup.h linux.18p17/include/asm-arm/setup.h
--- linux.vanilla/include/asm-arm/setup.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/setup.h	Fri Sep 15 23:32:34 2000
@@ -1,13 +1,26 @@
 /*
  * include/asm/setup.h
  *
- * Structure passed to kernel to tell it about the hardware it's running on
+ * Structure passed to kernel to tell it about the
+ * hardware it's running on.  See linux/Documentation/arm/Setup
+ * for more info.
  *
- * Copyright (C) 1997,1998 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
 #ifndef __ASMARM_SETUP_H
 #define __ASMARM_SETUP_H
 
+/*
+ * Usage:
+ *  - do not go blindly adding fields, add them at the end
+ *  - when adding fields, don't rely on the address until
+ *    a patch from me has been released
+ *  - unused fields should be zero (for future expansion)
+ *  - this structure is relatively short-lived - only
+ *    guaranteed to contain useful data in setup_arch()
+ */
+#define COMMAND_LINE_SIZE 1024
+
 struct param_struct {
     union {
 	struct {
@@ -33,6 +46,9 @@
 	    unsigned long initrd_start;		/* 64 */
 	    unsigned long initrd_size;		/* 68 */
 	    unsigned long rd_start;		/* 72 */
+	    unsigned long system_rev;		/* 76 */
+	    unsigned long system_serial_low;	/* 80 */
+	    unsigned long system_serial_high;	/* 84 */
 	} s;
 	char unused[256];
     } u1;
@@ -43,7 +59,7 @@
 	    char n[1024 - sizeof(unsigned long)];
 	} s;
     } u2;
-    char commandline[256];
+    char commandline[COMMAND_LINE_SIZE];
 };
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/smplock.h linux.18p17/include/asm-arm/smplock.h
--- linux.vanilla/include/asm-arm/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/smplock.h	Thu Aug 31 15:37:12 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/socket.h linux.18p17/include/asm-arm/socket.h
--- linux.vanilla/include/asm-arm/socket.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/socket.h	Fri Sep 15 23:32:34 2000
@@ -39,6 +39,7 @@
 #define SO_ATTACH_FILTER        26
 #define SO_DETACH_FILTER        27
 
+#ifdef __KERNEL__
 /* Socket types. */
 #define SOCK_STREAM	1		/* stream (connection) socket	*/
 #define SOCK_DGRAM	2		/* datagram (conn.less) socket	*/
@@ -50,5 +51,6 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#endif
 
 #endif /* _ASM_SOCKET_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/string.h linux.18p17/include/asm-arm/string.h
--- linux.vanilla/include/asm-arm/string.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/string.h	Fri Sep 15 23:32:34 2000
@@ -14,11 +14,24 @@
 #define __HAVE_ARCH_MEMCPY
 #define __HAVE_ARCH_MEMMOVE
 #define __HAVE_ARCH_MEMSET
+#define __HAVE_ARCH_MEMCHR
 
 #define __HAVE_ARCH_MEMZERO
-extern void memzero(void *ptr, int n);
 
-extern void memsetl (unsigned long *, unsigned long, int n);
+extern void __memzero(void *ptr, __kernel_size_t n);
+
+#define memset(p,v,n)							\
+	({								\
+		if ((n) != 0) {						\
+			if (__builtin_constant_p((v)) && (v) == 0)	\
+				__memzero((p),(n));			\
+			else						\
+				memset((p),(v),(n));			\
+		}							\
+		(p);							\
+	})
+
+#define memzero(p,n) ({ if ((n) != 0) __memzero((p),(n)); (p); })
 
 #endif
  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/system.h linux.18p17/include/asm-arm/system.h
--- linux.vanilla/include/asm-arm/system.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/system.h	Fri Sep 15 23:32:35 2000
@@ -35,7 +35,7 @@
 
 /*
  * Sort out a definition for machine_arch_type
- * The rules basically are:
+ * The rules are:
  * 1. If one architecture is selected, then all machine_is_xxx()
  *    are constant.
  * 2. If two or more architectures are selected, then the selected
@@ -118,16 +118,8 @@
 #define machine_arch_type	__machine_arch_type
 #endif
 
-/*
- * task_struct isn't always declared - forward-declare it here.
- */
-struct task_struct;
-
 #include <asm/proc-fns.h>
 
-extern void arm_malalignedptr(const char *, void *, volatile void *);
-extern void arm_invalidptr(const char *, int);
-
 #define xchg(ptr,x) \
 	((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
@@ -136,11 +128,13 @@
 /*
  * switch_to(prev, next) should switch from task `prev' to `next'
  * `prev' will never be the same as `next'.
- *
- * `next' and `prev' should be struct task_struct, but it isn't always defined
  */
 #define switch_to(prev,next,last) do { last = processor._switch_to(prev,next); } while (0)
 
+extern void arm_malalignedptr(const char *, void *, volatile void *);
+extern void arm_invalidptr(const char *, int);
+extern asmlinkage void __backtrace(void);
+
 /*
  * Include processor dependent parts
  */
@@ -151,8 +145,6 @@
 #define rmb() mb()
 #define wmb() mb()
 #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t");
-
-extern asmlinkage void __backtrace(void);
 
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/therm.h linux.18p17/include/asm-arm/therm.h
--- linux.vanilla/include/asm-arm/therm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-arm/therm.h	Fri Sep 15 23:32:35 2000
@@ -0,0 +1,28 @@
+/*
+ * linux/include/asm-arm/therm.h: Definitions for Dallas Semiconductor
+ *  DS1620 thermometer driver (as used in the Rebel.com NetWinder)
+ */
+#ifndef __ASM_THERM_H
+#define __ASM_THERM_H
+
+/* ioctl numbers for /dev/therm */
+#define CMD_SET_THERMOSTATE	0x53
+#define CMD_GET_THERMOSTATE	0x54
+#define CMD_GET_STATUS		0x56
+#define CMD_GET_TEMPERATURE	0x57
+#define CMD_SET_THERMOSTATE2	0x58
+#define CMD_GET_THERMOSTATE2	0x59
+#define CMD_GET_TEMPERATURE2	0x5a
+#define CMD_GET_FAN		0x5b
+#define CMD_SET_FAN		0x5c
+
+#define FAN_OFF			0
+#define FAN_ON			1
+#define FAN_ALWAYS_ON		2
+
+struct therm {
+	int hi;
+	int lo;
+};
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/types.h linux.18p17/include/asm-arm/types.h
--- linux.vanilla/include/asm-arm/types.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/types.h	Sat Sep  2 19:28:18 2000
@@ -41,6 +41,8 @@
 
 #define BITS_PER_LONG 32
 
+typedef u32 dma_addr_t;
+
 #endif /* __KERNEL__ */
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/uaccess.h linux.18p17/include/asm-arm/uaccess.h
--- linux.vanilla/include/asm-arm/uaccess.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/uaccess.h	Fri Sep 15 23:32:35 2000
@@ -121,12 +121,14 @@
 	return res;
 }
 
-extern __inline__ long strlen_user (const char *s)
+#define strlen_user(s)	strnlen_user(s, ~0UL >> 1)
+
+extern __inline__ long strnlen_user (const char *s, long n)
 {
 	unsigned long res = 0;
 
 	if (__addr_ok(s))
-		__do_strlen_user (s, res);
+		__do_strnlen_user (s, n, res);
 
 	return res;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/unaligned.h linux.18p17/include/asm-arm/unaligned.h
--- linux.vanilla/include/asm-arm/unaligned.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/unaligned.h	Fri Sep 15 23:32:35 2000
@@ -1,59 +1,115 @@
-#ifndef __ARM_UNALIGNED_H
-#define __ARM_UNALIGNED_H
+#ifndef __ASM_ARM_UNALIGNED_H
+#define __ASM_ARM_UNALIGNED_H
 
-#define get_unaligned(ptr) \
-	((__typeof__(*(ptr)))__get_unaligned((ptr), sizeof(*(ptr))))
+#include <asm/types.h>
 
-#define put_unaligned(val, ptr) \
-	__put_unaligned((unsigned long)(val), (ptr), sizeof(*(ptr)))
+extern int __bug_unaligned_x(void *ptr);
 
-extern void bad_unaligned_access_length (void);
-	
-extern inline unsigned long __get_unaligned(const void *ptr, size_t size)
+/*
+ * What is the most efficient way of loading/storing an unaligned value?
+ *
+ * That is the subject of this file.  Efficiency here is defined as
+ * minimum code size with minimum register usage for the common cases.
+ * It is currently not believed that long longs are common, so we
+ * trade efficiency for the chars, shorts and longs against the long
+ * longs.
+ *
+ * Current stats with gcc 2.7.2.2 for these functions:
+ *
+ *	ptrsize	get:	code	regs	put:	code	regs
+ *	1		1	1		1	2
+ *	2		3	2		3	2
+ *	4		7	3		7	3
+ *	8		20	6		16	6
+ *
+ * gcc 2.95.1 seems to code differently:
+ *
+ *	ptrsize	get:	code	regs	put:	code	regs
+ *	1		1	1		1	2
+ *	2		3	2		3	2
+ *	4		7	4		7	4
+ *	8		19	8		15	6
+ *
+ * which may or may not be more efficient (depending upon whether
+ * you can afford the extra registers).  Hopefully the gcc 2.95
+ * is inteligent enough to decide if it is better to use the
+ * extra register, but evidence so far seems to suggest otherwise.
+ *
+ * Unfortunately, gcc is not able to optimise the high word
+ * out of long long >> 32, or the low word from long long << 32
+ */
+
+#define __get_unaligned_2(__p)					\
+	(__p[0] | __p[1] << 8)
+
+#define __get_unaligned_4(__p)					\
+	(__p[0] | __p[1] << 8 | __p[2] << 16 | __p[3] << 24)
+
+#define get_unaligned(ptr)					\
+	({							\
+		__typeof__(*(ptr)) __v;				\
+		__u8 *__p = (__u8 *)(ptr);			\
+		switch (sizeof(*(ptr))) {			\
+		case 1:	__v = *(ptr);			break;	\
+		case 2: __v = __get_unaligned_2(__p);	break;	\
+		case 4: __v = __get_unaligned_4(__p);	break;	\
+		case 8: {					\
+				unsigned int __v1, __v2;	\
+				__v2 = __get_unaligned_4((__p+4)); \
+				__v1 = __get_unaligned_4(__p);	\
+				__v = ((unsigned long long)__v2 << 32 | __v1);	\
+			}					\
+			break;					\
+		default: __v = __bug_unaligned_x(__p);	break;	\
+		}						\
+		__v;						\
+	})
+
+
+static inline void __put_unaligned_2(__u32 __v, register __u8 *__p)
+{
+	*__p++ = __v;
+	*__p++ = __v >> 8;
+}
+
+static inline void __put_unaligned_4(__u32 __v, register __u8 *__p)
 {
-    unsigned long val;
-    switch (size) {
-	case 1:
-	    val = *(const unsigned char *)ptr;
-	    break;
-
-	case 2:
-	    val = ((const unsigned char *)ptr)[0] | (((const unsigned char *)ptr)[1] << 8);
-	    break;
-
-	case 4:
-	    val = ((const unsigned char *)ptr)[0]        | (((const unsigned char *)ptr)[1] << 8) |
-		 (((const unsigned char *)ptr)[2]) << 16 | (((const unsigned char *)ptr)[3] << 24);
-	    break;
-
-	default:
-	    bad_unaligned_access_length ();
-    }
-    return val;
+	__put_unaligned_2(__v >> 16, __p + 2);
+	__put_unaligned_2(__v, __p);
 }
 
-extern inline void __put_unaligned(unsigned long val, void *ptr, size_t size)
+static inline void __put_unaligned_8(const unsigned long long __v, register __u8 *__p)
 {
-    switch (size) {
-	case 1:
-	    *(unsigned char *)ptr = val;
-	    break;
-
-	case 2:
-	    ((unsigned char *)ptr)[0] = val;
-	    ((unsigned char *)ptr)[1] = val >> 8;
-	    break;
-
-	case 4:
-	    ((unsigned char *)ptr)[0] = val;
-	    ((unsigned char *)ptr)[1] = val >> 8;
-	    ((unsigned char *)ptr)[2] = val >> 16;
-	    ((unsigned char *)ptr)[3] = val >> 24;
-	    break;
-
-	default:
-	    bad_unaligned_access_length ();
-    }
+	/*
+	 * tradeoff: 8 bytes of stack for all unaligned puts (2
+	 * instructions), or an extra register in the long long
+	 * case - go for the extra register.
+	 */
+	__put_unaligned_4(__v >> 32, __p+4);
+	__put_unaligned_4(__v, __p);
 }
 
+/*
+ * Try to store an unaligned value as efficiently as possible.
+ */
+#define put_unaligned(val,ptr)					\
+	({							\
+		switch (sizeof(*(ptr))) {			\
+		case 1:						\
+			*(ptr) = (val);				\
+			break;					\
+		case 2: __put_unaligned_2((val),(__u8 *)(ptr));	\
+			break;					\
+		case 4:	__put_unaligned_4((val),(__u8 *)(ptr));	\
+			break;					\
+		case 8:	__put_unaligned_8((val),(__u8 *)(ptr)); \
+			break;					\
+		default: __bug_unaligned_x(ptr);		\
+			break;					\
+		}						\
+		(void) 0;					\
+	})
+
+
 #endif
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-arm/unistd.h linux.18p17/include/asm-arm/unistd.h
--- linux.vanilla/include/asm-arm/unistd.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-arm/unistd.h	Fri Sep 15 23:32:35 2000
@@ -59,7 +59,7 @@
 #define __NR_geteuid			(__NR_SYSCALL_BASE+ 49)
 #define __NR_getegid			(__NR_SYSCALL_BASE+ 50)
 #define __NR_acct			(__NR_SYSCALL_BASE+ 51)
-#define __NR_phys			(__NR_SYSCALL_BASE+ 52)
+#define __NR_umount2			(__NR_SYSCALL_BASE+ 52)
 #define __NR_lock			(__NR_SYSCALL_BASE+ 53)
 #define __NR_ioctl			(__NR_SYSCALL_BASE+ 54)
 #define __NR_fcntl			(__NR_SYSCALL_BASE+ 55)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-generic/smplock.h linux.18p17/include/asm-generic/smplock.h
--- linux.vanilla/include/asm-generic/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-generic/smplock.h	Thu Aug 31 15:37:12 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/bugs.h linux.18p17/include/asm-i386/bugs.h
--- linux.vanilla/include/asm-i386/bugs.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/bugs.h	Sat Sep 30 14:41:43 2000
@@ -330,7 +330,7 @@
 		if (ccr5 & 2) { /* possible wrong calibration done */
 			printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
 			calibrate_delay();
-			boot_cpu_data.loops_per_sec = loops_per_sec;
+			boot_cpu_data.loops_per_jiffy = loops_per_jiffy;
 		}
 	}
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/delay.h linux.18p17/include/asm-i386/delay.h
--- linux.vanilla/include/asm-i386/delay.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-i386/delay.h	Sat Sep 30 14:41:43 2000
@@ -7,13 +7,15 @@
  * Delay routines calling functions in arch/i386/lib/delay.c
  */
  
+extern void __bad_udelay(void);
+
 extern void __const_udelay(unsigned long usecs);
 extern void __udelay(unsigned long usecs);
 extern void __delay(unsigned long loops);
 
 
 #define udelay(n) (__builtin_constant_p(n) ? \
-	__const_udelay((n) * 0x10c6ul) : \
+	((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c6ul)) : \
 	__udelay(n))
 	
 #endif /* defined(_I386_DELAY_H) */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/math-emu.h linux.18p17/include/asm-i386/math-emu.h
--- linux.vanilla/include/asm-i386/math-emu.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-i386/math-emu.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/page.h linux.18p17/include/asm-i386/page.h
--- linux.vanilla/include/asm-i386/page.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/page.h	Sun Sep  3 00:04:06 2000
@@ -53,6 +53,25 @@
 #define __pgprot(x)	(x)
 
 #endif
+
+#define BUG() do { \
+        printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
+        __asm__ __volatile__(".byte 0x0f,0x0b"); \
+} while (0)
+
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
 #endif /* !__ASSEMBLY__ */
 
 /* to align the pointer to the (next) page boundary */
@@ -88,6 +107,7 @@
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define MAP_NR(addr)		(__pa(addr) >> PAGE_SHIFT)
+
 
 #endif /* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/processor.h linux.18p17/include/asm-i386/processor.h
--- linux.vanilla/include/asm-i386/processor.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/asm-i386/processor.h	Sat Sep 30 14:41:43 2000
@@ -36,7 +36,7 @@
 	int	fdiv_bug;
 	int	f00f_bug;
 	int	coma_bug;
-	unsigned long loops_per_sec;
+	unsigned long loops_per_jiffy;
 	unsigned long *pgd_quick;
 	unsigned long *pte_quick;
 	unsigned long pgtable_cache_sz;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/semaphore.h linux.18p17/include/asm-i386/semaphore.h
--- linux.vanilla/include/asm-i386/semaphore.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/semaphore.h	Sun Oct  1 11:41:00 2000
@@ -41,6 +41,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 asmlinkage void __down_failed(void /* special register calling convention */);
 asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
 asmlinkage int  __down_failed_trylock(void  /* params in registers */);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/smplock.h linux.18p17/include/asm-i386/smplock.h
--- linux.vanilla/include/asm-i386/smplock.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/smplock.h	Sun Oct  1 11:41:00 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/spinlock.h linux.18p17/include/asm-i386/spinlock.h
--- linux.vanilla/include/asm-i386/spinlock.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/spinlock.h	Sun Oct  1 11:17:45 2000
@@ -72,7 +72,7 @@
 #define spin_unlock_wait(x)	do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
 #define spin_unlock(x)		do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
 #define spin_lock_irq(x)	do {cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irq(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
-#define spin_unlock_irq(x)	do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; sti();} while (0)
+#define spin_unlock_irq(x)	do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_irq(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; sti();} while (0)
 
 #define spin_lock_irqsave(x,flags)      do {save_flags(flags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irqsave(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
 #define spin_unlock_irqrestore(x,flags) do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_irqrestore(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(flags);} while (0)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/system.h linux.18p17/include/asm-i386/system.h
--- linux.vanilla/include/asm-i386/system.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/asm-i386/system.h	Mon Sep 25 16:12:28 2000
@@ -130,7 +130,7 @@
 /*
  * Note: no "lock" prefix even on SMP: xchg always implies lock anyway
  */
-static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
+static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
 {
 	switch (size) {
 		case 1:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-i386/types.h linux.18p17/include/asm-i386/types.h
--- linux.vanilla/include/asm-i386/types.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-i386/types.h	Sat Sep  2 19:27:30 2000
@@ -41,6 +41,8 @@
 
 #define BITS_PER_LONG 32
 
+typedef u32 dma_addr_t;
+
 #endif /* __KERNEL__ */
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/adb.h linux.18p17/include/asm-m68k/adb.h
--- linux.vanilla/include/asm-m68k/adb.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/adb.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/adb_iop.h linux.18p17/include/asm-m68k/adb_iop.h
--- linux.vanilla/include/asm-m68k/adb_iop.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/adb_iop.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/bootinfo.h linux.18p17/include/asm-m68k/bootinfo.h
--- linux.vanilla/include/asm-m68k/bootinfo.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/bootinfo.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/fpu.h linux.18p17/include/asm-m68k/fpu.h
--- linux.vanilla/include/asm-m68k/fpu.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/fpu.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/ide.h linux.18p17/include/asm-m68k/ide.h
--- linux.vanilla/include/asm-m68k/ide.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/ide.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/init.h linux.18p17/include/asm-m68k/init.h
--- linux.vanilla/include/asm-m68k/init.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/init.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/irq.h linux.18p17/include/asm-m68k/irq.h
--- linux.vanilla/include/asm-m68k/irq.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/irq.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/keyboard.h linux.18p17/include/asm-m68k/keyboard.h
--- linux.vanilla/include/asm-m68k/keyboard.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/keyboard.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/linux_logo.h linux.18p17/include/asm-m68k/linux_logo.h
--- linux.vanilla/include/asm-m68k/linux_logo.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/linux_logo.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/m68kserial.h linux.18p17/include/asm-m68k/m68kserial.h
--- linux.vanilla/include/asm-m68k/m68kserial.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/m68kserial.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/mac_iop.h linux.18p17/include/asm-m68k/mac_iop.h
--- linux.vanilla/include/asm-m68k/mac_iop.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/mac_iop.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/mac_oss.h linux.18p17/include/asm-m68k/mac_oss.h
--- linux.vanilla/include/asm-m68k/mac_oss.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/mac_oss.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/mac_psc.h linux.18p17/include/asm-m68k/mac_psc.h
--- linux.vanilla/include/asm-m68k/mac_psc.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/mac_psc.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/mac_via.h linux.18p17/include/asm-m68k/mac_via.h
--- linux.vanilla/include/asm-m68k/mac_via.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/mac_via.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/machdep.h linux.18p17/include/asm-m68k/machdep.h
--- linux.vanilla/include/asm-m68k/machdep.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/machdep.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/machw.h linux.18p17/include/asm-m68k/machw.h
--- linux.vanilla/include/asm-m68k/machw.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/machw.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/macintosh.h linux.18p17/include/asm-m68k/macintosh.h
--- linux.vanilla/include/asm-m68k/macintosh.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/macintosh.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/macints.h linux.18p17/include/asm-m68k/macints.h
--- linux.vanilla/include/asm-m68k/macints.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/macints.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/page.h linux.18p17/include/asm-m68k/page.h
--- linux.vanilla/include/asm-m68k/page.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/page.h	Fri Oct 13 23:32:07 2000
@@ -11,6 +11,7 @@
 #ifdef __KERNEL__
 
 #include <asm/setup.h>
+#include <asm/bootinfo.h>
 
 #define STRICT_MM_TYPECHECKS
 
@@ -124,11 +125,29 @@
 #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)
+
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
 
 #endif /* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/semaphore.h linux.18p17/include/asm-m68k/semaphore.h
--- linux.vanilla/include/asm-m68k/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/semaphore.h	Fri Sep  1 21:40:01 2000
@@ -24,6 +24,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 asmlinkage void __down_failed(void /* special register calling convention */);
 asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
 asmlinkage int  __down_failed_trylock(void  /* params in registers */);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/setup.h linux.18p17/include/asm-m68k/setup.h
--- linux.vanilla/include/asm-m68k/setup.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/setup.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/smplock.h linux.18p17/include/asm-m68k/smplock.h
--- linux.vanilla/include/asm-m68k/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/smplock.h	Thu Aug 31 15:37:12 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/swim_iop.h linux.18p17/include/asm-m68k/swim_iop.h
--- linux.vanilla/include/asm-m68k/swim_iop.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/asm-m68k/swim_iop.h	Fri Oct 13 23:32:02 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/system.h linux.18p17/include/asm-m68k/system.h
--- linux.vanilla/include/asm-m68k/system.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/system.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/types.h linux.18p17/include/asm-m68k/types.h
--- linux.vanilla/include/asm-m68k/types.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/types.h	Sat Sep  2 19:28:44 2000
@@ -49,6 +49,8 @@
 
 #define BITS_PER_LONG 32
 
+typedef u32 dma_addr_t;
+
 #endif /* __KERNEL__ */
 
 #endif /* _M68K_TYPES_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-m68k/uaccess.h linux.18p17/include/asm-m68k/uaccess.h
--- linux.vanilla/include/asm-m68k/uaccess.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-m68k/uaccess.h	Fri Oct 13 23:32:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-mips/page.h linux.18p17/include/asm-mips/page.h
--- linux.vanilla/include/asm-mips/page.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-mips/page.h	Sun Sep  3 00:05:30 2000
@@ -25,6 +25,21 @@
 extern void (*clear_page)(unsigned long page);
 extern void (*copy_page)(unsigned long to, unsigned long from);
 
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
+#endif /* defined (__KERNEL__) */
+
 #ifdef STRICT_MM_TYPECHECKS
 /*
  * These are used to make use of C type-checking..
@@ -79,6 +94,5 @@
 #define __va(x)		((void *)((unsigned long) (x) + PAGE_OFFSET))
 #define MAP_NR(addr)	(__pa(addr) >> PAGE_SHIFT)
 
-#endif /* defined (__KERNEL__) */
 
 #endif /* __ASM_MIPS_PAGE_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-mips/semaphore.h linux.18p17/include/asm-mips/semaphore.h
--- linux.vanilla/include/asm-mips/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-mips/semaphore.h	Fri Sep  1 21:40:17 2000
@@ -25,6 +25,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 asmlinkage void __down(struct semaphore * sem);
 asmlinkage int  __down_interruptible(struct semaphore * sem);
 asmlinkage int __down_trylock(struct semaphore * sem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-mips/smplock.h linux.18p17/include/asm-mips/smplock.h
--- linux.vanilla/include/asm-mips/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-mips/smplock.h	Thu Aug 31 15:37:12 2000
@@ -18,8 +18,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-mips/types.h linux.18p17/include/asm-mips/types.h
--- linux.vanilla/include/asm-mips/types.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-mips/types.h	Sat Sep  2 19:28:52 2000
@@ -69,6 +69,8 @@
 #endif
 #define BITS_PER_LONG _MIPS_SZLONG
 
+typedef u32 dma_addr_t;
+
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_MIPS_TYPES_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-ppc/delay.h linux.18p17/include/asm-ppc/delay.h
--- linux.vanilla/include/asm-ppc/delay.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-ppc/delay.h	Sun Oct 15 23:56:33 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-ppc/page.h linux.18p17/include/asm-ppc/page.h
--- linux.vanilla/include/asm-ppc/page.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/asm-ppc/page.h	Sat Sep  2 19:33:24 2000
@@ -81,6 +81,20 @@
 #define MAP_PAGE_RESERVED	(1<<15)
 
 extern unsigned long get_zero_page_fast(void);
+
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
 #endif /* __KERNEL__ */
 #endif /* __ASSEMBLY__ */
 #endif /* _PPC_PAGE_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-ppc/semaphore.h linux.18p17/include/asm-ppc/semaphore.h
--- linux.vanilla/include/asm-ppc/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-ppc/semaphore.h	Fri Sep  1 21:40:34 2000
@@ -23,6 +23,11 @@
 #define MUTEX_LOCKED	((struct semaphore) \
 			 { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
 extern int  __down_trylock(struct semaphore * sem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-ppc/smp.h linux.18p17/include/asm-ppc/smp.h
--- linux.vanilla/include/asm-ppc/smp.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-ppc/smp.h	Sun Oct 15 23:56:33 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-ppc/smplock.h linux.18p17/include/asm-ppc/smplock.h
--- linux.vanilla/include/asm-ppc/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-ppc/smplock.h	Thu Aug 31 15:37:12 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-s390/irq.h linux.18p17/include/asm-s390/irq.h
--- linux.vanilla/include/asm-s390/irq.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/asm-s390/irq.h	Sat Sep  2 23:39:30 2000
@@ -799,12 +799,13 @@
  * x86 profiling function, SMP safe. We might want to do this in
  * assembly totally?
  */
-extern char _stext;
+extern char _stext[];
+
 static inline void s390_do_profile (unsigned long addr)
 {
         if (prof_buffer && current->pid) {
                 addr &= 0x7fffffff;
-                addr -= (unsigned long) &_stext;
+                addr -= (unsigned long)&_stext[0];
                 addr >>= prof_shift;
                 /*
                  * Don't ignore out-of-bounds EIP values silently,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-s390/page.h linux.18p17/include/asm-s390/page.h
--- linux.vanilla/include/asm-s390/page.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-s390/page.h	Sun Sep  3 00:06:05 2000
@@ -26,6 +26,19 @@
 #define clear_page(page)        memset((void *)(page), 0, PAGE_SIZE)
 #define copy_page(to,from)      memcpy((void *)(to), (void *)(from), PAGE_SIZE)
 
+extern __inline__ int get_order(unsigned long size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
 #ifdef STRICT_MM_TYPECHECKS
 /*
  * These are used to make use of C type-checking..
@@ -90,6 +103,7 @@
 #define __pa(x)                 ((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)                 ((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define MAP_NR(addr)            (__pa(addr) >> PAGE_SHIFT)
+
 
 #endif                                 /* __KERNEL__                       */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-s390/semaphore.h linux.18p17/include/asm-s390/semaphore.h
--- linux.vanilla/include/asm-s390/semaphore.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-s390/semaphore.h	Fri Sep  1 21:41:05 2000
@@ -26,6 +26,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 asmlinkage void __down_failed(void /* special register calling convention */);
 asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
 asmlinkage int  __down_failed_trylock(void  /* params in registers */);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-s390/smplock.h linux.18p17/include/asm-s390/smplock.h
--- linux.vanilla/include/asm-s390/smplock.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-s390/smplock.h	Thu Aug 31 15:37:12 2000
@@ -18,8 +18,6 @@
 do {                                       \
 	if (task->lock_depth >= 0)         \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu);              \
-	__sti();                           \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-s390/types.h linux.18p17/include/asm-s390/types.h
--- linux.vanilla/include/asm-s390/types.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/asm-s390/types.h	Sat Sep  2 19:29:07 2000
@@ -60,5 +60,7 @@
 #define FALSE 0
 #endif
 
+typedef u32 dma_addr_t;
+
 #endif                                 /* __KERNEL__                       */
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc/delay.h linux.18p17/include/asm-sparc/delay.h
--- linux.vanilla/include/asm-sparc/delay.h	Fri Apr 21 12:46:49 2000
+++ linux.18p17/include/asm-sparc/delay.h	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc/io.h linux.18p17/include/asm-sparc/io.h
--- linux.vanilla/include/asm-sparc/io.h	Fri Apr 21 12:46:50 2000
+++ linux.18p17/include/asm-sparc/io.h	Sun Sep 17 17:47:54 2000
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.18 1998/09/21 05:07:17 jj Exp $ */
+/* $Id: io.h,v 1.18.2.1 2000/09/17 05:11:47 davem Exp $ */
 #ifndef __SPARC_IO_H
 #define __SPARC_IO_H
 
@@ -98,6 +98,8 @@
 #define inb_p inb
 #define outb_p outb
 
+#ifdef __KERNEL__
+
 extern void sun4c_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
 extern void srmmu_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
 
@@ -161,5 +163,7 @@
 
 #define virt_to_phys(x) __pa((unsigned long)(x))
 #define phys_to_virt(x) __va((unsigned long)(x))
+
+#endif /* __KERNEL__ */
 
 #endif /* !(__SPARC_IO_H) */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc/semaphore.h linux.18p17/include/asm-sparc/semaphore.h
--- linux.vanilla/include/asm-sparc/semaphore.h	Fri Apr 21 12:46:50 2000
+++ linux.18p17/include/asm-sparc/semaphore.h	Fri Sep  1 21:41:23 2000
@@ -16,6 +16,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 extern void __down(struct semaphore * sem);
 extern int __down_interruptible(struct semaphore * sem);
 extern int __down_trylock(struct semaphore * sem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc/smplock.h linux.18p17/include/asm-sparc/smplock.h
--- linux.vanilla/include/asm-sparc/smplock.h	Fri Apr 21 12:46:50 2000
+++ linux.18p17/include/asm-sparc/smplock.h	Thu Aug 31 15:37:12 2000
@@ -15,8 +15,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/delay.h linux.18p17/include/asm-sparc64/delay.h
--- linux.vanilla/include/asm-sparc64/delay.h	Fri Apr 21 12:46:50 2000
+++ linux.18p17/include/asm-sparc64/delay.h	Sat Oct 14 00:28:12 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/io.h linux.18p17/include/asm-sparc64/io.h
--- linux.vanilla/include/asm-sparc64/io.h	Fri Apr 21 12:46:50 2000
+++ linux.18p17/include/asm-sparc64/io.h	Sun Sep 17 17:47:54 2000
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.19.2.1 2000/01/14 03:55:36 davem Exp $ */
+/* $Id: io.h,v 1.19.2.2 2000/09/17 05:11:47 davem Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -244,6 +244,8 @@
 	return retval;
 }
 
+#ifdef __KERNEL__
+
 /*
  * On the sparc we have the whole physical IO address space mapped at all
  * times, so ioremap() and iounmap() do not need to do anything.
@@ -279,5 +281,7 @@
 			    u32 io, int rdonly);
 extern void sparc_free_io (void *va, int sz);
 extern void *sparc_dvma_malloc (int sz, char *name, __u32 *dvma_addr);
+
+#endif /* __KERNEL__ */
 
 #endif /* !(__SPARC64_IO_H) */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/semaphore.h linux.18p17/include/asm-sparc64/semaphore.h
--- linux.vanilla/include/asm-sparc64/semaphore.h	Fri Apr 21 12:46:51 2000
+++ linux.18p17/include/asm-sparc64/semaphore.h	Fri Sep  1 21:41:39 2000
@@ -16,6 +16,11 @@
 #define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
 #define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
+#define init_MUTEX(x)				*(x)=MUTEX
+#define init_MUTEX_LOCKED(x)			*(x)=MUTEX_LOCKED
+#define DECLARE_MUTEX(name)			struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name)		struct semaphore name=MUTEX_LOCKED
+
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
 extern int  __down_trylock(struct semaphore * sem);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/smplock.h linux.18p17/include/asm-sparc64/smplock.h
--- linux.vanilla/include/asm-sparc64/smplock.h	Fri Apr 21 12:46:51 2000
+++ linux.18p17/include/asm-sparc64/smplock.h	Thu Aug 31 15:37:12 2000
@@ -16,8 +16,6 @@
 do { \
 	if (task->lock_depth >= 0) \
 		spin_unlock(&kernel_flag); \
-	release_irqlock(cpu); \
-	__sti(); \
 } while (0)
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/spitfire.h linux.18p17/include/asm-sparc64/spitfire.h
--- linux.vanilla/include/asm-sparc64/spitfire.h	Fri Apr 21 12:46:51 2000
+++ linux.18p17/include/asm-sparc64/spitfire.h	Thu Oct 19 01:15:54 2000
@@ -1,4 +1,4 @@
-/* $Id: spitfire.h,v 1.9 1998/04/28 08:23:33 davem Exp $
+/* $Id: spitfire.h,v 1.9.2.1 2000/10/06 13:14:35 anton Exp $
  * spitfire.h: SpitFire/BlackBird/Cheetah inline MMU operations.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -117,6 +117,7 @@
 	__asm__ __volatile__("stxa	%0, [%1] %2"
 			     : /* No outputs */
 			     : "r" (tag), "r" (addr), "i" (ASI_DCACHE_TAG));
+	membar("#Sync");
 }
 
 /* The instruction cache lines are flushed with this, but note that
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/asm-sparc64/system.h linux.18p17/include/asm-sparc64/system.h
--- linux.vanilla/include/asm-sparc64/system.h	Fri Apr 21 12:46:52 2000
+++ linux.18p17/include/asm-sparc64/system.h	Sun Sep 10 14:49:10 2000
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.50.2.1 2000/03/06 22:30:28 davem Exp $ */
+/* $Id: system.h,v 1.50.2.2 2000/09/05 00:10:58 davem Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
@@ -252,6 +252,63 @@
 }
 
 extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noreturn));
+
+/* 
+ * Atomic compare and exchange.  Compare OLD with MEM, if identical,
+ * store NEW in MEM.  Return the initial value in MEM.  Success is
+ * indicated by comparing RETURN with OLD.
+ */
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+extern __inline__ unsigned long
+__cmpxchg_u32(volatile int *m, int old, int new)
+{
+	__asm__ __volatile__("cas [%2], %3, %0\n\t"
+			     "membar #StoreStore | #StoreLoad"
+			     : "=&r" (new)
+			     : "0" (new), "r" (m), "r" (old)
+			     : "memory");
+
+	return new;
+}
+
+extern __inline__ unsigned long
+__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
+{
+	__asm__ __volatile__("casx [%2], %3, %0\n\t"
+			     "membar #StoreStore | #StoreLoad"
+			     : "=&r" (new)
+			     : "0" (new), "r" (m), "r" (old)
+			     : "memory");
+
+	return new;
+}
+
+/* This function doesn't exist, so you'll get a linker error
+   if something tries to do an invalid cmpxchg().  */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+static __inline__ unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	switch (size) {
+		case 4:
+			return __cmpxchg_u32(ptr, old, new);
+		case 8:
+			return __cmpxchg_u64(ptr, old, new);
+	}
+	__cmpxchg_called_with_bad_pointer();
+	return old;
+}
+
+#define cmpxchg(ptr,o,n)						 \
+  ({									 \
+     __typeof__(*(ptr)) _o_ = (o);					 \
+     __typeof__(*(ptr)) _n_ = (n);					 \
+     (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \
+				    (unsigned long)_n_, sizeof(*(ptr))); \
+  })
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/16c552.h linux.18p17/include/linux/16c552.h
--- linux.vanilla/include/linux/16c552.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/16c552.h	Sat Oct 14 00:14:39 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/ac97_codec.h linux.18p17/include/linux/ac97_codec.h
--- linux.vanilla/include/linux/ac97_codec.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/ac97_codec.h	Fri Sep 29 22:27:09 2000
@@ -142,6 +142,7 @@
 	char *name;
 	int id;
 	int dev_mixer; 
+	int type;
 
 	/* codec specific init/reset routines, used mainly for 4 or 6 channel support */
 	int  (*codec_init)  (struct ac97_codec *codec);
@@ -155,6 +156,8 @@
 	int supported_mixers;
 	int stereo_mixers;
 	int record_sources;
+
+	int bit_resolution;
 	
 	/* OSS mixer interface */
 	int  (*read_mixer) (struct ac97_codec *codec, int oss_channel);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/adfs_fs.h linux.18p17/include/linux/adfs_fs.h
--- linux.vanilla/include/linux/adfs_fs.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/adfs_fs.h	Fri Sep 15 23:24:41 2000
@@ -2,113 +2,43 @@
 #define _ADFS_FS_H
 
 #include <linux/types.h>
-/*
- * Structures of data on the disk
- */
 
 /*
  * Disc Record at disc address 0xc00
  */
 struct adfs_discrecord {
-    unsigned char  log2secsize;
-    unsigned char  secspertrack;
-    unsigned char  heads;
-    unsigned char  density;
-    unsigned char  idlen;
-    unsigned char  log2bpmb;
-    unsigned char  skew;
-    unsigned char  bootoption;
-    unsigned char  lowsector;
-    unsigned char  nzones;
-    unsigned short zone_spare;
-    unsigned long  root;
-    unsigned long  disc_size;
-    unsigned short disc_id;
-    unsigned char  disc_name[10];
-    unsigned long  disc_type;
-    unsigned long  disc_size_high;
-    unsigned char  log2sharesize:4;
-    unsigned char  unused:4;
-    unsigned char  big_flag:1;
+    __u8  log2secsize;
+    __u8  secspertrack;
+    __u8  heads;
+    __u8  density;
+    __u8  idlen;
+    __u8  log2bpmb;
+    __u8  skew;
+    __u8  bootoption;
+    __u8  lowsector;
+    __u8  nzones;
+    __u16 zone_spare;
+    __u32 root;
+    __u32 disc_size;
+    __u16 disc_id;
+    __u8  disc_name[10];
+    __u32 disc_type;
+    __u32 disc_size_high;
+    __u8  log2sharesize:4;
+    __u8  unused40:4;
+    __u8  big_flag:1;
+    __u8  unused41:1;
+    __u8  nzones_high;
+    __u32 format_version;
+    __u32 root_size;
+    __u8  unused52[60 - 52];
 };
 
 #define ADFS_DISCRECORD		(0xc00)
 #define ADFS_DR_OFFSET		(0x1c0)
 #define ADFS_DR_SIZE		 60
+#define ADFS_DR_SIZE_BITS	(ADFS_DR_SIZE << 3)
 #define ADFS_SUPER_MAGIC	 0xadf5
-#define ADFS_FREE_FRAG		 0
-#define ADFS_BAD_FRAG		 1
-#define ADFS_ROOT_FRAG		 2
-
-/*
- * Directory header
- */
-struct adfs_dirheader {
-	unsigned char startmasseq;
-	unsigned char startname[4];
-};
-
-#define ADFS_NEWDIR_SIZE	2048
-#define ADFS_OLDDIR_SIZE	1024
-#define ADFS_NUM_DIR_ENTRIES	77
-
-/*
- * Directory entries
- */
-struct adfs_direntry {
-	char dirobname[10];
-#define ADFS_NAME_LEN 10
-	__u8 dirload[4];
-	__u8 direxec[4];
-	__u8 dirlen[4];
-	__u8 dirinddiscadd[3];
-	__u8 newdiratts;
-#define ADFS_NDA_OWNER_READ	(1 << 0)
-#define ADFS_NDA_OWNER_WRITE	(1 << 1)
-#define ADFS_NDA_LOCKED		(1 << 2)
-#define ADFS_NDA_DIRECTORY	(1 << 3)
-#define ADFS_NDA_EXECUTE	(1 << 4)
-#define ADFS_NDA_PUBLIC_READ	(1 << 5)
-#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
-};
-
-#define ADFS_MAX_NAME_LEN	255
-struct adfs_idir_entry {
-	__u32		inode_no;			/* Address		*/
-	__u32		file_id;			/* file id		*/
-	__u32		name_len;			/* name length		*/
-	__u32		size;				/* size			*/
-	__u32		mtime;				/* modification time	*/
-	__u32		filetype;			/* RiscOS file type	*/
-	__u8		mode;				/* internal mode	*/
-	char		name[ADFS_MAX_NAME_LEN];	/* file name		*/
-};
-
-/*
- * Directory tail
- */
-union adfs_dirtail {
-	struct {
-		unsigned char dirlastmask;
-		char dirname[10];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		unsigned char reserved[14];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} old;
-	struct {
-		unsigned char dirlastmask;
-		unsigned char reserved[2];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		char dirname[10];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} new;
-};
 
 #ifdef __KERNEL__
 /*
@@ -129,47 +59,8 @@
 	return (result & 0xff) != ptr[511];
 }
 
-/* dir.c */
-extern unsigned int adfs_val (unsigned char *p, int len);
-extern int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp);
-extern int adfs_dir_read (struct inode *inode, struct buffer_head **bhp);
-extern int adfs_dir_check (struct inode *inode, struct buffer_head **bhp,
-			   int buffers, union adfs_dirtail *dtp);
-extern void adfs_dir_free (struct buffer_head **bhp, int buffers);
-extern int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
-			 int buffers, int pos, unsigned long parent_object_id,
-			 struct adfs_idir_entry *ide);
-extern int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
-				int buffers, unsigned int index,
-				struct adfs_idir_entry *ide);
-
-/* inode.c */
-extern int adfs_inode_validate (struct inode *inode);
-extern unsigned long adfs_inode_generate (unsigned long parent_id, int diridx);
-extern unsigned long adfs_inode_objid (struct inode *inode);
-extern unsigned int adfs_parent_bmap (struct inode *inode, int block);
-extern int adfs_bmap (struct inode *inode, int block);
-extern void adfs_read_inode (struct inode *inode);
-
-/* map.c */
-extern int adfs_map_lookup (struct super_block *sb, int frag_id, int offset);
-
-/* namei.c */
-extern struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry);
+#endif
 
-/* super.c */
 extern int init_adfs_fs (void);
-extern void adfs_error (struct super_block *, const char *, const char *, ...);
-
-/*
- * Inodes and file operations
- */
-
-/* dir.c */
-extern struct inode_operations adfs_dir_inode_operations;
-
-/* file.c */
-extern struct inode_operations adfs_file_inode_operations;
-#endif
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/adfs_fs_i.h linux.18p17/include/linux/adfs_fs_i.h
--- linux.vanilla/include/linux/adfs_fs_i.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/adfs_fs_i.h	Fri Sep 15 23:24:41 2000
@@ -11,7 +11,12 @@
  * adfs file system inode data in memory
  */
 struct adfs_inode_info {
-	unsigned long	file_id;		/* id of fragments containing actual data */
+	unsigned long	parent_id;	/* object id of parent		*/
+	__u32		loadaddr;	/* RISC OS load address		*/
+	__u32		execaddr;	/* RISC OS exec address		*/
+	unsigned int	filetype;	/* RISC OS file type		*/
+	unsigned int	attr;		/* RISC OS permissions		*/
+	int		stamped:1;	/* RISC OS file has date/time	*/
 };
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/adfs_fs_sb.h linux.18p17/include/linux/adfs_fs_sb.h
--- linux.vanilla/include/linux/adfs_fs_sb.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/adfs_fs_sb.h	Fri Sep 15 23:24:41 2000
@@ -1,33 +1,38 @@
 /*
  *  linux/include/linux/adfs_fs_sb.h
  *
- * Copyright (C) 1997 Russell King
+ * Copyright (C) 1997-1999 Russell King
  */
 
 #ifndef _ADFS_FS_SB
 #define _ADFS_FS_SB
 
-#include <linux/adfs_fs.h>
+/*
+ * Forward-declare this
+ */
+struct adfs_discmap;
+struct adfs_dir_ops;
 
 /*
- * adfs file system superblock data in memory
+ * ADFS file system superblock data in memory
  */
 struct adfs_sb_info {
-	struct buffer_head *s_sbh;	/* buffer head containing disc record	 */
-	struct adfs_discrecord *s_dr;	/* pointer to disc record in s_sbh	 */
-	uid_t	s_uid;			/* owner uid				 */
-	gid_t	s_gid;			/* owner gid				 */
-	int	s_owner_mask;		/* ADFS Owner perm -> unix perm		 */
-	int	s_other_mask;		/* ADFS Other perm -> unix perm		 */
-	__u16	s_zone_size;		/* size of a map zone in bits		 */
-	__u16	s_ids_per_zone;		/* max. no ids in one zone		 */
-	__u32	s_idlen;		/* length of ID in map			 */
-	__u32	s_map_size;		/* size of a map			 */
-	__u32	s_zonesize;		/* zone size (in map bits)		 */
-	__u32	s_map_block;		/* block address of map			 */
-	struct buffer_head **s_map;	/* bh list containing map		 */
-	__u32	s_root;			/* root disc address			 */
-	__s8	s_map2blk;		/* shift left by this for map->sector	 */
+	struct adfs_discmap *s_map;	/* bh list containing map		 */
+	struct adfs_dir_ops *s_dir;	/* directory operations			 */
+
+	uid_t		s_uid;		/* owner uid				 */
+	gid_t		s_gid;		/* owner gid				 */
+	umode_t		s_owner_mask;	/* ADFS owner perm -> unix perm		 */
+	umode_t		s_other_mask;	/* ADFS other perm -> unix perm		 */
+
+	__u32		s_ids_per_zone;	/* max. no ids in one zone		 */
+	__u32		s_idlen;	/* length of ID in map			 */
+	__u32		s_map_size;	/* sector size of a map			 */
+	unsigned long	s_size;		/* total size (in blocks) of this fs	 */
+	signed int	s_map2blk;	/* shift left by this for map->sector	 */
+	unsigned int	s_log2sharesize;/* log2 share size			 */
+	unsigned int	s_version;	/* disc format version			 */
+	unsigned int	s_namelen;	/* maximum number of characters in name	 */
 };
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/agp_backend.h linux.18p17/include/linux/agp_backend.h
--- linux.vanilla/include/linux/agp_backend.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/agp_backend.h	Tue Sep 26 17:00:25 2000
@@ -0,0 +1,232 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_BACKEND_H
+#define _AGP_BACKEND_H 1
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define AGPGART_VERSION_MAJOR 0
+#define AGPGART_VERSION_MINOR 99
+
+enum chipset_type {
+	NOT_SUPPORTED,
+	INTEL_GENERIC,
+	INTEL_LX,
+	INTEL_BX,
+	INTEL_GX,
+	INTEL_I810,
+	INTEL_I840,
+	VIA_GENERIC,
+	VIA_VP3,
+	VIA_MVP3,
+	VIA_MVP4,
+	VIA_APOLLO_PRO,
+	VIA_APOLLO_KT133,
+	VIA_APOLLO_KX133,
+	SIS_GENERIC,
+	AMD_GENERIC,
+	AMD_IRONGATE,
+	ALI_M1541,
+	ALI_GENERIC
+};
+
+typedef struct _agp_version {
+	u16 major;
+	u16 minor;
+} agp_version;
+
+typedef struct _agp_kern_info {
+	agp_version version;
+	struct pci_dev *device;
+	enum chipset_type chipset;
+	unsigned long mode;
+	off_t aper_base;
+	size_t aper_size;
+	int max_memory;		/* In pages */
+	int current_memory;
+} agp_kern_info;
+
+/* 
+ * The agp_memory structure has information
+ * about the block of agp memory allocated.
+ * A caller may manipulate the next and prev
+ * pointers to link each allocated item into
+ * a list.  These pointers are ignored by the 
+ * backend.  Everything else should never be
+ * written to, but the caller may read any of
+ * the items to detrimine the status of this
+ * block of agp memory.
+ * 
+ */
+
+typedef struct _agp_memory {
+	int key;
+	struct _agp_memory *next;
+	struct _agp_memory *prev;
+	size_t page_count;
+	int num_scratch_pages;
+	unsigned long *memory;
+	off_t pg_start;
+	u32 type;
+	u32 physical;
+	u8 is_bound;
+	u8 is_flushed;
+} agp_memory;
+
+#define AGP_NORMAL_MEMORY 0
+
+extern void agp_free_memory(agp_memory *);
+
+/*
+ * agp_free_memory :
+ * 
+ * This function frees memory associated with
+ * an agp_memory pointer.  It is the only function
+ * that can be called when the backend is not owned
+ * by the caller.  (So it can free memory on client
+ * death.)
+ * 
+ * It takes an agp_memory pointer as an argument.
+ * 
+ */
+
+extern agp_memory *agp_allocate_memory(size_t, u32);
+
+/*
+ * agp_allocate_memory :
+ * 
+ * This function allocates a group of pages of
+ * a certain type.
+ * 
+ * It takes a size_t argument of the number of pages, and
+ * an u32 argument of the type of memory to be allocated.  
+ * Every agp bridge device will allow you to allocate 
+ * AGP_NORMAL_MEMORY which maps to physical ram.  Any other
+ * type is device dependant.
+ * 
+ * It returns NULL whenever memory is unavailable.
+ * 
+ */
+
+extern void agp_copy_info(agp_kern_info *);
+
+/*
+ * agp_copy_info :
+ * 
+ * This function copies information about the
+ * agp bridge device and the state of the agp
+ * backend into an agp_kern_info pointer.
+ * 
+ * It takes an agp_kern_info pointer as an
+ * argument.  The caller should insure that
+ * this pointer is valid.
+ * 
+ */
+
+extern int agp_bind_memory(agp_memory *, off_t);
+
+/*
+ * agp_bind_memory :
+ * 
+ * This function binds an agp_memory structure
+ * into the graphics aperture translation table.
+ * 
+ * It takes an agp_memory pointer and an offset into
+ * the graphics aperture translation table as arguments
+ * 
+ * It returns -EINVAL if the pointer == NULL.
+ * It returns -EBUSY if the area of the table
+ * requested is already in use.
+ * 
+ */
+
+extern int agp_unbind_memory(agp_memory *);
+
+/* 
+ * agp_unbind_memory :
+ * 
+ * This function removes an agp_memory structure
+ * from the graphics aperture translation table.
+ * 
+ * It takes an agp_memory pointer as an argument.
+ * 
+ * It returns -EINVAL if this piece of agp_memory
+ * is not currently bound to the graphics aperture
+ * translation table or if the agp_memory 
+ * pointer == NULL
+ * 
+ */
+
+extern void agp_enable(u32);
+
+/* 
+ * agp_enable :
+ * 
+ * This function initializes the agp point-to-point
+ * connection.
+ * 
+ * It takes an agp mode register as an argument
+ * 
+ */
+
+extern int agp_backend_acquire(void);
+
+/*
+ * agp_backend_acquire :
+ * 
+ * This Function attempts to acquire the agp
+ * backend.
+ * 
+ * returns -EBUSY if agp is in use,
+ * returns 0 if the caller owns the agp backend
+ */
+
+extern void agp_backend_release(void);
+
+/*
+ * agp_backend_release :
+ * 
+ * This Function releases the lock on the agp
+ * backend.
+ * 
+ * The caller must insure that the graphics
+ * aperture translation table is read for use
+ * by another entity.  (Ensure that all memory
+ * it bound is unbound.)
+ * 
+ */
+
+/* Don't call this directly!! used in initialization */
+extern int agp_init(void);
+
+#endif				/* _AGP_BACKEND_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/agpgart.h linux.18p17/include/linux/agpgart.h
--- linux.vanilla/include/linux/agpgart.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/agpgart.h	Fri Sep  1 14:06:07 2000
@@ -0,0 +1,224 @@
+/*
+ * AGPGART module version 0.99
+ * Copyright (C) 1999 Jeff Hartmann
+ * Copyright (C) 1999 Precision Insight, Inc.
+ * Copyright (C) 1999 Xi Graphics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _AGP_H
+#define _AGP_H 1
+
+#define AGPIOC_BASE       'A'
+#define AGPIOC_INFO       _IOR (AGPIOC_BASE, 0, agp_info*)
+#define AGPIOC_ACQUIRE    _IO  (AGPIOC_BASE, 1)
+#define AGPIOC_RELEASE    _IO  (AGPIOC_BASE, 2)
+#define AGPIOC_SETUP      _IOW (AGPIOC_BASE, 3, agp_setup*)
+#define AGPIOC_RESERVE    _IOW (AGPIOC_BASE, 4, agp_region*)
+#define AGPIOC_PROTECT    _IOW (AGPIOC_BASE, 5, agp_region*)
+#define AGPIOC_ALLOCATE   _IOWR(AGPIOC_BASE, 6, agp_allocate*)
+#define AGPIOC_DEALLOCATE _IOW (AGPIOC_BASE, 7, int)
+#define AGPIOC_BIND       _IOW (AGPIOC_BASE, 8, agp_bind*)
+#define AGPIOC_UNBIND     _IOW (AGPIOC_BASE, 9, agp_unbind*)
+
+#define AGP_DEVICE      "/dev/agpgart"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef __KERNEL__
+#include <linux/types.h>
+#include <asm/types.h>
+
+typedef struct _agp_version {
+	__u16 major;
+	__u16 minor;
+} agp_version;
+
+typedef struct _agp_info {
+	agp_version version;	/* version of the driver        */
+	__u32 bridge_id;	/* bridge vendor/device         */
+	__u32 agp_mode;		/* mode info of bridge          */
+	off_t aper_base;	/* base of aperture             */
+	size_t aper_size;	/* size of aperture             */
+	size_t pg_total;	/* max pages (swap + system)    */
+	size_t pg_system;	/* max pages (system)           */
+	size_t pg_used;		/* current pages used           */
+} agp_info;
+
+typedef struct _agp_setup {
+	__u32 agp_mode;		/* mode info of bridge          */
+} agp_setup;
+
+/*
+ * The "prot" down below needs still a "sleep" flag somehow ...
+ */
+typedef struct _agp_segment {
+	off_t pg_start;		/* starting page to populate    */
+	size_t pg_count;	/* number of pages              */
+	int prot;		/* prot flags for mmap          */
+} agp_segment;
+
+typedef struct _agp_region {
+	pid_t pid;		/* pid of process               */
+	size_t seg_count;	/* number of segments           */
+	struct _agp_segment *seg_list;
+} agp_region;
+
+typedef struct _agp_allocate {
+	int key;		/* tag of allocation            */
+	size_t pg_count;	/* number of pages              */
+	__u32 type;		/* 0 == normal, other devspec   */
+   	__u32 physical;         /* device specific (some devices  
+				 * need a phys address of the     
+				 * actual page behind the gatt    
+				 * table)                        */
+} agp_allocate;
+
+typedef struct _agp_bind {
+	int key;		/* tag of allocation            */
+	off_t pg_start;		/* starting page to populate    */
+} agp_bind;
+
+typedef struct _agp_unbind {
+	int key;		/* tag of allocation            */
+	__u32 priority;		/* priority for paging out      */
+} agp_unbind;
+
+#else				/* __KERNEL__ */
+
+#define AGPGART_MINOR 175
+
+#define AGP_UNLOCK()      	up(&(agp_fe.agp_mutex));
+#define AGP_LOCK()    		down(&(agp_fe.agp_mutex));
+#define AGP_LOCK_INIT() 	sema_init(&(agp_fe.agp_mutex), 1)
+
+#ifndef _AGP_BACKEND_H
+typedef struct _agp_version {
+	u16 major;
+	u16 minor;
+} agp_version;
+
+#endif
+
+typedef struct _agp_info {
+	agp_version version;	/* version of the driver        */
+	u32 bridge_id;		/* bridge vendor/device         */
+	u32 agp_mode;		/* mode info of bridge          */
+	off_t aper_base;	/* base of aperture             */
+	size_t aper_size;	/* size of aperture             */
+	size_t pg_total;	/* max pages (swap + system)    */
+	size_t pg_system;	/* max pages (system)           */
+	size_t pg_used;		/* current pages used           */
+} agp_info;
+
+typedef struct _agp_setup {
+	u32 agp_mode;		/* mode info of bridge          */
+} agp_setup;
+
+/*
+ * The "prot" down below needs still a "sleep" flag somehow ...
+ */
+typedef struct _agp_segment {
+	off_t pg_start;		/* starting page to populate    */
+	size_t pg_count;	/* number of pages              */
+	int prot;		/* prot flags for mmap          */
+} agp_segment;
+
+typedef struct _agp_segment_priv {
+	off_t pg_start;
+	size_t pg_count;
+	pgprot_t prot;
+} agp_segment_priv;
+
+typedef struct _agp_region {
+	pid_t pid;		/* pid of process               */
+	size_t seg_count;	/* number of segments           */
+	struct _agp_segment *seg_list;
+} agp_region;
+
+typedef struct _agp_allocate {
+	int key;		/* tag of allocation            */
+	size_t pg_count;	/* number of pages              */
+	u32 type;		/* 0 == normal, other devspec   */
+	u32 physical;           /* device specific (some devices  
+				 * need a phys address of the     
+				 * actual page behind the gatt    
+				 * table)                        */
+} agp_allocate;
+
+typedef struct _agp_bind {
+	int key;		/* tag of allocation            */
+	off_t pg_start;		/* starting page to populate    */
+} agp_bind;
+
+typedef struct _agp_unbind {
+	int key;		/* tag of allocation            */
+	u32 priority;		/* priority for paging out      */
+} agp_unbind;
+
+typedef struct _agp_client {
+	struct _agp_client *next;
+	struct _agp_client *prev;
+	pid_t pid;
+	int num_segments;
+	agp_segment_priv **segments;
+} agp_client;
+
+typedef struct _agp_controller {
+	struct _agp_controller *next;
+	struct _agp_controller *prev;
+	pid_t pid;
+	int num_clients;
+	agp_memory *pool;
+	agp_client *clients;
+} agp_controller;
+
+#define AGP_FF_ALLOW_CLIENT		0
+#define AGP_FF_ALLOW_CONTROLLER 	1
+#define AGP_FF_IS_CLIENT		2
+#define AGP_FF_IS_CONTROLLER		3
+#define AGP_FF_IS_VALID 		4
+
+typedef struct _agp_file_private {
+	struct _agp_file_private *next;
+	struct _agp_file_private *prev;
+	pid_t my_pid;
+	u32 access_flags;
+} agp_file_private;
+
+struct agp_front_data {
+	struct semaphore agp_mutex;
+	agp_controller *current_controller;
+	agp_controller *controllers;
+	agp_file_private *file_priv_list;
+	u8 used_by_controller;
+	u8 backend_acquired;
+};
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _AGP_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/apm_bios.h linux.18p17/include/linux/apm_bios.h
--- linux.vanilla/include/linux/apm_bios.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/apm_bios.h	Mon Sep 11 17:05:42 2000
@@ -95,9 +95,6 @@
  */
 extern struct apm_bios_info	apm_bios_info;
 
-extern void		apm_init(void);
-extern void		apm_setup(char *, int *);
-
 extern int		apm_register_callback(int (*callback)(apm_event_t));
 extern void		apm_unregister_callback(int (*callback)(apm_event_t));
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/blkdev.h linux.18p17/include/linux/blkdev.h
--- linux.vanilla/include/linux/blkdev.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/linux/blkdev.h	Thu Oct 19 01:19:35 2000
@@ -42,15 +42,13 @@
 {
 	int read_latency;
 	int write_latency;
-	int max_bomb_segments;
 	unsigned int queue_ID;
 } elevator_t;
 
 #define ELEVATOR_DEFAULTS				\
 ((elevator_t) {						\
-	128,			/* read_latency */	\
-	8192,			/* write_latency */	\
-	32,			/* max_bomb_segments */	\
+	256,			/* read_latency */	\
+	512,			/* write_latency */	\
         0                       /* queue_ID */          \
 	})
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/capi.h linux.18p17/include/linux/capi.h
--- linux.vanilla/include/linux/capi.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/capi.h	Sat Sep 23 13:15:36 2000
@@ -1,21 +1,10 @@
 /*
- * $Id: capi.h,v 1.1 1997/03/04 21:27:33 calle Exp $
+ * $Id: capi.h,v 1.4 2000/06/12 09:20:20 kai Exp $
  * 
  * CAPI 2.0 Interface for Linux
  * 
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
- * $Log: capi.h,v $
- * Revision 1.1  1997/03/04 21:27:33  calle
- * First version in isdn4linux
- *
- * Revision 2.2  1997/02/12 09:31:39  calle
- * new version
- *
- * Revision 1.1  1997/01/31 10:32:20  calle
- * Initial revision
- *
- * 
  */
 
 #ifndef __LINUX_CAPI_H__
@@ -107,6 +96,7 @@
  * CAPI_INSTALLED
  */
 #define CAPI_INSTALLED		_IOR('C',0x22, __u16)
+
 
 /*
  * member contr is input for
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/cciss_ioctl.h linux.18p17/include/linux/cciss_ioctl.h
--- linux.vanilla/include/linux/cciss_ioctl.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/cciss_ioctl.h	Sun Sep  3 00:10:45 2000
@@ -0,0 +1,186 @@
+#ifndef CCISS_IOCTLH
+#define CCISS_IOCTLH
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define CCISS_IOC_MAGIC 'B'
+
+
+typedef struct _cciss_pci_info_struct
+{
+	unsigned char 	bus;
+	unsigned char 	dev_fn;
+	__u32 		board_id;
+} cciss_pci_info_struct; 
+
+typedef struct _cciss_coalint_struct
+{
+	__u32  delay;
+	__u32  count;
+} cciss_coalint_struct;
+
+typedef char NodeName_type[16];
+
+typedef __u32 Heartbeat_type;
+
+#define CISS_PARSCSIU2 	0x0001
+#define CISS_PARCSCIU3 	0x0002
+#define CISS_FIBRE1G	0x0100
+#define CISS_FIBRE2G	0x0200
+typedef __u32 BusTypes_type;
+
+typedef char FirmwareVer_type[4];
+typedef __u32 DriverVer_type;
+
+
+#ifndef CCISS_CMD_H
+// This defines are duplicated in cciss_cmd.h in the driver directory 
+
+//general boundary defintions
+#define SENSEINFOBYTES          32//note that this value may vary between host implementations
+
+//Command Status value
+#define CMD_SUCCESS             0x0000
+#define CMD_TARGET_STATUS       0x0001
+#define CMD_DATA_UNDERRUN       0x0002
+#define CMD_DATA_OVERRUN        0x0003
+#define CMD_INVALID             0x0004
+#define CMD_PROTOCOL_ERR        0x0005
+#define CMD_HARDWARE_ERR        0x0006
+#define CMD_CONNECTION_LOST     0x0007
+#define CMD_ABORTED             0x0008
+#define CMD_ABORT_FAILED        0x0009
+#define CMD_UNSOLICITED_ABORT   0x000A
+#define CMD_TIMEOUT             0x000B
+#define CMD_UNABORTABLE		0x000C
+
+//transfer direction
+#define XFER_NONE               0x00
+#define XFER_WRITE              0x01
+#define XFER_READ               0x02
+#define XFER_RSVD               0x03
+
+//task attribute
+#define ATTR_UNTAGGED           0x00
+#define ATTR_SIMPLE             0x04
+#define ATTR_HEADOFQUEUE        0x05
+#define ATTR_ORDERED            0x06
+#define ATTR_ACA                0x07
+
+//cdb type
+#define TYPE_CMD				0x00
+#define TYPE_MSG				0x01
+
+// Type defs used in the following structs
+#define BYTE __u8
+#define WORD __u16
+#define HWORD __u16
+#define DWORD __u32
+
+#define CISS_MAX_LUN	16	
+
+#pragma pack(1)
+
+//Command List Structure
+typedef union _SCSI3Addr_struct {
+   struct {
+    BYTE Bus:6;
+    BYTE Mode:2;        // b00
+    BYTE Dev;
+  } PeripDev;
+   struct {
+    BYTE DevMSB:6;
+    BYTE Mode:2;        // b01
+    BYTE DevLSB;
+  } LogDev;
+   struct {
+    BYTE Targ:6;
+    BYTE Mode:2;        // b10
+    BYTE Dev:5;
+    BYTE Bus:3;
+  } LogUnit;
+} SCSI3Addr_struct;
+
+typedef struct _PhysDevAddr_struct {
+  DWORD             TargetId:24;
+  DWORD             Bus:6;
+  DWORD             Mode:2;
+  SCSI3Addr_struct  Target[2]; //2 level target device addr
+} PhysDevAddr_struct;
+  
+typedef struct _LogDevAddr_struct {
+  DWORD            VolId:30;
+  DWORD            Mode:2;
+  BYTE             reserved[4];
+} LogDevAddr_struct;
+
+typedef union _LUNAddr_struct {
+  BYTE               LunAddrBytes[8];
+  SCSI3Addr_struct   SCSI3Lun[4];
+  PhysDevAddr_struct PhysDev;
+  LogDevAddr_struct  LogDev;
+} LUNAddr_struct;
+
+typedef struct _RequestBlock_struct {
+  BYTE   CDBLen;
+  struct {
+    BYTE Type:3;
+    BYTE Attribute:3;
+    BYTE Direction:2;
+  } Type;
+  HWORD  Timeout;
+  BYTE   CDB[16];
+} RequestBlock_struct;
+
+typedef union _MoreErrInfo_struct{
+  struct {
+    BYTE  Reserved[3];
+    BYTE  Type;
+    DWORD ErrorInfo;
+  }Common_Info;
+  struct{
+    BYTE  Reserved[2];
+    BYTE  offense_size;//size of offending entry
+    BYTE  offense_num; //byte # of offense 0-base
+    DWORD offense_value;
+  }Invalid_Cmd;
+}MoreErrInfo_struct;
+typedef struct _ErrorInfo_struct {
+  BYTE               ScsiStatus;
+  BYTE               SenseLen;
+  HWORD              CommandStatus;
+  DWORD              ResidualCnt;
+  MoreErrInfo_struct MoreErrInfo;
+  BYTE               SenseInfo[SENSEINFOBYTES];
+} ErrorInfo_struct;
+
+#pragma pack()
+#endif /* CCISS_CMD_H */ 
+
+typedef struct _IOCTL_Command_struct {
+  LUNAddr_struct	   LUN_info;
+  RequestBlock_struct      Request;
+  ErrorInfo_struct  	   error_info; 
+  WORD			   buf_size;  /* size in bytes of the buf */
+  BYTE			   *buf;
+} IOCTL_Command_struct;
+
+
+#define CCISS_GETPCIINFO _IOR(CCISS_IOC_MAGIC, 1, cciss_pci_info_struct)
+
+#define CCISS_GETINTINFO _IOR(CCISS_IOC_MAGIC, 2, cciss_coalint_struct)
+#define CCISS_SETINTINFO _IOW(CCISS_IOC_MAGIC, 3, cciss_coalint_struct)
+
+#define CCISS_GETNODENAME _IOR(CCISS_IOC_MAGIC, 4, NodeName_type)
+#define CCISS_SETNODENAME _IOW(CCISS_IOC_MAGIC, 5, NodeName_type)
+
+#define CCISS_GETHEARTBEAT _IOR(CCISS_IOC_MAGIC, 6, Heartbeat_type)
+#define CCISS_GETBUSTYPES  _IOR(CCISS_IOC_MAGIC, 7, BusTypes_type)
+#define CCISS_GETFIRMVER   _IOR(CCISS_IOC_MAGIC, 8, FirmwareVer_type)
+#define CCISS_GETDRIVVER   _IOR(CCISS_IOC_MAGIC, 9, DriverVer_type)
+#define CCISS_REVALIDVOLS  _IO(CCISS_IOC_MAGIC, 10)
+#define CCISS_PASSTHRU	   _IOWR(CCISS_IOC_MAGIC, 11, IOCTL_Command_struct)
+
+
+#endif  
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/compatmac.h linux.18p17/include/linux/compatmac.h
--- linux.vanilla/include/linux/compatmac.h	Wed May  3 22:26:00 2000
+++ linux.18p17/include/linux/compatmac.h	Thu Oct 19 01:41:25 2000
@@ -149,15 +149,4 @@
 
 #endif
 
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is 
-   the compatibility define... */
-#define wait_queue_head_t     struct wait_queue *
-#define DECLARE_MUTEX(name)   struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) \
-                              struct wait_queue wait = { current, NULL }
-
-#endif
-
-
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/cpia.h linux.18p17/include/linux/cpia.h
--- linux.vanilla/include/linux/cpia.h	Fri Apr 21 23:14:09 2000
+++ linux.18p17/include/linux/cpia.h	Thu Jan  1 01:00:00 1970
@@ -1,220 +0,0 @@
-#ifndef cpia_h
-#define cpia_h
-
-/*
- * CPiA Parallel Port Video4Linux driver
- *
- * Supports CPiA based parallel port Video Camera's.
- *
- * (C) Copyright 1999 Bas Huisman,
- *                    Peter Pregler,
- *                    Scott J. Bertin,
- *                    VLSI Vision Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define CPIA_MAJ_VER	0
-#define CPIA_MIN_VER    5
-#define CPIA_PATCH_VER	0
-
-#define CPIA_PP_MAJ_VER       0
-#define CPIA_PP_MIN_VER       5
-#define CPIA_PP_PATCH_VER     0
-
-#define CPIA_MAX_FRAME_SIZE_UNALIGNED	(352 * 288 * 4)   /* CIF at RGB32 */
-#define CPIA_MAX_FRAME_SIZE	((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
-
-#ifdef __KERNEL__
-
-#include <asm/uaccess.h>
-
-struct cpia_camera_ops
-{
-	/* open sets privdata to point to structure for this camera.
-         * Returns negative value on error, otherwise 0.
-	 */
-	int (*open)(int camnr, void **privdata);
-	
-	/* Registers callback function cb to be called with cbdata
-	 * when an image is ready.  If cb is NULL, only single image grabs
-	 * should be used.  cb should immediately call streamRead to read
-	 * the data or data may be lost. Returns negative value on error,
-	 * otherwise 0.
-	 */
-	int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
-	                        void *cbdata);
-	
-	/* transferCmd sends commands to the camera.  command MUST point to
-	 * an  8 byte buffer in kernel space. data can be NULL if no extra
-	 * data is needed.  The size of the data is given by the last 2
-	 * bytes of comand.  data must also point to memory in kernel space.
-	 * Returns negative value on error, otherwise 0.
-	 */
-	int (*transferCmd)(void *privdata, u8 *command, u8 *data);
-
-	/* streamStart initiates stream capture mode.
-	 * Returns negative value on error, otherwise 0.
-	 */
-	int (*streamStart)(void *privdata);
-	
-	/* streamStop terminates stream capture mode.
-	 * Returns negative value on error, otherwise 0.
-	 */
-	int (*streamStop)(void *privdata);
-        
-	/* streamRead reads a frame from the camera.  buffer points to a
-         * buffer large enough to hold a complete frame in kernel space.
-         * noblock indicates if this should be a non blocking read.
-	 * Returns the number of bytes read, or negative value on error.
-         */
-	int (*streamRead)(void *privdata, u8 *buffer, int noblock);
-	
-	/* close disables the device until open() is called again.
-	 * Returns negative value on error, otherwise 0.
-	 */
-	int (*close)(void *privdata);
-};
-
-/* cpia_register_camera is called by low level driver for each camera.
- * A unique camera number is returned, or a negative value on error */
-int cpia_register_camera(struct cpia_camera_ops *ops);
-
-/* cpia_unregister_camera is called by low level driver when a camera
- * is removed.  This must not fail. */
-void cpia_unregister_camera(int camnr);
-
-#define CPIA_MAXCAMS		4
-
-/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
- * one byte 16bit DMA alignment
- */
-#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
-
-/* constant value's */
-#define MAGIC_0		0x19
-#define MAGIC_1		0x68
-#define DATA_IN		0xC0
-#define DATA_OUT	0x40
-#define VIDEOSIZE_QCIF	0	/* 176x144 */
-#define VIDEOSIZE_CIF	1	/* 352x288 */
-#define VIDEOSIZE_SIF	2	/* 320x240 */
-#define VIDEOSIZE_QSIF	3	/* 160x120 */
-#define VIDEOSIZE_48_48		4 /* where no one has gone before, iconsize! */
-#define VIDEOSIZE_64_48		5
-#define VIDEOSIZE_128_96	6
-#define VIDEOSIZE_160_120	VIDEOSIZE_QSIF
-#define VIDEOSIZE_176_144	VIDEOSIZE_QCIF
-#define VIDEOSIZE_192_144	7
-#define VIDEOSIZE_224_168	8
-#define VIDEOSIZE_256_192	9
-#define VIDEOSIZE_288_216	10
-#define VIDEOSIZE_320_240	VIDEOSIZE_SIF
-#define VIDEOSIZE_352_288	VIDEOSIZE_CIF
-#define SUBSAMPLE_420	0
-#define SUBSAMPLE_422	1
-#define YUVORDER_YUYV	0
-#define YUVORDER_UYVY	1
-#define NOT_COMPRESSED	0
-#define COMPRESSED	1
-#define NO_DECIMATION	0
-#define DECIMATION_ENAB	1
-#define EOI		0xff	/* End Of Image */
-#define EOL		0xfd	/* End Of Line */
-
-/* Image grab modes */
-#define CPIA_GRAB_SINGLE	0
-#define CPIA_GRAB_CONTINUOUS	1
-
-/* Compression parameters */
-#define CPIA_COMPRESSION_NONE	0
-#define CPIA_COMPRESSION_AUTO	1
-#define CPIA_COMPRESSION_MANUAL	2
-#define CPIA_COMPRESSION_TARGET_QUALITY         0
-#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
-
-/* Return offsets for GetCameraState */
-#define SYSTEMSTATE	0
-#define GRABSTATE	1
-#define STREAMSTATE	2
-#define FATALERROR	3
-#define CMDERROR	4
-#define DEBUGFLAGS	5
-#define VPSTATUS	6
-#define ERRORCODE	7
-
-/* SystemState */
-#define UNINITIALISED_STATE	0
-#define PASS_THROUGH_STATE	1
-#define LO_POWER_STATE		2
-#define HI_POWER_STATE		3
-#define WARM_BOOT_STATE		4
-
-/* GrabState */
-#define GRAB_IDLE		0
-#define GRAB_ACTIVE		1
-#define GRAB_DONE		2
-
-/* StreamState */
-#define STREAM_NOT_READY	0
-#define STREAM_READY		1
-#define STREAM_OPEN		2
-#define STREAM_PAUSED		3
-#define STREAM_FINISHED		4
-
-/* Fatal Error, CmdError, and DebugFlags */
-#define CPIA_FLAG	  1
-#define SYSTEM_FLAG	  2
-#define INT_CTRL_FLAG	  4
-#define PROCESS_FLAG	  8
-#define COM_FLAG	 16
-#define VP_CTRL_FLAG	 32
-#define CAPTURE_FLAG	 64
-#define DEBUG_FLAG	128
-
-/* VPStatus */
-#define VP_STATE_OK			0x00
-
-#define VP_STATE_FAILED_VIDEOINIT	0x01
-#define VP_STATE_FAILED_AECACBINIT	0x02
-#define VP_STATE_AEC_MAX		0x04
-#define VP_STATE_ACB_BMAX		0x08
-
-#define VP_STATE_ACB_RMIN		0x10
-#define VP_STATE_ACB_GMIN		0x20
-#define VP_STATE_ACB_RMAX		0x40
-#define VP_STATE_ACB_GMAX		0x80
-
-/* ErrorCode */
-#define ERROR_FLICKER_BELOW_MIN_EXP     0x01 /*flicker exposure got below minimum exposure */
-
-#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args)
-#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args)
-
-#ifdef _CPIA_DEBUG_
-#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args)
-#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args)
-#else
-#define DBG(fmn,args...) {}
-#endif
-
-#define DEB_BYTE(p)\
-  DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
-      (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
-        (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
-
-#endif /* __KERNEL__ */
-
-#endif /* cpia_h */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/dcache.h linux.18p17/include/linux/dcache.h
--- linux.vanilla/include/linux/dcache.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/dcache.h	Sun Oct 15 21:15:17 2000
@@ -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
@@ -151,7 +157,7 @@
 /* test whether root is busy without destroying dcache */
 extern int is_root_busy(struct dentry *);
 
-/* test whether we have any submounts in a subdir tree */
+/* test whether we have any submounts */
 extern int have_submounts(struct dentry *);
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/delay.h linux.18p17/include/linux/delay.h
--- linux.vanilla/include/linux/delay.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/delay.h	Sat Sep 30 14:41:43 2000
@@ -4,10 +4,10 @@
 /*
  * Copyright (C) 1993 Linus Torvalds
  *
- * Delay routines, using a pre-computed "loops_per_second" value.
+ * Delay routines, using a pre-computed "loops_per_jiffy" value.
  */
 
-extern unsigned long loops_per_sec;
+extern unsigned long loops_per_jiffy;
 
 #include <asm/delay.h>
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/devfs_fs.h linux.18p17/include/linux/devfs_fs.h
--- linux.vanilla/include/linux/devfs_fs.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/devfs_fs.h	Sat Sep 30 14:48:14 2000
@@ -0,0 +1,42 @@
+#ifndef _LINUX_DEVFS_FS_H
+#define _LINUX_DEVFS_FS_H
+
+#include <linux/ioctl.h>
+
+#define DEVFSD_PROTOCOL_REVISION_KERNEL  5
+
+#define	DEVFSD_IOCTL_BASE	'd'
+
+/*  These are the various ioctls  */
+#define DEVFSDIOC_GET_PROTO_REV         _IOR(DEVFSD_IOCTL_BASE, 0, int)
+#define DEVFSDIOC_SET_EVENT_MASK        _IOW(DEVFSD_IOCTL_BASE, 2, int)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE   _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_DEBUG_MASK        _IOW(DEVFSD_IOCTL_BASE, 4, int)
+
+#define DEVFSD_NOTIFY_REGISTERED    0
+#define DEVFSD_NOTIFY_UNREGISTERED  1
+#define DEVFSD_NOTIFY_ASYNC_OPEN    2
+#define DEVFSD_NOTIFY_CLOSE         3
+#define DEVFSD_NOTIFY_LOOKUP        4
+#define DEVFSD_NOTIFY_CHANGE        5
+#define DEVFSD_NOTIFY_CREATE        6
+
+#define DEVFS_PATHLEN               1024  /*  Never change this otherwise the
+					      binary interface will change   */
+
+struct devfsd_notify_struct
+{
+    unsigned int type;           /*  DEVFSD_NOTIFY_* value                   */
+    unsigned int mode;           /*  Mode of the inode or device entry       */
+    unsigned int major;          /*  Major number of device entry            */
+    unsigned int minor;          /*  Minor number of device entry            */
+    unsigned int uid;            /*  Uid of process, inode or device entry   */
+    unsigned int gid;            /*  Gid of process, inode or device entry   */
+    unsigned int overrun_count;  /*  Number of lost events                   */
+    unsigned int namelen;        /*  Number of characters not including '\0' */
+    /*  The device name MUST come last                                       */
+    char devname[DEVFS_PATHLEN]; /*  This will be '\0' terminated            */
+};
+
+
+#endif  /*  _LINUX_DEVFS_FS_H  */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/devfs_fs_kernel.h linux.18p17/include/linux/devfs_fs_kernel.h
--- linux.vanilla/include/linux/devfs_fs_kernel.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/devfs_fs_kernel.h	Sun Oct 15 23:33:58 2000
@@ -0,0 +1,247 @@
+#ifndef _LINUX_DEVFS_FS_KERNEL_H
+#define _LINUX_DEVFS_FS_KERNEL_H
+
+#include <linux/fs.h>
+#include <linux/config.h>
+
+#define DEVFS_SUPER_MAGIC                0x1373
+
+#define IS_DEVFS_INODE(inode) (DEVFS_SUPER_MAGIC == (inode)->i_sb->s_magic)
+
+#define DEVFS_MINOR(inode) \
+    ({unsigned int m; /* evil GCC trickery */ \
+      ((inode)->i_sb && \
+       ((inode)->i_sb->s_magic==DEVFS_SUPER_MAGIC) && \
+       (devfs_get_maj_min(devfs_get_handle_from_inode((inode)),NULL,&m)==0) \
+      ) ? m : MINOR((inode)->r_dev); })
+
+
+#define DEVFS_FL_NONE           0x000 /* This helps to make code more readable
+				       */
+#define DEVFS_FL_AUTO_OWNER     0x001 /* When a closed inode is opened the
+					 ownerships are set to the opening
+					 process and the protection is set to
+					 that given in <<mode>>. When the inode
+					 is closed, ownership reverts back to
+					 <<uid>> and <<gid>> and the protection
+					 is set to read-write for all        */
+#define DEVFS_FL_SHOW_UNREG     0x002 /* Show unregistered entries in
+					 directory listings                  */
+#define DEVFS_FL_HIDE           0x004 /* Do not show entry in directory list */
+#define DEVFS_FL_AUTO_DEVNUM    0x008 /* Automatically generate device number
+				       */
+#define DEVFS_FL_AOPEN_NOTIFY   0x010 /* Asynchronously notify devfsd on open
+				       */
+#define DEVFS_FL_REMOVABLE      0x020 /* This is a removable media device    */
+#define DEVFS_FL_WAIT           0x040 /* Wait for devfsd to finish           */
+#define DEVFS_FL_NO_PERSISTENCE 0x080 /* Forget changes after unregister     */
+#define DEVFS_FL_CURRENT_OWNER  0x100 /* Set initial ownership to current    */
+#define DEVFS_FL_DEFAULT        DEVFS_FL_NONE
+
+
+#define DEVFS_SPECIAL_CHR     0
+#define DEVFS_SPECIAL_BLK     1
+
+typedef struct devfs_entry * devfs_handle_t;
+
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#  define ROOT_DEVICE_NAME ((real_root_dev ==ROOT_DEV) ? root_device_name:NULL)
+#else
+#  define ROOT_DEVICE_NAME root_device_name
+#endif
+
+
+#ifdef CONFIG_DEVFS_FS
+extern devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
+				      unsigned int flags,
+				      unsigned int major, unsigned int minor,
+				      umode_t mode,
+				      struct file_operations *fops,
+				      void *info);
+extern void devfs_unregister (devfs_handle_t de);
+extern int devfs_mk_symlink (devfs_handle_t dir,
+			     const char *name, unsigned int namelen,
+			     unsigned int flags,
+			     const char *link, unsigned int linklength,
+			     devfs_handle_t *handle, void *info);
+extern devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name,
+				    unsigned int namelen, void *info);
+extern int devfs_fill_file (struct inode *inode, struct file *file,
+			    devfs_handle_t de);
+extern devfs_handle_t devfs_find_handle (devfs_handle_t dir,
+					 const char *name,unsigned int namelen,
+					 unsigned int major,unsigned int minor,
+					 char type, int traverse_symlinks);
+extern int devfs_get_flags (devfs_handle_t de, unsigned int *flags);
+extern int devfs_set_flags (devfs_handle_t de, unsigned int flags);
+extern int devfs_get_maj_min (devfs_handle_t de, 
+			      unsigned int *major, unsigned int *minor);
+extern devfs_handle_t devfs_get_handle_from_inode (struct inode *inode);
+extern int devfs_generate_path (devfs_handle_t de, char *path, int buflen);
+extern struct file_operations *devfs_get_fops (devfs_handle_t de);
+extern int devfs_set_file_size (devfs_handle_t de, unsigned long size);
+extern void *devfs_get_info (devfs_handle_t de);
+extern int devfs_set_info (devfs_handle_t de, void *info);
+extern devfs_handle_t devfs_get_parent (devfs_handle_t de);
+extern devfs_handle_t devfs_get_first_child (devfs_handle_t de);
+extern devfs_handle_t devfs_get_next_sibling (devfs_handle_t de);
+extern void devfs_auto_unregister (devfs_handle_t master,devfs_handle_t slave);
+extern devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master);
+extern int devfs_register_chrdev (unsigned int major, const char *name,
+				  struct file_operations *fops);
+extern int devfs_register_blkdev (unsigned int major, const char *name,
+				  struct file_operations *fops);
+extern int devfs_unregister_chrdev (unsigned int major, const char *name);
+extern int devfs_unregister_blkdev (unsigned int major, const char *name);
+
+extern void devfs_register_tape (devfs_handle_t de);
+
+extern int init_devfs_fs (void);
+extern void mount_devfs_fs (void);
+extern void devfs_make_root (const char *name);
+#else  /*  CONFIG_DEVFS_FS  */
+static inline devfs_handle_t devfs_register (devfs_handle_t dir,
+					     const char *name,
+					     unsigned int flags,
+					     unsigned int major,
+					     unsigned int minor,
+					     umode_t mode,
+					     struct file_operations *fops,
+					     void *info)
+{
+    return NULL;
+}
+static inline void devfs_unregister (devfs_handle_t de)
+{
+    return;
+}
+static inline int devfs_mk_symlink (devfs_handle_t dir,
+				    const char *name, unsigned int namelen,
+				    unsigned int flags,
+				    const char *link, unsigned int linklength,
+				    devfs_handle_t *handle, void *info)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_mk_dir (devfs_handle_t dir,
+					   const char *name,
+					   unsigned int namelen, void *info)
+{
+    return NULL;
+}
+static inline int devfs_fill_file (struct inode *inode, struct file *file,
+				   devfs_handle_t de)
+{
+    return -ENOSYS;
+}
+static inline devfs_handle_t devfs_find_handle (devfs_handle_t dir,
+						const char *name,
+						unsigned int namelen,
+						unsigned int major,
+						unsigned int minor,
+						char type,
+						int traverse_symlinks)
+{
+    return NULL;
+}
+static inline int devfs_get_flags (devfs_handle_t de, unsigned int *flags)
+{
+    return 0;
+}
+static inline int devfs_set_flags (devfs_handle_t de, unsigned int flags)
+{
+    return 0;
+}
+static inline int devfs_get_maj_min (devfs_handle_t de, 
+				     unsigned int *major, unsigned int *minor)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_get_handle_from_inode (struct inode *inode)
+{
+    return NULL;
+}
+static inline int devfs_generate_path (devfs_handle_t de, char *path,
+				       int buflen)
+{
+    return -ENOSYS;
+}
+static inline struct file_operations *devfs_get_fops (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline int devfs_set_file_size (devfs_handle_t de, unsigned long size)
+{
+    return -ENOSYS;
+}
+static inline void *devfs_get_info (devfs_handle_t de, unsigned long size)
+{
+    return NULL;
+}
+static inline int devfs_set_info (devfs_handle_t de, void *info)
+{
+    return 0;
+}
+static inline devfs_handle_t devfs_get_parent (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline devfs_handle_t devfs_get_first_child (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
+{
+    return NULL;
+}
+static inline void devfs_auto_unregister (devfs_handle_t master,
+					  devfs_handle_t slave)
+{
+    return;
+}
+static inline devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
+{
+    return NULL;
+}
+static inline int devfs_register_chrdev (unsigned int major, const char *name,
+					 struct file_operations *fops)
+{
+    return register_chrdev (major, name, fops);
+}
+static inline int devfs_register_blkdev (unsigned int major, const char *name,
+					 struct file_operations *fops)
+{
+    return register_blkdev (major, name, fops);
+}
+static inline int devfs_unregister_chrdev (unsigned int major,const char *name)
+{
+    return unregister_chrdev (major, name);
+}
+static inline int devfs_unregister_blkdev (unsigned int major,const char *name)
+{
+    return unregister_blkdev (major, name);
+}
+
+static inline void devfs_register_tape (devfs_handle_t de)
+{
+    return;
+}
+
+static inline int init_devfs_fs (void)
+{
+    return 0;
+}
+static inline void mount_devfs_fs (void)
+{
+    return;
+}
+static inline void devfs_make_root (const char *name)
+{
+    return;
+}
+#endif  /*  CONFIG_DEVFS_FS  */
+
+#endif  /*  _LINUX_DEVFS_FS_KERNEL_H  */
+#define tty_register_devfs(driver,flags,minor)
+#define tty_unregister_devfs(driver,minor)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/dtlk.h linux.18p17/include/linux/dtlk.h
--- linux.vanilla/include/linux/dtlk.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/dtlk.h	Sat Sep 30 18:45:23 2000
@@ -27,7 +27,7 @@
 
 #define DTLK_CLEAR 0x18		/* stops speech */
 
-#define DTLK_MAX_RETRIES (loops_per_sec/10000)
+#define DTLK_MAX_RETRIES (loops_per_jiffy/(10000/HZ))
 
 	/* TTS Port Status Flags */
 #define TTS_READABLE     0x80	/* mask for bit which is nonzero if a
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/errno.h linux.18p17/include/linux/errno.h
--- linux.vanilla/include/linux/errno.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/errno.h	Fri Sep 15 22:10:44 2000
@@ -11,6 +11,16 @@
 #define ERESTARTNOHAND	514	/* restart if no handler.. */
 #define ENOIOCTLCMD	515	/* No ioctl command */
 
+/* Defined for the NFSv3 protocol */
+#define EBADHANDLE	521	/* Illegal NFS file handle */
+#define ENOTSYNC	522	/* Update synchronization mismatch */
+#define EBADCOOKIE	523	/* Cookie is stale */
+#define ENOTSUPP	524	/* Operation is not supported */
+#define ETOOSMALL	525	/* Buffer or request is too small */
+#define ESERVERFAULT	526	/* An untranslatable error occurred */
+#define EBADTYPE	527	/* Type not supported by server */
+#define EJUKEBOX	528	/* Request initiated, but will not complete before timeout */
+
 #endif
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/ext2_fs.h linux.18p17/include/linux/ext2_fs.h
--- linux.vanilla/include/linux/ext2_fs.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/ext2_fs.h	Sun Oct 15 21:15:17 2000
@@ -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 */
@@ -546,6 +546,7 @@
 /* file.c */
 extern int ext2_read (struct inode *, struct file *, char *, int);
 extern int ext2_write (struct inode *, struct file *, char *, int);
+extern void ext2_remove_suid (struct inode *); 
 
 /* fsync.c */
 extern int ext2_sync_file (struct file *, struct dentry *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/ext2_fs_i.h linux.18p17/include/linux/ext2_fs_i.h
--- linux.vanilla/include/linux/ext2_fs_i.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/ext2_fs_i.h	Sun Oct 15 21:15:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/ext2_fs_sb.h linux.18p17/include/linux/ext2_fs_sb.h
--- linux.vanilla/include/linux/ext2_fs_sb.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/ext2_fs_sb.h	Tue Sep 12 12:55:12 2000
@@ -16,7 +16,6 @@
 #ifndef _LINUX_EXT2_FS_SB
 #define _LINUX_EXT2_FS_SB
 
-#include <linux/ext2_fs.h>
 
 /*
  * The following is not needed anymore since the descriptors buffer
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/fs.h linux.18p17/include/linux/fs.h
--- linux.vanilla/include/linux/fs.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/linux/fs.h	Sun Oct 15 21:15:17 2000
@@ -279,6 +279,7 @@
 #include <linux/hfs_fs_i.h>
 #include <linux/adfs_fs_i.h>
 #include <linux/qnx4_fs_i.h>
+#include <linux/usbdev_fs_i.h>
 
 /*
  * Attribute flags.  These should be or-ed together to figure out what
@@ -360,6 +361,7 @@
 	struct wait_queue	*i_wait;
 	struct file_lock	*i_flock;
 	struct vm_area_struct	*i_mmap;
+	struct vm_area_struct	*i_mmap_shared;
 	struct page		*i_pages;
 	struct dquot		*i_dquot[MAXQUOTAS];
 
@@ -392,6 +394,7 @@
 		struct hfs_inode_info		hfs_i;
 		struct adfs_inode_info		adfs_i;
 		struct qnx4_inode_info		qnx4_i;
+		struct usbdev_inode_info	usbdev_i;
 		struct socket			socket_i;
 		void				*generic_ip;
 	} u;
@@ -466,6 +469,8 @@
 	off_t fl_end;
 
 	void (*fl_notify)(struct file_lock *);	/* unblock callback */
+	void (*fl_insert)(struct file_lock *);	/* lock insertion callback */
+	void (*fl_remove)(struct file_lock *);	/* lock removal callback */
 
 	union {
 		struct nfs_lock_info	nfs_fl;
@@ -514,6 +519,7 @@
 #include <linux/hfs_fs_sb.h>
 #include <linux/adfs_fs_sb.h>
 #include <linux/qnx4_fs_sb.h>
+#include <linux/usbdev_fs_sb.h>
 
 extern struct list_head super_blocks;
 
@@ -557,6 +563,7 @@
 		struct hfs_sb_info	hfs_sb;
 		struct adfs_sb_info	adfs_sb;
 		struct qnx4_sb_info	qnx4_sb;	   
+		struct usbdev_sb_info	usbdevfs_sb;
 		void			*generic_sbp;
 	} u;
 	/*
@@ -564,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;
 };
 
 /*
@@ -621,6 +637,8 @@
 	int (*smap) (struct inode *,int);
 	int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int, int);
 	int (*revalidate) (struct dentry *);
+	int (*prepare_write) (struct file *, struct page *, unsigned, unsigned);
+	int (*sync_page) (struct page *);
 };
 
 struct super_operations {
@@ -654,6 +672,13 @@
 	struct file_system_type * next;
 };
 
+#define DECLARE_FSTYPE(var,type,read,flags) \
+struct file_system_type var = { \
+        name:           type, \
+        read_super:     read, \
+        fs_flags:       flags, \
+}
+
 extern int register_filesystem(struct file_system_type *);
 extern int unregister_filesystem(struct file_system_type *);
 
@@ -798,6 +823,7 @@
 extern void sync_supers(kdev_t dev);
 extern int bmap(struct inode * inode,int block);
 extern int notify_change(struct dentry *, struct iattr *);
+extern int vfs_permission(struct inode * inode,int mask);
 extern int permission(struct inode * inode,int mask);
 extern int get_write_access(struct inode *inode);
 extern void put_write_access(struct inode *inode);
@@ -842,7 +868,10 @@
 extern void iput(struct inode *);
 extern struct inode * igrab(struct inode *inode);
 extern ino_t iunique(struct super_block *, ino_t);
-extern struct inode * iget(struct super_block *, unsigned long);
+
+typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+extern struct inode * iget4(struct super_block *, unsigned long, find_inode_t, void *);
+extern struct inode * iget(struct super_block *sb, unsigned long ino);
 extern struct inode * iget_in_use (struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
 extern struct inode * get_empty_inode(void);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/hdreg.h linux.18p17/include/linux/hdreg.h
--- linux.vanilla/include/linux/hdreg.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/hdreg.h	Sun Oct 15 22:14:08 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/i2c.h linux.18p17/include/linux/i2c.h
--- linux.vanilla/include/linux/i2c.h	Wed May  3 21:21:40 2000
+++ linux.18p17/include/linux/i2c.h	Thu Oct 19 01:37:34 2000
@@ -40,7 +40,8 @@
 	/* 2 is used in 2.3.x */
 #define I2C_BUSID_BUZ		3	/* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN		4	/* I2C bus on a Zoran */
-#define I2C_BUSID_SGIVWFB	5	/* Moved to be unique */
+#define I2C_BUSID_CYBER2000	5	/* I2C bus on a Cyber2000 */
+#define I2C_BUSID_SGIVWFB	6	/* Moved to be unique */
 
 /*
  * struct for a driver for a i2c chip (tuner, soundprocessor,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/i2o.h linux.18p17/include/linux/i2o.h
--- linux.vanilla/include/linux/i2o.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/linux/i2o.h	Sun Oct  1 11:57:11 2000
@@ -638,12 +638,6 @@
 #define I2O_POST_WAIT_OK	0
 #define I2O_POST_WAIT_TIMEOUT	-ETIMEDOUT
 
-#define DECLARE_MUTEX(name) struct semaphore name=MUTEX
-
-typedef struct wait_queue *wait_queue_head_t;
-#define DECLARE_WAIT_QUEUE_HEAD(wait)\
-        wait_queue_head_t wait
-
 #endif /* __KERNEL__ */
 
 #endif /* _I2O_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/in6.h linux.18p17/include/linux/in6.h
--- linux.vanilla/include/linux/in6.h	Fri Apr 21 23:11:02 2000
+++ linux.18p17/include/linux/in6.h	Fri Sep  8 16:53:11 2000
@@ -34,21 +34,10 @@
 		__u8		u6_addr8[16];
 		__u16		u6_addr16[8];
 		__u32		u6_addr32[4];
-#if (~0UL) > 0xffffffff
-#ifndef __RELAX_IN6_ADDR_ALIGNMENT
-		/* Alas, protocols do not respect 64bit alignmnet.
-		   rsvp/pim/... are broken. However, it is good
-		   idea to force correct alignment always, when
-		   it is possible.
-		 */
-		__u64		u6_addr64[2];
-#endif
-#endif
 	} in6_u;
 #define s6_addr			in6_u.u6_addr8
 #define s6_addr16		in6_u.u6_addr16
 #define s6_addr32		in6_u.u6_addr32
-#define s6_addr64		in6_u.u6_addr64
 };
 
 struct sockaddr_in6 {
@@ -138,7 +127,7 @@
  */
 #define IPV6_TLV_PAD0		0
 #define IPV6_TLV_PADN		1
-#define IPV6_TLV_ROUTERALERT	20
+#define IPV6_TLV_ROUTERALERT	5
 #define IPV6_TLV_JUMBO		194
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/init.h linux.18p17/include/linux/init.h
--- linux.vanilla/include/linux/init.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/init.h	Sat Sep 30 15:18:06 2000
@@ -17,7 +17,7 @@
  *    extern int z; z = x * y;
  * }
  *
- * Depricated: you can surround the whole function declaration 
+ * Deprecated: you can surround the whole function declaration 
  * just before function body into __initfunc() macro, like:
  *
  * __initfunc (static void initme(int x, int y))
@@ -65,4 +65,69 @@
 #define __initlocaldata
 #endif
 
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Used for kernel command line parameter setup
+ */
+struct new_kernel_param {
+	const char *str;
+	int (*setup_func)(char *);
+};
+
+extern struct new_kernel_param __setup_start, __setup_end;
+
+#define __setup(str, fn)								\
+	static char __setup_str_##fn[] __initdata = str;				\
+	static struct new_kernel_param __setup_##fn __initsetup = { __setup_str_##fn, fn }
+
+#define __initsetup	__attribute__ ((unused,__section__ (".setup.init")))
+
+
+/*
+ * Used for initialization calls..
+ */
+typedef int (*initcall_t)(void);
+typedef void (*exitcall_t)(void);
+
+#define __init_call	__attribute__ ((unused,__section__ (".initcall.init")))
+#define __exit_call	__attribute__ ((unused,__section__ (".exitcall.exit")))
+
+extern initcall_t __initcall_start, __initcall_end;
+
+#define __initcall(fn)								\
+	static initcall_t __initcall_##fn __init_call = fn
+#define __exitcall(fn)								\
+	static exitcall_t __exitcall_##fn __exit_call = fn
+
+#ifdef MODULE
+/* Not sure what version aliases were introduced in, but certainly in 2.91.66.  */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91)
+/* These macros create a dummy inline: gcc 2.9x does not count alias
+ as usage, hence the `unused function' warning when __init functions
+ are declared static. We use the dummy __*_module_inline functions
+ both to kill the warning and check the type of the init/cleanup
+ function. */
+typedef int (*__init_module_func_t)(void);
+typedef void (*__cleanup_module_func_t)(void);
+#define module_init(x) \
+	int init_module(void) __attribute__((alias(#x))); \
+	extern inline __init_module_func_t __init_module_inline(void) \
+	{ return x; }
+#define module_exit(x) \
+	void cleanup_module(void) __attribute__((alias(#x))); \
+	extern inline __cleanup_module_func_t __cleanup_module_inline(void) \
+	{ return x; }
+#else
+#define module_init(x)	int init_module(void) { return x(); }
+#define module_exit(x)	void cleanup_module(void) { x(); }
+#endif
+
+#else 
+#define module_init(x)	__initcall(x);
+#define module_exit(x)	__exitcall(x);
+#endif
+
+#endif /* __ASSEMBLY __ */
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/input.h linux.18p17/include/linux/input.h
--- linux.vanilla/include/linux/input.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/input.h	Sun Oct 15 23:40:45 2000
@@ -0,0 +1,532 @@
+#ifndef _INPUT_H
+#define _INPUT_H
+
+/*
+ * $Id: input.h,v 1.13 2000/05/29 10:54:53 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * 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
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#ifdef __KERNEL__
+#include <linux/time.h>
+#else
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#endif
+
+/*
+ * The event structure itself
+ */
+
+struct input_event {
+	struct timeval time;
+	unsigned short type;
+	unsigned short code;
+	unsigned int value;
+};
+
+/*
+ * Protocol version.
+ */
+
+#define EV_VERSION		0x010000
+
+/*
+ * IOCTLs (0x00 - 0x7f)
+ */
+
+#define EVIOCGVERSION		_IOR('E', 0x01, int)                    /* get driver version */
+#define EVIOCGID		_IOR('E', 0x02, short[4])		/* get device ID */
+#define EVIOCGREP		_IOR('E', 0x03, int[2])			/* get repeat settings */
+#define EVIOCSREP		_IOW('E', 0x03, int[2])			/* get repeat settings */
+#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
+#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */
+#define EVIOCGKEY		_IOR('E', 0x05, int[2])			/* get key value */
+#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
+
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, int[5])		/* get abs value/limits */ 
+
+/*
+ * Event types
+ */
+
+#define EV_RST			0x00
+#define EV_KEY			0x01
+#define EV_REL			0x02
+#define EV_ABS			0x03
+#define EV_LED			0x11
+#define EV_SND			0x12
+#define EV_REP			0x14
+#define EV_MAX			0x1f
+
+/*
+ * Keys and buttons
+ */
+
+#define KEY_RESERVED		0		
+#define KEY_ESC			1		
+#define KEY_1			2		
+#define KEY_2			3		
+#define KEY_3			4		
+#define KEY_4			5		
+#define KEY_5			6		
+#define KEY_6			7		
+#define KEY_7			8		
+#define KEY_8			9		
+#define KEY_9			10		
+#define KEY_0			11		
+#define KEY_MINUS		12		
+#define KEY_EQUAL		13		
+#define KEY_BACKSPACE		14		
+#define KEY_TAB			15		
+#define KEY_Q			16		
+#define KEY_W			17		
+#define KEY_E			18		
+#define KEY_R			19		
+#define KEY_T			20		
+#define KEY_Y			21		
+#define KEY_U			22		
+#define KEY_I			23		
+#define KEY_O			24		
+#define KEY_P			25		
+#define KEY_LEFTBRACE		26		
+#define KEY_RIGHTBRACE		27		
+#define KEY_ENTER		28		
+#define KEY_LEFTCTRL		29		
+#define KEY_A			30		
+#define KEY_S			31		
+#define KEY_D			32		
+#define KEY_F			33		
+#define KEY_G			34		
+#define KEY_H			35		
+#define KEY_J			36		
+#define KEY_K			37		
+#define KEY_L			38		
+#define KEY_SEMICOLON		39		
+#define KEY_APOSTROPHE		40		
+#define KEY_GRAVE		41		
+#define KEY_LEFTSHIFT		42		
+#define KEY_BACKSLASH		43		
+#define KEY_Z			44		
+#define KEY_X			45		
+#define KEY_C			46		
+#define KEY_V			47		
+#define KEY_B			48		
+#define KEY_N			49		
+#define KEY_M			50		
+#define KEY_COMMA		51		
+#define KEY_DOT			52		
+#define KEY_SLASH		53		
+#define KEY_RIGHTSHIFT		54		
+#define KEY_KPASTERISK		55		
+#define KEY_LEFTALT		56		
+#define KEY_SPACE		57		
+#define KEY_CAPSLOCK		58		
+#define KEY_F1			59		
+#define KEY_F2			60		
+#define KEY_F3			61		
+#define KEY_F4			62		
+#define KEY_F5			63		
+#define KEY_F6			64		
+#define KEY_F7			65		
+#define KEY_F8			66		
+#define KEY_F9			67		
+#define KEY_F10			68		
+#define KEY_NUMLOCK		69		
+#define KEY_SCROLLLOCK		70		
+#define KEY_KP7			71		
+#define KEY_KP8			72		
+#define KEY_KP9			73		
+#define KEY_KPMINUS		74		
+#define KEY_KP4			75		
+#define KEY_KP5			76		
+#define KEY_KP6			77		
+#define KEY_KPPLUS		78		
+#define KEY_KP1			79		
+#define KEY_KP2			80		
+#define KEY_KP3			81		
+#define KEY_KP0			82		
+#define KEY_KPDOT		83		
+#define KEY_103RD		84
+#define KEY_F13			85		
+#define KEY_102ND		86		
+#define KEY_F11			87		
+#define KEY_F12			88		
+#define KEY_F14			89		
+#define KEY_F15			90		
+#define KEY_F16			91		
+#define KEY_F17			92		
+#define KEY_F18			93		
+#define KEY_F19			94		
+#define KEY_F20			95		
+#define KEY_KPENTER		96		
+#define KEY_RIGHTCTRL		97		
+#define KEY_KPSLASH		98		
+#define KEY_SYSRQ		99		
+#define KEY_RIGHTALT		100		
+#define KEY_LINEFEED		101		
+#define KEY_HOME		102		
+#define KEY_UP			103		
+#define KEY_PAGEUP		104		
+#define KEY_LEFT		105		
+#define KEY_RIGHT		106		
+#define KEY_END			107		
+#define KEY_DOWN		108		
+#define KEY_PAGEDOWN		109		
+#define KEY_INSERT		110		
+#define KEY_DELETE		111		
+#define KEY_MACRO		112		
+#define KEY_MUTE		113		
+#define KEY_VOLUMEDOWN		114		
+#define KEY_VOLUMEUP		115		
+#define KEY_POWER		116		
+#define KEY_KPEQUAL		117		
+#define KEY_KPPLUSMINUS		118		
+#define KEY_PAUSE		119		
+#define KEY_F21			120		
+#define KEY_F22			121		
+#define KEY_F23			122		
+#define KEY_F24			123		
+#define KEY_JPN			124		
+#define KEY_LEFTMETA		125		
+#define KEY_RIGHTMETA		126		
+#define KEY_COMPOSE		127		
+
+#define KEY_STOP		128		
+#define KEY_AGAIN		129		
+#define KEY_PROPS		130		
+#define KEY_UNDO		131		
+#define KEY_FRONT		132		
+#define KEY_COPY		133		
+#define KEY_OPEN		134		
+#define KEY_PASTE		135		
+#define KEY_FIND		136		
+#define KEY_CUT			137		
+#define KEY_HELP		138		
+#define KEY_MENU		139		
+#define KEY_CALC		140		
+#define KEY_SETUP		141		
+#define KEY_SLEEP		142		
+#define KEY_WAKEUP		143		
+#define KEY_FILE		144		
+#define KEY_SENDFILE		145		
+#define KEY_DELETEFILE		146		
+#define KEY_XFER		147		
+#define KEY_PROG1		148		
+#define KEY_PROG2		149		
+#define KEY_WWW			150		
+#define KEY_MSDOS		151		
+#define KEY_COFFEE		152		
+#define KEY_DIRECTION		153		
+#define KEY_CYCLEWINDOWS	154		
+#define KEY_MAIL		155
+#define KEY_BOOKMARKS		156
+#define KEY_COMPUTER		157
+#define KEY_BACK		158
+#define KEY_FORWARD		159
+#define KEY_CLOSECD		160		
+#define KEY_EJECTCD		161		
+#define KEY_EJECTCLOSECD	162		
+#define KEY_NEXTSONG		163		
+#define KEY_PLAYPAUSE		164		
+#define KEY_PREVIOUSSONG	165		
+#define KEY_STOPCD		166		
+#define KEY_RECORD		167		
+#define KEY_REWIND		168		
+#define KEY_PHONE		169
+
+#define KEY_CONFIG		171
+#define KEY_HOMEPAGE		172
+#define KEY_REFRESH		173
+#define KEY_EXIT		174
+#define KEY_MOVE		175
+#define KEY_EDIT		176
+#define KEY_SCROLLUP		177
+#define KEY_SCROLLDOWN		178
+
+#define KEY_UNKNOWN		180
+
+#define BTN_MISC		0x100
+#define BTN_0			0x100
+#define BTN_1			0x101
+#define BTN_2			0x102
+#define BTN_3			0x103
+#define BTN_4			0x104
+#define BTN_5			0x105
+#define BTN_6			0x106
+#define BTN_7			0x107
+#define BTN_8			0x108
+#define BTN_9			0x109
+
+#define BTN_MOUSE		0x110
+#define BTN_LEFT		0x110
+#define BTN_RIGHT		0x111
+#define BTN_MIDDLE		0x112
+#define BTN_SIDE		0x113
+#define BTN_EXTRA		0x114
+#define BTN_FORWARD		0x115
+#define BTN_BACK		0x116
+
+#define BTN_JOYSTICK		0x120
+#define BTN_TRIGGER		0x120
+#define BTN_THUMB		0x121
+#define BTN_THUMB2		0x122
+#define BTN_TOP			0x123
+#define BTN_TOP2		0x124
+#define BTN_PINKIE		0x125
+#define BTN_BASE		0x126
+#define BTN_BASE2		0x127
+#define BTN_BASE3		0x128
+#define BTN_BASE4		0x129
+#define BTN_BASE5		0x12a
+#define BTN_BASE6		0x12b
+
+#define BTN_GAMEPAD		0x130
+#define BTN_A			0x130
+#define BTN_B			0x131
+#define BTN_C			0x132
+#define BTN_X			0x133
+#define BTN_Y			0x134
+#define BTN_Z			0x135
+#define BTN_TL			0x136
+#define BTN_TR			0x137
+#define BTN_TL2			0x138
+#define BTN_TR2			0x139
+#define BTN_SELECT		0x13a
+#define BTN_START		0x13b
+#define BTN_MODE		0x13c
+
+#define BTN_DIGI		0x140
+#define BTN_TOOL_PEN		0x140
+#define BTN_TOOL_RUBBER		0x141
+#define BTN_TOOL_BRUSH		0x142
+#define BTN_TOOL_PENCIL		0x143
+#define BTN_TOOL_AIRBRUSH	0x144
+#define BTN_TOOL_FINGER		0x145
+#define BTN_TOOL_MOUSE		0x146
+#define BTN_TOOL_LENS		0x147
+#define BTN_TOUCH		0x14a
+#define BTN_STYLUS		0x14b
+#define BTN_STYLUS2		0x14c
+
+#define KEY_MAX			0x1ff
+
+/*
+ * Relative axes
+ */
+
+#define REL_X			0x00
+#define REL_Y			0x01
+#define REL_Z			0x02
+#define REL_HWHEEL		0x06
+#define REL_DIAL		0x07
+#define REL_WHEEL		0x08
+#define REL_MISC		0x09
+#define REL_MAX			0x0f
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X			0x00
+#define ABS_Y			0x01
+#define ABS_Z			0x02
+#define ABS_RX			0x03
+#define ABS_RY			0x04
+#define ABS_RZ			0x05
+#define ABS_THROTTLE		0x06
+#define ABS_RUDDER		0x07
+#define ABS_TL			0x08
+#define ABS_TR			0x09
+#define ABS_HAT0X		0x10
+#define ABS_HAT0Y		0x11
+#define ABS_HAT1X		0x12
+#define ABS_HAT1Y		0x13
+#define ABS_HAT2X		0x14
+#define ABS_HAT2Y		0x15
+#define ABS_HAT3X		0x16
+#define ABS_HAT3Y		0x17
+#define ABS_PRESSURE		0x18
+#define ABS_DISTANCE		0x19
+#define ABS_TILT_X		0x1a
+#define ABS_TILT_Y		0x1b
+#define ABS_MISC		0x1c
+#define ABS_MAX			0x1f
+
+/*
+ * LEDs
+ */
+
+#define LED_NUML		0x00
+#define LED_CAPSL		0x01
+#define LED_SCROLLL		0x02
+#define LED_COMPOSE		0x03
+#define LED_KANA		0x04
+#define LED_MAX			0x0f
+
+/*
+ * Autorepeat values
+ */
+
+#define REP_DELAY		0x00
+#define REP_PERIOD		0x01
+#define REP_MAX			0x01
+
+/*
+ * Sounds
+ */
+
+#define SND_CLICK		0x00
+#define SND_BELL		0x01
+#define SND_MAX			0x07
+
+/*
+ * IDs.
+ */
+
+#define ID_BUS			0
+#define ID_VENDOR		1
+#define ID_PRODUCT		2
+#define ID_VERSION		3
+
+#define BUS_PCI			0x01
+#define BUS_ISAPNP		0x02
+#define BUS_USB			0x03
+
+#define BUS_ISA			0x10
+#define BUS_I8042		0x11
+#define BUS_XTKBD		0x12
+#define BUS_RS232		0x13
+#define BUS_GAMEPORT		0x14
+#define BUS_PARPORT		0x15
+#define BUS_AMIGA		0x16
+
+#ifdef __KERNEL__
+
+/*
+ * In-kernel definitions.
+ */
+
+#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
+
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define BIT(x)	(1UL<<((x)%BITS_PER_LONG))
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+struct input_dev {
+
+	void *private;
+
+	int number;
+	char *name;
+	unsigned short idbus;
+	unsigned short idvendor;
+	unsigned short idproduct;
+	unsigned short idversion;
+
+	unsigned long evbit[NBITS(EV_MAX)];
+	unsigned long keybit[NBITS(KEY_MAX)];
+	unsigned long relbit[NBITS(REL_MAX)];
+	unsigned long absbit[NBITS(ABS_MAX)];
+	unsigned long ledbit[NBITS(LED_MAX)];
+	unsigned long sndbit[NBITS(SND_MAX)];
+
+	unsigned int keycodemax;
+	unsigned int keycodesize;
+	void *keycode;
+
+	unsigned int repeat_key;
+	struct timer_list timer;
+
+	int abs[ABS_MAX + 1];
+	int rep[REP_MAX + 1];
+
+	unsigned long key[NBITS(KEY_MAX)];
+	unsigned long led[NBITS(LED_MAX)];
+	unsigned long snd[NBITS(SND_MAX)];
+	
+	int absmax[ABS_MAX + 1];
+	int absmin[ABS_MAX + 1];
+	int absfuzz[ABS_MAX + 1];
+	int absflat[ABS_MAX + 1];
+
+	int (*open)(struct input_dev *dev);
+	void (*close)(struct input_dev *dev);
+	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+	struct input_handle *handle;
+	struct input_dev *next;
+};
+
+struct input_handler {
+
+	void *private;
+
+	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+	struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev);
+	void (*disconnect)(struct input_handle *handle);
+
+	struct file_operations *fops;
+	int minor;
+
+	struct input_handle *handle;
+	struct input_handler *next;
+};
+
+struct input_handle {
+
+	void *private;
+
+	int open;
+	
+	struct input_dev *dev;
+	struct input_handler *handler;
+
+	struct input_handle *dnext;
+	struct input_handle *hnext;
+};
+
+void input_register_device(struct input_dev *);
+void input_unregister_device(struct input_dev *);
+
+void input_register_handler(struct input_handler *);
+void input_unregister_handler(struct input_handler *);
+
+int input_open_device(struct input_handle *);
+void input_close_device(struct input_handle *);
+
+devfs_handle_t input_register_minor(char *name, int minor, int minor_base);
+void input_unregister_minor(devfs_handle_t handle);
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c))
+#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c)
+#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c)
+
+#endif
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdn.h linux.18p17/include/linux/isdn.h
--- linux.vanilla/include/linux/isdn.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/isdn.h	Thu Oct 19 01:52:22 2000
@@ -1,5 +1,5 @@
-/* $Id: isdn.h,v 1.95 2000/03/04 16:20:42 detabc Exp $
- *
+/* $Id: isdn.h,v 1.107 2000/09/10 20:29:18 detabc Exp $
+
  * Main header for the Linux ISDN subsystem (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
@@ -19,338 +19,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
- *
- * $Log: isdn.h,v $
- * Revision 1.95  2000/03/04 16:20:42  detabc
- * copy frames before rewriting frame's saddr
- *
- * Revision 1.94  2000/02/26 00:29:40  keil
- * more softnet changes
- *
- * Revision 1.93  2000/02/25 11:29:17  paul
- * changed chargetime to ulong from int (after about 20 days the "chargetime of
- * ipppX is now 1234" message displays a negative number on alpha).
- *
- * Revision 1.92  2000/02/17 13:15:56  keil
- * fix backward compatibility for 2.2
- *
- * Revision 1.91  2000/02/16 14:56:27  paul
- * translated ISDN_MODEM_ANZREG to ISDN_MODEM_NUMREG for english speakers
- *
- * Revision 1.90  2000/02/06 21:50:00  detabc
- * add rewriting of socket's and frame's saddr for udp-ipv4 dynip-connections.
- * Include checksum-recompute of ip- and udp-header's.
- *
- * Revision 1.89  2000/02/05 22:11:33  detabc
- * Add rewriting of socket's and frame's saddr adressfield for
- * dynip-connections.  Only for tcp/ipv4 and switchable per interface.
- * Include checksum-recompute of ip- and tcp-header's.
- *
- * Revision 1.88  2000/01/20 19:59:43  keil
- * Add FAX Class 1 support
- *
- * Revision 1.87  2000/01/09 20:43:15  detabc
- * exand logical bind-group's for both call's (in and out).
- * add first part of kernel-config-help for abc-extension.
- *
- * Revision 1.86  1999/12/05 16:06:09  detabc
- * add resethandling for rawip-compression.
- * at now all B2-Protocols are usable with rawip-compression
- *
- * Revision 1.85  1999/11/30 11:29:06  detabc
- * add a on the fly frame-counter and limit
- *
- * Revision 1.84  1999/11/28 14:49:08  detabc
- * In case of rawip-compress adjust dev[x]->ibytes/obytes to reflect the
- * uncompressed size.
- *
- * Revision 1.83  1999/11/26 15:54:59  detabc
- * added compression (isdn_bsdcompress) for rawip interfaces with x75i B2-protocol.
- *
- * Revision 1.82  1999/11/20 22:14:14  detabc
- * added channel dial-skip in case of external use
- * (isdn phone or another isdn device) on the same NTBA.
- * usefull with two or more card's connected the different NTBA's.
- * global switchable in kernel-config and also per netinterface.
- *
- * add auto disable of netinterface's in case of:
- * 	to many connection's in short time.
- * 	config mistakes (wrong encapsulation, B2-protokoll or so on) on local
- * 	or remote side.
- * 	wrong password's or something else to a ISP (syncppp).
- *
- * possible encapsulations for this future are:
- * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP,
- * and ISDN_NET_ENCAP_CISCOHDLCK.
- *
- * Revision 1.81  1999/10/27 21:21:18  detabc
- * Added support for building logically-bind-group's per interface.
- * usefull for outgoing call's with more then one isdn-card.
- *
- * Switchable support to dont reset the hangup-timeout for
- * receive frames. Most part's of the timru-rules for receiving frames
- * are now obsolete. If the input- or forwarding-firewall deny
- * the frame, the line will be not hold open.
- *
- * Revision 1.80  1999/10/26 21:09:29  armin
- * New bufferlen for phonenumber only with kernel 2.3.x
- *
- * Revision 1.79  1999/10/16 17:52:38  keil
- * Changing the MSN length need new data versions
- *
- * Revision 1.78  1999/10/08 18:59:33  armin
- * Bugfix of too small MSN buffer and checking phone number
- * in isdn_tty_getdial()
- *
- * Revision 1.77  1999/09/23 22:22:42  detabc
- * added tcp-keepalive-detect with local response (ipv4 only)
- * added host-only-interface support
- * (source ipaddr == interface ipaddr) (ipv4 only)
- * ok with kernel 2.3.18 and 2.2.12
- *
- * Revision 1.76  1999/09/14 10:16:21  keil
- * change ABC include
- *
- * Revision 1.75  1999/09/13 23:25:17  he
- * serialized xmitting frames from isdn_ppp and BSENT statcallb
- *
- * Revision 1.74  1999/09/12 16:19:39  detabc
- * added abc features
- * low cost routing for net-interfaces (only the HL side).
- * need more implementation in the isdnlog-utility
- * udp info support (first part).
- * different EAZ on outgoing call's.
- * more checks on D-Channel callbacks (double use of channels).
- * tested and running with kernel 2.3.17
- *
- * Revision 1.73  1999/09/06 07:29:36  fritz
- * Changed my mail-address.
- *
- * Revision 1.72  1999/09/04 22:20:19  detabc
- *
- * Revision 1.71  1999/08/23 15:54:22  keil
- * more backported changes from kernel 2.3.14
- *
- * Revision 1.70  1999/07/31 12:59:58  armin
- * Added tty fax capabilities.
- *
- * Revision 1.69  1999/07/13 20:47:53  werner
- * added channel bit ISDN_USAGE_DISABLED for limiting b-channel access.
- *
- * Revision 1.68  1999/07/11 17:07:37  armin
- * Added tty modem register S23.
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- *
- * Revision 1.67  1999/07/07 10:17:24  detabc
- * remove unused messages
- *
- * Revision 1.66  1999/07/01 08:35:37  keil
- * compatibility to 2.3
- *
- * Revision 1.65  1999/06/10 11:51:27  paul
- * fixed comment for NET_DV
- *
- * Revision 1.64  1999/04/18 14:57:14  fritz
- * Removed TIMRU stuff
- *
- * Revision 1.63  1999/04/18 14:07:18  fritz
- * Removed TIMRU stuff.
- *
- * Revision 1.62  1999/04/12 13:16:54  fritz
- * Changes from 2.0 tree.
- *
- * Revision 1.61  1999/03/02 11:43:21  armin
- * Added variable to store connect-message of Modem.
- * Added Timer-define for RegS7 (Wait for Carrier).
- *
- * Revision 1.60  1998/10/25 14:50:29  fritz
- * Backported from MIPS (Cobalt).
- *
- * Revision 1.59  1998/10/23 10:18:55  paul
- * Implementation of "dialmode" (successor of "status")
- * You also need current isdnctrl for this!
- *
- * Revision 1.58  1998/10/23 10:10:06  fritz
- * Test-Checkin
- *
- * Revision 1.57  1998/08/31 21:10:01  he
- * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
- *     peer phone number)
- *
- * Revision 1.56  1998/07/26 18:46:52  armin
- * Added silence detection in voice receive mode.
- *
- * Revision 1.55  1998/06/26 15:13:17  fritz
- * Added handling of STAT_ICALL with incomplete CPN.
- * Added AT&L for ttyI emulator.
- * Added more locking stuff in tty_write.
- *
- * Revision 1.54  1998/06/18 23:32:01  fritz
- * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
- * Removed direct-senddown feature in isdn_tty_write because it will
- * never succeed with locking and is useless anyway.
- *
- * Revision 1.53  1998/06/17 19:51:51  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- * Revision 1.46  1998/04/14 16:28:59  he
- * Fixed user space access with interrupts off and remaining
- * copy_{to,from}_user() -> -EFAULT return codes
- *
- * Revision 1.45  1998/03/24 16:33:12  hipp
- * More CCP changes. BSD compression now "works" on a local loopback link.
- * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
- *
- * Revision 1.44  1998/03/22 18:50:56  hipp
- * Added BSD Compression for syncPPP .. UNTESTED at the moment
- *
- * Revision 1.43  1998/03/09 17:46:44  he
- * merged in 2.1.89 changes
- *
- *
- * Revision 1.40  1998/03/08 01:08:29  fritz
- * Increased NET_DV because of TIMRU
- *
- * Revision 1.39  1998/03/07 22:42:49  fritz
- * Starting generic module support (Nothing usable yet).
- *
- * Revision 1.38  1998/03/07 18:21:29  cal
- * Dynamic Timeout-Rule-Handling vs. 971110 included
- *
- * Revision 1.37  1998/02/22 19:45:24  fritz
- * Some changes regarding V.110
- *
- * Revision 1.36  1998/02/20 17:35:55  fritz
- * Added V.110 stuff.
- *
- * Revision 1.35  1998/01/31 22:14:14  keil
- * changes for 2.1.82
- *
- * Revision 1.34  1997/10/09 21:28:11  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.33  1997/08/21 14:44:22  fritz
- * Moved triggercps to end of struct for backwards-compatibility.
- *
- * Revision 1.32  1997/08/21 09:49:46  fritz
- * Increased NET_DV
- *
- * Revision 1.31  1997/06/22 11:57:07  fritz
- * Added ability to adjust slave triggerlevel.
- *
- * Revision 1.30  1997/06/17 13:07:23  hipp
- * compression changes , MP changes
- *
- * Revision 1.29  1997/05/27 15:18:02  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * Revision 1.28  1997/03/07 01:33:01  fritz
- * Added proper ifdef's for CONFIG_ISDN_AUDIO
- *
- * Revision 1.27  1997/03/05 21:11:49  fritz
- * Minor fixes.
- *
- * Revision 1.26  1997/02/28 02:37:53  fritz
- * Added some comments.
- *
- * Revision 1.25  1997/02/23 16:54:23  hipp
- * some initial changes for future PPP compresion
- *
- * Revision 1.24  1997/02/18 09:42:45  fritz
- * Bugfix: Increased ISDN_MODEM_ANZREG.
- * Increased TTY_DV.
- *
- * Revision 1.23  1997/02/10 22:07:13  fritz
- * Added 2 modem registers for numbering plan and screening info.
- *
- * Revision 1.22  1997/02/03 23:42:08  fritz
- * Added ISDN_TIMER_RINGING
- * Misc. changes for Kernel 2.1.X compatibility
- *
- * Revision 1.21  1997/01/17 01:19:10  fritz
- * Applied chargeint patch.
- *
- * Revision 1.20  1997/01/17 00:41:19  fritz
- * Increased TTY_DV.
- *
- * Revision 1.19  1997/01/14 01:41:07  fritz
- * Added ATI2 related variables.
- * Added variables for audio support in skbuffs.
- *
- * Revision 1.18  1996/11/06 17:37:50  keil
- * more changes for 2.1.X
- *
- * Revision 1.17  1996/09/07 12:53:57  hipp
- * moved a few isdn_ppp.c specific defines to drives/isdn/isdn_ppp.h
- *
- * Revision 1.16  1996/08/12 16:20:56  hipp
- * renamed ppp_minor to ppp_slot
- *
- * Revision 1.15  1996/06/15 14:56:57  fritz
- * Added version signatures for data structures used
- * by userlevel programs.
- *
- * Revision 1.14  1996/06/06 21:24:23  fritz
- * Started adding support for suspend/resume.
- *
- * Revision 1.13  1996/06/05 02:18:20  fritz
- * Added DTMF decoding stuff.
- *
- * Revision 1.12  1996/06/03 19:55:08  fritz
- * Fixed typos.
- *
- * Revision 1.11  1996/05/31 01:37:47  fritz
- * Minor changes, due to changes in isdn_tty.c
- *
- * Revision 1.10  1996/05/18 01:37:18  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.9  1996/05/17 03:58:20  fritz
- * Added flags for DLE handling.
- *
- * Revision 1.8  1996/05/11 21:49:55  fritz
- * Removed queue management variables.
- * Changed queue management to use sk_buffs.
- *
- * Revision 1.7  1996/05/07 09:10:06  fritz
- * Reorganized tty-related structs.
- *
- * Revision 1.6  1996/05/06 11:38:27  hipp
- * minor change in ippp struct
- *
- * Revision 1.5  1996/04/30 11:03:16  fritz
- * Added Michael's ippp-bind patch.
- *
- * Revision 1.4  1996/04/29 23:00:02  fritz
- * Added variables for voice-support.
- *
- * Revision 1.3  1996/04/20 16:54:58  fritz
- * Increased maximum number of channels.
- * Added some flags for isdn_net to handle callback more reliable.
- * Fixed delay-definitions to be more accurate.
- * Misc. typos
- *
- * Revision 1.2  1996/02/11 02:10:02  fritz
- * Changed IOCTL-names
- * Added rx_netdev, st_netdev, first_skb, org_hcb, and org_hcu to
- * Netdevice-local struct.
- *
- * Revision 1.1  1996/01/10 20:55:07  fritz
- * Initial revision
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdn_divertif.h linux.18p17/include/linux/isdn_divertif.h
--- linux.vanilla/include/linux/isdn_divertif.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/isdn_divertif.h	Sat Sep 23 13:15:36 2000
@@ -1,6 +1,5 @@
-/* 
- * $Id: isdn_divertif.h,v 1.3 1999/07/05 20:22:00 werner Exp $
- *
+/* $Id: isdn_divertif.h,v 1.4 2000/05/11 22:29:22 kai Exp $
+
  * Header for the diversion supplementary interface for i4l.
  *
  * Copyright 1998       by Werner Cornelius (werner@isdn4linux.de)
@@ -18,15 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
- *
- * $Log: isdn_divertif.h,v $
- * Revision 1.3  1999/07/05 20:22:00  werner
- * changes to use diversion sources for all kernel versions.
- * removed static device, only proc filesystem used
- *
- * Revision 1.2  1999/07/04 21:38:38  werner
- * ported from kernel version 2.0
- *
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isdnif.h linux.18p17/include/linux/isdnif.h
--- linux.vanilla/include/linux/isdnif.h	Fri Apr 21 23:22:17 2000
+++ linux.18p17/include/linux/isdnif.h	Sun Oct 15 23:43:30 2000
@@ -1,5 +1,5 @@
-/* $Id: isdnif.h,v 1.33 2000/01/20 19:59:43 keil Exp $
- *
+/* $Id: isdnif.h,v 1.35 2000/06/16 13:19:38 keil Exp $
+
  * Linux ISDN subsystem
  *
  * Definition of the interface between the subsystem and its low-level drivers.
@@ -20,126 +20,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
- *
- * $Log: isdnif.h,v $
- * Revision 1.33  2000/01/20 19:59:43  keil
- * Add FAX Class 1 support
- *
- * Revision 1.32  1999/10/11 22:03:00  keil
- * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
- *
- * Revision 1.31  1999/09/06 07:29:36  fritz
- * Changed my mail-address.
- *
- * Revision 1.30  1999/08/23 15:54:29  keil
- * more backported changes from kernel 2.3.14
- *
- * Revision 1.29  1999/07/31 13:00:02  armin
- * Added tty fax capabilities.
- *
- * Revision 1.28  1999/07/13 20:57:48  werner
- * added callback ISDN_STAT_DISCH for limiting b-channel resources.
- *
- * Revision 1.27  1999/07/11 17:07:39  armin
- * Added tty modem register S23.
- * Added new layer 2 and 3 protocols for Fax and DSP functions.
- *
- * Revision 1.26  1999/07/01 08:35:44  keil
- * compatibility to 2.3
- *
- * Revision 1.25  1998/06/17 19:51:55  he
- * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
- * brute force fix to avoid Ugh's in isdn_tty_write()
- * cleaned up some dead code
- *
- * Revision 1.24  1998/03/19 13:18:57  keil
- * Start of a CAPI like interface for supplementary Service
- * first service: SUSPEND
- *
- * Revision 1.23  1998/02/20 17:36:52  fritz
- * Added L2-protocols for V.110, changed FEATURE-Flag-constants.
- *
- * Revision 1.22  1998/01/31 22:14:12  keil
- * changes for 2.1.82
- *
- * Revision 1.21  1997/10/09 21:28:13  fritz
- * New HL<->LL interface:
- *   New BSENT callback with nr. of bytes included.
- *   Sending without ACK.
- *   New L1 error status (not yet in use).
- *   Cleaned up obsolete structures.
- * Implemented Cisco-SLARP.
- * Changed local net-interface data to be dynamically allocated.
- * Removed old 2.0 compatibility stuff.
- *
- * Revision 1.20  1997/05/27 15:18:06  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
- *
- * Revision 1.19  1997/03/25 23:13:56  keil
- * NI-1 US protocol
- *
- * Revision 1.18  1997/03/04 22:09:18  calle
- * Change macros copy_from_user and copy_to_user in inline function.
- * These are now correct replacements of the functions for 2.1.xx
- *
- * Revision 1.17  1997/02/10 21:12:53  fritz
- * More setup-interface changes.
- *
- * Revision 1.16  1997/02/10 19:42:57  fritz
- * New interface for reporting incoming calls.
- *
- * Revision 1.15  1997/02/09 00:18:42  keil
- * leased line support
- *
- * Revision 1.14  1997/02/03 23:43:00  fritz
- * Misc changes for Kernel 2.1.X compatibility.
- *
- * Revision 1.13  1996/11/13 02:39:59  fritz
- * More compatibility changes.
- *
- * Revision 1.12  1996/11/06 17:38:48  keil
- * more changes for 2.1.X
- *
- * Revision 1.11  1996/10/23 11:59:42  fritz
- * More compatibility changes.
- *
- * Revision 1.10  1996/10/22 23:14:19  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.9  1996/06/06 21:24:24  fritz
- * Started adding support for suspend/resume.
- *
- * Revision 1.8  1996/05/18 01:45:37  fritz
- * More spelling corrections.
- *
- * Revision 1.7  1996/05/18 01:37:19  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.6  1996/05/17 03:59:28  fritz
- * Marked rcvcallb and writebuf obsolete.
- *
- * Revision 1.5  1996/05/01 11:43:54  fritz
- * Removed STANDALONE
- *
- * Revision 1.4  1996/05/01 11:38:40  fritz
- * Added ISDN_FEATURE_L2_TRANS
- *
- * Revision 1.3  1996/04/29 22:57:54  fritz
- * Added driverId and channel parameters to
- * writecmd() and readstat().
- * Added constant for voice-support.
- *
- * Revision 1.2  1996/04/20 17:02:40  fritz
- * Changes to support skbuffs for Lowlevel-Drivers.
- * Misc. typos
- *
- * Revision 1.1  1996/01/09 05:50:51  fritz
- * Initial revision
  *
  */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/isicom.h linux.18p17/include/linux/isicom.h
--- linux.vanilla/include/linux/isicom.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/isicom.h	Wed Jun 21 06:42:02 2000
@@ -55,7 +55,6 @@
  *      PCI definitions
  */
 
- #define        DEVID_COUNT     9
  #define        VENDOR_ID       0x10b5
 
 /*
@@ -137,6 +136,8 @@
 	struct isi_port		* ports;
 	signed char		count;
 	unsigned char		isa;
+	spinlock_t		card_lock; /* Card wide lock 11/5/00 -sameer */
+	unsigned long		flags;
 };
 
 struct	isi_port {
@@ -164,6 +165,70 @@
 	struct termios		callout_termios;
 };
 
+/*
+ *	Locking/UnLocking primitives...
+ */
+
+extern inline unsigned char lock_card(struct isi_board *card)
+{
+	char		retries;
+	unsigned short base = card->base;
+
+	for (retries = 0; retries < 100; retries++) {
+		spin_lock_irqsave(&card->card_lock, card->flags);
+
+		if (inw(base + 0xe) & 0x1) {
+#ifdef ISICOM_DEBUG
+/*			printk(KERN_DEBUG "ISICOM:lock_card(0x%x)\n", 
+				card->base);*/
+#endif
+			return 1; 
+		}
+		else {
+			spin_unlock_irqrestore(&card->card_lock, card->flags);
+			schedule_timeout(HZ/100); /* 10ms */
+		}
+	}
+#ifdef ISICOM_DEBUG
+	printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%x)\n", card->base);
+#endif /* ISICOM_DEBUG */
+	return 0;	/* Failed to aquire the card! */
+}
+
+extern inline unsigned char lock_card_at_interrupt(struct isi_board *card)
+{
+	unsigned char		retries;
+	unsigned short 		base = card->base;
+
+	for (retries = 0; retries < 200; retries++) {
+		spin_lock_irqsave(&card->card_lock, card->flags);
+
+		if (inw(base + 0xe) & 0x1) {
+#ifdef ISICOM_DEBUG
+/*			printk(KERN_DEBUG "ISICOM:lock_card_at_interrupt(0x%x)"
+				"\n", 
+				card->base);*/
+#endif
+			return 1; 
+		}
+		else {
+			spin_unlock_irqrestore(&card->card_lock, card->flags);
+		}
+	}
+#ifdef ISICOM_DEBUG
+	printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%x)\n", card->base);
+#endif /* ISICOM_DEBUG */
+	return 0;	/* Failed to aquire the card! */
+}
+
+extern inline void unlock_card(struct isi_board *card)
+{
+	spin_unlock_irqrestore(&card->card_lock, card->flags);
+#ifdef ISICOM_DEBUG
+/*			printk(KERN_DEBUG "ISICOM:unlock_card(0x%x)\n", 
+				card->base);*/
+#endif
+}
 
 /*
  *  ISI Card specific ops ...
@@ -174,12 +239,18 @@
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: raise_dtr.\n");
 #endif	
@@ -187,6 +258,7 @@
 	outw(0x0504, base);
 	InterruptTheCard(base);
 	port->status |= ISI_DTR;
+	unlock_card(card);
 }
 
 extern inline void drop_dtr(struct isi_port * port)
@@ -194,12 +266,18 @@
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: drop_dtr.\n");
 #endif	
@@ -207,18 +285,25 @@
 	outw(0x0404, base);
 	InterruptTheCard(base);	
 	port->status &= ~ISI_DTR;
+	unlock_card(card);
 }
 extern inline void raise_rts(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: raise_rts.\n");
 #endif	
@@ -226,18 +311,25 @@
 	outw(0x0a04, base);
 	InterruptTheCard(base);	
 	port->status |= ISI_RTS;
+	unlock_card(card);
 }
 extern inline void drop_rts(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: drop_rts.\n");
 #endif	
@@ -245,18 +337,25 @@
 	outw(0x0804, base);
 	InterruptTheCard(base);	
 	port->status &= ~ISI_RTS;
+	unlock_card(card);
 }
 extern inline void raise_dtr_rts(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: raise_dtr_rts.\n");
 #endif	
@@ -264,18 +363,25 @@
 	outw(0x0f04, base);
 	InterruptTheCard(base);
 	port->status |= (ISI_DTR | ISI_RTS);
+	unlock_card(card);
 }
 extern inline void drop_dtr_rts(struct isi_port * port)
 {
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG_DTR_RTS	
 	printk(KERN_DEBUG "ISICOM: drop_dtr_rts.\n");
 #endif	
@@ -283,6 +389,7 @@
 	outw(0x0c04, base);
 	InterruptTheCard(base);	
 	port->status &= ~(ISI_RTS | ISI_DTR);
+	unlock_card(card);
 }
 
 extern inline void kill_queue(struct isi_port * port, short queue)
@@ -290,18 +397,45 @@
 	struct isi_board * card = port->card;
 	unsigned short base = card->base;
 	unsigned char channel = port->channel;
-	short wait=400;
+
+/*	short wait=400;		SMPCHG
 	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
 	if (wait <= 0) {
 		printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n");
 		return;
 	}
+*/
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n");
+		return;
+	}
 #ifdef ISICOM_DEBUG	
 	printk(KERN_DEBUG "ISICOM: kill_queue 0x%x.\n", queue);
 #endif	
 	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
 	outw((queue << 8) | 0x06, base);
 	InterruptTheCard(base);	
+	unlock_card(card);
+}
+
+extern inline void set_signal_mask(struct isi_port * port, char mask)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+
+	if (!lock_card(card)) {
+		printk(KERN_WARNING "ISICOM: Card found busy"
+					"in set_signal_mask.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: set_signal_mask 0x%x.\n", mask);
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+	outw((mask << 8) | 0x7, base);
+	InterruptTheCard(base);
+	unlock_card(card);
 }
 
 #endif	/*	__KERNEL__	*/
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/joystick.h linux.18p17/include/linux/joystick.h
--- linux.vanilla/include/linux/joystick.h	Wed May  3 21:21:40 2000
+++ linux.18p17/include/linux/joystick.h	Thu Oct 19 01:37:34 2000
@@ -134,18 +134,14 @@
 #error "You need to use at least v2.2 Linux kernel."
 #endif
 
+#include <linux/spinlock.h>
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-#include <asm/spinlock.h>
-typedef struct wait_queue *wait_queue_head_t;
-#define __setup(a,b)
+#define __exit
 #define BASE_ADDRESS(x,i)	((x)->base_address[i])
-#define DECLARE_WAITQUEUE(x,y)	struct wait_queue x = { y, NULL }
-#define init_waitqueue_head(x)	do { *(x) = NULL; } while (0)
-#define __set_current_state(x)	current->state = x
-#define SETUP_PARAM		char *str, int *ints
-#define SETUP_PARSE(x)		do {} while (0)
+#define SETUP_PARAM		char *str
+#define SETUP_PARSE(x)		int ints[x]; get_options(str, ints)
 #else
-#include <linux/spinlock.h>
 #define BASE_ADDRESS(x,i)	((x)->resource[i].start)
 #define SETUP_PARAM		char *str
 #define SETUP_PARSE(x)		int ints[x]; get_options(str, x, ints)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/kbd_kern.h linux.18p17/include/linux/kbd_kern.h
--- linux.vanilla/include/linux/kbd_kern.h	Fri Apr 21 23:10:44 2000
+++ linux.18p17/include/linux/kbd_kern.h	Mon Sep 25 16:12:56 2000
@@ -69,6 +69,8 @@
 
 extern int do_poke_blanked_console;
 
+extern void (*kbd_ledfunc)(unsigned int led);
+
 extern inline void show_console(void)
 {
 	do_poke_blanked_console = 1;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/kcomp.h linux.18p17/include/linux/kcomp.h
--- linux.vanilla/include/linux/kcomp.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/kcomp.h	Thu Oct 19 01:41:34 2000
@@ -0,0 +1,61 @@
+/*
+ * Kernel compatibility glue to allow USB compile on 2.2.x kernels
+ */
+
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/pagemap.h>
+
+#define __exit
+
+#ifdef __alpha
+extern long __kernel_thread (unsigned long, int (*)(void *), void *);
+static inline long kernel_thread (int (*fn) (void *), void *arg, unsigned long flags)
+{
+	return __kernel_thread (flags | CLONE_VM, fn, arg);
+}
+#undef CONFIG_APM
+#endif
+
+
+#define pci_enable_device(x)			0
+
+#define page_address(x)				(x | PAGE_OFFSET)
+
+#define	TTY_DRIVER_NO_DEVFS			0
+
+#define	net_device			device
+#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 netif_start_queue(dev)		do { dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; } while (0)
+#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
+
+#define IORESOURCE_IO			1
+#define pci_resource_start(dev, i)	(dev->base_address[i] & ~IORESOURCE_IO)
+#define pci_resource_flags(dev, i)	(dev->base_address[i] & IORESOURCE_IO)
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/kernel.h linux.18p17/include/linux/kernel.h
--- linux.vanilla/include/linux/kernel.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/kernel.h	Mon Sep 25 16:07:57 2000
@@ -48,6 +48,7 @@
 	ATTRIB_NORET;
 extern unsigned long simple_strtoul(const char *,char **,unsigned int);
 extern long simple_strtol(const char *,char **,unsigned int);
+extern char *get_options(char *str, int *ints);
 extern int sprintf(char * buf, const char * fmt, ...);
 extern int vsprintf(char *buf, const char *, va_list);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/kmod.h linux.18p17/include/linux/kmod.h
--- linux.vanilla/include/linux/kmod.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/linux/kmod.h	Sun Oct 15 23:29:17 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/list.h linux.18p17/include/linux/list.h
--- linux.vanilla/include/linux/list.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/list.h	Fri Sep  8 17:03:06 2000
@@ -17,6 +17,8 @@
 	struct list_head *next, *prev;
 };
 
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
 #define LIST_HEAD(name) \
 	struct list_head name = { &name, &name }
 
@@ -49,6 +51,14 @@
 }
 
 /*
+ * Insert a new entry at the tail
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
  * Delete a list entry by making the prev/next entries
  * point to each other.
  *
@@ -93,6 +103,9 @@
 
 #define list_entry(ptr, type, member) \
 	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each(pos, head) \
+        for (pos = (head)->next; pos != (head); pos = pos->next)
 
 #endif /* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/lockd/debug.h linux.18p17/include/linux/lockd/debug.h
--- linux.vanilla/include/linux/lockd/debug.h	Fri Apr 21 23:24:09 2000
+++ linux.18p17/include/linux/lockd/debug.h	Sun Oct 15 21:15:18 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/lockd/lockd.h linux.18p17/include/linux/lockd/lockd.h
--- linux.vanilla/include/linux/lockd/lockd.h	Fri Apr 21 23:24:09 2000
+++ linux.18p17/include/linux/lockd/lockd.h	Sun Oct 15 23:45:20 2000
@@ -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;
 
@@ -138,12 +140,14 @@
 					struct sockaddr_in *, int, int);
 struct rpc_clnt * nlm_bind_host(struct nlm_host *);
 void		  nlm_rebind_host(struct nlm_host *);
+struct nlm_host * nlm_get_host(struct nlm_host *);
 void		  nlm_release_host(struct nlm_host *);
 void		  nlm_shutdown_hosts(void);
 
 /*
  * Server-side lock handling
  */
+int		  nlmsvc_async_call(struct nlm_rqst *, u32, rpc_action);
 u32		  nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
 					struct nlm_lock *, int, struct nlm_cookie *);
 u32		  nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/lockd/nlm.h linux.18p17/include/linux/lockd/nlm.h
--- linux.vanilla/include/linux/lockd/nlm.h	Fri Apr 21 12:46:59 2000
+++ linux.18p17/include/linux/lockd/nlm.h	Fri Sep 15 22:10:44 2000
@@ -10,19 +10,21 @@
 #define LINUX_LOCKD_NLM_H
 
 /* Maximum file offset in file_lock.fl_end */
-#ifdef OFFSET_MAX
-# define NLM_OFFSET_MAX		OFFSET_MAX
-#else
 # define NLM_OFFSET_MAX		((off_t) 0x7fffffff)
-#endif
+# define NLM4_OFFSET_MAX	((s64) ((~(u64)0) >> 1))
 
 /* Return states for NLM */
 enum {
-	NLM_LCK_GRANTED = 0,
-	NLM_LCK_DENIED,
-	NLM_LCK_DENIED_NOLOCKS,
-	NLM_LCK_BLOCKED,
-	NLM_LCK_DENIED_GRACE_PERIOD,
+	NLM_LCK_GRANTED			= 0,
+	NLM_LCK_DENIED			= 1,
+	NLM_LCK_DENIED_NOLOCKS		= 2,
+	NLM_LCK_BLOCKED			= 3,
+	NLM_LCK_DENIED_GRACE_PERIOD	= 4,
+	NLM_DEADLCK			= 5,
+	NLM_ROFS			= 6,
+	NLM_STALE_FH			= 7,
+	NLM_FBIG			= 8,
+	NLM_FAILED			= 9,
 };
 
 #define NLM_PROGRAM		100021
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/lockd/xdr.h linux.18p17/include/linux/lockd/xdr.h
--- linux.vanilla/include/linux/lockd/xdr.h	Fri Apr 21 23:11:46 2000
+++ linux.18p17/include/linux/lockd/xdr.h	Sun Oct 15 23:31:07 2000
@@ -74,6 +74,7 @@
 #define NLMSVC_XDRSIZE		sizeof(struct nlm_args)
 
 void	nlmxdr_init(void);
+void	nlmxdr_shutdown(void);
 int	nlmsvc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
 int	nlmsvc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *);
 int	nlmsvc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/lockd/xdr4.h linux.18p17/include/linux/lockd/xdr4.h
--- linux.vanilla/include/linux/lockd/xdr4.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/lockd/xdr4.h	Sun Oct 15 23:45:20 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/m68kserial.h linux.18p17/include/linux/m68kserial.h
--- linux.vanilla/include/linux/m68kserial.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/m68kserial.h	Sat Oct 14 00:14:39 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/major.h linux.18p17/include/linux/major.h
--- linux.vanilla/include/linux/major.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/major.h	Mon Sep 11 18:14:04 2000
@@ -109,6 +109,15 @@
 #define SPECIALIX_NORMAL_MAJOR 75
 #define SPECIALIX_CALLOUT_MAJOR 76
 
+#define COMPAQ_CISS_MAJOR 	104
+#define COMPAQ_CISS_MAJOR1	105
+#define COMPAQ_CISS_MAJOR2      106
+#define COMPAQ_CISS_MAJOR3      107
+#define COMPAQ_CISS_MAJOR4      108
+#define COMPAQ_CISS_MAJOR5      109
+#define COMPAQ_CISS_MAJOR6      110
+#define COMPAQ_CISS_MAJOR7      111
+
 #define DASD_MAJOR      94	/* Official assignations from Peter */
 
 #define LVM_CHAR_MAJOR	109	/* Logical Volume Manager */
@@ -120,6 +129,9 @@
 #define UNIX98_PTY_MASTER_MAJOR	128
 #define UNIX98_PTY_MAJOR_COUNT	8
 #define UNIX98_PTY_SLAVE_MAJOR	(UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT)
+
+#define MSR_MAJOR 202
+#define CPUID_MAJOR 203
 
 /*
  * Tests for SCSI devices.
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/mc6821.h linux.18p17/include/linux/mc6821.h
--- linux.vanilla/include/linux/mc6821.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/mc6821.h	Sat Oct 14 00:14:39 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/mm.h linux.18p17/include/linux/mm.h
--- linux.vanilla/include/linux/mm.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/mm.h	Sun Oct 15 23:29:13 2000
@@ -47,7 +47,7 @@
 	struct vm_area_struct * vm_avl_left;
 	struct vm_area_struct * vm_avl_right;
 
-	/* For areas with inode, the list inode->i_mmap, for shm areas,
+	/* For areas with inode, the list inode->i_mmap{,_shared}, for shm areas,
 	 * the list of attaches, otherwise unused.
 	 */
 	struct vm_area_struct *vm_next_share;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/module.h linux.18p17/include/linux/module.h
--- linux.vanilla/include/linux/module.h	Wed May  3 21:21:21 2000
+++ linux.18p17/include/linux/module.h	Thu Oct 19 01:37:10 2000
@@ -142,8 +142,11 @@
 #define __MODULE_STRING(x)	__MODULE_STRING_1(x)
 
 /* Find a symbol exported by the kernel or another module */
+#ifndef CONFIG_MODULES
+static inline unsigned long get_module_symbol(char *A, char *B) { return 0; };
+#else
 extern unsigned long get_module_symbol(char *, char *);
-
+#endif
 #if defined(MODULE) && !defined(__GENKSYMS__)
 
 /* Embedded module documentation macros.  */
@@ -195,6 +198,7 @@
 /* Define the module variable, and usage macros.  */
 extern struct module __this_module;
 
+#define THIS_MODULE		(&__this_module)
 #define MOD_INC_USE_COUNT	__MOD_INC_USE_COUNT(&__this_module)
 #define MOD_DEC_USE_COUNT	__MOD_DEC_USE_COUNT(&__this_module)
 #define MOD_IN_USE		__MOD_IN_USE(&__this_module)
@@ -216,6 +220,7 @@
 #define MODULE_SUPPORTED_DEVICE(name)
 #define MODULE_PARM(var,type)
 #define MODULE_PARM_DESC(var,desc)
+#define THIS_MODULE		NULL
 
 #ifndef __GENKSYMS__
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/msdos_fs_sb.h linux.18p17/include/linux/msdos_fs_sb.h
--- linux.vanilla/include/linux/msdos_fs_sb.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/msdos_fs_sb.h	Thu Aug 31 14:33:07 2000
@@ -24,7 +24,9 @@
 		 posixfs:1,       /* Allow names like makefile and Makefile to coexist */
 		 numtail:1,       /* Does first alias have a numeric '~1' type tail? */
 		 atari:1,         /* Use Atari GEMDOS variation of MS-DOS fs */
-		 fat32:1;	  /* Is this a FAT32 partition? */
+		 fat32:1,	  /* Is this a FAT32 partition? */
+		 small_letter:1,  /* Use small letters for short name */
+		 force_long:1;	  /* Force create longname */
 };
 
 struct vfat_unicode {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/netdevice.h linux.18p17/include/linux/netdevice.h
--- linux.vanilla/include/linux/netdevice.h	Fri Apr 21 23:11:00 2000
+++ linux.18p17/include/linux/netdevice.h	Thu Oct 19 01:37:36 2000
@@ -30,6 +30,9 @@
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/if_packet.h>
+#ifdef __KERNEL__
+#include <net/divert.h>
+#endif
 
 #include <asm/atomic.h>
 
@@ -317,6 +320,11 @@
 	/* Semi-private data. Keep it at the end of device struct. */
 	struct dst_entry	*fastpath[NETDEV_FASTROUTE_HMASK+1];
 #endif
+
+#ifdef CONFIG_NET_DIVERT
+	/* this will get initialized at each interface type init routine */
+	struct divert_blk	*divert;
+#endif /* CONFIG_NET_DIVERT */
 };
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs.h linux.18p17/include/linux/nfs.h
--- linux.vanilla/include/linux/nfs.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/nfs.h	Fri Sep 15 22:10:44 2000
@@ -1,54 +1,60 @@
 /*
  * NFS protocol definitions
+ *
+ * This file contains constants mostly for Version 2 of the protocol,
+ * but also has a couple of NFSv3 bits in (notably the error codes).
  */
 #ifndef _LINUX_NFS_H
 #define _LINUX_NFS_H
 
 #include <linux/sunrpc/msg_prot.h>
 
-#define NFS_PORT	2049
-#define NFS_MAXDATA	8192
-#define NFS_MAXPATHLEN	1024
-#define NFS_MAXNAMLEN	255
-#define NFS_MAXGROUPS	16
-#define NFS_FHSIZE	32
-#define NFS_COOKIESIZE	4
-#define NFS_FIFO_DEV	(-1)
-#define NFSMODE_FMT	0170000
-#define NFSMODE_DIR	0040000
-#define NFSMODE_CHR	0020000
-#define NFSMODE_BLK	0060000
-#define NFSMODE_REG	0100000
-#define NFSMODE_LNK	0120000
-#define NFSMODE_SOCK	0140000
-#define NFSMODE_FIFO	0010000
+#define NFS_PROGRAM		100003
 
-	
+/*
+ * 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.
+ * Error codes that have a `--' in the v2 column are not part of the
+ * standard, but seem to be widely used nevertheless.
+ */
 enum nfs_stat {
-	NFS_OK = 0,
-	NFSERR_PERM = 1,
-	NFSERR_NOENT = 2,
-	NFSERR_IO = 5,
-	NFSERR_NXIO = 6,
-	NFSERR_EAGAIN = 11,
-	NFSERR_ACCES = 13,
-	NFSERR_EXIST = 17,
-	NFSERR_XDEV = 18,
-	NFSERR_NODEV = 19,
-	NFSERR_NOTDIR = 20,
-	NFSERR_ISDIR = 21,
-	NFSERR_INVAL = 22,	/* that Sun forgot */
-	NFSERR_FBIG = 27,
-	NFSERR_NOSPC = 28,
-	NFSERR_ROFS = 30,
-	NFSERR_OPNOTSUPP = 45,
-	NFSERR_NAMETOOLONG = 63,
-	NFSERR_NOTEMPTY = 66,
-	NFSERR_DQUOT = 69,
-	NFSERR_STALE = 70,
-	NFSERR_WFLUSH = 99
+	NFS_OK = 0,			/* v2 v3 */
+	NFSERR_PERM = 1,		/* v2 v3 */
+	NFSERR_NOENT = 2,		/* v2 v3 */
+	NFSERR_IO = 5,			/* v2 v3 */
+	NFSERR_NXIO = 6,		/* v2 v3 */
+	NFSERR_EAGAIN = 11,		/* v2 v3 */
+	NFSERR_ACCES = 13,		/* v2 v3 */
+	NFSERR_EXIST = 17,		/* v2 v3 */
+	NFSERR_XDEV = 18,		/*    v3 */
+	NFSERR_NODEV = 19,		/* v2 v3 */
+	NFSERR_NOTDIR = 20,		/* v2 v3 */
+	NFSERR_ISDIR = 21,		/* v2 v3 */
+	NFSERR_INVAL = 22,		/* v2 v3 that Sun forgot */
+	NFSERR_FBIG = 27,		/* v2 v3 */
+	NFSERR_NOSPC = 28,		/* v2 v3 */
+	NFSERR_ROFS = 30,		/* v2 v3 */
+	NFSERR_MLINK = 31,		/*    v3 */
+	NFSERR_OPNOTSUPP = 45,		/* v2 v3 */
+	NFSERR_NAMETOOLONG = 63,	/* v2 v3 */
+	NFSERR_NOTEMPTY = 66,		/* v2 v3 */
+	NFSERR_DQUOT = 69,		/* v2 v3 */
+	NFSERR_STALE = 70,		/* v2 v3 */
+	NFSERR_REMOTE = 71,		/* v2 v3 */
+	NFSERR_WFLUSH = 99,		/* v2    */
+	NFSERR_BADHANDLE = 10001,	/*    v3 */
+	NFSERR_NOT_SYNC = 10002,	/*    v3 */
+	NFSERR_BAD_COOKIE = 10003,	/*    v3 */
+	NFSERR_NOTSUPP = 10004,		/*    v3 */
+	NFSERR_TOOSMALL = 10005,	/*    v3 */
+	NFSERR_SERVERFAULT = 10006,	/*    v3 */
+	NFSERR_BADTYPE = 10007,		/*    v3 */
+	NFSERR_JUKEBOX = 10008		/*    v3 */
 };
 
+/* NFSv2 file types - beware, these are not the same in NFSv3 */
 enum nfs_ftype {
 	NFNON = 0,
 	NFREG = 1,
@@ -61,166 +67,42 @@
 	NFFIFO = 8
 };
 
-struct nfs_fh {
-	char			data[NFS_FHSIZE];
-};
-
-#define NFS_PROGRAM		100003
-#define NFS_VERSION		2
-#define NFSPROC_NULL		0
-#define NFSPROC_GETATTR		1
-#define NFSPROC_SETATTR		2
-#define NFSPROC_ROOT		3
-#define NFSPROC_LOOKUP		4
-#define NFSPROC_READLINK	5
-#define NFSPROC_READ		6
-#define NFSPROC_WRITECACHE	7
-#define NFSPROC_WRITE		8
-#define NFSPROC_CREATE		9
-#define NFSPROC_REMOVE		10
-#define NFSPROC_RENAME		11
-#define NFSPROC_LINK		12
-#define NFSPROC_SYMLINK		13
-#define NFSPROC_MKDIR		14
-#define NFSPROC_RMDIR		15
-#define NFSPROC_READDIR		16
-#define NFSPROC_STATFS		17
 
-/* Mount support for NFSroot */
-#ifdef __KERNEL__
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+/* 
+ * Mount support for NFSroot
+ */
 #define NFS_MNT_PROGRAM		100005
 #define NFS_MNT_VERSION		1
 #define NFS_MNT_PORT		627
 #define NFS_MNTPROC_MNT		1
 #define NFS_MNTPROC_UMNT	3
-#endif
-
-#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
-
-extern struct rpc_program	nfs_program;
-extern struct rpc_stat		nfs_rpcstat;
 
-struct nfs_time {
-	__u32			seconds;
-	__u32			useconds;
-};
-
-struct nfs_fattr {
-	enum nfs_ftype		type;
-	__u32			mode;
-	__u32			nlink;
-	__u32			uid;
-	__u32			gid;
-	__u32			size;
-	__u32			blocksize;
-	__u32			rdev;
-	__u32			blocks;
-	__u32			fsid;
-	__u32			fileid;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs_sattr {
-	__u32			mode;
-	__u32			uid;
-	__u32			gid;
-	__u32			size;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-};
-
-struct nfs_fsinfo {
-	__u32			tsize;
-	__u32			bsize;
-	__u32			blocks;
-	__u32			bfree;
-	__u32			bavail;
-};
-
-struct nfs_writeargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	const void *		buffer;
-};
-
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs_sattrargs {
-	struct nfs_fh *		fh;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_diropargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-};
-
-struct nfs_readargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	void *			buffer;
-};
-
-struct nfs_createargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_renameargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs_linkargs {
-	struct nfs_fh *		fromfh;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs_symlinkargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	const char *		topath;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs_readdirargs {
-	struct nfs_fh *		fh;
-	__u32			cookie;
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-struct nfs_diropok {
-	struct nfs_fh *		fh;
-	struct nfs_fattr *	fattr;
-};
+/*
+ * This is really a general kernel constant, but since nothing like
+ * this is defined in the kernel headers, I have to do it here.
+ */
+#define NFS_OFFSET_MAX		LONG_MAX
 
-struct nfs_readres {
-	struct nfs_fattr *	fattr;
-	unsigned int		count;
-};
+/*
+ * These data types are used exlusively by the NFS client implementation.
+ * They support both NFSv2 and NFSv3.
+ */
 
-struct nfs_readlinkres {
-	char **			string;
-	unsigned int *		lenp;
-	unsigned int		maxlen;
-	void *			buffer;
+/*
+ * This is the kernel NFS client file handle representation
+ */
+#define NFS_MAXFHSIZE		64
+struct nfs_fh {
+	unsigned short		size;
+	unsigned char		data[NFS_MAXFHSIZE];
 };
 
-struct nfs_readdirres {
-	void *			buffer;
-	unsigned int		bufsiz;
+enum nfs3_stable_how {
+	NFS_UNSTABLE = 0,
+	NFS_DATA_SYNC = 1,
+	NFS_FILE_SYNC = 2
 };
 
-#endif /* NFS_NEED_XDR_TYPES */
 #endif /* __KERNEL__ */
-
-#endif
+#endif /* _LINUX_NFS_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs2.h linux.18p17/include/linux/nfs2.h
--- linux.vanilla/include/linux/nfs2.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/nfs2.h	Tue Sep 26 18:26:30 2000
@@ -0,0 +1,76 @@
+/*
+ * NFS protocol definitions
+ *
+ * This file contains constants for Version 2 of the protocol.
+ */
+#ifndef _LINUX_NFS2_H
+#define _LINUX_NFS2_H
+
+#define NFS2_PORT	2049
+#define NFS2_MAXDATA	8192
+#define NFS2_MAXPATHLEN	1024
+#define NFS2_MAXNAMLEN	255
+#define NFS2_MAXGROUPS	16
+#define NFS2_FHSIZE	32
+#define NFS2_COOKIESIZE	4
+#define NFS2_FIFO_DEV	(-1)
+#define NFS2MODE_FMT	0170000
+#define NFS2MODE_DIR	0040000
+#define NFS2MODE_CHR	0020000
+#define NFS2MODE_BLK	0060000
+#define NFS2MODE_REG	0100000
+#define NFS2MODE_LNK	0120000
+#define NFS2MODE_SOCK	0140000
+#define NFS2MODE_FIFO	0010000
+
+	
+
+
+/* NFSv2 file types - beware, these are not the same in NFSv3 */
+enum nfs2_ftype {
+	NF2NON = 0,
+	NF2REG = 1,
+	NF2DIR = 2,
+	NF2BLK = 3,
+	NF2CHR = 4,
+	NF2LNK = 5,
+	NF2SOCK = 6,
+	NF2BAD = 7,
+	NF2FIFO = 8
+};
+
+struct nfs2_fh {
+	char			data[NFS2_FHSIZE];
+};
+
+/*
+ * Procedure numbers for NFSv2
+ */
+#define NFS2_VERSION		2
+#define NFSPROC_NULL		0
+#define NFSPROC_GETATTR		1
+#define NFSPROC_SETATTR		2
+#define NFSPROC_ROOT		3
+#define NFSPROC_LOOKUP		4
+#define NFSPROC_READLINK	5
+#define NFSPROC_READ		6
+#define NFSPROC_WRITECACHE	7
+#define NFSPROC_WRITE		8
+#define NFSPROC_CREATE		9
+#define NFSPROC_REMOVE		10
+#define NFSPROC_RENAME		11
+#define NFSPROC_LINK		12
+#define NFSPROC_SYMLINK		13
+#define NFSPROC_MKDIR		14
+#define NFSPROC_RMDIR		15
+#define NFSPROC_READDIR		16
+#define NFSPROC_STATFS		17
+
+#define NFS_MNT_PROGRAM		100005
+#define NFS_MNT_VERSION		1
+#define MNTPROC_NULL		0
+#define MNTPROC_MNT		1
+#define MNTPROC_UMNT		3
+#define MNTPROC_UMNTALL		4
+
+#endif /* _LINUX_NFS2_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs3.h linux.18p17/include/linux/nfs3.h
--- linux.vanilla/include/linux/nfs3.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/nfs3.h	Fri Sep 15 22:10:44 2000
@@ -4,15 +4,12 @@
 #ifndef _LINUX_NFS3_H
 #define _LINUX_NFS3_H
 
-#include <linux/sunrpc/msg_prot.h>
-#include <linux/nfs.h>
-
 #define NFS3_PORT		2049
-#define NFS3_MAXDATA		8192
+#define NFS3_MAXDATA		32768
 #define NFS3_MAXPATHLEN		PATH_MAX
 #define NFS3_MAXNAMLEN		NAME_MAX
 #define NFS3_MAXGROUPS		16
-#define NFS3_FHSIZE		NFS_FHSIZE
+#define NFS3_FHSIZE		64
 #define NFS3_COOKIESIZE		4
 #define NFS3_FIFO_DEV		(-1)
 #define NFS3MODE_FMT		0170000
@@ -24,39 +21,30 @@
 #define NFS3MODE_SOCK		0140000
 #define NFS3MODE_FIFO		0010000
 
-	
-enum nfs3_stat {
-	NFS3_OK			= 0,
-	NFS3ERR_PERM		= 1,
-	NFS3ERR_NOENT		= 2,
-	NFS3ERR_IO		= 5,
-	NFS3ERR_NXIO		= 6,
-	NFS3ERR_EAGAIN		= 11,
-	NFS3ERR_ACCES		= 13,
-	NFS3ERR_EXIST		= 17,
-	NFS3ERR_XDEV		= 18,	/* new in NFSv3 */
-	NFS3ERR_NODEV		= 19,
-	NFS3ERR_NOTDIR		= 20,
-	NFS3ERR_ISDIR		= 21,
-	NFS3ERR_INVAL		= 22,	/* new in NFSv3 */
-	NFS3ERR_FBIG		= 27,
-	NFS3ERR_NOSPC		= 28,
-	NFS3ERR_ROFS		= 30,
-	NFS3ERR_MLINK		= 31,	/* new in NFSv3 */
-	NFS3ERR_NAMETOOLONG	= 63,
-	NFS3ERR_NOTEMPTY	= 66,
-	NFS3ERR_DQUOT		= 69,
-	NFS3ERR_STALE		= 70,
-	NFS3ERR_REMOTE		= 71,	/* new in NFSv3 */
-	NFS3ERR_BADHANDLE	= 10001,/* ditto */
-	NFS3ERR_NOT_SYNC	= 10002,/* ditto */
-	NFS3ERR_BAD_COOKIE	= 10003,/* ditto */
-	NFS3ERR_NOTSUPP		= 10004,/* ditto */
-	NFS3ERR_TOOSMALL	= 10005,/* ditto */
-	NFS3ERR_SERVERFAULT	= 10006,/* ditto */
-	NFS3ERR_BADTYPE		= 10007,/* ditto */
-	NFS3ERR_JUKEBOX		= 10008,/* ditto */
-};
+/* Flags for access() call */
+#define NFS3_ACCESS_READ	0x0001
+#define NFS3_ACCESS_LOOKUP	0x0002
+#define NFS3_ACCESS_MODIFY	0x0004
+#define NFS3_ACCESS_EXTEND	0x0008
+#define NFS3_ACCESS_DELETE	0x0010
+#define NFS3_ACCESS_EXECUTE	0x0020
+
+/* Flags for create mode */
+enum nfs3_createmode {
+	NFS3_CREATE_UNCHECKED = 0,
+	NFS3_CREATE_GUARDED = 1,
+	NFS3_CREATE_EXCLUSIVE =	2
+};
+
+/* NFSv3 file system properties */
+#define NFS3_FSF_LINK		0x0001
+#define NFS3_FSF_SYMLINK	0x0002
+#define NFS3_FSF_HOMOGENEOUS	0x0008
+#define NFS3_FSF_CANSETTIME	0x0010
+/* Some shorthands. See fs/nfsd/nfs3proc.c */
+#define NFS3_FSF_DEFAULT	0x001B
+#define NFS3_FSF_BILLYBOY	0x0018
+#define NFS3_FSF_READONLY	0x0008
 
 enum nfs3_ftype {
 	NF3NON  = 0,
@@ -71,182 +59,41 @@
 };
 
 #define NFS3_VERSION		3
-#define NFSPROC_NULL		0
-#define NFSPROC_GETATTR		1
-#define NFSPROC_SETATTR		2
-#define NFSPROC_ROOT		3
-#define NFSPROC_LOOKUP		4
-#define NFSPROC_READLINK	5
-#define NFSPROC_READ		6
-#define NFSPROC_WRITECACHE	7
-#define NFSPROC_WRITE		8
-#define NFSPROC_CREATE		9
-#define NFSPROC_REMOVE		10
-#define NFSPROC_RENAME		11
-#define NFSPROC_LINK		12
-#define NFSPROC_SYMLINK		13
-#define NFSPROC_MKDIR		14
-#define NFSPROC_RMDIR		15
-#define NFSPROC_READDIR		16
-#define NFSPROC_STATFS		17
+#define NFS3PROC_NULL		0
+#define NFS3PROC_GETATTR	1
+#define NFS3PROC_SETATTR	2
+#define NFS3PROC_LOOKUP		3
+#define NFS3PROC_ACCESS		4
+#define NFS3PROC_READLINK	5
+#define NFS3PROC_READ		6
+#define NFS3PROC_WRITE		7
+#define NFS3PROC_CREATE		8
+#define NFS3PROC_MKDIR		9
+#define NFS3PROC_SYMLINK	10
+#define NFS3PROC_MKNOD		11
+#define NFS3PROC_REMOVE		12
+#define NFS3PROC_RMDIR		13
+#define NFS3PROC_RENAME		14
+#define NFS3PROC_LINK		15
+#define NFS3PROC_READDIR	16
+#define NFS3PROC_READDIRPLUS	17
+#define NFS3PROC_FSSTAT		18
+#define NFS3PROC_FSINFO		19
+#define NFS3PROC_PATHCONF	20
+#define NFS3PROC_COMMIT		21
+
+#define NFS_MNT3_PROGRAM	100005
+#define NFS_MNT3_VERSION	3
+#define MOUNTPROC3_NULL		0
+#define MOUNTPROC3_MNT		1
+#define MOUNTPROC3_UMNT		3
+#define MOUNTPROC3_UMNTALL	4
 
 #if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
 
-struct nfs3_fh {
-	__u32			size;
-	__u8			data[NFS3_FHSIZE];
-};
-
-struct nfs3_fattr {
-	enum nfs3_ftype		type;
-	__u32			mode;
-	__u32			nlink;
-	__u32			uid;
-	__u32			gid;
-	__u64			size;
-	__u64			used;
-	__u32			rdev_maj;
-	__u32			rdev_min;
-	__u32			fsid;
-	__u32			fileid;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs3_wcc_attr {
-	__u64			size;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-struct nfs3_wcc_data {
-	struct nfs3_wcc_attr	before;
-	struct nfs3_wcc_attr	after;
-};
-
-struct nfs3_sattr {
-	__u32			valid;
-	__u32			mode;
-	__u32			uid;
-	__u32			gid;
-	__u64			size;
-	struct nfs_time		atime;
-	struct nfs_time		mtime;
-};
-
-struct nfs3_entry {
-	__u32			fileid;
-	char *			name;
-	unsigned int		length;
-	__u32			cookie;
-	__u32			eof;
-};
-
-struct nfs3_fsinfo {
-	__u32			tsize;
-	__u32			bsize;
-	__u32			blocks;
-	__u32			bfree;
-	__u32			bavail;
-};
-
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs3_sattrargs {
-	struct nfs_fh *		fh;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_diropargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-};
-
-struct nfs3_readargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	void *			buffer;
-};
-
-struct nfs3_writeargs {
-	struct nfs_fh *		fh;
-	__u32			offset;
-	__u32			count;
-	const void *		buffer;
-};
+/* Number of 32bit words in post_op_attr */
+#define NFS3_POST_OP_ATTR_WORDS	22
+ 
 
-struct nfs3_createargs {
-	struct nfs_fh *		fh;
-	const char *		name;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_renameargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs3_linkargs {
-	struct nfs_fh *		fromfh;
-	struct nfs_fh *		tofh;
-	const char *		toname;
-};
-
-struct nfs3_symlinkargs {
-	struct nfs_fh *		fromfh;
-	const char *		fromname;
-	const char *		topath;
-	struct nfs_sattr *	sattr;
-};
-
-struct nfs3_readdirargs {
-	struct nfs_fh *		fh;
-	__u32			cookie;
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-struct nfs3_diropok {
-	struct nfs_fh *		fh;
-	struct nfs_fattr *	fattr;
-};
-
-struct nfs3_readres {
-	struct nfs_fattr *	fattr;
-	unsigned int		count;
-};
-
-struct nfs3_readlinkres {
-	char **			string;
-	unsigned int *		lenp;
-	unsigned int		maxlen;
-	void *			buffer;
-};
-
-struct nfs3_readdirres {
-	void *			buffer;
-	unsigned int		bufsiz;
-};
-
-/*
- * The following are for NFSv3
- */
-struct nfs3_fh {
-	__u32			size;
-	__u8			data[NFS3_FHSIZE]
-};
-
-struct nfs3_wcc_attr {
-	__u64			size;
-	struct nfs_time		mtime;
-	struct nfs_time		ctime;
-};
-
-#endif /* NFS_NEED_XDR_TYPES */
 #endif /* __KERNEL__ */
-
-#endif
+#endif /* _LINUX_NFS3_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_flushd.h linux.18p17/include/linux/nfs_flushd.h
--- linux.vanilla/include/linux/nfs_flushd.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/nfs_flushd.h	Fri Sep 15 22:10:44 2000
@@ -0,0 +1,64 @@
+#ifndef NFS_CLUSTER_H
+#define NFS_CLUSTER_H
+
+
+
+#ifdef __KERNEL__
+#include <linux/nfs_fs_sb.h>
+
+/*
+ * Counters of total number and pending number of requests.
+ * When the total number of requests exceeds the soft limit, we start
+ * flushing out requests. If it exceeds the hard limit, we stall until
+ * it drops again.
+ */
+#define MAX_REQUEST_SOFT        192
+#define MAX_REQUEST_HARD        256
+
+/*
+ * Maximum number of requests per write cluster.
+ * 32 requests per cluster account for 128K of data on an intel box.
+ * Note: it's a good idea to make this number smaller than MAX_REQUEST_SOFT.
+ *
+ * For 100Mbps Ethernet, 128 pages (i.e. 256K) per cluster gives much
+ * better performance.
+ */
+#define REQUEST_HASH_SIZE	16
+#define REQUEST_NR(off)		((off) >> PAGE_CACHE_SHIFT)
+#define REQUEST_HASH(ino, off)	(((ino) ^ REQUEST_NR(off)) & (REQUEST_HASH_SIZE - 1))
+
+
+/*
+ * Functions
+ */
+extern int		nfs_reqlist_alloc(struct nfs_server *);
+extern void		nfs_reqlist_free(struct nfs_server *);
+extern int		nfs_reqlist_init(struct nfs_server *);
+extern void		nfs_reqlist_exit(struct nfs_server *);
+extern void		inode_schedule_scan(struct inode *, unsigned long);
+extern void		inode_remove_flushd(struct inode *);
+extern void		nfs_wake_flushd(void);
+
+/*
+ * This is the per-mount writeback cache.
+ */
+struct nfs_reqlist {
+	unsigned int		nr_requests;
+	unsigned long		runat;
+	struct wait_queue	*request_wait;
+
+	/* The async RPC task that is responsible for scanning the
+	 * requests.
+	 */
+	struct rpc_task		*task;		/* request flush task */
+
+	/* Authentication flavor handle for this NFS client */
+	struct rpc_auth		*auth;
+
+	/* The list of all inodes with pending writebacks.  */
+	struct inode		*inodes;
+};
+
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_fs.h linux.18p17/include/linux/nfs_fs.h
--- linux.vanilla/include/linux/nfs_fs.h	Fri Apr 21 23:10:29 2000
+++ linux.18p17/include/linux/nfs_fs.h	Sun Oct 15 23:29:26 2000
@@ -9,13 +9,21 @@
 #ifndef _LINUX_NFS_FS_H
 #define _LINUX_NFS_FS_H
 
+#ifdef __KERNEL__
 #include <linux/signal.h>
 #include <linux/sched.h>
+#include <linux/kernel.h>
 #include <linux/in.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 
 #include <linux/sunrpc/sched.h>
+#endif /* __KERNEL__ */
+
 #include <linux/nfs.h>
-#include <linux/nfs_mount.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_xdr.h>
 
 /*
  * Enable debugging support for nfs client.
@@ -25,48 +33,45 @@
 # define NFS_DEBUG
 #endif
 
-/*
- * NFS_MAX_DIRCACHE controls the number of simultaneously cached
- * directory chunks. Each chunk holds the list of nfs_entry's returned
- * in a single readdir call in a memory region of size PAGE_SIZE.
- *
- * Note that at most server->rsize bytes of the cache memory are used.
- */
-#define NFS_MAX_DIRCACHE		16
-
-#define NFS_MAX_FILE_IO_BUFFER_SIZE	16384
-#define NFS_DEF_FILE_IO_BUFFER_SIZE	4096
+#define NFS_MAX_FILE_IO_BUFFER_SIZE	32768
+#define NFS_DEF_FILE_IO_BUFFER_SIZE	1024
 
 /*
  * The upper limit on timeouts for the exponential backoff algorithm.
  */
-#define NFS_MAX_RPC_TIMEOUT		(6*HZ)
-
-/*
- * Size of the lookup cache in units of number of entries cached.
- * It is better not to make this too large although the optimum
- * depends on a usage and environment.
- */
-#define NFS_LOOKUP_CACHE_SIZE		64
+#define NFS_READ_DELAY			(2*HZ)
+#define NFS_WRITEBACK_DELAY		(5*HZ)
+#define NFS_WRITEBACK_LOCKDELAY		(60*HZ)
+#define NFS_COMMIT_DELAY		(5*HZ)
 
 /*
  * superblock magic number for NFS
  */
 #define NFS_SUPER_MAGIC			0x6969
+#define NFS_FILE_MAGIC			0xA4F0
 
+#ifdef __KERNEL__
+/*
+ * Convenience macros
+ */
 #define NFS_FH(dentry)			((struct nfs_fh *) ((dentry)->d_fsdata))
 #define NFS_DSERVER(dentry)		(&(dentry)->d_sb->u.nfs_sb.s_server)
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
+#define NFS_PROTO(inode)		(NFS_SERVER(inode)->rpc_ops)
+#define NFS_REQUESTLIST(inode)		(NFS_SERVER(inode)->rw_requests)
 #define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
 #define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
-
+#define NFS_COOKIEVERF(inode)		((inode)->u.nfs_i.cookieverf)
 #define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
-#define NFS_OLDMTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_CTIME(inode)		((inode)->u.nfs_i.read_cache_ctime)
+#define NFS_CACHE_MTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_ATIME(inode)		((inode)->u.nfs_i.read_cache_atime)
+#define NFS_CACHE_ISIZE(inode)		((inode)->u.nfs_i.read_cache_isize)
+#define NFS_NEXTSCAN(inode)		((inode)->u.nfs_i.nextscan)
 #define NFS_CACHEINV(inode) \
 do { \
-	NFS_READTIME(inode) = jiffies - 1000000; \
-	NFS_OLDMTIME(inode) = 0; \
+	NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
 } while (0)
 #define NFS_ATTRTIMEO(inode)		((inode)->u.nfs_i.attrtimeo)
 #define NFS_MINATTRTIMEO(inode) \
@@ -75,10 +80,18 @@
 #define NFS_MAXATTRTIMEO(inode) \
 	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
 			       : NFS_SERVER(inode)->acregmax)
+#define NFS_ATTRTIMEO_UPDATE(inode)	((inode)->u.nfs_i.attrtimeo_timestamp)
 
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
-#define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
-#define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
+#define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
+
+#define NFS_FILEID(inode)	((inode)->u.nfs_i.fileid)
+#define NFS_FSID(inode)		((inode)->u.nfs_i.fsid)
+
+#define NFS_FILE(file)		((struct nfs_file *)(file)->private_data)
+
+/* Inode Flags */
+#define NFS_USE_READDIRPLUS(inode)	((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
 
 /*
  * These are the default flags for swap requests
@@ -88,116 +101,81 @@
 /* Flags in the RPC client structure */
 #define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
 
-#ifdef __KERNEL__
+#define NFS_RW_SYNC		0x0001	/* O_SYNC handling */
+#define NFS_RW_SWAP		0x0002	/* This is a swap request */
 
 /*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
-	struct rpc_listitem	wb_list;	/* linked list of req's */
-	struct rpc_task		wb_task;	/* RPC task */
-	struct file *		wb_file;	/* dentry referenced */
-	struct page *		wb_page;	/* page to be written */
-	struct wait_queue *	wb_wait;	/* wait for completion */
-	unsigned int		wb_offset;	/* offset within page */
-	unsigned int		wb_bytes;	/* dirty range */
-	unsigned int		wb_count;	/* user count */
-	int			wb_status;
-	pid_t			wb_pid;		/* owner process */
-	unsigned short		wb_flags;	/* status flags */
-
-	struct nfs_writeargs	wb_args;	/* NFS RPC stuff */
-	struct nfs_fattr	wb_fattr;	/* file attributes */
-};
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING		0	/* only flush old buffers */
+#define FLUSH_SYNC		1	/* file being synced, or contention */
+#define FLUSH_WAIT		2	/* wait for completion */
+#define FLUSH_STABLE		4	/* commit to stable storage */
 
-#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
 
 /*
- * Various flags for wb_flags
+ * Structure for file->private_data;
  */
-#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
-#define NFS_WRITE_INPROGRESS	0x0100	/* RPC call in progress */
-#define NFS_WRITE_COMPLETE	0x0200	/* RPC call completed */
-
-#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
-#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_COMPLETE(req)	((req)->wb_flags & NFS_WRITE_COMPLETE)
-
-/*
- * linux/fs/nfs/proc.c
- */
-extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr);
-extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_sattr *sattr, struct nfs_fattr *fattr);
-extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr);
-extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen);
-extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
-			int swap, unsigned long offset, unsigned int count,
-			void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
-			int swap, unsigned long offset, unsigned int count,
-			const void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name);
-extern int nfs_proc_rename(struct nfs_server *server,
-			struct nfs_fh *old_dir, const char *old_name,
-			struct nfs_fh *new_dir, const char *new_name);
-extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fh *dir, const char *name);
-extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, const char *path,
-			struct nfs_sattr *sattr);
-extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name, struct nfs_sattr *sattr,
-			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
-			const char *name);
-extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry);
-extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
-			struct nfs_fsinfo *res);
+struct nfs_file {
+	unsigned long		magic;		/* Magic number */
+	struct rpc_cred*	cred;		/* RPC Credentials */
+};
+
+static inline int
+nfs_check_file(struct file *file)
+{
+	if (NFS_FILE(file)->magic != NFS_FILE_MAGIC) {
+		printk(KERN_ERR "NFS: corrupt file structure!\n");
+		return 0;
+	}
+	return 1;
+}
 
+static inline
+unsigned long nfs_page_offset(struct page *page)
+{
+        return page->offset;
+}
+
+static inline
+unsigned long page_index(struct page *page)
+{
+	return page->offset >> PAGE_CACHE_SHIFT;
+}
 
 /*
  * linux/fs/nfs/inode.c
  */
-extern struct super_block *nfs_read_super(struct super_block *, void *, int);
-extern int init_nfs_fs(void);
 extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *,
-				struct nfs_fattr *);
-extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
-extern int nfs_revalidate(struct dentry *);
-extern int nfs_open(struct inode *, struct file *);
-extern int nfs_release(struct inode *, struct file *);
-extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *);
+			       struct nfs_fattr *);
+extern struct super_block *nfs_read_super(struct super_block *, void *, int);
+extern int	init_nfs_fs(void);
+extern void	nfs_zap_caches(struct inode *);
+extern int	nfs_revalidate(struct dentry *);
+extern int	nfs_open(struct inode *, struct file *);
+extern int	nfs_release(struct inode *, struct file *);
+extern int	__nfs_revalidate_inode(struct dentry *);
+extern int	nfs_refresh_inode(struct inode *, struct nfs_fattr *);
+extern int	nfs_wait_on_inode(struct inode *, int flag);
+extern void	nfs_unlock_inode(struct inode *);
+extern int	nfs_update_atime(struct dentry *);
+
 
 /*
  * linux/fs/nfs/file.c
  */
 extern struct inode_operations nfs_file_inode_operations;
+extern int	nfs_permission(struct inode *i, int msk);
 
 /*
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
 extern struct dentry_operations nfs_dentry_operations;
-extern void nfs_free_dircache(void);
-extern void nfs_invalidate_dircache(struct inode *);
-extern void nfs_invalidate_dircache_sb(struct super_block *);
+extern struct nfs_fh *nfs_fh_alloc(void);
+extern void nfs_fh_free(struct nfs_fh *);
+
 
 /*
  * linux/fs/nfs/symlink.c
@@ -210,53 +188,167 @@
 extern int nfs_lock(struct file *, int, struct file_lock *);
 
 /*
+ * linux/fs/nfs/unlink.c
+ */
+extern int  nfs_async_unlink(struct dentry *);
+extern void nfs_complete_unlink(struct dentry *);
+
+/*
  * linux/fs/nfs/write.c
  */
 extern int  nfs_writepage(struct file *, struct page *);
-extern int  nfs_check_failed_request(struct inode *);
-
+extern int  nfs_flush_incompatible(struct file *, struct page *);
+extern int  nfs_updatepage(struct file *, struct page *,
+                        unsigned long, unsigned int, int);
 /*
  * Try to write back everything synchronously (but check the
  * return value!)
  */
-extern int  nfs_wb_all(struct inode *);
-extern int  nfs_wb_page(struct inode *, struct page *);
-extern int  nfs_wb_file(struct inode *, struct file *);
+extern int  nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+extern int  nfs_flush_timeout(struct inode *, int);
+extern int  nfs_commit_timeout(struct inode *, int);
+
+static inline int
+nfs_have_read(struct inode *inode)
+{
+	return !list_empty(&inode->u.nfs_i.read);
+}
+
+static inline int
+nfs_have_writebacks(struct inode *inode)
+{
+	return !list_empty(&inode->u.nfs_i.writeback);
+}
+
+static inline int
+nfs_wb_all(struct inode *inode)
+{
+	int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
+
+/*
+ * Write back all requests on one page - we do this before reading it.
+ */
+static inline int
+nfs_wb_page(struct inode *inode, struct page* page)
+{
+	int error = nfs_sync_file(inode, 0, page_index(page), 1, FLUSH_WAIT | FLUSH_STABLE);
+	return (error < 0) ? error : 0;
+}
+
+/*
+ * Write back all pending writes for one user.. 
+ */
+static inline int
+nfs_wb_file(struct inode *inode, struct file *file)
+{
+	int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT);
+	return (error < 0) ? error : 0;
+}
 
 /*
- * Invalidate write-backs, possibly trying to write them
- * back first..
+ * Lock the page of an asynchronous request
  */
-extern void nfs_inval(struct inode *);
-extern int  nfs_updatepage(struct file *, struct page *, unsigned long, unsigned int, int);
+static __inline__ int
+nfs_lock_page(struct page *page)
+{
+	return !test_and_set_bit(PG_locked, &page->flags);
+}
+
+static __inline__ void
+nfs_unlock_page(struct page *page)
+{
+        clear_bit(PG_locked, &page->flags);
+        wake_up(&page->wait);
+}
 
 /*
  * linux/fs/nfs/read.c
  */
 extern int  nfs_readpage(struct file *, struct page *);
+extern int  nfs_pagein_inode(struct inode *, unsigned long, unsigned int);
+extern int  nfs_pagein_timeout(struct inode *);
+
+/*
+ * linux/fs/nfs2xdr.c
+ */
+extern u32 *nfs_decode_dirent(u32 *, struct nfs_entry *, int);
+
+/*
+ * linux/fs/nfs2xdr.c
+ */
+extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
 
 /*
  * linux/fs/mount_clnt.c
  * (Used only by nfsroot module)
  */
 extern int  nfs_mount(struct sockaddr_in *, char *, struct nfs_fh *);
+extern int  nfs3_mount(struct sockaddr_in *, char *, struct nfs_fh *);
 
 /*
  * inline functions
  */
 static inline int
-nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+nfs_revalidate_inode(struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
-	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+	if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
 		return 0;
-	return _nfs_revalidate_inode(server, dentry);
+	return __nfs_revalidate_inode(dentry);
+}
+
+static inline off_t
+nfs_size_to_off_t(__u64 size)
+{
+	return (size > (__u64)LONG_MAX) ? (off_t)LONG_MAX : (off_t) size;
+}
+
+static inline ino_t
+nfs_fileid_to_ino_t(u64 fileid)
+{
+	ino_t ino = (unsigned long) fileid;
+	if (sizeof(unsigned long) < sizeof(u64))
+		ino ^= fileid >> (sizeof(u64)-sizeof(unsigned long)) * 8;
+	return ino;
+}
+
+static inline time_t
+nfs_time_to_secs(__u64 time)
+{
+	return (time_t)(time >> 32);
+}
+
+static __inline__ struct rpc_cred *
+nfs_file_cred(struct file *file)
+{
+	if (!NFS_FILE(file) || !nfs_check_file(file)) {
+		printk("nfs_file_cred: invalid file!\n");
+		return NULL;
+	}
+	return NFS_FILE(file)->cred;
 }
 
 /* NFS root */
 
 extern int nfs_root_mount(struct super_block *sb);
 
+#define nfs_wait_event(clnt, wq, condition)				\
+({									\
+	int __retval = 0;						\
+	if (clnt->cl_intr) {						\
+		sigset_t oldmask;					\
+		rpc_clnt_sigmask(clnt, &oldmask);			\
+		__retval = wait_event_interruptible(wq, condition);	\
+		rpc_clnt_sigunmask(clnt, &oldmask);			\
+	} else								\
+		wait_event(wq, condition);				\
+	__retval;							\
+})
+
 #endif /* __KERNEL__ */
 
 /*
@@ -270,6 +362,7 @@
 #define NFSDBG_XDR		0x0020
 #define NFSDBG_FILE		0x0040
 #define NFSDBG_ROOT		0x0080
+#define NFSDBG_PARANOID		0x0100
 #define NFSDBG_ALL		0xFFFF
 
 #ifdef __KERNEL__
@@ -278,6 +371,11 @@
 #  define ifdebug(fac)		if (nfs_debug & NFSDBG_##fac)
 # else
 #  define ifdebug(fac)		if (0)
+# endif
+# ifdef NFS_PARANOIA
+#  define nfsparanoid(args...)	dfprintk(PARANOID, ##args)
+# else
+#  define nfsparanoid(args...)	do { } while (0)
 # endif
 #endif /* __KERNEL */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_fs_i.h linux.18p17/include/linux/nfs_fs_i.h
--- linux.vanilla/include/linux/nfs_fs_i.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/nfs_fs_i.h	Fri Sep 15 22:10:44 2000
@@ -1,4 +1,4 @@
-#ifndef _NFS_FS_I
+#ifndef _NFS_FS_i
 #define _NFS_FS_I
 
 #include <linux/nfs.h>
@@ -16,6 +16,12 @@
 	struct pipe_inode_info	pipeinfo;
 
 	/*
+	 * The 64bit fileid
+	 */
+	__u64 fsid;
+	__u64 fileid;
+
+	/*
 	 * Various flags
 	 */
 	unsigned short		flags;
@@ -38,22 +44,68 @@
 	 *	mtime != read_cache_mtime
 	 */
 	unsigned long		read_cache_jiffies;
-	unsigned long		read_cache_mtime;
+	__u64			read_cache_ctime;
+	__u64			read_cache_mtime;
+	__u64			read_cache_atime;
+	__u64			read_cache_isize;
 	unsigned long		attrtimeo;
+	unsigned long		attrtimeo_timestamp;
+
+	/*
+	 * This is the cookie verifier used for NFSv3 readdir
+	 * operations
+	 */
+	__u32			cookieverf[2];
 
 	/*
-	 * This is the list of dirty unwritten pages.
-	 * NFSv3 will want to add a list for written but uncommitted
-	 * pages.
+	 * This is the list of dirty pages.
 	 */
-	struct nfs_wreq *	writeback;
+	struct list_head	read;
+	struct list_head	dirty;
+	struct list_head	commit;
+	struct list_head	writeback;
+
+	unsigned int		nread,
+				ndirty,
+				ncommit,
+				npages;
+
+	/* Flush daemon info */
+	struct inode		*hash_next,
+				*hash_prev;
+	unsigned long		nextscan;
 };
 
 /*
  * Legal inode flag values
  */
-#define NFS_INO_REVALIDATE	0x0001		/* revalidating attrs */
+#define NFS_INO_LOCKED          0x0001          /* locked for revalidation */
+#define NFS_INO_ADVISE_RDPLUS   0x0002          /* advise readdirplus */
+#define NFS_INO_REVALIDATING    0x0004          /* in nfs_revalidate() */
+#define NFS_INO_INVALIDATE      0x0008          /* zap cache on next occasion */
 #define NFS_IS_SNAPSHOT		0x0010		/* a snapshot file */
+#define NFS_INO_STALE		0x0020		/* We suspect inode is stale */
+#define NFS_INO_FLUSH		0x0040		/* inode is due for flushing */
+
+/*
+ * NFS ACL info.
+ * This information will be used by nfs_permission() in the obvious fashion,
+ * but also helps the RPC engine to select whether to try the operation first
+ * with the effective or real uid/gid first.
+ *
+ * For NFSv2, this info is obtained by just trying the operation in
+ * question and updating the ACL info according to the result.
+ * For NFSv3, the access() call is used to fill in the permission bits.
+ *
+ * Not yet used.
+ */
+struct nfs_acl_info {
+	struct nfs_acl_info *	acl_next;
+	unsigned long		acl_read_time;
+	uid_t			acl_uid;
+	gid_t			acl_gid;
+	unsigned int		acl_bits;
+};
 
 /*
  * NFS lock info
@@ -61,6 +113,7 @@
 struct nfs_lock_info {
 	u32		state;
 	u32		flags;
+	struct nlm_host	*host;
 };
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_fs_sb.h linux.18p17/include/linux/nfs_fs_sb.h
--- linux.vanilla/include/linux/nfs_fs_sb.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/nfs_fs_sb.h	Fri Sep 15 22:10:44 2000
@@ -1,7 +1,6 @@
 #ifndef _NFS_FS_SB
 #define _NFS_FS_SB
 
-#include <linux/nfs.h>
 #include <linux/in.h>
 
 /*
@@ -9,23 +8,32 @@
  */
 struct nfs_server {
 	struct rpc_clnt *	client;		/* RPC client handle */
+	struct nfs_rpc_ops *	rpc_ops;	/* NFS protocol vector */
 	int			flags;		/* various flags */
-	int			rsize;		/* read size */
-	int			wsize;		/* write size */
+	unsigned int		rsize;		/* read size */
+	unsigned int		rpages;		/* read size (in pages) */
+	unsigned int		wsize;		/* write size */
+	unsigned int		wpages;		/* write size (in pages) */
+	unsigned int		dtsize;		/* readdir size */
 	unsigned int		bsize;		/* server block size */
 	unsigned int		acregmin;	/* attr cache timeouts */
 	unsigned int		acregmax;
 	unsigned int		acdirmin;
 	unsigned int		acdirmax;
+	unsigned int		namelen;
 	char *			hostname;	/* remote hostname */
+	struct nfs_dircache *	dircache;	/* readdir cache info */
+	struct nfs_reqlist *	rw_requests;    /* async read/write requests */
 };
 
+
 /*
  * nfs super-block data in memory
  */
 struct nfs_sb_info {
-	struct nfs_server	s_server;
-	struct nfs_fh		s_root;
+	struct nfs_server	s_server;	/* NFS server info */
+	unsigned int		s_fhsize;	/* File handle size */
+	struct nfs_fh *		s_root;		/* The root file handle */
 };
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_mount.h linux.18p17/include/linux/nfs_mount.h
--- linux.vanilla/include/linux/nfs_mount.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/nfs_mount.h	Sun Oct 15 23:30:59 2000
@@ -8,6 +8,9 @@
  *
  *  structure passed from user-space to kernel-space during an nfs mount
  */
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
 
 /*
  * WARNING!  Do not delete or change the order of these fields.  If
@@ -15,13 +18,17 @@
  * tracks which fields are present.  This will ensure some measure of
  * mount-to-kernel version compatibility.  Some of these aren't used yet
  * but here they are anyway.
+ *
+ * Lugging around two file handles is inevitable if we want to pass
+ * version 3 handles while maintaining compatibility. We may want to
+ * break it for the 2.2 kernel eventually...
  */
-#define NFS_MOUNT_VERSION	3
+#define NFS_MOUNT_VERSION	4
 
 struct nfs_mount_data {
 	int		version;		/* 1 */
 	int		fd;			/* 1 */
-	struct nfs_fh	root;			/* 1 */
+	struct nfs2_fh	old_root;		/* 1 */
 	int		flags;			/* 1 */
 	int		rsize;			/* 1 */
 	int		wsize;			/* 1 */
@@ -35,6 +42,7 @@
 	char		hostname[256];		/* 1 */
 	int		namlen;			/* 2 */
 	unsigned int	bsize;			/* 3 */
+	struct nfs_fh	root;			/* 4 */
 };
 
 /* bits in the flags field */
@@ -49,5 +57,7 @@
 #define NFS_MOUNT_VER3		0x0080	/* 3 */
 #define NFS_MOUNT_KERBEROS	0x0100	/* 3 */
 #define NFS_MOUNT_NONLM		0x0200	/* 3 */
- 
+#define NFS_MOUNT_BROKEN_SUID	0x0400	/* 4 */
+#define NFS_MOUNT_FLAGMASK	0xFFFF
+
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_page.h linux.18p17/include/linux/nfs_page.h
--- linux.vanilla/include/linux/nfs_page.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/nfs_page.h	Sun Oct 15 23:46:16 2000
@@ -0,0 +1,100 @@
+/*
+ * linux/include/linux/nfs_page.h
+ *
+ * Copyright (C) 2000 Trond Myklebust
+ *
+ * NFS page cache wrapper.
+ */
+
+#ifndef _LINUX_NFS_PAGE_H
+#define _LINUX_NFS_PAGE_H
+
+
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/nfs_xdr.h>
+
+/*
+ * Valid flags for a dirty buffer
+ */
+#define PG_BUSY			0x0001
+
+struct nfs_page {
+	struct list_head	wb_hash,	/* Inode */
+				wb_list,	/* Defines state of page: */
+				*wb_list_head;	/*      read/write/commit */
+	struct file		*wb_file;
+	struct dentry		*wb_dentry;
+	struct rpc_cred		*wb_cred;
+	struct page		*wb_page;	/* page to read in/write out */
+	struct wait_queue	*wb_wait;	/* wait queue */
+	unsigned long		wb_timeout;	/* when to read/write/commit */
+	unsigned int		wb_offset,	/* Offset of read/write */
+				wb_bytes,	/* Length of request */
+				wb_count,	/* reference count */
+				wb_flags;
+	struct nfs_writeverf	wb_verf;	/* Commit cookie */
+};
+
+#define NFS_WBACK_BUSY(req)	((req)->wb_flags & PG_BUSY)
+
+extern	struct nfs_page *nfs_create_request(struct file *file,
+					    struct page *page,
+					    unsigned int offset,
+					    unsigned int count);
+extern	void nfs_release_request(struct nfs_page *req);
+
+
+extern	void nfs_list_add_request(struct nfs_page *req,
+				  struct list_head *head);
+extern	void nfs_list_remove_request(struct nfs_page *req);
+
+extern	int nfs_scan_list_timeout(struct list_head *head,
+				  struct list_head *dst,
+				  struct inode *inode);
+extern	int nfs_scan_list(struct list_head *src, struct list_head *dst,
+			  struct file *file, unsigned long idx_start,
+			  unsigned int npages);
+extern	int nfs_coalesce_requests(struct list_head *src, struct list_head *dst,
+				  unsigned int maxpages);
+
+/*
+ * Lock the page of an asynchronous request
+ */
+static __inline__ int
+nfs_lock_request(struct nfs_page *req)
+{
+	if (NFS_WBACK_BUSY(req))
+		return 0;
+	req->wb_count++;
+	req->wb_flags |= PG_BUSY;
+	return 1;
+}
+
+static __inline__ void
+nfs_unlock_request(struct nfs_page *req)
+{
+	if (!NFS_WBACK_BUSY(req)) {
+		printk(KERN_ERR "NFS: Invalid unlock attempted\n");
+		return;
+	}
+	req->wb_flags &= ~PG_BUSY;
+	wake_up(&req->wb_wait);
+	nfs_release_request(req);
+}
+
+static __inline__ struct nfs_page *
+nfs_list_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_list);
+}
+
+static __inline__ struct nfs_page *
+nfs_inode_wb_entry(struct list_head *head)
+{
+	return list_entry(head, struct nfs_page, wb_hash);
+}
+
+#endif /* _LINUX_NFS_PAGE_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfs_xdr.h linux.18p17/include/linux/nfs_xdr.h
--- linux.vanilla/include/linux/nfs_xdr.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/nfs_xdr.h	Sun Oct 15 23:29:26 2000
@@ -0,0 +1,387 @@
+#ifndef _LINUX_NFS_XDR_H
+#define _LINUX_NFS_XDR_H
+
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+#include <linux/dcache.h>
+
+extern struct rpc_program	nfs_program;
+extern struct rpc_stat		nfs_rpcstat;
+
+struct nfs_fattr {
+	unsigned short		valid;		/* which fields are valid */
+	__u64			pre_size;	/* pre_op_attr.size	  */
+	__u64			pre_mtime;	/* pre_op_attr.mtime	  */
+	__u64			pre_ctime;	/* pre_op_attr.ctime	  */
+	enum nfs_ftype		type;		/* always use NFSv2 types */
+	__u32			mode;
+	__u32			nlink;
+	__u32			uid;
+	__u32			gid;
+	__u64			size;
+	union {
+		struct {
+			__u32	blocksize;
+			__u32	blocks;
+		} nfs2;
+		struct {
+			__u64	used;
+		} nfs3;
+	} du;
+	__u32			rdev;
+	__u64			fsid;
+	__u64			fileid;
+	__u64			atime;
+	__u64			mtime;
+	__u64			ctime;
+};
+
+#define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
+#define NFS_ATTR_FATTR		0x0002		/* post-op attributes */
+#define NFS_ATTR_FATTR_V3	0x0004		/* NFSv3 attributes */
+
+/*
+ * Info on the file system
+ */
+struct nfs_fsinfo {
+	__u32			rtmax;	/* max.  read transfer size */
+	__u32			rtpref;	/* pref. read transfer size */
+	__u32			rtmult;	/* reads should be multiple of this */
+	__u32			wtmax;	/* max.  write transfer size */
+	__u32			wtpref;	/* pref. write transfer size */
+	__u32			wtmult;	/* writes should be multiple of this */
+	__u32			dtpref;	/* pref. readdir transfer size */
+	__u64			maxfilesize;
+	__u64			bsize;	/* block size */
+	__u64			tbytes;	/* total size in bytes */
+	__u64			fbytes;	/* # of free bytes */
+	__u64			abytes;	/* # of bytes available to user */
+	__u64			tfiles;	/* # of files */
+	__u64			ffiles;	/* # of free files */
+	__u64			afiles;	/* # of files available to user */
+	__u32			linkmax;/* max # of hard links */
+	__u32			namelen;/* max name length */
+};
+
+/* Arguments to the read call.
+ * Note that NFS_READ_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_READ_MAXIOV 8
+
+struct nfs_readargs {
+	struct nfs_fh *		fh;
+	__u64			offset;
+	__u32			count;
+	unsigned int            nriov;
+	struct iovec            iov[NFS_READ_MAXIOV];
+};
+
+struct nfs_readres {
+	struct nfs_fattr *	fattr;
+	unsigned int		count;
+	int                     eof;
+};
+
+/* Arguments to the write call.
+ * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_WRITE_MAXIOV        8
+struct nfs_writeargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	enum nfs3_stable_how	stable;
+	unsigned int		nriov;
+	struct iovec		iov[NFS_WRITE_MAXIOV];
+};
+
+struct nfs_writeverf {
+	enum nfs3_stable_how	committed;
+	__u32			verifier[2];
+};
+
+struct nfs_writeres {
+	struct nfs_fattr *	fattr;
+	struct nfs_writeverf *	verf;
+	__u32			count;
+};
+
+/*
+ * Argument struct for decode_entry function
+ */
+struct nfs_entry {
+	__u64			ino;
+	__u64			cookie,
+				prev_cookie;
+	const char *		name;
+	unsigned int		len;
+	int			eof;
+	struct nfs_fh		fh;
+	struct nfs_fattr	fattr;
+};
+
+/*
+ * The following types are for NFSv2 only.
+ */
+struct nfs_sattrargs {
+	struct nfs_fh *		fh;
+	struct iattr *		sattr;
+};
+
+struct nfs_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+};
+
+struct nfs_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+};
+
+struct nfs_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	const char *		topath;
+	unsigned int		tolen;
+	struct iattr *		sattr;
+};
+
+struct nfs_readdirargs {
+	struct nfs_fh *		fh;
+	__u32			cookie;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_diropok {
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs_readlinkargs {
+	struct nfs_fh *		fh;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_readlinkres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_readdirres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_sattrargs {
+	struct nfs_fh *		fh;
+	struct iattr *		sattr;
+	unsigned int		guard;
+	__u64			guardtime;
+};
+
+struct nfs3_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+};
+
+struct nfs3_accessargs {
+	struct nfs_fh *		fh;
+	__u32			access;
+};
+
+struct nfs3_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+	enum nfs3_createmode	createmode;
+	__u32			verifier[2];
+};
+
+struct nfs3_mkdirargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	struct iattr *		sattr;
+};
+
+struct nfs3_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	const char *		topath;
+	unsigned int		tolen;
+	struct iattr *		sattr;
+};
+
+struct nfs3_mknodargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	unsigned int		len;
+	enum nfs3_ftype		type;
+	struct iattr *		sattr;
+	dev_t			rdev;
+};
+
+struct nfs3_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	unsigned int		fromlen;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs3_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+	unsigned int		tolen;
+};
+
+struct nfs3_readdirargs {
+	struct nfs_fh *		fh;
+	__u64			cookie;
+	__u32			verf[2];
+	void *			buffer;
+	unsigned int		bufsiz;
+	int			plus;
+};
+
+struct nfs3_diropres {
+	struct nfs_fattr *	dir_attr;
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs3_accessres {
+	struct nfs_fattr *	fattr;
+	__u32			access;
+};
+
+struct nfs3_readlinkargs {
+	struct nfs_fh *		fh;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_readlinkres {
+	struct nfs_fattr *	fattr;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_renameres {
+	struct nfs_fattr *	fromattr;
+	struct nfs_fattr *	toattr;
+};
+
+struct nfs3_linkres {
+	struct nfs_fattr *	dir_attr;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs3_readdirres {
+	struct nfs_fattr *	dir_attr;
+	__u32 *			verf;
+	void *			buffer;
+	unsigned int		bufsiz;
+	int			plus;
+};
+
+/*
+ * RPC procedure vector for NFSv2/NFSv3 demuxing
+ */
+struct nfs_rpc_ops {
+	int	version;		/* Protocol version */
+
+	int	(*getroot)(struct nfs_server *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*getattr)(struct dentry *, struct nfs_fattr *);
+	int	(*setattr)(struct dentry *, struct nfs_fattr *, struct iattr *);
+	int	(*lookup)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*access)(struct dentry *, int fmode, struct nfs_fattr *, int);
+	int	(*readlink)(struct dentry *, struct nfs_fattr *,
+			void *buffer, unsigned int buflen);
+	int	(*read)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			int flags, unsigned long offset,
+			unsigned int count, void *buffer, int *eofp);
+	int	(*write)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			int flags, unsigned long offset,
+			unsigned int count, void *buffer,
+			struct nfs_writeverf *verfp);
+	int	(*commit)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			unsigned long, unsigned int);
+	int	(*create)(struct dentry *, struct nfs_fattr *,
+			struct qstr *, struct iattr *, int flags,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*remove)(struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*unlink_setup)	(struct rpc_message *,
+			struct dentry *, struct qstr *);
+	void	(*unlink_done)	(struct dentry *, struct rpc_message *);
+	int	(*rename)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*link)(struct dentry *, struct nfs_fattr *,
+			struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*symlink)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct qstr *, struct iattr *,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*mkdir)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct iattr *, struct nfs_fh *, struct nfs_fattr *);
+	int	(*rmdir)(struct dentry *, struct nfs_fattr *, struct qstr *);
+	int	(*readdir)(struct dentry *, struct nfs_fattr *,
+			struct rpc_cred *,
+			__u64 cookie, void *, unsigned int size, int plus);
+	int	(*mknod)(struct dentry *, struct nfs_fattr *, struct qstr *,
+			struct iattr *, dev_t,
+			struct nfs_fh *, struct nfs_fattr *);
+	int	(*statfs)(struct nfs_server *, struct nfs_fh *,
+			struct nfs_fsinfo *);
+	__u32 *	(*decode_dirent)(__u32 *, struct nfs_entry *, int plus);
+};
+
+/*
+ * 	NFS_CALL(getattr, inode, (fattr));
+ * into
+ *	NFS_PROTO(inode)->getattr(fattr);
+ */
+#define NFS_CALL(op, inode, args)	NFS_PROTO(inode)->op args
+
+/*
+ * Function vectors etc. for the NFS client
+ */
+extern struct nfs_rpc_ops	nfs_v2_clientops;
+extern struct nfs_rpc_ops	nfs_v3_clientops;
+extern struct rpc_version	nfs_version2;
+extern struct rpc_version	nfs_version3;
+extern struct rpc_program	nfs_program;
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/cache.h linux.18p17/include/linux/nfsd/cache.h
--- linux.vanilla/include/linux/nfsd/cache.h	Fri Apr 21 23:25:10 2000
+++ linux.18p17/include/linux/nfsd/cache.h	Sun Oct 15 23:46:26 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/const.h linux.18p17/include/linux/nfsd/const.h
--- linux.vanilla/include/linux/nfsd/const.h	Fri Apr 21 23:11:46 2000
+++ linux.18p17/include/linux/nfsd/const.h	Sun Oct 15 21:15:18 2000
@@ -1,20 +1,21 @@
 /*
- * 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
+#define NFS_MAXNAMLEN		255
 
 /*
  * Maximum protocol version supported by knfsd
@@ -26,14 +27,8 @@
  */
 #define NFSSVC_MAXBLKSIZE	8192
 
-#define NFS2_MAXPATHLEN		1024
-#define NFS2_MAXNAMLEN		255
-#define NFS2_FHSIZE		NFS_FHSIZE
 #define NFS2_COOKIESIZE		4
 
-#define NFS3_MAXPATHLEN		PATH_MAX
-#define NFS3_MAXNAMLEN		NAME_MAX
-#define NFS3_FHSIZE		NFS_FHSIZE
 #define NFS3_COOKIEVERFSIZE	8
 #define NFS3_CREATEVERFSIZE	8
 #define NFS3_WRITEVERFSIZE	8
@@ -44,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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/export.h linux.18p17/include/linux/nfsd/export.h
--- linux.vanilla/include/linux/nfsd/export.h	Fri Apr 21 23:11:46 2000
+++ linux.18p17/include/linux/nfsd/export.h	Sun Oct 15 21:15:18 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/nfsd.h linux.18p17/include/linux/nfsd/nfsd.h
--- linux.vanilla/include/linux/nfsd/nfsd.h	Fri Apr 21 23:24:14 2000
+++ linux.18p17/include/linux/nfsd/nfsd.h	Sun Oct 15 23:45:24 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/nfsfh.h linux.18p17/include/linux/nfsd/nfsfh.h
--- linux.vanilla/include/linux/nfsd/nfsfh.h	Fri Apr 21 23:11:46 2000
+++ linux.18p17/include/linux/nfsd/nfsfh.h	Sun Oct 15 23:31:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/stats.h linux.18p17/include/linux/nfsd/stats.h
--- linux.vanilla/include/linux/nfsd/stats.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/nfsd/stats.h	Sun Oct 15 21:15:18 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/syscall.h linux.18p17/include/linux/nfsd/syscall.h
--- linux.vanilla/include/linux/nfsd/syscall.h	Fri Apr 21 23:11:46 2000
+++ linux.18p17/include/linux/nfsd/syscall.h	Sun Oct 15 23:31:07 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nfsd/xdr3.h linux.18p17/include/linux/nfsd/xdr3.h
--- linux.vanilla/include/linux/nfsd/xdr3.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/nfsd/xdr3.h	Sun Oct 15 23:46:29 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nls.h linux.18p17/include/linux/nls.h
--- linux.vanilla/include/linux/nls.h	Sat Sep  9 18:42:50 2000
+++ linux.18p17/include/linux/nls.h	Mon Oct  2 10:17:08 2000
@@ -6,7 +6,7 @@
 struct nls_table {
 	char *charset;
 	void (*uni2char) (unsigned char ch, unsigned char cl, unsigned char *out, int boundlen, int *outlen);
-	void (*char2uni) (unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2);
+	void (*char2uni) (const unsigned char *rawstring, int *offset, unsigned char *uni1, unsigned char *uni2);
 
 	void (*inc_use_count) (void);
 	void (*dec_use_count) (void);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/nubus.h linux.18p17/include/linux/nubus.h
--- linux.vanilla/include/linux/nubus.h	Fri Apr 21 12:47:00 2000
+++ linux.18p17/include/linux/nubus.h	Sat Oct 14 00:16:33 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/pagemap.h linux.18p17/include/linux/pagemap.h
--- linux.vanilla/include/linux/pagemap.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/pagemap.h	Sun Oct 15 23:29:13 2000
@@ -153,4 +153,9 @@
 extern void update_vm_cache_conditional(struct inode *, unsigned long, const char *, int, unsigned long);
 extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
 
+typedef int filler_t(void *, struct page*);
+
+extern struct page *read_cache_page(struct inode *, unsigned long,
+				filler_t *, void *);
+
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/pci.h linux.18p17/include/linux/pci.h
--- linux.vanilla/include/linux/pci.h	Sat Sep  9 18:42:51 2000
+++ linux.18p17/include/linux/pci.h	Tue Sep 26 17:08:54 2000
@@ -298,6 +298,7 @@
 #define PCI_DEVICE_ID_COMPAQ_NETEL100D	0xae40
 #define PCI_DEVICE_ID_COMPAQ_NETEL100PI	0xae43
 #define PCI_DEVICE_ID_COMPAQ_NETEL100I	0xb011
+#define PCI_DEVICE_ID_COMPAQ_CISS	0xb060
 #define PCI_DEVICE_ID_COMPAQ_THUNDER	0xf130
 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3B	0xf150
 
@@ -415,6 +416,7 @@
 #define PCI_DEVICE_ID_IBM_TR_WAKE	0x003e
 #define PCI_DEVICE_ID_IBM_MPIC		0x0046
 #define PCI_DEVICE_ID_IBM_3780IDSP	0x007d
+#define PCI_DEVICE_ID_IBM_SERVERAIDI960	0x01bd
 #define PCI_DEVICE_ID_IBM_MPIC_2	0xffff
 
 #define PCI_VENDOR_ID_WD		0x101c
@@ -579,6 +581,14 @@
 #define PCI_DEVICE_ID_APPLE_BANDIT	0x0001
 #define PCI_DEVICE_ID_APPLE_GC		0x0002
 #define PCI_DEVICE_ID_APPLE_HYDRA	0x000e
+ 
+#define PCI_VENDOR_ID_YAMAHA		0x1073
+#define PCI_DEVICE_ID_YAMAHA_724	0x0004
+#define PCI_DEVICE_ID_YAMAHA_724F	0x000d
+#define PCI_DEVICE_ID_YAMAHA_740	0x000a
+#define PCI_DEVICE_ID_YAMAHA_740C	0x000c
+#define PCI_DEVICE_ID_YAMAHA_744	0x0010
+#define PCI_DEVICE_ID_YAMAHA_754	0x0012
 
 #define PCI_VENDOR_ID_NEXGEN		0x1074
 #define PCI_DEVICE_ID_NEXGEN_82C501	0x4e78
@@ -749,6 +759,9 @@
 #define PCI_VENDOR_ID_INTERG		0x10ea
 #define PCI_DEVICE_ID_INTERG_1680	0x1680
 #define PCI_DEVICE_ID_INTERG_1682	0x1682
+#define PCI_DEVICE_ID_INTERG_2000	0x2000
+#define PCI_DEVICE_ID_INTERG_2010	0x2010
+#define PCI_DEVICE_ID_INTERG_5000	0x5000
 
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8029	0x8029
@@ -766,6 +779,7 @@
 #define PCI_DEVICE_ID_TTI_HPT343	0x0003
 
 #define PCI_VENDOR_ID_VIA		0x1106
+#define PCI_DEVICE_ID_VIA_8371_0        0x0391
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
 #define PCI_DEVICE_ID_VIA_82C586_1	0x0571
@@ -1054,11 +1068,16 @@
 #define PCI_DEVICE_ID_LAVA_PARALLEL	0x8000
 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_A	0x8002 /* The Lava Dual Parallel is */
 #define PCI_DEVICE_ID_LAVA_DUAL_PAR_B	0x8003 /* two PCI devices on a card */
+#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR	0x8800
 
 #define PCI_VENDOR_ID_TIMEDIA		0x1409
 #define PCI_DEVICE_ID_TIMEDIA_1889	0x7168
 #define PCI_DEVICE_ID_TIMEDIA_4008A	0x7268
 
+#define PCI_VENDOR_ID_OXSEMI		0x1415
+#define PCI_DEVICE_ID_OXSEMI_12PCI840	0x8403
+#define PCI_DEVICE_ID_OXSEMI_16PCI954PP	0x9513
+
 #define PCI_VENDOR_ID_AFAVLAB		0x14db
 #define PCI_DEVICE_ID_AFAVLAB_TK9902	0x2120
 
@@ -1357,6 +1376,7 @@
 struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
 struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from);
 struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
+unsigned long pci_resource_len (struct pci_dev *dev, int n_base);
 
 #define pci_present pcibios_present
 int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
@@ -1394,6 +1414,9 @@
 
 extern inline void pci_set_master(struct pci_dev *dev)
 { return; }
+
+extern inline unsigned long pci_resource_len (struct pci_dev *dev, int n_base) 
+{ return 0; }
 
 #endif /* !CONFIG_PCI */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/personality.h linux.18p17/include/linux/personality.h
--- linux.vanilla/include/linux/personality.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/personality.h	Fri Sep 15 23:32:01 2000
@@ -27,6 +27,7 @@
 #define PER_IRIX32              (0x0009 | STICKY_TIMEOUTS) /* IRIX5 32-bit     */
 #define PER_IRIXN32             (0x000a | STICKY_TIMEOUTS) /* IRIX6 new 32-bit */
 #define PER_IRIX64              (0x000b | STICKY_TIMEOUTS) /* IRIX6 64-bit     */
+#define PER_RISCOS		(0x000c)
 
 /* Prototype for an lcall7 syscall handler. */
 typedef void (*lcall7_func)(struct pt_regs *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/pm.h linux.18p17/include/linux/pm.h
--- linux.vanilla/include/linux/pm.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/pm.h	Mon Sep 11 19:58:51 2000
@@ -0,0 +1,88 @@
+#ifndef __LINUX_PM_H
+#define __LINUX_PM_H
+
+
+typedef int pm_request_t;
+
+struct pm_dev {
+	void *data;
+	struct pm_dev *next;
+	int (*event)(struct pm_dev *, pm_request_t, void *);
+};
+
+#define PM_SUSPEND	1
+#define PM_RESUME	2
+
+#ifdef CONFIG_APM
+
+#include <linux/apm_bios.h>
+
+#define PM_PCI_DEV	0
+#define PM_PCI_ID(a)	a
+
+static struct pm_dev *pm_dev = NULL;
+
+static int handle_apm_event(apm_event_t apm_event)
+{
+	struct pm_dev *list = pm_dev;
+	pm_request_t action;
+
+	switch (apm_event) {
+
+		case APM_SYS_SUSPEND:
+		case APM_CRITICAL_SUSPEND:
+		case APM_USER_SUSPEND:
+			action = PM_SUSPEND;
+			break;
+		case APM_NORMAL_RESUME:
+		case APM_CRITICAL_RESUME:
+		case APM_STANDBY_RESUME:
+			action = PM_RESUME;
+			break;
+		default:
+			return 0;
+	}
+
+	while (list) {
+		list->event(list, action, list->data);
+		list = list->next;
+	}
+
+	return 0;
+}
+
+static struct pm_dev *pm_register(int a, void *b, int (*event)(struct pm_dev *, pm_request_t, void *))
+{
+	struct pm_dev *list;
+	
+	list = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);
+	list->event = event;
+	list->next = pm_dev;
+	pm_dev = list;
+
+	apm_register_callback(handle_apm_event);
+
+	return list;
+}
+
+static void pm_unregister_all(int (*event)(struct pm_dev *, pm_request_t, void *))
+{
+	struct pm_dev *list;
+
+	while (pm_dev) {
+		list = pm_dev;
+		pm_dev = pm_dev->next;
+		kfree(list);
+	}
+
+	apm_unregister_callback(handle_apm_event);
+}
+
+#else
+
+#define pm_register(a,b,c)	0
+#define pm_unregister_all(a)	do {} while (0)
+
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/proc_fs.h linux.18p17/include/linux/proc_fs.h
--- linux.vanilla/include/linux/proc_fs.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/proc_fs.h	Sun Oct 15 23:29:13 2000
@@ -213,6 +213,7 @@
 	PROC_SCSI_I2O,
 	PROC_SCSI_3W_XXXX,
 	PROC_SCSI_USB_SCSI,
+	PROC_SCSI_CPQFCTS,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
@@ -436,6 +437,12 @@
 					 struct proc_dir_entry *parent);
 void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
 
+#define create_proc_info_entry(n, m, b, g) \
+	{ \
+		struct proc_dir_entry *r = create_proc_entry(n, m, b); \
+		if (r) r->get_info = g; \
+	}
+
 /*
  * proc_tty.c
  */
@@ -463,6 +470,13 @@
 	return NULL;
 }
 
+#define create_proc_info_entry(n, m, b, g) \
+	{ \
+		struct proc_dir_entry *r = create_proc_entry(n, m, b); \
+		if (r) r->get_info = g; \
+	}
+
+
 extern inline void remove_proc_entry(const char *name, struct proc_dir_entry *parent) {};
 
 extern inline void proc_tty_register_driver(struct tty_driver *driver) {};
@@ -471,4 +485,7 @@
 extern struct proc_dir_entry proc_root;
 
 #endif /* CONFIG_PROC_FS */
+
+#define proc_mkdir(buf, usbdir)	create_proc_entry(buf, S_IFDIR, usbdir)
+
 #endif /* _LINUX_PROC_FS_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sched.h linux.18p17/include/linux/sched.h
--- linux.vanilla/include/linux/sched.h	Sat Sep  9 18:42:51 2000
+++ linux.18p17/include/linux/sched.h	Sun Oct 15 23:29:13 2000
@@ -497,6 +497,13 @@
 #define wake_up(x)			__wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
 #define wake_up_interruptible(x)	__wake_up((x),TASK_INTERRUPTIBLE)
 
+#define __set_current_state(state_value)	do { current->state = state_value; } while (0)
+#ifdef __SMP__
+#define set_current_state(state_value)		do { __set_current_state(state_value); mb(); } while (0)
+#else
+#define set_current_state(state_value)		__set_current_state(state_value)
+#endif
+
 extern int in_group_p(gid_t grp);
 extern int in_egroup_p(gid_t grp);
 
@@ -687,6 +694,8 @@
 extern void exit_fs(struct task_struct *);
 extern void exit_files(struct task_struct *);
 extern void exit_sighand(struct task_struct *);
+
+extern void daemonize(void);
 
 extern int do_execve(char *, char **, char **, struct pt_regs *);
 extern int do_fork(unsigned long, unsigned long, struct pt_regs *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/slab.h linux.18p17/include/linux/slab.h
--- linux.vanilla/include/linux/slab.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/slab.h	Sun Oct 15 23:29:13 2000
@@ -51,6 +51,7 @@
 extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long,
 				       void (*)(void *, kmem_cache_t *, unsigned long),
 				       void (*)(void *, kmem_cache_t *, unsigned long));
+extern int kmem_cache_destroy(kmem_cache_t *);
 extern int kmem_cache_shrink(kmem_cache_t *);
 extern void *kmem_cache_alloc(kmem_cache_t *, int);
 extern void kmem_cache_free(kmem_cache_t *, void *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/smb.h linux.18p17/include/linux/smb.h
--- linux.vanilla/include/linux/smb.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/smb.h	Sat Sep  2 23:59:49 2000
@@ -62,6 +62,12 @@
 
 #ifdef __KERNEL__
 
+#define SMB_NLS_MAXNAMELEN 20
+struct smb_nls_codepage {
+	char local_name[SMB_NLS_MAXNAMELEN];
+	char remote_name[SMB_NLS_MAXNAMELEN];
+};
+
 #define SMB_MAXNAMELEN 255
 #define SMB_MAXPATHLEN 1024
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/smb_fs.h linux.18p17/include/linux/smb_fs.h
--- linux.vanilla/include/linux/smb_fs.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/smb_fs.h	Sun Oct 15 23:31:07 2000
@@ -70,14 +70,6 @@
 #define SMB_F_CACHEVALID	0x01	/* directory cache valid */
 #define SMB_F_LOCALWRITE	0x02	/* file modified locally */
 
-/*
- * Bug fix flags
- */
-#define SMB_FIX_WIN95	0x0001	/* Win 95 server */
-#define SMB_FIX_OLDATTR	0x0002	/* Use core getattr (Win 95 speedup) */
-#define SMB_FIX_DIRATTR	0x0004	/* Use find_first for getattr */
-
-
 /* NT1 protocol capability bits */
 #define SMB_CAP_RAW_MODE         0x0001
 #define SMB_CAP_MPX_MODE         0x0002
@@ -117,6 +109,7 @@
 extern int init_smb_fs(void);
 
 /* linux/fs/smbfs/proc.c */
+int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
 __u32 smb_len(unsigned char *);
 __u8 *smb_encode_smb_length(__u8 *, __u32);
 __u8 *smb_setup_header(struct smb_sb_info *, __u8, __u16, __u16);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/smb_fs_sb.h linux.18p17/include/linux/smb_fs_sb.h
--- linux.vanilla/include/linux/smb_fs_sb.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/smb_fs_sb.h	Sat Sep  2 23:59:49 2000
@@ -23,7 +23,7 @@
         enum smb_conn_state state;
 	struct file * sock_file;
 
-        struct smb_mount_data *mnt;
+        struct smb_mount_data_kernel *mnt;
         unsigned char *temp_buf;
 
 	/* Connections are counted. Each time a new socket arrives,
@@ -41,8 +41,20 @@
         unsigned short     rcls; /* The error codes we received */
         unsigned short     err;
 
-        /* We use our on data_ready callback, but need the original one */
+        /* We use our own data_ready callback, but need the original one */
         void *data_ready;
+
+	/* nls pointers for codepage conversions */
+	struct nls_table *remote_nls;
+	struct nls_table *local_nls;
+
+	/* utf8 can make strings longer so we can't do in-place conversion.
+	   This is a buffer for temporary stuff. We only need one so no need
+	   to put it on the stack. This points to temp_buf space. */
+	char *name_buf;
+
+	int (*convert)(char *, int, const char *, int,
+		       struct nls_table *, struct nls_table *);
 };
 
 #endif /* __KERNEL__ */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/smb_mount.h linux.18p17/include/linux/smb_mount.h
--- linux.vanilla/include/linux/smb_mount.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/smb_mount.h	Sun Sep  3 00:28:07 2000
@@ -11,7 +11,7 @@
 
 #include <linux/types.h>
 
-#define SMB_MOUNT_VERSION 6
+#define SMB_MOUNT_VERSION	6
 
 struct smb_mount_data {
 	int version;
@@ -21,5 +21,29 @@
 	__kernel_mode_t file_mode;
 	__kernel_mode_t dir_mode;
 };
+
+
+#ifdef __KERNEL__
+
+/* flags */
+#define SMB_MOUNT_WIN95		0x0001  /* Win 95 server */
+#define SMB_MOUNT_OLDATTR	0x0002  /* Use core getattr (Win 95 speedup) */
+#define SMB_MOUNT_DIRATTR	0x0004  /* Use find_first for getattr */
+#define SMB_MOUNT_CASE		0x0008  /* Be case sensitive */
+
+struct smb_mount_data_kernel {
+	int _version;
+	__kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */
+	__kernel_uid_t uid;
+	__kernel_gid_t gid;
+	__kernel_mode_t file_mode;
+	__kernel_mode_t dir_mode;
+
+	u32 flags;
+
+	struct smb_nls_codepage codepage;
+};
+
+#endif
 
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/smp.h linux.18p17/include/linux/smp.h
--- linux.vanilla/include/linux/smp.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/smp.h	Mon Sep 25 16:12:28 2000
@@ -80,7 +80,8 @@
 #define smp_threads_ready			1
 #define kernel_lock()
 #define cpu_logical_map(cpu)			0
-#define smp_call_function(func,info,retry,wait)
+#define smp_call_function(func,info,retry,wait)	({0;})
+#define cpu_online_map 1
 
 #endif
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sockios.h linux.18p17/include/linux/sockios.h
--- linux.vanilla/include/linux/sockios.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/sockios.h	Fri Sep 22 14:57:51 2000
@@ -68,6 +68,9 @@
 #define SIOCGIFTXQLEN	0x8942		/* Get the tx queue length	*/
 #define SIOCSIFTXQLEN	0x8943		/* Set the tx queue length 	*/
 
+#define SIOCGIFDIVERT	0x8944		/* Frame diversion support */
+#define SIOCSIFDIVERT	0x8945		/* Set frame diversion options */
+
 
 /* ARP cache control calls. */
 		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/spinlock.h linux.18p17/include/linux/spinlock.h
--- linux.vanilla/include/linux/spinlock.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/spinlock.h	Sun Oct  1 11:41:36 2000
@@ -0,0 +1,4 @@
+#ifndef __LINUX_SPINLOCK_H
+#define __LINUX_SPINLOCK_H
+#include <asm/spinlock.h>
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/auth.h linux.18p17/include/linux/sunrpc/auth.h
--- linux.vanilla/include/linux/sunrpc/auth.h	Fri Apr 21 23:24:09 2000
+++ linux.18p17/include/linux/sunrpc/auth.h	Sat Oct 14 00:36:07 2000
@@ -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 */
@@ -64,10 +65,10 @@
 	struct rpc_auth *	(*create)(struct rpc_clnt *);
 	void			(*destroy)(struct rpc_auth *);
 
-	struct rpc_cred *	(*crcreate)(struct rpc_task *);
+	struct rpc_cred *	(*crcreate)(int);
 	void			(*crdestroy)(struct rpc_cred *);
 
-	int			(*crmatch)(struct rpc_task *, struct rpc_cred*);
+	int			(*crmatch)(struct rpc_cred *, int);
 	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
@@ -83,10 +84,14 @@
 int			rpcauth_unregister(struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(unsigned int, struct rpc_clnt *);
 void			rpcauth_destroy(struct rpc_auth *);
-struct rpc_cred *	rpcauth_lookupcred(struct rpc_task *);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
+struct rpc_cred *	rpcauth_bindcred(struct rpc_task *);
 void			rpcauth_holdcred(struct rpc_task *);
-void			rpcauth_releasecred(struct rpc_task *);
-int			rpcauth_matchcred(struct rpc_task *, struct rpc_cred *);
+void			rpcauth_releasecred(struct rpc_auth *,
+					    struct rpc_cred *);
+void			rpcauth_unbindcred(struct rpc_task *);
+int			rpcauth_matchcred(struct rpc_auth *,
+					  struct rpc_cred *, int);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
 int			rpcauth_refreshcred(struct rpc_task *);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/clnt.h linux.18p17/include/linux/sunrpc/clnt.h
--- linux.vanilla/include/linux/sunrpc/clnt.h	Fri Apr 21 23:24:09 2000
+++ linux.18p17/include/linux/sunrpc/clnt.h	Sun Oct 15 23:45:20 2000
@@ -30,7 +30,7 @@
  * The high-level client handle
  */
 struct rpc_clnt {
-	unsigned int		cl_users;	/* number of references */
+	atomic_t		cl_users;	/* number of references */
 	struct rpc_xprt *	cl_xprt;	/* transport */
 	struct rpc_procinfo *	cl_procinfo;	/* procedure info */
 	u32			cl_maxproc;	/* max procedure number */
@@ -45,6 +45,7 @@
 				cl_chatty   : 1,/* be verbose */
 				cl_autobind : 1,/* use getport() */
 				cl_binding  : 1,/* doing a getport() */
+				cl_droppriv : 1,/* enable NFS suid hack */
 				cl_oneshot  : 1,/* dispose after use */
 				cl_dead     : 1;/* abandoned */
 	unsigned int		cl_flags;	/* misc client flags */
@@ -111,21 +112,23 @@
 void		rpc_getport(struct rpc_task *, struct rpc_clnt *);
 int		rpc_register(u32, u32, int, unsigned short, int *);
 
-int		rpc_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_call_async(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-void		rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags);
-int		rpc_do_call(struct rpc_clnt *clnt, u32 proc,
-				void *argp, void *resp, int flags,
-				rpc_action callback, void *clntdata);
+void		rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
+
+int		rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg,
+			       int flags, rpc_action callback, void *clntdata);
+int		rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg,
+			      int flags);
 void		rpc_restart_call(struct rpc_task *);
 void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
 
-#define rpc_call(clnt, proc, argp, resp, flags)	\
-		rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL)
+static __inline__
+int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
+{
+	struct rpc_message msg = { proc, argp, resp, NULL };
+	return rpc_call_sync(clnt, &msg, flags);
+}
+		
 
 extern __inline__ void
 rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/sched.h linux.18p17/include/linux/sunrpc/sched.h
--- linux.vanilla/include/linux/sunrpc/sched.h	Fri Apr 21 23:10:29 2000
+++ linux.18p17/include/linux/sunrpc/sched.h	Sun Oct  1 11:41:12 2000
@@ -11,6 +11,8 @@
 
 #include <linux/timer.h>
 #include <linux/tqueue.h>
+#include <asm/errno.h>
+#include <asm/spinlock.h>
 #include <linux/sunrpc/types.h>
 
 /*
@@ -20,6 +22,16 @@
 #undef  CONFIG_RPC_FASTSCHED
 
 /*
+ * This is the actual RPC procedure call info.
+ */
+struct rpc_message {
+	__u32			rpc_proc;	/* Procedure number */
+	void *			rpc_argp;	/* Arguments */
+	void *			rpc_resp;	/* Result */
+	struct rpc_cred *	rpc_cred;	/* Credentials */
+};
+
+/*
  * This is the RPC task struct
  */
 struct rpc_task {
@@ -30,19 +42,17 @@
 #endif
 	struct rpc_task *	tk_next_task;	/* global list of tasks */
 	struct rpc_task *	tk_prev_task;	/* global list of tasks */
+	struct rpc_task *	tk_parent;	/* parent task */
 	struct rpc_clnt *	tk_client;	/* RPC client */
 	struct rpc_rqst *	tk_rqstp;	/* RPC request */
-	struct rpc_cred *	tk_cred;	/* RPC credentials */
-	int			tk_status;	/* result of last operation */
+	volatile int		tk_status;	/* result of last operation */
 	struct rpc_wait_queue *	tk_rpcwait;	/* RPC wait queue we're on */
 
 	/*
 	 * RPC call state
 	 */
-	__u32			tk_proc;	/* procedure number */
+	struct rpc_message	tk_msg;		/* RPC call info */
 	__u32 *			tk_buffer;	/* XDR buffer */
-	void *			tk_argp;	/* argument storage */
-	void *			tk_resp;	/* result storage */
 	__u8			tk_garb_retry,
 				tk_cred_retry,
 				tk_suid_retry;
@@ -52,9 +62,11 @@
 	 * action	next procedure for async tasks
 	 * exit		exit async task and report to caller
 	 */
+	void			(*tk_timeout_fn)(struct rpc_task *);
 	void			(*tk_callback)(struct rpc_task *);
 	void			(*tk_action)(struct rpc_task *);
 	void			(*tk_exit)(struct rpc_task *);
+	void			(*tk_release)(struct rpc_task *);
 	void *			tk_calldata;
 
 	/*
@@ -65,9 +77,14 @@
 	struct timer_list	tk_timer;	/* kernel timer */
 	struct wait_queue *	tk_wait;	/* sync: sleep on this q */
 	unsigned long		tk_timeout;	/* timeout for rpc_sleep() */
-	unsigned short		tk_flags;	/* misc flags */
+	unsigned int		tk_flags;	/* misc flags */
+	unsigned int		tk_lock;	/* Task lock counter */
+	unsigned char		tk_active   : 1,/* Task has been activated */
+				tk_wakeup   : 1;/* Task waiting to wake up */
+	volatile unsigned char	tk_running  : 1,/* Task is running */
+				tk_sleeping : 1;/* Task is truly asleep */
 #ifdef RPC_DEBUG
-	unsigned short		tk_pid;		/* debugging aid */
+	unsigned int		tk_pid;		/* debugging aid */
 #endif
 };
 #define tk_auth			tk_client->cl_auth
@@ -78,27 +95,27 @@
 /*
  * RPC task flags
  */
-#define RPC_TASK_RUNNING	0x0001		/* is running */
-#define RPC_TASK_ASYNC		0x0002		/* is an async task */
-#define RPC_TASK_CALLBACK	0x0004		/* invoke callback */
-#define RPC_TASK_SWAPPER	0x0008		/* is swapping in/out */
-#define RPC_TASK_SETUID		0x0010		/* is setuid process */
-#define RPC_TASK_CHILD		0x0020		/* is child of other task */
-#define RPC_CALL_REALUID	0x0040		/* try using real uid */
-#define RPC_CALL_MAJORSEEN	0x0080		/* major timeout seen */
-#define RPC_TASK_ROOTCREDS	0x0100		/* force root creds */
-#define RPC_TASK_DYNAMIC	0x0200		/* task was kmalloc'ed */
-#define RPC_TASK_KILLED		0x0400		/* task was killed */
-#define RPC_TASK_NFSWRITE	0x1000		/* an NFS writeback */
+#define RPC_TASK_ASYNC		0x0001		/* is an async task */
+#define RPC_TASK_SWAPPER	0x0002		/* is swapping in/out */
+#define RPC_TASK_SETUID		0x0004		/* is setuid process */
+#define RPC_TASK_CHILD		0x0008		/* is child of other task */
+#define RPC_CALL_REALUID	0x0010		/* try using real uid */
+#define RPC_CALL_MAJORSEEN	0x0020		/* major timeout seen */
+#define RPC_TASK_ROOTCREDS	0x0040		/* force root creds */
+#define RPC_TASK_DYNAMIC	0x0080		/* task was kmalloc'ed */
+#define RPC_TASK_KILLED		0x0100		/* task was killed */
+#define RPC_TASK_PRIORITY	0x0200		/* resched when waking up */
 
-#define RPC_IS_RUNNING(t)	((t)->tk_flags & RPC_TASK_RUNNING)
 #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
 #define RPC_IS_SETUID(t)	((t)->tk_flags & RPC_TASK_SETUID)
 #define RPC_IS_CHILD(t)		((t)->tk_flags & RPC_TASK_CHILD)
 #define RPC_IS_SWAPPER(t)	((t)->tk_flags & RPC_TASK_SWAPPER)
-#define RPC_DO_CALLBACK(t)	((t)->tk_flags & RPC_TASK_CALLBACK)
 #define RPC_DO_ROOTOVERRIDE(t)	((t)->tk_flags & RPC_TASK_ROOTCREDS)
 #define RPC_ASSASSINATED(t)	((t)->tk_flags & RPC_TASK_KILLED)
+#define RPC_IS_RUNNING(t)	((t)->tk_running)
+#define RPC_IS_SLEEPING(t)	((t)->tk_sleeping)
+#define RPC_IS_ACTIVATED(t)	((t)->tk_active)
+#define RPC_DO_CALLBACK(t)	((t)->tk_callback != NULL)
 
 /*
  * RPC synchronization objects
@@ -110,6 +127,7 @@
 #endif
 };
 
+#define RPC_WAITQ_EMPTY(q)	((q)->task == NULL)
 #ifndef RPC_DEBUG
 # define RPC_INIT_WAITQ(name)	((struct rpc_wait_queue) { NULL })
 #else
@@ -125,51 +143,60 @@
 					rpc_action exitfunc, int flags);
 void		rpc_release_task(struct rpc_task *);
 void		rpc_killall_tasks(struct rpc_clnt *);
-void		rpc_execute(struct rpc_task *);
+int		rpc_execute(struct rpc_task *);
 void		rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
 					rpc_action action);
-int		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
+int		rpc_add_wait_queue(struct rpc_wait_queue *,
+				     struct rpc_task *);
 void		rpc_remove_wait_queue(struct rpc_task *);
 void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
 					rpc_action action, rpc_action timer);
-void		rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *,
-					unsigned char *,
-					rpc_action action, rpc_action timer);
+void		rpc_sleep_locked(struct rpc_wait_queue *, struct rpc_task *,
+				 rpc_action action, rpc_action timer);
+void		rpc_add_timer(struct rpc_task *task, rpc_action timer);
 void		rpc_wake_up_task(struct rpc_task *);
 void		rpc_wake_up(struct rpc_wait_queue *);
 struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
 void		rpc_wake_up_status(struct rpc_wait_queue *, int);
-void		rpc_add_timer(struct rpc_task *, rpc_action);
-void		rpc_del_timer(struct rpc_task *);
+int		__rpc_lock_task(struct rpc_task *);
+void		rpc_unlock_task(struct rpc_task *);
 void		rpc_delay(struct rpc_task *, unsigned long);
 void *		rpc_allocate(unsigned int flags, unsigned int);
 void		rpc_free(void *);
 int		rpciod_up(void);
 void		rpciod_down(void);
 void		rpciod_wake_up(void);
-void		rpciod_tcp_dispatcher(void);
 #ifdef RPC_DEBUG
 void		rpc_show_tasks(void);
 #endif
 
-extern __inline__ void *
+static __inline__ void *
 rpc_malloc(struct rpc_task *task, unsigned int size)
 {
 	return rpc_allocate(task->tk_flags, size);
 }
 
-extern __inline__ void
+static __inline__ void
 rpc_exit(struct rpc_task *task, int status)
 {
 	task->tk_status = status;
 	task->tk_action = NULL;
 }
 
+static __inline__ void
+rpc_kill(struct rpc_task *task, int status)
+{
+        rpc_exit(task, status);
+        rpc_wake_up_task(task);
+}
+
+extern spinlock_t rpc_queue_lock;
+
 #ifdef RPC_DEBUG
-extern __inline__ char *
+static __inline__ char *
 rpc_qname(struct rpc_wait_queue *q)
 {
-	return q->name? q->name : "unknown";
+	return q? (q->name? q->name : "unknown") : "none";
 }
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/svcsock.h linux.18p17/include/linux/sunrpc/svcsock.h
--- linux.vanilla/include/linux/sunrpc/svcsock.h	Fri Apr 21 23:24:12 2000
+++ linux.18p17/include/linux/sunrpc/svcsock.h	Sun Oct  1 11:57:53 2000
@@ -25,13 +25,13 @@
 
 	struct svc_serv *	sk_server;	/* service for this socket */
 	atomic_t		sk_inuse;	/* use count */
-	unsigned char		sk_busy;	/* enqueued/receiving */
-	unsigned char		sk_conn;	/* conn pending */
-	unsigned char		sk_close;	/* dead or dying */
-	int			sk_data;	/* data pending */
-	unsigned int		sk_temp : 1,	/* temp socket */
+	volatile int		sk_conn;	/* conn pending */
+	volatile int		sk_data;	/* data pending */
+	volatile unsigned char	sk_busy : 1,	/* enqueued/receiving */
+				sk_close: 1,	/* dead or dying */
 				sk_qued : 1,	/* on serv->sk_sockets */
 				sk_dead : 1;	/* socket closed */
+	unsigned char		sk_temp : 1;	/* temp socket */
 	int			(*sk_recvfrom)(struct svc_rqst *rqstp);
 	int			(*sk_sendto)(struct svc_rqst *rqstp);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/types.h linux.18p17/include/linux/sunrpc/types.h
--- linux.vanilla/include/linux/sunrpc/types.h	Fri Apr 21 23:10:29 2000
+++ linux.18p17/include/linux/sunrpc/types.h	Sun Oct  1 11:41:12 2000
@@ -58,6 +58,7 @@
 	}
 	if (*q == item)
 		*q = next;
+	item->prev = item->next = 0;
 }
 
 #define rpc_insert_list(q, i) \
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/xdr.h linux.18p17/include/linux/sunrpc/xdr.h
--- linux.vanilla/include/linux/sunrpc/xdr.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/sunrpc/xdr.h	Fri Sep 15 22:10:44 2000
@@ -1,7 +1,7 @@
 /*
  * include/linux/sunrpc/xdr.h
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef _SUNRPC_XDR_H_
@@ -59,13 +59,32 @@
 /*
  * Miscellaneous XDR helper functions
  */
-u32 *	xdr_encode_string(u32 *p, const char *s);
+u32 *	xdr_encode_string(u32 *p, const char *s, int len);
 u32 *	xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen);
 u32 *	xdr_encode_netobj(u32 *p, const struct xdr_netobj *);
 u32 *	xdr_decode_netobj(u32 *p, struct xdr_netobj *);
 u32 *	xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len);
 
 /*
+ * Decode 64bit quantities (NFSv3 support)
+ */
+static inline u32 *
+xdr_encode_hyper(u32 *p, __u64 val)
+{
+	*p++ = htonl(val >> 32);
+	*p++ = htonl(val & 0xFFFFFFFF);
+	return p;
+}
+
+static inline u32 *
+xdr_decode_hyper(u32 *p, __u64 *valp)
+{
+	*valp  = ((__u64) ntohl(*p++)) << 32;
+	*valp |= ntohl(*p++);
+	return p;
+}
+
+/*
  * Adjust iovec to reflect end of xdr'ed data (RPC client XDR)
  */
 static inline int
@@ -73,6 +92,9 @@
 {
 	return iov->iov_len = ((u8 *) p - (u8 *) iov->iov_base);
 }
+
+void xdr_shift_iovec(struct iovec *, int, size_t);
+void xdr_zero_iovec(struct iovec *, int, size_t);
 
 #endif /* __KERNEL__ */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sunrpc/xprt.h linux.18p17/include/linux/sunrpc/xprt.h
--- linux.vanilla/include/linux/sunrpc/xprt.h	Fri Apr 21 23:24:09 2000
+++ linux.18p17/include/linux/sunrpc/xprt.h	Sun Oct  1 11:57:50 2000
@@ -9,15 +9,17 @@
 #ifndef _LINUX_SUNRPC_XPRT_H
 #define _LINUX_SUNRPC_XPRT_H
 
+#include <linux/timer.h>
 #include <linux/uio.h>
 #include <linux/socket.h>
 #include <linux/in.h>
+#include <linux/list.h>
 #include <linux/sunrpc/sched.h>
 
 /*
  * Maximum number of iov's we use.
  */
-#define MAX_IOVEC	8
+#define MAX_IOVEC	10
 
 /*
  * The transport code maintains an estimate on the maximum number of out-
@@ -44,10 +46,10 @@
 #define RPC_MAXCWND		(RPC_MAXCONG * RPC_CWNDSCALE)
 #define RPC_INITCWND		RPC_CWNDSCALE
 #define RPCXPRT_CONGESTED(xprt) \
-	((xprt)->cong >= ((xprt)->nocong? RPC_MAXCWND : (xprt)->cwnd))
+	((xprt)->cong >= (xprt)->cwnd)
 
 /* Default timeout values */
-#define RPC_MAX_UDP_TIMEOUT	(6*HZ)
+#define RPC_MAX_UDP_TIMEOUT	(60*HZ)
 #define RPC_MAX_TCP_TIMEOUT	(600*HZ)
 
 /* RPC call and reply header size as number of 32bit words (verifier
@@ -96,8 +98,7 @@
 	struct rpc_task *	rq_task;	/* RPC task data */
 	__u32			rq_xid;		/* request XID */
 	struct rpc_rqst *	rq_next;	/* free list */
-	unsigned char		rq_gotit;	/* reply received */
-	unsigned char		rq_damaged;	/* being received */
+	volatile unsigned char	rq_received : 1;/* receive completed */
 
 	/*
 	 * For authentication (e.g. auth_des)
@@ -122,14 +123,6 @@
 #define rq_rlen			rq_rcv_buf.io_len
 
 struct rpc_xprt {
-	struct rpc_xprt *	link;		/* list of all clients */
-	struct rpc_xprt *	rx_pending;	/* receive pending list */
-	struct rpc_xprt *	tx_pending;	/* transmit pending list */
-	
-	int 			rx_pending_flag;/* are we on the rcv pending list ? */
-	int 			tx_pending_flag;/* are we on the xmit pending list ? */
-
-	struct file *		file;		/* VFS layer */
 	struct socket *		sock;		/* BSD socket layer */
 	struct sock *		inet;		/* INET layer */
 
@@ -147,61 +140,92 @@
 	struct rpc_wait_queue	reconn;		/* waiting for reconnect */
 	struct rpc_rqst *	free;		/* free slots */
 	struct rpc_rqst		slot[RPC_MAXREQS];
-	unsigned char		connected;	/* TCP: connected */
-	unsigned char		write_space;	/* TCP: can send */
-	unsigned int		shutdown   : 1,	/* being shut down */
-				nocong	   : 1,	/* no congestion control */
-				stream     : 1,	/* TCP */
-				tcp_more   : 1,	/* more record fragments */
-				connecting : 1;	/* being reconnected */
+	volatile unsigned char	connected   : 1,/* TCP: connected */
+				write_space : 1;/* TCP: can send */
+	unsigned char		nocong	    : 1,/* no congestion control */
+				stream      : 1,/* TCP */
+				shutdown    : 1,/* being shut down */
+				tcp_more    : 1,/* more record fragments */
+				connecting  : 1;/* being reconnected */
+
+	unsigned long		tcp_timeout;	/* TCP timeout window */
+	struct timer_list	tcp_timer;	/* TCP timeout */
 
 	/*
 	 * State of TCP reply receive stuff
 	 */
-	union {					/* record marker & XID */
-		u32		header[2];
-		u8 		data[8];
-	}			tcp_recm;
-	struct rpc_rqst *	tcp_rqstp;
-	struct iovec		tcp_iovec[MAX_IOVEC];
-	u32			tcp_total;	/* overall record length */
-	u32			tcp_reclen;	/* fragment length */
-	u32			tcp_offset;	/* fragment offset */
-	u32			tcp_copied;	/* copied to request */
+	u32			tcp_recm;	/* Fragment header */
+	u32			tcp_xid;	/* Current XID */
+	unsigned int		tcp_reclen,	/* fragment length */
+				tcp_offset,	/* fragment offset */
+				tcp_copied;	/* copied to request */
+	struct list_head	rx_pending;	/* receive pending list */
 
 	/*
-	 * TCP send stuff
+	 * Send stuff
 	 */
-	struct rpc_iov		snd_buf;	/* send buffer */
 	struct rpc_task *	snd_task;	/* Task blocked in send */
-	u32			snd_sent;	/* Bytes we have sent */
 
 
 	void			(*old_data_ready)(struct sock *, int);
 	void			(*old_state_change)(struct sock *);
 	void			(*old_write_space)(struct sock *);
+
+	struct wait_queue *	cong_wait;
 };
-#define tcp_reclen		tcp_recm.header[0]
-#define tcp_xid			tcp_recm.header[1]
 
 #ifdef __KERNEL__
 
-struct rpc_xprt *	xprt_create(struct file *socket,
-					struct sockaddr_in *addr,
-					struct rpc_timeout *toparms);
 struct rpc_xprt *	xprt_create_proto(int proto, struct sockaddr_in *addr,
 					struct rpc_timeout *toparms);
 int			xprt_destroy(struct rpc_xprt *);
+void			xprt_shutdown(struct rpc_xprt *);
 void			xprt_default_timeout(struct rpc_timeout *, int);
 void			xprt_set_timeout(struct rpc_timeout *, unsigned int,
 					unsigned long);
 
 int			xprt_reserve(struct rpc_task *);
+int			xprt_down_transmit(struct rpc_task *);
 void			xprt_transmit(struct rpc_task *);
+void			xprt_up_transmit(struct rpc_task *);
 void			xprt_receive(struct rpc_task *);
 int			xprt_adjust_timeout(struct rpc_timeout *);
 void			xprt_release(struct rpc_task *);
 void			xprt_reconnect(struct rpc_task *);
+void			__rpciod_tcp_dispatcher(void);
+
+extern struct list_head	rpc_xprt_pending;
+
+static inline
+int xprt_tcp_pending(void)
+{
+	return !list_empty(&rpc_xprt_pending);
+}
+
+static inline
+void rpciod_tcp_dispatcher(void)
+{
+	if (xprt_tcp_pending())
+		__rpciod_tcp_dispatcher();
+}
+
+static inline
+int xprt_connected(struct rpc_xprt *xprt)
+{
+	return xprt->connected;
+}
+
+static inline
+void xprt_set_connected(struct rpc_xprt *xprt)
+{
+	xprt->connected = 1;
+}
+
+static inline
+void xprt_clear_connected(struct rpc_xprt *xprt)
+{
+	xprt->connected = 0;
+}
 
 #endif /* __KERNEL__*/
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/sysctl.h linux.18p17/include/linux/sysctl.h
--- linux.vanilla/include/linux/sysctl.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/linux/sysctl.h	Sat Oct 14 00:51:40 2000
@@ -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 */
 };
 
 
@@ -166,7 +167,8 @@
 	NET_CORE_FASTROUTE=7,
 	NET_CORE_MSG_COST=8,
 	NET_CORE_MSG_BURST=9,
-	NET_CORE_OPTMEM_MAX=10
+	NET_CORE_OPTMEM_MAX=10,
+	NET_CORE_DIVERT_VERSION=12
 };
 
 /* /proc/sys/net/ethernet */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/tasks.h linux.18p17/include/linux/tasks.h
--- linux.vanilla/include/linux/tasks.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/linux/tasks.h	Fri Sep 15 12:50:44 2000
@@ -11,7 +11,7 @@
 #define NR_CPUS 1
 #endif
 
-#define NR_TASKS	512	/* On x86 Max 4092, or 4090 w/APM configured. */
+#define NR_TASKS	512	/* On x86 Max about 4000 */
 
 #define MAX_TASKS_PER_USER (NR_TASKS/2)
 #define MIN_TASKS_LEFT_FOR_ROOT 4
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usb.h linux.18p17/include/linux/usb.h
--- linux.vanilla/include/linux/usb.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/usb.h	Thu Oct 19 01:41:48 2000
@@ -0,0 +1,792 @@
+#ifndef __LINUX_USB_H
+#define __LINUX_USB_H
+
+/* USB constants */
+
+/*
+ * Device and/or Interface Class codes
+ */
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_AUDIO			1
+#define USB_CLASS_COMM			2
+#define USB_CLASS_HID			3
+#define USB_CLASS_PHYSICAL		5
+#define USB_CLASS_PRINTER		7
+#define USB_CLASS_MASS_STORAGE		8
+#define USB_CLASS_HUB			9
+#define USB_CLASS_DATA			10
+#define USB_CLASS_APP_SPEC		0xfe
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+/*
+ * USB types
+ */
+#define USB_TYPE_STANDARD		(0x00 << 5)
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+#define USB_TYPE_RESERVED		(0x03 << 5)
+
+/*
+ * USB recipients
+ */
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE		0x01
+#define USB_RECIP_ENDPOINT		0x02
+#define USB_RECIP_OTHER			0x03
+
+/*
+ * USB directions
+ */
+#define USB_DIR_OUT			0
+#define USB_DIR_IN			0x80
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE			0x01
+#define USB_DT_CONFIG			0x02
+#define USB_DT_STRING			0x03
+#define USB_DT_INTERFACE		0x04
+#define USB_DT_ENDPOINT			0x05
+
+#define USB_DT_HID			(USB_TYPE_CLASS | 0x01)
+#define USB_DT_REPORT			(USB_TYPE_CLASS | 0x02)
+#define USB_DT_PHYSICAL			(USB_TYPE_CLASS | 0x03)
+#define USB_DT_HUB			(USB_TYPE_CLASS | 0x09)
+
+/*
+ * Descriptor sizes per descriptor type
+ */
+#define USB_DT_DEVICE_SIZE		18
+#define USB_DT_CONFIG_SIZE		9
+#define USB_DT_INTERFACE_SIZE		9
+#define USB_DT_ENDPOINT_SIZE		7
+#define USB_DT_ENDPOINT_AUDIO_SIZE	9	/* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE		7
+#define USB_DT_HID_SIZE			9
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK	0x0f	/* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK		0x80
+
+#define USB_ENDPOINT_XFERTYPE_MASK	0x03	/* in bmAttributes */
+#define USB_ENDPOINT_XFER_CONTROL	0
+#define USB_ENDPOINT_XFER_ISOC		1
+#define USB_ENDPOINT_XFER_BULK		2
+#define USB_ENDPOINT_XFER_INT		3
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0                        0xf0
+#define USB_PID_OUT                            0xe1
+#define USB_PID_ACK                            0xd2
+#define USB_PID_DATA0                          0xc3
+#define USB_PID_PING                           0xb4	/* USB 2.0 */
+#define USB_PID_SOF                            0xa5
+#define USB_PID_NYET                           0x96	/* USB 2.0 */
+#define USB_PID_DATA2                          0x87	/* USB 2.0 */
+#define USB_PID_SPLIT                          0x78	/* USB 2.0 */
+#define USB_PID_IN                             0x69
+#define USB_PID_NAK                            0x5a
+#define USB_PID_DATA1                          0x4b
+#define USB_PID_PREAMBLE                       0x3c	/* Token mode */
+#define USB_PID_ERR                            0x3c	/* USB 2.0: handshake mode */
+#define USB_PID_SETUP                          0x2d
+#define USB_PID_STALL                          0x1e
+#define USB_PID_MDATA                          0x0f	/* USB 2.0 */
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS		0x00
+#define USB_REQ_CLEAR_FEATURE		0x01
+#define USB_REQ_SET_FEATURE		0x03
+#define USB_REQ_SET_ADDRESS		0x05
+#define USB_REQ_GET_DESCRIPTOR		0x06
+#define USB_REQ_SET_DESCRIPTOR		0x07
+#define USB_REQ_GET_CONFIGURATION	0x08
+#define USB_REQ_SET_CONFIGURATION	0x09
+#define USB_REQ_GET_INTERFACE		0x0A
+#define USB_REQ_SET_INTERFACE		0x0B
+#define USB_REQ_SYNCH_FRAME		0x0C
+
+/*
+ * HID requests
+ */
+#define USB_REQ_GET_REPORT		0x01
+#define USB_REQ_GET_IDLE		0x02
+#define USB_REQ_GET_PROTOCOL		0x03
+#define USB_REQ_SET_REPORT		0x09
+#define USB_REQ_SET_IDLE		0x0A
+#define USB_REQ_SET_PROTOCOL		0x0B
+
+
+#ifdef __KERNEL__
+
+#include <linux/kcomp.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>	/* for in_interrupt() */
+#include <linux/config.h>
+#include <linux/list.h>
+
+#define USB_MAJOR 180
+
+static __inline__ void wait_ms(unsigned int ms)
+{
+	if(!in_interrupt()) {
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + ms * HZ / 1000);
+	}
+	else
+		mdelay(ms);
+}
+
+typedef struct {
+	__u8 requesttype;
+	__u8 request;
+	__u16 value;
+	__u16 index;
+	__u16 length;
+} devrequest __attribute__ ((packed));
+
+/*
+ * USB-status codes:
+ * USB_ST* maps to -E* and should go away in the future
+ */
+
+#define USB_ST_NOERROR		0
+#define USB_ST_CRC		(-EILSEQ)
+#define USB_ST_BITSTUFF		(-EPROTO)
+#define USB_ST_NORESPONSE	(-ETIMEDOUT)			/* device not responding/handshaking */
+#define USB_ST_DATAOVERRUN	(-EOVERFLOW)
+#define USB_ST_DATAUNDERRUN	(-EREMOTEIO)
+#define USB_ST_BUFFEROVERRUN	(-ECOMM)
+#define USB_ST_BUFFERUNDERRUN	(-ENOSR)
+#define USB_ST_INTERNALERROR	(-EPROTO) 			/* unknown error */
+#define USB_ST_SHORT_PACKET    	(-EREMOTEIO)
+#define USB_ST_PARTIAL_ERROR  	(-EXDEV)			/* ISO transfer only partially completed */
+#define USB_ST_URB_KILLED     	(-ENOENT)			/* URB canceled by user */
+#define USB_ST_URB_PENDING       (-EINPROGRESS)
+#define USB_ST_REMOVED		(-ENODEV) 			/* device not existing or removed */
+#define USB_ST_TIMEOUT		(-ETIMEDOUT)			/* communication timed out, also in urb->status**/
+#define USB_ST_NOTSUPPORTED	(-ENOSYS)			
+#define USB_ST_BANDWIDTH_ERROR	(-ENOSPC)			/* too much bandwidth used */
+#define USB_ST_URB_INVALID_ERROR  (-EINVAL)			/* invalid value/transfer type */
+#define USB_ST_URB_REQUEST_ERROR  (-ENXIO)			/* invalid endpoint */
+#define USB_ST_STALL		(-EPIPE) 			/* pipe stalled, also in urb->status*/
+
+/*
+ * USB device number allocation bitmap. There's one bitmap
+ * per USB tree.
+ */
+struct usb_devmap {
+	unsigned long devicemap[128 / (8*sizeof(unsigned long))];
+};
+
+#define USB_MAXBUS		64
+
+struct usb_busmap {
+	unsigned long busmap[USB_MAXBUS / (8*sizeof(unsigned long))];
+};
+
+/*
+ * This is a USB device descriptor.
+ *
+ * USB device information
+ */
+
+/* Everything but the endpoint maximums are aribtrary */
+#define USB_MAXCONFIG		8
+#define USB_ALTSETTINGALLOC     4
+#define USB_MAXALTSETTING	128  /* Hard limit */
+#define USB_MAXINTERFACES	32
+#define USB_MAXENDPOINTS	32
+
+/* All standard descriptors have these 2 fields in common */
+struct usb_descriptor_header {
+	__u8  bLength;
+	__u8  bDescriptorType;
+} __attribute__ ((packed));
+
+/* Device descriptor */
+struct usb_device_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdUSB;
+	__u8  bDeviceClass;
+	__u8  bDeviceSubClass;
+	__u8  bDeviceProtocol;
+	__u8  bMaxPacketSize0;
+	__u16 idVendor;
+	__u16 idProduct;
+	__u16 bcdDevice;
+	__u8  iManufacturer;
+	__u8  iProduct;
+	__u8  iSerialNumber;
+	__u8  bNumConfigurations;
+} __attribute__ ((packed));
+
+/* Endpoint descriptor */
+struct usb_endpoint_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bEndpointAddress;
+	__u8  bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8  bInterval;
+	__u8  bRefresh;
+	__u8  bSynchAddress;
+
+   	unsigned char *extra;   /* Extra descriptors */
+	int extralen;
+} __attribute__ ((packed));
+
+/* Interface descriptor */
+struct usb_interface_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bInterfaceNumber;
+	__u8  bAlternateSetting;
+	__u8  bNumEndpoints;
+	__u8  bInterfaceClass;
+	__u8  bInterfaceSubClass;
+	__u8  bInterfaceProtocol;
+	__u8  iInterface;
+
+  	struct usb_endpoint_descriptor *endpoint;
+
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
+} __attribute__ ((packed));
+
+struct usb_interface {
+	struct usb_interface_descriptor *altsetting;
+
+	int act_altsetting;		/* active alternate setting */
+	int num_altsetting;		/* number of alternate settings */
+	int max_altsetting;             /* total memory allocated */
+ 
+	struct usb_driver *driver;	/* driver */
+	void *private_data;
+};
+
+/* Configuration descriptor information.. */
+struct usb_config_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 wTotalLength;
+	__u8  bNumInterfaces;
+	__u8  bConfigurationValue;
+	__u8  iConfiguration;
+	__u8  bmAttributes;
+	__u8  MaxPower;
+
+	struct usb_interface *interface;
+
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
+} __attribute__ ((packed));
+
+/* String descriptor */
+struct usb_string_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 wData[1];
+} __attribute__ ((packed));
+
+struct usb_device;
+
+struct usb_driver {
+	const char *name;
+
+	void * (*probe)(struct usb_device *, unsigned int);
+	void (*disconnect)(struct usb_device *, void *);
+
+	struct list_head driver_list;
+
+	struct file_operations *fops;
+	int minor;
+
+	struct semaphore serialize;
+
+	int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);
+};
+
+/*
+ * Pointer to a device endpoint interrupt function -greg
+ *   Parameters:
+ *     int status - This needs to be defined.  Right now each HCD
+ *         passes different transfer status bits back.  Don't use it
+ *         until we come up with a common meaning.
+ *     void *buffer - This is a pointer to the data used in this
+ *         USB transfer.
+ *     int length - This is the number of bytes transferred in or out
+ *         of the buffer by this transfer.  (-1 = unknown/unsupported)
+ *     void *dev_id - This is a user defined pointer set when the IRQ
+ *         is requested that is passed back.
+ *
+ *   Special Cases:
+ *     if (status == USB_ST_REMOVED), don't trust buffer or len.
+ */
+typedef int (*usb_device_irq)(int, void *, int, void *);
+
+/*----------------------------------------------------------------------------* 
+ * New USB Structures                                                         *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * urb->transfer_flags:
+ */
+#define USB_DISABLE_SPD         0x0001
+#define USB_ISO_ASAP            0x0002
+#define USB_URB_EARLY_COMPLETE  0x0004
+#define USB_ASYNC_UNLINK        0x0008
+#define USB_QUEUE_BULK          0x0010
+#define USB_TIMEOUT_KILLED	0x1000	// only set by HCD!
+
+typedef struct
+{
+	unsigned int offset;
+	unsigned int length;		// expected length
+	unsigned int actual_length;
+	unsigned int status;
+} iso_packet_descriptor_t, *piso_packet_descriptor_t;
+
+struct urb;
+typedef void (*usb_complete_t)(struct urb *);
+
+typedef struct urb
+{
+	spinlock_t lock;		// lock for the URB
+	void *hcpriv;			// private data for host controller
+	struct list_head urb_list;	// list pointer to all active urbs 
+	struct urb *next;		// pointer to next URB	
+	struct usb_device *dev;		// pointer to associated USB device
+	unsigned int pipe;		// pipe information
+	int status;			// returned status
+	unsigned int transfer_flags;	// USB_DISABLE_SPD | USB_ISO_ASAP | USB_URB_EARLY_COMPLETE
+	void *transfer_buffer;		// associated data buffer
+	int transfer_buffer_length;	// data buffer length
+	int actual_length;              // actual data buffer length	
+	int bandwidth;			// bandwidth for this transfer request (INT or ISO)
+	unsigned char *setup_packet;	// setup packet (control only)
+	//
+	int start_frame;		// start frame (iso/irq only)
+	int number_of_packets;		// number of packets in this request (iso)
+	int interval;                   // polling interval (irq only)
+	int error_count;		// number of errors in this transfer (iso only)
+	int timeout;			// timeout (in jiffies)
+	//
+	void *context;			// context for completion routine
+	usb_complete_t complete;	// pointer to completion routine
+	//
+	iso_packet_descriptor_t	 iso_frame_desc[0];
+} urb_t, *purb_t;
+
+#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->setup_packet=c;\
+	(a)->transfer_buffer=d;\
+	(a)->transfer_buffer_length=e;\
+	(a)->complete=f;\
+	(a)->context=g;\
+    } while (0)
+
+#define FILL_BULK_URB(a,aa,b,c,d,e,f) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->transfer_buffer=c;\
+	(a)->transfer_buffer_length=d;\
+	(a)->complete=e;\
+	(a)->context=f;\
+    } while (0)
+    
+#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->transfer_buffer=c;\
+	(a)->transfer_buffer_length=d;\
+	(a)->complete=e;\
+	(a)->context=f;\
+	(a)->interval=g;\
+	(a)->start_frame=-1;\
+    } while (0)
+
+#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->setup_packet=c;\
+	(a)->transfer_buffer=d;\
+	(a)->transfer_buffer_length=e;\
+	(a)->complete=f;\
+	(a)->context=g;\
+	(a)->timeout=h;\
+    } while (0)
+
+#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+    do {\
+	spin_lock_init(&(a)->lock);\
+	(a)->dev=aa;\
+	(a)->pipe=b;\
+	(a)->transfer_buffer=c;\
+	(a)->transfer_buffer_length=d;\
+	(a)->complete=e;\
+	(a)->context=f;\
+	(a)->timeout=g;\
+    } while (0)
+    
+purb_t usb_alloc_urb(int iso_packets);
+void usb_free_urb (purb_t purb);
+int usb_submit_urb(purb_t purb);
+int usb_unlink_urb(purb_t purb);
+int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd,  void *data, int len, int timeout);
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
+
+/*-------------------------------------------------------------------*
+ *                         COMPATIBILITY STUFF                       *
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+  wait_queue_head_t *wakeup;
+
+  usb_device_irq handler;
+  void* stuff;
+  /* more to follow */
+} api_wrapper_data;
+
+struct irq_wrapper_data {
+	void *context;
+	usb_device_irq handler;
+};
+
+/* -------------------------------------------------------------------------- */
+
+struct usb_operations {
+	int (*allocate)(struct usb_device *);
+	int (*deallocate)(struct usb_device *);
+	int (*get_frame_number) (struct usb_device *usb_dev);
+	int (*submit_urb) (struct urb* purb);
+	int (*unlink_urb) (struct urb* purb);
+};
+
+/*
+ * Allocated per bus we have
+ */
+struct usb_bus {
+	int busnum;			/* Bus number (in order of reg) */
+
+	struct usb_devmap devmap;       /* Device map */
+	struct usb_operations *op;      /* Operations (specific to the HC) */
+	struct usb_device *root_hub;    /* Root hub */
+	struct list_head bus_list;
+	void *hcpriv;                   /* Host Controller private data */
+
+	int bandwidth_allocated;	/* on this Host Controller; */
+					  /* applies to Int. and Isoc. pipes; */
+					  /* measured in microseconds/frame; */
+					  /* range is 0..900, where 900 = */
+					  /* 90% of a 1-millisecond frame */
+	int bandwidth_int_reqs;		/* number of Interrupt requesters */
+	int bandwidth_isoc_reqs;	/* number of Isoc. requesters */
+
+	/* usbdevfs inode list */
+	struct list_head inodes;
+};
+
+#define USB_MAXCHILDREN (8)	/* This is arbitrary */
+
+struct usb_device {
+	int devnum;			/* Device number on USB bus */
+	int slow;			/* Slow device? */
+
+	atomic_t refcnt;		/* Reference count */
+
+	unsigned int toggle[2];		/* one bit for each endpoint ([0] = IN, [1] = OUT) */
+	unsigned int halted[2];		/* endpoint halts; one bit per endpoint # & direction; */
+					/* [0] = IN, [1] = OUT */
+	int epmaxpacketin[16];		/* INput endpoint specific maximums */
+	int epmaxpacketout[16];		/* OUTput endpoint specific maximums */
+
+	struct usb_device *parent;
+	struct usb_bus *bus;		/* Bus we're part of */
+
+	struct usb_device_descriptor descriptor;/* Descriptor */
+	struct usb_config_descriptor *config;	/* All of the configs */
+	struct usb_config_descriptor *actconfig;/* the active configuration */
+
+	char **rawdescriptors;		/* Raw descriptors for each config */
+
+	int have_langid;		/* whether string_langid is valid yet */
+	int string_langid;		/* language ID for strings */
+  
+	void *hcpriv;			/* Host Controller private data */
+	
+        /* usbdevfs inode list */
+	struct list_head inodes;
+	struct list_head filelist;
+
+	/*
+	 * Child devices - these can be either new devices
+	 * (if this is a hub device), or different instances
+	 * of this same device.
+	 *
+	 * Each instance needs its own set of data structures.
+	 */
+
+	int maxchild;			/* Number of ports if hub */
+	struct usb_device *children[USB_MAXCHILDREN];
+};
+
+extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum);
+
+extern int usb_register(struct usb_driver *);
+extern void usb_deregister(struct usb_driver *);
+extern void usb_scan_devices(void);
+
+/* used these for multi-interface device registration */
+extern void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv);
+extern int usb_interface_claimed(struct usb_interface *iface);
+extern void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface);
+
+extern struct usb_bus *usb_alloc_bus(struct usb_operations *);
+extern void usb_free_bus(struct usb_bus *);
+extern void usb_register_bus(struct usb_bus *);
+extern void usb_deregister_bus(struct usb_bus *);
+
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *);
+extern void usb_free_dev(struct usb_device *);
+extern void usb_inc_dev_use(struct usb_device *);
+#define usb_dec_dev_use usb_free_dev
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc);
+extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc);
+
+extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
+
+extern void usb_init_root_hub(struct usb_device *dev);
+extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len);
+extern void usb_connect(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+
+extern void usb_destroy_configuration(struct usb_device *dev);
+
+int usb_get_current_frame_number (struct usb_device *usb_dev);
+
+/*
+ * Calling this entity a "pipe" is glorifying it. A USB pipe
+ * is something embarrassingly simple: it basically consists
+ * of the following information:
+ *  - device number (7 bits)
+ *  - endpoint number (4 bits)
+ *  - current Data0/1 state (1 bit)
+ *  - direction (1 bit)
+ *  - speed (1 bit)
+ *  - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]
+ *  - pipe type (2 bits: control, interrupt, bulk, isochronous)
+ *
+ * That's 18 bits. Really. Nothing more. And the USB people have
+ * documented these eighteen bits as some kind of glorious
+ * virtual data structure.
+ *
+ * Let's not fall in that trap. We'll just encode it as a simple
+ * unsigned int. The encoding is:
+ *
+ *  - max size:		bits 0-1	(00 = 8, 01 = 16, 10 = 32, 11 = 64) [Historical; now gone.]
+ *  - direction:	bit 7		(0 = Host-to-Device [Out], 1 = Device-to-Host [In])
+ *  - device:		bits 8-14
+ *  - endpoint:		bits 15-18
+ *  - Data0/1:		bit 19
+ *  - speed:		bit 26		(0 = Full, 1 = Low Speed)
+ *  - pipe type:	bits 30-31	(00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
+ *
+ * Why? Because it's arbitrary, and whatever encoding we select is really
+ * up to us. This one happens to share a lot of bit positions with the UHCI
+ * specification, so that much of the uhci driver can just mask the bits
+ * appropriately.
+ */
+
+#define PIPE_ISOCHRONOUS		0
+#define PIPE_INTERRUPT			1
+#define PIPE_CONTROL			2
+#define PIPE_BULK			3
+
+#define usb_maxpacket(dev, pipe, out)	(out \
+				? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \
+				: (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] )
+#define usb_packetid(pipe)	(((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT)
+
+#define usb_pipeout(pipe)	((((pipe) >> 7) & 1) ^ 1)
+#define usb_pipein(pipe)	(((pipe) >> 7) & 1)
+#define usb_pipedevice(pipe)	(((pipe) >> 8) & 0x7f)
+#define usb_pipe_endpdev(pipe)	(((pipe) >> 8) & 0x7ff)
+#define usb_pipeendpoint(pipe)	(((pipe) >> 15) & 0xf)
+#define usb_pipedata(pipe)	(((pipe) >> 19) & 1)
+#define usb_pipeslow(pipe)	(((pipe) >> 26) & 1)
+#define usb_pipetype(pipe)	(((pipe) >> 30) & 3)
+#define usb_pipeisoc(pipe)	(usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)
+#define usb_pipeint(pipe)	(usb_pipetype((pipe)) == PIPE_INTERRUPT)
+#define usb_pipecontrol(pipe)	(usb_pipetype((pipe)) == PIPE_CONTROL)
+#define usb_pipebulk(pipe)	(usb_pipetype((pipe)) == PIPE_BULK)
+
+#define PIPE_DEVEP_MASK		0x0007ff00
+
+/* The D0/D1 toggle bits */
+#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1)
+#define	usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << ep))
+#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep))
+
+/* Endpoint halt control/status */
+#define usb_endpoint_out(ep_dir)	(((ep_dir >> 7) & 1) ^ 1)
+#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
+#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))
+#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))
+
+static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
+{
+	return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26);
+}
+
+static inline unsigned int __default_pipe(struct usb_device *dev)
+{
+	return (dev->slow << 26);
+}
+
+/* Create various pipes... */
+#define usb_sndctrlpipe(dev,endpoint)	((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvctrlpipe(dev,endpoint)	((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndisocpipe(dev,endpoint)	((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvisocpipe(dev,endpoint)	((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndbulkpipe(dev,endpoint)	((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvbulkpipe(dev,endpoint)	((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndintpipe(dev,endpoint)	((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvintpipe(dev,endpoint)	((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_snddefctrl(dev)		((PIPE_CONTROL << 30) | __default_pipe(dev))
+#define usb_rcvdefctrl(dev)		((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN)
+
+/*
+ * Send and receive control messages..
+ */
+int usb_new_device(struct usb_device *dev);
+int usb_reset_device(struct usb_device *dev);
+int usb_set_address(struct usb_device *dev);
+int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,
+	unsigned char descindex, void *buf, int size);
+int usb_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char desctype,
+	unsigned char descindex, void *buf, int size);
+int usb_get_device_descriptor(struct usb_device *dev);
+int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr);
+int usb_get_status(struct usb_device *dev, int type, int target, void *data);
+int usb_get_configuration(struct usb_device *dev);
+int usb_get_protocol(struct usb_device *dev, int ifnum);
+int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol);
+int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
+int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id);
+int usb_set_configuration(struct usb_device *dev, int configuration);
+int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type,
+	unsigned char id, void *buf, int size);
+int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type,
+	unsigned char id, void *buf, int size);
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
+int usb_clear_halt(struct usb_device *dev, int pipe);
+void usb_set_maxpacket(struct usb_device *dev);
+
+#define usb_get_extra_descriptor(ifpoint,type,ptr)\
+	__usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,type,(void**)ptr)
+
+/*
+ * Some USB bandwidth allocation constants.
+ */
+#define BW_HOST_DELAY	1000L		/* nanoseconds */
+#define BW_HUB_LS_SETUP	333L		/* nanoseconds */
+                        /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS         12000L		/* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC	(90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_USECS	1000L
+#define FRAME_TIME_MAX_USECS_ALLOC	(90L * FRAME_TIME_USECS / 100L)
+
+#define BitTime(bytecount)  (7 * 8 * bytecount / 6)  /* with integer truncation */
+		/* Trying not to use worst-case bit-stuffing
+                   of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+		/* bytecount = data payload byte count */
+
+#define NS_TO_US(ns)	((ns + 500L) / 1000L)
+			/* convert & round nanoseconds to microseconds */
+
+/*
+ * Debugging helpers..
+ */
+void usb_show_device_descriptor(struct usb_device_descriptor *);
+void usb_show_config_descriptor(struct usb_config_descriptor *);
+void usb_show_interface_descriptor(struct usb_interface_descriptor *);
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
+void usb_show_device(struct usb_device *);
+void usb_show_string(struct usb_device *dev, char *id, int index);
+
+#ifdef DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg)
+
+
+/*
+ * bus and driver list
+ */
+
+extern struct list_head usb_driver_list;
+extern struct list_head usb_bus_list;
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbdevfs_add_bus(struct usb_bus *bus);
+extern void usbdevfs_remove_bus(struct usb_bus *bus);
+extern void usbdevfs_add_device(struct usb_device *dev);
+extern void usbdevfs_remove_device(struct usb_device *dev);
+
+extern int usbdevfs_init(void);
+extern void usbdevfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+extern inline void usbdevfs_add_bus(struct usb_bus *bus) {}
+extern inline void usbdevfs_remove_bus(struct usb_bus *bus) {}
+extern inline void usbdevfs_add_device(struct usb_device *dev) {}
+extern inline void usbdevfs_remove_device(struct usb_device *dev) {}
+
+extern inline int usbdevfs_init(void) { return 0; }
+extern inline void usbdevfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
+
+#endif  /* __KERNEL__ */
+
+#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usbdev_fs_i.h linux.18p17/include/linux/usbdev_fs_i.h
--- linux.vanilla/include/linux/usbdev_fs_i.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/usbdev_fs_i.h	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,11 @@
+struct usb_device;
+struct usb_bus;
+
+struct usbdev_inode_info {
+	struct list_head dlist;
+	struct list_head slist;
+	union {
+		struct usb_device *dev;
+		struct usb_bus *bus;
+	} p;
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usbdev_fs_sb.h linux.18p17/include/linux/usbdev_fs_sb.h
--- linux.vanilla/include/linux/usbdev_fs_sb.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/usbdev_fs_sb.h	Fri Sep  1 13:51:30 2000
@@ -0,0 +1,10 @@
+struct usbdev_sb_info {
+	struct list_head slist;
+	struct list_head ilist;
+	uid_t devuid;
+	gid_t devgid;
+	umode_t devmode;
+	uid_t busuid;
+	gid_t busgid;
+	umode_t busmode;
+};
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/usbdevice_fs.h linux.18p17/include/linux/usbdevice_fs.h
--- linux.vanilla/include/linux/usbdevice_fs.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/usbdevice_fs.h	Sun Oct  1 11:52:59 2000
@@ -0,0 +1,204 @@
+/*****************************************************************************/
+
+/*
+ *	usbdevice_fs.h  --  USB device file system.
+ *
+ *	Copyright (C) 2000
+ *          Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ *
+ *  $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $
+ */
+
+/*****************************************************************************/
+
+#ifndef _LINUX_USBDEVICE_FS_H
+#define _LINUX_USBDEVICE_FS_H
+
+#include <linux/types.h>
+
+/* --------------------------------------------------------------------- */
+
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+
+/* usbdevfs ioctl codes */
+
+struct usbdevfs_ctrltransfer {
+	__u8 requesttype;
+	__u8 request;
+	__u16 value;
+	__u16 index;
+	__u16 length;
+	__u32 timeout;  /* in milliseconds */
+ 	void *data;
+};
+
+struct usbdevfs_bulktransfer {
+	unsigned int ep;
+	unsigned int len;
+	unsigned int timeout; /* in milliseconds */
+	void *data;
+};
+
+struct usbdevfs_setinterface {
+	unsigned int interface;
+	unsigned int altsetting;
+};
+
+struct usbdevfs_disconnectsignal {
+	unsigned int signr;
+	void *context;
+};
+
+#define USBDEVFS_MAXDRIVERNAME 255
+
+struct usbdevfs_getdriver {
+	unsigned int interface;
+	char driver[USBDEVFS_MAXDRIVERNAME + 1];
+};
+
+struct usbdevfs_connectinfo {
+	unsigned int devnum;
+	unsigned char slow;
+};
+
+#define USBDEVFS_URB_DISABLE_SPD           1
+#define USBDEVFS_URB_ISO_ASAP              2
+
+#define USBDEVFS_URB_TYPE_ISO		   0
+#define USBDEVFS_URB_TYPE_INTERRUPT	   1
+#define USBDEVFS_URB_TYPE_CONTROL	   2
+#define USBDEVFS_URB_TYPE_BULK		   3
+
+struct usbdevfs_iso_packet_desc {
+	unsigned int length;
+	unsigned int actual_length;
+	unsigned int status;
+};
+
+struct usbdevfs_urb {
+	unsigned char type;
+	unsigned char endpoint;
+	int status;
+	unsigned int flags;
+	void *buffer;
+	int buffer_length;
+	int actual_length;
+	int start_frame;
+	int number_of_packets;
+	int error_count;
+	unsigned int signr;  /* signal to be sent on error, -1 if none should be sent */
+	void *usercontext;
+	struct usbdevfs_iso_packet_desc iso_frame_desc[0];
+};
+
+/* ioctls for talking to drivers in the usbcore module: */
+struct usbdevfs_ioctl {
+	int	ifno;		/* interface 0..N ; negative numbers reserved */
+	int	ioctl_code;	/* MUST encode size + direction of data so the
+				 * macros in <asm/ioctl.h> give correct values */
+	void	*data;		/* param buffer (in, or out) */
+};
+
+/* You can do most things with hubs just through control messages,
+ * except find out what device connects to what port. */
+struct usbdevfs_hub_portinfo {
+	char nports;		/* number of downstream ports in this hub */
+	char port [127];	/* e.g. port 3 connects to device 27 */
+};
+
+#define USBDEVFS_CONTROL           _IOWR('U', 0, struct usbdevfs_ctrltransfer)
+#define USBDEVFS_BULK              _IOWR('U', 2, struct usbdevfs_bulktransfer)
+#define USBDEVFS_RESETEP           _IOR('U', 3, unsigned int)
+#define USBDEVFS_SETINTERFACE      _IOR('U', 4, struct usbdevfs_setinterface)
+#define USBDEVFS_SETCONFIGURATION  _IOR('U', 5, unsigned int)
+#define USBDEVFS_GETDRIVER         _IOW('U', 8, struct usbdevfs_getdriver)
+#define USBDEVFS_SUBMITURB         _IOR('U', 10, struct usbdevfs_urb)
+#define USBDEVFS_DISCARDURB        _IO('U', 11)
+#define USBDEVFS_REAPURB           _IOW('U', 12, void *)
+#define USBDEVFS_REAPURBNDELAY     _IOW('U', 13, void *)
+#define USBDEVFS_DISCSIGNAL        _IOR('U', 14, struct usbdevfs_disconnectsignal)
+#define USBDEVFS_CLAIMINTERFACE    _IOR('U', 15, unsigned int)
+#define USBDEVFS_RELEASEINTERFACE  _IOR('U', 16, unsigned int)
+#define USBDEVFS_CONNECTINFO       _IOW('U', 17, struct usbdevfs_connectinfo)
+#define USBDEVFS_IOCTL             _IOWR('U', 18, struct usbdevfs_ioctl)
+#define USBDEVFS_HUB_PORTINFO      _IOR('U', 19, struct usbdevfs_hub_portinfo)
+#define USBDEVFS_RESET             _IO('U', 20)
+#define USBDEVFS_CLEAR_HALT        _IOR('U', 21, unsigned int)
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <asm/semaphore.h>
+
+/*
+ * inode number macros
+ */
+#define ITYPE(x)   ((x)&(0xf<<28))
+#define ISPECIAL   (0<<28)
+#define IBUS       (1<<28)
+#define IDEVICE    (2<<28)
+#define IBUSNR(x)  (((x)>>8)&0xff)
+#define IDEVNR(x)  ((x)&0xff)
+
+#define IROOT      1
+
+/*
+ * sigh. rwsemaphores do not (yet) work from modules
+ */
+
+#define rw_semaphore semaphore
+#define init_rwsem init_MUTEX
+#define down_read down
+#define down_write down
+#define up_read up
+#define up_write up
+
+
+struct dev_state {
+	struct list_head list;      /* state list */
+	struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */ 
+	struct usb_device *dev;
+	struct file *file;
+	spinlock_t lock;            /* protects the async urb lists */
+	struct list_head async_pending;
+	struct list_head async_completed;
+	wait_queue_head_t wait;     /* wake up if a request completed */
+	unsigned int discsignr;
+	struct task_struct *disctask;
+	void *disccontext;
+	unsigned long ifclaimed;
+};
+
+/* internal methods & data */
+extern struct usb_driver usbdevfs_driver;
+extern struct file_operations usbdevfs_drivers_fops;
+extern struct file_operations usbdevfs_devices_fops;
+extern struct file_operations usbdevfs_device_file_operations;
+extern struct inode_operations usbdevfs_device_inode_operations;
+extern struct inode_operations usbdevfs_bus_inode_operations;
+extern struct file_operations usbdevfs_bus_file_operations;
+extern void usbdevfs_conn_disc_event(void);
+
+#endif /* __KERNEL__ */
+
+/* --------------------------------------------------------------------- */
+#endif /* _LINUX_USBDEVICE_FS_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/videodev.h linux.18p17/include/linux/videodev.h
--- linux.vanilla/include/linux/videodev.h	Wed May  3 22:25:25 2000
+++ linux.18p17/include/linux/videodev.h	Thu Oct 19 01:40:49 2000
@@ -279,6 +279,8 @@
 #define VID_HARDWARE_VINO	20	/* Reserved for SGI Indy Vino */
 #define VID_HARDWARE_CADET	21	/* Cadet radio */
 #define VID_HARDWARE_TRUST	22	/* Trust FM Radio */
+#define VID_HARDWARE_CPIA	24
+#define VID_HARDWARE_OV511	27
 
 /*
  *	Initialiser list
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/vmalloc.h linux.18p17/include/linux/vmalloc.h
--- linux.vanilla/include/linux/vmalloc.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/vmalloc.h	Sun Oct 15 23:29:13 2000
@@ -19,6 +19,9 @@
 long vread(char *buf, char *addr, unsigned long count);
 void vmfree_area_pages(unsigned long address, unsigned long size);
 int vmalloc_area_pages(unsigned long address, unsigned long size);
-
+extern inline void * vmalloc_32(unsigned long size)
+{
+	return vmalloc(size);
+}
 #endif
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/wait.h linux.18p17/include/linux/wait.h
--- linux.vanilla/include/linux/wait.h	Fri Apr 21 23:10:16 2000
+++ linux.18p17/include/linux/wait.h	Sun Sep  3 00:06:13 2000
@@ -15,7 +15,14 @@
 	struct wait_queue * next;
 };
 
+typedef struct wait_queue wait_queue_t;
+typedef struct wait_queue *wait_queue_head_t;
+
 #define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1))
+#define DECLARE_WAITQUEUE(wait, current)	struct wait_queue wait = { current, NULL }
+#define DECLARE_WAIT_QUEUE_HEAD(wait)		wait_queue_head_t wait
+#define init_waitqueue_head(x)			*(x)=NULL
+#define init_waitqueue_entry(q,p)		((q)->task)=(p)
 
 static inline void init_waitqueue(struct wait_queue **q)
 {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/linux/xpds-ioctl.h linux.18p17/include/linux/xpds-ioctl.h
--- linux.vanilla/include/linux/xpds-ioctl.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/linux/xpds-ioctl.h	Sun Oct 15 21:57:15 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/af_unix.h linux.18p17/include/net/af_unix.h
--- linux.vanilla/include/net/af_unix.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/net/af_unix.h	Thu Oct 19 01:11:21 2000
@@ -7,7 +7,7 @@
 typedef struct sock unix_socket;
 extern void unix_gc(void);
 
-#define UNIX_HASH_SIZE	16
+#define UNIX_HASH_SIZE	256
 
 extern unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/divert.h linux.18p17/include/net/divert.h
--- linux.vanilla/include/net/divert.h	Thu Jan  1 01:00:00 1970
+++ linux.18p17/include/net/divert.h	Thu Oct 19 01:19:03 2000
@@ -0,0 +1,118 @@
+/*
+ * Frame Diversion, Benoit Locher <Benoit.Locher@skf.com>
+ *
+ * Changes:
+ * 		06/09/2000	BL:	initial version
+ * 
+ */
+ 
+#ifndef _LINUX_DIVERT_H
+#define _LINUX_DIVERT_H
+
+#include <asm/types.h>
+
+#define	MAX_DIVERT_PORTS	8	/* Max number of ports to divert (tcp, udp) */
+
+/* Divertable protocols */
+#define	DIVERT_PROTO_NONE	0x0000
+#define	DIVERT_PROTO_IP		0x0001
+#define	DIVERT_PROTO_ICMP	0x0002
+#define	DIVERT_PROTO_TCP	0x0004
+#define	DIVERT_PROTO_UDP	0x0008
+
+/*
+ *	This is an Ethernet Frame Diverter option block
+ */
+struct divert_blk
+{
+	int		divert;  /* are we active */
+	unsigned int protos;	/* protocols */
+	u16		tcp_dst[MAX_DIVERT_PORTS]; /* specific tcp dst ports to divert */
+	u16		tcp_src[MAX_DIVERT_PORTS]; /* specific tcp src ports to divert */
+	u16		udp_dst[MAX_DIVERT_PORTS]; /* specific udp dst ports to divert */
+	u16		udp_src[MAX_DIVERT_PORTS]; /* specific udp src ports to divert */
+};
+
+/*
+ * Diversion control block, for configuration with the userspace tool
+ * divert
+ */
+
+typedef union _divert_cf_arg
+{
+	s16		int16;
+	u16		uint16;
+	s32		int32;
+	u32		uint32;
+	s64		int64;
+	u64		uint64;
+	void	*ptr;
+} divert_cf_arg;
+
+
+struct divert_cf
+{
+	int	cmd;				/* Command */
+	divert_cf_arg 	arg1,
+					arg2,
+					arg3;
+	int	dev_index;	/* device index (eth0=0, etc...) */
+};
+
+
+/* Diversion commands */
+#define	DIVCMD_DIVERT			1 /* ENABLE/DISABLE diversion */
+#define	DIVCMD_IP				2 /* ENABLE/DISABLE whold IP diversion */
+#define	DIVCMD_TCP				3 /* ENABLE/DISABLE whold TCP diversion */
+#define	DIVCMD_TCPDST			4 /* ADD/REMOVE TCP DST port for diversion */
+#define	DIVCMD_TCPSRC			5 /* ADD/REMOVE TCP SRC port for diversion */
+#define	DIVCMD_UDP				6 /* ENABLE/DISABLE whole UDP diversion */
+#define	DIVCMD_UDPDST			7 /* ADD/REMOVE UDP DST port for diversion */
+#define	DIVCMD_UDPSRC			8 /* ADD/REMOVE UDP SRC port for diversion */
+#define	DIVCMD_ICMP				9 /* ENABLE/DISABLE whole ICMP diversion */
+#define	DIVCMD_GETSTATUS		10 /* GET the status of the diverter */
+#define	DIVCMD_RESET			11 /* Reset the diverter on the specified dev */
+#define DIVCMD_GETVERSION		12 /* Retrieve the diverter code version (char[32]) */
+
+/* General syntax of the commands:
+ * 
+ * DIVCMD_xxxxxx(arg1, arg2, arg3, dev_index)
+ * 
+ * SIOCSIFDIVERT:
+ *   DIVCMD_DIVERT(DIVARG1_ENABLE|DIVARG1_DISABLE, , ,ifindex)
+ *   DIVCMD_IP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ *   DIVCMD_TCP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ *   DIVCMD_TCPDST(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ *   DIVCMD_TCPSRC(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ *   DIVCMD_UDP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ *   DIVCMD_UDPDST(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ *   DIVCMD_UDPSRC(DIVARG1_ADD|DIVARG1_REMOVE, port, , ifindex)
+ *   DIVCMD_ICMP(DIVARG1_ENABLE|DIVARG1_DISABLE, , , ifindex)
+ *   DIVCMD_RESET(, , , ifindex)
+ *   
+ * SIOGIFDIVERT:
+ *   DIVCMD_GETSTATUS(divert_blk, , , ifindex)
+ *   DIVCMD_GETVERSION(string[3])
+ */
+
+
+/* Possible values for arg1 */
+#define	DIVARG1_ENABLE			0 /* ENABLE something */
+#define	DIVARG1_DISABLE			1 /* DISABLE something */
+#define DIVARG1_ADD				2 /* ADD something */
+#define DIVARG1_REMOVE			3 /* REMOVE something */
+
+
+#ifdef __KERNEL__
+
+/* diverter functions */
+#include <linux/skbuff.h>
+void dv_init(void);
+int alloc_divert_blk(struct device *);
+void free_divert_blk(struct device *);
+int divert_ioctl(unsigned int cmd, struct divert_cf *arg);
+void divert_frame(struct sk_buff *skb);
+
+#endif __KERNEL__
+
+#endif	/* _LINUX_DIVERT_H */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/ip.h linux.18p17/include/net/ip.h
--- linux.vanilla/include/net/ip.h	Fri Apr 21 23:11:52 2000
+++ linux.18p17/include/net/ip.h	Thu Oct 19 01:38:15 2000
@@ -171,10 +171,8 @@
 int ip_decrease_ttl(struct iphdr *iph)
 {
 	u16 check = iph->check;
-	check = ntohs(check) + 0x0100;
-	if ((check & 0xFF00) == 0)
-		check++;		/* carry overflow */
-	iph->check = htons(check);
+	check += __constant_htons(0x0100);
+	iph->check = check + (check>=0xFFFF);
 	return --iph->ttl;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/ipconfig.h linux.18p17/include/net/ipconfig.h
--- linux.vanilla/include/net/ipconfig.h	Sun Jun 11 21:44:23 2000
+++ linux.18p17/include/net/ipconfig.h	Fri Sep  8 16:53:11 2000
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.h,v 1.3 1999/01/04 20:13:29 davem Exp $
+ *  $Id: ipconfig.h,v 1.3.2.1 2000/06/09 02:27:24 davem Exp $
  *
  *  Copyright (C) 1997 Martin Mares
  *
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/net/x25.h linux.18p17/include/net/x25.h
--- linux.vanilla/include/net/x25.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/net/x25.h	Tue Sep 26 21:50:14 2000
@@ -183,7 +183,7 @@
 extern void x25_link_free(void);
 
 /* x25_out.c */
-extern void x25_output(struct sock *, struct sk_buff *);
+extern  int x25_output(struct sock *, struct sk_buff *);
 extern void x25_kick(struct sock *);
 extern void x25_enquiry_response(struct sock *);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/scsi/scsi.h linux.18p17/include/scsi/scsi.h
--- linux.vanilla/include/scsi/scsi.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/scsi/scsi.h	Fri Sep 15 22:44:39 2000
@@ -205,6 +205,9 @@
 /* Used to get the bus number for a device */
 #define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
 
+/* Used to get Fibre Channel WWN and port_id from device */
+#define SCSI_IOCTL_FC_TARGET_ADDRESS 0x5387
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/include/scsi/scsi_ioctl.h linux.18p17/include/scsi/scsi_ioctl.h
--- linux.vanilla/include/scsi/scsi_ioctl.h	Fri Apr 21 12:47:01 2000
+++ linux.18p17/include/scsi/scsi_ioctl.h	Fri Sep 15 22:45:02 2000
@@ -32,6 +32,13 @@
 	__u32 host_unique_id;
 } Scsi_Idlun;
 
+/* Fibre Channel WWN, port_id struct */
+typedef struct scsi_fctargaddress
+{
+	__u32 host_port_id;
+	unsigned char host_wwn[8]; // include NULL term.
+} Scsi_FCTargAddress;
+
 extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
 extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
 extern int scsi_ioctl_send_command(Scsi_Device *dev,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/init/main.c linux.18p17/init/main.c
--- linux.vanilla/init/main.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/init/main.c	Sat Oct 14 00:41:23 2000
@@ -22,6 +22,7 @@
 #include <linux/smp_lock.h>
 #include <linux/blk.h>
 #include <linux/hdreg.h>
+#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -42,10 +43,6 @@
 #  include <asm/mtrr.h>
 #endif
 
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-#endif
-
 #ifdef CONFIG_DASD
 #include <asm/dasd.h>
 #endif
@@ -107,8 +104,12 @@
 extern void ioapic_pirq_setup(char *str, int *ints);
 extern void ioapic_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_MICROCODE
+extern int microcode_init(void);
+#endif
 extern void no_scroll(char *str, int *ints);
 extern void kbd_reset_setup(char *str, int *ints);
+extern void aux_reconnect_setup(char *str, int *ints);
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
@@ -224,6 +225,7 @@
 extern void aha1542_setup(char *str, int *ints);
 extern void gdth_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
+extern void ips_setup(char *str, int *ints);
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
 extern void ncr53c8xx_setup(char *str, int *ints);
@@ -399,6 +401,10 @@
 extern void md_setup(char *str,int *ints) __init;
 #endif
 
+#ifdef CONFIG_AGP
+extern int agp_init (void);
+#endif
+
 /*
  * Boot command-line arguments
  */
@@ -753,6 +759,9 @@
 #ifdef CONFIG_VT
 	{ "kbd-reset", kbd_reset_setup },
 #endif
+#if defined(CONFIG_PSMOUSE) && defined (CONFIG_VT)
+	{ "psaux-reconnect", aux_reconnect_setup },
+#endif
 #ifdef CONFIG_BUGi386
 	{ "no-hlt", no_halt },
 	{ "no387", no_387 },
@@ -862,6 +871,9 @@
 #ifdef CONFIG_SCSI_AIC7XXX
 	{ "aic7xxx=", aic7xxx_setup},
 #endif
+#ifdef CONFIG_SCSI_IPS
+	{ "ips=", ips_setup},
+#endif
 #ifdef CONFIG_SCSI_BUSLOGIC
 	{ "BusLogic=", BusLogic_Setup},
 #endif
@@ -1080,9 +1092,6 @@
 #ifdef CONFIG_PARIDE_PG
         { "pg.", pg_setup },
 #endif
-#ifdef CONFIG_APM
-	{ "apm=", apm_setup },
-#endif
 #ifdef CONFIG_N2
 	{ "n2=", n2_setup },
 #endif
@@ -1108,6 +1117,7 @@
 	{ 0, 0 }
 };
 
+
 #ifdef CONFIG_BLK_DEV_RAM
 static void __init ramdisk_start_setup(char *str, int *ints)
 {
@@ -1136,6 +1146,7 @@
 
 static int __init checksetup(char *line)
 {
+	struct new_kernel_param *p;
 	int i, ints[11];
 
 #ifdef CONFIG_BLK_DEV_IDE
@@ -1159,14 +1170,25 @@
 			return 1;
 		}
 	}
+
+	/* Now handle new-style __setup parameters */
+	p = &__setup_start;
+	while (p < &__setup_end) {
+		int n = strlen(p->str);
+		if (!strncmp(line,p->str,n)) {
+			if (p->setup_func(line+n))
+				return 1;
+		}
+		p++;
+	}
 	return 0;
 }
 
 /* this should be approx 2 Bo*oMips to start (note initial shift), and will
    still work even if initially too large, it will just take slightly longer */
-unsigned long loops_per_sec = (1<<12);
+unsigned long loops_per_jiffy = (1<<12);
 
-/* This is the number of bits of precision for the loops_per_second.  Each
+/* This is the number of bits of precision for the loops_per_jiffy.  Each
    bit takes on average 1.5/HZ seconds.  This (like the original) is a little
    better than 1% */
 #define LPS_PREC 8
@@ -1176,42 +1198,40 @@
 	unsigned long ticks, loopbit;
 	int lps_precision = LPS_PREC;
 
-	loops_per_sec = (1<<12);
+	loops_per_jiffy = (1<<12);
 
 	printk("Calibrating delay loop... ");
-	while (loops_per_sec <<= 1) {
+	while (loops_per_jiffy <<= 1) {
 		/* wait for "start of" clock tick */
 		ticks = jiffies;
 		while (ticks == jiffies)
 			/* nothing */;
 		/* Go .. */
 		ticks = jiffies;
-		__delay(loops_per_sec);
+		__delay(loops_per_jiffy);
 		ticks = jiffies - ticks;
 		if (ticks)
 			break;
 	}
 
-/* Do a binary approximation to get loops_per_second set to equal one clock
+/* Do a binary approximation to get loops_per_jiffy set to equal one clock
    (up to lps_precision bits) */
-	loops_per_sec >>= 1;
-	loopbit = loops_per_sec;
+	loops_per_jiffy >>= 1;
+	loopbit = loops_per_jiffy;
 	while ( lps_precision-- && (loopbit >>= 1) ) {
-		loops_per_sec |= loopbit;
+		loops_per_jiffy |= loopbit;
 		ticks = jiffies;
 		while (ticks == jiffies);
 		ticks = jiffies;
-		__delay(loops_per_sec);
+		__delay(loops_per_jiffy);
 		if (jiffies != ticks)	/* longer than 1 tick */
-			loops_per_sec &= ~loopbit;
+			loops_per_jiffy &= ~loopbit;
 	}
 
-/* finally, adjust loops per second in terms of seconds instead of clocks */	
-	loops_per_sec *= HZ;
 /* Round the value and print it */	
 	printk("%lu.%02lu BogoMIPS\n",
-		(loops_per_sec+2500)/500000,
-		((loops_per_sec+2500)/5000) % 100);
+		loops_per_jiffy/(500000/HZ),
+		(loops_per_jiffy/(5000/HZ)) % 100);
 }
 
 /*
@@ -1269,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;
@@ -1457,6 +1481,21 @@
 
 struct task_struct *child_reaper = &init_task;
 
+
+static void __init do_initcalls(void)
+{
+	initcall_t *call;
+
+	call = &__initcall_start;
+	
+	while (call < &__initcall_end)
+	{
+		(*call)();
+		call++;
+	} 
+}
+
+
 /*
  * Ok, the machine is now initialized. None of the devices
  * have been touched yet, but the CPU subsystem is up and
@@ -1521,6 +1560,9 @@
 #ifdef CONFIG_MAC
 	nubus_init();
 #endif
+#ifdef CONFIG_AGP
+	agp_init();
+#endif
 
 	/* Networking initialization needs a process context */ 
 	sock_init();
@@ -1555,11 +1597,19 @@
 	/* .. executable formats .. */
 	binfmt_setup();
 
+	/* the functions marked initcall  */
+	
+	do_initcalls();
+
 	/* .. filesystems .. */
 	filesystem_setup();
 
 #ifdef CONFIG_IRDA
 	irda_device_init(); /* Must be done after protocol initialization */
+#endif
+
+#ifdef CONFIG_MICROCODE
+	microcode_init();
 #endif
 
 	/* Mount the root filesystem.. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/dma.c linux.18p17/kernel/dma.c
--- linux.vanilla/kernel/dma.c	Fri Apr 21 12:47:02 2000
+++ linux.18p17/kernel/dma.c	Tue Sep 26 21:50:59 2000
@@ -115,9 +115,8 @@
 	return -EINVAL;
 }
 
-int free_dma(unsigned int dmanr)
+void free_dma(unsigned int dmanr)
 {
-	return -EINVAL;
 }
 
 int get_dma_list(char *buf)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/kmod.c linux.18p17/kernel/kmod.c
--- linux.vanilla/kernel/kmod.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/kernel/kmod.c	Sat Oct 14 00:51:40 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/ksyms.c linux.18p17/kernel/ksyms.c
--- linux.vanilla/kernel/ksyms.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/kernel/ksyms.c	Sat Oct 14 00:51:40 2000
@@ -23,8 +23,6 @@
 #include <linux/serial.h>
 #include <linux/locks.h>
 #include <linux/delay.h>
-#include <linux/minix_fs.h>
-#include <linux/ext2_fs.h>
 #include <linux/random.h>
 #include <linux/reboot.h>
 #include <linux/pagemap.h>
@@ -76,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
@@ -90,6 +91,7 @@
 EXPORT_SYMBOL(exit_files);
 EXPORT_SYMBOL(exit_fs);
 EXPORT_SYMBOL(exit_sighand);
+EXPORT_SYMBOL(daemonize);
 
 /* internal kernel memory management */
 EXPORT_SYMBOL(__get_free_pages);
@@ -98,6 +100,7 @@
 EXPORT_SYMBOL(kmem_find_general_cachep);
 EXPORT_SYMBOL(kmem_cache_create);
 EXPORT_SYMBOL(kmem_cache_shrink);
+EXPORT_SYMBOL(kmem_cache_destroy);
 EXPORT_SYMBOL(kmem_cache_alloc);
 EXPORT_SYMBOL(kmem_cache_free);
 EXPORT_SYMBOL(kmalloc);
@@ -126,6 +129,7 @@
 EXPORT_SYMBOL(igrab);
 EXPORT_SYMBOL(iunique);
 EXPORT_SYMBOL(iget);
+EXPORT_SYMBOL(iget4);
 EXPORT_SYMBOL(iget_in_use);
 EXPORT_SYMBOL(iput);
 EXPORT_SYMBOL(__namei);
@@ -156,6 +160,7 @@
 EXPORT_SYMBOL(invalidate_inode_pages);
 EXPORT_SYMBOL(truncate_inode_pages);
 EXPORT_SYMBOL(fsync_dev);
+EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(inode_setattr);
 EXPORT_SYMBOL(inode_change_ok);
@@ -195,9 +200,16 @@
 EXPORT_SYMBOL(vfs_unlink);
 EXPORT_SYMBOL(vfs_rename);
 EXPORT_SYMBOL(__pollwait);
+EXPORT_SYMBOL(read_cache_page);
 EXPORT_SYMBOL(ROOT_DEV);
 EXPORT_SYMBOL(inode_generation_count);
 
+EXPORT_SYMBOL(page_cache_size);
+EXPORT_SYMBOL(page_hash_table);
+EXPORT_SYMBOL(page_hash_mask);
+EXPORT_SYMBOL(page_hash_bits);
+EXPORT_SYMBOL(__wait_on_page);
+
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
 #endif
@@ -338,7 +350,7 @@
 EXPORT_SYMBOL(jiffies);
 EXPORT_SYMBOL(xtime);
 EXPORT_SYMBOL(do_gettimeofday);
-EXPORT_SYMBOL(loops_per_sec);
+EXPORT_SYMBOL(loops_per_jiffy);
 EXPORT_SYMBOL(kstat);
 EXPORT_SYMBOL(pidhash); 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/sched.c linux.18p17/kernel/sched.c
--- linux.vanilla/kernel/sched.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/kernel/sched.c	Sun Oct 15 21:27:00 2000
@@ -696,6 +696,7 @@
 	struct task_struct *prev, *next, *p;
 	int this_cpu, c;
 
+	sti();
 	if (tq_scheduler)
 		goto handle_tq_scheduler;
 tq_scheduler_back:
@@ -1930,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;
 	}
 
@@ -2044,6 +2053,34 @@
 	for_each_task(p)
 		show_task((p->tarray_ptr - &task[0]),p);
 	read_unlock(&tasklist_lock);
+}
+
+/*
+ *      Put all the gunge required to become a kernel thread without
+ *      attached user resources in one place where it belongs.
+ */
+
+void daemonize(void)
+{
+	struct fs_struct *fs;
+
+	/*
+	 * If we were started as result of loading a module, close all of the
+	 * user space pages.  We don't need them, and if we didn't close them
+	 * they would be locked into memory.
+	 */
+	exit_mm(current);
+
+	current->session = 1;
+	current->pgrp = 1;
+
+	/* Become as one with the init task */
+
+	exit_fs(current);	/* current->fs->count--; */
+	fs = init_task.fs;
+	current->fs = fs;
+	atomic_inc(&fs->count);
+
 }
 
 void __init init_idle(void)
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/kernel/sysctl.c linux.18p17/kernel/sysctl.c
--- linux.vanilla/kernel/sysctl.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/kernel/sysctl.c	Sat Oct 14 00:51:40 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/filemap.c linux.18p17/mm/filemap.c
--- linux.vanilla/mm/filemap.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/mm/filemap.c	Fri Sep 15 22:10:44 2000
@@ -55,6 +55,15 @@
 static inline void 
 make_pio_request(struct file *, unsigned long, unsigned long);
 
+static inline int sync_page(struct page *page)
+{
+	struct inode *inode = page->inode;
+
+	if (inode && inode->i_op && inode->i_op->sync_page)
+		return inode->i_op->sync_page(page);
+	run_task_queue(&tq_disk);
+	return 0;
+}
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -350,7 +359,7 @@
 	add_wait_queue(&page->wait, &wait);
 repeat:
 	tsk->state = TASK_UNINTERRUPTIBLE;
-	run_task_queue(&tq_disk);
+	sync_page(page);
 	if (PageLocked(page)) {
 		schedule();
 		goto repeat;
@@ -621,7 +630,8 @@
 {
 	struct dentry *dentry = filp->f_dentry;
 	struct inode *inode = dentry->d_inode;
-	size_t pos, pgpos, page_cache;
+	unsigned long page_cache;
+	size_t pos, pgpos;
 	int reada_ok;
 	int max_readahead = get_max_readahead(inode);
 
@@ -708,6 +718,13 @@
 	{
 		unsigned long offset, nr;
 
+		/* If users can be writing to this page using arbitrary
+		 * virtual addresses, take care about potential aliasing
+		 * before reading the page on the kernel side.
+		 */
+		if (inode->i_mmap_shared != NULL)
+			flush_dcache_page(page_address(page));
+
 		offset = pos & ~PAGE_CACHE_MASK;
 		nr = PAGE_CACHE_SIZE - offset;
 		if (nr > inode->i_size - pos)
@@ -1466,6 +1483,71 @@
 	return error;
 }
 
+static inline
+struct page *__read_cache_page(struct inode *inode, 
+			       unsigned long offset,
+			       int (*filler)(void *,struct page*),
+			       void *data)
+{
+	struct page **hash = page_hash(inode, offset);
+	struct page *page;
+	unsigned long cached_page = 0;
+	int err;
+
+	offset &= PAGE_CACHE_MASK;
+repeat:
+	page = __find_page(inode, offset, *hash);
+	if (!page) {
+		if (!cached_page) {
+			cached_page = page_cache_alloc();
+			if (!cached_page)
+				return ERR_PTR(-ENOMEM);
+			goto repeat;
+		}
+		page = page_cache_entry(cached_page);
+		cached_page = 0;
+		add_to_page_cache(page, inode, offset, hash);
+		set_bit(PG_locked, &page->flags);
+		err = filler(data, page);
+		if (err < 0) {
+			page_cache_release(page);
+			page = ERR_PTR(err);
+		}
+	}
+	if (cached_page)
+		page_cache_free(cached_page);
+	return page;
+}
+
+/*
+ * Read into the page cache. If a page already exists,
+ * and Page_Uptodate() is not set, try to fill the page.
+ */
+struct page *read_cache_page(struct inode *inode,
+				unsigned long offset,
+				int (*filler)(void *,struct page*),
+				void *data)
+{
+	struct page *page = __read_cache_page(inode, offset, filler, data);
+	int err;
+
+	if (IS_ERR(page) || PageUptodate(page))
+		goto out;
+
+	wait_on_page(page);
+	if (PageUptodate(page))
+		goto out;
+
+	set_bit(PG_locked, &page->flags);
+	err = filler(data, page);
+	if (err < 0) {
+		page_cache_release(page);
+		page = ERR_PTR(err);
+	}
+ out:
+	return page;
+}
+
 /*
  * Write to a file through the page cache. This is mainly for the
  * benefit of NFS and possibly other network-based file systems.
@@ -1562,6 +1644,11 @@
 		wait_on_page(page);
 		set_bit(PG_locked, &page->flags);
 
+		if (inode->i_op->prepare_write)
+			status = inode->i_op->prepare_write(file, page, offset, bytes);
+		if (status < 0)
+			goto unlock;
+
 		/*
 		 * Do the real work.. If the writer ends up delaying the write,
 		 * the writer needs to increment the page use counts until he
@@ -1576,6 +1663,7 @@
 		if (bytes)
 			status = inode->i_op->updatepage(file, page, offset, bytes, sync);
 
+ unlock:
 		/* Mark it unlocked again and drop the page.. */
 		clear_bit(PG_locked, &page->flags);
 		wake_up(&page->wait);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/memory.c linux.18p17/mm/memory.c
--- linux.vanilla/mm/memory.c	Fri Apr 21 12:47:02 2000
+++ linux.18p17/mm/memory.c	Wed Sep 13 16:48:19 2000
@@ -722,22 +722,8 @@
 	flush_page_to_ram(pte_page(pte));
 }
 
-/*
- * Handle all mappings that got truncated by a "truncate()"
- * system call.
- *
- * NOTE! We have to be ready to update the memory sharing
- * between the file and the memory map for a potential last
- * incomplete page.  Ugly, but necessary.
- */
-void vmtruncate(struct inode * inode, unsigned long offset)
+static void vmtruncate_list(struct vm_area_struct *mpnt, unsigned long offset)
 {
-	struct vm_area_struct * mpnt;
-
-	truncate_inode_pages(inode, offset);
-	if (!inode->i_mmap)
-		return;
-	mpnt = inode->i_mmap;
 	do {
 		struct mm_struct *mm = mpnt->vm_mm;
 		unsigned long start = mpnt->vm_start;
@@ -767,6 +753,23 @@
 		zap_page_range(mm, start, len);
 		flush_tlb_range(mm, start, end);
 	} while ((mpnt = mpnt->vm_next_share) != NULL);
+}
+
+/*
+ * Handle all mappings that got truncated by a "truncate()"
+ * system call.
+ *
+ * NOTE! We have to be ready to update the memory sharing
+ * between the file and the memory map for a potential last
+ * incomplete page.  Ugly, but necessary.
+ */
+void vmtruncate(struct inode * inode, unsigned long offset)
+{
+	truncate_inode_pages(inode, offset);
+	if (inode->i_mmap)
+		vmtruncate_list(inode->i_mmap, offset);
+	if (inode->i_mmap_shared)
+		vmtruncate_list(inode->i_mmap_shared, offset);
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/mmap.c linux.18p17/mm/mmap.c
--- linux.vanilla/mm/mmap.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/mm/mmap.c	Wed Sep 13 16:48:19 2000
@@ -70,7 +70,7 @@
 	return free > pages;
 }
 
-/* Remove one vm structure from the inode's i_mmap ring. */
+/* Remove one vm structure from the inode's i_mmap{,_shared} ring. */
 static inline void remove_shared_vm_struct(struct vm_area_struct *vma)
 {
 	struct file * file = vma->vm_file;
@@ -755,7 +755,7 @@
 }
 
 /* Insert vm structure into process list sorted by address
- * and into the inode's i_mmap ring.
+ * and into the inode's i_mmap{,_shared} ring.
  */
 void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
 {
@@ -783,14 +783,20 @@
 	file = vmp->vm_file;
 	if (file) {
 		struct inode * inode = file->f_dentry->d_inode;
+		struct vm_area_struct **head;
+
 		if (vmp->vm_flags & VM_DENYWRITE)
 			inode->i_writecount--;
       
+		head = &inode->i_mmap;
+		if (vmp->vm_flags & VM_SHARED)
+			head = &inode->i_mmap_shared;
+
 		/* insert vmp into inode's share list */
-		if((vmp->vm_next_share = inode->i_mmap) != NULL)
-			inode->i_mmap->vm_pprev_share = &vmp->vm_next_share;
-		inode->i_mmap = vmp;
-		vmp->vm_pprev_share = &inode->i_mmap;
+		if((vmp->vm_next_share = *head) != NULL)
+			(*head)->vm_pprev_share = &vmp->vm_next_share;
+		*head = vmp;
+		vmp->vm_pprev_share = head;
 	}
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/slab.c linux.18p17/mm/slab.c
--- linux.vanilla/mm/slab.c	Fri Apr 21 12:47:02 2000
+++ linux.18p17/mm/slab.c	Sat Sep  2 11:11:21 2000
@@ -987,8 +987,8 @@
  * is available.  To help debugging, a zero exit status indicates all slabs
  * were released.
  */
-int
-kmem_cache_shrink(kmem_cache_t *cachep)
+
+static int __kmem_cache_shrink(kmem_cache_t *cachep, int validated)
 {
 	kmem_cache_t	*searchp;
 	kmem_slab_t	*slabp;
@@ -1003,26 +1003,29 @@
 		return 2;
 	}
 
-	/* Find the cache in the chain of caches. */
-	down(&cache_chain_sem);		/* Semaphore is needed. */
-	searchp = &cache_cache;
-	for (;searchp->c_nextp != &cache_cache; searchp = searchp->c_nextp) {
-		if (searchp->c_nextp != cachep)
-			continue;
+	if(validated==0)
+	{
+		/* Find the cache in the chain of caches. */
+		down(&cache_chain_sem);		/* Semaphore is needed. */
+		searchp = &cache_cache;
+		for (;searchp->c_nextp != &cache_cache; searchp = searchp->c_nextp) {
+			if (searchp->c_nextp != cachep)
+				continue;
 
-		/* Accessing clock_searchp is safe - we hold the mutex. */
-		if (cachep == clock_searchp)
-			clock_searchp = cachep->c_nextp;
-		goto found;
-	}
-	up(&cache_chain_sem);
-	printk(KERN_ERR "kmem_shrink: Invalid cache addr %p\n", cachep);
-	return 2;
+			/* Accessing clock_searchp is safe - we hold the mutex. */
+			if (cachep == clock_searchp)
+				clock_searchp = cachep->c_nextp;
+			goto found;
+		}
+		up(&cache_chain_sem);
+		printk(KERN_ERR "kmem_shrink: Invalid cache addr %p\n", cachep);
+		return 2;
 found:
-	/* Release the semaphore before getting the cache-lock.  This could
-	 * mean multiple engines are shrinking the cache, but so what.
-	 */
-	up(&cache_chain_sem);
+		/* Release the semaphore before getting the cache-lock.  This could
+		 * mean multiple engines are shrinking the cache, but so what.
+		 */
+		up(&cache_chain_sem);
+	}
 	spin_lock_irq(&cachep->c_spinlock);
 
 	/* If the cache is growing, stop shrinking. */
@@ -1040,6 +1043,71 @@
 		ret--;		/* Cache is empty. */
 	spin_unlock_irq(&cachep->c_spinlock);
 	return ret;
+}
+
+int kmem_cache_shrink(kmem_cache_t *cachep)
+{
+	return __kmem_cache_shrink(cachep,0);
+}
+
+/*
+ * Remove a kmem_cache_t object from the slab cache. When returns 0 it
+ * completed succesfully. -arca
+ */
+int kmem_cache_destroy(kmem_cache_t * cachep)
+{
+	kmem_cache_t * prev;
+	int ret;
+
+	if (!cachep) {
+		printk(KERN_ERR "kmem_destroy: NULL ptr\n");
+		return 1;
+	}
+	if (in_interrupt()) {
+		printk(KERN_ERR "kmem_destroy: Called during int - %s\n",
+		       cachep->c_name);
+		return 1;
+	}
+
+	ret = 0;
+	/* Find the cache in the chain of caches. */
+	down(&cache_chain_sem);
+	for (prev = &cache_cache; prev->c_nextp != &cache_cache;
+	     prev = prev->c_nextp) {
+		if (prev->c_nextp != cachep)
+			continue;
+
+		/* Accessing clock_searchp is safe - we hold the mutex. */
+		if (cachep == clock_searchp)
+			clock_searchp = cachep->c_nextp;
+
+		/* remove the cachep from the cache_cache list. -arca */
+		prev->c_nextp = cachep->c_nextp;
+
+		ret = 1;
+		break;
+	}
+	up(&cache_chain_sem);
+
+	if (!ret) {
+		printk(KERN_ERR "kmem_destroy: Invalid cache addr %p\n",
+		       cachep);
+		return 1;
+	}
+
+	if (__kmem_cache_shrink(cachep, 1)) {
+		printk(KERN_ERR "kmem_destroy: Can't free all objects %p\n",
+		       cachep);
+		down(&cache_chain_sem);
+		cachep->c_nextp = cache_cache.c_nextp;
+		cache_cache.c_nextp = cachep;
+		up(&cache_chain_sem);
+		return 1;
+	}
+
+	kmem_cache_free(&cache_cache, cachep);
+
+	return 0;
 }
 
 /* Get the memory for a slab management obj. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/mm/swapfile.c linux.18p17/mm/swapfile.c
--- linux.vanilla/mm/swapfile.c	Fri Apr 21 12:47:02 2000
+++ linux.18p17/mm/swapfile.c	Sun Sep 17 17:51:58 2000
@@ -494,6 +494,7 @@
 	int lock_map_size = PAGE_SIZE;
 	int nr_good_pages = 0;
 	unsigned long tmp_lock_map = 0;
+	int swapfilesize;
 	
 	lock_kernel();
 	if (!capable(CAP_SYS_ADMIN))
@@ -532,27 +533,32 @@
 	error = -EINVAL;
 
 	if (S_ISBLK(swap_dentry->d_inode->i_mode)) {
-		p->swap_device = swap_dentry->d_inode->i_rdev;
-		set_blocksize(p->swap_device, PAGE_SIZE);
+		kdev_t dev = swap_dentry->d_inode->i_rdev;
+
+		p->swap_device = dev;
+		set_blocksize(dev, PAGE_SIZE);
 		
 		filp.f_dentry = swap_dentry;
 		filp.f_mode = 3; /* read write */
 		error = blkdev_open(swap_dentry->d_inode, &filp);
 		if (error)
 			goto bad_swap_2;
-		set_blocksize(p->swap_device, PAGE_SIZE);
+		set_blocksize(dev, PAGE_SIZE);
 		error = -ENODEV;
-		if (!p->swap_device ||
-		    (blk_size[MAJOR(p->swap_device)] &&
-		     !blk_size[MAJOR(p->swap_device)][MINOR(p->swap_device)]))
+		if (!dev || (blk_size[MAJOR(dev)] &&
+			     !blk_size[MAJOR(dev)][MINOR(dev)]))
 			goto bad_swap;
 		error = -EBUSY;
 		for (i = 0 ; i < nr_swapfiles ; i++) {
 			if (i == type)
 				continue;
-			if (p->swap_device == swap_info[i].swap_device)
+			if (dev == swap_info[i].swap_device)
 				goto bad_swap;
 		}
+		swapfilesize = 0;
+		if (blk_size[MAJOR(dev)])
+			swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
+				>> (PAGE_SHIFT - 10);
 	} else if (S_ISREG(swap_dentry->d_inode->i_mode)) {
 		error = -EBUSY;
 		for (i = 0 ; i < nr_swapfiles ; i++) {
@@ -561,6 +567,7 @@
 			if (swap_dentry->d_inode == swap_info[i].swap_file->d_inode)
 				goto bad_swap;
 		}
+		swapfilesize = swap_dentry->d_inode->i_size  >> PAGE_SHIFT;
 	} else
 		goto bad_swap;
 
@@ -656,7 +663,13 @@
 		if (error) 
 			goto bad_swap;
 	}
-	
+
+	if (swapfilesize && p->max > swapfilesize) {
+		printk(KERN_WARNING
+		       "Swap area shorter than signature indicates\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
 	if (!nr_good_pages) {
 		printk(KERN_WARNING "Empty swap-file\n");
 		error = -EINVAL;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/Config.in linux.18p17/net/Config.in
--- linux.vanilla/net/Config.in	Fri Apr 21 12:47:11 2000
+++ linux.18p17/net/Config.in	Fri Sep 22 14:57:51 2000
@@ -42,6 +42,7 @@
   if [ "$CONFIG_BRIDGE" != "n" ]; then
 	int '  Maximum number of bridged interfaces' CONFIG_BRIDGE_NUM_PORTS 8
   fi
+  bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
   bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
 #  if [ "$CONFIG_LLC" = "y" ]; then
 #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ax25/Config.in linux.18p17/net/ax25/Config.in
--- linux.vanilla/net/ax25/Config.in	Fri Apr 21 12:47:10 2000
+++ linux.18p17/net/ax25/Config.in	Thu Oct 19 01:28:39 2000
@@ -1,10 +1,5 @@
 #
 # Amateur Radio protocols and AX.25 device configuration
-#
-# 19971130	Now in an own category to make correct compilation of the 
-#		AX.25 stuff easier...
-#		Joerg Reuter DL1BKE <jreuter@poboxes.com>
-# 19980129	Moved to net/ax25/Config.in, sourcing device drivers.
 
 mainmenu_option next_comment
 comment 'Amateur Radio support'
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ax25/af_ax25.c linux.18p17/net/ax25/af_ax25.c
--- linux.vanilla/net/ax25/af_ax25.c	Fri Apr 21 12:47:10 2000
+++ linux.18p17/net/ax25/af_ax25.c	Thu Aug 31 15:29:34 2000
@@ -1140,12 +1140,19 @@
 	 */
 	if (sk->zapped) {
 		if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
+		{
+			if(digi)
+				kfree(digi);
 			return err;
+		}
 		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
 		ax25_insert_socket(sk->protinfo.ax25);
 	} else {
 		if (sk->protinfo.ax25->ax25_dev == NULL)
+		{
+			if (digi != NULL) kfree(digi);
 			return -EHOSTUNREACH;
+		}
 	}
 
 	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) {
@@ -1191,7 +1198,9 @@
 
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+	{
 		return -EINPROGRESS;
+	}
 
 	cli();	/* To avoid races on the sleep */
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ax25/ax25_ds_in.c linux.18p17/net/ax25/ax25_ds_in.c
--- linux.vanilla/net/ax25/ax25_ds_in.c	Fri Apr 21 12:47:10 2000
+++ linux.18p17/net/ax25/ax25_ds_in.c	Thu Oct 19 01:28:39 2000
@@ -87,13 +87,6 @@
 					ax25->sk->state_change(ax25->sk);
 			}
 			ax25_dama_on(ax25);
-
-			/* according to DK4EG´s spec we are required to
-			 * send a RR RESPONSE FINAL NR=0. Please mail
-			 * <jreuter@poboxes.com> if this causes problems
-			 * with the TheNetNode DAMA Master implementation.
-			 */ 
-
 			ax25_std_enquiry_response(ax25);
 			break;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/bridge/br.c linux.18p17/net/bridge/br.c
--- linux.vanilla/net/bridge/br.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/bridge/br.c	Sun Oct  1 11:21:22 2000
@@ -2455,7 +2455,7 @@
 
 	/* Set up MAC address based on BogoMIPs figure for first CPU and time
 	 */ 
-	bogomips = (loops_per_sec+2500)/500000 ;
+	bogomips = (loops_per_jiffy+2500)/500000/HZ ;
 	get_fast_time(&utime);
 
 	/* Ummmm....  YES! */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/core/Makefile linux.18p17/net/core/Makefile
--- linux.vanilla/net/core/Makefile	Fri Apr 21 12:47:11 2000
+++ linux.18p17/net/core/Makefile	Fri Sep 22 14:57:52 2000
@@ -29,6 +29,10 @@
 OX_OBJS += firewall.o
 endif
 
+ifeq ($(CONFIG_NET_DIVERT),y)
+O_OBJS += dv.o
+endif
+
 endif
 
 ifdef CONFIG_NET_PROFILE
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/core/dev.c linux.18p17/net/core/dev.c
--- linux.vanilla/net/core/dev.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/core/dev.c	Sun Oct 15 22:02:21 2000
@@ -59,6 +59,7 @@
  *	Paul Rusty Russel	:	SIOCSIFNAME
  *	Andrea Arcangeli	:	dev_clear_backlog() needs the
  *					skb_queue_lock held.
+ *	Benoit Locher	:	Added the Frame Diversion code
  */
 
 #include <asm/uaccess.h>
@@ -84,6 +85,7 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <net/br.h>
+#include <net/divert.h>
 #include <net/dst.h>
 #include <net/pkt_sched.h>
 #include <net/profile.h>
@@ -839,6 +841,17 @@
 }
 #endif
 
+
+#ifdef CONFIG_NET_DIVERT
+static inline void handle_diverter(struct sk_buff *skb)
+{
+	/* if diversion is supported on device, then divert */
+	if (skb->dev->divert && skb->dev->divert->divert)
+		divert_frame(skb);
+	return;
+}
+#endif	 /* CONFIG_NET_DIVERT */
+
 /*
  *	When we are called the queue is ready to grab, the interrupts are
  *	on and hardware can interrupt and queue to the receive queue as we
@@ -900,6 +913,12 @@
 		 */
 		skb = skb_dequeue(&backlog);
 
+		/* This can happen if dev_clear_backlog is running
+		 * at the same time and it empties the queue.
+		 */
+		if (skb == NULL)
+			break;
+
 #ifdef CONFIG_CPU_IS_SLOW
 		if (ave_busy > 128*16) {
 			kfree_skb(skb);
@@ -942,6 +961,15 @@
 
 		type = skb->protocol;
 
+#ifdef CONFIG_NET_DIVERT
+		/*
+		 * Have the frame diverted ?
+		 *
+		 */
+		handle_diverter(skb);
+#endif	/* CONFIG_NET_DIVERT */
+		
+
 #ifdef CONFIG_BRIDGE
 		/*
 		 *	If we are bridging then pass the frame up to the
@@ -1754,10 +1782,12 @@
 
 static int dev_boot_phase = 1;
 
-
 int register_netdevice(struct device *dev)
 {
 	struct device *d, **dp;
+#ifdef CONFIG_NET_DIVERT
+	int	ret;
+#endif
 
 	if (dev_boot_phase) {
 		/* This is NOT bug, but I am not sure, that all the
@@ -1780,6 +1810,11 @@
 		}
 		dev->next = NULL;
 		*dp = dev;
+#ifdef CONFIG_NET_DIVERT
+		ret=alloc_divert_blk(dev);
+		if (ret)
+			return ret;
+#endif /* CONFIG_NET_DIVERT */
 		return 0;
 	}
 
@@ -1803,7 +1838,11 @@
 
 	/* Notify protocols, that a new device appeared. */
 	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
-
+#ifdef CONFIG_NET_DIVERT
+	ret=alloc_divert_blk(dev);
+	if (ret)
+		return ret;
+#endif
 	return 0;
 }
 
@@ -1851,6 +1890,9 @@
 
 			if (dev->destructor)
 				dev->destructor(dev);
+#ifdef CONFIG_NET_DIVERT
+			free_divert_blk(dev);	
+#endif /* CONFIG_NET_DIVERT */
 			return 0;
 		}
 	}
@@ -1872,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);
@@ -1926,6 +1969,13 @@
 #ifdef CONFIG_BRIDGE	 
 	br_init();
 #endif	
+
+	/*
+	 * Frame Diverter init
+	 */
+#ifdef CONFIG_NET_DIVERT
+	dv_init();
+#endif /* CONFIG_NET_DIVERT */
 	
 	/*
 	 * This is Very Ugly(tm).
@@ -2048,6 +2098,9 @@
 #endif
 #ifdef CONFIG_WANXL
 	wanxl_init();
+#endif
+#ifdef CONFIG_XPEED
+	xpdsl_init();
 #endif
 #ifdef CONFIG_PC300
 	cpc_init();
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/core/dv.c linux.18p17/net/core/dv.c
--- linux.vanilla/net/core/dv.c	Thu Jan  1 01:00:00 1970
+++ linux.18p17/net/core/dv.c	Thu Oct 19 01:19:03 2000
@@ -0,0 +1,553 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Generic frame diversion
+ *
+ * Version:	@(#)eth.c	0.41	09/09/2000
+ *
+ * Authors:	
+ * 		Benoit LOCHER:	initial integration within the kernel with support for ethernet
+ * 		Dave Miller:	improvement on the code (correctness, performance and source files)
+ *
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <net/dst.h>
+#include <net/arp.h>
+#include <net/sock.h>
+#include <net/ipv6.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/checksum.h>
+#include <net/divert.h>
+#include <linux/sockios.h>
+
+const char sysctl_divert_version[32]="0.461";	/* Current version */
+
+__initfunc(void dv_init(void))
+{
+	printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
+}
+
+/*
+ * Allocate a divert_blk for a device. This must be an ethernet nic.
+ *
+ */
+
+int alloc_divert_blk(struct device *dev)
+{
+    int alloc_size=(sizeof(struct divert_blk)+3)&~3;
+
+	if (!strncmp(dev->name, "eth", 3))
+	{
+		printk(KERN_DEBUG "divert: allocating divert_blk for %s\n",
+		dev->name);
+		dev->divert=(struct divert_blk *)kmalloc(alloc_size, GFP_KERNEL);
+		if (dev->divert==NULL)
+		{
+			printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
+			dev->name);
+			return -EFAULT;
+		}
+		else
+			memset(dev->divert, 0, sizeof(struct divert_blk));
+	}
+	else
+	{
+		printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %s\n",
+					dev->name);
+		dev->divert=NULL;
+	}
+	return 0;
+} 
+
+
+/*
+ * Free a divert_blk allocated by the above function, if it was 
+ * allocated on that device.
+ *
+ */
+
+
+void free_divert_blk(struct device *dev)
+{
+	if (dev->divert)
+	{
+		kfree(dev->divert);
+		dev->divert=NULL;
+		 printk(KERN_DEBUG "divert: freeing divert_blk for %s\n",
+						 dev->name);
+	}
+	else
+	{
+		printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernet\n",
+						dev->name);
+	}
+	return;
+}
+
+
+
+/*
+ * Adds a tcp/udp (source or dest) port to an array
+ */
+int add_port(u16 ports[], u16 port)
+{
+	int	i;
+
+	if (port==0)
+		return -EINVAL;
+
+	/* Storing directly in network format for performance, thanks Dave :) */
+	port=htons(port);
+
+	for (i=0; i<MAX_DIVERT_PORTS; i++)
+		if (ports[i]==port)
+			return -EALREADY;
+	
+	for (i=0; i<MAX_DIVERT_PORTS; i++)
+		if (ports[i]==0)
+		{
+			ports[i]=port;
+			return 0;
+		}
+	return -ENOBUFS;
+}
+
+/*
+ * Removes a port from an array tcp/udp (source or dest)
+ */
+int remove_port(u16 ports[], u16 port)
+{
+	int i;
+
+	if (port==0)
+		return -EINVAL;
+	
+	/* Storing directly in network format for performance, thanks Dave ! */
+	port=htons(port);
+
+	for (i=0; i<MAX_DIVERT_PORTS; i++)
+		if (ports[i]==port)
+		{
+			ports[i]=0;
+			return 0;
+		}
+	return -EINVAL;
+}
+
+/* Some basic sanity checks on the arguments passed to divert_ioctl() */
+int check_args(struct divert_cf *div_cf, struct device **dev)
+{
+	char	devname[32];
+		
+	if (dev==NULL)
+		return -EFAULT;
+
+	/* GETVERSION: all other args are unused */
+	if (div_cf->cmd==DIVCMD_GETVERSION)
+		return 0;
+	
+	/* Network device index should reasonably be between 0 and 1000 :) */
+	if (div_cf->dev_index<0 || div_cf->dev_index>1000) 
+		return -EINVAL;
+			
+	/* Let's try to find the ifname */
+	sprintf(devname, "eth%d", div_cf->dev_index);
+	*dev=dev_get(devname);
+	
+	/* dev should NOT be null */
+	if (*dev==NULL)
+		return -EINVAL;
+	
+	/* user issuing the ioctl must be a super one :) */
+	if (!suser())
+		return -EPERM;
+
+	/* Device must have a divert_blk member NOT null */
+	if ((*dev)->divert==NULL)
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * control function of the diverter
+ */
+int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
+{
+	struct divert_cf	div_cf;
+	struct divert_blk	*div_blk;
+	struct device		*dev;
+	int					ret;
+
+
+	switch(cmd)
+	{
+		case SIOCGIFDIVERT:
+			if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
+				return -EFAULT;
+			
+			ret=check_args(&div_cf, &dev);
+			if (ret)
+				return ret;
+
+			div_blk=dev->divert;
+			
+			switch(div_cf.cmd)
+			{
+				case DIVCMD_GETSTATUS:
+					/* Now, just give the user the raw divert block for him to play with :) */
+					if(copy_to_user(div_cf.arg1.ptr, dev->divert, sizeof(struct divert_blk)))
+						return -EFAULT;
+					break;
+
+				case DIVCMD_GETVERSION:
+					if (div_cf.arg1.ptr == NULL)
+						return -EINVAL;
+					if(copy_to_user(div_cf.arg1.ptr, sysctl_divert_version, 32))
+						return -EFAULT;
+					break;
+
+				default:
+					return -EINVAL;
+			}
+			break;
+
+		case SIOCSIFDIVERT:
+			if(copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
+				return -EFAULT;
+
+			ret=check_args(&div_cf, &dev);
+			if (ret)
+				return ret;
+
+			div_blk=dev->divert;
+			
+			switch(div_cf.cmd)
+			{
+				case DIVCMD_RESET:
+					div_blk->divert=0;
+					div_blk->protos=DIVERT_PROTO_NONE;
+					memset(div_blk->tcp_dst, 0, MAX_DIVERT_PORTS*sizeof(u16));
+					memset(div_blk->tcp_src, 0, MAX_DIVERT_PORTS*sizeof(u16));
+					memset(div_blk->udp_dst, 0, MAX_DIVERT_PORTS*sizeof(u16));
+					memset(div_blk->udp_src, 0, MAX_DIVERT_PORTS*sizeof(u16));
+					return 0;
+					break;
+						
+				
+				case DIVCMD_DIVERT:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ENABLE:
+							if (div_blk->divert)
+								return -EALREADY;
+							div_blk->divert=1;
+							break;
+							
+						case DIVARG1_DISABLE:
+							if (!div_blk->divert)
+								return -EALREADY;
+							div_blk->divert=0;
+							break;
+							
+						default:
+							return -EINVAL;
+					}
+					break;
+				
+					
+				case DIVCMD_IP:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ENABLE:
+							if (div_blk->protos&DIVERT_PROTO_IP)
+								return -EALREADY;
+							div_blk->protos|=DIVERT_PROTO_IP;
+							break;
+
+						case DIVARG1_DISABLE:
+							if (!(div_blk->protos&DIVERT_PROTO_IP))
+								return -EALREADY;
+							div_blk->protos&=~DIVERT_PROTO_IP;
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_TCP:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ENABLE:
+							if (div_blk->protos&DIVERT_PROTO_TCP)
+								return -EALREADY;
+							div_blk->protos|=DIVERT_PROTO_TCP;
+							break;
+
+						case DIVARG1_DISABLE:
+							if (!(div_blk->protos&DIVERT_PROTO_TCP))
+								return -EALREADY;
+							div_blk->protos&=~DIVERT_PROTO_TCP;
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_TCPDST:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ADD:
+							return add_port(div_blk->tcp_dst, div_cf.arg2.uint16);
+							break;
+
+						case DIVARG1_REMOVE:
+							return remove_port(div_blk->tcp_dst, div_cf.arg2.uint16);
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_TCPSRC:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ADD:
+							return add_port(div_blk->tcp_src, div_cf.arg2.uint16);
+							break;
+
+						case DIVARG1_REMOVE:
+							return remove_port(div_blk->tcp_src, div_cf.arg2.uint16);
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_UDP:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ENABLE:
+							if (div_blk->protos&DIVERT_PROTO_UDP)
+								return -EALREADY;
+							div_blk->protos|=DIVERT_PROTO_UDP;
+							break;
+
+						case DIVARG1_DISABLE:
+							if (!(div_blk->protos&DIVERT_PROTO_UDP))
+								return -EALREADY;
+							div_blk->protos&=~DIVERT_PROTO_UDP;
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_UDPDST:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ADD:
+							return add_port(div_blk->udp_dst, div_cf.arg2.uint16);
+							break;
+
+						case DIVARG1_REMOVE:
+							return remove_port(div_blk->udp_dst, div_cf.arg2.uint16);
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_UDPSRC:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ADD:
+							return add_port(div_blk->udp_src, div_cf.arg2.uint16);
+							break;
+
+						case DIVARG1_REMOVE:
+							return remove_port(div_blk->udp_src, div_cf.arg2.uint16);
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				case DIVCMD_ICMP:
+					switch(div_cf.arg1.int32)
+					{
+						case DIVARG1_ENABLE:
+							if (div_blk->protos&DIVERT_PROTO_ICMP)
+								return -EALREADY;
+							div_blk->protos|=DIVERT_PROTO_ICMP;
+							break;
+
+						case DIVARG1_DISABLE:
+							if (!(div_blk->protos&DIVERT_PROTO_ICMP))
+								return -EALREADY;
+							div_blk->protos&=~DIVERT_PROTO_ICMP;
+							break;
+
+						default:
+							return -EINVAL;
+					}
+					break;
+
+					
+				default:
+					return -EINVAL;
+			}
+			break;
+
+			
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+
+/*
+ * Check if packet should have its dest mac address set to the box itself
+ * for diversion
+ */
+
+#define	ETH_DIVERT_FRAME(skb) \
+	memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); \
+	skb->pkt_type=PACKET_HOST
+		
+void divert_frame(struct sk_buff *skb)
+{
+	struct ethhdr			*eth=skb->mac.ethernet;
+	struct iphdr			*iph;
+	struct tcphdr			*tcph;
+	struct udphdr			*udph;
+	struct divert_blk		*divert=skb->dev->divert;
+	int						i, src, dst;
+	unsigned char			*skb_data_end=skb->data+skb->len;
+
+	/* Packet is already aimed at us, return */
+	if (skb->pkt_type==PACKET_HOST)
+		return;
+	
+	/* proto is not IP, do nothing */
+	if (eth->h_proto != htons(ETH_P_IP))
+		return;
+	
+	/* Divert all IP frames ? */
+	if (divert->protos&DIVERT_PROTO_IP)
+	{
+		ETH_DIVERT_FRAME(skb);
+		return;
+	}
+	
+	iph=(struct iphdr *)(skb->data);
+	/* Check for possible (maliciously) malformed IP frame (thanks Dave) */
+	if (((iph->ihl<<2)+(unsigned char*)(iph))>=skb_data_end)
+	{
+		printk(KERN_INFO "divert: malformed IP packet !\n");
+		return;
+	}
+	switch(iph->protocol)
+	{
+		/* Divert all ICMP frames ? */
+		case IPPROTO_ICMP:
+			if (divert->protos&DIVERT_PROTO_ICMP)
+			{
+				ETH_DIVERT_FRAME(skb);
+				return;
+			}
+			break;
+	
+		/* Divert all TCP frames ? */
+		case IPPROTO_TCP:
+			if (divert->protos&DIVERT_PROTO_TCP)
+			{
+				ETH_DIVERT_FRAME(skb);
+				return;
+			}
+			tcph=(struct tcphdr *)(((unsigned char *)iph) + (iph->ihl<<2));
+			/* Check for possible (maliciously) malformed IP frame (thanx Dave) */
+			if (((unsigned char *)(tcph+1))>=skb_data_end)
+			{
+				printk(KERN_INFO "divert: malformed TCP packet !\n");
+				return;
+			}
+			/* Divert some tcp dst/src ports only ?*/
+			for (i=0; i<MAX_DIVERT_PORTS; i++)
+			{
+				dst=divert->tcp_dst[i];
+				src=divert->tcp_src[i];
+				if ((dst && dst==tcph->dest) ||
+					(src && src==tcph->source))
+				{
+					ETH_DIVERT_FRAME(skb);
+					return;
+				}
+			}
+			break;
+			
+		/* Divert all UDP frames ? */
+		case IPPROTO_UDP:
+			if (divert->protos&DIVERT_PROTO_UDP)
+			{
+				ETH_DIVERT_FRAME(skb);
+				return;
+			}
+			udph=(struct udphdr *)(((unsigned char *)iph) + (iph->ihl<<2));
+			/* Check for possible (maliciously) malformed IP packet (thanks Dave) */
+			if (((unsigned char *)(udph+1))>=skb_data_end)
+			{
+				printk(KERN_INFO "divert: malformed UDP packet !\n");
+				return;
+			}
+			/* Divert some udp dst/src ports only ? */
+			for (i=0; i<MAX_DIVERT_PORTS; i++)
+			{
+				dst=divert->udp_dst[i];
+				src=divert->udp_src[i];
+				if ((dst && dst==udph->dest) ||
+					(src && src==udph->source))
+				{
+					ETH_DIVERT_FRAME(skb);
+					return;
+				}
+			}
+			break;
+	}
+	return;
+}
+
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/core/sock.c linux.18p17/net/core/sock.c
--- linux.vanilla/net/core/sock.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/core/sock.c	Fri Sep  8 16:53:11 2000
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	$Id: sock.c,v 1.80 1999/05/08 03:04:34 davem Exp $
+ * Version:	$Id: sock.c,v 1.80.2.3 2000/08/10 00:37:05 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/core/sysctl_net_core.c linux.18p17/net/core/sysctl_net_core.c
--- linux.vanilla/net/core/sysctl_net_core.c	Fri Apr 21 12:47:12 2000
+++ linux.18p17/net/core/sysctl_net_core.c	Fri Sep 22 14:57:52 2000
@@ -23,6 +23,9 @@
 
 extern int sysctl_core_destroy_delay;
 extern int sysctl_optmem_max;
+#ifdef CONFIG_NET_DIVERT
+extern char sysctl_divert_version[];
+#endif /* CONFIG_NET_DIVERT */
 
 ctl_table core_table[] = {
 #ifdef CONFIG_NET
@@ -55,6 +58,11 @@
 	{NET_CORE_OPTMEM_MAX, "optmem_max",
 	 &sysctl_optmem_max, sizeof(int), 0644, NULL,
 	 &proc_dointvec},
+#ifdef CONFIG_NET_DIVERT
+	{NET_CORE_DIVERT_VERSION, "divert_version",
+	 (void *)sysctl_divert_version, 32, 0444, NULL,
+	 &proc_dostring},
+#endif /* CONFIG_NET_DIVERT */
 #endif /* CONFIG_NET */
 	{ 0 }
 };
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/Config.in linux.18p17/net/ipv4/Config.in
--- linux.vanilla/net/ipv4/Config.in	Sun Jun 11 21:44:23 2000
+++ linux.18p17/net/ipv4/Config.in	Sat Sep  2 23:47:24 2000
@@ -15,7 +15,7 @@
     bool 'IP: fast network address translation' CONFIG_IP_ROUTE_NAT
   fi
 fi
-bool 'IP: kernel level autoconfiguration' CONFIG_IP_PNP
+bool 'IP: kernel-level configuration support' CONFIG_IP_PNP
 if [ "$CONFIG_IP_PNP" = "y" ]; then
   bool '      DHCP support' CONFIG_IP_PNP_DHCP
   bool '      BOOTP support' CONFIG_IP_PNP_BOOTP
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/af_inet.c linux.18p17/net/ipv4/af_inet.c
--- linux.vanilla/net/ipv4/af_inet.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/af_inet.c	Fri Sep 22 14:57:52 2000
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.87.2.6 2000/01/13 04:28:16 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.87.2.10 2000/09/20 01:47:40 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -109,6 +109,11 @@
 #ifdef CONFIG_BRIDGE
 #include <net/br.h>
 #endif
+ 
+#ifdef CONFIG_NET_DIVERT
+#include <net/divert.h>
+#endif /* CONFIG_NET_DIVERT */
+ 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
@@ -536,7 +541,8 @@
 	snum = ntohs(addr->sin_port);
 #ifdef CONFIG_IP_MASQUERADE
 	/* The kernel masquerader needs some ports. */
-	if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+	if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END) && 
+	   chk_addr_ret != RTN_MULTICAST)
 		return -EADDRINUSE;
 #endif		 
 	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
@@ -912,6 +918,14 @@
 			return -ENOPKG;
 #endif						
 			
+		case SIOCGIFDIVERT:
+		case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+			return(divert_ioctl(cmd, (struct divert_cf *) arg));
+#else
+			return -ENOPKG;
+#endif	/* CONFIG_NET_DIVERT */
+
 		case SIOCADDDLCI:
 		case SIOCDELDLCI:
 #ifdef CONFIG_DLCI
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/fib_rules.c linux.18p17/net/ipv4/fib_rules.c
--- linux.vanilla/net/ipv4/fib_rules.c	Fri Apr 21 12:47:12 2000
+++ linux.18p17/net/ipv4/fib_rules.c	Thu Oct 19 01:19:03 2000
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.9.2.1 2000/10/06 10:34:16 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -224,7 +224,7 @@
 
 		if (addrtype == RTN_NAT) {
 			/* Packet is from  translated source; remember it */
-			saddr = (saddr&~r->r_srcmask)|r->r_srcmap;
+			saddr = r->r_srcmask ? (saddr&~r->r_srcmask)|r->r_srcmap : r->r_srcmap;
 			*flags |= RTCF_SNAT;
 		} else if (addrtype == RTN_LOCAL || r->r_srcmap == 0) {
 			/* Packet is from masqueraded source; remember it */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/icmp.c linux.18p17/net/ipv4/icmp.c
--- linux.vanilla/net/ipv4/icmp.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/net/ipv4/icmp.c	Fri Sep  8 16:53:11 2000
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.52.2.5 2000/04/08 02:45:56 davem Exp $
+ *	Version: $Id: icmp.c,v 1.52.2.6 2000/08/31 23:49:16 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -522,6 +522,7 @@
 	struct icmp_bxm icmp_param;
 	struct rtable *rt = (struct rtable*)skb_in->dst;
 	struct ipcm_cookie ipc;
+	unsigned int offset;
 	u32 saddr;
 	u8  tos;
 	
@@ -651,7 +652,12 @@
 	room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
 	room -= sizeof(struct icmphdr);
 
-	icmp_param.data_len=(iph->ihl<<2)+skb_in->len;
+	if (skb_in->data > skb_in->nh.raw) 
+		offset = (skb_in->data - skb_in->nh.raw);
+	else 
+		offset = 0;
+	icmp_param.data_len = (skb_in->len + offset);
+
 	if (icmp_param.data_len > room)
 		icmp_param.data_len = room;
 	
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_fw.c linux.18p17/net/ipv4/ip_fw.c
--- linux.vanilla/net/ipv4/ip_fw.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/ip_fw.c	Sat Sep 23 13:27:25 2000
@@ -573,7 +573,7 @@
 			strcpy(outskb->data+sizeof(__u32)*2, rif);
 			memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip, 
 			       len-(sizeof(__u32)*2+IFNAMSIZ));
-			netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_KERNEL);
+			netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC);
 		}
 		else {
 			if (net_ratelimit())
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq_irc.c linux.18p17/net/ipv4/ip_masq_irc.c
--- linux.vanilla/net/ipv4/ip_masq_irc.c	Fri Apr 21 12:47:14 2000
+++ linux.18p17/net/ipv4/ip_masq_irc.c	Wed Sep 27 12:25:18 2000
@@ -20,9 +20,8 @@
  *	Juan Jose Ciarlante	:  put new ms entry to listen()
  *	Scottie Shore		:  added support for clients that add extra args
  *	  <sshore@escape.ca>
- *
- * FIXME:
- *	- detect also previous "PRIVMSG" string ?.
+ *	Scottie Shore		:  added support for mIRC DCC resume negotiation
+ *	  <sshore@escape.ca>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -78,22 +77,24 @@
  * List of supported DCC protocols
  */
 
-#define NUM_DCCPROTO 5
-
 struct dccproto 
 {
   char *match;
   int matchlen;
 };
 
-struct dccproto dccprotos[NUM_DCCPROTO] = {
+struct dccproto dccprotos[] = {
  { "SEND ", 5 },
  { "CHAT ", 5 },
  { "MOVE ", 5 },
  { "TSEND ", 6 },
- { "SCHAT ", 6 }
+ { "SCHAT ", 6 },
+ { "ACCEPT ", 7 },
 };
-#define MAXMATCHLEN 6
+
+#define NUM_DCCPROTO (sizeof dccprotos / sizeof dccprotos[0])
+
+#define MAXMATCHLEN 7
 
 static int
 masq_irc_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
@@ -137,6 +138,7 @@
 	 *	strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
 	 *	strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
 	 *	strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
+	 *	strlen("\1DCC ACCEPT F AAAAAAAA P S\1\n")=28
 	 *		AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits)
 	 *		P:         bound port (min 1 d )
 	 *		F:         filename   (min 1 d )
@@ -197,17 +199,26 @@
 				s_port = simple_strtoul(data,&data,10);
 				addr_end_p = data;
 
+				/*	Do we already have a port open for this client?
+				 *	If so, use it (for DCC ACCEPT)
+				 */
+
+				n_ms = ip_masq_out_get(IPPROTO_TCP,
+						htonl(s_addr),htons(s_port),
+						0, 0);
+
 				/*
-				 *	Now create an masquerade entry for it
-				 * 	must set NO_DPORT and NO_DADDR because
+				 *	If we didn't already have a port, we need to make one.
+				 * 	We must set NO_DPORT and NO_DADDR because
 				 *	connection is requested by another client.
 				 */
 
-				n_ms = ip_masq_new(IPPROTO_TCP,
-						maddr, 0,
-						htonl(s_addr),htons(s_port),
-						0, 0,
-						IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR);
+				if (n_ms==NULL)
+					n_ms = ip_masq_new(IPPROTO_TCP,
+							maddr, 0,
+							htonl(s_addr),htons(s_port),
+							0, 0,
+							IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR);
 				if (n_ms==NULL)
 					return 0;
 
@@ -215,7 +226,7 @@
 				 * Replace the old "address port" with the new one
 				 */
 
-				buf_len = sprintf(buf,"%lu %u",
+				buf_len = sprintf(buf,"%u %u",
 						ntohl(n_ms->maddr),ntohs(n_ms->mport));
 
 				/*
@@ -252,6 +263,125 @@
 
 }
 
+int
+masq_irc_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+        struct sk_buff *skb;
+	struct iphdr *iph;
+	struct tcphdr *th;
+	char *data, *data_limit;
+	__u32 s_addr;
+	__u16 s_port;
+	struct ip_masq *n_ms;
+	char buf[20];		/* "m_addr m_port" (dec base)*/
+        unsigned buf_len;
+	int diff;
+        char *dcc_p, *addr_beg_p, *addr_end_p;
+
+        skb = *skb_p;
+	iph = skb->nh.iph;
+        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+        data = (char *)&th[1];
+
+        /*
+	 *	Hunt irc DCC RESUME string:
+	 *
+	 *	strlen("\1DCC RESUME chat AAAAAAAA P\1\n")=29
+	 *		AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits)
+	 *		P:         bound port (min 1 d )
+	 *		F:         filename   (min 1 d )
+	 *		S:         size       (min 1 d ) 
+	 *		0x01, \n:  terminators
+         */
+
+        data_limit = skb->h.raw + skb->len;
+        
+	while (data < (data_limit - 29 ) )
+	{
+		if (memcmp(data,"\1DCC RESUME ",12))  {
+			data ++;
+			continue;
+		}
+
+		dcc_p = data;
+		data += 12;
+
+		while( *data++ != ' ')
+			/*
+			 *	must still parse, at least, "AAAAAAAA P\1\n",
+			 *      12 bytes left.
+			 */
+			if (data > (data_limit-12)) return 0;
+
+
+		addr_beg_p = data;
+
+		/*
+		 *	masq bound address in dec base
+		 */
+
+		s_addr = simple_strtoul(data,&data,10);
+		if (*data++ !=' ')
+		continue;
+
+		/*
+		 *	masq bound port in dec base
+		 */
+
+		s_port = simple_strtoul(data,&data,10);
+		addr_end_p = data;
+
+		/*
+		 *	Find the masq entry associated with this connection.
+		 *	The entry should have no dest addr/port yet.
+		 *	If there is no entry, return 0.
+		 */
+
+		n_ms = ip_masq_in_get(IPPROTO_TCP,
+			0, 0,
+			htonl(s_addr), htons(s_port));
+					
+		if (n_ms==NULL)
+			return 0;
+
+		/*
+		 * Replace the outside address with the inside address
+		 */
+
+		buf_len = sprintf(buf,"%u %u",
+			ntohl(n_ms->saddr),ntohs(n_ms->sport));
+
+		/*
+		 * Calculate required delta-offset to keep TCP happy
+		 */
+
+		diff = buf_len - (addr_end_p-addr_beg_p);
+
+		*addr_beg_p = '\0';
+		IP_MASQ_DEBUG(1-debug, "masq_irc_in(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff);
+
+		/*
+		 *	No shift.
+		 */
+
+		if (diff==0) {
+			/*
+			 * simple case, just copy.
+			 */
+			memcpy(addr_beg_p,buf,buf_len);
+		} else {
+			*skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC,
+				addr_beg_p, addr_end_p-addr_beg_p,
+				buf, buf_len);
+		}
+
+		return diff;
+
+	}
+	return 0;
+
+}
+
 /*
  *	Main irc object
  *     	You need 1 object per port in case you need
@@ -263,12 +393,12 @@
 struct ip_masq_app ip_masq_irc = {
         NULL,			/* next */
 	"irc",			/* name */
-        0,                      /* type */
-        0,                      /* n_attach */
-        masq_irc_init_1,        /* init_1 */
-        masq_irc_done_1,        /* done_1 */
-        masq_irc_out,           /* pkt_out */
-        NULL                    /* pkt_in */
+        0,			/* type */
+        0,			/* n_attach */
+        masq_irc_init_1,	/* init_1 */
+        masq_irc_done_1,	/* done_1 */
+        masq_irc_out,		/* pkt_out */
+        masq_irc_in		/* pkt_in */
 };
 
 /*
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ip_masq_user.c linux.18p17/net/ipv4/ip_masq_user.c
--- linux.vanilla/net/ipv4/ip_masq_user.c	Fri Apr 21 12:47:14 2000
+++ linux.18p17/net/ipv4/ip_masq_user.c	Wed Sep 27 12:11:16 2000
@@ -401,7 +401,7 @@
 
 		magic_control = atomic_read(&ms->n_control);
 		if (!magic_control && ms->control) magic_control = -1;
-		sprintf(temp,"%-4s %08lX:%04X %08lX:%04X %08lX:%04X %-12s %3X %4d %3d %7lu %4d %4d",
+		sprintf(temp,"%-4s %08X:%04X %08X:%04X %08X:%04X %-12s %3X %4d %3d %7lu %4d %4d",
 			masq_proto_name(ms->protocol),
 			ntohl(ms->saddr), ntohs(ms->sport),
 			ntohl(ms->daddr), ntohs(ms->dport),
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/ipconfig.c linux.18p17/net/ipv4/ipconfig.c
--- linux.vanilla/net/ipv4/ipconfig.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/net/ipv4/ipconfig.c	Sat Sep  2 23:47:24 2000
@@ -74,8 +74,9 @@
 #define CONF_POST_OPEN		(1*HZ)	/* After opening: 1 second */
 
 /* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */
-#define CONF_OPEN_RETRIES 	3	/* (Re)open devices three times */
-#define CONF_SEND_RETRIES 	3	/* Send requests three times */
+#define CONF_OPEN_RETRIES 	2	/* (Re)open devices twice */
+#define CONF_SEND_RETRIES 	6	/* Send six requests per open */
+#define CONF_INTER_TIMEOUT	(HZ/2)	/* Inter-device timeout: 1/2 second */
 #define CONF_BASE_TIMEOUT	(HZ*2)	/* Initial timeout: 2 seconds */
 #define CONF_TIMEOUT_RANDOM	(HZ)	/* Maximum amount of randomization */
 #define CONF_TIMEOUT_MULT	*7/4	/* Rate of timeout growth */
@@ -86,7 +87,7 @@
  * Public IP configuration
  */
 
-int ic_enable __initdata = 1;			/* Automatic IP cfg enabled? */
+int ic_enable __initdata = 0;			/* IP config enabled? */
 
 /* Protocol choice */
 static int ic_proto_enabled __initdata = 0
@@ -129,10 +130,11 @@
 static int ic_proto_have_if __initdata = 0;
 
 #ifdef IPCONFIG_DYNAMIC
-static volatile int ic_got_reply = 0;	    /* Protocol(s) we got reply from */
+static spinlock_t ic_recv_lock = SPIN_LOCK_UNLOCKED;
+static volatile int ic_got_reply __initdata = 0;    /* Proto(s) that replied */
 #endif
 #ifdef IPCONFIG_DHCP
-static int ic_dhcp_msgtype __initdata = 0;	/* Last incoming msg type */
+static int ic_dhcp_msgtype __initdata = 0;	/* DHCP msg type received */
 #endif
 
 
@@ -144,11 +146,12 @@
 	struct ic_device *next;
 	struct device *dev;
 	unsigned short flags;
-	int able;
+	short able;
+	u32 xid;
 };
 
-static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
-static struct device *ic_dev __initdata = NULL;		/* Selected device */
+static struct ic_device *ic_first_dev __initdata = NULL; /* Open devices */
+static struct device *ic_dev __initdata = NULL;		 /* Selected device */
 
 static int __init ic_open_devs(void)
 {
@@ -185,8 +188,13 @@
 			last = &d->next;
 			d->flags = oflags;
 			d->able = able;
+			if (able & IC_BOOTP)
+				get_random_bytes(&d->xid, sizeof(u32));
+			else
+				d->xid = 0;
 			ic_proto_have_if |= able;
-			DBG((SELF "%s UP (able=%d)\n", dev->name, able));
+			DBG((SELF "%s UP (able=%d, xid=%08x)\n",
+			     dev->name, able, d->xid));
 		}
 	*last = NULL;
 
@@ -386,11 +394,22 @@
 	unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
 	unsigned long sip, tip;
 	unsigned char *sha, *tha;		/* s for "source", t for "target" */
+	struct ic_device *d;
+
+	/* One reply at a time, please. */
+	spin_lock(&ic_recv_lock);
 
 	/* If we already have a reply, just drop the packet */
 	if (ic_got_reply)
 		goto drop;
 
+	/* Find the ic_device that the packet arrived on */
+	d = ic_first_dev;
+	while (d && d->dev != dev)
+		d = d->next;
+	if (!d)
+		goto drop;	/* should never happen */
+
 	/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
 	if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd))
 		goto drop;
@@ -420,7 +439,7 @@
 	if (ic_servaddr != INADDR_NONE && ic_servaddr != sip)
 		goto drop;
 
-	/* Victory! The packet is what we were looking for! */
+	/* We have a winner! */
 	ic_dev = dev;
 	if (ic_myaddr == INADDR_NONE)
 		ic_myaddr = tip;
@@ -428,8 +447,12 @@
 	ic_got_reply = IC_RARP;
 
 drop:
-	/* and throw the packet out */
+	/* Show's over.  Nothing to see here.  */
+	spin_unlock(&ic_recv_lock);
+
+	/* Throw the packet out. */
 	kfree_skb(skb);
+
 	return 0;
 }
 
@@ -437,16 +460,23 @@
 /*
  *  Send RARP request packet over all devices which allow RARP.
  */
+static void __init ic_rarp_send_if(struct ic_device *d)
+{
+	struct device *dev = d->dev;
+	arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+		 dev->dev_addr, dev->dev_addr);
+}
+
+/*
+ *  Send RARP request package over a single interface.
+ */
 static void __init ic_rarp_send(void)
 {
 	struct ic_device *d;
 
 	for (d=ic_first_dev; d; d=d->next)
-		if (d->able & IC_RARP) {
-			struct device *dev = d->dev;
-			arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
-				 dev->dev_addr, dev->dev_addr);
-		}
+		if (d->able & IC_RARP)
+			ic_rarp_send_if(d);
 }
 
 #endif
@@ -491,8 +521,6 @@
 #define DHCPRELEASE	7
 #define DHCPINFORM	8
 
-static u32 ic_bootp_xid;
-
 static int ic_bootp_recv(struct sk_buff *skb, struct device *dev,
 			 struct packet_type *pt);
 
@@ -516,12 +544,12 @@
 static void __init
 ic_dhcp_init_options(u8 *options)
 {
-	u8 msgtype = ((ic_dhcp_msgtype == DHCPOFFER)
-		      ? DHCPREQUEST : DHCPDISCOVER);
+	u8 mt = ((ic_servaddr == INADDR_NONE)
+		 ? DHCPDISCOVER : DHCPREQUEST);
 	u8 *e = options;
 
 #ifdef IPCONFIG_DEBUG
-	printk("DHCP: Sending message type %d\n", msgtype);
+	printk("DHCP: Sending message type %d\n", mt);
 #endif
 
 	memcpy(e, ic_bootp_cookie, 4);	/* RFC1048 Magic Cookie */
@@ -529,9 +557,14 @@
 
 	*e++ = 53;		/* DHCP message type */
 	*e++ = 1;
-	*e++ = msgtype;
+	*e++ = mt;
+
+	if (mt == DHCPREQUEST) {
+		*e++ = 54;	/* Server ID (IP address) */
+		*e++ = 4;
+		memcpy(e, &ic_servaddr, 4);
+		e += 4;
 
-	if (msgtype == DHCPREQUEST) {
 		*e++ = 50;	/* Requested IP address */
 		*e++ = 4;
 		memcpy(e, &ic_myaddr, 4);
@@ -595,8 +628,6 @@
 static inline void
 ic_bootp_init(void)
 {
-	get_random_bytes(&ic_bootp_xid, sizeof(u32));
-	DBG(("DHCP/BOOTP: XID=%08x\n", ic_bootp_xid));
 	dev_add_pack(&bootp_packet_type);
 }
 
@@ -656,7 +687,7 @@
 	b->server_ip = INADDR_NONE;
 	memcpy(b->hw_addr, dev->dev_addr, dev->addr_len);
 	b->secs = htons(jiffies / HZ);
-	b->xid = ic_bootp_xid;
+	b->xid = d->xid;
 
 	/* add DHCP options or BOOTP extensions */
 #ifdef IPCONFIG_DHCP
@@ -756,12 +787,23 @@
 {
 	struct bootp_pkt *b = (struct bootp_pkt *) skb->nh.iph;
 	struct iphdr *h = &b->iph;
+	struct ic_device *d;
 	int len;
 
+	/* One reply at a time, please. */
+	spin_lock(&ic_recv_lock);
+
 	/* If we already have a reply, just drop the packet */
 	if (ic_got_reply)
 		goto drop;
 
+	/* Find the ic_device that the packet arrived on */
+	d = ic_first_dev;
+	while (d && d->dev != dev)
+		d = d->next;
+	if (!d)
+		goto drop;  /* should never happen */
+
 	/* Check whether it's a BOOTP packet */
 	if (skb->pkt_type == PACKET_OTHERHOST ||
 	    skb->len < sizeof(struct udphdr) + sizeof(struct iphdr) ||
@@ -783,9 +825,9 @@
 
 	/* Is it a reply to our BOOTP request? */
 	len = ntohs(b->udph.len) - sizeof(struct udphdr);
-	if (len < 300 ||				    /* See RFC 951:2.1 */
+	if (len < 300 ||		/* See RFC 951:2.1 */
 	    b->op != BOOTP_REPLY ||
-	    b->xid != ic_bootp_xid) {
+	    b->xid != d->xid) {
 		printk("?");
 		goto drop;
 	}
@@ -798,7 +840,7 @@
 #ifdef IPCONFIG_DHCP
 
 		u32 server_id = INADDR_NONE;
-		ic_dhcp_msgtype = 0;
+		int mt = 0;
 
 		ext = &b->exten[4];
 		while (ext < end && *ext != 0xff) {
@@ -811,7 +853,7 @@
 			switch (*opt) {
 			    case 53:	/* Message type */
 				if (opt[1])
-					ic_dhcp_msgtype = opt[2];
+					mt = opt[2];
 				break;
 			    case 54:	/* Server ID (IP address) */
 				if (opt[1] >= 4)
@@ -821,11 +863,15 @@
 		}
 
 #ifdef IPCONFIG_DEBUG
-		printk("DHCP: Got message type %d\n", ic_dhcp_msgtype);
+		printk("DHCP: Got message type %d\n", mt);
 #endif
 
-		switch (ic_dhcp_msgtype) {
+		switch (mt) {
 		    case DHCPOFFER:
+			/* While in the process of accepting one offer,
+			   ignore all others. */
+			if (ic_myaddr != INADDR_NONE)
+				goto drop;
 			/* Let's accept that offer. */
 			ic_myaddr = b->your_ip;
 			ic_servaddr = server_id;
@@ -833,7 +879,7 @@
 			printk("DHCP: Offered address %s", in_ntoa(ic_myaddr));
 			printk(" by server %s\n", in_ntoa(ic_servaddr));
 #endif
-			goto drop;
+			break;
 
 		    case DHCPACK:
 			/* Yeah! */
@@ -846,6 +892,8 @@
 			goto drop;
 		}
 
+		ic_dhcp_msgtype = mt;
+
 #endif /* IPCONFIG_DHCP */
 
 		ext = &b->exten[4];
@@ -859,6 +907,7 @@
 		}
 	}
 
+	/* We have a winner! */
 	ic_dev = dev;
 	ic_myaddr = b->your_ip;
 	ic_servaddr = b->server_ip;
@@ -869,8 +918,12 @@
 	ic_got_reply = IC_BOOTP;
 
 drop:
-	/* and throw the packet out */
+	/* Show's over.  Nothing to see here.  */
+	spin_unlock(&ic_recv_lock);
+
+	/* Throw the packet out. */
 	kfree_skb(skb);
+
 	return 0;
 }	
 
@@ -887,8 +940,8 @@
 static int __init ic_dynamic(void)
 {
 	int retries;
-	unsigned long timeout, jiff;
-	unsigned long start_jiffies;
+	struct ic_device *d;
+	unsigned long start_jiffies, timeout, jiff;
 	int do_bootp = ic_proto_have_if & IC_BOOTP;
 	int do_rarp = ic_proto_have_if & IC_RARP;
 
@@ -936,36 +989,39 @@
 	 * [Actually we could now, but the nothing else running note still 
 	 *  applies.. - AC]
 	 */
-	printk(KERN_NOTICE "Sending %s%s%s requests ",
+	printk(KERN_NOTICE "Sending %s%s%s requests .",
 	       do_bootp
 		? ((ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP") : "",
 	       (do_bootp && do_rarp) ? " and " : "",
 	       do_rarp ? "RARP" : "");
+
 	start_jiffies = jiffies;
+	d = ic_first_dev;
 	retries = CONF_SEND_RETRIES;
 	get_random_bytes(&timeout, sizeof(timeout));
 	timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
 	for(;;) {
 #ifdef IPCONFIG_BOOTP
-		if (do_bootp)
-			ic_bootp_send(jiffies - start_jiffies);
+		if (do_bootp && (d->able & IC_BOOTP))
+			ic_bootp_send_if(d, jiffies - start_jiffies);
 #endif
 #ifdef IPCONFIG_RARP
-		if (do_rarp)
-			ic_rarp_send();
+		if (do_rarp && (d->able & IC_RARP))
+			ic_rarp_send_if(d);
 #endif
-		printk(".");
 
-		jiff = jiffies + timeout;
+		jiff = jiffies + (d->next ? CONF_INTER_TIMEOUT : timeout);
 		while (jiffies < jiff && !ic_got_reply)
 			;
+
 #ifdef IPCONFIG_DHCP
+		/* DHCP isn't done until we get a DHCPACK. */
 		if ((ic_got_reply & IC_BOOTP)
 		    && (ic_proto_enabled & IC_USE_DHCP)
 		    && ic_dhcp_msgtype != DHCPACK)
 		{
-			printk(",");
 			ic_got_reply = 0;
+			printk(",");
 			continue;
 		}
 #endif /* IPCONFIG_DHCP */
@@ -975,14 +1031,21 @@
 			break;
 		}
 
+		if ((d = d->next))
+			continue;
+
 		if (! --retries) {
 			printk(" timed out!\n");
 			break;
 		}
 
+		d = ic_first_dev;
+
 		timeout = timeout CONF_TIMEOUT_MULT;
 		if (timeout > CONF_TIMEOUT_MAX)
 			timeout = CONF_TIMEOUT_MAX;
+
+		printk(".");
 	}
 
 #ifdef IPCONFIG_BOOTP
@@ -997,7 +1060,8 @@
 	if (!ic_got_reply)
 		return -1;
 
-	printk(SELF "Got %s answer from %s\n",
+	printk(SELF "%s: Got %s answer from %s\n",
+	       ic_dev->name, 
 	       ((ic_got_reply & IC_RARP) ? "RARP"
 		: (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
 	       in_ntoa(ic_servaddr));
@@ -1167,7 +1231,7 @@
 	 * Clue in the operator.
 	 */
 	printk(SELF "Complete:");
-	printk("\n      device=%s", ic_dev->name);
+	printk( "\n     if=%s", ic_dev->name);
 	printk(", addr=%s", in_ntoa(ic_myaddr));
 	printk(", mask=%s", in_ntoa(ic_netmask));
 	printk(", gw=%s", in_ntoa(ic_gateway));
@@ -1199,14 +1263,14 @@
  *			  by BOOTP
  *	<device>	- use all available devices
  *	<PROTO>:
- *	   dhcp|bootp|rarp  - use given protocol
- *	   both or empty    - use both BOOTP and RARP (not DHCP)
- *	   off or none	    - don't do autoconfig at all
+ *	   off|none	    - don't do autoconfig at all (DEFAULT)
+ *	   on|any           - use any configured protocol
+ *	   dhcp|bootp|rarp  - use only the specified protocol
+ *	   both             - use both BOOTP and RARP (not DHCP)
  */
 static int __init ic_proto_name(char *name)
 {
-	if (!strcmp(name, "off") || !strcmp(name, "none")) {
-		ic_proto_enabled = 0;
+	if (!strcmp(name, "on") || !strcmp(name, "any")) {
 		return 1;
 	}
 #ifdef CONFIG_IP_PNP_DHCP
@@ -1241,10 +1305,10 @@
 	char *cp, *ip, *dp;
 	int num = 0;
 
-	if (!strcmp(addrs, "off") || !strcmp(addrs, "none")) {
-		ic_enable = 0;
+	ic_enable = (*addrs && strcmp(addrs, "off") && strcmp(addrs, "none"));
+	if (!ic_enable)
 		return;
-	}
+
 	if (ic_proto_name(addrs))
 		return;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/proc.c linux.18p17/net/ipv4/proc.c
--- linux.vanilla/net/ipv4/proc.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/proc.c	Sun Sep 17 17:56:18 2000
@@ -7,7 +7,7 @@
  *		PROC file system.  It is mainly used for debugging and
  *		statistics.
  *
- * Version:	$Id: proc.c,v 1.34.2.1 2000/01/14 01:22:20 davem Exp $
+ * Version:	$Id: proc.c,v 1.34.2.3 2000/06/21 02:14:45 davem Exp $
  *
  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/route.c linux.18p17/net/ipv4/route.c
--- linux.vanilla/net/ipv4/route.c	Fri Apr 21 12:47:15 2000
+++ linux.18p17/net/ipv4/route.c	Fri Sep  8 16:53:11 2000
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.67.2.4 1999/11/16 02:28:43 davem Exp $
+ * Version:	$Id: route.c,v 1.67.2.5 2000/09/01 23:19:46 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -222,8 +222,8 @@
 				(unsigned long)r->rt_dst,
 				(unsigned long)r->rt_gateway,
 				r->rt_flags,
-				atomic_read(&r->u.dst.use),
 				atomic_read(&r->u.dst.refcnt),
+				atomic_read(&r->u.dst.use),
 				0,
 				(unsigned long)r->rt_src, (int)r->u.dst.pmtu,
 				r->u.dst.window,
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/sysctl_net_ipv4.c linux.18p17/net/ipv4/sysctl_net_ipv4.c
--- linux.vanilla/net/ipv4/sysctl_net_ipv4.c	Sun Jun 11 21:44:23 2000
+++ linux.18p17/net/ipv4/sysctl_net_ipv4.c	Sun Sep 17 17:56:18 2000
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.38.2.3 2000/04/17 05:46:06 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.38.2.4 2000/09/16 09:40:00 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -15,19 +15,6 @@
 #include <net/route.h>
 #include <net/tcp.h>
 
-/*
- *	TCP configuration parameters
- */
-
-#define TCP_PMTU_DISC	0x00000001	/* perform PMTU discovery	  */
-#define TCP_CONG_AVOID	0x00000002	/* congestion avoidance algorithm */
-#define TCP_DELAY_ACKS	0x00000003	/* delayed ack stategy		  */
-
-#if 0
-static int boolean_min = 0;
-static int boolean_max = 1;
-#endif
-
 /* From icmp.c */
 extern int sysctl_icmp_echo_ignore_all;
 extern int sysctl_icmp_echo_ignore_broadcasts;
@@ -74,7 +61,10 @@
 /* From igmp.c */
 extern int sysctl_igmp_max_memberships;
 
-int tcp_retr1_max = 255; 
+static int tcp_retr1_max = 255; 
+
+static int ip_local_port_range_min[] = { 1, 1 };
+static int ip_local_port_range_max[] = { 65535, 65535 };
 
 struct ipv4_config ipv4_config;
 
@@ -184,7 +174,8 @@
 	 sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_LOCAL_PORT_RANGE, "ip_local_port_range",
 	 &sysctl_local_port_range, sizeof(sysctl_local_port_range), 0644, 
-	 NULL, &proc_dointvec},
+	 NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 ip_local_port_range_min, ip_local_port_range_max },
 	{NET_IPV4_ICMP_ECHO_IGNORE_ALL, "icmp_echo_ignore_all",
 	 &sysctl_icmp_echo_ignore_all, sizeof(int), 0644, NULL,
 	 &proc_dointvec},
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/tcp.c linux.18p17/net/ipv4/tcp.c
--- linux.vanilla/net/ipv4/tcp.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/tcp.c	Thu Oct 19 01:19:03 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.140.2.11 2000/04/17 05:57:01 davem Exp $
+ * Version:	$Id: tcp.c,v 1.140.2.15 2000/10/06 23:29:33 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -922,7 +922,7 @@
 				tmp += copy;
 				queue_it = 0;
 			}
-			skb = sock_wmalloc_err(sk, tmp, 0, GFP_KERNEL, &err);
+			skb = sock_wmalloc_err(sk, tmp, 0, sk->allocation, &err);
 
 			/* If we didn't get any memory, we need to sleep. */
 			if (skb == NULL) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/tcp_input.c linux.18p17/net/ipv4/tcp_input.c
--- linux.vanilla/net/ipv4/tcp_input.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/tcp_input.c	Sun Sep 17 17:54:23 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.164.2.15 2000/05/27 04:01:49 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.164.2.17 2000/09/16 16:32:57 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -367,16 +367,16 @@
 
 		switch (opcode) {
 			case TCPOPT_EOL:
-				return;
+				goto check_syn;
 			case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
 				length--;
 				continue;
 			default:
 				opsize=*ptr++;
 				if (opsize < 2) /* "silly options" */
-					return;
+					goto check_syn;
 				if (opsize > length)
-					break;	/* don't parse partial options */
+					goto check_syn;	/* don't parse partial options */
 	  			switch(opcode) {
 				case TCPOPT_MSS:
 					if(opsize==TCPOLEN_MSS && th->syn) {
@@ -439,7 +439,8 @@
 	  			length-=opsize;
 	  	};
 	}
-	if(th->syn && saw_mss == 0)
+check_syn:
+	if (th->syn && saw_mss == 0)
 		tp->mss_clamp = 536;
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/tcp_ipv4.c linux.18p17/net/ipv4/tcp_ipv4.c
--- linux.vanilla/net/ipv4/tcp_ipv4.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/tcp_ipv4.c	Fri Sep  8 16:53:11 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.175.2.13 1999/11/16 06:33:53 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.175.2.16 2000/06/28 04:54:17 davem Exp $
  *
  *		IPv4 specific functions
  *
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv4/tcp_output.c linux.18p17/net/ipv4/tcp_output.c
--- linux.vanilla/net/ipv4/tcp_output.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/ipv4/tcp_output.c	Fri Sep  8 16:53:11 2000
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.108.2.6 2000/02/07 20:20:11 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.108.2.9 2000/09/02 01:27:51 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -811,7 +811,6 @@
  */
 void tcp_send_active_reset(struct sock *sk)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
 
 	/* NOTE: No TCP options attached and we never retransmit this. */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv6/ip6_input.c linux.18p17/net/ipv6/ip6_input.c
--- linux.vanilla/net/ipv6/ip6_input.c	Fri Apr 21 12:47:16 2000
+++ linux.18p17/net/ipv6/ip6_input.c	Sun Sep 17 17:56:18 2000
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
  *
- *	$Id: ip6_input.c,v 1.11.2.1 1999/08/14 04:02:43 davem Exp $
+ *	$Id: ip6_input.c,v 1.11.2.2 2000/09/13 01:27:53 davem Exp $
  *
  *	Based in linux/net/ipv4/ip_input.c
  *
@@ -99,7 +99,7 @@
 	struct raw6_opt *opt;
 
 	opt = &sk->tp_pinfo.tp_raw;
-	icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+	icmph = (struct icmp6hdr *) skb->h.raw;
 	return test_bit(icmph->icmp6_type, &opt->filter);
 }
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv6/ip6_output.c linux.18p17/net/ipv6/ip6_output.c
--- linux.vanilla/net/ipv6/ip6_output.c	Fri Apr 21 12:47:16 2000
+++ linux.18p17/net/ipv6/ip6_output.c	Sun Sep 17 17:56:18 2000
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $
+ *	$Id: ip6_output.c,v 1.17.2.1 2000/09/13 01:27:53 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -121,7 +121,7 @@
 
 		if (skb_headroom(skb) < head_room) {
 			struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
-			kfree(skb);
+			kfree_skb(skb);
 			skb = skb2;
 			if (skb == NULL)
 				return -ENOBUFS;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv6/proc.c linux.18p17/net/ipv6/proc.c
--- linux.vanilla/net/ipv6/proc.c	Fri Apr 21 12:47:16 2000
+++ linux.18p17/net/ipv6/proc.c	Thu Oct 19 01:22:07 2000
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.9.2.1 2000/01/14 01:22:24 davem Exp $
+ * Version:	$Id: proc.c,v 1.9.2.2 2000/10/18 17:55:00 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -59,7 +59,7 @@
 		int tw_bucket = 0;
 
 		pos += 149;
-		if(pos < offset)
+		if(pos <= offset)
 			goto next;
 		tp = &(sp->tp_pinfo.af_tcp);
 		if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipv6/reassembly.c linux.18p17/net/ipv6/reassembly.c
--- linux.vanilla/net/ipv6/reassembly.c	Fri Apr 21 12:47:16 2000
+++ linux.18p17/net/ipv6/reassembly.c	Sun Sep 17 17:56:18 2000
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $
+ *	$Id: reassembly.c,v 1.11.2.1 2000/09/13 00:49:39 davem Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
@@ -160,6 +160,15 @@
 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
 		return NULL;
 	}
+ 
+	if (!(fhdr->frag_off & __constant_htons(0xFFF9))) {
+		/* It is not a fragmented frame */
+		skb->h.raw += sizeof(struct frag_hdr);
+		ipv6_statistics.Ip6ReasmOKs++;
+
+		return &fhdr->nexthdr;
+	}
+
 	if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
 		frag_prune();
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/ipx/af_ipx.c linux.18p17/net/ipx/af_ipx.c
--- linux.vanilla/net/ipx/af_ipx.c	Sun Jun 11 21:44:24 2000
+++ linux.18p17/net/ipx/af_ipx.c	Fri Sep 29 10:11:45 2000
@@ -1605,7 +1605,7 @@
 				       s->protinfo.af_ipx.node[5],
 				       htons(s->protinfo.af_ipx.port));
 #else
-			len += sprintf(buffer+len,"%08lX:%04X  ",
+			len += sprintf(buffer+len,"%08X:%04X  ",
 				       htonl(i->if_netnum),
 				       htons(s->protinfo.af_ipx.port));
 #endif	/* CONFIG_IPX_INTERN */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/irda/compressors/Config.in linux.18p17/net/irda/compressors/Config.in
--- linux.vanilla/net/irda/compressors/Config.in	Fri Apr 21 12:47:16 2000
+++ linux.18p17/net/irda/compressors/Config.in	Mon Sep 11 17:14:10 2000
@@ -4,7 +4,7 @@
 if [ "$CONFIG_IRDA_COMPRESSION" != "n" ]; then
    comment '    IrDA compressors'
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-      dep_tristate '    Deflate compression (EXPERIMENTAL)' CONFIG_IRDA_DEFLATE $CONFIG_IRDA
+      dep_tristate '    Deflate compression (EXPERIMENTAL)' CONFIG_IRDA_DEFLATE $CONFIG_IRDA m
    fi
 #   tristate '    BZIP2 compression' CONFIG_IRDA_BZIP2
 #   tristate '    BSD compression' CONFIG_IRDA_BSD
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/netsyms.c linux.18p17/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Sun Jun 11 21:44:24 2000
+++ linux.18p17/net/netsyms.c	Fri Sep 22 14:57:52 2000
@@ -33,6 +33,10 @@
 #include <net/br.h>
 #endif
 
+#ifdef CONFIG_NET_DIVERT
+#include <net/divert.h>
+#endif /* CONFIG_NET_DIVERT */
+
 #ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <net/protocol.h>
@@ -224,6 +228,12 @@
 #ifdef CONFIG_BRIDGE 
 EXPORT_SYMBOL(br_ioctl);
 #endif
+
+#ifdef CONFIG_NET_DIVERT
+EXPORT_SYMBOL(alloc_divert_blk);
+EXPORT_SYMBOL(free_divert_blk);
+EXPORT_SYMBOL(divert_ioctl);
+#endif /* CONFIG_NET_DIVERT */
 
 #ifdef CONFIG_INET
 /* Internet layer registration */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/packet/af_packet.c linux.18p17/net/packet/af_packet.c
--- linux.vanilla/net/packet/af_packet.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/packet/af_packet.c	Fri Sep 22 14:57:52 2000
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.19.2.1 1999/12/04 05:45:02 davem Exp $
+ * Version:	$Id: af_packet.c,v 1.19.2.2 2000/09/20 01:47:40 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -72,6 +72,10 @@
 #include <net/br.h>
 #endif
 
+#ifdef CONFIG_NET_DIVERT
+#include <net/divert.h>
+#endif /* CONFIG_NET_DIVERT */
+
 #ifdef CONFIG_DLCI
 extern int dlci_ioctl(unsigned int, void*);
 #endif
@@ -1132,6 +1136,14 @@
 #else
 			return -ENOPKG;
 #endif						
+			
+		case SIOCGIFDIVERT:
+		case SIOCSIFDIVERT:
+#ifdef CONFIG_NET_DIVERT
+			return(divert_ioctl(cmd, (struct divert_cf *) arg));
+#else
+			return -ENOPKG;
+#endif /* CONFIG_NET_DIVERT */
 			
 #ifdef CONFIG_INET
 		case SIOCADDRT:
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/socket.c linux.18p17/net/socket.c
--- linux.vanilla/net/socket.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/socket.c	Sat Sep 23 13:20:51 2000
@@ -579,7 +579,7 @@
 	 *	Check protocol is in range
 	 */
 	if(family<0||family>=NPROTO)
-		return -EINVAL;
+		return -EAFNOSUPPORT;
 		
 #if defined(CONFIG_KMOD) && defined(CONFIG_NET)
 	/* Attempt to load a protocol module if the find failed. 
@@ -597,7 +597,7 @@
 #endif
 
 	if (net_families[family]==NULL)
-		return -EINVAL;
+		return -EAFNOSUPPORT;
 
 /*
  *	Check that this is a type that we know how to manipulate and
@@ -1165,7 +1165,6 @@
 			/* Note - when this code becomes multithreaded on
 			 * SMP machines you have a race to fix here.
 			 */
-			err = -ENOBUFS;
 			ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
 			if (ctl_buf == NULL) 
 				goto out_freeiov;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/auth.c linux.18p17/net/sunrpc/auth.c
--- linux.vanilla/net/sunrpc/auth.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/auth.c	Sat Oct 14 00:36:07 2000
@@ -78,6 +78,15 @@
 	auth->au_nextgc = jiffies + (auth->au_expire >> 1);
 }
 
+static inline void
+rpcauth_crdestroy(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+	if (auth->au_ops->crdestroy)
+		auth->au_ops->crdestroy(cred);
+	else
+		rpc_free(cred);
+}
+
 /*
  * Clear the RPC credential cache
  */
@@ -107,17 +116,14 @@
 rpcauth_gc_credcache(struct rpc_auth *auth)
 {
 	struct rpc_cred	**q, *cred, *free = NULL;
-	int		i, safe = 0;
+	int		i;
 
 	dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
 	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
 		q = &auth->au_credcache[i];
 		while ((cred = *q) != NULL) {
-			if (++safe > 500) {
-				printk("RPC: rpcauth_gc_credcache looping!\n");
-				break;
-			}
-			if (!cred->cr_count && time_before(cred->cr_expire, jiffies)) {
+			if (!cred->cr_count &&
+			    time_before(cred->cr_expire, jiffies)) {
 				*q = cred->cr_next;
 				cred->cr_next = free;
 				free = cred;
@@ -128,7 +134,7 @@
 	}
 	while ((cred = free) != NULL) {
 		free = cred->cr_next;
-		rpc_free(cred);
+		rpcauth_crdestroy(auth, cred);
 	}
 	auth->au_nextgc = jiffies + auth->au_expire;
 }
@@ -136,36 +142,37 @@
 /*
  * Insert credential into cache
  */
-inline void
+void
 rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	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_expire = jiffies + auth->au_expire;
 	cred->cr_count++;
+	cred->cr_expire = jiffies + auth->au_expire;
 }
 
 /*
  * Look up a process' credentials in the authentication cache
  */
 static struct rpc_cred *
-rpcauth_lookup_credcache(struct rpc_task *task)
+rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
 	struct rpc_cred	**q, *cred = NULL;
-	int		nr;
+	int		nr = 0;
 
-	nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
+	if (!(taskflags & RPC_TASK_ROOTCREDS))
+		nr = current->uid & RPC_CREDCACHE_MASK;
 
 	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
 
 	q = &auth->au_credcache[nr];
 	while ((cred = *q) != NULL) {
-		if (auth->au_ops->crmatch(task, cred)) {
+		if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) &&
+		    auth->au_ops->crmatch(cred, taskflags)) {
 			*q = cred->cr_next;
 			break;
 		}
@@ -173,7 +180,7 @@
 	}
 
 	if (!cred)
-		cred = auth->au_ops->crcreate(task);
+		cred = auth->au_ops->crcreate(taskflags);
 
 	if (cred)
 		rpcauth_insert_credcache(auth, cred);
@@ -184,76 +191,97 @@
 /*
  * Remove cred handle from cache
  */
-static inline void
+static void
 rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	struct rpc_cred	**q, *cr;
 	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) {
 			*q = cred->cr_next;
-			return;
+			cred->cr_next = NULL;
+			break;
 		}
 		q = &cred->cr_next;
 	}
 }
 
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_task *task)
+rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
 {
+	dprintk("RPC:     looking up %s cred\n",
+		auth->au_ops->au_name);
+	return rpcauth_lookup_credcache(auth, taskflags);
+}
+
+struct rpc_cred *
+rpcauth_bindcred(struct rpc_task *task)
+{
+	struct rpc_auth *auth = task->tk_auth;
+
 	dprintk("RPC: %4d looking up %s cred\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name);
-	return task->tk_cred = rpcauth_lookup_credcache(task);
+	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags);
+	if (task->tk_msg.rpc_cred == 0)
+		task->tk_status = -ENOMEM;
+	return task->tk_msg.rpc_cred;
 }
 
 int
-rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred)
+rpcauth_matchcred(struct rpc_auth *auth, struct rpc_cred *cred, int taskflags)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-
-	dprintk("RPC: %4d matching %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	return auth->au_ops->crmatch(task, cred);
+	dprintk("RPC:     matching %s cred %d\n",
+		auth->au_ops->au_name, taskflags);
+	return auth->au_ops->crmatch(cred, taskflags);
 }
 
 void
 rpcauth_holdcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d holding %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_count++;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred) {
+		task->tk_msg.rpc_cred->cr_count++;
+		task->tk_msg.rpc_cred->cr_expire = jiffies + task->tk_auth->au_expire;
+	}
 }
 
 void
-rpcauth_releasecred(struct rpc_task *task)
+rpcauth_releasecred(struct rpc_auth *auth, struct rpc_cred *cred)
 {
-	struct rpc_auth	*auth = task->tk_auth;
-	struct rpc_cred	*cred;
-
-	dprintk("RPC: %4d releasing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
-	if ((cred = task->tk_cred) != NULL) {
+	if (cred != NULL && cred->cr_count > 0) {
 		cred->cr_count--;
 		if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
 			rpcauth_remove_credcache(auth, cred);
 			if (!cred->cr_count)
-				auth->au_ops->crdestroy(cred);
+				rpcauth_crdestroy(auth, cred);
 		}
-		task->tk_cred = NULL;
 	}
 }
 
+void
+rpcauth_unbindcred(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
+
+	dprintk("RPC: %4d releasing %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, cred);
+
+	rpcauth_releasecred(auth, cred);
+	task->tk_msg.rpc_cred = NULL;
+}
+
 u32 *
 rpcauth_marshcred(struct rpc_task *task, u32 *p)
 {
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d marshaling %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crmarshal(task, p,
 				task->tk_flags & RPC_CALL_REALUID);
 }
@@ -264,7 +292,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d validating %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	return auth->au_ops->crvalidate(task, p);
 }
 
@@ -274,7 +302,7 @@
 	struct rpc_auth	*auth = task->tk_auth;
 
 	dprintk("RPC: %4d refreshing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+		task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred);
 	task->tk_status = auth->au_ops->crrefresh(task);
 	return task->tk_status;
 }
@@ -283,14 +311,14 @@
 rpcauth_invalcred(struct rpc_task *task)
 {
 	dprintk("RPC: %4d invalidating %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
-	if (task->tk_cred)
-		task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
+	if (task->tk_msg.rpc_cred)
+		task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
 }
 
 int
 rpcauth_uptodatecred(struct rpc_task *task)
 {
-	return !(task->tk_cred) ||
-		(task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+	return !(task->tk_msg.rpc_cred) ||
+		(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/auth_null.c linux.18p17/net/sunrpc/auth_null.c
--- linux.vanilla/net/sunrpc/auth_null.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/auth_null.c	Sat Oct 14 00:36:07 2000
@@ -38,6 +38,7 @@
 nul_destroy(struct rpc_auth *auth)
 {
 	dprintk("RPC: destroying NULL authenticator %p\n", auth);
+	rpcauth_free_credcache(auth);
 	rpc_free(auth);
 }
 
@@ -45,17 +46,15 @@
  * Create NULL creds for current process
  */
 static struct rpc_cred *
-nul_create_cred(struct rpc_task *task)
+nul_create_cred(int flags)
 {
 	struct rpc_cred	*cred;
 
-	if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
-
 	cred->cr_count = 0;
 	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
+	cred->cr_uid = current->uid;
 
 	return cred;
 }
@@ -73,7 +72,7 @@
  * Match cred handle against current process
  */
 static int
-nul_match(struct rpc_task *task, struct rpc_cred *cred)
+nul_match(struct rpc_cred *cred, int taskflags)
 {
 	return 1;
 }
@@ -110,10 +109,12 @@
 		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
 		return NULL;
 	}
-	if ((n = ntohl(*p++)) != 0) {
+	if ((n = ntohl(*p++)) > 400) {
 		printk("RPC: bad verf size: %ld\n", (unsigned long) n);
 		return NULL;
 	}
+	if (n)
+		p += XDR_QUADLEN(n);
 
 	return p;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/auth_unix.c linux.18p17/net/sunrpc/auth_unix.c
--- linux.vanilla/net/sunrpc/auth_unix.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/auth_unix.c	Fri Sep 15 22:10:44 2000
@@ -27,7 +27,7 @@
 
 #define UNX_CRED_EXPIRE		(60 * HZ)
 
-#define UNX_WRITESLACK		(21 + (UNX_MAXNODENAME >> 2))
+#define UNX_WRITESLACK		(9 + NFS_NGROUPS + (UNX_MAXNODENAME >> 2))
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_AUTH
@@ -60,7 +60,7 @@
 }
 
 static struct rpc_cred *
-unx_create_cred(struct rpc_task *task)
+unx_create_cred(int flags)
 {
 	struct unx_cred	*cred;
 	int		i;
@@ -68,14 +68,12 @@
 	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
 				current->uid, current->gid);
 
-	if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) {
-		task->tk_status = -ENOMEM;
+	if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred))))
 		return NULL;
-	}
 
 	cred->uc_count = 0;
 	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
-	if (RPC_DO_ROOTOVERRIDE(task)) {
+	if (flags & RPC_TASK_ROOTCREDS) {
 		cred->uc_uid = cred->uc_fsuid = 0;
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
@@ -116,7 +114,7 @@
 	cred->uc_fsgid = gid;
 	cred->uc_gids[0] = (gid_t) NOGROUP;
 
-	return task->tk_cred = (struct rpc_cred *) cred;
+	return task->tk_msg.rpc_cred = (struct rpc_cred *) cred;
 }
 
 static void
@@ -131,12 +129,12 @@
  * request root creds (e.g. for NFS swapping).
  */
 static int
-unx_match(struct rpc_task * task, struct rpc_cred *rcred)
+unx_match(struct rpc_cred *rcred, int taskflags)
 {
 	struct unx_cred	*cred = (struct unx_cred *) rcred;
 	int		i;
 
-	if (!RPC_DO_ROOTOVERRIDE(task)) {
+	if (!(taskflags & RPC_TASK_ROOTCREDS)) {
 		int groups;
 
 		if (cred->uc_uid != current->uid
@@ -166,7 +164,7 @@
 unx_marshal(struct rpc_task *task, u32 *p, int ruid)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
-	struct unx_cred	*cred = (struct unx_cred *) task->tk_cred;
+	struct unx_cred	*cred = (struct unx_cred *) task->tk_msg.rpc_cred;
 	u32		*base, *hold;
 	int		i, n;
 
@@ -182,7 +180,8 @@
 	memcpy(p, clnt->cl_nodename, n);
 	p += (n + 3) >> 2;
 
-	if (ruid) {
+	/* Note: we don't use real uid if it involves raising priviledge */
+	if (ruid && cred->uc_uid != 0 && cred->uc_gid != 0) {
 		*p++ = htonl((u32) cred->uc_uid);
 		*p++ = htonl((u32) cred->uc_gid);
 	} else {
@@ -207,7 +206,7 @@
 static int
 unx_refresh(struct rpc_task *task)
 {
-	task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
+	task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
 	return task->tk_status = -EACCES;
 }
 
@@ -216,7 +215,7 @@
 {
 	u32		n = ntohl(*p++);
 
-	if (n != RPC_AUTH_NULL && n != RPC_AUTH_UNIX && n != RPC_AUTH_SHORT) {
+	if (n != RPC_AUTH_NULL && n != RPC_AUTH_SHORT) {
 		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
 		return NULL;
 	}
@@ -224,8 +223,12 @@
 		printk("RPC: giant verf size: %ld\n", (unsigned long) n);
 		return NULL;
 	}
-	task->tk_auth->au_rslack = (n >> 2) + 2;
-	p += (n >> 2);
+	if (n) {
+		n = XDR_QUADLEN(n);
+		if (n + 2 > task->tk_auth->au_rslack)
+			task->tk_auth->au_rslack = n + 2;
+		p += n;
+	}
 
 	return p;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/clnt.c linux.18p17/net/sunrpc/clnt.c
--- linux.vanilla/net/sunrpc/clnt.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/clnt.c	Fri Sep 22 15:03:25 2000
@@ -29,11 +29,12 @@
 #include <linux/malloc.h>
 #include <linux/in.h>
 #include <linux/utsname.h>
+#include <linux/interrupt.h>
 
 #include <linux/sunrpc/clnt.h>
 
 
-#define RPC_SLACK_SPACE		1024	/* total overkill */
+#define RPC_SLACK_SPACE		512	/* total overkill */
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_CALL
@@ -42,14 +43,13 @@
 static struct wait_queue *	destroy_wait = NULL;
 
 
-static void	call_bind(struct rpc_task *task);
 static void	call_reserve(struct rpc_task *task);
 static void	call_reserveresult(struct rpc_task *task);
 static void	call_allocate(struct rpc_task *task);
 static void	call_encode(struct rpc_task *task);
 static void	call_decode(struct rpc_task *task);
+static void	call_bind(struct rpc_task *task);
 static void	call_transmit(struct rpc_task *task);
-static void	call_receive(struct rpc_task *task);
 static void	call_status(struct rpc_task *task);
 static void	call_refresh(struct rpc_task *task);
 static void	call_refreshresult(struct rpc_task *task);
@@ -76,6 +76,12 @@
 	dprintk("RPC: creating %s client for %s (xprt %p)\n",
 		program->name, servname, xprt);
 
+	xdr_init();
+
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
+
 	if (!xprt)
 		goto out;
 	if (vers >= program->nrvers || !(version = program->version[vers]))
@@ -85,6 +91,7 @@
 	if (!clnt)
 		goto out_no_clnt;
 	memset(clnt, 0, sizeof(*clnt));
+	atomic_set(&clnt->cl_users, 0);
 
 	clnt->cl_xprt     = xprt;
 	clnt->cl_procinfo = version->procs;
@@ -94,7 +101,7 @@
 	clnt->cl_port     = xprt->addr.sin_port;
 	clnt->cl_prog     = program->number;
 	clnt->cl_vers     = version->number;
-	clnt->cl_prot     = IPPROTO_UDP;
+	clnt->cl_prot     = xprt->prot;
 	clnt->cl_stats    = program->stats;
 	clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait");
 
@@ -113,10 +120,10 @@
 	return clnt;
 
 out_no_clnt:
-	printk("RPC: out of memory in rpc_create_client\n");
+	printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
 	goto out;
 out_no_auth:
-	printk("RPC: Couldn't create auth handle (flavor %d)\n",
+	printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n",
 		flavor);
 	rpc_free(clnt);
 	clnt = NULL;
@@ -134,10 +141,10 @@
 {
 	dprintk("RPC: shutting down %s client for %s\n",
 		clnt->cl_protname, clnt->cl_server);
-	while (clnt->cl_users) {
+	while (atomic_read(&clnt->cl_users)) {
 #ifdef RPC_DEBUG
-		printk("rpc_shutdown_client: client %s, tasks=%d\n",
-			clnt->cl_protname, clnt->cl_users);
+		dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n",
+			clnt->cl_protname, atomic_read(&clnt->cl_users));
 #endif
 		/* Don't let rpc_release_client destroy us */
 		clnt->cl_oneshot = 0;
@@ -176,14 +183,10 @@
 rpc_release_client(struct rpc_clnt *clnt)
 {
 	dprintk("RPC:      rpc_release_client(%p, %d)\n",
-				clnt, clnt->cl_users);
-	if (clnt->cl_users) {
-		if (--(clnt->cl_users) > 0)
-			return;
-	} else
-		printk("rpc_release_client: %s client already free??\n",
-			clnt->cl_protname);
+				clnt, atomic_read(&clnt->cl_users));
 
+	if (!atomic_dec_and_test(&clnt->cl_users))
+		return;
 	wake_up(&destroy_wait);
 	if (clnt->cl_oneshot || clnt->cl_dead)
 		rpc_destroy_client(clnt);
@@ -195,7 +198,6 @@
 static void
 rpc_default_callback(struct rpc_task *task)
 {
-	rpc_release_task(task);
 }
 
 /*
@@ -236,42 +238,73 @@
 /*
  * New rpc_call implementation
  */
-int
-rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
-				int flags, rpc_action func, void *data)
+int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 {
 	struct rpc_task	my_task, *task = &my_task;
 	sigset_t	oldset;
-	int		async, status;
+	int		status;
 
 	/* If this client is slain all further I/O fails */
 	if (clnt->cl_dead) 
 		return -EIO;
 
+	if (flags & RPC_TASK_ASYNC) {
+		printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
+		flags &= ~RPC_TASK_ASYNC;
+	}
+
 	rpc_clnt_sigmask(clnt, &oldset);		
 
 	/* Create/initialize a new RPC task */
-	if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
-		if (!func)
-			func = rpc_default_callback;
-		status = -ENOMEM;
-		if (!(task = rpc_new_task(clnt, func, flags)))
-			goto out;
-		task->tk_calldata = data;
-	} else {
-		rpc_init_task(task, clnt, NULL, flags);
+	rpc_init_task(task, clnt, NULL, flags);
+	rpc_call_setup(task, msg, 0);
+
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else {
+		status = task->tk_status;
+		rpc_release_task(task);
 	}
 
-	/* Bind the user cred, set up the call info struct and
-	 * execute the task */
-	if (rpcauth_lookupcred(task) != NULL) {
-		rpc_call_setup(task, proc, argp, resp, 0);
-		rpc_execute(task);
-	} else
-		async = 0;
+	rpc_clnt_sigunmask(clnt, &oldset);		
+
+	return status;
+}
 
-	status = 0;
-	if (!async) {
+/*
+ * New rpc_call implementation
+ */
+int
+rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
+	       rpc_action callback, void *data)
+{
+	struct rpc_task	*task;
+	sigset_t	oldset;
+	int		status;
+
+	/* If this client is slain all further I/O fails */
+	if (clnt->cl_dead) 
+		return -EIO;
+
+	flags |= RPC_TASK_ASYNC;
+
+	rpc_clnt_sigmask(clnt, &oldset);		
+
+	/* Create/initialize a new RPC task */
+	if (!callback)
+		callback = rpc_default_callback;
+	status = -ENOMEM;
+	if (!(task = rpc_new_task(clnt, callback, flags)))
+		goto out;
+	task->tk_calldata = data;
+
+	rpc_call_setup(task, msg, 0);
+
+	/* Set up the call info struct and execute the task */
+	if (task->tk_status == 0)
+		status = rpc_execute(task);
+	else {
 		status = task->tk_status;
 		rpc_release_task(task);
 	}
@@ -284,17 +317,24 @@
 
 
 void
-rpc_call_setup(struct rpc_task *task, u32 proc,
-				void *argp, void *resp, int flags)
+rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
 {
-	task->tk_action = call_bind;
-	task->tk_proc   = proc;
-	task->tk_argp   = argp;
-	task->tk_resp   = resp;
+	task->tk_msg   = *msg;
 	task->tk_flags |= flags;
+	/* Bind the user cred */
+	if (task->tk_msg.rpc_cred != NULL) {
+		rpcauth_holdcred(task);
+	} else
+		rpcauth_bindcred(task);
+
+	if (task->tk_status == 0)
+		task->tk_action = call_reserve;
+	else
+		task->tk_action = NULL;
 
 	/* Increment call count */
-	rpcproc_count(task->tk_client, proc)++;
+	if (task->tk_msg.rpc_proc < task->tk_client->cl_maxproc)
+		rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -304,26 +344,11 @@
 void
 rpc_restart_call(struct rpc_task *task)
 {
-	if (task->tk_flags & RPC_TASK_KILLED) {
-		rpc_release_task(task);
+	if (RPC_ASSASSINATED(task))
 		return;
-	}
-	task->tk_action = call_bind;
-	rpcproc_count(task->tk_client, task->tk_proc)++;
-}
-
-/*
- * 0.	Get the server port number if not yet set
- */
-static void
-call_bind(struct rpc_task *task)
-{
-	struct rpc_clnt	*clnt = task->tk_client;
 
 	task->tk_action = call_reserve;
-	task->tk_status = 0;
-	if (!clnt->cl_port)
-		rpc_getport(task, clnt);
+	rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 /*
@@ -334,25 +359,22 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 
-	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
-	if (!clnt->cl_port) {
-		printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n",
-			clnt->cl_protname, clnt->cl_server,
-			clnt->cl_softrtry? "giving up" : "retrying");
-		if (!clnt->cl_softrtry) {
-			rpc_delay(task, 5*HZ);
-			return;
-		}
+	if (task->tk_msg.rpc_proc >= clnt->cl_maxproc) {
+		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
+			clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc);
 		rpc_exit(task, -EIO);
 		return;
 	}
+
+	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
 	if (!rpcauth_uptodatecred(task)) {
 		task->tk_action = call_refresh;
 		return;
 	}
+
+	task->tk_status  = 0;
 	task->tk_action  = call_reserveresult;
 	task->tk_timeout = clnt->cl_timeout.to_resrvval;
-	task->tk_status  = 0;
 	clnt->cl_stats->rpccnt++;
 	xprt_reserve(task);
 }
@@ -363,6 +385,8 @@
 static void
 call_reserveresult(struct rpc_task *task)
 {
+	int status = task->tk_status;
+
 	dprintk("RPC: %4d call_reserveresult (status %d)\n",
 				task->tk_pid, task->tk_status);
 	/*
@@ -376,25 +400,27 @@
 
 	if (task->tk_status >= 0) {
 		task->tk_action = call_allocate;
-		goto out;
-	} else if (task->tk_status == -EAGAIN) {
+		return;
+	}
+
+	task->tk_status = 0;
+	switch (status) {
+	case -EAGAIN:
+	case -ENOBUFS:
 		task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
-		task->tk_status = 0;
-		xprt_reserve(task);
-		goto out;
-	} else if (task->tk_status == -ETIMEDOUT) {
+		task->tk_action = call_reserve;
+		break;
+	case -ETIMEDOUT:
 		dprintk("RPC: task timed out\n");
 		task->tk_action = call_timeout;
-		goto out;
-	} else {
-		task->tk_action = NULL;
-	}
-	if (!task->tk_rqstp) {
-		printk("RPC: task has no request, exit EIO\n");
-		rpc_exit(task, -EIO);
+		break;
+	default:
+		if (!task->tk_rqstp) {
+			printk(KERN_INFO "RPC: task has no request, exit EIO\n");
+			rpc_exit(task, -EIO);
+		} else
+			rpc_exit(task, status);
 	}
-out:
-	return;
 }
 
 /*
@@ -415,16 +441,15 @@
 
 	/* FIXME: compute buffer requirements more exactly using
 	 * auth->au_wslack */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
 
-	if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
+	if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL)
 		return;
-	printk("RPC: buffer allocation failed for task %p\n", task); 
+	printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); 
 
-	if (!signalled()) {
-		xprt_release(task);
-		task->tk_action = call_reserve;
-		rpc_delay(task, HZ);
+	if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
+		task->tk_action = call_allocate;
+		rpc_delay(task, HZ>>4);
 		return;
 	}
 
@@ -447,36 +472,26 @@
 	dprintk("RPC: %4d call_encode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
-	task->tk_action = call_transmit;
+	task->tk_action = call_bind;
 
 	/* Default buffer setup */
-	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE;
-	req->rq_svec[0].iov_base = task->tk_buffer;
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE;
+	req->rq_svec[0].iov_base = (void *)task->tk_buffer;
 	req->rq_svec[0].iov_len  = bufsiz;
 	req->rq_slen		 = 0;
 	req->rq_snr		 = 1;
-	req->rq_rvec[0].iov_base = task->tk_buffer;
+	req->rq_rvec[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz);
 	req->rq_rvec[0].iov_len  = bufsiz;
 	req->rq_rlen		 = bufsiz;
 	req->rq_rnr		 = 1;
 
-	if (task->tk_proc > clnt->cl_maxproc) {
-		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
-			clnt->cl_protname, clnt->cl_vers, task->tk_proc);
-		rpc_exit(task, -EIO);
-		return;
-	}
-
-	/* Zero buffer so we have automatic zero-padding of opaque & string */
-	memset(task->tk_buffer, 0, bufsiz);
-
 	/* Encode header and provided arguments */
-	encode = rpcproc_encode(clnt, task->tk_proc);
+	encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc);
 	if (!(p = call_header(task))) {
-		printk("RPC: call_header failed, exit EIO\n");
+		printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
 		rpc_exit(task, -EIO);
 	} else
-	if ((status = encode(req, p, task->tk_argp)) < 0) {
+	if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
 		printk(KERN_WARNING "%s: can't encode arguments: %d\n",
 				clnt->cl_protname, -status);
 		rpc_exit(task, status);
@@ -484,42 +499,60 @@
 }
 
 /*
- * 4.	Transmit the RPC request
+ * 4.	Get the server port number if not yet set
  */
 static void
-call_transmit(struct rpc_task *task)
+call_bind(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_transmit (status %d)\n", 
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_xprt *xprt = task->tk_xprt;
+
+	task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_reconnect;
+
+	if (!clnt->cl_port) {
+		task->tk_action = call_reconnect;
+		task->tk_timeout = clnt->cl_timeout.to_maxval;
+		rpc_getport(task, clnt);
+	}
+}
+
+/*
+ * 4a.	Reconnect to the RPC server (TCP case)
+ */
+static void
+call_reconnect(struct rpc_task *task)
+{
+	struct rpc_clnt *clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_reconnect status %d\n",
 				task->tk_pid, task->tk_status);
 
-	task->tk_action = call_receive;
-	task->tk_status = 0;
-	xprt_transmit(task);
+	task->tk_action = call_transmit;
+	if (task->tk_status < 0 || !clnt->cl_xprt->stream)
+		return;
+	clnt->cl_stats->netreconn++;
+	xprt_reconnect(task);
 }
 
 /*
- * 5.	Wait for the RPC reply
+ * 5.	Transmit the RPC request, and wait for reply
  */
 static void
-call_receive(struct rpc_task *task)
+call_transmit(struct rpc_task *task)
 {
-	dprintk("RPC: %4d call_receive (status %d)\n", 
-		task->tk_pid, task->tk_status);
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_transmit (status %d)\n", 
+				task->tk_pid, task->tk_status);
 
 	task->tk_action = call_status;
-	/* In case of error, evaluate status */
 	if (task->tk_status < 0)
 		return;
-
-	/* If we have no decode function, this means we're performing
-	 * a void call (a la lockd message passing). */
-	if (!rpcproc_decode(task->tk_client, task->tk_proc)) {
-		rpc_remove_wait_queue(task); /* remove from xprt_pending */
+	xprt_transmit(task);
+	if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc)) {
 		task->tk_action = NULL;
-		return;
+		rpc_wake_up_task(task);
 	}
-
-	xprt_receive(task);
 }
 
 /*
@@ -529,6 +562,7 @@
 call_status(struct rpc_task *task)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_xprt	*xprt = clnt->cl_xprt;
 	struct rpc_rqst	*req;
 	int		status = task->tk_status;
 
@@ -537,28 +571,42 @@
 
 	if (status >= 0) {
 		task->tk_action = call_decode;
-	} else if (status == -ETIMEDOUT) {
+		return;
+	}
+
+	task->tk_status = 0;
+	req = task->tk_rqstp;
+	switch(status) {
+	case -ETIMEDOUT:
 		task->tk_action = call_timeout;
-	} else if (status == -EAGAIN) {
-		if (!(req = task->tk_rqstp))
-			task->tk_action = call_reserve;
-		else if (!task->tk_buffer)
-			task->tk_action = call_allocate;
-		else if (req->rq_damaged)
-			task->tk_action = call_encode;
-		else
-			task->tk_action = call_transmit;
-	} else if (status == -ENOTCONN) {
-		task->tk_action = call_reconnect;
-	} else if (status == -ECONNREFUSED && clnt->cl_autobind) {
-		task->tk_action = call_bind;
-		clnt->cl_port = 0;
-	} else {
+		break;
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		req->rq_bytes_sent = 0;
+		if (clnt->cl_autobind || !clnt->cl_port) {
+			clnt->cl_port = 0;
+			task->tk_action = call_bind;
+			break;
+		}
+		if (xprt->stream) {
+			task->tk_action = call_reconnect;
+			break;
+		}
+		/*
+		 * Sleep and dream of an open connection
+		 */
+		task->tk_timeout = 5 * HZ;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+	case -ENOMEM:
+	case -EAGAIN:
+		task->tk_action = call_transmit;
+		clnt->cl_stats->rpcretrans++;
+		break;
+	default:
 		if (clnt->cl_chatty)
 			printk("%s: RPC call returned error %d\n",
-				clnt->cl_protname, -status);
-		task->tk_action = NULL;
-		return;
+			       clnt->cl_protname, -status);
+		rpc_exit(task, status);
 	}
 }
 
@@ -581,38 +629,42 @@
 				task->tk_pid);
 			goto minor_timeout;
 		}
-		to->to_initval <<= 1;
-		if (to->to_initval > to->to_maxval)
-			to->to_initval = to->to_maxval;
+		to->to_retries = clnt->cl_timeout.to_retries;
+	} else {
+		printk(KERN_NOTICE "%s: task %d can't get a request slot\n",
+		       clnt->cl_protname, task->tk_pid);
+		goto minor_timeout;
 	}
 
 	dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
 	if (clnt->cl_softrtry) {
 		if (clnt->cl_chatty && !task->tk_exit)
-			printk("%s: server %s not responding, timed out\n",
+			printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
 				clnt->cl_protname, clnt->cl_server);
 		rpc_exit(task, -EIO);
 		return;
 	}
+
 	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
 		task->tk_flags |= RPC_CALL_MAJORSEEN;
-		if (req)
-			printk("%s: server %s not responding, still trying\n",
-				clnt->cl_protname, clnt->cl_server);
-		else 
-			printk("%s: task %d can't get a request slot\n",
-				clnt->cl_protname, task->tk_pid);
+		printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
+		       clnt->cl_protname, clnt->cl_server);
+	} else if (clnt->cl_chatty) {
+		printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
+		       clnt->cl_protname, clnt->cl_server);
 	}
+
 	if (clnt->cl_autobind)
 		clnt->cl_port = 0;
 
 minor_timeout:
-	if (!clnt->cl_port) {
-		task->tk_action = call_bind;
-	} else if (!req) {
+	if (!req)
 		task->tk_action = call_reserve;
-	} else if (req->rq_damaged) {
-		task->tk_action = call_encode;
+	else if (!clnt->cl_port) {
+		task->tk_action = call_bind;
+		clnt->cl_stats->rpcretrans++;
+	} else if (clnt->cl_xprt->stream && !xprt_connected(clnt->cl_xprt)) {
+		task->tk_action = call_reconnect;
 		clnt->cl_stats->rpcretrans++;
 	} else {
 		task->tk_action = call_transmit;
@@ -622,23 +674,6 @@
 }
 
 /*
- * 6b.	Reconnect to the RPC server (TCP case)
- */
-static void
-call_reconnect(struct rpc_task *task)
-{
-	dprintk("RPC: %4d call_reconnect status %d\n",
-				task->tk_pid, task->tk_status);
-	if (task->tk_status == 0) {
-		task->tk_action = call_status;
-		task->tk_status = -EAGAIN;
-		return;
-	}
-	task->tk_client->cl_stats->netreconn++;
-	xprt_reconnect(task);
-}
-
-/*
  * 7.	Decode the RPC reply
  */
 static void
@@ -646,20 +681,20 @@
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_proc);
+	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc);
 	u32		*p;
 
 	dprintk("RPC: %4d call_decode (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
 	if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
-		printk("%s: server %s OK\n",
+		printk(KERN_NOTICE "%s: server %s OK\n",
 			clnt->cl_protname, clnt->cl_server);
 		task->tk_flags &= ~RPC_CALL_MAJORSEEN;
 	}
 
 	if (task->tk_status < 12) {
-		printk("%s: too small RPC reply size (%d bytes)\n",
+		printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n",
 			clnt->cl_protname, task->tk_status);
 		rpc_exit(task, -EIO);
 		return;
@@ -673,18 +708,21 @@
 	 * The following is an NFS-specific hack to cater for setuid
 	 * processes whose uid is mapped to nobody on the server.
 	 */
-	if (task->tk_client->cl_prog == 100003 && 
+	if (task->tk_client->cl_droppriv &&
             (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
-		if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
+		if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
 			dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
 			task->tk_flags ^= RPC_CALL_REALUID;
 			task->tk_action = call_encode;
+			task->tk_suid_retry--;
 			return;
 		}
 	}
 
 	task->tk_action = NULL;
-	task->tk_status = decode(req, p, task->tk_resp);
+
+	if (decode)
+		task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
 	dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
 					task->tk_status);
 }
@@ -713,10 +751,9 @@
 	dprintk("RPC: %4d call_refreshresult (status %d)\n", 
 				task->tk_pid, task->tk_status);
 
-	if (task->tk_status < 0) {
-		task->tk_status = -EACCES;
-		task->tk_action = NULL;
-	} else
+	if (task->tk_status < 0)
+		rpc_exit(task, -EACCES);
+	else
 		task->tk_action = call_reserve;
 }
 
@@ -728,17 +765,18 @@
 {
 	struct rpc_clnt *clnt = task->tk_client;
 	struct rpc_xprt *xprt = clnt->cl_xprt;
-	u32		*p = task->tk_buffer;
+	struct rpc_rqst	*req = task->tk_rqstp;
+	u32		*p = req->rq_svec[0].iov_base;
 
 	/* FIXME: check buffer size? */
 	if (xprt->stream)
 		*p++ = 0;		/* fill in later */
-	*p++ = task->tk_rqstp->rq_xid;	/* XID */
+	*p++ = req->rq_xid;		/* XID */
 	*p++ = htonl(RPC_CALL);		/* CALL */
 	*p++ = htonl(RPC_VERSION);	/* RPC version */
 	*p++ = htonl(clnt->cl_prog);	/* program number */
 	*p++ = htonl(clnt->cl_vers);	/* program version */
-	*p++ = htonl(task->tk_proc);	/* procedure */
+	*p++ = htonl(task->tk_msg.rpc_proc);	/* procedure */
 	return rpcauth_marshcred(task, p);
 }
 
@@ -748,25 +786,26 @@
 static u32 *
 call_verify(struct rpc_task *task)
 {
-	u32	*p = task->tk_buffer, n;
+	u32	*p = task->tk_rqstp->rq_rvec[0].iov_base, n;
 
 	p += 1;	/* skip XID */
 
 	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		printk("call_verify: not an RPC reply: %x\n", n);
+		printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
 		goto garbage;
 	}
 	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
 		int	error = -EACCES;
 
 		if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
-			printk("call_verify: RPC call rejected: %x\n", n);
+			printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
 		} else
 		switch ((n = ntohl(*p++))) {
 		case RPC_AUTH_REJECTEDCRED:
 		case RPC_AUTH_REJECTEDVERF:
-			if (!task->tk_cred_retry--)
+			if (!task->tk_cred_retry)
 				break;
+			task->tk_cred_retry--;
 			dprintk("RPC: %4d call_verify: retry stale creds\n",
 							task->tk_pid);
 			rpcauth_invalcred(task);
@@ -775,17 +814,19 @@
 		case RPC_AUTH_BADCRED:
 		case RPC_AUTH_BADVERF:
 			/* possibly garbled cred/verf? */
-			if (!task->tk_garb_retry--)
+			if (!task->tk_garb_retry)
 				break;
+			task->tk_garb_retry--;
 			dprintk("RPC: %4d call_verify: retry garbled creds\n",
 							task->tk_pid);
 			task->tk_action = call_encode;
 			return NULL;
 		case RPC_AUTH_TOOWEAK:
-			printk("call_verify: server requires stronger "
+			printk(KERN_NOTICE "call_verify: server requires stronger "
 			       "authentication.\n");
+			break;
 		default:
-			printk("call_verify: unknown auth error: %x\n", n);
+			printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
 			error = -EIO;
 		}
 		dprintk("RPC: %4d call_verify: call rejected %d\n",
@@ -794,7 +835,7 @@
 		return NULL;
 	}
 	if (!(p = rpcauth_checkverf(task, p))) {
-		printk("call_verify: auth check failed\n");
+		printk(KERN_WARNING "call_verify: auth check failed\n");
 		goto garbage;		/* bad verifier, retry */
 	}
 	switch ((n = ntohl(*p++))) {
@@ -803,19 +844,20 @@
 	case RPC_GARBAGE_ARGS:
 		break;			/* retry */
 	default:
-		printk("call_verify: server accept status: %x\n", n);
+		printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
 		/* Also retry */
 	}
 
 garbage:
 	dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
 	task->tk_client->cl_stats->rpcgarbage++;
-	if (task->tk_garb_retry--) {
-		printk("RPC: garbage, retrying %4d\n", task->tk_pid);
+	if (task->tk_garb_retry) {
+		printk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
 		task->tk_action = call_encode;
+		task->tk_garb_retry--;
 		return NULL;
 	}
-	printk("RPC: garbage, exit EIO\n");
+	printk(KERN_WARNING "RPC: garbage, exit EIO\n");
 	rpc_exit(task, -EIO);
 	return NULL;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/pmap_clnt.c linux.18p17/net/sunrpc/pmap_clnt.c
--- linux.vanilla/net/sunrpc/pmap_clnt.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/pmap_clnt.c	Fri Sep 15 22:10:44 2000
@@ -41,6 +41,7 @@
 {
 	struct rpc_portmap *map = &clnt->cl_pmap;
 	struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+	struct rpc_message msg = { PMAP_GETPORT, map, &clnt->cl_port, NULL };
 	struct rpc_clnt	*pmap_clnt;
 	struct rpc_task	*child;
 
@@ -66,7 +67,7 @@
 		goto bailout;
 
 	/* Setup the call info struct */
-	rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
+	rpc_call_setup(child, &msg, 0);
 
 	/* ... and run the child task */
 	rpc_run_child(task, child, pmap_getport_done);
@@ -121,7 +122,7 @@
 		task->tk_action = NULL;
 	} else if (clnt->cl_port == 0) {
 		/* Program not registered */
-		task->tk_status = -EACCES;
+		task->tk_status = -EPROTONOSUPPORT;
 		task->tk_action = NULL;
 	} else {
 		/* byte-swap port number first */
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/sched.c linux.18p17/net/sunrpc/sched.c
--- linux.vanilla/net/sunrpc/sched.c	Sat Sep  9 18:42:51 2000
+++ linux.18p17/net/sunrpc/sched.c	Fri Sep 15 22:10:44 2000
@@ -31,7 +31,7 @@
  */
 #define GFP_RPC			GFP_NFS
 
-static void			__rpc_default_timer(struct rpc_task *task);
+static void			rpc_default_timer(struct rpc_task *task);
 static void			rpciod_killall(void);
 
 /*
@@ -68,12 +68,110 @@
 static int			rpc_inhibit = 0;
 
 /*
+ * Spinlock for wait queues. Access to the latter has to be interrupt-safe
+ * since we want to wake up tasks from sk->write_space().
+ */
+spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED;
+
+/*
  * This is the last-ditch buffer for NFS swap requests
  */
 static u32			swap_buffer[PAGE_SIZE >> 2];
 static int			swap_buffer_used = 0;
 
 /*
+ * Make allocation of the swap_buffer SMP-safe
+ */
+static __inline__ int rpc_lock_swapbuf(void)
+{
+	return !test_and_set_bit(1, &swap_buffer_used);
+}
+static __inline__ void rpc_unlock_swapbuf(void)
+{
+	clear_bit(1, &swap_buffer_used);
+}
+
+/*
+ * Disable the timer for a given RPC task. Should be called with
+ * rpc_queue_lock and bh_disabled in order to avoid races within
+ * rpc_run_timer().
+ */
+static inline void
+__rpc_disable_timer(struct rpc_task *task)
+{
+	dprintk("RPC: %4d disabling timer\n", task->tk_pid);
+	task->tk_timeout_fn = NULL;
+	task->tk_timeout = 0;
+}
+
+/*
+ * Run a timeout function.
+ * We use the callback in order to allow __rpc_wake_up_task()
+ * and friends to disable the timer synchronously on SMP systems
+ * without calling del_timer_sync(). The latter could cause a
+ * deadlock if called while we're holding spinlocks...
+ */
+static void
+rpc_run_timer(struct rpc_task *task)
+{
+	void (*callback)(struct rpc_task *);
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	callback = task->tk_timeout_fn;
+	task->tk_timeout_fn = NULL;
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	if (callback) {
+		dprintk("RPC: %4d running timer\n", task->tk_pid);
+		callback(task);
+	}
+}
+
+/*
+ * Set up a timer for the current task.
+ */
+static inline void
+__rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	if (!task->tk_timeout)
+		return;
+
+	dprintk("RPC: %4d setting alarm for %lu ms\n",
+			task->tk_pid, task->tk_timeout * 1000 / HZ);
+
+	if (timer)
+		task->tk_timeout_fn = timer;
+	else
+		task->tk_timeout_fn = rpc_default_timer;
+	mod_timer(&task->tk_timer, jiffies + task->tk_timeout);
+}
+
+/*
+ * Set up a timer for an already sleeping task.
+ */
+void rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	if (!(RPC_IS_RUNNING(task) || task->tk_wakeup))
+		__rpc_add_timer(task, timer);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+/*
+ * Delete any timer for the current task.
+ */
+static inline void
+rpc_delete_timer(struct rpc_task *task)
+{
+	if (timer_pending(&task->tk_timer)) {
+		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+		del_timer(&task->tk_timer);
+	}
+}
+
+/*
  * Add new request to wait queue.
  *
  * Swapper tasks always get inserted at the head of the queue.
@@ -81,16 +179,17 @@
  * improve overall performance.
  * Everyone else gets appended to the queue to ensure proper FIFO behavior.
  */
-int
-rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
+static inline int
+__rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
 {
-	if (task->tk_rpcwait) {
-		if (task->tk_rpcwait != queue)
-		{
-			printk(KERN_WARNING "RPC: doubly enqueued task!\n");
-			return -EWOULDBLOCK;
-		}
+	if (task->tk_rpcwait == queue)
 		return 0;
+
+	if (task->tk_rpcwait) {
+		printk(KERN_WARNING "RPC: task already queued!\n");
+		dprintk("task already on %s, to be added to %s\n",
+			rpc_qname(task->tk_rpcwait), rpc_qname(queue));
+		return -EWOULDBLOCK;
 	}
 	if (RPC_IS_SWAPPER(task))
 		rpc_insert_list(&queue->task, task);
@@ -104,17 +203,30 @@
 	return 0;
 }
 
+int
+rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task)
+{
+	unsigned long	oldflags;
+	int		result;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	result = __rpc_add_wait_queue(q, task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	return result;
+}
+
 /*
  * Remove request from queue.
- * Note: must be called with interrupts disabled.
+ * Note: must be called with spin lock held.
  */
-void
-rpc_remove_wait_queue(struct rpc_task *task)
+static inline void
+__rpc_remove_wait_queue(struct rpc_task *task)
 {
-	struct rpc_wait_queue *queue;
+	struct rpc_wait_queue *queue = task->tk_rpcwait;
 
-	if (!(queue = task->tk_rpcwait))
+	if (!queue)
 		return;
+
 	rpc_remove_list(&queue->task, task);
 	task->tk_rpcwait = NULL;
 
@@ -122,73 +234,63 @@
 				task->tk_pid, queue, rpc_qname(queue));
 }
 
-/*
- * Set up a timer for the current task.
- */
-inline void
-rpc_add_timer(struct rpc_task *task, rpc_action timer)
+void
+rpc_remove_wait_queue(struct rpc_task *task)
 {
-	unsigned long	expires = jiffies + task->tk_timeout;
-
-	dprintk("RPC: %4d setting alarm for %lu ms\n",
-			task->tk_pid, task->tk_timeout * 1000 / HZ);
-	if (!timer)
-		timer = __rpc_default_timer;
-	if (time_before(expires, jiffies)) {
-		printk(KERN_ERR "RPC: bad timeout value %ld - setting to 10 sec!\n",
-					task->tk_timeout);
-		expires = jiffies + 10 * HZ;
-	}
-	task->tk_timer.expires  = expires;
-	task->tk_timer.data     = (unsigned long) task;
-	task->tk_timer.function = (void (*)(unsigned long)) timer;
-	task->tk_timer.prev     = NULL;
-	task->tk_timer.next     = NULL;
-	add_timer(&task->tk_timer);
-}
+	unsigned long	oldflags;
 
-/*
- * Delete any timer for the current task.
- * Must be called with interrupts off.
- */
-inline void
-rpc_del_timer(struct rpc_task *task)
-{
-	if (task->tk_timeout) {
-		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
-		del_timer(&task->tk_timer);
-		task->tk_timeout = 0;
-	}
+	if (!task->tk_rpcwait)
+		return;
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_remove_wait_queue(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Make an RPC task runnable.
  *
  * Note: If the task is ASYNC, this must be called with 
- * interrupts disabled to protect the wait queue operation.
+ * spin lock held in order to protect the wait queue operation.
  */
 static inline void
-rpc_make_runnable(struct rpc_task *task)
+__rpc_make_runnable(struct rpc_task *task)
 {
 	if (task->tk_timeout) {
 		printk(KERN_ERR "RPC: task w/ running timer in rpc_make_runnable!!\n");
 		return;
 	}
-	task->tk_flags |= RPC_TASK_RUNNING;
+	task->tk_running = 1;
 	if (RPC_IS_ASYNC(task)) {
-		int status;
-		status = rpc_add_wait_queue(&schedq, task);
-		if (status)
-		{
-			printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-			task->tk_status = status;
+		if (RPC_IS_SLEEPING(task)) {
+			int status;
+			status = __rpc_add_wait_queue(&schedq, task);
+			if (status < 0) {
+				printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
+				task->tk_status = status;
+			} else
+				task->tk_sleeping = 0;
 		}
 		wake_up(&rpciod_idle);
 	} else {
+		task->tk_sleeping = 0;
 		wake_up(&task->tk_wait);
 	}
 }
 
+/*
+ * Place a newly initialized task on the schedq.
+ */
+static inline void
+__rpc_schedule_run(struct rpc_task *task)
+{
+	/* Don't run a child twice! */
+	if (RPC_IS_ACTIVATED(task))
+		return;
+	task->tk_active = 1;
+	task->tk_sleeping = 1;
+	__rpc_make_runnable(task);
+}
+
 
 /*
  *	For other people who may need to wake the I/O daemon
@@ -198,9 +300,7 @@
 void rpciod_wake_up(void)
 {
 	if(rpciod_pid==0)
-	{
 		printk(KERN_ERR "rpciod: wot no daemon?\n");
-	}
 	wake_up(&rpciod_idle);
 }
 
@@ -214,33 +314,32 @@
 __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
 			rpc_action action, rpc_action timer)
 {
-	unsigned long	oldflags;
 	int status;
 
 	dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
 				rpc_qname(q), jiffies);
 
-	/*
-	 * Protect the execution below.
-	 */
-	save_flags(oldflags); cli();
+	if (!RPC_IS_ASYNC(task) && !RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive synchronous task put to sleep!\n");
+		return;
+	}
+
+	/* Mark the task as being activated if so needed */
+	if (!RPC_IS_ACTIVATED(task)) {
+		task->tk_active = 1;
+		task->tk_sleeping = 1;
+	}
 
-	status = rpc_add_wait_queue(q, task);
-	if (status)
-	{
+	status = __rpc_add_wait_queue(q, task);
+	if (status) {
 		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
 		task->tk_status = status;
-		task->tk_flags |= RPC_TASK_RUNNING;
-	}
-	else
-	{
+	} else {
+		task->tk_running = 0;
 		task->tk_callback = action;
-		if (task->tk_timeout)
-			rpc_add_timer(task, timer);
-		task->tk_flags &= ~RPC_TASK_RUNNING;
+		__rpc_add_timer(task, timer);
 	}
 
-	restore_flags(oldflags);
 	return;
 }
 
@@ -248,17 +347,39 @@
 rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
 				rpc_action action, rpc_action timer)
 {
+	unsigned long	oldflags;
+
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	__rpc_sleep_on(q, task, action, timer);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+void
+rpc_sleep_locked(struct rpc_wait_queue *q, struct rpc_task *task,
+		 rpc_action action, rpc_action timer)
+{
+	unsigned long	oldflags;
+
+	/*
+	 * Protect the queue operations.
+	 */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_sleep_on(q, task, action, timer);
+	__rpc_lock_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
- * Wake up a single task -- must be invoked with bottom halves off.
+ * Wake up a single task -- must be invoked with spin lock held.
  *
  * It would probably suffice to cli/sti the del_timer and remove_wait_queue
  * operations individually.
  */
-static void
-__rpc_wake_up(struct rpc_task *task)
+static inline void
+__rpc_wake_up_task(struct rpc_task *task)
 {
 	dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n",
 					task->tk_pid, jiffies, rpc_inhibit);
@@ -267,16 +388,32 @@
 	if (task->tk_magic != 0xf00baa) {
 		printk(KERN_ERR "RPC: attempt to wake up non-existing task!\n");
 		rpc_debug = ~0;
+		rpc_show_tasks();
 		return;
 	}
 #endif
-	rpc_del_timer(task);
-	if (task->tk_rpcwait != &schedq)
-		rpc_remove_wait_queue(task);
-	if (!RPC_IS_RUNNING(task)) {
-		task->tk_flags |= RPC_TASK_CALLBACK;
-		rpc_make_runnable(task);
+	/* Has the task been executed yet? If not, we cannot wake it up! */
+	if (!RPC_IS_ACTIVATED(task)) {
+		printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task);
+		return;
 	}
+	if (RPC_IS_RUNNING(task))
+		return;
+
+	__rpc_disable_timer(task);
+	if (task->tk_rpcwait != &schedq)
+		__rpc_remove_wait_queue(task);
+
+	/* If the task has been locked, then set tk_wakeup so that
+	 * rpc_unlock_task() wakes us up... */
+	if (task->tk_lock) {
+		task->tk_wakeup = 1;
+		return;
+	} else
+		task->tk_wakeup = 0;
+
+	__rpc_make_runnable(task);
+
 	dprintk("RPC:      __rpc_wake_up done\n");
 }
 
@@ -284,12 +421,12 @@
  * Default timeout handler if none specified by user
  */
 static void
-__rpc_default_timer(struct rpc_task *task)
+rpc_default_timer(struct rpc_task *task)
 {
-	dprintk("RPC: %d timeout (default timer)\n", task->tk_pid);
+	dprintk("RPC: %4d timeout (default timer)\n", task->tk_pid);
 	task->tk_status = -ETIMEDOUT;
 	task->tk_timeout = 0;
-	__rpc_wake_up(task);
+	rpc_wake_up_task(task);
 }
 
 /*
@@ -300,26 +437,38 @@
 {
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
-	__rpc_wake_up(task);
-	restore_flags(oldflags);
+	if (RPC_IS_RUNNING(task))
+		return;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_wake_up_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Wake up the next task on the wait queue.
  */
-struct rpc_task *
-rpc_wake_up_next(struct rpc_wait_queue *queue)
+static inline struct rpc_task *
+__rpc_wake_up_next(struct rpc_wait_queue *queue)
 {
-	unsigned long	oldflags;
 	struct rpc_task	*task;
 
 	dprintk("RPC:      wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue));
-	save_flags(oldflags); cli();
 	if ((task = queue->task) != 0)
-		__rpc_wake_up(task);
-	restore_flags(oldflags);
+		__rpc_wake_up_task(task);
+
+	return task;
+}
+
+struct rpc_task *
+rpc_wake_up_next(struct rpc_wait_queue *queue)
+{
+	struct rpc_task	*task;
+	unsigned long	oldflags;
 
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	task = __rpc_wake_up_next(queue);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 	return task;
 }
 
@@ -331,10 +480,10 @@
 {
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	while (queue->task)
-		__rpc_wake_up(queue->task);
-	restore_flags(oldflags);
+		__rpc_wake_up_task(queue->task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -346,30 +495,63 @@
 	struct rpc_task	*task;
 	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	while ((task = queue->task) != NULL) {
 		task->tk_status = status;
-		__rpc_wake_up(task);
+		__rpc_wake_up_task(task);
 	}
-	restore_flags(oldflags);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+}
+
+/*
+ * Lock down a sleeping task to prevent it from waking up
+ * and disappearing from beneath us.
+ *
+ * This function should always be called with the
+ * rpc_queue_lock held.
+ */
+int
+__rpc_lock_task(struct rpc_task *task)
+{
+	if (!RPC_IS_RUNNING(task))
+		return ++task->tk_lock;
+	return 0;
+}
+
+static inline void
+__rpc_unlock_task(struct rpc_task *task)
+{
+	if (task->tk_lock && !--task->tk_lock && task->tk_wakeup)
+		__rpc_wake_up_task(task);
+}
+
+void
+rpc_unlock_task(struct rpc_task *task)
+{
+	unsigned long	oldflags;
+
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	__rpc_unlock_task(task);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
  * Run a task at a later time
  */
-static void	__rpc_atrun(struct rpc_task *);
+static void	rpc_atrun(struct rpc_task *);
 void
 rpc_delay(struct rpc_task *task, unsigned long delay)
 {
 	task->tk_timeout = delay;
-	rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun);
+	rpc_sleep_on(&delay_queue, task, NULL, rpc_atrun);
 }
 
 static void
-__rpc_atrun(struct rpc_task *task)
+rpc_atrun(struct rpc_task *task)
 {
 	task->tk_status = 0;
-	__rpc_wake_up(task);
+	task->tk_timeout = 0;
+	rpc_wake_up_task(task);
 }
 
 /*
@@ -389,15 +571,15 @@
 		return 0;
 	}
 
+restarted:
 	while (1) {
 		/*
 		 * Execute any pending callback.
 		 */
-		if (task->tk_flags & RPC_TASK_CALLBACK) {
+		if (RPC_DO_CALLBACK(task)) {
 			/* Define a callback save pointer */
 			void (*save_callback)(struct rpc_task *);
 	
-			task->tk_flags &= ~RPC_TASK_CALLBACK;
 			/* 
 			 * If a callback exists, save it, reset it,
 			 * call it.
@@ -405,11 +587,9 @@
 			 * another callback set within the callback handler
 			 * - Dave
 			 */
-			if (task->tk_callback) {
-				save_callback=task->tk_callback;
-				task->tk_callback=NULL;
-				save_callback(task);
-			}
+			save_callback=task->tk_callback;
+			task->tk_callback=NULL;
+			save_callback(task);
 		}
 
 		/*
@@ -418,6 +598,10 @@
 		 * by someone else.
 		 */
 		if (RPC_IS_RUNNING(task)) {
+			/*
+			 * Garbage collection of pending timers...
+			 */
+			rpc_delete_timer(task);
 			if (!task->tk_action)
 				break;
 			task->tk_action(task);
@@ -425,86 +609,98 @@
 
 		/*
 		 * Check whether task is sleeping.
-		 * Note that if the task may go to sleep in tk_action,
+		 * Note that if the task goes to sleep in tk_action,
 		 * and the RPC reply arrives before we get here, it will
 		 * have state RUNNING, but will still be on schedq.
+		 * 27/9/99: The above has been attempted fixed by
+		 *          introduction of task->tk_sleeping.
 		 */
-		save_flags(oldflags); cli();
-		if (RPC_IS_RUNNING(task)) {
-			if (task->tk_rpcwait == &schedq)
-				rpc_remove_wait_queue(task);
-		} else while (!RPC_IS_RUNNING(task)) {
+		spin_lock_irqsave(&rpc_queue_lock, oldflags);
+		if (!RPC_IS_RUNNING(task)) {
+			task->tk_sleeping = 1;
 			if (RPC_IS_ASYNC(task)) {
-				restore_flags(oldflags);
+				spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 				return 0;
 			}
+		} else
+			task->tk_sleeping = 0;
+		spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 
+		while (RPC_IS_SLEEPING(task)) {
 			/* sync task: sleep here */
 			dprintk("RPC: %4d sync task going to sleep\n",
 							task->tk_pid);
-			if (current->pid == rpciod_pid)
-				printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
+			if (current->pid == rpciod_pid) {
+				printk(KERN_ERR "RPC: rpciod waiting on sync task %4d!\n", task->tk_pid);
+				rpc_show_tasks();
+			}
 
-			__wait_event(task->tk_wait, RPC_IS_RUNNING(task));
+			__wait_event(task->tk_wait, !RPC_IS_SLEEPING(task));
+			dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
 
 			/*
-			 * When the task received a signal, remove from
-			 * any queues etc, and make runnable again.
+			 * When a sync task receives a signal, it exits with
+			 * -ERESTARTSYS. In order to catch any callbacks that
+			 * clean up after sleeping on some queue, we don't
+			 * break the loop here, but go around once more.
 			 */
-			if (signalled()) { 
-				cli(); 
-				__rpc_wake_up(task);
+			if (task->tk_client->cl_intr && signalled()) {
+				dprintk("RPC: %4d got signal\n", task->tk_pid);
+				task->tk_flags |= RPC_TASK_KILLED;
+				rpc_exit(task, -ERESTARTSYS);
+				rpc_wake_up_task(task);
 			}
-
-			dprintk("RPC: %4d sync task resuming\n",
-							task->tk_pid);
-		}
-		restore_flags(oldflags);
-
-		/*
-		 * When a sync task receives a signal, it exits with
-		 * -ERESTARTSYS. In order to catch any callbacks that
-		 * clean up after sleeping on some queue, we don't
-		 * break the loop here, but go around once more.
-		 */
-		if (!RPC_IS_ASYNC(task) && signalled()) {
-			dprintk("RPC: %4d got signal\n", task->tk_pid);
-			rpc_exit(task, -ERESTARTSYS);
 		}
 	}
 
 	dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
 	if (task->tk_exit) {
-		status = task->tk_status;
 		task->tk_exit(task);
+		/* If tk_action is non-null, the user wants us to restart */
+		if (task->tk_action) {
+			if (!RPC_ASSASSINATED(task)) {
+					/* Release RPC slot and buffer memory */
+					if (task->tk_rqstp)
+						xprt_release(task);
+					if (task->tk_buffer) {
+						rpc_free(task->tk_buffer);
+						task->tk_buffer = NULL;
+					}
+					goto restarted;
+			}
+			printk(KERN_ERR "RPC: dead task tries to walk away.\n");
+		}
 	}
 
+	/* Save the task exit status */
+	status = task->tk_status;
+
+	/* Release all resources associated with the task */
+	rpc_release_task(task);
 	return status;
 }
 
 /*
  * User-visible entry point to the scheduler.
- * The recursion protection is for debugging. It should go away once
- * the code has stabilized.
+ *
+ * This may be called recursively if e.g. an async NFS task updates
+ * the attributes and finds that dirty pages must be flushed.
  */
-void
+int
 rpc_execute(struct rpc_task *task)
 {
-	static int	executing = 0;
-	int		incr = RPC_IS_ASYNC(task)? 1 : 0;
-
-	if (incr) {
-		if (rpc_inhibit) {
-			printk(KERN_INFO "RPC: execution inhibited!\n");
-			return;
-		}
-		if (executing)
-			printk(KERN_WARNING "RPC: %d tasks executed\n", executing);
+	if (rpc_inhibit) {
+		printk(KERN_INFO "RPC: execution inhibited!\n");
+		return -EIO;
+	}
+	task->tk_running = 1;
+	if (task->tk_active) {
+		printk(KERN_ERR "RPC: active task was run twice!\n");
+		return -EWOULDBLOCK;
 	}
+	task->tk_active = 1;
 	
-	executing += incr;
-	__rpc_execute(task);
-	executing -= incr;
+	return __rpc_execute(task);
 }
 
 /*
@@ -514,31 +710,36 @@
 __rpc_schedule(void)
 {
 	struct rpc_task	*task;
-	int		count = 0;
 	unsigned long	oldflags;
-	int need_resched = current->need_resched;
+	int		count = 0;
 
 	dprintk("RPC:      rpc_schedule enter\n");
-	save_flags(oldflags);
 	while (1) {
-		cli();
-		if (!(task = schedq.task))
+		/* Ensure equal rights for tcp tasks... */
+		rpciod_tcp_dispatcher();
+
+		spin_lock_irqsave(&rpc_queue_lock, oldflags);
+		if (!(task = schedq.task)) {
+			spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 			break;
-		rpc_del_timer(task);
-		rpc_remove_wait_queue(task);
-		task->tk_flags |= RPC_TASK_RUNNING;
-		restore_flags(oldflags);
+		}
+		if (task->tk_lock) {
+			spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+			printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n");
+			rpc_debug = ~0;
+			rpc_show_tasks();
+			break;
+		}
+		__rpc_remove_wait_queue(task);
+		spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 
 		__rpc_execute(task);
 
-		if (++count >= 200) {
+		if (++count >= 200 || current->need_resched) {
+			schedule();
 			count = 0;
-			need_resched = 1;
 		}
-		if (need_resched)
-			schedule();
 	}
-	restore_flags(oldflags);
 	dprintk("RPC:      rpc_schedule leave\n");
 }
 
@@ -579,7 +780,8 @@
 			dprintk("RPC:      allocated buffer %p\n", buffer);
 			return buffer;
 		}
-		if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) {
+		if ((flags & RPC_TASK_SWAPPER) && size <= sizeof(swap_buffer)
+		 && rpc_lock_swapbuf()) {
 			dprintk("RPC:      used last-ditch swap buffer\n");
 			return swap_buffer;
 		}
@@ -587,6 +789,7 @@
 			return NULL;
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(HZ>>4);
+		current->state = TASK_RUNNING;
 	} while (!signalled());
 
 	return NULL;
@@ -599,20 +802,24 @@
 		kfree(buffer);
 		return;
 	}
-	swap_buffer_used = 0;
+	rpc_unlock_swapbuf();
 }
 
 /*
  * Creation and deletion of RPC task structures
  */
-inline void
+void
 rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
 				rpc_action callback, int flags)
 {
 	memset(task, 0, sizeof(*task));
+	init_timer(&task->tk_timer);
+	task->tk_timer.data     = (unsigned long) task;
+	task->tk_timer.function = (void (*)(unsigned long)) rpc_run_timer;
+
 	task->tk_client = clnt;
-	task->tk_flags  = RPC_TASK_RUNNING | flags;
 	task->tk_exit   = callback;
+	task->tk_flags  = flags;
 	if (current->uid != current->fsuid || current->gid != current->fsgid)
 		task->tk_flags |= RPC_TASK_SETUID;
 
@@ -621,6 +828,11 @@
 	task->tk_cred_retry = 2;
 	task->tk_suid_retry = 1;
 
+#ifdef RPC_DEBUG
+	task->tk_magic = 0xf00baa;
+	task->tk_pid = rpc_task_id++;
+#endif
+
 	/* Add to global list of all tasks */
 	task->tk_next_task = all_tasks;
 	task->tk_prev_task = NULL;
@@ -629,14 +841,11 @@
 	all_tasks = task;
 
 	if (clnt)
-		clnt->cl_users++;
+		atomic_inc(&clnt->cl_users);
 
-#ifdef RPC_DEBUG
-	task->tk_magic = 0xf00baa;
-	task->tk_pid = rpc_task_id++;
-#endif
-	dprintk("RPC: %4d new task procpid %d\n", task->tk_pid,
-				current->pid);
+	dprintk("RPC: %d new task procpid %d%s\n",
+			task->tk_pid, current->pid,
+			(flags & RPC_TASK_DYNAMIC) ? " (alloc)" : "");
 }
 
 /*
@@ -653,10 +862,7 @@
 	if (!task)
 		goto cleanup;
 
-	rpc_init_task(task, clnt, callback, flags);
-
-	dprintk("RPC: %4d allocated task\n", task->tk_pid);
-	task->tk_flags |= RPC_TASK_DYNAMIC;
+	rpc_init_task(task, clnt, callback, flags | RPC_TASK_DYNAMIC);
 out:
 	return task;
 
@@ -664,8 +870,8 @@
 	/* Check whether to release the client */
 	if (clnt) {
 		printk("rpc_new_task: failed, users=%d, oneshot=%d\n",
-			clnt->cl_users, clnt->cl_oneshot);
-		clnt->cl_users++; /* pretend we were used ... */
+			atomic_read(&clnt->cl_users), clnt->cl_oneshot);
+		atomic_inc(&clnt->cl_users); /* pretend we were used ... */
 		rpc_release_client(clnt);
 	}
 	goto out;
@@ -675,9 +881,19 @@
 rpc_release_task(struct rpc_task *task)
 {
 	struct rpc_task	*next, *prev;
+	unsigned long	oldflags;
 
 	dprintk("RPC: %4d release task\n", task->tk_pid);
 
+#ifdef RPC_DEBUG
+	if (task->tk_magic != 0xf00baa) {
+		printk(KERN_ERR "RPC: attempt to release a non-existing task!\n");
+		rpc_debug = ~0;
+		rpc_show_tasks();
+		return;
+	}
+#endif
+
 	/* Remove from global task list */
 	prev = task->tk_prev_task;
 	next = task->tk_next_task;
@@ -687,12 +903,29 @@
 		prev->tk_next_task = next;
 	else
 		all_tasks = next;
+	task->tk_next_task = task->tk_prev_task = NULL;
+
+	/* Protect the execution below. */
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+
+	/* Disable timer to prevent zombie wakeup */
+	__rpc_disable_timer(task);
+
+	/* Remove from any wait queue we're still on */
+	__rpc_remove_wait_queue(task);
+
+	task->tk_active = 0;
+
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+
+	/* Synchronously delete any running timer */
+	rpc_delete_timer(task);
 
 	/* Release resources */
 	if (task->tk_rqstp)
 		xprt_release(task);
-	if (task->tk_cred)
-		rpcauth_releasecred(task);
+	if (task->tk_msg.rpc_cred)
+		rpcauth_unbindcred(task);
 	if (task->tk_buffer) {
 		rpc_free(task->tk_buffer);
 		task->tk_buffer = NULL;
@@ -708,9 +941,12 @@
 
 	if (task->tk_flags & RPC_TASK_DYNAMIC) {
 		dprintk("RPC: %4d freeing task\n", task->tk_pid);
+		if (task->tk_release)
+			task->tk_release(task);
 		task->tk_flags &= ~RPC_TASK_DYNAMIC;
 		rpc_free(task);
-	}
+	} else if (task->tk_release)
+			task->tk_release(task);
 }
 
 /*
@@ -719,14 +955,18 @@
  * parent task may already have gone away
  */
 static inline struct rpc_task *
-rpc_find_parent(struct rpc_task *child)
+__rpc_find_parent(struct rpc_task *child)
 {
-	struct rpc_task	*temp, *parent;
+	struct rpc_task	*head, *parent;
 
-	parent = (struct rpc_task *) child->tk_calldata;
-	for (temp = childq.task; temp; temp = temp->tk_next) {
-		if (temp == parent)
-			return parent;
+	parent = child->tk_parent;
+	if ((head = childq.task) != NULL) {
+		struct rpc_task	*task = head;
+		do {
+			if (task == parent)
+				return parent;
+			task = task->tk_next;
+		} while (task != head);
 	}
 	return NULL;
 }
@@ -735,12 +975,14 @@
 rpc_child_exit(struct rpc_task *child)
 {
 	struct rpc_task	*parent;
+	unsigned long	oldflags;
 
-	if ((parent = rpc_find_parent(child)) != NULL) {
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
+	if ((parent = __rpc_find_parent(child)) != NULL) {
 		parent->tk_status = child->tk_status;
-		rpc_wake_up_task(parent);
+		__rpc_wake_up_task(parent);
 	}
-	rpc_release_task(child);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -755,7 +997,7 @@
 	if (!task)
 		goto fail;
 	task->tk_exit = rpc_child_exit;
-	task->tk_calldata = parent;
+	task->tk_parent = parent;
 	return task;
 
 fail:
@@ -766,13 +1008,13 @@
 void
 rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func)
 {
-	unsigned long oldflags;
+	unsigned long	oldflags;
 
-	save_flags(oldflags); cli();
-	rpc_make_runnable(child);
-	restore_flags(oldflags);
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	/* N.B. Is it possible for the child to have already finished? */
-	rpc_sleep_on(&childq, task, func, NULL);
+	__rpc_sleep_on(&childq, task, func, NULL);
+	__rpc_schedule_run(child);
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
 }
 
 /*
@@ -785,8 +1027,9 @@
 	struct rpc_task	**q, *rovr;
 
 	dprintk("RPC:      killing all tasks for client %p\n", clnt);
-	/* N.B. Why bother to inhibit? Nothing blocks here ... */
-	rpc_inhibit++;
+	/*
+	 * Spin lock all_tasks to prevent changes...
+	 */
 	for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) {
 		if (!clnt || rovr->tk_client == clnt) {
 			rovr->tk_flags |= RPC_TASK_KILLED;
@@ -794,11 +1037,16 @@
 			rpc_wake_up_task(rovr);
 		}
 	}
-	rpc_inhibit--;
 }
 
 static struct semaphore rpciod_running = MUTEX_LOCKED;
 
+static inline int
+rpciod_task_pending(void)
+{
+	return schedq.task != NULL || xprt_tcp_pending();
+}
+
 /*
  * This is the rpciod kernel thread
  */
@@ -806,11 +1054,12 @@
 rpciod(void *ptr)
 {
 	struct wait_queue **assassin = (struct wait_queue **) ptr;
-	unsigned long	oldflags;
 	int		rounds = 0;
 
 	MOD_INC_USE_COUNT;
+
 	lock_kernel();
+
 	/*
 	 * Let our maker know we're running ...
 	 */
@@ -837,22 +1086,17 @@
 		}
 		__rpc_schedule();
 
-		if (++rounds >= 64) {	/* safeguard */
+		if (++rounds >= 64 || current->need_resched) {	/* safeguard */
 			schedule();
 			rounds = 0;
 		}
-		save_flags(oldflags); cli();
-		dprintk("RPC: rpciod running checking dispatch\n");
-		rpciod_tcp_dispatcher();
 
-		if (!schedq.task) {
+		if (!rpciod_task_pending()) {
 			dprintk("RPC: rpciod back to sleep\n");
-			interruptible_sleep_on(&rpciod_idle);
+			wait_event_interruptible(rpciod_idle, rpciod_task_pending());
 			dprintk("RPC: switch to rpciod\n");
-			rpciod_tcp_dispatcher();
 			rounds = 0;
 		}
-		restore_flags(oldflags);
 	}
 
 	dprintk("RPC: rpciod shutdown commences\n");
@@ -882,6 +1126,7 @@
 			dprintk("rpciod_killall: waiting for tasks to exit\n");
 			current->state = TASK_INTERRUPTIBLE;
 			schedule_timeout(1);
+			current->state = TASK_RUNNING;
 		}
 	}
 
@@ -953,6 +1198,7 @@
 	current->sigpending = 0;
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(1);
+	current->state = TASK_RUNNING;
 	/*
 	 * Display a message if we're going to wait longer.
 	 */
@@ -972,36 +1218,23 @@
 	MOD_DEC_USE_COUNT;
 }
 
-#ifdef RPC_DEBUG
-#include <linux/nfs_fs.h>
 void rpc_show_tasks(void)
 {
 	struct rpc_task *t = all_tasks, *next;
-	struct nfs_wreq *wreq;
 
-	if (!t)
+	t = all_tasks;
+	if (!t) {
 		return;
+	}
 	printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout "
 		"-rpcwait -action- --exit--\n");
 	for (; t; t = next) {
 		next = t->tk_next_task;
 		printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n",
-			t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status,
+			t->tk_pid, t->tk_msg.rpc_proc, t->tk_flags, t->tk_status,
 			t->tk_client, t->tk_client->cl_prog,
 			t->tk_rqstp, t->tk_timeout,
 			t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " <NULL> ",
 			t->tk_action, t->tk_exit);
-
-		if (!(t->tk_flags & RPC_TASK_NFSWRITE))
-			continue;
-		/* NFS write requests */
-		wreq = (struct nfs_wreq *) t->tk_calldata;
-		printk("     NFS: flgs=%08x, pid=%d, pg=%p, off=(%d, %d)\n",
-			wreq->wb_flags, wreq->wb_pid, wreq->wb_page,
-			wreq->wb_offset, wreq->wb_bytes);
-		printk("          name=%s/%s\n",
-			wreq->wb_file->f_dentry->d_parent->d_name.name,
-			wreq->wb_file->f_dentry->d_name.name);
 	}
 }
-#endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/stats.c linux.18p17/net/sunrpc/stats.c
--- linux.vanilla/net/sunrpc/stats.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/stats.c	Fri Sep 15 22:10:44 2000
@@ -128,6 +128,7 @@
 {
 	struct proc_dir_entry	*ent;
 
+	rpc_proc_init();
 	dprintk("RPC: registering /proc/net/rpc/%s\n", name);
 	ent = create_proc_entry(name, 0, proc_net_rpc);
 	ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
@@ -174,6 +175,9 @@
 			proc_net_rpc = ent;
 		}
 	}
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
 }
 
 void
@@ -184,6 +188,9 @@
 		proc_net_rpc = NULL;
 		remove_proc_entry("net/rpc", 0);
 	}
+#ifdef RPC_DEBUG
+	rpc_unregister_sysctl();
+#endif
 }
 
 #ifdef MODULE
@@ -206,9 +213,6 @@
 int
 init_module(void)
 {
-#ifdef RPC_DEBUG
-	rpc_register_sysctl();
-#endif
 	rpc_proc_init();
 	return 0;
 }
@@ -216,9 +220,6 @@
 void
 cleanup_module(void)
 {
-#ifdef RPC_DEBUG
-	rpc_unregister_sysctl();
-#endif
 	rpc_proc_exit();
 }
 #endif
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/sunrpc_syms.c linux.18p17/net/sunrpc/sunrpc_syms.c
--- linux.vanilla/net/sunrpc/sunrpc_syms.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/sunrpc_syms.c	Fri Sep 15 22:10:44 2000
@@ -26,13 +26,17 @@
 EXPORT_SYMBOL(rpc_allocate);
 EXPORT_SYMBOL(rpc_free);
 EXPORT_SYMBOL(rpc_execute);
+EXPORT_SYMBOL(rpc_new_task);
 EXPORT_SYMBOL(rpc_init_task);
 EXPORT_SYMBOL(rpc_release_task);
 EXPORT_SYMBOL(rpc_sleep_on);
+EXPORT_SYMBOL(rpc_wake_up);
 EXPORT_SYMBOL(rpc_wake_up_next);
 EXPORT_SYMBOL(rpc_wake_up_task);
+EXPORT_SYMBOL(rpc_wake_up_status);
 EXPORT_SYMBOL(rpc_new_child);
 EXPORT_SYMBOL(rpc_run_child);
+EXPORT_SYMBOL(rpciod_wake_up);
 EXPORT_SYMBOL(rpciod_down);
 EXPORT_SYMBOL(rpciod_up);
 
@@ -41,7 +45,8 @@
 EXPORT_SYMBOL(rpc_destroy_client);
 EXPORT_SYMBOL(rpc_shutdown_client);
 EXPORT_SYMBOL(rpc_killall_tasks);
-EXPORT_SYMBOL(rpc_do_call);
+EXPORT_SYMBOL(rpc_call_sync);
+EXPORT_SYMBOL(rpc_call_async);
 EXPORT_SYMBOL(rpc_call_setup);
 EXPORT_SYMBOL(rpc_clnt_sigmask);
 EXPORT_SYMBOL(rpc_clnt_sigunmask);
@@ -60,7 +65,9 @@
 EXPORT_SYMBOL(rpcauth_free_credcache);
 EXPORT_SYMBOL(rpcauth_insert_credcache);
 EXPORT_SYMBOL(rpcauth_lookupcred);
+EXPORT_SYMBOL(rpcauth_bindcred);
 EXPORT_SYMBOL(rpcauth_matchcred);
+EXPORT_SYMBOL(rpcauth_holdcred);
 EXPORT_SYMBOL(rpcauth_releasecred);
 
 /* RPC server stuff */
@@ -93,6 +100,9 @@
 EXPORT_SYMBOL(xdr_encode_netobj);
 EXPORT_SYMBOL(xdr_zero);
 EXPORT_SYMBOL(xdr_one);
+EXPORT_SYMBOL(xdr_two);
+EXPORT_SYMBOL(xdr_shift_iovec);
+EXPORT_SYMBOL(xdr_zero_iovec);
 
 /* RPC errors */
 EXPORT_SYMBOL(rpc_success);
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/svc.c linux.18p17/net/sunrpc/svc.c
--- linux.vanilla/net/sunrpc/svc.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/svc.c	Sun Oct 15 21:15:18 2000
@@ -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 -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/svcsock.c linux.18p17/net/sunrpc/svcsock.c
--- linux.vanilla/net/sunrpc/svcsock.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/svcsock.c	Thu Oct 19 01:13:04 2000
@@ -344,8 +344,9 @@
 		return;
 	dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n",
 		svsk, sk, count, svsk->sk_busy);
-	svsk->sk_data = 1;
+	svsk->sk_data++;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -358,19 +359,10 @@
 	struct svc_serv	*serv = svsk->sk_server;
 	struct sk_buff	*skb;
 	u32		*data;
-	int		err, len;
-
-	svsk->sk_data = 0;
-	while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) {
-		svc_sock_received(svsk, 0);
-		if (err == -EAGAIN)
-			return err;
-		/* possibly an icmp error */
-		dprintk("svc: recvfrom returned error %d\n", -err);
-	}
+	int		err, len, recvd = svsk->sk_data;
 
-	/* There may be more data */
-	svsk->sk_data = 1;
+	if ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL)
+		goto out_err;
 
 	len  = skb->len - sizeof(struct udphdr);
 	data = (u32 *) (skb->h.raw + sizeof(struct udphdr));
@@ -390,15 +382,24 @@
 #else
 	rqstp->rq_addr.sin_addr.s_addr = skb->saddr;
 #endif
+	memset(rqstp->rq_addr.sin_zero, 0, sizeof(rqstp->rq_addr.sin_zero));
 
 	if (serv->sv_stats)
 		serv->sv_stats->netudpcnt++;
 
 	/* One down, maybe more to go... */
 	svsk->sk_sk->stamp = skb->stamp;
-	svc_sock_received(svsk, 0);
+	svc_sock_received(svsk, 1);
 
 	return len;
+ out_err:
+	if (err != -EAGAIN) {
+		/* possibly an icmp error */
+		dprintk("svc: recvfrom returned error %d\n", -err);
+		svc_sock_received(svsk, 0);
+	} else
+		svc_sock_received(svsk, recvd);
+	return -EAGAIN;
 }
 
 static int
@@ -458,6 +459,7 @@
 	}
 	svsk->sk_conn++;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -477,6 +479,7 @@
 	}
 	svsk->sk_close = 1;
 	svc_sock_enqueue(svsk);
+	wake_up_interruptible(sk->sleep);
 }
 
 static void
@@ -548,15 +551,18 @@
 
 	/* Ideally, we would want to reject connections from unauthorized
 	 * hosts here, but we have no generic client tables. For now,
-	 * we just punt connects from unprivileged ports. */
-	if (ntohs(sin.sin_port) >= 1024) {
-		if (net_ratelimit())
-			printk(KERN_WARNING
-				   "%s: connect from unprivileged port: %s:%d",
-				   serv->sv_name, 
-				   in_ntoa(sin.sin_addr.s_addr), ntohs(sin.sin_port));
-		goto failed;
-	}
+	 * we just punt connects from unprivileged ports. 
+         * hosts here, but when we get encription, the IP of the host won't
+         * tell us anything. For now just warn about unpriv connections.
+         */
+        if (ntohs(sin.sin_port) >= 1024) {
+                if (net_ratelimit())
+                        printk(KERN_WARNING
+                                   "%s: connect from unprivileged port: %u.%u.%u.%u:%d\n",
+                                   serv->sv_name, 
+                                   NIPQUAD(sin.sin_addr.s_addr), 
+			           ntohs(sin.sin_port));
+        }
 
 	dprintk("%s: connect from %s:%04x\n", serv->sv_name,
 			in_ntoa(sin.sin_addr.s_addr), ntohs(sin.sin_port));
@@ -590,7 +596,7 @@
 	struct svc_sock	*svsk = rqstp->rq_sock;
 	struct svc_serv	*serv = svsk->sk_server;
 	struct svc_buf	*bufp = &rqstp->rq_argbuf;
-	int		len, ready;
+	int		len, ready, used;
 
 	dprintk("svc: tcp_recv %p data %d conn %d close %d\n",
 			svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close);
@@ -624,10 +630,11 @@
 
 		svsk->sk_reclen = ntohl(svsk->sk_reclen);
 		if (!(svsk->sk_reclen & 0x80000000)) {
-			/* FIXME: shutdown socket */
-			printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
-			       (unsigned long) svsk->sk_reclen);
-			return -EIO;
+			if (net_ratelimit())
+				printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
+			       		(unsigned long) svsk->sk_reclen);
+			svc_delete_socket(svsk);
+			return 0;
 		}
 		svsk->sk_reclen &= 0x7fffffff;
 		dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen);
@@ -642,8 +649,19 @@
 		dprintk("svc: incomplete TCP record (%d of %d)\n",
 			len, svsk->sk_reclen);
 		svc_sock_received(svsk, ready);
-		len = -EAGAIN;	/* record not complete */
+		return -EAGAIN;	/* record not complete */
+	}
+
+        /* if we think there is only one more record to read, but
+         * it is bigger than we expect, then two records must have arrived
+         * together, so pretend we aren't using the record.. */
+        if (len > svsk->sk_reclen && ready == 1){
+                used = 0;
+		dprintk("svc_recv: more data at hte socket len %d > svsk->sk_reclen %d",
+                        len, svsk->sk_reclen);
 	}
+        else    used = 1;
+ 
 
 	/* Frob argbuf */
 	bufp->iov[0].iov_base += 4;
@@ -670,7 +688,7 @@
 	svsk->sk_reclen = 0;
 	svsk->sk_tcplen = 0;
 
-	svc_sock_received(svsk, 1);
+	svc_sock_received(svsk, used);
 	if (serv->sv_stats)
 		serv->sv_stats->nettcpcnt++;
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/sysctl.c linux.18p17/net/sunrpc/sysctl.c
--- linux.vanilla/net/sunrpc/sysctl.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/sysctl.c	Fri Sep 15 22:10:44 2000
@@ -99,9 +99,8 @@
 			left--, p++;
 		*(unsigned int *) table->data = value;
 		/* Display the RPC tasks on writing to rpc_debug */
-		if (table->ctl_name == CTL_RPCDEBUG) {
+		if (table->ctl_name == CTL_RPCDEBUG)
 			rpc_show_tasks();
-		}
 	} else {
 		if (!access_ok(VERIFY_WRITE, buffer, left))
 			return -EFAULT;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/xdr.c linux.18p17/net/sunrpc/xdr.c
--- linux.vanilla/net/sunrpc/xdr.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/xdr.c	Mon Sep 25 16:35:44 2000
@@ -9,9 +9,11 @@
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/string.h>
+#include <linux/kernel.h>
 #include <linux/in.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/msg_prot.h>
+#include <linux/nfs2.h>
 
 u32	rpc_success, rpc_prog_unavail, rpc_prog_mismatch, rpc_proc_unavail,
 	rpc_garbage_args, rpc_system_err;
@@ -84,11 +86,13 @@
 }
 
 u32 *
-xdr_encode_string(u32 *p, const char *string)
+xdr_encode_string(u32 *p, const char *string, int len)
 {
-	int len = strlen(string);
-	int quadlen = XDR_QUADLEN(len);
+	int quadlen;
 
+	if (len < 0)
+		len = strlen(string);
+	quadlen = XDR_QUADLEN(len);
 	p[quadlen] = 0;
 	*p++ = htonl(len);
 	memcpy(p, string, len);
@@ -116,3 +120,51 @@
 	return p + XDR_QUADLEN(len);
 }
 
+/*
+ * Realign the iovec if the server missed out some reply elements
+ * (such as post-op attributes,...)
+ * Note: This is a simple implementation that assumes that
+ *            len <= iov->iov_len !!!
+ *       The RPC header (assumed to be the 1st element in the iov array)
+ *            is not shifted.
+ */
+void xdr_shift_iovec(struct iovec *iov, int nr, size_t len)
+{
+	struct iovec *pvec;
+
+	for (pvec = iov + nr - 1; nr > 1; nr--, pvec--) {
+		struct iovec *svec = pvec - 1;
+
+		if (len > pvec->iov_len) {
+			printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
+			return;
+		}
+		memmove((char *)pvec->iov_base + len, pvec->iov_base,
+			pvec->iov_len - len);
+
+		if (len > svec->iov_len) {
+			printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
+			return;
+		}
+		memcpy(pvec->iov_base,
+		       (char *)svec->iov_base + svec->iov_len - len, len);
+	}
+}
+
+/*
+ * Zero the last n bytes in an iovec array of 'nr' elements
+ */
+void xdr_zero_iovec(struct iovec *iov, int nr, size_t n)
+{
+	struct iovec *pvec;
+
+	for (pvec = iov + nr - 1; n && nr > 0; nr--, pvec--) {
+		if (n < pvec->iov_len) {
+			memset((char *)pvec->iov_base + pvec->iov_len - n, 0, n);
+			n = 0;
+		} else {
+			memset(pvec->iov_base, 0, pvec->iov_len);
+			n -= pvec->iov_len;
+		}
+	}
+}
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/sunrpc/xprt.c linux.18p17/net/sunrpc/xprt.c
--- linux.vanilla/net/sunrpc/xprt.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/sunrpc/xprt.c	Fri Sep 15 22:10:44 2000
@@ -31,7 +31,7 @@
  *  primitives that `transparently' work for processes as well as async
  *  tasks that rely on callbacks.
  *
- *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ *  Copyright (C) 1995-1997, Olaf Kirch <okir@monad.swb.de>
  *
  *  TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
  *  TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
@@ -56,17 +56,20 @@
 #include <linux/file.h>
 
 #include <net/sock.h>
+#include <net/checksum.h>
 
 #include <asm/uaccess.h>
 
-#define SOCK_HAS_USER_DATA
+/* Following value should be > 32k + RPC overhead */
+#define XPRT_MIN_WRITE_SPACE (35000 + MIN_WRITE_SPACE)
+#define RPCXPRT_TIMEOUT 300*HZ
 
 /*
  * Local variables
  */
-#ifndef SOCK_HAS_USER_DATA
-static struct rpc_xprt *	sock_list = NULL;
-#endif
+
+/* Spinlock for critical sections in the code. */
+spinlock_t xprt_sock_lock = SPIN_LOCK_UNLOCKED;
 
 #ifdef RPC_DEBUG
 # undef  RPC_DEBUG_DATA
@@ -82,13 +85,15 @@
  * Local functions
  */
 static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
-static void	xprt_transmit_status(struct rpc_task *task);
-static void	xprt_receive_status(struct rpc_task *task);
-static void	xprt_reserve_status(struct rpc_task *task);
-static void	xprt_reconn_timeout(struct rpc_task *task);
+static void	do_xprt_transmit(struct rpc_task *);
+static void	__xprt_reserve_status(struct rpc_task *task);
+static void	__xprt_disconnect(struct rpc_xprt *);
 static void	xprt_reconn_status(struct rpc_task *task);
-static struct socket *xprt_create_socket(int, struct sockaddr_in *,
-					struct rpc_timeout *);
+static struct socket *xprt_create_socket(int, struct rpc_timeout *);
+static int	xprt_bind_socket(struct rpc_xprt *, struct socket *);
+static void	__xprt_append_pending(struct rpc_xprt *);
+static void	__xprt_remove_pending(struct rpc_xprt *);
+static int	__xprt_clear_backlog(struct rpc_xprt *xprt);
 
 #ifdef RPC_DEBUG_DATA
 /*
@@ -127,98 +132,88 @@
 static inline struct rpc_xprt *
 xprt_from_sock(struct sock *sk)
 {
-#ifndef SOCK_HAS_USER_DATA
-	struct rpc_xprt		*xprt;
-
-	for (xprt = sock_list; xprt && sk != xprt->inet; xprt = xprt->link)
-		;
-	return xprt;
-#else
 	return (struct rpc_xprt *) sk->user_data;
-#endif
 }
 
 /*
  *	Adjust the iovec to move on 'n' bytes
  */
  
-extern inline void xprt_move_iov(struct msghdr *msg, struct iovec *niv, int amount)
+extern inline void
+xprt_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
 {
 	struct iovec *iv=msg->msg_iov;
+	int i;
 	
 	/*
 	 *	Eat any sent iovecs
 	 */
-
-	while(iv->iov_len < amount)
-	{
-		amount-=iv->iov_len;
+	while (iv->iov_len <= amount) {
+		amount -= iv->iov_len;
 		iv++;
 		msg->msg_iovlen--;
 	}
-	
-	msg->msg_iov=niv;
-	
+
 	/*
 	 *	And chew down the partial one
 	 */
-
 	niv[0].iov_len = iv->iov_len-amount;
 	niv[0].iov_base =((unsigned char *)iv->iov_base)+amount;
 	iv++;
-	
+
 	/*
 	 *	And copy any others
 	 */
-	 
-	for(amount=1;amount<msg->msg_iovlen; amount++)
-	{
-		niv[amount]=*iv++;
-	}
+	for(i = 1; i < msg->msg_iovlen; i++)
+		niv[i]=*iv++;
+
+	msg->msg_iov=niv;
 }
  
 /*
  * Write data to socket.
  */
 
-static inline int
-xprt_sendmsg(struct rpc_xprt *xprt)
+static int
+xprt_sendmsg(struct rpc_xprt *xprt, struct rpc_rqst *req)
 {
 	struct socket	*sock = xprt->sock;
 	struct msghdr	msg;
 	mm_segment_t	oldfs;
 	int		result;
+	int		slen = req->rq_slen - req->rq_bytes_sent;
 	struct iovec	niv[MAX_IOVEC];
 
+	if (slen <= 0)
+		return 0;
+
+	if (!sock)
+		return -ENOTCONN;
+
 	xprt_pktdump("packet data:",
-				xprt->snd_buf.io_vec->iov_base,
-				xprt->snd_buf.io_vec->iov_len);
+				req->rq_svec->iov_base,
+				req->rq_svec->iov_len);
 
-	msg.msg_flags   = MSG_DONTWAIT;
-	msg.msg_iov	= xprt->snd_buf.io_vec;
-	msg.msg_iovlen	= xprt->snd_buf.io_nr;
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
+	msg.msg_iov	= req->rq_svec;
+	msg.msg_iovlen	= req->rq_snr;
 	msg.msg_name	= (struct sockaddr *) &xprt->addr;
 	msg.msg_namelen = sizeof(xprt->addr);
 	msg.msg_control = NULL;
 	msg.msg_controllen = 0;
 
 	/* Dont repeat bytes */
-	
-	if(xprt->snd_sent)
-		xprt_move_iov(&msg, niv, xprt->snd_sent);
-		
+	if (req->rq_bytes_sent)
+		xprt_move_iov(&msg, niv, req->rq_bytes_sent);
+
 	oldfs = get_fs(); set_fs(get_ds());
-	result = sock_sendmsg(sock, &msg, xprt->snd_buf.io_len);
+	result = sock_sendmsg(sock, &msg, slen);
 	set_fs(oldfs);
 
-	dprintk("RPC:      xprt_sendmsg(%d) = %d\n",
-				xprt->snd_buf.io_len, result);
+	dprintk("RPC:      xprt_sendmsg(%d) = %d\n", slen, result);
 
-	if (result >= 0) {
-		xprt->snd_buf.io_len -= result;
-		xprt->snd_sent += result;
+	if (result >= 0)
 		return result;
-	}
 
 	switch (result) {
 	case -ECONNREFUSED:
@@ -227,13 +222,17 @@
 		 */
 		break;
 	case -EAGAIN:
-		return 0;
-	case -ENOTCONN: case -EPIPE:
+		if (sock->flags & SO_NOSPACE)
+			result = -ENOMEM;
+		break;
+	case -ENOTCONN:
+	case -EPIPE:
 		/* connection broken */
+		if (xprt->stream)
+			result = -ENOTCONN;
 		break;
 	default:
 		printk(KERN_NOTICE "RPC: sendmsg returned error %d\n", -result);
-		result = 0;
 	}
 	return result;
 }
@@ -241,41 +240,33 @@
 /*
  * Read data from socket
  */
-static inline int
-xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len)
+static int
+xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, unsigned len, unsigned shift)
 {
 	struct socket	*sock = xprt->sock;
-	struct sockaddr_in sin;
 	struct msghdr	msg;
 	mm_segment_t	oldfs;
+	struct iovec	niv[MAX_IOVEC];
 	int		result;
 
-#if LINUX_VERSION_CODE >= 0x020100
-	msg.msg_flags   = MSG_DONTWAIT;
-	msg.msg_iov	= iov;
-	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sin;
-	msg.msg_namelen = sizeof(sin);
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
+	if (!sock)
+		return -ENOTCONN;
 
-	oldfs = get_fs(); set_fs(get_ds());
-	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
-	set_fs(oldfs);
-#else
-	int		alen = sizeof(sin);
-	msg.msg_flags   = 0;
+	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
 	msg.msg_iov	= iov;
 	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sin;
-	msg.msg_namelen = sizeof(sin);
+	msg.msg_name	= NULL;
+	msg.msg_namelen = 0;
 	msg.msg_control = NULL;
 	msg.msg_controllen = 0;
 
+	/* Adjust the iovec if we've already filled it */
+	if (shift)
+		xprt_move_iov(&msg, niv, shift);
+
 	oldfs = get_fs(); set_fs(get_ds());
-	result = sock->ops->recvmsg(sock, &msg, len, 1, 0, &alen);
+	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
 	set_fs(oldfs);
-#endif
 
 	dprintk("RPC:      xprt_recvmsg(iov %p, len %d) = %d\n",
 						iov, len, result);
@@ -288,7 +279,7 @@
  * We use a time-smoothed congestion estimator to avoid heavy oscillation.
  */
 static void
-xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
+__xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
 {
 	unsigned long	cwnd = xprt->cwnd;
 
@@ -304,14 +295,14 @@
 			cwnd = RPC_MAXCWND;
 		else
 			pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
-		xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE;
+		xprt->congtime = jiffies + ((cwnd * HZ/10) << 2) / RPC_CWNDSCALE;
 		dprintk("RPC:      cong %08lx, cwnd was %08lx, now %08lx, "
 			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
 			(xprt->congtime-jiffies)*1000/HZ);
 	} else if (result == -ETIMEDOUT) {
 		if ((cwnd >>= 1) < RPC_CWNDSCALE)
 			cwnd = RPC_CWNDSCALE;
-		xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE;
+		xprt->congtime = jiffies + ((cwnd * HZ/10) << 3) / RPC_CWNDSCALE;
 		dprintk("RPC:      cong %ld, cwnd was %ld, now %ld, "
 			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
 			(xprt->congtime-jiffies)*1000/HZ);
@@ -327,21 +318,82 @@
 int
 xprt_adjust_timeout(struct rpc_timeout *to)
 {
-	if (to->to_exponential)
-		to->to_current <<= 1;
-	else
-		to->to_current += to->to_increment;
-	if (to->to_maxval && to->to_current >= to->to_maxval) {
-		to->to_current = to->to_maxval;
-		to->to_retries = 0;
+	if (to->to_retries > 0) {
+		if (to->to_exponential)
+			to->to_current <<= 1;
+		else
+			to->to_current += to->to_increment;
+		if (to->to_maxval && to->to_current >= to->to_maxval)
+			to->to_current = to->to_maxval;
+	} else {
+		if (to->to_exponential)
+			to->to_initval <<= 1;
+		else
+			to->to_initval += to->to_increment;
+		if (to->to_maxval && to->to_initval >= to->to_maxval)
+			to->to_initval = to->to_maxval;
+		to->to_current = to->to_initval;
 	}
+
 	if (!to->to_current) {
 		printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n");
 		to->to_current = 5 * HZ;
 	}
 	pprintk("RPC: %lu %s\n", jiffies,
 			to->to_retries? "retrans" : "timeout");
-	return (to->to_retries)--;
+	return to->to_retries-- > 0;
+}
+
+static inline void
+__xprt_disable_tcp_timer(struct rpc_xprt *xprt)
+{
+	xprt->tcp_timeout = 0;
+}
+
+static inline void
+__xprt_delete_tcp_timer(struct rpc_xprt *xprt)
+{
+	if (timer_pending(&xprt->tcp_timer))
+		del_timer(&xprt->tcp_timer);
+}
+
+/*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_delete_tcp_timer(struct rpc_xprt *xprt)
+{
+	start_bh_atomic();
+	__xprt_delete_tcp_timer(xprt);
+	end_bh_atomic();
+}
+
+static void
+xprt_tcp_timeout(struct rpc_xprt *xprt)
+{
+	if (xprt->tcp_timeout) {
+		__xprt_disable_tcp_timer(xprt);
+		__xprt_disconnect(xprt);
+	}
+}
+
+static inline void
+__xprt_add_tcp_timer(struct rpc_xprt *xprt, long timeout)
+{
+	if ((xprt->tcp_timeout = timeout) == 0)
+		return;
+	mod_timer(&xprt->tcp_timer, jiffies + xprt->tcp_timeout);
+}
+
+/*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_add_tcp_timer(struct rpc_xprt *xprt, long timeout)
+{
+	start_bh_atomic();
+	__xprt_add_tcp_timer(xprt, timeout);
+	end_bh_atomic();
 }
 
 /*
@@ -350,24 +402,39 @@
 static void
 xprt_close(struct rpc_xprt *xprt)
 {
-	struct sock	*sk = xprt->inet;
+	struct socket	*sock;
+	struct sock	*sk;
+
+	start_bh_atomic();
+	if (!(sock = xprt->sock)) {
+		end_bh_atomic();
+		return;
+	}
+
+	__xprt_disable_tcp_timer(xprt);
+	xprt_clear_connected(xprt);
+	__xprt_remove_pending(xprt);
+	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+
+	sk = xprt->inet;
+	xprt->inet = NULL;
+	xprt->sock = NULL;
 
-#ifdef SOCK_HAS_USER_DATA
 	sk->user_data    = NULL;
-#endif
 	sk->data_ready   = xprt->old_data_ready;
 	sk->state_change = xprt->old_state_change;
 	sk->write_space  = xprt->old_write_space;
 
-	if (xprt->file)
-		fput(xprt->file);
-	else
-		sock_release(xprt->sock);
+	end_bh_atomic();
+
+	xprt_delete_tcp_timer(xprt);
+
+	sock_release(sock);
 	/*
 	 *	TCP doesnt require the rpciod now - other things may
 	 *	but rpciod handles that not us.
 	 */
-	if(xprt->stream && !xprt->connecting)
+	if(xprt->stream)
 		rpciod_down();
 }
 
@@ -375,12 +442,11 @@
  * Mark a transport as disconnected
  */
 static void
-xprt_disconnect(struct rpc_xprt *xprt)
+__xprt_disconnect(struct rpc_xprt *xprt)
 {
 	dprintk("RPC:      disconnected transport %p\n", xprt);
-	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
-	rpc_wake_up_status(&xprt->sending, -ENOTCONN);
-	xprt->connected = 0;
+	xprt_clear_connected(xprt);
+	__xprt_append_pending(xprt);
 }
 
 /*
@@ -390,151 +456,145 @@
 xprt_reconnect(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
-	struct socket	*sock;
-	struct sock	*inet;
+	struct socket	*sock = xprt->sock;
+	struct sock	*inet = xprt->inet;
 	int		status;
 
 	dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
-				task->tk_pid, xprt, xprt->connected);
-	task->tk_status = 0;
+				task->tk_pid, xprt, xprt_connected(xprt));
+	if (xprt->shutdown)
+		return;
+
+	if (!xprt->stream)
+		return;
+
+	if (!xprt->addr.sin_port) {
+		task->tk_status = -EIO;
+		return;
+	}
 
 	if (xprt->connecting) {
-		task->tk_timeout = xprt->timeout.to_maxval;
+		task->tk_timeout = 0;
 		rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
 		return;
 	}
 	xprt->connecting = 1;
 
-	/* Create an unconnected socket */
-	if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout)))
-		goto defer;
-
-#if LINUX_VERSION_CODE >= 0x020100
-	inet = sock->sk;
-#else
-	inet = (struct sock *) sock->data;
-#endif
-	inet->data_ready   = xprt->inet->data_ready;
-	inet->state_change = xprt->inet->state_change;
-	inet->write_space  = xprt->inet->write_space;
-#ifdef SOCK_HAS_USER_DATA
-	inet->user_data    = xprt;
-#endif
-
-	dprintk("RPC: %4d closing old socket\n", task->tk_pid);
-	xprt_disconnect(xprt);
-	xprt_close(xprt);
+	status = -ENOTCONN;
+	if (!inet) {
+		/* Create an unconnected socket */
+		if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout)))
+			goto defer;
+		xprt_bind_socket(xprt, sock);
+		inet = sock->sk;
+	}
 
-	/* Reset to new socket and default congestion */
-	xprt->sock = sock;
-	xprt->inet = inet;
-	xprt->cwnd = RPC_INITCWND;
+	xprt->tcp_offset = 0;
+	xprt->tcp_copied = 0;
+	xprt->tcp_more = 0;
 
 	/* Now connect it asynchronously. */
 	dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
 	status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
 				sizeof(xprt->addr), O_NONBLOCK);
+
+	xprt_add_tcp_timer(xprt, RPCXPRT_TIMEOUT);
+
 	if (status < 0) {
-		if (status != -EINPROGRESS && status != -EALREADY) {
+		switch (status) {
+		case -EALREADY:
+		case -EINPROGRESS:
+			status = 0;
+			break;
+		case -EISCONN:
+		case -EPIPE:
+			status = 0;
+			xprt_close(xprt);
+			goto defer;
+		default:
 			printk("RPC: TCP connect error %d!\n", -status);
+			xprt_close(xprt);
 			goto defer;
 		}
 
 		dprintk("RPC: %4d connect status %d connected %d\n",
-				task->tk_pid, status, xprt->connected);
-		task->tk_timeout = 60 * HZ;
+				task->tk_pid, status, xprt_connected(xprt));
 
 		start_bh_atomic();
-		if (!xprt->connected) {
-			rpc_sleep_on(&xprt->reconn, task,
-				xprt_reconn_status, xprt_reconn_timeout);
+		if (!xprt_connected(xprt)) {
+			task->tk_timeout = xprt->timeout.to_maxval;
+			rpc_sleep_on(&xprt->reconn, task, xprt_reconn_status, NULL);
 			end_bh_atomic();
 			return;
 		}
 		end_bh_atomic();
 	}
-
-	xprt->connecting = 0;
-	rpc_wake_up(&xprt->reconn);
-	return;
-
 defer:
-	task->tk_timeout = 30 * HZ;
-	rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
 	xprt->connecting = 0;
+	if (status < 0) {
+		rpc_delay(task, 5*HZ);
+		task->tk_status = -ENOTCONN;
+	}
+	rpc_wake_up(&xprt->reconn);
 }
 
 /*
- * Reconnect status
+ * Reconnect done
  */
 static void
 xprt_reconn_status(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 
-	dprintk("RPC: %4d xprt_reconn_status %d\n",
-				task->tk_pid, task->tk_status);
-	if (!xprt->connected && task->tk_status != -ETIMEDOUT) {
-		task->tk_timeout = 30 * HZ;
-		rpc_sleep_on(&xprt->reconn, task, NULL, xprt_reconn_timeout);
-	}
-}
-
-/*
- * Reconnect timeout. We just mark the transport as not being in the
- * process of reconnecting, and leave the rest to the upper layers.
- */
-static void
-xprt_reconn_timeout(struct rpc_task *task)
-{
-	dprintk("RPC: %4d xprt_reconn_timeout %d\n",
-				task->tk_pid, task->tk_status);
-	task->tk_status = -ENOTCONN;
-	task->tk_xprt->connecting = 0;
-	task->tk_timeout = 0;
-	rpc_wake_up_task(task);
+	xprt->connecting = 0;
+	rpc_wake_up(&xprt->reconn);
 }
 
 /*
- * Look up the RPC request corresponding to a reply.
+ * Look up the RPC request corresponding to a reply, and then lock it.
  */
-static inline struct rpc_rqst *
+static struct rpc_rqst *
 xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid)
 {
 	struct rpc_task	*head, *task;
 	struct rpc_rqst	*req;
+	unsigned long	oldflags;
 	int		safe = 0;
 
+	spin_lock_irqsave(&rpc_queue_lock, oldflags);
 	if ((head = xprt->pending.task) != NULL) {
 		task = head;
 		do {
 			if ((req = task->tk_rqstp) && req->rq_xid == xid)
-				return req;
+				goto out;
 			task = task->tk_next;
 			if (++safe > 100) {
 				printk("xprt_lookup_rqst: loop in Q!\n");
-				return NULL;
+				goto out_bad;
 			}
 		} while (task != head);
 	}
 	dprintk("RPC:      unknown XID %08x in reply.\n", xid);
-	return NULL;
+ out_bad:
+	req = NULL;
+ out:
+	if (req && !__rpc_lock_task(req->rq_task))
+		req = NULL;
+	spin_unlock_irqrestore(&rpc_queue_lock, oldflags);
+	return req;
 }
 
 /*
  * Complete reply received.
  * The TCP code relies on us to remove the request from xprt->pending.
  */
-static inline void
-xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+static void
+__xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
 {
 	struct rpc_task	*task = req->rq_task;
 
-	req->rq_rlen   = copied;
-	req->rq_gotit  = 1;
-
 	/* Adjust congestion window */
-	xprt_adjust_cwnd(xprt, copied);
+	__xprt_adjust_cwnd(xprt, copied);
 
 #ifdef RPC_PROFILE
 	/* Profile only reads for now */
@@ -555,19 +615,31 @@
 	}
 #endif
 
-	/* ... and wake up the process. */
 	dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied);
 	task->tk_status = copied;
+	req->rq_received = 1;
 
+	/* ... and wake up the process. */
 	rpc_wake_up_task(task);
 	return;
 }
 
 /*
+ * Safe for use outside BH contexts.
+ */
+static inline void
+xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+{
+	start_bh_atomic();
+	__xprt_complete_rqst(xprt, req, copied);
+	end_bh_atomic();
+}
+
+/*
  * Input handler for RPC replies. Called from a bottom half and hence
  * atomic.
  */
-static inline void
+static void
 udp_data_ready(struct sock *sk, int len)
 {
 	struct rpc_task	*task;
@@ -578,26 +650,34 @@
 	int		err, repsize, copied;
 
 	dprintk("RPC:      udp_data_ready...\n");
-	if (!(xprt = xprt_from_sock(sk)))
-		return;
+	if (!(xprt = xprt_from_sock(sk))) {
+		printk("RPC:      udp_data_ready request not found!\n");
+		goto wake;
+	}
+
 	dprintk("RPC:      udp_data_ready client %p\n", xprt);
 
 	if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
-		return;
-	repsize = skb->len - 8;	/* don't account for UDP header */
+		goto wake;
 
+	if (xprt->shutdown)
+		goto dropit;
+
+	repsize = skb->len - sizeof(struct udphdr);
 	if (repsize < 4) {
 		printk("RPC: impossible RPC reply size %d!\n", repsize);
 		goto dropit;
 	}
 
-	/* Look up the request corresponding to the given XID */
-	if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8))))
+	/* Look up and lock the request corresponding to the given XID */
+	rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + sizeof(struct udphdr)));
+	if (!rovr)
 		goto dropit;
 	task = rovr->rq_task;
 
-	dprintk("RPC: %4d received reply\n", task->tk_pid);
-	xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize);
+	dprintk("RPC: %4d received reply, (%d bytes)\n", task->tk_pid, repsize);
+	xprt_pktdump("packet data:",
+		     (u32 *) (skb->h.raw+sizeof(struct udphdr)), repsize);
 
 	if ((copied = rovr->rq_rlen) > repsize)
 		copied = repsize;
@@ -605,226 +685,284 @@
 	/* Okay, we have it. Copy datagram... */
 	memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
 	/* This needs to stay tied with the usermode skb_copy_dagram... */
-	memcpy_tokerneliovec(iov, skb->data+8, copied);
+	memcpy_tokerneliovec(iov, skb->h.raw + sizeof(struct udphdr), copied);
 
-	xprt_complete_rqst(xprt, rovr, copied);
+	__xprt_complete_rqst(xprt, rovr, copied);
 
-dropit:
+	rpc_unlock_task(task);
+ dropit:
 	skb_free_datagram(sk, skb);
-	return;
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
- * TCP record receive routine
- * This is not the most efficient code since we call recvfrom twice--
- * first receiving the record marker and XID, then the data.
- * 
- * The optimal solution would be a RPC support in the TCP layer, which
- * would gather all data up to the next record marker and then pass us
- * the list of all TCP segments ready to be copied.
+ * TCP read fragment marker
  */
 static inline int
-tcp_input_record(struct rpc_xprt *xprt)
+tcp_read_fraghdr(struct rpc_xprt *xprt)
 {
-	struct rpc_rqst	*req;
-	struct iovec	*iov;
 	struct iovec	riov;
-	u32		offset;
-	int		result, maxcpy, reclen, avail, want;
+	int		want, result;
 
-	dprintk("RPC:      tcp_input_record\n");
-	offset = xprt->tcp_offset;
-	result = -EAGAIN;
-	if (offset < 4 || (!xprt->tcp_more && offset < 8)) {
-		want = (xprt->tcp_more? 4 : 8) - offset;
-		dprintk("RPC:      reading header (%d bytes)\n", want);
-		riov.iov_base = xprt->tcp_recm.data + offset;
+	if (xprt->tcp_offset >= xprt->tcp_reclen + sizeof(xprt->tcp_recm)) {
+		xprt->tcp_offset = 0;
+		xprt->tcp_reclen = 0;
+	}
+	if (xprt->tcp_offset >= sizeof(xprt->tcp_recm))
+		goto done;
+
+	want = sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+	dprintk("RPC:      reading header (%d bytes)\n", want);
+	do {
+		riov.iov_base = ((u8*) &xprt->tcp_recm) + xprt->tcp_offset;
 		riov.iov_len  = want;
-		result = xprt_recvmsg(xprt, &riov, 1, want);
-		if (!result)
-		{
-			dprintk("RPC: empty TCP record.\n");
-			return -ENOTCONN;
-		}
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+			return result;
+		xprt->tcp_offset += result;
+		want -= result;
+	} while (want);
 
-		/* Get the record length and mask out the more_fragments bit */
-		reclen = ntohl(xprt->tcp_reclen);
-		dprintk("RPC:      reclen %08x\n", reclen);
-		xprt->tcp_more = (reclen & 0x80000000)? 0 : 1;
-		reclen &= 0x7fffffff;
-		xprt->tcp_total += reclen;
-		xprt->tcp_reclen = reclen;
-
-		dprintk("RPC:      got xid %08x reclen %d morefrags %d\n",
-			xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more);
-		if (!xprt->tcp_copied
-		 && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) {
-			iov = xprt->tcp_iovec;
-			memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
-#if 0
-*(u32 *)iov->iov_base = req->rq_xid;
-#endif
-			iov->iov_base += 4;
-			iov->iov_len  -= 4;
-			xprt->tcp_copied = 4;
-			xprt->tcp_rqstp  = req;
-		}
-	} else {
-		reclen = xprt->tcp_reclen;
-	}
+	/* Is this another fragment in the last message */
+	if (!xprt->tcp_more)
+		xprt->tcp_copied = 0; /* No, so we're reading a new message */
+
+	/* Get the record length and mask out the last fragment bit */
+	xprt->tcp_reclen = ntohl(xprt->tcp_recm);
+	xprt->tcp_more = (xprt->tcp_reclen & 0x80000000) ? 0 : 1;
+	xprt->tcp_reclen &= 0x7fffffff;
+
+	dprintk("RPC:      New record reclen %d morefrags %d\n",
+				   xprt->tcp_reclen, xprt->tcp_more);
+ done:
+	return xprt->tcp_reclen + sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+}
+
+/*
+ * TCP read xid
+ */
+static inline int
+tcp_read_xid(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	int		want, result;
+
+	if (xprt->tcp_copied >= sizeof(xprt->tcp_xid) || !avail)
+		goto done;
+	want = MIN(sizeof(xprt->tcp_xid) - xprt->tcp_copied, avail);
+	do {
+		dprintk("RPC:      reading xid (%d bytes)\n", want);
+		riov.iov_base = ((u8*) &xprt->tcp_xid) + xprt->tcp_copied;
+		riov.iov_len  = want;
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
+		if (result < 0)
+			return result;
+		xprt->tcp_copied += result;
+		xprt->tcp_offset += result;
+		want  -= result;
+		avail -= result;
+	} while (want);
+ done:
+	return avail;
+}
 
-	avail = reclen - (offset - 4);
-	if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid
-	 && req->rq_task->tk_rpcwait == &xprt->pending) {
-		want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+/*
+ * TCP read and complete request
+ */
+static inline int
+tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req, int avail)
+{
+	int	want, result;
 
+	if (req->rq_rlen <= xprt->tcp_copied || !avail)
+		goto done;
+	want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+	do {
 		dprintk("RPC: %4d TCP receiving %d bytes\n",
-					req->rq_task->tk_pid, want);
-		result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
-		if (!result && want)
-			result = -EAGAIN;
+			req->rq_task->tk_pid, want);
+
+		result = xprt_recvmsg(xprt, req->rq_rvec, req->rq_rnr, want, xprt->tcp_copied);
 		if (result < 0)
-			goto done;
+			return result;
 		xprt->tcp_copied += result;
-		offset += result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
+		want   -= result;
+	} while (want);
 
-		maxcpy = MIN(req->rq_rlen, xprt->tcp_total);
-		if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) {
-			dprintk("RPC: %4d received reply complete\n",
-					req->rq_task->tk_pid);
-			xprt_complete_rqst(xprt, req, xprt->tcp_total);
-			xprt->tcp_copied = 0;
-			xprt->tcp_rqstp  = NULL;
-		}
-		/* Request must be re-encoded before retransmit */
-		req->rq_damaged = 1;
-	}
+ done:
+	if (req->rq_rlen > xprt->tcp_copied && xprt->tcp_more)
+		return avail;
+	dprintk("RPC: %4d received reply complete\n", req->rq_task->tk_pid);
+	xprt_complete_rqst(xprt, req, xprt->tcp_copied);
 
-	/* Skip over any trailing bytes on short reads */
-	while (avail) {
-		static u8	dummy[64];
+	return avail;
+}
+
+/*
+ * TCP discard extra bytes from a short read
+ */
+static inline int
+tcp_read_discard(struct rpc_xprt *xprt, int avail)
+{
+	struct iovec	riov;
+	static u8	dummy[64];
+	int		want, result = 0;
 
+	while (avail) {
 		want = MIN(avail, sizeof(dummy));
 		riov.iov_base = dummy;
 		riov.iov_len  = want;
 		dprintk("RPC:      TCP skipping %d bytes\n", want);
-		result = xprt_recvmsg(xprt, &riov, 1, want);
-		if (!result && want)
-			result=-EAGAIN;
+		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
 		if (result < 0)
-			goto done;
-		offset += result;
+			return result;
+		xprt->tcp_offset += result;
 		avail  -= result;
-		if (result < want) {
-			result = -EAGAIN;
-			goto done;
-		}
 	}
-	if (!xprt->tcp_more)
-		xprt->tcp_total = 0;
-	offset = 0;
-
-done:
-	dprintk("RPC:      tcp_input_record done (off %d total %d copied %d)\n",
-			offset, xprt->tcp_total, xprt->tcp_copied);
-	xprt->tcp_offset = offset;
-	return result;
+	return avail;
 }
 
-static __inline__ void tcp_output_record(struct rpc_xprt *xprt)
+/*
+ * TCP record receive routine
+ * This is not the most efficient code since we call recvfrom thrice--
+ * first receiving the record marker, then the XID, then the data.
+ * 
+ * The optimal solution would be a RPC support in the TCP layer, which
+ * would gather all data up to the next record marker and then pass us
+ * the list of all TCP segments ready to be copied.
+ */
+static int
+tcp_input_record(struct rpc_xprt *xprt)
 {
-	if(xprt->snd_sent && xprt->snd_task)
-		dprintk("RPC: write space\n");
-	if(xprt->write_space == 0)
-	{
-		xprt->write_space = 1;
-		if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
-		{
-			if(xprt->snd_sent)
-				dprintk("RPC: Write wakeup snd_sent =%d\n",
-					xprt->snd_sent);
-			rpc_wake_up_task(xprt->snd_task);			
-		}
+	struct rpc_rqst	*req = NULL;
+	struct rpc_task	*task = NULL;
+	int		avail, result;
+
+	dprintk("RPC:      tcp_input_record\n");
+
+	if (xprt->shutdown)
+		return -EIO;
+	if (!xprt_connected(xprt))
+		return -ENOTCONN;
+
+	/* Read in a new fragment marker if necessary */
+	/* Can we ever really expect to get completely empty fragments? */
+	if ((result = tcp_read_fraghdr(xprt)) <= 0)
+		return result;
+	avail = result;
+
+	/* Read in the xid if necessary */
+	if ((result = tcp_read_xid(xprt, avail)) <= 0)
+		return result;
+	avail = result;
+
+	/* Find and lock the request corresponding to this xid */
+	req = xprt_lookup_rqst(xprt, xprt->tcp_xid);
+	if (req) {
+		task = req->rq_task;
+		/* Read in the request data */
+		result = tcp_read_request(xprt,  req, avail);
+		rpc_unlock_task(task);
+		if (result < 0)
+			return result;
+		avail = result;
 	}
+
+	/* Skip over any trailing bytes on short reads */
+	if ((result = tcp_read_discard(xprt, avail)) < 0)
+		return result;
+
+	dprintk("RPC:      tcp_input_record done (off %d reclen %d copied %d)\n",
+			xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_copied);
+	result = xprt->tcp_reclen;
+	return result;
 }
 
 /*
  *	TCP task queue stuff
  */
- 
-static struct rpc_xprt *rpc_rx_xprt_pending = NULL;	/* Chain by rx_pending of rpc_xprt's */
-static struct rpc_xprt *rpc_tx_xprt_pending = NULL;	/* Chain by tx_pending of rpc_xprt's */
-
-/*
- *	This is protected from tcp_data_ready and the stack as its run
- *	inside of the RPC I/O daemon
- */
+LIST_HEAD(rpc_xprt_pending);	/* List of xprts having pending tcp requests */
 
-void rpciod_tcp_dispatcher(void)
+static inline
+void tcp_rpciod_queue(void)
 {
-	struct rpc_xprt *xprt;
-	int result;
+	rpciod_wake_up();
+}
+
+static void
+__xprt_append_pending(struct rpc_xprt *xprt)
+{
+	if (list_empty(&xprt->rx_pending)) {
+		list_add(&xprt->rx_pending, rpc_xprt_pending.prev);
+		dprintk("RPC:     xprt queue %p\n", xprt);
+		tcp_rpciod_queue();
+	}
+}
+
+static void
+__xprt_remove_pending(struct rpc_xprt *xprt)
+{
+	if (!list_empty(&xprt->rx_pending)) {
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+}
+
+static void
+xprt_remove_pending(struct rpc_xprt *xprt)
+{
+	start_bh_atomic();
+	__xprt_remove_pending(xprt);
+	end_bh_atomic();
+}
+
+static inline
+struct rpc_xprt *xprt_remove_pending_next(void)
+{
+	struct rpc_xprt	*xprt = NULL;
+
+	start_bh_atomic();
+	if (!list_empty(&rpc_xprt_pending)) {
+		xprt = list_entry(rpc_xprt_pending.next, struct rpc_xprt, rx_pending);
+		list_del(&xprt->rx_pending);
+		INIT_LIST_HEAD(&xprt->rx_pending);
+	}
+	end_bh_atomic();
+	return xprt;
+}
+
+/*
+ *	This is protected from tcp_data_ready and the stack as its run
+ *	inside of the RPC I/O daemon
+ */
+void
+__rpciod_tcp_dispatcher(void)
+{
+	struct rpc_xprt *xprt;
+	int safe_retry = 0, result;
 
 	dprintk("rpciod_tcp_dispatcher: Queue Running\n");
-	
+
 	/*
 	 *	Empty each pending socket
 	 */
-	 
-	while((xprt=rpc_rx_xprt_pending)!=NULL)
-	{
-		int safe_retry=0;
-		
-		rpc_rx_xprt_pending=xprt->rx_pending;
-		xprt->rx_pending_flag=0;
-		
+	while ((xprt = xprt_remove_pending_next()) != NULL) {
 		dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt);
-		
-		do 
-		{
-			if (safe_retry++ > 50)
-				break;
-			result = tcp_input_record(xprt);
-		}
-		while (result >= 0);
-	
-		switch (result) {
-			case -EAGAIN:
-				continue;
-			case -ENOTCONN:
-			case -EPIPE:
-				xprt_disconnect(xprt);
-				continue;
-			default:
-				printk(KERN_WARNING "RPC: unexpected error %d from tcp_input_record\n",
-					result);
-		}
-	}
 
-	while((xprt=rpc_tx_xprt_pending)!=NULL)
-	{
-		rpc_tx_xprt_pending = xprt->tx_pending;
-		xprt->tx_pending_flag = 0;
-		tcp_output_record(xprt);
-	}
-}
+		do {
+			result = tcp_input_record(xprt);
+		} while (result >= 0);
 
+		if (!xprt_connected(xprt) && !xprt->connecting)
+			xprt_close(xprt);
 
-extern inline void tcp_rpciod_queue(void)
-{
-	rpciod_wake_up();
+		if (safe_retry++ > 200) {
+			schedule();
+			safe_retry = 0;
+		}
+	}
 }
 
 /*
@@ -839,37 +977,22 @@
 	struct rpc_xprt	*xprt;
 
 	dprintk("RPC:      tcp_data_ready...\n");
-	if (!(xprt = xprt_from_sock(sk)))
-	{
+	if (!(xprt = xprt_from_sock(sk))) {
 		printk("Not a socket with xprt %p\n", sk);
-		return;
+		goto wake;
 	}
+
+	if (xprt->shutdown)
+		goto wake;
+
+	__xprt_append_pending(xprt);
+
 	dprintk("RPC:      tcp_data_ready client %p\n", xprt);
 	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
-				sk->state, xprt->connected,
+				sk->state, xprt_connected(xprt),
 				sk->dead, sk->zapped);
-	/*
-	 *	If we are not waiting for the RPC bh run then
-	 *	we are now
-	 */
-	if (!xprt->rx_pending_flag)
-	{
-		int start_queue=0;
-
-		dprintk("RPC:     xprt queue %p\n", rpc_rx_xprt_pending);
-		if(rpc_rx_xprt_pending==NULL)
-			start_queue=1;
-		xprt->rx_pending_flag=1;
-		xprt->rx_pending=rpc_rx_xprt_pending;
-		rpc_rx_xprt_pending=xprt;
-		if (start_queue)
-		  {
-		    tcp_rpciod_queue();
-		    start_queue=0;
-		  }
-	}
-	else
-		dprintk("RPC:     xprt queued already %p\n", xprt);
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
 
@@ -879,41 +1002,88 @@
 	struct rpc_xprt	*xprt;
 
 	if (!(xprt = xprt_from_sock(sk)))
-		return;
+		goto wake;
 	dprintk("RPC:      tcp_state_change client %p...\n", xprt);
 	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
-				sk->state, xprt->connected,
+				sk->state, xprt_connected(xprt),
 				sk->dead, sk->zapped);
 
-	if (sk->state == TCP_ESTABLISHED && !xprt->connected) {
-		xprt->connected = 1;
-		xprt->connecting = 0;
+	switch (sk->state) {
+	case TCP_ESTABLISHED:
+		xprt_set_connected(xprt);
+		if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+			rpc_wake_up_task(xprt->snd_task);
 		rpc_wake_up(&xprt->reconn);
-	} else if (sk->zapped) {
-		rpc_wake_up_status(&xprt->pending, -ENOTCONN);
-		rpc_wake_up_status(&xprt->sending, -ENOTCONN);
-		rpc_wake_up_status(&xprt->reconn,  -ENOTCONN);
+		break;
+	case TCP_SYN_SENT:
+	case TCP_SYN_RECV:
+		break;
+	default:
+		__xprt_disconnect(xprt);
+		break;
 	}
+ wake:
+	wake_up_interruptible(sk->sleep);
 }
 
+/*
+ * The following 2 routines allow a task to sleep while socket memory is
+ * low.
+ */
 static void
 tcp_write_space(struct sock *sk)
 {
 	struct rpc_xprt	*xprt;
+	unsigned long	oldflags;
 
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
-	if (!xprt->tx_pending_flag) {
-		int start_queue = 0;
+	if (xprt->shutdown)
+		return;
 
-		if (rpc_tx_xprt_pending == NULL)
-			start_queue = 1;
-		xprt->tx_pending_flag = 1;
-		xprt->tx_pending = rpc_tx_xprt_pending;
-		rpc_tx_xprt_pending = xprt;
-		if (start_queue)
-			tcp_rpciod_queue();
-	}
+	/* Wait until we have enough socket memory */
+	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
+		return;
+
+	spin_lock_irqsave(&xprt_sock_lock, oldflags);
+	if (xprt->write_space)
+		goto out_unlock;
+
+	xprt->write_space = 1;
+
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+	wake_up_interruptible(sk->sleep);
+}
+
+static void
+udp_write_space(struct sock *sk)
+{
+	struct rpc_xprt *xprt;
+	unsigned long	oldflags;
+
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	if (xprt->shutdown)
+		return;
+
+	/* Wait until we have enough socket memory */
+	if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE))
+		return;
+
+	spin_lock_irqsave(&xprt_sock_lock, oldflags);
+	if (xprt->write_space)
+		goto out_unlock;
+
+	xprt->write_space = 1;
+
+	if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
+		rpc_wake_up_task(xprt->snd_task);
+ out_unlock:
+	spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+	wake_up_interruptible(sk->sleep);
 }
 
 /*
@@ -924,9 +1094,8 @@
 {
 	struct rpc_rqst	*req = task->tk_rqstp;
 
-	if (req) {
-		xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
-	}
+	if (req)
+		__xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
 
 	dprintk("RPC: %4d xprt_timer (%s request)\n",
 		task->tk_pid, req ? "pending" : "backlogged");
@@ -936,32 +1105,47 @@
 	rpc_wake_up_task(task);
 }
 
+
 /*
- * (Partly) transmit the RPC packet
- * Note that task->tk_status is either 0 or negative on return.
- * Only when the reply is received will the status be set to a
- * positive value.
+ * Serialize access to sockets, in order to prevent different
+ * requests from interfering with each other.
  */
-static inline int
-xprt_transmit_some(struct rpc_xprt *xprt, struct rpc_task *task)
+int
+xprt_down_transmit(struct rpc_task *task)
 {
+	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	int		result;
 
-	task->tk_status = 0;
-	if ((result = xprt_sendmsg(xprt)) >= 0) {
-		if (!xprt->snd_buf.io_len || !xprt->stream) {
-			rpc_wake_up_next(&xprt->sending);
-			return req->rq_slen;
-		}
-		result = -EAGAIN;
-	} else if (xprt->stream) {
-		if (result == -ENOTCONN || result == -EPIPE) {
-			xprt_disconnect(xprt);
-			result = -ENOTCONN;
-		}
+	xprt_delete_tcp_timer(xprt);
+	if (xprt->snd_task && xprt->snd_task != task) {
+		dprintk("RPC: %4d TCP write queue full (task %d)\n",
+			task->tk_pid, xprt->snd_task->tk_pid);
+		task->tk_timeout = 0;
+		task->tk_status = -EAGAIN;
+		rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+	} else if (!xprt->snd_task) {
+		xprt->snd_task = task;
+#ifdef RPC_PROFILE
+		req->rq_xtime = jiffies;
+#endif
+		req->rq_bytes_sent = 0;
+	}
+	return xprt->snd_task == task;
+}
+
+/*
+ * Releases the socket for use by other requests.
+ */
+void
+xprt_up_transmit(struct rpc_task *task)
+{
+	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
+
+	if (xprt->snd_task && xprt->snd_task == task) {
+		xprt->snd_task = NULL;
+		if (!rpc_wake_up_next(&xprt->sending) && xprt->stream)
+			xprt_add_tcp_timer(xprt, RPCXPRT_TIMEOUT);
 	}
-	return task->tk_status = result;
 }
 
 /*
@@ -971,129 +1155,137 @@
 void
 xprt_transmit(struct rpc_task *task)
 {
-	struct rpc_timeout *timeo;
 	struct rpc_rqst	*req = task->tk_rqstp;
 	struct rpc_xprt	*xprt = req->rq_xprt;
-	int status;
 
-	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, 
-				*(u32 *)(req->rq_svec[0].iov_base));
+	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, req->rq_xid);
 
-	if (xprt->shutdown) {
+	if (xprt->shutdown)
 		task->tk_status = -EIO;
+
+	if (!xprt_connected(xprt))
+		task->tk_status = -ENOTCONN;
+
+	if (task->tk_status < 0)
 		return;
-	}
 
-	/* If we're not already in the process of transmitting our call,
-	 * set up everything as needed. */
-	if (xprt->snd_task != task) {
-		/* Write the record marker */
-		if (xprt->stream) {
-			u32	marker;
+	/* set up everything as needed. */
+	/* Write the record marker */
+	if (xprt->stream) {
+		u32	*marker = req->rq_svec[0].iov_base;
 
-			if (!xprt->connected) {
-				task->tk_status = -ENOTCONN;
-				return;
-			}
-			marker = htonl(0x80000000|(req->rq_slen-4));
-			*((u32 *) req->rq_svec[0].iov_base) = marker;
-		}
+		*marker = htonl(0x80000000|(req->rq_slen-sizeof(*marker)));
+	}
 
-		/* Reset timeout parameters */
-		timeo = &req->rq_timeout;
-		if (timeo->to_retries < 0) {
-			dprintk("RPC: %4d xprt_transmit reset timeo\n",
-						task->tk_pid);
-			timeo->to_retries = xprt->timeout.to_retries;
-			timeo->to_current = timeo->to_initval;
-		}
+	if (!xprt_down_transmit(task))
+		return;
 
-#ifdef RPC_PROFILE
-		req->rq_xtime = jiffies;
-#endif
-		req->rq_gotit = 0;
+	do_xprt_transmit(task);
+}
+
+static void
+do_xprt_transmit(struct rpc_task *task)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	struct rpc_xprt	*xprt = req->rq_xprt;
+	unsigned long	oldflags;
+	int status, retry = 0;
 
-		if (xprt->snd_task) {
-			dprintk("RPC: %4d TCP write queue full (task %d)\n",
-					task->tk_pid, xprt->snd_task->tk_pid);
-			rpc_sleep_on(&xprt->sending, task,
-					xprt_transmit_status, NULL);
-			return;
-		}
-		xprt->snd_buf  = req->rq_snd_buf;
-		xprt->snd_task = task;
-		xprt->snd_sent = 0;
-	}
 
 	/* For fast networks/servers we have to put the request on
 	 * the pending list now:
+	 * Note that we don't want the task timing out during the
+	 * call to xprt_sendmsg(), so we initially disable the timeout,
+	 * and then reset it later...
 	 */
-	start_bh_atomic();
-	status = rpc_add_wait_queue(&xprt->pending, task);
-	if (!status)
-		task->tk_callback = NULL;
-	end_bh_atomic();
-
-	if (status)
-	{
-		printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);
-		task->tk_status = status;
-		return;
-	}
+	xprt_receive(task);
 
 	/* Continue transmitting the packet/record. We must be careful
 	 * to cope with writespace callbacks arriving _after_ we have
 	 * called xprt_sendmsg().
 	 */
-	while (1) {
+	for (;;) {
+		status = -ENOTCONN;
+		if (!xprt->inet)
+			break;
+
 		xprt->write_space = 0;
-		if (xprt_transmit_some(xprt, task) != -EAGAIN) {
-			dprintk("RPC: %4d xmit complete\n", task->tk_pid);
-			xprt->snd_task = NULL;
-			return;
+		status = -ENOMEM;
+		if (sock_wspace(xprt->inet) < req->rq_slen + MIN_WRITE_SPACE)
+			break;
+		status = xprt_sendmsg(xprt, req);
+
+		if (status < 0)
+			break;
+
+		if (xprt->stream) {
+			req->rq_bytes_sent += status;
+
+			if (req->rq_bytes_sent >= req->rq_slen)
+				goto out_receive;
+		} else {
+			if (status >= req->rq_slen)
+				goto out_receive;
+			status = -ENOMEM;
+			break;
 		}
 
-		/*d*/dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
-				task->tk_pid, xprt->snd_buf.io_len,
+		dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
+				task->tk_pid, req->rq_slen - req->rq_bytes_sent,
 				req->rq_slen);
-		task->tk_status = 0;
-		start_bh_atomic();
+
+		status = -EAGAIN;
+		if (retry++ > 50)
+			break;
+	}
+	rpc_unlock_task(task);
+
+	/* Note: at this point, task->tk_sleeping has not yet been set,
+	 *	 hence there is no danger of the waking up task being put on
+	 *	 schedq, and being picked up by a parallel run of rpciod().
+	 */
+	rpc_wake_up_task(task);
+	if (!RPC_IS_RUNNING(task))
+		goto out_release;
+	if (req->rq_received)
+		goto out_release;
+
+	task->tk_status = status;
+
+	switch (status) {
+	case -ENOMEM:
+		/* Protect against (udp|tcp)_write_space */
+		spin_lock_irqsave(&xprt_sock_lock, oldflags);
 		if (!xprt->write_space) {
-			/* Remove from pending */
-			rpc_remove_wait_queue(task);
-			rpc_sleep_on(&xprt->sending, task,
-					xprt_transmit_status, NULL);
-			end_bh_atomic();
+			task->tk_timeout = req->rq_timeout.to_current;
+			rpc_sleep_on(&xprt->sending, task, NULL, NULL);
+		}
+		spin_unlock_irqrestore(&xprt_sock_lock, oldflags);
+		return;
+	case -EAGAIN:
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		/* Keep holding the socket if it is blocked */
+		if (!xprt->stream) {
+			rpc_delay(task, HZ>>4);
 			return;
 		}
-		end_bh_atomic();
+	default:
+		goto out_release;
 	}
-}
-
-/*
- * This callback is invoked when the sending task is forced to sleep
- * because the TCP write buffers are full
- */
-static void
-xprt_transmit_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_client->cl_xprt;
 
-	dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status);
-	if (xprt->snd_task == task) 
-	{
-		if (task->tk_status < 0)
-		{
-			xprt->snd_task = NULL;
-			xprt_disconnect(xprt);
-		}
-		else
-			xprt_transmit(task);
-	}
+ out_receive:
+	dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+	/* Set the task's receive timeout value */
+	task->tk_timeout = req->rq_timeout.to_current;
+	rpc_add_timer(task, xprt_timer);
+	rpc_unlock_task(task);
+ out_release:
+	xprt_up_transmit(task);
 }
 
 /*
- * Wait for the reply to our call.
+ * Queue the task for a reply to our call.
  * When the callback is invoked, the congestion window should have
  * been updated already.
  */
@@ -1104,34 +1296,10 @@
 	struct rpc_xprt	*xprt = req->rq_xprt;
 
 	dprintk("RPC: %4d xprt_receive\n", task->tk_pid);
-	if (xprt->connected == 0) {
-		task->tk_status = -ENOTCONN;
-		return;
-	}
-
-	/*
-	 * Wait until rq_gotit goes non-null, or timeout elapsed.
-	 */
-	task->tk_timeout = req->rq_timeout.to_current;
-
-	start_bh_atomic();
-	if (!req->rq_gotit) {
-		rpc_sleep_on(&xprt->pending, task,
-				xprt_receive_status, xprt_timer);
-	}
-	end_bh_atomic();
 
-	dprintk("RPC: %4d xprt_receive returns %d\n",
-				task->tk_pid, task->tk_status);
-}
-
-static void
-xprt_receive_status(struct rpc_task *task)
-{
-	struct rpc_xprt	*xprt = task->tk_xprt;
-
-	if (xprt->stream && xprt->tcp_rqstp == task->tk_rqstp)
-		xprt->tcp_rqstp = NULL;
+	req->rq_received= 0;
+	task->tk_timeout = 0;
+	rpc_sleep_locked(&xprt->pending, task, NULL, NULL);
 }
 
 /*
@@ -1148,15 +1316,18 @@
 
 	dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n",
 				task->tk_pid, xprt->cong, xprt->cwnd);
-	if ((!RPCXPRT_CONGESTED(xprt) && xprt->free)) {
-		xprt_reserve_status(task);
+	start_bh_atomic();
+	__xprt_reserve_status(task);
+	if (task->tk_rqstp) {
 		task->tk_timeout = 0;
 	} else if (!task->tk_timeout) {
 		task->tk_status = -ENOBUFS;
 	} else {
 		dprintk("RPC:      xprt_reserve waiting on backlog\n");
-		rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL);
+		task->tk_status = -EAGAIN;
+		rpc_sleep_on(&xprt->backlog, task, NULL, NULL);
 	}
+	end_bh_atomic();
 	dprintk("RPC: %4d xprt_reserve returns %d\n",
 				task->tk_pid, task->tk_status);
 	return task->tk_status;
@@ -1166,7 +1337,7 @@
  * Reservation callback
  */
 static void
-xprt_reserve_status(struct rpc_task *task)
+__xprt_reserve_status(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst	*req;
@@ -1177,40 +1348,25 @@
 		/* NOP */
 	} else if (task->tk_rqstp) {
 		/* We've already been given a request slot: NOP */
-	} else if (!RPCXPRT_CONGESTED(xprt)) {
+	} else {
+		if (RPCXPRT_CONGESTED(xprt) || !(req = xprt->free))
+			goto out_nofree;
 		/* OK: There's room for us. Grab a free slot and bump
 		 * congestion value */
-		req = xprt->free;
-		if (!req)
-			goto bad_list;
-		if (req->rq_xid)
-			goto bad_used;
 		xprt->free     = req->rq_next;
+		req->rq_next   = NULL;
 		xprt->cong    += RPC_CWNDSCALE;
 		task->tk_rqstp = req;
-		req->rq_next   = NULL;
 		xprt_request_init(task, xprt);
-	} else {
-		task->tk_status = -EAGAIN;
-	}
 
-	if (xprt->free && !RPCXPRT_CONGESTED(xprt))
-		rpc_wake_up_next(&xprt->backlog);
+		if (xprt->free)
+			__xprt_clear_backlog(xprt);
+	}
 
 	return;
 
-bad_list:
-	printk(KERN_ERR 
-		"RPC: %4d inconsistent free list (cong %ld cwnd %ld)\n",
-		task->tk_pid, xprt->cong, xprt->cwnd);
-	rpc_debug = ~0;
-	goto bummer;
-bad_used:
-	printk(KERN_ERR "RPC: used rqst slot %p on free list!\n", req);
-bummer:
-	task->tk_status = -EIO;
-	xprt->free = NULL;
-	return;
+out_nofree:
+	task->tk_status = -EAGAIN;
 }
 
 /*
@@ -1227,7 +1383,6 @@
 
 	dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
 	task->tk_status = 0;
-	req->rq_gotit	= 0;
 	req->rq_timeout = xprt->timeout;
 	req->rq_task	= task;
 	req->rq_xprt    = xprt;
@@ -1245,6 +1400,7 @@
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct rpc_rqst	*req;
 
+	xprt_up_transmit(task);
 	if (!(req = task->tk_rqstp))
 		return;
 	task->tk_rqstp = NULL;
@@ -1252,46 +1408,15 @@
 
 	dprintk("RPC: %4d release request %p\n", task->tk_pid, req);
 
-	/* remove slot from queue of pending */
 	start_bh_atomic();
-	if (task->tk_rpcwait) {
-		printk("RPC: task of released request still queued!\n");
-#ifdef RPC_DEBUG
-		printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait));
-#endif
-		rpc_del_timer(task);
-		rpc_remove_wait_queue(task);
-	}
-	end_bh_atomic();
+	req->rq_next = xprt->free;
+	xprt->free   = req;
 
 	/* Decrease congestion value. */
 	xprt->cong -= RPC_CWNDSCALE;
 
-#if 0
-	/* If congestion threshold is not yet reached, pass on the request slot.
-	 * This looks kind of kludgy, but it guarantees backlogged requests
-	 * are served in order.
-	 * N.B. This doesn't look completely safe, as the task is still
-	 * on the backlog list after wake-up.
-	 */
-	if (!RPCXPRT_CONGESTED(xprt)) {
-		struct rpc_task	*next = rpc_wake_up_next(&xprt->backlog);
-
-		if (next && next->tk_rqstp == 0) {
-			xprt->cong += RPC_CWNDSCALE;
-			next->tk_rqstp = req;
-			xprt_request_init(next, xprt);
-			return;
-		}
-	}
-#endif
-
-	req->rq_next = xprt->free;
-	xprt->free   = req;
-
-	/* If not congested, wake up the next backlogged process */
-	if (!RPCXPRT_CONGESTED(xprt))
-		rpc_wake_up_next(&xprt->backlog);
+	__xprt_clear_backlog(xprt);
+	end_bh_atomic();
 }
 
 /*
@@ -1303,7 +1428,7 @@
 	if (proto == IPPROTO_UDP)
 		xprt_set_timeout(to, 5,  5 * HZ);
 	else
-		xprt_set_timeout(to, 5, 15 * HZ);
+		xprt_set_timeout(to, 5, 60 * HZ);
 }
 
 /*
@@ -1330,56 +1455,36 @@
 {
 	struct rpc_xprt	*xprt;
 	struct rpc_rqst	*req;
-	struct sock	*inet;
 	int		i;
 
 	dprintk("RPC:      setting up %s transport...\n",
 				proto == IPPROTO_UDP? "UDP" : "TCP");
 
-#if LINUX_VERSION_CODE >= 0x020100
-	inet = sock->sk;
-#else
-	inet = (struct sock *) sock->data;
-#endif
-
 	if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
 		return NULL;
 	memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
 
-	xprt->file = NULL;
-	xprt->sock = sock;
-	xprt->inet = inet;
 	xprt->addr = *ap;
 	xprt->prot = proto;
 	xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
-	xprt->cwnd = RPC_INITCWND;
-#ifdef SOCK_HAS_USER_DATA
-	inet->user_data = xprt;
-#else
-	xprt->link = sock_list;
-	sock_list = xprt;
-#endif
-	xprt->old_data_ready = inet->data_ready;
-	xprt->old_state_change = inet->state_change;
-	xprt->old_write_space = inet->write_space;
-	if (proto == IPPROTO_UDP) {
-		inet->data_ready = udp_data_ready;
-	} else {
-		inet->data_ready = tcp_data_ready;
-		inet->state_change = tcp_state_change;
-		inet->write_space = tcp_write_space;
+	if (xprt->stream) {
+		xprt->cwnd = RPC_MAXCWND;
 		xprt->nocong = 1;
-	}
-	xprt->connected = 1;
+	} else
+		xprt->cwnd = RPC_INITCWND;
+	xprt->congtime = jiffies;
 
 	/* Set timeout parameters */
 	if (to) {
 		xprt->timeout = *to;
 		xprt->timeout.to_current = to->to_initval;
 		xprt->timeout.to_resrvval = to->to_maxval << 1;
-	} else {
+	} else
 		xprt_default_timeout(&xprt->timeout, xprt->prot);
-	}
+
+	init_timer(&xprt->tcp_timer);
+	xprt->tcp_timer.data = (unsigned long)xprt;
+	xprt->tcp_timer.function = (void(*)(unsigned long)) xprt_tcp_timeout;
 
 	xprt->pending = RPC_INIT_WAITQ("xprt_pending");
 	xprt->sending = RPC_INIT_WAITQ("xprt_sending");
@@ -1392,50 +1497,15 @@
 	req->rq_next = NULL;
 	xprt->free = xprt->slot;
 
+	INIT_LIST_HEAD(&xprt->rx_pending);
+
 	dprintk("RPC:      created transport %p\n", xprt);
 	
-	/*
-	 *	TCP requires the rpc I/O daemon is present
-	 */
-	if(proto==IPPROTO_TCP)
-		rpciod_up();
+	xprt_bind_socket(xprt, sock);
 	return xprt;
 }
 
 /*
- * Create and initialize an RPC client given an open file.
- * This is obsolete now.
- */
-#if 0
-struct rpc_xprt *
-xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to)
-{
-	struct rpc_xprt	*xprt;
-	struct socket	*sock;
-	int		proto;
-
-	if (!file) {
-		printk("RPC: file == NULL in xprt_create!\n");
-		return NULL;
-	}
-
-	sock = &file->f_inode->u.socket_i;
-	if (sock->ops->family != PF_INET) {
-		printk(KERN_WARNING "RPC: only INET sockets supported\n");
-		return NULL;
-	}
-
-	proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP;
-	if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) {
-		xprt->file = file;
-		file->f_count++;
-	}
-
-	return xprt;
-}
-#endif
-
-/*
  * Bind to a reserved port
  */
 static inline int
@@ -1459,38 +1529,66 @@
 	return err;
 }
 
+static int 
+xprt_bind_socket(struct rpc_xprt *xprt, struct socket *sock)
+{
+	struct sock	*sk = sock->sk;
+
+	if (xprt->inet)
+		return -EBUSY;
+
+	start_bh_atomic();
+	sk->user_data = xprt;
+	xprt->old_data_ready = sk->data_ready;
+	xprt->old_state_change = sk->state_change;
+	xprt->old_write_space = sk->write_space;
+	if (xprt->prot == IPPROTO_UDP) {
+		sk->data_ready = udp_data_ready;
+		sk->write_space = udp_write_space;
+		xprt_set_connected(xprt);
+	} else {
+		sk->data_ready = tcp_data_ready;
+		sk->state_change = tcp_state_change;
+		sk->write_space = tcp_write_space;
+		xprt_clear_connected(xprt);
+	}
+
+	/* Reset to new socket */
+	xprt->sock = sock;
+	xprt->inet = sk;
+	end_bh_atomic();
+	/*
+	 *	TCP requires the rpc I/O daemon is present
+	 */
+	if(xprt->stream)
+		rpciod_up();
+
+	return 0;
+}
+
 /*
  * Create a client socket given the protocol and peer address.
  */
 static struct socket *
-xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+xprt_create_socket(int proto, struct rpc_timeout *to)
 {
 	struct socket	*sock;
 	int		type, err;
 
-	dprintk("RPC:      xprt_create_socket(%08x, %s %d)\n",
-			   sap? ntohl(sap->sin_addr.s_addr) : 0,
+	dprintk("RPC:      xprt_create_socket(%s %d)\n",
 			   (proto == IPPROTO_UDP)? "udp" : "tcp", proto);
 
 	type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+
 	if ((err = sock_create(PF_INET, type, proto, &sock)) < 0) {
 		printk("RPC: can't create socket (%d).\n", -err);
 		goto failed;
 	}
 
-	/* If the caller has root privs, bind to a reserved port */
-	if (!current->fsuid && xprt_bindresvport(sock) < 0)
+	/* If the caller has the capability, bind to a reserved port */
+	if (capable(CAP_NET_BIND_SERVICE) && xprt_bindresvport(sock) < 0)
 		goto failed;
 
-	if (type == SOCK_STREAM && sap) {
-		err = sock->ops->connect(sock, (struct sockaddr *) sap,
-						sizeof(*sap), 0);
-		if (err < 0) {
-			printk("RPC: TCP connect failed (%d).\n", -err);
-			goto failed;
-		}
-	}
-
 	return sock;
 
 failed:
@@ -1509,7 +1607,7 @@
 
 	dprintk("RPC:      xprt_create_proto called\n");
 
-	if (!(sock = xprt_create_socket(proto, sap, to)))
+	if (!(sock = xprt_create_socket(proto, to)))
 		return NULL;
 
 	if (!(xprt = xprt_setup(sock, proto, sap, to)))
@@ -1529,6 +1627,20 @@
 	rpc_wake_up(&xprt->pending);
 	rpc_wake_up(&xprt->backlog);
 	rpc_wake_up(&xprt->reconn);
+	wake_up(&xprt->cong_wait);
+}
+
+/*
+ * Clear the xprt backlog queue
+ */
+static int
+__xprt_clear_backlog(struct rpc_xprt *xprt)
+{
+	if (RPCXPRT_CONGESTED(xprt))
+		return 0;
+	rpc_wake_up_next(&xprt->backlog);
+	wake_up(&xprt->cong_wait);
+	return 1;
 }
 
 /*
@@ -1537,20 +1649,11 @@
 int
 xprt_destroy(struct rpc_xprt *xprt)
 {
-#ifndef SOCK_HAS_USER_DATA
-	struct rpc_xprt	**q;
-
-	for (q = &sock_list; *q && *q != xprt; q = &((*q)->link))
-		;
-	if (!*q) {
-		printk(KERN_WARNING "xprt_destroy: unknown socket!\n");
-		return -EIO;	/* why is there no EBUGGYSOFTWARE */
-	}
-	*q = xprt->link;
-#endif
-
 	dprintk("RPC:      destroying transport %p\n", xprt);
+	xprt_shutdown(xprt);
 	xprt_close(xprt);
+	xprt_delete_tcp_timer(xprt);
+	xprt_remove_pending(xprt);
 	kfree(xprt);
 
 	return 0;
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/unix/af_unix.c linux.18p17/net/unix/af_unix.c
--- linux.vanilla/net/unix/af_unix.c	Sun Jun 11 21:44:24 2000
+++ linux.18p17/net/unix/af_unix.c	Thu Oct 19 01:11:21 2000
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.76.2.4 2000/05/27 04:46:44 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.76.2.5 2000/10/17 09:08:37 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -110,6 +110,8 @@
 
 #define min(a,b)	(((a)<(b))?(a):(b))
 
+extern int unix_tot_inflight;
+
 int sysctl_unix_delete_delay = HZ;
 int sysctl_unix_destroy_delay = 10*HZ;
 int sysctl_unix_max_dgram_qlen = 10;
@@ -130,7 +132,6 @@
 {
 	hash ^= hash>>16;
 	hash ^= hash>>8;
-	hash ^= hash>>4;
 	return hash;
 }
 
@@ -240,7 +241,7 @@
 {
 	unix_socket *s;
 
-	for (s=unix_socket_table[(hash^type)&0xF]; s; s=s->next)
+	for (s=unix_socket_table[(hash^type)&(UNIX_HASH_SIZE-1)]; s; s=s->next)
 	{
 		if(s->protinfo.af_unix.addr->len==len &&
 		   memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0 &&
@@ -257,7 +258,7 @@
 {
 	unix_socket *s;
 
-	for (s=unix_socket_table[i->i_ino & 0xF]; s; s=s->next)
+	for (s=unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]; s; s=s->next)
 	{
 		struct dentry *dentry = s->protinfo.af_unix.dentry;
 
@@ -344,7 +345,8 @@
 	 *	  What the above comment does talk about? --ANK(980817)
 	 */
 
-	unix_gc();		/* Garbage collect fds */	
+	if (unix_tot_inflight)
+		unix_gc();		/* Garbage collect fds */	
 	return 0;
 }
 	
@@ -519,7 +521,7 @@
 
 	sk->protinfo.af_unix.addr = addr;
 	unix_remove_socket(sk);
-	sk->protinfo.af_unix.list = &unix_socket_table[(addr->hash ^ sk->type)&0xF];
+	sk->protinfo.af_unix.list = &unix_socket_table[(addr->hash ^ sk->type)&(UNIX_HASH_SIZE - 1)];
 	unix_insert_socket(sk);
 	return 0;
 }
@@ -607,7 +609,7 @@
 		}
 		unix_remove_socket(sk);
 		sk->protinfo.af_unix.addr = addr;
-		sk->protinfo.af_unix.list = &unix_socket_table[(hash^sk->type)&0xF];
+		sk->protinfo.af_unix.list = &unix_socket_table[(hash^sk->type)&(UNIX_HASH_SIZE - 1)];
 		unix_insert_socket(sk);
 		return 0;
 	}
@@ -628,7 +630,7 @@
 			return err;
 	}
 	unix_remove_socket(sk);
-	sk->protinfo.af_unix.list = &unix_socket_table[dentry->d_inode->i_ino & 0xF];
+	sk->protinfo.af_unix.list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE - 1)];
 	sk->protinfo.af_unix.dentry = dentry;
 	unix_insert_socket(sk);
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/unix/garbage.c linux.18p17/net/unix/garbage.c
--- linux.vanilla/net/unix/garbage.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/unix/garbage.c	Thu Oct 19 01:11:21 2000
@@ -108,6 +108,8 @@
 	return u_sock;
 }
 
+int unix_tot_inflight;
+
 /*
  *	Keep the number of times in flight count for the file
  *	descriptor if it is for an AF_UNIX socket.
@@ -116,15 +118,19 @@
 void unix_inflight(struct file *fp)
 {
 	unix_socket *s=unix_get_socket(fp);
-	if(s)
+	if (s) {
 		s->protinfo.af_unix.inflight++;
+		unix_tot_inflight++;
+	}
 }
 
 void unix_notinflight(struct file *fp)
 {
 	unix_socket *s=unix_get_socket(fp);
-	if(s)
+	if (s) {
 		s->protinfo.af_unix.inflight--;
+		unix_tot_inflight--;
+	}
 }
 
 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/x25/af_x25.c linux.18p17/net/x25/af_x25.c
--- linux.vanilla/net/x25/af_x25.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/x25/af_x25.c	Tue Sep 26 21:50:14 2000
@@ -16,7 +16,10 @@
  *	X.25 001	Jonathan Naylor	Started coding.
  *	X.25 002	Jonathan Naylor	Centralised disconnect handling.
  *					New timer architecture.
- *	2000-11-03	Henner Eisen	MSG_EOR handling more POSIX compliant.
+ *	2000-03-11	Henner Eisen	MSG_EOR handling more POSIX compliant.
+ *	2000-08-27	Arnaldo C. Melo s/suser/capable/
+ *	2000-04-09	Henner Eisen	Set sock->state in x25_accept().
+ *					Fixed x25_output() related skb leakage.
  */
 
 #include <linux/config.h>
@@ -726,6 +729,7 @@
 	kfree_skb(skb);
 	sk->ack_backlog--;
 	newsock->sk = newsk;
+	newsock->state = SS_CONNECTED;
 
 	return 0;
 }
@@ -971,7 +975,11 @@
 	if (msg->msg_flags & MSG_OOB) {
 		skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb);
 	} else {
-		x25_output(sk, skb);
+		err = x25_output(sk, skb);
+		if(err){
+			len = err;
+			kfree_skb(skb);
+		}
 	}
 
 	x25_kick(sk);
@@ -1116,7 +1124,7 @@
 			return x25_subscr_ioctl(cmd, (void *)arg);
 
 		case SIOCX25SSUBSCRIP:
-			if (!suser()) return -EPERM;
+			if (!capable(CAP_NET_ADMIN)) return -EPERM;
 			return x25_subscr_ioctl(cmd, (void *)arg);
 
 		case SIOCX25GFACILITIES: {
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/x25/x25_dev.c linux.18p17/net/x25/x25_dev.c
--- linux.vanilla/net/x25/x25_dev.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/x25/x25_dev.c	Tue Sep 26 21:50:14 2000
@@ -14,6 +14,7 @@
  *
  *	History
  *	X.25 001	Jonathan Naylor	Started coding.
+ *      2000-09-04	Henner Eisen	Prevent freeing a dangling skb.
  */
 
 #include <linux/config.h>
@@ -85,12 +86,13 @@
 		return x25_rx_call_request(skb, neigh, lci);
 
 	/*
-	 *	Its not a Call Request, nor is it a control frame, throw it awa
+	 *	Its not a Call Request, nor is it a control frame.
+	 *      Let caller throw it away.
 	 */
 /*
 	x25_transmit_clear_request(neigh, lci, 0x0D);
 */
-	kfree_skb(skb);
+	printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype);
 
 	return 0;
 }
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/net/x25/x25_out.c linux.18p17/net/x25/x25_out.c
--- linux.vanilla/net/x25/x25_out.c	Fri Apr 21 12:47:17 2000
+++ linux.18p17/net/x25/x25_out.c	Tue Sep 26 21:50:14 2000
@@ -15,6 +15,7 @@
  *	History
  *	X.25 001	Jonathan Naylor	Started coding.
  *	X.25 002	Jonathan Naylor	New timer architecture.
+ *      2000-09-04	Henner Eisen    Prevented x25_output() skb leakage.
  */
 
 #include <linux/config.h>
@@ -56,7 +57,7 @@
 /*
  *	This is where all X.25 information frames pass;
  */
-void x25_output(struct sock *sk, struct sk_buff *skb)
+int x25_output(struct sock *sk, struct sk_buff *skb)
 {
 	struct sk_buff *skbn;
 	unsigned char header[X25_EXT_MIN_LEN];
@@ -73,9 +74,12 @@
 		frontlen = skb_headroom(skb);
 
 		while (skb->len > 0) {
-			if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, 0, &err)) == NULL)
-				return;
-
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, 0, &err)) == NULL){
+				int unsent = skb->len - header_len;
+				SOCK_DEBUG(sk, "x25_output: fragment allocation failed, err=%d, %d bytes unsent\n", err, unsent); 
+				return err;
+				}
+				
 			skb_reserve(skbn, frontlen);
 
 			len = (max_len > skb->len) ? skb->len : max_len;
@@ -102,6 +106,7 @@
 	} else {
 		skb_queue_tail(&sk->write_queue, skb);
 	}
+	return 0;
 }
 
 /* 
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/scripts/ksymoops/ksymoops.c linux.18p17/scripts/ksymoops/ksymoops.c
--- linux.vanilla/scripts/ksymoops/ksymoops.c	Fri Apr 21 12:47:18 2000
+++ linux.18p17/scripts/ksymoops/ksymoops.c	Sat Oct 14 00:53:12 2000
@@ -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:");
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/scripts/kwhich linux.18p17/scripts/kwhich
--- linux.vanilla/scripts/kwhich	Thu Jan  1 01:00:00 1970
+++ linux.18p17/scripts/kwhich	Sun Oct  1 00:20:36 2000
@@ -0,0 +1,24 @@
+# kwhich 1.0 (C) 2000 Miquel van Smoorenburg
+# This program is GPLed
+
+if [ $# -lt 1 ]
+then
+        echo "Usage: $0 cmd [cmd..]" >&2
+        exit 1
+fi
+
+IFS=:
+for cmd in $*
+do
+        for path in $PATH
+        do
+                if [ -x "$path/$cmd" ]
+                then
+                        echo "$path/$cmd"
+                        exit 0
+                fi
+        done
+done
+
+echo "$*: not found" >&2
+exit 1
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/scripts/mkdep.c linux.18p17/scripts/mkdep.c
--- linux.vanilla/scripts/mkdep.c	Sun Jun 11 21:44:24 2000
+++ linux.18p17/scripts/mkdep.c	Mon Sep 25 16:08:59 2000
@@ -285,6 +285,7 @@
  * The state machine looks for (approximately) these Perl regular expressions:
  *
  *    m|\/\*.*?\*\/|
+ *    m|\/\/.*|
  *    m|'.*?'|
  *    m|".*?"|
  *    m|#\s*include\s*"(.*?)"|
@@ -319,9 +320,18 @@
 	CASE('_',  underscore);
 	goto start;
 
+/* // */
+slash_slash:
+	GETNEXT
+	CASE('\n', start);
+	NOTCASE('\\', slash_slash);
+	GETNEXT
+	goto slash_slash;
+
 /* / */
 slash:
 	GETNEXT
+	CASE('/',  slash_slash);
 	NOTCASE('*', __start);
 slash_star_dot_star:
 	GETNEXT
